6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenShift Advent Calendar 2024

Day 12

緊急OpenShift!CICDパイプラインぜんぶGUIで作る大作戦

Last updated at Posted at 2024-12-11

OpenShift Advent Calendar 2024の12日目の記事です。

タイトルの元ネタ

緊急SOS!池の水ぜんぶ抜く大作戦

この記事の目的

OpenShift Pipelinesを利用して、以下のようなパイプラインをゼロからGUIで作成します。必要な手順を全て解説していますので、初心者の方でも「なんかそれっぽいパイプライン」を作れるようになります。

image.png

image.png

image.png

また、OpenShift上で「継続的デリバリー」を実現する為のツール全般についても丁寧に解説しています。

それではやっていきましょう〜!

はじめに

Kubernetes/OpenShiftを「使いこなす」上で避けては通れないのが「継続的デリバリー(Continuous Delivery)」の実現です。「入門 継続的デリバリー(オライリー)」によれば、継続的デリバリーで実施していることは以下2つだそうです。

  • いつでも安全にソフトウェアの変更を提供することができる
  • ボタンを1つ押すぐらい簡単に、そのソフトウェアをデリバリーできる

また、CDF(Continuous Delivery Foundation)による定義は以下のとおりです。

  • いつでも変更がリリース可能であることを立証している
  • リリースプロセスを自動化している

なお、よく「継続的デリバリー」は継続的インテグレーション(Continuous Integration)とセットで「CICD」と言われたりもしますが、上述の「入門 継続的デリバリー」によると、「なにか厳密に違うとか同じとかはなく、まぁ意図するところとしては同じです」という感じらしい。

これをOpenShiftの上で実現しようとした場合、以下の2つのツールを使うことが有効でしょう。

OpenShift GitOps

image.png

  • OSSのArgoCDをOpenShiftに組み込んで提供するもの
  • Gitリポジトリに格納したManifestファイル(Single Source of Truth)を自動的にKubernetesクラスタに適用してくれる
  • GitOpsを実現するための要

OpenShift Pipelines

image.png

  • OSSのパイプラインマネジメントツールである「Tekton」をRed Hatによるエンタープライズサポート付き機能として取り込んだもの
  • オリジナルのTektonが用意する豊富なTaskテンプレートを活用し、ローコードでパイプライン作成や編集が可能
  • オリジナルのTektonにGUI(Graphical User Interface)が具備され、圧倒的に直感的な操作が可能

どちらのツールもOpenShiftの「OperatorHub」からインストールできます。

OpenShift初心者にとっての壁

それはなんといってもパイプライン作成ではないでしょうか。パイプラインとは、アプリケーションをコーディングしてからコンテナ化(Build)してOpenShiftにDeployするまでの一連の必要な作業(Task)をコードとして定義し、それらの順序やデータの受け渡し、各種パラメータを定義したものです。つまり、「継続的デリバリー」を構成する非常に重要な要素です。コンテナアプリケーションを本格的に活用し、アプリケーション開発の生産性を向上するためには、高度に自動化された再現性のあるプロセスを実現することが重要です。そのためにパイプラインが必要になるのです。

パイプラインの必要性に気づくプロセス

OpenShiftでのコンテナアプリケーション開発運用に足を踏み入れた多くの方々は、概ね以下のような順でパイプラインの必要性に気づくと思われます。

  1. 君は「コンテナ仮想化」という仮想化技術について知り、その有用性に期待を持つ
  2. 次にDockerfileが書けるようになり、コンテナイメージのBuildができるようになり、イメージレジストリにPushしたり、イメージを指定してコンテナアプリケーションをDeployできるようになった
  3. 君は「どうやら本格的に『イミュータブルインフラストラクチャ』とやらを実現するには、Manifestファイルを書かないといけないらしい」という事に気づき、YAMLを書くようになった
  4. さらにManifestをそのままOpenShiftクラスタに適用(Apply)するのではなく、GitリポジトリにSingle Source of Truthとして管理し、ArgoCDを介してGitOpsすることを覚えた
  5. 最終的に君はソースコードを編集し、それを契機にソースの静的解析やコンテナイメージのBuild、レジストリへのPushやイメージスキャンを実行し、OKとなったコンテナイメージをもってManifestをApplyしたいと思うようになり、「継続的デリバリー」を実現するパイプラインの作成に関心をもった
  6. そこで君は、自分のOpenShiftクラスタにOpenShift Pipelines Operatorのインストールし、コンソール左側メニューに「Pipelines」というメニューが増えていることに気づいた。

image.png

君は期待に胸を踊らせて左側の「Pipelines」をクリックしてみた

image.png

そこにはなにもなかった。不思議に思った君はすかさず「Create」をクリックしてみた。

「そうか、これからパイプラインを作るんだ。さぁ、やるぞ〜」

image.png

いきなりこの画面に行き、君は頭が真っ白になり、そしてこう言った。

image.png
画像:児童相談所の保護訓練で虐待親になりきる警察が「役に入りすぎ」と話題に

テーブルトークRPG風のテキストは一旦ここまでにしておいて...

先に、本記事を通して作成する簡単なTektonパイプラインを示します。

image.png

このパイプラインは非常に基本的なものですが、それでも立派なパイプラインです。パイプラインは、左から右へ各Taskを実行していきます。各Taskでやっていることを先に説明します。

  1. Gitlab上でソースコードを編集するとパイプラインに対してPush通知が飛ぶ
  2. それを受けてGitlab上のソースコードをgit cloneしてくる
  3. OpenShift上にDeploy済みの「Sonarqube」でソースコードの静的解析を行う
  4. コンテナイメージをBuildし、内部レジストリにPushする
  5. 内部レジストリ内のイメージを「Trivy」でスキャンする
  6. スキャン結果が問題なければ、「Skopeo」を使って内部レジストリから外部レジストリ(DockerHub)にイメージをコピーする
  7. oc rolloutコマンドで、指定したDeploymentのロールアウトを実行し、最新のコンテナイメージを反映してアプリケーションをアップデートする

基本的なパイプラインではありますが、最低限必要なセキュリティを確保しながら、「自動化されたパイプライン感」があるものを作れます。こいつを全てGUIで作成することを目指しましょう。

Tektonパイプラインについて

作成を開始する前に、改めてTektonパイプラインの仕組み・概念を説明します。

Tektonパイプラインを構成する主な要素は以下の3つです。

  • Pipeline
  • Task
  • Workspace

それぞれ見ていきます。

Pipeline

Pipelineとは、後述する「Task」の順序性や「Workspace」との紐づけを行い、「このTaskをやってOKだったら次はこのTaskを実行して...」を管理する存在です。また、各Taskに適用するパラメータ(変数)を定義します。

これらの要素を図解すると以下のようになります。

image.png

Task

Taskは、文字通り何らかの作業です。先程示したパイプラインの例で言えば、git cloneしてくるTaskや、コンテナイメージスキャンを実行するTask、ArgoCDをSyncするTaskなど。これらのTaskは実はコンテナイメージから起動するPodです。「〇〇する」Taskもひとつのコンテナアプリケーションとして動きます。そしてそれはKubernetes上ではPodとして起動・管理されます。Taskを実行するタイミングでPodが起動し、Taskが完了すればPodは削除されます。これの連なりがパイプラインです。

Taskは、特定のNameSpace内のみで利用できるものと、クラスタを横断して利用できる「Cluster Task(CT)」の2種類が存在します。Tektonで利用できるTaskのテンプレートはTekton Hubに多く掲載されており、すべてのTaskをゼロから作る必要はありません。だいたいのパイプラインはテンプレートの組み合わせだけでなんとかなります。

Workspace

Podとして起動・実行される各Taskは、Task終了時にPodが消えてしまいますので、その間に生成されたデータも消えてしまいます。「①ソースコードをダウンロード(git clone)してくるTask」の後に、「②ソースコードを静的解析するTask」を実行したい場合、Workspaceが無いと①のTaskが完了すると、ダウンロードしてきたソースコードが消えてしまいます。そうすると次の②のTask(Sonarqubeによるソースコード静的解析)が実施できません。それは困ります。

そこで、パイプライン実行中に各Task間でやりとりされるデータを保持する場(Workspace)を提供する永続ボリュームが必要になります。つまり、パイプラインには基本的にひとつ以上のPV(PersistentVolume)が要求されます。それを要求するPVC(PersistentVolumeClaim)も必要になります。

パイプラインが持つ特徴

まず、各Task自体は必ず「変数」を持ちます。そしてパイプラインは「汎用性」をもたせることを前提とします。ここでいう汎用性とは、「基本的に固有のアプリケーションにしか使えないようなハードコーディングはしない」ことを意図しています。例えば、今回作るパイプラインの最初のTaskでgit cloneしてくるTaskがありますが、このTaskはいくつかの変数をもっています。

例えばgit clone元のURLや、ブランチ名などです。今回利用するソースコードが置いてあるリポジトリのURLは「 https://gitlab.com/masaki-oomura/simple-stateful-app 」ですが、このURLをそのままTaskにハードコーディングしてしまった場合、このアプリケーションのみをgit cloneしてくる用途にしか使えません。

一方でパイプラインを利用する大前提となる変数については、「定数」としてTaskに値をハードコーディングすることもあるでしょう。例えば、mainブランチにあるソースコードしか使わないといった場合、git cloneTaskのブランチを司る変数には直接mainと埋め込んでしまっても良いはずです。

また、各Taskの変数はPipelineが持つパラメータを参照させます。つまり、パイプラインを実行する際に引数として与えられたパラメータが、各Taskの変数に代入されて実際に実行されます。

TaskRunとPipelineRun

あくまでTaskやPipelineは抽象化されたリソースです。これらが実際に実行される際には対応するインスタンスが起動してきます。それらはそのまま、TaskRunやPipelineRunと言います。

image.png

同じPipelineに異なるパラメータを入れて3回実行した場合は、PipelineRunは3つ立ち上がります。その中で各TaskはTaskRunとして起動し、そのTaskRunの実体はPodです。

image.png

ちなみに、本記事を通して作成するパイプライン

image.png

を構成しているYAMLファイルは以下の通りです。Tektonパイプライン自体もOpenShiftの上で動くものである以上、YAML形式のコードとして定義することができます。
※長いですが、一旦無心でスクロールしてください。

pipeline.yaml
apiVersion: tekton.dev/v1
kind: Pipeline
  name: simple-stateful-app-pipeline
  namespace: simple-stateful-app
spec:
  finally:
    - name: openshift-client
      params:
        - name: SCRIPT
          value: oc rollout restart deployment $(params.DEPLOYMENT_NAME) -n $(params.NAMESPACE_NAME)
        - name: VERSION
          value: latest
      taskRef:
        kind: ClusterTask
        name: openshift-client
      workspaces:
        - name: manifest-dir
          workspace: workspace
  params:
    - default: 'https://gitlab.com/<your-gitlab-username>/simple-stateful-app.git'
      description: ソースコードを取ってくる先のGitリポジトリのURL
      name: GIT_URL
      type: string
    - default: ./source/Dockerfile
      description: イメージをBuildする為のDockerfileのありかを示すパス
      name: DOCKERFILE_PATH
      type: string
    - default: ./source
      description: イメージをBuildする対象のアプリケーションのソースコードのありかを示すパス
      name: SOURCE_PATH
      type: string
    - default: 'image-registry.openshift-image-registry.svc:5000/simple-stateful-app/simple-stateful-app'
      description: コピー元の内部レジストリのURL
      name: INTERNAL_REGISTRY_URL
      type: string
    - default: docker.io/<your-dockerhub-username>/simple-stateful-app
      description: コピー先の外部レジストリのURL
      name: EXTERNAL_REGISTRY_URL
      type: string
    - default: node-app-deployment
      description: ロールアウト対象のDeployment名を指定します
      name: DEPLOYMENT_NAME
      type: string
    - default: simple-stateful-app
      description: Deploymentの存在するNameSpaceを指定します
      name: NAMESPACE_NAME
      type: string
    - default: 'http://docker-sonarqube-git.sonarqube.svc.cluster.local:9000'
      description: Serviceから提供されるSonarqubeの内部ホスト名
      name: SONAR_HOST_URL
      type: string
    - default: <your-projectkey>
      description: SonarqubeのCI統合設定で払い出された`projectKey`
      name: SONAR_PROJECT_KEY
      type: string
    - default: <your-sonar-token>
      description: SonarqubeのCI統合設定で払い出された`SONAR_TOKEN`
      name: SONAR_TOKEN
      type: string
  tasks:
    - name: git-clone
      params:
        - name: url
          value: $(params.GIT_URL)
        - name: revision
          value: main
        - name: refspec
          value: ''
        - name: submodules
          value: 'true'
        - name: depth
          value: '1'
        - name: sslVerify
          value: 'true'
        - name: crtFileName
          value: ca-bundle.crt
        - name: subdirectory
          value: ''
        - name: sparseCheckoutDirectories
          value: ''
        - name: deleteExisting
          value: 'true'
        - name: httpProxy
          value: ''
        - name: httpsProxy
          value: ''
        - name: noProxy
          value: ''
        - name: verbose
          value: 'true'
        - name: gitInitImage
          value: 'registry.redhat.io/openshift-pipelines/pipelines-git-init-rhel8@sha256:dd5c8d08d52e304a542921634ebe6b5ff3d63c5f68f6d644e88417859b173ec8'
        - name: userHome
          value: /home/git
      taskRef:
        kind: ClusterTask
        name: git-clone
      workspaces:
        - name: output
          workspace: workspace
    - name: buildah
      params:
        - name: IMAGE
          value: $(params.INTERNAL_REGISTRY_URL)
        - name: BUILDER_IMAGE
          value: 'registry.redhat.io/rhel8/buildah@sha256:5c7cd7c9a3d49e8905fc98693f6da605aeafae36bde5622dc78e12f31db3cd59'
        - name: STORAGE_DRIVER
          value: vfs
        - name: DOCKERFILE
          value: $(params.DOCKERFILE_PATH)
        - name: CONTEXT
          value: $(params.SOURCE_PATH)
        - name: TLSVERIFY
          value: 'true'
        - name: FORMAT
          value: oci
        - name: BUILD_EXTRA_ARGS
          value: ''
        - name: PUSH_EXTRA_ARGS
          value: ''
        - name: SKIP_PUSH
          value: 'false'
      runAfter:
        - sonarqube-scanner
      taskRef:
        kind: ClusterTask
        name: buildah
      workspaces:
        - name: source
          workspace: workspace
    - name: skopeo-copy
      params:
        - name: srcImageURL
          value: 'docker://$(params.INTERNAL_REGISTRY_URL)'
        - name: destImageURL
          value: 'docker://$(params.EXTERNAL_REGISTRY_URL)'
        - name: srcTLSverify
          value: 'true'
        - name: destTLSverify
          value: 'true'
      runAfter:
        - trivy-scanner
      taskRef:
        kind: ClusterTask
        name: skopeo-copy
      workspaces:
        - name: images-url
          workspace: workspace
    - name: trivy-scanner
      params:
        - name: ARGS
          value:
            - image
            - '--severity HIGH,CRITICAL'
        - name: TRIVY_IMAGE
          value: 'docker.io/aquasec/trivy@sha256:944a044451791617cc0ed2ee4d1942a4f66b790d527fcd0575a6b399ccbc05a1'
        - name: IMAGE_PATH
          value: $(params.INTERNAL_REGISTRY_URL)
        - name: AIR_GAPPED_ENABLED
          value: 'false'
      runAfter:
        - buildah
      taskRef:
        kind: Task
        name: trivy-scanner
      workspaces:
        - name: manifest-dir
          workspace: workspace
    - name: sonarqube-scanner
      params:
        - name: SONAR_HOST_URL
          value: $(params.SONAR_HOST_URL)
        - name: SONAR_PROJECT_KEY
          value: $(params.SONAR_PROJECT_KEY)
        - name: SONAR_TOKEN
          value: $(params.SONAR_TOKEN)
      runAfter:
        - git-clone
      taskRef:
        kind: Task
        name: sonarqube-scanner
      workspaces:
        - name: source
          workspace: workspace
        - name: sonar-settings
          workspace: workspace
  workspaces:
    - name: workspace

ab30047b.jpg

OpenShift初心者の方、これから初めてパイプライン作成を学ぶ方にいきなり「YAMLファイルを見て勉強しろ」と言うのは酷です。このパイプラインを全てGUIで作っちゃおうというのが本記事の趣旨です。一度GUIで作られたパイプラインはOpenShiftの中でYAMLファイル化されますので、それを元に少しずつ学んでいけるはずです。

さて、そんなパイプラインの作成を始める前に、事前準備をしていきます。

事前準備

OpenShiftクラスタの準備

もし気軽に触れるOpenShiftクラスタがない場合は、無料のRed Hatアカウントさえ作れば、ROSAがすぐに試せる「Red Hat OpenShift Service on AWS Hands-on Experience」を利用しましょう。

「Create a ROSA cluster」をクリックすれば、ROSAが起動します。Cluster-admin権限をもったアカウントがもらえるので、自由にOperatorもインストール可能です。

image.png

「Red Hat OpenShift Service on AWS Hands-on Experience」は8時間×3回まで利用できます。詳しい利用開始までの流れは、日本語ガイドを参照ください。

2つのOperatorをインストールする

image.png

日本語ガイドからのリンク先である日本語の説明書を参考にして、「OpenShift GitOps Operator」と「OpenShift Pipelines Operator」をインストールしておきます。以下リンクから直接それぞれのOperatorのインストール方法を見ることができます。

必要なファイルとコンテナイメージレジストリの用意

OpenShiftクラスタが用意できたら、今回DeployするサンプルアプリのソースコードとManifest一式を用意します。また、パイプラインの中でBuildしたイメージをPushする先の外部イメージレジストリを用意しましょう。

リポジトリのフォーク

まずはご自身のGitリポジトリに「 https://gitlab.com/masaki-oomura/simple-stateful-app.git 」をフォークしておいてください。プロジェクト名は「simple-stateful-app」、リポジトリは「公開(パブリック)」にしておきましょう。

以後はフォーク後のリポジトリを使っていきます。今、ご自身のリポジトリのURLはhttps://gitlab.com/<your-gitlab-username>/simple-stateful-app.gitとなっているはずです。

Docker Hubに自身のイメージレジストリを作成

DockerHubは無料でコンテナレジストリを作成することができるサービスです。自身のアカウントを作成、ログイン後に「Create repository」をクリックします。

image.png

NameSpaceは自身のそれ、リポジトリ名はとりあえず「simple-stateful-app」としておきます。レジストリも「公開(パブリック)」にしておきます。

image.png

Docker commands欄にコンテナレジストリへのアクセスコマンドが表示されます。

image.png

つまり、<your-docker-username>/simple-stateful-appがコンテナイメージのPush先ということです。

サンプルアプリのManifestをArgoCD経由でApplyする

では、サンプルアプリを先にOpenShiftにDeployします。今回はフォークしてきたリポジトリにあるManifestをSingle Source of Truthとし、ArgoCDによるGitOpsを実施します。

Gitに存在するManifestファイルを唯一無二の真実(Single Source of Truth)として管理し、ArgoCDというOSSがKubernetes Clusterに適用(Apply)する。手作業でManifestファイルをApplyさせず、変更反映もGitを介して行う。

image.png

サンプルアプリについて

サンプルアプリ「simple-stateful-app」は、バックエンドがMysql、フロントエンドがNode.jsアプリケーションとなっている、非常に簡単なステートフルアプリケーションです。「Enter Text」欄に文字列を入力し「Add Text」をクリックすると、Mysqlのテーブルに文字列が保存されます。

image.png

データベースに登録された文字列を検索したい場合は「Search Text」欄に検索ワードを入力し「Search」をクリックします。

image.png

部分一致で検索結果が表示されます。このアプリケーションは、フロントエンドがPodが3つで起動し、アクセスのたびにラウンドロビンして負荷分散しています。現在アクセスしているPodの名称が「Host Name」欄に表示されます。
なお、MysqlのPodはひとつだけです。MysqlのデータはPersistent Volumeによって永続化されています。

image.png

さて、まずはこのアプリをご自身のOpenShiftクラスタにDeployしますが、コンテナイメージをBuild&Pushし、さらにソースコードを一部編集します。

コンテナイメージをBuildする

まずは、先ほどフォークしてきたリポジトリをローカルPCにPullしてきて、コンテナイメージをBuildします。アプリケーションのソースコード及びDockerfileの格納場所はsimple-stateful-app/source配下です。

説明
<your-gitlab-username> ご自身のGitlabのユーザ名に置き換えてください
<your-docker-hub-username> ご自身のDockerHubのユーザ名に置き換えてください
.sh
~ % git clone https://gitlab.com/<your-gitlab-username>/simple-stateful-app.git
~ % cd simple-stateful-app
simple-stateful-app % docker build -t <your-dockerhub-username>/simple-stateful-app:latest ./source
(中略)
Successfully tagged <your-dockerhub-username>/simple-stateful-app:latest

次に、今BuildしたイメージをPushしたいと思いますが、ターミナルからDockerHubにログインする必要があります。そのためには、「Personal access token」を作成する必要があります。https://app.docker.com/settings/personal-access-tokensにアクセスし、

image.png

「Generate new token」をクリックしたら、

image.png

Read, Write, Delete権限を持つToken(今回はlogin tokenとしました)を作成しておきます。

スクリーンショット 2024-12-05 0.30.34.png

ログインコマンドが表示されますので、これを使ってターミナルからログインしてください。

なお、この「Personal access token」は後ほども使いますので、忘れないようにどこかにコピーしておいてください。

.sh
simple-stateful-app % docker login <your-dockerhub-username>
Password: <Personal access token>
Login Succeeded!

これでDockerHubにイメージをPushできるようになりましたので、早速Pushします。

.sh
simple-stateful-app % docker push <your-dockerhub-username>/simple-stateful-app:latest
(中略)
Writing manifest to image destination

DockerHubの画面を見ると、イメージがPushされた事を確認できます。

image.png

次は、Manifestを編集します。

Manifestを編集

ソースコードを一部修正する必要があります。先ほどフォークしてきたリポジトリの中で修正する対象ファイルは4つです。

root/
 ├ app-of-apps/
 | └ app-of-apps.yaml ←修正対象
 ├ applications/
 | ├ mysql-app.yaml ←修正対象
 | ├ namespace-app.yaml ←修正対象
 | └ nodejs-app.yaml ←修正対象
 | manifest/
 | ├ namespace/
 | | └ namespace.yaml
 | ├ mysql/
 | | ├ configmap.yaml
 | | ├ deployment.yaml ←修正対象
 | | ├ pvc.yaml
 | | ├ secret.yaml
 | | └ service.yaml
 | └ nodejs/
 |   ├ configmap.yaml
 |   ├ deployment.yaml
 |   ├ route.yaml
 |   └ service.yaml
 | 
(以下略)

それぞれ、自身のGitリポジトリのURL及び自身のDockerHubの情報に書き換えます。ターミナルで以下コマンドを実行することで、対象のファイルを自身のユーザ情報に書き換えることができます。

説明
<your-gitlab-username> ご自身のGitlabのユーザ名に置き換えてください
<your-docker-hub-username> ご自身のDockerHubのユーザ名に置き換えてください
.sh
# 環境変数の設定。ご自身のユーザ名に置き換えて実行してください。
simple-stateful-app % export YOUR_GITLAB_USERNAME=<your-gitlab-username>
simple-stateful-app % export YOUR_DOCKERHUB_USERNAME=<your-dockerhub-username>

# 対象ファイルに対する値の置換
simple-stateful-app % sed -i '' "s/<your-gitlab-username>/${YOUR_GITLAB_USERNAME}/g" app-of-apps/app-of-apps.yaml
simple-stateful-app % sed -i '' "s/<your-gitlab-username>/${YOUR_GITLAB_USERNAME}/g" apps/mysql-app.yaml
simple-stateful-app % sed -i '' "s/<your-gitlab-username>/${YOUR_GITLAB_USERNAME}/g" apps/namespace-app.yaml
simple-stateful-app % sed -i '' "s/<your-gitlab-username>/${YOUR_GITLAB_USERNAME}/g" apps/nodejs-app.yaml
simple-stateful-app % sed -i '' "s/<your-dockerhub-username>/${YOUR_DOCKERHUB_USERNAME}/g" manifest/nodejs/deployment.yaml

これでご自身のGitlab(リポジトリ)及びDockerHub(イメージレジストリ)の情報に書き換えが完了しましたので、それを自身のGitlabに再度Pushしておきます。

.sh
simple-stateful-app % git add .
simple-stateful-app % git commit -m "overwrote to my user info."
simple-stateful-app % git push

これでManifestファイルの準備が完了しました。では、このアプリケーションをApplyしましょう。

ArgoCDプロジェクト・アプリケーションをApply

今回のサンプルアプリはGitopsのApp of AppsパターンでDeployします。

image.png

ご安心ください。簡単にイラストで説明します。

image.png

今から手作業でApplyするYAMLファイルは2つです。1つめはproject.yamlです。

project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: simple-stateful-app
  namespace: openshift-gitops
spec:
  clusterResourceWhitelist:
    - group: '*'
      kind: '*'
  destinations:
    - namespace: '*'
      server: '*'
  sourceRepos:
    - '*'

ArgoCDにはArgoCD側独自の論理空間「プロジェクト」を有しています。これがOpenShiftの「プロジェクト」と同名なので非常にややこしいので注意してください。ArgoCDアプリケーションもArgoCDプロジェクトもGitOps Operatorをインストールすると使えるAPIを使って作成するカスタムリソースです。

2つめはapp-of-apps.yamlです。

app-of-apps/app-of-apps.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: simple-stateful-app
  namespace: openshift-gitops
spec:
  destination:
    namespace: openshift-gitops
    server: 'https://kubernetes.default.svc'
  source:
    path: apps
    repoURL: >-
      https://gitlab.com/<your-gitlab-username>/simple-stateful-app.git
    #自身のGitlabリポジトリ名に変更します
    targetRevision: main
  project: simple-stateful-app
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

OpenShift/KubernetesにアプリケーションをDeployするためには、ManifestファイルをApplyする必要がありますが、それらのManifestをひとつひとつApplyするのではなく、

「Gitリポジトリにに一式入れておいたんで、ArgoCDくんまとめてApplyしておいてね〜。なんならGit上で変更があったら勝手に反映よろしくニキ〜」

とするのがGitOpsですが、この 「よろしくニキ〜」 自体もYAMLファイルとして表す事ができるのです。これが「ArgoCDアプリケーション」というManifestファイルです。今回、app-of-apps.yamlをApplyすると、更に別のArgoCDアプリケーション(YAMLファイル)をApplyさせます。具体的には

  • namespace-app.yaml
  • mysql-app.yaml
  • nodejs-app.yaml

です。

image.png

これら3つのArgoCDアプリケーションが、最終的にNameSpace、Mysql、Node.jsアプリケーションのYAMLファイルをそれぞれApplyしていくわけです。この様に、複数のコンポーネントが連携して構成されるアプリケーションについて、各コンポーネント毎のArgoCDアプリケーション(子アプリケーション)を定義し、それらを束ねるArgoCDアプリケーション(親アプリケーション)のYAMLファイルによって管理するパターンを「App of appsパターン」と言います。

それでは、OpenShiftクラスタに「ArgoCDプロジェクト」と「ArgoCDアプリケーション」をApplyします。OpenShiftにログイン済みのターミナルで以下コマンドを実行してください

.sh
simple-stateful-app % oc apply -f project.yaml
simple-stateful-app % oc apply -f app-of-apps/app-of-apps.yaml

OpenShift Gitopsのコンソールにログインしてみます。

image.png

OpenShiftのコンソール画面の右上の■が9こ並んだボタンを押すと、「OpenShift GitOps」のログイン画面に遷移できます。ログインするとアプリケーションがDeployされている状況を確認できます。しばらく待つと全てのコンポーネントがSyncされます。

image.png

OpenShiftのトポロジー画面でもNode.jsのPodとMysqlのPodが確認できました。

image.png

Routeからアプリケーションにアクセスして遊んでみてください。

image.png

これでArgoCDを介したアプリケーションのDeployが完了しました。ここからいよいよ本題のパイプライン作成に入ります。(やっと本編)

プロジェクト「simple-stateful-app」内でパイプラインを作成する

「開発者向け表示」の左端メニュー「Pipelines」をクリックします。まだ何もないので「Create」からパイプライン作成をスタートしましょう。

image.png

パイプラインの名前はお好きなものにしておきます。

image.png

3つのTaskを追加

いきなり完成形のパイプラインを作る前に、まずは「ソースコードを取ってきてイメージをBuildしDocker HubにPushさせる」パイプラインの作成を目指します。その為には3つのTaskが必要です。

ひとつめはソースコードをgit-cloneしてくるTaskです。「Taskの追加」をクリックすると、Task検索窓が出てきます。

image.png

git-cloneと打つと、関連Taskが出てきます。ここではRed Hatが提供するTaskを選択します。このTask「git-clone」は、最初からOpenShiftクラスタ内のすべてのNameSpaceで利用できるTask「Cluster Task」です。「追加」をクリックしましょう。

image.png

Taskが追加されました。

image.png

こんな要領でTaskの追加ができます。次にイメージをBuildするTask「Buildah」を追加します。今追加したTask「git-clone」の横にマウスオーバーすると青い+ボタンが出てきます。

image.png

ここから後続Taskを追加します。

image.png

「Taskの追加」をクリックすると、再び検索窓が出ました。

image.png

「buildah」と入れると関連するTaskが複数出てきますが、先程と同様にRed Hatが提供するCluster Task(CT)を選択、追加します。

image.png

できました。

image.png

全く同じ流れで、次はレジストリ間でイメージをコピーするTask「skopeo-copy」を追加します。

image.png

できました。

image.png

image.png
「Buildah」はデーモンレスのビルドツールで、Red Hatが主導して開発しているOSSです。詳細はこちら
(ブルドッグがパンツ被っているようなアイコンです)


image.png
「Skopeo」はレジストリ間のイメージコピーツールです。今回の用途は、OpenShiftクラスタ内のイメージレジストリ(内部レジストリ)から、Docker Hub(外部レジストリ)へイメージをコピー(Push)させることです。おなじくRed Hatが主導して開発しているOSSです。詳細はこちら
(「Skopeo」とはギリシャ語で「見る、調べる、検討する)」という意味だそうです)

パラメータを設定する

Taskを追加しても、PipeRunやTaskRunを実行するためのパラメータが設定されていないと動きません。

さて、この3つのTaskのパイプラインの連なり「ソースコードを取ってきてイメージをBuildしDocker HubにPushさせる」に必要なパラメータとは何でしょうか??

それは最低限以下の5つです。

  • git-clone
    • ソースコードを取ってくる先のGitリポジトリのURL
  • buildah
    • イメージをBuildする為のDockerfileのありかを示すパス
    • イメージをBuildする対象のアプリケーションのソースコードのありかを示すパス
  • skopeo-copy
    • コピー元の内部レジストリのURL
    • コピー先の外部レジストリのURL

例えば、Task「git-clone」をクリックして設定メニューを開くと、urlというパラメータが必須になっています。

image.png

これはGitリポジトリのURL(https://gitlab.com/<your-gitlab-username>/simple-stateful-app.git)に他なりません。ただし、これをこんな風に

image.png

Taskのパラメータにハードコーディングしてしまうと、他のアプリケーションに対しても使い回せるパイプラインになりません。Task内のパラメータは「変数」としておいて、その変数に代入する値(パラメータ)はPipeline側に持たせます。

「パラメーターの追加」をクリックすると、文字通りパラメータ追加画面になります。パラメータには名前と説明、そしてデフォルト値を設定する事ができます。ここで名前とはKeyの様なものです。デフォルト値は、PipelineRunを実行する際にパラメータが空欄だった際にデフォルトで突っ込んでくれる値を指定できます。

image.png

ここにそれぞれ必要な5つのパラメータを設定します。「名前」は必ずしも記事の通りである必要はありません。他の人が見てわかりやすいものを選んでください。また大文字である必要もありません。
(ただし、よくパラメータは大文字とアンダーバーを使うのでそれに倣っているだけです)

名前 説明 デフォルト値
GIT_URL ソースコードを取ってくる先のGitリポジトリのURL https://gitlab.com/<your-gitlab-username>/simple-stateful-app.git
DOCKERFILE_PATH イメージをBuildする為のDockerfileのありかを示すパス ./source/Dockerfile
SOURCE_PATH イメージをBuildする対象のアプリケーションのソースコードのありかを示すパス ./source
INTERNAL_REGISTRY_URL コピー元の内部レジストリのURL image-registry.openshift-image-registry.svc:5000/simple-stateful-app/simple-stateful-app
EXTERNAL_REGISTRY_URL コピー先の外部レジストリのURL docker.io/<your-dockerhub-username>/simple-stateful-app

image.png

いくつかのパラメータについては少し詳しく解説します。

DOCKERFILE_PATHですが、Gitリポジトリのディレクトリ「source」内にDockerfileがありますね。なのでこのパスを指定しています。ルートディレクトリをカレントディレクトリ.とするならば、Dockerfileのパスは./source/Dockerfileです。また、「DockerfileでイメージとしてBuildする対象のアプリケーションのソースコードのありか」を示すSOURCE_PATHは、./sourceとなります。

次に内部レジストリのURLを表すINTERNAL_REGISTRY_URLですが、OpenShiftクラスタ内からアクセスできる内部レジストリのURLの命名規則は以下のようになっています。

image-registry.openshift-image-registry.svc:5000/<NameSpace名>/<Image名>

今回はNameSpace「simple-stateful-app」内に「simple-stateful-app」というイメージ名でBuildしたイメージをPushする形にしました。一方、外部レジストリのURLEXTERNAL_REGISTRY_URLについてですが、Docker Hubで作成したリポジトリの命名規則は以下の通りとなっています。

<your-dockerhub-username>/<repositry-name>

これでPipeline側には必要なパラメータについては入力が完了しました。

Workspaceの追加

次にこのパイプラインにWorkspaceを追加します。が、めちゃくちゃ簡単です。「ワークスペースの追加」をクリックして任意の名前(例:workspace)と入力して完了です。

image.png

各Taskにパラメータを参照させる

次に、Pipeline側に設定した変数を各Taskから参照させる作業を行います。

まずはTask「git-clone」からいきましょう。Taskをクリックしてパラメータ設定メニューをひらきます。

image.png

Task側のパラメータurlには、Pipeline側のパラメータGIT_URLを参照させたいです。ご丁寧に「このフォームで変数を参照する際は、この形式を使用します: $(」と書いてあるので、やってみます。

image.png

Pipeline側で設定したパラメータ一覧が出てきました。S(params.GIT_URL)を選びます。

image.png

ついでに、Git cloneしてくるべきブランチも明示的に指定しておきます。ここではmainと直入力しちゃいます。

image.png

あとのパラメータは空欄でOKです。このまま下の方にスクロールすると、Workspaceを設定する箇所が出てきます。

image.png

このTask「git-clone」にとって必須のワークスペース「output」に、先程Pipelineに追加したWorkspace(名称:workspace)を追加しましょう。これでTask「git-clone」のパラメータとWorkspaceの設定は完了です。次にTask「buildah」をクリックして同様に設定していきましょう。

Task「buildah」ではIMAGEというパラメータが必須の模様です。これは説明読んでもちょっとわかりくいんですが、イメージの最初のPush先(つまり内部レジストリ)の宛先になります。つまり、Pipeline側のパラメータINTERNAL_REGISTRY_URLを参照させる必要があります。

image.png

つまり、以下の様に入力できればOKです。
$(params.INTERNAL_REGISTRY_URL)

image.png

次に設定する必要があるパラメータはDOCEKRFILECONTEXTです。

image.png

それぞれ、DockerfileのパスDOCKERFILE_PATHとソースコードのパスSOURCE_PATHを参照させればOKです。つまり以下のように入力できればOKです。

image.png

DOCKERFILEにもCONTEXTにも最初から値が入っていますが、これはGitリポジトリのディレクトリ構造によって入れるべき値が違うので、自分の状況に合わせて変更する必要があります。ここは初心者には難しいポイントです。

04f6a3e2.jpg

7b4a9469ab95b1bbe8c47d22f76ad142.png

必須のWorkspacesourceについても「workspace」を選択しておきましょう。

image.png

さて、これでTask「buildah」に必要なパラメータ設定も完了です。次はTask「skopeo」です。「skopeo-copy」をクリックします。

image.png

「skopeo-copy」で必要なパラメータは、srcImageURLdestImageURLであり、それぞれのパラメータの説明にある通り、イメージのコピー元とコピー先を指定します。つまり、INTERNAL_REGISTRY_URLEXTERNAL_REGISTRY_URLを指定すればOKです。

ただし!Skopeoの仕様上、イメージレジストリの宛先(URL)の前にdocker://と入れる必要があります。これは仕様です。つまり以下の様に入力できればOKです。

image.png

必須のWorkspace名images-url についてもこれまで同様「workspace」を入れておきます。

お疲れ様でした。3つのTaskから構成されるパイプラインはこれで完成まであと一歩です。一旦、「作成」をクリックしてください。

image.png

各Taskのパラメータの詳細説明や仕様については、Tekton Hubを確認することで理解できます。例えば、「skopeo-copy」の仕様(イメージレジストリの宛先(URL)の前にdocker://と入れる必要がある 等)はTekton Hubの「skopeo-copy」ページに記載されています。

PVCを追加する

パイプラインにはWorkspace用のPVCが必要でしたね。以下のManifestをOpenShiftコンソール画面のYAML適用画面からApplyして、NameSpace「simple-stateful-app」にPVCを作成します。

workspace-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: workspace-pvc
  namespace: simple-stateful-app
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 2Gi

image.png

「作成」をクリックします。

image.png

ステータスが「Pending」となっているのは、まだPVCで要求したPVがバインドされていないからです。一回パイプラインを回せばバインドされますので気にせずでOKです。

とりあえずパイプラインを回してみる

パイプラインを回してみましょう。右上プルダウン「Actions」から「Start」をクリックします。

image.png

パラメータにはデフォルト値が最初から入っています。下の方にスクロールして「Workspaces」欄の「workspace」でPVCを選択し、先程作成したworkspace-pvcを選びます。

image.png

これで一旦OKです。右下青いボタン「Start」をクリックすると、パイプラインが回り始めました!

image.png

残念。パイプラインが失敗しました。イメージのBuildまでは上手く行ったのですが...

image.png

image.png
https://x.com/showshow_jp/status/1645443699814588416/photo/1

パイプラインが失敗した場合は必ず何らかのログが出るので参考にしましょう。
パソコンを殴ってはいけません。パソコンが壊れてしまいます。

「Logs」タブでTaskのログが見れます。

image.png

Task「skopeo-copy」のログを抜粋すると、

docker.io: requested access to the resource is denied

ということでした。これ、Docker Hubにイメージをアップロード(Push)するための権限が無いよということです。そういえば、パイプラインのパラメータで外部レジストリのURLEXTERNAL_REGISTRY_URLのデフォルト値に<your-dockerhub-username>/simple-stateful-app/simple-stateful-appを設定したものの、DockerHubにイメージをPushするための認証情報の設定をしておりません。これだとパイプラインがイメージをPushすることができません。

DockerHubの認証情報の設定

OpenShiftコンソール画面の「開発者向け表示」にて、「シークレット」をクリックします。

image.png

NameSpace「simple-stateful-app」に作成されたシークレット一覧が見えますが、pipeline-dockercfg-<ランダム文字列>をクリックします。このSecretはOpenShift Pipleines OperatorをインストールしたタイミングですべてのNameSpaceに自動的に作成されるService Account「pipeline」にマウントされるSecretです。Service Account「pipeline」に更にSecretを追加でマウントします。追加でマウントするSecretこそが、DockerHubの認証情報を含むそれになります。

Secretを追加作成

直前と同じ画面の右上「作成」からSecretを作成しますが、「イメージのプルシークレット」を選択しましょう。

image.png

シークレット名は何か適当なもの(例:dockerhub-secret)を入れておきます。その他、以下のように値を入れておきます。

項目 入れるべき値
認証タイプ イメージレジストリーの認証情報
レジストリーサーバーのアドレス docker.io
ユーザー名 自身のDockerHubのユーザー名
パスワード DockerHubのPersonal access token

image.png

「作成」をクリックしてください。これでDockerHubにイメージをPushする為の認証情報をSecretとして作成できました。あとは、こいつをService Account「pipeline」にマウントすればOKです。

DockerHubの認証情報をService Accountにマウントする

「管理者向け表示」にて左端メニュー「ユーザー管理」から「ServiceAccounts」をクリックすると、Service Account「pipeline」が確認できます。

image.png

YAMLビューで中身を見ると、既にひとつのSecretを参照しています。これがOpenShift Pipelinesをインストールすると自動で作成されるSecret(内部イメージレジストリのプルシークレット)です。

kind: ServiceAccount
apiVersion: v1
metadata:
  name: pipeline
  namespace: simple-stateful-app
secrets:
  - name: pipeline-dockercfg-<ランダム文字列>
imagePullSecrets:
  - name: pipeline-dockercfg-<ランダム文字列>

これを以下のように追記しましょう。

kind: ServiceAccount
apiVersion: v1
metadata:
  name: pipeline
  namespace: simple-stateful-app
secrets:
  - name: pipeline-dockercfg-<ランダム文字列>
+ - name: dockerhub-secret
imagePullSecrets:
  - name: pipeline-dockercfg-<ランダム文字列>
+ - name: dockerhub-secret

追記ができたら「保存」をクリックします。

image.png

このService Account「pipeline」がパイプラインの中で実行されるTask「skopeo-copy」におけるイメージのPushを担いますので、こいつが参照するSecretにDockerHubの認証情報を設定しました。

再度パイプラインを回す

パイプライン画面に戻ります。

image.png

右端の三点リーダをクリック、プルダウンから開始」を選択し、初回同様、WorkspaceにPVCを選択しておきます。「開始」をクリックするとパイプラインが再び回り始めます。

image.png

今度はパイプラインが成功しました。やったぜ。続いて、DockerHubのリポジトリ画面を見てみます。

image.png

イメージがPushされたことが確認できます。おめでとうございます。これで非常に簡単ではありますが、Tektonパイプラインをゼロから作成し、コンテナイメージをBuildしレジストリにPushする流れの自動化を達成しました。ここからは、もう少し実用的なパイプラインにするべく、Taskの追加と設定を行っていきます。ただし、容量としてはこれまで追加した3つのTaskのそれと全く同様です。

  1. Pipeline編集画面でTaskを検索して追加する
  2. 汎用的にTaskに持たせたい変数をPipeline側で定義する
  3. その変数をTask側で参照させる$(params.環境変数)

基本的には全てこの流れで新しいTaskを追加・設定していきます。

DeploymentをRolloutするTaskを追加

3つのTaskで構成したパイプラインを元にTaskを増やしましょう。まずはDeploymentをロールアウトするTaskを新規に作成・追加します。もし先ほど作成したパイプラインにおいてコンテナイメージが再度Buildされても、Deploymentに対してRollout、要はコンテナイメージを再度PullしてきてPodを再作成する指示を出さないと、アプリケーションの変更が反映されません。なお、任意のDeploymentのRolloutはocコマンドやkubectlコマンドで実施できる非常に簡単なものです。例えばocコマンドだと以下のコマンドで可能です。

oc rollout restart deployment DEPLOYMENT_NAME -n NAMESPACE_NAME

ここで、DEPLOYMENT_NAMEは任意のDeloymentの名称です。今回の例で言えば「node-app-deployment」です。

また、NAMESPACE_NAMEは今回の例で言えば「simple-stateful-app」です。

image.png

っていうocコマンドを打ってくれるTaskを追加すれば、DeploymentのRolloutをしてくれるわけです。

Task「openshift-client」を追加

早速やっていきます。パイプラインの編集モードに切り替えます。

image.png

「Finally Taskの追加」をクリックします。

image.png

Task検索窓で「openshift-client」と検索し、Red Hatが提供するCluster Task(CT)のそれを追加しましょう。「openshift-client」は任意のocコマンドを実行してくれるTaskです。

image.png

パラメータを設定

さて、このパイプラインにパラメータDEPLOYMENT_NAMENAMESPACE_NAMEを新たに追加します。

名前 説明 デフォルト値
・・・ ・・・ ・・・
DEPLOYMENT_NAME ロールアウト対象のDeployment名を指定します node-app-deployment
NAMESPACE_NAME Deploymentの存在するNameSpaceを指定します simple-stateful-app

画面の状況でいうとこんな感じです。下2つが今新たに追加したパラメータです。

image.png

次に「openshift-client」をクリックします。「SCRIPT」欄にはそのままocコマンド自体を入力できます。しかしパラメータを変数部分で指定してあげる必要があるので、以下のように入力しましょう。

oc rollout restart deployment $(params.DEPLOYMENT_NAME) -n $(params.NAMESPACE_NAME)

image.png

他のTaskと同様にWorkspaceも指定してあげればこれでOKです。「保存」をクリックします。パイプラインにTaskを追加していく方法がわかってきたでしょうか?繰り返しになりますが、Taskに必要なパラメータ(PARAMETER)をPipeline側に設定し、それを参照する様に$(params.PARAMETER)と記述すれば、汎用性の高いパイプラインを作成できます。

Sourceコードを編集

では、Gitlab上のアプリケーションのソースコードを編集して、パイプライン回してみます。GitLabのWeb IDE上で編集してもいいし、ローカルPCにCloneしてきたソースコードを編集&プッシュしてもOKです。

image.png

今回編集するファイルはroot/source/public/index.htmlです。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Node.js MySQL App</title>
    <style>
        .title {
          color: rgb(255, 0, 0);
        }
    </style>
(以下略)

color: rgb(255, 0, 0)では、アプリのタイトル部分の文字の色を司っています。

image.png

ソースコード編集の一環としてRGBコードを変えてみます。ソースコードを編集したら、Commit&Pushしておいてください。今回は適当にcolor: rgb(147, 14, 214);としてみました。紫っぽい色です。

image.png

パイプラインを回す

では、パイプラインをぶん回しましょう。先ほどと同様の手順で「開始」をクリック、

image.png

PVCを選択して実行します。

image.png

パイプラインが回り始めます。

image.png

Task「openshift-client」が動いているタイミングでトポロジー画面でアプリケーションのアイコンを確認すると、Rolloutが走っている状況が見えます。

image.png

image.png

アプリケーションに変更が反映されたか、アプリの画面を更新して確認してください。

image.png

タイトル欄が指定したカラーコードの色になりました。ソースコードを編集し、パイプラインを回したら自動的にコンテナイメージがBuildされ、外部レジストリにイメージがPushされ、Rolloutされてアプリケーションが更新されました。だいぶいい感じに自動化されてきた気がします。ただし、これだと「半自動」な状態です。理想的には「ソースコードの編集結果をPushしたら、何もせずとも勝手にパイプラインが回り始めてアプリケーションに変更が反映されること」を目指したいです。

ソースコードのPushを受けてパイプラインを自動的に回す

ソースコードを編集し、Gitリポジトリに内容をPushしたら、自動的にPipelineRunを実行してくれたらとても便利です。なお、GitリポジトリにはソースのPushを含む各種のイベント発生を契機にして、特定のAPIエンドポイントに対してWebhookを発信する機能があります。その通知内容を受けてパラメータに値を突っ込んでPipelineRunを実行してくれる、そんな便利な存在が「Trigger」です。

Triggerの追加

パイプライン画面の三点リーダから「トリガーの追加」を選択します。

image.png

「Webhook - Git provider type」のプルダウンからは各種Gitサービスのイベント通知(Webhook)に対応した選択肢が存在します。今回は「Gitlab-push」を選択します。

image.png

Gitlabにソースコードの変更がPushされた事を契機(トリガー)にして、Triggerがパイプラインのパラメータに値を入れ(何も指定しない場合はデフォルト値が入る)、PipelineRunを作成・実行してくれます。また、TriggerではWorkspaceの設定も可能です。

image.png

パイプラインを実行する際に毎度手入力していたWorkspaceの設定をTrigger側で設定しておけば、PipelineRun実行時に自動的に同内容を設定して実行してくれます。WebhookとWorkspaceの設定ができたら「追加」をクリックします。NameSpaceのトポロジー画面を見ると、何やら「el-evnet-listener-<ランダム文字列>」とかいうDeploymentが適用されています。

image.png

こいつが公開するRouteにTriggerで設定したイベント通知(Webhook通知)を飛ばすと、設定した値をパラメータに代入してパイプラインをぶん回すところまで一気にやってくれます。早速GitlabでWebhook通知の設定を行いましょう。

GitlabのWebhook通知設定

それでは、このEventListenerに対して送信するWebhookの設定をGitlab上で行います。Gitlabリポジトリ「log-test」の左端メニュー「設定」→「Webhooks」と進みます。

image.png

「新しいWebhookを追加」をクリックします。

image.png

URLにはEventListenerのRouteのURLを入力します。

image.png

EventListenerのRouteのURLはOpenShiftのトポロジー画面で確認できます。
image.png

トリガーには「プッシュイベント」を選択します。今回はmainブランチに対するPushのみをWebhook通知発信イベントとして定義しようと思いますので、「ワイルドカードパターン」にmainと入れておきます。

スクリーンショット 2024-10-07 21.06.57.png

これで設定は完了です。一番下までスクロールして「Webhookを追加」をクリックします。

image.png

これでWebhookが追加されました。早速試してみます。

再びGitlab上でソースコードを編集してPushする

再度、root/source/public/index.htmlで色を変更してみます。今度はこんな色color: rgb(14, 214, 27)にしてみました。黄緑色っぽい色です。再びソースコードをPushします。

image.png

パイプラインを確認してください。おー!自動的にパイプラインが回り始めています!

image.png

パイプラインが完了しました。

image.png

アプリケーションの画面をリフレッシュすると、ホスト名が先程編集した通りのカラーコードになっています。

image.png

この様に、パイプラインにTriggerを追加するとGitリポジトリのWebhook通知を受けるEventListenerがDeployされます。ソースコードを編集しGitにPushすると、そのイベントがWebhook通知としてEventListenerに飛びます。それを受けて自動的にパイプラインが回るようになりました。これでかなり「継続的デリバリー」みが出てきました。

コンテナイメージスキャンTaskを追加

さて、これまではソースコードの変更をGitにPushすると自動的にコンテナイメージがBuildされ、外部レジストリにPush、そのままDeployされていました。そこで、コンテナイメージのスキャンを実行するTaskを入れたいと思います。イメージスキャンのTaskとしてOSSの「Trivy」を使います。

image.png

Trivy」は軽量な脆弱性検知ツールで、コンテナイメージもスキャンできます。コンテナイメージのみならず、ManifestファイルやIaCのファイル(Terraform 等)もスキャンできます。DesktopやServerにインストールして利用することもできますが、TektonのTaskとしても実行可能です。

Task「Trivy-scanner」を追加

早速パイプラインにTaskを追加しましょう。これまで同様、パイプライン編集画面からTask「Skopeo-copy」の手前にTaskを追加し、

image.png

検索窓にて「Trivy」と検索、TektonHubで公開されている「Trivy-scanner」を追加します。

image.png

Trivyコマンド

Trivyは先述の通りコンテナイメージスキャンにも対応しているツールですが、TrivyをDesktop等で動かす場合のコマンド(Trivyコマンド)にて、CVSSにて定義される深刻度(UNKNOWN、LOW、MEDIUM、HIGH、CRITICAL)を指定することができます。例えばHIGH以上の脆弱性をチェック対象とする場合、

trivy image IMAGE_NAME --severity HIGH,CRITICAL

とコマンドを記載する必要があります。

パラメータを設定

パイプライン編集画面でTask「trivy-scanner」をクリックすると、ARGというパラメータが必須になっていますが、ここにはTrivyコマンドの要素を記載します。今回は上述のコマンドを実行するものとします。

image.png

この様に記載することで、先に示したTrivyコマンドと同じ事を実行してくれます。次に、同じく必須になっているパラメータIMAGE_PATHですが、これはコンテナイメージのパスになります。直前のTask「Buildah」でBuildされたコンテナイメージはINTERNAL_REGISTRY_URLで指定した内部レジストリに格納されています。ということは、IMAGE_PATHにはINTERNAL_REGISTRY_URLを指定してあげればOKです。

image.png

これまで通りWorkspaceには「workspace」を指定すればOKです。「保存」をクリックします。

プッシュイベントを発生させてパイプラインを回す

ではパイプラインを再度回してみましょう。毎度毎度ソースコードを編集するのも面倒なので、GitlabのWebhookのプルダウン「テスト」から「プッシュイベント」をクリックして、強制的にGitLabからWebhook通知を送ります。

image.png

するとパイプラインが回り始めます。

image.png

しばらくすると、パイプラインが成功しました。

image.png

ログのタブからTask「trivy-scanner」のログを見てみます。

image.png

HIGH以上の脆弱性は検知されなかったようです。良かったです。

Total: 0 (HIGH: 0, CRITICAL: 0)

Sonarqubeによるソースコード静的解析を実現

更にパイプラインを増強したいと思います。いわゆる「シフトレフト」を志向する観点で、ソースコードの静的解析は避けては通れないでしょう。「Sonarqube」はソースコードやManifestファイル、IaCの静的解析ツールとして非常に幅広く使われています。

Sonarqubeが利用できるようにする

まずはOpenShiftの中にSonarqubeをDeployします。その前段として、とりあえず適当なNameSpace(例:sonarqube)を作成しました。

image.png

PostgresqlをDeployする

では「SonarqubeをDeploy!」の前に、Sonarqubeのデータを永続化するためのPostgresqlをDeployします。

simple-stateful-app $ oc apply -f sonarqube/postgresql -n sonarqube
deployment.apps/postgresql created
persistentvolumeclaim/postgresql-pvc created
secret/postgresql-secret created
service/postgresql-service created

PostgresqlがDeployされました。Postgresqlはインターネットに公開する必要はないので、Routeは作成しません。

image.png

PostgresqlコンテナイメージはRed Hatが公開しているregistry.redhat.io/rhel8/postgresql-15を利用します。これには、環境変数を設定する事ができます。今回はSecretファイルで以下の環境変数を設定しています。

apiVersion: v1
kind: Secret
metadata:
  name: postgresql-secret
type: Opaque 
stringData: #ユーザ名とパスワード、初期作成するDB名を指定できます
  POSTGRESQL_USER: user
  POSTGRESQL_PASSWORD: password
  POSTGRESQL_DATABASE: sonarqubedb
# 通常のApplication開発において、Secretを公開Repositoryに置くのはNGです。あくまでデモ用途です。

Postgresqlのユーザ名/パスワードに加え、初期作成するDatabase名を指定する事ができます。その他にもいくつかの環境変数を設定する事ができます。詳細はRed Hat Ecosystem Catalog を参照ください。

Sonarqubeの環境変数をApplyする

SonarqubeをPostgresqlに接続するために必要な情報をそれぞれConfigMapとSecretとして用意しています。環境変数のKeyはSonarqubeの仕様に準じています。以下のコマンドをターミナルで実行してこれらをApplyしましょう。

simple-stateful-app $ oc apply -f sonarqube/sonarqube-env -n sonarqube
configmap/mysql-initdb-config created
secret/sonarqube-secret created

Sonarqubeコンテナが接続するPostgresqlコンテナの宛先をConfigMapで指定してます。具体的には以下のYAMLです。

apiVersion: v1
kind: ConfigMap
metadata:
  name: sonarqube-configmap
data:
  SONAR_JDBC_URL: jdbc:postgresql://postgresql-service.sonarqube.svc.cluster.local:5432/sonarqubedb

ここで、postgresql-service.sonarqube.svc.cluster.local:5432はOpenShiftクラスタ内でPostgresqlコンテナにアクセスする為の内部ホスト名です。これは、Serviceによって提供されます。

image.png

また、SonarqubeがPostgresqlコンテナのDatabase「sonarqubedb」にアクセスするための情報(ユーザ名/パスワード)は以下のSecretで提供しています。

apiVersion: v1
kind: Secret
metadata:
  name: sonarqube-secret
type: Opaque 
stringData: 
  SONAR_JDBC_USERNAME: user
  SONAR_JDBC_PASSWORD: password

これは、元々PostgresqlコンテナをDeployする際に利用したSecret「postgresql-secret」で指定したユーザ名/パスワードと同じものです。

SonarqubeをS2IでDeploy

それでは、SonarqubeをDeployしましょう。Sonarqubeの公式Dockerfileを使い、Source ot Image(S2I)を使ってOpenShiftクラスタにSonarqubeをDeployします。本記事の執筆時点ではVer9とVer10が存在しているようですが、今回はサポートなしの無料で利用するため、9/communityの中のDockerfileを使います。

では、OpenShiftのトポロジー画面を右クリックし、「Gitからのインポート」をクリックします。
image.png

必要なパラメータを入力します。

項目
GitリポジトリーURL https://github.com/SonarSource/docker-sonarqube.git
Gitリファレンス master
コンテキストディレクトリー /9/community

値を正しく入力すると、Dockerfileを検知してくれます。
image.png

少し画面を下にスクロールします。「一般」及び「Build」欄はデフォルトの設定をそのまま使います。もしアプリケーション名や名前を変更したければご自由にわかりやすいものに変えてください。

image.png

さらに画面を下にスクロールします。「デプロイ」欄は以下のように設定します。

項目
リソースタイプ Deployment
SONAR_JDBC_URL sonarqube-configmap / SONAR_JDBC_URL
SONAR_JDBC_USERNAME sonarqube-secret / SONAR_JDBC_USERNAME
SONAR_JDBC_PASSWORD sonarqube-secret / SONAR_JDBC_PASSWORD

以下のように入力できればOKです。

image.png

これで、S2Iの際に当該SecretとConfigMapから環境変数を渡してコンテナイメージをBuildしてくれます。もう少し画面を下にスクロールし、「作成」をクリックしましょう。

image.png

Sonarqubeの公式Dockerfileを確認してみると、Sonarqubeコンテナは9000番ポートで公開されています。故にターゲットポートは9000となるわけです。また、この後Sonarqubeにログインしていくつかの設定を行いますので、そのために「routeの作成」にチェックを入れておきます(デフォルトでチェック済み)

SonarqubeコンテナのS2Iが始まるので、しばし待ちます。

image.png

Deployできました!

Sonarqubeにログインする

ではRouteからSonarqubeのコンソール画面にログインしましょう。初期ユーザ情報は以下の通りです。

  • ID: admin
  • Password: admin

image.png

初期パスワードの変更を求められるので、適宜変更して「Update」をクリックします。

image.png

無事にログインできました。

無事にSonarqubeが外部Database(Postgresqlコンテナ)に接続できていると、Administrationメニューの「System」欄でDatabase接続状況を確認することができます。
image.png

SonarqubeとGitlabリポジトリを接続する

ログインが成功しましたので、Sonarqubeのソース解析対象プロジェクトを登録します。

スクリーンショット 2024-12-10 22.58.24.png

今回はGitlabをクリックします。以下のようなポップアップが出てきます。

image.png

項目
Configuration name 適当なものを入力します。今回は「simple-stateful-app」とします
GitLab API URL https://gitlab.com/api/v4
Personal Access Token ご自身のGitリポジトリで作成したもの

Gitlabの「Personal Access Token」は、「設定」→「アクセストークン」と進み、

image.png

「新しいトークンを追加」をクリックし、

image.png

トークン名は適当もの(例:mytoken)を入力し、先ほどのポップアップ画面の「Personal Access Token」欄の説明にあったようにロールは「Reporter」を選択し、「api」スコープを選択します。

SonarQube needs a Personal Access Token to report the Quality Gate status on Merge Requests in GitLab. To create this token, we recommend using a dedicated GitLab account with Reporter permission to all target projects. The token itself needs the api scope.

有効期限は適切に設定してください。設定しなくても構いません。

image.png

設定が完了したら、少し画面を下にスクロールして、「プロジェクトのアクセストークンを作成」をクリックします。

image.png

アクセストークンが作成されました。一度しか表示されないので必ずコピーしてSonarqubeに忘れずに設定しましょう。

image.png

今作成した「Personal Access Token」をペーストしたら、「Save Configration」をクリックします。

image.png

再度「Personal Access Token」の入力を求められますので、同じものをペーストして「Save」をクリックします。

image.png

SonarqubeとGitlabリポジトリが接続されました。それでは、CIツールとの連携の設定を進めます。「Set up」をクリックします。

image.png

SonarqubeとTekton Pipelineを統合する為の設定

Sonarqubeに接続されたGitlabリポジトリの内容を分析する為のCIツールの設定を行いますが、Tektonは選択肢にラインナップされていないので、「Other CI」をクリックします。

スクリーンショット 2024-12-10 23.30.51.png

ここで、CIツール(=Tekton)にわたすべきトークン情報を設定します。「Token name」は適当なものを入力します。デフォルト値でも構いません。また、トークン有効期限は適宜選んでください。設定しなくても構いません。「Generate」をクリックすると、トークンが作成されます。

image.png

トークンが作成されました。「Continue」をクリックします。

スクリーンショット 2024-12-10 23.34.57.png

ここで適切なアプリケーションのBuild方式を選択します。今回作成したアプリケーションは、Node.jsになるので「What option best describes your build?」欄は「Other」をクリックします。次にOS種別を聞かれますので、「What is your OS?」欄は「Linux」を選択します。

image.png

画面を少し下にスクロールすると、「Configure a SONAR_TOKEN environment variable in your CI settings」欄に、SONAR_TOKENというKeyに先ほど作成したトークンの値を設定する様に指定する旨が表示されます。

スクリーンショット 2024-12-10 23.40.14.png

実は、この後設定するSonarqubeによるソースコードスキャンのパラメータに、このSONAR_TOKENを設定することで、Taskが実行できるようになります。また、画面をまた少し下にスクロールすると、「Execute the Scanner」欄にいくつかのパラメータが設定されていますが、この中でprojectKeyを後ほど利用します。

image.png

sonar-scanner \
  -Dsonar.projectKey=gitou-laboo_simple-stateful-app_AZOw-iNqbM60U_OATmZD \
  -Dsonar.sources=. \
  -Dsonar.host.url=https://docker-sonarqube-git-sonarqube.apps.rosa.moomura-hcp.elgb.p3.openshiftapps.com

お疲れ様でした。ともかくSONAR_TOKENprojectKeyを忘れないようにメモしておいてください。

Sonarqubeによるソースコードスキャンをパイプラインに追加&設定する

一旦Sonarqubeの画面はそのままにして、再びOpenShiftのコンソール画面に戻ってきます。これまで通りの流れで、パイプライン編集画面に入ります。

image.png

では、Taskを追加します。

Task「sonarqube-scanner」を追加

Task「git-clone」の前にTaskを追加します。

image.png

これまで通りTaskを検索しますが、検索ボックスには「sonarqube-scanner」と入力します。ここでver1.0かver2.0を選択してInstallしてください。Ver3.0移行はSonarQubeのSaaS版との連携に必要なパラメータ指定が必要なのですが、今回はSelf-Install版を使っているので、1.0か2.0でやってください。今回は一旦2.0のほうでやっていきます

image.png

追加した「sonarqube-scanner」をクリックするとパラメータの一覧が表示されます。

image.png

しかし、設定したい「SONAR_TOKEN」の入力欄が見当たりません。実は、少しTaskのYAMLファイルを編集してあげる必要があります。一度、この編集のしかかり状態のまま、コンソール画面を「管理者向け表示」に変え、「Pipelines」メニューから「Tasks」をクリックすると、NameSpace「simple-stateful-app」に適用されているTaskを確認できます。「Tasks」欄には、既に追加済みの2つのTaskが確認できますが、「sonarqube-scanner」をクリックします。YAMLビューに変更し、以下のように追記してください。

apiVersion: tekton.dev/v1
kind: Task
(中略)
spec:
  params:
    - default: ''
      description: Host URL where the sonarqube server is running
      name: SONAR_HOST_URL
      type: string
    - default: ''
      description: Project's unique key
      name: SONAR_PROJECT_KEY
      type: string
+   - default: ''
+     description: token info
+     name: SONAR_TOKEN
+     type: string
  (中略)
 workspaces:
    - name: source
    - name: sonar-settings

これで、SONAR_TOKENをTaskのパラメータとして追加し、そこに与えられた値をSonarqubeの設定ファイルに引き渡す設定を追記することができました。追記できたら「保存」をクリックします。

image.png

再び、「開発者向け表示」でパイプライン編集画面に戻ります。

image.png

もう一度同じ手順でTask「sonarqube-scanner」のver0.2(インストール済み)を追加します。

image.png

再び「sonarqube-scanner」をクリックすると、今後は「SONAR_TOKEN」がパラメーター欄に表示されました。

image.png

ただし、これまでのTaskの追加の方法同様、Taskのパラメータに具体的な値をハードコーディングするのは避けます。Pipeline側のパラメーターに以下の値を設定しましょう。

名前 説明 デフォルト値
SONAR_HOST_URL Serviceから提供されるSonarqubeの内部ホスト名 http://docker-sonarqube-git.sonarqube.svc.cluster.local:9000
SONAR_PROJECT_KEY SonarqubeのCI統合設定で払い出されたprojectKey <your-projectkey>
SONAR_TOKEN SonarqubeのCI統合設定で払い出されたSONAR_TOKEN <your-sonar-token>

また、Task「sonarqube-scanner」にも、これまで通りパラメーターを$(params.ENV)の形式で設定してあげます。ワークスペースについては、いずれも「workspace」を設定しましょう。つまり、以下のようになっていればOKです。「保存」をクリックしてパイプラインの編集結果を保存します。

スクリーンショット 2024-12-11 0.22.01.png

Sonarqubeの内部ホスト名(クラスタ内からアクセスする際のアドレス)は、SonarqubeコンテナをDeployした際に自動的に作成されたServiceから確認できます。
image.png

さて、SonarqubeをDeployしソースコード静的解析の為のTask「sonarqube-scanner」も追加できました。頑張って各種パラメータも設定しました。はぁ〜疲れた。上手くいくでしょうか!?運命のパイプライン実行です。

Sonarqubeによるソースコードスキャンをパイプライン内で実行する

再度、Gitlabで「プッシュイベント」を発生させて、パイプラインを自動的に回します。

image.png

パイプラインが回り始めました!どうなるでしょうか!?

image.png

あ、、、

image.png

失敗しました。

image.png

焦らず怒らずログを見ます。

image.png

んん?どうやらSonarqubeにログインできていないようです。

step-sonar-properties-create

2024-12-11T09:11:26.790483519Z ---------------------------
2024-12-11T09:11:26.791557738Z sonar.projectKey=gitou-laboo_simple-stateful-app_AZOw-iNqbM60U_OATmZD
2024-12-11T09:11:26.791557738Z sonar.host.url=http://docker-sonarqube-git.sonarqube.svc.cluster.local:9000
2024-12-11T09:11:26.791557738Z sonar.sources=.
step-sonar-scan

2024-12-11T09:11:26.948568549Z INFO: Scanner configuration file: /opt/sonar-scanner/conf/sonar-scanner.properties
2024-12-11T09:11:26.948934483Z INFO: Project root configuration file: /workspace/source/sonar-project.properties
(中略)
Not authorized. Analyzing this project requires authentication. Please provide a user token in sonar.login or other credentials in sonar.login and sonar.password.

どうやら、Task「sonarqube-scanner」においては、「step-sonar-properties-create」において、各種パラメータをプロパティファイルに反映している様です。そして、どうやらパイプライン上のTask「sonarqube-scanner」に設定したパラメータSONAR_TOKENsonar.loginに引き渡されていないということでエラーが起きているという風に読み取れます。では、改めてTask「sonarqube-scanner」のYAMLを確認してみます。パイプラインの詳細画面から、Taskの詳細画面に飛ぶことができます。「Tasks」欄の「sonarqube-scanner」をクリックしてください。

image.png

98行目から101行目までを見ると、なにやらやってます。

image.png

(中略)
        else
          touch sonar-project.properties
          echo "sonar.projectKey=$(params.SONAR_PROJECT_KEY)" >> sonar-project.properties
          echo "sonar.host.url=$(params.SONAR_HOST_URL)" >> sonar-project.properties
          echo "sonar.sources=." >> sonar-project.properties
        fi
(中略)

ここでどうやら、sonar.projectKeyにPipeline側で設定したSONAR_PROJECT_KEYを代入したり、sonar.host.urlSONAR_HOST_URLを代入したりして、sonar-project.propertiesに反映(echo)している様です。ということは、以下のように追記したら、sonar.loginSONAR_TOKENを渡してプロパティファイルに反映してくれるのではないでしょうか?

(中略)
        else
          touch sonar-project.properties
          echo "sonar.projectKey=$(params.SONAR_PROJECT_KEY)" >> sonar-project.properties
          echo "sonar.host.url=$(params.SONAR_HOST_URL)" >> sonar-project.properties
          echo "sonar.sources=." >> sonar-project.properties
+         echo "sonar.login=$(params.SONAR_TOKEN)" >> sonar-project.properties
        fi
(中略)

追記できたら「保存」をクリックします。

image.png

もう一度パイプラインを回す。

三度、root/source/public/index.htmlで色を変更してみましょう。こんな色color: rgb(214, 121, 14);にしてみました。オレンジっぽい色です。ソースコードをPushし、パイプラインを自動起動させましょう!

image.png

パイプラインが回り始めました。

image.png

やりました!パイプラインが成功しました!

image.png

PipelineRunのログで「sonarqube-scanner」をクリックすると、Sonarqubeによるソースコードスキャン結果のログを確認できます。

image.png

また、Sonarqubeの画面コンソールに切り替えると、表示が変わり、スキャン結果の詳細を確認する事ができるようになっています。

image.png

今回、脆弱性は発見されなかったものの、Dockerfileの内容について確認するような指摘をもらいました。

image.png

無事にソースコード編集結果がアプリケーションに反映されています。

image.png

おわりに

最後までお疲れ様でした。本記事で少しでもOpenShift Pipelines(Tekton)でパイプラインを作成・編集し、自動化されたデリバリーの仕組み構築に踏み出す際の敷居を低くできれば幸いです。素のKubernetes自体はコード(YAML)とコマンド(kubectl)による操作しか対応していませんが、OpenShiftはそうした敷居の高さを少しでも低くする為に、豊富な画面コンソールを有しています。Tektonについても、OpenShift Pipelinesとして独自のGUIを提供しています。これは、一部の「CLI絶対君主制」や「YAML原理主義者」の方々からは「けしからん」と思われてしまうかもしれませんが、、しかし「便利なものは使う」という精神に基づき、OpenShiftでは積極的にGUIも使いたいと思う筆者なのでした。

おわり

6
3
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
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?