Docker & Kubernetes 완전 정복 9편: ConfigMap, Secret, Ingress 활용
Docker & Kubernetes Complete Guide Part 9: ConfigMap, Secret, and Ingress
서론: 설정 관리와 외부 접근의 중요성
컨테이너화된 애플리케이션을 운영하면서 가장 중요한 과제 중 하나는 설정의 분리입니다. 애플리케이션 코드와 설정을 분리하면 같은 이미지를 개발, 스테이징, 프로덕션 환경에서 재사용할 수 있습니다. Kubernetes는 이를 위해 ConfigMap과 Secret이라는 리소스를 제공합니다.
또한 클러스터 외부에서 내부 서비스에 접근하려면 적절한 라우팅 메커니즘이 필요합니다. Ingress는 HTTP/HTTPS 트래픽을 클러스터 내부 서비스로 라우팅하는 L7 로드밸런서 역할을 합니다. 이번 편에서는 이 세 가지 핵심 리소스를 상세히 알아보겠습니다.
1. 설정 관리의 중요성
1.1 왜 설정을 분리해야 하는가?
12-Factor App 방법론에서 강조하는 것처럼, 설정은 코드와 분리되어야 합니다:
- 환경별 배포: 동일한 이미지를 다양한 환경(개발, 테스트, 프로덕션)에 배포할 수 있습니다
- 보안 강화: 민감한 정보를 코드 저장소에 포함하지 않아도 됩니다
- 유연한 변경: 설정 변경 시 이미지 재빌드 없이 적용할 수 있습니다
- 팀 협업: 개발자와 운영자가 각자의 영역을 독립적으로 관리할 수 있습니다
1.2 Kubernetes의 설정 관리 방식
Kubernetes는 두 가지 리소스로 설정을 관리합니다:
| 리소스 | 용도 | 저장 방식 |
|---|---|---|
| ConfigMap | 일반 설정 데이터 | 평문(Plain text) |
| Secret | 민감한 정보 | Base64 인코딩 |
2. ConfigMap 생성과 사용
2.1 ConfigMap이란?
ConfigMap은 키-값 쌍으로 구성된 설정 데이터를 저장하는 Kubernetes 리소스입니다. 애플리케이션의 환경 변수, 설정 파일, 명령행 인자 등을 저장할 수 있습니다.
2.2 ConfigMap 생성 방법
방법 1: 리터럴 값으로 생성
# 단일 키-값 쌍
kubectl create configmap app-config --from-literal=APP_ENV=production
# 여러 키-값 쌍
kubectl create configmap app-config \
--from-literal=APP_ENV=production \
--from-literal=LOG_LEVEL=info \
--from-literal=MAX_CONNECTIONS=100
방법 2: 파일에서 생성
# 단일 파일
kubectl create configmap nginx-config --from-file=nginx.conf
# 디렉토리 전체
kubectl create configmap app-configs --from-file=./configs/
방법 3: YAML 매니페스트로 생성
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: default
data:
# 단순 키-값 쌍
APP_ENV: "production"
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
# 여러 줄의 설정 파일
application.properties: |
server.port=8080
spring.datasource.url=jdbc:mysql://mysql:3306/mydb
spring.jpa.hibernate.ddl-auto=update
logging.level.root=INFO
# JSON 형식 설정
config.json: |
{
"apiEndpoint": "https://api.example.com",
"timeout": 30,
"retryCount": 3
}
kubectl apply -f configmap.yaml
2.3 ConfigMap 사용: 환경 변수
개별 키를 환경 변수로 사용
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
env:
# 특정 키만 환경 변수로 주입
- name: APPLICATION_ENV
valueFrom:
configMapKeyRef:
name: app-config
key: APP_ENV
- name: LOG_LEVEL
valueFrom:
configMapKeyRef:
name: app-config
key: LOG_LEVEL
모든 키를 환경 변수로 사용
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
envFrom:
- configMapRef:
name: app-config
# 접두사 추가 가능
- configMapRef:
name: app-config
prefix: CONFIG_
2.4 ConfigMap 사용: 볼륨 마운트
설정 파일이 필요한 경우 볼륨으로 마운트할 수 있습니다:
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: config-volume
mountPath: /etc/nginx/conf.d
readOnly: true
volumes:
- name: config-volume
configMap:
name: nginx-config
# 특정 키만 마운트할 경우
items:
- key: nginx.conf
path: default.conf
subPath를 사용한 특정 파일 마운트
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: config-volume
mountPath: /app/config/application.properties
subPath: application.properties
volumes:
- name: config-volume
configMap:
name: app-config
3. Secret 생성과 사용
3.1 Secret이란?
Secret은 비밀번호, OAuth 토큰, SSH 키와 같은 민감한 정보를 저장하는 리소스입니다. ConfigMap과 유사하지만, 데이터가 Base64로 인코딩되어 저장됩니다.
3.2 Secret 유형
| 유형 | 설명 | 사용 예 |
|---|---|---|
| Opaque | 임의의 사용자 정의 데이터 | 비밀번호, API 키 |
| kubernetes.io/tls | TLS 인증서 | HTTPS 인증서 |
| kubernetes.io/dockerconfigjson | Docker 레지스트리 인증 | Private 레지스트리 접근 |
| kubernetes.io/basic-auth | 기본 인증 정보 | 사용자명/비밀번호 |
| kubernetes.io/ssh-auth | SSH 인증 | SSH 개인 키 |
3.3 Opaque Secret 생성
방법 1: 명령행으로 생성
# 리터럴 값으로 생성
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=s3cr3tP@ssw0rd
# 파일에서 생성
kubectl create secret generic ssh-key \
--from-file=ssh-privatekey=/path/to/id_rsa
방법 2: YAML 매니페스트로 생성
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
type: Opaque
data:
# Base64 인코딩된 값
# echo -n 'admin' | base64 -> YWRtaW4=
username: YWRtaW4=
# echo -n 's3cr3tP@ssw0rd' | base64 -> czNjcjN0UEBzc3cwcmQ=
password: czNjcjN0UEBzc3cwcmQ=
---
# stringData 사용 (자동으로 Base64 인코딩)
apiVersion: v1
kind: Secret
metadata:
name: api-credentials
type: Opaque
stringData:
api-key: my-super-secret-api-key
api-secret: another-secret-value
3.4 TLS Secret 생성
# TLS 인증서 Secret 생성
kubectl create secret tls tls-secret \
--cert=path/to/tls.crt \
--key=path/to/tls.key
# tls-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
type: kubernetes.io/tls
data:
tls.crt: |
LS0tLS1CRUdJTi... (Base64 인코딩된 인증서)
tls.key: |
LS0tLS1CRUdJTi... (Base64 인코딩된 키)
3.5 Secret 사용하기
환경 변수로 주입
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
env:
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: username
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: password
볼륨으로 마운트
apiVersion: v1
kind: Pod
metadata:
name: app-pod
spec:
containers:
- name: app
image: myapp:latest
volumeMounts:
- name: secret-volume
mountPath: /etc/secrets
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: db-credentials
# 파일 권한 설정
defaultMode: 0400
3.6 Secret 보안 고려사항
Secret은 기본적으로 Base64 인코딩만 되어 있어 암호화되지 않습니다. 다음 보안 조치를 고려해야 합니다:
- etcd 암호화: etcd에 저장되는 Secret을 암호화합니다
# EncryptionConfiguration apiVersion: apiserver.config.k8s.io/v1 kind: EncryptionConfiguration resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: base64-encoded-32-byte-key - identity: {} - RBAC 제한: Secret에 대한 접근 권한을 최소화합니다
- 외부 Secret 관리 도구 사용: HashiCorp Vault, AWS Secrets Manager, Azure Key Vault 등
- Secret 로테이션: 정기적으로 Secret 값을 변경합니다
- 감사 로깅: Secret 접근에 대한 감사 로그를 활성화합니다
4. Ingress란?
4.1 Ingress 개념
Ingress는 클러스터 외부에서 내부 서비스로의 HTTP/HTTPS 트래픽을 관리하는 API 오브젝트입니다. L7(애플리케이션 계층) 로드밸런서로서 다음 기능을 제공합니다:
- 호스트 기반 라우팅: 도메인 이름에 따라 다른 서비스로 라우팅
- 패스 기반 라우팅: URL 경로에 따라 다른 서비스로 라우팅
- TLS/SSL 종료: HTTPS 트래픽 처리
- 로드 밸런싱: 여러 Pod에 트래픽 분산
4.2 Ingress vs Service (NodePort/LoadBalancer)
| 특성 | NodePort | LoadBalancer | Ingress |
|---|---|---|---|
| 계층 | L4 | L4 | L7 |
| 프로토콜 | TCP/UDP | TCP/UDP | HTTP/HTTPS |
| URL 라우팅 | 불가 | 불가 | 가능 |
| TLS 종료 | 불가 | 불가 | 가능 |
| 비용 | 무료 | 클라우드 비용 발생 | 단일 LB로 여러 서비스 |
5. Ingress Controller
5.1 Ingress Controller란?
Ingress 리소스는 선언적인 라우팅 규칙일 뿐이며, 실제로 작동하려면 Ingress Controller가 필요합니다. Ingress Controller는 Ingress 리소스를 감시하고 그에 맞게 로드밸런서를 구성합니다.
5.2 주요 Ingress Controller
Nginx Ingress Controller
# Nginx Ingress Controller 설치 (Helm)
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install nginx-ingress ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace
# 또는 manifest로 설치
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/cloud/deploy.yaml
Traefik Ingress Controller
# Traefik 설치 (Helm)
helm repo add traefik https://helm.traefik.io/traefik
helm repo update
helm install traefik traefik/traefik \
--namespace traefik \
--create-namespace
기타 Ingress Controller
- HAProxy Ingress: 고성능 로드밸런싱
- Contour: Envoy 기반
- AWS ALB Ingress Controller: AWS Application Load Balancer 연동
- GKE Ingress Controller: Google Cloud Load Balancer 연동
6. Ingress YAML 작성
6.1 기본 Ingress 구조
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
# Ingress Controller 특정 설정
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx # 사용할 Ingress Controller 지정
rules:
- host: example.com # 호스트 기반 라우팅
http:
paths:
- path: / # 패스 기반 라우팅
pathType: Prefix
backend:
service:
name: my-service
port:
number: 80
6.2 pathType 옵션
- Exact: 정확히 일치하는 경로만 매칭
- Prefix: 지정된 경로로 시작하는 모든 요청 매칭
- ImplementationSpecific: Ingress Controller에 따라 동작
7. 호스트/패스 기반 라우팅
7.1 호스트 기반 라우팅
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: host-based-ingress
spec:
ingressClassName: nginx
rules:
# api.example.com -> api-service
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
# web.example.com -> web-service
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
# admin.example.com -> admin-service
- host: admin.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: admin-service
port:
number: 3000
7.2 패스 기반 라우팅
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-based-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
# example.com/api/* -> api-service
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 8080
# example.com/web/* -> web-service
- path: /web(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: web-service
port:
number: 80
# example.com/* (기본) -> frontend-service
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
7.3 복합 라우팅 (호스트 + 패스)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: combined-ingress
spec:
ingressClassName: nginx
rules:
- host: shop.example.com
http:
paths:
- path: /products
pathType: Prefix
backend:
service:
name: product-service
port:
number: 8080
- path: /orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 8080
- path: /users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 8080
8. TLS 설정
8.1 TLS Secret 생성
# 자체 서명 인증서 생성 (테스트용)
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout tls.key \
-out tls.crt \
-subj "/CN=example.com"
# TLS Secret 생성
kubectl create secret tls example-tls \
--cert=tls.crt \
--key=tls.key
8.2 Ingress에 TLS 적용
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com
- www.example.com
secretName: example-tls
- hosts:
- api.example.com
secretName: api-tls
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
8.3 cert-manager를 이용한 자동 인증서 관리
# cert-manager 설치
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml
# Let's Encrypt ClusterIssuer 설정
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: your-email@example.com
privateKeySecretRef:
name: letsencrypt-prod
solvers:
- http01:
ingress:
class: nginx
---
# 자동 인증서 발급 Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auto-tls-ingress
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- example.com
secretName: example-tls-auto # cert-manager가 자동 생성
rules:
- host: example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80
9. 실전 예제: 마이크로서비스 라우팅
# 완전한 마이크로서비스 Ingress 구성
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: microservices-ingress
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
# API Gateway
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 8080
- path: /api/v1/products
pathType: Prefix
backend:
service:
name: product-service
port:
number: 8080
- path: /api/v1/orders
pathType: Prefix
backend:
service:
name: order-service
port:
number: 8080
# WebSocket
- path: /ws
pathType: Prefix
backend:
service:
name: websocket-service
port:
number: 8081
# 프론트엔드 (기본)
- path: /
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
10. 결론
이번 편에서는 Kubernetes의 핵심 설정 관리 리소스인 ConfigMap과 Secret, 그리고 외부 트래픽을 관리하는 Ingress에 대해 알아보았습니다:
- ConfigMap: 일반 설정 데이터를 관리하며, 환경 변수나 볼륨으로 Pod에 주입할 수 있습니다
- Secret: 민감한 정보를 안전하게 저장하며, 추가적인 보안 조치가 필요합니다
- Ingress: L7 로드밸런서로서 호스트/패스 기반 라우팅과 TLS 종료를 제공합니다
- Ingress Controller: Ingress 리소스를 실제로 구현하는 컴포넌트입니다
다음 10편에서는 Kubernetes 패키지 매니저인 Helm과 CI/CD 파이프라인 구축에 대해 알아보겠습니다. Helm Chart를 활용한 애플리케이션 배포와 GitOps 기반의 자동화된 배포 전략을 다룰 예정입니다.