Skupperを使ってクラスタ間を結びコンテナイメージをコピー

こんにちは、Red Hatでソリューションアーキテクトをしている石川です。

前回の記事ではOpenShift Pipelines(Tekton)の中でskopeoを動かし、CIパイプラインでビルドしたイメージを別クラスタのコンテナレジストリにコピーしました。 この際、あて先となるクラスタではコンテナレジストリに接続するためのrouteを公開し、それを通じてCI実行側のクラスタからイメージをコピーしました。

rheb.hatenablog.com

今回の記事ではSkupperを使うことであて先クラスタ側でrouteを公開することなくイメージのコピーを実施したいと思います。
そもそもSkupperとは?について知りたい方は赤帽ブログのこちらの記事を参照下さい。 rheb.hatenablog.com

TL;DR

・Skupperを使うと異なるクラスタのNamespace間で動いているサービスを簡単に繋ぐことができます
・OpenShiftではSkupper Operatorがあるので導入しやすいです
・今回はCIパイプラインのユースケースでSkupperを使ってみました

作りたい構成

今回も前回と同様にCIパイプラインを実行し、コンテナのビルドやイメージのコピーを行うクラスタとしてROSAクラスタを構築します。(上図左)
そしてイメージのコピー先としてAWS上にIPIでOCPクラスタを構築します。(上図右)
今回はこの2つのクラスタの特定のNamespace間でSkupperによるAMQP Linkを結び、こちらを介してコンテナイメージのコピーを実施したいと思います。

クラスタ間でのLink作成

Skupperを使いクラスタ間でLinkを作成するステップとしては以下となります。
・各クラスタにSkupper Operatorをインストール
・Linkを結びたいNamespaceにてSkupperを初期化
・Tokenを生成しLinkを作成

Skupper Operatorのインストール

まずそれぞれのクラスタでSkupper Operatorをインストールします。

OpenShiftコンソールからOperatorHubを開き、Skupperを検索してインストールしましょう。
デフォルトの設定だとクラスタ全体でSkupper Operatorを利用可能ですが、特定のNamespaceのみを対象としてインストールすることも可能です。 今回はデフォルト設定のままインストールを行います。

Skupperの初期化

Skupper Operatorをインストールできたら続いて各NamespaceでSkupperの初期化を行います。
ここからはskupperCLIを使用していきます。

Skupperのサイトに記載されている通り以下のコマンドでskupperコマンドを導入することができます。

# For Linux or Mac
curl https://skupper.io/install.sh | sh

導入されたskupperコマンドを実行すると以下が表示されます。

skupper
---
Usage:
  skupper [command]

Available Commands:
  completion       Output shell completion code for bash
  debug            Debug skupper installation
  delete           Delete skupper installation
  expose           Expose a set of pods through a Skupper address
  gateway          Manage skupper gateway definitions
  help             Help about any command
  init             Initialise skupper installation
  link             Manage skupper links definitions
  revoke-access    Revoke all previously granted access to the site.
  service          Manage skupper service definitions
  status           Report the status of the current Skupper site
  token            Manage skupper tokens
  unexpose         Unexpose a set of pods previously exposed through a Skupper address
  update           Update skupper installation version
  version          Report the version of the Skupper CLI and services

Flags:
  -c, --context string      The kubeconfig context to use
  -h, --help                help for skupper
      --kubeconfig string   Path to the kubeconfig file to use
  -n, --namespace string    The Kubernetes namespace to use

Use "skupper [command] --help" for more information about a command.

skupperコマンドが導入できたら各Namespace上でskupper initを実行します。
各クラスタにログインし、Linkを作成したいNamespaceへ変更しておきましょう。

## 以下をそれぞれのクラスタに対し実施
# ログイン
oc login https://api.xxxxxxx.xxxx.openshiftapps.com:6443 --username $USER --password $PASSWORD

# Namespace変更
oc project source

# skupper初期化
skupper init
---
Skupper is now installed in namespace 'source'.  Use 'skupper status' to get more information.

ここで1つ気を付けるポイントとして、あて先クラスタのNamespaceはopenshift-image-registryを指定します。

最終的なコンテナイメージのコピー先はdestinationNamespaceとなりますが、Link作成はコンテナレジストリのPod等が動いているこちらのNamespaceで実施する必要があります。

Tokenの生成とLink作成

続いてTokenを作成し、そのTokenを使いLinkを作成していきます。
今回はCI実行側のクラスタでTokenを生成し、それをあて先クラスタ側で適用します。

# Tokenの生成(CI実行側のクラスタで実施)
skupper token create $HOME/secret.yaml
# Tokenの適用(あて先クラスタで実施)
skupper link create $HOME/secret.yaml

それぞれのコマンドを実行する際にクラスタを切り替えるため適宜oc loginコマンドを実施しましょう。

正しくLinkが作成できると以下のコマンドで状況を確認できます。

skupper link status
---
Link link1 is active

無事アクティブなLinkが作成されました。
せっかくなのでSkupperのコンソールでも状況を確認したいと思います。
コンソールのURLについてはskupper statusコマンドで確認できます。

# URLの確認
skupper status
---
Skupper is enabled for namespace "source" in interior mode. It is connected to 1 other site. It has no exposed services.
The site console url is:  https://skupper-source.apps.xxxxxxx.xxxx.openshiftapps.com
The credentials for internal console-auth mode are held in secret: 'skupper-console-users'

# 認証情報の確認
oc get secret skupper-console-users -o json | jq .data.admin -r | base64 -d

ブラウザから表示されたURLにアクセスし認証を行うとSkupperコンソールが表示されます。
画面左の"Network"から2つのクラスタのNamespace間でLinkが張られていることが確認できます。

Serviceのexpose

Linkが作成できたら、あて先クラスタのopenshift-image-registryNamespaceにあるimage-registryServiceをexposeすることで、CI実行側のクラスタからあて先クラスタのコンテナレジストリに対しアクセス可能とします。
あて先クラスタにログインし以下のコマンドを実行します。

# レジストリの公開
skupper expose service image-registry --address dest-registry

あらためてSkupperのコンソールを見てみましょう。

画面左の"Site"を選択するとexposed serviceとしてServiceが公開されているのが確認できました。

またCI実行側クラスタのコンソールを見ると、確かにdest-registryという名前でServiceが作成されているのがわかります。

これでCI実行側のNamespaceからあて先クラスタのコンテナレジストリに対しアクセス可能な状態となりました。

CIパイプラインの実行

レジストリにアクセス可能な状態を作ることができたため、CIパイプラインを実行していきます。
TektonのPipelineについては前回の記事と同じものを利用します。

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'

CI実行側のクラスタに適用しておきましょう。

続いてレジストリへの認証情報とServiceAccountの紐付けです。 今回もあて先クラスタのdestinationNamespaceにおける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'

こちらをpipeline-dockercfgから始まるSecretに追加していくのですが、今回はここであて先となるレジストリサーバーのアドレスにクラスタ内部のdest-registryServiceのアドレスを指定します。

パスワードには取得した認証情報を登録します。 これでCIパイプラインによりイメージをコピーする準備が整いました。

前回と同様にTektonのPipelineRunを作成し、実行してみましょう。

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://dest-registry.source.svc:5000/destination/starter-kit-cicd-app'
  workspaces:
    - name: shared-workspace
      volumeClaimTemplate:
        spec:
          accessModes:
            - ReadWriteOnce
          resources:
            requests:
              storage: 1Gi
    - name: tmp-vol
      emptyDir: {}

.spec.params.dest-imageurlの中であて先のレジストリのアドレスを記載していますが、今回はこちらをdest-registryServiceのアドレスに書き換えています。

PipelineRunを適用してしばらく待つとPipeline実行が成功し、無事にskopeoによるイメージのコピーまで完了したことを確認できます。

またあて先クラスタのコンソールからイメージストリームを確認すると無事にイメージが保管されています。

Skupperを通じてイメージのコピーを実行することができました!

まとめ

前回の方法ではコンテナレジストリのrouteを外部公開していましたが、今回はSkupperを使うことでコンテナレジストリへのアクセスを実現しました。
今回はCIパイプラインと組み合わせてSkupperを使用してみましたが、Skupper自体には他にも色々な用途があると考えられます。記事の中でもご紹介した通り、OpenShiftにはSkupper Operatorもあり、比較的簡単に試すことができるため皆さんもぜひ触ってみて下さい。

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