OpenShift Pipeline 入門～PipelineからTemplateの利用

今回は以下にあるPipelineのTutorialを実施します。
OpenShiftのドキュメントに記載されたものになります。
[5.4. Pipeline ビルド 4.2 | Red Hat Customer Portal](https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.2/html/builds/builds-strategy-pipeline-build_build-strategies)

>nodejs-mongodb.json テンプレートを使用して Node.js/MongoDB アプリケーションをビルドし、デプロイし、検証する OpenShift Pipeline を作成する方法を紹介します。 

# サンプル Jenkins Pipeline の作成

Pipelineのyaml (nodejs-sample-pipeline.yaml) を作成して、BuildConfigを作成します。(Jenkins Masterは構成済みの想定)

```
>oc create -f nodejs-sample-pipeline.yaml
buildconfig.build.openshift.io/nodejs-sample-pipeline created
```

nodejs-sample-pipeline.yamlの内容は以下の通りです。
(Jenkinsfile部が長いですがそのままいきます)


```yaml

kind: "BuildConfig"
apiVersion: "v1"
metadata:
  name: "nodejs-sample-pipeline"
spec:
  strategy:
    jenkinsPipelineStrategy:
      jenkinsfile: |-
        def templatePath = 'https://raw.githubusercontent.com/openshift/nodejs-ex/master/openshift/templates/nodejs-mongodb.json' 
        def templateName = 'nodejs-mongodb-example' 
        pipeline {
          agent {
            node {
              label 'nodejs' 
            }
          }
          options {
            timeout(time: 20, unit: 'MINUTES') 
          }
          stages {
            stage('preamble') {
                steps {
                    script {
                        openshift.withCluster() {
                            openshift.withProject() {
                                echo "Using project: ${openshift.project()}"
                            }
                        }
                    }
                }
            }
            stage('cleanup') {
              steps {
                script {
                    openshift.withCluster() {
                        openshift.withProject() {
                          openshift.selector("all", [ template : templateName ]).delete() 
                          if (openshift.selector("secrets", templateName).exists()) { 
                            openshift.selector("secrets", templateName).delete()
                          }
                        }
                    }
                }
              }
            }
            stage('create') {
              steps {
                script {
                    openshift.withCluster() {
                        openshift.withProject() {
                          openshift.newApp(templatePath) 
                        }
                    }
                }
              }
            }
            stage('build') {
              steps {
                script {
                    openshift.withCluster() {
                        openshift.withProject() {
                          def builds = openshift.selector("bc", templateName).related('builds')
                          timeout(5) { 
                            builds.untilEach(1) {
                              return (it.object().status.phase == "Complete")
                            }
                          }
                        }
                    }
                }
              }
            }
            stage('deploy') {
              steps {
                script {
                    openshift.withCluster() {
                        openshift.withProject() {
                          def rm = openshift.selector("dc", templateName).rollout()
                          timeout(5) { 
                            openshift.selector("dc", templateName).related('pods').untilEach(1) {
                              return (it.object().status.phase == "Running")
                            }
                          }
                        }
                    }
                }
              }
            }
            stage('tag') {
              steps {
                script {
                    openshift.withCluster() {
                        openshift.withProject() {
                          openshift.tag("${templateName}:latest", "${templateName}-staging:latest") 
                        }
                    }
                }
              }
            }
          }
        }
    type: JenkinsPipeline

```

ポイントは以下のステージ、Templateを指定して新規アプリケーションを作成しています。必要なリソースのビルド、デプロイはこのTemplateにより行われます。

```groovy
   stage('create') {
      steps {
        script {
            openshift.withCluster() {
                openshift.withProject() {
                  openshift.newApp(templatePath) 
                }
            }
        }
      }
    }
```

#サンプル Pipeline の実行

作成したBuildConfigを実行します。

```
>oc start-build nodejs-sample-pipeline
build.build.openshift.io/nodejs-sample-pipeline-1 started
```

WebコンソールからPipeline (Build:nodejs-sample-pipeline-1) の状況を確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/de8a7330-8d07-d29b-60db-208dcc7968e8.png)

----

暫く待っていたのですが、buildステージで失敗してしまいました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/38a36f74-7adc-ee5f-08ef-00aa934a6f0b.png)

LogsからJenkinsのログを参照します。
Red Hat Developer IDによるOpenShiftログインが求められ、以下の画面でアクセスを許可します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/4695f66b-f535-3a74-8bac-341d8985f078.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/88d8e003-9ebd-0e4a-ef8d-ee7ee44ced42.png)

見慣れたJenkinsのコンソールへ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/54986bc5-5ca8-1323-4304-f4985de66234.png)

どうもTimeoutしている (待ち時間内にTemplateで定義されたBuildが終了しなかった) ようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/c260e9ba-4721-6f94-2695-550af7bb9c72.png)

Jenkinsfileを見ると、BuildConfig:nodejs-mongodb-exampleのBuildを待っていたようです。
そこで該当のBuildConfigのEventsを確認すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/45bcb49a-4873-49f9-664a-8c771c6b4d8c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/35fc6c5b-f661-e339-afe2-12e9872239e3.png)

指定されているImageStreamTagが存在しないようです。
`oc get imagestreamtag` で調べてみると、

```
>oc get imagestreamtag -n openshift |findstr nodejs
jenkins-agent-nodejs:latest                  image-registry.openshift-image-registry.svc:5000/openshift/jenkins-agent-nodejs@sha256:8fa18b9fd93cac72f3d9f356fe3633c42e080940ebe53eb14a00551d7db10148                  8 weeks ago
jenkins-agent-nodejs:v4.0                    image-registry.openshift-image-registry.svc:5000/openshift/jenkins-agent-nodejs@sha256:8fa18b9fd93cac72f3d9f356fe3633c42e080940ebe53eb14a00551d7db10148                  8 weeks ago
nodejs:10                                    image-registry.openshift-image-registry.svc:5000/openshift/nodejs@sha256:74a3ef2964efc03dfc239da3f09691b720ce54ff4bb47588864adb222133f0fc                                8 months ago
nodejs:8                                     image-registry.openshift-image-registry.svc:5000/openshift/nodejs@sha256:bf4030ac45b8b3ff0ea2892261f1fb5df5b077a67ef717836ac0de879cc775d9                                5 months ago
nodejs:8-RHOAR                               image-registry.openshift-image-registry.svc:5000/openshift/nodejs@sha256:7f4b9099ea7e1ba5b2be98304c2d0c2eb1d738d8ceee9d9a1464e71a08d78dd4                                6 months ago
nodejs:latest                                image-registry.openshift-image-registry.svc:5000/openshift/nodejs@sha256:74a3ef2964efc03dfc239da3f09691b720ce54ff4bb47588864adb222133f0fc                                8 months ago
```
なので、確かに指定された`nodejs:12`というTagが存在しません。

--------------

tagの部分をlatest (存在するImageStreamTag) に修正してみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/9986d85c-141c-588f-08de-ece92f3ddd1c.png)

Saveで反映されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/2df13b9a-2a06-3156-ce82-3b1f7bb28adf.png)

修正により自動で新たなBuildが実行され、成功したようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/15bee274-9d8e-7221-4542-4a6d77899228.png)

サンプルのnodejsやmongodbのPodがデプロイされていました。
(元のPipelineは失敗したままです・・・)

```
>oc get all
NAME                                  READY   STATUS      RESTARTS   AGE
pod/mongodb-1-8r2jx                   1/1     Running     0          67m
pod/mongodb-1-deploy                  0/1     Completed   0          67m
pod/nodejs-mongodb-example-1-build    0/1     Completed   0          4m50s
pod/nodejs-mongodb-example-1-deploy   0/1     Completed   0          3m42s
pod/nodejs-mongodb-example-1-mb9zx    1/1     Running     0          3m39s
・・・・
```

# Pipeline の修正と再実行

この状況でサンプル Pipeline を成功させるにはどうすればよいでしょうか。
(そのまま再実行しても手で修正したBuildConfigがTemplateから再作成されるため再度失敗します)

使用しているTemplateの中身を確認してみます。(Jenkinsfileの先頭で定義)
https://raw.githubusercontent.com/openshift/nodejs-ex/master/openshift/templates/nodejs-mongodb.json

以下抜粋です

```json
    {
      "kind": "BuildConfig",
      "apiVersion": "v1",
      "metadata": {
        "name": "${NAME}",
        "annotations": {
          "description": "Defines how to build the application",
          "template.alpha.openshift.io/wait-for-ready": "true"
        }
      },
      "spec": {
        "source": {
          "type": "Git",
          "git": {
            "uri": "${SOURCE_REPOSITORY_URL}",
            "ref": "${SOURCE_REPOSITORY_REF}"
          },
          "contextDir": "${CONTEXT_DIR}"
        },
        "strategy": {
          "type": "Source",
          "sourceStrategy": {
            "from": {
              "kind": "ImageStreamTag",
              "namespace": "${NAMESPACE}",
              "name": "nodejs:${NODEJS_VERSION}"
            },
            "env": [
              {
                "name": "NPM_MIRROR",
                "value": "${NPM_MIRROR}"
              }
            ]
          }
        },
・・・
  "parameters": [
    {
      "name": "NAME",
      "displayName": "Name",
      "description": "The name assigned to all of the frontend objects defined in this template.",
      "required": true,
      "value": "nodejs-mongodb-example"
    },
    {
      "name": "NAMESPACE",
      "displayName": "Namespace",
      "description": "The OpenShift Namespace where the ImageStream resides.",
      "required": true,
      "value": "openshift"
    },
    {
      "name": "NODEJS_VERSION",
      "displayName": "Version of NodeJS Image",
      "description": "Version of NodeJS image to be used (10, 12, or latest).",
      "value": "12",
      "required": true
    },
    {
      "name": "MONGODB_VERSION",
      "displayName": "Version of MongoDB Image",
      "description": "Version of MongoDB image to be used (3.6 or latest).",
      "value": "3.6",
      "required": true
    },
・・・
```
これによると、該当のBuildConfigで使用するnodejsのImageStreamTagは`NODEJS_VERSION`という変数でパラメータ化されているようです。
ということは、Templateの利用時にこのパラメータに存在するバージョンのImageStreamTagを指定すれば良さそうです。

元のPipelie用BuildConfigにて、Jenkinsfile部のcreateステージ (先ほどポイントとして抜粋した部分) を以下のように修正します。
`--param`以下の指定は`oc new-app`のオプションで、実行時Templateにパラメータを渡すための指定方法としてここでも使えます。
この辺りは、HelmのTemplateとよく似た仕組みですね。

```groovy
    stage('create') {
      steps {
        script {
            openshift.withCluster() {
                openshift.withProject() {
                  openshift.newApp(templatePath, "--param=NODEJS_VERSION=latest") 
                }
            }
        }
      }
    }
```

再度BuildConfigを作成し、変更反映を確認します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/1f83924c-e3ed-94d2-8dc5-72d27f45b917.png)

Pipelineを再実行すると、今度はBuildが無事成功しました。
(OpenShiftのコンソール)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/4edacfc0-aa75-d7e7-6e4e-3875f8445a34.png)
(Jenkinsのコンソール)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/1de362cb-6ec2-166d-75e2-7bf7875febcb.png)

tagステージの処理により、ImageStreamへのタグ付けも行われています。

```
>oc get imagestream
NAME                             IMAGE REPOSITORY                                                                                   TAGS     UPDATED
nodejs-mongodb-example           default-route-openshift-image-registry.apps-crc.testing/nkproject/nodejs-mongodb-example           latest   38 minutes ago
nodejs-mongodb-example-staging   default-route-openshift-image-registry.apps-crc.testing/nkproject/nodejs-mongodb-example-staging   latest   38 minutes ago

>oc get imagestreamtag
NAME                                    IMAGE REF                                                                                                                                                           UPDATED
nodejs-mongodb-example:latest           image-registry.openshift-image-registry.svc:5000/nkproject/nodejs-mongodb-example@sha256:be25fa2653adde1b9ee70ac82cd8bba9f717ad128635640a4db659c07d44b402           38 minutes ago
nodejs-mongodb-example-staging:latest   image-registry.openshift-image-registry.svc:5000/nkproject/nodejs-mongodb-example-staging@sha256:be25fa2653adde1b9ee70ac82cd8bba9f717ad128635640a4db659c07d44b402   38 minutes ago

```

当然ですが、RouteのURLにアクセスするとアプリケーションにアクセスできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/96701/39bebdd7-f176-9430-f3dd-6148cd8bf9e5.png)

#おわりに
今回はJenkins PipelineからのTemplate利用を確認しました。
よくあるリソース構成のパターンをTemplateとして定義しておけば、Pipelineもシンプルにでき、横展開も容易になりそうですね。