ArgoCD Final¶
ArgoCD 고가용성 구성, 멀티 클러스터 관리, LDAP 통합을 학습한다.
ArgoCD 고가용성 구성¶
1. 고가용성 아키텍처¶
ArgoCD 컴포넌트별 확장 전략¶
프로덕션 ArgoCD는 다음과 같은 고가용성 요구사항을 만족해야 한다:
graph TB
subgraph "High Availability Architecture"
subgraph "API/UI Layer"
AS1[ArgoCD Server<br/>Replica 1]
AS2[ArgoCD Server<br/>Replica 2]
AS3[ArgoCD Server<br/>Replica N]
end
subgraph "Application Controller Layer"
AC1[Application Controller<br/>Replica 1]
AC2[Application Controller<br/>Replica N]
end
subgraph "ApplicationSet Layer"
ASC1[ApplicationSet Controller<br/>Replica 1]
ASC2[ApplicationSet Controller<br/>Replica N]
end
subgraph "Cache Layer"
RH[Redis HA<br/>Sentinel Cluster]
RM1[Redis Master]
RS1[Redis Replica 1]
RS2[Redis Replica N]
end
subgraph "Repository Layer"
RC1[Repo Server<br/>Replica 1]
RC2[Repo Server<br/>Replica N]
end
end
LB[Load Balancer] --> AS1
LB --> AS2
LB --> AS3
AS1 --> RH
AS2 --> RH
AS3 --> RH
AC1 --> RH
AC2 --> RH
AS1 --> RC1
AS2 --> RC2
AC1 --> RC1
AC2 --> RC2
RH --> RM1
RH --> RS1
RH --> RS2
컴포넌트별 역할:
| 컴포넌트 | 역할 | HA 요구사항 |
|---|---|---|
| ArgoCD Server | API/UI 제공, 인증/인가 | 2+ replicas + LoadBalancer |
| Application Controller | Git → K8s 동기화 수행 | Shard 분할 (default: 0, max: N) |
| ApplicationSet Controller | ApplicationSet 처리 | 2+ replicas + Leader Election |
| Repo Server | Git clone, Helm render | 2+ replicas (stateless) |
| Redis HA | 캐시, 세션 저장 | Sentinel 기반 자동 failover |
고가용성 설치¶
# ArgoCD HA values 파일 생성
cat <<EOF > argocd-ha-values.yaml
# Redis HA 활성화
redis-ha:
enabled: true
haproxy:
enabled: true
replicas: 3
# Application Controller HA
controller:
replicas: 2
env:
- name: ARGOCD_CONTROLLER_REPLICAS
value: "2"
- name: ARGOCD_CONTROLLER_SHARD
valueFrom:
fieldRef:
fieldPath: metadata.name
# ArgoCD Server HA
server:
replicas: 3
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
targetCPUUtilizationPercentage: 70
# Repo Server HA
repoServer:
replicas: 2
autoscaling:
enabled: true
minReplicas: 2
maxReplicas: 5
# ApplicationSet Controller HA
applicationSet:
replicas: 2
env:
- name: ARGOCD_APPLICATIONSET_CONTROLLER_ENABLE_LEADER_ELECTION
value: "true"
EOF
# ArgoCD HA 설치
helm upgrade --install argocd argo/argo-cd \
--version 9.0.5 \
-f argocd-ha-values.yaml \
--namespace argocd
2. Redis HA 설정¶
Redis Sentinel 아키텍처¶
Redis HA는 Sentinel을 사용하여 자동 failover를 제공한다.
graph TB
subgraph "Redis HA Cluster"
S1[Sentinel 1] --> M[Redis Master]
S2[Sentinel 2] --> M
S3[Sentinel 3] --> M
S1 --> R1[Redis Replica 1]
S2 --> R1
S3 --> R1
S1 --> R2[Redis Replica 2]
S2 --> R2
S3 --> R2
M -.->|Replication| R1
M -.->|Replication| R2
end
subgraph "Failover Process"
S1 -.->|1. Master Down| X[Master Fails]
S2 -.->|2. Vote| VOTE[Quorum Vote]
S3 -.->|2. Vote| VOTE
VOTE -.->|3. Elect| NEW[🆕 New Master]
NEW -.->|4. Promote| R1
end
Sentinel 동작 방식:
- 최소 2개의 Sentinel이 Master 장애 합의
- 과반수 Sentinel이 새 Master 선출
- 선출된 Replica가 Master로 승격
- 나머지 Replica들이 새 Master를 따르도록 재구성
Redis HA 확인¶
# Redis HA Pod 확인
kubectl get pod -n argocd | grep redis
# 출력 예시:
# argocd-redis-ha-haproxy-xxx 1/1 Running
# argocd-redis-ha-server-0 3/3 Running
# argocd-redis-ha-server-1 3/3 Running
# argocd-redis-ha-server-2 3/3 Running
# Redis Master 확인
kubectl exec -n argocd argocd-redis-ha-server-0 -c redis -- \
redis-cli -p 26379 sentinel get-master-addr-by-name mymaster
# 출력 예시:
# 1) "10.244.0.15"
# 2) "6379"
# Sentinel 상태 확인
kubectl exec -n argocd argocd-redis-ha-server-0 -c sentinel -- \
redis-cli -p 26379 sentinel masters
3. ApplicationSet Controller 확장¶
Leader Election 메커니즘¶
ApplicationSet Controller는 Leader Election을 사용하여 Active-Standby 고가용성을 제공한다.
sequenceDiagram
participant L as Leader Pod
participant S as Standby Pod
participant K as K8s API (Lease)
participant G as Git Repo
L->>K: 1. Acquire Lease (leader election)
K->>L: 2. Lease granted (become leader)
L->>G: 3. Process ApplicationSets
Note over L,S: Leader is active
S->>K: 4. Try acquire Lease
K->>S: 5. Lease denied (leader exists)
Note over L: Leader Pod crashes
S->>K: 6. Detect Lease expired
K->>S: 7. Lease granted (become leader)
S->>G: 8. Take over processing
Leader Election 설정:
# ApplicationSet Controller Leader Election 확인
kubectl get lease -n argocd argocd-applicationset-controller
# 출력 예시:
# NAME HOLDER AGE
# argocd-applicationset-controller argocd-applicationset-controller-xxx-yyy 5m
# Leader Pod 확인
kubectl describe lease -n argocd argocd-applicationset-controller
# Holder Identity: argocd-applicationset-controller-7d5f9b8c5-abc12
고급 Sync 전략¶
1. Sync Windows¶
Sync Window란?¶
Sync Window는 특정 시간대에만 동기화를 허용하거나 차단하는 기능이다.
사용 사례: - 업무 시간 외(야간)에만 프로덕션 배포 - 유지보수 시간대 자동 동기화 차단 - 주중/주말 배포 정책 분리
Sync Window 설정¶
# AppProject에 Sync Window 추가
cat <<EOF | kubectl apply -f -
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: production
namespace: argocd
spec:
syncWindows:
# 평일 야간 (22:00-06:00) 배포 허용
- kind: allow
schedule: '0 22 * * 1-5'
duration: 8h
applications:
- '*'
namespaces:
- production
clusters:
- https://kubernetes.default.svc
# 주말 전체 배포 차단
- kind: deny
schedule: '0 0 * * 0,6'
duration: 24h
applications:
- '*'
# 긴급 패치 허용 (관리자만)
- kind: allow
schedule: '* * * * *' # 항상 허용
duration: 1h
manualSync: true # 수동 Sync만 허용
EOF
schedule: Cron 형식 시간표 (분 시 일 월 요일)
- duration: 윈도우 지속 시간
- kind: allow (허용) 또는 deny (차단)
- manualSync: true면 자동 Sync 차단, 수동만 허용
Sync Window 확인¶
# AppProject Sync Window 확인
argocd proj get production
# Application Sync 가능 여부 확인
argocd app get myapp | grep "Sync Windows"
2. Progressive Delivery¶
Argo Rollouts 통합¶
Argo Rollouts는 Blue-Green, Canary 배포 전략을 제공한다.
graph LR
subgraph "Canary Deployment"
V1["Version 1<br/>100% Traffic"]
V2A["Version 2<br/>10% Traffic"]
V2B["Version 2<br/>50% Traffic"]
V2C["Version 2<br/>100% Traffic"]
end
V1 -->|1. Deploy| V2A
V2A -->|2. Analysis OK| V2B
V2B -->|3. Analysis OK| V2C
V2A -.->|Analysis Fail| ROLLBACK[Rollback to V1]
V2B -.->|Analysis Fail| ROLLBACK
Rollout 예시¶
# Canary Rollout 정의
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp
spec:
replicas: 5
strategy:
canary:
steps:
- setWeight: 20 # 1단계: 20% 트래픽
- pause: {duration: 5m}
- setWeight: 40 # 2단계: 40% 트래픽
- pause: {duration: 5m}
- setWeight: 60 # 3단계: 60% 트래픽
- pause: {duration: 5m}
- setWeight: 80 # 4단계: 80% 트래픽
- pause: {duration: 5m}
# 분석 결과 OK면 100% 전환
# Prometheus 기반 자동 분석
analysis:
templates:
- templateName: success-rate
args:
- name: service-name
value: myapp
# 자동 Rollback 트리거
antiAffinity:
requiredDuringSchedulingIgnoredDuringExecution: {}
revisionHistoryLimit: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:v2.0.0
ports:
- containerPort: 8080
AnalysisTemplate 정의¶
# Prometheus 기반 성공률 분석
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
name: success-rate
spec:
args:
- name: service-name
metrics:
- name: success-rate
interval: 1m
count: 5
successCondition: result[0] >= 0.95
failureLimit: 3
provider:
prometheus:
address: http://prometheus:9090
query: |
sum(rate(
http_requests_total{
service="{{ args.service-name }}",
status=~"2.."
}[5m]
)) /
sum(rate(
http_requests_total{
service="{{ args.service-name }}"
}[5m]
))
3. Automated Self-Healing¶
Self-Healing 설정¶
Self-Healing은 Git 상태와 클러스터 상태가 불일치할 때 자동으로 복구한다.
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
spec:
# Self-Healing 활성화
syncPolicy:
automated:
prune: true # 삭제된 리소스 제거
selfHeal: true # Drift 자동 복구
allowEmpty: false # 빈 매니페스트 거부
# Sync 재시도 설정
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
# IgnoreDifferences - 무시할 필드 지정
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # HPA가 관리하는 replicas 무시
- group: ""
kind: Secret
jqPathExpressions:
- .data.token # 동적으로 생성되는 토큰 무시
| 사용 사례 | 설명 |
|---|---|
| HPA 관리 replicas | HPA가 동적으로 조정하는 replicas 무시 |
| Cluster Autoscaler annotations | CA가 추가하는 annotation 무시 |
| Istio sidecar injected fields | Istio가 주입하는 필드 무시 |
| 동적 생성 Secret | cert-manager가 생성하는 인증서 무시 |
Resource Tracking¶
1. Tracking Methods¶
ArgoCD Resource Tracking 방식¶
ArgoCD는 3가지 방식으로 리소스를 추적한다:
| 방식 | 설명 | 장점 | 단점 |
|---|---|---|---|
| Label | app.kubernetes.io/instance label 사용 |
기본값, 간단함 | Label 충돌 가능 |
| Annotation | argocd.argoproj.io/tracking-id annotation 사용 |
Label 충돌 방지 | Annotation 크기 제한 |
| Annotation+Label | 둘 다 사용 | 호환성 최대화 | 약간의 오버헤드 |
Tracking Method 설정¶
# argocd-cm ConfigMap 수정
KUBE_EDITOR="nano" kubectl edit cm -n argocd argocd-cm
# data 섹션에 추가
# application.resourceTrackingMethod: annotation
# 또는
# application.resourceTrackingMethod: annotation+label
# ArgoCD Application Controller 재시작
kubectl rollout restart deployment argocd-application-controller -n argocd
2. Annotation vs Label¶
Annotation 방식 동작¶
Annotation 기반 Tracking은 argocd.argoproj.io/tracking-id 값을 사용한다.
# Annotation 기반 Tracking 예시
apiVersion: v1
kind: ConfigMap
metadata:
name: myconfig
annotations:
# ArgoCD가 자동 추가
argocd.argoproj.io/tracking-id: "myapp:v1:ConfigMap:default/myconfig"
data:
key: value
3. Best Practices¶
Resource Tracking 모범 사례¶
프로덕션 권장 설정:
# argocd-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
# Annotation 기반 Tracking 사용
application.resourceTrackingMethod: "annotation"
# Resource Customization
resource.customizations: |
# Deployment의 replicas 필드 무시 (HPA 사용 시)
apps/Deployment:
ignoreDifferences: |
jsonPointers:
- /spec/replicas
# Secret의 data 필드 무시 (External Secrets 사용 시)
v1/Secret:
ignoreDifferences: |
jqPathExpressions:
- .data
# Service의 clusterIP 무시 (동적 할당)
v1/Service:
ignoreDifferences: |
jsonPointers:
- /spec/clusterIP
- /spec/clusterIPs
ApplicationSet 고급 활용¶
1. Matrix Generator¶
Matrix Generator란?¶
Matrix Generator는 2개의 Generator를 조합하여 애플리케이션을 생성한다.
graph TB
subgraph "Matrix Generator"
G1[Git Generator<br/>Folders: app1, app2]
G2[Cluster Generator<br/>Clusters: dev, prod]
M[Matrix<br/>Combine]
G1 --> M
G2 --> M
M --> A1[app1-dev]
M --> A2[app1-prod]
M --> A3[app2-dev]
M --> A4[app2-prod]
end
Matrix Generator 예시¶
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: multi-env-apps
spec:
generators:
- matrix:
generators:
# Generator 1: Git 폴더
- git:
repoURL: https://github.com/example/apps.git
revision: main
directories:
- path: apps/*
# Generator 2: 클러스터
- list:
elements:
- cluster: dev
url: https://dev.example.com:6443
namespace: dev
- cluster: prod
url: https://prod.example.com:6443
namespace: prod
template:
metadata:
# 조합된 이름: app1-dev, app1-prod, ...
name: '{{path.basename}}-{{cluster}}'
spec:
project: default
source:
repoURL: https://github.com/example/apps.git
targetRevision: main
path: '{{path}}'
destination:
server: '{{url}}'
namespace: '{{namespace}}'
syncPolicy:
automated:
prune: true
selfHeal: true
apps/app1 × dev → app1-dev
- apps/app1 × prod → app1-prod
- apps/app2 × dev → app2-dev
- apps/app2 × prod → app2-prod
2. Git File Generator¶
Git File Generator란?¶
Git File Generator는 Git 리포지토리의 JSON/YAML 파일을 읽어 애플리케이션을 생성한다.
사용 사례: - 각 팀이 자신의 애플리케이션 목록 관리 - Self-Service 배포 플랫폼 - 중앙화된 설정 저장소
Git File Generator 예시¶
Git 리포지토리 구조:
team-a.yaml:# Team A의 애플리케이션 목록
applications:
- name: frontend
repoURL: https://github.com/team-a/frontend.git
path: k8s/overlays/prod
namespace: team-a-frontend
- name: backend
repoURL: https://github.com/team-a/backend.git
path: k8s/overlays/prod
namespace: team-a-backend
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: team-apps
spec:
generators:
- git:
repoURL: https://github.com/example/apps-config.git
revision: main
files:
- path: "*.yaml"
template:
metadata:
name: '{{name}}'
spec:
project: default
source:
repoURL: '{{repoURL}}'
targetRevision: main
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{namespace}}'
syncPolicy:
automated:
prune: true
selfHeal: true
3. Pull Request Generator¶
Pull Request Generator란?¶
Pull Request Generator는 Git Pull Request를 기반으로 preview 환경을 자동 생성한다.
sequenceDiagram
participant D as Developer
participant G as GitHub/GitLab
participant AS as ApplicationSet
participant K as Kubernetes
D->>G: 1. Create Pull Request
G->>AS: 2. Webhook/Polling
AS->>K: 3. Create preview namespace
AS->>K: 4. Deploy PR branch
AS->>G: 5. Comment with preview URL
D->>D: 6. Test preview environment
D->>G: 7. Merge PR
G->>AS: 8. PR closed webhook
AS->>K: 9. Delete preview namespace
Pull Request Generator 예시¶
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: pr-preview
spec:
generators:
- pullRequest:
github:
owner: example-org
repo: myapp
tokenRef:
secretName: github-token
key: token
labels:
- preview # 'preview' 라벨이 있는 PR만
# 필터
requeueAfterSeconds: 60 # 1분마다 PR 확인
template:
metadata:
name: 'preview-pr-{{number}}'
finalizers:
- resources-finalizer.argocd.argoproj.io # PR 닫히면 리소스 삭제
spec:
project: default
source:
repoURL: 'https://github.com/example-org/myapp.git'
targetRevision: '{{head_sha}}' # PR의 최신 커밋
path: k8s/overlays/preview
kustomize:
commonLabels:
pr: 'pr-{{number}}'
destination:
server: https://kubernetes.default.svc
namespace: 'preview-pr-{{number}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
kubectl create secret generic github-token \
--from-literal=token=<GITHUB_PERSONAL_ACCESS_TOKEN> \
-n argocd
멀티 클러스터 관리¶
1. Cluster Bootstrap¶
Bootstrap 프로세스¶
새 Kubernetes 클러스터를 ArgoCD에 추가하는 전체 과정:
graph TB
subgraph "Bootstrap Process"
C1[1. 클러스터 생성]
C2[2. ArgoCD 클러스터 등록]
C3[3. Bootstrap App 배포]
C4[4. Core Apps 자동 배포]
C5[5. Tenant Apps 자동 배포]
end
C1 --> C2
C2 --> C3
C3 --> C4
C4 --> C5
subgraph "Core Apps"
A1[Ingress Controller]
A2[Cert Manager]
A3[External Secrets]
A4[Monitoring Stack]
end
C4 --> A1
C4 --> A2
C4 --> A3
C4 --> A4
클러스터 등록¶
# ArgoCD CLI로 클러스터 추가
argocd cluster add <CONTEXT_NAME>
# 예시
argocd cluster add kind-prod-cluster \
--name prod-cluster \
--label env=production \
--label region=ap-northeast-2
# 클러스터 목록 확인
argocd cluster list
# SERVER NAME VERSION STATUS MESSAGE
# https://kubernetes.default.svc in-cluster 1.28 Success
# https://prod.example.com:6443 prod-cluster 1.28 Success
2. Cluster Credentials 관리¶
ServiceAccount 기반 인증¶
ArgoCD는 클러스터 접근을 위해 ServiceAccount Token을 사용한다:
# 대상 클러스터에서 ServiceAccount 생성
kubectl create namespace argocd
kubectl create serviceaccount argocd-manager -n argocd
# ClusterRole 생성
cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: argocd-manager-role
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
EOF
# ClusterRoleBinding 생성
kubectl create clusterrolebinding argocd-manager-binding \
--clusterrole=argocd-manager-role \
--serviceaccount=argocd:argocd-manager
# Secret Token 생성 (K8s 1.24+)
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: argocd-manager-token
namespace: argocd
annotations:
kubernetes.io/service-account.name: argocd-manager
type: kubernetes.io/service-account-token
EOF
# Token 확인
kubectl get secret -n argocd argocd-manager-token -o jsonpath='{.data.token}' | base64 -d
3. App of Apps 패턴¶
App of Apps란?¶
App of Apps는 하나의 Application이 다른 Application들을 관리하는 패턴이다.
graph TB
subgraph "App of Apps Pattern"
ROOT[Root App<br/>apps/root.yaml]
subgraph "Core Apps"
C1[Ingress Controller]
C2[Cert Manager]
C3[External Secrets]
end
subgraph "Platform Apps"
P1[Monitoring]
P2[Logging]
P3[Service Mesh]
end
subgraph "Tenant Apps"
T1[Team A Apps]
T2[Team B Apps]
end
end
ROOT --> C1
ROOT --> C2
ROOT --> C3
ROOT --> P1
ROOT --> P2
ROOT --> P3
ROOT --> T1
ROOT --> T2
App of Apps 구현¶
Git 리포지토리 구조:
gitops-repo/
├── apps/
│ ├── root.yaml # Root Application
│ ├── core/
│ │ ├── ingress-nginx.yaml
│ │ ├── cert-manager.yaml
│ │ └── external-secrets.yaml
│ ├── platform/
│ │ ├── monitoring.yaml
│ │ └── logging.yaml
│ └── tenants/
│ ├── team-a.yaml
│ └── team-b.yaml
└── manifests/
├── core/
├── platform/
└── tenants/
# apps/root.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: root
namespace: argocd
# Finalizer: Root App 삭제 시 모든 하위 앱도 삭제
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/example/gitops-repo.git
targetRevision: main
path: apps/core # Core Apps 먼저 배포
destination:
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
# apps/core/ingress-nginx.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ingress-nginx
namespace: argocd
spec:
project: default
source:
repoURL: https://kubernetes.github.io/ingress-nginx
chart: ingress-nginx
targetRevision: 4.8.0
helm:
values: |
controller:
service:
type: LoadBalancer
destination:
server: https://kubernetes.default.svc
namespace: ingress-nginx
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
LDAP/Active Directory 통합¶
1. OpenLDAP 서버 구축¶
LDAP란?¶
LDAP (Lightweight Directory Access Protocol)는 사용자, 그룹, 권한 정보를 계층적으로 관리하는 디렉터리 서비스이다.
쉬운 비유: - LDAP 서버 = 회사의 인사/보안부 (모든 직원 정보 중앙 관리) - 디렉터리 구조 = 회사 조직도 (본사-부서-팀-직원) - 인증(Authentication) = 신분증 검사 - 권한 부여(Authorization) = 출입증/권한 확인
LDAP 디렉터리 구조 (DIT)¶
dc=example,dc=org # Base DN (Root DN)
├── ou=people # Organizational Unit: 사용자
│ ├── uid=alice
│ │ ├── cn: Alice
│ │ ├── sn: Kim
│ │ └── mail: alice@example.org
│ └── uid=bob
│ ├── cn: Bob
│ ├── sn: Lee
│ └── mail: bob@example.org
└── ou=groups # Organizational Unit: 그룹
├── cn=devs
│ └── member: uid=bob,ou=people,dc=example,dc=org
└── cn=admins
└── member: uid=alice,ou=people,dc=example,dc=org
| 용어 | 설명 |
|---|---|
| DN (Distinguished Name) | uid=alice,ou=people,dc=example,dc=org |
| RDN (Relative Distinguished Name) | uid=alice |
| Base DN | dc=example,dc=org |
| Entry | 디렉터리의 기본 단위 (다수의 Attribute로 구성) |
| Attribute | Entry의 각 속성 (cn, sn, uid, mail 등) |
OpenLDAP 서버 배포¶
# OpenLDAP + phpLDAPadmin 배포
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: openldap
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: openldap
namespace: openldap
spec:
replicas: 1
selector:
matchLabels:
app: openldap
template:
metadata:
labels:
app: openldap
spec:
containers:
# OpenLDAP Server
- name: openldap
image: osixia/openldap:1.5.0
ports:
- containerPort: 389
name: ldap
- containerPort: 636
name: ldaps
env:
- name: LDAP_ORGANISATION
value: "Example Org"
- name: LDAP_DOMAIN
value: "example.org"
- name: LDAP_ADMIN_PASSWORD
value: "admin"
- name: LDAP_CONFIG_PASSWORD
value: "admin"
# phpLDAPadmin (Web UI)
- name: phpldapadmin
image: osixia/phpldapadmin:0.9.0
ports:
- containerPort: 80
name: phpldapadmin
env:
- name: PHPLDAPADMIN_HTTPS
value: "false"
- name: PHPLDAPADMIN_LDAP_HOSTS
value: "localhost"
---
apiVersion: v1
kind: Service
metadata:
name: openldap
namespace: openldap
spec:
selector:
app: openldap
ports:
- name: phpldapadmin
port: 80
targetPort: 80
nodePort: 30000
- name: ldap
port: 389
targetPort: 389
- name: ldaps
port: 636
targetPort: 636
type: NodePort
EOF
# 배포 확인
kubectl get deploy,pod,svc,ep -n openldap
OpenLDAP 초기 설정¶
1. phpLDAPadmin 웹 UI 접속:
# 브라우저에서 접속
open http://127.0.0.1:30000
# 로그인 정보:
# - Login DN: cn=admin,dc=example,dc=org
# - Password: admin
kubectl -n openldap exec -it deploy/openldap -c openldap -- bash
# ou=people, ou=groups 생성
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: ou=people,dc=example,dc=org
objectClass: organizationalUnit
ou: people
dn: ou=groups,dc=example,dc=org
objectClass: organizationalUnit
ou: groups
EOF
# alice 사용자 추가
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: uid=alice,ou=people,dc=example,dc=org
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: alice
cn: Alice
sn: Kim
mail: alice@example.org
userPassword: password123
uidNumber: 10001
gidNumber: 10001
homeDirectory: /home/alice
EOF
# bob 사용자 추가
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: uid=bob,ou=people,dc=example,dc=org
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: bob
cn: Bob
sn: Lee
mail: bob@example.org
userPassword: password456
uidNumber: 10002
gidNumber: 10002
homeDirectory: /home/bob
EOF
# devs 그룹 생성
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: cn=devs,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: devs
member: uid=bob,ou=people,dc=example,dc=org
EOF
# admins 그룹 생성
cat <<EOF | ldapadd -x -D "cn=admin,dc=example,dc=org" -w admin
dn: cn=admins,ou=groups,dc=example,dc=org
objectClass: groupOfNames
cn: admins
member: uid=alice,ou=people,dc=example,dc=org
EOF
# 모든 사용자 조회
ldapsearch -x -H ldap://localhost:389 \
-b "ou=people,dc=example,dc=org" \
-D "cn=admin,dc=example,dc=org" \
-w admin
# 특정 사용자 조회
ldapsearch -x -H ldap://localhost:389 \
-b "dc=example,dc=org" \
-D "cn=admin,dc=example,dc=org" \
-w admin \
"(uid=alice)"
# 모든 그룹 조회
ldapsearch -x -H ldap://localhost:389 \
-b "ou=groups,dc=example,dc=org" \
-D "cn=admin,dc=example,dc=org" \
-w admin
2. Keycloak LDAP Federation¶
LDAP 연동 아키텍처¶
Keycloak을 사용하여 OpenLDAP를 ArgoCD SSO에 통합할 수 있다.
graph TB
subgraph "Enterprise Identity"
LDAP[LDAP/Active Directory]
subgraph "User Groups"
G1[DevOps Team]
G2[Platform Team]
G3[App Team A]
end
end
subgraph "Keycloak"
KC[Keycloak Server]
LDAPF[LDAP Federation]
MAPPER[Group Mapper]
end
subgraph "ArgoCD"
OIDC[OIDC Config]
RBAC[RBAC Policy]
APP[Applications]
end
LDAP --> G1
LDAP --> G2
LDAP --> G3
G1 --> LDAPF
G2 --> LDAPF
G3 --> LDAPF
LDAPF --> KC
KC --> MAPPER
MAPPER --> OIDC
OIDC --> RBAC
RBAC --> APP
Keycloak LDAP 설정¶
# Keycloak Admin Console 접속
# http://keycloak.example.com/admin
# 1. User Federation → Add provider → ldap 선택
# LDAP 기본 설정:
# Edit Mode: READ_ONLY (LDAP 수정 불가)
# Vendor: Active Directory 또는 Other
# Connection URL: ldap://ldap.example.com:389
# Users DN: ou=users,dc=example,dc=com
# Bind DN: cn=admin,dc=example,dc=com
# Bind Credential: <LDAP_ADMIN_PASSWORD>
# 2. LDAP Group Mapper 생성
# Mappers → Create
# Name: group-mapper
# Mapper Type: group-ldap-mapper
# LDAP Groups DN: ou=groups,dc=example,dc=com
# Group Name LDAP Attribute: cn
# Group Object Classes: groupOfNames
# Membership LDAP Attribute: member
# Membership Attribute Type: DN
# Mode: READ_ONLY
3. ArgoCD RBAC with LDAP Groups¶
LDAP 그룹 기반 RBAC 정책¶
# argocd-rbac-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-rbac-cm
namespace: argocd
data:
policy.csv: |
# Platform Team (Full Admin)
g, /Platform Team, role:admin
# DevOps Team (Read-only Admin)
g, /DevOps Team, role:readonly
# App Team A (특정 프로젝트만)
g, /App Team A, role:app-team-a
p, role:app-team-a, applications, *, team-a/*, allow
p, role:app-team-a, applications, get, */*, allow
p, role:app-team-a, repositories, get, *, allow
# App Team B (특정 프로젝트만)
g, /App Team B, role:app-team-b
p, role:app-team-b, applications, *, team-b/*, allow
p, role:app-team-b, applications, get, */*, allow
policy.default: role:readonly
# LDAP 그룹 클레임 매핑
scopes: '[groups, email]'
# argocd-cm ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cm
namespace: argocd
data:
oidc.config: |
name: Keycloak
issuer: https://keycloak.example.com/realms/master
clientID: argocd
clientSecret: $oidc.keycloak.clientSecret
requestedScopes:
- openid
- profile
- email
- groups
# LDAP 그룹 클레임
claimMapping:
groups: groups
4. LDAP 동기화 및 캐싱¶
Keycloak User Storage SPI 최적화¶
# Keycloak Admin Console
# User Federation → ldap → Settings
# Cache Settings:
# Cache Policy: DEFAULT
# Eviction Day: 1
# Eviction Hour: 0
# Eviction Minute: 0
# Max Lifespan: 86400000 (24시간)
# Sync Settings:
# Periodic Full Sync: Enabled
# Full Sync Period: 604800 (7일)
# Periodic Changed Users Sync: Enabled
# Changed Users Sync Period: 86400 (1일)
LDAP 연결 풀 최적화¶
# Keycloak StatefulSet 환경 변수
env:
- name: LDAP_CONNECTION_POOL_SIZE
value: "20"
- name: LDAP_CONNECTION_POOL_TIMEOUT
value: "5000"
- name: LDAP_READ_TIMEOUT
value: "60000"
시크릿 관리 전략¶
1. Sealed Secrets¶
Sealed Secrets란?¶
Sealed Secrets는 암호화된 Secret을 안전하게 Git에 저장할 수 있게 한다.
sequenceDiagram
participant D as Developer
participant K as kubeseal CLI
participant SS as SealedSecret Controller
participant KS as Kubernetes Secret
D->>K: 1. kubeseal < secret.yaml > sealed-secret.yaml
Note over K: Public Key로 암호화
D->>D: 2. sealed-secret.yaml을 Git에 커밋
D->>SS: 3. kubectl apply -f sealed-secret.yaml
SS->>SS: 4. Private Key로 복호화
SS->>KS: 5. 일반 Secret 생성
Sealed Secrets 설치 및 사용¶
# Sealed Secrets Controller 설치
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
# kubeseal CLI 설치 (macOS)
brew install kubeseal
# 기존 Secret 생성
kubectl create secret generic mysecret \
--from-literal=username=admin \
--from-literal=password=secret123 \
--dry-run=client -o yaml > secret.yaml
# SealedSecret로 암호화
kubeseal < secret.yaml > sealed-secret.yaml \
--controller-name=sealed-secrets-controller \
--controller-namespace=kube-system
# Git에 커밋 가능 (암호화됨)
cat sealed-secret.yaml
# apiVersion: bitnami.com/v1alpha1
# kind: SealedSecret
# metadata:
# name: mysecret
# spec:
# encryptedData:
# username: AgB8F3vZ...
# password: AgC9K2xL...
# 배포
kubectl apply -f sealed-secret.yaml
# 복호화된 Secret 확인
kubectl get secret mysecret -o jsonpath='{.data.password}' | base64 -d
2. External Secrets Operator¶
External Secrets Operator란?¶
External Secrets Operator (ESO)는 외부 Secret 저장소와 Kubernetes를 동기화한다.
지원 백엔드: - AWS Secrets Manager - Azure Key Vault - Google Secret Manager - HashiCorp Vault - 1Password
graph LR
subgraph "External Secret Store"
V[Vault]
A[AWS Secrets Manager]
AZ[Azure Key Vault]
G[Google Secret Manager]
end
subgraph "Kubernetes"
ES[ExternalSecret]
ESO[ESO Controller]
KS[Kubernetes Secret]
end
ES --> ESO
ESO --> V
ESO --> A
ESO --> AZ
ESO --> G
ESO --> KS
External Secrets Operator 사용¶
# ESO 설치
helm repo add external-secrets https://charts.external-secrets.io
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets-system \
--create-namespace
# SecretStore 정의 (AWS Secrets Manager 예시)
cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: aws-secrets
namespace: default
spec:
provider:
aws:
service: SecretsManager
region: ap-northeast-2
auth:
secretRef:
accessKeyIDSecretRef:
name: aws-credentials
key: access-key-id
secretAccessKeySecretRef:
name: aws-credentials
key: secret-access-key
EOF
# ExternalSecret 정의
cat <<EOF | kubectl apply -f -
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: db-password
namespace: default
spec:
refreshInterval: 1h # 1시간마다 동기화
secretStoreRef:
name: aws-secrets
kind: SecretStore
target:
name: db-password # 생성될 K8s Secret 이름
creationPolicy: Owner
data:
- secretKey: password
remoteRef:
key: prod/db/password # AWS Secrets Manager 경로
EOF
# 생성된 Secret 확인
kubectl get secret db-password -o jsonpath='{.data.password}' | base64 -d
3. HashiCorp Vault 통합¶
Vault External Secrets 설정¶
# SecretStore - Vault
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
spec:
provider:
vault:
server: "https://vault.example.com"
path: "secret"
version: "v2"
auth:
# Kubernetes Auth Method
kubernetes:
mountPath: "kubernetes"
role: "argocd"
serviceAccountRef:
name: vault-auth
---
# ExternalSecret - Vault에서 가져오기
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: vault-secret
spec:
refreshInterval: 10m
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: app-credentials
data:
- secretKey: api-key
remoteRef:
key: secret/data/prod/api
property: key
- secretKey: db-password
remoteRef:
key: secret/data/prod/database
property: password
모니터링 및 관찰성¶
1. Prometheus Metrics¶
ArgoCD Metrics 수집¶
ArgoCD는 Prometheus 형식의 메트릭을 노출한다.
# argocd-metrics Service 확인
kubectl get svc -n argocd argocd-metrics
# Metrics 엔드포인트 확인
kubectl port-forward -n argocd svc/argocd-metrics 8082:8082
curl http://localhost:8082/metrics
# 주요 메트릭:
# argocd_app_info - Application 정보
# argocd_app_sync_total - Sync 횟수
# argocd_app_reconcile_count - Reconciliation 횟수
# argocd_git_request_total - Git 요청 횟수
# argocd_redis_request_total - Redis 요청 횟수
Prometheus ServiceMonitor 정의¶
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: argocd-metrics
namespace: argocd
spec:
selector:
matchLabels:
app.kubernetes.io/name: argocd-metrics
endpoints:
- port: metrics
interval: 30s
path: /metrics
Grafana 대시보드¶
ArgoCD 공식 Grafana 대시보드:
- Dashboard ID: 14584 (ArgoCD)
- Dashboard ID: 19993 (ApplicationSet Controller)
# Grafana에서 Import
# 1. Dashboards → Import
# 2. Grafana.com Dashboard ID: 14584
# 3. Prometheus 데이터 소스 선택
2. Notification 설정¶
Notification 아키텍처¶
graph TB
subgraph "ArgoCD Notification"
AC[Application Controller]
NC[Notification Controller]
subgraph "Triggers"
T1[on-sync-succeeded]
T2[on-sync-failed]
T3[on-health-degraded]
end
subgraph "Templates"
TM1[Slack Template]
TM2[Email Template]
TM3[Webhook Template]
end
subgraph "Channels"
C1[Slack]
C2[Email]
C3[Webhook]
C4[Teams]
end
end
AC --> NC
NC --> T1
NC --> T2
NC --> T3
T1 --> TM1
T2 --> TM2
T3 --> TM3
TM1 --> C1
TM2 --> C2
TM3 --> C3
Slack Notification 설정¶
# argocd-notifications-secret 생성
kubectl create secret generic argocd-notifications-secret \
--from-literal=slack-token=<SLACK_BOT_TOKEN> \
-n argocd
# argocd-notifications-cm ConfigMap 수정
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-notifications-cm
namespace: argocd
data:
# Slack 서비스 설정
service.slack: |
token: $slack-token
# Trigger 정의
trigger.on-sync-succeeded: |
- when: app.status.operationState.phase in ['Succeeded']
send: [app-sync-succeeded]
trigger.on-sync-failed: |
- when: app.status.operationState.phase in ['Error', 'Failed']
send: [app-sync-failed]
trigger.on-health-degraded: |
- when: app.status.health.status == 'Degraded'
send: [app-health-degraded]
# Template 정의
template.app-sync-succeeded: |
message: |
Application {{.app.metadata.name}} has been successfully synced.
Repository: {{.app.spec.source.repoURL}}
Revision: {{.app.status.sync.revision}}
slack:
attachments: |
[{
"title": "{{.app.metadata.name}}",
"color": "good",
"fields": [{
"title": "Sync Status",
"value": "{{.app.status.sync.status}}",
"short": true
}, {
"title": "Health Status",
"value": "{{.app.status.health.status}}",
"short": true
}]
}]
template.app-sync-failed: |
message: |
Application {{.app.metadata.name}} sync has failed.
Error: {{.app.status.operationState.message}}
slack:
attachments: |
[{
"title": "{{.app.metadata.name}}",
"color": "danger",
"fields": [{
"title": "Sync Status",
"value": "{{.app.status.sync.status}}",
"short": true
}, {
"title": "Error",
"value": "{{.app.status.operationState.message}}",
"short": false
}]
}]
EOF
Application에 Notification 적용¶
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
annotations:
# Slack 채널 지정
notifications.argoproj.io/subscribe.on-sync-succeeded.slack: argocd-notifications
notifications.argoproj.io/subscribe.on-sync-failed.slack: argocd-alerts
notifications.argoproj.io/subscribe.on-health-degraded.slack: argocd-alerts
spec:
# ...
3. Audit Logging¶
Audit Log 활성화¶
# argocd-cmd-params-cm ConfigMap 수정
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cmd-params-cm
namespace: argocd
data:
# Server Audit Log
server.log.level: debug
server.log.format: json
# Controller Audit Log
controller.log.level: info
controller.log.format: json
# ApplicationSet Controller Audit Log
applicationset.log.level: info
applicationset.log.format: json
EOF
# ArgoCD Server 재시작
kubectl rollout restart deployment argocd-server -n argocd
Audit Log 확인¶
# Server 로그 확인
kubectl logs -n argocd deployment/argocd-server --tail=100 -f
# 로그 예시 (JSON 형식):
# {
# "level": "info",
# "msg": "finished unary call with code OK",
# "grpc.code": "OK",
# "grpc.method": "Get",
# "grpc.service": "application.ApplicationService",
# "grpc.start_time": "2024-01-01T00:00:00Z",
# "grpc.time_ms": 5.123,
# "span.kind": "server",
# "system": "grpc"
# }
6주차 학습 정리¶
- ArgoCD Notification (Slack, Email, Webhook)
- RBAC 및 프로젝트 기반 멀티테넌시
- Prometheus 메트릭 및 Grafana 대시보드
- Audit Logging (JSON 형식)
- Sealed Secrets (Git에 암호화된 Secret 저장)
- External Secrets Operator (AWS Secrets Manager, Vault 연동)