LabHub
学习 学习路径 课程

GitLab CI/CD

出事时,没人知道生产环境上跑的是什么

在 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 규칙보다 앞에 있어야 합니다. 동결을 사람의 기억에 맡기지 않고 파이프라인이 거절하게 하는 장치입니다.