produced by Cloud Ace

認証機能付きAPIが簡単に作れるCloud Endpoints入門

本稿では、Cloud Endpointsとは何か、どんな場合に活用できるのか、それを使うとどんなメリットがあるのかについて解説します。また実際のソースコードを使って認証機能を持ったAPIを構築する例をご紹介します。本稿を読むことで、Cloud Endpointsを使ったAPI構築から活用までの流れを把握することができます。

本稿ではOpenAPIを使用したCloud Endpointsについての記事です。Cloud EndpointsはgRPCや AppEngine/Java, Pythonにも対応していますが、本稿ではそれらについては言及しません。予めご承知おき下さい。

Cloud Endpointsとは

Cloud EndpointsはGoogle Cloud Platform(GCP)のサービスの1つで、API管理のための機能を提供します。Cloud Endpointsの主な機能は3つです。

  • 認証(APIキー、JWT)
  • モニタリング(メトリクス分析、ロギング)
  • 共有APIドキュメント

APIのリクエストやレスポンスの仕様(ルール)を定義し、それをデプロイすることでその仕様を実際のAPIに適用することができます。リクエストがAPIバックエンドに届く前にCloud Endpointsがルールを元にリクエストをチェックし、リクエストを通すかどうかを判断します。

Cloud Endpointsは複数のコンポーネントの集まりとして構成されています。例えば、APIルールをデプロイ・管理するツールやサービス、リクエストを監視するリバースプロキシです。これらを操作することによってAPIに上記の3つの機能を付加することができます。

Cloud Endpointsのユースケース

APIを外部に公開する

APIを外部に公開する場合、認証機能をAPIに付与することで、APIにアクセスするユーザーを識別することができ、悪意のあるユーザーからのリクエストを遮断できます。認証方法はAPIキーかOAuth2のどちらか、もしくは両方を選択することができます。

マイクロサービスを使ったアプリケーションを構築する

マイクロサービスを構築する際、それぞれのマイクロサービスにサービスアカウントキーを持たせることでマイクロサービスとAPI間の通信に認証機能を付加することができます。そうすることでAPIを利用するマイクロサービスを限定することができ、セキュリティが向上します。

Cloud Endpointsを使うメリット

APIのドキュメント化が簡単

Cloud EndpointsはOpenAPI仕様に対応しています。OpenAPIはコードベースのAPI定義フォーマットです。コードベースなのでGitで管理することが可能です。またOpenAPIではAPI定義ファイルからドキュメントを自動生成することができますので、別途APIドキュメントを作る手間が省けます。

APIの管理・デプロイが簡単

APIルールは1つの定義ファイルの中で管理されているので、それを編集することによりAPIの追加や削除は簡単に行えます。またAPIキーやOAuthなどのセキュリティルールの定義を一度行えば、ルールを全APIに適用したり、一部のAPIのみに適用するといった設定も簡単に行えます。作成したAPIルールはコマンド一回でデプロイすることができます。

APIの利用状況の分析ができる

Cloud EndpointsにはAPIモニタリング用の画面が用意されており、そこで個々のAPIの利用状況をモニタリングしたり、ログを見ることができます。これを活用することで、デバッグやAPI開発のヒントを得ることができます。

Cloud Endpointsを試してみる

Cloud Endpointsの仕組みについてはこちらが詳しいです。なので本稿では仕組みについては詳しく触れません。その代わり、GKEを使用したEndpointsの使用例を紹介することでCloud Endpointsの全体像を掴んでもらいたいと思います。
構築の手順は以下の通りです。

  1. APIバックエンドのコンテナイメージを作成し、Google Container Registryにアップロードする
  2. 作成したコンテナイメージを元にGKEでバックエンドAPIのクラスタを構築する
  3. OpenAPIフォーマットのAPI定義ファイルをCloud SDKでデプロイする

上記の手順を行うと以下のようなアーキテクチャが構築できます。クライアントはプロジェクトに割り当てられたAPIキーをあらかじめ持っておき、それをAPIのリクエストに含めることでAPIへのアクセスが可能となります。逆に、APIキーをリクエストに含めない場合はエラーになります。

以降ではサービスという単語が頻出しますが、サービスにはCloud EndpointsのサービスとKubernetes(k8s)のサービスの2種類があります。それぞれを区別するために便宜上「EPサービス」「K8sサービス」と記載します。

コンテナイメージを作成し、Google Container Registryにアップロードする

デフォルトのプロジェクトを設定します。[PROJECT_ID]はご自身のプロジェクトIDに置き換えてください。

コマンド
$ gcloud config set project [PROJECT_ID]
以降も度々[PROJECT_ID]が登場しますが、それらも全て置き換えてください。

以下のファイルを作り同じディレクトリに配置します。今回作るAPIはリクエストに対し「Hello, World」とプレーンテキストを返すだけの単純なものです。

app.go
package main

import (
        "fmt"
        "net/http"
)

type String string

func (s String) ServeHTTP(w http.ResponseWriter, r *http.Request) {
        fmt.Fprint(w, s)
}

func main() {
        http.Handle("/hello", String("Hello, World"))
        http.ListenAndServe("0.0.0.0:8080", nil)
}
Dockerfile
FROM golang:1.11.2-alpine3.8
WORKDIR /go
ADD app.go .
RUN ["go", "build", "app.go"]
ENTRYPOINT ["./app"]

コンテナイメージをビルドします。

コマンド
$ docker build -t gcr.io/[PROJECT_ID]/backend-go .

コンテナイメージをContainer Registryにプッシュします。

コマンド
$ docker push gcr.io/[PROJECT_ID]/backend-go

Google Kubernetes EngineでバックエンドAPIのクラスタを構築する

Kubernetesの構成ファイルを用意します。今回は以下の構成ファイルを使います。

backend.yaml
kind: Service
apiVersion: v1
metadata:
  name: endpoints-service
spec:
  selector:
    app: myendpoints-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 8081
  type: LoadBalancer
---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: endpoints-deployment
  labels:
    app: myendpoints-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myendpoints-app
  template:
    metadata:
      labels:
        app: myendpoints-app
    spec:
      containers:
      - name: esp
        image: gcr.io/endpoints-release/endpoints-runtime:1
        args: [
          "-p", "8081",
          "-a", "0.0.0.0:8080",
          "-s", "sample-api.endpoints.[PROJECT_ID].cloud.goog",
          "-R", "managed"
        ]
        ports:
        - containerPort: 8081
      - name: backend-go
        image: gcr.io/[PROJECT_ID]/backend-go
        ports:
        - containerPort: 8080

GKEクラスタを作成します。

コマンド
$ gcloud container clusters create endpoints-cluster --zone asia-east1-b --preemptible

構成ファイルをクラスタにデプロイします。

コマンド
$ kubectl apply -f backend.yaml

EPサービスをデプロイする

OpenAPI定義ファイルを用意します。今回は以下の定義ファイルを使います。

openapi.yaml
swagger: "2.0"
info:
  description: "A simple Google Cloud Endpoints API example."
  title: "Endpoints Example"
  version: "1.0.0"
host: "sample-api.endpoints.[PROJECT_ID].cloud.goog"
x-google-endpoints:
    - name: "sample-api.endpoints.[PROJECT_ID].cloud.goog"
      target: "[BACKEND_IP]" 
paths:
  /hello:
    get:
      operationId: helloWorld
      description: Returns greeting message.
      produces:
        - text/plain
      responses:
        '200':
          description: returns hello world.
          schema:
            type: string
      security:
      - api_key: []
securityDefinitions:
  api_key:
    type: apiKey
    name: key
    in: query

hostはEndpointsにデプロイされるEPサービスの一意な名前です。

YAML
host: "sample-api.endpoints.[PROJECT_ID].cloud.goog"

x-google-endpointsは任意のドメインを使用してAPIにアクセスしたい場合に記述します。nameは所有しているドメイン(.endpoints.[PROJECT_ID].cloud.googドメインは自由に使うことができます)、targetはバックエンドのIPアドレスを記載します。今回はGKEにデプロイしたk8sサービスの外部IPアドレスを使用します。

openapi.yaml
x-google-endpoints:
    - name: "sample-api.endpoints.[PROJECT_ID].cloud.goog"
      target: "[BACKEND_IP]" 

K8sサービスの外部IPアドレスを確認します。

コマンド
$ kubectl get service
NAME                TYPE           CLUSTER-IP     EXTERNAL-IP       PORT(S)        AGE
endpoints-service   LoadBalancer   10.31.248.72   [BACKEND_IP]      80:30738/TCP   17h
kubernetes          ClusterIP      10.31.240.1                443/TCP        1d

k8sサービスのEXTERNAL-IPの値をコピーし、openapi.yamlのファイルの[BACKEND_IP]の値と置き換えます。

securityDefinitionsにはAPIのセキュリティルールを記載します。例えば以下の場合、APIキーをクエリ文字列のkeyの値にセットしてリクエストを送信しなければならないというルールを定義しています。

openapi.yaml
securityDefinitions:
  api_key:
    type: apiKey
    name: key
    in: query

このルールをAPIに適用するには、APIのメソッド直下にsecurityプロパティを追加し、定義した認証ルールの名前を記載します。(リストは空で構いません)

openapi.yaml
paths:
  /hello:
    get:
      // 中略
      security:
      - api_key: []

OpenAPI定義ファイルをデプロイします。

コマンド
$ gcloud endpoints services deploy openapi.yaml

これで手順は全て完了です。試しに構築したAPIを使って、APIキーを使った場合と使わなかった場合の動作比較をしてみたいと思います。

構築したAPIを試してみる

APIキーを取得する

Cloud Consoleにアクセスし、サービスメニューから「APIとサービス」→「認証情報」のページへ移動します。

認証情報のページに移動したら、「認証情報を作成」→「APIキー」の順にクリックし、生成されたAPIキーをクリップボードにコピーします。

リクエストを投げる

APIキーがある場合

コマンド
$ curl sample-api.endpoints.[PROJECT_ID].cloud.goog/hello?key=[API_KEY]
Hello, World

APIキーが無い場合

コマンド
$ curl sample-api.endpoints.[PROJECT_ID].cloud.goog/hello
{
 "code": 16,
 "message": "Method doesn't allow unregistered callers (callers without established identity). Please use API Key or other form of API consumer identity to call this API.",
 "details": [
  {
   "@type": "type.googleapis.com/google.rpc.DebugInfo",
   "stackEntries": [],
   "detail": "service_control"
  }
 ]
}

APIキーをクエリに含めなかった場合、APIの呼び出しに失敗しました。

APIのドキュメントを見る

デプロイしたAPI定義ファイルからドキュメントを自動生成してプロジェクトで共有することができます。これを見るには、Cloud Consoleから「エンドポイント」→「デベロッパーポータル」にアクセスし、サービスリストからEPサービスを選択します。

「Try this API」でAPIを試しに実行することもできます。

APIをモニタリングする

APIのリクエスト数やレイテンシなどのメトリクスをモニタリングするには、Cloud Consoleから「エンドポイント」→「サービス」へアクセスします。

またAPIリストの各行にある「ログを表示」ボタンをクリックすると、各APIのログを見ることができます。

まとめ

いかがでしたでしょうか。 Cloud Endpointsを使うとAPIの管理が簡単にできることがお分かりいただけたと思います。 個人的には、APIのドキュメントを自動で作成してくれるデベロッパーポータルが特に魅力的でした。これを使えば、APIのドキュメントを別途Gitなどで管理する必要が無くなり、属人化が発生しにくくなります。使いこなすには少し知識が必要ですが、一度使いこなすととても便利なツールです。 皆さんもぜひこの機会にCloud Endpointsを試してみてはいかがでしょうか。

次の記事を読み込んでいます
次の記事を読み込んでいます
次の記事を読み込んでいます
次の記事を読み込んでいます