Docker를 통한 Hangman 서비스 CI/CD 구축 (TIL 37)

석형원·2024년 6월 3일

TIL

목록 보기
37/52

✏️ 오늘 학습한 내용

1. Hangman 서비스
2. CI/CD와 Github Actions

3. Github Actions 사용

- Python Application
- Docker Image

🔎 Hangman 서비스

목표

Hangman이라는 웹 서비스를 Docker로 구동하는 것입니다.

개요

  1. Push를 통해 GitHub의 코드의 변경이 발생하면

  2. 그 Action이 Trigger가 되어서

  3. Docker image를 빌드하고 Docker hub에 Push합니다.

  4. Continuous delivery가 되는 환경이라면,
    Production 서버에 명령을 보내
    Docker hub의 최신 이미지를 내려 받아
    컨테이너를 돌릴 수 있도록 합니다.

  5. 위 과정들을 자동화시킵니다.

Hangman 프로그램 소개

  • https://github.com/learndataeng/hangman_web

  • 동작하지 않는 미완성 Hangman 게임 프로그램

    • Python의 flask를 사용하여 구현
    • flask 관련 모듈의 설치가 필요 (requirements.txt)
  • 실행 방법

    • python3 -m flask run --host=0.0.0.0 --port=4000

Repo 구성

  • app.py

    flask의 메인 함수가 들어있는 코드,
    Commandline으로 받은 포트번호에 bind하고 요청을 기다림

  • test.py

    app.py에 있는 코드의 유닛 테스트 로직을 담당,
    CI/CD 구성시 자동으로 실행이 되게 구성

Docker 컨테이너 내부 프로세스와 Host 프로세스 간 통신

Container는 완전히 별개의 공간이기 때문에 Port번호를 연다고 해서 Container 밖에서 보이지 않습니다.

-> 포트 포워딩이 필요

  • 포트 맵핑(포트 포워딩)

    • -p 옵션을 사용
      docker run -p host_port:container_port image_name
  • -p 옵션이란?
    호스트(host) 컴퓨터에서 컨테이너에서 리스닝하고 있는 포트로 접속할 수 있도록 설정하는 옵션
    docker run -p 4000:4000 skqltldnjf77/hangman

📃 Docker image 생성 및 배포

Dockerfile 생성

FROM python:3.8-slim-buster
# docker inspect로 찾을 수 있는 사용자정보 메타데이터
LABEL Maintainer="skqltldnjf77@gmail.com"
COPY . /app
WORKDIR /app
# build시 실행
RUN pip3 install -r requirements.txt
# 포트번호를 4000쓴다고 알림
EXPOSE 4000
# container 실행시 실행
CMD ["python3", "-m", "flask", "run", "--host=0.0.0.0", "--port=4000"]

build

# 이미지 이름을 hangman이라 지정,
# 현재 경로의 dockerfile 기반 이미지 생성
docker build -t hangman .

# docker hub에 업로드를 위한 이미지 이름 변경
docker tag hangman:latest skqltldnjf77/hangman:latest

# 동작 확인
docker run -p 4000:4000 skqltldnjf77/hangman
# detach 명령어를 통해 백그라운드에서 돌아가게도 가능
docker run -p 4000:4000 -d skqltldnjf77/hangman

docker hub에 등록

# docker hub에 업로드
docker push skqltldnjf77/hangman

외부에서 이미지 실행

https://labs.play-with-docker.com/ 에서 이미지를 실행해보겠습니다.


🔎 CI/CD와 Github Actions

📃 CI/CD

소프트웨어 빌드란?

  • 개발한 소프트웨어를 최종적으로 출시하기 위한 형태로 만드는 것
    ( 배포하기 이전에 패키지로 만드는 것)

    • 테스트가 빌드의 중요한 일부!
    • 패키지 포맷 역할 -> docker image
  • 개발이 끝나기 전부터 빌드를 진행하면 소프트웨어의 안정성이 증대됨

    • CI(Continuous Integration)!
    • CI가 의미가 있으려면 테스트 코드가 충분히 의미가 있어야합니다!
      ( 지속적으로 빌드를 반복할 때마다 테스트를 통해 올바르게 작동하는 지를 확인해야 하므로 )

Continuous Integration이란?

  • Software Engineering Practice의 하나

  • 기본 원칙

    • 코드 Repo는 하나만 유지 (Master)
      ( 코드를 수정하고 싶을 때, Master를 건드리지 말고
      Branch를 만들어서 작업하고 Merge하도록 )

    • 코드 변경은 자주, 조금씩하는 것이 좋음

    • 테스트를 최대한 추가 (핵심)

      • Test Coverage
    • 빌드를 계속적으로 수행 (자동화)

      • Commit Build vs. Nightly Build
    • 성공한 빌드의 프로덕션 릴리스 (자동화)

      • CD : Continuous Delivery

빌드를 실패하는 경우

  • 새 코드의 커밋으로 인해 테스트가 실패하는 경우

  • 대부분의 경우 빌드 실패시 빌드가 다시 성공할 때까지 코드 변경을 금지

    • 빌드 실패는 모든 사람을 잡아두는 족쇄

    • 빌드 실패시 가벼운 형태의 페널티 부여하는 방식도 좋음

📃 Github Actions

Github Actions - Workflow

Github에 Push, Merge 시점이 CI/CD를 실행하기 위한 절호의 순간입니다.

  • 코드가 Main/Master 브랜치에 추가되는 순간 CI/CD를 트리거

    • 이때, 테스트를 수행하고 최종적으로 Docker Image 등을 만들도록 하는 것이 가능

    • 그래서 CI/CD는 Github에 구현하는 것이 가장 자연스러움

  • Github에서 이를 가능하게 해주는 것이 Actions라는 기능

    • Actions :
      특정 이벤트(Merge, Commit)를 대상으로 조건을 만족하는 순간,
      Workflow를 실행할 수 있고 다수의 Workflow를 세팅 가능

    • Workflow : CI/CD를 트리거하는 역할

Github Actions이란?

  • CI/CD를 Github 위에서 구현하기 위한 서비스
    • 코드 테스트, 빌드, 배포 자동화 기능 제공
  • Workflow라 부르며 아래 컴포넌트로 구성
    • Events
    • Jobs
    • Actions
    • Runner
      • Github hosted runners ( 깃허브가 제공하는 서버 이용 )
      • Self hosted runners ( 직접 서버를 운영 )

Workflow

  • Workflow는 트리거 이벤트가 발생하면 시작되는 일련의 동작들을 지칭

  • 트리거 이벤트의 예시

    • Code commit
    • PR 생성
    • 다른 Workflow의 성공적인 실행
  • Workflow를 위한 명령어들은 YAML 파일로 저장

    • 명령어들로는 환경설정scripts 실행들이 대표적
  • Workflow는 하나 이상의 Job으로 이루어집니다.

    • 각 Job은 일련의 스텝을 수행
      • 각 스텝은 하나 혹은 그 이상의 명령어를 실행
        • 이 명령어들의 집합을 Actions라 부름
      • 각 스텝은 윈도우나 리눅스 서버 위에서 Runner에 의해 실행
        • 이를 Docker Image에서 수행하는 것이 일반적

Workflow ∋ Job ∋ Step(명령어 실행)

Github Actions 선택하는 법

  • Actions 메뉴 선택

  • workflow 생성

  • yml 파일을 직접 생성 혹은 템플릿(CI Templates) 선택 후 수정

    • Docker Image or Python Application

🔎 Github Actions 사용

Github Actions를 사용해 Main 브랜치에 push, PR이 발생하는 경우
test.py를 실행하여 테스트가 동작할 수 있도록 해보겠습니다.

📕 CI Template : Python Application - 테스트 기능 추가

Python Application이라는 CI Template을 사용해보겠습니다.

( Python Application 상세 )

  • 기본으로 pytest가 테스트 프레임웍으로 설치됨

    • 이 pytest를 unittest 변경해 사용할 것입니다.
  • Python code linting tool으로 flake8 설치

    • 테스트 코드 외에도 flake8를 사용해 문법 에러, 코딩 스타일을 체크할 것입니다.

flake8

  • 파이썬 코드에서 에러나 코딩 스타일 등에서 이슈를 체크해주는 툴
    • 이런 툴을 Linting tool이라 부름

e.g.)
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics

📃 테스트 추가

일반적으로 pytest를 많이 사용하는 추세이지만,
이번 경우는 unittest로 진행합니다.

테스트 코드 실행 명령어

# 메인 메소드가 정해지지 않은 경우에도 사용
python3 -m unittest test.py

# 메인 메소드가 정해진 경우 사용 가능
python3 test.py

# 메인 메소드가 정해져있고 메인 메소드가 실행이될 때
# 테스트 코드가 돌아가므로 둘 다 사용해도 무방합니다.

Github Actions 사용

Github Repo에서 Actions를 선택하고
CI Template으로 Python Application 선택합니다.

이제 이 python-app.yml을 편집할 것입니다.

name: Python application

# "on" : 트리거 이벤트 지정
# main브랜치에 push, PR가 생긴 경우 발생
on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: read

# jobs : 실제 CI 프로세스가 스텝(name) 별로 기술되는 곳
jobs:
  build:

    runs-on: ubuntu-latest
	
    # 명령어들이 모여있는 Steps 
    # 각 명령이 name별로 구분됨
    steps:
    - uses: actions/checkout@v4
    
    # Python 3.10 설치
    - name: Set up Python 3.10
      uses: actions/setup-python@v3
      with:
        python-version: "3.10"
        
    # 변경 사항 : 기존에 pytest가 있던 부분을 삭제
    # flake8을 설치,
    # requirements.txt가 있으면 설치
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install flake8 #pytest
        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
        
    # flake 8를 두번 실행해서 문법, 코딩 스타일 체크
    - name: Lint with flake8
      run: |
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
        
    # 변경 사항 : 기존 pytest를 unittest로 변경
    # run에 test.py가 동작할 수 있도록 새로운 명령을 부여
    # test로 시작하는 모든 python 파일을 실행
    - name: Test with unittest
      run: |
        python -m unittest discover -p 'test*.py' 

결과 확인

이제 Github repo의 main branch에 push, PR이 발생하면 매번 테스트가 동작하게 될 것입니다.

이를 확인하기 위해 Readme.md를 수정해보겠습니다.

그 결과, PR 후 테스트가 자동으로 동작한 것을 확인할 수 있습니다.

📕 CI Template : Docker Image - Docker hub에 업로드

Github에서는 Docker에 대한 정보를 가지고 있지 않으므로 Docker hub ID, PW와 같은 정보를 전달해주어야합니다.

Docker 관련 스텝들

  • docker login

    • 하드 코딩하지 않고 Github내에 저장
      • secrets.DOCKER_USER
      • secrets.DOCKER_PASSWORD
  • docker build

  • docker push

=> 이 과정을 docker-image.yml에 기술

Github - Docker Hub 정보 보관


Github에 Settings를 선택해서
Security -> Secrets and variables -> Actions으로 접근 후,
New respository secret을 선택합니다.

여기서 Docker hub ID, PW를 저장해주면
YML 파일 안에서 ${{secrets.DOCKER_USER}}의 형태로 접근할 수 있게 됩니다.

ID, PW를 각각 작업해주어야합니다.

  • DOCKER_USER
  • DOCKER_PASSWORD

Github Actions - Docker Image

Github Repo의 Actions에서 New workflow를 생성해줍니다.

CI Template으로 Docker Image 선택합니다.

이제 이 docker-image.yml을 편집할 것입니다.

  • docker-image.yml 편집
name: Docker Image CI

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

jobs:

  build:

    runs-on: ubuntu-latest
	
    # 변경 사항 : 
    # name: docker login 추가
    # name: Build the Docker image 수정
    # name: docker push 추가
    
    steps:
    - uses: actions/checkout@v4
    
    - name: docker login
      env:
        DOCKER_USER: ${{secrets.DOCKER_USER}}
        DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
      run: |
        docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
    
    - name: Build the Docker image
      run: docker build --tag ${{secrets.DOCKER_USER}}/hangman:latest .
    
    - name : docker push
      run : docker push ${{secrets.DOCKER_USER}}/hangman:latest

결과 확인

Docker Image 빌드 후 Docker hub로 Push까지 되는 것을 확인할 수 있습니다.


Github 링크

https://github.com/lv1turtle/hangman_web

profile
데이터 엔지니어를 꿈꾸는 거북이, 한걸음 한걸음

0개의 댓글