네트워크 프록시로 컨트롤러 액세스가 가능한 GKE 비공개 클러스터 만들기

비공개 클러스터 컨트롤러 엔드포인트가 있는 GKE 비공개 클러스터를 만들면 공개 인터넷에서 클러스터의 컨트롤러 노드에 액세스할 수 없지만 관리를 위해 이 노드에 액세스할 수 있어야 합니다.

기본적으로 클러스터는 비공개 엔드포인트를 통해 컨트롤러에 액세스할 수 있고 승인된 네트워크는 VPC 네트워크 내에서 정의될 수 있습니다.

하지만 온프레미스 또는 다른 VPC 네트워크에서 컨트롤러에 액세스하려면 추가적인 단계가 필요합니다. 이는 Google에서 컨트롤러를 호스팅하는 VPC 네트워크를 소유하며 다른 VPC 네트워크 피어링 연결, Cloud VPN 또는 Cloud Interconnect를 통해 연결된 리소스에서 액세스할 수 없기 때문입니다.

온프레미스 또는 Cloud VPN 또는 Cloud Interconnect에 연결된 다른 VPC 네트워크에서 컨트롤러에 액세스하려면 VPC 네트워크에서 Google 소유 VPC로 경로 내보내기를 사용 설정합니다.

다른 VPC 네트워크에서 또는 다른 VPC 네트워크 피어링을 통해 연결된 온프레미스(예: 허브 및 스포크 설계)에서 컨트롤러에 액세스할 수 있도록 하려면 승인된 IP 주소 공간에 호스팅된 프록시를 만듭니다. VPC 네트워크 피어링이 전환할 수 없기 때문입니다.

이 가이드에서는 GKE 비공개 클러스터 내에서 프록시를 구성하는 방법을 설명합니다.

목표

  • 외부 액세스를 허용하지 않는 GKE 비공개 클러스터를 만듭니다.
  • 프록시를 실행하기 위한 Docker 이미지를 만들고 배포합니다.
  • 프록시에 액세스하기 위한 Kubernetes Service를 만듭니다.
  • 프록시에 대한 액세스를 테스트합니다.

비용

이 가이드에서는 다음과 같이 비용이 청구될 수 있는 Google Cloud Platform 구성요소를 사용합니다.

가격 계산기를 사용하여 예상 사용량을 기준으로 예상 비용을 산출할 수 있습니다.

이 가이드를 마치면 만든 리소스를 삭제하여 비용이 계속 청구되지 않게 할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

  1. Google Cloud 계정에 로그인합니다. Google Cloud를 처음 사용하는 경우 계정을 만들고 Google 제품의 실제 성능을 평가해 보세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  3. Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다. 프로젝트에 결제가 사용 설정되어 있는지 확인하는 방법을 알아보세요.

  4. Compute Engine and Google Kubernetes Engine API를 사용 설정합니다.

    API 사용 설정

환경 설정

이 가이드에서는 Cloud Shell을 사용하여 명령어를 입력합니다. Cloud Shell은 Cloud Console의 명령줄에 대한 액세스를 제공하며 Google Cloud 개발에 필요한 Cloud SDK와 기타 도구를 포함합니다. Cloud Shell은 Cloud Console 하단에 창으로 표시됩니다. 초기화되는 데 몇 분 정도 걸릴 수 있지만 창은 즉시 표시됩니다.

Cloud Shell을 사용하려면 다음과 같이 환경을 설정합니다.

  1. Cloud Console에서 Cloud Shell을 엽니다.

    Cloud Shell 열기

  2. 이전에 만들었거나 선택한 프로젝트에서 작업하고 있는지 확인합니다. [YOUR_PROJECT_ID]를 Google Cloud 프로젝트로 바꿉니다.

    gcloud config set project [YOUR_PROJECT_ID]
    export PROJECT_ID=`gcloud config list --format="value(core.project)"`
    
  3. 기본 컴퓨팅 영역을 설정합니다. 이 가이드의 목적에 따라 이 영역은 us-central1-c입니다. 프로덕션 환경에 배포하는 경우 선택한 리전에 배포합니다.

    gcloud config set compute/region us-central1
    gcloud config set compute/zone us-central1-c
    export REGION=us-central1
    export ZONE=us-central1-c
    
    

VPC 네트워크 및 클라이언트 VM 만들기

리소스를 호스팅할 VPC 네트워크 및 서브넷을 만듭니다.

  1. VPC 네트워크를 만듭니다.

    gcloud compute networks create k8s-proxy --subnet-mode=custom
    
  2. 새롭게 만든 VPC 네트워크에서 커스텀 서브넷을 만듭니다.

    gcloud compute networks subnets create subnet-cluster \
        --network=k8s-proxy --range=10.50.0.0/16
    
  3. 리소스를 Kubernetes 클러스터에 배포할 때 사용할 클라이언트 VM을 만듭니다.

    gcloud compute instances create --subnet=subnet-cluster \
        --scopes cloud-platform proxy-temp
    
  4. 새롭게 만든 인스턴스의 내부 IP 주소를 환경 변수에 저장합니다.

    export CLIENT_IP=`gcloud compute instances describe proxy-temp \
        --format="value(networkInterfaces[0].networkIP)"`
    
  5. VPC 네트워크에 대한 SSH 액세스를 허용하는 방화벽 규칙을 만듭니다.

    gcloud compute firewall-rules create k8s-proxy-ssh --network k8s-proxy \
        --allow tcp:22
    

비공개 클러스터 만들기

이번에는 이 가이드에서 사용할 비공개 클러스터를 만듭니다.

사용하기를 원하는 클러스터가 이미 있는 경우에는 클러스터를 만드는 단계를 건너뛸 수 있지만, 클라이언트 머신에서 몇 가지 초기 액세스 형태를 구성해야 합니다.

  • Cloud Shell에서 클러스터를 만듭니다.

    gcloud container clusters create frobnitz  \
        --master-ipv4-cidr=172.16.0.64/28 \
        --network k8s-proxy \
        --subnetwork=subnet-cluster \
        --enable-ip-alias \
        --enable-private-nodes \
        --enable-private-endpoint \
        --master-authorized-networks $CLIENT_IP/32 \
        --enable-master-authorized-networks
    

    이 명령어는 master-authorized-networks가 있는 frobnitz라는 GKE 비공개 클러스터를 만들고 클라이언트 머신만 액세스하도록 허용합니다.

Docker 이미지 만들기

다음 단계를 수행하여 Kubernetes API 서버에 전달 프록시 역할을 하는 k8s-api-proxy,라는 Kubernetes API 프록시 이미지를 빌드합니다.

  1. Cloud Shell에서 디렉터리를 만든 후 해당 디렉터리로 변경합니다.

    mkdir k8s-api-proxy && cd k8s-api-proxy
  2. Dockerfile을 만듭니다. 다음 구성은 Privoxy 프록시가 있는 간단한 컨테이너 배포인 Alpine에서 컨테이너를 만듭니다. 또한 Dockerfile은 컨테이너 초기화를 위해 curljq를 설치하고, 필요한 구성 파일을 추가하고, 포트 8118을 GKE에 내부적으로 노출하고, 시작 스크립트를 추가합니다.

    FROM alpine
    RUN apk add -U curl privoxy jq && \ mv /etc/privoxy/templates /etc/privoxy-templates && \ rm -rf /var/cache/apk/* /etc/privoxy/* && \ mv /etc/privoxy-templates /etc/privoxy/templates ADD --chown=privoxy:privoxy config \ /etc/privoxy/ ADD --chown=privoxy:privoxy k8s-only.action \ /etc/privoxy/ ADD --chown=privoxy:privoxy k8s-rewrite-internal.filter \ /etc/privoxy/ ADD k8s-api-proxy.sh /
    EXPOSE 8118/tcp
    ENTRYPOINT ["./k8s-api-proxy.sh"]
  3. k8s-api-proxy 디렉터리에서 config 파일을 만들고 다음 콘텐츠를 추가합니다.

    #config directory
    confdir /etc/privoxy
    # Allow Kubernetes API access only
    actionsfile /etc/privoxy/k8s-only.action
    # Rewrite https://CLUSTER_IP to https://kubernetes.default
    filterfile /etc/privoxy/k8s-rewrite-internal.filter
    # Don't show the pod name in errors
    hostname k8s-privoxy
    # Bind to all interfaces, port :8118
    listen-address  :8118
    # User cannot click-through a block
    enforce-blocks 1
    # Allow more than one outbound connection
    tolerate-pipelining 1
    
  4. 동일한 디렉터리에서 k8s-only.action 파일을 만들고 다음 콘텐츠를 추가합니다. CLUSTER_IPk8s-api-proxy.sh가 실행될 때 대체됩니다.

    # Block everything...
    {+block{Not Kubernetes}}
    /
    # ... except the internal k8s endpoint, which you rewrite (see # k8s-rewrite-internal.filter). {+client-header-filter{k8s-rewrite-internal} -block{Kubernetes}} CLUSTER_IP/
  5. k8s-rewrite-internal.filter 파일을 만들고 다음 콘텐츠를 추가합니다. CLUSTER_IPk8s-api-proxy.sh가 실행될 때 대체됩니다.

    CLIENT-HEADER-FILTER: k8s-rewrite-internal\
     Rewrite https://CLUSTER_IP/ to https://kubernetes.default/
    s@(CONNECT) CLUSTER_IP:443\
     (HTTP/\d\.\d)@$1 kubernetes.default:443 $2@ig
    
  6. k8s-api-proxy.sh 파일을 만들고 다음 콘텐츠를 추가합니다.

    #!/bin/sh
    
    set -o errexit
    set -o pipefail
    set -o nounset
    
    # Get the internal cluster IP
    export TOKEN=$(cat /run/secrets/kubernetes.io/serviceaccount/token)
    INTERNAL_IP=$(curl -H "Authorization: Bearer $TOKEN" -k -SsL https://kubernetes.default/api |
    jq -r '.serverAddressByClientCIDRs[0].serverAddress')
    
    # Replace CLUSTER_IP in the rewrite filter and action file
    sed -i "s/CLUSTER_IP/${INTERNAL_IP}/g"\
     /etc/privoxy/k8s-rewrite-internal.filter
    sed -i "s/CLUSTER_IP/${INTERNAL_IP}/g"\
     /etc/privoxy/k8s-only.action
    
    # Start Privoxy un-daemonized
    privoxy --no-daemon /etc/privoxy/config
    
  7. k8s-api-proxy.sh를 실행 파일로 만듭니다.

    chmod +x k8s-api-proxy.sh
  8. 컨테이너를 빌드하고 프로젝트에 내보냅니다.

    docker build -t gcr.io/$PROJECT_ID/k8s-api-proxy:0.1 .
    docker push gcr.io/$PROJECT_ID/k8s-api-proxy:0.1
    

이미지 및 서비스 배포

  1. Cloud Shell에서 앞에서 만든 클라이언트 VM에 로그인합니다.

    gcloud compute ssh proxy-temp
    
  2. kubectl 도구를 설치합니다.

    sudo apt-get install kubectl
    
  3. 프로젝트 ID를 환경 변수로 저장합니다.

    export PROJECT_ID=`gcloud config list --format="value(core.project)"`
    
  4. 클러스터 사용자 인증 정보를 가져옵니다.

    gcloud container clusters get-credentials frobnitz \
    --zone us-central1-c --internal-ip
    
  5. 방금 만든 컨테이너를 노출하는 Kubernetes 배포를 만듭니다.

    kubectl run k8s-api-proxy \
        --image=gcr.io/$PROJECT_ID/k8s-api-proxy:0.1 \
        --port=8118
    
  6. 내부 부하 분산기에 사용할 ilb.yaml 파일을 만들고 다음을 복사합니다.

    apiVersion: v1
    kind: Service
    metadata:
      labels:
        run: k8s-api-proxy
      name: k8s-api-proxy
      namespace: default
      annotations:
        cloud.google.com/load-balancer-type: "Internal"
    spec:
      ports:
      - port: 8118
        protocol: TCP
        targetPort: 8118
      selector:
        run: k8s-api-proxy
      type: LoadBalancer
    
  7. 내부 부하 분산기를 배포합니다.

    kubectl create -f ilb.yaml
  8. 서비스를 확인하고 IP 주소를 기다립니다.

    kubectl get service/k8s-api-proxy

    다음과 같이 출력됩니다. 외부 IP가 보이면 프록시가 준비된 것입니다.

    NAME            TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
    k8s-api-proxy   LoadBalancer   10.24.13.129   10.24.24.3    8118:30282/TCP   2m
    

    이 단계에 있는 외부 IP 주소가 프록시 주소입니다.

  9. ILB의 IP 주소를 환경 변수로 저장합니다.

    export LB_IP=`kubectl get  service/k8s-api-proxy \
    -o jsonpath='{.status.loadBalancer.ingress[].ip}'`
    
  10. 클러스터의 컨트롤러 IP 주소를 환경 변수에 저장합니다.

    export CONTROLLER_IP=`gcloud container clusters describe frobnitz \
    --zone=us-central1-c \
    --format="get(privateClusterConfig.privateEndpoint)"`
    
  11. 프록시를 통해 Kubernetes API에 액세스하여 프록시 사용 여부를 확인합니다.

    curl -k -x $LB_IP:8118 https://$CONTROLLER_IP/version
    
    출력은 다음과 같습니다(출력이 다를 수 있음).
    {
      "major": "1",
      "minor": "15+",
      "gitVersion": "v1.15.11-gke.5",
      "gitCommit": "a5bf731ea129336a3cf32c3375317b3a626919d7",
      "gitTreeState": "clean",
      "buildDate": "2020-03-31T02:49:49Z",
      "goVersion": "go1.12.17b4",
      "compiler": "gc",
      "platform": "linux/amd64"
    }
    
  12. kubectl 명령어가 어디서나 내부 부하 분산기에 도달할 수 있도록 https_proxy 환경 변수를 HTTP(S) 프록시로 설정합니다.

    export https_proxy=$LB_IP:8118
  13. kubectl 명령어를 실행하여 프록시와 https_proxy 변수를 테스트합니다.

    kubectl get pods

    다음과 같이 출력되면 프록시를 통해 Kubernetes API에 성공적으로 연결된 것입니다.

    NAME                             READY   STATUS    RESTARTS   AGE
    k8s-api-proxy-766c69dd45-mfqf4   1/1     Running   0          6m15s
    
  14. 클라이언트 VM에서 나옵니다.

    exit

삭제

이 가이드에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

프로젝트 삭제

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

    리소스 관리로 이동

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

GKE 클러스터 삭제

프로젝트를 삭제하지 않으려면 GKE 클러스터를 삭제합니다.

gcloud container clusters delete frobnitz

다음 단계