DevSecOps 파이프라인
DevSecOps 파이프라인을 Kubernetes(Kind) 기반 구축 가이드. AWS/GCP/Azure 또는 온프레미스 환경에 맞게 변형 가능.
1. 인프라 아키텍처 개요
graph TD
A[Developer] -->|Git Push| B(GitHub/GitLab)
B -->|Webhook| C[Jenkins CI]
C -->|Build & Scan| D[Docker Registry]
C -->|Security Scan| E[SonarQube/Trivy]
D -->|Pull Image| F[ArgoCD]
F -->|Deploy| G[Kubernetes]
G -->|Monitoring| H[Prometheus+Grafana]
H -->|Alert| I[Slack/Email]
2. Kind 클러스터 구성 (Production-Grade)
# kind-ha.yaml (3노드 클러스터)
cat <<EOF > kind-ha.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
- role: worker
- role: worker
EOF
kind create cluster --name prod-cluster --config kind-ha.yaml
---
3. 필수 툴 설치 (Helm 3)
1) Jenkins (CI 서버)
# jenkins-values-prod.yaml (실무용 설정)
cat <<EOF > jenkins-values-prod.yaml
controller:
serviceType: NodePort
servicePort: 8080
nodePort: 32000
installPlugins:
- workflow-aggregator
- git:4.11.4
- blueocean:1.25.3
- docker-workflow:1.28
- sonar:2.14
- owasp-dependency-check:2.1.1
- pipeline-aws:1.43
resources:
requests:
cpu: "1"
memory: "2Gi"
limits:
cpu: "2"
memory: "4Gi"
jenkinsAdminPassword: "admin123!"
adminUser: "admin"
adminPassword: "admin123!"
agent:
podTemplate:
containers:
- name: jnlp
resources:
limits:
cpu: "500m"
memory: "1Gi"
EOF
helm upgrade --install jenkins jenkins/jenkins \
-n jenkins --create-namespace \
-f jenkins-values-prod.yaml
---
2) SonarQube (정적 분석)
# sonarqube-values-prod.yaml
cat <<EOF > sonarqube-values-prod.yaml
service:
type: NodePort
nodePort: 32001
postgresql:
enabled: true
persistence:
size: 20Gi
resources:
requests:
memory: 2Gi
cpu: 1
limits:
memory: 4Gi
cpu: 2
EOF
helm upgrade --install sonarqube sonarqube/sonarqube \
-n sonarqube --create-namespace \
-f sonarqube-values-prod.yaml
---
3) Trivy (보안 스캐닝)
# trivy-operator 설치 (지속적 스캐닝)
helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm upgrade --install trivy-operator aqua/trivy-operator \
-n trivy-system --create-namespace \
--set="trivy.ignoreUnfixed=true"
---
4. CI/CD 파이프라인 (Jenkinsfile - 실무용)
pipeline {
agent {
kubernetes {
label 'jenkins-agent-pod'
yaml """
apiVersion: v1
kind: Pod
spec:
containers:
- name: jnlp
image: jenkins/inbound-agent:4.11.2-jdk11
resources:
limits:
cpu: 500m
memory: 1Gi
- name: docker
image: docker:20.10-dind
securityContext:
privileged: true
env:
- name: DOCKER_TLS_CERTDIR
value: ""
"""
}
}
environment {
REGISTRY = "registry.example.com"
DOCKER_CRED = credentials('docker-registry')
KUBECONFIG = credentials('kubeconfig')
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/your-org/your-app.git'
}
}
stage('Unit Test') {
steps {
sh 'mvn test' # or pytest/npm test 등
}
}
stage('Build & Push') {
steps {
script {
docker.withRegistry("https://${REGISTRY}", 'docker-registry') {
def customImage = docker.build("${REGISTRY}/your-app:${env.BUILD_ID}")
customImage.push()
}
}
}
}
stage('Security Scan') {
parallel {
stage('SonarQube') {
steps {
withSonarQubeEnv('sonarqube') {
sh 'sonar-scanner -Dsonar.projectKey=your-app -Dsonar.java.binaries=target/classes'
}
}
}
stage('Trivy Scan') {
steps {
sh '''
trivy image --exit-code 1 \
--severity CRITICAL,HIGH \
--ignore-unfixed \
${REGISTRY}/your-app:${BUILD_ID}
'''
}
}
}
}
stage('Deploy to Staging') {
steps {
sh '''
kubectl apply -f k8s/staging/ -n staging
kubectl rollout status deploy/your-app -n staging --timeout=300s
'''
}
}
stage('Integration Test') {
steps {
sh 'curl -sSf http://staging.your-domain.com/health'
}
}
stage('Promote to Prod') {
when {
branch 'main'
}
steps {
sh '''
kubectl apply -f k8s/prod/ -n prod
kubectl rollout status deploy/your-app -n prod --timeout=300s
'''
}
}
}
post {
always {
junit '**/target/surefire-reports/*.xml'
archiveArtifacts artifacts: '**/target/*.jar', fingerprint: true
}
failure {
slackSend channel: '#alerts', message: "Build Failed: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
}
}
---
5. GitOps 배포 ( ArgoCD 설치, HA 모드 - Production Grade)
# argocd-values-prod.yaml
cat <<EOF > argocd-values-prod.yaml
server:
service:
type: NodePort
nodePort: 32002
autoscaling:
enabled: true
minReplicas: 2
resources:
limits:
cpu: 1
memory: 1Gi
repoServer:
replicas: 2
controller:
replicas: 2
redis:
ha:
enabled: true
resources:
limits:
cpu: 500m
memory: 512Mi
EOF
helm upgrade --install argocd argo/argo-cd \
-n argocd --create-namespace \
-f argocd-values-prod.yaml
---
ApplicationSet 예시 (멀티 환경 배포)
# applicationset.yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: your-app
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/your-org/gitops-repo.git
revision: HEAD
directories:
- path: "apps/your-app/overlays/*"
template:
metadata:
name: '{{path.basename}}'
spec:
project: default
source:
repoURL: https://github.com/your-org/gitops-repo.git
targetRevision: HEAD
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path.basename}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
---
6. 모니터링 스택 (Prometheus + Grafana)
# monitoring-values-prod.yaml
cat <<EOF > monitoring-values-prod.yaml
grafana:
service:
type: NodePort
nodePort: 32003
adminPassword: "admin123!"
prometheus:
service:
type: NodePort
nodePort: 32004
alertmanager:
config:
global:
slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'
route:
receiver: 'slack-notifications'
routes:
- match:
severity: critical
receiver: 'slack-critical'
EOF
helm upgrade --install monitoring prometheus-community/kube-prometheus-stack \
-n monitoring --create-namespace \
-f monitoring-values-prod.yaml
---
커스텀 대시보드 예시
# grafana-dashboard.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-dashboard
namespace: monitoring
labels:
grafana_dashboard: "1"
data:
app-metrics.json: |-
{
"title": "App Metrics",
"panels": [
{
"title": "HTTP Requests",
"type": "graph",
"datasource": "Prometheus",
"targets": [{
"expr": "sum(rate(http_requests_total[1m])) by (status_code)",
"legendFormat": "{{status_code}}"
}]
}
]
}
---
7. 인그레스 구성 (Traefik/Nginx)
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: devsecops-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: "jenkins.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: jenkins
port:
number: 8080
- host: "argocd.example.com"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
---
8. 고려사항
1. Persistent Storage
# jenkins-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: jenkins-data
spec:
storageClassName: standard
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 50Gi
2. 백업 전략
# Velero를 이용한 백업
velero install \
--provider aws \
--bucket your-backup-bucket \
--secret-file ./credentials-velero \
--use-restic
3. Zero-Downtime 배포
# deployment-strategy.yaml
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 0
type: RollingUpdate
4. HPA (Auto Scaling)
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: your-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: your-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
---