취미가 좋다

[3-4] github action으로 CI 파이프라인 구축하기 본문

Sparta Coding Club/Docker

[3-4] github action으로 CI 파이프라인 구축하기

benlee73 2021. 8. 5. 17:03

테스트 코드 작성

링크 글을 통해 테스트 코드의 장점을 참고해라.

 

python 의 pytest를 사용해서 testcode를 작성하기 위해 pytest를 설치한다.

pip install pytest

매번 pytest를 설치하기 불편하니까, requirements.dev.txt를 생성한다.

-r requirements.txt
pytest

 

아래의 코드로 파일 구조를 조금 바꾼다.

├── app.py
├── templates
│   └── index.html
├── test_utils.py
└── utils.py

app.py

더보기
from flask import Flask, render_template, jsonify, request

from utils import get_movie_info

app = Flask(__name__)

from pymongo import MongoClient

client = MongoClient('mongo', 27017)
db = client.dbsparta


## HTML을 주는 부분
@app.route('/')
def home():
    return render_template('index.html')


@app.route('/memo', methods=['GET'])
def listing():
    articles = list(db.articles.find({}, {'_id': False}))
    return jsonify({'all_articles': articles})


## API 역할을 하는 부분
@app.route('/memo', methods=['POST'])
def saving():
    url_receive = request.form['url_give']
    comment_receive = request.form['comment_give']

    title, image, desc = get_movie_info(url_receive)
    doc = {
        'title': title,
        'image': image,
        'desc': desc,
        'url': url_receive,
        'comment': comment_receive
    }

    db.articles.insert_one(doc)

    return jsonify({'msg': '저장이 완료되었습니다!'})


if __name__ == '__main__':
    app.run('0.0.0.0', port=5000, debug=True)

utils.py

더보기
import requests
from bs4 import BeautifulSoup


def get_movie_info(url_receive):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
    data = requests.get(url_receive, headers=headers)

    soup = BeautifulSoup(data.text, 'html.parser')

    title = soup.select_one('meta[property="og:title"]')['content']
    image = soup.select_one('meta[property="og:image"]')['content']
    desc = soup.select_one('meta[property="og:description"]')['content']

    return title, image, desc

test_utils.py

더보기
from utils import get_movie_info


def test_get_movie_info():
    test_url = "https://movie.naver.com/movie/bi/mi/basic.nhn?code=185293"
    title, image, desc = get_movie_info(test_url)

    assert title == "내일의 기억"
    assert image == "https://movie-phinf.pstatic.net/20210406_131/1617688160755B157W_JPEG/movie_image.jpg?type=m665_443_2"
    assert desc == """사고로 기억을 잃은 채 깨어난 수진 옆엔자상한 남편 지훈이 그녀를 세심하게 돌봐주고 있다.그리고 집..."""

pytest 라는 명령어를 터미널에 입력하면, test_utils.py 에서 테스트가 실행된다.

 

작성한 코드 gihub repository 에 업로드하기

개인 github 계정에 로그인하고, 새로운 repository를 만든다.

처음 제공되는 방법으로 위의 코드들을 레포에 올린다.

 


github action이란?

github에서 제공하는 자동화 툴로, CI/CD 기능도 제공한다.

 

CI/CD를 할 수 있는 방법은 travis, circle ci, jenkins 등이 있지만, github action을 사용하는 이유는 아래와 같다.

  • 일정 횟수까지 무료로 사용할 수 있다. 가격 정보
  • 다른 사람이 만들어 놓은 action을 가져다가 사용할 수 있다.

github action 용어

Workflows

자동화 하려는 과정들.

한개 이상의 job으로 구성되고 event에 의해 시작된다.

빌드 / 테스트 / 릴리즈 / 배포 등의 작업이라고 생각하면 된다.

Events

workflow를 실행시키기 위한 것

push / pull request / cronjob 등이 있다.

Jobs

동일한 runner에서 실행하려고 하는 여러 step의 모임

Runner

Job이 실행되는 환경

우분투 / MacOS / 윈도우 를 제공한다.

Steps

job을 구성하는 한 개의 커맨드로, action이나 shell command로 구성된다.

Actions

따로 정의된 커맨드의 모임

 

실제 life cycle

github action 기본 문법

github action은 docker-compose처럼 yaml 파일로 정의한다.

test를 해보기 위해 push를 할 때, hello world를 출력하는 yaml 파일을 만들어보자.

 

아래의 디렉토리를 지켜서 yaml파일을 만든다.

./.github/workflows/learn-github-action.yaml

name: learn-github-actions
on:
  push:
    branches:
      - main
jobs:
  python-hello-world:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: "3.8"
      - run: python -c "print('hello world')"

on : 어떤 events를 가지고 workflow를 실행할 것인가

      push events 가 들어왔을 때 main 브랜치로 푸시하겠다는 의미이다.

 

jobs : python-hello-world 라는 이름의 jobs를 정의한다.

 

runs-on : 어떤 환경에서 github action을 실행할 것인지 정의

 

steps : 여러 step을 정의한다.

uses : 다른 사람이 정의한 action을 사용할 수 있다.

run : 쉘 커맨드를 실행할 수 있다.

 

 

github action 실행

추가한 yaml 파일을 github에 올려서 실행해보자.

git add .github/workflows/learn-github-action.yaml
git commit -m "add learn-github-aciton.yaml"
git push

레포지토리에 Actions에 들어가면, 추가한 yaml 파일이 보인다.

클릭하면 실행한 내용들을 볼 수 있다.

 

더 많은 github action 정보를 확인하고 싶으면 링크를 참고하자.

 


main branch에 push할 때, test를 하게 만들기

위에서 yaml 파일을 추가하여 action을 실행시켜봤으니, test를 위해 아래의 파일을 생성한다.

./.github/workflow/ci-pipeline.yaml

name: ci-pipeline
on:
  push:
    branches:
      - main
jobs:
  run-test-code:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: "3.8"
      - run: pip install -r requirements.dev.txt
      - run: pytest

이전 yaml 파일과 매우 비슷하다.

on : main 브랜치에 push할 때 실행된다.

 

run : "hello world"를 출력하는 대신, pytest를 설치하고 pytest를 실행하도록 한다.

 

 

위에서 hello world를 위한 yaml 파일을 삭제하고, 추가한 yaml 파일을 github 레포에 올린다.

repository > Actions 에 들어가서, pytest가 동작하는 것을 확인할 수 있다.

 

 

결론적으로, 앞으로 코드를 수정하여 main 브랜치에 추가할 때마다, 위에서 추가한 test_utils.py 에서 작성한 코드가 실행되어 테스를 실행한다. 만약에 오류가 발생하면 그 내용을 레포지토리의 Actions에서 확인할 수 있다.

이렇게 통합할 때마다, 코드가 잘 돌아가는지 확인하는 과정을 자동화하였다.

 

main branch에 push할 때, docker image를 빌드하고 push하게 만들기

docker repository에 이미지를 올리기 위해서는, 해당 repository의 Access token이 필요하다.

 

링크에 들어가서 New Access Token을 클릭하여 토큰을 생성하고, 화면에 보이는 토큰을 복사한다.

 

github actions에서 이 토큰을 사용할 수 있도록 설정한다.

Repository > Settings > Secrets > New Repository secrets

 

2개의 secret을 추가한다. 

Name : DOCKERHUB_TOKEN / Value : 복사한 토큰

Name : DOCKERHUB_USERNAME / Value : 도커 허브의 유저 네임

 

ci-pipeline.yaml 을 수정한다.

name: ci-pipeline
on:
  push:
    branches:
      - main
jobs:
  run-test-code:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-python@v2
        with:
          python-version: "3.8"
      - run: pip install -r requirements.dev.txt
      - run: pytest
  build-image:
    needs: [run-test-code]
    runs-on: ubuntu-latest
    steps:
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v1
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v1
      -
        name: Login to DockerHub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      -
        name: Build and push
        id: docker_build
        uses: docker/build-push-action@v2
        with:
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/docker-memo:latest
      -
        name: Image digest
        run: echo ${{ steps.docker_build.outputs.digest }}

build-image 라는 job이 추가되었다.

 

needs : run-test-code job이 끝나고 나서 이 job을 실행시키겠다는 의미

uses : docker에서 제공하는 여러 step 을 사용한다. 도커허브에 로그인하고 이미지를 빌드하고 푸시한다.

with : 이 것을 통해 등록한 secrets를 사용할 수 있게 된다.

 

마지막 Image digest 라는 step은 docker_build 라는 step을 진행하면서 나온 출력 중, digest 를 출력하는 작업을 수행한다. 잘 push 되었는 지에 대한 안전 장치이다.

 

 

위의 파일을 repository 로 push하고 잘 실행이 되는지 확인한다.

 

이를 통해, main branch에 push를 할 때마다, 코드 test를 진행한 후, 도커 이미지를 빌드해서 도커허브에 push하는 것까지 자동화하여 처리하게 되었다.

Comments