Django:제로부터 시작하는 인스타그램 만들기 - DevOps #4 - CI/CD구축하기, 테스트코드 작성, github 액션을 통한 CI/C

     

2021.09.15 - [Course/django : 제로부터 시작하는 인스타그램] - Django:제로부터 시작하는 인스타그램 만들기 - clone instagram 목차
 
github - tkdlek11112/django_zero_to_instagram_devops: 장고 인스타그램 클론코딩 데브옵스편 (github.com) 

유튜브 버젼 - https://youtu.be/7bf4rK2rMPM

   

시작하기 전에

 
이번시간에는 CI/CD에 대해서 다룹니다. CI/CD라는 말을 저는 2015년? 2016년? 정도에 처음 들었던 것 같아요. 이때 한창 유행했던 게 CI/CD, RPA등과 같은 자동화에 대한 기술들이었습니다. CI/CD라는 말을 처음 들었을 때는 도대체 그게 뭐임?이라는 생각이 들었는데 ㅋㅋㅋ 사실 지금도 뭐 비슷합니다. 
 
일단 CI와 CD를 분리해서 봐야 하는데요, CI는 Continuous Integration이고 CD는 Continuous Deploy입니다. 번역하면 CI는 지속적인 통합이고 CD는 지속적인 배포죠. 배포라는 말은 알겠는데 통합이라는 건 대체 뭔가? 하시는 분이 있을 겁니다. 저도 통합이라고 하면 뭐지? 하는데, 실제로 어떤 일들이 일어나는지 보면, 아~ 이게 통합이구나 알 수 있습니다. ㅎㅎ
 
일단 핵심은 Continuous라는 말인데요, '지속적인'이라는 게 무슨 뜻일까요? 여태까지 우리는 django로 인스타그램 클론코딩을 할 때 변경한 사항을 커밋하고, 푸시해서 github에 올리고, 서버에서 pull 해서 변경된 소스들을 다운로드하고, restart를 통해 변경사항을 적용했습니다. 대부분의 회사에서도 비슷한 일을 하겠죠? 
 
소스 수정 -> 변경사항 확인 (테스트) -> 코드 배포 -> 서버에 적용
 
대충 이런 식입니다. 저희는 테스트코드를 만들지 않았기 때문에 로컬에서 실행해서 기능이 잘 되나 확인하고 서버로 배포를 했었는데, CI/CD를 적용하면 소스 수정 후 커밋/푸시만 하면 뒤에 테스트, 코드배포, 서버에 적용을 자동으로 적용해 줍니다. 즉, 개발자는 소스 변경하고 커밋, 푸시만 하면 되는 거죠. 
 
CI/CD를 구성하는 건 회사마다 너무 다릅니다. 보통 제가 다녔던 회사에서는 CI는 구성하지만 CD는 구성하지 않습니다. 왜냐하면 자동으로 배포가 되는 게 상당히 리스키 할 수 있기 때문이죠. 많은 개발자가 소스를 매번 푸시하고 머지하고 있는 회사에서는 CD가 오히려 독이 될 수 있습니다. 하지만 푸시하는 권한이 한정된 사용자에게만 있고, 승인을 받아야 푸시가 되는 프로세스라면, CD는 도움이 될 수 있습니다. 
 
CI의 경우는 자동화하는 게 상당히 좋은데요, 보통 회사에서는 테스트 코드를 많이 작성해 놓기 때문에 내가 소스를 변경하고 커밋하고 푸시하는 순간 바로 테스트가 자동으로 돌면서 내가 변경한 소스가 테스트를 통과하는지 확인할 수 있습니다. 물론 내가 변경한 소스 쪽 테스트뿐만 아니라 전체 테스트가 돌기 때문에 더 쉽죠. 
 
CI에서는 테스트만 하는 것이 아닙니다. compile이 필요한 프레임워크의 경우 compile도 해주고, docker와 같은 이미지를 사용하는 회사라면 docker이미지로 build도 해줍니다. 즉 CI는 배포하기 전에 갖추어야 할 모든 것을 준비한다 의미죠. 통합이라는 게 이제는 좀 이해가 가시죠?
 
 
 

일단 테스트 코드를 만들어 보자

 
일단 저희는 테스트코드가 없습니다. ㅋㅋㅋ 클론코딩에 집중하다 보니 간단한 단위테스트조차 없는데요, 한때 TDD라는 게 유행했던 적이 있습니다. Test Driven Development로 테스트위주의 개발을 하는 것입니다. '아니 개발도 안 했는데 테스트를 어떻게 해?'라고 생각할 수 도 있는데요, 저도 참 아이러니한 방식이라고 생각합니다만 일단 기능을 구현하기 전에 예외 케이스들을 수도코드로 작성해 보고 테스트코드를 작성한 뒤, 기능을 만들면서 내가 만든 테스트코드를 통과하는지 확인하는 방식으로 진행하는 것이 보통입니다. 이렇게 하면 혹시나 발생할 수 있는 실수를 막을 수 있고, 미처 예상하지 못한 예외케이스도 발견할 수 있죠. 대신 시간이 오래 걸립니다. 그냥 기능만 만들면 30분이면 만드는 걸 테스트코드 짜느라 3시간이 걸릴 수도 있습니다. 
 
따라서 저는 테스트코드는 작성하되, TDD는 조금 꺼리는 스타일입니다. (제 개인적인 의견입니다 ㅎㅎㅎ ㅎ ㅎㅎ ㅓ헣ㅎ)
 
아무튼 저희는 인스타 클론코딩을 하면서 몇몇 기능들을 이미 구현했기 때문에 해당 기능에 대해 테스트코드를 한번 작성해 보겠습니다.
 
먼저 시작하기 전에, 저희가 지난 시간에 AWS RDS를 보게 해 두었는데, 로컬에서 실행할 때도 AWS RDS를 보는 건 아닌 것 같고, 그렇다고 sqlite로 설정을 바꾸면 나중에 배포할 때 또 바꿔줘야 하는 번거로움이 있습니다.
 
이럴 때는 로컬에서 실행할 때의 settings.py와 서버에서 실행되는 settings.py를 나누는 방법이 있습니다. settings.py가 있는 곳에 settings_local.py 파일을 만듭니다.
 

settings_local 생성

from .settings import *를 통해 settings의 모든 내용을 가져옵니다. 거기에 변경할 부분, 데이터베이스 설정만 sqlite로 변경합니다. 이러면 이제 settings가 두 개가 되는데, 로컬에서 runserver를 실행할 때 --settings=Jinstagram.settings_local 옵션을 통해서 새로 만든 settings를 보고 실행되게 할 수 있습니다. migrate도 마찬가지로 뒤에 --settings를 붙이면 aws가 아니라 sqlite3에 마이그레이트를 하게 됩니다. 
 

옵션 추가

 
더 본격적으로 settings를 분리하는 방법은 다음 링크를 참조하세요~
4-08 settings.py 분리 - 점프 투 장고 (wikidocs.net)
 
 
이제 테스트 코드를 만들 텐데, 테스트 코드의 위치는 프로젝트마다 다양합니다. 테스트 코드만 따로 모아서 관리하는 프로젝트도 있고, 장고에 각 APP별로 테스트 코드를 작성하는 프로젝트도 있습니다. 저희는 CI를 위해 간단한 테스트만 작성할 것이므로, 각각의 APP폴더 안에 테스트 코드를 만들어보도록 하겠습니다. 
 

이미 tests.py 파일이 있다.

 
python manage.py createapp으로 만든 폴더들은 자동으로 tests.py까지 생성해 주네요. 제가 따로 만들지 않아도 user폴더와 content폴더에는 이미 tests.py파일이 있습니다. 저희는 user폴더에 있는 tests.py에 테스트를 추가해 보겠습니다.
 

# user/tests.py
from django.test import TestCase


# Create your tests here.
class TestUser(TestCase):
    def test_1(self):
        self.assertEqual(1, 1)

 
테스트코드는 문법이 조금 특이합니다. 왜냐하면 값이 있다고 가정하거나, 비교해서 맞는지 검증하는 코드이기 때문에 일반적으로 사용하는 python 코드와 이름이 조금 다릅니다. 아마 처음 테스트코드를 작성하신다면 약간의 괴리감(?)이 들 수 있어요. 
 
TestUser라는 테스트 클래스와 그 안에 test_1이라는 테스트를 하나 만들었습니다. test_1의 내용은 간단한데요, assertEqual(1, 1) 한 줄이 있습니다. assertEqual(a, b)는 a와 b가 같은지 검증하고, 맞으면 true, 틀리면 false를 반환합니다. 즉 a=b면 테스트 통과이고, 아니라면 테스트 실패죠. 테스트 실행하는 법은 쉽습니다. 만약 IDE툴에서 코드옆에 화살표가 보이신다면 화살표를 눌러도 테스트코드가 실행됩니다.
 

화살표로 테스트 코드 실행

클래스 옆에 화살표를 선택하면 클래스 전체의 테스트를 실행하고, 함수 옆에 화살표를 선택하면 함수단위로 실행됩니다. 커맨드로 전체 테스트를 실행하고 싶다면 python manage.py test (--settings=Jinstagram.settings_local)를 입력하면 됩니다.
 

테스트 성공

테스트가 1건이 있는데 OK가 된 거 보니 성공했군요. assertEqual(1, 1)이 정상적으로 1과 1을 같다고 판단했나 봅니다. ㅋㅋ 
 
그럼 실패되는 경우는 어떻게 되는지 값을 바꿔보겠습니다.
 

from django.test import TestCase


# Create your tests here.
class TestUser(TestCase):
    def test_1(self):
        self.assertEqual(1, 1)

    def test_2(self):
        self.assertEqual(1, 2)

 
테스트 2번을 추가했습니다. 1과 2가 같아야 한다는 테스트코드를 심어놨습니다. 한번 돌려볼까요?
 

테스트 결과

아까는 OK가 나왔지만, 지금은 FAILED입니다. failures=1은 실패한 테스트의 수를 나타냅니다. 
친절하게도 어떤 테스트가 실패했는지 정보도 나오는데, test_2에서 self.assertEqual(1, 2)가 틀렸다고 나오네요. ㅎㅎ
 
자 이렇게 간단한 테스트를 해보았는데, User에 대한 테스트이니까 회원가입을 호출할 경우 정상적으로 회원가입이 되는지 확인을 해볼까요?
 

# /user/tests.py
from django.test import TestCase
from user.models import User


# Create your tests here.
class TestUser(TestCase):
    def test_1(self):
        self.assertEqual(1, 1)

    def test_post_join(self):
        response = self.client.post('/user/join',
                                    dict(
                                        email="test_email@google.com",
                                        nickname="test_nickname",
                                        name="test_name",
                                        password="test_password")
                                    )
        self.assertEqual(response.status_code, 200)

        test_user = User.objects.filter(email="test_email@google.com").first()
        self.assertEqual(test_user.nickname, "test_nickname")
        self.assertEqual(test_user.name, "test_name")
        self.assertTrue(test_user.check_password("test_password"))

 
test_post_join 테스트를 추가했습니다. 코드가 조금 길죠? 하나씩 봐보도록 하겠습니다.
 

  • self.client.post('/user/join', dict(...)): ~/user/join API를 호출합니다. 저희가 회원가입하기 위해 만든 API입니다. param으로 email, nickname, name, password를 받아서 회원가입을 시켜줍니다. 이런 param은 dict로 넘겨주면 됩니다.
  • self.assertEqual(response.status_code, 200): 회원가입 API호출 결과가 200(성공)인지 체크합니다.
  • test_user = User.objects.filter(email='test_email@google.com').first(): 회원가입 api를 한번 호출했기 때문에 User테이블에 회원이 만들어졌을 겁니다. 만들어진 테스트 유저를 불러옵니다.
  • self.assertEqual(xxxx, xxxx): DB에 데이터와 API호출했을 때 데이터가 같은지 비교합니다. 호출한 데이터로 잘 만들어졌는지 체크하는 로직입니다.
  • self.assertTrue(xxxx): assertEqual이 같아야 한다는 테스트 코드라면, assertTrue는 True가 나올 것이라고 예상하는 테스트 코드입니다.

 
이제 어떻게 테스트하는지 알 것 같죠? 그럼 회원가입 후 로그인까지 잘 되는지 테스트해 보겠습니다.
 

    def test_post_login(self):
        response = self.client.post('/user/join',
                                    dict(
                                        email="test_email@google.com",
                                        nickname="test_nickname",
                                        name="test_name",
                                        password="test_password")
                                    )
        self.assertEqual(response.status_code, 200)
        
        response = self.client.post('/user/login',
                                    dict(
                                        email="test_email@google.com",
                                        password="test_password")
                                    )
        self.assertEqual(response.status_code, 200)

 
로그인은 test_post_login이라는 테스트로 만들었습니다. 여기서 한 가지 의문을 품으시는 분이 있을 수 있는데요, 아까 위에서 회원가입 테스트를 할 때 test_email@google.com이라는 계정을 만들었는데, 아래서도 같은 계정을 만들고 있습니다. 저희는 email을 UNIQUE로 해놓았기 때문에 같은 값이 들어가서는 안 되는데요, 어떻게 된 것일까요?
 
바로 테스트코드는 함수마다 데이터를 생성한 후 해당 테스트가 끝나면 지우게 됩니다. 즉 회원가입 테스트를 할 때 생성했던 User는 해당 테스트가 끝나면 삭제됩니다. 따라서 로그인 테스트할 때 다시 만들 수 있게 되는 거죠. 즉 데이터를 생성하고 해당 데이터를 가공하는 테스트가 필요하다면 하나의 함수 안에서 진행해야 합니다. 그래서 로그인을 테스트하기 위해 해당 함수에서 회원가입까지 진행한 것이죠.
 
이렇게 하나의 함수 안에서 하지 않고 미리 데이터를 정의하는 방법도 있습니다. 저희가 TestUser라는 클래스를 만들 때 TestCase를 상속받았는데, 그 안에 테스트에 필요한 다양한 함수들이 있습니다. 그중에 SetUpTestData라는 함수가 있는데, 요거는 모든 테스트를 시작하기 전에 필요한 데이터를 넣어놓을 수 있습니다.
 

class TestUser(TestCase):

    @classmethod
    def setUpTestData(cls):
        User.objects.create(email="login_test@gmail.com",
                            nickname="login_test",
                            name="login_test",
                            password=make_password("login_test"),
                            profile_image="default_profile.png"
                            )
                            
    def test_post_login(self):
        response = self.client.post('/user/login',
                                    dict(
                                        email="login_test@gmail.com",
                                        password="login_test")
                                    )
        self.assertEqual(response.status_code, 200)

 
이런 식으로 로그인 테스트를 좀 더 간단하게 만들 수 있습니다.
 

 
테스트를 짜다 보니 재미가 붙어서 더 짜고 싶지만, 저희 목적은 테스트를 짜는 게 아니라 CI/CD이기 때문에 그만 만들고 다음 단계로 넘어가도록 하겠습니다. ㅋㅋㅋ 
 
 
 

github action을 이용한 테스트 자동화

 
이제 우리가 만든 테스트를 자동으로 돌리는 Job을 만들 건데요, 예전에는 젠킨스를 많이 사용했는데, 요즘엔 github action을 많이 이용하는 것 같습니다. 젠킨스 자체가 서버에 설치하기도 해야 하고 메모리도 은근 많이 먹어서,, 공자 요금제 EC2에서는 설치하면 다른 거 아무것도 못할 정도의 리소스를 잡아먹습니다 ㅋㅋ 
 
아무튼 github action이란 놈을 사용할 건데요, github action은 github에서 제공하는 workflow 자동화 툴입니다. workflow라는 말이 아주 고급스러워 보이는데, 배치를 돌리거나 CI/CD를 할 수 있는데, 젠킨스와 비슷한 툴이라고 보면 됩니다. github action의 좋은 점은 github에서 일어난 이벤트들과 연동하여 트리거할 수 있다는 점이죠. 따라서 우리는 main에서 push 혹은 pull request가 일어날 때 알아서 테스트가 돌아가는 github action을 만들어보도록 하겠습니다.
 
말로는 아주 장황한데, 실제로는 너무 간단합니다. (요즘 너무 좋아졌네요 ㅎㅎ)
github action을 사용하기 위해선 당연히 github에 내 소스를 올린 repo가 있어야 하는데요, 강의를 따라 하셨으면 아마 여러분들의 github repo를 만드셨을 겁니다. 

action 메뉴가 있다.

저희는 보통 상단의 code 메뉴를 많이 보고 있는데, actions라는 메뉴가 있습니다. 해당 메뉴로 들어가면 위와 같이 workflow를 선택하라는 말이 나옵니다. github이 알아서 제 repo가 django라는 것을 알고, django workflow를 추천해 주네요. 만약 뜨지 않는다면 검색을 통해서 django를 선택합니다. build and test a django project라고 쓰여있습니다.
 

yml로 정의된 workflow

configure를 누르면 위와 같이 yml파일을 생성하는 화면이 나옵니다. 내용이 어느 정도 채워져 있는데 하나씩 볼까요?
 

  • name: workflow의 이름입니다. 나중에 actions 사이드바에 여기에 정한 이름으로 actions들이 정렬됩니다. 디폴트로 django ci로 되어있네요.
  • on: 해당 workflow가 실행되는 시점을 정합니다. 여기에서는 main이라는 브랜치에 push, pull_request가 될 때 실행되는 코드가 적혀있습니다.
  • jobs: 실제적으로 작업들을 명시하는 곳입니다. 순서대로 실행됩니다.
  • runs-on: 해당 workflow가 실행되는 컨테이너의 운영체제를 선택합니다. ubuntu-latest로 되어있는데 우분투 최신버전으로 이 job을 돌리겠다는 의미입니다. 꼭 리눅스가 아니라 윈도우나 맥도 가능합니다. 단 윈도우나 맥은 리눅스보다 무료 사용 시간을 더 많이 잡아먹습니다. 
  • python-version: 테스트할 파이썬 버전을 뜻합니다. 여기는 3가지 버전이 디폴트로 적혀있는데, 테스트를 각각의 버전에서 실행해서 총 3번 돌립니다. 저희는 3개 중에 하나만 있어도 될 것 같네요.
  • steps: 여기에 이제 할 일들을 순서대로 나열합니다. 앞에 - 이걸로 구분된 게 스텝들인데 총 4개의 스텝이 있는 거죠. 앞에 3개는 python 세팅이고, 맨 아래 run test라고 된 곳이 실제로 테스트를 실행하는 step입니다.

디폴트로 만들어준 설정을 하나도 건드리지 않아도 테스트가 가능하지만, 저희는 하나 바꿀 게 있습니다. 테스트를 실행할 때 --settings=Jinstagram.settings_local을 입력해 줘야 한다는 것이죠. 이름도 django-ci로 변경해 줍니다.
 

다 하고 나면 오른쪽 위에 commit을 누릅니다. action을 생성하는 것은 사실 저희 소스에 .github폴더를 만들고 그 안에 workflow폴더를 만들어서 그 안에 xxx.yml파일을 만드는 것과 같습니다. 이걸 github 웹사이트에서 하는 것이죠.
 

커밋메시지

그래서 commit을 누르면 커밋 메시지를 적으라고 합니다. github action 생성이라고 적어줍니다. 그러면 commit과 함께 push가 되기 때문에 바로 action이 실행되는데요, 다시 action으로 가보면 아까와 다르게 왼쪽에 리스트가 보입니다.
 

왼쪽에 action 리스트가 나오고 가운데는 실행한 이력이 나온다

 
저는 미리 하나를 만들어봐서 2개가 표시되는데, 여러분은 하나가 표시될 거예요. 가운데 job이 실행된 이력이 있는데 클릭해 보면 상세한 이력을 확인할 수 있습니다.
 

테스트 완료

 
자 엄청 쉽죠? 이제 저희가 코드를 수정하고 main에 push 하게 되면 자동으로 테스트가 돌아갑니다. 즉 우리가 일일이 테스트를 돌리지 않아도 github에서 테스트를 해준다는 거죠. 테스트뿐만 아니라 pip install requirement도 해주기 때문에 패키지를 설치해서 테스트를 돌리는 과정까지 해줍니다. package를 올바르게 설치해서 장고 앱을 실행할 수 있는지까지 테스트를 해준다는 것이죠. 저희가 사용하는 게 장고라는 프레임워크라서 그렇지 만약 스프링부트를 사용한다고 하면 build까지 해줍니다. (대단!!!😎)
 
자 이렇게 해서 CI가 끝났습니다. 생각보다 간단하죠? 만약 저희가 docker를 사용하고 있었다면 CI *.yml파일에 docker이미지를 만드는 코드를 추가하고 만든 이미지를 도커 허브와 같은 컨테이너 저장 서비스에 올리는 과정까지 추가할 수 있습니다. 이 부분은 조금 심화과정이기 때문에 여기서는 다루지 않겠습니다.
 
 

github action을 이용한 배포

 
CI를 다 만들었으니 이제 CD를 만들어봅시다. CD는 만드는 방법이 아~~~ 주 다양한데, 여기서는 github action에서 EC2에 접속해서 git pull을 받고 django를 재시작하는 스크립트를 실행하는 걸로 만들어보겠습니다. 즉 저희가 수동으로 서버에 접속해서 실행하는 명령어를 github액션에서 실행해 주도록 설정하는 것이죠. 
 
이번엔 웹페이지에서 만드는 게 아니라 로컬에서 workflow를 직접 만들어보겠습니다. main을 pull받으면 .github/workflow폴더가 보일 텐데 여기에 새로운 *.yml를 추가합니다. 배포니까 django-deploy로 정해보도록 하죠.
 

django-deploy

내용은 아래와 같이 채웁니다.
 

name: Django Deploy

on:
  workflow_run:
    workflows: ["Django CI"]
    types:
      - completed

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - name: excuting remote ssh commands
      uses: appleboy/ssh-action@v0.1.10
      with:
        host: ${{ secrets.EC2_HOST }}
        username: ${{ secrets.EC2_USER }}
        key: ${{ secrets.PRIVATE_KEY }} 
        port: 22
        script: |
          cd /home/ubuntu/django_zero_to_instagram_devops_youtube
          git pull
          sudo systemctl restart uwsgi

 
전체적인 구성은 이전과 같으나 다른 점이 몇 개 있습니다.
 

  • on: 이전에는 push와 pull_request가 일어날 경우 실행되게 했지만 이번엔 worklofws: ["Django CI"]라고 되어있습니다. 배포는 항상 CI이후에 일어나기 때문에 Django CI라는 액션이 성공하면 이 workflow가 실행되도록 설정한 것입니다. 이렇게 action 실행 조건을 이전 action의 성공으로 설정할 수 있습니다.
  • steps: 여기는 크게 바뀌었는데, ssh를 이용해 서버에 접속해서 script:에 적힌 명령어를 치도록 실행됩니다. uses: appleboy/ssh-action@v0.1.10이라는게 있는데, appleboy라는 사람이 만든 ssh-action이라는 소스를 이용해 실행한다는 의미입니다. github action도 유저들이 미리 만들어놓은 소스들을 사용할 수 있습니다. 궁금하신 분은 appleboy/ssh-action repo에 들어가면 확인할 수 있습니다. 

자 여기서 커밋을 하기 전에 host, username, key를 보면 secrets.XXX로 정의된 것을 볼 수 있습니다. 이전에 mysql을 연동할 때 환경변수를 설정한 게 기억나시나요? 소스에 중요정보들이 노출되는 것을 막기 위해 시스템 환경변수를 이용했었는데, action도 마찬가지로 중요 정보가 노출될 수 있습니다. 따라서 해당 정보들을 github secret에 저장해서 사용할 수 있습니다.
 
github secret은 github repo에 들어가서 settings메뉴에서 설정할 수 있습니다.
 

액션에서 사용할 secret 설정

 
new repository secret버튼을 사용해서 새로운 secret 값을 생성할 수 있습니다. EC2_HOST에는 여러분들의 EC2 IP주소를 적고, EC2_USER에는 ubuntu라고 적어줍니다. 만약 여러분들이 ubuntu ec2를 사용하지 않았다면 여러분들이 접속할 때 사용하는 user를 적으면 됩니다. 마지막으로 PRIVATE_KEY인데, 만약 여러분이 pem파일로 key pair를 생성했다면 pem파일을 열어서 거기에 내용을 복사해서 넣으시면 됩니다. 만약 ppk로 키를 받으셨으면 putty를 이용해 ppk -> pem으로 변환하여 키를 뽑을 수 있습니다.
 
여러분들이 putty를 설치하셨다면 puttygen이라는 프로그램도 아마 같이 설치되었을 텐데요,

puttygen

 
이 puttygen을 이용해 ppk -> pem으로 변환 가능합니다.
먼저 puttygen을 실행하고, load를 선택하여 ppk을 읽어옵니다.

load
ppk를 읽어온다

 
그리고 conversions-> export openssh key (force new file format)을 눌러서 xxx.pem으로 저장하면 끝~!
 

export
pem으로 저장

이렇게 저장한 다음 문서를 읽을 수 있는 프로그램으로 pem을 읽으면 다음과 같이 private 키가 나옵니다.
 

private key

이렇게 공개하면 안 되지만 누가 이거 가져다 써도 상관없으니까요 ^^ 여러분들은 공개하지 마세요~ ㅋㅋ 
 
이 문서의 전체를 복사해서 PRIVATE_KEY secret에 붙여 넣기 해줍니다. 그럼 준비 끝!!
 
그럼 우리가 만든 django-deploy.yml을 추가하고 commit후 push 해봅시다~!
 

 
정상적으로 동작한다면 Django CI가 실행되고 나서 바로 Django Deploy가 실행되는 것을 확인할 수 있습니다.

 
 
 

마무리하며

간단하지만 github action을 이용한 CI/CD를 구현했습니다. CI/CD를 구현하는 방법은 아주 많습니다. 저희는 그중에 github action을 이용했고 배포 방법으로는 ec2에 직접 접속하여 소스를 다운로드하고 재시작하는 로직을 사용했습니다. 저희처럼 아주 간단한 사이드프로젝트에서는 이 방법이 좋지만 실제 회사들이 사용하고 있는 환경은 더 복잡하기 때문에 단순히 서버에 접속해서 git pull, restart형식으로 구현하지는 않습니다. AWS를 사용한다면 AWS Code Deploy라는 서비스를 많이 사용하게 됩니다. 직접 ec2에 들어가는 것이 아닌 AWS에서 제공하는 배포 서비스를 이용해 ec2에 배포하는 것이죠. 아니면 쿠버네티스(k8s)를 사용하는 곳이라면 kube명령어를 이용해 배포하는 곳도 있습니다. 이럴 경우 container이미지를 만들기 위한 CI와 이미지 저장서비스까지 이용하게 되죠. (ex. horbor.io) 
 
이렇게 다양한 CI/CD방식이 있지만 동작 원리는 같습니다. 따라서 전부를 아실 필요도 없고 어떻게 돌아가는지만 알면 구현하는 찾아서 하면 됩니다. 사실 요즘은 devops분들이 해주시기 때문에 저희가 할 건 없지만요. ㅎㅎ 하지만 알고 모르고 가 상당히 중요합니다. 😎
 
처음에는 docker로 만들어서 이미지로 배포하는 것까지 하려고 했었는데, 그렇게 하면 CI/CD에 초점을 맞출 수 없을 것 같아서 제외했습니다. ㅎㅎ 나~~ 중에 여유가 생기면 추가해 보도록 하겠습니다. 
 
다음시간은 SSL 기능을 넣어서 저희 인스타 서비스가 https로 접속 가능하도록 추가해 보겠습니다.
읽어주셔서 감사합니다 😀😉
 
 

반응형

댓글

Designed by JB FACTORY