LabHub
배우기 러닝패스 코스

GitLab CI/CD

When the incident hit, nobody knew what was running in production

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

고정 환경과 브랜치별 리뷰 환경, 환경을 닫는 잡, 수동 운영 배포를 설정하고, 커밋 SHA 로 남는 배포 기록과 그 기록에서 고르는 롤백, 배포 동결 규칙을 gitlab-ci-local 로 실제로 돌립니다.

왜 중요한가

배포는 되돌릴 수 있어야 안전합니다. 무엇이 어디에 언제 나갔는지가 environment 로 남고, 나간 것이 커밋 SHA 로 식별돼야 사고 때 '그때의 것' 으로 몇 분 안에 돌아갈 수 있습니다. 리뷰 환경은 편하지만 닫는 짝이 없으면 쌓여서 비용과 노출이 되고, 운영 배포의 승인은 allow_failure 값 하나로 게이트가 되기도 장식이 되기도 합니다. 동결 기간처럼 '지금은 내보내지 않는다' 는 약속도 설정으로 새겨야 지켜집니다.

단계

  1. /root/glci-deploy 을 git 저장소로 만들고(.gitignore 에 .gitlab-ci-local/) .gitlab-ci.yml 에 stages [deploy, cleanup], 전역 변수 DEPLOY_LOG: /root/glci-deploy-history/deploys.log, 그리고 main 에서만 만들어지는 잡 deploy-staging(deploy)을 두세요. environment 는 이름 staging, URL https://staging.example.com 이고 스크립트는 echo "env=$CI_ENVIRONMENT_NAME url=$CI_ENVIRONMENT_URL" 입니다. main 에 커밋하고 실행하세요.
  2. main 이 아닌 브랜치에서만 만들어지는 잡 review-app(deploy)을 더하세요. environment 이름은 review/$CI_COMMIT_REF_SLUG, URL 은 https://$CI_COMMIT_REF_SLUG.review.example.com 이고 스크립트는 1단계와 같은 echo 입니다. 커밋하세요. 채점기는 사본에서 Feature/Login-Page 브랜치로 옮겨 이름이 review/feature-login-page, URL 이 https://feature-login-page.review.example.com 인지 봅니다.
  3. review-app 의 environment 에 on_stop: stop-reviewauto_stop_in: 1 day 를 더하고, 잡 stop-review(cleanup)를 만드세요. 같은 환경 이름에 action: stop, main 이 아닌 브랜치에서 when: manual, 스크립트는 echo "stopping $CI_ENVIRONMENT_NAME" 입니다. 커밋하세요. feature 브랜치에서 stop-review 는 목록에 manual 로 있고, --manual stop-review 로 돌리면 stopping review/<슬러그> 가 찍혀야 합니다.
  4. main 에서 when: manual, allow_failure: false 로 만들어지는 잡 deploy-prod(deploy)를 더하세요. environment 는 production, URL 은 https://www.example.com, 스크립트는 echo "env=$CI_ENVIRONMENT_NAME" 입니다. 커밋하세요. 그냥 실행하면 deploy-prod 는 돌지 않고, --manual deploy-prod 를 주면 돌아야 합니다.
  5. /root/glci-deploy/scripts/deploy.sh <환경> 을 만드세요. ROLLBACK_TO 가 비어 있으면 태그를 $CI_COMMIT_SHORT_SHA 로 정해 $DEPLOY_LOG<환경> registry.example.com/app:<태그> release 한 줄을 덧붙이고 deployed ... 를 출력합니다. deploy-staging·deploy-prod 의 스크립트 끝에 bash scripts/deploy.sh staging·bash scripts/deploy.sh production 을 더해 커밋하세요. 채점기는 사본에서 커밋 둘을 차례로 배포해 기록이 커밋마다 다른 태그로 남는지 봅니다.
  6. deploy.sh 가 ROLLBACK_TO 를 받으면 새 태그를 만들지 않고 그 태그를 다시 내보내게 하세요. 그 환경의 이력에 registry.example.com/app:<ROLLBACK_TO> 로 나간 기록이 없으면 이력에 없는 태그입니다 를 출력하고 실패합니다. 기록 줄의 마지막 낱말은 rollback 입니다. 채점기는 사본에서 두 번 배포한 뒤 첫 태그로 되돌리는 실행과, 없는 태그로 되돌리려는 실행을 합니다.
  7. deploy-prod 의 rules 맨 앞에 if: $CI_DEPLOY_FREEZE 이면 when: never 를 두세요. 커밋하세요. --variable CI_DEPLOY_FREEZE=1 로 목록을 보면 deploy-prod 가 없고, 변수가 없으면 manual 로 있어야 합니다. deploy-staging 은 동결과 관계없이 있어야 합니다.

참고

배포 잡에 이름표를 붙인다

/root/glci-deploy 을 git 저장소로 만들고(.gitignore 에 .gitlab-ci-local/) .gitlab-ci.yml 에 stages [deploy, cleanup], 전역 변수 DEPLOY_LOG: /root/glci-deploy-history/deploys.log, 그리고 main 에서만 만들어지는 잡 deploy-staging(deploy)을 두세요. environment 는 이름 staging, URL https://staging.example.com 이고 스크립트는 echo "env=$CI_ENVIRONMENT_NAME url=$CI_ENVIRONMENT_URL" 입니다. main 에 커밋하고 실행하세요.

environment 를 붙인 잡은 GitLab 에서 배포로 기록되고 환경 화면에 무엇이 언제 나갔는지 남습니다. 잡 안에서는 CI_ENVIRONMENT_NAME·CI_ENVIRONMENT_URL 로 자기가 어디로 가는지 알 수 있습니다.

브랜치마다 생기는 리뷰 환경

main 이 아닌 브랜치에서만 만들어지는 잡 review-app(deploy)을 더하세요. environment 이름은 review/$CI_COMMIT_REF_SLUG, URL 은 https://$CI_COMMIT_REF_SLUG.review.example.com 이고 스크립트는 1단계와 같은 echo 입니다. 커밋하세요. 채점기는 사본에서 Feature/Login-Page 브랜치로 옮겨 이름이 review/feature-login-page, URL 이 https://feature-login-page.review.example.com 인지 봅니다.

브랜치 이름에는 대문자·슬래시처럼 호스트 이름에 못 쓰는 글자가 섞입니다. CI_COMMIT_REF_SLUG 는 소문자로 바꾸고 허용되지 않는 글자를 하이픈으로 바꾼 값이라 URL·환경 이름에 그대로 쓸 수 있습니다.

리뷰 환경은 닫는 잡과 짝을 이룬다

review-app 의 environment 에 on_stop: stop-reviewauto_stop_in: 1 day 를 더하고, 잡 stop-review(cleanup)를 만드세요. 같은 환경 이름에 action: stop, main 이 아닌 브랜치에서 when: manual, 스크립트는 echo "stopping $CI_ENVIRONMENT_NAME" 입니다. 커밋하세요. feature 브랜치에서 stop-review 는 목록에 manual 로 있고, --manual stop-review 로 돌리면 stopping review/<슬러그> 가 찍혀야 합니다.

리뷰 환경은 브랜치 수만큼 쌓입니다. on_stop 은 '이 환경을 닫는 잡은 저것' 이라는 연결이고, 브랜치를 지우거나 병합하면 GitLab 이 그 잡을 부릅니다. auto_stop_in 은 잊힌 환경을 시간이 지나면 닫게 합니다.

운영 배포는 사람이 누른다

main 에서 when: manual, allow_failure: false 로 만들어지는 잡 deploy-prod(deploy)를 더하세요. environment 는 production, URL 은 https://www.example.com, 스크립트는 echo "env=$CI_ENVIRONMENT_NAME" 입니다. 커밋하세요. 그냥 실행하면 deploy-prod 는 돌지 않고, --manual deploy-prod 를 주면 돌아야 합니다.

allow_failure: false 인 수동 잡은 GitLab 에서 누를 때까지 파이프라인을 '차단됨' 으로 둡니다. true 면 누르지 않아도 파이프라인이 성공으로 끝나 승인 게이트가 되지 못합니다. gitlab-ci-local 은 이 차단을 재현하지 않으니 목록의 allowFailure 칸으로 확인합니다.

무엇이 나갔는지 커밋 SHA 로 남긴다

/root/glci-deploy/scripts/deploy.sh <환경> 을 만드세요. ROLLBACK_TO 가 비어 있으면 태그를 $CI_COMMIT_SHORT_SHA 로 정해 $DEPLOY_LOG<환경> registry.example.com/app:<태그> release 한 줄을 덧붙이고 deployed ... 를 출력합니다. deploy-staging·deploy-prod 의 스크립트 끝에 bash scripts/deploy.sh staging·bash scripts/deploy.sh production 을 더해 커밋하세요. 채점기는 사본에서 커밋 둘을 차례로 배포해 기록이 커밋마다 다른 태그로 남는지 봅니다.

latest 같은 움직이는 태그로 배포하면 사고가 났을 때 '그때 나간 것' 을 누구도 모릅니다. 커밋 SHA 는 소스·빌드·배포를 한 줄로 잇는 식별자입니다. 배포 기록 경로는 변수로 받아 실행할 때 바꿀 수 있게 둡니다.

되돌리기는 이력에 있는 태그를 다시 내보내는 것

deploy.sh 가 ROLLBACK_TO 를 받으면 새 태그를 만들지 않고 그 태그를 다시 내보내게 하세요. 그 환경의 이력에 registry.example.com/app:<ROLLBACK_TO> 로 나간 기록이 없으면 이력에 없는 태그입니다 를 출력하고 실패합니다. 기록 줄의 마지막 낱말은 rollback 입니다. 채점기는 사본에서 두 번 배포한 뒤 첫 태그로 되돌리는 실행과, 없는 태그로 되돌리려는 실행을 합니다.

되돌리기는 '옛날 소스로 다시 빌드' 가 아니라 '이미 검증돼 나갔던 산출물을 다시 내보내기' 입니다. 이력에 없는 태그를 막아야 오타 하나로 검증되지 않은 이미지가 운영에 나가지 않습니다. 실행할 때 --variable ROLLBACK_TO=<태그> 로 넘깁니다.

배포 동결 기간에는 운영 배포를 만들지 않는다

deploy-prod 의 rules 맨 앞에 if: $CI_DEPLOY_FREEZE 이면 when: never 를 두세요. 커밋하세요. --variable CI_DEPLOY_FREEZE=1 로 목록을 보면 deploy-prod 가 없고, 변수가 없으면 manual 로 있어야 합니다. deploy-staging 은 동결과 관계없이 있어야 합니다.

GitLab 은 프로젝트에 동결 기간을 설정해 두면 그 시간 동안 CI_DEPLOY_FREEZE 를 채워 줍니다. 규칙은 첫 매치만 쓰므로 동결 규칙이 main 규칙보다 앞에 있어야 합니다. 동결을 사람의 기억에 맡기지 않고 파이프라인이 거절하게 하는 장치입니다.