프로그래밍/DevOps

[DevOps] github actions을 사용해서 CI/CD를 해보자! (2)

riroan 2023. 2. 12. 16:52

main.yml

우선 지난시간에 작성한 yml파일부터 살펴보자.

name: auto pull

github actions에서 보일 이름을 정하는 부분이다.

원하는대로 지으면 된다.

on: [push]

언제 이 워크플로우를 작동할지 정하는 코드이다.

어느 브랜치든 어떤 폴더든 푸시만하면 작동하게 설정했다.

옵션으로 push, pull request, issue를 정할 수 있고 브랜치별, 폴더별로도 정할 수 있다.

 

jobs:
  build:
    name: Build

    runs-on: ubuntu-latest

이제 push가 되면 무슨 행동을 할 지 정해야한다.

build: 라고 쓰여진 부분도 별칭이니 바꿔도 된다.

일단 이름을 Build라고 정했다. (실제 빌드하는 작업은 아니다)

그리고 그 작업을 수행할 서버를 github측에서 잠시 빌리는데 ubuntu를 빌린다는 뜻이다. (mac-os, window도 있지만 ubuntu가 가장 싸다.)

 

    steps:
    - name: ssh test
      uses: appleboy/ssh-action@v0.1.7
      with:
        host: ${{secrets.HOST}}
        username: ${{secrets.USERNAME}}
        key: ${{secrets.KEY}}
        port: ${{secrets.PORT}}
        script: |
          cd CICD
          git pull

이제 steps를 시작으로 할 작업을 적는다.

name은 역시 맘대로 정하는 이름이다.

uses는 다른 사람이 만든 action을 import하여 사용하는 개념이다.

with부터는 그 다른 사람이 만든 문법에 따라 달라진다.

host, username, key, port를 action secret에서 가져온다.

script에 ssh접속 후에 할 명령어들을 작성한다.

 

즉 우리가 작성한 main.yml은 ssh로 원격 서버에 접속하여 해당 폴더로 들어가서 git pull명령을 수행하게 하는 코드를 작성한 것이다.

 

파이썬 파일 수정

그럼 이제 파이썬 파일을 수정해보자.

# main.py (local)
from fastapi import FastAPI
import uvicorn
app = FastAPI()

@app.get("/")
def func():
    return {"message":"hello world"}

if __name__ == "__main__":
    uvicorn.run(app, port = 9000, host="0.0.0.0")

hello -> hello world로 수정하고 푸시하자.

 

반영이 됐다.

하지만 저 주소로 접속하면?

코드는 pull해서 변경이 됐지만 다시 배포하지 않았기 때문에 결과는 이전과 같다.

fastapi는 reload옵션을 제공하지만 배포환경에선 사용하지 않고 다른 프레임워크는 없는 경우도 있기 때문에 수동으로 재시작해야한다.

 

1. ps -ef 명령으로 해당 fastapi 프로세스를 찾는다.

2. kill 명령으로 해당 프로세스를 종료한다.

3. 다시 fastapi를 실행한다.

 

위 작업을 자동화하기는 쉽지 않다.

그래서 이 문제를 해결하기 위해 도커를 사용한다.

 

도커 사용

도커 설명

우선 도커 파일을 작성하자

FROM python:latest

COPY . .

RUN pip install -r requirements.txt

ENTRYPOINT ["python", "main.py"]

EXPOSE 9000

이제 푸시하면 자동으로 원격지에도 도커파일을 불러올 것이다.

그럼 그 파일을 빌드하고 실행하는 코드를 yml파일에 추가해주면 된다!

 

# .github/workflows/main.yml (local)

name: auto pull
on: [push]
jobs:
  build:
    name: Build

    runs-on: ubuntu-latest

    steps:
    - name: ssh test
      uses: appleboy/ssh-action@v0.1.7
      with:
        host: ${{secrets.HOST}}
        username: ${{secrets.USERNAME}}
        key: ${{secrets.KEY}}
        port: ${{secrets.PORT}}
        script: |
          cd CICD
          git pull
          docker build -t myimage .
          docker run --rm -d -it --name mycontainer -p 9000:9000 myimage

 

성공적으로 빌드하고 실행한 모습이다.

결과도 올바르게 나온다.

 

수정 테스트

그럼 이제 정말로 잘 되는지 궁금하니까 다시한번 파이썬 파일을 수정해보자.

from fastapi import FastAPI
import uvicorn
app = FastAPI()

@app.get("/")
def func():
    return {"message":"Hello wonderful world!!"}

if __name__ == "__main__":
    uvicorn.run(app, port = 9000, host="0.0.0.0")

다음과 같이 수정하고 푸시했다.

에러가 났다!

docker: Error response from daemon: Conflict. The container name "/mycontainer" is already in use by container ...

로그를 확인하면 이런 메시지를 볼 수 있다.

우리는 아직 위에서 소개한 1,2,3번 문제를 해결하지 않았다.

도커는 컨테이너 제거가 쉬우니 yml파일을 조금만 수정하면 해결할 수 있다.

 

# .github/workflows/main.yml (local)

name: auto pull
on: [push]
jobs:
  build:
    name: Build

    runs-on: ubuntu-latest

    steps:
    - name: ssh test
      uses: appleboy/ssh-action@v0.1.7
      with:
        host: ${{secrets.HOST}}
        username: ${{secrets.USERNAME}}
        key: ${{secrets.KEY}}
        port: ${{secrets.PORT}}
        script: |
          cd CICD
          git pull
          docker rmi myimage
          docker stop mycontainer
          docker build -t myimage .
          docker run --rm -d -it --name mycontainer -p 9000:9000 myimage

name 옵션을 줘서 컨테이너 삭제를 쉽게 했고 rm 옵션을 추가해서 stop하면 자동으로 삭제하게 했다.

 

이제 결과가 올바르게 나오고 github actions를 사용하여 자동 배포에 성공했다.