AWS EKS에서 엔드투엔드 마이크로서비스 배포: Jenkins, Docker, Kubernetes 및 Argo CD를 활용한 CI/CD
Source: Dev.to
위의 링크에 있는 전체 글을 번역하려면, 번역하고자 하는 텍스트(본문)를 제공해 주세요.
본문을 그대로 복사해서 보내 주시면, 마크다운 형식과 코드 블록, URL은 그대로 유지하면서 한국어로 번역해 드리겠습니다.
사용자–주문 마이크로서비스 애플리케이션
아키텍처 개요
두 개의 서비스(User Service와 Order Service)로 구성된 간단한 시스템입니다. 두 서비스 모두 Spring Boot으로 구축하고 Docker 이미지로 패키징한 뒤 Amazon EKS 클러스터에 배포합니다.
프로젝트 구조 (학습용 모노레포)
microservices-project/
├─ user-service/
│ ├─ src/
│ └─ Dockerfile
├─ order-service/
│ ├─ src/
│ └─ Dockerfile
└─ k8s/
├─ db-deployment.yaml
├─ user-deployment.yaml
├─ order-deployment.yaml
└─ ingress.yaml
Spring Boot 예시 스니펫
@Entity
public class User {
@Id @GeneratedValue
private Long id;
// …
}
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public ResponseEntity create(@RequestBody User user) { … }
@GetMapping("/{id}")
public ResponseEntity get(@PathVariable Long id) { … }
@GetMapping("/health")
public String health() { return "OK"; }
}
@RestController
@RequestMapping("/orders")
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@PostMapping
public ResponseEntity create(@RequestBody Order order) {
// Call user service
String url = "http://user-service:8080/users/" + order.getUserId();
restTemplate.getForObject(url, User.class);
// …
}
}
빌드 – 두 서비스 도커화
# user-service/Dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/user-service.jar app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]
# order-service/Dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/order-service.jar app.jar
ENTRYPOINT ["java","-jar","/app/app.jar"]
# Build and tag images
mvn clean package -f user-service/pom.xml
docker build -t user-service:1.0 ./user-service
mvn clean package -f order-service/pom.xml
docker build -t order-service:1.0 ./order-service
쿠버네티스 – 데이터베이스 배포
# k8s/db-deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: postgres
spec:
ports:
- port: 5432
selector:
app: postgres
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: postgres
spec:
replicas: 1
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:13
env:
- name: POSTGRES_DB
value: microservices
- name: POSTGRES_USER
value: admin
- name: POSTGRES_PASSWORD
value: secret
ports:
- containerPort: 5432
쿠버네티스 – 사용자 서비스 배포
# k8s/user-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
spec:
replicas: 2
selector:
matchLabels:
app: user-service
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: user-service:1.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
selector:
app: user-service
ports:
- port: 8080
targetPort: 8080
Kubernetes – 주문 서비스 배포
# k8s/order-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 2
selector:
matchLabels:
app: order-service
template:
metadata:
labels:
app: order-service
spec:
containers:
- name: order-service
image: order-service:1.0
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
selector:
app: order-service
ports:
- port: 8080
targetPort: 8080
Ingress – 단일 진입점
# k8s/ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: microservices-ingress
spec:
rules:
- http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 8080
- path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 8080
전체 배포
kubectl apply -f k8s/
기능 증명 (중요 테스트)
curl -X POST http:///orders \
-H "Content-Type: application/json" \
-d '{"userId":1,"productId":42,"quantity":2}'
롤백 시나리오
# Revert to previous Git commit that contains the older manifests
git revert
# Argo CD (or kubectl) will apply the reverted state automatically
프로덕션‑급 개선
- 헬스 체크와 레디니스 프로브를 추가합니다.
- 리소스 제한 및 요청을 활성화합니다.
- 로깅/메트릭을 위한 사이드카 사용 (예: Prometheus exporter).
- 비밀을 AWS Secrets Manager 또는 KMS로 암호화된 Kubernetes Secrets에 저장합니다.
- 블루‑그린 또는 카나리 배포를 구현합니다 (아래 참고).
Part 2 – 전체 CI 파이프라인 (Jenkins)
pipeline {
agent any
environment {
REGISTRY = 'your-registry.io'
IMAGE_TAG = "${env.BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
git branch: 'main',
url: 'https://github.com/your-org/microservices-project.git'
}
}
stage('Build User Service') {
steps {
dir('user-service') {
sh 'mvn clean package'
}
}
}
stage('Build Order Service') {
steps {
dir('order-service') {
sh 'mvn clean package'
}
}
}
stage('Docker Build') {
steps {
sh '''
docker build -t $REGISTRY/user-service:$IMAGE_TAG user-service/
docker build -t $REGISTRY/order-service:$IMAGE_TAG order-service/
'''
}
}
stage('Docker Push') {
steps {
withDockerRegistry([credentialsId: 'dockerhub-creds', url: '']) {
sh '''
docker push $REGISTRY/user-service:$IMAGE_TAG
docker push $REGISTRY/order-service:$IMAGE_TAG
'''
}
}
}
stage('Update K8s Manifests Repo') {
steps {
sh '''
git clone https://github.com/your-org/k8s-manifests.git
cd k8s-manifests
sed -i "s|image:.*user-service.*|image: $REGISTRY/user-service:$IMAGE_TAG|" user-deployment.yaml
sed -i "s|image:.*order-service.*|image: $REGISTRY/order-service:$IMAGE_TAG|" order-deployment.yaml
git commit -am "Update images to $IMAGE_TAG"
git push
'''
}
}
}
}
파이프라인은 고전적인 흐름을 따릅니다: Trigger → Checkout → Build → Test (생략) → Docker Build → Docker Push → Update Git → CD via Argo CD.
Part 3 – Argo CD (GitOps 배포)
Install Argo CD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl port-forward svc/argocd-server -n argocd 8080:443
초기 admin 비밀번호 가져오기:
kubectl get secret argocd-initial-admin-secret -n argocd -o jsonpath="{.data.password}" | base64 -d
Connect Argo CD to the Manifests Repository
# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: microservices-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/k8s-manifests.git
targetRevision: HEAD
path: .
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
애플리케이션 적용:
kubectl apply -f application.yaml
Argo CD는 Git 매니페스트와 실시간 클러스터 상태를 지속적으로 조정하며, 롤아웃, 롤백 및 헬스 체크를 자동으로 처리합니다.
Automatic Operations
- Sync:
kubectl apply -f k8s/→ Argo CD가 드리프트를 감지하고 동기화합니다. - Rollback:
git revert→ Argo CD가 클러스터를 이전 상태로 되돌립니다. - Status:
kubectl get pods또는 Argo CD UI를 사용합니다.
Source: …
Argo CD를 활용한 블루‑그린 및 카나리 배포
블루‑그린 배포 (Zero Downtime)
- “그린” 배포(새 버전)를 기존 “블루” 배포와 함께 생성합니다.
# user-service-green.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-green
spec:
replicas: 2
selector:
matchLabels:
app: user-service
version: green
template:
metadata:
labels:
app: user-service
version: green
spec:
containers:
- name: user-service
image: your-registry.io/user-service:2.0
ports:
- containerPort: 8080
- 서비스 셀렉터는 초기에는 블루 버전을 가리키도록 설정합니다.
# user-service-service.yaml
apiVersion: v1
metadata:
name: user-service
spec:
selector:
app: user-service
version: blue # 트래픽 전환 시 "green"으로 변경
ports:
- port: 8080
targetPort: 8080
- Git에서 셀렉터를 업데이트하여 트래픽을 전환합니다.
selector:
app: user-service
version: green
- 커밋 & 푸시 → Argo CD가 동기화 → 포드 재시작 없이 즉시 트래픽이 이동합니다.
- 롤백: 셀렉터 변경을 되돌리기(
git revert) 하면 Argo CD가 블루 배포를 복구합니다.
카나리 배포 (점진적 롤아웃)
- 작은 복제본 수(예: 1 pod ≈ 10 % 트래픽)로 카나리 배포를 생성합니다.
# user-service-canary.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service-canary
spec:
replicas: 1
selector:
matchLabels:
app: user-service
version: canary
template:
metadata:
labels:
app: user-service
version: canary
spec:
containers:
- name: user-service
image: your-registry.io/user-service:2.0
ports:
- containerPort: 8080
- 서비스 셀렉터에 안정 버전과 카나리 포드 모두를 포함시킵니다(두 버전을 모두 매칭하는 라벨 셀렉터 사용).
selector:
app: user-service
-
Git에서 매니페스트를 편집하여 카나리 복제본 수를 점진적으로 늘립니다(예: 2 → 3 pod). Argo CD가 변경 사항을 적용합니다.
-
안정 버전으로 승격: 신뢰도가 충분히 높아지면 안정 배포의 이미지를 새 버전으로 교체하고 카나리 배포를 삭제합니다.
-
롤백: 카나리 복제본을 0으로 스케일 다운(
kubectl scale deployment user-service-canary --replicas=0)하거나 카나리를 도입한 Git 커밋을 되돌립니다.