파드를 위한 서비스어카운트 구성하기

파드를 위한 서비스어카운트 구성하기 (Configure Service Accounts for Pods)

쿠버네티스는 클러스터 내부에서 실행되거나 클러스터의 컨트롤 플레인과 관계가 있는 클라이언트가 API 서버에 인증하는 두 가지 뚜렷한 방법을 제공해요.

서비스어카운트(service account)는 Pod에서 실행되는 프로세스에 정체성(identity)을 제공하며, ServiceAccount 객체에 매핑돼요. API 서버에 인증할 때 여러분은 특정 사용자로 자신을 식별해요. 쿠버네티스는 사용자(user)라는 개념을 인식하지만, 쿠버네티스 자체에는 User API가 없어요.

이 작업 가이드는 쿠버네티스 API에 실제로 존재하는 ServiceAccount에 관한 것이에요. 이 가이드는 파드를 위해 ServiceAccount를 구성하는 몇 가지 방법을 보여드려요.

출처: 문서

본문

시작하기 전에 (Before you begin)

쿠버네티스 클러스터가 필요하고, kubectl 명령줄 도구가 클러스터와 통신하도록 구성돼 있어야 해요. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 두 개 이상 있는 클러스터에서 실행하는 것을 권장해요. 아직 클러스터가 없다면 minikube로 만들거나 다음 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있어요.

  • iximiuz Labs
  • Killercoda
  • KodeKloud

기본 서비스어카운트로 API 서버에 접근하기 (Use the default service account to access the API server)

파드가 API 서버에 접속할 때 특정 ServiceAccount(예: default)로 인증해요. 각 네임스페이스에는 항상 서비스어카운트가 최소 하나 이상 있어요.

모든 쿠버네티스 네임스페이스에는 최소 하나의 ServiceAccount가 있어요. 바로 default라는 이름의 기본 ServiceAccount예요. 파드를 생성할 때 ServiceAccount를 지정하지 않으면, 쿠버네티스는 그 네임스페이스의 default라는 ServiceAccount를 자동으로 할당해요.

생성한 파드의 세부 정보를 가져올 수 있어요. 예를 들어:

kubectl get pods/<podname> -o yaml

출력에서 spec.serviceAccountName 필드를 볼 수 있어요. 파드를 생성할 때 이 값을 지정하지 않으면 쿠버네티스가 자동으로 설정해요.

파드 내부에서 실행되는 애플리케이션은 자동으로 마운트된 서비스어카운트 자격 증명을 사용해 쿠버네티스 API에 접근할 수 있어요. 자세한 내용은 클러스터 접근(accessing the Cluster)을 참고하세요.

파드가 ServiceAccount로 인증할 때, 그 접근 수준은 사용 중인 인가 플러그인과 정책에 따라 달라져요.

API 자격 증명은 파드가 삭제될 때, finalizer가 있어도 자동으로 취소돼요. 특히 API 자격 증명은 파드에 설정된 .metadata.deletionTimestamp 이후 60초가 지나면 취소돼요 (삭제 타임스탬프는 보통 삭제 요청이 수락된 시점에 파드의 종료 유예 기간을 더한 시간이에요).

API 자격 증명 자동 마운트 거부하기 (Opt out of API credential automounting)

kubelet이 ServiceAccount의 API 자격 증명을 자동으로 마운트하지 않기를 원한다면 기본 동작을 거부할 수 있어요. ServiceAccount에 automountServiceAccountToken: false를 설정하면 /var/run/secrets/kubernetes.io/serviceaccount/token에 API 자격 증명을 자동 마운트하지 않도록 거부할 수 있어요.

예를 들어:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false
...

특정 파드에 대해서도 API 자격 증명 자동 마운트를 거부할 수 있어요.

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: build-robot
  automountServiceAccountToken: false
  ...

ServiceAccount와 파드의 .spec 둘 다 automountServiceAccountToken 값을 지정하면 파드 스펙이 우선해요.

둘 이상의 ServiceAccount 사용하기 (Use more than one ServiceAccount)

모든 네임스페이스에는 최소 하나의 ServiceAccount, 즉 default라는 기본 ServiceAccount 리소스가 있어요. 현재 네임스페이스의 모든 ServiceAccount 리소스를 나열할 수 있어요.

kubectl get serviceaccounts

출력은 다음과 비슷해요.

NAME      SECRETS    AGE
default   1          1d

다음과 같이 추가 ServiceAccount 객체를 생성할 수 있어요.

kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
EOF

ServiceAccount 객체의 이름은 유효한 DNS 서브도메인 이름이어야 해요.

서비스어카운트 객체의 전체 덤프를 얻으면 다음과 같아요.

kubectl get serviceaccounts/build-robot -o yaml

출력은 다음과 비슷해요.

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2019-06-16T00:12:34Z
  name: build-robot
  namespace: default
  resourceVersion: "272500"
  uid: 721ab723-13bc-11e5-aec2-42010af0021e

인가 플러그인을 사용해 서비스어카운트에 권한을 설정할 수 있어요.

기본이 아닌 서비스어카운트를 사용하려면, 파드의 spec.serviceAccountName 필드를 사용하려는 ServiceAccount의 이름으로 설정하세요.

serviceAccountName 필드는 파드를 생성할 때나 새 파드 템플릿에서만 설정할 수 있어요. 이미 존재하는 파드의 .spec.serviceAccountName 필드는 업데이트할 수 없어요.

참고: .spec.serviceAccount 필드는 .spec.serviceAccountName의 폐기된 별칭이에요. 워크로드 리소스에서 이 필드들을 제거하려면 파드 템플릿에서 두 필드를 명시적으로 빈 값으로 설정하세요.

정리 (Cleanup):

위 예시에서 만든 build-robot ServiceAccount를 정리하려면 다음을 실행하세요.

kubectl delete serviceaccount/build-robot

ServiceAccount용 API 토큰 수동 생성하기 (Manually create an API token for a ServiceAccount)

앞서 언급한 "build-robot"이라는 기존 서비스어카운트가 있다고 가정해 보죠. kubectl로 그 ServiceAccount의 시간 제한 API 토큰을 얻을 수 있어요.

kubectl create token build-robot

이 명령의 출력은 그 ServiceAccount로 인증할 때 사용할 수 있는 토큰이에요. kubectl create token--duration 명령줄 인자를 사용해 특정 토큰 유효 기간을 요청할 수 있어요 (발급된 토큰의 실제 기간은 더 짧거나 길어질 수 있어요).

기능 상태: Kubernetes v1.33부터 Stable. 이것은 쿠버네티스의 안정적인 기능이며 v1.33부터 그랬어요. 처음에는 v1.29 릴리스에서 사용할 수 있었어요. 더 이상 이 기능이나 동작을 비활성화하거나 거부할 수 없어요(잠겨 있음). 관련 기능 게이트인 ServiceAccountTokenNodeBinding에 값을 명시적으로 설정하면 쿠버네티스는 이를 무시하지만 오류는 보고하지 않아요.

kubectl v1.31 이상을 사용하면 노드에 직접 바인딩된 서비스어카운트 토큰을 만들 수 있어요.

kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456

이 토큰은 만료되거나 관련 노드나 서비스어카운트가 삭제될 때까지 유효해요.

참고: v1.22 이전 쿠버네티스 버전은 쿠버네티스 API에 접근하기 위한 장기 자격 증명을 자동으로 생성했어요. 이 구식 메커니즘은 실행 중인 파드에 마운트할 수 있는 token Secret을 만드는 방식이었어요. 쿠버네티스 v1.37을 포함한 최신 버전에서는 API 자격 증명을 TokenRequest API로 직접 얻고, projected volume을 사용해 파드에 마운트해요. 이 방법으로 얻은 토큰은 수명이 제한적이고, 마운트된 파드가 삭제되면 자동으로 무효화돼요.

서비스어카운트 토큰 Secret을 여전히 수동으로 만들 수 있어요. 예를 들어 영원히 만료되지 않는 토큰이 필요하다면요. 하지만 API에 접근할 토큰을 얻으려면 TokenRequest 하위 리소스를 사용하는 것이 권장돼요.

ServiceAccount용 장기(live-long) API 토큰 수동 생성하기

ServiceAccount용 API 토큰을 얻고 싶다면, kubernetes.io/service-account.name 특수 어노테이션이 있는 새 Secret을 생성해요.

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: build-robot-secret
  annotations:
    kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token
EOF

다음으로 Secret을 조회하면:

kubectl get secret/build-robot-secret -o yaml

Secret이 이제 "build-robot" ServiceAccount용 API 토큰을 포함하고 있음을 볼 수 있어요.

설정한 어노테이션 덕분에 컨트롤 플레인이 그 ServiceAccount용 토큰을 자동으로 생성해 관련 Secret에 저장해요. 컨트롤 플레인은 삭제된 ServiceAccount의 토큰도 정리해요.

kubectl describe secrets/build-robot-secret

출력은 다음과 비슷해요.

Name:           build-robot-secret
Namespace:      default
Labels:         <none>
Annotations:    kubernetes.io/service-account.name: build-robot
                kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1338 bytes
namespace:      7 bytes
token:          ...

참고: token의 내용은 여기서 생략했어요. kubernetes.io/service-account-token Secret의 내용을 주변인이 화면을 볼 수 있는 곳에 표시하지 않도록 주의하세요.

연관된 Secret이 있는 ServiceAccount를 삭제하면 쿠버네티스 컨트롤 플레인이 그 Secret에서 장기 토큰을 자동으로 정리해요.

참고: ServiceAccount를 조회하면 (kubectl get serviceaccount build-robot -o yaml), ServiceAccount API 객체의 .secrets 필드에 build-robot-secret Secret이 보이지 않아요. 그 필드는 자동 생성된 Secret만 채워지기 때문이에요.

서비스어카운트에 ImagePullSecrets 추가하기 (Add ImagePullSecrets to a service account)

먼저 imagePullSecret을 생성하고, 생성됐는지 확인해요.

파드에 ImagePullSecrets 지정하기에서 설명한 대로 imagePullSecret을 생성해요.

kubectl create secret docker-registry myregistrykey --docker-server=<registry name> \
        --docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
        --docker-email=DUMMY_DOCKER_EMAIL

생성됐는지 확인해요.

kubectl get secrets myregistrykey

출력은 다음과 비슷해요.

NAME             TYPE                              DATA    AGE
myregistrykey    kubernetes.io/.dockerconfigjson   1       1d

서비스어카운트에 이미지 풀 시크릿 추가하기

다음으로 네임스페이스의 기본 서비스어카운트가 이 Secret을 imagePullSecret으로 사용하도록 수정해요.

kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'

객체를 수동으로 편집해도 같은 결과를 얻을 수 있어요.

kubectl edit serviceaccount/default

sa.yaml 파일의 출력은 다음과 비슷해요. 선택한 텍스트 편집기가 다음과 비슷한 구성과 함께 열려요.

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  resourceVersion: "243024"
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6

편집기에서 resourceVersion 키가 있는 줄을 삭제하고 imagePullSecrets: 줄을 추가한 뒤 저장하세요. uid 값은 찾은 그대로 두세요.

변경 후 편집된 ServiceAccount는 다음과 비슷해요.

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
imagePullSecrets:
  - name: myregistrykey

새 파드에 imagePullSecrets가 설정되는지 확인하기

이제 현재 네임스페이스에서 기본 ServiceAccount를 사용해 새 파드를 만들면, 새 파드의 spec.imagePullSecrets 필드가 자동으로 설정돼요.

kubectl run nginx --image=<registry name>/nginx --restart=Never
kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}'

출력은 다음과 같아요.

myregistrykey

ServiceAccount 토큰 볼륨 프로젝션 (ServiceAccount token volume projection)

기능 상태: Kubernetes v1.20부터 Stable.

참고: 토큰 요청 프로젝션을 활성화하고 사용하려면 kube-apiserver에 다음 명령줄 인자를 각각 지정해야 해요.

  • --service-account-issuer — 서비스어카운트 토큰 발급자의 식별자를 정의해요. --service-account-issuer 인자를 여러 번 지정할 수 있는데, 이는 발급자의 중단 없는 변경을 활성화하는 데 유용해요. 이 플래그를 여러 번 지정하면 첫 번째가 토큰 생성에 사용되고, 모두가 허용되는 발급자를 결정하는 데 사용돼요. --service-account-issuer를 여러 번 지정하려면 Kubernetes v1.22 이상을 실행해야 해요.
  • --service-account-key-file — ServiceAccount 토큰을 검증하는 데 사용되는 PEM 인코딩 X.509 개인/공개 키(RSA 또는 ECDSA)가 담긴 파일의 경로를 지정해요. 지정한 파일은 여러 키를 포함할 수 있고, 플래그를 다른 파일로 여러 번 지정할 수 있어요. 여러 번 지정하면 지정된 키 중 어떤 것으로 서명된 토큰도 쿠버네티스 API 서버가 유효한 것으로 간주해요.
  • --service-account-signing-key-file — 서비스어카운트 토큰 발급자의 현재 개인 키가 담긴 파일의 경로를 지정해요. 발급자는 이 개인 키로 발급된 ID 토큰에 서명해요.
  • --api-audiences (생략 가능) — ServiceAccount 토큰의 audiences를 정의해요. 서비스어카운트 토큰 인증자는 API에 사용되는 토큰이 이 audiences 중 적어도 하나에 바인딩돼 있는지 검증해요. api-audiences를 여러 번 지정하면 지정된 audiences 중 어떤 것에 대한 토큰이든 쿠버네티스 API 서버가 유효한 것으로 간주해요. --service-account-issuer 명령줄 인자를 지정하면서 --api-audiences를 설정하지 않으면, 컨트롤 플레인은 발급자 URL만 포함하는 단일 요소 audience 목록을 기본값으로 사용해요.

kubelet은 ServiceAccount 토큰을 Pod에 프로젝션할 수도 있어요. 토큰의 audience와 유효 기간 같은 원하는 속성을 지정할 수 있어요. 이러한 속성은 기본 ServiceAccount 토큰에서는 구성할 수 없어요. 토큰은 Pod나 ServiceAccount가 삭제되면 API에서도 유효하지 않게 돼요.

ServiceAccountToken이라는 프로젝션 볼륨 유형을 사용해 파드의 spec에 대해 이 동작을 구성할 수 있어요.

이 프로젝션 볼륨의 토큰은 JWT(JSON Web Token)예요. 이 토큰의 JSON 페이로드는 잘 정의된 스키마를 따르는데, 파드에 바인딩된 토큰의 페이로드 예시는 다음과 같아요.

{
  "aud": [  # 요청된 audiences 또는 명시적으로 요청되지 않았을 때 API 서버의 기본 audiences와 일치
    "https://kubernetes.default.svc"
  ],
  "exp": 1731613413,
  "iat": 1700077413,
  "iss": "https://kubernetes.default.svc",  # --service-account-issuer 플래그에 전달된 첫 번째 값과 일치
  "jti": "ea28ed49-2e11-4280-9ec5-bc3d1d84661a", 
  "kubernetes.io": {
    "namespace": "kube-system",
    "node": {
      "name": "127.0.0.1",
      "uid": "58456cb0-dd00-45ed-b797-5578fdceaced"
    },
    "pod": {
      "name": "coredns-69cbfb9798-jv9gn",
      "uid": "778a530c-b3f4-47c0-9cd5-ab018fb64f33"
    },
    "serviceaccount": {
      "name": "coredns",
      "uid": "a087d5a0-e1dd-43ec-93ac-f13d89cd13af"
    },
    "warnafter": 1700081020
  },
  "nbf": 1700077413,
  "sub": "system:serviceaccount:kube-system:coredns"
}

서비스어카운트 토큰 프로젝션으로 파드 실행하기 (Launch a Pod using service account token projection)

파드에 vault라는 audience와 2시간 유효 기간의 토큰을 제공하려면 다음과 비슷한 파드 매니페스트를 정의하면 돼요.

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: build-robot
  volumes:
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          expirationSeconds: 7200
          audience: vault

pods/pod-projected-svc-token.yaml 파일로 파드를 생성해요.

kubectl create -f https://k8s.io/examples/pods/pod-projected-svc-token.yaml

kubelet은 파드를 대신해 토큰을 요청하고 저장하며, 파드가 구성 가능한 파일 경로에서 토큰을 사용할 수 있게 하고, 토큰이 만료에 가까워지면 갱신해요. kubelet은 토큰이 총 TTL(Time-to-Live)의 80%보다 오래됐거나 24시간보다 오래된 경우 토큰의 순환을 선제적으로 요청해요.

애플리케이션은 토큰이 순환될 때 토큰을 다시 로드할 책임이 있어요. 애플리케이션이 실제 만료 시간을 추적하지 않고 주기적으로(예: 5분마다) 토큰을 로드하는 것만으로도 충분한 경우가 많아요.

서비스어카운트 발급자 발견 (Service account issuer discovery)

기능 상태: Kubernetes v1.21부터 Stable.

클러스터에서 ServiceAccount용 토큰 프로젝션을 활성화했다면 발견(discovery) 기능도 활용할 수 있어요. 쿠버네티스는 클라이언트가 ID 제공자(identity provider)로 페더레이션할 수 있는 방법을 제공해, 하나 이상의 외부 시스템이 신뢰 당사자(relying party)로 작동할 수 있게 해요.

참고: 발급자 URL은 OIDC Discovery Spec을 준수해야 해요. 실질적으로는 https 스킴을 사용해야 하고, {service-account-issuer}/.well-known/openid-configuration에서 OpenID 제공자 구성을 제공해야 해요. URL이 준수하지 않으면 ServiceAccount 발급자 발견 엔드포인트가 등록되거나 접근 가능하지 않아요.

활성화되면 쿠버네티스 API 서버가 HTTP를 통해 OpenID Provider Configuration 문서를 게시해요. 구성 문서는 /.well-known/openid-configuration에 게시돼요. OpenID Provider Configuration은 발견 문서(discovery document)라고도 불려요. 쿠버네티스 API 서버는 관련 JWKS(JSON Web Key Set)도 HTTP를 통해 /openid/v1/jwks에 게시해요.

참고: /.well-known/openid-configuration/openid/v1/jwks에서 제공되는 응답은 OIDC 호환이 되도록 설계됐지만, 엄격한 OIDC 준수는 아니에요. 이 문서들은 쿠버네티스 서비스어카운트 토큰 검증을 수행하는 데 필요한 매개변수만 포함해요.

RBAC를 사용하는 클러스터에는 system:service-account-issuer-discovery라는 기본 ClusterRole이 있어요. 기본 ClusterRoleBinding이 이 역할을 system:serviceaccounts 그룹에 할당하는데, 모든 ServiceAccount가 암묵적으로 이 그룹에 속해요. 이를 통해 클러스터에서 실행되는 파드가 마운트된 서비스어카운트 토큰을 통해 서비스어카운트 발견 문서에 접근할 수 있어요. 관리자는 보안 요구 사항과 페더레이션하려는 외부 시스템에 따라 system:authenticatedsystem:unauthenticated에 이 역할을 바인딩하도록 선택할 수도 있어요.

JWKS 응답에는 신뢰 당사자가 쿠버네티스 서비스어카운트 토큰을 검증하는 데 사용할 수 있는 공개 키가 포함돼 있어요. 신뢰 당사자는 먼저 OpenID Provider Configuration을 조회한 뒤, 응답의 jwks_uri 필드를 사용해 JWKS를 찾아요.

많은 경우 쿠버네티스 API 서버는 공개 인터넷에서 사용할 수 없지만, API 서버의 캐시된 응답을 제공하는 공용 엔드포인트는 사용자나 서비스 제공업체가 제공할 수 있어요. 이 경우 --service-account-jwks-uri 플래그를 API 서버에 전달해 OpenID Provider Configuration의 jwks_uri가 API 서버 주소 대신 공용 엔드포인트를 가리키도록 재정의할 수 있어요. 발급자 URL과 마찬가지로 JWKS URI도 https 스킴을 사용해야 해요.

다음 단계 (What's next)

함께 보기:

  • 서비스어카운트 클러스터 관리 가이드 읽기
  • 쿠버네티스의 인가(Authorization)에 대해 읽기
  • Secrets에 대해 읽거나, Secrets를 사용해 자격 증명을 안전하게 배포하는 방법 배우기 — 다만 ServiceAccount로 인증하는 데 Secrets를 사용하는 것은 폐기됐다는 점에 유의하세요. 권장되는 대안은 ServiceAccount 토큰 볼륨 프로젝션이에요.
  • 프로젝션 볼륨(projected volumes)에 대해 읽기
  • OIDC 발견 배경은 ServiceAccount 서명 키 검색 쿠버네티스 개선 제안 읽기
  • OIDC Discovery Spec 읽기

더 알아보기 (Learn more)