홍카나의 공부방

[Docker] Image build - DockerHub - CI/CD 프로세스 실습 본문

Data Engineering/Docker

[Docker] Image build - DockerHub - CI/CD 프로세스 실습

홍문관카페나무 2023. 6. 26. 18:36

이번 글에서는 Docker Image 빌드 및 DockerHub에 push하는 과정과

Github Actions를 이용하여 CI/CD 프로세스를 실습해본다.

 

먼저 CI/CD가 무엇인지 간단하게 이해하고 넘어가면,

 

개발자가 코드를 변경할 때 마다 정기적으로

빌드와 테스트를 자동화하는 과정을 Continous Integration(CI, 지속적인 통합)이라고 하며,

Continuous Delivery(CD)는 CI의 연장선으로,

CI의 과정을 통과한 코드 버전을 마지막에 배포하는 과정을 의미한다.

 

CI의 기본 원칙 중에서

코드 Repo는 하나만 유지(Master or  Main)하고,

코드 변경은 자주, 조금씩 하는 게 좋다는 것을 기억하고 넘어가자.

https://www.hanl.tech/blog/ci-cd-%EA%B8%B0%EB%B3%B8%EA%B0%9C%EB%85%90%EA%B3%BC-%EA%B0%80%EC%9E%A5-%EB%A7%8E%EC%9D%B4-%EC%93%B0%EC%9D%B4%EB%8A%94-%EB%8F%84%EA%B5%AC-5%EA%B0%80%EC%A7%80/

 

CI/CD를 도입하면 개발 측면에서 속도와 효율을 가져올 수 있다!

그러면 Docker와 CI/CD를 함께 실습해보기 위한 과정을 본격적으로 진행해 본다.

 

1. 실행 밑바탕이 되는 프로그램 코드를 repo에 등록하고 clone

 

먼저 DE 교육에서 사용하는 실습용 Repo 및 코드들을 내 Github에 Fork했다.

실습용 프로그램 코드는 hangman 게임을 Python의 Flask 기반 서버에서 제공하는 프로그램이다.

꼭 해당 repo를 fork하지 않아도, 본인이 실습할 repository와 프로그램 코드들을 만들어서 등록해 놓으면 된다!

 

필자의 실습용 Repo 파일 구성은 다음과 같다.

 

app.py - flask의 메인 함수가 있고, 커맨드라인으로 받은 포트에 바인드. 이후 요청이 들어오기를 기다림.

requirements.txt - 가상환경 라이브러리 설치용

test.py - app.py에 있는 코드의 unittest logic이 들어가 있음. CI/CD 구성시 실행이 되게 구성할 예정

README.md

 

이후 repo에 있는 파일들을 git clone하고,

가상환경을 만들고 pip install -r requirements.txt로 필요한 라이브러리들을 모두 로컬에 설치했다.

 

그리고 `python3 -m flask run --host=0.0.0.0 --port=4000`

을 터미널에 입력하여 로컬호스트 서버에 접속하여 구동을 확인하였다.

실습용 코드가 flask 웹 서버가 아니라면, 굳이 체크할 필요 없다.

로컬에서 구동 확인

 

* 여기서 만약 Docker Container로 특정 port 번호에 실행된 Flask app에

호스트 운영체제에서 접근하려고 하면 접근되지 않는다.

컨테이너는 완전히 독립된 별개의 공간이기 때문이다.

 

해결책은 Docker 컨테이너 실행(run)시에 포트포워딩을 해줘야 한다.

이 과정을 거치면 아래와 같은 그림 형태로 환경이 만들어진다.

docker run -p 4000:4000 이미지이름

Docker Container에 포트포워딩하기

 


 

2. Dockerfile 만들고 빌드하기

 

다음에는 Dockerfile을 만들 것이다.

FROM에 사용할 Base Image로는 python:3.8-slim-buster를 사용한다.

(buster태그가 붙은 이미지의 경우, 다양한 데비안 배포판의 코드명이다.)

그리고 RUN에서는 pip install -r requirements.txt가 지정되어야 한다.

 

FROM python:3.8-slim-buster
LABEL Maintainer="hongcana@kakao.com"
WORKDIR /app
COPY app.py ./
COPY requirements.txt ./
RUN pip install -r requirements.txt
EXPOSE 4000
CMD ["python", "-m", "flask", "run", "--host=0.0.0.0", "--port=4000"]

위에서 LABEL은 maintainer 즉, 누가 만들었느냐를 메타데이터로 만들어주는 명령이다.

그리고 이미지를 빌드해보자.

docker build --platform=linux/amd64 -t hongcana/hangman .
또는
docker build -t hongcana/hangman .

맥 M1 chip을 사용한다면 빌드시에 --platform=linux/amd64를 이용해 주는 것이 좋다.

 

docker run -p 4848:4000 hongcana/hangman

나는 웹 브라우저에서 4848번 포트를 타고 들어가서

컨테이너 내부에 4000번 포트로 서버를 구성한 Flask 웹서버에 접속하려고 한다.

포트번호는 꼭 같게 하지 않아도 된다.

 

만약 docker run에 -d 태그를 붙여주면 detach되며 실행되는데

이 모드는 컨테이너가 백그라운드에서 실행되고, 터미널에 컨테이너 로그가 표시되지 않는다.

 


 

3. 이미지를 dockerhub에 push 해보기 (선택)

 

이후에는 docker login --username={아이디} 명령어로 dockerhub에 로그인하고,

docker push {이미지이름} 으로 repo에 push 해보자.

 

어차피 추후에 Github Actions를 이용하여 CD를 진행하므로 스킵해도 되는 과정이다.

완성

 

다음 Github Actions를 이용하는 단계부터 본격적인 CI/CD 과정이다.


 

4. CI를 위한 Github Repository의 Actions 설정하기

 

Github Actions는 깃헙에서 CI/CD를 구현하기 위한 서비스다.

코드 테스트, 빌드, 배포 자동화 기능을 제공한다.

하나의 repo에 대해 다수의 workflow들이 존재할 수 있다.

 

Actions를 이용하여 main branch에 push나 PR이 있는 경우

test.py를 실행할 예정이다.

 

사용해볼 CI 템플릿은 Python application이다.

테스트 코드 이외에도 flake8라는 Python Linting tool을 이용해서 문법 에러나 코딩 스타일도 체크해 보겠다.

이 템플릿은 pytest를 테스트 프레임워크로 사용하나, test.py는 unittest로 작성되어 있다.

 

본인 repo에 Actions 탭에 들어가서 위 Python application의 Configure를 누른다.

Workflow를 위한 명령어들이 YAML 파일에 작성되어 있을 것이다.

 

on

 

첫 번째로 on이라는 key 값을 살펴보면,

main 브랜치에 push 되거나 PR이 만들어지는 경우를 트리거 이벤트로 지정했다.

만약 모든 브랜치에 트리거를 적용하고 싶다면, [ "main", "dev" ]로 수정한다.

 

jobs

 

jobs 부분이 실제 CI 프로세스가 스텝(name) 별로 기술되는 부분이다.

스텝을 살펴보면, Python 3.10 설치, 라이브러리 설치, flake8로 린팅, pytest로 테스트로 구성되어 있다.

 

나의 test.py는 unittest 모듈로 사용하므로, - name : Test with pytest 부분과

-name : Install dependencies 부분을 일부 수정할 것이다.

- name : Install dependencies
  run: |
    python -m pip install --upgrade pip
    pip install flake8
    
    ...
    
- name: Test with unittest
  run: |
    python -m unittest discover -p 'test*.py'

test에 *가 붙은 이유는 test로 시작하는 모든 py를 실행해라 라는 뜻이다.

 

이후 변경사항을 저장하고,

다른 임시 브랜치에 커밋하고 PR을 이용해 main으로 머지했다. 그러고 나서 임시 브랜치를 삭제한다.

PR이후 머지를 시키면 workflows라는 폴더가 만들어진 게 main 브랜치에 보인다.

(이렇게 안해도 되고 그냥 main 브랜치에 yaml을 만들어도 된다.)

완료!

 

이후에는 README.md 하나만 수정해도 자동으로 CI가 진행되는 것을 볼 수 있다!


5. DockerHub에 이미지 push하기(CD 과정)

 

이제는 Docker Image라는 템플릿을 사용하여 workflow를 하나 더 만들어보겠다.

먼저 Docker Hub의 정보를 Github에 보관하겠다.

(Docker Hub의 계정 정보를 Github에 저장해야 하는데, Yml에 하드코딩 하는 것은 좋은 방법이 아니다.)

 

repo 내부의 settings tab

위 그림의 Actions에 들어가서

new repository secret을 선택하고

Name은 DOCKER_USER와 DOCKER_PASSWORD로 각각 생성해주고

Secret에는 해당하는 value값(dockerhub id와 비밀번호)을 넣으면 된다.

 

그리고 Docker-image.yml의 구성을 편집해보자.

name: Docker Image CI

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

jobs:

  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v3
    - 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

 

이후 변경사항을 저장하고 commit하면 실행이 될 것이다.

 

dockerhub 빌드

 

dockerhub에도 배포가 됨을 확인하여 CD도 정상적으로 된것을 확인하였다!

 

 

 


참고하면 좋은 글

https://zzsza.github.io/development/2020/06/06/github-action/ - 변성윤님 블로그

반응형