「Shipwright」コンテナビルドのフレームワーク

今回はRed Hat 北山(OpenShiftのSA担当)がお届けします。
もうすぐKubeCon NAの季節ですね。ちなみに前夜祭(Oct 12th)にOpenShift Commons Gatheringが開催されますよっ!!

commons.openshift.org

いつもアドベントカレンダーの季節くらいしかブログは書かないのですが、ちょうど Kubernetes Meetup Tokyo #45でLT の機会を頂いたので、その時間で触れられなかった内容を簡単にご紹介できればと思います。
今回は、まだあまり聞き慣れない Shipwright プロジェクトについてのご紹介です。

speakerdeck.com

なお、まだ始まったばかりのプロジェクトなので、動向や紹介内容も温かい目で御覧くださいw

OpenShift Build v1

まずは、これまでのOpenShiftのビルド機構についておさらい。

OpenShift BuildConfigs

ところで皆さま、OpenShiftの BuildConfig って使用されていますか?
BuildConfig はOpenShiftにある、コンテナイメージの作成プロセスを自動化してくれる便利な独自機能の一つです。たとえば、Gitリポジトリ上のソースコード更新を契機に、Jenkinsパイプラインを動作させるといったビルドも、OpenShiftではこの BuildConfig を設定します。
独自機能と言いましたが、実体は BuildConfig というカスタムリソースです。

kind: BuildConfig
apiVersion: build.openshift.io/v1
metadata:
  name: "ruby-sample-build" 
spec:
  runPolicy: "Serial" 
  triggers: 
    - type: "GitHub"
      github:
        secret: "secret101"
    - type: "ImageChange"
  source: 
    git:
      uri: "https://github.com/openshift/ruby-hello-world"
  strategy: 
    sourceStrategy:
      from:
        kind: "ImageStreamTag"
        name: "ruby-20-centos7:latest"
  output: 
    to:
      kind: "ImageStreamTag"
      name: "origin-ruby-sample:latest"

ここで重要なフィールドは、以下の3つ。

  • sourceフィールド : ビルドリソースに応じて、コードのGitリポジトリ、Dockerfile、またはバイナリのペイロードの場所を指定
  • strategyフィールド : ビルドの実行に使用するビルド戦略(Docker Build, s2i Build, Custom Build, Pipeline Build)を記述
  • outputフィールド : ビルドされたコンテナイメージをプッシュするレジストリを指定

これらを使用することで、コンテナビルドにおける作業の簡略化を行っています。
BuildConfig はOpenShift v3の頃からあるリソースであり、クラウドネイティブ黎明期にはKubernetes上でコンテナをどのようにビルドするのかという観点で、先陣を切るビルド機構でした。

BuildConfigの課題

しかし、Kubernetesが普及してきたと同時に、ビルドに関する機構も多様化したことから、OpenShiftのBuildConfigだけでは標準化としての課題がいくつかでてきました。

  • BuildConfig自体がOpenShift独自の機能(リソース)になってしまっている。(他のKubernetesとの親和性が薄い)
  • BuildConfigstrategyフィールドで利用できるビルド方式が固定される。
  • BuildConfigに(Jenkinsの呼び出しや事後処理など)複数の機能/役割をもたせてしまっている。

これらを払拭すべく、OpenShiftでは次世代のクラウドネイティブなBuild機構 (OpenShift Builds v2) の導入検討が始まっています。

f:id:shkitayama:20210927134151p:plain
OpenShift Roadmap

引用: What's New in OpenShift 4.8 - Speaker Deck

この候補に挙げられているのが、 Shipwright です。

Shipwrightの概要

Shipwright Logo

ShipwrightとはKubernetes上でコンテナイメージをビルドするための拡張可能なフレームワークです。
開発者に、コンテナツールの予備知識を必要とせず、最小限のYAMLを定義することでコンテナイメージを構築するためのアプローチを提供しています。
このYAMLを定義することによって、ビルドツールに依存せずに同じ書式(YAML形式)で好きなビルドツール利用してコンテナイメージをつくることができます。

f:id:shkitayama:20210927142802p:plain
Shipwright overview

すでにThe Continuous Delivery Foundation(CDF)のInqubationプロジェクトとして登録されています。

Shipwightのアーキテクチャ

Shipwrightは、Kubernetes Operator パターンで提供されており、カスタムリソースとカスタムコントローラーからできています。ただし、BuildConfig のようにOpenShift独自のコントローラーではなく、どのKubernetes上にも展開できるOperatorとして提供されています。

OperatorHub.ioへの登録も始まっています。
OperatorHub.io | The registry for Kubernetes Operators

Shipwrightが管理するカスタムリソースは、主に以下の4つ。(2021/09時点)

  • Build リソース: ビルド戦略、ビルド対象のソース(Gitリポジトリ)、ビルド後のイメージ出力先(コンテナレジストリ)を指定
  • BuildRun リソース: イメージのビルドを実行する
    (≒Tekton PipelinesのTaskRunを生成する)
  • BuildStrategy リソース(ClusterBuildStrategyもある) : ビルドツールに応じた処理内容を抽象化する
    (≒Tekton PipelinesのTaskのStepを定義する)

f:id:shkitayama:20210927165035p:plain
Shipwright architecture

Shipwrightは、バックエンドでTekton PipelinesのTaskRunを利用することでビルド処理を行っています。
基本は Build オブジェクトにビルド定義を行い、BuildRun オブジェクトをつくるとTekton Pipelinesによってビルド処理が実行されます。この際にビルド機構( BuildStrategyオブジェクト )を予め登録しておくことで、使いたいビルド戦略(ビルドツール)を Build オブジェクト内に定義できます。

Shipwrightの導入

ということで、新しいツールは実装してみるのが一番理解が早いので触ってみましょう。手順としては以下の順番で行います。

  1. Tekton Pipelinesのインストール
  2. Tekton Pipelines CLIのインストール
  3. Shipwrightのインストール

1. Tekton Pipelinesのインストール

まずは、Shipwrightで利用するTekton Pipelinesを入れておきます。

OpenShiftにTekton Pipeliensをインストールする場合

$ oc new-project tekton-pipelines
$ oc adm policy add-scc-to-user anyuid -z tekton-pipelines-controller
$ oc adm policy add-scc-to-user anyuid -z tekton-pipelines-webhook
$ export TEKTON_PIPELINE_VERSION=v0.27.3
$ oc apply -f https://storage.googleapis.com/tekton-releases/pipeline/previous/${TEKTON_PIPELINE_VERSION}/release.notags.yaml

OperatorHubからのインストール1も可能です。
KubernetesにTekton Pipeliensをインストールする場合

$ kubectl create namespace tekton-pipelines
$ export TEKTON_PIPELINE_VERSION=v0.27.3
$ kubectl apply -f https://storage.googleapis.com/tekton-releases/pipeline/previous/${TEKTON_PIPELINE_VERSION}/release.yaml

2. Tekton Pipelines CLIのインストール

Tekton Pipelinesのオブジェクト管理は専用の tkn コマンドを使うと便利です。合わせてインストールしておきましょう。

$ export TEKTON_CLI_VERSION=v0.20.0
$ curl -LO https://github.com/tektoncd/cli/releases/download/${TEKTON_CLI_VERSION}/tkn_${TEKTON_CLI_VERSION#v}_Linux_x86_64.tar.gz
$ sudo tar xvzf tkn_${TEKTON_CLI_VERSION#v}_Linux_x86_64.tar.gz -C /usr/local/bin/ tkn
$ tkn version
Client version: 0.20.0
Pipeline version: v0.27.3

3. Shipwrightのインストール

ShipwrightのControllerは、shipwright-buildnamespacesにインストールされるため、あらかじめ設定しておきます。

$ kubectl create namespace shipwright-build
## OpenShiftの場合のみSCCの設定
$ oc adm policy add-scc-to-user anyuid -z shipwright-build-controller -n shipwright-build

次にShipwrightのCRDとControllerをインストール。

$ kubectl apply -f https://github.com/shipwright-io/build/releases/download/v0.5.1/release.yaml

以上でShipwrightの設定は完了です。

Shipwrightの利用

いよいよShipwrightのオブジェクトを登録してビルドを開始しましょう。今回は sample-shipwright namespaceという名前空間でビルドを行います。なお、kubens2は事前に入れておいてくださいね。

$ kubectl create namespace sample-shipwright
$ kubens sample-shipwright

OpenShift上で実装する場合は、SCCの設定から始めてください。

$ oc adm policy add-scc-to-user anyuid -z default -n sample-shipwright
$ oc create serviceaccount pipeline -n sample-shipwright
$ oc adm policy add-scc-to-user privileged -z pipeline -n sample-shipwright
$ oc adm policy add-role-to-user edit -z pipeline -n sample-shipwright

Shipwrightの利用は、以下の手順で行います。

  1. BuildStrategyオブジェクトの登録
  2. Buildオブジェクトの登録
  3. BuildRunオブジェクトの登録

a. BuildStrategyオブジェクトの登録

はじめにBuildStrategyオブジェクトを設定します。基本のBuildStrategyオブジェクトには以下のような種類があります。

BuildStrategyオブジェクトは各名前空間内に設定できますが、クラスター全体でビルド機構を再利用する場合はClusterBuildStrategyオブジェクトに設定します。 そして、今回はこのClusterBuildStrategyオブジェクトを登録します。 なお、基本のClusterBuildStrategyオブジェクトは、プロジェクトに用意 されており、一つ一つ登録せずとも1コマンドですべて登録が可能です。

$ kubectl apply -f https://github.com/shipwright-io/build/releases/download/nightly/default_strategies.yaml
$ kubectl get cbs --show-kind=true
NAME
clusterbuildstrategy.shipwright.io/buildah
clusterbuildstrategy.shipwright.io/buildkit
clusterbuildstrategy.shipwright.io/buildpacks-v3
clusterbuildstrategy.shipwright.io/kaniko
clusterbuildstrategy.shipwright.io/ko
clusterbuildstrategy.shipwright.io/source-to-image

BuildStrategyオブジェクトの内容

ちなみに、このBuildStrategyオブジェクトの中身を見ると、Tekton PipelinesのTaskの内容ととても似ていることがわかります。つまり、各Step(buildStep)でTekton PipelinesのTaskRunを生成するための内容が定義されています。

apiVersion: shipwright.io/v1alpha1
kind: ClusterBuildStrategy
metadata:
  name: kaniko
spec:
  buildSteps:
    - name: build-and-push
      image: gcr.io/kaniko-project/executor:v1.6.0
      workingDir: $(params.shp-source-root)
      …
      command:
        - /kaniko/executor
      args:
        - --skip-tls-verify=true
        - --dockerfile=$(build.dockerfile)
        - --context=$(params.shp-source-context)
        - --destination=$(params.shp-output-image)
        - --oci-layout-path=/kaniko/oci-image-layout
        - --snapshotMode=redo
        - --push-retry=3
      resources:
        limits:
          cpu: 500m
          memory: 1Gi
        requests:
          cpu: 250m
          memory: 65Mi

b. Buildオブジェクトの登録

さて、つぎにBuildオブジェクトを登録しましょう。

認証情報の登録

あらかじめビルド時に使用するGitリポジトリやコンテナレジストリ用の認証情報が必要です。通常のPullSecret同様に認証情報をSecret形式で登録すればよいのですが、このSecretを確認するのは最終的にTekton Pipelinesです。したがって、Tekton Pipelinesで認識できる形でSecret登録しておくと認証が行われます。
ここではAccess Tokenを使ったBasic認証を行っています。

## GitLab.comの場合
$ export REPOSITORY_SERVER=gitlab.com
$ export REGISTRY_SERVER=registry.gitlab.com
$ export REGISTRY_REPO=<your registory repo name>
$ export REGISTRY_USER=<your registry name>
$ export REGISTRY_PASS=<your password>

$ cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
  name: gitlab-token
  annotations:
    tekton.dev/git-0: https://${REPOSITORY_SERVER}
    tekton.dev/docker-0: https://${REGISTRY_SERVER}
    build.shipwright.io/referenced.secret: "true"
type: kubernetes.io/basic-auth
stringData:
  username: ${REGISTRY_USER}
  password: ${REGISTRY_PASSWORD}
EOF

Buildオブジェクトの内容

Buildオブジェクトには以下の3つを定義します。

  • sourceフィールド: ビルド対象のソースコード、またはDockerfileが入ったGitリポジトリの指定
  • strategyフィールド: ClusterBuildStrategyで登録したビルド機構の指定
  • outputフィールド: ビルド後のコンテナイメージ格納先の指定

これらに先程登録した認証情報を関連付けます。

$ cat <<EOF | kubectl apply -f -
apiVersion: shipwright.io/v1alpha1
kind: Build
metadata:
  name: go-tutorial-kaniko
spec:
  source:
    url: https://github.com/shipwright-io/sample-go
    contextDir: docker-build
  strategy:
    name: kaniko
    kind: ClusterBuildStrategy
  output:
    image: ${REGISTRY_SERVER}/${REGISTRY_USER}/${REGISTRY_REPO}/go-tutorial
    credentials:
      name: gitlab-token
EOF

登録されているかを確認しておきましょう。ここでOpenShiftを利用している方は、APIの呼び出しに気をつけて下さい。OpenShiftにはもともとBuildConfig用のリソース(Build v1=builds.config.openshift.io)があるため、$ kubectl get buildsとしても出力されません。

$ kubectl get crd |grep builds
builds.config.openshift.io                                        2021-09-23T06:36:15Z
builds.shipwright.io                                              2021-09-23T08:15:09Z
…
$ kubectl get builds.shipwright.io go-tutorial-kaniko
NAME                 REGISTERED   REASON      BUILDSTRATEGYKIND      BUILDSTRATEGYNAME   CREATIONTIME
go-tutorial-kaniko   True         Succeeded   ClusterBuildStrategy   kaniko              4d2h

c. BuildRunオブジェクトの登録

最後にBuildRunオブジェクトを登録し、ビルドを実行します。BuildRunオブジェクトでは、buildRefフィールドで先程登録したBuildオブジェクトを指定します。

$ cat <<EOF | kubectl create -f -
apiVersion: shipwright.io/v1alpha1
kind: BuildRun
metadata:
  generateName: go-tutorial-kaniko-run-
spec:
  buildRef:
    name: go-tutorial-kaniko
EOF

これらを登録するとTekton TaskRunによるビルドが始まり、うまくいくとBuildオブジェクトで指定したコンテナレジストリにイメージが格納されます。

$ tkn taskrun list
NAME                                       STARTED          DURATION     STATUS
go-tutorial-kaniko-run-w4ssd-z446p         13 seconds ago   ---          Running
$ tkn taskrun logs -L
…
[build-and-push] INFO[0114] COPY --from=build /tmp/helloworld ./helloworld
[build-and-push] INFO[0114] Taking snapshot of files...
[build-and-push] INFO[0114] ENTRYPOINT [ "./helloworld" ]
[build-and-push] INFO[0114] EXPOSE 8080
[build-and-push] INFO[0114] cmd: EXPOSE
[build-and-push] INFO[0114] Adding exposed port: 8080/tcp
[build-and-push] INFO[0114] Pushing image to registry.gitlab.com/<your registry name>/shipwright-tutorial/go-tutorial
[build-and-push] INFO[0122] Pushed image to 1 destinations

ビルドが成功したら、自身のコンテナレジストリからも確認を行って見てください。

f:id:shkitayama:20210928000316p:plain
GitLab Contaner Registry

今回はKanikoを使ってBuildを行いましたが、BuildオブジェクトのClusterBuildStrategyを切り替えると、別の方式でビルドが可能です。
またTekton PipelinesのTaskRunの内容を見るとShipwrightの仕組みがよくわかります。BuildRunオブジェクトはTekton PipelinesのTaskオブジェクトは生成せずに、TaskRunのspec.taskSpecフィールドを使って、TaskRunオブジェクトを生成しています。

$ tkn task list
No Tasks found
## TaskRunの名前を指定
$ kubectl get taskrun go-tutorial-kaniko-run-w4ssd-z446p -o yaml |less

つまり、このTaskRunの生成元がBuildStrategyオブジェクトということです。そのため、BuildStrategyオブジェクト自体をカスタマイズすると、いろんなビルドに対応することが可能です。
このあたりのカスタマイズは、参考資料のProject Shipwright In Depthも合わせて御覧ください。

まとめ

ということで、Shipwrightの導入についてご紹介しました。すこし興味をもっていただけたでしょうか?

f:id:shkitayama:20210927211648p:plain

今後のOpenShift Roadmapで紹介される OpenShift Builds v2 についても、楽しみにしておいてください!!

参考資料

最後に宣伝

最後にとてもだいぢなお知らせをしておきます。ちなみに、ここが一番重要です。
Tekton / Argoを使ったCI/CDの新しい本が出るので、是非お買い求めください。

www.amazon.co.jp

いじょ。またねー。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。