블루/그린 및 카나리 배포 구성하기

블루/그린 및 카나리 배포 구성하기 (Configure blue-green and canary deployments)

때로는 롤링 업데이트가 프로덕션에서 애플리케이션을 업데이트하는 데 필요한 유연성을 제공하지 못할 때가 있어요. 많은 조직에서 "카나리(canary)" 빌드를 프로덕션에 배치하거나, 다운타임을 최소화하면서 프로덕션으로의 안전한 애플리케이션 출시를 보장하기 위해 "블루/그린(blue/green)" 배포로 알려진 기법을 선호해요.

출처: 문서

본문

블루/그린 배포 (Blue/Green deployments)

블루/그린 배포는 레드/블랙(Red/Black)이나 A/B 등 여러 다른 이름으로도 불리지만, 개념은 일반적으로 동일해요. 블루/그린 배포에는 두 개의 애플리케이션 버전이 있어요. 한 버전에서 다음 버전으로 전환하는 단계를 제외하고는 한 번에 하나의 애플리케이션 버전만 활성화돼요. "활성(active)"이라는 용어는 대개 "트래픽을 받고 있음" 또는 "서비스 중"을 의미해요.

가상의 API 서버가 버전 1.3으로 프로덕션에 5개 인스턴스가 배포되어 있고, 이 서버를 버전 1.4로 안전하게 업데이트하려 한다고 상상해보세요. 버전 1.4의 새 인스턴스 5개를 만들고, 이들이 올바르게 작동한다면 승격(promote)한 다음 1.3을 실행 중인 5개 버전을 내리려고 해요. 실패할 경우에는 1.3으로 빠르게 롤백할 수 있어요.

먼저 프로덕션에서 실행 중인 작업을 살펴봐요:

job "docs" {
  # ...

  group "api" {
    count = 5

    update {
      max_parallel     = 1
      canary           = 5
      min_healthy_time = "30s"
      healthy_deadline = "10m"
      auto_revert      = true
      auto_promote     = false
    }

    task "api-server" {
      driver = "docker"

      config {
        image = "api-server:1.3"
      }
    }
  }
}

작업에 canary 수가 원하는 수(count)와 같은 update 스탠자가 있는 것을 주목하세요. 이를 통해 Nomad 작업이 블루/그린 배포를 모델링할 수 있어요. 작업을 "api-server:1.4" 이미지를 실행하도록 변경하면, Nomad는 원래 "api-server:1.3" 할당을 계속 실행하면서 새 할당 5개를 만들어요.

이미지를 변경해 새 버전을 실행하도록 하면 어떻게 작동하는지 관찰해 보세요:

@@ -2,6 +2,8 @@ job "docs" {
  group "api" {
    task "api-server" {
      config {
-       image = "api-server:1.3"
+       image = "api-server:1.4"

다음으로 이 변경 사항을 계획(plan)해요. 새 버전의 api-server가 포함된 수정된 jobspec을 docs.nomad.hcl이라는 파일 이름으로 저장해요.

$ nomad job plan docs.nomad.hcl
+/- Job: "docs"
+/- Task Group: "api" (5 canary, 5 ignore)
  +/- Task: "api-server" (forces create/destroy update)
    +/- Config {
      +/- image: "api-server:1.3" => "api-server:1.4"
        }

Scheduler dry-run:
- All tasks successfully allocated.

Job Modify Index: 7
To submit the job with version verification run:

nomad job run -check-index 7 docs.nomad.hcl

When running the job with the check-index flag, the job will only be run if the
job modify index given matches the server-side version. If the index has
changed, another user has modified the job and the plan's results are
potentially invalid.

변경 사항을 실행해요.

$ nomad job run docs.nomad.hcl
## ...

계획 출력에 따르면 Nomad가 "api-server:1.4" 이미지를 실행하는 카나리 5개를 만들고, 이전 이미지를 실행하는 모든 할당은 무시할 예정이에요. 이제 작업의 상태를 살펴보면 블루("api-server:1.3")와 그린("api-server:1.4") 세트가 모두 실행 중인 것을 볼 수 있어요.

$ nomad status docs
ID            = docs
Name          = docs
Submit Date   = 07/26/17 19:57:47 UTC
Type          = service
Priority      = 50
Datacenters   = dc1
Status        = running
Periodic      = false
Parameterized = false

Summary
Task Group  Queued  Starting  Running  Failed  Complete  Lost
api         0       0         10       0       0         0

Latest Deployment
ID          = 32a080c1
Status      = running
Description = Deployment is running but requires manual promotion

Deployed
Task Group  Auto Revert  Promoted  Desired  Canaries  Placed  Healthy  Unhealthy
api         true         false     5        5         5       5        0

Allocations
ID        Node ID   Task Group  Version  Desired  Status   Created At
6d8eec42  087852e2  api         1        run      running  07/26/17 19:57:47 UTC
7051480e  087852e2  api         1        run      running  07/26/17 19:57:47 UTC
36c6610f  087852e2  api         1        run      running  07/26/17 19:57:47 UTC
410ba474  087852e2  api         1        run      running  07/26/17 19:57:47 UTC
85662a7a  087852e2  api         1        run      running  07/26/17 19:57:47 UTC
3ac3fe05  087852e2  api         0        run      running  07/26/17 19:53:56 UTC
4bd51979  087852e2  api         0        run      running  07/26/17 19:53:56 UTC
2998387b  087852e2  api         0        run      running  07/26/17 19:53:56 UTC
35b813ee  087852e2  api         0        run      running  07/26/17 19:53:56 UTC
b53b4289  087852e2  api         0        run      running  07/26/17 19:53:56 UTC

이제 새 버전이 프로덕션에서 실행되고 있으므로, 트래픽을 이 버전으로 라우팅하고 올바르게 작동하는지 검증할 수 있어요. 그렇다면 배포를 승격(promote)하고 Nomad가 이전 버전을 실행하는 할당을 중지하도록 해요. 그렇지 않다면 실행 중인 컨테이너 중 하나를 문제 해결하거나, 배포를 실패시켜 새 컨테이너를 파괴해요.

배포 승격하기 (Promote the deployment)

새 이미지를 이전 버전과 함께 배포한 후 정상 작동한다고 판단했고, 새 버전으로 완전히 전환하고 싶다면 배포를 승격하기만 하면 돼요:

$ nomad deployment promote 32a080c1
==> Monitoring evaluation "61ac2be5"
    Evaluation triggered by job "docs"
    Evaluation within deployment: "32a080c1"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "61ac2be5" finished with status "complete"

작업의 상태를 검사하면 승격 후 Nomad가 이전 할당을 중지하고 새 할당만 실행하는 것을 볼 수 있어요. 이로써 블루/그린 배포가 완료돼요.

$ nomad status docs
ID            = docs
Name          = docs
Submit Date   = 07/26/17 19:57:47 UTC
Type          = service
Priority      = 50
Datacenters   = dc1
Status        = running
Periodic      = false
Parameterized = false

Summary
Task Group  Queued  Starting  Running  Failed  Complete  Lost
api         0       0         5        0       5         0

Latest Deployment
ID          = 32a080c1
Status      = successful
Description = Deployment completed successfully

Deployed
Task Group  Auto Revert  Promoted  Desired  Canaries  Placed  Healthy  Unhealthy
api         true         true      5        5         5       5        0

Allocations
ID        Node ID   Task Group  Version  Desired  Status    Created At
6d8eec42  087852e2  api         1        run      running   07/26/17 19:57:47 UTC
7051480e  087852e2  api         1        run      running   07/26/17 19:57:47 UTC
36c6610f  087852e2  api         1        run      running   07/26/17 19:57:47 UTC
410ba474  087852e2  api         1        run      running   07/26/17 19:57:47 UTC
85662a7a  087852e2  api         1        run      running   07/26/17 19:57:47 UTC
3ac3fe05  087852e2  api         0        stop     complete  07/26/17 19:53:56 UTC
4bd51979  087852e2  api         0        stop     complete  07/26/17 19:53:56 UTC
2998387b  087852e2  api         0        stop     complete  07/26/17 19:53:56 UTC
35b813ee  087852e2  api         0        stop     complete  07/26/17 19:53:56 UTC
b53b4289  087852e2  api         0        stop     complete  07/26/17 19:53:56 UTC

배포 실패시키기 (Fail a deployment)

새 이미지를 이전 버전과 함께 배포한 후 정상 작동하지 않는다고 판단했고, 이전 버전으로 롤백하고 싶다면 배포를 실패시키기만 하면 돼요:

$ nomad deployment fail 32a080c1
Deployment "32a080c1-de5a-a4e7-0218-521d8344c328" failed. Auto-reverted to job version 0.

==> Monitoring evaluation "6840f512"
    Evaluation triggered by job "example"
    Evaluation within deployment: "32a080c1"
    Allocation "0ccb732f" modified: node "36e7a123", group "cache"
    Allocation "64d4f282" modified: node "36e7a123", group "cache"
    Allocation "664e33c7" modified: node "36e7a123", group "cache"
    Allocation "a4cb6a4b" modified: node "36e7a123", group "cache"
    Allocation "fdd73bdd" modified: node "36e7a123", group "cache"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "6840f512" finished with status "complete"

배포를 실패시킨 후 작업의 상태를 확인해요. Nomad가 새 할당을 중지하고 이전 할당만 실행 중인지, 그리고 작업의 작업 복사본이 "api-server:1.3"을 실행하는 원래 명세로 되돌아갔는지 확인해요.

$ nomad status docs
ID            = docs
Name          = docs
Submit Date   = 07/26/17 19:57:47 UTC
Type          = service
Priority      = 50
Datacenters   = dc1
Status        = running
Periodic      = false
Parameterized = false

Summary
Task Group  Queued  Starting  Running  Failed  Complete  Lost
api         0       0         5        0       5         0

Latest Deployment
ID          = 6f3f84b3
Status      = successful
Description = Deployment completed successfully

Deployed
Task Group  Auto Revert  Desired  Placed  Healthy  Unhealthy
cache       true         5        5       5        0

Allocations
ID        Node ID   Task Group  Version  Desired  Status    Created At
27dc2a42  36e7a123  api         1        stop     complete  07/26/17 20:07:31 UTC
5b7d34bb  36e7a123  api         1        stop     complete  07/26/17 20:07:31 UTC
983b487d  36e7a123  api         1        stop     complete  07/26/17 20:07:31 UTC
d1cbf45a  36e7a123  api         1        stop     complete  07/26/17 20:07:31 UTC
d6b46def  36e7a123  api         1        stop     complete  07/26/17 20:07:31 UTC
0ccb732f  36e7a123  api         2        run      running   07/26/17 20:06:29 UTC
64d4f282  36e7a123  api         2        run      running   07/26/17 20:06:29 UTC
664e33c7  36e7a123  api         2        run      running   07/26/17 20:06:29 UTC
a4cb6a4b  36e7a123  api         2        run      running   07/26/17 20:06:29 UTC
fdd73bdd  36e7a123  api         2        run      running   07/26/17 20:06:29 UTC



$ nomad job deployments docs
ID        Job ID   Job Version  Status      Description
6f3f84b3  example  2            successful  Deployment completed successfully
32a080c1  example  1            failed      Deployment marked as failed - rolling back to job version 0
c4c16494  example  0            successful  Deployment completed successfully

카나리로 배포하기 (Deploy with canaries)

카나리 업데이트는 롤링 업데이트를 시작하기 전에 작업의 새 버전을 테스트하는 유용한 방법이에요. update 스탠자는 canary 매개변수를 통해 작업이 변경될 때 작업 운영자가 Nomad가 만들도록 원하는 카나리 수를 설정하는 것을 지원해요. 작업 명세가 업데이트되면 Nomad는 이전 작업의 어떤 할당도 중지하지 않고 카나리를 만들어요.

이 패턴을 사용하면 운영자가 트래픽을 라우팅하고 로그를 살펴보는 등 새 애플리케이션이 올바르게 작동하는지 확인할 수 있으므로 새 작업 버전에 대한 더 높은 확신을 얻을 수 있어요.

job "docs" {
  # ...

  group "api" {
    count = 5

    update {
      max_parallel     = 1
      canary           = 1
      min_healthy_time = "30s"
      healthy_deadline = "10m"
      auto_revert      = true
      auto_promote     = false
    }

    task "api-server" {
      driver = "docker"

      config {
        image = "api-server:1.3"
      }
    }
  }
}

위 예제에서 update 스탠자는 작업 명세가 변경될 때 단일 카나리를 만들도록 Nomad에 지시해요.

이미지를 변경해 새 버전을 실행하도록 하면 이 동작을 경험할 수 있어요:

@@ -2,6 +2,8 @@ job "docs" {
  group "api" {
    task "api-server" {
      config {
-       image = "api-server:1.3"
+       image = "api-server:1.4"

다음으로 이 변경 사항을 계획해요.

$ nomad job plan docs.nomad.hcl
+/- Job: "docs"
+/- Task Group: "api" (1 canary, 5 ignore)
  +/- Task: "api-server" (forces create/destroy update)
    +/- Config {
      +/- image: "api-server:1.3" => "api-server:1.4"
        }

Scheduler dry-run:
- All tasks successfully allocated.

Job Modify Index: 7
To submit the job with version verification run:

nomad job run -check-index 7 docs.nomad.hcl

When running the job with the check-index flag, the job will only be run if the
job modify index given matches the server-side version. If the index has
changed, another user has modified the job and the plan's results are
potentially invalid.

$ nomad job run docs.nomad.hcl
# ...

변경 사항을 실행해요.

$ nomad job run docs.nomad.hcl
## ...

계획 출력에서 Nomad가 "api-server:1.4" 이미지를 실행하는 카나리 하나를 만들고 이전 이미지를 실행하는 모든 할당을 무시한다는 점을 주목하세요. 작업을 실행한 후 nomad status 명령 출력은 카나리가 이전 버전의 작업과 함께 실행 중임을 보여줘요:

$ nomad status docs
ID            = docs
Name          = docs
Submit Date   = 07/26/17 19:57:47 UTC
Type          = service
Priority      = 50
Datacenters   = dc1
Status        = running
Periodic      = false
Parameterized = false

Summary
Task Group  Queued  Starting  Running  Failed  Complete  Lost
api         0       0         6        0       0         0

Latest Deployment
ID          = 32a080c1
Status      = running
Description = Deployment is running but requires manual promotion

Deployed
Task Group  Auto Revert  Promoted  Desired  Canaries  Placed  Healthy  Unhealthy
api         true         false     5        1         1       1        0

Allocations
ID        Node ID   Task Group  Version  Desired  Status   Created At
85662a7a  087852e2  api         1        run      running  07/26/17 19:57:47 UTC
3ac3fe05  087852e2  api         0        run      running  07/26/17 19:53:56 UTC
4bd51979  087852e2  api         0        run      running  07/26/17 19:53:56 UTC
2998387b  087852e2  api         0        run      running  07/26/17 19:53:56 UTC
35b813ee  087852e2  api         0        run      running  07/26/17 19:53:56 UTC
b53b4289  087852e2  api         0        run      running  07/26/17 19:53:56 UTC

이제 카나리를 승격하면 이전 이미지를 실행하는 나머지 할당을 교체하는 롤링 업데이트가 트리거돼요. 롤링 업데이트는 max_parallel의 속도로 진행되므로 이 경우 한 번에 하나의 할당이 교체돼요.

$ nomad deployment promote 37033151
==> Monitoring evaluation "37033151"
    Evaluation triggered by job "docs"
    Evaluation within deployment: "ed28f6c2"
    Allocation "f5057465" created: node "f6646949", group "cache"
    Allocation "f5057465" status changed: "pending" -> "running"
    Evaluation status changed: "pending" -> "complete"
==> Evaluation "37033151" finished with status "complete"

상태를 확인해요.

$ nomad status docs
ID            = docs
Name          = docs
Submit Date   = 07/26/17 20:28:59 UTC
Type          = service
Priority      = 50
Datacenters   = dc1
Status        = running
Periodic      = false
Parameterized = false

Summary
Task Group  Queued  Starting  Running  Failed  Complete  Lost
api         0       0         5        0       2         0

Latest Deployment
ID          = ed28f6c2
Status      = running
Description = Deployment is running

Deployed
Task Group  Auto Revert  Promoted  Desired  Canaries  Placed  Healthy  Unhealthy
api         true         true      5        1         2       1        0

Allocations
ID        Node ID   Task Group  Version  Desired  Status    Created At
f5057465  f6646949  api         1        run      running   07/26/17 20:29:23 UTC
b1c88d20  f6646949  api         1        run      running   07/26/17 20:28:59 UTC
1140bacf  f6646949  api         0        run      running   07/26/17 20:28:37 UTC
1958a34a  f6646949  api         0        run      running   07/26/17 20:28:37 UTC
4bda385a  f6646949  api         0        run      running   07/26/17 20:28:37 UTC
62d96f06  f6646949  api         0        stop     complete  07/26/17 20:28:37 UTC
f58abbb2  f6646949  api         0        stop     complete  07/26/17 20:28:37 UTC

또는 카나리가 제대로 작동하지 않았다면 블루/그린 예제와 유사하게 nomad deployment fail 명령을 사용해 변경 사항을 중단할 수 있어요.

더 알아보기 (Learn more)