프로젝티드 볼륨
프로젝티드 볼륨 (Projected Volumes)
이 문서는 Kubernetes의 projected volume을 설명해요. volumes에 대한 친숙함이 권장돼요.
출처: 문서
본문
소개 (Introduction)
projected volume은 여러 기존 볼륨 소스를 같은 디렉터리에 매핑해요.
현재 다음 유형의 볼륨 소스를 프로젝트할 수 있어요:
모든 소스는 파드와 같은 네임스페이스에 있어야 해요. 자세한 내용은 all-in-one volume 설계 문서를 참고해요.
secret, downwardAPI, configMap을 포함한 예시 구성
apiVersion: v1
kind: Pod
metadata:
name: volume-test
spec:
containers:
- name: container-test
image: busybox:1.28
command: ["sleep", "3600"]
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: mysecret
items:
- key: username
path: my-group/my-username
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "cpu_limit"
resourceFieldRef:
containerName: container-test
resource: limits.cpu
- configMap:
name: myconfigmap
items:
- key: config
path: my-group/my-config
예시 구성: 비기본 권한 모드가 설정된 secret들
apiVersion: v1
kind: Pod
metadata:
name: volume-test
spec:
containers:
- name: container-test
image: busybox:1.28
command: ["sleep", "3600"]
volumeMounts:
- name: all-in-one
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: mysecret
items:
- key: username
path: my-group/my-username
- secret:
name: mysecret2
items:
- key: password
path: my-group/my-password
mode: 0777
각 projected volume 소스는 spec의 sources 아래에 나열돼요. 매개변수는 거의 동일한데 두 가지 예외가 있어요:
- secret의 경우
secretName필드가 ConfigMap 명명과 일관되도록name으로 변경되었어요. defaultMode는 각 볼륨 소스가 아니라 projected 레벨에서만 지정할 수 있어요. 하지만 위에서 설명한 대로 각 개별 프로젝션에 대해mode를 명시적으로 설정할 수 있어요.
serviceAccountToken projected volumes (#serviceaccounttoken)
현재 서비스 계정의 토큰을 지정된 경로의 파드에 주입할 수 있어요. 예:
apiVersion: v1
kind: Pod
metadata:
name: sa-token-test
spec:
containers:
- name: container-test
image: busybox:1.28
command: ["sleep", "3600"]
volumeMounts:
- name: token-vol
mountPath: "/service-account"
readOnly: true
serviceAccountName: default
volumes:
- name: token-vol
projected:
sources:
- serviceAccountToken:
audience: api
expirationSeconds: 3600
path: token
예시 파드는 주입된 서비스 계정 토큰을 포함하는 projected volume을 가지고 있어요. 이 파드의 컨테이너는 그 토큰을 사용해 파드의 ServiceAccount의 신원으로 인증하면서 Kubernetes API 서버에 접근할 수 있어요. audience 필드는 토큰의 의도된 수신자를 포함해요. 토큰의 수신자는 토큰의 audience에 지정된 식별자로 스스로를 식별해야 하고, 그렇지 않으면 토큰을 거부해야 해요. 이 필드는 선택 사항이며 기본값은 API 서버의 식별자예요.
expirationSeconds는 서비스 계정 토큰의 유효 기간이에요. 기본값은 1시간이고 최소 10분(600초)이어야 해요. 관리자는 API 서버에 대해 --service-account-max-token-expiration 옵션을 지정해 최대값을 제한할 수도 있어요. path 필드는 projected volume의 마운트 지점에 대한 상대 경로를 지정해요.
clusterTrustBundle projected volumes (#clustertrustbundle)
clusterTrustBundle projected volume 소스는 하나 이상의 ClusterTrustBundle 객체의 내용을 컨테이너 파일시스템의 자동 업데이트 파일로 주입해요.
ClusterTrustBundle은 이름별 또는 서명자 이름별로 선택할 수 있어요.
이름으로 선택하려면 name 필드를 사용해 단일 ClusterTrustBundle 객체를 지정해요.
서명자 이름으로 선택하려면 signerName 필드(그리고 선택적으로 labelSelector 필드)를 사용해 주어진 서명자 이름을 사용하는 ClusterTrustBundle 객체 집합을 지정해요. labelSelector가 없으면 해당 서명자에 대한 모든 ClusterTrustBundle이 선택돼요.
kubelet은 선택된 ClusterTrustBundle 객체의 인증서를 중복 제거하고, PEM 표현을 정규화하며(주석과 헤더 버림), 인증서를 재정렬하고, path라는 이름의 파일에 써요. 선택된 ClusterTrustBundle의 집합이나 내용이 변경됨에 따라 kubelet은 파일을 최신 상태로 유지해요.
기본적으로 이름이 지정된 ClusterTrustBundle을 찾을 수 없거나 signerName/labelSelector가 어떤 ClusterTrustBundle에도 일치하지 않으면 kubelet은 파드 시작을 방지해요. 이 동작이 원하는 것이 아니라면 optional 필드를 true로 설정해요. 그러면 파드가 path에 빈 파일로 시작돼요.
apiVersion: v1
kind: Pod
metadata:
name: sa-ctb-name-test
spec:
containers:
- name: container-test
image: busybox
command: ["sleep", "3600"]
volumeMounts:
- name: token-vol
mountPath: "/root-certificates"
readOnly: true
serviceAccountName: default
volumes:
- name: token-vol
projected:
sources:
- clusterTrustBundle:
name: example
path: example-roots.pem
- clusterTrustBundle:
signerName: "example.com/mysigner"
labelSelector:
matchLabels:
version: live
path: mysigner-roots.pem
optional: true
podCertificate projected volumes (#podcertificate)
podCertificate projected volume 소스는 파드가 클라이언트 또는 서버 자격 증명으로 사용할 개인 키와 X.509 인증서 체인을 안전하게 프로비저닝해요. 그런 다음 kubelet이 개인 키와 인증서 체인이 만료에 가까워지면 새로 고침을 처리해요. 애플리케이션은 inotify나 폴링 같은 메커니즘으로 파일이 변경될 때 신속하게 다시 로드하기만 하면 돼요.
각 podCertificate 프로젝션은 다음 구성 필드를 지원해요:
signerName: 인증서를 발급할 서명자(signer). 서명자는 자체 접근 요구 사항이 있을 수 있고, 파드에 인증서 발급을 거부할 수 있음을 주의해요.keyType: 생성되어야 할 개인 키의 유형. 유효한 값은ED25519,ECDSAP256,ECDSAP384,ECDSAP521,RSA3072,RSA4096이에요.maxExpirationSeconds: 파드에 발급된 인증서에 대해 수락할 최대 수명. 설정하지 않으면86400(24시간)으로 기본 설정돼요. 최소3600(1시간), 최대7862400(91일)이어야 해요. Kubernetes 내장 서명자는 최대 수명86400(1일)로 제한돼요. 서명자는 지정한 것보다 더 짧은 수명의 인증서를 발급할 수 있어요.credentialBundlePath: 자격 증명 번들이 기록되어야 하는 프로젝션 내의 상대 경로. 자격 증명 번들은 PEM 형식 파일이며, 첫 번째 블록은 PKCS#8 직렬화된 개인 키를 포함하는 "PRIVATE KEY" 블록이고 나머지 블록은 인증서 체인(리프 인증서와 중간 인증서)을 구성하는 "CERTIFICATE" 블록이에요.keyPath와certificateChainPath: kubelet이 개인 키 또는 인증서 체인만 기록해야 하는 별도의 경로.userAnnotations: 서명자 구현에 추가 정보를 전달할 수 있게 하는 맵. kubelet이 만드는 PodCertificateRequest 객체의spec.unverifiedUserAnnotations필드에 그대로 복사돼요. 항목은 객체 메타데이터 어노테이션과 동일한 검증을 받으며, 추가로 모든 키가 도메인 접두사여야 해요. 값에는 전체 필드의 크기 제한 외에는 제한이 없어요. 이 기본 검증 외에는 API 서버가 추가 검증을 수행하지 않아요. 서명자 구현은 이 데이터를 소비할 때 매우 조심해야 해요. 서명자는 먼저 적절한 검증 단계를 수행하지 않고 이 데이터를 본질적으로 신뢰해서는 안 돼요. 서명자는 자신이 지원하는 키와 값을 문서화해야 해요. 서명자는 인식하지 못하는 키를 포함하는 요청을 거부해야 해요.- 참고: 이 필드에 데이터를 설정하는 것은 서명자가 이러한 필드를 명시적으로 지원하고 어떤 값이 의미하는지 명확히 문서화할 때만 권장돼요.
# Sample Pod spec that uses a podCertificate projection to request an ED25519
# private key, a certificate from the `coolcert.example.com/foo` signer, and
# write the results to `/var/run/my-x509-credentials/credentialbundle.pem`.
apiVersion: v1
kind: Pod
metadata:
namespace: default
name: podcertificate-pod
spec:
serviceAccountName: default
containers:
- image: debian
name: main
command: ["sleep", "infinity"]
volumeMounts:
- name: my-x509-credentials
mountPath: /var/run/my-x509-credentials
volumes:
- name: my-x509-credentials
projected:
defaultMode: 0644
sources:
- podCertificate:
keyType: ED25519
signerName: coolcert.example.com/foo
credentialBundlePath: credentialbundle.pem
userAnnotations:
example.com/annotation1: "value1"
example.com/annotation2: "value2"
SecurityContext 상호작용 (SecurityContext interactions)
projected service account volume 강화를 위한 파일 권한 처리를 도입한 제안서는 projected 파일이 올바른 소유자 권한을 설정하도록 도입했어요.
Linux
projected volume을 갖고 파드 SecurityContext에 RunAsUser가 설정된 Linux 파드에서 projected 파일은 컨테이너 사용자 소유권을 포함한 올바른 소유권을 가져요.
파드의 모든 컨테이너가 PodSecurityContext 또는 컨테이너 SecurityContext에 같은 runAsUser를 설정하면, kubelet이 serviceAccountToken 볼륨의 내용이 그 사용자가 소유하도록 보장하고 토큰 파일의 권한 모드를 0600으로 설정해요.
- 참고: 파드가 생성된 후 파드에 추가된 임시 컨테이너(ephemeral containers)는 파드가 생성될 때 설정된 볼륨 권한을 변경하지 않아요.
- 파드의 serviceAccountToken 볼륨 권한이 파드의 다른 모든 컨테이너가 같은
runAsUser를 가지기 때문에0600으로 설정된 경우, 임시 컨테이너는 토큰을 읽을 수 있으려면 같은runAsUser를 사용해야 해요.
Windows
projected volume을 갖고 파드 SecurityContext에 RunAsUsername이 설정된 Windows 파드에서 Windows에서 사용자 계정이 관리되는 방식 때문에 소유권이 강제되지 않아요. Windows는 로컬 사용자 및 그룹 계정을 Security Account Manager(SAM)라는 데이터베이스 파일에 저장하고 관리해요. 각 컨테이너는 자체 SAM 데이터베이스 인스턴스를 유지하며, 컨테이너가 실행되는 동안 호스트가 그것을 볼 수 없어요. Windows 컨테이너는 OS의 사용자 모드 부분을 호스트와 격리하여 실행하도록 설계되었으므로 가상 SAM 데이터베이스를 유지해요. 그 결과 호스트에서 실행되는 kubelet은 가상화된 컨테이너 계정에 대해 호스트 파일 소유권을 동적으로 구성할 능력이 없어요. 호스트 머신의 파일을 컨테이너와 공유해야 한다면 C:\ 외부의 자체 볼륨 마운트에 배치하는 것이 권장돼요.
기본적으로 projected 파일은 예시 projected volume 파일에 대해 표시된 대로 다음 소유권을 가질 거예요:
PS C:\> Get-Acl C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt | Format-List
Path : Microsoft.PowerShell.Core\FileSystem::C:\var\run\secrets\kubernetes.io\serviceaccount\..2021_08_31_22_22_18.318230061\ca.crt
Owner : BUILTIN\Administrators
Group : NT AUTHORITY\SYSTEM
Access : NT AUTHORITY\SYSTEM Allow FullControl
BUILTIN\Administrators Allow FullControl
BUILTIN\Users Allow ReadAndExecute, Synchronize
Audit :
Sddl : O:BAG:SYD:AI(A;ID;FA;;;SY)(A;ID;FA;;;BA)(A;ID;0x1200a9;;;BU)
이것은 ContainerAdministrator와 같은 모든 관리자 사용자가 읽기, 쓰기, 실행 접근을 가지는 반면, 비관리자 사용자는 읽기와 실행 접근을 가짐을 의미해요.
- 참고: 일반적으로 컨테이너에 호스트 접근을 부여하는 것은 잠재적인 보안 악용의 문을 열 수 있으므로 권장되지 않아요.
- SecurityContext에
RunAsUser를 설정한 Windows 파드를 만들면 파드가 영원히 ContainerCreating에 머물게 돼요. 그러므로 Windows 파드에는 Linux 전용 옵션인RunAsUser를 사용하지 않는 것이 좋아요.