# 4. CI/CD 서비스 관리

[목차로 돌아가기](./README.md) | 서버: sam-cicd (110.10.147.46)

---

## Jenkins

**서비스 제어:**

```bash
sudo systemctl start jenkins
sudo systemctl stop jenkins
sudo systemctl restart jenkins
sudo systemctl status jenkins
```

**설정 파일:**

| 파일 | 용도 |
|------|------|
| /var/lib/jenkins/ | Jenkins 홈 (jobs, plugins, credentials) |
| /etc/systemd/system/jenkins.service.d/override.conf | JVM 메모리 설정 |
| /var/lib/jenkins/env-files/ | 배포 환경변수 (.env 파일) |
| /var/lib/jenkins-agent/ | Agent 워크스페이스 (빌드 실행 격리) |
| /etc/systemd/system/jenkins-agent.service | Agent systemd 서비스 |

**JVM 메모리 설정:**

```bash
# /etc/systemd/system/jenkins.service.d/override.conf
# Environment="JAVA_OPTS=-Xmx2048m -Xms512m -Djava.awt.headless=true"

# 변경 후 적용
sudo systemctl daemon-reload
sudo systemctl restart jenkins
```

**로그:**

```bash
sudo journalctl -u jenkins -f
sudo journalctl -u jenkins --since "2 hours ago" --no-pager
```

**웹 UI:** https://ci.sam.it.kr (관리자: hskwon)

### Credential 관리

| Credential ID | 유형 | 용도 |
|--------------|------|------|
| deploy-ssh-key | SSH Username with private key | 운영/개발서버 SSH 배포 |
| gitea-api-token | Username with password | Gitea API 연동 (token을 username, 비밀번호 빈값) |

**Credential 위치:** Jenkins 관리 > Credentials > System > Global credentials

**SSH 키 경로:** /var/lib/jenkins/.ssh/id_ed25519

**환경변수 파일:**

```
/var/lib/jenkins/env-files/
  react/
    .env.develop    # 개발서버용
    .env.stage      # Stage용
    .env.main       # 운영용
```

### 설치된 주요 플러그인

- Gitea Plugin -- Gitea Webhook 연동
- SSH Agent Plugin -- SSH 키 기반 배포
- Pipeline / Workflow Aggregator -- Jenkinsfile 지원
- Pipeline Stage View -- 파이프라인 시각화
- Blue Ocean -- 모던 UI
- NodeJS Plugin -- Node.js 도구 관리 (22.22.0)

플러그인 업데이트 후 Jenkins 재시작이 필요한 경우: `sudo systemctl restart jenkins`

### Build Agent (분산 빌드)

Built-in Node의 executor는 0으로 설정되어 있으며, 빌드는 로컬 Agent(`local-agent`)에서 실행된다.

| 항목 | 값 |
|------|-----|
| Agent 이름 | local-agent |
| Workspace | /var/lib/jenkins-agent/ |
| Executor 수 | 2 |
| 라벨 | build |
| 연결 방식 | WebSocket (Inbound) |

**서비스 제어:**

```bash
sudo systemctl start jenkins-agent
sudo systemctl stop jenkins-agent
sudo systemctl restart jenkins-agent
sudo systemctl status jenkins-agent

# Agent 로그
sudo journalctl -u jenkins-agent -f
```

> **참고**: Jenkins 마스터 재시작 시 Agent가 자동 재연결된다. Agent가 연결 실패하면 `sudo systemctl restart jenkins-agent`로 수동 재시작.

### Workspace 정리

```bash
# Agent workspace 용량 확인
sudo du -sh /var/lib/jenkins-agent/workspace/*

# 특정 workspace 삭제
sudo rm -rf /var/lib/jenkins-agent/workspace/<JOB_NAME>

# 전체 workspace 정리 (빌드 중이 아닌지 확인 후)
sudo rm -rf /var/lib/jenkins-agent/workspace/*

# 레거시 Built-in workspace (이전 빌드 잔존 시)
sudo du -sh /var/lib/jenkins/workspace/*
sudo rm -rf /var/lib/jenkins/workspace/*

# 임시 파일 정리
sudo find /tmp -name "jenkins*" -mtime +7 -delete
```

---

## Gitea

**서비스 제어:**

```bash
sudo systemctl start gitea
sudo systemctl stop gitea
sudo systemctl restart gitea
sudo systemctl status gitea
```

**설정 파일:**

| 파일 | 용도 |
|------|------|
| /etc/gitea/app.ini | 메인 설정 |
| /var/lib/gitea/data/repositories/ | Git 저장소 데이터 |
| /var/lib/gitea/log/ | Gitea 로그 |
| /var/lib/gitea/custom/ | 커스텀 설정 |

**주요 설정 (app.ini):**

```ini
[server]
DOMAIN           = git.sam.it.kr
HTTP_PORT        = 3000
ROOT_URL         = https://git.sam.it.kr/

[service]
DISABLE_REGISTRATION = true    # 회원가입 비활성화
REQUIRE_SIGNIN_VIEW  = true    # 로그인 필수
```

**로그:**

```bash
sudo journalctl -u gitea -f
sudo tail -f /var/lib/gitea/log/gitea.log
```

**웹 UI:** https://git.sam.it.kr (관리자: hskwon)

### 저장소 현황

| Organization | 저장소 | 설명 |
|-------------|--------|------|
| SamProject | sam-api | Laravel REST API |
| SamProject | sam-manage | Laravel Admin (mng) |
| SamProject | sam-react-prod | Next.js 프론트엔드 |
| SamProject | sam-sales | 영업자 사이트 (레거시) |

### 사용자/조직 관리

- 사이트 관리: https://git.sam.it.kr/-/admin
- 사용자 관리: https://git.sam.it.kr/-/admin/users
- 조직 관리: https://git.sam.it.kr/-/admin/orgs

**CLI로 사용자 추가:**

```bash
sudo -u git /usr/local/bin/gitea admin user create \
  --config /etc/gitea/app.ini \
  --username 사용자명 \
  --password 비밀번호 \
  --email 이메일 \
  --admin    # 관리자 권한 (선택)
```

### Webhook 설정

각 저장소에 Jenkins Webhook이 설정되어 있다.

| 항목 | 값 |
|------|-----|
| URL | https://ci.sam.it.kr/gitea-webhook/post |
| Content Type | application/json |
| Events | Push Events |

**Webhook 확인/테스트:** 저장소 > Settings > Webhooks

### 개발서버 동기화 (post-receive hook)

개발서버 Gitea에서 CI/CD Gitea로 자동 동기화:

**Hook 위치 (개발서버):** `/data/GIT/samproject/<repo>.git/hooks/post-receive.d/push-to-cicd`

**토큰 파일 (개발서버):** `/data/GIT/.cicd-env` (chmod 600, owner: git)

| 저장소 | 동기화 브랜치 | 비고 |
|--------|-------------|------|
| sam-react-prod | main, develop | post-update hook 비활성화 (CI/CD가 개발서버 배포 담당) |
| sam-api | main | develop은 기존 post-update hook 유지 |
| sam-sales | main | |
| sam-manage | main | 2026-02-24 hook 추가 |

> **참고:** react의 개발서버 배포는 Jenkins CI/CD 파이프라인이 처리한다.
> 기존 post-update hook의 git pull 방식(`pull_react.sh`)은 비활성화됨 (2026-02-24).
> 스크립트 위치: `/home/webservice/script/pull_react.sh`

**동기화 로그 확인:**

```bash
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_react-prod.log"
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_api.log"
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_sales.log"
ssh sam-dev "tail -20 /home/webservice/logs/cicd_push_manage.log"
```

---

## Prometheus

**서비스 제어:**

```bash
sudo systemctl start prometheus
sudo systemctl stop prometheus
sudo systemctl restart prometheus
sudo systemctl status prometheus
```

**설정 파일:**

| 파일 | 용도 |
|------|------|
| /etc/prometheus/prometheus.yml | 스크래핑 설정 |
| /var/lib/prometheus/ | 시계열 데이터 |

**바인딩:** 127.0.0.1:9090 (외부 접근 차단)

**데이터 보존:** 30일 (--storage.tsdb.retention.time=30d)

**설정 변경 후 적용:**

```bash
promtool check config /etc/prometheus/prometheus.yml    # 문법 검사
sudo systemctl restart prometheus
# 또는 설정 리로드 (재시작 없이)
curl -X POST http://localhost:9090/-/reload
```

---

## Grafana

**서비스 제어:**

```bash
sudo systemctl start grafana-server
sudo systemctl stop grafana-server
sudo systemctl restart grafana-server
sudo systemctl status grafana-server
```

**설정 파일:**

| 파일 | 용도 |
|------|------|
| /etc/grafana/grafana.ini | 메인 설정 |
| /var/lib/grafana/ | 대시보드 데이터, 플러그인 |

**주요 설정:**

```ini
[server]
http_port = 3100
domain = monitor.sam.it.kr

[users]
allow_sign_up = false
```

**웹 UI:** https://monitor.sam.it.kr

---

## MySQL (CI/CD)

```bash
sudo systemctl status mysql
sudo systemctl restart mysql

# 접속
mysql                    # hskwon (auth_socket)
sudo mysql               # root (auth_socket)
```

**주요 튜닝 설정:**

```ini
innodb_buffer_pool_size = 1536M
max_connections = 50
slow_query_log = 1
long_query_time = 2
```

**데이터베이스:** gitea (Gitea 데이터)

---

## Nginx (CI/CD)

```bash
sudo nginx -t && sudo systemctl reload nginx    # 무중단 리로드
sudo systemctl restart nginx
sudo systemctl status nginx
```

**사이트 설정:**

| 파일 | 서비스 |
|------|--------|
| /etc/nginx/sites-available/git.sam.it.kr | Gitea 리버스 프록시 |
| /etc/nginx/sites-available/ci.sam.it.kr | Jenkins 리버스 프록시 |
| /etc/nginx/sites-available/monitor.sam.it.kr | Grafana 리버스 프록시 |

**git.sam.it.kr 주요 설정:**

```nginx
client_max_body_size 500M;          # 대용량 Git push 허용
proxy_request_buffering off;        # 스트리밍 전송 (413 방지)
```

---

## node_exporter / Certbot / fail2ban / UFW

운영서버와 동일한 명령어 체계. [운영서버 서비스 관리](./03-service-prod.md) 참조.

**UFW 규칙 (CI/CD):**

| 포트 | 프로토콜 | 용도 |
|------|---------|------|
| 22/tcp | ALLOW | SSH |
| 80/tcp | ALLOW | HTTP |
| 443/tcp | ALLOW | HTTPS |