LoginSignup
0
0

GitLab CI/CDを使ってOASからKong Gatewayの設定を更新する

Posted at

API Gatewayの1つであるKong Gatewayは設定内容をYAMLで定義でき、deckコマンドというCLIで反映することが出来る。
またdeckコマンドはOpen API Specification(以下OAS)形式で書かれたAPI仕様をKong Gatewayの設定値に変更することが出来る。
これを利用すると、OAS形式でAPIの仕様を作成することでKong Gatewayの設計を個別にしなくてもそのままGatewayに反映する事ができる。
またAPIクライアントであるInsomnia上でOASを作成すると、そのCLIであるinsoコマンドでテストすることが出来る
これにより、API開発者が実装したAPIの仕様をテストからデプロイまでコマンドラインのみで行う事ができる。
これをGitOpsの仕組みと組み合わせると、以下のような感じでPush契機で反映させることも可能。
20240506164139.png

このフロー(APIOps)を実現する際、GitHub Actionsを使った例は見かけるのだがGitLabを使った例はなかったので試してみた。
なお、検証に使ったファイルはこちらに置いている。

検証内容

ここでは以下を実現するパイプラインを作成し、動作確認を行う。

Merge Request(PR)時

  1. Linterの実行(inso lint spec)
  2. OASをdeck形式のYAMLに変換(deck file openapi2kong)
  3. YAMLの検証(deck gateway validate)
  4. 現環境とのdiffの取得(deck gateway diff)

Merge時

  1. OASをdeck形式のYAMLに変換(deck file openapi2kong)
  2. YAMLの検証(deck gateway validate)
  3. 現環境とのdiffの取得(deck gateway diff)
  4. Kong Gatewayに設定を反映(deck gateway sync)

本来はInsomniaによるOASのテストなども含めることが出来るが、パイプラインが肥大化するためテストはシンプルにLintのみに留めた。

前提

検証環境については以下が既に用意されているものとする。

  • Kubernetes
  • GitLab
  • GitLab Runner
  • Kong Gateway

GitLab、GitLab Runnerの準備についてはこちらの方法で用意した。
ただし、RunnerについてはRun untagged jobsを指定してタグなしでも動くようにしている。
Kong Gatewayについてはこちらの方法で用意した。

検証時のファイル構成

以下のファイルを作成して検証する。

.
├── .gitlab-ci.yml
├── k8s
│   ├── 00_httpbin-ns.yaml
│   ├── 01_httpbin-deploy.yaml
│   └── 02_httpbin-svc.yaml
└── openapi
    └── httpbin.yaml

ファイルはそれぞれ以下の通り。

  • .gitlab-ci.yml:GitLabで使うパイプラインの定義
  • k8s/:APIの発行先となるサンプルアプリケーション(httpbin)のManifest(アプリのコードは含まない)
  • openapi/httpbin.yaml:OAS形式のAPI仕様書

k8s/に関しては、今回はManifest修正契機でのGitOpsの動作確認は対象外なのでこのリポジトリ内に含める必要はない。(将来的にこちらもGitOpsで使いたいので入れただけ)

ファイルの作成

リポジトリ内にManifestとOASのYAML、パイプラインのYAMLを作成する。
リポジトリとしては今回以下のリポジトリを作成した。

  • リポジトリ名:gitlab-apiops-test
  • 公開範囲:Private

リポジトリのメンバーにRunnerのトークンと紐づくメンバー(Administrator含む)を追加しておくこと。
追加していない場合、Runnerが以下のメッセージを出力して失敗する。

remote: You are not allowed to download code from this project.
fatal: unable to access 'https://gitlab.eks.xxxx.com/imurata/gitlab-apiops-test.git/': The requested URL returned error: 403

作成後、リポジトリをCloneしてディレクトリ内に移動する。

MYHOST=gitlab.eks.xxxx.com
git clone https://${MYHOST}/imurata/gitlab-apiops-test.git
cd gitlab-apiops-test

サンプルAPIアプリのManifestの作成

APIを使ったアプリケーションを用意する。
ここではhttpbinと同等の動きをするコンテナを利用し、このPodをAPIアプリケーションとして利用する。
k8sというディレクトリを作成し、Namespace,Deployment,Serviceを作成する。

mkdir k8s
kubectl create ns httpbin --dry-run=client -o yaml | kubectl neat -f - > k8s/00_httpbin-ns.yaml
kubectl create deploy -n httpbin --image kennethreitz/httpbin httpbin -r 1 --dry-run=client -o yaml | kubectl neat -f - > k8s/01_httpbin-deploy.yaml
kubectl create svc clusterip -n httpbin httpbin --tcp=80:80 --dry-run=client -o yaml | kubectl neat -f - > k8s/02_httpbin-svc.yaml

上記のManifestにより、httpbinというDeploymenthttpbinというNamespace上に作成され、Service httpbinによって公開される

起動して動作確認する。
最初にクラスタ上にリソースを展開する。

kubectl apply -f k8s/

適当なPodを立ち上げてhttpbin.httpbin.svcにアクセスしてみる。

$ kubectl run curl --image centos --rm -it --restart=Never -- curl httpbin.httpbin.svc/ip
{
  "origin": "192.168.55.194"
}

httpbinは/ipにアクセスするとアクセス元のIPを返すので問題なさそうだ。

OASの作成

シンプルにGETPOSTのみ確認するためのエンドポイントを定義する。

mkdir ./openapi
cat <<EOF > ./openapi/httpbin.yaml
openapi: 3.0.0
info:
  title: httpbin
  version: "0.1"
paths:
  /get:
    get:
      operationId: /get
      responses: 
        "200":
          description: "get: response 200"
  /post:
    post:
      operationId: /post
      responses: 
        "200":
          description: "post: response 200"
servers:
  - url: http://httpbin.httpbin.svc
EOF

servers.urlはKong GatewayのDataPlaneがKubernetes内にいることを想定してService経由でアクセスしているが、Kubernetesクラスタ外に存在する場合はtype: LoadBalancerIngress等を利用してData Plane(Proxy)を外部に公開し、そのURLを指定する必要がある。
responsesについては記載がなくてもdeckコマンドでconvert出来るが、inso lintでエラーになってパイプラインが止まるため足している。
作成したらinso lintdeck file openapi2kongが通ることを確認する。

inso lint spec openapi/httpbin.yaml
deck file openapi2kong -s openapi/httpbin.yaml

inso lintに関してはWarningが表示されるが、コマンドの戻り値はゼロなのでパイプライン的には問題ない。

パイプラインの定義(.gitlab-ci.yml)の作成

以下のパイプラインを作成する。

Merge Request時:
20240509160328.png

MainへのMerge時:
20240509154831.png

以下、.gitlab-ci.ymlの内容となる。

cat <<EOF > ./.gitlab-ci.yml
stages:
- test
- convert
- valid
- deploy

variables:
  DECK_KONG_ADDR: https://kong.aws.unohude.info/api
  DECK_TLS_SKIP_VERIFY: true
  DECK_HEADERS: "kong-admin-token:$CICD_KONG_ADMIN_TOKEN"
  WORKSPACE: "httpbin"

lint:
  rules:
  - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    changes:
    - openapi/**/*
  image: kong/inso
  stage: test
  script:
  - |
    set -x 
    echo "Lint OAS spec."
    inso lint spec openapi/httpbin.yaml

convert-deck:
  rules:
  - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    changes:
    - openapi/**/*
  - if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
    changes:
    - openapi/**/*
  image: kong/deck
  stage: convert
  script:
  - |
    set -x
    echo "Convert OAS to deck YAML."
    deck file openapi2kong -s openapi/httpbin.yaml -o deck.yaml
  artifacts:
    paths:
      - deck.yaml

valid-deck:
  rules:
  - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    changes:
    - openapi/**/*
  - if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
    changes:
    - openapi/**/*
  image: kong/deck
  stage: valid
  script:
  - |
    set -x
    echo "Validate deck.yaml"
    deck gateway validate deck.yaml

diff-deck:
  rules:
  - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    changes:
    - openapi/**/*
  - if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
    changes:
    - openapi/**/*
  image: kong/deck
  stage: valid
  script:
  - |
    set -x
    echo "Diff deck.yaml and Gateway"
    deck gateway diff deck.yaml --workspace $WORKSPACE

deploy:
  rules:
  - if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
    changes:
    - openapi/**/*
  image: kong/deck
  stage: deploy
  script:
  - |
    set -x
    echo "Deploy"
    deck gateway sync deck.yaml --workspace $WORKSPACE

以下、.gitlab-ci.yml内のポイントを解説する。

variables:
  DECK_KONG_ADDR: https://kong.aws.unohude.info/api
  DECK_TLS_SKIP_VERIFY: true
  DECK_HEADERS: "kong-admin-token:$CICD_KONG_ADMIN_TOKEN"
  WORKSPACE: "httpbin"

variablesではdeckコマンド利用時に必要な環境変数を定義する。DECK_KONG_ADDRはAdminAPIのURLで、コマンドの引数やファイルでも渡すことが可能だが、環境変数でも渡せるためここでは環境変数で渡している。
DECK_TLS_SKIP_VERIFYについてはKong Ingress Controller(KIC)で自己証明書を使って構築しているため、証明書のチェックをスキップするために指定する。
DECK_HEADERSはKong GatewayをRBACを有効化して起動しており、認証を突破するためにkong-admin-token:でAdminのTokenを渡している。
TokenはCICD_KONG_ADMIN_TOKENというリポジトリのCI/CD設定(Settings->CI/CD->Variables)で指定して、リポジトリのファイル内に残らないようにしている。
RBACを使っていない人はDECK_HEADERSは指定しなくてもよい。
WORKSPACEはKong GatewayのWorkspace名を設定している。
事前に作っておく必要はなく、なければdeck gateway sync時に自動で生成される。

lint:
  rules:
  - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    changes:
    - openapi/**/*

Lint処理はMerge Request発生時のみ動かしたいので"merge_request_event"の条件で発動条件を絞っている。
また、changesで更新対象のディレクトリを絞り、OASの更新以外では動かないようにしている。

convert-deck:
   :(省略)
  - if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "push"'
    changes:
    - openapi/**/*

Lint以外のJobはmainブランチへのMergeのタイミングでも動いて欲しいため、条件を足している。

  artifacts:
    paths:
      - deck.yaml

deck file openapi2kongで変換したdeck用のYAMLは後続のJobでも利用するため、Artifactsで引き継ぐようにしている。
パイプラインからデプロイした環境に問題があった場合はこのArtifactsをDownloadしてトラブルシュートに利用することになる。

全てのファイルを作成し終わったらファイルをPushする。

git add -A
git commit -m "initial commit"
git push

動作確認

ブラウザでササッと確認する。
GitLabのUIからリポジトリのhttpbin.yamlを開き、Edit->Edit single fileから編集モードに移行し、
version: "0.1"version: "0.2"に変更する。
Commitの際にTarget Branchmainから適当なブランチ名に変更し、Start a new merge request with these changesにチェックを入れてCommit changesをクリックする。
20240509164022.png

次にMerge Requestの作成画面に移行するのでCreate merge requestをクリックする。
クリックするとパイプラインが動き出す。
20240509164327.png
しばらく待つと問題なければpassedという表示とともに全てがグリーンとなる。
20240509164513.png

それぞれのJobをクリックすれば、実行内容も確認できる。
20240509164736.png
また、OASからdeck形式に変換されたYAMLはJobconvert-deckからのartifactsから確認できる。
20240509164952.png

Merge Requestのパイプライン実行後、Merge Requestの画面からMergeボタンをクリックしてMergeを実行する。
すると今度はMerge用のパイプラインが起動する。
20240509165159.png

こちらもしばらく待てば全てグリーンになり、Jobdeployの結果を見ることでdeck gateway syncによりKong Gateway側に反映されたことが分かる。
20240509165515.png

Kong GatewayのUIからもRouteが設定されていることが確認できる。
20240509165644.png

実際にアクセスしてみる。

PROXY_URL=xxx.us-east-1.elb.amazonaws.com
curl -X GET $PROXY_URL/get
curl -X POST $PROXY_URL/post

それぞれ正常なレスポンスが取得できると思う。
また、今回は/get/postしか実装しなかったため、/ipなどでは以下の様にRouteが見つからずエラーとなる。

$ curl -X GET $PROXY_URL/ip
{
  "message":"no Route matched with those values",
  "request_id":"b6b4a717abd07c19b6d6a873d6a27849"
}

こちらも期待した動作となった。

ということで、GitLabのCI/CDの仕組みを使ってKong GatewayのAPIOpsが実現出来ることが確認できた。

0
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
0
0