버전 1.8

GKE 클러스터에서 Anthos Service Mesh 이그레스 게이트웨이 사용: 가이드

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

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

목표

  • Anthos Service Mesh를 실행하기 위한 인프라 설정하기:
  • 전용 노드 풀에서 실행되는 이그레스 게이트웨이로 Anthos 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. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기 페이지로 이동

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

  3. Cloud Console에서 Cloud Shell을 활성화합니다.

    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. 스크립트를 실행 가능하게 만들고 source 명령어로 실행하여 환경을 초기화합니다.

    chmod +x ./init-egress-tutorial.sh
    source ./init-egress-tutorial.sh
    
  7. 필요한 Identity and Access Management(IAM) 역할을 설정합니다. 프로젝트 소유자인 경우 설치를 완료하는 데 필요한 모든 권한을 보유합니다. 프로젝트 소유자가 아닌 경우 관리자에게 다음 IAM 역할을 부여해 달라고 요청하세요. 다음 명령어에서 PROJECT_EMAIL_ADDRESS를 Google Cloud에 로그인하는 데 사용하는 계정으로 변경합니다.

    gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member user:PROJECT_EMAIL_ADDRESS \
        --role=roles/editor \
        --role=roles/compute.admin \
        --role=roles/container.admin \
        --role=roles/resourcemanager.projectIamAdmin \
        --role=roles/iam.serviceAccountAdmin \
        --role=roles/iam.serviceAccountKeyAdmin \
        --role=roles/gkehub.admin \
        --role=roles/serviceusage.serviceUsageAdmin
    
  8. 가이드에 필요한 API를 사용 설정합니다.

    gcloud services enable \
        dns.googleapis.com \
        container.googleapis.com \
        compute.googleapis.com \
        monitoring.googleapis.com \
        logging.googleapis.com \
        cloudtrace.googleapis.com \
        meshca.googleapis.com \
        meshtelemetry.googleapis.com \
        meshconfig.googleapis.com \
        iamcredentials.googleapis.com \
        gkeconnect.googleapis.com \
        gkehub.googleapis.com \
        cloudresourcemanager.googleapis.com \
        stackdriver.googleapis.com
    

    API를 사용 설정을 완료하는 데 1분 이상 걸릴 수 있습니다. API가 사용 설정되면 다음과 비슷한 출력이 표시됩니다.

    Operation "operations/acf.601db672-88e6-4f98-8ceb-aa3b5725533c" finished
    successfully.
    

인프라 설정

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

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

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

    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 노드 풀에서 사용할 서비스 계정 2개를 만듭니다. 특정 노드에 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에서 비공개 컨테이너 이미지를 로깅, 모니터링, pull 하는 데 필요합니다.

    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 외부의 대상에 도달할 수 있으려면 특정 연결이 필요합니다. 특정 방화벽 규칙 최소 집합은 필요한 연결을 허용하도록 기본 모두 거부 규칙을 재정의합니다.

  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 \
        --destination-ranges 10.5.0.0/28 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow nodes to reach the Kubernetes API server."
    
  4. Anthos Service Mesh는 사이드카 프록시를 작업 부하에 삽입할 때 웹훅을 사용합니다. GKE API 서버가 노드에서 실행되는 서비스 메시 제어 영역에서 노출된 웹훅을 호출하도록 허용하세요.

    gcloud compute firewall-rules create allow-ingress-api-server-to-webhook \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:15017 \
        --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. 클러스터에서 실행되는 Pod와 서비스 간의 이그레스 연결을 허용하세요. 참고로 GKE는 해당 인그레스 규칙을 자동으로 만듭니다.

    gcloud compute firewall-rules create allow-egress-pods-and-services \
        --action ALLOW \
        --direction EGRESS \
        --rules all \
        --destination-ranges 10.1.0.0/16,10.2.0.0/20 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow pods and services on nodes to reach each other"
    
  6. Calico라는 서비스는 GKE용 NetworkPolicy API 기능을 제공합니다. 서브넷에서 Calico에 연결을 허용하세요.

    gcloud compute firewall-rules create allow-egress-calico \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:5473 \
        --destination-ranges 10.0.0.0/24 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow Calico Typha within the subnet"
    
  7. GKE가 노드 측정항목을 읽으려면 kubelet 읽기 전용 포트가 필요합니다. 서브넷 내에서 여기에 대한 액세스를 허용하세요.

    gcloud compute firewall-rules create allow-egress-kubelet-readonly \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp:10255 \
        --destination-ranges 10.0.0.0/24 \
        --network vpc-network \
        --priority 1000 \
        --description "Allow access to the kubelet read-only port within the subnet"
    
  8. 비공개 Google 액세스가 Google API, Container Registry, 기타 서비스를 제공하기 위해 사용하는 예약된 IP 주소 집합에 대한 액세스를 허용하세요.

    gcloud compute firewall-rules create allow-egress-gcp-apis \
        --action ALLOW \
        --direction EGRESS \
        --rules tcp \
        --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)"
    
  9. Google Cloud 상태 확인 서비스가 클러스터에서 실행 중인 pod에 액세스하도록 허용하세요.

    gcloud compute firewall-rules create allow-ingress-gcp-health-checker \
        --action ALLOW \
        --direction INGRESS \
        --rules tcp:80,tcp:443 \
        --source-ranges 130.211.0.0/22,35.191.0.0/16,35.191.0.0/16,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 네트워크를 벗어나지 않습니다.

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

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. 비공개 GKE 클러스터를 만듭니다.

    gcloud container clusters create cluster1 \
        --enable-ip-alias \
        --enable-private-nodes \
        --release-channel "regular" \
        --no-enable-basic-auth \
        --no-issue-client-certificate \
        --enable-master-authorized-networks \
        --master-authorized-networks ${SHELL_IP//\"}/32 \
        --master-ipv4-cidr 10.5.0.0/28 \
        --enable-network-policy \
        --service-account "sa-application-nodes@${PROJECT_ID}.iam.gserviceaccount.com" \
        --machine-type "e2-standard-4" \
        --num-nodes "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 주소가 있는 비공개 노드가 있습니다. Pod 및 서비스에는 VPC 서브넷을 만들 때 정의한 명명된 보조 범위 IP가 할당됩니다.

    Anthos Service Mesh는 vCPU가 4개 이상인 머신 유형을 사용하는 클러스터 노드가 필요합니다. 노드가 Anthos Service Mesh 지원 Kubernetes 버전을 실행할 수 있도록 클러스터가 '일반' 출시 채널을 구독하는 것이 좋습니다. 자세한 내용은 Anthos Service Mesh 설치 가이드를 참조하세요.

    워크로드 아이덴티티가 클러스터에서 사용 설정되었습니다. Anthos Service Mesh는 워크로드 아이덴티티가 필요하며, 이는 GKE 워크로드에서 Google API에 액세스하는 데 권장되는 방법입니다.

  3. 게이트웨이라는 노드 풀을 만드세요. 이 노드 풀은 이그레스 게이트웨이가 배포되는 위치입니다. 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만 게이트웨이 노드 풀의 노드에서 실행되도록 보장합니다.

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

    gcloud container clusters get-credentials cluster1
    
  5. 게이트웨이 노드에 올바른 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]
    

Anthos Service Mesh 설치 및 설정

이 가이드에서는 Anthos Service Mesh의 선택적 기능을 사용합니다. 스크립트를 사용하여 Anthos Service Mesh를 설치하는 방법은 이 문서의 설치 가이드를 참조하세요.

  1. 서비스 메시 제어 영역과 네임스페이스를 배포할 배포 게이트웨이의 네임스페이스를 만듭니다.

    kubectl create ns istio-system
    kubectl create ns istio-egress
    
  2. istio-egress, istio-system, kube-system 네임스페이스에 라벨을 지정합니다.

    kubectl label ns istio-egress istio=egress istio-injection=disabled
    kubectl label ns istio-system istio=system
    kubectl label ns kube-system kube-system=true
    

    이러한 라벨은 나중에 Kubernetes NetworkPolicy를 적용하는 데 사용됩니다. istio-injection=disabled 라벨은 istioctl 분석을 실행할 때 가짜 경고를 방지합니다.

  3. Istio OperatorAPI를 사용하여 Anthos Service Mesh 설치를 맞춤설정하는 매니페스트 파일을 만듭니다.

    cat << 'EOF' > ./asm-custom-install.yaml
    apiVersion: install.istio.io/v1alpha1
    kind: IstioOperator
    metadata:
      name: "egress-gateway"
    spec:
      meshConfig:
        accessLogFile: "/dev/stdout"
      components:
        egressGateways:
          - name: "istio-egressgateway"
            enabled: true
            namespace: "istio-egress"
            label:
              istio: "egress"
            k8s:
              tolerations:
              - key: "dedicated"
                operator: "Equal"
                value: "gateway"
              nodeSelector:
                cloud.google.com/gke-nodepool: "gateway"
    EOF
    

    이 파일은 설치 스크립트의 인수로 제공되며 다음 구성을 지정합니다.

    • gateway 노드에서만 실행되도록 톨러레이션(toleration) 및 nodeSelector를 사용하여 istio-egress 네임스페이스에서 실행되는 이그레스 게이트웨이 배포
    • 모든 사이드카 프록시의 'stdout'에 대한 액세스 로깅
  4. 설치 스크립트를 다운로드합니다.

    curl -O https://storage.googleapis.com/csm-artifacts/asm/install_asm
    
  5. 파일의 SHA-256 서명을 작업 디렉터리에 다운로드합니다.

    curl -O https://storage.googleapis.com/csm-artifacts/asm/install_asm.sha256
    
  6. 같은 디렉터리에서 두 파일의 다운로드 파일의 유효성을 검증합니다.

    sha256sum -c --ignore-missing install_asm.sha256
    

    검증이 성공하면 출력은 다음과 같습니다.

    install_asm: OK
    
  7. 스크립트를 실행 가능하게 만듭니다.

    chmod +x install_asm
    
  8. 스크립트를 실행하여 Anthos Service Mesh를 설치합니다.

    ./install_asm \
        --mode install \
        --project_id ${PROJECT_ID} \
        --cluster_name cluster1 \
        --cluster_location ${ZONE} \
        --custom_overlay ./asm-custom-install.yaml \
        --output_dir ./ \
        --enable_all
    
  9. 스크립트가 완료되면 istioctl 도구의 경로를 저장할 환경 변수를 설정하고 초기화 스크립트에 추가합니다.

    ISTIOCTL=$(find "$(pwd -P)" -name istioctl)
    echo "ISTIOCTL=\"${ISTIOCTL}\"" >> ./init-egress-tutorial.sh
    

Anthos Service Mesh 설치 확인

  1. Anthos Service Mesh 제어 영역 구성요소가 istio-system 네임스페이스에서 실행 중인지 확인합니다.

    kubectl get pod -n istio-system
    

    istio-ingressgatewayistiod-asm pod가 실행 중입니다.

  2. istio-egress 네임스페이스와 gateway 노드 풀의 노드에서 이그레스 게이트웨이 Pod가 실행 중인지 확인합니다.

    kubectl get pods -n istio-egress -o wide
    
  3. 이그레스 게이트웨이 pod에는 gateway 노드 풀의 노드를 선택할 수 있는 nodeSelector와 연결된 게이트웨이 노드에서 실행할 수 있는 톨러레이션이 있습니다. nodeSelector 및 이그레스 게이트웨이 pod의 톨러레이션을 검토하세요.

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

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

    name                                   nodeSelector                                 tolerations
    istio-egressgateway-74687946f5-dg9mp   map[cloud.google.com/gke-nodepool:gateway]   map[key:dedicated operator:Equal value:gateway]
    

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

  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. Anthos Service Mesh가 프록시 사이드카를 자동으로 삽입하려면 워크로드 네임스페이스에 버전 라벨을 설정해야 합니다. 버전 라벨은 클러스터에 배포된 Anthos Service Mesh 제어 영역의 버전과 일치해야 합니다. istiod Pod에서 버전 라벨을 찾아 환경 변수에 저장합니다.

    REVISION_LABEL=$(kubectl get pod -n istio-system -l app=istiod \
      -o jsonpath='{.items[0].metadata.labels.istio\.io/rev}')
    
  5. team-xteam-y 네임스페이스에서 버전 라벨을 설정합니다.

    kubectl label ns team-x istio.io/rev=${REVISION_LABEL}
    kubectl label ns team-y istio.io/rev=${REVISION_LABEL}
    
  6. 테스트 배포에 사용할 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
    
  7. 테스트 애플리케이션을 team-x 네임스페이스에 배포합니다.

    kubectl -n team-x create -f ./test.yaml
    
  8. 테스트 애플리케이션이 기본 풀의 노드에 배포되고 프록시 사이드카 컨테이너가 삽입되었는지 확인합니다. 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가 기본 노드 풀의 노드에서 실행 중입니다.

  9. 테스트 컨테이너에서 외부 사이트로의 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 리소스를 적용하는 것이 좋습니다.

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

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

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

    목록에 20개의 Envoy 클러스터가 있으며 여기에는 이그레스 게이트웨이용 여러 개가 포함됩니다.

  2. 프록시 구성을 istio-egressteam-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로 설정하면 서비스 항목을 정의함으로써 메시의 서비스 레지스트리에 명시적으로 추가된 외부 호스트만 포함하도록 프록시 구성이 제한됩니다.

    'istio-egress/*' 부분은 사이드카 프록시가 exportTo 속성을 사용하여 사용 가능한 istio-egress 네임스페이스에서 경로를 선택하도록 지정합니다. '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
    

    출력에는 이그레스 게이트웨이용 테스트 클러스터와 테스트 pod 자체에 대한 클러스터만 포함됩니다.

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

  1. 포트 80에서 HTTP 트래픽에 대해 Gateway를 구성하세요. Gateway는 설치 프로그램이 istio-egress 네임스페이스에 배포된 istio-egressgateway 프록시를 선택합니다. Gateway 구성은 istio-egress 네임스페이스에 적용되고 모든 호스트의 트래픽을 처리합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: egress-gateway
      namespace: istio-egress
    spec:
      selector:
        istio: egress
      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:
          loadBalancer:
            simple: ROUND_ROBIN
          tls:
            mode: ISTIO_MUTUAL
    EOF
    
  3. istio-egress 네임스페이스에 ServiceEntry를 만들어 team-x 네임스페이스의 메시 서비스 레지스트리에 example.com을 명시적으로 등록합니다.

    cat <<EOF | kubectl apply -f -
    apiVersion: networking.istio.io/v1beta1
    kind: ServiceEntry
    metadata:
      name: example-com-ext
      namespace: istio-egress
    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. VirtualService를 만들어 이그레스 게이트웨이를 통해 example.com으로 트래픽을 라우팅하세요. 두 가지 일치 조건이 있습니다. 첫 번째 조건은 트래픽을 이그레스 게이트웨이로 보내고 두 번째 조건은 이그레스 게이트웨이에서 대상 호스트로 트래픽을 인도합니다. 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
    

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

    ✔ 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=egress \
        -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
    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
    

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

    ✔ 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:
      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:
      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 금지됨 상태 코드가 표시됩니다.

  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로 '업그레이드'하도록 이그레스 게이트웨이를 구성할 수 있습니다. 애플리케이션이 일반 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:
            loadBalancer:
              simple: ROUND_ROBIN
            portLevelSettings:
            - port:
                number: 443
              tls:
                mode: SIMPLE
                sni: example.com
    EOF
    
  2. example.com의 가상 서비스를 업데이트합니다. 게이트웨이의 포트 80에 대한 요청이 대상 호스트로 전송될 때 포트 443에서 TLS로 '업그레이드'됩니다.

    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=egress \
        -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 -
    

    프록시 사이드카가 포트 443에서 발신되는 포트 80 및 TLS를 사용하여 요청을 게이트웨이에 전송했습니다.

HTTPS/TLS 연결 패스 스루

외부 애플리케이션과 통신할 때 기존 애플리케이션에서 이미 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: egress
      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를 사용하지 않습니다. 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:
          loadBalancer:
            simple: ROUND_ROBIN
          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:
      rules:
        - when:
          - key: destination.port
            values: ["8443"]
    EOF
    
  6. istioctl analyze를 실행하여 구성 오류를 확인하세요.

    ${ISTIOCTL} analyze -n istio-egress
    

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

    ✔ 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=egress \
        -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:
              istio: system
          podSelector:
            matchLabels:
              istio: istiod
        - namespaceSelector:
            matchLabels:
              istio: egress
          podSelector:
            matchLabels:
              istio: egress
    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:
              kube-system: "true"
        ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP
    EOF
    
  3. 워크로드 및 프록시가 Mesh CA를 포함한 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
        ports:
        - 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:
              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에 직접 연결할 수 있습니다. 워크로드 아이덴티티 및 IAM을 사용하여 특정 Kubernetes 서비스 계정 및 네임스페이스에서 사용할 수 있는 API를 제어할 수 있습니다. 이그레스 게이트웨이가 Google API에 대한 연결을 처리하지 않으므로 Istio 승인은 적용되지 않습니다.

pod가 Google API를 호출하려면 먼저 IAM을 사용하여 권한을 부여해야 합니다. 이 가이드에서 사용 중인 클러스터는 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
    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
    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
    gsutil mb gs://${PROJECT_ID}-bucket
    gsutil cp /tmp/hello gs://${PROJECT_ID}-bucket/
    
  8. 서비스 계정에 버킷의 파일을 나열하고 볼 수 있는 권한을 부여하세요.

    gsutil iam ch \
    serviceAccount:sa-test-app-team-x@${PROJECT_ID}.iam.gserviceaccount.com:objectViewer \
        gs://${PROJECT_ID}-bucket/
    
  9. 테스트 애플리케이션이 테스트 버킷에 액세스할 수 있는지 확인합니다.

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

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

    Hello, World!
    

삭제

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

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

프로젝트 삭제

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

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

    리소스 관리로 이동

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

다음 단계