1
0

ローカルPC+GitHubActionsでAPIOpsを実装する

Last updated at Posted at 2024-03-12

APIの開発を行う場合、APIの仕様書に従ってアプリケーションを実装し、それを更に開発者自身やテストチーム・QAチームがテストする。
1710124851737.png
APIの仕様が変更された場合やアプリケーションの実装を変更した場合にはテストを毎回行うが、これを手動でやるのは面倒なのでCI/CDパイプラインを実装して自動化してみようと思う。
なお、APIの開発・デプロイにGitOps・DevOpsの概念を適用したものをAPIOpsと呼ぶ模様。今回の実装はAPIOpsをどう小さく実現するかの検証にもなっている。

ここでは以下のような条件で自動化する。

  • APIはアプリ側で管理せず、API Gatewayで管理する
  • API Gatewayの設定も自動で変更する
  • ラボは用意せず、自身のPCで完結させる(テストなどは自PC上で動くようにし、API Gatewayも自PC上に立てる)
  • GitOps部分はGitHubを利用する

流れとしては以下のようにする。
1710124336593.png

ユーザがPullRequest(PR)かPushをすると、GitHubがRunnerをキックして自PC内のツールを使ってテストや設定のデプロイなどを自動で行う。
なお、Runnerが実行するコードをミスると自PCにも影響を及ぼす可能性があるため、この構成を推奨している訳ではない点は注意。

また、前提として以下で進める。

今回、動作確認で使う自PCは以下のものを利用した。

  • MacBook Pro M2 (32GBRAM)

メモリ使用量を見ている感じだと16GBでも行けそうではある。

手順としては以下の順番で進める。

  1. Kong Gatewayの構築
  2. GitOpsに使うリポジトリ・Runnerの用意
  3. テストやデプロイに使うCLIの用意
  4. CLIの動作確認
  5. GitHubActionsのYAMLの作成と動作確認

Kong Gatewayの構築

GitHubのKongのGetting Startedに従ってMac上に起動すると各コンテナは正常に起動するのだが、何故かDBlessモードと判断されて、後々以下のエラーが出てGateway Servicesの作成に失敗する。

Error: 1 errors occurred:
	while processing event: Create service Qiita_API failed: HTTP status 405 (message: "cannot create or update 'services' entities when not using a database")

なので、ここではGitHubで提供されているやり方ではなく、KongのDocsで提供されているやり方でKong Gatewayを起動する。
以下でスクリプトを生成する。

cat <<EOF > ./run.sh
#!/bin/bash
COMM=$1
if [ "$COMM" == "start" ]; then
	docker network create kong-net
	docker run -d --name kong-database \
	  --network=kong-net \
	  -p 5432:5432 \
	  -e "POSTGRES_USER=kong" \
	  -e "POSTGRES_DB=kong" \
	  -e "POSTGRES_PASSWORD=kongpass" \
	  postgres:13
	docker run --rm --network=kong-net \
	  -e "KONG_DATABASE=postgres" \
	  -e "KONG_PG_HOST=kong-database" \
	  -e "KONG_PG_PASSWORD=kongpass" \
	  -e "KONG_PASSWORD=test" \
	  kong/kong-gateway:3.6.1.1 kong migrations bootstrap
	docker run -d --name kong-gateway \
	  --network=kong-net \
	  -e "KONG_DATABASE=postgres" \
	  -e "KONG_PG_HOST=kong-database" \
	  -e "KONG_PG_USER=kong" \
	  -e "KONG_PG_PASSWORD=kongpass" \
	  -e "KONG_PROXY_ACCESS_LOG=/dev/stdout" \
	  -e "KONG_ADMIN_ACCESS_LOG=/dev/stdout" \
	  -e "KONG_PROXY_ERROR_LOG=/dev/stderr" \
	  -e "KONG_ADMIN_ERROR_LOG=/dev/stderr" \
	  -e "KONG_ADMIN_LISTEN=0.0.0.0:8001" \
	  -e "KONG_ADMIN_GUI_URL=http://localhost:8002" \
	  -e KONG_LICENSE_DATA \
	  -p 8000:8000 \
	  -p 8443:8443 \
	  -p 8001:8001 \
	  -p 8444:8444 \
	  -p 8002:8002 \
	  -p 8445:8445 \
	  -p 8003:8003 \
	  -p 8004:8004 \
	  kong/kong-gateway:3.6.1.1
elif [ "$COMM" == "stop" ]; then
	docker kill kong-gateway
	docker kill kong-database
	docker container rm kong-gateway
	docker container rm kong-database
	docker network rm kong-net
else
	echo "Unknown argument."
fi
EOF
chmod +x ./run.sh

スクリプトを実行する。なお、止めたい時は引数をstopに変更する。

./run.sh start

起動したらブラウザでhttp://localhost:8002につなげばKong ManagerのUIが確認できる。
1710223888417.png

リポジトリとRunnerの準備

GitHub上に適当なリポジトリをPrivateで作成する。
1710119836631.png

Runnerを登録する。
Settings->Actions->RunnersからNew self-hosted runnerを選択し、Runnerを自PCで動かすための設定を行う。
1710120854510.png

ここではMacで進めるため、Macを選択して表示されたコードを実行する。

mkdir actions-runner && cd actions-runner
curl -o actions-runner-osx-x64-2.314.1.tar.gz -L https://github.com/actions/runner/releases/download/v2.314.1/actions-runner-osx-x64-2.314.1.tar.gz
tar xzf ./actions-runner-osx-x64-2.314.1.tar.gz
./config.sh --url https://github.com/imurata/sandbox-actions --token AOSRR5YX6xxxxxxxxxxxx

config.shを実行すると色々聞かれるが、これは全てデフォルトのままでOK。
公式手順では最後にrun.shを実行するが、サービス化して自動実行したい人は「セルフホストランナーアプリケーションをサービスとして設定する」を参考にsvc.shを使ってサービスをインストール・実行するとよい。
問題なく追加できると、GitHub上でStatusが緑で表示されるようになる。
1710121253141.png

RunnerのホストにCLIをインストール

API作成・管理の前提としてInsomnia、Kongを利用するとしていたが、これらを使っている場合、それらの機能を利用するためのCLIが
Insomniaにはinsoコマンド、KongにはdecKコマンドとして用意されている。
これら各コマンドをRunnerに実行させて自動化を進める。
自動化したい内容とCLIのマッピングは以下となる。

自動化項目 コマンド
Linterでのチェック inso lint spec
APIのテスト inso run test
API Gatewayの設定の生成・チェック inso export spec
deck file openapi2kong
deck gateway validate
API Gatewayの設定の反映 deck gateway sync
動作確認 http --check-status

ということで、上記のコマンドをインストールする。

brew install inso
brew install deck
brew install httpie

コマンドの確認

Runnerに実行させる前に一度手でコマンドを実行する。

Linterでのチェック

insoCLIの前提として、.insomniaディレクトリがない場合は以下のディレクトリを参照する。

OS 場所
Windows %APPDATA%\Insomnia
Linux $XDG_CONFIG_HOME/Insomnia~/.config/Insomnia
Mac ~/Library/Application\ Support/Insomnia

今回は開発端末上でinsoコマンドを実行するので問題ないが、Dockerコンテナ上でinsoコマンドを使う場合は注意。(参考:公式のDockerでの利用手順
またコマンドのリファレンスはCLI Command Referenceに記載がある。
APIのSpecのチェックはinso lint specで行うことが出来、引数にInsomniaのプロジェクト作成後に作ったドキュメントの名前を渡すことで、そのドキュメントのチェックが行われる。また、--verboseをつけると詳細情報が出るので、これも併せて渡しておく。

inso lint spec my-spec.yaml --verbose

実行した結果は以下のようになった。

$ inso lint spec  my-spec.yaml --verbose
› Data store configured from app data directory at /Users/imurata/Library/Application Support/Insomnia
› Load api specification with identifier my-spec.yaml from data store
› Found 1.
› Linting specification from database contents
No linting errors or warnings.
$ echo $?
0

なお、コマンドの引数にファイル名を渡さない場合、対話型でファイルを指定することが出来る。

APIのテスト

テストはinso run testで実行できる。
基本的にはテストスイートの名前と環境変数の箱の名前を指定して実行する。またこちらも--verboseによる詳細化が可能なのでつけるようにしておく。

inso run test "Qiita Test Suite" --env myenv --verbose

ここでは紹介しないがオプションでテストケースを絞ったり、テスト失敗時の挙動を変更したり出来るので、CI/CDの中でテストケースを弄りたい場合はどういうオプションがあるかを確認しておくとよい。
実行結果は以下となる。

$ inso run test "Qiita Test Suite" --env myenv --verbose
› Data store configured from app data directory at /Users/imurata/Library/Application Support/Insomnia
› Load api specification with identifier Qiita Test Suite from data store
› Found 0.
› Load workspace with identifier Qiita Test Suite from data store
› Found 0.
› Load unit test suite with identifier Qiita Test Suite from data store
› Found 1.
› Load base environment for the workspace wrk_cb60ad72964441d2bc9656c516bf46b5 from data store
› Found 1.
› Load sub environment with identifier myenv from data store
› Found 1


  Qiita Test Suite
[network] Response succeeded req=req_59c8b171e7f74c6d8cec4c09d06ecc99 status=200
    ✔ Returns 200 (1019ms)
[network] Response succeeded req=req_f1c6f73cd4f945e89bc6ca5d1acc3386 status=200
    ✔ Returns 200 (206ms)
[network] Response succeeded req=req_77c3a7e52760490fa13707f8348eb084 status=200
    ✔ Returns 200 (210ms)


  3 passing (1s)

ちなみにテストに失敗した時はInsomniaのUIから実行したときと比べてCLIで実行した時の方が細かくエラー情報が表示される。

API Gatewayの設定の生成・チェック

ここでは以下の手順で設定ファイルを生成・チェックしていく。

  1. OpenAPIの仕様をファイル化(inso export spec
  2. OpenAPIの仕様が書かれたファイルからKongの設定ファイルを生成(deck file openapi2kong
  3. Kongと通信して設定ファイルの有効性を検証(deck gateway validate

OpenAPIの仕様をファイル化

以下のコマンドでInsomniaのDB内にあるOpenAPIの仕様をYAMLファイルとして出力する。

inso export spec my-spec.yaml --verbose --output ./my-spec-export.yaml

こちらも--verboseをつけて詳細情報を出すようにしておく。また、--outputオプションがないと標準出力に表示するため、--outputをつけてファイル化する。

OpenAPIの仕様が書かれたファイルからKongの設定ファイルを生成

ここからはdeckコマンドで行う。deckコマンドはKong API Gatewayの設定を管理するためのCLIであり、コマンド仕様がinsoとは少し異なる。
OpenAPIの仕様をKongの設定ファイルに変換するには以下のコマンドを実行する。

 cat my-spec-export.yaml | deck file openapi2kong --inso-compatible --output-file my-spec-kong.yaml --verbose 1

deck file openapi2kongは標準出力を入力として受け取るため、catで吐いたものをパイプで渡す。
また、互換性のため--inso-compatibleをつけることが推奨されているため、こちらも付与しておく。

Kongと通信して設定ファイルの有効性を検証

以下のコマンドで先ほど作成したKongの設定ファイルの検証を行う。

deck gateway validate my-spec-kong.yaml --verbose 1

--verbose 1については、つけておくと確認先のKong Gatewayのアドレスが表示されるので、トラシュ用につけている。
なお、参照先のKong Gatewayのアドレスはデフォルトでhttp://localhost:8001となっている。
これは自PCにKongを立ち上げている人は問題ないが、別の環境にKongを立てている人は--kong-addrもしくは環境変数DECK_KONG_ADDRで変更してあげる必要がある。

API Gatewayの設定の反映

最後にdeck gateway syncで設定を反映する。
ただ、ここではお試しで確認するので、反映後に元に戻せるようにしておきたい。
そのために、先にバックアップを取っておく。バックアップはdeck gateway dumpで取得できる。

deck gateway dump -o backup.yaml

また、設定を適用したらどう変わるかを確認したい場合はdeck gateway diffで確認できる。

$ deck gateway diff my-spec-kong.yaml
creating service Qiita_API
creating route Qiita_API-getitems
creating route Qiita_API-getitemsid
creating route Qiita_API-getuserinfo
Summary:
  Created: 4
  Updated: 0
  Deleted: 0

バックアップや変更点の確認が終わったら、実際にコマンドを実行して設定を反映する。

deck gateway sync my-spec-kong.yaml

問題なければ以下のような出力が得られる。

creating service Qiita_API
creating route Qiita_API-getitems
creating route Qiita_API-getuserinfo
creating route Qiita_API-getitemsid
Summary:
  Created: 4
  Updated: 0
  Deleted: 0

http://localhost:8002を開いてKong ManagerからGateway Servicesを確認すると、Qiita_APIというのが作成されているのがわかる。
1710224465994.png
以下のようにRouteも作成されており、Kong経由でアクセスできるようになった。
1710224567052.png

動作確認

httpコマンドはcurlの上位版みたいなコマンドで、このコマンドの--check-statusがCI/CDパイプラインに使いやすいのでここではこのコマンドで動作確認する。
Kong Gatewayはデフォルトでポート8000にアクセスすることで、先ほどKong ManagerのUIで確認したRoute内のパスにルーティングしてくれるので、以下のような感じで各リクエストを発行する。

http -h --check-status localhost:8000/items
http -h --check-status localhost:8000/items/bf1a1aa9f2a609864e7e
http -h --check-status --auth-type bearer --auth 56738b89775bcxxxxxx localhost:8000/authenticated_user 

-hオプションでヘッダのみ表示し、--check-statusオプションで200以外の値の時に非ゼロでコマンドの戻り値を返すようにしている。
認証のチェックでは--auth-type--authでBearerの指定およびトークン渡しを実施している。
問題なければそれぞれHTTP/1.1 200 OKが返ってくる。
確認が終えたら先程のバックアップを使ってKong Gatewayの設定を元に戻す。

$ deck gateway sync backup.yaml
deleting route Qiita_API-getuserinfo
deleting route Qiita_API-getitems
deleting route Qiita_API-getitemsid
deleting service Qiita_API
Summary:
  Created: 0
  Updated: 0
  Deleted: 4

CLIでの手動確認は以上となる。

GitHubActionsの設定

ここからは先程確認したCLIの内容をGitHubActionsに落とし込んでいく。
最初にBearerのトークンをYAMLに直接書くのを避けるために、トークンをActionsのSecretととして定義する。
リポジトリのSetting->Secrets and variables->ActionsからNew repository secretを選択して値を作成する。
1710226316206.png

次にActionsのYAMLを書いていく。
先ほど用意したリポジトリをCloneしてディレクトリに移動する。

git clone https://github.com/imurata/sandbox-actions.git
cd sandbox-actions

GitHub Actionsは.github/workflowsの下のYAMLを解釈して動作するため、ディレクトリを作成してYAMLをその中に作成する。
ワークフロー構文を見ながら以下のような感じで作成する。

mkdir -p .github/workflows
cat <<'EOF' > ./.github/workflows/push.yaml
name: APIOps Test

on:
  push:
    branches:
      - main

jobs:
  apiops_test:
    runs-on: self-hosted
    name: Test, Generate Config, Deploy
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - name: Linting
        run: inso lint spec my-spec.yaml --verbose
      - name: Testing
        run: inso run test "Qiita Test Suite" --env myenv
      - name: Export OpenAPI Spec
        run: inso export spec my-spec.yaml --verbose --output ./my-spec-export.yaml
      - name: Convert from OpenAPI Spec to Kong Confing
        run: cat my-spec-export.yaml | deck file openapi2kong --inso-compatible --output-file my-spec-kong.yaml --verbose 1
      - name: Validate Kong Config
        run: deck gateway validate my-spec-kong.yaml --verbose 1
      - name: Deploy Kong Config
        run: deck gateway sync my-spec-kong.yaml
      - name: Confirmation
        run: |
          sleep 5
          http -h --check-status GET localhost:8000/items
          http -h --check-status GET localhost:8000/items/bf1a1aa9f2a609864e7e
          http -h --check-status --auth-type bearer --auth ${{secrets.QIITA_API_TOKEN}} GET localhost:8000/authenticated_user
EOF

今回はガチ実装ではなくAPIOps実装の検証目的であるため、PR時の処理は見送った。
基本的に手作業で確認した際のコマンドをコピペしているが、Bearerトークンの箇所だけ${{secrets.QIITA_API_TOKEN}}に置き換えている点に注意。
また、Kongへの設定反映後、アクセスが早すぎるとRouteが作成されていなくてhttpコマンドが失敗するため、sleepを入れている。
あと原因が分からないが、Runnerから実行した場合に限りhttpコマンドがなぜかPOSTで動いて上手く動かなかったので、明示的にGETで動くよう引数を追加している。
上記ファイルを作成後、pushする。

git add .
git commit -m "initial commit"
git push origin main

PushするとGitHub Actionsが動き出し、上記で定義したYAMLの内容が実行される。
1710231498922.png
走りきった後にはKong ManagerからServiceやRouteも確認できる。
1710229804602.png

終わりに

ということで、GitHubの力を借りて自PCだけでAPIOps的なCI/CDパイプラインを実装することが出来た。

と言いつつ、大事な点に触れていなかったことにお気づきだろうか。
OpenAPIのYAMLをGitで管理していないので、パイプラインのトリガーがOpenAPIの仕様変更に対応していなかったことに。
実はInsomniaのVersion8でフリー版からGit Sync機能が削除されたので、Insomnia利用時にAPIの仕様変更契機で直接キックすることが出来なさそう。
Insomniaを使った厳密なGitOpsをやりたい場合はVersion7系を使うか、有料版を使う必要がある点は注意が必要となる。
(※Insomniaを無理して使わず、OpenAPIのYAMLをリポジトリ上で管理してしまえばもっと簡単に実現は出来る)
その点を置いておけば、GitHubをGitLabに置き換えQiitaのAPIをローカルなサービスに置き換えれば、自PCで全て完結するオフラインでも利用可能なAPIOps環境なんかも作れそうなので、それなりに使えそうではある。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0