Add CI/CD workflow examples and serving manifest
CI - Lint & Test / lint-and-test (push) Has been cancelled
Details
CI - Lint & Test / lint-and-test (push) Has been cancelled
Details
- ci.yaml: lint (ruff) + test (pytest) on push/PR - train.yaml: dvc pull → train → dvc push → docker build → deploy - dvc-data-update.yaml: data validation on .dvc file changes - manifests/deployment.yaml: example serving Deployment + Service Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4f7c3da543
commit
b606ed18ae
|
|
@ -0,0 +1,30 @@
|
|||
name: CI - Lint & Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
lint-and-test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
pip install ruff pytest
|
||||
|
||||
- name: Lint
|
||||
run: ruff check .
|
||||
|
||||
- name: Test
|
||||
run: pytest tests/ -v
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
name: DVC Data Validation
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- "**.dvc"
|
||||
- "dvc.yaml"
|
||||
- "dvc.lock"
|
||||
|
||||
jobs:
|
||||
validate-data:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install dvc pandas
|
||||
|
||||
- name: Pull updated data
|
||||
run: dvc pull
|
||||
|
||||
- name: Validate data
|
||||
run: |
|
||||
python -c "
|
||||
import pandas as pd
|
||||
import sys
|
||||
|
||||
# TODO: 실제 데이터 경로와 검증 로직으로 수정
|
||||
# df = pd.read_csv('data/dataset.csv')
|
||||
# assert len(df) > 0, 'Dataset is empty'
|
||||
# assert df.isnull().sum().sum() == 0, 'Dataset has null values'
|
||||
print('Data validation passed')
|
||||
"
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
name: Train & Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "src/**"
|
||||
- "configs/**"
|
||||
- "dvc.yaml"
|
||||
|
||||
jobs:
|
||||
train:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install -r requirements.txt
|
||||
pip install dvc
|
||||
|
||||
- name: Pull data from DVC
|
||||
run: dvc pull
|
||||
|
||||
- name: Train model
|
||||
run: python src/train.py
|
||||
|
||||
- name: Track model with DVC
|
||||
run: |
|
||||
dvc add models/
|
||||
dvc push
|
||||
|
||||
- name: Commit DVC metadata
|
||||
run: |
|
||||
git config user.name "gitea-runner"
|
||||
git config user.email "runner@mlops"
|
||||
git add models/*.dvc models/.gitignore
|
||||
git diff --cached --quiet || git commit -m "Update model - $(date +%Y%m%d-%H%M%S)"
|
||||
git push
|
||||
|
||||
build-and-deploy:
|
||||
needs: train
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
|
||||
- name: Build Docker image
|
||||
run: |
|
||||
IMAGE_TAG="${{ github.sha }}"
|
||||
docker build -t $REGISTRY_URL/mlops-serving:${IMAGE_TAG} .
|
||||
docker push $REGISTRY_URL/mlops-serving:${IMAGE_TAG}
|
||||
env:
|
||||
REGISTRY_URL: ${{ vars.REGISTRY_URL }}
|
||||
|
||||
- name: Update manifest
|
||||
run: |
|
||||
IMAGE_TAG="${{ github.sha }}"
|
||||
sed -i "s|image:.*mlops-serving.*|image: $REGISTRY_URL/mlops-serving:${IMAGE_TAG}|" manifests/deployment.yaml
|
||||
git config user.name "gitea-runner"
|
||||
git config user.email "runner@mlops"
|
||||
git add manifests/
|
||||
git commit -m "Deploy model ${IMAGE_TAG:0:7}"
|
||||
git push
|
||||
env:
|
||||
REGISTRY_URL: ${{ vars.REGISTRY_URL }}
|
||||
# ↑ manifests/ 변경 → ArgoCD가 자동 감지 → soo ns에 배포
|
||||
52
README.md
52
README.md
|
|
@ -163,20 +163,58 @@ dvc version
|
|||
|
||||
---
|
||||
|
||||
## 5. CI/CD 워크플로우
|
||||
|
||||
`.gitea/workflows/` 에 정의. Gitea Runner가 실행합니다.
|
||||
|
||||
### ci.yaml — 코드 push 시 린트 + 테스트
|
||||
|
||||
```
|
||||
trigger: push to main/develop, PR to main
|
||||
동작: ruff lint → pytest
|
||||
```
|
||||
|
||||
### train.yaml — 모델 학습 + 배포
|
||||
|
||||
```
|
||||
trigger: src/, configs/, dvc.yaml 변경 시
|
||||
동작:
|
||||
1. dvc pull (NAS에서 데이터 가져오기)
|
||||
2. python src/train.py (학습)
|
||||
3. dvc add + dvc push (모델 → NAS)
|
||||
4. Docker build + push (서빙 이미지)
|
||||
5. manifests/ 이미지 태그 업데이트 → git push
|
||||
6. → ArgoCD 자동 감지 → soo ns에 배포
|
||||
```
|
||||
|
||||
### dvc-data-update.yaml — 데이터 변경 시 검증
|
||||
|
||||
```
|
||||
trigger: .dvc 파일, dvc.yaml, dvc.lock 변경 시
|
||||
동작: dvc pull → 데이터 검증 스크립트 실행
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 파일 구조
|
||||
|
||||
```
|
||||
mlops_architecture/
|
||||
├── .env # 접속 정보, 시크릿 (git 제외)
|
||||
├── .env # 접속 정보, 시크릿 (git 제외)
|
||||
├── .gitignore
|
||||
├── config.yaml # 인프라 구성 정보
|
||||
├── README.md # 설치 가이드 (이 파일)
|
||||
├── .dvc/ # DVC 설정 (remote 등)
|
||||
├── .dvc/ # DVC 설정 (remote 등)
|
||||
├── .gitea/workflows/ # CI/CD 파이프라인
|
||||
│ ├── ci.yaml # 린트 + 테스트
|
||||
│ ├── train.yaml # 학습 → 빌드 → 배포
|
||||
│ └── dvc-data-update.yaml # 데이터 검증
|
||||
├── argocd/
|
||||
│ └── application.yaml # ArgoCD Application 정의
|
||||
│ └── application.yaml # ArgoCD Application 정의
|
||||
├── config.yaml # 인프라 구성 정보
|
||||
├── gitea-runner/
|
||||
│ └── deployment.yaml # Gitea Runner K8s 배포 (Secret+ConfigMap+Deployment)
|
||||
└── manifests/ # K8s 배포 manifest (ArgoCD가 감시)
|
||||
│ └── deployment.yaml # Gitea Runner K8s 배포
|
||||
├── manifests/ # K8s 배포 manifest (ArgoCD 감시)
|
||||
│ └── deployment.yaml # 서빙 Deployment + Service
|
||||
└── README.md # 설치 가이드 (이 파일)
|
||||
```
|
||||
|
||||
## 설정 파일 사용법
|
||||
|
|
|
|||
|
|
@ -0,0 +1,52 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mlops-serving
|
||||
namespace: soo
|
||||
labels:
|
||||
app: mlops-serving
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mlops-serving
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mlops-serving
|
||||
spec:
|
||||
containers:
|
||||
- name: serving
|
||||
image: ${REGISTRY_URL}/mlops-serving:latest # CI에서 자동 업데이트
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 2Gi
|
||||
# env:
|
||||
# - name: MODEL_PATH
|
||||
# value: /models/model.pt
|
||||
# volumeMounts:
|
||||
# - name: model-storage
|
||||
# mountPath: /models
|
||||
# volumes:
|
||||
# - name: model-storage
|
||||
# persistentVolumeClaim:
|
||||
# claimName: mlops-model-pvc
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mlops-serving
|
||||
namespace: soo
|
||||
spec:
|
||||
selector:
|
||||
app: mlops-serving
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 8080
|
||||
type: ClusterIP
|
||||
Loading…
Reference in New Issue