Cloud Service Mesh에서 프록시 문제 해결

이 문서에서는 일반적인 Cloud Service Mesh 문제와 해결 방법을 설명합니다. 추가 지원이 필요하면 지원 받기를 참조하세요.

Istio를 사용하여 엔드포인트에 연결할 때 연결이 거부됨

애플리케이션 워크로드가 연결해야 하는 Memorystore Redis, CloudSQL 또는 외부 서비스와 같이 클러스터에서 엔드포인트로 통신할 때 갑자기 연결 거부(ECONNREFUSED) 오류가 발생할 수 있습니다.

이 문제는 애플리케이션 워크로드가 istio-proxy(Envoy) 컨테이너보다 빠르게 시작되고 외부 엔드포인트에 연결하려고 시도할 때 발생할 수 있습니다. 이 단계에서는 istio-init(initContainer)가 이미 실행되었기 때문에 iptables 규칙에 따라 모든 송신 트래픽이 Envoy로 리디렉션됩니다. istio-proxy가 아직 준비되지 않았기 때문에 iptables 규칙이 아직 시작되지 않은 사이드카 프록시로 트래픽을 리디렉션하고, 따라서 애플리케이션에 ECONNREFUSED 오류가 표시됩니다.

다음 단계에서는 발생한 문제가 이 오류에 해당하는지 확인하는 방법을 설명합니다.

  1. 다음 필터로 Stackdriver 로그를 확인해서 문제가 발생한 포드를 식별합니다.

    다음 예시는 일반적인 오류 메시지를 보여줍니다.

    Error: failed to create connection to feature-store redis, err=dial tcp   192.168.9.16:19209: connect: connection refused
    [ioredis] Unhandled error event: Error: connect ECONNREFUSED
    
  2. 문제가 발생한 항목을 검색합니다. 기존 Stackdriver를 사용 중이면 resource.type="container"를 사용합니다.

    resource.type="k8s_container"
    textPayload:"$ERROR_MESSAGE$"
    
  3. 최신 항목을 확장해서 포드 이름을 확인하고 resource.labels 아래의 pod_name을 기록해 둡니다.

  4. 해당 포드에서 발생한 첫 번째 문제 항목을 확인합니다.

    resource.type="k8s_container"
    resource.labels.pod_name="$POD_NAME$"
    

    출력 예시:

    E 2020-03-31T10:41:15.552128897Z
    post-feature-service post-feature-service-v1-67d56cdd-g7fvb failed to create
    connection to feature-store redis, err=dial tcp 192.168.9.16:19209: connect:
    connection refused post-feature-service post-feature-service-v1-67d56cdd-g7fvb
    
  5. 이 포드에서 첫 번째 오류가 발생한 타임스탬프를 기록해 둡니다.

  6. 다음 필터를 사용해서 포드 시작 이벤트를 확인합니다.

    resource.type="k8s_container"
    resource.labels.pod_name="$POD_NAME$"
    

    출력 예시:

    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Container image "docker.io/istio/proxyv2:1.3.3" already present on machine  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Created container  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{istio-proxy} Started container  spec.containers{istio-proxy}
    I 2020-03-31T10:41:15Z spec.containers{APP-CONTAINER-NAME} Created container  spec.containers{APP-CONTAINER-NAME}
    W 2020-03-31T10:41:17Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:26Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:28Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:31Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    W 2020-03-31T10:41:58Z spec.containers{istio-proxy} Readiness probe failed: HTTP probe failed with statuscode: 503  spec.containers{istio-proxy}
    
  7. 오류 및 istio-proxy 시작 이벤트의 타임스탬프를 사용해서 Envoy가 준비되지 않았을 때 오류가 발생하는지 확인합니다.

    istio-proxy 컨테이너가 아직 준비되지 않았을 때 오류가 발생한 경우 연결 거부 오류가 발생하는 것이 일반적입니다. 앞의 예시에서는 2020-03-31T10:41:15.552128897Z가 되는 즉시 포드가 Redis에 연결하려고 시도하지만 2020-03-31T10:41:58Z까지 istio-proxy에서 프로브 준비가 실패했습니다.

    istio-proxy 컨테이너가 먼저 시작되었지만 앱이 외부 엔드포인트에 연결을 시도하기 전에 충분히 빠르게 준비되지 않았을 수 있습니다.

    이 문제에 해당하는 경우에는 다음 문제 해결 단계를 수행합니다.

  8. 포드 레벨에서 구성을 주석 처리합니다. 이것은 포드 레벨에서만 사용할 수 있고 전역 레벨에서는 사용할 수 없습니다.

    annotations:
    proxy.istio.io/config: '{ "holdApplicationUntilProxyStarts": true }'
    
  9. 외부 서비스를 요청하기 전에 Envoy가 준비되었는지 확인하도록 애플리케이션 코드를 수정합니다. 예를 들어 애플리케이션 시작 시 istio-proxy 상태 엔드포인트에 요청을 수행하고 200이 회신된 다음에만 작업을 계속하는 루프를 시작합니다. istio-proxy 상태 엔드포인트는 다음과 같습니다.

    http://localhost:15020/healthz/ready
    

Vault와 istio 사이의 사이드카 삽입 중 경합 상태

보안 비밀 관리를 위해 vault를 사용할 때 일부 경우에는 vaultistio 앞에 사이드카를 삽입하여 포드가 Init 상태로 중단될 수 있습니다. 이 문제가 발생하면 배포를 다시 시작하거나 새 항목을 배포한 후 생성된 포드가 Init 상태로 잠깁니다. 예를 들면 다음과 같습니다.

E 2020-03-31T10:41:15.552128897Z
post-feature-service post-feature-service-v1-67d56cdd-g7fvb failed to create
connection to feature-store redis, err=dial tcp 192.168.9.16:19209: connect:
connection refused post-feature-service post-feature-service-v1-67d56cdd-g7fvb

이 문제는 경합 상태에서 발생합니다. Istio와 vault가 모두 사이드카를 삽입하지만 Istio가 마지막으로 삽입해야 합니다. istio 프록시는 init 컨테이너 중에 실행되지 않습니다. istio init 컨테이너는 모든 트래픽을 프록시로 리디렉션하는 iptables 규칙을 설정합니다. 아직 실행 중이 아니므로 이러한 규칙에 따라 리디렉션이 수행되지 않고 모든 트래픽이 차단됩니다. 따라서 init 컨테이너가 마지막 컨테이너여야 하며, 따라서 iptables 규칙이 설정된 직후 프록시가 작동되고 실행됩니다. 하지만 이러한 순서가 결정적이지 않기 때문에 Istio가 먼저 삽입되면 문제가 발생합니다.

이 문제를 해결하기 위해서는 Vault IP로 이동하는 트래픽이 아직 준비되지 않은 Envoy 프록시로 리디렉션되지 않도록 해서 통신을 방해하지 않도록 vault IP 주소를 허용해야 합니다. 이렇게 하려면 excludeOutboundIPRanges라는 새 주석을 추가해야 합니다.

관리형 Cloud Service Mesh의 경우에는 spec.template.metadata.annotations 아래의 배포 또는 포드 수준에서만 사용할 수 있으며 예를 들면 다음과 같습니다.

apiVersion: apps/v1
kind: Deployment
...
...
...
spec:
  template:
    metadata:
      annotations:
        traffic.sidecar.istio.io/excludeOutboundIPRanges:

클러스터 내 Cloud Service Mesh의 경우에는 spec.values.global.proxy.excludeIPRanges 아래에서 IstioOperator를 사용하여 전역 항목으로 설정하는 옵션이 있으며 예를 들면 다음과 같습니다.

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  values:
    global:
      proxy:
        excludeIPRanges: ""

주석을 추가한 후에는 워크로드를 다시 시작합니다.