GKE でのモバイル クライアント用の Pub/Sub プロキシの設定


このチュートリアルでは、クライアント側の認証情報ではなく、認証と認可のロジックを処理するプロキシを使用して、モバイルアプリまたはクライアント側アプリから Pub/Sub にメッセージをパブリッシュする方法を説明します。

Identity and Access Management(IAM)を使用して、クライアントから Pub/Sub へのメッセージを認証できますが、このような長期間有効な認証情報は期限切れになりません。クライアント側アプリでは、これらの認証情報はアプリの逆コンパイルやリバース エンジニアリングなどの手法で検出されます。

その代わり、認証と承認のロジックを、次のタスクを行うプロキシにオフロードできます。

  • 受信リクエストを認証してユーザーを検証する。
  • 適切な IAM 権限とともにリクエストを Pub/Sub に転送する。

このチュートリアルでは、Google Kubernetes Engine(GKE)に Pub/Sub プロキシを実装する方法について説明します。このチュートリアルは、モバイル アプリケーションまたはクライアント側アプリケーション向けの設計を定義して実装する、アプリケーション開発者とシステム設計者を対象としています。Kubernetes の基本的なコンセプトのを理解していることと、Cloud Endpoints に精通していることを前提としています。

このチュートリアルのリクエスト フロー

Pub/Sub がストリーミング パイプラインにどのように適合するかを理解するには、クリックストリーム分析を検討します。このユースケースでは、ユーザーがモバイルアプリをどのように操作しているかを把握できます。こうした分析情報を得るには、ユーザーのアクションをリアルタイムで取得します。次の図は、データの流れを示しています。

Pub/Sub プロキシは、データが集計される前にクライアントからメッセージを受信します。

アプリによってキャプチャされたデータは、プロキシを経由して Pub/Sub に push されます。Pub/Sub には、有意義な分析を行えるようにデータを集計する Dataflow や Dataproc のような、サブスクライバーのダウンストリームがあります。

次の図は、このチュートリアルで使用するリクエスト フローの詳細を示しています。

ユーザー リクエストでのパイプライン コンポーネントの相互作用の様子。

次のセクションでは、この図のそれぞれのコンポーネントの相互作用について説明します。

ユーザー認証

モバイルアプリでは、さまざまな方法でユーザーを認証できます。認証フローはアプリに固有のものです。このチュートリアルでは、このようなユーザー認証のソリューションを説明します。このチュートリアルには、このソリューションの実装も伴います。

クライアント アプリから Pub/Sub プロキシへのリクエスト

アプリのバックエンドは、クライアントがローカルに保存する有効期間が短い認証トークンを生成します(たとえば、Android キーストア システムiOS キーチェーン サービスを使用します)。このチュートリアルでは、OpenID Connect(OIDC)ID トークンを使用してクライアント アプリを認証します。Google は OIDC ID トークンを発行して署名します。

クライアント側のアプリは、OIDC ID トークンを使用して、リクエストを Pub/Sub プロキシに送信します。Pub/Sub プロキシはトークンを検証し、適切な IAM 認証情報とともにリクエストを Pub/Sub に転送します。

メッセージのパブリッシュ

クライアント アプリが正しく認証されたら、Pub/Sub プロキシはパブリッシュ リクエストを Pub/Sub に送信します。Pub/Sub は IAM を使用することで、呼び出し元(Pub/Sub プロキシ)にパブリッシュ リクエストを送信するための適切な権限があることを保証できます。このチュートリアルでは、Pub/Sub プロキシは Compute Engine のデフォルトのサービス アカウントを使用して、Pub/Sub で認証します。Compute Engine のデフォルトのサービス アカウントには編集者 IAM ロール(roles/editor)があり、これは公開者アクセス権を Pub/Sub プロキシに付与します。

目標

  • Pub/Sub プロキシを運用する GKE クラスタを作成する。
  • Pub/Sub トピックを作成する。
  • Pub/Sub プロキシをデプロイする。
  • Pub/Sub プロキシへのリクエストを認証するように Endpoints を構成する。
  • メッセージが Pub/Sub にパブリッシュされることを検証する。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

  1. Google Cloud コンソールでプロジェクトの選択ページに移動します。

    プロジェクト セレクタに移動

  2. Google Cloud プロジェクトを選択または作成します。

  3. Google Cloud プロジェクトで課金が有効になっていることを確認します

  4. Google Cloud コンソールで、「Cloud Shell をアクティブにする」をクリックします。

    Cloud Shell をアクティブにする

    Google Cloud コンソールの下部で Cloud Shell セッションが開始し、コマンドライン プロンプトが表示されます。Cloud Shell はシェル環境です。Google Cloud CLI がすでにインストールされており、現在のプロジェクトの値もすでに設定されています。セッションが初期化されるまで数秒かかることがあります。

  5. このチュートリアルに必要な環境変数を定義します。
        export PROJECT=$(gcloud config get-value project)
        export REGION=us-central1
        export ZONE=${REGION}-b
        export CLUSTER=pubsub-proxy
        export TOPIC=proxy-test
        export SERVICE_ACCOUNT=publish-test
        export ENDPOINTS_SERVICE="pubtest.endpoints.${PROJECT}.cloud.goog"
        export GENERATE_TOKEN="https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts"
  6. Cloud Build、Compute Engine、Google Kubernetes Engine、Artifact Analysis、Container Registry、Endpoints、Service Management、Service Control、Pub/Sub の API を有効にします。
        gcloud services enable \
            cloudbuild.googleapis.com \
            compute.googleapis.com \
            container.googleapis.com \
            containeranalysis.googleapis.com \
            containerregistry.googleapis.com \
            endpoints.googleapis.com \
            servicemanagement.googleapis.com \
            servicecontrol.googleapis.com \
            pubsub.googleapis.com

Pub/Sub トピックの作成

  • Cloud Shell で、メッセージをパブリッシュする Pub/Sub トピックを作成します。

    gcloud pubsub topics create $TOPIC
    

GKE クラスタの作成

  1. Cloud Shell で、GKE クラスタを作成します。

    gcloud container clusters create $CLUSTER \
        --zone $ZONE \
        --scopes "https://www.googleapis.com/auth/cloud-platform"
    
  2. 稼働しているクラスタ用に、認証情報を取得します。

    gcloud container clusters get-credentials $CLUSTER \
        --zone $ZONE \
        --project $PROJECT
    

コンテナ イメージのビルド

  1. Cloud Shell で、コード リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/solutions-pubsub-proxy-rest
    
  2. Cloud Build を使用して、ソースからコンテナ イメージをビルドし、Container Registry に保存します。

    cd solutions-pubsub-proxy-rest && \
        gcloud builds submit --tag gcr.io/$PROJECT/pubsub-proxy:v1
    

静的外部 IP アドレスの作成

  1. Cloud Shell で、Pub/Sub プロキシ ロードバランサにあとで割り当てるための静的外部 IP アドレスを作成します。

    gcloud compute addresses create service-ip --region $REGION
    
  2. 静的 IP アドレスを環境変数 PROXY_IP に保存します。

    PROXY_IP=$(gcloud compute addresses describe service-ip \
        --region $REGION --format='value(address)')
    

Endpoints のデプロイ

Pub/Sub プロキシは Endpoints を使用して、ユーザーからのリクエストを認証します。Endpoints は Extensible Service Proxy(ESP)を使用して、認証モニタリングトレースAPI ライフサイクル管理などの、API 管理機能を提供します。このチュートリアルでは、Endpoints を Pub/Sub プロキシへの受信リクエストの認証のみに使用します。

このチュートリアルでは、Pub/Sub プロキシと一緒に ESP をサイドカーとしてデプロイします。ESP は、受信リクエストを Pub/Sub プロキシに転送する前にインターセプトして認証します。

  1. Cloud Shell で、[PROJECT_ID] プレースホルダを openapi.yaml ファイル内の Google Cloud プロジェクト ID に置き換えます。

    sed -i -e "s/\[PROJECT_ID\]/$PROJECT/g" openapi.yaml
    
  2. OpenAPI マニフェスト ファイル内で、[IP_ADDRESS] プレースホルダを PROXY_IP の値に置き換えます。

    sed -i -e "s/\[IP_ADDRESS\]/$PROXY_IP/g" openapi.yaml
    
  3. OpenAPI サービス定義を Endpoints にデプロイします。

    gcloud endpoints services deploy openapi.yaml
    

    上記のコマンドにより、以下が作成されます。

    • openapi.yaml ファイル(pubtest.endpoints.project-id.cloud.goog)のホスト フィールドで指定した名前を持つマネージド サービスproject-id は Google Cloud プロジェクトの ID)。
    • サービス名と、openapi.yaml ファイルの x-google-endpoints 拡張機能で定義された Pub/Sub プロキシ ロードバランサの IP アドレス マッピングを使用する DNS の A レコード。

    このチュートリアルでは API キーではなく OIDC ID トークンを使用するため、デプロイ中に無視できる警告が表示されます。

    WARNING: openapi.yaml: Operation 'post' in path '/publish': Operation does
    not require an API key; callers may invoke the method without specifying an
    associated API-consuming project. To enable API key all the
    SecurityRequirement Objects
    (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#security-requirement-object)
    inside security definition must reference at least one SecurityDefinition
    of type : 'apiKey'.
    
  4. サービスが正しくデプロイされているかどうかを確認します。

    gcloud endpoints services describe ${ENDPOINTS_SERVICE}
    

    出力は次のようになります。

    [...]
    producerProjectId: project-id
    serviceConfig:
      documentation:
        summary: Pub/Sub proxy exposed as an Endpoint API
    [...]
      name: pubtest.endpoints.project-id.cloud.goog
      title: PubSub Proxy
      usage: {}
    serviceName: pubtest.endpoints.project-id.cloud.goog
    

    出力の中で:

    • project-id: Google Cloud プロジェクトの ID。

プロキシのデプロイ

  1. Cloud Shell で、プロキシへの HTTPS 接続を許可する自己署名 SSL 証明書を生成します。

    openssl req -x509 -nodes -days 365 \
        -newkey rsa:2048 -keyout ./nginx.key \
        -out ./nginx.crt \
        -subj "/CN=${ENDPOINTS_SERVICE}"
    
  2. SSL 証明書と秘密鍵を使用して Kubernetes Secret を作成します。

    kubectl create secret generic nginx-ssl \
        --from-file=./nginx.crt \
        --from-file=./nginx.key
    
  3. デプロイ マニフェスト ファイルの [PROJECT_ID] プレースホルダを、Google Cloud プロジェクト ID に置き換えます。

    sed -i -e "s/\[PROJECT_ID\]/$PROJECT/g" kube/deployment.yaml
    
  4. サービス マニフェスト ファイルの [IP_ADDRESS] プレースホルダを、PROXY_IP の値に置き換えます。

    sed -i -e "s/\[IP_ADDRESS\]/$PROXY_IP/g" kube/service.yaml
    
  5. プロキシをデプロイします。

    kubectl apply -f kube/
    
  6. デプロイが成功したことを確認します。

    kubectl rollout status deployment/pubsub-proxy
    

    出力は次のようになります。

    [...]
    deployment "pubsub-proxy" successfully rolled out
    
  7. 2 つのコンテナ(ESP と Pub/Sub プロキシ)が Pod で稼働していることを確認します。

    kubectl get pods $(kubectl get pod \
        -l app=pubsub-proxy \
        -o jsonpath="{.items[0].metadata.name}") \
        -o jsonpath={.spec.containers[*].name}
    

    出力は次のようになります。

    esp  pubsub-proxy
    
  8. EXTERNAL-IP の値が <pending> から、先に作成した静的外部 IP アドレスに変化することを監視します。

    kubectl get svc pubsub-proxy -w
    

    出力は次のようになります。

    NAME          TYPE          CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
    pubsub-proxy  LoadBalancer  10.7.247.212  <pending>     443:31104/TCP  6m32s
    pubsub-proxy  LoadBalancer  10.7.247.212  <PROXY_IP>    443:31104/TCP  6m5s
    

    監視を停止するには、CTRL+C を押します。

    Pub/Sub プロキシが正常にデプロイされると、https://${ENDPOINTS_SERVICE}/publish で公開されます。新しい DNS 構成が反映されるまでに数分かかる場合があります。

  9. DNS 構成を確認します。

    watch nslookup ${ENDPOINTS_SERVICE}
    

    出力は次のようになります。

    Server:   169.254.169.254
    Address:  169.254.169.254#53
    
    Non-authoritative answer:
    Name: pubtest.endpoints.project-id.cloud.goog
    Address: gke-load-balancer-ip
    

    出力の中で:

    • gke-load-balancer-ip は、GKE ロードバランサの IP アドレス(プロキシ IP)です。

    監視を停止するには、CTRL+C を押します。

上記の手順でエラーが発生した場合は、トラブルシューティングをご覧ください。

認証トークンの生成

次の認証トークンの生成手順は、1 つの例として説明しています。本番環境にでは、ユーザーが自身の認証トークンを生成する方法が必要になります。たとえば、Identity-Aware Proxy のドキュメントでは、プログラムで OIDC ID トークンを取得するサンプルコードを探すことができます。

認証トークンを生成するには、次の手順を行います。

  1. OIDC ID トークンを生成する Google Cloud サービス アカウントを作成します。

    gcloud iam service-accounts create \
        $SERVICE_ACCOUNT \
        --display-name $SERVICE_ACCOUNT
    
  2. サービス アカウントのメール ID を取得します。

    SA_EMAIL=${SERVICE_ACCOUNT}@${PROJECT}.iam.gserviceaccount.com
    
  3. サービス アカウントのサービス アカウント トークン作成者の IAM ロール(roles/iam.serviceAccountTokenCreator)を付与します。

    gcloud iam service-accounts add-iam-policy-binding $SA_EMAIL \
        --member user:$(gcloud config get-value account) \
        --role roles/iam.serviceAccountTokenCreator
    
  4. IAM 認証情報 API を使用して OIDC ID トークンを生成します。

    TOKEN=$(curl -s ${GENERATE_TOKEN}/${SA_EMAIL}:generateIdToken \
        -H "Content-Type: application/json" \
        -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
        -d '{"audience": "'${ENDPOINTS_SERVICE}'", "includeEmail": "true"}' | jq -r ".token")
    

    Endpoints のサービス名は audience フィールドで指定されます。audience クレームは、トークンの受信先を識別します。

  5. トークンが正常に作成されたことを確認します。

    echo $TOKEN
    

    JSON ウェブトークン(JWT)は次のようになります。

    eyJhbGciOiJSUzI1NiIsImtpZCI6IjY4NjQyODlm[...].eyJhdWQiOiJwdWJ0ZXN0LmVuZHBvaW50cy52aXR
    hbC1vY3RhZ29uLTEwOTYxMi5jbG91ZC5nb[...].SjBI4TZjZAlYo6lFKkrvfAcVUp_AJzFKoSsjNbmD_n[...]
    

プロキシを使用した Pub/Sub の呼び出し

  1. Cloud Shell で、テスト メッセージをパブリッシュします。

    curl -i -k -X POST https://${ENDPOINTS_SERVICE}/publish \
        -H "Authorization: Bearer $TOKEN" \
        -H "Content-Type: application/json" \
        -d '{"topic": "'$TOPIC'", "messages": [ {"attributes": {"key1": "value1", "key2" : "value2"}, "data": "test data"}]}'
    

    出力は次のようになります。

    HTTP/2 200
    server: nginx
    date: Sun, 02 Jun 2019 03:53:46 GMT
    ...
    
  2. メッセージが Pub/Sub トピックに正常にパブリッシュされたかどうかを確認します。

    kubectl logs -f --tail=5 deployment/pubsub-proxy -c pubsub-proxy
    

    Pub/Sub プロキシのデプロイログにメッセージ Successfully published が表示されます。

    2019-06-02 03:49:39.723:INFO:oejs.Server:main: Started @2554ms
    Jun 02, 2019 3:53:44 AM com.google.pubsub.proxy.publish.PublishMessage
    getPublisher
    INFO: Creating new publisher for: proxy-test
    Jun 02, 2019 3:53:47 AM
    com.google.pubsub.proxy.publish.PublishMessage$1 onSuccess
    INFO: Successfully published: 569006136173844
    

トラブルシューティング

  1. Cloud Shell で、Pub/Sub プロキシ Pod 内の両方のコンテナの状態を確認します。

    kubectl describe pods $(kubectl get pod -l app=pubsub-proxy \
        -o jsonpath="{.items[0].metadata.name}")
    

    ログの出力では、コンテナの状態は Running です。

    [...]
    Containers:
      esp:
    [...]
      State:  Running
        Started:  Fri, 21 Jun 2019 16:41:30 +0530
      Ready:  True
      Restart Count:  0
    [...]
      pubsub-proxy:
        State:  Running
          Started:  Fri, 21 Jun 2019 16:41:42 +0530
        Ready:  True
        Restart Count:  0
    [...]
    
  2. (省略可)コンテナのログで、他のエラーがないか確認します。たとえば、Pub/Sub プロキシのログを確認するには、次のコマンドを実行します。

    kubectl logs -f --tail=10 deployment/pubsub-proxy -c pubsub-proxy
    

トラブルシューティングについては、次のドキュメントをご覧ください。

クリーンアップ

このチュートリアルで使用したリソースに対して Google Cloud アカウントに課金されないようにするには、このチュートリアル用に作成した 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.

リソースを削除する

このチュートリアルで使用した Google Cloud プロジェクトを残しておく場合は、個々のリソースを削除します。

  1. Cloud Shell で、GKE クラスタを削除します。

    gcloud container clusters delete $CLUSTER --zone $ZONE --async
    
  2. ダウンロードしたコード、アーティファクト、その他の依存関係を削除します。

    cd .. && rm -rf solutions-pubsub-proxy-rest
    
  3. Container Registry 内のイメージを削除します。

    gcloud container images list-tags \
        gcr.io/$PROJECT/pubsub-proxy \
        --format 'value(digest)' | \
        xargs -I {} gcloud container images delete \
        --force-delete-tags --quiet \
        gcr.io/${PROJECT}/pubsub-proxy@sha256:{}
    
  4. Pub/Sub トピックを削除します。

    gcloud pubsub topics delete $TOPIC
    
  5. サービス アカウントを削除します。

    gcloud iam service-accounts delete $SA_EMAIL
    
  6. Endpoints を削除します。

    gcloud endpoints services delete ${ENDPOINTS_SERVICE}
    
  7. 静的 IP アドレスを削除します。

    gcloud compute addresses delete service-ip --region $REGION
    

次のステップ