GitLab Ops Server Poll-Release¶
0. 전제¶
- 본 문서는
- 23-GitLab Ops Server Bootstrap
- 24-GitLab Ops Server Bootstrap Runbook
기준으로 bootstrap 이 완료된 운영 서버에서 동작하는
poll-release.sh의 구조, 정책, 실행 흐름, 검증 기준, 장애 대응 기준을 정의함.
poll-release.sh 는 Release Storage 에 publish 된 desired release state 를 기준으로 운영 서버가 self-pull deploy 를 수행하는 runtime script 임.
poll-release 전체 흐름:
systemd timer 또는 수동 실행
▼
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}로 실행함poll-release.sh는 root 로 직접 실행하지 않음- working directory 는
/var/www/{SERVICE_NAME}이어야 함 - Release Storage 는 MinIO/S3 compatible API 로 조회함
- 운영 서버는 GitLab 에 직접 접근하지 않음
- 운영 서버는 build 를 수행하지 않음
- release package 는 CI/CD 단계에서 이미 실행 가능한 상태로 생성되어야 함
- current symlink 만 mutable activate 지점으로 사용함
- release directory 는 immutable release archive 로 취급함
- 현재 스크립트는 별도 HTTP health check 와 자동 rollback 을 수행하지 않음
1. 목적¶
poll-release.sh 의 목적은 운영 서버의 현재 상태를 Release Storage 에 선언된 desired state 로 수렴시키는 것임.
GitLab CI/CD 는 release package 와 release metadata 를 publish 하고, 운영 서버는 해당 metadata 를 polling 하여 필요한 경우 self-pull deploy 를 수행함.
운영 기준:
- GitLab 은 deploy executor 가 아님
- GitLab 은 release state 를 선언함
- 운영 서버는 desired state 를 polling 함
- 운영 서버는 desired state 기준으로 직접 deploy 를 수행함
- 동일
package_version이 이미 적용된 경우 deploy 를 수행하지 않음 - partially deployed 상태를 정상 상태로 간주하지 않음
current-release-state.json은 성공적으로 activate 된 release 기준으로만 갱신함
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 path 변수:
DEPLOY_BASE=/var/www/{SERVICE_NAME}
RELEASES_PATH=$DEPLOY_BASE/releases
SHARED_PATH=$DEPLOY_BASE/shared
CURRENT_LINK=$DEPLOY_BASE/current
DOWNLOAD_PATH=$DEPLOY_BASE/downloads
TMP_PATH=$DEPLOY_BASE/tmp
LOG_PATH=$DEPLOY_BASE/logs
LOCK_FILE=$DEPLOY_BASE/deploy.lock
STATE_FILE=$SHARED_PATH/current-release-state.json
POLL_LOG_FILE=$LOG_PATH/poll-release.log
SERVICE_ENV_FILE=$SHARED_PATH/.service.env
RELEASE_STORAGE_ENV_FILE=$SHARED_PATH/.release-storage.env
ENV_TARGET_FILE=$SHARED_PATH/.env.{RELEASE_ENV}
POLL_RELEASE_HOOKS_PATH=$SHARED_PATH/poll-release-hooks
AFTER_PREPARE_RELEASE_HOOK=$POLL_RELEASE_HOOKS_PATH/01-after-prepare-release.sh
각 directory 역할:
current: 현재 활성 release 를 가리키는 symlink 임releases: immutable release directory 를 보관함downloads: release package 다운로드 임시 보관 영역임tmp: desired state, tar list, extract, stage, temporary symlink 작업 영역임logs: poll-release file log 저장 영역임shared: release-independent env/state/hook 저장 영역임deploy.lock: bootstrap 과 poll-release 실행을 직렬화하기 위한 lock file 임
3. systemd 실행 구조¶
poll-release 는 systemd service + timer 기반으로 실행함.
systemd service 기준:
User={SERVICE_USER}
Group={SERVICE_GROUP}
WorkingDirectory=/var/www/{SERVICE_NAME}
Environment=SERVICE_NAME={SERVICE_NAME}
Environment=SERVICE_USER={SERVICE_USER}
Environment=SERVICE_GROUP={SERVICE_GROUP}
Environment=DEPLOY_BASE=/var/www/{SERVICE_NAME}
Environment=RELEASE_ENV={RELEASE_ENV}
ExecStart=/opt/bin/poll-release.{SERVICE_NAME}.sh
timer 기준:
OnBootSec=1m
OnUnitActiveSec=1m
RandomizedDelaySec=15s
AccuracySec=5s
Persistent=true
운영 기준:
- polling 은 systemd timer 기반으로 수행함
- service 는
Type=oneshot형태임 - polling script 는 resident daemon 으로 동작하지 않음
- timer enable/start 대상은 service 가 아니라 timer 임
- 다중 서버 환경에서는
RandomizedDelaySec로 polling 시점을 분산함
4. Runtime 환경 변수¶
필수 환경 변수:
SERVICE_NAME
SERVICE_USER
SERVICE_GROUP
DEPLOY_BASE
RELEASE_ENV
검증 기준:
SERVICE_NAME은 비어 있으면 안 됨SERVICE_USER는 비어 있으면 안 됨SERVICE_GROUP은 비어 있으면 안 됨DEPLOY_BASE는 비어 있으면 안 됨RELEASE_ENV는 비어 있으면 안 됨SERVICE_NAME,SERVICE_USER,SERVICE_GROUP은 식별자 허용 문자 기준을 통과해야 함
허용 문자 기준:
A-Z a-z 0-9 . _ -
RELEASE_ENV 허용값:
production
pre-production
PM2 env 매핑:
production -> production
pre-production -> pre_production
운영 기준:
- systemd unit 의
RELEASE_ENV값과/var/www/{SERVICE_NAME}/shared/.env.{RELEASE_ENV}내부의RELEASE_ENV값은 일치해야 함 .env.{RELEASE_ENV}파일이 없으면 polling cycle 을 skip 함.env.{RELEASE_ENV}로드 후RELEASE_ENV값이 요청값과 다르면 실패함- GitLab CI/CD 의 release target branch 는
.ops/ci/release-branch-versions/{branch}.yaml기준으로 확장 가능함.
5. Release Storage 정책¶
운영 서버는 Release Storage 를 MinIO/S3 compatible API 로 조회함.
Release Storage env 파일:
/var/www/{SERVICE_NAME}/shared/.release-storage.env
필수 변수:
MINIO_ENDPOINT
MINIO_REGION
MINIO_ACCESS_KEY
MINIO_SECRET_KEY
MINIO_BUCKET
MINIO_SERVICE_PREFIX
desired release state object:
s3://{MINIO_BUCKET}/{SERVICE_PREFIX}/{SERVICE_NAME}/{RELEASE_ENV}/current/{RELEASE_ENV}.json
예시:
s3://{MINIO_BUCKET}/{SERVICE_PREFIX}/{SERVICE_NAME}/production/current/production.json
s3://{MINIO_BUCKET}/{SERVICE_PREFIX}/{SERVICE_NAME}/pre-production/current/pre-production.json
조회 방식:
AWS_ACCESS_KEY_ID="$MINIO_ACCESS_KEY" \
AWS_SECRET_ACCESS_KEY="$MINIO_SECRET_KEY" \
AWS_DEFAULT_REGION="$MINIO_REGION" \
AWS_EC2_METADATA_DISABLED=true \
aws --endpoint-url "$MINIO_ENDPOINT" \
s3 cp \
"s3://$MINIO_BUCKET/$RELEASE_STATE_OBJECT" \
"$DESIRED_STATE_FILE"
운영 기준:
MINIO_ENDPOINT는 API endpoint 이며, desired release state object path 자체에 포함하지 않음- 운영 서버는 public HTTP URL 이 아니라 MinIO/S3 compatible API 를 통해 desired release state 를 조회함
6. Release Metadata 기준¶
poll-release 는 desired state JSON 에서 다음 필드를 읽음.
주요 필드:
target_env
project_name
service_prefix
service_name
declared_version
package_version
package_name
package_path
package_url
package_sha256
package_size_bytes
release_storage.endpoint
release_storage.bucket
metadata_path
current_metadata_path
commit_sha
commit_short_sha
pipeline_id
필수 검증:
target_env 비어 있지 않아야 함
target_env == RELEASE_ENV
project_name 비어 있지 않아야 함
project_name == PROJECT_NAME == MINIO_BUCKET
service_prefix 비어 있지 않아야 함
service_prefix == SERVICE_PREFIX
service_name 비어 있지 않아야 함
service_name == SERVICE_NAME
package_version 비어 있지 않아야 함
package_version path-safe 식별자여야 함
package_sha256 비어 있지 않아야 함
package_sha256 64자리 hex 값이어야 함
package_path 또는 package_url 중 하나는 Release Storage object path 로 제공되어야 함
선택 검증:
project_name 이 있으면 release_storage.bucket 또는 MINIO_BUCKET 과 일치해야 함
service_prefix 가 있으면 MINIO_SERVICE_PREFIX 에서 마지막 `/{SERVICE_NAME}` 을 제외한 값과 일치해야 함
release_storage.bucket 이 있으면 MINIO_BUCKET 과 일치해야 함
path safety 기준:
package_version 허용 문자: A-Z a-z 0-9 . _ -
package_name 은 basename 이어야 함
package_path 는 relative storage path 여야 함
package_url 은 http/https URL 이면 실패함
운영 기준:
package_version은 immutable deploy identifier 로 사용함declared_version은 semantic version / release label 용도로 사용함package_path는 운영 서버가 다운로드할 Release Storage object path 임package_path를 package download 기준으로 우선 사용함package_sha256은 실제 package tarball 의 SHA256 hash 값임- 운영 서버는 metadata 내부의
package_sha256값을 기준으로 다운로드한 package 를 검증함 package_sha256_path는 checksum file 의 Release Storage object path metadata 임package_sha256_path는 감사, 추적, 수동 검증 용도로 유지함package_sha256과package_sha256_path는 의미가 다름package_url은 legacy / fallback metadata 로 취급함- 현재
poll-release.sh기준package_url이http://또는https://로 시작하면 실패함 - 운영 서버는 public HTTP URL 이 아니라 MinIO/S3 compatible API 로 package 를 다운로드함
source.gitlab_package_registry는 CI/CD source metadata 이며, 운영 서버 deploy download 기준은 Release Storage 임- 운영 서버는
source.gitlab_package_registry값을 deploy download 에 사용하지 않음 - metadata 의
service_prefix는 GitLab 문서 변수SERVICE_PREFIX단독값임 MINIO_SERVICE_PREFIX는 Release Storage object prefix 전체값이며{SERVICE_PREFIX}/{SERVICE_NAME}조합값임
7. Desired State / Current State¶
운영 서버는 마지막으로 성공적으로 적용한 release state 를 local state 로 유지함.
local state file:
/var/www/{SERVICE_NAME}/shared/current-release-state.json
비교 기준:
CURRENT_VERSION="$(jq -r '.package_version // .version // empty' "$STATE_FILE" 2>/dev/null || true)"
동작 기준:
desired package_version 이 비어 있으면 polling cycle skip
current package_version == desired package_version 이면 polling cycle skip
current package_version != desired package_version 이면 deploy 진행
운영 기준:
current-release-state.json은 마지막 성공 deploy 의 desired state JSON 을 복사한 파일임- 동일
package_version은 재배포하지 않음 current-release-state.json이{}상태이면 최초 deploy 대상으로 판단함- local state 는 PM2 reload/start 성공 이후에만 갱신함
8. Deploy Lock 기준¶
poll-release 는 deploy 중복 실행을 방지하기 위해 flock 기반 lock 을 사용함.
lock file:
/var/www/{SERVICE_NAME}/deploy.lock
동작:
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
skip "Another bootstrap/poll-release process is already running. skipped."
fi
운영 기준:
- 동일 서비스의 bootstrap 과 poll-release 는 동시에 실행되면 안 됨
- 동일 서비스의 poll-release cycle 은 동시에 실행되면 안 됨
- lock 획득 실패는 장애가 아니라 현재 polling cycle skip 으로 처리함
- lock 은 process lifetime 동안 유지됨
- timer interval 보다 deploy 시간이 길어도 중복 deploy 를 방지함
9. Package Download / Verify 기준¶
package download path:
/var/www/{SERVICE_NAME}/downloads/{PACKAGE_NAME}
temporary download path:
/var/www/{SERVICE_NAME}/tmp/{PACKAGE_NAME}.tmp
package object:
s3://{MINIO_BUCKET}/{PACKAGE_DOWNLOAD_OBJECT}
동작 기준:
1. downloads/{PACKAGE_NAME} 이 이미 존재하는지 확인함
2. 기존 package 가 있으면 sha256 을 계산함
3. 기존 package sha256 이 expected sha256 과 같으면 download 를 skip 함
4. 기존 package sha256 이 다르면 기존 package 를 삭제하고 다시 다운로드함
5. tmp file 로 package 를 다운로드함
6. 다운로드 파일이 비어 있으면 실패함
7. tmp file sha256 을 검증함
8. sha256 이 일치하면 downloads/{PACKAGE_NAME} 으로 mv 함
9. package file 을 600 권한으로 설정함
sha256 기준:
expected sha256 = desired state 의 package_sha256
actual sha256 = sha256sum 으로 계산한 tarball hash
운영 기준:
- package 다운로드는 MinIO/S3 compatible API 로 수행함
- public HTTP download 는 현재 스크립트 기준으로 허용하지 않음
- sha256 검증 실패 시 deploy 를 중단함
- sha256 검증 실패 시 current symlink 와 current-release-state 는 변경하지 않음
- 다운로드된 package file 은 deploy 완료 후 cleanup 단계에서 삭제됨
10. Tar Archive Safety / Extract 기준¶
extract path:
/var/www/{SERVICE_NAME}/tmp/extract.{PACKAGE_VERSION}
tar list file:
/var/www/{SERVICE_NAME}/tmp/package-tar-list.{PACKAGE_VERSION}.txt
검증 기준:
tar -tzf 가능해야 함
archive 내부 path 가 absolute path 이면 실패함
archive 내부 path 에 .. path traversal 이 있으면 실패함
extract 결과가 비어 있으면 실패함
unsafe path 조건:
^/
(^|/)\.\.(/|$)
운영 기준:
- tar archive 는 gzip tarball 기준임
- archive safety 검증 실패 시 deploy 를 중단함
- extract 실패 시 current symlink 와 current-release-state 는 변경하지 않음
- 운영 서버는 extract 이후 build 를 수행하지 않음
11. Stage / Promote 기준¶
release path:
/var/www/{SERVICE_NAME}/releases/{PACKAGE_VERSION}
stage path:
/var/www/{SERVICE_NAME}/tmp/stage.{PACKAGE_VERSION}
동작 기준:
1. RELEASE_PATH 가 이미 존재하는지 확인함
2. RELEASE_PATH 가 존재하고 non-empty directory 이면 재사용함
3. RELEASE_PATH 가 존재하지만 유효한 non-empty directory 가 아니면 실패함
4. RELEASE_PATH 가 없으면 STAGE_PATH 를 새로 생성함
5. extract path 내용을 stage path 로 rsync 함
6. stage path 가 비어 있으면 실패함
7. stage path 권한을 u+rwX,g+rX,o-rwx 로 정리함
8. stage path 를 release path 로 mv 함
운영 기준:
- release path 는 immutable release archive 로 취급함
- 동일 package_version 의 release path 가 이미 정상 존재하면 재사용함
- stage path 에서 준비 완료 후 release path 로 promote 함
- release path 로 직접 extract 하지 않음
- stage 준비 실패 시 current symlink 와 current-release-state 는 변경하지 않음
12. After-prepare-release Hook 기준¶
hook path:
/var/www/{SERVICE_NAME}/shared/poll-release-hooks/01-after-prepare-release.sh
실행 시점:
release package download 완료
sha256 검증 완료
tar safety 검증 완료
extract 완료
stage prepare 완료
release path 준비 완료
current symlink 변경 전
hook 에 전달되는 환경변수:
SERVICE_NAME
SERVICE_USER
SERVICE_GROUP
DEPLOY_BASE
RELEASE_ENV
PACKAGE_VERSION
RELEASE_PATH
CURRENT_LINK
운영 기준:
- hook 은 서비스별 release 검증/후처리 확장 지점임
- hook 이 executable 이면 실행함
- hook 이 없거나 executable 이 아니면 skip 함
- hook 실패 시 poll-release 는 실패함
- hook 실패 시 current symlink 는 변경하지 않음
- hook 내부 검증 항목은 서비스 runtime 특성에 따라 다르게 작성함
- C++, PHP, Node.js, Node.js + Express, Next.js 등 서비스별 실행 가능성 검증은 hook 에서 수행하는 것을 기준으로 함
13. Current Symlink Activate 기준¶
current link:
/var/www/{SERVICE_NAME}/current
temporary symlink:
/var/www/{SERVICE_NAME}/tmp/current.{PACKAGE_VERSION}.link
동작 기준:
1. release path 존재 여부 확인
2. release path non-empty 여부 확인
3. current 가 존재하면서 symlink 가 아니면 실패
4. 기존 current target 을 기록함
5. tmp 영역에 새 symlink 생성
6. mv -Tf 로 current symlink 를 atomic swap 함
7. current target 이 release path 와 일치하는지 검증함
activate 방식:
ln -s "$RELEASE_PATH" "$NEW_CURRENT_LINK"
mv -Tf "$NEW_CURRENT_LINK" "$CURRENT_LINK"
운영 기준:
ln -sfn단독 사용이 아니라 temporary symlink +mv -Tf기반으로 activate 함- current symlink 교체는 atomic rename 기반으로 수행함
- current 가 일반 file/directory 이면 실패함
- activate 실패 시 current-release-state 는 갱신하지 않음
- 현재 스크립트는 activate 이후 PM2 실패 시 자동 rollback 을 수행하지 않음
14. Release Marker 기준¶
marker file:
/var/www/{SERVICE_NAME}/current/{PACKAGE_VERSION}
동작 기준:
1. current link 가 symlink 인지 확인함
2. current target 이 directory 인지 확인함
3. current/{PACKAGE_VERSION} 파일을 touch 함
4. marker file 권한을 600 으로 설정함
운영 기준:
- release marker 는 해당 current target 이 어떤 package_version 으로 activate 되었는지 확인하기 위한 파일임
- marker 작성 실패 시 poll-release 는 실패할 수 있음
- marker 파일은 최종 검증 단계에서 확인 대상임
15. PM2 Delete / Start 기준¶
PM2 app name:
{SERVICE_NAME}
PM2 binary:
/usr/bin/pm2
PM2 ecosystem file:
/var/www/{SERVICE_NAME}/current/ecosystem.config.cjs
PM2 env:
production -> production
pre-production -> pre_production
동작 기준:
1. 기존 PM2 app 을 delete 함
2. delete 대상이 없어도 실패로 보지 않음
3. ecosystem.config.cjs 기준으로 PM2 start 수행함
4. --env {PM2_ENV} 를 전달함
5. --update-env 를 사용함
6. pm2 save --force 를 수행함
7. pm2 status 를 출력함
실행 형태:
SERVICE_NAME="$SERVICE_NAME" \
/usr/bin/pm2 start "$CURRENT_LINK/ecosystem.config.cjs" \
--env "$PM2_ENV" \
--update-env
운영 기준:
- 현재 스크립트는
ecosystem.config.cjs만 지원함 - 현재 스크립트는
ecosystem.config.jsfallback 을 수행하지 않음 - PM2 start 실패 시 poll-release 는 실패함
- PM2 start 실패 시 current-release-state 는 갱신하지 않음
- 현재 스크립트는 PM2 start 실패 시 자동 rollback 을 수행하지 않음
- framework-specific 실행 가능성 검증은 after-prepare hook 에서 수행함
16. Current Release State 갱신 기준¶
state file:
/var/www/{SERVICE_NAME}/shared/current-release-state.json
temporary state file:
/var/www/{SERVICE_NAME}/tmp/current-release-state.{PACKAGE_VERSION}.tmp.json
갱신 방식:
1. desired state file 존재 여부 확인
2. desired state JSON parse 가능 여부 확인
3. desired state file 을 tmp state file 로 복사
4. tmp state file 을 current-release-state.json 으로 mv
5. current-release-state.json 권한을 600 으로 설정
운영 기준:
- current-release-state 는 desired state JSON 원본을 복사하여 저장함
- current-release-state 는 PM2 reload/start 성공 이후에만 갱신함
- metadata 검증 실패 시 갱신하지 않음
- package download 실패 시 갱신하지 않음
- sha256 검증 실패 시 갱신하지 않음
- extract/stage 실패 시 갱신하지 않음
- hook 실패 시 갱신하지 않음
- activate 실패 시 갱신하지 않음
- PM2 start 실패 시 갱신하지 않음
17. Cleanup 기준¶
기본 retention:
KEEP_RELEASES=3
환경변수 override:
KEEP_RELEASES={integer >= 1}
release cleanup 기준:
1. KEEP_RELEASES 값이 정수인지 확인함
2. KEEP_RELEASES 값이 1 이상인지 확인함
3. current target 을 readlink -f 로 확인함
4. current target 이 releases path 내부인지 검증함
5. releases 하위 directory 를 sort -r 기준으로 순회함
6. current target 은 항상 keep 함
7. current target 외 release directory 는 keep count 기준으로 보존/삭제함
downloads cleanup 기준:
/var/www/{SERVICE_NAME}/downloads 하위 항목 전체 삭제
tmp cleanup 기준:
/var/www/{SERVICE_NAME}/tmp 하위 항목 전체 삭제
운영 기준:
- current target 은 삭제하지 않음
- cleanup 대상 release path 가 releases path 밖이면 실패함
- downloads path 가
$DEPLOY_BASE/downloads가 아니면 실패함 - tmp path 가
$DEPLOY_BASE/tmp가 아니면 실패함 - previous rollback target 을 별도로 보호하지 않음
- 현재 스크립트는 local rollback 기능이 없으므로 rollback target retention 도 별도 수행하지 않음
- cleanup 은 deploy 성공 이후 수행함
18. Skip / Failure 처리 기준¶
skip 조건:
.env.{RELEASE_ENV} 파일이 아직 준비되지 않음
poll-release lock 획득 실패
desired state 의 package_version 이 비어 있음
current-release-state.json 의 package_version 과 desired package_version 이 동일함
skip 결과:
current symlink 변경 없음
current-release-state.json 변경 없음
PM2 reload/start 없음
cleanup 없음
exit 0
activate 전 실패 시 영향:
current symlink 변경 없음
current-release-state.json 갱신 없음
PM2 reload/start 없음
activate 후 PM2 이전 실패 시 영향:
current symlink 는 새 release 를 가리킬 수 있음
current-release-state.json 갱신 없음
PM2 reload/start 안 됨
수동 확인 필요
PM2 실패 시 영향:
current symlink 는 새 release 를 가리킬 수 있음
current-release-state.json 갱신 없음
PM2 app start 실패 상태 가능
자동 rollback 없음
수동 복구 필요 가능
운영 기준:
- metadata download 실패 시 current release 유지
- metadata validation 실패 시 current release 유지
- package download 실패 시 current release 유지
- sha256 검증 실패 시 current release 유지
- tar safety 검증 실패 시 current release 유지
- extract 실패 시 current release 유지
- stage prepare 실패 시 current release 유지
- after-prepare hook 실패 시 current release 유지
- current symlink activate 실패 시 current-release-state 갱신 금지
- PM2 start 실패 시 current-release-state 갱신 금지
- 현재 스크립트는 자동 health verify 를 수행하지 않음
- 현재 스크립트는 자동 local rollback 을 수행하지 않음
19. 운영 확인 절차¶
빠른 상태 확인:
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}
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
20. 수동 실행 절차¶
timer 중지:
sudo systemctl stop release-poll.{SERVICE_NAME}.timer
runtime directory 이동:
cd /var/www/{SERVICE_NAME}
수동 실행:
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
운영 기준:
- 수동 실행 시에도 root 로 실행하지 않음
- 수동 실행 시에도
sudo -u {SERVICE_USER} -H env ...형태로 실행함 cd /var/www/{SERVICE_NAME}이후 실행해야 함RELEASE_ENV는 bootstrap 으로 생성된 systemd service 환경과 동일한 값을 사용함
21. 수동 복구 기준¶
현재 스크립트는 자동 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}
22. 보안 기준¶
운영 기준:
- 운영 서버는 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 경로 하위만 삭제함
23. 결론¶
poll-release.sh 는 Release Storage 의 desired state 를 기준으로 운영 서버가 self-pull deploy 를 수행하는 runtime script 임.
운영 서버는 GitLab 에 직접 접근하지 않고, Release Storage 에 publish 된 current metadata 를 MinIO/S3 compatible API 로 조회함.
신규 package_version 이 감지되면 release package 를 다운로드하고, sha256 검증, tar safety 검증, extract, stage, release promote, after-prepare hook, current symlink activate, PM2 delete/start/save, current-release-state 갱신, cleanup, 최종 검증 순서로 동작함.
본 구조는 immutable release archive 와 current symlink 기반 activate 를 전제로 함.
현재 스크립트는 PM2 process manager 기준이며, ecosystem.config.cjs 를 사용함.
현재 스크립트는 별도 HTTP health verify 와 local rollback 을 수행하지 않음.