Devops #1 Python 프로젝트 CI/CD & 클라우드 빌드 #
#2025-08-11
실습 #
메이크파일, 린팅, 테스트와 같이 파이썬 프로젝트 스캐폴딩에 필수적인 요소가 포함된 깃허브 저장소를 생성해보자. 그리고 간단하게 코드 포매팅을 수행하도록 메이크파일 스크립트를 작성해보자.
깃허브 액션을 사용하여 두개 이상의 파이썬 버전에 대해 깃허브 프로젝트 테스트를 수행해보자.
클라우드 네이티브 빌드 서버(AWS 코드빌드, GCP 클라우드 빌드, 애저 DevOps 파이프라인)를 사용하여 지속적 통합을 수행해보자.
깃허브 프로젝트를 도커 파일로 컨테이너화하고, 자동으로 컨테이너 레지스트리에 새로운 컨테이너가 등록되도록 만들어보자.
locust 또는 loader io와 같은 부하 테스트 프레임워크를 사용하여 애플리케이션에 대한 간단한 부하 테스트 코드를 작성한다. 그리고 스테이징 브랜치에 변경 사항을 푸시할 때 이 테스트가 자동으로 수행되도록 만들어보자.
#
1. 파이썬 프로젝트 스캐폴딩 + 메이크파일/린팅/테스트 + 포매팅 #
#1 새 프로젝트 만들기 (로컬)
# 프로젝트 폴더 생성
mkdir py-skeleton && cd py-skeleton
# 가상환경
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\Activate.ps1
# 기본 폴더 구조
mkdir -p src/awesome_pkg tests
#2 최소 패키지/테스트 코드 넣기
# src/awesome_pkg/__init__.py
__all__ = ["add"]
def add(a: int, b: int) -> int:
return a + b
# tests/test_add.py
from awesome_pkg import add
def test_add():
assert add(2, 3) == 5
#3 개발 도구 설치 파일
- ruff(린터+포매터), pytest(테스트), mypy(타입체크)만 사용
# pyproject.toml
[project]
name = "awesome-pkg"
version = "0.1.0"
requires-python = ">=3.9"
[tool.ruff]
line-length = 100
target-version = "py39"
lint.select = ["E","F","I","B","UP"] # 기본 + 모던화 제안
lint.ignore = ["E501"] # 길이제한은 포매터가 처리
src = ["src"]
extend-exclude = ["tests/fixtures"]
[tool.ruff.format]
quote-style = "double"
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-q"
[tool.mypy]
python_version = "3.9"
packages = ["awesome_pkg"]
strict = true
ignore_missing_imports = true
# requirements-dev.txt
ruff
pytest
mypy
# .gitignore
.venv/
__pycache__/
*.pyc
.pytest_cache/
.mypy_cache/
.cache/
#4 메이크파일 작성 (포매팅/린팅/테스트 일괄 실행)
# Makefile
PY := .venv/bin/python
PIP := .venv/bin/pip
RUFF := .venv/bin/ruff
PYTEST := .venv/bin/pytest
MYPY := .venv/bin/mypy
.PHONY: help init install format lint test typecheck check clean
help:
@echo "make init - 가상환경과 기본 의존성 설치"
@echo "make format - 코드 포매팅 (ruff format)"
@echo "make lint - 린트 검사 (ruff)"
@echo "make test - 테스트 실행 (pytest)"
@echo "make typecheck - 타입체크 (mypy)"
@echo "make check - lint+typecheck+test 종합"
@echo "make clean - 캐시/산출물 정리"
init:
python3 -m venv .venv
$(PIP) install -U pip
$(PIP) install -r requirements-dev.txt
format:
$(RUFF) format src tests
lint:
$(RUFF) check src tests
test:
$(PYTEST)
typecheck:
$(MYPY) src
check: lint typecheck test
clean:
rm -rf .pytest_cache .mypy_cache .ruff_cache __pycache__ */__pycache__
#5 의존성 설치 & 동작 확인
make init # 가상환경+개발도구 설치
make format # 포매팅
make lint # 린트
make typecheck # 타입체크
make test # 테스트
make check # 일괄 점검
#6. 깃허브 저장소 만들고 푸시
git init
git add .
git commit -m "feat: project scaffold with makefile/lint/test/format"
# 깃허브에서 빈 저장소 생성 후, 아래처럼 원격 추가/푸시
git branch -M main
git remote add origin https://github.com/<YOUR_ID>/py-skeleton.git
git push -u origin main
#
2. 여러 파이썬 버전으로 GitHub Actions 테스트 #
#1 리포지토리 준비
#2 브랜치 생성
git checkout -b ci-setup
#3 워크플로우 폴더 만들기
mkdir -p .github/workflows
#4 CI 설정 파일 생성
- 파일 경로: .github/workflows/ci.yml
- 내용: 매트릭스로 3.9~3.12 테스트, Makefile 타깃 사용
name: CI
on:
push:
branches: [ main, develop, staging ]
pull_request:
branches: [ main, develop, staging ]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [ "3.9", "3.10", "3.11", "3.12" ]
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
cache-dependency-path: |
requirements-dev.txt
pyproject.toml
- name: Install dev deps
run: |
python -m pip install -U pip
pip install -r requirements-dev.txt
- name: Lint
run: make lint
- name: Format check (ruff format --check)
run: ruff format --check src tests
- name: Type check
run: make typecheck
- name: Test
run: make test
#5 커밋 & 푸시
git add .github/workflows/ci.yml
git commit -m "chore: add CI for multi-python versions"
git push -u origin ci-setup
#6 PR 생성
- GitHub에서 ci-setup → main으로 Pull Request 생성
- PR이 생성되면 Actions 탭에서 파이썬 3.9/3.10/3.11/3.12 네 개 잡이 병렬로 도는 걸 볼 수 있음.
#7 배지 추가
- README.md에 아래 한 줄 추가(리포지토리 경로는 본인 것으로 교체)

#cf
- Secrets 불필요: 단순 테스트만 하면 깃허브 액션 기본 권한으로 충분.
- 기본은 ubuntu-latest지만, OS 매트릭스를 늘리고 싶으면 다음과같이 설정
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.9","3.10","3.11","3.12"]
- Makefile 없이도 가능. 위 Lint/Type/Test 단계를 ruff/mypy/pytest 직접 실행으로 바꿔도 동작.
#
3. 클라우드 네이티브 빌드 서버로 CI #
1. AWS CodeBuild
#1 리포에 buildspec 추가
- 리포 루트에 buildspec.yml 생성
version: 0.2
env:
variables:
PIP_CACHE_DIR: "/root/.cache/pip"
phases:
install:
runtime-versions:
python: 3.12
commands:
- python -m pip install --upgrade pip
- pip install -r requirements-dev.txt
pre_build:
commands:
- ruff check src tests
- ruff format --check src tests
- mypy src
build:
commands:
- pytest -q
artifacts:
files:
- "**/*"
discard-paths: no
- 여러 파이썬 버전을 돌리고 싶다면 CodeBuild 프로젝트를 버전별로 2~3개 만들거나, Docker 이미지를 바꿔 실행하는 별도 프로젝트를 추가하는 방식이 단순함.
#2 CodeBuild 프로젝트 만들기(콘솔)
1. 사전 준비
- 깃허브 리포에 buildspec.yml이 루트에 있어야 함.
- 리포 권한: 본인 GitHub 계정이 관리자여야 함.
1. AWS 콘솔 접속
- 콘솔 검색창 → CodeBuild → 좌측 Build projects → Create build project 클릭.
1. Project configuration
- Project name: py-skeleton-ci (원하는 이름)
- (선택) Description: “Python lint/test CI”
1. Source (소스 설정)
- Source provider: GitHub
- Repository: “Connect using OAuth” 클릭 → GitHub 로그인/승인 → 리포 선택
- Webhook: Enable 체크(푸시 시 자동 빌드)
- Primary source webhook events: 기본값 유지(Push로 충분)
1. Environment (빌드 환경)
- Environment image: Managed image
- Operating system: Ubuntu
- Runtime(s): Standard
- Image: aws/codebuild/standard:7.0 선택
- Image version: Always use the latest image
- Environment type: Linux
- Service role: “New service role” 선택(자동 생성)
- Additional configuration:
- Privileged: 비활성(Docker 빌드가 필요할 때만 활성)
- (선택) Compute: 기본 Small(빠른 빌드 원하면 Medium)
6. Buildspec (빌드 스펙)
- Build specifications: Use a buildspec file 선택
- Buildspec name: buildspec.yml (리포 루트에 있는 그 파일)
7. Artifacts (산출물)
- Artifacts type: No artifacts (테스트/린팅만이면 산출물 불필요)
8. Logs (로그)
- CloudWatch logs: Enabled
- Group/Stream은 기본값 그대로(자동 생성)
9. (선택) Cache (pip 캐시)
- Cache: Enabled
- Type: Local → Custom cache 체크 → 경로에 /root/.cache/pip 입력
10. Triggers (브랜치 트리거)
- Build triggers: Enable webhook 이미 켰다면 OK
- Filter groups에서 브랜치에 main, develop, staging 추가
- Example: EVENT: PUSH + BASE_REF: ^refs/heads/(main|develop|staging)$
11. 만들기
- 맨 아래 Create build project 클릭.
12. 권한 확인(IAM 자동역할)
- 생성 후 상단에 “Service role” 링크 클릭 → IAM에서 자동 생성된 codebuild-py-skeleton-ci-service-role 확인.
- 보통 기본 정책으로 충분(CloudWatch Logs/CodeBuild 권한). 별도 리소스 접근이 필요 없다면 추가 작업 없음.
13. 첫 빌드 실행(테스트)
- 프로젝트 상세 화면 → Start build 클릭 → 기본값 그대로 Start build.
- Build history에서 진행 상황 확인 → Status가 Succeeded면 성공.
- 실패하면 Phase details에서 어느 단계(install/pre_build/build)에서 실패했는지 로그 확인.
14. 푸시로 자동 트리거 확인
- 로컬에서 아무 커밋 후 git push origin main (또는 develop/staging)
- GitHub → 리포의 Settings → Webhooks에 CodeBuild 웹훅이 생긴 것 확인.
- AWS CodeBuild Build history에 새 빌드가 자동으로 뜨는지 확인.
#
2. GCP Cloud Build
#1 리포에 cloudbuild.yaml 추가
steps:
- name: 'python:3.12'
entrypoint: bash
args:
- -lc
- |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
ruff check src tests
ruff format --check src tests
mypy src
pytest -q
# 캐시(선택): pip 캐시용 볼륨
options:
volumes:
- name: pip-cache
path: /root/.cache/pip
#2 트리거 연결
1. GCP 프로젝트/권한 준비
- GCP 콘솔 상단 프로젝트가 맞는지 확인.
- Cloud Build API가 꺼져 있다면 켜기(Enable).
- 결제 활성화 필요하면 켜두기.
2. GitHub(App) 연결
- 콘솔 좌측 메뉴 → Cloud Build → Triggers → Manage repositories(또는 “Connect repository”).
- GitHub (Cloud Build GitHub App) 선택 → GitHub 계정으로 로그인/Authorize.
- 연결할 Organization/Repository 선택 → Connect.
3. 트리거 생성
- Create trigger 클릭.
- Event: Push to a branch 선택.
- Repository: 방금 연결한 리포 선택.
- Branch: 정규식 입력 → ^main$|^develop$|^staging$
- 의미: main · develop · staging 브랜치에 push될 때만 발동.
4. 빌드 설정 지정
- Configuration: Cloud Build configuration file (yaml or json) 선택.
- Location: Repository.
- Cloud Build configuration file: cloudbuild.yaml (루트가 아니면 경로 입력, 예: .cloud/cloudbuild.yaml).
- (선택) Substitution variables: 필요 시 버전 등 넘길 값 정의(예: _PY_VERSION=3.12).
5. 저장
- Create 클릭 → 트리거 목록에 생성됐는지 확인.
6. 동작 확인(첫 빌드)
- 리포의 README에 공백 한 줄 추가하고 main/develop/staging 중 하나에 push.
- Cloud Build → History에서 실행되는지 확인.
- 로그에서 pip install, ruff check, ruff format --check, mypy, pytest가 순서대로 실행되는지 본다.
cf) cloudbuild.yaml 예시(루트에 있어야 함)
steps:
- name: 'python:3.12'
entrypoint: bash
args:
- -lc
- |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
ruff check src tests
ruff format --check src tests
mypy src
pytest -q
options:
volumes:
- name: pip-cache
path: /root/.cache/pip
cf2) 여러 파이썬 버전으로 돌리기 – 두 가지 방법
- 스텝을 여러 개 두기
steps:
- name: 'python:3.9' # 동일 스크립트
entrypoint: bash
args: [ "-lc", "…" ]
- name: 'python:3.12'
entrypoint: bash
args: [ "-lc", "…" ]
- 트리거를 2개 만들고, 각각 Substitution으로 버전 넘기기
트리거1: _PY_VERSION=3.9
트리거2: _PY_VERSION=3.12
cloudbuild.yaml에서 ${_PY_VERSION} 사용:
steps:
- name: "python:${_PY_VERSION}"
entrypoint: bash
args: [ "-lc", "…" ]
#
3. Azure DevOps Pipelines
#1 리포에 buildspec 추가
- 리포 루트에 azure-pipelines.yml 생성
trigger:
branches:
include: [ main, develop, staging ]
pool:
vmImage: 'ubuntu-latest'
strategy:
matrix:
py39:
PY: '3.9'
py310:
PY: '3.10'
py311:
PY: '3.11'
py312:
PY: '3.12'
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(PY)'
- script: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
displayName: Install dev deps
- script: ruff check src tests
displayName: Lint (ruff)
- script: ruff format --check src tests
displayName: Format check
- script: mypy src
displayName: Type check
- script: pytest -q
displayName: Test (pytest)
#2 Azure DevOps 파이프라인 생성
1. Azure DevOps 접속
- 브라우저에서 dev.azure.com → 본인 Organization 선택 → Project 선택
2. 새 파이프라인 만들기
- 좌측 메뉴 Pipelines → Create Pipeline (또는 New pipeline)
3. 코드 위치 선택
- “Where is your code?” 화면에서 GitHub 선택
- 처음이면 GitHub Authorize(연동 승인) 창이 뜸 → Authorize로 진행
- 연동 후 리포지토리 목록에서 해당 리포 클릭
4. 구성 방식 선택
- “Configure your pipeline” 화면에서 Existing Azure Pipelines YAML file 선택
5. 브랜치/파일 경로 지정
- Branch: main(또는 사용 중인 기본 브랜치) 선택
- Path: /azure-pipelines.yml 지정(루트에 위치한 파일)
- Continue 클릭
6. 저장 & 실행
- 상단 Run 또는 Save and run 클릭
- 커밋 메시지(자동 생성됨) 확인 → Save and run 확정
7. 권한 승인(처음 1회)
- 실행 직후 상단에 Authorize 또는 Grant permission 배너가 뜨면 클릭해서 허용 (GitHub 리포 접근 / Service connection 권한 부여)
8. 실행 확인
- Pipelines → Runs에서 방금 실행된 파이프라인 클릭
- 단계별 로그(Install → Lint → Format check → Type check → Test)가 성공(Succeeded)인지 확인
9. 자동 트리거 확인
- 로컬에서 아무 변경(예: README 공백 추가) → git push origin main
- Runs에 새 실행이 자동으로 생성되는지 확인
cf)
만약 GitHub 권한 에러가 나면?
- 좌측 하단 Project settings → Service connections → New service connection → GitHub → Grant access(또는 OAuth) → 연결 생성
- 다시 Pipelines → Create pipeline부터 진행
이미 리포에 있어야 하는 파일 예시
- azure-pipelines.yml (다중 파이썬 버전 매트릭스)
trigger:
branches:
include: [ main, develop, staging ]
pool:
vmImage: 'ubuntu-latest'
strategy:
matrix:
py39: { PY: '3.9' }
py310: { PY: '3.10' }
py311: { PY: '3.11' }
py312: { PY: '3.12' }
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '$(PY)'
- script: |
python -m pip install -U pip
pip install -r requirements-dev.txt
displayName: Install dev deps
- script: ruff check src tests
displayName: Lint (ruff)
- script: ruff format --check src tests
displayName: Format check
- script: mypy src
displayName: Type check
- script: pytest -q
displayName: Test (pytest)