|
|
|
|
@ -1,14 +1,16 @@
|
|
|
|
|
# 배포 가이드 (EC2 + systemd + Jenkins) |
|
|
|
|
# 배포 가이드 (EC2 + start.sh + Jenkins) |
|
|
|
|
|
|
|
|
|
slack-notifier 를 **EC2(리눅스)** 에 단일 정적 바이너리로 올리고, **systemd** 로 운영하며, |
|
|
|
|
**Jenkins** 로 빌드·배포를 자동화한다. 도커 레지스트리 불필요(바이너리 직접 배포). |
|
|
|
|
slack-notifier 를 **EC2(리눅스)** 에 단일 정적 바이너리로 올리고, **start.sh**(백그라운드, sudo/systemd 불필요) |
|
|
|
|
로 운영하며, **Jenkins** 로 빌드·배포를 자동화한다. 도커 레지스트리 불필요(바이너리 직접 배포). |
|
|
|
|
|
|
|
|
|
``` |
|
|
|
|
[git push main] → Jenkins (golang:1.26 컨테이너) → go test/build → scp 바이너리 → EC2 |
|
|
|
|
→ systemctl restart slack-notifier |
|
|
|
|
[git push] → Jenkins (네이티브 Go) → go vet/test/build → sshpass scp(바이너리+start.sh) → EC2 |
|
|
|
|
→ start.sh restart |
|
|
|
|
[Gitea/Notion] ──webhook(HTTPS)──→ Caddy(:443) ──→ slack-notifier(:8000) ──→ Slack |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
기본 경로: `APP_DIR=/data/app/notifier`, 유저 `ec2-user`, 아키텍처 `amd64` (Jenkins 파라미터로 조정). |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## 1. EC2 최초 1회 셋업 |
|
|
|
|
@ -16,11 +18,12 @@ slack-notifier 를 **EC2(리눅스)** 에 단일 정적 바이너리로 올리
|
|
|
|
|
### 1-1. 설치 경로 / 환경파일 |
|
|
|
|
|
|
|
|
|
```bash |
|
|
|
|
sudo mkdir -p /opt/slack-notifier/{logs,data} |
|
|
|
|
sudo chown -R $USER:$USER /opt/slack-notifier # Jenkins 배포 유저가 쓸 수 있게(또는 sudo 권한) |
|
|
|
|
# 배포 계정(ec2-user)이 소유 → sudo 없이 배포/기동 가능 |
|
|
|
|
sudo mkdir -p /data/app/notifier |
|
|
|
|
sudo chown -R ec2-user:ec2-user /data/app/notifier |
|
|
|
|
|
|
|
|
|
# 비밀값/설정 — 서버에만 두고 절대 커밋하지 않음 |
|
|
|
|
sudo vi /opt/slack-notifier/.env.prod |
|
|
|
|
vi /data/app/notifier/.env.prod |
|
|
|
|
# SLACK_BOT_TOKEN=xoxb-... |
|
|
|
|
# DEFAULT_SLACK_CHANNEL=C... (브로드캐스트 채널, GITEA_BROADCAST=on 일 때) |
|
|
|
|
# GITEA_BROADCAST=off |
|
|
|
|
@ -31,64 +34,65 @@ sudo vi /opt/slack-notifier/.env.prod
|
|
|
|
|
# LOG_RETENTION_DAYS=7 |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
> systemd 서비스는 `-env prod` 로 실행되어 `.env.prod` 를 읽고, 로그는 `/opt/slack-notifier/logs/app-YYYY-MM-DD.log` 로 날짜별 회전된다. |
|
|
|
|
> 앱은 `-env prod` 로 실행되어 `.env.prod` 를 읽고, 로그는 `/data/app/notifier/logs/app-YYYY-MM-DD.log` |
|
|
|
|
> 로 날짜별 회전된다(7일 보존). stdout/패닉은 `logs/console.log` 에 남는다. |
|
|
|
|
|
|
|
|
|
### 1-2. 실행 스크립트(start.sh) |
|
|
|
|
|
|
|
|
|
### 1-2. systemd 서비스 등록 |
|
|
|
|
`start.sh` 는 **Jenkins 배포 시 바이너리와 함께 자동 전송**되므로 수동 복사는 보통 불필요하다. |
|
|
|
|
최초 수동 기동이 필요하면: |
|
|
|
|
|
|
|
|
|
```bash |
|
|
|
|
sudo cp deploy/slack-notifier.service /etc/systemd/system/slack-notifier.service |
|
|
|
|
sudo systemctl daemon-reload |
|
|
|
|
sudo systemctl enable --now slack-notifier |
|
|
|
|
sudo systemctl status slack-notifier |
|
|
|
|
# 바이너리·start.sh·.env.prod 를 /data/app/notifier 에 둔 뒤 |
|
|
|
|
cd /data/app/notifier |
|
|
|
|
./start.sh start prod # 시작 (start|stop|restart|status) |
|
|
|
|
./start.sh status |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
서비스는 `/opt/slack-notifier/slack-notifier` 바이너리를 실행한다(첫 배포 전이면 아직 없음 → Jenkins 첫 배포 후 정상 기동). |
|
|
|
|
|
|
|
|
|
### 1-3. HTTPS 리버스 프록시(Caddy) |
|
|
|
|
|
|
|
|
|
웹훅은 공개 HTTPS 가 필수. Caddy 가 Let's Encrypt 인증서를 자동 발급한다. |
|
|
|
|
|
|
|
|
|
```bash |
|
|
|
|
sudo cp deploy/Caddyfile /etc/caddy/Caddyfile |
|
|
|
|
sudo vi /etc/caddy/Caddyfile # your-domain.com 을 실제 도메인으로 교체 |
|
|
|
|
sudo vi /etc/caddy/Caddyfile # your-domain.com 을 실제 도메인으로 교체 (reverse_proxy localhost:8000) |
|
|
|
|
sudo systemctl restart caddy |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
- 도메인 A 레코드 → EC2 퍼블릭 IP |
|
|
|
|
- 보안그룹 인바운드 **80, 443** 개방(ACME 검증 + 웹훅 수신) |
|
|
|
|
- 보안그룹 인바운드 **80, 443**(ACME + 웹훅 수신), 그리고 **Jenkins → EC2 22(SSH)** 허용 |
|
|
|
|
- 웹훅 등록 주소: `https://your-domain.com/webhooks/{gitea,notion}` |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## 2. Jenkins 셋업 |
|
|
|
|
|
|
|
|
|
### 2-1. 플러그인 / Go 도구 |
|
|
|
|
- 플러그인: **Go Plugin**, **SSH Agent** |
|
|
|
|
- **Manage Jenkins → Tools → Go installations** 에서 Go 추가: |
|
|
|
|
- Name: **`go-1.26`** (Jenkinsfile 의 `GO_TOOL` 과 일치, 다르면 Jenkinsfile 수정) |
|
|
|
|
- "Install automatically" 로 1.26.x 선택(또는 에이전트에 설치된 Go 경로 지정) |
|
|
|
|
- 컨테이너 없이 에이전트의 네이티브 Go 로 빌드 → 이미지 풀/디스크 오버헤드 없음 |
|
|
|
|
|
|
|
|
|
### 2-2. 자격증명(SSH 키) |
|
|
|
|
- Jenkins → Credentials → **SSH Username with private key** 추가 |
|
|
|
|
- Username: EC2 SSH 유저(`ubuntu` 등) |
|
|
|
|
- Private key: EC2 접속 키(.pem) |
|
|
|
|
- **ID: `slack-notifier-ec2`** (Jenkinsfile 의 `SSH_CRED_ID` 와 일치, 다르면 Jenkinsfile 수정) |
|
|
|
|
- 배포 유저는 `/opt/slack-notifier` 쓰기 + `systemctl restart slack-notifier` 가능해야 함 |
|
|
|
|
(sudoers 에 무암호 허용 권장): |
|
|
|
|
``` |
|
|
|
|
ubuntu ALL=(root) NOPASSWD: /bin/systemctl restart slack-notifier, /bin/systemctl is-active slack-notifier, /bin/mv, /bin/mkdir, /bin/chmod |
|
|
|
|
``` |
|
|
|
|
### 2-1. 플러그인 / Go 도구 / sshpass |
|
|
|
|
- 플러그인: **Go Plugin**, **Credentials Binding** |
|
|
|
|
- **Manage Jenkins → Tools → Go installations** 에 Go 추가: |
|
|
|
|
- Name: **`go-1.26.4`** (Jenkinsfile 의 `tools { go '...' }` 와 일치) |
|
|
|
|
- "Install automatically" 로 1.26.x (또는 에이전트의 Go 경로) |
|
|
|
|
- **빌드 에이전트에 `sshpass` 설치** (아이디/비번 SSH): `sudo yum install -y sshpass` / `apt install -y sshpass` |
|
|
|
|
|
|
|
|
|
### 2-2. 자격증명(아이디/비번) |
|
|
|
|
- Jenkins → Credentials → **Username with password** 추가 |
|
|
|
|
- **ID: `palnet-dev-ops`** (Jenkinsfile 의 `DEPLOY_CRED_ID` 와 일치) |
|
|
|
|
- **Username = EC2 SSH 유저(ec2-user), Password = 접속 비밀번호** — 둘 다 파이프라인이 자격증명에서 가져옴 |
|
|
|
|
- (호스트 IP 는 자격증명에 없으므로 `DEPLOY_HOST` 파라미터로 받음) |
|
|
|
|
- 대상 EC2 는 **비밀번호 SSH 허용**(`/etc/ssh/sshd_config` 의 `PasswordAuthentication yes`) 필요 |
|
|
|
|
- 배포 계정이 `APP_DIR` 소유 → **sudo 불필요** |
|
|
|
|
|
|
|
|
|
### 2-3. 파이프라인 잡 |
|
|
|
|
- **Pipeline script from SCM** → 이 저장소 → `Jenkinsfile` |
|
|
|
|
- 파라미터(첫 실행 시 생성됨): |
|
|
|
|
- `DEPLOY_HOST` = EC2 DNS/IP (비우면 빌드·테스트만, 배포 스킵) |
|
|
|
|
- `DEPLOY_USER` = `ubuntu`(또는 `ec2-user`) |
|
|
|
|
- `APP_DIR` = `/opt/slack-notifier` |
|
|
|
|
- `ARCH` = `arm64`(Graviton/t4g) 또는 `amd64`(x86) ← **인스턴스 아키텍처와 반드시 일치** |
|
|
|
|
- `APP_DIR` = `/data/app/notifier` |
|
|
|
|
- `ARCH` = `amd64`(x86) 또는 `arm64`(Graviton/t4g) ← **인스턴스 아키텍처와 반드시 일치** |
|
|
|
|
- (SSH 유저는 `palnet-dev-ops` 자격증명의 username 사용 → 별도 파라미터 없음) |
|
|
|
|
|
|
|
|
|
배포는 **main 브랜치 + DEPLOY_HOST 지정** 시에만 수행된다. |
|
|
|
|
(일반 Pipeline 잡은 `BRANCH_NAME` 이 비어 `branch 'main'` 매칭이 안 되므로, Multibranch Pipeline 권장 — |
|
|
|
|
단일 잡으로 쓸 거면 Jenkinsfile 의 `when` 에서 `branch 'main'` 을 제거할 것.) |
|
|
|
|
|
|
|
|
|
### 2-4. 파라미터 변경 시 (stale 가드) |
|
|
|
|
|
|
|
|
|
@ -100,22 +104,20 @@ Jenkins 의 `parameters` 는 변경해도 **1빌드 늦게** 등록된다(step/e
|
|
|
|
|
2. **`parameters` 의 `PARAMS_VERSION` 기본값**과 **`environment.PARAMS_VERSION`** 을 **둘 다 +1** |
|
|
|
|
3. 커밋/푸시 → 첫 빌드는 `Guard` 에서 **의도적으로 실패**(파라미터 재등록만 됨) → **한 번 더 빌드**하면 정상 진행 |
|
|
|
|
|
|
|
|
|
즉 "파라미터가 실제로 적용된 빌드"에서만 배포가 일어나도록 강제된다. |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## 3. 배포 동작 / 롤백 |
|
|
|
|
|
|
|
|
|
- 무중단 교체: 새 바이너리를 임시 전송 후 **같은 파일시스템에서 rename** 으로 교체(실행 중 `ETXTBSY` 회피) → `systemctl restart`. |
|
|
|
|
- 로그 확인: `journalctl -u slack-notifier -f` 또는 `tail -f /opt/slack-notifier/logs/app-$(date +%F).log` |
|
|
|
|
- 롤백: 이전 빌드의 바이너리로 다시 배포(Jenkins "Rebuild" 또는 이전 커밋으로 빌드). 필요하면 배포 전 `cp slack-notifier slack-notifier.bak` 단계를 추가해도 됨. |
|
|
|
|
- 교체 순서: 바이너리(임시명)·start.sh 전송 → `start.sh stop` → `mv` 로 교체(정지 상태라 `ETXTBSY` 없음) → `start.sh start prod` → `status`. |
|
|
|
|
- 로그 확인: `tail -f /data/app/notifier/logs/app-$(date +%F).log` (구조적 로그) / `logs/console.log` (stdout·패닉) |
|
|
|
|
- 롤백: 이전 커밋으로 다시 빌드/배포. 필요하면 배포 전 `cp slack-notifier slack-notifier.bak` 단계를 추가해도 됨. |
|
|
|
|
|
|
|
|
|
--- |
|
|
|
|
|
|
|
|
|
## 4. 수동 배포(참고, Jenkins 없이) |
|
|
|
|
|
|
|
|
|
```bash |
|
|
|
|
make build-linux ARCH=arm64 # 정적 바이너리 생성 |
|
|
|
|
scp slack-notifier ubuntu@EC2:/tmp/sn.new |
|
|
|
|
ssh ubuntu@EC2 'sudo mv /tmp/sn.new /opt/slack-notifier/slack-notifier && sudo systemctl restart slack-notifier' |
|
|
|
|
make build-linux ARCH=amd64 # 정적 바이너리 생성 |
|
|
|
|
sshpass -e scp -P 22 slack-notifier deploy/start.sh ec2-user@EC2:/data/app/notifier/ # SSHPASS 환경변수 사용 |
|
|
|
|
ssh ec2-user@EC2 'cd /data/app/notifier && chmod +x start.sh && ./start.sh restart prod && ./start.sh status' |
|
|
|
|
``` |
|
|
|
|
|