DaemonSet으로 GKE 노드를 자동으로 부트스트랩

이 튜토리얼에서는 DaemonSets를 사용하여 Google Kubernetes Engine (GKE) 클러스터의 노드를 맞춤설정하는 방법을 보여줍니다. DaemonSet는 모든 노드 또는 선택한 노드에서 포드의 복사본이 실행되도록 합니다. 클러스터에 새 노드가 추가되면 DaemonSet의 포드도 실행됩니다.

클러스터의 초기화에 사용하는 도구 및 시스템이 워크로드 실행에 사용하는 도구 및 시스템과 다를 경우 환경 관리 시간이 늘어날 수 있습니다. 예를 들어 구성 관리 도구를 사용하여 클러스터 노드를 초기화하면 나머지 워크로드가 실행되는 런타임 환경 범위를 벗어난 절차를 사용하게 됩니다. DaemonSet를 사용하면 GKE 노드를 수정할 때와 동일한 도구를 사용하여 워크로드를 조정할 수 있습니다.

이 가이드의 목표는 시스템 관리자, 시스템 엔지니어 또는 인프라 운영자가 Kubernetes 클러스터 초기화를 간소화하도록 돕는 것입니다.

이 페이지를 읽기 전에 다음 사항을 숙지해야 합니다.

이 튜토리얼에서는 Kubernetes taint 및 톨러레이션(toleration)을 사용하여 애플리케이션 워크로드를 노드에 예약하기 전에 DaemonSet로 노드를 구성하는 방법을 알아봅니다.

목표

이 튜토리얼에서는 다음과 같은 작업을 수행하게 됩니다.

  • GKE 클러스터를 프로비저닝합니다.
  • 노드 구성을 적용하기 전에 워크로드 예약이 되지 않도록 노드 풀에 taint를 적용합니다.
  • 노드를 구성하고 taint를 삭제하는 DaemonSet를 배포합니다.
  • 클러스터 노드가 구성되고 taint가 삭제되었는지 확인합니다.

비용

이 문서에서는 비용이 청구될 수 있는 구성요소를 사용합니다 Google Cloud.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요.

Google Cloud 신규 사용자는 무료 체험판을 사용할 수 있습니다.

이 문서에 설명된 태스크를 완료했으면 만든 리소스를 삭제하여 청구가 계속되는 것을 방지할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

  1. 계정에 로그인합니다. Google Cloud 를 처음 사용하는 경우 Google Cloud 계정을 만들어 실제 시나리오에서 제품이 어떻게 작동하는지 평가하세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. Verify that billing is enabled for your Google Cloud project.

  4. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  5. Verify that billing is enabled for your Google Cloud project.

권한이 있는 DaemonSet 보안 영향

DaemonSet (또는 모든 포드)에서 securityContext: privileged: true 설정을 사용하는 것은 강력하지만 해당 포드의 대부분의 컨테이너 격리 경계를 사용 중지하므로 상당한 보안 영향을 미칩니다. 이로 인해 발생하는 다음과 같은 보안 제한사항과 위험을 알고 있어야 합니다.

  • 컨테이너 이스케이프 또는 호스트 손상: 권한이 있는 컨테이너 애플리케이션 또는 이미지 내의 취약점으로 인해 호스트 노드에 직접 루트 액세스 권한이 부여될 수 있습니다.
  • 최소 권한 위반: 권한이 있는 모드는 특정 작업에 필요한 것보다 훨씬 많은 모든 기능을 부여합니다. 이 광범위한 액세스는 컨테이너가 손상된 경우 잠재적 손상을 증가시킵니다.
  • 노드 불안정화: 실수로 또는 악의적인 명령어가 권한이 있는 컨테이너 내에서 실행될 수 있습니다(예: 잘못된 sysctl 값 또는 rm -rf /host/boot와 같은 명령어). 이러한 유형의 명령어는 호스트 노드 운영체제를 비정상 종료하거나 손상시킬 수 있습니다.
  • 측면 이동: 권한이 있는 DaemonSet를 통해 하나의 노드를 손상시키면 공격자가 다른 노드, Kubernetes 제어 영역 또는 연결된 시스템을 공격할 수 있는 강력한 발판을 마련할 수 있습니다.
  • 데이터 노출: 호스트 파일 시스템(/)에 대한 무제한 액세스는 노드에 저장된 민감한 정보(예: 사용자 인증 정보, 키 또는 hostPath 볼륨을 사용하는 경우 다른 포드에 속한 데이터)를 노출할 수 있습니다.
  • 공격 대상 영역 증가: 권한이 있는 모드는 컨테이너 내에서 잠재적인 악용에 대해 더 많은 호스트 커널 시스템 호출 및 기능을 노출합니다.

보안 위험을 방지하려면 다음과 같은 권장사항과 완화 방법을 알고 있어야 합니다.

  • 권한이 있는 모드 사용 방지: 가장 안전한 방법은 privileged: true 설정을 완전히 피하는 것입니다.
  • Linux 기능 사용: 권한 상승이 필요한 경우 전체 권한 대신 securityContext.capabilities.add 필드에서 NET_ADMIN, SYS_ADMIN, SYS_MODULE과 같은 특정 Linux 기능을 부여할 수 있습니다. 이 접근 방식은 광범위한 권한을 부여하는 것보다 권장되는 최소 권한 원칙을 따릅니다.
  • 범위 제한: 컨테이너가 손상된 경우 잠재적 영향을 포함하도록 전용으로 사용되며 taint가 적용될 수 있는 노드 풀에서만 권한이 있는 DaemonSet를 실행합니다.
  • 정책 적용: 정책 컨트롤러 또는 Gatekeeper와 같은 도구를 사용하여 권한이 있는 컨테이너 배포를 제한, 감사 또는 정당화해야 하는 정책을 만듭니다.
  • 신뢰할 수 있는 이미지 검사 및 사용: Binary Authorization 및 엄격한 이미지 검사를 사용하여 검증되고 신뢰할 수 있는 컨테이너 이미지만 승격된 권한으로 실행되도록 합니다.
  • 호스트 마운트 최소화: 필요한 특정 호스트 경로만 마운트하고 가능한 경우 readOnly: true를 사용합니다. 전체 루트 파일 시스템 (/)을 마운트하지 마세요.
  • 정기적인 감사 실행: privileged: true 설정으로 실행되는 모든 워크로드를 정기적으로 검토합니다.

환경 부트스트랩

이 섹션에서는 다음과 같은 작업을 수행하게 됩니다.

  1. 필요한 Cloud API를 사용 설정합니다.
  2. GKE 클러스터의 노드에 대해 제한된 권한을 가진 서비스 계정을 프로비저닝합니다.
  3. GKE 클러스터를 준비합니다.
  4. 사용자 클러스터 관리 권한을 부여합니다.

Cloud API 사용 설정

  1. Cloud Shell을 엽니다.

    Cloud Shell 열기

  2. Google Cloud 프로젝트를 선택합니다.

    gcloud config set project project-id
    

    project-id를 이 튜토리얼용으로 만들거나 선택한 Google Cloud 프로젝트의 ID로 바꿉니다.

  3. Kubernetes Engine API를 사용 설정합니다.

    gcloud services enable container.googleapis.com
    

GKE 클러스터를 관리하기 위한 서비스 계정 프로비저닝

이 섹션에서는 클러스터의 노드와 연결된 서비스 계정을 만듭니다. 이 가이드에서 GKE 노드는 기본 서비스 계정 대신 이 서비스 계정을 사용합니다. 애플리케이션을 실행하는 데 필요한 역할과 액세스 권한을 서비스 계정에 부여하는 것이 가장 좋습니다.

서비스 계정에 필요한 역할은 다음과 같습니다.

  • 모니터링 뷰어 역할 (roles/monitoring.viewer). 이 역할은 모니터링 데이터에 대한 읽기 전용 액세스 권한을 제공합니다.
  • 모니터링 측정항목 작성자 역할 (roles/monitoring.metricWriter). 이 역할은 모니터링 데이터 쓰기를 허용합니다.
  • 로그 작성자 역할 (roles/logging.logWriter). 이 역할은 로그를 작성할 수 있는 권한을 부여합니다.

서비스 계정을 프로비저닝하려면 다음 단계를 따르세요.

  1. Cloud Shell에서 서비스 계정 이름을 저장할 환경 변수를 만듭니다.

    GKE_SERVICE_ACCOUNT_NAME=ds-init-tutorial-gke
    
  2. 서비스 계정을 만듭니다.

    gcloud iam service-accounts create "$GKE_SERVICE_ACCOUNT_NAME" \
      --display-name="$GKE_SERVICE_ACCOUNT_NAME"
    
  3. 서비스 계정 이메일 계정 이름을 저장할 환경 변수를 초기화합니다.

    GKE_SERVICE_ACCOUNT_EMAIL="$(gcloud iam service-accounts list \
        --format='value(email)' \
        --filter=displayName:"$GKE_SERVICE_ACCOUNT_NAME")"
    
  4. Identity and Access Management(IAM) 역할을 서비스 계정에 결합합니다.

    gcloud projects add-iam-policy-binding \
        "$(gcloud config get-value project 2> /dev/null)" \
        --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \
        --role roles/monitoring.viewer
    gcloud projects add-iam-policy-binding \
        "$(gcloud config get-value project 2> /dev/null)" \
        --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \
        --role roles/monitoring.metricWriter
    gcloud projects add-iam-policy-binding \
        "$(gcloud config get-value project 2> /dev/null)" \
        --member serviceAccount:"$GKE_SERVICE_ACCOUNT_EMAIL" \
        --role roles/logging.logWriter
    

GKE 클러스터 준비

이 섹션에서는 GKE 클러스터를 실행하고, 권한을 부여하고, 클러스터 구성을 완료합니다.

이 튜토리얼에서는 비교적 적은 수의 작은 범용 노드가 있는 클러스터만으로도 이 튜토리얼의 개념을 보여줄 수 있습니다. 한 개의 노드 풀 (기본값) 로 클러스터를 만듭니다.

  • Cloud Shell에서 리전 GKE 클러스터를 만들고 실행합니다.

    gcloud container clusters create ds-init-tutorial \
        --enable-ip-alias \
        --machine-type=n1-standard-2 \
        --metadata disable-legacy-endpoints=true \
        --node-labels=app=default-init \
        --node-locations us-central1-a,us-central1-b,us-central1-c \
        --no-enable-basic-auth \
        --no-issue-client-certificate \
        --num-nodes=1 \
        --location us-central1 \
        --service-account="$GKE_SERVICE_ACCOUNT_EMAIL"
    

DaemonSet를 사용하여 노드 구성 적용

이 섹션에서는 노드 풀에 taint를 적용하여 구성이 완료되기 전에 노드에서 워크로드가 실행되지 않도록 합니다. 그런 다음 다음 작업을 실행하는 DaemonSet를 배포합니다.

  1. taint에 대한 톨러레이션(toleration)을 사용하여 taint가 적용된 노드에 포드를 예약합니다.
  2. 먼저 sysctl을 사용하여 노드 구성을 적용한 다음 kubectl을 사용하여 노드에서 taint를 삭제하는 권한이 있는 init 컨테이너를 실행합니다. taint를 삭제하면 노드를 워크로드에 예약할 수 있습니다.
  3. 유휴 상태로 유지되며 리소스를 사용하지 않는 일시중지 컨테이너를 예약하고 실행하여 DaemonSet가 구성에 사용되는 포드를 재예약하지 못하도록 합니다.

이 튜토리얼에서는 vm.max_map_count=262144 커널 매개변수를 구성 예시로 적용합니다.

  1. 기본 노드 풀에 taint를 적용합니다.

    gcloud container node-pools update default-pool \
      --cluster=ds-init-tutorial \
      --node-taints=node.config.status/stage=configuring:NoSchedule \
      --region=us-central1
    

    이 taint를 사용하면 DaemonSet 포드와 같이 taint를 허용하는 포드만 이 노드 풀에 예약할 수 있습니다.

  2. taint가 적용되었는지 확인합니다.

    kubectl describe nodes -l cloud.google.com/gke-nodepool=default-pool | grep Taints
    

    노드 상태에 node.config.status/stage=configuring:NoSchedule이 표시되어야 합니다.

  3. 다음 매니페스트를 auto-untaint-daemonset.yaml로 저장합니다.

    # WARNING: This DaemonSet runs as privileged, which has significant
    # security implications. Only use this on clusters where you have
    # strict controls over what is deployed.
    ---
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: node-config-sa
      namespace: default
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: node-patcher-role
    rules:
    - apiGroups: [""]
      resources: ["nodes"]
      # Permissions needed to read and remove a taint from the node.
      verbs: ["get", "patch", "update"]
    ---
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: node-config-binding
    subjects:
    - kind: ServiceAccount
      name: node-config-sa
      namespace: default
    roleRef:
      kind: ClusterRole
      name: node-patcher-role
      apiGroup: rbac.authorization.k8s.io
    ---
    apiVersion: apps/v1
    kind: DaemonSet
    metadata:
      name: auto-untaint-daemonset
      labels:
        app: auto-untaint-configurator
    spec:
      selector:
        matchLabels:
          app: auto-untaint-configurator
      updateStrategy:
        type: RollingUpdate
      template:
        metadata:
          labels:
            app: auto-untaint-configurator
        spec:
          serviceAccountName: node-config-sa
          hostPID: true
          # Toleration now matches the taint on your node.
          tolerations:
          - key: "node.config.status/stage"
            operator: "Equal"
            value: "configuring"
            effect: "NoSchedule"
          volumes:
          - name: host-root-fs
            hostPath:
              path: /
          initContainers:
          - name: configure-and-untaint
            image: ubuntu:22.04 # Using a standard container image.
            securityContext:
              privileged: true # Required for chroot and sysctl.
            env:
            - name: NODE_NAME
              valueFrom:
                fieldRef:
                  fieldPath: spec.nodeName
            volumeMounts:
            - name: host-root-fs
              mountPath: /host
            command: ["/bin/bash", "-c"]
            args:
            - |
              # Using explicit error checking for each critical command.
    
              # Define the configuration and taint details.
              SYSCTL_PARAM="vm.max_map_count"
              SYSCTL_VALUE="262144"
              TAINT_KEY="node.config.status/stage"
    
              echo "Running configuration on node: ${NODE_NAME}"
    
              # 1. APPLY CONFIGURATION
              echo "--> Applying ${SYSCTL_PARAM}=${SYSCTL_VALUE}..."
              if ! chroot /host sysctl -w "${SYSCTL_PARAM}=${SYSCTL_VALUE}"; then
                echo "ERROR: Failed to apply sysctl parameter." >&2
                exit 1
              fi
              echo "--> Configuration applied successfully."
    
              # 2. UNTAINT THE NODE
              # This command removes the taint from the node this Pod is running on.
              echo "--> Untainting node ${NODE_NAME} by removing taint ${TAINT_KEY}..."
              if ! /host/home/kubernetes/bin/kubectl taint node "${NODE_NAME}" "${TAINT_KEY}:NoSchedule-"; then
                echo "ERROR: Failed to untaint the node." >&2
                exit 1
              fi
              echo "--> Node has been untainted and is now schedulable."
          # The main container is minimal; it just keeps the Pod running.
          containers:
          - name: pause-container
            image: registry.k8s.io/pause:3.9
    

    이 매니페스트는 DaemonSet에 노드에서 taint를 삭제할 수 있는 권한을 부여하기 위해 ServiceAccount, ClusterRole, ClusterRoleBinding을 만듭니다. DaemonSet는 configuring:NoSchedule taint를 허용하는 각 노드에 포드를 배포합니다. 이 포드는 sysctl 구성 (vm.max_map_count=262144)을 적용하고 노드 taint를 삭제하는 권한이 있는 init 컨테이너를 실행하여 노드를 예약할 수 있도록 합니다. 그런 다음 일시중지 컨테이너가 포드를 계속 실행하기 시작합니다.

    init 컨테이너는 보안 영향을 미치는 권한이 있는 모드에서 실행됩니다. 자세한 내용은 권한이 있는 DaemonSet 절충안 및 보안 제한사항을 참조하세요.

  4. 매니페스트를 적용합니다.

    kubectl apply -f auto-untaint-daemonset.yaml
    
  5. DaemonSet 포드가 생성되었는지 확인하고 Running 상태가 될 때까지 기다립니다.

    kubectl get pods -l app=auto-untaint-configurator -o wide
    

    Running 상태는 init 컨테이너가 성공적으로 완료되었음을 나타냅니다. 다음 섹션에서 초기화를 확인할 수 있도록 포드 이름을 기록해 둡니다.

초기화 절차의 검증 및 확인

노드 구성이 완료되면 로그를 확인하여 결과를 확인할 수 있습니다.

  1. 포드 중 하나의 init 컨테이너 로그를 확인하여 출력을 확인합니다.

    kubectl logs POD_NAME -c configure-and-untaint
    

    POD_NAME을 포드 이름으로 바꿉니다.

    구성 및 노드 taint 해제가 성공했음을 나타내는 출력이 표시되어야 합니다.

  2. taint가 삭제되었는지 확인합니다.

    kubectl describe nodes -l cloud.google.com/gke-nodepool=default-pool | grep Taints
    

    노드 상태에 Taints: <none>이 표시되거나 키가 node.config.status/stage인 taint가 표시되어야 합니다.

정리

이 튜토리얼에서 사용된 리소스에 대해 계정에 요금이 청구되지 않도록 하려면 이 튜토리얼을 위해 만든 프로젝트를 삭제하면 됩니다. Google Cloud 이 가이드 전용으로 프로젝트를 만들었으면 전체 프로젝트를 삭제해도 됩니다. 기존 프로젝트를 사용했지만 삭제하지 않으려면 다음 단계에 따라 프로젝트를 정리하세요.

프로젝트 정리

프로젝트를 삭제하지 않고 정리하려면 이 가이드에서 만든 리소스를 삭제해야 합니다.

  1. Cloud Shell에서 GKE 클러스터를 삭제합니다.

    gcloud container clusters delete ds-init-tutorial --quiet --region us-central1
    
  2. 서비스 계정을 삭제합니다.

    gcloud iam service-accounts delete "$GKE_SERVICE_ACCOUNT_EMAIL" --quiet
    

프로젝트 삭제

청구되지 않도록 하는 가장 쉬운 방법은 가이드에서 만든 프로젝트를 삭제하는 것입니다.

  1. 콘솔에서 리소스 관리 페이지로 이동합니다. Google Cloud

    리소스 관리로 이동

  2. 프로젝트 목록에서 삭제할 프로젝트를 선택하고 삭제를 클릭합니다.
  3. 대화상자에서 프로젝트 ID를 입력한 후 종료 를 클릭하여 프로젝트를 삭제합니다.

다음 단계