こんにちは、Red Hatでソリューションアーキテクトをしている石川です。
以前に本ブログでOpenShiftで利用可能なCIパイプラインであるTektonについてご紹介しましたが、今回はTektonを使ってCIパイプラインの中でコンテナイメージを外部のレジストリにコピーする方法についてご紹介させて頂こうと思います。
以前の記事は以下となります。 Tektonの基本やインストール方法について知りたいという方はこちらの記事をご参照下さい。 rheb.hatenablog.com rheb.hatenablog.com
なぜイメージのコピーが必要か?
最近ではOpenShiftをシステムの本番環境だけでなく、開発環境、ステージング環境と、一つのシステムであっても複数のクラスタをご利用頂くケースが多くなっています。
OpenShiftにはクラスタの内部にイメージを保管するレジストリがあるのですが、ステージング環境や本番環境で動かすアプリケーションのイメージを個々のクラスタの内部レジストリから取ってこようとする場合、あらかじめ開発環境でビルドしたイメージをそれぞれの環境にコピーしておく必要があります。
コピーを行う方法として、Podmanなどを使い手動でpullとpushを繰り返す方法も考えられますが、新規のコンテナイメージが作られるたびにこうした作業を繰り返すのは手間がかかり、また人手による操作だと作業ミスが発生する可能性もあります。
そのため本記事ではOpenShift Pipelines(Tekton)とskopeoを使ってCIパイプラインの中でイメージコピーの仕組みを実装していきたいと思います。
利用するパイプライン
今回利用するパイプラインとして以下を準備しました。
それぞれのTaskでは以下を行なっています。
・git-clone: コンテナ化するアプリケーションのソースコードの取得
・s2i-nodejs: コンテナのビルドと内部レジストリへのプッシュ
・skopeo-copy: コンテナイメージの別クラスタへのコピー
コンテナイメージのコピーについてはskopeoを使用します。
skopeoはコンテナイメージを複数のレジストリ間で移動させたいときに便利なツールです。
デーモンレスで動作し、かつコピーの実行にあたってroot権限が不要など、気軽に利用することができます。
skopeoの利用方法についてはこちらのブログが参考になると思います。
さて今回はこのskopeoをTektonのTaskとして利用するわけですが、OpenShiftではOpenShift Pipelines Operatorをインストールする際に、クラスタ全体で利用可能なClusterTaskの一つとしてskopeo-copy
がインストールされます。そのため今回はそちらを使ってPipelineを作成します。
実際に作成したPipelineのyamlは以下となります。(クリックで展開)
skopeo-pipeline.yaml
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: skopeo-pipeline
spec:
workspaces:
- name: shared-workspace
- name: tmp-vol
params:
- name: git-url
type: string
- name: git-revision
type: string
- name: target-path
type: string
- name: dest-imageurl
type: string
tasks:
- name: git-clone-health
taskRef:
name: git-clone
kind: ClusterTask
params:
- name: url
value: $(params.git-url)
- name: revision
value: $(params.git-revision)
workspaces:
- name: output
workspace: shared-workspace
- name: build-container
taskRef:
name: s2i-nodejs
kind: ClusterTask
runAfter:
- git-clone-health
workspaces:
- name: source
workspace: shared-workspace
params:
- name: PATH_CONTEXT
value: $(workspaces.source.path)/$(params.target-path)
- name: IMAGE
value: image-registry.openshift-image-registry.svc:5000/source/health-record:latest
- name: copy-image
taskRef:
name: skopeo-copy
kind: ClusterTask
runAfter:
- build-container
workspaces:
- name: images-url
workspace: tmp-vol
params:
- name: srcImageURL
value: 'docker://image-registry.openshift-image-registry.svc:5000/source/health-record'
- name: destImageURL
value: $(params.dest-imageurl)
- name: srcTLSverify
value: 'false'
- name: destTLSverify
value: 'false'
環境の準備
今回は2つのOpenShiftクラスタを準備し、その間でイメージのコピーを行います。
一つはAWSのオハイオ(us-east-2)リージョンに構築したROSAクラスタで、もう一つは東京(ap-northeast-1)リージョンにIPIで構築したOCPクラスタです。
ROSAクラスタ側でCIパイプラインを実行し、OCP IPIクラスタの内部レジストリにイメージをコピーしたいと思います。
クラスタを構築したらすぐにイメージのコピーができるかというとそうではなく、事前に以下の三点の準備が必要となります。
1. それぞれのクラスタでNamespaceを作成しておく
2. あて先となるクラスタ(OCP IPI側)でレジストリを外部公開しておく
3. あて先のレジストリの認証情報を取得し、CIパイプライン実行時のServiceAccountに紐付ける
1. それぞれのクラスタでNamespaceを作成しておく
CI実行を行うクラスタにはsource
、あて先のクラスタではdestination
というNamespaceをそれぞれ作成し、これらを使っていきます。
それぞれのクラスタに対して、oc new-project
コマンド、もしくはOpenShiftのコンソールを使ってこれらのNamespaceを作成しておきましょう。
2. あて先となるクラスタでレジストリを公開しておく
IPIで作成されたOCPクラスタではデフォルトだと内部レジストリが外部に公開されていないため、コンテナイメージをPushすることができません。
そのため以下のコマンドを実行し、レジストリの外部公開を行うよう設定を変更します。
oc patch configs.imageregistry.operator.openshift.io/cluster --patch '{"spec":{"defaultRoute":true}}' --type=merge
設定が反映されるとopenshift-image-registry
のNamespaceにて、外部公開用のRouteを確認することができます。
レジストリの公開についての詳細はこちらのドキュメントをご参照下さい。
3. あて先のレジストリの認証情報を取得し、CIパイプライン実行時のServiceAccountに紐付ける
公開されたレジストリにイメージをPushするためには、イメージのPushを行うことが可能なユーザー、もしくはServiceAccountとして認証される必要があります。
OpenShiftでは各Namespaceにデフォルトで、default
、builder
、deployer
という3つのServiceAccountが存在します。このうちbuilder
はレジストリにイメージをPushすることができる権限を持っているため、今回はこのbuilder
の認証情報を利用したいと思います。
あて先クラスタ側で以下のコマンドを実行するとdestination
Namespaceにおけるbuilder
の認証情報が出力されます。
SECRET=$(oc -n destination get secret | grep builder-docker | awk {'print $1'}) oc -n destination get secret $SECRET -o jsonpath="{.data['\.dockercfg']}" | base64 --decode | jq '.["image-registry.openshift-image-registry.svc:5000"].password'
# 実行結果の例 "eyJhbGci...(中略)...HddvV0"
得られた認証情報をCI実行を行う方のクラスタに追加していきましょう。
ここからはコンソールで操作をしていきます。
source
NamespaceにてSecret一覧を開き、pipeline-dockercfgから始まるSecretを選択して編集を行います。
このSecretはパイプライン実行を行うServiceAccountであるpipeline
で利用されます。
"認証情報の追加"をクリックし以下を入力します。
レジストリサーバーのアドレス: 公開したあて先クラスタのレジストリのURL
ユーザー名: serviceaccount
パスワード: builder
の認証情報
認証情報を登録することで、CI実行側のクラスタであて先クラスタのレジストリにアクセスする準備が整いました。
パイプラインの実行
それではいよいよパイプラインを実行していきたいと思います。
CI実行を行うクラスタにはOpenShift Pipelinesをインストールした上で、先ほどのskopeo-pipeline.yamlを適用しておきましょう。
oc apply -n source -f skopeo-pipeline.yaml
今回はPipelineRunを作成し、Pipelineの実行を行います。EventListnerを使ったPipelineの実行については本記事の冒頭にある以前の記事をご参照下さい。
PipelineRunのyamlは以下となります。(クリックで展開)
skopeo-pipelinerun.yaml
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: skopeo-pipeline-run-
spec:
pipelineRef:
name: skopeo-pipeline
params:
- name: target-path
value: 'site'
- name: git-url
value: 'https://gitlab.com/jpishikawa/starter-kit-cicd-app'
- name: git-revision
value: 'main'
- name: dest-imageurl
value: 'docker://default-route-openshift-image-registry.apps.mycluster3.sandbox663.opentlc.com/destination/starter-kit-cicd-app'
workspaces:
- name: shared-workspace
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
- name: tmp-vol
emptyDir: {}
あて先となるレジストリのURLを.spec.params.dest-imageurl
というパラメーターの中で指定していますが、先頭部分がhttps://
ではなくdocker://
から始まる点に注意して下さい。
このファイルを適用するとパイプラインの実行が開始されます。
oc create -n source -f skopeo-pipelinerun.yaml
実行してしばらく待つと以下のような形でパイプラインが成功となります。
今度はあて先側のクラスタを見て、イメージがコピーされているか確認をしましょう。
コンソールのdestination
Namespaceにて、画面左側の"ビルド">"イメージストリーム"を開くとコピーされたイメージを確認することができます。
これでCIパイプラインの中でイメージをコピーできることが確認できました。
まとめ
本記事ではTektonとskopeoを使って、イメージのビルドから外部のレジストリへのコピーまで一連の流れとして行う方法をご紹介しました。
いくつかの事前準備は必要となりますが、skopeoを使うことでイメージのコピーが簡単に実施できることをご理解いただけたのではないでしょうか。
さて、今回はあて先のクラスタにてレジストリを外部公開してコピーを実現しましたが、そもそもレジストリを外部公開したくないケースもあるのではと考えます。
そこで次回はskupperを使い、レジストリを外部公開をせずにイメージをコピーする方法についてご紹介したいと思います。