はじめに
以下記事を読んで、OpenAPI Specをもっとフロントエンド側で活用できたら良いなと思いました。
そこで、同じようにGitHub Actionsを使って、GitHub PagesにReDocを配置、Mock Server(Prism)をCloud Runへデプロイしてみることにしました。
なお、Mock Server(Prism)のデプロイ先をCloud Runにしたのは、最小インスタンスを0にする(つまり、アクセスが無い時はインスタンスが落ちている状態にできる)ことができ、課金を抑えられると思ったからです。
ReDocとは?
ReDocは、OpenAPI Specからドキュメントを生成してくれるツールです。
同じようなツールで、Swagger UIもありますが、Swagger UIはリファレンス要素が強く、ReDocはガイド要素が強い(見易さ重視)印象を受けました。そのため、ドキュメントとして参照するならReDocの方が良いと思いました。
OpenAPI SpecからReDocを静的HTMLとして出力するには、 redoc-cli
を使用します。
yarn add -D redoc-cli
// -o オプションを付けて静的HTMLとして出力する
redoc-cli bundle ${OpenAPI Spec Yaml} -o docs/index.html
Mock Server(Prism)
Prismは、OpenAPI SpecからMock Serverを構築してくれるツールです。
PrismはNode.jsで動くので、ローカル環境で動かすことができます。
yarn add -D @stoplight/prism-cli
prism mock ${OpenAPI Spec Yaml} -h 0.0.0.0 -p 8080
これをCloud Runにアップして動かすには、Dockerコンテナで動かす必要があります。
なので、以下のような Dockerfile
を用意します。
FROM node:16
WORKDIR /openapi
COPY ${OpenAPI Spec Yaml} .
# install stoplight/prism
RUN npm install -g @stoplight/prism-cli
CMD ["prism", "mock", ${OpenAPI Spec Yaml}, "-h", "0.0.0.0", "-p", "8080"]
EXPOSE 8080
GitHub Actions
GitHub PagesにReDocを配置、Mock Server(Prism)をCloud RunへデプロイするGitHub Actionsを書きます。
name: Build and Push Image
on: [push]
jobs:
build-and-publish:
name: Build and Push Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
with:
ref: ${{ github.ref }}
- name: Node setup
uses: actions/setup-node@v2
with:
node-version: 16
cache: yarn
- name: Yarn install
run: |
yarn install
- name: Yarn build:doc
run: |
yarn build:doc
- name: Redoc Deploy
uses: peaceiris/actions-gh-pages@v3
if: ${{ github.ref == 'refs/heads/main' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./docs
- name: Setup Google Cloud
uses: google-github-actions/setup-gcloud@v0
with:
service_account_key: ${{ secrets.GCLOUD_AUTH }}
project_id: ${{ secrets.GCLOUD_PROJECT_ID }}
- name: Configure docker for artifact registry
run: |
gcloud auth configure-docker asia-northeast1-docker.pkg.dev
- name: Set TAG
run: |
echo "TAG=$(npx -c 'echo "$npm_package_version"')" >> $GITHUB_ENV
- name: Docker Build
run: |
docker build -t asia-northeast1-docker.pkg.dev/${{ secrets.GCLOUD_PROJECT_ID }}/prism/prism-image:${{ env.TAG }} ./
- name: Docker Push
run: |
docker push asia-northeast1-docker.pkg.dev/${{ secrets.GCLOUD_PROJECT_ID }}/prism/prism-image:${{ env.TAG }}
- name: Deploy to Cloud Run
uses: google-github-actions/deploy-cloudrun@v0
with:
service: prism-server
image: asia-northeast1-docker.pkg.dev/${{ secrets.GCLOUD_PROJECT_ID }}/prism/prism-image:${{ env.TAG }}
region: asia-northeast1
- name: Public Access Cloud Run
run: |
gcloud run services add-iam-policy-binding prism-server \
--member="allUsers" \
--region="asia-northeast1" \
--role="roles/run.invoker"
上から説明していきます。
-
一旦、
push
されるとGitHub Actionsが起動する仕組みにしています。 -
Node setup
にて、Node.js
の環境を用意します。 -
Yarn build:doc
にて、redoc-cli bundle ${OpenAPI Spec Yaml} -o docs/index.html
を実行しています。 -
Redoc Deploy
では、peaceiris/actions-gh-pages
を使って、静的HTMLのReDocファイルをGitHub Pagesへデプロイしています。デフォルトだと、gh-pages
ブランチを作って、そこに./docs
フォルダの中身をデプロイします。 -
Setup Google Cloud
から、Google Cloudのセットアップです。(事前にGCPへアクセスできるサービスアカウントの設定やトークンをGitHub Secrets
へ登録する必要があります)
Cloud Runへデプロイするためには、以下作業をしなくてはなりません。- Dockerイメージを作成
- DockerイメージをArtifact Registry(旧:Container Registry)へ格納
- Artifact RegistryからCloud Runを起動
このあたりは
Terraform
を使ったほうが便利かと思ったのですが、Terraform
を使う場合、Cloud Build等でDockerイメージをビルドする必要があり、だったらGitHub Actions上でビルドした方が楽かなと思ったので、Terraform
の採用は止めました。(もし、違っていたら、ご教示頂けると助かります。) -
Configure docker for artifact registry
にて、Artifact Registryの設定を行います。 -
Set TAG
で、Dockerイメージのタグを付与します。タグはpackage.json
のバージョンにしました。 -
Docker Build
にて、Dockerイメージを作成。 -
Docker Push
にて、DockerイメージをArtifact RegistryへPush。 -
Deploy to Cloud Run
では、google-github-actions/deploy-cloudrun
を使って、Dockerイメージを使ってCloud Runにデプロイ。 -
最後に
Public Access Cloud Run
で、Cloud Runへのアクセス制限をします。今回はアクセス制限せずに公開(allUsers
に設定)
あとは、GitHubにソースをPushするだけで、ReDocの生成とCloud RunでMock Server(Prism)が動きます。
Cloud Runのアクセス制御について
今回は allUsers
を使って、アクセス制限をかけずに公開しました。
しかし、用途によっては、アクセス制限が必要な場合があるので、その手段と個人的に感じたデメリットを以下に纏めます。
1) Cloud Load BalansingによるServerless NEGとCloud Armorを使った制限
Cloud Runの前に、Cloud Load BalansingのServerless NEGを置いて、Cloud Armorを使ってアクセス制限する方法。IPアドレスによる制限等が可能です。
- デメリット
Cloud Load Balansingが高価。
2) Cloud Endpointによる制限
Cloud Runの前に、Cloud EndpointのESP(Extensible Service Proxy)を置くことで、なにかしらの認証(Google認証、Auth0を使用した認証、API Keyを使った認証など)を行った上でないとアクセスできないようにすることができます。
- デメリット
ESPv2のDockerイメージ格納場所が、Artifact Registryに対応していない。(2022/04時点)
構築が少し大変。
3) Cloud RunのIAM(サービスアカウント)による制限
Cloud Runを起動できるIAM(サービスアカウントやGoogle Workspace)を用意してアクセスする。
- デメリット
Authorizationヘッダーにトークンが必要になるので、トークン取得処理が別途必要。
まとめ
金銭的に余裕があるのであれば、Cloud Load BalansingによるServerless NEGとCloud Armorを使った制限をオススメします。
金銭的に余裕がなく、Cloud RunにアップしたMock Server(Prism)を使う人が少数なら、Cloud RunのIAM(サービスアカウント)による制限でも良いと思います。
ただし、Mock Server(Prism)を動かしたい人には、サービスアカウントの鍵情報を渡す必要がるので、注意が必要です。(自己責任でお願いしますm(_ _)m)
あと、トークンの取得が必要なので、以下ライブラリも必要になります。
APIを呼び出すコードのサンプルも置いておきます。
import { JWT } from "google-auth-library"
import service_account from '../${サービスアカウントの鍵}.json'
import axios from 'axios'
;(async () => {
const client = new JWT({
forceRefreshOnFailure: true,
key: service_account.private_key,
email: service_account.client_email,
})
const url = "${Cloud Runのエンドポイント}"
const token = await client.fetchIdToken(url)
const { data } = await axios.get(`${url}`, {
headers: {
Authorization: `Bearer ${token}`
}
})
console.log(`data: ${JSON.stringify(data)}`)
})()
さいごに
OpenAPI Specを活用すれば、フロントとバックエンドでAPIの仕様が共有できます。
しかも、事前にこのようなCIを用意しておくことで、共有漏れを防げぐことができ、バックエンド側でAPIを用意できない間でもフロントエンドは手を止めなくて済みます。
とはいえ、実は私自身、これを実践で使ったことがありません😭
でも、こういったことは事前に考えておくことが重要なのだと思いました。🙂