[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,395 @@
|
||||
---
|
||||
id: devops-external-secrets-atlantis
|
||||
title: External Secrets / Atlantis / GitHub Actions deep
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [devops, secrets, gitops, vibe-coding]
|
||||
tech_stack: { language: "YAML", applicable_to: ["DevOps"] }
|
||||
applied_in: []
|
||||
aliases: [external secrets operator, Atlantis, Terraform PR, GitHub Actions, OIDC, reusable workflow]
|
||||
---
|
||||
|
||||
# External Secrets / Atlantis / GHA
|
||||
|
||||
> K8s + Vault + Terraform + CI 의 modern integration. **External Secrets Operator (vault → K8s), Atlantis (Terraform PR), GHA (advanced)**.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- ESO: 외부 secret store → K8s Secret 자동 sync.
|
||||
- Atlantis: Terraform 의 GitOps.
|
||||
- GHA OIDC: cloud auth 가 token 없이.
|
||||
- Reusable workflow / matrix.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### External Secrets Operator
|
||||
```yaml
|
||||
# SecretStore (Vault 연결)
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: SecretStore
|
||||
metadata:
|
||||
name: vault-store
|
||||
spec:
|
||||
provider:
|
||||
vault:
|
||||
server: https://vault.example.com
|
||||
path: secret
|
||||
auth:
|
||||
kubernetes:
|
||||
mountPath: kubernetes
|
||||
role: my-role
|
||||
```
|
||||
|
||||
```yaml
|
||||
# ExternalSecret (sync)
|
||||
apiVersion: external-secrets.io/v1beta1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: db-credentials
|
||||
spec:
|
||||
refreshInterval: 1m
|
||||
secretStoreRef:
|
||||
name: vault-store
|
||||
kind: SecretStore
|
||||
target:
|
||||
name: db-secret # → K8s Secret
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: secret/myapp/db
|
||||
property: password
|
||||
```
|
||||
|
||||
→ App 가 K8s Secret 만 read. Vault 가 source.
|
||||
|
||||
### AWS Secrets Manager 도
|
||||
```yaml
|
||||
spec:
|
||||
provider:
|
||||
aws:
|
||||
service: SecretsManager
|
||||
region: us-east-1
|
||||
auth:
|
||||
jwt:
|
||||
serviceAccountRef:
|
||||
name: external-secrets-sa
|
||||
```
|
||||
|
||||
→ AWS IRSA (IAM Role for SA).
|
||||
|
||||
### Auto rotation
|
||||
```yaml
|
||||
# AWS Secrets Manager 가 매 30 days rotate
|
||||
# ESO 가 매 1 min check + sync
|
||||
# K8s pod 가 secret mount = 자동 update (eventually)
|
||||
```
|
||||
|
||||
### Pod restart
|
||||
```yaml
|
||||
# Stakater Reloader 가 secret 변경 시 pod restart
|
||||
metadata:
|
||||
annotations:
|
||||
secret.reloader.stakater.com/reload: 'db-secret'
|
||||
```
|
||||
|
||||
→ Secret 변경 → pod restart → 새 credential.
|
||||
|
||||
### Atlantis (Terraform 의 GitOps)
|
||||
```yaml
|
||||
# atlantis.yaml
|
||||
version: 3
|
||||
projects:
|
||||
- name: prod-vpc
|
||||
dir: terraform/prod/vpc
|
||||
autoplan:
|
||||
when_modified: ['*.tf', '*.tfvars']
|
||||
apply_requirements: [approved]
|
||||
```
|
||||
|
||||
### Workflow
|
||||
```
|
||||
1. Engineer 가 PR 가 terraform 변경.
|
||||
2. Atlantis 가 자동 `terraform plan` → PR comment 에 결과.
|
||||
3. Reviewer 가 plan 검토.
|
||||
4. Approved → engineer 가 `atlantis apply`.
|
||||
5. Atlantis 가 `terraform apply`.
|
||||
6. PR merge.
|
||||
```
|
||||
|
||||
→ "Apply 후 merge" — drift 안.
|
||||
|
||||
### Atlantis 의 lock
|
||||
```
|
||||
매 project 가 1 PR 만 apply 가능.
|
||||
- A 가 apply 중 → B 가 wait.
|
||||
- "Apply" PR comment 가 lock 가져.
|
||||
- 종료 시 release.
|
||||
```
|
||||
|
||||
→ Concurrent apply 가 conflict 방지.
|
||||
|
||||
### terraform plan output
|
||||
```diff
|
||||
$ atlantis plan
|
||||
+ aws_instance.web
|
||||
ami = "ami-12345"
|
||||
instance_type = "t3.micro"
|
||||
- aws_security_group.old
|
||||
~ aws_db_instance.main
|
||||
~ instance_class: "db.t3.micro" -> "db.t3.small"
|
||||
```
|
||||
|
||||
→ PR 의 visual plan.
|
||||
|
||||
### Atlantis 의 alternative
|
||||
```
|
||||
- Spacelift (managed, 더 강력)
|
||||
- Terraform Cloud / HCP Terraform (HashiCorp)
|
||||
- env0 (managed)
|
||||
- Terragrunt + custom CI
|
||||
```
|
||||
|
||||
→ Atlantis 가 OSS + 자체 host.
|
||||
|
||||
### GitHub Actions OIDC (cloud auth)
|
||||
```yaml
|
||||
# .github/workflows/deploy.yml
|
||||
permissions:
|
||||
id-token: write # OIDC token
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: aws-actions/configure-aws-credentials@v4
|
||||
with:
|
||||
role-to-assume: arn:aws:iam::123456789:role/GHA-Deploy
|
||||
aws-region: us-east-1
|
||||
|
||||
- run: aws s3 sync ./build s3://my-bucket
|
||||
```
|
||||
|
||||
→ Long-lived AWS key X. OIDC token 가 short-lived.
|
||||
|
||||
→ AWS / GCP / Azure 가 OIDC trust 설정.
|
||||
|
||||
### Reusable workflow
|
||||
```yaml
|
||||
# .github/workflows/test.yml (reusable)
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
node-version:
|
||||
type: string
|
||||
default: '20'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: ${{ inputs.node-version }} }
|
||||
- run: npm ci && npm test
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Caller
|
||||
jobs:
|
||||
test:
|
||||
uses: ./.github/workflows/test.yml
|
||||
with:
|
||||
node-version: '22'
|
||||
```
|
||||
|
||||
### Matrix
|
||||
```yaml
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
node: [18, 20, 22]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: ${{ matrix.node }} }
|
||||
```
|
||||
|
||||
→ 9 jobs 가 parallel.
|
||||
|
||||
### Conditional matrix
|
||||
```yaml
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
include:
|
||||
- os: macos-latest
|
||||
special: true
|
||||
exclude:
|
||||
- os: windows-latest
|
||||
node: 18
|
||||
```
|
||||
|
||||
### Environment + secrets
|
||||
```yaml
|
||||
jobs:
|
||||
deploy:
|
||||
environment: production # → manual approval gate
|
||||
steps:
|
||||
- run: ./deploy.sh
|
||||
env:
|
||||
API_KEY: ${{ secrets.API_KEY }} # environment-scoped
|
||||
```
|
||||
|
||||
→ Prod environment 가 별 secrets + manual approval.
|
||||
|
||||
### Composite action
|
||||
```yaml
|
||||
# .github/actions/setup/action.yml
|
||||
name: 'Setup'
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with: { node-version: '20' }
|
||||
- shell: bash
|
||||
run: npm ci
|
||||
```
|
||||
|
||||
```yaml
|
||||
# Use
|
||||
- uses: ./.github/actions/setup
|
||||
```
|
||||
|
||||
→ Reusable inline.
|
||||
|
||||
### Concurrency
|
||||
```yaml
|
||||
concurrency:
|
||||
group: ${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
```
|
||||
|
||||
→ 같은 branch 의 새 push = 옛 cancel.
|
||||
|
||||
### Cache
|
||||
```yaml
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
```
|
||||
|
||||
→ Build 빠름.
|
||||
|
||||
### Dependabot
|
||||
```yaml
|
||||
# .github/dependabot.yml
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'weekly'
|
||||
```
|
||||
|
||||
→ Action 도 자동 update.
|
||||
|
||||
### Self-hosted runner
|
||||
```yaml
|
||||
runs-on: self-hosted
|
||||
# 자체 server 가 runner.
|
||||
```
|
||||
|
||||
→ Cost ↓ (큰 traffic). Setup overhead.
|
||||
|
||||
### Action security
|
||||
```
|
||||
- Pin to SHA (version 가 mutable).
|
||||
- uses: actions/checkout@v4.1.0
|
||||
- uses: actions/checkout@b32f140b0c872d58512e0a66172253c302617b90 # SHA
|
||||
|
||||
→ Supply chain 안전.
|
||||
```
|
||||
|
||||
### GitHub Actions 의 secret 주의
|
||||
```
|
||||
Secret value 가 log mask (자동).
|
||||
하지만:
|
||||
- 부분 echo 가 가능.
|
||||
- Forked PR 가 secret access X.
|
||||
- 외부 action 가 secret access (악성 가능).
|
||||
|
||||
→ Pin SHA. Secret 최소화.
|
||||
```
|
||||
|
||||
### Workflow_dispatch (manual)
|
||||
```yaml
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
env:
|
||||
description: 'Deploy environment'
|
||||
required: true
|
||||
type: choice
|
||||
options: [dev, staging, prod]
|
||||
```
|
||||
|
||||
→ UI 에서 click 가 trigger.
|
||||
|
||||
### Schedule
|
||||
```yaml
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 * * 1-5' # 평일 9 AM UTC
|
||||
```
|
||||
|
||||
→ Cron 가 GHA 안.
|
||||
|
||||
### Workflow run 의 cost
|
||||
```
|
||||
Public repo: 무료.
|
||||
Private repo:
|
||||
- 2000 min / month free.
|
||||
- $0.008 / minute (Linux).
|
||||
- $0.04 / minute (Windows / macOS).
|
||||
|
||||
→ Optimization (cache, matrix exclude) 가 매 cost.
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 작업 | 추천 |
|
||||
|---|---|
|
||||
| K8s + Vault | External Secrets Operator |
|
||||
| Terraform GitOps | Atlantis |
|
||||
| Cloud auth | GHA OIDC |
|
||||
| 반복 logic | Reusable workflow / composite action |
|
||||
| Multi-env | Environment + protection |
|
||||
| Cost-sensitive | Self-hosted runner |
|
||||
| Security | Pin SHA |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **K8s Secret 직접 commit (encrypted SealedSecret 도)**: rotation 어려움.
|
||||
- **Long-lived AWS key**: OIDC.
|
||||
- **모든 거 inline workflow**: reuse 안 됨.
|
||||
- **Secret echo**: leak.
|
||||
- **No environment**: prod 가 dev 와 같은 gate.
|
||||
- **No concurrency**: 중복 deploy.
|
||||
- **Action @main**: supply chain.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- ESO 가 K8s + 외부 secret store 의 답.
|
||||
- Atlantis 가 Terraform 의 GitOps.
|
||||
- GHA OIDC 가 modern cloud auth.
|
||||
- Reusable workflow 가 DRY.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[DevOps_Vault_Secrets]]
|
||||
- [[DevOps_Terraform_Patterns]]
|
||||
- [[DevOps_CI_CD_Pipeline_Patterns]]
|
||||
Reference in New Issue
Block a user