GKE 클러스터에서 Cloud Service Mesh 이그레스 게이트웨이 사용: 튜토리얼


이 튜토리얼에서는 Cloud Service Mesh 이그레스 게이트웨이 및 기타 Google Cloud 컨트롤을 사용하여 Google Kubernetes Engine 클러스터에 배포된 워크로드에서 아웃바운드 트래픽(이그레스)을 보호하는 방법을 설명합니다. 이 튜토리얼은 GKE 클러스터에서 Cloud Service Mesh 이그레스 게이트웨이를 사용하기 위한 권장사항의 보충 자료입니다.

이 튜토리얼의 주요 이용 대상에는 하나 이상의 소프트웨어 배포팀이 사용하는 Google Kubernetes Engine 클러스터를 관리하는 네트워크, 플랫폼, 보안 엔지니어가 포함됩니다. 여기에 설명된 제어 기능은 규정 준수를 입증해야 하는 조직에 특히 유용합니다(예를 들어 GDPRPCI).

목표

  • Cloud Service Mesh를 실행하기 위한 인프라 설정:
  • Cloud Service Mesh 설치
  • 전용 노드 풀에서 실행되는 이그레스 게이트웨이 프록시 설치
  • 이그레스 게이트웨이를 통해 외부 트래픽에 대한 멀티 테넌트 라우팅 규칙 구성:
    • team-x 네임스페이스의 애플리케이션이 example.com에 연결할 수 있습니다.
    • team-y 네임스페이스의 애플리케이션이 httpbin.org에 연결할 수 있습니다.
  • Sidecar 리소스를 사용하여 각 네임스페이스의 사이드카 프록시 이그레스 구성 범위 제한
  • 이그레스 규칙을 시행하도록 승인 정책 구성
  • 일반 HTTP 요청을 TLS(TLS 원본)로 업그레이드하도록 이그레스 게이트웨이 구성
  • TLS 트래픽을 통과하도록 이그레스 게이트웨이 구성
  • Kubernetes 네트워크 정책을 추가 이그레스 제어로 설정
  • 비공개 Google 액세스 및 Identity and Access Management(IAM) 권한을 사용하여 Google API에 대한 직접 액세스 구성

비용

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

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

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

시작하기 전에

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

    Go to project selector

  2. Make sure that billing is enabled for your Google Cloud project.

  3. In the Google Cloud console, activate Cloud Shell.

    Activate Cloud Shell

  4. 튜토리얼에 따라 사용할 작업 디렉터리를 만듭니다.

    mkdir -p ~/WORKING_DIRECTORY
    cd ~/WORKING_DIRECTORY
    
  5. 튜토리얼에 따라 셸 스크립트를 만들어 환경을 초기화합니다. 프로젝트 및 환경설정에 따라 변수를 바꾸고 수정합니다. 셸 세션이 만료되면 source 명령어로 이 스크립트를 실행하여 환경을 다시 초기화합니다.

    cat << 'EOF' > ./init-egress-tutorial.sh
    #! /usr/bin/env bash
    PROJECT_ID=YOUR_PROJECT_ID
    REGION=REGION
    ZONE=ZONE
    
    gcloud config set project ${PROJECT_ID}
    gcloud config set compute/region ${REGION}
    gcloud config set compute/zone ${ZONE}
    
    EOF
    
  6. compute.googleapis.com 사용 설정:

    gcloud services enable compute.googleapis.com --project=YOUR_PROJECT_ID
    
  7. 스크립트를 실행 가능하게 만들고 source 명령어와 함께 실행하여 환경을 초기화합니다. compute.googleapis.com을 사용 설정하라는 메시지가 표시되면 Y를 선택합니다.

    chmod +x ./init-egress-tutorial.sh
    source ./init-egress-tutorial.sh
    

인프라 설정

VPC 네트워크 및 서브넷 만들기

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

    gcloud compute networks create vpc-network \
        --subnet-mode custom
    
  2. pod 및 서비스에 사전 할당된 보조 IP 주소 범위를 사용하여 클러스터를 실행할 서브넷을 만듭니다. 내부 IP 주소만 있는 애플리케이션이 Google API 및 서비스에 연결할 수 있도록 비공개 Google 액세스가 사용 설정됩니다.

    gcloud compute networks subnets create subnet-gke \
        --network vpc-network \
        --range 10.0.0.0/24 \
        --secondary-range pods=10.1.0.0/16,services=10.2.0.0/20 \
        --enable-private-ip-google-access
    

Cloud NAT 구성

Cloud NAT를 사용하면 외부 IP 주소가 없는 워크로드를 인터넷의 대상에 연결하고 해당 대상에서 인바운드 응답을 수신할 수 있습니다.

  1. Cloud Router를 만듭니다.

    gcloud compute routers create nat-router \
        --network vpc-network
    
  2. 라우터에 NAT 구성을 추가합니다.

    gcloud compute routers nats create nat-config \
        --router nat-router \
        --nat-all-subnet-ip-ranges \
        --auto-allocate-nat-external-ips
    

각 GKE 노드 풀의 서비스 계정 만들기

2개의 GKE 노드 풀 두 개에서 사용할 서비스 계정 두 개를 만듭니다. 특정 노드에 VPC 방화벽 규칙을 적용할 수 있도록 각 노드 풀에 별도의 서비스 계정을 할당합니다.

  1. 기본 노드 풀의 노드에서 사용할 서비스 계정을 만듭니다.

    gcloud iam service-accounts create sa-application-nodes \
        --description="SA for application nodes" \
        --display-name="sa-application-nodes"
    
  2. 게이트웨이 노드 풀의 노드에서 사용할 서비스 계정을 만듭니다.

    gcloud iam service-accounts create sa-gateway-nodes \
        --description="SA for gateway nodes" \
        --display-name="sa-gateway-nodes"
    

서비스 계정에 권한 부여

애플리케이션 및 게이트웨이 서비스 계정에 최소한의 IAM 역할을 추가합니다. 이러한 역할은 Container Registry에서 비공개 컨테이너 이미지를 로깅, 모니터링, 가져오는 데 필요합니다.

    project_roles=(
        roles/logging.logWriter
        roles/monitoring.metricWriter
        roles/monitoring.viewer
        roles/storage.objectViewer
    )
    for role in "${project_roles[@]}"
    do
        gcloud projects add-iam-policy-binding ${PROJECT_ID} \
            --member="serviceAccount:sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
            --role="$role"
        gcloud projects add-iam-policy-binding ${PROJECT_ID} \
            --member="serviceAccount:sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
            --role="$role"
    done

방화벽 규칙 만들기

다음 단계에서는 기본적으로 모든 이그레스 트래픽이 거부되도록 VPC 네트워크에 방화벽 규칙을 적용합니다. 클러스터가 작동하고 게이트웨이 노드가 VPC 외부의 대상에 연결하려면 특정 연결이 필요합니다. 특정 방화벽 규칙 최소 집합은 필요한 연결을 허용하도록 기본 deny-all 규칙을 재정의합니다.

  1. VPC 네트워크의 모든 이그레스를 거부하는 기본(낮은 우선순위) 방화벽 규칙을 만듭니다.

    gcloud compute firewall-rules create global-deny-egress-all \
        --action DENY \
        --direction EGRESS \
        --rules all \
        --destination-ranges 0.0.0.0/0 \
        --network vpc-network \
        --priority 65535 \
        --description "Default rule to deny all egress from the network."
    
  2. 게이트웨이 서비스 계정이 있는 노드만 인터넷 연결을 허용하는 규칙을 만듭니다.

    gcloud compute firewall-rules create gateway-allow-egress-web \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:80,tcp:443 \
        --target-service-accounts sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --network vpc-network \
        --priority 1000 \
        --description "Allow the nodes running the egress gateways to connect to the web"
    
  3. 노드가 Kubernetes 컨트롤 플레인에 연결할 수 있도록 허용합니다.

    gcloud compute firewall-rules create allow-egress-to-api-server \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:443,tcp:10250 \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --destination-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow nodes to reach the Kubernetes API server."
    
  4. (선택사항) 관리형 Cloud Service Mesh를 사용하는 경우 이 방화벽 규칙은 필요하지 않습니다.

    Cloud Service Mesh는 사이드카 프록시를 워크로드에 삽입할 때 웹훅을 사용합니다. GKE API 서버가 노드에서 실행되는 서비스 메시 컨트롤 플레인에서 노출된 웹훅을 호출하도록 허용하세요.

    gcloud compute firewall-rules create allow-ingress-api-server-to-webhook \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:15017 \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --source-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow the API server to call the webhooks exposed by istiod discovery"
    
  5. 클러스터에서 실행되는 노드와 포드 간의 이그레스 연결을 허용합니다. GKE는 해당 인그레스 규칙을 자동으로 만듭니다. iptables 라우팅 체인이 항상 서비스 IP 주소를 포드 IP 주소로 변환하기 때문에 서비스 연결에 규칙이 필요하지 않습니다.

    gcloud compute firewall-rules create allow-egress-nodes-and-pods \
        --action ALLOW \
        --direction EGRESS \
        --rules all \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --destination-ranges 10.0.0.0/24,10.1.0.0/16 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow egress to other Nodes and Pods"
    
  6. 비공개 Google 액세스가 Google API, Container Registry, 기타 서비스를 제공하기 위해 사용하는 예약된 IP 주소 집합에 대한 액세스를 허용하세요.

    gcloud compute firewall-rules create allow-egress-gcp-apis \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --destination-ranges 199.36.153.8/30 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow access to the VIPs used by Google Cloud APIs (Private Google Access)"
    
  7. Google Cloud 상태 확인 서비스가 클러스터에서 실행 중인 포드에 액세스하도록 허용하세요. 자세한 내용은 상태 점검을 참조하세요.

    gcloud compute firewall-rules create allow-ingress-gcp-health-checker \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:80,tcp:443 \
        --target-service-accounts sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com,sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com \
        --source-ranges 35.191.0.0/16,130.211.0.0/22,209.85.152.0/22,209.85.204.0/22 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow workloads to respond to Google Cloud health checks"
    

Google Cloud API에 대한 비공개 액세스 구성

비공개 Google 액세스를 사용하면 내부 IP 주소만 있는 VM 및 pod가 Google API 및 서비스에 액세스할 수 있습니다. Google API 및 서비스는 외부 IP에서 제공되지만 노드의 트래픽은 비공개 Google 액세스를 사용할 때 Google 네트워크를 벗어나지 않습니다.

Cloud DNS API 사용 설정:

gcloud services enable dns.googleapis.com

노드 및 워크로드가 비공개 Google 액세스 및 private.googleapis.com 호스트 이름을 사용하여 Google API 및 서비스에 연결할 수 있도록 비공개 DNS 영역, CNAMEA 레코드를 만듭니다.

gcloud dns managed-zones create private-google-apis \
    --description "Private DNS zone for Google APIs" \
    --dns-name googleapis.com \
    --visibility private \
    --networks vpc-network

gcloud dns record-sets transaction start --zone private-google-apis

gcloud dns record-sets transaction add private.googleapis.com. \
    --name "*.googleapis.com" \
    --ttl 300 \
    --type CNAME \
    --zone private-google-apis

gcloud dns record-sets transaction add "199.36.153.8" \
"199.36.153.9" "199.36.153.10" "199.36.153.11" \
    --name private.googleapis.com \
    --ttl 300 \
    --type A \
    --zone private-google-apis

gcloud dns record-sets transaction execute --zone private-google-apis

Container Registry에 대한 비공개 액세스 구성

노드가 비공개 Google 액세스 및 gcr.io 호스트 이름을 사용하여 Container Registry에 연결할 수 있도록 비공개 DNS 영역, CNAME, A 레코드를 만듭니다.

gcloud dns managed-zones create private-gcr-io \
    --description "private zone for Container Registry" \
    --dns-name gcr.io \
    --visibility private \
    --networks vpc-network

gcloud dns record-sets transaction start --zone private-gcr-io

gcloud dns record-sets transaction add gcr.io. \
    --name "*.gcr.io" \
    --ttl 300 \
    --type CNAME \
    --zone private-gcr-io

gcloud dns record-sets transaction add "199.36.153.8" "199.36.153.9" "199.36.153.10" "199.36.153.11" \
    --name gcr.io \
    --ttl 300 \
    --type A \
    --zone private-gcr-io

gcloud dns record-sets transaction execute --zone private-gcr-io

비공개 GKE 클러스터 만들기

  1. 클러스터의 API 서버에 액세스하도록 허용된 네트워크 목록에 추가할 수 있도록 Cloud Shell의 외부 IP 주소를 찾습니다.

    SHELL_IP=$(dig TXT -4 +short @ns1.google.com o-o.myaddr.l.google.com)
    

    일정 기간 활동이 없으면 Cloud Shell VM의 외부 IP 주소가 변경될 수 있습니다. 이 경우 클러스터의 승인된 네트워크 목록을 업데이트해야 합니다. 다음 명령어를 초기화 스크립트에 추가합니다.

    cat << 'EOF' >> ./init-egress-tutorial.sh
    SHELL_IP=$(dig TXT -4 +short @ns1.google.com o-o.myaddr.l.google.com)
    gcloud container clusters update cluster1 \
        --enable-master-authorized-networks \
        --master-authorized-networks ${SHELL_IP//\"}/32
    EOF
    
  2. Google Kubernetes Engine API를 사용 설정합니다.

    gcloud services enable container.googleapis.com
    
  3. 비공개 GKE 클러스터를 만듭니다.

    gcloud container clusters create cluster1 \
        --enable-ip-alias \
        --enable-private-nodes \
        --release-channel "regular" \
        --enable-master-authorized-networks \
        --master-authorized-networks ${SHELL_IP//\"}/32 \
        --master-ipv4-cidr 10.5.0.0/28 \
        --enable-dataplane-v2 \
        --service-account "sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
        --machine-type "e2-standard-4" \
        --network "vpc-network" \
        --subnetwork "subnet-gke" \
        --cluster-secondary-range-name "pods" \
        --services-secondary-range-name "services" \
        --workload-pool "${PROJECT_ID}.svc.id.goog" \
        --zone ${ZONE}
    

    클러스터를 만드는 데 몇 분 정도 걸립니다. 클러스터에 내부 IP 주소가 있는 비공개 노드가 있습니다. VPC 서브넷을 만들 때 정의한 이름이 지정된 보조 범위의 IP가 pod 및 서비스에 할당됩니다.

    클러스터 내 컨트롤 플레인이 있는 Cloud Service Mesh에는 클러스터 노드에서 vCPU가 4개 이상인 머신 유형을 사용해야 합니다.

    노드가 Cloud Service Mesh에서 지원하는 Kubernetes 버전을 실행하도록 하려면 클러스터가 '일반' 출시 채널을 구독하는 것이 좋습니다.

    클러스터 내 컨트롤 플레인에서 Cloud Service Mesh를 실행하기 위한 기본 요건에 대한 자세한 내용은 클러스터 내 기본 요건을 참조하세요.

    관리형 Cloud Service Mesh 실행에 관한 요구사항 및 제한사항에 대한 자세한 내용은 관리형 Cloud Service Mesh 지원 기능을 참조하세요.

    GKE용 워크로드 아이덴티티 제휴는 클러스터에서 사용 설정됩니다. Cloud Service Mesh를 사용하려면 GKE용 워크로드 아이덴티티 제휴가 필요하며, 이는 GKE 워크로드에서 Google API에 액세스할 때 권장되는 방법입니다.

  4. gateway라는 노드 풀을 만듭니다. 이 노드 풀은 이그레스 게이트웨이가 배포되는 위치입니다. dedicated=gateway:NoSchedule taint가 게이트웨이 노드 풀의 모든 노드에 추가됩니다.

    gcloud container node-pools create "gateway" \
        --cluster "cluster1" \
        --machine-type "e2-standard-4" \
        --node-taints dedicated=gateway:NoSchedule \
        --service-account "sa-gateway-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
        --num-nodes "1"
    

    Kubernetes taint 및 톨러레이션(toleration)은 이그레스 노드 pod만 게이트웨이 노드 풀의 노드에서 실행되도록 보장합니다.

  5. kubectl을 사용하여 클러스터에 연결할 수 있도록 사용자 인증 정보를 다운로드하세요.

    gcloud container clusters get-credentials cluster1
    
  6. 게이트웨이 노드에 올바른 taint가 있는지 확인합니다.

    kubectl get nodes -l cloud.google.com/gke-nodepool=gateway -o yaml \
    -o=custom-columns='name:metadata.name,taints:spec.taints[?(@.key=="dedicated")]'
    

    출력은 다음과 비슷합니다.

    name                                 taints
    gke-cluster1-gateway-9d65b410-cffs   map[effect:NoSchedule key:dedicated value:gateway]
    

Cloud Service Mesh 설치 및 설정

Cloud Service Mesh의 설치 옵션 중 하나를 수행합니다.

Cloud Service Mesh를 설치한 후 인그레스 또는 이그레스 게이트웨이를 설치하지 않고 중지하고 이 튜토리얼로 돌아갑니다.

이그레스 게이트웨이 설치

  1. 이그레스 게이트웨이의 Kubernetes 네임스페이스를 만듭니다.

    kubectl create namespace istio-egress
    
  2. 네임스페이스의 삽입을 사용 설정합니다. 이 단계는 컨트롤 플레인 구현에 따라 다릅니다.

    관리형(TD)

    기본 삽입 라벨을 네임스페이스에 적용합니다.

    kubectl label namespace istio-egress \
        istio.io/rev- istio-injection=enabled --overwrite
    

    관리형(Istiod)

    권장: 다음 명령어를 실행하여 네임스페이스에 기본 삽입 라벨을 적용합니다.

      kubectl label namespace istio-egress \
          istio.io/rev- istio-injection=enabled --overwrite
    

    관리형 Istiod 컨트롤 플레인이 있는 기존 사용자: 기본 삽입을 사용하는 것이 좋지만 버전 기반 삽입은 지원됩니다. 다음 안내를 따르세요.

    1. 다음 명령어를 실행하여 사용 가능한 출시 채널을 찾습니다.

      kubectl -n istio-system get controlplanerevision
      

      출력은 다음과 비슷합니다.

      NAME                AGE
      asm-managed-rapid   6d7h
      

      출력에서 NAME 열 아래의 값은 Cloud Service Mesh 버전에 사용 가능한 출시 채널에 해당하는 버전 라벨입니다.

    2. 네임스페이스에 버전 라벨을 적용합니다.

      kubectl label namespace istio-egress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      

    클러스터 내

    권장: 다음 명령어를 실행하여 네임스페이스에 기본 삽입 라벨을 적용합니다.

      kubectl label namespace istio-egress \
          istio.io/rev- istio-injection=enabled --overwrite
    

    기본 삽입을 사용하는 것이 좋지만 버전 기반 삽입이 지원됩니다. 다음 안내를 따르세요.

    1. 다음 명령어를 사용하여 istiod에서 버전 라벨을 찾습니다.

      kubectl get deploy -n istio-system -l app=istiod -o \
         jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'
      
    2. 네임스페이스에 버전 라벨을 적용합니다. 다음 명령어에서 REVISION_LABEL은 이전 단계에서 확인한 istiod 버전 라벨의 값입니다.

      kubectl label namespace istio-egress \
          istio-injection- istio.io/rev=REVISION_LABEL --overwrite
      
  3. 이그레스 게이트웨이에 대해 연산자 매니페스트를 만듭니다.

    cat << EOF > egressgateway-operator.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: egressgateway-operator
      annotations:
        config.kubernetes.io/local-config: "true"
    spec:
      profile: empty
      revision: REVISION
      components:
        egressGateways:
        - name: istio-egressgateway
          namespace: istio-egress
          enabled: true
      values:
        gateways:
          istio-egressgateway:
            injectionTemplate: gateway
            tolerations:
              - key: "dedicated"
                operator: "Equal"
                value: "gateway"
            nodeSelector:
              cloud.google.com/gke-nodepool: "gateway"
    EOF
    
  4. istioctl 도구를 다운로드합니다. Cloud Service Mesh 버전 1.15 이하를 사용하는 경우에도 1.16.2-asm.2 이상 버전을 사용해야 합니다. 올바른 istioctl 버전 다운로드를 참조하세요.

  5. 다운로드한 보관 파일 추출한 후 istioctl 도구의 경로를 보존할 환경 변수를 설정하고 초기화 스크립트에 추가합니다.

    ISTIOCTL=$(find "$(pwd -P)" -name istioctl)
    echo "ISTIOCTL=\"${ISTIOCTL}\"" >> ./init-egress-tutorial.sh
    
  6. 연산자 매니페스트 및 istioctl을 사용하여 이그레스 게이트웨이 설치 매니페스트를 만듭니다.

    ${ISTIOCTL} manifest generate \
        --filename egressgateway-operator.yaml \
        --output egressgateway \
        --cluster-specific
    
  7. 이그레스 게이트웨이를 설치합니다.

    kubectl apply --recursive --filename egressgateway/
    
  8. 이그레스 게이트웨이가 gateway 노드 풀의 노드에서 실행 중인지 확인합니다.

    kubectl get pods -n istio-egress -o wide
    
  9. 이그레스 게이트웨이 포드에는 gateway 노드 풀의 노드에 대한 affinity 및 연결된 게이트웨이 노드에서 실행할 수 있는 톨러레이션(toleration)이 있습니다. 노드 어피니티 및 이그레스 게이트웨이 포드의 톨러레이션(toleration)을 검토합니다.

    kubectl -n istio-egress get pod -l istio=egressgateway \
        -o=custom-columns='name:metadata.name,node-affinity:spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms,tolerations:spec.tolerations[?(@.key=="dedicated")]'
    

    출력은 다음과 비슷합니다.

    name                                   node-affinity                                                                                   tolerations
    istio-egressgateway-754d9684d5-jjkdz   [map[matchExpressions:[map[key:cloud.google.com/gke-nodepool operator:In values:[gateway]]]]]   map[key:dedicated operator:Equal value:gateway]
    

Envoy 액세스 로깅 사용 설정

Envoy 액세스 로그를 사용 설정하는 데 필요한 단계는 관리형 또는 클러스터 내 Cloud Service Mesh 유형에 따라 다릅니다.

메시 및 테스트 애플리케이션 준비

  1. STRICT 상호 TLS가 사용 설정되어 있는지 확인합니다. istio-system 네임스페이스에 메시의 기본 PeerAuthentication 정책을 적용합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: "security.istio.io/v1beta1"
    kind: "PeerAuthentication"
    metadata:
      name: "default"
      namespace: "istio-system"
    spec:
      mtls:
        mode: STRICT
    EOF
    

    특정 네임스페이스에서 PeerAuthentication 리소스를 만들어 이 구성을 재정의할 수 있습니다.

  2. 테스트 워크로드를 배포하는 데 사용할 네임스페이스를 만듭니다. 이 튜토리얼의 이후 단계에서는 각 네임스페이스에 서로 다른 이그레스 라우팅 규칙을 구성하는 방법을 설명합니다.

    kubectl create namespace team-x
    kubectl create namespace team-y
    
  3. Kubernetes 네트워크 정책에서 선택할 수 있도록 네임스페이스에 라벨을 지정합니다.

    kubectl label namespace team-x team=x
    kubectl label namespace team-y team=y
    
  4. Cloud Service Mesh가 프록시 사이드카를 자동으로 삽입하도록 워크로드 네임스페이스에 컨트롤 플레인 버전 라벨을 설정합니다.

    kubectl label ns team-x istio.io/rev- istio-injection=enabled --overwrite
    kubectl label ns team-y istio.io/rev- istio-injection=enabled --overwrite
    
  5. 테스트 배포에 사용할 YAML 파일을 만듭니다.

    cat << 'EOF' > ./test.yaml
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      name: test
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: test
      labels:
        app: test
    spec:
      ports:
      - port: 80
        name: http
      selector:
        app: test
    ---
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: test
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: test
      template:
        metadata:
          labels:
            app: test
        spec:
          serviceAccountName: test
          containers:
          - name: test
            image: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
            command: ["/bin/sleep", "infinity"]
            imagePullPolicy: IfNotPresent
    EOF
    
  6. 테스트 애플리케이션을 team-x 네임스페이스에 배포합니다.

    kubectl -n team-x create -f ./test.yaml
    
  7. 테스트 애플리케이션이 기본 풀의 노드에 배포되었고 프록시 사이드카 컨테이너가 삽입되었는지 확인합니다. pod 상태가 Running이 될 때까지 다음 명령어를 반복합니다.

    kubectl -n team-x get po -l app=test -o wide
    

    출력은 다음과 비슷합니다.

    NAME                   READY   STATUS    RESTARTS   AGE   IP          NODE                                      NOMINATED NODE   READINESS GATES
    test-d5bdf6f4f-9nxfv   2/2     Running   0          19h   10.1.1.25   gke-cluster1-default-pool-f6c7a51f-wbzj
    

    컨테이너 2개 중 2개가 Running입니다. 한 컨테이너는 테스트 애플리케이션이고 다른 컨테이너는 프록시 사이드카입니다.

    pod가 기본 노드 풀의 노드에서 실행 중입니다.

  8. 테스트 컨테이너에서 외부 사이트로 HTTP 요청을 보낼 수 없는지 확인합니다.

    kubectl -n team-x exec -it \
        $(kubectl -n team-x get pod -l app=test -o jsonpath={.items..metadata.name}) \
        -c test -- curl -v http://example.com
    

    global-deny-egress-all 방화벽 규칙이 업스트림 연결을 거부하므로 사이드카 프록시에서 오류 메시지가 생성됩니다.

사이드카 리소스를 사용하여 사이드카 프록시 구성 범위 제한

사이드카 리소스를 사용하여 사이드카 프록시용으로 구성된 이그레스 리스너의 범위를 제한할 수 있습니다. 구성 블로트 및 메모리 사용량을 줄이려면 모든 네임스페이스에 기본 Sidecar 리소스를 적용하는 것이 좋습니다.

사이드카에서 Cloud Service Mesh가 실행하는 프록시는 Envoy입니다. Envoy 용어에서 cluster는 부하 분산의 대상으로 사용되는 논리적으로 유사한 업스트림 엔드포인트 그룹입니다.

  1. istioctl proxy-config 명령어를 실행하여 테스트 pod의 Envoy 사이드카 프록시에 구성된 아웃바운드 클러스터를 검사합니다.

    ${ISTIOCTL} pc c $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}).team-x --direction outbound
    

    목록에 이그레스 게이트웨이용의 일부 클러스터를 포함한 약 11개의 Envoy 클러스터가 있습니다.

  2. 프록시 구성을 이그레스 및 team-x 네임스페이스의 서비스 항목으로 명시적으로 정의된 이그레스 경로로 제한합니다. Sidecar 리소스를 team-x 네임스페이스에 적용합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Sidecar
    metadata:
      name: default
      namespace: team-x
    spec:
      outboundTrafficPolicy:
        mode: REGISTRY_ONLY
      egress:
      - hosts:
        - 'istio-egress/*'
        - 'team-x/*'
    EOF
    

    아웃바운드 트래픽 정책 모드를 REGISTRY_ONLY로 설정하면 서비스 항목을 정의함으로써 메시의 서비스 레지스트리에 명시적으로 추가된 외부 호스트만 포함하도록 프록시 구성이 제한됩니다.

    egress.hosts을 설정하면 사이드카 프록시가 exportTo 속성을 사용하여 사용할 수 있는 이그레스 네임스페이스에서만 경로를 선택하도록 지정합니다. 'team-x/*' 부분에는 team-x 네임스페이스에 로컬로 구성된 모든 경로가 포함됩니다.

  3. Envoy 사이드카 프록시에서 구성된 아웃바운드 클러스터를 열람하고 Sidecar 리소스를 적용하기 전에 구성된 클러스터 목록과 비교합니다.

    ${ISTIOCTL} pc c $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}).team-x --direction outbound
    

    이그레스 게이트웨이용 클러스터와 테스트 포드 자체에 대한 클러스터 한 개가 표시됩니다.

이그레스 게이트웨이를 통해 트래픽을 라우팅하도록 Cloud Service Mesh 구성

  1. 포트 80에서 HTTP 트래픽의 Gateway를 구성합니다. Gateway가 이그레스 네임스페이스에 배포한 이그레스 게이트웨이 프록시를 선택합니다. Gateway 구성이 이그레스 네임스페이스에 적용되고 모든 호스트의 트래픽을 처리합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: egress-gateway
      namespace: istio-egress
    spec:
      selector:
        istio: egressgateway
      servers:
      - port:
          number: 80
          name: https
          protocol: HTTPS
        hosts:
          - '*'
        tls:
          mode: ISTIO_MUTUAL
    EOF
    
  2. 인증 및 암호화용 상호 TLS를 사용하여 이그레스 게이트웨이의 DestinationRule을 만듭니다. 모든 외부 호스트에 단일 공유 대상 규칙을 사용합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: target-egress-gateway
      namespace: istio-egress
    spec:
      host: istio-egressgateway.istio-egress.svc.cluster.local
      subsets:
      - name: target-egress-gateway-mTLS
        trafficPolicy:
          tls:
            mode: ISTIO_MUTUAL
    EOF
    
  3. team-x 네임스페이스에 ServiceEntry를 만들어 이그레스 네임스페이스의 메시 서비스 레지스트리에 example.com을 명시적으로 등록합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: example-com-ext
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: example.com
    spec:
      hosts:
      - example.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'team-x'
      - 'istio-egress'
    EOF
    
  4. 이그레스 게이트웨이를 통해 트래픽을 example.com으로 라우팅하도록 VirtualService을 만듭니다. 두 가지 일치 조건이 있습니다. 첫 번째 조건은 이그레스 게이트웨이로 트래픽을 전달하고 두 번째 조건은 이그레스 게이트웨이에서 대상 호스트로 트래픽을 전달합니다. exportTo 속성은 가상 서비스를 사용할 수 있는 네임스페이스를 제어합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: example-com-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - example.com
      gateways:
      - istio-egress/egress-gateway
      - mesh
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
          weight: 100
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: example.com
            port:
              number: 80
          weight: 100
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  5. istioctl analyze를 실행하여 구성 오류를 확인하세요.

    ${ISTIOCTL} analyze -n istio-egress --revision REVISION
    

    출력은 다음과 비슷합니다.

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. 이그레스 게이트웨이를 통해 외부 사이트로 여러 요청을 보냅니다.

    for i in {1..4}
    do
        kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
            -o jsonpath={.items..metadata.name}) -c test -- \
        curl -s -o /dev/null -w "%{http_code}\n" http://example.com
    done
    

    4개의 응답 모두에 200 상태 코드가 표시됩니다.

  7. 프록시 액세스 로그를 확인하여 요청이 이그레스 게이트웨이를 통해 전달되었는지 확인합니다. 먼저 테스트 애플리케이션과 함께 배포된 프록시 사이드카의 액세스 로그를 확인합니다.

    kubectl -n team-x logs -f $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) istio-proxy
    

    보내는 요청마다 다음과 비슷한 로그 항목이 표시됩니다.

    [2020-09-14T17:37:08.045Z] "HEAD / HTTP/1.1" 200 - "-" "-" 0 0 5 4 "-" "curl/7.67.0" "d57ea5ad-90e9-46d9-8b55-8e6e404a8f9b" "example.com" "10.1.4.12:8080" outbound|80||istio-egressgateway.istio-egress.svc.cluster.local 10.1.0.17:42140 93.184.216.34:80 10.1.0.17:60326 - -
    
  8. 이그레스 게이트웨이 액세스 로그도 확인하세요.

    kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egressgateway \
        -o jsonpath="{.items[0].metadata.name}") istio-proxy
    

    보내는 요청마다 다음과 비슷한 이그레스 게이트웨이 액세스 로그 항목이 표시됩니다.

    [2020-09-14T17:37:08.045Z] "HEAD / HTTP/2" 200 - "-" "-" 0 0 4 3 "10.1.0.17" "curl/7.67.0" "095711e6-64ef-4de0-983e-59158e3c55e7" "example.com" "93.184.216.34:80" outbound|80||example.com 10.1.4.12:37636 10.1.4.12:8080 10.1.0.17:44404 outbound_.80_.target-egress-gateway-mTLS_.istio-egressgateway.istio-egress.svc.cluster.local -
    

두 번째 네임스페이스에 다른 라우팅 구성

두 번째 외부 호스트에 라우팅을 구성하여 여러 팀의 여러 외부 연결을 구성할 수 있습니다.

  1. team-y 네임스페이스용 Sidecar 리소스를 만듭니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Sidecar
    metadata:
      name: default
      namespace: team-y
    spec:
      outboundTrafficPolicy:
        mode: REGISTRY_ONLY
      egress:
      - hosts:
        - 'istio-egress/*'
        - 'team-y/*'
    EOF
    
  2. 테스트 애플리케이션을 team-y 네임스페이스에 배포합니다.

    kubectl -n team-y create -f ./test.yaml
    
  3. 두 번째 외부 호스트를 등록하고 team-xteam-y 네임스페이스로 내보냅니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: httpbin-org-ext
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: httpbin.org
    spec:
      hosts:
      - httpbin.org
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
      - 'team-y'
    EOF
    
  4. 이그레스 게이트웨이를 통해 트래픽을 httpbin.org로 라우팅하는 가상 서비스를 만듭니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: httpbin-org-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      gateways:
      - istio-egress/egress-gateway
      - mesh
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
          weight: 100
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: httpbin.org
            port:
              number: 80
          weight: 100
      exportTo:
      - 'istio-egress'
      - 'team-x'
      - 'team-y'
    EOF
    
  5. istioctl analyze를 실행하여 구성 오류를 확인하세요.

    ${ISTIOCTL} analyze -n istio-egress --revision REVISION
    

    다음과 같은 항목이 포함되어 있습니다.

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  6. team-y 테스트 앱에서 httpbin.org에 요청합니다.

    kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test -o \
        jsonpath={.items..metadata.name}) -c test -- curl -I http://httpbin.org
    

    200 OK 응답이 표시됩니다.

  7. 또한 team-x 테스트 앱에서 httpbin.org로 요청합니다.

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://httpbin.org
    

    200 OK 응답이 표시됩니다.

  8. team-y 네임스페이스에서 example.com에 요청을 시도하세요.

    kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    

    example.com 호스트에 구성된 아웃바운드 경로가 없으므로 요청이 실패합니다.

승인 정책을 사용하여 트래픽에 대한 추가 컨트롤 제공

이 튜토리얼에서는 이그레스 게이트웨이의 승인 정책이 istio-egress 네임스페이스에 생성됩니다. 네트워크 관리자만 istio-egress 네임스페이스에 액세스할 수 있도록 Kubernetes RBAC를 구성할 수 있습니다.

  1. AuthorizationPolicy를 만드세요. 그러면 team-x 네임스페이스의 애플리케이션은 example.com에 연결할 수 있지만 포트 80을 사용하여 요청을 보낼 때 다른 외부 호스트에 연결할 수 없습니다. 이그레스 게이트웨이 pod의 해당 targetPort는 8080입니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: egress-team-x-to-example-com
      namespace: istio-egress
    spec:
      action: ALLOW
      rules:
        - from:
          - source:
              namespaces:
              - 'team-x'
          to:
          - operation:
              hosts:
                - 'example.com'
          when:
          - key: destination.port
            values: ["8080"]
    EOF
    
  2. team-x 네임스페이스의 테스트 애플리케이션에서 example.com에 요청을 보낼 수 있는지 확인합니다.

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    

    200 OK 응답이 표시됩니다.

  3. team-x 네임스페이스의 테스트 애플리케이션에서 httpbin.org에 요청을 보내 봅니다.

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -s -w " %{http_code}\n" \
        http://httpbin.org
    

    RBAC: access denied 메시지 및 403 Forbidden 상태 코드와 함께 요청이 실패합니다. 승인 정책이 적용되기 전에 약간의 지연이 발생하는 경우가 있으므로 몇 초 정도 기다려야 할 수 있습니다.

  4. 승인 정책은 허용 또는 거부되는 트래픽을 세부적으로 제어합니다. 다음 승인 정책을 적용하여 team-y 네임스페이스의 테스트 앱이 포트 80을 사용하여 요청을 보낼 때 특정 URL 경로를 사용하여 httpbin.org에 요청하도록 허용합니다. 이그레스 게이트웨이 pod의 해당 targetPort는 8080입니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: egress-team-y-to-httpbin-teapot
      namespace: istio-egress
    spec:
      action: ALLOW
      rules:
        - from:
          - source:
              namespaces:
              - 'team-y'
          to:
          - operation:
              hosts:
              - httpbin.org
              paths: ['/status/418']
          when:
          - key: destination.port
            values: ["8080"]
    EOF
    
  5. team-y 네임스페이스의 테스트 앱에서 httpbin.org에 연결을 시도합니다.

    kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -s -w " %{http_code}\n" \
        http://httpbin.org
    

    RBAC: 액세스 거부 메시지 및 403 Forbidden 상태 코드와 함께 요청이 실패합니다.

  6. 이제 동일한 앱에서 httpbin.org/status/418에 요청을 보냅니다.

    kubectl -n team-y exec -it $(kubectl -n team-y get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl http://httpbin.org/status/418
    

    경로가 승인 정책의 패턴과 일치하므로 요청이 성공합니다. 출력은 다음과 비슷합니다.

       -=[ teapot ]=-
          _...._
        .'  _ _ `.
       | ."` ^ `". _,
       \_;`"---"`|//
         |       ;/
         \_     _/
           `"""`
    

이그레스 게이트웨이에서 TLS 시작

일반 HTTP 요청을 TLS 또는 상호 TLS에 대한 upgrade(원본)로 이그레스 게이트웨이를 구성할 수 있습니다. 애플리케이션이 일반 HTTP 요청을 하도록 허용하면 Istio 상호 TLS와 TLS 시작과 함께 사용할 때 여러 가지 이점이 있습니다. 자세한 내용은 권장사항 튜토리얼을 참조하세요.

이그레스 게이트웨이에서 TLS 시작

  1. DestinationRule. The DestinationRule을 만드세요. 이는 게이트웨이로부터 example.com에 TLS 연결이 시작되도록 지정합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: DestinationRule
    metadata:
      name: example-com-originate-tls
      namespace: istio-egress
    spec:
      host: example.com
      subsets:
        - name: example-com-originate-TLS
          trafficPolicy:
            portLevelSettings:
            - port:
                number: 443
              tls:
                mode: SIMPLE
                sni: example.com
    EOF
    
  2. 게이트웨이의 포트 80에 대한 요청이 대상 호스트로 전송될 때 포트 443에서 TLS로 upgraded되도록 example.com의 가상 서비스를 업데이트합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: example-com-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - example.com
      gateways:
      - mesh
      - istio-egress/egress-gateway
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: example.com
            port:
              number: 443
            subset: example-com-originate-TLS
          weight: 100
    EOF
    
  3. team-x 네임스페이스의 테스트 앱에서 example.com에 대한 여러 요청을 전송합니다.

    for i in {1..4}
    do
        kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
            -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    done
    

    전과 마찬가지로 요청은 200 OK 응답과 함께 성공합니다.

  4. 이그레스 게이트웨이 로그에서 게이트웨이가 발신 TLS 연결을 통해 요청을 대상 호스트로 라우팅했는지 확인하세요.

    kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egressgateway \
        -o jsonpath="    {.items[0].metadata.name}") istio-proxy
    

    출력은 다음과 비슷합니다.

    [2020-09-24T17:58:02.548Z] "HEAD / HTTP/2" 200 - "-" "-" 0 0 6 5 "10.1.1.15" "curl/7.67.0" "83a77acb-d994-424d-83da-dd8eac902dc8" "example.com" "93.184.216.34:443" outbound|443|example-com-originate-TLS|example.com 10.1.4.31:49866 10.1.4.31:8080 10.1.1.15:37334 outbound_.80_.target-egress-gateway-mTLS_.istio-egressgateway.istio-egress.svc.cluster.local -
    

    프록시 사이드카가 포트 80을 사용하여 게이트웨이로 요청을 보냈고, 포트 443에서 TLS가 시작되어 대상 호스트로 요청이 전송되었습니다.

HTTPS/TLS 연결 패스 스루

기존 애플리케이션은 이미 외부 서비스와 통신할 때 TLS 연결을 사용하고 있을 수 있습니다. TLS 연결을 복호화하지 않고 전달하도록 이그레스 게이트웨이를 구성할 수 있습니다.

tls 패스스루

  1. 이그레스 게이트웨이가 포트 443에 연결을 위해 TLS 패스스루를 사용하도록 구성을 수정합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: egress-gateway
      namespace: istio-egress
    spec:
      selector:
        istio: egressgateway
      servers:
      - port:
          number: 80
          name: https
          protocol: HTTPS
        hosts:
          - '*'
        tls:
          mode: ISTIO_MUTUAL
      - port:
          number: 443
          name: tls
          protocol: TLS
        hosts:
        - '*'
        tls:
          mode: PASSTHROUGH
    EOF
    
  2. 이그레스 게이트웨이를 가리키는 DestinationRule을 업데이트하여 게이트웨이에 포트 443의 두 번째 하위 집합을 추가합니다. 이 새 하위 집합은 상호 TLS를 사용하지 않습니다. TLS 연결의 패스스루에는 Istio 상호 TLS가 지원되지 않습니다. 포트 80의 연결에서는 mTLS를 계속 사용합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: target-egress-gateway
      namespace: istio-egress
    spec:
      host: istio-egressgateway.istio-egress.svc.cluster.local
      subsets:
      - name: target-egress-gateway-mTLS
        trafficPolicy:
          portLevelSettings:
          - port:
              number: 80
            tls:
              mode: ISTIO_MUTUAL
      - name: target-egress-gateway-TLS-passthrough
    EOF
    
  3. 포트 443의 TLS 트래픽이 게이트웨이를 패스스루하도록 example.com의 가상 서비스를 업데이트하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: example-com-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - example.com
      gateways:
      - mesh
      - istio-egress/egress-gateway
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: example.com
            port:
              number: 443
            subset: example-com-originate-TLS
          weight: 100
      tls:
      - match:
        - gateways:
          - mesh
          port: 443
          sniHosts:
          - example.com
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-TLS-passthrough
            port:
              number: 443
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 443
          sniHosts:
          - example.com
        route:
        - destination:
            host: example.com
            port:
              number: 443
          weight: 100
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  4. 포트 443의 TLS 트래픽이 게이트웨이를 패스스루하도록 httpbin.org의 가상 서비스를 업데이트하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: VirtualService
    metadata:
      name: httpbin-org-through-egress-gateway
      namespace: istio-egress
    spec:
      hosts:
      - httpbin.org
      gateways:
      - istio-egress/egress-gateway
      - mesh
      http:
      - match:
        - gateways:
          - mesh
          port: 80
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-mTLS
            port:
              number: 80
          weight: 100
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 80
        route:
        - destination:
            host: httpbin.org
            port:
              number: 80
          weight: 100
      tls:
      - match:
        - gateways:
          - mesh
          port: 443
          sniHosts:
          - httpbin.org
        route:
        - destination:
            host: istio-egressgateway.istio-egress.svc.cluster.local
            subset: target-egress-gateway-TLS-passthrough
            port:
              number: 443
      - match:
        - gateways:
          - istio-egress/egress-gateway
          port: 443
          sniHosts:
          - httpbin.org
        route:
        - destination:
            host: httpbin.org
            port:
              number: 443
          weight: 100
      exportTo:
      - 'istio-egress'
      - 'team-x'
      - 'team-y'
    EOF
    
  5. 이그레스 게이트웨이 서비스의 포트 443으로 전송된 모든 종류의 트래픽을 허용하는 승인 정책을 추가합니다. 게이트웨이 pod의 해당 targetPort는 8443입니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: egress-all-443
      namespace: istio-egress
    spec:
      action: ALLOW
      rules:
        - when:
          - key: destination.port
            values: ["8443"]
    EOF
    
  6. istioctl analyze를 실행하여 구성 오류를 확인하세요.

    ${ISTIOCTL} analyze -n istio-egress --revision REVISION
    

    다음과 같은 항목이 포함되어 있습니다.

    ✔ No validation issues found when analyzing namespace: istio-egress.
    
  7. team-x 네임스페이스의 테스트 애플리케이션에서 example.com으로 일반 HTTP 요청을 보냅니다.

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    

    200 OK 응답과 함께 요청이 성공합니다.

  8. 이제 team-x 네임스페이스의 테스트 애플리케이션에서 여러 TLS(HTTPS) 요청을 수행합니다.

    for i in {1..4}
    do
        kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
            -o jsonpath={.items..metadata.name}) -c test -- curl -s -o /dev/null \
            -w "%{http_code}\n" \
            https://example.com
    done
    

    200개의 응답이 표시됩니다.

  9. 이그레스 게이트웨이 로그를 다시 조사합니다.

    kubectl -n istio-egress logs -f $(kubectl -n istio-egress get pod -l istio=egressgateway \
        -o jsonpath="{.items[0].metadata.name}") istio-proxy
    

    다음과 비슷한 로그 항목이 표시됩니다.

    [2020-09-24T18:04:38.608Z] "- - -" 0 - "-" "-" 1363 5539 10 - "-" "-" "-" "-" "93.184.216.34:443" outbound|443||example.com 10.1.4.31:51098 10.1.4.31:8443 10.1.1.15:57030 example.com -
    

    HTTPS 요청이 TCP 트래픽으로 취급되어 게이트웨이를 통해 대상 호스트로 전달되었으므로 HTTP 정보가 로그에 포함되지 않습니다.

Kubernetes NetworkPolicy를 추가 컨트롤로 사용

애플리케이션이 사이드카 프록시를 우회할 수 있는 시나리오는 여러 가지가 있습니다. Kubernetes NetworkPolicy를 사용하여 허용되는 연결 워크로드를 추가로 지정할 수 있습니다. 단일 네트워크 정책이 적용되면 명시적으로 허용되지 않은 모든 연결이 거부됩니다.

이 튜토리얼에서는 네트워크 정책의 이그레스 연결과 이그레스 선택기만 고려합니다. 자체 클러스터에서 네트워크 정책으로 인그레스를 제어하는 경우 이그레스 정책에 해당하는 인그레스 정책을 만들어야 합니다. 예를 들어 team-x 네임스페이스의 워크로드에서 team-y 네임스페이스로의 이그레스를 허용하는 경우 team-x 네임스페이스에서 team-y 네임스페이스로의 인그레스도 허용해야 합니다.

  1. team-x 네임스페이스에 배포된 워크로드 및 프록시가 istiod 및 이그레스 게이트웨이에 연결하도록 허용하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-control-plane
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - namespaceSelector:
            matchLabels:
              "kubernetes.io/metadata.name": istio-system
          podSelector:
            matchLabels:
              istio: istiod
        - namespaceSelector:
            matchLabels:
              "kubernetes.io/metadata.name": istio-egress
          podSelector:
            matchLabels:
              istio: egressgateway
    EOF
    
  2. 워크로드 및 프록시에서 DNS를 쿼리할 수 있도록 허용하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-dns
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - namespaceSelector:
            matchLabels:
              "kubernetes.io/metadata.name": kube-system
        ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP
    EOF
    
  3. 워크로드 및 프록시가 Cloud Service Mesh 인증 기관을 포함한 Google API 및 서비스를 제공하는 IP에 연결하도록 허용하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-google-apis
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - ipBlock:
            cidr: 199.36.153.4/30
        - ipBlock:
            cidr: 199.36.153.8/30
    EOF
    
  4. 워크로드 및 프록시가 GKE 메타데이터 서버에 연결하도록 허용하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-metadata-server
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to: # For GKE data plane v2
        - ipBlock:
            cidr: 169.254.169.254/32
      - to: # For GKE data plane v1
        - ipBlock:
            cidr: 127.0.0.1/32 # Prior to 1.21.0-gke.1000
        - ipBlock:
            cidr: 169.254.169.252/32 # 1.21.0-gke.1000 and later
        ports:
        - protocol: TCP
          port: 987
        - protocol: TCP
          port: 988
    EOF
    
  5. 선택사항: team-x 네임스페이스의 워크로드와 프록시가 서로 연결하도록 허용하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-same-namespace
      namespace: team-x
    spec:
      podSelector: {}
      ingress:
        - from:
          - podSelector: {}
      egress:
        - to:
          - podSelector: {}
    EOF
    
  6. 선택사항: team-x 네임스페이스의 워크로드와 프록시가 다른 팀에서 배포한 워크로드에 연결할 수 있도록 허용하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-egress-to-team-y
      namespace: team-x
    spec:
      podSelector: {}
      policyTypes:
        - Egress
      egress:
      - to:
        - namespaceSelector:
            matchLabels:
              "kubernetes.io/metadata.name": team-y
    EOF
    
  7. 사이드카 프록시 간의 연결이 유지됩니다. 새 네트워크 정책을 적용해도 기존 연결은 종료되지 않습니다. team-x 네임스페이스에서 워크로드를 다시 시작하여 기존 연결이 종료되었는지 확인합니다.

    kubectl -n team-x rollout restart deployment
    
  8. team-x 네임스페이스의 테스트 애플리케이션에서 example.com에 HTTP 요청을 계속할 수 있는지 확인하세요.

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- curl -I http://example.com
    

    200 OK 응답과 함께 요청이 성공합니다.

비공개 Google 액세스 및 IAM 권한을 사용하여 Google API에 직접 액세스

Google의 API 및 서비스는 외부 IP 주소를 사용하여 노출됩니다. VPC 기반 별칭 IP 주소가 있는 pod가 비공개 Google 액세스를 사용하여 Google API에 연결되면 트래픽이 Google 네트워크에서 벗어나지 않습니다.

이 튜토리얼의 인프라를 설정할 때 GKE pod에서 사용하는 서브넷에 비공개 Google 액세스를 사용 설정했습니다. 비공개 Google 액세스에서 사용되는 IP 주소에 대한 액세스를 허용하기 위해 경로, VPC 방화벽 규칙, 비공개 DNS 영역을 만들었습니다. 이 구성을 사용하면 이그레스 게이트웨이를 통해 트래픽을 전송하지 않고도 pod가 Google API에 직접 도달할 수 있습니다. GKE용 워크로드 아이덴티티 제휴 및 IAM을 사용하여 특정 Kubernetes 서비스 계정 및 네임스페이스에서 사용할 수 있는 API를 제어할 수 있습니다. 이그레스 게이트웨이가 Google API에 대한 연결을 처리하지 않기 때문에 Istio 승인이 적용되지 않습니다.

포드가 Google API를 호출할 수 있도록 하려면 먼저 IAM을 사용하여 권한을 부여해야 합니다. 이 튜토리얼에서 사용하는 클러스터는 GKE용 워크로드 아이덴티티 제휴를 사용하도록 구성되었으므로 Kubernetes 서비스 계정이 Google 서비스 계정으로 작동할 수 있습니다.

  1. 애플리케이션이 사용할 Google 서비스 계정을 만드세요.

    gcloud iam service-accounts create sa-test-app-team-x
    
  2. Kubernetes 서비스 계정이 Google 서비스 계정을 가장하도록 허용하세요.

    gcloud iam service-accounts add-iam-policy-binding \
      --role roles/iam.workloadIdentityUser \
      --member "serviceAccount:${PROJECT_ID}.svc.id.goog[team-x/test]" \
      sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com
    
  3. team-x 네임스페이스에서 테스트 앱의 Kubernetes 서비스 계정을 Google 서비스 계정의 이메일 주소로 주석 처리하세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: v1
    kind: ServiceAccount
    metadata:
      annotations:
        iam.gke.io/gcp-service-account: sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com
      name: test
      namespace: team-x
    EOF
    
  4. 테스트 애플리케이션 pod는 Google API 호출을 위한 임시 사용자 인증 정보를 얻기 위해 DaemonSet로 실행되는 Google 메타데이터 서버에 액세스할 수 있어야 합니다. GKE 메타데이터 서버의 서비스 항목을 만듭니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: metadata-google-internal
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: metadata.google.internal
    spec:
      hosts:
      - metadata.google.internal
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  5. 또한 private.googleapis.com 및 storage.googleapis.com에 대한 서비스 항목을 만드세요.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: private-googleapis-com
      namespace: istio-egress
      labels:
        # Show this service and its telemetry in the Cloud Service Mesh page of the Google Cloud console
        service.istio.io/canonical-name: googleapis.com
    spec:
      hosts:
      - private.googleapis.com
      - storage.googleapis.com
      ports:
      - number: 80
        name: http
        protocol: HTTP
      - number: 443
        name: tls
        protocol: TLS
      resolution: DNS
      location: MESH_EXTERNAL
      exportTo:
      - 'istio-egress'
      - 'team-x'
    EOF
    
  6. Kubernetes 서비스 계정이 Google 서비스 계정 역할을 하도록 올바르게 구성되었는지 확인합니다.

    kubectl -n team-x exec -it $(kubectl -n team-x get pod -l app=test \
        -o jsonpath={.items..metadata.name}) -c test -- gcloud auth list
    

    Google 서비스 계정이 유일한 활성 ID로 표시됩니다.

  7. Cloud Storage 버킷에 테스트 파일을 만듭니다.

    echo "Hello, World!" > /tmp/hello
    gcloud storage buckets create gs://${PROJECT_ID}-bucket
    gcloud storage cp /tmp/hello gs://${PROJECT_ID}-bucket/
    
  8. 서비스 계정에 버킷의 파일을 나열하고 볼 수 있는 권한을 부여하세요.

    gcloud storage buckets add-iam-policy-binding gs://${PROJECT_ID}-bucket/ \
        --member=serviceAccount:sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com \
        --role=roles/storage.objectViewer
    
  9. 테스트 애플리케이션이 테스트 버킷에 액세스할 수 있는지 확인합니다.

    kubectl -n team-x exec -it \
    $(kubectl -n team-x get pod -l app=test -o jsonpath={.items..metadata.name}) \
    -c test \
    -- gcloud storage cat gs://${PROJECT_ID}-bucket/hello
    

    다음과 같은 항목이 포함되어 있습니다.

    Hello, World!
    

삭제

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

이 튜토리얼에서 사용한 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 다음 섹션의 절차를 완료하세요.

프로젝트 삭제

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

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

다음 단계