8.3 KiB
8.3 KiB
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
#!/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 에서 정리.
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)
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 "$@" 패턴
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 은 분리
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 을 쓴다:
kubectl apply -k "${OVERLAY}" --dry-run=server
10. destructive 작업은 --yes 또는 CONFIRM=yes gate
delete / prune / restore overwrite 류는 명시적 opt-in 없이 실행 금지.
if [[ "${CONFIRM:-no}" != "yes" ]]; then
fatal "destructive operation requires CONFIRM=yes"
fi
또는:
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 로 하지 않는다
# 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:
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. 폴더 구조
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 는 함수화, 무한 루프 금지
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=yesgate. kubectl diff→apply,--dry-run=servervalidation.- 환경 추론 금지, overlay/namespace/context 명시.
- JSON 은 jq / jsonpath, 절대 regex 금지.
- secret 은 log / argv 에 남기지 않고 masking.
- 스크립트는 선언형 원본을 소유하지 않는 orchestration 레이어.
bin/ lib/ tasks/ ci/폴더 분리, giant deploy.sh 금지.