GitLab Ops Server Bootstrap Runbook (운영 기준)¶
0. 전제¶
- 23-GitLab Ops Server Bootstrap 기준으로 정의된 bootstrap 구조를 운영 서버에 적용하는 실행 절차를 정의함.
bootstrap.sh는 운영 서버를 polling deploy 가능한 runtime 상태로 구성하는 installer 임.- 운영자는 root shell 로 접속하지 않고, root 권한이 필요한 명령은
sudo로 실행함.
bootstrap 전체 흐름:
운영 서버 초기 준비
▼
.ops/cd source 파일 확인
▼
source 권한 / 개행 / 문법 확인
▼
RELEASE_ENV 지정 후 bootstrap.sh 실행
▼
runtime directory 생성
▼
shared env 복사
▼
poll-release hook 동기화
▼
current-release-state.json / poll-release.log 초기화
▼
poll-release runtime script 설치
▼
systemd service/timer 생성
▼
logrotate config 생성
▼
systemd daemon-reload
▼
timer enable/restart
▼
bootstrap 결과 확인
▼
polling deploy 가능 상태 진입
운영 기준:
- bootstrap source of truth 는
.ops/cd/bootstrap.sh임 - 운영 서버의 systemd unit 파일은 수동 수정하지 않음
- systemd unit 변경이 필요하면
.ops/cd/bootstrap.sh를 수정한 뒤 재실행함 RELEASE_ENV허용값은production,pre-production임- bootstrap 은 runtime provisioning 만 수행함
- 실제 release polling/deploy 동작은
poll-release.sh가 수행함 - GitLab CI/CD 의 release target branch 는 Release Yaml 파일 (
.ops/ci/release-branch-versions/{CI_COMMIT_BRANCH}.yaml) 기준으로 확장 가능 - 본 runbook 의 bootstrap 실행은 현재
bootstrap.sh가 허용하는RELEASE_ENV값 기준으로 수행함.
1. Bootstrap 실행 전 source 확인¶
운영 서버에 .ops/cd 작업 디렉토리가 배치되어 있어야 함.
cd /home/{SERVICE_USER}/.ops/cd
source 구조 확인:
sudo ls -ld /home/{SERVICE_USER}/.ops
sudo ls -ld /home/{SERVICE_USER}/.ops/cd
sudo ls -ld /home/{SERVICE_USER}/.ops/cd/env
sudo ls -l /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
sudo ls -l /home/{SERVICE_USER}/.ops/cd/opt/bin/poll-release.sh
sudo ls -l /home/{SERVICE_USER}/.ops/cd/env/.service.env
sudo ls -l /home/{SERVICE_USER}/.ops/cd/env/.release-storage.env
sudo ls -la /home/{SERVICE_USER}/.ops/cd/poll-release-hooks
필수 source 파일:
/home/{SERVICE_USER}/.ops/cd/bootstrap.sh
/home/{SERVICE_USER}/.ops/cd/opt/bin/poll-release.sh
/home/{SERVICE_USER}/.ops/cd/env/.service.env
/home/{SERVICE_USER}/.ops/cd/env/.release-storage.env
선택 source directory:
/home/{SERVICE_USER}/.ops/cd/poll-release-hooks/
정상 기준:
.ops/cd/bootstrap.sh가 존재해야 함.ops/cd/opt/bin/poll-release.sh가 존재해야 함.ops/cd/env/.service.env가 존재해야 함.ops/cd/env/.release-storage.env가 존재해야 함- hook directory 가 있으면 bootstrap 시 shared hook 영역으로 동기화됨
2. Source 권한 정리¶
source directory 소유자를 정리함.
sudo chown -R {SERVICE_USER}:{SERVICE_GROUP} /home/{SERVICE_USER}/.ops
기본 권한을 정리함.
sudo find /home/{SERVICE_USER}/.ops -type d -exec chmod 755 {} \;
sudo find /home/{SERVICE_USER}/.ops -type f -exec chmod 644 {} \;
실행 스크립트 권한을 설정함.
sudo chmod 755 /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
sudo chmod 755 /home/{SERVICE_USER}/.ops/cd/opt/bin/poll-release.sh
hook 권한을 설정함.
if [ -d /home/{SERVICE_USER}/.ops/cd/poll-release-hooks ]; then
sudo find /home/{SERVICE_USER}/.ops/cd/poll-release-hooks -type d -exec chmod 755 {} \;
sudo find /home/{SERVICE_USER}/.ops/cd/poll-release-hooks -type f -exec chmod 755 {} \;
fi
민감 env 권한을 설정함.
sudo chmod 700 /home/{SERVICE_USER}/.ops/cd/env
sudo chmod 600 /home/{SERVICE_USER}/.ops/cd/env/.service.env
sudo chmod 600 /home/{SERVICE_USER}/.ops/cd/env/.release-storage.env
3. Source 개행 / 문법 확인¶
BOM / CRLF 를 제거함.
sudo sed -i '1s/^\xEF\xBB\xBF//' /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
sudo sed -i '1s/^\xEF\xBB\xBF//' /home/{SERVICE_USER}/.ops/cd/opt/bin/poll-release.sh
sudo sed -i 's/\r$//' /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
sudo sed -i 's/\r$//' /home/{SERVICE_USER}/.ops/cd/opt/bin/poll-release.sh
hook script 도 동일하게 정리함.
if [ -d /home/{SERVICE_USER}/.ops/cd/poll-release-hooks ]; then
sudo find /home/{SERVICE_USER}/.ops/cd/poll-release-hooks -type f -name '*.sh' -exec sed -i '1s/^\xEF\xBB\xBF//' {} \;
sudo find /home/{SERVICE_USER}/.ops/cd/poll-release-hooks -type f -name '*.sh' -exec sed -i 's/\r$//' {} \;
fi
문법을 확인함.
sudo bash -n /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
sudo bash -n /home/{SERVICE_USER}/.ops/cd/opt/bin/poll-release.sh
hook 문법을 확인함.
if [ -d /home/{SERVICE_USER}/.ops/cd/poll-release-hooks ]; then
sudo find /home/{SERVICE_USER}/.ops/cd/poll-release-hooks -type f -name '*.sh' -exec bash -n {} \;
fi
정상 기준:
bash -n 결과가 아무 출력 없이 종료되어야 함
4. Bootstrap 실행¶
production 환경 bootstrap:
cd /home/{SERVICE_USER}/.ops/cd
sudo RELEASE_ENV=production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
pre-production 환경 bootstrap:
cd /home/{SERVICE_USER}/.ops/cd
sudo RELEASE_ENV=pre-production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
주의:
RELEASE_ENV는 필수임RELEASE_ENV는production또는pre-production이어야 함- bootstrap 은 지정한
RELEASE_ENV기준으로 systemd service 환경변수를 생성함 - 동일 서버에서 release env 를 변경하려면 해당
RELEASE_ENV값으로 bootstrap 을 다시 실행함
5. Bootstrap 실행 로그 확인¶
bootstrap 실행 중 출력에서 다음 항목을 확인함.
Bootstrap start
Service Name
Service User
Service Group
Release Env
Deploy Base
Runtime Script
Service Unit
Timer Unit
PM2 Binary
정상 완료 로그:
Bootstrap completed.
Summary:
Service Name
Service User
Service Group
Release Env
Deploy Base
Runtime Script
Service Unit
Timer Unit
실패 시 우선 확인:
sudo journalctl -u release-poll.{SERVICE_NAME}.service -n 100 --no-pager
sudo systemctl status release-poll.{SERVICE_NAME}.service -n 100 --no-pager
6. Runtime 결과 확인¶
bootstrap 완료 후 runtime directory 구조를 확인함.
/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
runtime script 확인:
sudo ls -l /opt/bin/poll-release.{SERVICE_NAME}.sh
정상 기준:
/var/www/{SERVICE_NAME} 755 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/downloads 750 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/logs 750 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/releases 755 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/shared 700 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/tmp 750 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/deploy.lock 600 {SERVICE_USER}:{SERVICE_GROUP}
/opt/bin/poll-release.{SERVICE_NAME}.sh 755 root:root
7. Shared 파일 확인¶
shared directory 내용을 확인함.
sudo ls -la /var/www/{SERVICE_NAME}/shared
개별 파일 확인:
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/.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
application env 파일은 bootstrap 이 생성하지 않음. 운영 서버에서 별도로 배치함.
sudo ls -l /var/www/{SERVICE_NAME}/shared/.env.{RELEASE_ENV}
# 현재 기본 운영 env 예시
sudo ls -l /var/www/{SERVICE_NAME}/shared/.env.production
sudo ls -l /var/www/{SERVICE_NAME}/shared/.env.pre-production
정상 기준:
shared/.service.env 600 {SERVICE_USER}:{SERVICE_GROUP}
shared/.release-storage.env 600 {SERVICE_USER}:{SERVICE_GROUP}
shared/current-release-state.json 600 {SERVICE_USER}:{SERVICE_GROUP}
shared/poll-release-hooks 700 {SERVICE_USER}:{SERVICE_GROUP}
shared/poll-release-hooks/*.sh 700 {SERVICE_USER}:{SERVICE_GROUP}
8. systemd Unit 확인¶
service unit 확인:
sudo ls -l /etc/systemd/system/release-poll.{SERVICE_NAME}.service
sudo systemctl cat release-poll.{SERVICE_NAME}.service --no-pager
timer unit 확인:
sudo ls -l /etc/systemd/system/release-poll.{SERVICE_NAME}.timer
sudo systemctl cat release-poll.{SERVICE_NAME}.timer --no-pager
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
Unit=release-poll.{SERVICE_NAME}.service
9. systemd Timer 상태 확인¶
timer 등록 상태를 확인함.
sudo systemctl list-timers | grep release-poll
timer 상태를 확인함.
sudo systemctl status release-poll.{SERVICE_NAME}.timer -n 100 --no-pager
service 상태를 확인함.
sudo systemctl status release-poll.{SERVICE_NAME}.service -n 100 --no-pager
정상 기준:
release-poll.{SERVICE_NAME}.timer loaded active waiting
release-poll.{SERVICE_NAME}.service loaded inactive dead
주의:
- service 는
Type=oneshot이므로 실행 완료 후inactive (dead)상태가 정상일 수 있음 - 자동 실행 대상은 service 가 아니라 timer 임
10. Logrotate 확인¶
logrotate config 를 확인함.
sudo ls -l /etc/logrotate.d/release-poll.{SERVICE_NAME}
sudo cat /etc/logrotate.d/release-poll.{SERVICE_NAME}
debug 모드로 확인함.
sudo logrotate -d /etc/logrotate.d/release-poll.{SERVICE_NAME}
확인 대상:
/var/www/{SERVICE_NAME}/logs/*.log
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
create 600 {SERVICE_USER} {SERVICE_GROUP}
11. Bootstrap 이후 수동 poll-release 검증¶
bootstrap 후 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
주의:
.env.{RELEASE_ENV}파일이 없으면 poll-release 는 skip 됨- Release Storage current metadata 가 없으면 poll-release 는 실패할 수 있음
- poll-release 상세 확인은 26-GitLab Ops Server Poll-Release Runbook (운영 기준) 기준으로 수행함
12. 로그 확인¶
unit 기준 로그 확인:
sudo journalctl -u release-poll.{SERVICE_NAME}.service -n 100 --no-pager
SyslogIdentifier 기준 로그 확인:
sudo journalctl -t release-poll.{SERVICE_NAME} -n 100 --no-pager
file log 확인:
sudo tail -n 100 /var/www/{SERVICE_NAME}/logs/poll-release.log
실시간 확인:
sudo journalctl -u release-poll.{SERVICE_NAME}.service -f
sudo tail -F /var/www/{SERVICE_NAME}/logs/poll-release.log
13. 장애 대응 기준¶
13.1 root 권한 없이 bootstrap 실행¶
증상:
bootstrap.sh must be run as root
대응:
cd /home/{SERVICE_USER}/.ops/cd
sudo RELEASE_ENV=production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
13.2 RELEASE_ENV 누락¶
증상:
Missing RELEASE_ENV
대응:
sudo RELEASE_ENV=production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
sudo RELEASE_ENV=pre-production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
13.3 RELEASE_ENV 값 오류¶
증상:
Invalid RELEASE_ENV
Allowed values: production, pre-production
대응:
sudo RELEASE_ENV=production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
sudo RELEASE_ENV=pre-production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
13.4 release-poll service 실행 중 bootstrap 시도¶
증상:
Release service is currently running
확인:
sudo systemctl status release-poll.{SERVICE_NAME}.service -n 100 --no-pager
sudo journalctl -u release-poll.{SERVICE_NAME}.service -n 100 --no-pager
대응:
현재 polling cycle 이 종료된 뒤 bootstrap 을 재실행함
13.5 current 경로가 symlink 가 아닌 경우¶
증상:
/var/www/{SERVICE_NAME}/current exists but is not a symlink
확인:
sudo ls -la /var/www/{SERVICE_NAME}/current
대응:
current 는 release directory 를 가리키는 symlink 여야 함
일반 directory/file 로 존재하면 bootstrap 이 중단됨
필요 시 수동 백업 후 current 경로를 정리함
13.6 PM2 누락¶
증상:
Missing pm2 executable: /usr/bin/pm2
대응:
sudo npm install -g pm2
sudo which pm2
sudo ls -l /usr/bin/pm2
13.7 필수 실행 파일 누락¶
증상:
Missing executable
대응:
sudo dnf install -y curl jq tar gzip coreutils util-linux rsync logrotate awscli
그 후 bootstrap 을 재실행함.
13.8 .env.{RELEASE_ENV} 누락¶
증상:
.env.{RELEASE_ENV} file not found
poll-release skipped
확인:
sudo ls -l /var/www/{SERVICE_NAME}/shared/.env.{RELEASE_ENV}
대응:
sudo cp /path/to/.env.{RELEASE_ENV} /var/www/{SERVICE_NAME}/shared/.env.{RELEASE_ENV}
sudo chown {SERVICE_USER}:{SERVICE_GROUP} /var/www/{SERVICE_NAME}/shared/.env.{RELEASE_ENV}
sudo chmod 600 /var/www/{SERVICE_NAME}/shared/.env.{RELEASE_ENV}
그 후 poll-release 를 다시 수동 실행하거나 timer 를 재개함.
14. 보안 기준¶
운영 기준:
.release-storage.env는 민감정보 파일로 취급함.release-storage.env는600권한 기준으로 관리함.service.env는600권한 기준으로 관리함- shared directory 는
700권한 기준으로 관리함 - bootstrap source env 파일은 Git 저장소에 실제 credential 을 포함하지 않음
- 운영 서버의 runtime shared env 파일은 신뢰된 운영자만 수정함
- systemd unit 은 bootstrap 결과물이며 수동 수정하지 않음
- root 권한이 필요한 수동 명령은
sudo로 실행함