DevOps는 개발과 운영의 경계를 허물고 각 팀이 협업하며 아이디어를 개발/배포하는 개발 환경이나 도구를 의미합니다. DevOps는 소프트웨어 개발 및 IT 운영 팀의 작업을 결합하고 자동화함으로써 고품질의 소프트웨어를 보다 빠르게 제공합니다.
이 글에서는 github actions 를 통해 깃허브에 CI/CD (Continuous Integration/Continuous Delivery) 를 추가하는 법을 배웁니다.
CI/CD
목표 : 어떻게 Github의 코드가 여러 단계를 거쳐 최종 배포 과정까지 자동화되는지 DevOps 전체 흐름을 이해하고 구현합니다.
1. 실습을 위한 간단한 flask app과 파이썬 프로그램 구현
위 repository를 clone하여 실습을 수행할 수 있습니다.
directory 구조가 다음과 같이 구성되어 있습니다.
OSS_mlops
├── Dockerfile # 빌드에 사용할 Dockerfile
├── README.md
├── core_codes # 실제 실행할 코드들이 모여있는 폴더
│ ├── __init__.py
│ ├── app.py # flask app 실행 코드
│ ├── application # flask app codes
│ │ ├── __init__.py
│ │ ├── memo.txt
│ │ ├── utils
│ │ │ ├── __init__.py
│ │ │ └── image.py
│ │ └── views
│ │ └── main_views.py
│ └── quiz # CI/CD 실습을 위한 간단한 파이썬 코드들
│ ├── __init__.py
│ ├── coined_term_quiz.py
│ ├── hello_world.py
│ └── up_down_quiz.py
└── tests # test 수행을 위한 폴더
├── test_coined_term.py
├── test_hello_world.py
└── test_updown.py
Python
복사
2. github actions로 CI를 동작시키기
이제부터 일반적인 오픈소스 프로젝트 과정을 이해하고 수행해봅시다.
전체 과정을 먼저 설명해보겠습니다.
1.
새로운 branch 생성 git branch <branchname>
2.
git fetch 명령어를 통해 git repository의 내용을 새로운 branch로 가져온다.
git fetch <원격저장소이름>
git checkout -b <branchname>
3.
개발을 수행한 후, tests폴더에 유닛테스트 코드를 추가
4.
git push 혹은 pull request를 수행하면 자동으로 flake8을 통한 lint와 pytest를 통한 유닛테스트가 수행되도록 github actions workflow를 설정.
5.
코드 add / commit 하고 branch를 push
6.
pull request (PR) 진행 → github actions로 CI가 동작
7.
다른 팀원의 코드 리뷰
8.
main branch로 merge (새로 생성했던 branch는 제거)
9.
연관된 issue가 있었다면 close
이제 이 과정을 직접 수행해봅시다
•
main branch에 보호규칙 설정
이 설정으로 main branch에 대해 다음 규칙이 적용됐다.
◦
merging 전에는 무조건 PR이 필요하며, 1명의 코드 리뷰가 필요
◦
테스트 결과 통과한 코드만 merge 가능
•
github actions 추가
◦
github actions은 레포지토리의 .github/workflows폴더에 yml파일로 정의할 수 있습니다.
◦
저희가 이 단계에서 사용할 yml파일은 python-workflow.yml파일로, CI 워크플로우를 만들어 파이썬 프로젝트를 빌드하고 테스트 하는 것을 목표로 합니다.
◦
공식 docs에서 더 자세한 내용을 확인할 수 있습니다.
name: Python application
on:
push:
branches:
- '!main' # excludes main
pull_request:
branches:
- '!main'
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- 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
- 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
- name: Test with pytest
run: |
python -m pytest tests
YAML
복사
◦
우리는 위 yml파일로 “Python application”이라는 이름의 workflow를 추가한 것입니다.
◦
on 의 내용을 보아하니 main외의 branch들에서 push나 pull request가 발생하면 jobs가 수행되겠죠. 또한 permissions 로 jobs에 대한 권한을 지정해줬습니다. contents:read는 list the commits 을 허가합니다. permissions에 대한 다양한 옵션은 공식 docs에서 확인 가능합니다.
◦
jobs 살펴보기
▪
build라는 이름의 job이 정의가 되었네요
▪
이 안에 runs-on과 steps라는 key가 있는데요,
▪
▪
steps는 말 그대로 job을 한단계씩 정의해줍니다. 이 yml에서는
[1] runner에 레포가 복사되었다는 메세지 출력 : run이라는 key로 명령어를 실행할 수 있다.
[2] actions/checkout@v3 라는 action 수행 : workflow가 접근할 수 있도록 레포를 체크아웃한다.
[3] Set up Python 3.10이라는 이름으로 정의한 step. actions/setup-python@v3이라는 액션을 수행한다 : 파이썬을 설치하고 PATH에 추가한다.
[4] Install dependencies라는 이름으로 정의한 step. run으로 pip 업그레이드와 flake8, pytest 패키지 설치
[5] Lint with flake8이라는 이름으로 정의한 step. run으로 flake8을 수행한다. 여기서 에러가 나면 빌드가 중단된다.
[6] Test with pytest라는 이름으로 정의한 step. tests 폴더의 테스트 코드들로 pytest를 수행한다.
•
다음 템플릿을 따라 임의의 issue 생성
템플릿:
## Issue Description
[Provide a brief description of the issue you're encountering or the feature you're requesting.]
## Expected Behavior
[Explain what you expected to happen.]
## Current Behavior
[Explain what is currently happening that you think is a problem or what is missing.]
## Steps to Reproduce
[If applicable, provide steps to reproduce the issue.]
1. [Step 1]
2. [Step 2]
3. [Step 3]
...
## Screenshots/Attachments
[If relevant, include screenshots, logs, or any other relevant files.]
## Environment
- OS: [e.g., Windows 10, macOS Big Sur, Linux]
- Browser: [if the issue is related to a web interface]
- Version/Commit: [e.g., 1.0.0, SHA of the commit]
- Any other relevant information about your environment.
## Additional Information
[Any other information that might be helpful for understanding or diagnosing the issue.]
Markdown
복사
•
새로운 브랜치에서 이슈 해결을 위한 작업 수행
◦
git checkout -b [branch name] : 새로운 브랜치를 만들면서 그 브랜치로 체크아웃 (이동)
git fetch [원격저장소 이름] :원격저장소에 변동사항만을 가져 옵니다.
git merge FETCH_HEAD : FETCH_HEAD에 업데이트된 원격저장소의 최신 커밋이, 현재 브랜치에 병합 됩니다.
◦
이 새로운 브랜치에서 코드 수정 수행
◦
코드 수정이 완료되면 git add, git commit, git push 수행
◦
테스트 코드도 수정해야 함을 주의
•
workflow가 잘 수행되는지 확인
actions에 들어가봤을 때 다음과 같이 status가 success라면 테스트를 통과한 것이다.
•
PR (Pull Request) 수행
다음 템플릿에 맞추어 PR을 수행합니다.
## Description
[Provide a brief description of the changes introduced by this PR.]
## Related Issues
[Reference any related issues by mentioning their numbers, e.g., "Closes #123" or "Fixes #456".]
## Changes Made
[Explain the changes you've made in this PR. This can include new features, bug fixes, improvements, etc.]
## Screenshots
[If applicable and helpful, provide screenshots or GIFs that demonstrate the changes.]
## Checklist
- [ ] I have read the [contributing guidelines](CONTRIBUTING.md) and followed the process outlined there.
- [ ] I have updated the documentation to reflect the changes (if applicable).
- [ ] All tests are passing, and I've added new tests for the changes made.
- [ ] The code follows the project's coding standards and style.
- [ ] I have tested the changes locally and verified that they work as expected.
- [ ] I have added necessary comments to the code, especially in complex or tricky parts.
- [ ] I have considered and handled potential edge cases.
- [ ] I have addressed the review comments (if any) from previous PRs.
## Additional Information
[Any additional context, information, or rationale you'd like to provide.]
Markdown
복사
•
collaborator 한명의 approval을 받고 merge합니다.
여기까지 실습을 완료했습니다.
더 큰 규모의 프로젝트에서는 브랜치 생성 권한이 없기 때문에 fork해서 내 레포지토리에서 작업한 후 PR을 올리는 것으로 작업합니다.
다음 글에서는 빌드 및 배포까지 자동화하는 실습을 해보겠습니다.