Kubernetes 볼륨 권한 문제 해결 방법

KubernetesBeginner
지금 연습하기

소개

Kubernetes 에서 볼륨 권한을 관리하는 것은 개발자와 시스템 관리자에게 어려울 수 있습니다. 컨테이너가 영구 볼륨에서 읽거나 쓰기를 해야 할 때, 컨테이너 사용자 ID 와 볼륨 소유권 간의 불일치로 인해 권한 문제가 자주 발생합니다. 이러한 문제는 애플리케이션 실패 및 데이터 접근 문제를 야기할 수 있습니다.

이 Lab 에서는 Kubernetes 에서 흔히 발생하는 볼륨 권한 문제를 안내하고 이를 해결하기 위한 실질적인 솔루션을 제공합니다. 보안 컨텍스트를 적절하게 구성하고, init 컨테이너를 사용하며, Kubernetes 배포에서 볼륨 권한 관리를 위한 모범 사례를 구현하는 방법을 배우게 됩니다.

Kubernetes 볼륨 이해

이 단계에서는 Kubernetes 볼륨을 살펴보고 작동 방식을 이해합니다. Kubernetes 볼륨은 컨테이너가 다시 시작되거나 재스케줄링되더라도 데이터를 영구적으로 저장하고 접근할 수 있는 방법을 제공합니다.

Kubernetes 볼륨 유형

Kubernetes 는 여러 볼륨 유형을 지원합니다.

  • EmptyDir: Pod 의 수명 동안 존재하는 간단한 빈 디렉토리입니다.
  • HostPath: 호스트 노드의 파일 시스템에서 파일 또는 디렉토리를 Pod 에 마운트합니다.
  • PersistentVolume: Pod 와 독립적인 수명 주기를 가진 관리자가 프로비저닝한 스토리지 리소스입니다.
  • ConfigMap 및 Secret: 구성 데이터 및 민감한 정보를 주입하는 방법을 제공합니다.

첫 번째 볼륨 생성

EmptyDir 볼륨을 사용하여 간단한 Pod 를 생성해 보겠습니다.

  1. Pod 구성을 위한 YAML 파일을 생성합니다.
cd ~/project/k8s-volume-demo
nano emptydir-pod.yaml
  1. 다음 내용을 파일에 복사합니다.
apiVersion: v1
kind: Pod
metadata:
  name: emptydir-pod
spec:
  containers:
    - name: container-1
      image: ubuntu:22.04
      command:
        [
          "/bin/bash",
          "-c",
          "while true; do echo $(date) >> /data/log.txt; sleep 10; done"
        ]
      volumeMounts:
        - name: data-volume
          mountPath: /data
  volumes:
    - name: data-volume
      emptyDir: {}
  1. 파일을 저장합니다 (Ctrl+X, Y, Enter).

  2. Kubernetes 클러스터에서 Pod 를 생성합니다.

kubectl apply -f emptydir-pod.yaml
  1. Pod 가 준비될 때까지 기다립니다.
kubectl get pods

출력 결과는 Pod 가 실행 중인 상태로 표시되어야 합니다.

NAME           READY   STATUS    RESTARTS   AGE
emptydir-pod   1/1     Running   0          30s
  1. 볼륨의 내용을 확인해 보겠습니다.
kubectl exec emptydir-pod -- cat /data/log.txt

컨테이너가 볼륨에 쓰고 있음을 보여주는 타임스탬프 목록이 표시되어야 합니다.

Mon Jan 1 12:34:56 UTC 2023
Mon Jan 1 12:35:06 UTC 2023
Mon Jan 1 12:35:16 UTC 2023

볼륨 마운트 이해

위의 예에서:

  • emptyDir 유형의 data-volume이라는 볼륨을 정의했습니다.
  • 이 볼륨을 /data 경로의 컨테이너에 마운트했습니다.
  • 컨테이너는 이 볼륨의 파일에 타임스탬프를 씁니다.

이는 Kubernetes 볼륨의 기본 개념을 보여줍니다. 즉, Pod 의 컨테이너에서 접근할 수 있는 스토리지를 제공합니다. EmptyDir 볼륨은 Pod 의 수명 동안 존재하므로 Pod 가 삭제되면 데이터가 손실됩니다.

정리

생성한 Pod 를 삭제해 보겠습니다.

kubectl delete pod emptydir-pod

다음 단계에서는 볼륨을 사용할 때 권한 문제가 발생할 수 있는 방법과 이를 식별하는 방법을 살펴보겠습니다.

볼륨 권한 문제 식별

이 단계에서는 Kubernetes 에서 흔히 발생하는 볼륨 권한 문제를 보여주는 시나리오를 생성합니다. 이러한 문제는 일반적으로 HostPath 볼륨 또는 파일 시스템 권한이 컨테이너에서 실행되는 사용자 ID 와 일치하지 않는 영구 볼륨을 사용할 때 발생합니다.

문제 이해

컨테이너가 root 가 아닌 사용자로 실행되지만 root(또는 다른 사용자) 가 소유한 볼륨에 접근하려고 할 때 권한 거부 오류가 발생할 수 있습니다. 이는 컨테이너를 root 가 아닌 사용자로 실행하는 것이 보안 모범 사례인 프로덕션 환경에서 흔히 발생하는 문제입니다.

권한 문제가 있는 HostPath 볼륨 생성

root 소유권으로 HostPath 볼륨에 접근하려는 Pod 를 생성해 보겠습니다.

  1. Pod 구성을 위한 YAML 파일을 생성합니다.
cd ~/project/k8s-volume-demo
nano hostpath-pod.yaml
  1. 다음 내용을 파일에 복사합니다.
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-pod
spec:
  containers:
    - name: container-1
      image: ubuntu:22.04
      command:
        [
          "/bin/bash",
          "-c",
          "while true; do echo 'Trying to write' >> /data/output.txt; sleep 10; done"
        ]
      volumeMounts:
        - name: host-data
          mountPath: /data
      securityContext:
        runAsUser: 1000 ## Run as non-root user
  volumes:
    - name: host-data
      hostPath:
        path: /home/labex/project/k8s-volume-demo/data
        type: Directory
  1. 파일을 저장합니다 (Ctrl+X, Y, Enter).

  2. Kubernetes 클러스터에서 Pod 를 생성합니다.

kubectl apply -f hostpath-pod.yaml
  1. 잠시 기다린 다음 Pod 상태를 확인합니다.
kubectl get pods
  1. Pod 가 실행 중인 것을 볼 수 있지만, 로그를 확인하면 오류가 표시될 수 있습니다.
kubectl logs hostpath-pod

다음과 같은 권한 거부 오류가 표시될 수 있습니다.

bash: /data/output.txt: Permission denied
  1. 호스트 디렉토리의 권한을 확인하여 문제를 확인해 보겠습니다.
ls -la ~/project/k8s-volume-demo/data

다음과 같은 출력이 표시되어야 합니다.

total 12
drwxr-xr-x 2 root  root  4096 Jan  1 12:00 .
drwxr-xr-x 3 labex labex 4096 Jan  1 12:00 ..
-rw-r--r-- 1 root  root    19 Jan  1 12:00 test.txt

디렉토리와 파일은 root가 소유하고 있지만, 컨테이너는 사용자 ID 1000으로 실행됩니다. 이러한 불일치로 인해 권한 거부 오류가 발생합니다.

컨테이너의 사용자 및 그룹 ID 이해

Kubernetes 에서 컨테이너는 securityContext 구성을 통해 특정 사용자 ID 로 실행될 수 있습니다. 이 예에서:

securityContext:
  runAsUser: 1000 ## Run as non-root user

이는 Kubernetes 에 컨테이너 프로세스를 사용자 ID 1000 으로 실행하도록 지시합니다. 이 사용자는 root 가 소유한 파일에 쓸 권한이 없습니다.

정리

다음 단계로 넘어가기 전에 Pod 를 삭제해 보겠습니다.

kubectl delete pod hostpath-pod

다음 단계에서는 이러한 권한 문제에 대한 솔루션을 살펴보겠습니다.

Security Context 를 사용하여 권한 문제 해결

이 단계에서는 Kubernetes SecurityContext 를 사용하여 볼륨 권한 문제를 해결하는 방법을 살펴보겠습니다. SecurityContext 는 Pod 및 컨테이너에 대한 권한 및 접근 제어 설정을 정의합니다.

fsGroup 을 사용하여 권한 수정

SecurityContext 의 fsGroup 설정은 권한 문제를 해결하는 데 도움이 될 수 있습니다. 지정되면 Kubernetes 는 볼륨의 그룹 소유권을 지정된 그룹 ID 와 일치하도록 변경하고, 해당 그룹에서 볼륨을 읽고 쓸 수 있도록 권한을 설정합니다.

적절한 보안 컨텍스트로 Pod 를 생성해 보겠습니다.

  1. Pod 구성을 위한 YAML 파일을 생성합니다.
cd ~/project/k8s-volume-demo
nano fixed-pod.yaml
  1. 다음 내용을 파일에 복사합니다.
apiVersion: v1
kind: Pod
metadata:
  name: fixed-pod
spec:
  securityContext:
    fsGroup: 2000 ## Set group ID for all containers in the pod
  containers:
    - name: container-1
      image: ubuntu:22.04
      command:
        [
          "/bin/bash",
          "-c",
          "while true; do echo $(date) >> /data/output.txt; cat /data/test.txt; sleep 10; done"
        ]
      volumeMounts:
        - name: host-data
          mountPath: /data
      securityContext:
        runAsUser: 1000 ## Run as non-root user
  volumes:
    - name: host-data
      hostPath:
        path: /home/labex/project/k8s-volume-demo/data
        type: Directory
  1. 파일을 저장합니다 (Ctrl+X, Y, Enter).

  2. Kubernetes 클러스터에서 Pod 를 생성합니다.

kubectl apply -f fixed-pod.yaml
  1. Pod 가 준비될 때까지 기다립니다.
kubectl get pods
  1. 이제 로그를 확인하여 권한 문제가 해결되었는지 확인해 보겠습니다.
kubectl logs fixed-pod

컨테이너가 이제 볼륨에 쓸 수 있음을 보여주는 타임스탬프가 성공적으로 기록되는 것을 볼 수 있습니다.

This is a test file
Mon Jan 1 12:45:06 UTC 2023
This is a test file
Mon Jan 1 12:45:16 UTC 2023
  1. 볼륨 권한에 어떤 일이 일어났는지 살펴보겠습니다.
## Get into the container
kubectl exec -it fixed-pod -- bash

## Inside the container, check the permissions
ls -la /data

## Exit the container
exit

Kubernetes 가 fsGroup 설정을 적용했기 때문에 볼륨의 파일에 이제 컨테이너에서 접근할 수 있는 것을 볼 수 있습니다.

Security Context 설정 이해

  • runAsUser: 컨테이너 프로세스가 실행될 사용자 ID 를 지정합니다.
  • fsGroup: 볼륨 접근에 사용되는 그룹 ID 를 제어합니다. Pod 의 모든 프로세스는 이 보조 그룹의 일부가 됩니다.
  • runAsGroup: 컨테이너 내의 모든 프로세스에 대한 기본 그룹 ID 를 지정합니다 (선택 사항).

이러한 설정은 컨테이너가 root 로 실행되지 않는 보안 모범 사례를 유지하면서 볼륨 데이터에 적절하게 접근할 수 있도록 돕습니다.

정리

생성한 Pod 를 삭제해 보겠습니다.

kubectl delete pod fixed-pod

다음 단계에서는 init 컨테이너를 사용하여 권한 문제를 해결하는 또 다른 접근 방식을 살펴보겠습니다.

Init 컨테이너를 사용하여 권한 수정

경우에 따라 fsGroup을 사용하는 것이 충분하지 않거나 불가능할 수 있습니다. 예를 들어, 특정 볼륨 유형을 사용하거나 이전 버전의 Kubernetes 에서 실행할 때입니다. 이러한 경우, 주 컨테이너가 시작되기 전에 올바른 권한을 설정하기 위해 init 컨테이너를 사용할 수 있습니다.

Init 컨테이너란 무엇인가요?

Init 컨테이너는 Pod 의 주 컨테이너 전에 실행됩니다. 권한 설정, 콘텐츠 다운로드 또는 종속성 대기 등 초기화 작업을 수행할 수 있습니다. Init 컨테이너는 볼륨 파일의 소유권 및 권한을 변경하기 위해 높은 권한으로 실행될 수 있으므로 권한 관리에 특히 유용합니다.

Init 컨테이너가 있는 Pod 생성

init 컨테이너를 사용하여 권한을 수정하는 Pod 를 생성해 보겠습니다.

  1. Pod 구성을 위한 YAML 파일을 생성합니다.
cd ~/project/k8s-volume-demo
nano init-pod.yaml
  1. 다음 내용을 파일에 복사합니다.
apiVersion: v1
kind: Pod
metadata:
  name: init-pod
spec:
  initContainers:
    - name: permission-fixer
      image: ubuntu:22.04
      command:
        ["/bin/bash", "-c", "chown -R 1000:1000 /data && chmod -R 755 /data"]
      volumeMounts:
        - name: host-data
          mountPath: /data
      securityContext:
        runAsUser: 0 ## Run as root to change permissions
  containers:
    - name: main-container
      image: ubuntu:22.04
      command:
        [
          "/bin/bash",
          "-c",
          "while true; do echo $(date) >> /data/init-output.txt; cat /data/test.txt; sleep 10; done"
        ]
      volumeMounts:
        - name: host-data
          mountPath: /data
      securityContext:
        runAsUser: 1000 ## Run as non-root user
  volumes:
    - name: host-data
      hostPath:
        path: /home/labex/project/k8s-volume-demo/data
        type: Directory
  1. 파일을 저장합니다 (Ctrl+X, Y, Enter).

  2. Kubernetes 클러스터에서 Pod 를 생성합니다.

kubectl apply -f init-pod.yaml
  1. Pod 가 준비될 때까지 기다립니다. init 컨테이너가 완료될 때까지 Pod 는 "Init" 상태로 유지됩니다.
kubectl get pods
  1. Pod 가 실행 상태가 되면 주 컨테이너의 로그를 확인합니다.
kubectl logs init-pod

컨테이너가 이제 볼륨에 쓸 수 있음을 보여주는 타임스탬프가 성공적으로 기록되는 것을 볼 수 있습니다.

This is a test file
Mon Jan 1 13:05:06 UTC 2023
This is a test file
Mon Jan 1 13:05:16 UTC 2023
  1. 볼륨 권한에 어떤 일이 일어났는지 살펴보겠습니다.
## Check the content of the data directory
ls -la ~/project/k8s-volume-demo/data

파일 소유권이 init 컨테이너의 명령에 지정된 사용자 ID 를 반영하도록 변경된 것을 알 수 있습니다.

Init 컨테이너 접근 방식 이해

이 예에서:

  1. init 컨테이너는 root 권한으로 실행됩니다 (runAsUser: 0)
  2. 볼륨 내용의 소유권 및 권한을 변경합니다.
  3. init 컨테이너가 완료되면 주 컨테이너가 시작됩니다.
  4. 주 컨테이너는 root 가 아닌 사용자 (runAsUser: 1000) 로 실행됩니다.
  5. 주 컨테이너는 이제 볼륨을 읽고 쓸 수 있습니다.

이 기술은 특히 다음과 같은 경우에 유용합니다.

  • 특정 소유권 패턴으로 볼륨을 준비해야 하는 경우
  • fsGroup을 지원하지 않는 볼륨으로 작업하는 경우
  • 복잡한 권한 설정 로직을 수행해야 하는 경우

정리

생성한 Pod 를 삭제해 보겠습니다.

kubectl delete pod init-pod

다음 단계에서는 모범 사례를 살펴보고 강력한 볼륨 권한 관리를 위해 이러한 접근 방식을 결합하는 방법을 배우겠습니다.

모범 사례 및 결합된 접근 방식

실제 시나리오에서는 볼륨 권한을 효과적으로 처리하기 위해 여러 접근 방식을 결합해야 할 수 있습니다. 이 마지막 단계에서는 모범 사례를 살펴보고 강력한 솔루션을 구현하는 포괄적인 예제를 만들 것입니다.

볼륨 권한 모범 사례

Kubernetes 에서 볼륨 권한을 관리하기 위한 몇 가지 주요 모범 사례는 다음과 같습니다.

  1. 가능한 경우 컨테이너를 root 가 아닌 사용자로 실행합니다.
  2. 애플리케이션이 작동하는 데 필요한 최소 권한 사용자를 사용합니다.
  3. Pod 및 컨테이너 수준에서 보안 컨텍스트를 활용합니다.
  4. 조직 전체에서 UID/GID 값을 표준화합니다.
  5. 복잡한 설정 시나리오에 init 컨테이너를 사용합니다.
  6. 권한 문제에 대한 적절한 오류 처리를 구현합니다.

포괄적인 솔루션 생성

학습 내용을 강력한 솔루션으로 결합하는 배포를 만들어 보겠습니다.

  1. 배포를 위한 YAML 파일을 생성합니다.
cd ~/project/k8s-volume-demo
nano best-practice-deployment.yaml
  1. 다음 내용을 파일에 복사합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
  name: volume-best-practices
  labels:
    app: volume-demo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: volume-demo
  template:
    metadata:
      labels:
        app: volume-demo
    spec:
      ## Pod-level security context
      securityContext:
        fsGroup: 2000
      ## Init container for advanced preparation
      initContainers:
        - name: volume-permissions
          image: ubuntu:22.04
          command:
            - /bin/bash
            - -c
            - |
              ## Create directory structure if it doesn't exist
              mkdir -p /data/app /data/logs /data/config
              ## Set appropriate permissions
              chmod 755 /data
              chmod 775 /data/app /data/logs
              chmod 755 /data/config
              ## Set ownership (belt and suspenders approach)
              chown -R 1000:2000 /data
              echo "Volume prepared successfully"
          volumeMounts:
            - name: data-volume
              mountPath: /data
          securityContext:
            runAsUser: 0 ## Run as root for setup
      ## Application containers
      containers:
        - name: app-container
          image: ubuntu:22.04
          command:
            - /bin/bash
            - -c
            - |
              echo "Starting application with user $(id)"
              while true; do
                echo "$(date) - Application running" >> /data/logs/app.log
                echo "Writing to app directory" >> /data/app/status.txt
                echo "Reading from config" 
                cat /data/config/test.txt || echo "No config found"
                sleep 10
              done
          volumeMounts:
            - name: data-volume
              mountPath: /data
          securityContext:
            runAsUser: 1000 ## Run as non-root user
            runAsGroup: 2000 ## Use the same group as fsGroup
            allowPrivilegeEscalation: false ## Prevent privilege escalation
      volumes:
        - name: data-volume
          hostPath:
            path: /home/labex/project/k8s-volume-demo/data
            type: Directory
  1. 파일을 저장합니다 (Ctrl+X, Y, Enter).

  2. Kubernetes 클러스터에서 배포를 생성합니다.

kubectl apply -f best-practice-deployment.yaml
  1. 배포가 준비될 때까지 기다립니다.
kubectl get pods -l app=volume-demo
  1. init 컨테이너의 로그를 확인해 보겠습니다.
## Get the pod name
POD_NAME=$(kubectl get pods -l app=volume-demo -o jsonpath='{.items[0].metadata.name}')

## Check the init container logs
kubectl logs $POD_NAME -c volume-permissions

볼륨이 성공적으로 준비되었다는 메시지가 표시되어야 합니다.

  1. 이제 애플리케이션 컨테이너의 로그를 확인합니다.
kubectl logs $POD_NAME -c app-container

애플리케이션이 실행 중이며 볼륨을 읽고 쓸 수 있는 것을 볼 수 있습니다.

  1. 배포에서 생성한 파일을 살펴보겠습니다.
ls -la ~/project/k8s-volume-demo/data

init 컨테이너에서 생성한 디렉토리 구조가 적절한 권한 및 소유권과 함께 표시되어야 합니다.

포괄적인 솔루션 이해

이 솔루션은 여러 가지 모범 사례를 결합합니다.

  1. 기본 권한을 설정하기 위한 fsGroup을 사용한 Pod 수준 보안 컨텍스트
  2. 복잡한 디렉토리 구조 설정을 위한 Init 컨테이너
  3. root 가 아닌 사용자로 실행하기 위한 컨테이너 수준 보안 컨텍스트
  4. fsGrouprunAsGroup 간의 적절한 그룹 정렬
  5. allowPrivilegeEscalation: false를 사용한 향상된 보안

이 접근 방식은 다음을 보장합니다.

  • 애플리케이션이 작동하는 데 필요한 권한이 있습니다.
  • 최소 권한의 원칙이 준수됩니다.
  • 솔루션은 다양한 환경에서 강력합니다.

정리

이 랩에서 생성된 모든 리소스를 정리해 보겠습니다.

kubectl delete deployment volume-best-practices
rm -rf ~/project/k8s-volume-demo/data/app ~/project/k8s-volume-demo/data/logs ~/project/k8s-volume-demo/data/config

이제 Kubernetes 에서 볼륨 권한 문제를 해결하기 위한 여러 가지 접근 방식을 배우고 모범 사례를 따르는 포괄적인 솔루션을 구현했습니다.

요약

이 랩에서는 다음과 같은 여러 가지 접근 방식을 통해 Kubernetes 볼륨 권한 문제를 식별하고 해결하는 방법을 배웠습니다.

  1. 먼저, Kubernetes 볼륨이 작동하는 방식을 이해하고 간단한 EmptyDir 볼륨 예제를 만들었습니다.
  2. 그런 다음, 사용자 권한이 일치하지 않는 HostPath 볼륨을 생성하여 일반적인 볼륨 권한 문제를 식별했습니다.
  3. 적절한 볼륨 권한을 설정하기 위해 fsGroup을 사용하여 Kubernetes SecurityContext 를 활용하는 솔루션을 구현했습니다.
  4. 주 컨테이너가 시작되기 전에 권한을 명시적으로 설정하기 위해 init 컨테이너를 사용하는 대체 접근 방식을 탐색했습니다.
  5. 마지막으로, 이러한 기술을 포괄적인 모범 사례 솔루션으로 결합하여 Kubernetes 에서 볼륨 권한을 관리하는 강력한 방법을 제공했습니다.

이러한 기술은 컨테이너화된 애플리케이션이 보안 모범 사례를 유지하면서 영구 저장소에 적절하게 액세스할 수 있도록 하는 데 도움이 됩니다. 보안 컨텍스트, init 컨테이너 및 권한 관리 기술을 적절하게 조합하여 Kubernetes 배포에서 일반적인 권한 문제를 방지할 수 있습니다.