init: 폴더구조 설계 및 인프라 설계

This commit is contained in:
DongHyeonka
2026-07-24 14:31:53 +09:00
parent 34ad612281
commit f9c463f87a
1839 changed files with 323096 additions and 1 deletions
+293
View File
@@ -0,0 +1,293 @@
# infra scripts 기준
## 목적
이 문서는 1000+ 서비스 플랫폼에서 인프라 스크립트가 지켜야 할 품질 기준선이다. 스크립트는 선언형 원본 (Kustomize / Helm / ArgoCD / Flux) 을 **대체하지 않는다**. 렌더 / diff / 적용 / 백업 / 복구 / 부트스트랩을 **orchestration** 하는 얇은 레이어로 제한한다.
## 공식 / 업계 근거
- **Google Shell Style Guide**: `#!/usr/bin/env bash`, `set -e`, `main "$@"`, function-first, `local`.
- **Unofficial Bash Strict Mode (Aaron Maxwell)**: `set -euo pipefail` + `IFS=$'\n\t'` 가 사실상 표준.
- **ShellCheck** (https://www.shellcheck.net/): 정적 분석. CI에서 mandatory.
- **shfmt** (mvdan/sh): 자동 포맷터. line-length / indent 규격 강제.
- **GitOps 원칙** (Weaveworks 정의): 선언형 원본 + auto-reconcile. 스크립트는 원본을 소유하지 않는다.
## 기본 규칙
### 1. 모든 스크립트 맨 위에 strict mode
```bash
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
```
의미:
- `set -e` : 명령 실패 시 즉시 종료.
- `set -u` : unset variable 참조 시 에러.
- `set -o pipefail` : pipeline 중 하나라도 실패하면 전체 실패.
- `IFS=$'\n\t'` : 기본 IFS에서 space 제거 → 파일명 공백 sane split.
예외 금지. CI lint 에서 검사.
### 2. 정리 작업은 `trap` 으로 보장
임시 파일 / 임시 kubeconfig / port-forward / background job 은 반드시 trap EXIT 에서 정리.
```bash
TMPDIR="$(mktemp -d)"
trap 'rm -rf "${TMPDIR}"' EXIT INT TERM
```
- `EXIT`: 정상/비정상 종료 모두 잡음.
- `INT TERM`: signal 기반 종료 시에도 실행.
- trap은 setup 직후 즉시 설치.
### 3. ShellCheck + shfmt 는 CI 에서 필수
- `shellcheck -S style scripts/**/*.sh` → CI fail 시 merge 금지.
- `shfmt -i 2 -bn -ci -d scripts/` → 자동 포맷 검증.
- suppress (`# shellcheck disable=...`) 는 **줄 단위**로만, 이유 주석 필수.
- "경고 너무 많아서 꺼둔다" 금지.
### 4. 표준 `log()` 함수 (ISO 8601 timestamp + level, stderr)
```bash
log() {
local level="$1"; shift
local ts
ts="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
printf '%s [%s] %s\n' "${ts}" "${level}" "$*" >&2
}
info() { log INFO "$@"; }
warn() { log WARN "$@"; }
error() { log ERROR "$@"; }
fatal() { log FATAL "$@"; exit 1; }
```
- stdout 은 머신 판독용 결과 전용.
- stderr 로 로그 → pipeline 안전.
- UTC ISO 8601 로 tz 모호성 제거.
### 5. 엔트리포인트 `main "$@"` 패턴
```bash
usage() {
cat <<'EOF' >&2
Usage: render-diff-apply.sh [--overlay PATH] [--context NAME] [--yes]
--overlay PATH path to kustomize overlay (required)
--context NAME kube context (required)
--yes skip confirmation for apply
EOF
}
main() {
# 인자 파싱
# 환경 검증
# 함수 호출
:
}
main "$@"
```
- 엔트리포인트 스크립트는 **얇게**. 비즈니스 로직은 `lib/` 또는 `tasks/`.
- `usage()` 함수 필수.
### 6. 변수는 `local`, command substitution 은 분리
```bash
bad_pattern() {
local ctx="$(kubectl config current-context)" # local 이 exit status 가려버림
}
good_pattern() {
local ctx
ctx="$(kubectl config current-context)" # 분리 → $? 보존
}
```
ShellCheck SC2155 가 이것을 잡음.
### 7. Idempotent 를 기본값으로
- `create` 보다 `apply` / `ensure` 성격.
- `kubectl apply -k` 는 idempotent.
- `mkdir -p`, `kubectl create namespace X --dry-run=client -o yaml | kubectl apply -f -` 패턴.
- destroy 성격은 반드시 opt-in.
### 8. `kubectl diff` → `kubectl apply` 필수 흐름
프로덕션 적용 스크립트 기본 흐름:
```
1. kubectl kustomize <overlay> > render.yaml # render
2. kubeconform / kubectl apply --dry-run=server # validate
3. kubectl diff -k <overlay> # preview
4. confirm gate (CONFIRM=yes 또는 --yes)
5. kubectl apply -k <overlay> # apply
6. kubectl rollout status ... --timeout=10m # watch
```
### 9. `--dry-run=server` 를 validation 기본값으로
client-side dry run 은 CRD schema / admission webhook 을 평가하지 않는다. **server-side dry run** 을 쓴다:
```bash
kubectl apply -k "${OVERLAY}" --dry-run=server
```
### 10. destructive 작업은 `--yes` 또는 `CONFIRM=yes` gate
delete / prune / restore overwrite 류는 명시적 opt-in 없이 실행 금지.
```bash
if [[ "${CONFIRM:-no}" != "yes" ]]; then
fatal "destructive operation requires CONFIRM=yes"
fi
```
또는:
```bash
if [[ "${YES:-0}" -ne 1 ]]; then
warn "re-run with --yes to confirm"
exit 2
fi
```
### 11. 환경을 암묵적으로 추론하지 않는다
- 대상 overlay / namespace / context 는 **명시적 인자**로.
- `kubectl config current-context` 에 몰래 의존 금지.
- 필요한 env var 는 시작 시 `[[ -z "${FOO:-}" ]] && fatal "FOO required"` 로 검증.
### 12. JSON 파싱은 `jq` / `kubectl -o jsonpath`, 절대 regex 로 하지 않는다
```bash
# BAD
kubectl get pod foo -o yaml | grep "image:" | awk '{print $2}'
# GOOD
kubectl get pod foo -o jsonpath='{.spec.containers[0].image}'
# GOOD
kubectl get pod foo -o json | jq -r '.spec.containers[0].image'
```
kubectl/kubernetes 출력에 regex 쓰면 field 순서 / 라벨 / 버전 변화에 깨진다.
### 13. 비밀값은 로그 / stdout / 파일에 남기지 않는다
- env var / secret value 를 `set -x` 아래에서 직접 사용 금지.
- debug 모드에서는 masking:
```bash
mask_secrets() {
sed -E \
-e 's/(password=)[^ ]+/\1***/g' \
-e 's/(token=)[^ ]+/\1***/g' \
-e 's/(Authorization: Bearer )[A-Za-z0-9._-]+/\1***/g'
}
some_command --debug | mask_secrets
```
- secret 을 참조해야 하면 `--from-file` 이나 stdin pipe 로 주입, argv 금지.
### 14. 스크립트는 선언형 원본을 소유하지 않는다
**금지**:
- 대규모 heredoc YAML 생성기 (스크립트 내부에 매니페스트 숨김).
- 환경별 로직이 if/else 로만 존재.
- 스크립트만 실행해야 실제 상태를 알 수 있는 구조.
**허용**:
- `kubectl apply -k overlays/<env>` wrapping.
- Helm chart render + apply orchestration.
- backup / restore (stateful data 만 대상).
- bootstrap (namespace, secret store 설치 같은 일회성).
- smoke test.
### 15. 폴더 구조
```text
scripts/
bin/ # 엔트리포인트 (얇게)
render
diff
apply
backup-k3s
restore-k3s
lib/ # 공통 함수
common.sh # log, fatal, require_cmd, confirm
kubectl.sh # kubectl wrappers
kustomize.sh # kustomize render helpers
tasks/ # 도메인 작업
keycloak.sh
vault.sh
flyway.sh
ci/ # CI 검증 전용
lint.sh
validate.sh
```
- `bin/` 파일 이름은 동사.
- `lib/` 는 20개 내외, 잡동사니 함수 금지.
- 하나의 거대 `deploy.sh` 금지.
### 16. retry 는 함수화, 무한 루프 금지
```bash
retry() {
local max="$1"; shift
local delay="$1"; shift
local n=0
until "$@"; do
n=$((n + 1))
if (( n >= max )); then
return 1
fi
sleep "${delay}"
done
}
retry 5 3 kubectl rollout status deployment/foo --timeout=30s
```
backoff 는 선형/지수 명시, 무한 retry 금지.
### 17. quoting / array 기본값
- 모든 변수 전개는 `"${VAR}"`.
- 인자 list 는 array: `args=(--namespace foo --context bar)`.
- `"$@"` 유지.
- unquoted glob / word splitting 금지.
### 18. 출력 채널 규칙
- stdout → 머신 판독 결과 (jsonpath 결과, 렌더된 YAML 등).
- stderr → 로그, 경고, 에러, 진행 표시.
- exit code → 0 success, 1 error, 2 usage error.
pipeline 하류 도구가 stdout 을 parse 한다는 전제로 작성.
## 프로젝트 기준 요약
- strict mode `set -euo pipefail` + `IFS=$'\n\t'` 필수.
- trap EXIT INT TERM 으로 정리 보장.
- ShellCheck + shfmt CI 필수.
- ISO 8601 UTC + LEVEL 로그 함수 (stderr).
- `main "$@"` 패턴 + usage() 함수.
- local 선언과 command substitution 분리.
- idempotent 기본, destructive 는 `--yes` / `CONFIRM=yes` gate.
- `kubectl diff``apply`, `--dry-run=server` validation.
- 환경 추론 금지, overlay/namespace/context 명시.
- JSON 은 jq / jsonpath, 절대 regex 금지.
- secret 은 log / argv 에 남기지 않고 masking.
- 스크립트는 선언형 원본을 소유하지 않는 orchestration 레이어.
- `bin/ lib/ tasks/ ci/` 폴더 분리, giant deploy.sh 금지.