블루/그린 및 카나리 배포 구성하기
블루/그린 및 카나리 배포 구성하기 (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 명령을 사용해 변경 사항을 중단할 수 있어요.