Deploy function via GitLab Serverless


この投稿はFUJITSU その2 Advent Calendar 2018の22日目の記事です。
(注意)この記事は個人の見解であり、所属する会社、組織を代表するものではありません。

諸事情により投稿が遅くなりました。ごめんなさい。

GitLab Serverlessとは

12月22日にリリースされたばかりのGitLab 11.6の新機能です。
GitLab Serverlessを利用することで、KnativeをインストールしたKubernetes上にGitLab CI経由でサーバレスアプリケーションをデプロイすることができます。

また、サーバレスアプリケーションの管理ツールであるTriggerMeshを利用することでサーバレスアプリケーションデプロイ後の管理もGitLab上から行えるようになっています。

今回はいち早くGitLab Serverlessを利用して実際にアプリケーションをデプロイするところまでやってみたいと思います。

...が、先に言ってしまうと結果としてはCI実行中にエラーが発生し、アプリケーションを実行するまでには至りませんでした。

時間切れとなってしまったので、今後解決でき次第更新したいと思います。

事前準備

GitLab環境構築

Cloud Native GitLab Helm Chartを使ってKubernetesクラスタ上にデプロイしたGitLabで検証しています。

GitLabのk8s連携機能を利用するにあたりGitLab自体をk8sクラスタ上で実行する必要は特にありません。
が、今回はk8s上ですべて完結させてみたかったので上記helmチャートを利用しました。

構築方法は主題ではないので引っかかった部分だけ簡単に説明すると、
今回利用したバージョン1.4.0のhelmチャートが依存しているgitlab-runner helmチャートのバージョン0.1.38では
以下の問題が発生したので、公式ドキュメントの方法にいくつか設定を追加しています。

  • gitlab-runnerのPodがCrashLoopBackOffを繰り返し立ち上がらない
  • CI実行時にa DNS-1123 subdomain must consist of lower case alphanumeric characters...というエラーが発生し失敗する
    • CI JobをPodとして実行できるrunnerの実行方式であるkubernetes executorに問題があり、CI Job実行時に起動するPod名がk8sの命名規則に準拠していないため、GitLabとの通信時のDNS参照に失敗します。
    • gitlab-runner helmチャートのバージョン0.1.38で起動するgitlab-runner:alpine-v11.5.0コンテナイメージに固有の問題のようなので、helm upgradle --install時に明示的に--set gitlab-runner.image=gitlab/gitlab-runner:alpine-v11.5.1以上を指定することで解決します。

これ以降の内容はk8s上でGitLabを実行し、GitLabのk8s連携を有効にしている前提とします。

Knativeをk8sにデプロイする

KnativeはKubernetesのオペレーションやコンテナイメージビルド、デプロイの複雑さを低減し、開発者がコードを書くこと自体により集中できるようにしつつ、コンテナの恩恵を受けたサービス構築を実現するツールです。

また、冒頭にサーバレスアプリケーションをデプロイできるといったとおり、通常Kubernetesではできない、普段Podが0の状態で、トリガーとなるイベント発生時に初めてPodを起動するといったワークロードが実現できます。
Knativeは以下のコンポーネントで構成されています。

  • Build: ソースコードからコンテナイメージをビルド、レジストリへプッシュの一連の作業を実行
  • Eventing: サポートするSubscribe先からのメッセージを受け取る
  • Serving: アプリケーションをデプロイ、公開し、デフォルトではPodが0の状態からリクエストを受けPodのスケーリングを行います。また、サービスメッシュであるIstioによりルーティングを制御します。

GitLab Serverlessを利用するため事前にKubernetesへデプロイしておく必要がありますが、GitLabではKnativeのインストールもGitLabのWeb UI経由で実行できてしまいます。

helm-tillerのデプロイ

GitLab経由でKnativeをデプロイするにあたり、まずはGitLab経由でhelmをインストールする必要があります。

k8sを有効にしたプロジェクトのOperations > Kubernetes Clusters > 連携済みKubernetesクラスタ名の画面からHelm Tillerのinstallを実行します。

しばらくすると表示がinstalledとなります。

ここでk8sのPodを確認すると、gitlab-managed-appsにtiller podがデプロイされていることがわかります。

$ kubectl get pod -n gitlab-managed-apps
NAME                             READY     STATUS    RESTARTS   AGE
tiller-deploy-74f5d65d77-k5pzn   1/1       Running   0          5m

knativeデプロイ

helmインストール時と同じ画面からKnativeをデプロイします。
この際、サーバレスアプリケーションを公開するドメイン名を指定する必要があります。

しばらくすると、表示がinstalledとなり、Knative IP Address欄が追加されます。
ここに表示されているIPアドレスを指定したドメインのワイルドカードDNS Aレコードとして登録しておきます。

このIPアドレスはアプリケーションへのルーティングを担うknative-ingressgatewayコンポーネントのEXTERNAL-IPであり、以下のコマンドでも取得できます。

$ kubectl get svc --namespace=istio-system knative-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip} '
35.243.127.45

ここでk8sクラスタの状態を確認します。
まず、Istioがデプロイされていることが確認できます。

$ kubectl get pod -n istio-system 
NAME                                        READY     STATUS    RESTARTS   AGE
istio-citadel-84fb7985bf-c6vfg              1/1       Running   0          15m
istio-egressgateway-bd9fb967d-mvblb         1/1       Running   0          15m
istio-galley-655c4f9ccd-h5mhx               1/1       Running   0          15m
istio-ingressgateway-688865c5f7-jcqpp       1/1       Running   0          15m
istio-pilot-6cd69dc444-bb77k                2/2       Running   0          15m
istio-policy-6b9f4697d-nbtmz                2/2       Running   0          15m
istio-sidecar-injector-8975849b4-jt8d2      1/1       Running   0          15m
istio-statsd-prom-bridge-7f44bb5ddb-mqqq2   1/1       Running   0          15m
istio-telemetry-6b5579595f-pxrf9            2/2       Running   0          15m
knative-ingressgateway-5f5dc4b4cd-xw2qg     1/1       Running   0          15m

さらに、Knativeのコンポーネントであるknative-buildknative-servingがそれぞれ別のネームスペースにデプロイされています。

$ kubectl get pod -n knative-build
NAME                                READY     STATUS    RESTARTS   AGE
build-controller-65f855ccd6-dgg87   1/1       Running   0          14m
build-webhook-864d45f66f-hdtcs      1/1       Running   0          14m
$ kubectl get pod -n knative-serving
NAME                          READY     STATUS    RESTARTS   AGE
activator-847cf57479-6tzmk    2/2       Running   0          15m
controller-5d7f46bfd6-rbqrf   1/1       Running   0          15m
webhook-7f8ddf4499-hgv49      1/1       Running   0          15m

ここまでで事前準備は完了です。
実際にアプリケーションをデプロイしていきます。

サーバレスアプリケーションのデプロイ

GitLab Serverlessで利用可能なランタイム

現在はnode.jsまたはkanikoがサポートされているようです。
今回はnode.jsアプリケーションを試します。

.gitlab-ci.ymlの作成

以下の内容の.gitlab-ci.ymlをプロジェクトのリポジトリに追加します。
TriggerMeshのCLIであるtmコマンドを実行し、Knativeの機能を利用してコンテナイメージビルド〜デプロイまでを実行するJobを定義しています。

stages:
  - deploy

functions:
  stage: deploy
  environment: test
  image: gcr.io/triggermesh/tm:v0.0.7
  script:
    - tm -n "$KUBE_NAMESPACE" set registry-auth gitlab-registry --registry "$CI_REGISTRY" --username "$CI_REGISTRY_USER" --password "$CI_JOB_TOKEN"
    - tm -n "$KUBE_NAMESPACE" --registry-host "$CI_REGISTRY_IMAGE" deploy --wait

serverless.yamlの作成

GitLab Serverlessでは下記ファイルでKnativeでアプリケーションをデプロイする際のメタ情報を定義するようです。
このあたりがGitLab CI/CDと統合された、ピュアKnativeとは異なるところでしょうか。

アプリケーションのサービス名やビルドするソースコードを格納するディレクトリ、アプリケーションのランタイムなどを定義します。

service: knative-test
description: "Deploying functions from GitLab using Knative"

provider:
  name: triggermesh
  registry-secret: gitlab-registry
  environment:
    FOO: BAR

functions:
  echo:
    handler: echo
    runtime: https://gitlab.com/triggermesh/runtimes/raw/master/nodejs.yaml
    description: "echo function using node.js runtime"
    buildargs:
      - DIRECTORY=echo
  environment:
    FUNCTION: echo

アプリケーションコードをリポジトリに格納

TriggerMeshのサンプルコードのうち、node.jsのサンプルコードをserverless.yamlbuildargsに定義したソース格納先であるechoディレクトリ配下に格納します。

リポジトリにPushしCIを実行

リポジトリにソースコード及びserverless.yaml.gitlab-ci.ymlをPushすることでCIが実行され、Knativeによりアプリケーションのコンテナイメージビルド〜デプロイ、公開までが実行されます。

...のはずでしたが、どうやらGitLabのコンテナレジストリへのコンテナイメージPush時に認証エラーとなっており、CIに失敗してしまいました。

以下はCI Jobのログです。

Running with gitlab-runner 11.5.1 (7f00c780)
  on gitlab-gitlab-runner-5c7d4bfbdf-bvqbj EnNm5sja
Using Kubernetes namespace: default
Using Kubernetes executor with image gcr.io/triggermesh/tm:v0.0.7 ...
Waiting for pod default/runner-ennm5sja-project-1-concurrent-08fk4p to be running, status is Pending
Waiting for pod default/runner-ennm5sja-project-1-concurrent-08fk4p to be running, status is Pending
Waiting for pod default/runner-ennm5sja-project-1-concurrent-08fk4p to be running, status is Pending
Waiting for pod default/runner-ennm5sja-project-1-concurrent-08fk4p to be running, status is Pending
Waiting for pod default/runner-ennm5sja-project-1-concurrent-08fk4p to be running, status is Pending
Waiting for pod default/runner-ennm5sja-project-1-concurrent-08fk4p to be running, status is Pending
Running on runner-ennm5sja-project-1-concurrent-08fk4p via gitlab-gitlab-runner-5c7d4bfbdf-bvqbj...
Cloning repository...
Cloning into '/k-srkw/serverless-test'...
Checking out 6a038601 as master...
Skipping Git submodules setup
$ tm -n "$KUBE_NAMESPACE" set registry-auth gitlab-registry --registry "$CI_REGISTRY" --username "$CI_REGISTRY_USER" --password "$CI_JOB_TOKEN"
Registry credentials set
$ tm -n "$KUBE_NAMESPACE" --registry-host "$CI_REGISTRY_IMAGE" deploy --wait
Creating knative-test-echo function
Deployment started. Run "tm -n serverless-test-1 describe service knative-test-echo" to see the details
Waiting for ready state....2018/12/23 18:32:09 Revision "knative-test-echo-00001" failed with message: "no token in bearer response:\n{\"errors\":[{\"code\":\"DENIED\",\"message\":\"access forbidden\"}],\"http_status\":403}".
ERROR: Job failed: command terminated with exit code 1

実際にアプリケーションがデプロイされるとプロジェクトのWeb UI上のOperations > Serverlessからfunction名やURLが確認できるのですが、その登録自体は行われていました。

ただし、アプリケーションはデプロイできていないため、アクセスはできませんでした。

今回はここまで。
上記問題が解決できたら更新したいと思います。