GKEのIngressを利用してインターネットにWebサービスを公開しようとしたときに、発生した「外部アクセスをした際に502 Server Errorが表示された」問題の原因と対処方法について、紹介します。

前提

  • フロントエンドの開発言語・フレームワークは、nuxt.jsを利用
  • “/”にアクセスするとログイン済みユーザか確認し、ログイン済みでない場合には、ログイン画面(“/login”)へリダイレクト(302)
  • GKEのIngressのヘルスチェックは、デフォルトを使用

GKEのIngressヘルスチェック

デフォルトのヘルスチェックは、下記の設定になっています。

GKEのIngressの下記仕様もポイントになります。

デフォルトの Ingress コントローラを使用して Ingress によって 1 つ以上の Service を公開すると、GKE によって Google Cloud 外部 HTTP(S) ロードバランサまたは Google Cloud 内部 HTTP(S) ロードバランサが作成されます。こうしたロードバランサでは、いずれも 1 つの URL マップで複数のバックエンド サービスがサポートされます。各バックエンド サービスは Kubernetes Service に対応し、各バックエンド サービスは Google Cloud ヘルスチェックを参照する必要があります。このヘルスチェックはクラスタの外部で実装されるため、Kubernetes の liveness Probe または readiness Probe とは異なります。

今回の問題に影響する箇所は、リクエストパス、想定されるレスポンスの部分になります。今回のケースは、”/”へのリクエストでHTTP 302応答を返すので、異常判定されているということになります。

詳しくは、Ingress for GKEのドキュメントを参照してください。

対処方法

専用のヘルスチェックパスを実装して、HTTP 200応答を実装すればOKとなります。

Nuxt.jsでの対処例

今回は、nuxt.jsを利用しているので、nuxt.jsでの対策方法を紹介します。
nuxt.jsでは、serverMiddlewareという機能があります。このserverMiddlewareを利用して、カスタムAPIエンドポイントを作成し、そこで常にHTTP 200応答を返すようにします。

const express = require('express')
const app = express()

app.get('/healthz', (_req: Request, res: Response | any) => {
  return res.sendStatus(200)
})

module.exports = {
  path: '/',
  handler: app
}

serverMiddlewareを使えるようにします。パスと作成したserverMiddlewareをマッピングします。

export default {
 ・・
  serverMiddleware: [
    // ヘルスチェック向けエンドポイント
    { path: '/healthz', handler: '~/src/server-middleware/health.ts' },
  ],
  ・・・
}

ヘルスチェックエンドポイントを有効化する

Readiness Probe or Liveness Probeで先程作成したエンドポイントへリクエストが流れるようにします。詳しくは、kubernetesドキュメントを参照してください。

下記の例は、nuxtで作ったアプリケーションのマニュフェストの例です。


{{- $servicePort := 3000 -}}
apiVersion: v1
kind: Service
metadata:
  name: nuxt-app-web
spec:
  type: LoadBalancer
  selector:
    app: nuxt-app-web
  ports:
    - protocol: TCP
      port: 80
      targetPort: {{ $servicePort }}
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nuxt-app-web
spec:
  selector:
    matchLabels:
      app: nuxt-app-web
  template:
    metadata:
      labels:
        app: nuxt-app-web
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      containers:
        - name: nuxt-app-web
          image: {{ .Values.webui.container.image }}:{{ .Values.container.tag }}
          imagePullPolicy: Always
          ports:
          - containerPort: {{ $servicePort }}
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /health
              port: {{ $servicePort }}
              scheme: HTTP
            initialDelaySeconds: 15
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 1

バックエンドサービスのヘルスチェックのパスを変更する

バックエンドサービスのヘルスチェックのパスが”/”になっているので、下記のように変更します。

まとめ

今回は、GKEのIngressを利用した際に502エラーが出るというケースについて調査しました。ユーザ認証済みの場合のみ、トップページを見せるケースでは、このような対処方法が有効になるかと思います。お疲れ様でした。