Kubernetes 런타임 구성하기

Kubernetes 런타임 구성하기

Kubernetes 런타임은 함수 워커(function worker)가 Kubernetes 매니페스트(manifest)를 생성하고 적용해서 동작해요. 함수 워커가 생성하는 매니페스트에는 StatefulSet, Service, 그리고 인증 자격 증명용 Secret이 포함돼요.

Helm 차트로 Pulsar 클러스터를 Kubernetes에 구성했다면 함수 워커도 Kubernetes에 이미 설정된 상태라 비교적 쉽게 런타임을 쓸 수 있어요. Kubernetes 시크릿 통합, 토큰 인증, 런타임 커스터마이징까지 차근차근 살펴볼게요.

출처: 문서

본문

Kubernetes 런타임은 함수 워커가 Kubernetes 매니페스트를 생성하고 적용하면서 동작해요. 함수 워커가 생성하는 매니페스트는 다음과 같아요.

  • StatefulSet — 기본적으로 이 매니페스트는 복제 수(replica)를 가진 단일 pod로 구성돼요. 복제 수는 함수의 병렬성(parallelism)에 따라 결정돼요. pod는 부팅 시 (함수 워커 REST API를 통해) 함수 페이로드를 다운로드해요. 함수 런타임이 구성되어 있으면 pod의 컨테이너 이미지를 설정할 수 있어요.
  • Service — pod와 통신하는 데 사용해요.
  • Secret — 인증 자격 증명용 (해당하는 경우). Kubernetes 런타임은 시크릿을 지원해요. Kubernetes 시크릿을 만들고 pod 안에서 환경 변수로 노출할 수 있어요.

팁: Pulsar 객체 이름을 Kubernetes 리소스 라벨로 변환하는 규칙은 아래 "Kubernetes에서 Pulsar를 실행할 때 Pulsar 리소스 이름 정의하기"를 참고해요.

기본 설정 구성 (Configure basic settings)

Kubernetes 런타임을 빠르게 구성하려면 conf/functions_worker.yml 파일에서 KubernetesRuntimeFactoryConfig의 기본 설정을 사용하면 돼요.

[Helm 차트로 Pulsar 클러스터를 Kubernetes에 설정해서 함수 워커도 Kubernetes에 설정되어 있다면], 함수 워커가 실행 중인 pod와 연결된 serviceAccount를 사용할 수 있어요. 그렇지 않으면 functionRuntimeFactoryConfigsk8Uri로 설정해서 함수 워커가 Kubernetes 클러스터와 통신하도록 구성할 수 있어요.

Kubernetes 시크릿 통합 (Integrate Kubernetes secrets)

Kubernetes의 Secret은 비밀번호, 토큰, 키 같은 기밀 데이터를 담는 객체예요. 함수가 배포된 Kubernetes 네임스페이스에 시크릿을 만들면, 함수가 이 시크릿을 안전하게 참조하고 배포할 수 있어요. 이 기능을 활성화하려면 conf/functions-worker.yml 파일에서 secretsProviderConfiguratorClassNameorg.apache.pulsar.functions.secretsproviderconfigurator.KubernetesSecretsProviderConfigurator로 설정해요.

예를 들어 pulsar-func Kubernetes 네임스페이스에 함수를 배포하고, password라는 필드를 가진 database-creds라는 시크릿을 만들어 pod에 DATABASE_PASSWORD라는 환경 변수로 마운트하고 싶다고 가정해 볼게요. 다음 구성이 함수가 시크릿을 참조하고 값을 pod에 환경 변수로 마운트할 수 있게 해줘요.

tenant: "mytenant"
namespace: "mynamespace"
name: "myfunction"
inputs: [ "persistent://mytenant/mynamespace/myfuncinput" ]
className: "com.company.pulsar.myfunction"

secrets:
  # the secret will be mounted from the `password` field in the `database-creds` secret as an env var called `DATABASE_PASSWORD`
  DATABASE_PASSWORD:
    path: "database-creds"
    key: "password"

토큰 인증 활성화 (Enable token authentication)

토큰 인증, TLS 암호화, 또는 커스텀 인증으로 Pulsar 클러스터와의 통신을 보호할 때, Pulsar는 인증 기관(CA)을 클라이언트에 전달해서 클라이언트가 서명된 인증서로 클러스터를 인증하게 해요.

Pulsar 클러스터의 토큰 인증을 활성화하려면, 함수를 실행하는 pod가 브로커를 인증하는 메커니즘을 org.apache.pulsar.functions.auth.KubernetesFunctionAuthProvider 인터페이스를 구현해 지정해야 해요.

  • 토큰 인증의 경우, Pulsar는 위 인터페이스의 구현을 포함해 CA를 배포해요. 함수 워커는 함수를 배포(또는 갱신)할 때 사용된 토큰을 캡처해서 시크릿으로 저장하고 pod에 마운트해요.

conf/function-worker.yml 파일의 구성은 다음과 같아요. functionAuthProviderClassName은 이 구현의 경로를 지정하는 데 사용돼요.

functionAuthProviderClassName: org.apache.pulsar.functions.auth.KubernetesSecretsTokenAuthProvider
  • TLS 또는 커스텀 인증의 경우 org.apache.pulsar.functions.auth.KubernetesFunctionAuthProvider 인터페이스를 구현하거나 대체 메커니즘을 사용할 수 있어요.

알아두기: 함수를 배포할 때 사용한 토큰에 만료일이 있으면, 만료 후 함수를 다시 배포해야 할 수 있어요.

함수 pod 인증을 위한 Kubernetes 서비스 계정 토큰 프로젝션 활성화 (Enable Kubernetes service account token projection for function pod authentication)

KubernetesServiceAccountTokenAuthProvider는 서비스 계정 토큰 볼륨 프로젝션(volume projection)을 사용해 함수의 pod에 토큰을 마운트해요. 함수 워커와 브로커는 OpenID Connect로 이 토큰을 검증할 수 있어요. 이 통합의 주요 장점은 토큰 수명이 짧고 Kubernetes가 관리하며, 함수를 만들 때 사용한 권한을 상속하지 않는다는 점이에요.

알아두기: 이 기능을 사용하려면 브로커와 함수 워커가 AuthenticationProviderOpenID를 사용하도록 구성되어야 해요. 이 공급자 활성화 문서는 여기에서 찾을 수 있어요.

함수 워커가 이 기능을 사용하는 예시 구성은 다음과 같아요.

functionAuthProviderClassName: "org.apache.pulsar.functions.auth.KubernetesServiceAccountTokenAuthProvider"
kubernetesContainerFactory:
  kubernetesFunctionAuthProviderConfig:
    # Required
    serviceAccountTokenExpirationSeconds: "600"
    serviceAccountTokenAudience: "the-required-audience"
    # Optional
    brokerClientTrustCertsSecretName: "my-secret-pulsar-broker-client-trust-certs"

함수 pod는 대상 네임스페이스의 기본 Kubernetes 서비스 계정으로 배포돼요. 서비스 계정 이름이 pod 파일시스템에 프로젝션된 JWT의 sub 클레임에 매핑되므로, 같은 서비스 계정을 가진 모든 pod는 Pulsar 안에서 같은 권한을 가지게 돼요. 이 통합을 개선하는 작업이 진행 중이에요.

EKS에서 실행되는 이 기능으로 생성된 샘플 JWT(일부 정보는 가림)는 다음과 같아요.

{
  "aud": [
    "your-audience"
  ],
  "exp": 1710969822,
  "iat": 1679433822,
  "iss": "https://oidc.eks.us-east-2.amazonaws.com/id/some-id",
  "kubernetes.io": {
    "namespace": "pulsar-function",
    "pod": {
      "name": "function-pod-0",
      "uid": "fbac8f9e-a47d-4ad7-a8f0-cc9a65d1331c"
    },
    "serviceaccount": {
      "name": "default",
      "uid": "5964f9d3-3dce-467c-8dbe-d0f463063d7a"
    },
    "warnafter": 1679437429
  },
  "nbf": 1679433822,
  "sub": "system:serviceaccount:pulsar-function:default"
}

이 함수 pod에 권한을 부여하려면 role 클레임(기본적으로 sub 클레임인 system:serviceaccount:pulsar-function:default)에 권한을 부여해야 해요.

Kubernetes 런타임 커스터마이징 (Customize Kubernetes runtime)

Kubernetes 런타임을 커스터마이즈하면 런타임이 만드는 Kubernetes 리소스를 조정할 수 있어요. 매니페스트 생성 방법, pod에 인증 데이터를 전달하는 방법, 시크릿을 통합하는 방법 등을 조정할 수 있죠.

Kubernetes 런타임을 커스터마이즈하려면 conf/functions-worker.yml 파일에서 runtimeCustomizerClassName을 설정하고 정규화된 클래스 이름(FQCN)을 사용해요.

함수 API는 customRuntimeOptions라는 플래그를 제공하며, 이 값은 org.apache.pulsar.functions.runtime.kubernetes.KubernetesManifestCustomizer 인터페이스로 전달돼요. KubernetesManifestCustomizer를 초기화하려면 conf/functions-worker.yml 파일에서 runtimeCustomizerConfig를 설정해요.

알아두기: runtimeCustomizerConfig는 모든 함수에 대해 동일해요. runtimeCustomizerConfigcustomRuntimeOptions를 모두 제공한다면, KubernetesManifestCustomizer 인터페이스 구현에서 이 두 설정을 어떻게 관리할지 직접 결정해야 해요.

Pulsar에는 runtimeCustomizerConfig로 초기화되는 내장 구현이 포함되어 있어요. 특정 프로퍼티를 보강(augment)하기 위해 JSON 문서를 customRuntimeOptions로 전달할 수 있게 해주죠. 이 내장 구현을 사용하려면 runtimeCustomizerClassNameorg.apache.pulsar.functions.runtime.kubernetes.BasicKubernetesManifestCustomizer로 설정해요.

runtimeCustomizerConfigcustomRuntimeOptions가 모두 제공되고 충돌하면, BasicKubernetesManifestCustomizercustomRuntimeOptions를 사용해 runtimeCustomizerConfig를 덮어써요.

아래는 customRuntimeOptions를 구성하는 예시예요.

{
  "jobName": "jobname", // the k8s pod name to run this function instance
  "jobNamespace": "namespace", // the k8s namespace to run this function in
  "extractLabels": {           // extra labels to attach to the statefulSet, service, and pods
    "extraLabel": "value"
  },
  "extraAnnotations": {        // extra annotations to attach to the statefulSet, service, and pods
    "extraAnnotation": "value"
  },
  "nodeSelectorLabels": {      // node selector labels to add on to the pod spec
    "customLabel": "value"
  },
  "tolerations": [             // tolerations to add to the pod spec
    {
      "key": "custom-key",
      "value": "value",
      "effect": "NoSchedule"
    }
  ],
  "resourceRequirements": {  // values for cpu and memory should be defined as described here: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container
    "requests": {
      "cpu": 1,
      "memory": "4G"
    },
    "limits": {
      "cpu": 2,
      "memory": "8G"
    }
  }
}

Kubernetes에서 Pulsar를 실행할 때 Pulsar 리소스 이름 정의하기 (How to define Pulsar resource names when running Pulsar in Kubernetes)

Kubernetes에서 Pulsar 함수나 커넥터를 실행한다면, 어떤 admin 인터페이스를 사용하든 Pulsar 리소스의 이름을 Kubernetes 명명 규칙에 따라 정의해야 해요.

Kubernetes는 RFC 1123에 정의된 대로 DNS 서브도메인 이름으로 쓸 수 있는 이름을 요구해요. Pulsar는 Kubernetes 명명 규칙보다 더 많은 합법 문자를 지원해요. Kubernetes가 지원하지 않는 특수 문자가 포함된 Pulsar 리소스 이름(Pulsar 네임스페이스 이름에 콜론이 포함된 경우 등)을 만들면, Kubernetes 런타임이 Pulsar 객체 이름을 RFC 1123 준수 형식의 Kubernetes 리소스 라벨로 변환해요. 결과적으로 Kubernetes 런타임으로 함수나 커넥터를 실행할 수 있어요. Pulsar 객체 이름을 Kubernetes 리소스 라벨로 변환하는 규칙은 다음과 같아요.

  • 63자로 잘라낸다.
  • 다음 문자를 대시(-)로 바꾼다.
    • 알파벳·숫자가 아닌 문자
    • 밑줄(_)
    • 점(.)
  • 시작과 끝의 알파벳·숫자가 아닌 문자를 0으로 바꾼다.

팁:

  • Pulsar 객체 이름을 Kubernetes 리소스 라벨로 변환할 때 오류가 나면(예: Pulsar 객체 이름이 너무 길어 이름 충돌이 발생하거나) 또는 변환 규칙을 커스터마이즈하고 싶다면 Kubernetes 런타임 커스터마이즈 문서를 참고해요.
  • Kubernetes 런타임 구성 방법은 안내 문서를 참고해요.

더 알아보기 (Learn more)

  • 런타임 유형의 차이가 궁금하다면 함수 런타임 문서를 참고해요.
  • Kubernetes에 Pulsar를 배포하는 방법은 Helm 설치 문서를 살펴보세요.
  • Kubernetes 시크릿 개념이 궁금하다면 Kubernetes 공식 문서를 참고해요.