GitLab Ops Server Bootstrap¶
0. 전제¶
- 운영 서버가 GitLab CI/CD + Release Storage + Polling Deploy 구조에 참여하기 전, CD runtime bootstrap 을 수행하는 절차를 정의함.
- 운영 서버는 polling deploy 를 수행하기 전에 runtime directory, shared env, deploy lock, current-release-state, runtime script, service hook, systemd unit, logrotate 설정이 먼저 준비되어야 함.
bootstrap 흐름:
운영 서버 초기 준비
▼
root 실행 여부 확인
▼
service env 로드 (.ops/cd/env/.service.env)
▼
SERVICE_USER / SERVICE_GROUP / SERVICE_NAME 필수값 및 식별자 검증
▼
RELEASE_ENV 필수값 및 허용값 검증 (production / pre-production)
▼
runtime path 변수 정의
▼
service user/group 존재 확인
▼
source runtime script 확인 (.ops/cd/opt/bin/poll-release.sh)
▼
release storage env 확인 (.ops/cd/env/.release-storage.env)
▼
PM2 절대 경로 확인 (/usr/bin/pm2)
▼
필수 패키지 설치 및 실행 파일 확인
▼
기존 timer 중지 및 실행 중인 service 확인
▼
runtime directory 생성 (/var/www/{SERVICE_NAME}/*)
▼
기본 ownership / 권한 고정
▼
deploy.lock 준비 및 bootstrap lock 획득
▼
shared env 파일 복사 (.service.env / .release-storage.env)
▼
poll-release hook 동기화
▼
current-release-state.json 초기화
▼
poll-release.log 초기화
▼
current symlink 상태 확인
▼
poll-release runtime script 설치 (/opt/bin/poll-release.{SERVICE_NAME}.sh)
▼
systemd service/timer 생성 (/etc/systemd/system)
▼
logrotate 설정 생성 (/etc/logrotate.d/release-poll.{SERVICE_NAME})
▼
SELinux context restore (restorecon)
▼
systemd daemon-reload
▼
systemd timer enable/restart
▼
bootstrap 결과 검증
▼
polling deploy 가능 상태 진입
운영 기준:
bootstrap.sh는 운영 서버 최초 설치 entrypoint 로 사용함bootstrap.sh는 runtime provisioning 만 수행함- bootstrap source of truth 는
.ops/cd/bootstrap.sh이며, 운영 서버의 systemd unit 파일을 수동 수정하지 않음 - GitLab CI/CD 의 release target branch 는
.ops/ci/release-branch-versions/{branch}.yaml기준으로 확장 가능함.
1. Project Operations Structure¶
- 본 프로젝트는 GitLab CI/CD 와 운영 서버 polling deploy 구조를 분리하여 관리함.
각 Project 의 .ops 디렉토리는 아래 두 영역으로 구성됨.
.ops/
├── cd/
└── ci/
설명:
-
cd- 운영 서버 runtime/deploy layer 관리
- 운영 서버 최초 bootstrap installer 관리
- polling deploy runtime script 관리
- 서비스별 poll-release hook 관리
- Release Storage 접속 설정 관리
-
ci- GitLab pipeline orchestration 관리
- build/package/release-state publish 관리
- 환경 branch 별 release version 선언 관리
2. CD Runtime Overlay¶
.ops/cd는 운영 서버 runtime 을 구성하기 위한 원본(source of truth) 역할을 수행함..ops/cd/opt/bin/poll-release.sh는 운영 서버에 아래 경로로 설치됨.
/opt/bin/poll-release.{SERVICE_NAME}.sh
.ops/cd/env의 env 파일은 bootstrap 시 운영 runtime 의 shared 영역으로 복사됨.
.ops/cd/env/.service.env
.ops/cd/env/.release-storage.env
▼
/var/www/{SERVICE_NAME}/shared/.service.env
/var/www/{SERVICE_NAME}/shared/.release-storage.env
.ops/cd/poll-release-hooks 의 hook 파일은 bootstrap 시 운영 runtime 의 shared hook 영역으로 동기화됨.
.ops/cd/poll-release-hooks/
▼
/var/www/{SERVICE_NAME}/shared/poll-release-hooks/
운영 기준:
.ops/cd/bootstrap.sh는 설치 entrypoint 임.ops/cd/opt/bin/poll-release.sh는 runtime script source 임.ops/cd/poll-release-hooks는 서비스별 release 검증/후처리 hook source 임- 운영 서버에서 생성된 systemd unit, logrotate config, runtime script 는 bootstrap 결과물임
3. Project Repository 구조¶
확정 구조:
Project root
├── .ops/
│ ├── cd/
│ │ ├── env/
│ │ │ ├── .release-storage.env
│ │ │ └── .service.env
│ │ ├── opt/
│ │ │ └── bin/
│ │ │ └── poll-release.sh
│ │ ├── poll-release-hooks/
│ │ │ └── 01-after-prepare-release.sh
│ │ └── bootstrap.sh
│ └── ci/
│ ├── release-branch-versions/
│ │ ├── pre-production.yaml
│ │ ├── production.yaml
│ │ └── {release-target-branch}.yaml
│ ├── scripts/
│ │ ├── 01-detect-version-change.sh
│ │ ├── 02-build.sh
│ │ ├── 03-package.sh
│ │ └── 04-update-release.sh
│ └── set-release-state-pipeline.yaml
└── .gitlab-ci.yml
4. CD env 파일 구조¶
.ops/cd/env의 env 파일은 bootstrap source env 파일임- bootstrap 실행 시
.service.env와.release-storage.env는 운영 runtime 의 shared 영역으로 복사됨 - runtime shared env 파일은
{SERVICE_USER}:{SERVICE_GROUP}소유,600권한으로 관리함
4.1 .service.env¶
경로:
.ops/cd/env/.service.env
변수:
SERVICE_USER: poll-release runtime 실행 사용자이며 runtime 파일 소유자 기준으로 사용함SERVICE_GROUP: poll-release runtime 실행 group이며 runtime 파일 소유 group 기준으로 사용함SERVICE_NAME: /var/www/{SERVICE_NAME} runtime root, systemd unit name, PM2 app name 구성에 사용함
SERVICE_USER={SERVICE_USER}
SERVICE_GROUP={SERVICE_GROUP}
SERVICE_NAME={SERVICE_NAME}
허용 문자 기준:
A-Z a-z 0-9 . _ -
4.2 .release-storage.env¶
- MinIO/S3 호환 Release Storage 접속 정보 정의
- release state metadata 조회 위치 계산 기준 제공
- release package 다운로드 기준 제공
- MinIO 에서 IAM User 생성 후 해당 계정의 Access Key 를 생성하면 만들어지는 정보를 그대로 사용.
- AWS S3 등 Cloud Service 에서 제공하는 S3 제품과 호환됨.
경로:
.ops/cd/env/.release-storage.env
MINIO_ENDPOINT=https://release.{ReleaseStorageUrl}
MINIO_REGION=ap-northeast-2
MINIO_ACCESS_KEY={MINIO_ACCESS_KEY}
MINIO_SECRET_KEY={MINIO_SECRET_KEY}
MINIO_BUCKET={PROJECT_NAME}
MINIO_SERVICE_PREFIX={SERVICE_PREFIX}/{SERVICE_NAME}
5. 운영 서버 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 root 는
/var/www/{SERVICE_NAME}기준으로 통일함 shared는 mutable runtime state/env 영역임releases는 immutable release archive 영역임downloads는 release package download 임시 보관 영역임tmp는 desired state, extract, stage 등 작업 영역임logs는 poll-release file log 영역임deploy.lock은 bootstrap 과 poll-release 동시 실행을 직렬화함current는 활성 release directory 를 가리키는 symlink 임
권한 기준:
/var/www/{SERVICE_NAME} 755 {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}/downloads 750 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/tmp 750 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/logs 750 {SERVICE_USER}:{SERVICE_GROUP}
/var/www/{SERVICE_NAME}/deploy.lock 600 {SERVICE_USER}:{SERVICE_GROUP}
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}
logs/poll-release.log 600 {SERVICE_USER}:{SERVICE_GROUP}
6. bootstrap.sh 역할¶
bootstrap.sh는 운영 서버 최초 설치 entrypoint 임.- 운영 서버를 polling deploy 가능한 상태로 만드는 installer 역할을 수행함.
- bootstrap 단계는 provisioning/runtime preparation 에 집중함.
상세 흐름:
1. root 실행 여부 확인
2. .service.env 로드
3. SERVICE_USER / SERVICE_GROUP / SERVICE_NAME 필수값 확인
4. SERVICE_USER / SERVICE_GROUP / SERVICE_NAME 식별자 검증
5. RELEASE_ENV 필수값 및 허용값 확인
6. runtime path 변수 정의
7. service user 존재 확인
8. service group 존재 확인
9. source poll-release.sh 파일 확인
10. PM2 절대 경로 확인
11. .release-storage.env source 파일 확인
12. 필수 패키지 설치
13. 필수 실행 파일 확인
14. 기존 timer 중지
15. 실행 중인 release-poll service 확인
16. runtime directory 생성
17. deploy.lock 준비
18. bootstrap lock 획득
19. shared env 파일 복사
20. poll-release hook 동기화
21. current-release-state.json 초기화
22. poll-release.log 초기화
23. current symlink 상태 확인
24. poll-release runtime script 설치
25. systemd service/timer 생성
26. logrotate config 생성
27. SELinux context restore
28. systemd daemon-reload
29. timer enable
30. timer restart
31. bootstrap 결과 검증
주의:
- bootstrap 은
release-poll.{SERVICE_NAME}.timer를 중지한 뒤 runtime 파일과 unit 을 재생성함 release-poll.{SERVICE_NAME}.service가 실행 중이면 bootstrap 을 중단함- bootstrap 은 systemd unit 을 항상 source of truth 기준으로 재생성함
- 운영 서버에서 unit 파일을 수동 수정하지 않음
- runtime script 실행 사용자는 systemd service 의
User={SERVICE_USER}기준임 - SELinux context restore 는
restorecon명령이 존재하는 환경에서 수행하며,restorecon이 없으면 skip 함
7. Runtime Script 설치 구조¶
설치 규칙:
source : .ops/cd/opt/bin/poll-release.sh
installed : /opt/bin/poll-release.{SERVICE_NAME}.sh
owner : root:root
mode : 755
8. Poll-release Hook 설치 구조¶
- poll-release hook 은 서비스별 release 검증/후처리 확장 지점임.
- hook source directory 가 없으면 bootstrap 은 hook 동기화를 skip 함
설치 규칙:
source : .ops/cd/poll-release-hooks/
installed : /var/www/{SERVICE_NAME}/shared/poll-release-hooks/
owner : {SERVICE_USER}:{SERVICE_GROUP}
mode : 700
8.1 01-after-prepare-release.sh¶
- release package extract 및 release path 준비가 완료된 뒤 실행되는 서비스별 검증 hook 임
currentsymlink 변경 전에 새 release directory 가 서비스 실행 가능한 상태인지 검증함- hook 실패 시 release 활성화와 PM2 reload 는 진행되지 않으며 기존 current 상태가 유지됨
- hook 내부 검증 항목은 C++, PHP, Node.js, Node.js + Express, Next.js 등 각 서비스 runtime 특성에 따라 다르게 작성함
9. systemd Unit 정의¶
- 운영 서버 polling deploy 는 systemd timer 가 주기적으로 service 를 실행하는 구조임.
구성 파일:
/etc/systemd/system/release-poll.{SERVICE_NAME}.service
/etc/systemd/system/release-poll.{SERVICE_NAME}.timer
9.1 release-poll.{SERVICE_NAME}.service¶
- service 는 timer 에 의해 주기 실행되며 resident daemon 으로 동작하지 않음
[Unit]
Description=Release polling deploy service ({SERVICE_NAME})
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
TimeoutStartSec=300
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}
StandardOutput=journal
StandardError=journal
SyslogIdentifier=release-poll.{SERVICE_NAME}
PrivateTmp=true
NoNewPrivileges=true
ExecStart=/opt/bin/poll-release.{SERVICE_NAME}.sh
9.2 release-poll.{SERVICE_NAME}.timer¶
RandomizedDelaySec=15s를 사용하여 다중 서버 polling 시점을 분산함Persistent=true를 사용하여 timer missed event 를 보정함- timer enable/start 대상은 service 가 아니라 timer 임
[Unit]
Description=Run Release polling deploy periodically ({SERVICE_NAME})
[Timer]
OnBootSec=1m
OnUnitActiveSec=1m
RandomizedDelaySec=15s
AccuracySec=5s
Persistent=true
Unit=release-poll.{SERVICE_NAME}.service
[Install]
WantedBy=timers.target
10. Shared 환경 파일 정책¶
- shared 환경 파일은 운영 runtime 에서 poll-release 와 application 실행에 필요한 mutable env 영역임.
- bootstrap.sh 는 shared 환경 파일 존재 여부를 검사하지 않음
- poll-release.{SERVICE_NAME}.sh 내부에서 runtime validation 시 검사 수행
- 실행 환경에 대응하는
.env.{RELEASE_ENV}파일이 없으면 polling cycle 을 수행하지 않음
10.1 bootstrap 이 관리하는 파일¶
경로:
/var/www/{SERVICE_NAME}/shared/.service.env
/var/www/{SERVICE_NAME}/shared/.release-storage.env
설치 규칙:
source : .ops/cd/env/
installed : /var/www/{SERVICE_NAME}/shared/
owner : {SERVICE_USER}:{SERVICE_GROUP}
mode : 디렉토리 : 700, 파일 : 600
10.2 운영 서버에서 별도로 관리하는 파일¶
- Bootstrap 완료 직후 운영 서버는 polling deploy 수행을 위해 shared 디렉토리에 실행 환경별 application env 파일을 수동 배치해야 함.
/var/www/{SERVICE_NAME}/shared/.env.{RELEASE_ENV}
source : 운영 서버별 application env 파일 또는 별도 env template
installed : /var/www/{SERVICE_NAME}/shared/
owner : {SERVICE_USER}:{SERVICE_GROUP}
mode : 디렉토리 : 700, 파일 : 600
10.2.1 .env.{RELEASE_ENV}¶
용도:
- application runtime 환경변수
- PM2 실행 시 application 에 전달되는 환경변수
- 실행 환경별 application 설정 분리
운영 기준:
.env.{RELEASE_ENV}파일은 poll-release runtime 이 실행 환경별로 source 하는 application env 파일임- 파일명은 systemd service 의
Environment=RELEASE_ENV값을 기준으로 결정됨 .env.{RELEASE_ENV}파일이 없으면 poll-release cycle 은 skip 됨.env.{RELEASE_ENV}안에서RELEASE_ENV를 선언하는 경우, systemd service 의Environment=RELEASE_ENV값과 반드시 일치해야 함- 서비스 runtime 종류에 따라 필요한 application env 값은 다르게 정의함
예시:
# =============================================================================
# Application Runtime
# =============================================================================
NODE_ENV=production
10.3 권한 명령어¶
sudo chown {SERVICE_USER}:{SERVICE_GROUP} /var/www/{SERVICE_NAME}/shared/{Files}
sudo chmod 700 /var/www/{SERVICE_NAME}/shared
sudo chmod 600 /var/www/{SERVICE_NAME}/shared/{Files}
11. Release Storage 설정 정책¶
- Release Storage 는 GitLab CI 가 publish 한 desired release state 와 release package 를 운영 서버가 polling 하기 위한 MinIO/S3 호환 저장소임.
- Release Storage 접속 정보는
.ops/cd/env/.release-storage.env에 정의함. - bootstrap 실행 시
.release-storage.env는 운영 runtime 의 shared 영역으로 복사됨. - poll-release runtime 은
/var/www/{SERVICE_NAME}/shared/.release-storage.env를 source 하여 desired release state 와 release package 를 조회함.
관련 설정 파일:
.ops/cd/env/.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
운영 기준:
MINIO_ENDPOINT는 MinIO/S3 compatible API endpoint 임MINIO_REGION은 S3 client region 값임MINIO_ACCESS_KEY와MINIO_SECRET_KEY는 Release Storage 접근용 credential 임MINIO_BUCKET은 release state metadata 와 release package 가 저장되는 bucket 임MINIO_SERVICE_PREFIX는 bucket 내부에서 서비스별 release object 를 구분하기 위한 prefix 임MINIO_SERVICE_PREFIX는 Release Storage object prefix 전체값이며{SERVICE_PREFIX}/{SERVICE_NAME}형식을 사용함- GitLab 문서 변수
SERVICE_PREFIX는frontend,backend,infra같은 단독 분류값임 - 예:
SERVICE_PREFIX=frontend,SERVICE_NAME=example.com,MINIO_SERVICE_PREFIX=frontend/example.com
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
Release Storage endpoint 예시:
MINIO_ENDPOINT=https://release.{ReleaseStorageUrl}
중요:
MINIO_ENDPOINT는 API endpoint 이며, desired release state object path 자체에 포함하지 않음- 운영 서버는 public HTTP URL 이 아니라 MinIO/S3 compatible API 를 통해 desired release state 를 조회함
12. Logrotate 정의¶
bootstrap.sh 는 polling runtime log file rotation 을 위해 아래 파일을 생성함.
/etc/logrotate.d/release-poll.{SERVICE_NAME}
대상 로그:
/var/www/{SERVICE_NAME}/logs/*.log
logrotate 정책:
daily
rotate 14
compress
delaycompress
missingok
notifempty
copytruncate
create 600 {SERVICE_USER} {SERVICE_GROUP}
운영 기준:
- polling runtime log 는
/var/www/{SERVICE_NAME}/logs/poll-release.log기준 사용 - runtime log file 은
600권한 기준 사용 - poll-release runtime 은 oneshot script 이므로 reopen signal 대신
copytruncate사용함 - logrotate config 는 Git 관리 대상이 아니라 bootstrap generated runtime artifact 임
13. 필수 패키지 설치 및 실행 파일 확인¶
bootstrap.sh는 필수 패키지를 설치한 뒤 실행 파일 존재 여부를 확인함.
설치 패키지:
sudo dnf install -y curl jq tar gzip coreutils util-linux rsync logrotate awscli
for cmd in curl jq tar sha256sum systemctl logrotate flock rsync aws; do
command -v "$cmd" >/dev/null 2>&1 || {
echo "missing: $cmd"
exit 1
}
done
추가 확인 대상:
sudo ls -l /usr/bin/pm2
운영 기준:
- PM2 는
/usr/bin/pm2기준으로 사용함 aws명령은 MinIO/S3 호환 Release Storage 에서 desired state 와 package 를 가져오기 위해 필요함flock은 bootstrap 과 poll-release 동시 실행 방지를 위해 필요함
14. Bootstrap 실행 전 준비¶
- 운영 서버에
.ops/cd작업 디렉토리를 배치함.
cd /home/{SERVICE_USER}/.ops/cd
권한 정리:
SERVICE_USER={SERVICE_USER}
SERVICE_GROUP={SERVICE_GROUP}
SERVICE_USER_HOME="/home/$SERVICE_USER"
OPERATIONS_PATH="$SERVICE_USER_HOME/.ops"
CD_PATH="$OPERATIONS_PATH/cd"
BOOTSTRAP_SCRIPT="$CD_PATH/bootstrap.sh"
POLL_RELEASE_SCRIPT="$CD_PATH/opt/bin/poll-release.sh"
HOOKS_PATH="$CD_PATH/poll-release-hooks"
ENV_PATH="$CD_PATH/env"
SERVICE_ENV_FILE="$ENV_PATH/.service.env"
RELEASE_STORAGE_ENV_FILE="$ENV_PATH/.release-storage.env"
# 기본 경로 확인
sudo test -d "$OPERATIONS_PATH"
sudo test -d "$CD_PATH"
sudo test -f "$BOOTSTRAP_SCRIPT"
sudo test -f "$POLL_RELEASE_SCRIPT"
sudo test -d "$ENV_PATH"
sudo test -f "$SERVICE_ENV_FILE"
sudo test -f "$RELEASE_STORAGE_ENV_FILE"
# 소유자 정리
sudo chown -R "$SERVICE_USER":"$SERVICE_GROUP" "$OPERATIONS_PATH"
# 기본 권한 정리
sudo find "$OPERATIONS_PATH" -type d -exec chmod 755 {} \;
sudo find "$OPERATIONS_PATH" -type f -exec chmod 644 {} \;
# 실행 스크립트 권한
sudo chmod 755 "$BOOTSTRAP_SCRIPT"
sudo chmod 755 "$POLL_RELEASE_SCRIPT"
if [ -d "$HOOKS_PATH" ]; then
sudo find "$HOOKS_PATH" -type d -exec chmod 755 {} \;
sudo find "$HOOKS_PATH" -type f -exec chmod 755 {} \;
fi
# 민감 env 권한
sudo chmod 700 "$ENV_PATH"
sudo chmod 600 "$SERVICE_ENV_FILE"
sudo chmod 600 "$RELEASE_STORAGE_ENV_FILE"
# BOM/CRLF 제거
sudo sed -i '1s/^\xEF\xBB\xBF//' "$BOOTSTRAP_SCRIPT"
sudo sed -i '1s/^\xEF\xBB\xBF//' "$POLL_RELEASE_SCRIPT"
sudo sed -i 's/\r$//' "$BOOTSTRAP_SCRIPT"
sudo sed -i 's/\r$//' "$POLL_RELEASE_SCRIPT"
if [ -d "$HOOKS_PATH" ]; then
sudo find "$HOOKS_PATH" -type f -name '*.sh' -exec sed -i '1s/^\xEF\xBB\xBF//' {} \;
sudo find "$HOOKS_PATH" -type f -name '*.sh' -exec sed -i 's/\r$//' {} \;
fi
# sed -i 이후 소유자/권한 재확정
sudo chown -R "$SERVICE_USER":"$SERVICE_GROUP" "$OPERATIONS_PATH"
sudo find "$OPERATIONS_PATH" -type d -exec chmod 755 {} \;
sudo find "$OPERATIONS_PATH" -type f -exec chmod 644 {} \;
sudo chmod 755 "$BOOTSTRAP_SCRIPT"
sudo chmod 755 "$POLL_RELEASE_SCRIPT"
if [ -d "$HOOKS_PATH" ]; then
sudo find "$HOOKS_PATH" -type d -exec chmod 755 {} \;
sudo find "$HOOKS_PATH" -type f -exec chmod 755 {} \;
fi
sudo chmod 700 "$ENV_PATH"
sudo chmod 600 "$SERVICE_ENV_FILE"
sudo chmod 600 "$RELEASE_STORAGE_ENV_FILE"
# 문법 확인
sudo -u "$SERVICE_USER" bash -n "$BOOTSTRAP_SCRIPT"
sudo -u "$SERVICE_USER" bash -n "$POLL_RELEASE_SCRIPT"
if [ -d "$HOOKS_PATH" ]; then
sudo -u "$SERVICE_USER" find "$HOOKS_PATH" -type f -name '*.sh' -exec bash -n {} \;
fi
15. Bootstrap 실행¶
- 운영 서버 최초 구성 시
bootstrap.sh를 실행하여 runtime bootstrap 을 수행함.
production 기준:
cd /home/{SERVICE_USER}/.ops/cd
sudo RELEASE_ENV=production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
pre-production 기준:
cd /home/{SERVICE_USER}/.ops/cd
sudo RELEASE_ENV=pre-production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
주의:
RELEASE_ENV는 필수임- bootstrap 은 실행된
RELEASE_ENV기준으로 systemd service 환경변수를 생성함 - 동일 서버에서 다른 release env 로 재구성할 경우 bootstrap 을 해당
RELEASE_ENV값으로 다시 실행해야 함
16. Bootstrap 결과 확인¶
16.1 Runtime Script / systemd Unit 확인¶
# runtime script
sudo ls -l /opt/bin/poll-release.{SERVICE_NAME}.sh
# systemd unit
sudo ls -l /etc/systemd/system/release-poll.{SERVICE_NAME}.service
sudo ls -l /etc/systemd/system/release-poll.{SERVICE_NAME}.timer
예상 결과:
# runtime script
-rwxr-xr-x root root /opt/bin/poll-release.{SERVICE_NAME}.sh
# systemd units
-rw-r--r-- root root /etc/systemd/system/release-poll.{SERVICE_NAME}.service
-rw-r--r-- root root /etc/systemd/system/release-poll.{SERVICE_NAME}.timer
16.2 Runtime Directory 확인¶
# runtime directory
sudo ls -ld /var/www/{SERVICE_NAME}
# releases
sudo ls -ld /var/www/{SERVICE_NAME}/releases
# shared
sudo ls -ld /var/www/{SERVICE_NAME}/shared
# downloads
sudo ls -ld /var/www/{SERVICE_NAME}/downloads
# tmp
sudo ls -ld /var/www/{SERVICE_NAME}/tmp
# logs
sudo ls -ld /var/www/{SERVICE_NAME}/logs
예상 결과:
drwxr-xr-x {SERVICE_USER} {SERVICE_GROUP} /var/www/{SERVICE_NAME}
drwxr-xr-x {SERVICE_USER} {SERVICE_GROUP} /var/www/{SERVICE_NAME}/releases
drwx------ {SERVICE_USER} {SERVICE_GROUP} /var/www/{SERVICE_NAME}/shared
drwxr-x--- {SERVICE_USER} {SERVICE_GROUP} /var/www/{SERVICE_NAME}/downloads
drwxr-x--- {SERVICE_USER} {SERVICE_GROUP} /var/www/{SERVICE_NAME}/tmp
drwxr-x--- {SERVICE_USER} {SERVICE_GROUP} /var/www/{SERVICE_NAME}/logs
16.3 Shared 파일 확인¶
sudo ls -la /var/www/{SERVICE_NAME}/shared
예상 결과:
-rw------- {SERVICE_USER} {SERVICE_GROUP} .service.env
-rw------- {SERVICE_USER} {SERVICE_GROUP} .release-storage.env
-rw------- {SERVICE_USER} {SERVICE_GROUP} current-release-state.json
drwx------ {SERVICE_USER} {SERVICE_GROUP} poll-release-hooks
16.4 Poll-release Hook 확인¶
sudo ls -la /var/www/{SERVICE_NAME}/shared/poll-release-hooks
예상 결과:
-rwx------ {SERVICE_USER} {SERVICE_GROUP} 01-after-prepare-release.sh
16.5 deploy.lock 확인¶
sudo ls -l /var/www/{SERVICE_NAME}/deploy.lock
예상 결과:
-rw------- {SERVICE_USER} {SERVICE_GROUP} /var/www/{SERVICE_NAME}/deploy.lock
16.6 Polling Log 확인¶
sudo ls -l /var/www/{SERVICE_NAME}/logs/poll-release.log
예상 결과:
-rw------- {SERVICE_USER} {SERVICE_GROUP} /var/www/{SERVICE_NAME}/logs/poll-release.log
16.7 Logrotate 확인¶
sudo ls -l /etc/logrotate.d/release-poll.{SERVICE_NAME}
sudo logrotate -d /etc/logrotate.d/release-poll.{SERVICE_NAME}
예상 결과:
-rw-r--r-- root root /etc/logrotate.d/release-poll.{SERVICE_NAME}
16.8 systemd service 환경 확인¶
sudo systemctl cat release-poll.{SERVICE_NAME}.service --no-pager | grep -E 'WorkingDirectory=|Environment=|ExecStart='
확인 대상:
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
RELEASE_ENV 는 bootstrap 실행 시 지정한 값과 일치해야 함.
16.9 systemd timer 등록 및 상태 확인¶
- polling service 는
Type=oneshot기준이므로 polling 종료 후inactive (dead)상태가 정상일 수 있음.
# 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
예상 결과:
# timer 등록 확인
release-poll.{SERVICE_NAME}.timer loaded active waiting
# service 등록 확인
release-poll.{SERVICE_NAME}.service loaded inactive dead
# timer 상태
Loaded: loaded
Active: active (waiting)
# service 상태
Loaded: loaded
Active: inactive (dead)
16.10 Journal / Polling Log 조회¶
# 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
17. Bootstrap 이후 수동 poll-release 검증¶
- bootstrap 이후 timer 를 잠시 중지하고 poll-release runtime 을 수동 실행하여 검증할 수 있음.
sudo systemctl stop release-poll.{SERVICE_NAME}.timer
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
18. 장애 대응 기준¶
18.1 bootstrap 실행 계정 오류¶
증상:
bootstrap.sh must be run as root
대응:
sudo RELEASE_ENV=production bash /home/{SERVICE_USER}/.ops/cd/bootstrap.sh
18.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
18.3 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 을 재실행함.
18.4 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 이 중단됨.
18.5 PM2 누락¶
증상:
Missing pm2 executable: /usr/bin/pm2
대응:
sudo npm install -g pm2
sudo which pm2
sudo ls -l /usr/bin/pm2