Kubernetes: ConfigMap/Secret 이해하기

Kubernetes ConfigMap

Concept

ConfigMap은 쿠버네티스(Kubernetes)에서 애플리케이션의 구성을 파드에서 분리하여 저장하고 관리하기 위한 리소스입니다. ConfigMap을 사용하면 환경 변수, 커맨드라인 인수, 설정 파일 또는 다른 구성 데이터를 컨테이너화된 애플리케이션에 주입할 수 있습니다.

https://www.weave.works/blog/kubernetes-configmap

Features

ConfigMap의 주요 특징 및 사용 사례는 다음과 같습니다:

  1. 환경 변수로서의 구성: 파드 내의 컨테이너에 환경 변수로 구성 데이터를 주입할 수 있습니다.
  2. 파일로서의 구성: 파드 내의 컨테이너의 특정 경로에 구성 파일로 데이터를 마운트 할 수 있습니다.
  3. 커맨드라인 인수로서의 구성: 애플리케이션을 시작할 때 커맨드라인 인수로 구성 데이터를 사용할 수 있습니다.
  4. 재사용 가능: 동일한 ConfigMap을 여러 파드 및 서비스에서 재사용할 수 있습니다.
  5. 동적으로 변경 가능: ConfigMap의 내용이 변경되면, 해당 ConfigMap을 사용하는 파드에서도 이러한 변경 사항을 반영할 수 있습니다. 그러나 이를 반영하기 위해서는 파드나 애플리케이션의 재시작이 필요할 수 있습니다.
  6. 구성과 코드 분리: ConfigMap을 사용하면 애플리케이션의 이미지와 구성을 분리할 수 있으므로, 동일한 이미지를 다양한 구성으로 배포할 수 있습니다.

ConfigMap을 생성하는 방법은 여러 가지가 있습니다.
예를 들면, 파일, 디렉토리, 리터럴 값에서 ConfigMap을 생성할 수 있습니다.

예제: 리터럴 값을 사용하여 ConfigMap 생성:

kubectl create configmap my-config --from-literal=key1=value1 --from-literal=key2=value2

ConfigMap은 매우 유용한 리소스이지만, 민감한 정보, 예를 들면 비밀번호나 API 키와 같은 것들을 저장하기 위한 것이 아닙니다.
그러한 용도로는 Secret이 더 적합합니다.

Kubernetes Secret

Concept

Secret은 쿠버네티스(Kubernetes)에서 민감한 정보, 예를 들면 비밀번호, OAuth 토큰, SSH 키 등을 안전하게 저장하고 관리하기 위한 리소스입니다. Secret을 사용하면 이러한 정보를 파드의 정의나 이미지 자체에 직접 저장하지 않고도 컨테이너화된 애플리케이션에 주입할 수 있습니다.

Features

Secret의 주요 특징 및 사용 사례는 다음과 같습니다:

  1. 데이터 암호화: Secret에 저장된 데이터는 etcd에서 암호화됩니다.
    etcd는 쿠버네티스 클러스터의 기본 데이터 저장소입니다.
  2. 볼륨으로서의 주입1: Secret 데이터는 파드 내의 컨테이너에 파일로 마운트되어 액세스할 수 있습니다.
  3. 환경 변수로서의 주입: Secret 데이터를 컨테이너의 환경 변수로 설정할 수 있습니다.
  4. 이미지 풀 시크릿: Docker (또는 다른) 컨테이너 레지스트리의 인증 정보를 저장하는 데 사용될 수 있으며, 이를 통해 프라이빗 레지스트리에서 이미지를 풀할 수 있습니다.
  5. 재사용 가능: 동일한 Secret을 여러 파드에서 재사용할 수 있습니다.
  6. 크기 제한: Secret의 최대 크기는 1MiB로 제한됩니다.

Secret을 생성하는 방법은 여러 가지가 있습니다. 파일, 디렉토리, 리터럴 값에서 Secret을 생성할 수 있습니다.

예제: 파일을 사용하여 Secret 생성:

echo -n 'my-secret-value' > secret.txt
kubectl create secret generic my-secret --from-file=secret.txt

물론, Secret은 etcd에서 암호화되긴 하지만, 기본적으로 메모리 (RAM)에 디코드된 형태2로 파드에서 실행됩니다.
따라서 클러스터 내에서 Secret에 대한 접근을 제어하는 것은 매우 중요합니다.
RBAC (Role-Based Access Control)와 같은 메커니즘을 사용하여 적절한 권한 제어를 수행해야 합니다.

또한, 실제 암호화 요구 사항이 있는 경우, KMS (Key Management Service)와 같은 도구와 함께 사용하여 at-rest 암호화를 강화할 수 있습니다.

Why we need to use Kubernetes secret?

파드의 정의나 이미지 자체에 민감한 정보를 직접 저장하는 것은 여러 가지 이유로 바람직하지 않습니다.

  1. 보안 위협: 이미지나 파드의 정의에 민감한 정보를 직접 포함시키면, 그 정보가 노출될 위험이 크게 증가합니다.
    이미지가 누출되거나 무단으로 액세스되면 그 안에 포함된 모든 정보도 노출됩니다.
  2. 이미지 버전 관리: 민감한 정보가 변경될 때마다 새로운 이미지를 빌드하고 배포해야 합니다.
    이는 관리 및 유지 보수의 복잡성을 증가시킵니다.
  3. 환경 간의 이동성: 개발, 스테이징, 프로덕션 등 다양한 환경에서 동일한 이미지를 사용하려면 구성 정보를 외부에서 주입할 수 있어야 합니다.
    이미지 내부에 구성을 포함시키면 이러한 이동성이 제한됩니다.
  4. 재사용성: 다른 프로젝트나 팀에서 동일한 이미지를 사용하려면 민감한 정보를 제거하거나 수정해야 할 수 있습니다.
    이미지에서 구성을 분리하면 재사용성이 크게 향상됩니다.
  5. 감사 및 로깅: 민감한 정보의 변경 사항을 추적하거나, 누가, 언제, 어떻게 액세스하는지 등의 정보를 로깅하거나 감사하기 어렵습니다.
  6. RBAC 및 액세스 제어: 쿠버네티스의 Role-Based Access Control (RBAC)와 같은 기능을 사용하여 Secret에 대한 액세스를 세밀하게 제어할 수 있습니다. 이미지나 파드의 정의에 직접 포함된 정보에 대해서는 이러한 제어를 수행하기 어렵습니다.

따라서, 보안 및 운영의 편의성을 위해 쿠버네티스에서는 민감한 정보를 Secret과 같은 별도의 리소스에 저장하고 이를 필요한 위치에 동적으로 주입하는 방식을 권장합니다.

Note


  1. 볼륨으로서 주입한다는 것은 구체적으로 무슨 의미인가?
    “볼륨으로서 주입한다”는 말은, Secret에 저장된 데이터를 파드 내의 컨테이너에 파일 시스템으로 마운트하여 사용한다는 뜻입니다.
    이 방식을 사용하면, 컨테이너 내에서는 해당 데이터를 일반 파일처럼 읽을 수 있게 됩니다.

    예를 들어보겠습니다:
    Secret을 생성하면서 username이라는 키에 myuser, password라는 키에 mypass라는 값을 저장했다고 가정합시다.
    이 Secret을 파드에 볼륨으로 마운트하도록 설정합니다.

    파드가 실행되면, 컨테이너 내의 지정된 경로 (예: /etc/secret-volume)에 username 및 password라는 이름의 파일이 생성됩니다.
    /etc/secret-volume/username 파일을 열면 myuser 라는 내용이 저장되어 있게 됩니다.
    /etc/secret-volume/password 파일을 열면 mypass 라는 내용이 저장되어 있게 됩니다.

    따라서, 애플리케이션은 해당 파일들을 읽어서 필요한 민감한 정보를 사용할 수 있습니다.

    이런 방식의 장점은 다음과 같습니다:
    1. 애플리케이션 코드는 파일을 읽는 일반적인 방법으로 민감한 정보에 액세스할 수 있습니다.
    2. 환경 변수를 통해 노출되는 정보보다 파일로 저장된 정보가 좀 더 안전하게 관리될 수 있습니다.
    3. 쿠버네티스의 다른 리소스와 유사하게 볼륨을 사용하여 데이터를 주입받는 것이므로, 일관된 방식으로 민감한 정보를 관리할 수 있습니다.
    4. Secret에 저장된 데이터를 안전하게 컨테이너에 주입하고 사용할 수 있습니다.
    ↩︎
  2. 메모리 (RAM)에 디코드된 형태가 무슨 의미인가?
    기본적으로, 쿠버네티스의 Secret은 민감한 정보를 안전하게 저장하기 위한 메커니즘입니다.
    그러나, 파드가 이러한 Secret 정보를 사용할 때, 해당 정보는 파드가 실행되는 노드의 메모리(RAM)에 일시적으로 디코드된 상태로 로드됩니다.

    간단한 예로 설명하자면:
    Secret에는 “P@ssr\0d123″라는 암호화된 비밀번호가 저장되어 있다고 가정해봅시다.
    어떤 애플리케이션이 이 비밀번호를 필요로 할 때, 그 애플리케이션을 위한 파드가 실행됩니다.
    이 파드가 시작될 때, “P@ssr\0d123″는 메모리(RAM)에 평문(디코드된 형태)로 로드됩니다. 

    이렇게 해야 애플리케이션이 이 비밀번호를 읽고 사용할 수 있게 됩니다.

    즉, “기본적으로 메모리 (RAM)에 디코드된 형태로 파드에서 실행”이라는 말은, Secret에 저장된 정보가 실제로 사용될 때 그 정보는 메모리에 암호화되지 않은 상태로 임시적으로 저장된다는 것을 의미합니다. 이것은 필요한 작업이지만, 이러한 정보에 대한 접근을 제한하기 위한 적절한 보안 조치가 필요하다는 것도 의미합니다.
    ↩︎