GitLab Ops Server Poll-Release Runbook (운영 기준)¶
0. 전제¶
- 본 문서는
- 23-GitLab Ops Server Bootstrap
- 24-GitLab Ops Server Bootstrap Runbook 기준으로 bootstrap 이 완료된 운영 서버를 전제로 함.
poll-release 전체 흐름:
실행 요청
▼
runtime 환경 검증
▼
.env.{RELEASE_ENV} / .release-storage.env 로드
▼
deploy.lock 획득
▼
desired release state 조회
▼
metadata 검증
▼
현재 package_version 과 비교
▼
신규 release package 다운로드
▼
sha256 / tar safety 검증
▼
extract / stage / release promote
▼
after-prepare-release hook 실행
▼
current symlink activate
▼
release marker 작성
▼
PM2 delete/start/save
▼
current-release-state.json 갱신
▼
downloads / tmp / old releases cleanup
▼
최종 검증
운영 기준:
poll-release.sh는{SERVICE_USER}로 실행함- working directory 는
/var/www/{SERVICE_NAME}이어야 함 - Release Storage 는 MinIO/S3 compatible API 로 조회함
- 운영 서버는 GitLab 에 직접 접근하지 않음
- current symlink 만 mutable activate 지점으로 사용함
- release directory 는 immutable release archive 로 취급함
- GitLab CI/CD 의 release target branch 는
.ops/ci/release-branch-versions/{branch}.yaml기준으로 확장 가능함.
1. 빠른 상태 확인¶
poll-release 상태를 먼저 확인:
sudo systemctl status release-poll.{SERVICE_NAME}.timer -n 100 --no-pager
sudo systemctl status release-poll.{SERVICE_NAME}.service -n 100 --no-pager
sudo systemctl list-timers | grep release-poll
최근 실행 로그 확인:
sudo journalctl -u release-poll.{SERVICE_NAME}.service -n 100 --no-pager
sudo tail -n 100 /var/www/{SERVICE_NAME}/logs/poll-release.log
현재 활성 release 확인:
sudo readlink -f /var/www/{SERVICE_NAME}/current
sudo jq -r '.package_version // .version // empty' /var/www/{SERVICE_NAME}/shared/current-release-state.json
sudo -u {SERVICE_USER} -H pm2 status {SERVICE_NAME}
정상 기준:
- timer 상태가 active(waiting) 이어야 함
- service 는 oneshot 실행 후 inactive(dead) 일 수 있음
- current 는 releases/{PACKAGE_VERSION} 을 가리켜야 함
- current-release-state.json 의 package_version 이 current release 와 일치해야 함
- PM2 app 이 online 상태여야 함
2. Runtime 사전 확인¶
수동 실행 또는 장애 분석 전 runtime 구조를 확인:
/var/www/{SERVICE_NAME}/
├── current -> /var/www/{SERVICE_NAME}/releases/{PACKAGE_VERSION}
├── downloads/
├── logs/
│ └── poll-release.log
├── releases/
├── shared/
│ ├── poll-release-hooks/
│ │ └── 01-after-prepare-release.sh
│ ├── .env.pre-production
│ ├── .env.production
│ ├── .release-storage.env
│ ├── .service.env
│ └── current-release-state.json
├── tmp/
└── deploy.lock
runtime directory 확인:
sudo ls -ld /var/www/{SERVICE_NAME}
sudo ls -ld /var/www/{SERVICE_NAME}/downloads
sudo ls -ld /var/www/{SERVICE_NAME}/logs
sudo ls -l /var/www/{SERVICE_NAME}/logs/poll-release.log
sudo ls -ld /var/www/{SERVICE_NAME}/releases
sudo ls -ld /var/www/{SERVICE_NAME}/shared
sudo ls -ld /var/www/{SERVICE_NAME}/tmp
sudo ls -l /var/www/{SERVICE_NAME}/deploy.lock
sudo ls -l /opt/bin/poll-release.{SERVICE_NAME}.sh
shared directory 확인:
sudo ls -ld /var/www/{SERVICE_NAME}/shared/poll-release-hooks
sudo ls -l /var/www/{SERVICE_NAME}/shared/poll-release-hooks/01-after-prepare-release.sh
sudo ls -l /var/www/{SERVICE_NAME}/shared/.env.pre-production
sudo ls -l /var/www/{SERVICE_NAME}/shared/.env.production
sudo ls -l /var/www/{SERVICE_NAME}/shared/.release-storage.env
sudo ls -l /var/www/{SERVICE_NAME}/shared/.service.env
sudo ls -l /var/www/{SERVICE_NAME}/shared/current-release-state.json
3. systemd 자동 실행 확인¶
timer 기준으로 poll-release 자동 실행 여부를 확인:
sudo systemctl status release-poll.{SERVICE_NAME}.timer -n 100 --no-pager
sudo systemctl cat release-poll.{SERVICE_NAME}.timer --no-pager
service unit 확인:
sudo systemctl cat release-poll.{SERVICE_NAME}.service --no-pager
timer 재시작:
sudo systemctl restart release-poll.{SERVICE_NAME}.timer
sudo systemctl status release-poll.{SERVICE_NAME}.timer -n 100 --no-pager
sudo systemctl list-timers | grep release-poll
4. 수동 poll-release 실행¶
root로 직접 실행하지 않음/var/www/{SERVICE_NAME}에서 실행함
자동 timer 를 잠시 중지:
sudo systemctl stop release-poll.{SERVICE_NAME}.timer
runtime directory 로 이동:
cd /var/www/{SERVICE_NAME}
poll-release 를 서비스 사용자로 실행:
sudo -u {SERVICE_USER} -H env \
SERVICE_NAME={SERVICE_NAME} \
SERVICE_USER={SERVICE_USER} \
SERVICE_GROUP={SERVICE_GROUP} \
DEPLOY_BASE=/var/www/{SERVICE_NAME} \
RELEASE_ENV={RELEASE_ENV} \
/opt/bin/poll-release.{SERVICE_NAME}.sh
timer 를 재개:
sudo systemctl start release-poll.{SERVICE_NAME}.timer
sudo systemctl status release-poll.{SERVICE_NAME}.timer -n 100 --no-pager
sudo systemctl list-timers | grep release-poll
5. 실행 중 로그 확인¶
systemd journal 확인:
sudo journalctl -u release-poll.{SERVICE_NAME}.service -f
SyslogIdentifier 기준 확인:
sudo journalctl -t release-poll.{SERVICE_NAME} -f
file log 확인:
sudo tail -F /var/www/{SERVICE_NAME}/logs/poll-release.log
6. 정상 배포 결과 확인¶
current symlink 확인:
sudo readlink -f /var/www/{SERVICE_NAME}/current
release marker 확인:
PACKAGE_VERSION="$(sudo jq -r '.package_version // .version // empty' /var/www/{SERVICE_NAME}/shared/current-release-state.json)"
test -n "$PACKAGE_VERSION"
sudo ls -l /var/www/{SERVICE_NAME}/current/"$PACKAGE_VERSION"
state 확인:
sudo jq . /var/www/{SERVICE_NAME}/shared/current-release-state.json
PM2 확인:
sudo -u {SERVICE_USER} -H pm2 status {SERVICE_NAME}
sudo -u {SERVICE_USER} -H pm2 describe {SERVICE_NAME}
7. 수동 복구 기준¶
- 현재 스크립트는 자동 rollback 을 수행하지 않음.
- 수동 rollback 후 current-release-state.json 은 자동으로 되돌아가지 않음
- 필요 시 이전 release metadata 기준으로 state 를 수동 복구해야 함
현재 current 확인:
sudo readlink -f /var/www/{SERVICE_NAME}/current
release 목록 확인:
sudo ls -dt /var/www/{SERVICE_NAME}/releases/*
복구 대상 release 선택:
ROLLBACK_RELEASE=/var/www/{SERVICE_NAME}/releases/{PREVIOUS_PACKAGE_VERSION}
current symlink 수동 교체:
sudo rm -f /var/www/{SERVICE_NAME}/tmp/current.rollback.link
sudo ln -s "$ROLLBACK_RELEASE" /var/www/{SERVICE_NAME}/tmp/current.rollback.link
sudo mv -Tf /var/www/{SERVICE_NAME}/tmp/current.rollback.link /var/www/{SERVICE_NAME}/current
PM2 env 기준:
production -> production
pre-production -> pre_production
PM2 재시작:
sudo -u {SERVICE_USER} -H env SERVICE_NAME={SERVICE_NAME} /usr/bin/pm2 delete {SERVICE_NAME} || true
sudo -u {SERVICE_USER} -H env SERVICE_NAME={SERVICE_NAME} /usr/bin/pm2 start /var/www/{SERVICE_NAME}/current/ecosystem.config.cjs --env {PM2_ENV} --update-env
sudo -u {SERVICE_USER} -H /usr/bin/pm2 save --force
sudo -u {SERVICE_USER} -H /usr/bin/pm2 status {SERVICE_NAME}
주의:
- 수동 rollback 후 Release Storage 의
current/{RELEASE_ENV}.json이 여전히 최신 package_version 을 가리키고 있으면, 다음 polling cycle 에서 다시 최신 desired state 로 배포될 수 있음. - rollback 을 유지하려면 Release Storage current metadata 를 이전 release metadata 로 재지정하거나, 운영 서버의 timer 를 중지한 상태에서 원인 분석을 수행함.
8. Cleanup 확인¶
poll-release 성공 후 downloads 와 tmp 는 비워짐.
sudo find /var/www/{SERVICE_NAME}/downloads -mindepth 1 -maxdepth 1 -print
sudo find /var/www/{SERVICE_NAME}/tmp -mindepth 1 -maxdepth 1 -print
release retention 확인:
sudo ls -dt /var/www/{SERVICE_NAME}/releases/*
9. 보안 기준¶
운영 기준:
- 운영 서버는 Release Storage read-only 권한만 사용함
- 운영 서버는 GitLab 직접 접근 없이 Release Storage 만 조회함
- Release Storage credential 은
/var/www/{SERVICE_NAME}/shared/.release-storage.env에서 관리함 - shared env 파일은 Git 저장소에 포함하지 않음
- shared env 파일은 shell script 로 source 되므로 신뢰된 파일만 배치함
- shared env 파일 권한은
600기준으로 관리함 - poll-release runtime 은
{SERVICE_USER}로 실행함 - root 권한이 필요한 수동 명령은
sudo로 실행함 - package archive 는 extract 전 tar path safety 검사를 수행함
- package sha256 이 일치하지 않으면 activate 하지 않음
- cleanup 은 지정된 downloads/tmp 경로 하위만 삭제함