Monitoring

트래픽 조절하면서 Canary 배포 해보기 (used by Istio)

황동리 2025. 7. 24. 17:03
반응형

이번엔 트래픽을 조절하면서 Canary 배포를 해보겠습니다.

왜 Canary 배포 + 헤더 기반 트래픽 분기를 도입했는가?

  1. 기존 방식 (RollingUpdate)의 한계
  • 기존 배포 방식(RollingUpdate)은 새 버전의 파드를 조금씩 올리면서 기존 파드를 내림
  • 이 과정에서 사용자가 기존 파드에 연결되어 작업 중이면, 세션 끊김/진행 중인 요청 실패/예상치 못한 오류가 발생할 수 있음
  1. Canary 배포 전략 도입
  • 이러한 문제를 최소화하기 위해, 새 버전의 파드를 일부 트래픽에만 점진적으로 적용
  • 초기에 적은 비율만 새 버전으로 넘기고, 문제가 없는지 충분히 관찰
    → 장애/버그가 발생해도 전체 서비스에는 영향이 최소화
  1. 헤더 기반 트래픽 분기 활용
  • 추가로, 트래픽에 특정 조건(예: 헤더)을 걸어
    • 내부 테스트, 운영자, 베타 사용자 등 일부 그룹에게만 먼저 새 버전을 제공
    • 일반 사용자는 안정적인 기존 버전을 계속 이용 가능
  • 이 방식 덕분에
    • 신규 유저/특정 유저는 바로 새로운 버전 체험 가능
    • 기존 작업 중인 유저의 서비스 연속성 보장
    • 전체 배포로 전환하기 전까지 버그 및 문제를 사전 검증할 수 있음

4.결과적으로

  • 사용자 경험의 연속성을 지키면서
  • 운영 리스크를 줄이고
  • 배포/릴리즈의 유연성과 품질을 동시에 확보할 수 있음

이제 실습을 통해 좀 더 자세히 알아보겠습니다.

  1. 사전 준비
    먼저 nginx 를 사용해서 index.html 에서 Blue, Green이 표시 되도록 이미지 두 개를 생성해주도록 하겠습니다.
  • Blue_index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Blue</title>
    </head>
    <body>
      <h1>Blue</h1>
    </body>
    </html>
  • Blue_Dockerfile

    # 기본 이미지를 nginx:latest 로 사용
    FROM nginx:latest
    # 현재 디렉터리에 있는 index.html 파일을 컨테이너 내 /usr/share/nginx/index.html 위치에 복사
    COPY index.html /usr/share/nginx/html/index.html
    # 80 포트를 사용하겠다는 선언
    EXPOSE 80
    # nginx 서비스를 forground에서 실행
    CMD ["nginx", "-g", "daemon off;"]
  • Green_index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Green</title>
    </head>
    <body>
      <h1>Green</h1>
    </body>
    </html>
  • Green_Dockerfile

    # 기본 이미지를 nginx:latest 로 사용
    FROM nginx:latest
    # 현재 디렉터리에 있는 index.html 파일을 컨테이너 내 /usr/share/nginx/index.html 위치에 복사
    COPY index.html /usr/share/nginx/html/index.html
    # 80 포트를 사용하겠다는 선언
    EXPOSE 80
    # nginx 서비스를 forground에서 실행
    CMD ["nginx", "-g", "daemon off;"]

이제 각 Dockerfile을 빌드하여 Public 레포지토리에 넣어주면 됩니다. (저는 제 Dockerhub에 넣었습니다)


그리고 Istio 설치 및 Istio-ingressgateway 작업은 아래 링크를 보고 따라 하시면 됩니다.

Istio 설치
Istio ingressgateway 와 ALB 연결


이제 실습 사전 준비가 되었으니까 rollout 부터 설치해보도록 하겠습니다.

  1. Argo Rollout 설치

    helm repo add argo https://argoproj.github.io/argo-helm
    helm install my-argo-rollouts argo/argo-rollouts
  2. Rollout.yaml 작성

    apiVersion: argoproj.io/v1alpha1
    kind: Rollout
    metadata:
    name: kingsuhan
    namespace: default
    spec:
    replicas: 3
    revisionHistoryLimit: 2 # 배포 기간 동안 이전 버전의 파드의 개수
    selector:
    matchLabels:
      app: kingsuhan
    template:
    metadata:
      labels:
        app: kingsuhan
        version: blue  
    spec:
      containers:
      - name: kingsuhan
        image: kingsuhan/nginx:blue
        ports:
        - containerPort: 80
    strategy:
    canary:
      canaryService: kingsuhan-canary # 신규 배포된 애플리케이션과 연결될 서비스
      stableService: kingsuhan-stable # 기존 배포되었던 애플리케이션과 연결된 서비스
        trafficRouting: # 여기서 트래픽 라우팅은 istio로 정의
        istio:
          virtualService: 
            name: kingsuhan # 생성할 VirtualService의 이름
            routes:
            - primary # VirtualService에서 조정할 라우팅 이름
      steps: # 배포 비율 정의
      - setWeight: 10
      - pause: { duration: 2m }
      - setWeight: 50
      - pause: { duration: 3m }
      - setWeight: 100      
  3. 이제 Rollout과 연결될 서비스 생성

    apiVersion: v1
    kind: Service
    metadata:
    name: kingsuhan-stable
    namespace: default
    spec:
    selector:
     app: kingsuhan
    ports:
    - port: 80
     targetPort: 80
    apiVersion: v1
    kind: Service
    metadata:
    name: kingsuhan-canary
    namespace: default
    spec:
    selector:
     app: kingsuhan
    ports:
    - port: 80
     targetPort: 80
  4. Istio Gateway 리소스 정의

    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
    name: kingsuhan-gateway
    namespace: default
    spec:
    selector:
    istio: gateway
    servers:
    - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - "kingsuhan.outcode.ai" # ALB Ingress에서 정의한 hosts 명 넣으면 됩니다.
  5. VirtualService 정의

    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
    name: kingsuhan
    namespace: default
    spec:
    hosts:
    - kingsuhan.outcode.ai
    gateways:
    - kingsuhan-gateway
    http:
    - name: primary # 배포 될 때 rollout이 관리하는 라우팅 규칙
    route:
    - destination:
        host: kingsuhan-stable.default.svc.cluster.local
        port:
          number: 80
      weight: 100
    - destination:
        host: kingsuhan-canary.default.svc.cluster.local
        port:
          number: 80
      weight: 0
    - match: # 헤더 값에 "x-version: new" 가 존재하는 사용자들의 트래픽 조절
    - headers:
        x-version:
          exact: "new"
    route:
    - destination:
        host: kingsuhan-canary.default.svc.cluster.local
        port:
          number: 80
      weight: 100 # 새롭게 배포되는 서비스로만 트래픽이 가도록 설정
    - destination:
        host: kingsuhan-stable.default.svc.cluster.local
        port:
          number: 80
      weight: 0
  6. 결과 확인

    curl -H "x-version: new" https://kingsuhan.outcode.ai

헤더에 “x-version: new” 값을 넣고 호출을 하면,

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Green</title>
</head>
<body>
    <h1>Green</h1>
</body>
</html>

이렇게 새롭게 배포된 내용만 보이게 됩니다.


감사합니다.

반응형