관리 메뉴

근묵자흑

[쿠버네티스] 서비스, 네임스페이스, 컨피그맵, 시크릿 본문

k8s

[쿠버네티스] 서비스, 네임스페이스, 컨피그맵, 시크릿

Luuuuu 2025. 4. 13. 14:48

소개

쿠버네티스(Kubernetes)는 컨테이너화된 애플리케이션의 배포, 확장 및 관리를 자동화하는 강력한 오픈소스 플랫폼입니다. 쿠버네티스는 다양한 리소스 타입을 통해 컨테이너 오케스트레이션을 효과적으로 수행합니다. 이 가이드에서는 쿠버네티스의 핵심 개념 중 서비스(Service), 네임스페이스(Namespace), 컨피그맵(ConfigMap), 시크릿(Secret)에 대해 자세히 알아보겠습니다.

이 네 가지 리소스는 쿠버네티스 애플리케이션의 네트워킹, 조직화, 구성 관리, 기밀 정보 관리를 위한 필수적인 구성 요소입니다. 이들을 이해하고 효과적으로 활용하면 쿠버네티스의 장점을 극대화할 수 있습니다.

서비스 (Service)

서비스의 필요성

파드(Pod)는 쿠버네티스의 기본 실행 단위이지만, 여러 특성으로 인해 네트워킹에 있어 몇 가지 과제를 제시합니다:

  1. 파드의 일시성: 파드는 일시적이며 언제든지 생성되거나 삭제될 수 있습니다. 클러스터 상태 변화, 노드 장애, 스케일링 등으로 인해 파드가 재생성되면 새로운 IP 주소를 할당받습니다.
  2. 수평적 확장: 동일한 애플리케이션의 여러 파드 인스턴스가 실행될 수 있으며, 클라이언트는 이들 간에 로드 밸런싱이 필요합니다.
  3. 서비스 검색: 다른 애플리케이션이 특정 서비스를 사용하려면 해당 서비스를 제공하는 파드를 찾을 방법이 필요합니다.

쿠버네티스 서비스는 이러한 문제를 해결하기 위해 설계되었습니다. 서비스는 논리적으로 관련된 파드 집합에 대한 안정적인 엔드포인트와 추상화를 제공합니다.

ClusterIP 서비스

ClusterIP는 쿠버네티스의 기본 서비스 타입으로, 클러스터 내부에서만 접근 가능한 고정 IP 주소를 제공합니다.

주요 특징:

  • 클러스터 내부에서만 접근 가능
  • 안정적인 내부 IP 주소 제공
  • 선택된 파드 간 자동 로드 밸런싱
  • 서비스 이름으로 DNS 접근 가능

YAML 정의 예시:

apiVersion: v1
kind: Service
metadata:
  name: my-backend-service
spec:
  selector:
    app: backend
  ports:
    - protocol: TCP
      port: 80        # 서비스가 노출되는 포트
      targetPort: 8080  # 파드의 대상 포트
  type: ClusterIP      # 기본값이므로 생략 가능

위 예시에서:

  • selector는 어떤 파드가 이 서비스에 포함될지 결정합니다. 여기서는 app: backend 레이블을 가진 모든 파드가 서비스의 일부가 됩니다.
  • port는 서비스가 노출되는 포트입니다.
  • targetPort는 트래픽이 전달될 파드의 포트입니다.

서비스 생성 및 조회 명령어:

# YAML 파일로 서비스 생성
kubectl apply -f backend-service.yaml

# 서비스 목록 조회
kubectl get services

# 특정 서비스 상세 정보 조회
kubectl describe service my-backend-service

사용 사례:

  • 마이크로서비스 아키텍처에서 서비스 간 통신
  • 프론트엔드와 백엔드 분리 구조
  • 내부 데이터베이스 접근
  • 캐싱 서버나 메시지 큐와 같은 내부 인프라 서비스

NodePort 서비스

NodePort 서비스는 ClusterIP 서비스의 기능을 확장하여, 클러스터의 모든 노드에서 특정 포트를 통해 서비스에 접근할 수 있게 합니다.

주요 특징:

  • ClusterIP의 모든 기능 포함
  • 각 노드의 특정 포트(NodePort)를 통해 외부에서 접근 가능
  • 포트 범위: 30000-32767(기본)
  • 각 노드의 IP와 할당된 NodePort를 통해 서비스에 접근

YAML 정의 예시:

apiVersion: v1
kind: Service
metadata:
  name: my-web-service
spec:
  selector:
    app: web
  ports:
    - protocol: TCP
      port: 80          # 서비스 포트
      targetPort: 8080  # 파드 포트
      nodePort: 30080   # 노드 포트(생략 시 30000-32767 범위에서 자동 할당)
  type: NodePort

위 예시에서:

  • nodePort는 각 노드에서 서비스에 접근하기 위한 포트를 지정합니다. 생략하면 자동으로 할당됩니다.
  • 서비스는 http://<노드IP>:30080으로 접근할 수 있습니다.

서비스 접근 방법:

# 노드 IP 확인
kubectl get nodes -o wide

# 서비스 정보 확인(NodePort 포함)
kubectl get service my-web-service -o wide

# 브라우저나 curl로 접근
curl http://<노드IP>:30080

사용 사례:

  • 개발 및 테스트 환경에서 빠른 외부 접근
  • 데모나 프로토타입 구축
  • 소규모 프로덕션 환경
  • 로드 밸런서 없이 외부 서비스 노출이 필요한 경우

주의사항:

  • 노드 IP가 변경될 수 있으므로 프로덕션 환경에선 안정적이지 않을 수 있음
  • 모든 노드의 방화벽이 해당 NodePort를 허용해야 함
  • 단일 노드 실패 시 가용성 문제 발생 가능

기타 서비스 타입

쿠버네티스는 ClusterIP와 NodePort 외에도 다음과 같은 서비스 타입을 제공합니다:

  1. LoadBalancer: 클라우드 제공자의 로드 밸런서를 프로비저닝하여 서비스를 외부에 노출합니다.
   apiVersion: v1
   kind: Service
   metadata:
     name: my-loadbalanced-service
   spec:
     selector:
       app: frontend
     ports:
       - port: 80
         targetPort: 8080
     type: LoadBalancer
  1. ExternalName: 외부 서비스를 쿠버네티스 내부로 가져오기 위한 서비스 타입입니다.
   apiVersion: v1
   kind: Service
   metadata:
     name: my-database
   spec:
     type: ExternalName
     externalName: db.example.com
  1. Headless Service: clusterIP: None으로 설정하여 클러스터 IP 없이 서비스 디스커버리만 제공합니다.
   apiVersion: v1
   kind: Service
   metadata:
     name: my-headless-service
   spec:
     clusterIP: None
     selector:
       app: backend
     ports:
       - port: 80
         targetPort: 8080

서비스 디스커버리와 DNS

쿠버네티스는 서비스 디스커버리를 위해 내부 DNS 시스템을 제공합니다. 클러스터 내 모든 서비스는 자동으로 DNS 레코드를 받습니다.

DNS 이름 형식:

<서비스 이름>.<네임스페이스>.svc.cluster.local

예시:

  • 같은 네임스페이스 내에서는 단순히 서비스 이름만으로 접근 가능: curl http://my-backend-service
  • 다른 네임스페이스의 서비스에 접근: curl http://my-backend-service.other-namespace.svc.cluster.local

환경 변수:
서비스가 생성되면 쿠버네티스는 자동으로 모든 파드에 환경 변수를 주입합니다:

<서비스 이름 대문자>_SERVICE_HOST=<서비스 ClusterIP>
<서비스 이름 대문자>_SERVICE_PORT=<서비스 포트>

예시:

MY_BACKEND_SERVICE_SERVICE_HOST=10.0.0.1
MY_BACKEND_SERVICE_SERVICE_PORT=80

네임스페이스 (Namespace)

네임스페이스의 필요성

쿠버네티스 네임스페이스는 클러스터 내에서 리소스 그룹을 논리적으로 분리하는 방법을 제공합니다. 네임스페이스를 사용하면 다음과 같은 이점이 있습니다:

  1. 리소스 격리: 다양한 팀, 프로젝트 또는 환경(개발, 테스트, 프로덕션)을 분리할 수 있습니다.
  2. 이름 충돌 방지: 다른 네임스페이스에서는 같은 이름의 리소스를 가질 수 있습니다.
  3. 액세스 제어: RBAC(Role-Based Access Control)을 네임스페이스 단위로 적용할 수 있습니다.
  4. 리소스 쿼터: 네임스페이스별로 리소스 사용량을 제한할 수 있습니다.

네임스페이스는 특히 멀티 테넌트 환경이나 대규모 클러스터에서 리소스 관리를 단순화하는 데 유용합니다.

기본 네임스페이스

쿠버네티스 클러스터는 기본적으로 다음 네임스페이스와 함께 제공됩니다:

  1. default: 특별히 네임스페이스를 지정하지 않은 리소스가 생성되는 기본 네임스페이스입니다.
  2. kube-system: 쿠버네티스 시스템이 사용하는 네임스페이스로, 컨트롤 플레인 구성요소 및 애드온이 여기에 위치합니다.
  3. kube-public: 모든 사용자(인증되지 않은 사용자 포함)가 읽을 수 있는 리소스를 포함합니다.
  4. kube-node-lease: 노드 하트비트 정보를 저장하는 네임스페이스입니다.

이러한 네임스페이스는 다음 명령으로 확인할 수 있습니다:

kubectl get namespaces

네임스페이스 생성 및 관리

네임스페이스 생성:

YAML 파일을 사용한 생성:

apiVersion: v1
kind: Namespace
metadata:
  name: development

명령어를 사용한 생성:

kubectl create namespace development

네임스페이스 삭제:

kubectl delete namespace development

특정 네임스페이스의 리소스 관리:

네임스페이스에 리소스 생성:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: development  # 네임스페이스 지정
spec:
  containers:
  - name: nginx
    image: nginx

네임스페이스의 리소스 조회:

kubectl get pods -n development
kubectl get all -n development

특정 네임스페이스를 기본값으로 설정:

kubectl config set-context --current --namespace=development

네임스페이스 활용 전략

환경 분리:
각 환경(개발, 테스트, 스테이징, 프로덕션)을 별도의 네임스페이스로 구성할 수 있습니다.

kubectl create namespace development
kubectl create namespace testing
kubectl create namespace staging
kubectl create namespace production

팀 또는 프로젝트 분리:
각 팀이나 프로젝트에 대해 별도의 네임스페이스를 생성하여 관리할 수 있습니다.

kubectl create namespace team-a
kubectl create namespace team-b
kubectl create namespace project-x

리소스 쿼터 적용:
네임스페이스별로 리소스 사용량을 제한할 수 있습니다.

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-a-quota
  namespace: team-a
spec:
  hard:
    pods: "10"
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi

네임스페이스 레이블 지정:
네임스페이스에 레이블을 추가하여 분류할 수 있습니다.

kubectl label namespace development environment=dev
kubectl label namespace team-a team=frontend

주의사항:

  • 일부 리소스(노드, 퍼시스턴트 볼륨 등)는 네임스페이스에 속하지 않습니다.
  • 네임스페이스는 네트워크 격리를 제공하지 않습니다. 네트워크 정책을 별도로 구성해야 합니다.

컨피그맵 (ConfigMap)

컨피그맵의 필요성

애플리케이션을 개발하고 배포할 때 환경별로 다른 구성 설정이 필요합니다. 그러나 애플리케이션 코드에 직접 구성을 하드코딩하는 것은 여러 문제를 야기합니다:

  1. 코드 변경 없이 구성 변경 불가: 설정을 변경하려면 코드를 수정하고 다시 빌드해야 합니다.
  2. 환경별 다른 빌드 필요: 개발, 테스트, 프로덕션 환경마다 다른 빌드가 필요합니다.
  3. 보안 위험: 민감한 구성 정보가 코드에 노출될 수 있습니다.

컨피그맵은 이러한 문제를 해결하기 위해 설계되었으며, 다음과 같은 이점을 제공합니다:

  1. 구성과 코드 분리: 12 팩터 앱(The Twelve-Factor App)의 원칙에 따라 코드와 구성을 분리합니다.
  2. 환경별 쉬운 구성 변경: 각 환경마다 다른 컨피그맵을 사용할 수 있습니다.
  3. 런타임 구성 업데이트: 애플리케이션을 재배포하지 않고도 구성을 업데이트할 수 있습니다(재시작이 필요할 수 있음).

컨피그맵 생성 방법

컨피그맵은 다양한 방법으로 생성할 수 있습니다:

1. YAML 파일로 정의:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  database_host: "mysql.database"
  database_port: "3306"
  features.json: |
    {
      "feature1": true,
      "feature2": false,
      "timeout": 30
    }
  app.properties: |
    app.name=MyApp
    app.environment=development
    app.log.level=INFO

2. 명령어로 생성:

리터럴 값 사용:

kubectl create configmap app-config \
  --from-literal=database_host=mysql.database \
  --from-literal=database_port=3306

파일 사용:

# 단일 파일
kubectl create configmap app-properties --from-file=app.properties

# 디렉토리 내 모든 파일
kubectl create configmap config-files --from-file=config-dir/

# 특정 키 이름으로 파일 내용 매핑
kubectl create configmap app-config --from-file=features=features.json

3. 복수 방법 조합:

kubectl create configmap app-config \
  --from-literal=database_host=mysql.database \
  --from-file=app.properties \
  --from-file=config-dir/

컨피그맵 활용 방법

컨피그맵은 다음과 같은 방법으로 파드에서 사용할 수 있습니다:

1. 환경 변수로 사용:

특정 키 참조:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: my-app:1.0
    env:
    - name: DB_HOST
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: database_host
    - name: DB_PORT
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: database_port

모든 키를 환경 변수로 가져오기:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: my-app:1.0
    envFrom:
    - configMapRef:
        name: app-config

2. 볼륨으로 마운트:

전체 컨피그맵을 볼륨으로 마운트:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: my-app:1.0
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: app-config

특정 키만 마운트:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: my-app:1.0
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config/app.properties
      subPath: app.properties
  volumes:
  - name: config-volume
    configMap:
      name: app-config
      items:
      - key: app.properties
        path: app.properties

3. 파일 권한 설정:

apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: my-app:1.0
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: app-config
      defaultMode: 0400  # 읽기 전용 권한 설정

실제 사용 예시

애플리케이션 설정 관리:

컨피그맵으로 애플리케이션 설정 관리:

# 개발 환경 컨피그맵
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-dev
  namespace: development
data:
  config.properties: |
    app.environment=development
    app.log.level=DEBUG
    app.feature.experimental=true
---
# 프로덕션 환경 컨피그맵
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config-prod
  namespace: production
data:
  config.properties: |
    app.environment=production
    app.log.level=WARN
    app.feature.experimental=false

데이터베이스 연결 구성:

apiVersion: v1
kind: ConfigMap
metadata:
  name: db-config
data:
  db.conf: |
    host=mysql.database
    port=3306
    max_connections=100
    timeout=30

로깅 구성:

apiVersion: v1
kind: ConfigMap
metadata:
  name: logging-config
data:
  log4j.properties: |
    log4j.rootLogger=INFO, stdout
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

실전 디플로이먼트 예시:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web
        image: my-web-app:1.0
        ports:
        - containerPort: 8080
        env:
        - name: DB_URL
          valueFrom:
            configMapKeyRef:
              name: db-config
              key: url
        volumeMounts:
        - name: config-volume
          mountPath: /app/config
        - name: logging-volume
          mountPath: /app/logging
      volumes:
      - name: config-volume
        configMap:
          name: app-config
      - name: logging-volume
        configMap:
          name: logging-config

컨피그맵 업데이트 및 파드 동작:

컨피그맵을 업데이트하면 볼륨으로 마운트된 파일은 자동으로 업데이트되지만(일반적으로 몇 분 내), 환경 변수는 파드가 재시작될 때까지 업데이트되지 않습니다.

# 컨피그맵 업데이트
kubectl edit configmap app-config

# 또는
kubectl patch configmap app-config --patch '{"data":{"database_host":"new-mysql.database"}}'

# 파드 재시작을 위한 디플로이먼트 롤아웃 재시작
kubectl rollout restart deployment web-app

시크릿 (Secret)

시크릿의 필요성

애플리케이션은 종종 데이터베이스 비밀번호, API 키, TLS 인증서와 같은 민감한 데이터에 접근해야 합니다. 이러한 민감 정보를 안전하게 관리하기 위해 쿠버네티스는 시크릿이라는 별도의 리소스 타입을 제공합니다.

시크릿은 컨피그맵과 유사하지만 다음과 같은 특징이 있습니다:

  1. 민감 데이터 전용: 비밀번호, 토큰, 키와 같은 민감한 정보를 저장합니다.
  2. 메모리 저장: 시크릿은 노드의 tmpfs(메모리 기반 파일 시스템)에 저장되어 디스크에 기록되지 않습니다.
  3. 접근 제한: RBAC를 통해 특정 서비스 계정이나 사용자만 시크릿에 접근할 수 있도록 제한할 수 있습니다.
  4. 용량 제한: 시크릿은 일반적으로 1MB 미만의 작은 데이터를 저장하도록 설계되었습니다.

시크릿 타입

쿠버네티스는 다양한 유형의 시크릿을 제공합니다:

  1. Opaque(기본 타입): 임의의 키-값 쌍을 저장하는 일반적인 시크릿입니다.
   apiVersion: v1
   kind: Secret
   metadata:
     name: db-credentials
   type: Opaque
   data:
     username: YWRtaW4=  # base64로 인코딩된 "admin"
     password: cGFzc3dvcmQxMjM=  # base64로 인코딩된 "password123"
  1. kubernetes.io/service-account-token: 서비스 계정 토큰을 저장합니다.
   apiVersion: v1
   kind: Secret
   metadata:
     name: service-account-token
     annotations:
       kubernetes.io/service-account.name: my-service-account
   type: kubernetes.io/service-account-token
  1. kubernetes.io/dockerconfigjson: Docker 레지스트리 인증 정보를 저장합니다.
   apiVersion: v1
   kind: Secret
   metadata:
     name: docker-registry-credentials
   type: kubernetes.io/dockerconfigjson
   data:
     .dockerconfigjson: <base64 인코딩된 Docker 설정>
  1. kubernetes.io/tls: TLS 인증서와 키를 저장합니다.
   apiVersion: v1
   kind: Secret
   metadata:
     name: tls-secret
   type: kubernetes.io/tls
   data:
     tls.crt: <base64 인코딩된 인증서>
     tls.key: <base64 인코딩된 키>
  1. kubernetes.io/ssh-auth: SSH 인증을 위한 데이터를 저장합니다.
   apiVersion: v1
   kind: Secret
   metadata:
     name: ssh-key-secret
   type: kubernetes.io/ssh-auth
   data:
     ssh-privatekey: <base64 인코딩된 SSH 개인 키>
  1. kubernetes.io/basic-auth: 기본 인증을 위한 자격 증명을 저장합니다.
   apiVersion: v1
   kind: Secret
   metadata:
     name: basic-auth
   type: kubernetes.io/basic-auth
   data:
     username: <base64 인코딩된 사용자 이름>
     password: <base64 인코딩된 비밀번호>

시크릿 생성 방법

시크릿은 다양한 방법으로 생성할 수 있습니다:

1. YAML 파일로 정의:

먼저 민감한 데이터를 base64로 인코딩합니다:

echo -n 'admin' | base64
# 출력: YWRtaW4=
echo -n 'password123' | base64
# 출력: cGFzc3dvcmQxMjM=

그런 다음 YAML 파일에 인코딩된 값을 사용합니다:

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=
  password: cGFzc3dvcmQxMjM=

2. 명령어로 생성:

리터럴 값 사용(자동 인코딩):

kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=password123

파일 사용:

# 파일에서 생성
echo -n 'admin' > ./username.txt
echo -n 'password123' > ./password.txt
kubectl create secret generic db-credentials \
  --from-file=username=./username.txt \
  --from-file=password=./password.txt

# 특정 파일 타입 (TLS)
kubectl create secret tls tls-secret \
  --cert=path/to/tls.cert \
  --key=path/to/tls.key

# Docker 레지스트리 인증
kubectl create secret docker-registry regcred \
  --docker-server=<레지스트리 주소> \
  --docker-username=<사용자 이름> \
  --docker-password=<비밀번호> \
  --docker-email=<이메일>

3. 평문으로 정의하고 적용 시 인코딩:

apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
stringData:  # base64 인코딩 없이 평문 사용
  username: admin
  password: password123

시크릿 활용 방법

시크릿은 컨피그맵과 유사한 방식으로 파드에서 사용할 수 있습니다:

1. 환경 변수로 사용:

특정 키 참조:

apiVersion: v1
kind: Pod
metadata:
  name: db-client-pod
spec:
  containers:
  - name: db-client
    image: db-client:1.0
    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: db-client-pod
spec:
  containers:
  - name: db-client
    image: db-client:1.0
    envFrom:
    - secretRef:
        name: db-credentials

2. 볼륨으로 마운트:

전체 시크릿을 볼륨으로 마운트:

apiVersion: v1
kind: Pod
metadata:
  name: db-client-pod
spec:
  containers:
  - name: db-client
    image: db-client:1.0
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true  # 중요: 읽기 전용으로 마운트
  volumes:
  - name: secret-volume
    secret:
      secretName: db-credentials

특정 키만 마운트:

apiVersion: v1
kind: Pod
metadata:
  name: db-client-pod
spec:
  containers:
  - name: db-client
    image: db-client:1.0
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets/credentials.txt
      subPath: username
  volumes:
  - name: secret-volume
    secret:
      secretName: db-credentials
      items:
      - key: username
        path: credentials.txt

3. 이미지 풀 시크릿 사용:

apiVersion: v1
kind: Pod
metadata:
  name: private-image-pod
spec:
  containers:
  - name: app
    image: private-registry.com/my-app:1.0
  imagePullSecrets:
  - name: regcred

4. TLS 시크릿을 인그레스에 사용:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: secure-ingress
spec:
  tls:
  - hosts:
    - myapp.example.com
    secretName: tls-secret
  rules:
  - host: myapp.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80

시크릿 보안 고려사항

시크릿은 민감한 정보를 관리하기 위한 것이지만, 기본 쿠버네티스 설정에서는 완벽한 보안을 제공하지 않습니다. 다음 사항을 고려해야 합니다:

1. etcd 암호화:

기본적으로 시크릿은 etcd(쿠버네티스 데이터 저장소)에 암호화되지 않은 상태로 저장됩니다. etcd에 저장된 데이터를 암호화하려면 다음과 같은 설정이 필요합니다:

# /etc/kubernetes/encryption-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <32바이트 base64 인코딩 키>
      - identity: {}  # 비암호화 옵션, 필요시 사용

API 서버에 설정 적용:

kube-apiserver --encryption-provider-config=/etc/kubernetes/encryption-config.yaml

2. RBAC 권한 제한:

시크릿에 대한 접근을 최소 권한 원칙에 따라 제한해야 합니다:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
  resourceNames: ["db-credentials"]  # 특정 시크릿으로 제한
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-secrets
  namespace: default
subjects:
- kind: ServiceAccount
  name: my-service-account
  namespace: default
roleRef:
  kind: Role
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

3. 네임스페이스 격리:

시크릿은 네임스페이스에 따라 격리되므로, 중요한 시크릿은 접근이 제한된 별도의 네임스페이스에 보관하는 것이 좋습니다.

4. 볼륨 마운트 vs 환경 변수:

환경 변수로 사용된 시크릿은 프로세스 정보에 노출될 수 있으므로, 매우 중요한 시크릿은 볼륨으로 마운트하는 것이 더 안전할 수 있습니다.

5. 외부 시크릿 관리 솔루션 고려:

프로덕션 환경에서는 HashiCorp Vault, AWS Secrets Manager, Azure Key Vault 등과 같은 외부 시크릿 관리 솔루션을 고려할 수 있습니다. 이를 쿠버네티스와 통합하기 위한 여러 오퍼레이터와 컨트롤러가 있습니다.

6. 시크릿 순환:

보안을 강화하기 위해 정기적으로 시크릿을 교체하는 프로세스를 구축하는 것이 좋습니다.

# 시크릿 업데이트
kubectl create secret generic db-credentials \
  --from-literal=username=admin \
  --from-literal=password=new-password123 \
  --dry-run=client -o yaml | kubectl apply -f -

7. GitOps 보안:

GitOps 워크플로우에서 시크릿을 관리할 때는 암호화된 형태로 저장해야 합니다. Sealed Secrets, SOPS(Secrets OPerationS), Vault 등의 도구를 고려할 수 있습니다.

모범 사례 및 팁

서비스 모범 사례

  1. 적절한 서비스 타입 선택:
    • 내부 통신만 필요한 경우 ClusterIP 사용
    • 외부 접근이 필요하지만 로드 밸런서가 없는 경우 NodePort 고려
    • 프로덕션 환경의 외부 노출은 LoadBalancer 또는 Ingress 사용
  2. 서비스 디스커버리 최적화:
    • 의미 있는 서비스 이름 사용
    • 헤드리스 서비스(clusterIP: None)를 통한 직접 Pod 접근 (StatefulSet에 유용)
  3. 레이블 선택자 관리:
    • 명확하고 일관된 레이블링 전략 사용
    • 특정 버전이나 변경 가능한 레이블로 서비스 대상 지정 피하기
  4. 서비스 메시 고려:
    • 복잡한 마이크로서비스 환경에서는 Istio, Linkerd와 같은 서비스 메시 도입 고려

네임스페이스 모범 사례

  1. 명확한 네임스페이스 전략:
    • 네임스페이스 사용 목적 정의 (팀, 프로젝트, 환경 등)
    • 일관된 네이밍 규칙 적용
  2. 리소스 쿼터 설정:
    • 모든 네임스페이스에 적절한 리소스 쿼터 적용
    • CPU, 메모리, 스토리지, 객체 수 제한 설정
  3. 네임스페이스 레이블 및 주석:
    • 소유자, 환경, 용도 등 메타데이터 추가
    • 네임스페이스 사용 목적 문서화
  4. 네트워크 정책 적용:
    • 네임스페이스 간 통신 제한을 위한 네트워크 정책 구성
    • 기본 거부 정책으로 시작하여 필요한 통신만 허용

컨피그맵 모범 사례

  1. 적절한 데이터 크기:
    • 컨피그맵은 1MB 미만의 작은 데이터에 적합
    • 대용량 데이터는 PV/PVC 또는 외부 저장소 고려
  2. 환경별 구성 관리:
    • 환경별로 접미사 사용 (app-config-dev, app-config-prod)
    • Kustomize, Helm 등의 도구로 환경별 구성 관리
  3. 불변성 유지:
    • 컨피그맵 수정보다 새 버전 생성 고려
    • 버전 관리를 위한 레이블/주석 사용
  4. 변경 감지 및 재로드:
    • 볼륨 마운트된 컨피그맵 변경 시 자동 업데이트 활용
    • 필요시 애플리케이션의 구성 리로드 메커니즘 구현

시크릿 모범 사례

  1. 시크릿과 컨피그맵 구분:
    • 민감한 정보는 항상 컨피그맵이 아닌 시크릿에 저장
    • API 키, 비밀번호, 인증서 등은 시크릿 사용
  2. 최소 권한 원칙:
    • 시크릿에 대한 RBAC 권한 최소화
    • 특정 시크릿만 접근 가능하도록 resourceNames 지정
  3. 시크릿 암호화:
    • etcd 암호화 구성
    • 외부 시크릿 관리 도구 사용 고려
  4. 시크릿 순환:
    • 정기적인 시크릿 교체 자동화
    • 시크릿 만료 시간 추적 및 관리
  5. 시크릿 보호:
    • 민감한 시크릿은 볼륨으로 마운트 (환경 변수보다 안전)
    • 필요시 시크릿 사용 후 메모리에서 제거

참고