Cloud Run for Anthos on Google Cloud 문제 해결

이 페이지에서는 몇 가지 일반적인 오류의 해결 방법과 문제 해결 전략을 제공합니다.

Cloud Run for Anthos on Google Cloud 문제를 해결할 때는 먼저 컨테이너 이미지를 로컬에서 실행할 수 있는지 확인합니다.

애플리케이션이 로컬에서 실행되고 있지 않으면 진단 후 수정해야 합니다. 배포된 프로젝트를 디버깅하려면 Cloud Logging을 사용하세요.

Cloud Run for Anthos on Google Cloud의 문제 해결이 필요한 경우 다음 섹션을 참조하여 문제를 해결할 수 있습니다.

명령줄 결과 확인

gcloud 명령줄을 사용하는 경우 명령어 결과에서 명령어의 성공 여부를 확인합니다. 예를 들어 배포가 종료에 실패하면 실패 이유를 설명하는 오류 메시지가 표시됩니다.

배포 실패는 잘못 구성된 매니페스트 또는 잘못된 명령어로 인해 발생했을 가능성이 큽니다. 예를 들어 다음 출력에 따르면 경로 트래픽 비율 합계가 100이 되도록 구성해야 합니다.

Error from server (InternalError): error when applying patch:</p><pre>{"metadata":{"annotations":{"kubectl.kubernetes.io/last-applied-configuration":"{\"apiVersion\":\"serving.knative.dev/v11\",\"kind\":\"Route\",\"metadata\":{\"annotations\":{},\"name\":\"route-example\",\"namespace\":\"default\"},\"spec\":{\"traffic\":[{\"configurationName\":\"configuration-example\",\"percent\":50}]}}\n"}},"spec":{"traffic":[{"configurationName":"configuration-example","percent":50}]}}
to:
&{0xc421d98240 0xc421e77490 default route-example STDIN 0xc421db0488 264682 false}
for: "STDIN": Internal error occurred: admission webhook "webhook.knative.dev" denied the request: mutation failed: The route must have traffic percent sum equal to 100.
ERROR: Non-zero return code '1' from command: Process exited with status 1

서비스 로그 확인

Cloud Console의 Cloud Logging 또는 Cloud Run 페이지에서 요청 로그 및 컨테이너 로그를 확인할 수 있습니다. 자세한 내용은 로깅 및 로그 보기를 참조하세요.

Cloud Logging을 사용하는 경우 필터링해야 하는 리소스는 Kubernetes 컨테이너입니다.

경로 상태 확인

다음 명령어를 실행하여 애플리케이션을 배포할 때 사용한 경로 객체의 상태를 가져옵니다.

kubectl get route ROUTE

status의 조건은 실패 이유를 설명합니다.

Istio 라우팅 확인

경로 상태를 확인하면서 얻은 Istio 경로 객체의 구성을 Istio RouteRule 객체의 구성과 비교합니다.

다음을 입력하되 ROUTERULE-NAME을 적절한 값으로 바꿉니다.

kubectl get routerule ROUTERULE-NAME -o yaml

경로 규칙의 이름을 모르는 경우 kubectl get routerule을 사용하여 규칙을 찾습니다.

이 명령어는 경로 규칙의 구성을 반환합니다. 경로와 경로 규칙의 도메인을 서로 비교합니다. 이 두 도메인은 일치해야 합니다.

버전 상태 확인

구성에서 경로를 구성하는 경우 다음 명령어를 실행하여 배포용으로 만든 버전의 이름을 가져오고 경로의 .yaml 파일에서 구성 이름을 찾습니다.

kubectl get configuration CONFIGURATION-NAME -o jsonpath="{.status.latestCreatedRevisionName}"

경로를 직접 버전으로 구성하는 경우 경로 yaml 파일에서 버전 이름을 찾으세요.

그런 후 다음을 실행합니다.

kubectl get revision REVISION-NAME -o yaml

준비된 버전의 조건은 다음과 같아야 합니다.

conditions:
  - reason: ServiceReady
    status: "True"
    type: Ready

이 조건이 표시되면 다음을 확인하여 디버깅을 계속합니다.

  • Pod 상태 확인
  • 애플리케이션 로그 확인
  • Istio 라우팅 확인

다른 조건이 표시되면 추가로 디버깅합니다.

Pod 상태 확인

모든 배포의 Pod를 가져오려면 다음 명령어를 실행하세요.

kubectl get pods

이렇게 하면 모든 Pod의 상태가 간략하게 나열됩니다. 예를 들면 다음과 같습니다.

NAME                                                      READY     STATUS             RESTARTS   AGE
configuration-example-00001-deployment-659747ff99-9bvr4   2/2       Running            0          3h
configuration-example-00002-deployment-5f475b7849-gxcht   1/2       CrashLoopBackOff   2          36s

이러한 상태를 자세히 알아보려면 다음 명령어를 사용하세요. 몇 가지 유용한 필드는 조건 및 containerStatuses입니다.

kubectl get pod POD-NAME -o yaml

외부 IP가 장시간 <pending> 상태임

클러스터를 만든 직후 외부 IP 주소를 가져올 수 없는 경우 외부 IP가 pending으로 표시될 수 있습니다. 예를 들어 다음 명령어를 호출하여 이 문제를 확인할 수 있습니다.

Istio 인그레스 게이트웨이의 외부 IP를 가져오려면 다음 안내를 따르세요.

kubectl get svc ISTIO-GATEWAY -n NAMESPACE 
다음과 같이 ISTIO-GATEWAYNAMESPACE를 바꿉니다.
클러스터 버전 ISTIO-GATEWAY NAMESPACE
1.15.3-gke.19 이상
1.14.3-gke.12 이상
1.13.10-gke.8 이상
istio-ingress gke-system
다른 모든 버전 istio-ingressgateway istio-system

여기서 결과 출력은 다음과 같이 표시됩니다.

NAME            TYPE           CLUSTER-IP     EXTERNAL-IP  PORT(S)
ISTIO-GATEWAY    LoadBalancer   XX.XX.XXX.XX   pending     80:32380/TCP,443:32390/TCP,32400:32400/TCP

부하 분산기의 EXTERNAL-IP는 반드시 사용해야 하는 IP 주소입니다.

즉, Google Cloud의 외부 IP 주소 할당량을 모두 사용했을 수 있습니다. 다음을 호출하여 가능한 원인을 확인할 수 있습니다.

kubectl describe svc ISTIO-GATEWAY -n NAMESPACE

ISTIO-GATEWAYNAMESPACE를 위의 표에 나온 값으로 바꿉니다. 이렇게 하면 다음과 비슷한 출력이 표시됩니다.

stem
Name:                     ISTIO-GATEWAY
Namespace:                NAMESPACE
Labels:                   addonmanager.kubernetes.io/mode=Reconcile
                          app=ISTIO-GATEWAY
                          chart=gateways-1.0.3
                          heritage=Tiller
                          istio=ingressgateway
                          k8s-app=istio
                          kubernetes.io/cluster-service=true
                          release=istio
Annotations:              kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"labels":{"addonmanager.kubernetes.io/mode":"Reconcile","app":"istio-ingressgateway","...
Selector:                 app=ISTIO-GATEWAY,istio=ingressgateway
Type:                     LoadBalancer
IP:                       10.XX.XXX.XXX
LoadBalancer Ingress:     35.XXX.XXX.188
Port:                     http2  80/TCP
TargetPort:               80/TCP
NodePort:                 http2  31380/TCP
Endpoints:                XX.XX.1.6:80
Port:                     https  443/TCP
TargetPort:               443/TCP
NodePort:                 https  3XXX0/TCP
Endpoints:                XX.XX.1.6:XXX
Port:                     tcp  31400/TCP
TargetPort:               3XX00/TCP
NodePort:                 tcp  3XX00/TCP
Endpoints:                XX.XX.1.6:XXXXX
Port:                     tcp-pilot-grpc-tls  15011/TCP
TargetPort:               15011/TCP
NodePort:                 tcp-pilot-grpc-tls  32201/TCP
Endpoints:                XX.XX.1.6:XXXXX
Port:                     tcp-citadel-grpc-tls  8060/TCP
TargetPort:               8060/TCP
NodePort:                 tcp-citadel-grpc-tls  31187/TCP
Endpoints:                XX.XX.1.6:XXXX
Port:                     tcp-dns-tls  853/TCP
TargetPort:               XXX/TCP
NodePort:                 tcp-dns-tls  31219/TCP
Endpoints:                10.52.1.6:853
Port:                     http2-prometheus  15030/TCP
TargetPort:               XXXXX/TCP
NodePort:                 http2-prometheus  30944/TCP
Endpoints:                10.52.1.6:15030
Port:                     http2-grafana  15031/TCP
TargetPort:               XXXXX/TCP
NodePort:                 http2-grafana  31497/TCP
Endpoints:                XX.XX.1.6:XXXXX
Session Affinity:         None
External Traffic Policy:  Cluster
Events:
  Type    Reason                Age                  From                Message
  ----    ------                ----                 ----                -------
  Normal  EnsuringLoadBalancer  7s (x4318 over 15d)  service-controller  Ensuring load balancer

출력에 IN_USE_ADDRESSES 할당량이 초과되었다는 표시가 있으면 Google Cloud Console의 IAM 및 관리자 페이지로 이동하여 추가 할당량을 요청할 수 있습니다.

게이트웨이는 외부 IP 주소가 할당될 때까지 계속 시도합니다. 몇 분 정도 걸릴 수 있습니다.

관리형 TLS 문제 해결

아래의 문제 해결 단계를 사용하여 관리형 TLS 인증서 기능의 일반적인 문제를 해결하세요.

특정 도메인 매핑의 상태 확인

특정 도메인 매핑의 상태를 확인하려면 다음 안내를 따르세요.

  1. 다음 명령어를 실행합니다.

    kubectl get domainMapping DOMAIN -n NAMESPACE -oyaml

    다음과 같이 바꿉니다.

    • DOMAIN을 사용 중인 도메인 이름으로 바꿉니다.
    • NAMESPACE를 도메인 매핑에 사용하는 네임스페이스로 바꿉니다.
  2. 이 명령어의 yaml 결과에서 CertificateProvisioned 필드의 조건을 검사하여 오류의 본질을 파악할 수 있습니다.

  3. 표시된 오류가 있다면 아래 표에 나와 있는 오류 중 하나와 일치해야 합니다. 표에 나와 있는 제안사항에 따라 문제를 해결하세요.

사용자 구성 오류

오류 코드 세부 메시지 문제 해결 안내
DNSErrored DNS 레코드가 올바르게 구성되지 않았습니다. [XXX] 도메인을 IP XX.XX.XX.XX에 매핑해야 합니다. 제공된 안내에 따라 DNS 레코드를 올바르게 구성합니다.
RateLimitExceeded acme: urn:ietf:params:acme:error:rateLimited: 새 주문을 만드는 중에 오류가 발생했습니다.

:: 이미 같은 도메인 집합에 대해 발급된 인증서가 너무 많습니다.

test.example.com:

https://letsencrypt.org/docs/rate-limits/를 참조하세요.

해당 호스트의 인증서 할당량을 늘리려면 Let's Encrypt에 문의하세요.
InvalidDomainMappingName DomainMapping 이름 %s은 경로 URL 호스트 %s와 같을 수 없습니다. DomainMapping 이름은 매핑되는 경로의 호스트와 정확히 같을 수 없습니다. DomainMapping 이름에 다른 도메인을 사용하세요.
ChallengeServingErrored 시스템에서 HTTP01 요청을 제공할 수 없습니다. 이 오류는 istio-ingress 서비스가 도메인 소유권 검증을 위해 Let's Encrypt에서 요청을 제공할 수 없는 경우에 발생할 수 있습니다.
  1. Virtual Private Cloud를 사용하지 않고 공개 인터넷에서 istio-ingress 서비스에 액세스할 수 있는지 확인합니다.
  2. istio-ingress 서비스가 URL http://DOMAIN/.well-known/acme-challenge/...의 요청을 허용하는지 확인합니다. 여기서 DOMAIN은 유효성 검사 중인 도메인입니다.

시스템 오류

오류 코드 세부 메시지 문제 해결 안내
OrderErrored

AuthzErrored

ChallengeErrored

이러한 3가지 유형의 오류는 Let's Encrypt에서 도메인 소유권 확인이 실패하는 경우 발생합니다.

이러한 오류는 일반적으로 일시적 오류이며 Cloud Run에서 다시 시도합니다.

재시도 지연 시간은 최소 8초에서 최대 8시간까지 차이가 큽니다.

오류를 수동으로 재시도하려면 실패한 주문을 수동으로 삭제하면 됩니다.

kubectl delete order DOMAIN -n NAMESPACE

ACMEAPIFailed 이러한 유형의 오류는 Cloud Run이 Let's Encrypt 호출에 실패할 경우에 발생합니다. 일반적으로 일시적 오류이며 Cloud Run에서 재시도합니다.

오류를 수동으로 재시도하려면 실패한 주문을 수동으로 삭제하세요.

kubectl delete order DOMAIN -n NAMESPACE

UnknownErrored 이 오류는 알 수 없는 시스템 오류를 표시하며 이는 GKE 클러스터에서 매우 드물게 발생합니다. 이러한 오류가 표시되는 경우 Cloud 지원팀에 문의하여 디버깅에 대한 지원을 받으세요.

주문 상태 확인

주문 상태는 Let's Encrypt와 상호작용하는 과정을 기록하므로 Let's Encrypt와 관련된 문제를 디버깅하는 데 사용할 수 있습니다. 필요한 경우 다음 명령어를 실행하여 주문 상태를 확인합니다.

kubectl get order DOMAIN -n NAMESPACE -oyaml

다음과 같이 바꿉니다.

  • DOMAIN을 사용 중인 도메인 이름으로 바꿉니다.
  • NAMESPACE를 도메인 매핑에 사용하는 네임스페이스로 바꿉니다.

주문이 성공하면 결과에는 발급된 인증서 및 기타 정보가 포함됩니다.

Let's Encrypt 할당량 초과

DomainMapping 상태를 확인합니다. Let's Encrypt 할당량을 초과하는 경우 DomainMapping에 다음과 같은 오류 메시지가 표시됩니다.

acme: urn:ietf:params:acme:error:rateLimited: Error creating new order
:: too many certificates already issued for exact set of domains:
test.example.com:
see https://letsencrypt.org/docs/rate-limits/'

인증서 할당량을 늘리려면 비율 제한에 대한 Let's Encrypt 문서를 참조하세요.

주문 시간 초과

계속 인증서를 가져올 수 없으면 주문 객체는 20분 후에 만료됩니다.

  1. 도메인 매핑 상태를 확인합니다. 시간 초과의 경우 상태 출력에서 다음과 같은 오류 메시지를 찾습니다.

    order (test.example.com) timed out (20.0 minutes)
  2. 시간 초과 문제의 일반적인 원인은 사용 중인 도메인을 gke-systemistio-ingress 서비스 IP 주소에 매핑하도록 DNS 레코드가 올바르게 구성되지 않았기 때문입니다. 다음 명령어를 실행하여 DNS 레코드를 확인합니다.

    host DOMAIN
  3. 다음 명령어를 실행하여 gke-system에서 istio-ingress 서비스의 외부 IP 주소를 확인합니다.

    kubectl get svc istio-ingress -n gke-system

    도메인의 외부 IP 주소가 인그레스 IP 주소와 일치하지 않는 경우 올바른 IP 주소에 매핑되도록 DNS 레코드를 다시 구성합니다.

  4. (업데이트된) DNS 레코드가 적용되면 다음 명령어를 실행하여 주문 객체를 삭제하고 TLS 인증서 요청 프로세스를 다시 트리거합니다.

    kubectl delete order DOMAIN -n NAMESPACE

    다음과 같이 바꿉니다.

    • DOMAIN을 사용 중인 도메인 이름으로 바꿉니다.
    • NAMESPACE를 사용된 네임스페이스로 바꿉니다.

승인 실패

승인 실패는 DNS 레코드가 적시에 전역적으로 전파되지 않을 때 발생할 수 있습니다. 따라서 Let's Encrypt는 도메인 소유권을 확인할 수 없습니다.

  1. 주문 상태를 확인합니다. 상태의 acmeAuthorizations 필드에서 승인 링크를 찾습니다. URL은 다음과 같이 표시됩니다.

    https://acme-v02.api.letsencrypt.org/acme/authz-v3/1717011827
  2. 링크를 엽니다. 다음과 비슷한 메시지가 표시됩니다.

    urn:ietf:params:acme:error:dns

    이 경우 문제의 원인은 불완전한 DNS 전파입니다.

  3. DNS 전파 오류를 해결하려면 다음 안내를 따르세요.

    1. 다음 명령어를 실행하여 gke-system에서 istio-ingress 서비스의 외부 IP를 가져옵니다.
      kubectl get svc istio-ingress -n gke-system
    2. 다음 명령어를 실행하여 도메인에 대한 DNS 레코드를 확인합니다.

      host DOMAIN

      DNS 레코드의 IP 주소가 gke-system에서 istio-ingress 서비스의 외부 IP와 일치하지 않으면 사용자의 도메인을 외부 IP에 매핑하도록 DNS 레코드를 구성합니다.

    3. (업데이트된) DNS 레코드가 적용되면 다음 명령어를 실행하여 주문 객체를 삭제하고 TLS 인증서 요청 프로세스를 다시 트리거합니다.

      kubectl delete order DOMAIN -n NAMESPACE

    다음과 같이 바꿉니다.

    • DOMAIN을 사용 중인 도메인 이름으로 바꿉니다.
    • NAMESPACE를 도메인 매핑에 사용하는 네임스페이스로 바꿉니다.

비공개 클러스터 배포 실패: 웹훅 오류 호출 실패

다음 메시지와 함께 비공개 클러스터에 대한 배포가 실패한 경우 방화벽이 제대로 설정되지 않았을 수 있습니다.

ERROR: (gcloud.run.deploy) Error: failed calling webhook "webhook.serving.knative.dev": Post
https://webhook.knative-serving.svc:443/?timeout=30s: context deadline exceeded (Client.Timeout
exceeded while awaiting headers)

비공개 클러스터에 대한 배포를 지원하는 데 필요한 방화벽 변경사항에 대한 자세한 내용은 비공개 클러스터에서 배포 사용 설정을 참조하세요.

IngressNotConfigured의 서비스 보고서 상태

서비스 상태로 IngressNotConfigured가 표시되면 gke-system 네임스페이스에서 istio-pilot 배포를 다시 시작해야 할 수 있습니다. kubernetes 1.14에서 자주 발생했었던 이 오류는 istio_pilotVirtualServices를 조정하고 인그레스 게이트웨이로 envoy 구성을 푸시하기 시작하기 전에 서비스가 생성된 경우에 발생할 수 있습니다.

이 문제를 해결하려면 배포를 수평 축소한 후 다음과 비슷한 명령어를 사용하여 다시 수평 확장하세요.

kubectl scale deployment istio-pilot -n gke-system --replicas=0
kubectl scale deployment istio-pilot -n gke-system --replicas=1

요청 수 및 요청 지연 시간 측정항목 누락

워크로드 아이덴티티를 사용 설정하고 서비스에서 사용하는 서비스 계정에 특정 권한을 부여하지 않은 경우 서비스에서 버전 요청 수 및 요청 지연 시간 측정항목을 보고하지 않을 수 있습니다.

워크로드 아이덴티티로 클러스터의 측정항목 사용 설정 섹션의 단계에 따라 이 문제를 해결할 수 있습니다.

커스텀 도메인과 함께 WebSocket 사용

기본적으로 커스텀 도메인 매핑에는 WebSocket이 사용 중지됩니다.

커스텀 도메인에 WebSocket을 사용 설정하려면 다음 명령어를 실행하여 allow_connect: true로 Istio EnvoyFilter 객체를 만듭니다.

cat <<EOF | kubectl apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: allowconnect-cluser-local-gateway
  namespace: gke-system
spec:
  workloadSelector:
    labels:
      app: cluster-local-gateway
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
      listener:
        portNumber: 80
        filterChain:
          filter:
            name: "envoy.http_connection_manager"
    patch:
      operation: MERGE
      value:
        typed_config:
          "@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager"
          http2_protocol_options:
            allow_connect: true
EOF