Ver3.5 Argo Workflows로 배포 자동화 파이프라인 구성

Argo Workflows로 배포 자동화 파이프라인 구성
쿠버네티스 위에서 돌아가는 워크플로우 엔진, 제대로 파헤쳐보자 🚀
🤔 Argo Workflows가 뭔데? 진짜로
야 솔직히 처음 들으면 "아르고? 그게 뭔 배 이름이야?" 싶잖아ㅋㅋㅋ
근데 이거 알고 나면 진짜 DevOps 세계에서 없으면 안 되는 존재임.
Argo Workflows는 쿠버네티스(Kubernetes) 위에서 동작하는 오픈소스 워크플로우 엔진이야.
2017년에 Applatix라는 회사에서 만들었고, 지금은 CNCF(Cloud Native Computing Foundation)의 인큐베이팅 프로젝트로 관리되고 있어.
쉽게 말하면 "이 작업 하고, 저 작업 하고, 그 다음에 이거 해" 같은 복잡한 작업 흐름을 자동화해주는 도구야.
Argo Workflows는 쿠버네티스 CRD(Custom Resource Definition)를 활용해서
워크플로우를 쿠버네티스 오브젝트로 관리해. 즉, kubectl로 워크플로우를 조회하고 관리할 수 있다는 뜻!
기존 CI/CD 도구들이 별도 서버가 필요한 것과 달리, 쿠버네티스 클러스터 자체가 실행 환경이 됨.
Jenkins 써봤어? Jenkins도 좋은데 솔직히 플러그인 지옥이고 유지보수가 좀 빡세잖아ㅋㅋ
GitHub Actions는 편한데 쿠버네티스 환경에 최적화된 건 아니고.
Argo Workflows는 쿠버네티스 네이티브라서 클러스터 리소스를 그대로 활용하고,
각 워크플로우 스텝이 독립적인 파드(Pod)로 실행돼. 이게 진짜 강점임.
Argo Workflows - 워크플로우 엔진 (오늘의 주인공!)
Argo CD - GitOps 기반 지속적 배포
Argo Events - 이벤트 기반 트리거
Argo Rollouts - 고급 배포 전략 (카나리, 블루/그린)
이 네 가지가 합쳐지면 진짜 완전체 DevOps 파이프라인이 완성됨 🔥
🏆 왜 Argo Workflows를 써야 하냐고?
"그냥 Jenkins 쓰면 되지 않냐"는 말 나올 거 알아ㅋㅋㅋ
근데 쿠버네티스 환경에서 작업하다 보면 Argo Workflows가 왜 게임체인저인지 바로 느껴짐.
비교해볼게.
| 비교 항목 | Jenkins | GitHub Actions | Argo Workflows |
|---|---|---|---|
| 실행 환경 | 별도 Jenkins 서버 | GitHub 호스팅 러너 | 쿠버네티스 파드 |
| 쿠버네티스 통합 | 플러그인 필요 | 제한적 | 네이티브 통합 ✅ |
| 병렬 실행 | 복잡한 설정 | matrix 전략 | 기본 지원 ✅ |
| DAG 워크플로우 | 제한적 | 제한적 | 완전 지원 ✅ |
| 리소스 관리 | 수동 관리 | GitHub 관리 | K8s 스케줄러 활용 |
| 아티팩트 관리 | 플러그인 | 내장 | S3/GCS 등 통합 |
1. DAG(Directed Acyclic Graph) 지원
단순한 순차 실행이 아니라 복잡한 의존성 그래프를 표현할 수 있어.
"A 끝나면 B, C 동시에 실행하고, B, C 둘 다 끝나면 D 실행" 이런 거 YAML로 깔끔하게 표현 가능!
2. 스텝 레벨 아티팩트 전달
각 스텝에서 생성된 파일이나 데이터를 다음 스텝으로 자연스럽게 전달할 수 있어.
3. 재시도 및 타임아웃 설정
스텝별로 재시도 횟수, 타임아웃을 세밀하게 설정 가능.
4. 워크플로우 템플릿
재사용 가능한 워크플로우 컴포넌트를 만들어서 여러 파이프라인에서 공유 가능!
⚙️ 설치하고 기본 설정해보자
자 이제 진짜 실전으로 들어가보자!
쿠버네티스 클러스터가 있다는 전제 하에 진행할게.
없으면 minikube나 kind로 로컬 클러스터 먼저 만들어놔.
공식 릴리즈 YAML로 설치하는 게 제일 간단함.
# argo 네임스페이스 생성
kubectl create namespace argo
# Argo Workflows 설치 (최신 버전 확인 후 사용)
kubectl apply -n argo -f https://github.com/argoproj/argo-workflows/releases/download/v3.5.4/install.yaml
# 설치 확인
kubectl get pods -n argo
워크플로우를 커맨드라인에서 관리하려면 CLI가 필요해.
# macOS (Homebrew)
brew install argo
# Linux
curl -sLO https://github.com/argoproj/argo-workflows/releases/download/v3.5.4/argo-linux-amd64.gz
gunzip argo-linux-amd64.gz
chmod +x argo-linux-amd64
sudo mv argo-linux-amd64 /usr/local/bin/argo
# 버전 확인
argo version
Argo Workflows는 웹 UI를 제공해. 포트 포워딩으로 접근 가능!
# 포트 포워딩
kubectl -n argo port-forward deployment/argo-server 2746:2746
# 브라우저에서 접근
# https://localhost:2746
개발 환경에서 빠르게 테스트하려면 인증을 bypass할 수 있어.
(프로덕션에서는 절대 이렇게 하면 안 됨! 보안 설정 필수!)
# argo-server deployment 수정
kubectl patch deployment argo-server \
-n argo \
--type='json' \
-p='[{"op": "replace", "path": "/spec/template/spec/containers/0/args", "value": ["server", "--auth-mode=server"]}]'
Dex나 Keycloak 같은 OIDC 프로바이더와 연동하는 게 일반적인 방법이야.
✍️ 첫 번째 워크플로우 작성해보기
이론은 충분히 봤으니까 이제 진짜 코드 써보자!
간단한 "Hello World" 워크플로우부터 시작해서 점점 복잡하게 만들어볼게.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: hello-world-
spec:
entrypoint: hello-world
templates:
- name: hello-world
container:
image: alpine:3.18
command: [echo]
args: ["안녕하세요! Argo Workflows 첫 실행!"]
# 워크플로우 실행
kubectl apply -f hello-world.yaml -n argo
# 또는 argo CLI로 실행
argo submit hello-world.yaml -n argo --watch
# 워크플로우 목록 확인
argo list -n argo
# 로그 확인
argo logs @latest -n argo
실행하면 쿠버네티스가 파드를 하나 생성하고, echo 명령어 실행하고, 완료되면 파드가 사라져.
이게 Argo Workflows의 기본 동작 방식이야. 각 스텝 = 각 파드!
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: sequential-pipeline-
spec:
entrypoint: main-steps
templates:
- name: main-steps
steps:
- - name: step-1-checkout
template: git-checkout
- - name: step-2-build
template: docker-build
- - name: step-3-test
template: run-tests
- - name: step-4-deploy
template: k8s-deploy
- name: git-checkout
container:
image: alpine/git:latest
command: [sh, -c]
args: ["echo '코드 체크아웃 완료!'"]
- name: docker-build
container:
image: docker:24-dind
command: [sh, -c]
args: ["echo 'Docker 이미지 빌드 완료!'"]
- name: run-tests
container:
image: node:20-alpine
command: [sh, -c]
args: ["echo '테스트 통과!'"]
- name: k8s-deploy
container:
image: bitnami/kubectl:latest
command: [sh, -c]
args: ["echo '쿠버네티스 배포 완료!'"]
steps: 순차적 실행. 각 단계(이중 대괄호 안)는 병렬, 단계 간은 순차.
dag: 의존성 기반 실행. 더 유연하고 복잡한 워크플로우 표현 가능.
복잡한 파이프라인은 dag를 쓰는 게 훨씬 직관적이야!
🕸️ DAG 워크플로우로 병렬 처리하기
이제 진짜 Argo Workflows의 꽃인 DAG를 써보자!
실제 CI/CD 파이프라인에서는 "빌드 후 단위테스트, 통합테스트 동시에 돌리고, 둘 다 통과하면 배포" 같은 패턴이 많잖아.
이걸 DAG로 표현하면 이렇게 돼:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dag-pipeline-
spec:
entrypoint: ci-cd-dag
templates:
- name: ci-cd-dag
dag:
tasks:
# 1단계: 코드 체크아웃 (시작점)
- name: checkout
template: git-clone
arguments:
parameters:
- name: repo-url
value: "https://github.com/myorg/myapp.git"
# 2단계: 빌드 (checkout 완료 후)
- name: build
dependencies: [checkout]
template: docker-build
# 3단계: 단위 테스트와 통합 테스트 병렬 실행
- name: unit-test
dependencies: [build]
template: run-unit-tests
- name: integration-test
dependencies: [build]
template: run-integration-tests
# 4단계: 보안 스캔도 병렬로!
- name: security-scan
dependencies: [build]
template: trivy-scan
# 5단계: 모든 테스트 통과 후 스테이징 배포
- name: deploy-staging
dependencies: [unit-test, integration-test, security-scan]
template: k8s-deploy
arguments:
parameters:
- name: environment
value: "staging"
# 6단계: 스테이징 검증 후 프로덕션 배포
- name: deploy-production
dependencies: [deploy-staging]
template: k8s-deploy
arguments:
parameters:
- name: environment
value: "production"
# 템플릿 정의들
- name: git-clone
inputs:
parameters:
- name: repo-url
container:
image: alpine/git:latest
command: [sh, -c]
args: ["git clone {{inputs.parameters.repo-url}} /workspace && echo '클론 완료'"]
volumeMounts:
- name: workspace
mountPath: /workspace
volumes:
- name: workspace
emptyDir: {}
- name: docker-build
container:
image: gcr.io/kaniko-project/executor:latest
args:
- --context=dir:///workspace
- --destination=myregistry/myapp:{{workflow.name}}
- --cache=true
- name: run-unit-tests
container:
image: node:20-alpine
command: [sh, -c]
args: ["cd /workspace && npm test -- --testPathPattern=unit"]
- name: run-integration-tests
container:
image: node:20-alpine
command: [sh, -c]
args: ["cd /workspace && npm test -- --testPathPattern=integration"]
- name: trivy-scan
container:
image: aquasec/trivy:latest
command: [trivy]
args: ["image", "--exit-code", "1", "--severity", "HIGH,CRITICAL", "myregistry/myapp:{{workflow.name}}"]
- name: k8s-deploy
inputs:
parameters:
- name: environment
container:
image: bitnami/kubectl:latest
command: [sh, -c]
args: ["kubectl set image deployment/myapp myapp=myregistry/myapp:{{workflow.name}} -n {{inputs.parameters.environment}}"]
이 파이프라인 보면 unit-test, integration-test, security-scan이 동시에 실행되잖아!
Jenkins로 이거 구현하려면 진짜 복잡한데, Argo Workflows는 YAML 몇 줄로 끝남ㅋㅋㅋ
그리고 각 스텝이 독립적인 파드로 실행되니까 리소스 격리도 완벽해.
위 예시는 개념 설명용이야. 실제로는:
1. 볼륨 공유: 스텝 간 파일 공유는 PVC(PersistentVolumeClaim)나 아티팩트를 사용해야 해
2. Kaniko: Docker-in-Docker 대신 Kaniko 사용 권장 (보안상)
3. 시크릿 관리: 레지스트리 인증 정보는 K8s Secret으로 관리
4. 리소스 제한: 각 컨테이너에 resources.requests/limits 설정 필수
📦 아티팩트 관리 - 스텝 간 데이터 전달
실제 파이프라인에서 스텝 간에 파일을 주고받아야 하는 경우가 엄청 많잖아.
빌드 결과물을 테스트 스텝에 넘기거나, 테스트 리포트를 저장하거나.
Argo Workflows는 아티팩트(Artifact) 시스템으로 이걸 우아하게 처리해!
S3 (AWS) GCS (Google Cloud) Azure Blob Storage MinIO (온프레미스) Artifactory HTTP Git
apiVersion: v1
kind: ConfigMap
metadata:
name: workflow-controller-configmap
namespace: argo
data:
artifactRepository: |
s3:
bucket: my-argo-artifacts
endpoint: s3.amazonaws.com
accessKeySecret:
name: my-s3-credentials
key: accessKey
secretKeySecret:
name: my-s3-credentials
key: secretKey
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: artifact-pipeline-
spec:
entrypoint: artifact-dag
templates:
- name: artifact-dag
dag:
tasks:
- name: build-step
template: build-and-package
- name: test-step
dependencies: [build-step]
template: test-with-artifact
arguments:
artifacts:
- name: build-output
from: "{{tasks.build-step.outputs.artifacts.build-result}}"
# 빌드 스텝: 아티팩트 출력
- name: build-and-package
container:
image: maven:3.9-eclipse-temurin-17
command: [sh, -c]
args: ["mvn package -DskipTests && echo '빌드 완료'"]
outputs:
artifacts:
- name: build-result
path: /workspace/target/myapp.jar
s3:
key: "{{workflow.name}}/build/myapp.jar"
# 테스트 스텝: 아티팩트 입력
- name: test-with-artifact
inputs:
artifacts:
- name: build-output
path: /workspace/myapp.jar
container:
image: eclipse-temurin:17-jre
command: [sh, -c]
args: ["java -jar /workspace/myapp.jar --test && echo '테스트 완료'"]
outputs:
artifacts:
- name: test-report
path: /workspace/test-results
s3:
key: "{{workflow.name}}/reports/test-results"
이렇게 하면 build-step에서 만든 JAR 파일이 S3에 올라가고,
test-step에서 그걸 다운받아서 테스트를 실행해.
스텝 간 파일 공유가 이렇게 깔끔하게 되는 거야!
🔧 WorkflowTemplate으로 재사용성 높이기
여러 프로젝트에서 같은 빌드/배포 로직을 쓴다면?
매번 YAML 복붙하는 건 너무 비효율적이잖아ㅋㅋㅋ
WorkflowTemplate을 쓰면 공통 로직을 한 번만 정의하고 여러 곳에서 참조할 수 있어!
# 공통 템플릿 정의 (한 번만!)
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: common-ci-templates
namespace: argo
spec:
templates:
# Docker 빌드 공통 템플릿
- name: docker-build
inputs:
parameters:
- name: image-name
- name: dockerfile-path
value: "Dockerfile"
- name: context-path
value: "."
container:
image: gcr.io/kaniko-project/executor:latest
args:
- --dockerfile={{inputs.parameters.dockerfile-path}}
- --context={{inputs.parameters.context-path}}
- --destination={{inputs.parameters.image-name}}:{{workflow.name}}
- --cache=true
- --cache-ttl=24h
volumeMounts:
- name: docker-config
mountPath: /kaniko/.docker
volumes:
- name: docker-config
secret:
secretName: docker-registry-credentials
# Kubernetes 배포 공통 템플릿
- name: k8s-rolling-deploy
inputs:
parameters:
- name: deployment-name
- name: image-name
- name: namespace
value: "default"
resource:
action: patch
manifest: |
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{inputs.parameters.deployment-name}}
namespace: {{inputs.parameters.namespace}}
spec:
template:
spec:
containers:
- name: app
image: {{inputs.parameters.image-name}}:{{workflow.name}}
# Slack 알림 공통 템플릿
- name: slack-notify
inputs:
parameters:
- name: message
- name: channel
value: "#deployments"
container:
image: curlimages/curl:latest
command: [sh, -c]
args:
- |
curl -X POST -H 'Content-type: application/json' \
--data '{"channel":"{{inputs.parameters.channel}}","text":"{{inputs.parameters.message}}"}' \
$SLACK_WEBHOOK_URL
env:
- name: SLACK_WEBHOOK_URL
valueFrom:
secretKeyRef:
name: slack-credentials
key: webhook-url
# 실제 프로젝트에서 공통 템플릿 참조
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: myapp-deploy-
spec:
entrypoint: deploy-pipeline
templates:
- name: deploy-pipeline
dag:
tasks:
# 공통 템플릿 참조! (templateRef 사용)
- name: build
templateRef:
name: common-ci-templates
template: docker-build
arguments:
parameters:
- name: image-name
value: "myregistry/myapp"
- name: deploy-staging
dependencies: [build]
templateRef:
name: common-ci-templates
template: k8s-rolling-deploy
arguments:
parameters:
- name: deployment-name
value: "myapp"
- name: image-name
value: "myregistry/myapp"
- name: namespace
value: "staging"
- name: notify-success
dependencies: [deploy-staging]
templateRef:
name: common-ci-templates
template: slack-notify
arguments:
parameters:
- name: message
value: "✅ myapp 스테이징 배포 완료! 버전: {{workflow.name}}"
WorkflowTemplate은 특정 네임스페이스에만 적용되는데,
ClusterWorkflowTemplate은 클러스터 전체에서 사용 가능해.
조직 공통 CI/CD 템플릿은 ClusterWorkflowTemplate으로 관리하는 게 베스트 프랙티스야!
⚡ Argo Events로 자동 트리거 설정하기
워크플로우 만들었으면 이제 자동으로 실행되게 해야지!
Argo Events는 다양한 이벤트 소스를 감지해서 Argo Workflows를 자동으로 트리거해주는 도구야.
GitHub 푸시, 웹훅, 메시지 큐, 스케줄 등 다양한 트리거를 지원해.
EventSource - 이벤트를 수신하는 소스 정의 (GitHub, Webhook, Kafka 등)
Sensor - 이벤트를 감지하고 트리거를 실행하는 컴포넌트
Trigger - 이벤트 발생 시 실행할 액션 (워크플로우 실행 등)
# Argo Events 설치
kubectl create namespace argo-events
kubectl apply -f https://github.com/argoproj/argo-events/releases/download/v1.9.0/install.yaml
kubectl apply -n argo-events -f https://github.com/argoproj/argo-events/releases/download/v1.9.0/install-validating-webhook.yaml
apiVersion: argoproj.io/v1alpha1
kind: EventSource
metadata:
name: github-eventsource
namespace: argo-events
spec:
service:
ports:
- port: 12000
targetPort: 12000
github:
myapp-repo:
repositories:
- owner: myorg
names:
- myapp
webhook:
endpoint: /push
port: "12000"
method: POST
url: https://my-argo-events.example.com
events:
- push
apiToken:
name: github-access
key: token
webhookSecret:
name: github-webhook-secret
key: secret
insecure: false
active: true
contentType: json
apiVersion: argoproj.io/v1alpha1
kind: Sensor
metadata:
name: github-sensor
namespace: argo-events
spec:
template:
serviceAccountName: argo-events-sa
dependencies:
- name: github-dep
eventSourceName: github-eventsource
eventName: myapp-repo
filters:
data:
# main 브랜치 푸시만 트리거
- path: body.ref
type: string
value:
- refs/heads/main
triggers:
- template:
name: argo-workflow-trigger
argoWorkflow:
group: argoproj.io
version: v1alpha1
resource: workflows
operation: submit
source:
resource:
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: github-triggered-
namespace: argo
spec:
workflowTemplateRef:
name: myapp-ci-pipeline
parameters:
# GitHub 이벤트 데이터를 워크플로우 파라미터로 전달
- src:
dependencyName: github-dep
dataKey: body.after
dest: spec.arguments.parameters.0.value
- src:
dependencyName: github-dep
dataKey: body.repository.clone_url
dest: spec.arguments.parameters.1.value
이렇게 설정하면 GitHub main 브랜치에 푸시할 때마다 자동으로 CI/CD 파이프라인이 실행돼!
커밋 해시나 레포 URL 같은 이벤트 데이터도 워크플로우 파라미터로 넘길 수 있어서 진짜 유용함.
🚀 실전! 완전한 배포 자동화 파이프라인
자 이제 진짜 실전 파이프라인을 만들어보자!
Node.js 앱을 기준으로 코드 체크아웃부터 프로덕션 배포까지 전 과정을 담아볼게.
재능넷 같은 플랫폼에서도 이런 파이프라인으로 안정적인 배포를 할 수 있어!
apiVersion: argoproj.io/v1alpha1
kind: WorkflowTemplate
metadata:
name: nodejs-full-pipeline
namespace: argo
spec:
arguments:
parameters:
- name: git-repo
value: "https://github.com/myorg/myapp.git"
- name: git-branch
value: "main"
- name: image-registry
value: "registry.example.com"
- name: image-name
value: "myapp"
- name: app-version
value: "{{workflow.name}}"
entrypoint: full-pipeline
# 볼륨 설정 (스텝 간 공유)
volumeClaimTemplates:
- metadata:
name: workspace
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 2Gi
templates:
# ===== 메인 DAG =====
- name: full-pipeline
dag:
tasks:
# 1. 코드 체크아웃
- name: checkout
template: git-checkout
# 2. 의존성 설치 및 빌드
- name: install-and-build
dependencies: [checkout]
template: npm-build
# 3. 병렬 실행: 단위테스트, 린트, 타입체크
- name: unit-tests
dependencies: [install-and-build]
template: npm-test
arguments:
parameters:
- name: test-script
value: "test:unit"
- name: lint-check
dependencies: [install-and-build]
template: npm-test
arguments:
parameters:
- name: test-script
value: "lint"
- name: type-check
dependencies: [install-and-build]
template: npm-test
arguments:
parameters:
- name: test-script
value: "type-check"
# 4. Docker 이미지 빌드 (테스트 통과 후)
- name: docker-build
dependencies: [unit-tests, lint-check, type-check]
template: kaniko-build
# 5. 이미지 보안 스캔
- name: image-scan
dependencies: [docker-build]
template: trivy-image-scan
# 6. 스테이징 배포
- name: staging-deploy
dependencies: [image-scan]
template: helm-deploy
arguments:
parameters:
- name: environment
value: "staging"
- name: replicas
value: "2"
# 7. 스테이징 스모크 테스트
- name: staging-smoke-test
dependencies: [staging-deploy]
template: smoke-test
arguments:
parameters:
- name: target-url
value: "https://staging.myapp.example.com"
# 8. 프로덕션 배포 (승인 필요)
- name: production-deploy
dependencies: [staging-smoke-test]
template: helm-deploy
arguments:
parameters:
- name: environment
value: "production"
- name: replicas
value: "5"
# 9. 완료 알림
- name: notify-complete
dependencies: [production-deploy]
template: send-notification
arguments:
parameters:
- name: status
value: "SUCCESS"
# ===== 개별 템플릿들 =====
- name: git-checkout
container:
image: alpine/git:2.40.1
command: [sh, -c]
args:
- |
git clone --depth=1 --branch {{workflow.parameters.git-branch}} \
{{workflow.parameters.git-repo}} /workspace/app
echo "체크아웃 완료: $(git -C /workspace/app rev-parse --short HEAD)"
volumeMounts:
- name: workspace
mountPath: /workspace
- name: npm-build
container:
image: node:20-alpine
command: [sh, -c]
args:
- |
cd /workspace/app
npm ci --prefer-offline
npm run build
echo "빌드 완료!"
volumeMounts:
- name: workspace
mountPath: /workspace
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
- name: npm-test
inputs:
parameters:
- name: test-script
container:
image: node:20-alpine
command: [sh, -c]
args:
- |
cd /workspace/app
npm run {{inputs.parameters.test-script}}
volumeMounts:
- name: workspace
mountPath: /workspace
outputs:
artifacts:
- name: test-results
path: /workspace/app/coverage
optional: true
- name: kaniko-build
container:
image: gcr.io/kaniko-project/executor:v1.19.0
args:
- --context=/workspace/app
- --dockerfile=/workspace/app/Dockerfile
- --destination={{workflow.parameters.image-registry}}/{{workflow.parameters.image-name}}:{{workflow.parameters.app-version}}
- --destination={{workflow.parameters.image-registry}}/{{workflow.parameters.image-name}}:latest
- --cache=true
- --cache-repo={{workflow.parameters.image-registry}}/{{workflow.parameters.image-name}}-cache
- --compressed-caching=false
volumeMounts:
- name: workspace
mountPath: /workspace
- name: docker-config
mountPath: /kaniko/.docker
volumes:
- name: docker-config
secret:
secretName: registry-credentials
items:
- key: .dockerconfigjson
path: config.json
- name: trivy-image-scan
container:
image: aquasec/trivy:0.48.0
command: [trivy]
args:
- image
- --exit-code=1
- --severity=HIGH,CRITICAL
- --ignore-unfixed
- --format=table
- "{{workflow.parameters.image-registry}}/{{workflow.parameters.image-name}}:{{workflow.parameters.app-version}}"
retryStrategy:
limit: "2"
retryPolicy: "OnFailure"
- name: helm-deploy
inputs:
parameters:
- name: environment
- name: replicas
container:
image: alpine/helm:3.13.0
command: [sh, -c]
args:
- |
helm upgrade --install myapp ./charts/myapp \
--namespace {{inputs.parameters.environment}} \
--create-namespace \
--set image.repository={{workflow.parameters.image-registry}}/{{workflow.parameters.image-name}} \
--set image.tag={{workflow.parameters.app-version}} \
--set replicaCount={{inputs.parameters.replicas}} \
--set environment={{inputs.parameters.environment}} \
--wait \
--timeout=5m
echo "{{inputs.parameters.environment}} 배포 완료!"
volumeMounts:
- name: workspace
mountPath: /workspace
env:
- name: KUBECONFIG
value: /root/.kube/config
- name: smoke-test
inputs:
parameters:
- name: target-url
container:
image: curlimages/curl:8.5.0
command: [sh, -c]
args:
- |
echo "스모크 테스트 시작: {{inputs.parameters.target-url}}"
for i in 1 2 3 4 5; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" {{inputs.parameters.target-url}}/health)
if [ "$STATUS" = "200" ]; then
echo "✅ 헬스체크 통과! (HTTP $STATUS)"
exit 0
fi
echo "재시도 $i/5... (HTTP $STATUS)"
sleep 10
done
echo "❌ 스모크 테스트 실패!"
exit 1
- name: send-notification
inputs:
parameters:
- name: status
container:
image: curlimages/curl:8.5.0
command: [sh, -c]
args:
- |
curl -X POST $SLACK_WEBHOOK \
-H 'Content-type: application/json' \
-d "{\"text\": \"🚀 배포 {{inputs.parameters.status}}! 버전: {{workflow.parameters.app-version}}\"}"
env:
- name: SLACK_WEBHOOK
valueFrom:
secretKeyRef:
name: slack-config
key: webhook-url
이게 진짜 실전에서 쓸 수 있는 완전한 파이프라인이야!
코드 푸시 → 빌드 → 병렬 테스트 → 이미지 빌드 → 보안 스캔 → 스테이징 배포 → 스모크 테스트 → 프로덕션 배포 → 알림
이 모든 게 자동으로 돌아가는 거임ㅋㅋㅋ 진짜 개쩔지 않냐?
🎓 고급 기능들 - 이것까지 알면 진짜 고수
templates:
- name: flaky-step
retryStrategy:
limit: "3" # 최대 3번 재시도
retryPolicy: "Always" # Always, OnFailure, OnError, OnTransientError
backoff:
duration: "10s" # 첫 재시도 대기 시간
factor: "2" # 지수 백오프 (10s, 20s, 40s)
maxDuration: "1m" # 최대 대기 시간
container:
image: alpine
command: [sh, -c]
args: ["curl https://flaky-api.example.com/data"]
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: timeout-example-
spec:
# 전체 워크플로우 타임아웃
activeDeadlineSeconds: 3600 # 1시간
entrypoint: main
templates:
- name: main
# 특정 스텝 타임아웃
activeDeadlineSeconds: 300 # 5분
container:
image: alpine
command: [sh, -c]
args: ["long-running-task.sh"]
templates:
- name: conditional-pipeline
dag:
tasks:
- name: check-environment
template: get-env
# 프로덕션 환경일 때만 실행
- name: production-only-step
dependencies: [check-environment]
template: prod-task
when: "{{tasks.check-environment.outputs.result}} == production"
# 스테이징이 아닐 때 실행
- name: non-staging-step
dependencies: [check-environment]
template: other-task
when: "{{tasks.check-environment.outputs.result}} != staging"
templates:
- name: parallel-deploy
dag:
tasks:
# 여러 리전에 동시 배포!
- name: multi-region-deploy
template: deploy-to-region
arguments:
parameters:
- name: region
value: "{{item}}"
withItems:
- ap-northeast-2 # 서울
- us-east-1 # 버지니아
- eu-west-1 # 아일랜드
- name: deploy-to-region
inputs:
parameters:
- name: region
container:
image: bitnami/kubectl:latest
command: [sh, -c]
args: ["kubectl --context={{inputs.parameters.region}} apply -f deployment.yaml"]
withItems 쓰면 여러 리전에 동시 배포가 이렇게 간단해져!
Jenkins로 이거 구현하려면 진짜 복잡한 Groovy 코드 써야 하는데ㅋㅋㅋ
templates:
- name: tracked-step
metrics:
prometheus:
# 워크플로우 완료 카운터
- name: workflow_complete_total
help: "완료된 워크플로우 수"
labels:
- key: status
value: "{{status}}"
counter:
value: "1"
# 실행 시간 히스토그램
- name: workflow_duration_seconds
help: "워크플로우 실행 시간"
when: "{{status}} == Succeeded"
gauge:
value: "{{duration}}"
container:
image: alpine
command: [sh, -c]
args: ["deploy.sh"]
Argo Workflows는 Prometheus 메트릭을 기본으로 제공해.
Grafana에 Argo Workflows 공식 대시보드(ID: 13927)를 임포트하면
워크플로우 성공률, 실행 시간, 실패 패턴 등을 한눈에 볼 수 있어!
🔒 보안 설정 - 프로덕션에서 꼭 해야 하는 것들
보안 설정 안 하면 나중에 진짜 큰일 남ㅋㅋㅋ
프로덕션 환경에서 반드시 챙겨야 할 보안 설정들 정리해줄게.
# 워크플로우 전용 ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: workflow-runner
namespace: argo
---
# 필요한 권한만 부여 (최소 권한 원칙)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: workflow-role
namespace: argo
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "patch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get", "watch"]
- apiGroups: ["argoproj.io"]
resources: ["workflows"]
verbs: ["get", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: workflow-role-binding
namespace: argo
subjects:
- kind: ServiceAccount
name: workflow-runner
roleRef:
kind: Role
name: workflow-role
apiGroup: rbac.authorization.k8s.io
# 워크플로우에서 ServiceAccount 지정
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: secure-workflow-
spec:
serviceAccountName: workflow-runner # 전용 SA 사용
securityContext:
runAsNonRoot: true # root 실행 금지
runAsUser: 1000
entrypoint: main
templates:
- name: main
container:
image: alpine
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
command: [sh, -c]
args: ["echo '보안 강화된 워크플로우 실행!'"]
External Secrets Operator - AWS Secrets Manager, Vault 등과 연동
Sealed Secrets - 암호화된 시크릿을 Git에 저장
Vault Agent Injector - HashiCorp Vault와 직접 연동
절대로 YAML 파일에 시크릿 값을 하드코딩하지 마!
항상 K8s Secret 참조나 외부 시크릿 관리 도구를 사용해야 해.
🔍 모니터링 & 트러블슈팅
파이프라인 만들었으면 잘 돌아가는지 봐야지!
그리고 뭔가 잘못됐을 때 빠르게 원인 찾는 법도 알아야 해.
# 워크플로우 상태 확인
argo list -n argo
argo list -n argo --status Failed # 실패한 것만
# 특정 워크플로우 상세 정보
argo get my-workflow-xyz -n argo
# 실시간 로그 확인
argo logs my-workflow-xyz -n argo --follow
# 특정 스텝 로그
argo logs my-workflow-xyz -n argo --node-id=my-workflow-xyz-build-123
# 워크플로우 재실행
argo resubmit my-workflow-xyz -n argo
# 실패한 스텝부터 재시작
argo retry my-workflow-xyz -n argo
# 워크플로우 중단
argo terminate my-workflow-xyz -n argo
# 완료된 워크플로우 삭제 (정리)
argo delete --completed -n argo
1. "Pending" 상태에서 멈춤
→ 리소스 부족이나 노드 셀렉터 문제. kubectl describe pod 로 이벤트 확인
2. "Error" 상태 - ImagePullBackOff
→ 이미지 이름/태그 오류 또는 레지스트리 인증 문제. imagePullSecrets 설정 확인
3. 아티팩트 업로드 실패
→ S3/GCS 권한 문제. IAM 역할이나 서비스 계정 권한 확인
4. 워크플로우 컨트롤러 메모리 부족
→ 동시 실행 워크플로우가 많을 때. controller의 resources.limits 증가
5. 파드 간 볼륨 공유 안 됨
→ ReadWriteOnce PVC는 같은 노드에서만 공유 가능. ReadWriteMany 스토리지 사용 고려
# 워크플로우 컨트롤러 로그 확인
kubectl logs -n argo deployment/workflow-controller -f
# 아르고 서버 로그
kubectl logs -n argo deployment/argo-server -f
# 특정 워크플로우 파드 직접 확인
kubectl get pods -n argo -l workflows.argoproj.io/workflow=my-workflow-xyz
kubectl describe pod -n argo my-workflow-xyz-build-123
kubectl logs -n argo my-workflow-xyz-build-123 -c main
🔗 Argo CD와 연동해서 GitOps 완성하기
Argo Workflows로 CI(빌드/테스트)를 처리하고,
Argo CD로 CD(배포)를 처리하면 진짜 완전한 GitOps 파이프라인이 완성돼!
이 조합이 요즘 쿠버네티스 환경에서 가장 많이 쓰이는 패턴이야.
1️⃣ 개발자가 앱 코드 푸시 → Argo Events가 감지
2️⃣ Argo Workflows가 CI 실행 (빌드 → 테스트 → 이미지 빌드 → 보안 스캔)
3️⃣ CI 성공 시 Argo Workflows가 Helm 차트 레포의 이미지 태그 업데이트
4️⃣ Argo CD가 Helm 차트 레포 변경 감지
5️⃣ Argo CD가 자동으로 쿠버네티스 클러스터에 배포
6️⃣ 배포 상태 Slack 알림!
# Argo Workflows에서 Helm 차트 레포 업데이트하는 스텝
- name: update-helm-chart
inputs:
parameters:
- name: new-image-tag
container:
image: alpine/git:latest
command: [sh, -c]
args:
- |
# Helm 차트 레포 클론
git clone https://github.com/myorg/helm-charts.git /tmp/helm-charts
cd /tmp/helm-charts
# 이미지 태그 업데이트 (yq 사용)
yq e '.image.tag = "{{inputs.parameters.new-image-tag}}"' \
-i charts/myapp/values.yaml
# 변경사항 커밋 및 푸시
git config user.email "argo-bot@myorg.com"
git config user.name "Argo Bot"
git add charts/myapp/values.yaml
git commit -m "chore: update myapp image to {{inputs.parameters.new-image-tag}}"
git push origin main
echo "Helm 차트 업데이트 완료! Argo CD가 자동 배포할 거야 🚀"
env:
- name: GIT_CREDENTIALS
valueFrom:
secretKeyRef:
name: git-credentials
key: token
이렇게 하면 Argo Workflows가 CI를 완료하고 Helm 차트를 업데이트하면,
Argo CD가 그 변경을 감지해서 자동으로 배포해줘.
완전 자동화된 GitOps 파이프라인 완성! 🎉
이런 자동화 파이프라인 구성 능력은 DevOps 엔지니어로서 진짜 핵심 스킬이야.
재능넷에서도 이런 DevOps 자동화 관련 재능을 찾는 수요가 꾸준히 있으니까 참고해봐!
✨ 베스트 프랙티스 총정리
완료된 워크플로우를 DB에 저장해서 히스토리 관리. PostgreSQL 연동 권장.
모든 컨테이너에 resources.requests/limits 설정. 클러스터 안정성 필수!
공통 로직은 WorkflowTemplate으로 분리. DRY(Don't Repeat Yourself) 원칙 준수.
완료된 워크플로우가 쌓이면 etcd 부하가 생겨. ttlStrategy로 자동 삭제 설정.
완료된 파드를 자동으로 삭제해서 클러스터 리소스 절약.
parallelism 설정으로 동시 실행 워크플로우/스텝 수 제한. 클러스터 과부하 방지.
보안상 DinD보다 Kaniko 사용 권장. 루트 권한 없이 이미지 빌드 가능.
latest 태그 사용 금지! 항상 특정 버전 태그 사용. 재현성 보장.
# TTL 및 PodGC 설정 예시
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: best-practice-
spec:
# 완료 후 24시간 뒤 자동 삭제
ttlStrategy:
secondsAfterCompletion: 86400
secondsAfterSuccess: 86400
secondsAfterFailure: 259200 # 실패는 3일 보관
# 완료된 파드 자동 삭제
podGC:
strategy: OnPodSuccess # OnPodCompletion, OnWorkflowCompletion, OnWorkflowSuccess
# 동시 실행 제한
parallelism: 5
entrypoint: main
templates:
- name: main
container:
image: alpine:3.18.5 # 정확한 버전 태그!
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
command: [echo]
args: ["베스트 프랙티스 적용 완료!"]
🎯 마무리 - Argo Workflows 도입 로드맵
자 여기까지 Argo Workflows로 배포 자동화 파이프라인 구성하는 법을 쭉 살펴봤어!
처음엔 좀 복잡해 보여도 하나씩 따라하다 보면 진짜 강력한 도구라는 걸 느끼게 될 거야.
도입 순서를 추천하자면 이렇게 해봐:
1주차: 로컬 환경(minikube/kind)에 Argo Workflows 설치, 기본 워크플로우 실습
2주차: DAG 워크플로우 작성, 아티팩트 관리 실습
3주차: WorkflowTemplate 작성, Argo Events 연동
4주차: 실제 프로젝트 파이프라인 구성, 보안 설정
5주차~: Argo CD 연동, GitOps 완성, 모니터링 구축
Argo Workflows는 단순한 CI/CD 도구를 넘어서 ML 파이프라인, 데이터 처리, 배치 작업 등
다양한 분야에서도 활용되고 있어. 한 번 익혀두면 정말 다양하게 써먹을 수 있는 도구야!
DevOps 자동화에 관심 있다면 재능넷에서 관련 전문가들의 도움을 받거나,
직접 재능을 공유하는 것도 좋은 방법이야. 이 분야 수요가 진짜 많거든!
쿠버네티스 환경에서 일한다면 Argo Workflows는 이제 선택이 아니라 필수야.
오늘 배운 내용으로 여러분의 배포 파이프라인을 한 단계 업그레이드해봐! 🚀
관련 키워드
댓글 0
지식인의 숲 - 지적 재산권 보호 고지
지적 재산권 보호 고지
- 저작권 및 소유권: 본 컨텐츠는 재능넷의 독점 AI 기술로 생성되었으며, 대한민국 저작권법 및 국제 저작권 협약에 의해 보호됩니다.
- AI 생성 컨텐츠의 법적 지위: 본 AI 생성 컨텐츠는 재능넷의 지적 창작물로 인정되며, 관련 법규에 따라 저작권 보호를 받습니다.
- 사용 제한: 재능넷의 명시적 서면 동의 없이 본 컨텐츠를 복제, 수정, 배포, 또는 상업적으로 활용하는 행위는 엄격히 금지됩니다.
- 데이터 수집 금지: 본 컨텐츠에 대한 무단 스크래핑, 크롤링, 및 자동화된 데이터 수집은 법적 제재의 대상이 됩니다.
- AI 학습 제한: 재능넷의 AI 생성 컨텐츠를 타 AI 모델 학습에 무단 사용하는 행위는 금지되며, 이는 지적 재산권 침해로 간주됩니다.

댓글 작성
이 글에 대한 여러분의 생각을 들려주세요
로그인이 필요합니다
댓글을 작성하려면 먼저 로그인해주세요.