2026-06-18
Today
EKS CI/CD를 종이 위에서 한 바퀴 다 돌린 날. 클러스터가 실제로 떴는지 확인하는 데서 시작해 IRSA → Jenkins 파이프라인 → ArgoCD/GitOps까지 설계가 이어졌다.
- dev-eks 실측 — 구축담당이 EKS 모듈을 dev에 apply.
dev-eks ACTIVE, OIDC provider 등록 확인. IAM에 OIDC provider 2개 공존(EKS·GHA). 모듈엔 IRSA 역할이 하나도 없어서 그건 내 몫으로 확정. 단 클러스터 access entry에 내가 빠져 있어 kubectl은 403(apply한 영우님만 admin).
- jenkins-tf-runner IRSA 설계 — 신뢰정책·권한정책·permission boundary 3종 실값 확정.
iam:CreateRole을 주되 boundary 없이는 못 만들게 막고, 그 boundary를 떼지도 못하게 해서 권한 상승을 2겹으로 차단.
- Jenkins Terraform 파이프라인 §3 완성 — Checkout부터 Slack까지. checkov는 soft→baseline→hard로 단계 강화. 승인은 ECS처럼 자동으로 가되, 사람 게이트를 뺀 자리를 파괴-변경 가드(CI
jq)와 prevent_destroy로 메움.
- ArgoCD/GitOps 설계 — 구축담당이 ArgoCD·LBC·App-of-Apps를 이미 깔아둔 걸 확인. 토폴로지는 클러스터별(B), 멀티환경은 Kustomize overlay로 확정. 첫 컴포넌트로 External Secrets를 잡음.
- 블루/그린 ALB 분석 — 구축담당이 ALB 가중 TG로 ECS↔EKS 전환 블루그린을 깔아둠. 여기서 "ALB=마이그레이션 / Istio=버전 카나리" 2층을 구분한 게 핵심. Istio 카나리 선택이 더 맞다는 근거가 됐다.
- 산출물 — 설계서 2개(EKS CI/CD ①Terraform파이프라인 ②GitOps, 번호 시리즈로 통일) + 블로그 9·10편(humanize 완료) + 메모리·타임라인·MOC 등 문서 전수 갱신.
Notes
오늘 흐름 한 줄 정리
"EKS가 실제로 떴다 → 그 위에 내 IRSA·CI/CD·GitOps를 설계" 로 정리된다. 설계는 거의 완성, 실제 apply·검증만 남았다(access entry 등록·부트스트랩 의존).
구축담당과 맞춰야 할 경계
설계하다 보니 내 영역과 구축담당 영역이 겹치는 지점이 드러났다.
- IRSA 소유권 — 구축담당이 LBC IRSA를 직접 만들었다. LBC는 부트스트랩이라 예외로 두되, 나머지(ESO·Rollouts·앱·jenkins-runner)는 내가 설계하는 걸로 합의 필요.
- permission boundary 팀 규약 — LBC 역할엔 boundary가 없다. 나만 boundary 쓰면 우회 경로가 남으니 팀 IRSA에 규약을 맞춰야 한다.
- state 분리 — 구축담당은 IRSA를 dev 루트 state에 인라인 중. 나는
dev/irsa.tfstate로 분리하려 했는데, 마찰을 줄이려면 합류가 실용적일 수도. 재고 중.
- ALB weight 소유권 — 전환 중 리스너 weight를 누가 옮기고, terraform
ignore_changes를 언제 켤지.
Learned
IRSA (IAM Roles for Service Accounts)
- EKS에서 Pod 단위로 AWS IAM 권한을 부여하는 메커니즘
- 기존 문제: EC2 노드에 IAM Role 부여 → 노드 내 모든 Pod가 동일 권한 (최소 권한 원칙 위반)
- 해결: Kubernetes ServiceAccount ↔ IAM Role 1:1 매핑
- 동작 흐름: OIDC Provider → Trust Policy 조건 매칭 → STS
AssumeRoleWithWebIdentity → 임시 자격증명 발급
- ECS Task Role과 대응 관계:
- ECS: Task Definition에 Task Role 지정 (Task 단위)
- EKS: ServiceAccount에 IAM Role annotation (Pod 단위)
- 장점: 최소 권한 원칙 / STS 임시 토큰(자동 로테이션) / CloudTrail 감사 추적 가능
Checkov
- IaC 정적 분석 보안 스캐너 (오픈소스, Bridgecrew/Palo Alto Networks)
- Terraform·CloudFormation·K8s·Helm·Dockerfile 등 멀티 프레임워크 지원
- 배포 전 보안 취약점 탐지 (Shift-Left Security)
- 탐지 예시: 과도한 IAM 권한, 열린 SG, 암호화 미적용, 로깅 누락
- 각 검사 항목에 CKV ID 부여 (예:
CKV_AWS_18 = S3 access logging 누락)
- CI/CD 파이프라인(GitHub Actions)에 통합하여 PR 시 자동 스캔 가능
- tfsec 대비 장점: 멀티 프레임워크 지원, Python으로 커스텀 정책 작성
Application Auto Scaling이 desiredCount 하한을 강제한다
- ECS 서비스에 Auto Scaling(min=1)이 걸리면
UpdateService desiredCount=0으로 내려도 "min 위반"으로 즉시 1로 복원 → dev 야간종료가 무력화됐던 원인.
- 해결: stop 시
RegisterScalableTarget으로 Min=0/Max=0부터 내린 뒤 desired=0. start는 Min1/Max2 복원.
- 검증:
aws application-autoscaling describe-scalable-targets의 MinCapacity(0이면 정상).
CI/CD IAM 역할 난립이 왜 위험한가
- 안 쓰는 CodePipeline/CodeBuild 실험 역할이 쌓이면 공격표면↑(강권한 유휴 역할 assume 가능)·최소권한 위배·감사 추적 혼탁.
- 정리: Access Advisor last-used로 미사용 확인 후 백업·삭제.
AUTH_DEV_MASTER = 인증 우회 (편의 기능의 양날)
- enabled=true면 특정 code(
DEV_MASTER)로 OAuth를 건너뛰고 JWT 발급. 우회코드가 공개·고정값이면 백도어가 됨.
- 완화: 우회코드 난수화 + Secrets Manager 이관(제거 대신).
Jenkins vs GitHub Actions
- 상호배타 아님(공존 가능). GHA=SaaS·OIDC 키리스·제로인프라 / Jenkins=셀프호스팅·플러그인 방대·운영(패치)책임.
- 선택 기준: 클라우드네이티브·키리스면 GHA, 엔터프라이즈 표준·복잡 파이프라인이면 Jenkins.
EKS CI/CD = AWS 권장 2레이어
- AWS 권장: CI 도구가 빌드/검증 → GitOps(ArgoCD/Flux)가 "배포 단계". CI에서
kubectl apply보다 GitOps(풀·드리프트감지·감사) 권장.
- 분리 원칙: "IAM/인프라는 terraform(CI), K8s 오브젝트는 GitOps".
GitHub Actions OIDC ≠ EKS IRSA OIDC
- 둘 다 키리스지만 issuer가 다름: GHA=
token.actions.githubusercontent.com(sub=repo:org/repo), EKS=oidc.eks...(sub=system:serviceaccount:ns:sa).
- issuer가 달라 GHA용을 IRSA에 재사용 불가. 전환 후 계정에 OIDC provider 2개 공존.
Jenkins on EKS 부트스트랩 (닭-달걀)
- Jenkins가 Terraform으로 EKS를 만드는데 정작 Jenkins는 그 EKS 위에 산다 → 자기 클러스터를 못 만듦.
- 해결: 최초 클러스터는 수동/로컬 apply(부트스트랩), 이후 day-2 변경만 Jenkins.
IRSA = 신뢰정책 + 권한정책 (의존성 분리)
- 신뢰정책(누가 assume = OIDC): 클러스터 필요 → 나중. 권한정책(무엇 가능): 클러스터 무관 → 미리 설계 가능.
- 덕분에 클러스터 전에 권한정책·Jenkinsfile·agent 이미지를 선제 준비(반응형 접근).
- GitHub Actions에서 Terraform 코드 PR 시 배포 전 자동 검증하는 단계
- 실행 도구 4가지:
terraform fmt -check(포맷) → terraform validate(문법) → tflint(린트) → checkov(보안)
- 앞 3개는 pass/fail 명확(자동), checkov만 결정 필요 (false positive, 심각도 다양)
- 결정 포인트: soft-fail(경고만) vs hard-fail-on CRITICAL,HIGH(심각한 것만 블로킹) vs 전체 hard-fail
- skip 정책:
.checkov.yml 또는 인라인 #checkov:skip=CKV_ID:사유로 의도적 예외 관리
- 베이스라인(
--create-baseline): 지금 있는 위반을 동결하고 신규 위반만 실패시킴 → 레거시 부채를 다 못 고쳐도 "더 나빠지지 않기"를 즉시 강제
- 점진 강화 패턴: 보안 게이트는 soft(관측) → baseline(신규 차단) → hard-fail-on HIGH(심각도 차단) 순으로 올린다. 처음부터 전체 차단하면 우회·skip 남발로 무력화됨 (WAF Count→Block과 동일 원리)
SCM (Source Code Management)
- 소스 코드 관리 시스템 = Git + 호스팅 플랫폼 통칭 (GitHub, GitLab, Bitbucket, CodeCommit 등)
- CI/CD에서 "SCM에서 코드 받아오는 단계" =
actions/checkout 또는 git clone (첫 단계, 설계 결정 불필요)
- VCS(Version Control System)와 거의 동의어로 혼용됨 (VCS=도구 자체, SCM=플랫폼 포괄)
Jenkins 동작 원리
- Controller-Agent 구조: Controller(중앙)가 스케줄링·UI·큐 관리, Agent(워커)가 실제 빌드 실행
- Executor = Agent 내 동시 실행 슬롯 수
- 실행 흐름: 트리거 → 빌드 큐 등록 → label 매칭으로 Agent 할당 → Jenkinsfile 실행 → 결과 리포트
- Agent 연결: SSH(전통) / JNLP(Agent→Controller 역접속) / Kubernetes Plugin(Pod 동적 생성·삭제)
- EKS 위 Jenkins: Controller Pod만 상주, 빌드 시 Agent Pod 동적 생성 → 끝나면 삭제 (비용 절감)
- 확장: Plugin 기반 (1,800+개), 설정은 Jenkinsfile(Groovy) + UI
- GHA 대비: 셀프호스팅(운영 부담↑) 대신 플러그인·커스터마이징 자유도 높음
- terraform은 시키면 지운다 — "위험하니 막기"는 terraform 밖에서 따로 넣어야 함.
- CI 가드:
terraform show -json으로 plan을 떠 change.actions에 delete 든 리소스를 jq로 세고, 있으면 파이프라인이 exit 1로 차단. 차단 주체 = CI(파이프라인), terraform 아님. (policy as code — OPA/conftest/Sentinel로 확장)
lifecycle { prevent_destroy = true }: terraform-native 최후 방어선. 박아둔 리소스를 지우는 plan이면 terraform core가 직접 거부. 핵심 리소스(RDS·state 버킷)에만, 삭제만 막음.
- 실무 = 둘 다(넓게 CI + 깊게 terraform).
- replace =
["delete","create"]: in-place 불가 변경은 지우고 새로 만듦(무중단 아님, 데이터 손실 위험) → 가드 핵심 대상.
- 인자 없는
terraform apply는 그 자리서 plan을 재계산 → 검토한 것과 다른 게 적용될 수 있음.
plan -out=tfplan → apply tfplan = 저장한 계획만 적용. 그새 state가 바뀌면 "stale" 거부(오래된 계획이 최신 인프라 덮어쓰기 방지).
Jenkins 승인 게이트 실무 패턴
- 사람 승인(
input)은 agent none으로 — pod 안 잡고 대기(유휴비용·controller 재시작 시 input 유실 방지). "승인 구간은 executor 점유 금지"가 정석.
stash/unstash = 같은 빌드의 stage 간 파일 전달(Plan pod → Apply pod로 tfplan). 빌드 내 전달이라 아티팩트와 다름.
submitter로 승인자 제한(GHA Environments Required reviewers 대응). 직무 분리 = 작성자 ≠ 승인자.
GitOps
- Git을 단일 진실 원천(SSOT)으로 삼아, Git 상태 = 클러스터 실제 상태가 되도록 자동 동기화하는 운영 모델
- 핵심 4원칙: 선언적 / Git=SSOT / 자동 적용 / 드리프트 감지·자가 복구
- Pull 모델: 클러스터 내부의 Agent(ArgoCD/Flux)가 Git을 감시 → 변경 감지 시 sync (CI에 클러스터 credential 불필요)
- Push(전통 CI/CD)와 차이: CI가
kubectl apply 직접 실행 vs GitOps Agent가 내부에서 당겨옴
- 실무 분리: CI = 빌드·테스트·이미지 push까지 / 배포 = GitOps 도구 담당
- 대표 도구: ArgoCD(UI·멀티클러스터·RBAC), Flux(CLI·가벼움·이미지 자동 업데이트)
- 장점: 보안(credential 노출 없음) / 감사(Git 커밋=배포 이력) / 롤백(
git revert) / 드리프트 자동 복구
Prompt(회고)
- 오늘부터 우리 프로젝트는 EKS로 진입을 시작했다. ECS 때처럼 내가 담당한 부분인 CI/CD 설계를 했다. 일단 ECS와 달리 ArgoCD와 Jenkins를 사용하려고 한다. 왜냐하면 일단 채용공고에 나오는 기술을 해봐야하지 않나 생각으로 한다.
- ECS와 달리 많은 Learning Curve가 필요한 부분인 만큼 더 시간을 할애해서 잘해봐야겠다.