쿠버네티스 확장

쿠버네티스 클러스터를 작업 환경의 요구에 맞게 조정하는 고급 과정을 이해한다.

1 - 확장 API 서버 설정

애그리게이션 레이어(aggregation layer)와 작동하도록 확장 API 서버를 설정하면 쿠버네티스 API 서버를 쿠버네티스의 핵심 API의 일부가 아닌 추가 API로 확장할 수 있다.

시작하기 전에

쿠버네티스 클러스터가 필요하고, kubectl 커맨드-라인 툴이 클러스터와 통신할 수 있도록 설정되어 있어야 한다. 이 튜토리얼은 컨트롤 플레인 호스트가 아닌 노드가 적어도 2개 포함된 클러스터에서 실행하는 것을 추천한다. 만약, 아직 클러스터를 가지고 있지 않다면, minikube를 사용해서 생성하거나 다음의 쿠버네티스 플레이그라운드 중 하나를 사용할 수 있다.

버전 확인을 위해서, 다음 커맨드를 실행 kubectl version.

애그리게이션 레이어와 작동하도록 확장 API 서버 설정

다음 단계는 확장 API 서버를 높은 수준 으로 설정하는 방법을 설명한다. 이 단계는 YAML 구성을 사용하거나 API를 사용하는 것에 상관없이 적용된다. 둘 사이의 차이점을 구체적으로 식별하려고 시도한다. YAML 구성을 사용하여 구현하는 방법에 대한 구체적인 예를 보려면, 쿠버네티스 리포지터리에서 sample-apiserver를 참고할 수 있다.

또는, apiserver-builder와 같은 기존의 타사 솔루션을 사용하여 스켈레톤(skeleton)을 생성하고 다음 단계를 모두 자동화해야 한다.

  1. API서비스(APIService) API가 활성화되어 있는지 확인한다(--runtime-config 확인). 클러스터에서 일부러 해제하지 않았다면, 기본적으로 활성화되어 있어야 한다.
  2. API서비스 오브젝트를 추가하거나 클러스터 관리자가 작성하도록 RBAC 규칙을 작성해야 할 수도 있다. (API 확장은 전체 클러스터에 영향을 주기 때문에, 운영 중인 클러스터에서 API 확장에 대한 테스트/개발/디버깅을 수행하지 않는 것이 좋다.)
  3. 확장 API 서비스를 실행하려는 쿠버네티스 네임스페이스를 생성한다.
  4. HTTPS를 위해 확장 API 서버가 사용하는 서버 인증서에 서명하는 데 사용할 CA 인증서를 생성하거나 가져온다.
  5. HTTPS를 위해 API 서버가 사용할 서버 인증서/키를 생성한다. 이 인증서는 위의 CA 인증서에 의해 서명해야 한다. 또한 Kube DNS 이름의 CN이 있어야 한다. 이것은 쿠버네티스 서비스에서 파생되었으며 <service name>.<service name namespace>.svc 형식이다.
  6. 네임스페이스에 서버 인증서/키를 사용하여 쿠버네티스 시크릿을 생성한다.
  7. 확장 API 서버에 대한 쿠버네티스 디플로이먼트를 생성하고 시크릿을 볼륨으로 로드하는지 확인한다. 확장 API 서버의 작동하는(working) 이미지에 대한 참조를 포함해야 한다. 디플로이먼트는 네임스페이스에도 있어야 한다.
  8. 확장 API 서버가 해당 볼륨에서 해당 인증서를 로드하고 HTTPS 핸드셰이크에 사용되는지 확인한다.
  9. 네임스페이스에서 쿠버네티스 서비스 어카운트를 생성한다.
  10. 리소스에 허용하려는 작업에 대한 쿠버네티스 클러스터 롤(role)을 생성한다.
  11. 네임스페이스의 서비스 어카운트에서 방금 만든 클러스터 롤로 쿠버네티스 클러스터 롤 바인딩을 생성한다.
  12. 네임스페이스의 서비스 어카운트에서 system:auth-delegator 클러스터 롤로 쿠버네티스 클러스터 롤 바인딩을 만들어 인증 결정을 쿠버네티스 핵심 API 서버에 위임한다.
  13. 네임스페이스의 서비스 어카운트에서 extension-apiserver-authentication-reader 롤로 쿠버네티스 롤 바인딩을 생성한다. 이를 통해 확장 API 서버가 extension-apiserver-authentication 컨피그맵(configmap)에 접근할 수 있다.
  14. 쿠버네티스 API 서비스를 생성한다. 위의 CA 인증서는 base64로 인코딩되어, 새로운 라인이 제거되고 API 서비스에서 spec.caBundle로 사용되어야 한다. 이것은 namespaced가 아니어야 한다. kube-aggregator API를 사용하는 경우, base64 인코딩이 수행되므로 PEM 인코딩된 CA 번들만 통과한다.
  15. kubectl을 사용하여 리소스를 얻는다. kubectl을 실행하면, "No resources found."가 반환된다. 이 메시지는 모든 것이 작동됐지만 현재 해당 리소스 유형의 오브젝트가 생성되지 않았음을 나타낸다.

다음 내용

2 - Konnectivity 서비스 설정

Konnectivity 서비스는 컨트롤 플레인에 클러스터 통신을 위한 TCP 수준 프록시를 제공한다.

시작하기 전에

쿠버네티스 클러스터가 있어야 하며, kubectl 명령줄 도구가 클러스터와 통신하도록 설정되어 있어야 한다. 컨트롤 플레인 호스트가 아닌 두 개 이상의 노드로 구성된 클러스터에서 이 튜토리얼을 수행하는 것을 권장한다. 클러스터가 없다면, minikube를 이용하여 생성할 수 있다.

Konnectivity 서비스 설정

다음 단계에는 송신(egress) 설정이 필요하다. 예를 들면 다음과 같다.

apiVersion: apiserver.k8s.io/v1beta1
kind: EgressSelectorConfiguration
egressSelections:
# 클러스터에 대한 송신(egress) 트래픽을 제어하기 위해 
# "cluster"를 name으로 사용한다. 기타 지원되는 값은 "etcd" 및 "master"이다.
- name: cluster
  connection:
    # API 서버와 Konnectivity 서버 간의 프로토콜을
    # 제어한다. 지원되는 값은 "GRPC" 및 "HTTPConnect"이다. 두 모드 간에
    # 최종 사용자가 볼 수 있는 차이점은 없다. 동일한 모드에서 작동하도록
    # Konnectivity 서버를 설정해야 한다.
    proxyProtocol: GRPC
    transport:
      # API 서버가 Konnectivity 서버와 통신하는 데 사용하는 
      # transport를 제어한다. Konnectivity 서버가 API 서버와 동일한 시스템에 
      # 있는 경우 UDS를 사용하는 것이 좋다. 동일한 UDS 소켓에서 
      # 수신 대기하도록 Konnectivity 서버를 구성해야 한다. 
      # 지원되는 다른 전송은 "tcp"이다. TCP 전송을 보호하려면 TLS 구성을 설정해야 한다.
      uds:
        udsName: /etc/kubernetes/konnectivity-server/konnectivity-server.socket

Konnectivity 서비스를 사용하고 네트워크 트래픽을 클러스터 노드로 보내도록 API 서버를 구성해야 한다.

  1. Service Account Token Volume Projection 기능이 활성화되어 있는지 확인한다. 쿠버네티스 v1.20부터는 기본적으로 활성화되어 있다.
  2. admin/konnectivity/egress-selector-configuration.yaml과 같은 송신 구성 파일을 생성한다.
  3. API 서버의 --egress-selector-config-file 플래그를 API 서버 송신 구성 파일의 경로로 설정한다.
  4. UDS 연결을 사용하는 경우 kube-apiserver에 볼륨 구성을 추가한다.
    spec:
      containers:
        volumeMounts:
        - name: konnectivity-uds
          mountPath: /etc/kubernetes/konnectivity-server
          readOnly: false
      volumes:
      - name: konnectivity-uds
        hostPath:
          path: /etc/kubernetes/konnectivity-server
          type: DirectoryOrCreate
    

konnectivity-server에 대한 인증서 및 kubeconfig를 생성하거나 얻는다. 예를 들어 OpenSSL 커맨드라인 툴을 사용하여 컨트롤 플레인 호스트에서 클러스터 CA 인증서 /etc/kubernetes/pki/ca.crt를 사용하여 X.509 인증서를 발급할 수 있다.

openssl req -subj "/CN=system:konnectivity-server" -new -newkey rsa:2048 -nodes -out konnectivity.csr -keyout konnectivity.key -out konnectivity.csr
openssl x509 -req -in konnectivity.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out konnectivity.crt -days 375 -sha256
SERVER=$(kubectl config view -o jsonpath='{.clusters..server}')
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-credentials system:konnectivity-server --client-certificate konnectivity.crt --client-key konnectivity.key --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-cluster kubernetes --server "$SERVER" --certificate-authority /etc/kubernetes/pki/ca.crt --embed-certs=true
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config set-context system:konnectivity-server@kubernetes --cluster kubernetes --user system:konnectivity-server
kubectl --kubeconfig /etc/kubernetes/konnectivity-server.conf config use-context system:konnectivity-server@kubernetes
rm -f konnectivity.crt konnectivity.key konnectivity.csr

다음으로 Konnectivity 서버와 에이전트를 배포해야 한다. kubernetes-sigs/apiserver-network-proxy에서 구현을 참조할 수 있다.

컨트롤 플레인 노드에 Konnectivity 서버를 배포한다. 제공된 konnectivity-server.yaml 매니페스트는 쿠버네티스 구성 요소가 클러스터에 스태틱 파드(static Pod)로 배포되었다고 가정한다. 그렇지 않은 경우에는 Konnectivity 서버를 데몬셋(DaemonSet)으로 배포할 수 있다.

apiVersion: v1
kind: Pod
metadata:
  name: konnectivity-server
  namespace: kube-system
spec:
  priorityClassName: system-cluster-critical
  hostNetwork: true
  containers:
  - name: konnectivity-server-container
    image: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-server:v0.0.16
    command: ["/proxy-server"]
    args: [
            "--logtostderr=true",
            # 이것은 egressSelectorConfiguration에 설정된 값과 일치해야 한다.
            "--uds-name=/etc/kubernetes/konnectivity-server/konnectivity-server.socket",
            # 다음 두 줄은 Konnectivity 서버가 apiserver와 
            # 동일한 시스템에 배포되고 API 서버의 인증서와 
            # 키가 지정된 위치에 있다고 가정한다.
            "--cluster-cert=/etc/kubernetes/pki/apiserver.crt",
            "--cluster-key=/etc/kubernetes/pki/apiserver.key",
            # 이것은 egressSelectorConfiguration에 설정된 값과 일치해야 한다.
            "--mode=grpc",
            "--server-port=0",
            "--agent-port=8132",
            "--admin-port=8133",
            "--health-port=8134",
            "--agent-namespace=kube-system",
            "--agent-service-account=konnectivity-agent",
            "--kubeconfig=/etc/kubernetes/konnectivity-server.conf",
            "--authentication-audience=system:konnectivity-server"
            ]
    livenessProbe:
      httpGet:
        scheme: HTTP
        host: 127.0.0.1
        port: 8134
        path: /healthz
      initialDelaySeconds: 30
      timeoutSeconds: 60
    ports:
    - name: agentport
      containerPort: 8132
      hostPort: 8132
    - name: adminport
      containerPort: 8133
      hostPort: 8133
    - name: healthport
      containerPort: 8134
      hostPort: 8134
    volumeMounts:
    - name: k8s-certs
      mountPath: /etc/kubernetes/pki
      readOnly: true
    - name: kubeconfig
      mountPath: /etc/kubernetes/konnectivity-server.conf
      readOnly: true
    - name: konnectivity-uds
      mountPath: /etc/kubernetes/konnectivity-server
      readOnly: false
  volumes:
  - name: k8s-certs
    hostPath:
      path: /etc/kubernetes/pki
  - name: kubeconfig
    hostPath:
      path: /etc/kubernetes/konnectivity-server.conf
      type: FileOrCreate
  - name: konnectivity-uds
    hostPath:
      path: /etc/kubernetes/konnectivity-server
      type: DirectoryOrCreate

그런 다음 클러스터에 Konnectivity 에이전트를 배포한다.

apiVersion: apps/v1
# 에이전트를 Deployment(디플로이먼트)로 배포할 수도 있다. 각 노드에 에이전트가
# 있을 필요는 없다.
kind: DaemonSet
metadata:
  labels:
    addonmanager.kubernetes.io/mode: Reconcile
    k8s-app: konnectivity-agent
  namespace: kube-system
  name: konnectivity-agent
spec:
  selector:
    matchLabels:
      k8s-app: konnectivity-agent
  template:
    metadata:
      labels:
        k8s-app: konnectivity-agent
    spec:
      priorityClassName: system-cluster-critical
      tolerations:
        - key: "CriticalAddonsOnly"
          operator: "Exists"
      containers:
        - image: us.gcr.io/k8s-artifacts-prod/kas-network-proxy/proxy-agent:v0.0.16
          name: konnectivity-agent
          command: ["/proxy-agent"]
          args: [
                  "--logtostderr=true",
                  "--ca-cert=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt",
                  # konnectivity 서버는 hostNetwork=true로 실행되기 때문에,
                  # 이것은 마스터 머신의 IP 주소이다.
                  "--proxy-server-host=35.225.206.7",
                  "--proxy-server-port=8132",
                  "--admin-server-port=8133",
                  "--health-server-port=8134",
                  "--service-account-token-path=/var/run/secrets/tokens/konnectivity-agent-token"
                  ]
          volumeMounts:
            - mountPath: /var/run/secrets/tokens
              name: konnectivity-agent-token
          livenessProbe:
            httpGet:
              port: 8134
              path: /healthz
            initialDelaySeconds: 15
            timeoutSeconds: 15
      serviceAccountName: konnectivity-agent
      volumes:
        - name: konnectivity-agent-token
          projected:
            sources:
              - serviceAccountToken:
                  path: konnectivity-agent-token
                  audience: system:konnectivity-server

마지막으로 클러스터에서 RBAC가 활성화된 경우 관련 RBAC 규칙을 생성한다.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: system:konnectivity-server
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: system:konnectivity-server
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: konnectivity-agent
  namespace: kube-system
  labels:
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile