y-ohgi's blog

しがないSREなフリーランスの徒然

Pull Request毎にGAEでSwaggerをプレビューさせる

f:id:y-ohgi:20180531190442p:plain

概要

APIの量産が必要になったものの、複数人でAPIを量産しているとSwaggerのPRも量産されてく。
Swaggerのレビューはyamlを見てレビューするわけだけど、わざわざSwaggerのプレビューをするためにブランチにチェックアウトしてpullしてサーバー立てるのはしんどい・・・。
なんで、SwaggerリポジトリにPull Requestが立つ毎(& Commit毎)にGAEにデプロイさせて、GAEでSwaggerを確認できるようにしてレビューのフローを改善させるのがモチベーション

なんでGAE

  • GAE/SEは安い。
    • リクエスト毎にインスタンスが起動され、リクエストが来なくなれば一瞬で落ちる。
    • 無料枠もある
      • 開発メンバーで行うSwaggerのレビューレベルであれば実質タダ
  • IP制限
    • Cloud Storageだと制限がかけられない問題
    • プロジェクトがGCPではなくAWSだったらS3使ってた案件。
  • 複数PRのプレビューを用意することが可能
    • GAEのバージョン機能は古いバージョンもURLを知っていればアクセス可能。
      • このURLは予測可能なURLが取得できる
      • e.g. https://<バージョン番号>-dot-<サービス名>-dot-<GCPプロジェクト名>.appspot.com
    • バージョン番号は任意の文字列を設定可能
      • バージョン番号をPR番号にすることで他のPRと競合せずに公開可能!GAEすごい!

f:id:y-ohgi:20180531191151p:plain

やったこと

1. SwaggerリポジトリGitHub Pagesに対応させる

  1. Swagger専用リポジトリが無ければ作成& Swaggerの定義ファイルをリポジトリへ配置
  2. "gh-pages" ブランチを作成&チェックアウト
  3. Swagger UIリポジトリから静的ファイルを取得してルートに配置
  4. 追加したindex.html で Swaggerの定義ファイルをリポジトリに対応させたパスへ変更
  5. pushしてGitHub Pagesが見れることを確認

2. GCPでサービスアカウントの作成

GCPのGAEデプロイ権限のついたアクセストークンを発行してキーファイルをダウンロード。
権限はだいたいこんな感じで通った。たぶん最小。

  • App Engine コード閲覧者
  • App Engine サービス管理者
  • App Engine デプロイ担当者
  • ストレージオブジェクト閲覧者
  • ストレージのオブジェクト作成者

3. CircleCIのリポジトリ登録と環境変数の設定

  1. CircleCIへリポジトリの登録
    • サイドバーの「ADD PROJECT」からSwaggerリポジトリをフォロー
  2. CircleCIのフックをPRのみに限定
    • サイドバーの「SETTING」 > 「Projects」 > 「<Swaggerリポジトリ>」の右端の歯車マーク > 「Advanced Setting」 > 「Only build pull requests」をOnに設定
  3. 環境変数の設定
    • サイドバーの「SETTING」 > 「Projects」 > 「<Swaggerリポジトリ>」の右端の歯車マーク > 「Advanced Setting」 > 「Environment Variables」 > 「ADd Variable」

CircleCIの環境変数

環境変数 説明
GCP_PROJECT GCPのプロジェクト名を入力
SWAGGER_SERVICE_ACCOUNT_CREDENTIAL サービスアカウントのキーファイルをbase64したものを登録
GITHUB_TOKEN GitHubトークン。リポジトリ閲覧権限とコメント権限がついたものを作成して渡す

4. GCPデプロイ用Dockerの作成

CircleCI上でGAEのデプロイを行うためのDocker。

やってることは、

  1. googleの各種コマンド入りDockerイメージを使う。
  2. (Swaggerリポジトリの)ファイルをDocker内へコピー
  3. CMDで認証からデプロイまでを行う(環境変数を使いたい&シェルスク用意したくないためCMDで無理やり行った。
    • サービスアカウントの秘密鍵JSONを復元
      • echo $SERVICE_ACCOUNT_CREDENTIAL | base64 --decode > /tmp/service-account.json
    • 秘密鍵JSONをもとに認証
      • gcloud auth activate-service-account --key-file /tmp/service-account.json --project $PROJECT
    • GAEへデプロイ
      • yes Y | gcloud app deploy --version $GAE_VERSION

Dockerfileの作成

FROM google/cloud-sdk:slim

WORKDIR /app
COPY . /app

CMD bash -c "echo $SERVICE_ACCOUNT_CREDENTIAL | base64 --decode > /tmp/service-account.json && gcloud auth activate-service-account --key-file /tmp/service-account.json --project $PROJECT && yes Y | gcloud app deploy --version $GAE_VERSION"

5. GAEデプロイコンフィグの作成

php5.5が楽そうだったのでphp5.5を採用
app.yaml の作成後、Swaggerリポジトリの直下へ配置

やってることは簡単で以下の3つだけ。
1. GAE/SEのphp5.5を指定 2. swagger用サービスを作成 - GAEの名前空間的なもの。 3. 各種パスでどのファイルにルーティングするかの設定 - 静的ファイルっぽいのをわちゃわちゃ書いておく。 - swaggerの定義ファイルはyamlyamlを追加するの大事。

app.yamlの作成

runtime: php55

service: swagger

handlers:
  - url: /
    static_files: index.html
    upload: index.html

  - url: /(.+\.(html|css|js|gif|png|jpg|map|yaml|yml|))$
    static_files: \1
    upload: (.+\.(html|css|js|gif|png|jpg|map|yaml|yml|))$

6. CircleCIコンフィグの作成

雑に解説すると...

  1. CircleCIをVMで起動
  2. 環境変数からPRの番号を取り出す
  3. デプロイ用Dockerのビルド
  4. Dockerを走らせ、GAEへデプロイ
  5. 1~4 のJobの完了後、PRへGAEのURLを通知

config.ymlを眺める

version: 2
jobs:
  deploy:
    machine: true
    steps:
      - checkout
      - run:
          name: set up environment variables
          command: |
            cat <<EOS > envs.txt
            export PR_NUM=`echo $CI_PULL_REQUEST | sed -r 's/.*\/([0-9].*)$/\1/'`
            export GAE_URL=https://`echo $CI_PULL_REQUEST | sed -r 's/.*\/([0-9].*)$/\1/'`-dot-swagger-dot-${GCP_PROJECT}.appspot.com/
            EOS
      - run:
          name: docker build
          command: docker build -t swagger .

      - run:
          name: gcloud deploy
          command: |
            source envs.txt
            docker run -e GAE_VERSION=$PR_NUM -e SERVICE_ACCOUNT_CREDENTIAL=$SWAGGER_SERVICE_ACCOUNT_CREDENTIAL -e PROJECT=$GCP_PROJECT swagger
      - run:
          name: comment GAE URL to Pull Request
          command: |
            source envs.txt
            curl -v -X POST https://api.github.com/repos/${CIRCLE_PR_USERNAME}/${CIRCLE_PR_REPONAME}/issues/${PR_NUM}/comments \
              -H "Authorization: token $GITHUB_TOKEN" \
              -H "Content-Type: application/json" \
              -d "{\"body\":\"$GAE_URL\"}"
workflows:
  version: 2
  build_and_test:
    jobs:
      - deploy

所感

GAEのサービスとバージョニング機能でURLが生成できて、なおかつ無料枠とIP制限が良い感じにマッチして良い