OpenShift Pipeline 入門 ~ Jenkins Pipeline によるビルドとデプロイ


これからOpenShift PipelineによるCI/CDパイプラインに取り組みたいと思います。
今回は以下の記事で説明されているJenkins Pipelineのサンプルを作成、実行してみます。
OpenShift Jenkins Pipeline (DSL) Plugin 入門 - 赤帽エンジニアブログ

Jenkins Master のセットアップ

事前にJenkinsのMasterをOpenShiftクラスタ上にセットアップします。
今回はephemeral版を利用します。実際にMasterが利用可能になるまでには数分かかります。

>oc new-app jenkins-ephemeral
warning: Cannot find git. Ensure that it is installed and in your path. Git is required to work with git repositories.
--> Deploying template "openshift/jenkins-ephemeral" to project nkproject

     Jenkins (Ephemeral)
     ---------
     Jenkins service, without persistent storage.

     WARNING: Any data stored will be lost upon pod destruction. Only use this template for testing.

     A Jenkins service has been created in your project.  Log into Jenkins with your OpenShift account.  The tutorial at https://github.com/openshift/origin/blob/master/examples/jenkins/README.md contains more information about using this template.

     * With parameters:
        * Jenkins Service Name=jenkins
        * Jenkins JNLP Service Name=jenkins-jnlp
        * Enable OAuth in Jenkins=true
        * Memory Limit=1Gi
        * Jenkins ImageStream Namespace=openshift
        * Disable memory intensive administrative monitors=false
        * Jenkins ImageStreamTag=jenkins:2
        * Allows use of Jenkins Update Center repository with invalid SSL certificate=false

--> Creating resources ...
    route.route.openshift.io "jenkins" created
    deploymentconfig.apps.openshift.io "jenkins" created
    serviceaccount "jenkins" created
    rolebinding.authorization.openshift.io "jenkins_edit" created
    service "jenkins-jnlp" created
    service "jenkins" created
--> Success
    Access your application via route 'jenkins-nkproject.2886795287-80-shadow04.environments.katacoda.com'
    Run 'oc status' to view your app.

Jenkins Pipeline サンプルの作成

まずPipeline上でアプリケーションのコンテナイメージをビルドするためのBuildConfigを作成します。
--binary=trueは事前ビルド済のアプリケーションバイナリを実行時に指定してビルドすることを示します。
(指定しているWildflyイメージは元の記事より新しいものを使用しています)

>oc new-build --name=mavenapp --docker-image=openshift/wildfly-160-centos7 --binary=true
warning: Cannot find git. Ensure that it is installed and in your path. Git is required to work with git repositories.
--> Found container image 7ff222e (11 months old) from Docker Hub for "openshift/wildfly-160-centos7"

    WildFly 16.0.0.Final
    --------------------
    Platform for building and running JEE applications on WildFly 16.0.0.Final

    Tags: builder, wildfly, wildfly16

    * An image stream tag will be created as "wildfly-160-centos7:latest" that will track the source image
    * A source build using binary input will be created
      * The resulting image will be pushed to image stream tag "mavenapp:latest"
      * A binary build was created, use 'oc start-build --from-dir' to trigger a new build

--> Creating resources with label build=mavenapp ...
    imagestream.image.openshift.io "wildfly-160-centos7" created
    imagestream.image.openshift.io "mavenapp" created
    buildconfig.build.openshift.io "mavenapp" created
--> Success

次にJenkinsfileによるPipeline定義を含むBuildConfigを作成します。

>oc create -f https://raw.githubusercontent.com/hashnao/openshift-jee-sample/master/mavenapp-pipeline.yml
buildconfig.build.openshift.io/mavenapp-pipeline created

Jenkinsfileの内容は以下の通りで、BuildConfigに埋め込まれています。
内容の詳しい説明は元の記事にありますが、以下のステージで構成されています。

  • Git リポジトリからアプリケーションのソースを git clone (Clone Source)
  • Maven で成果物 (WAR) をビルド (Build Artifact)
  • Maven で単体テストを実行 (Run Unit Test)
  • 生成した成果物を指定し、コンテナイメージをバイナリビルドする (Build Image)
  • ビルドしたコンテナイメージにバージョン + ビルド番号でタグをリリースする (Promote Image)
        pipeline {
          agent { label "maven" }
          environment {
            version = "1.0"
            devTag = "${version}-${BUILD_NUMBER}"
          }
          stages {
            stage("Clone Source") {
              steps {
                git url: "https://github.com/openshift/openshift-jee-sample.git", branch: "master"
              }
            }
            stage("Build Artifacts") {
              steps {
                sh "mvn clean package -Popenshift -DskipTests=true"
                stash includes: "target/ROOT.war", name: "war"
              }
            }
            stage("Run Unit Test") {
              steps {
                sh "mvn test"
              }
            }
            stage("Build Image") {
              steps {
                unstash "war"
                script {
                  openshift.withCluster() {
                    openshift.withProject() {
                      def nb = openshift.selector("bc", "mavenapp")
                      nb.startBuild("--from-file=./target/ROOT.war").logs("--follow")
                      def buildSelector = nb.narrow("bc").related("builds")
                      timeout(5) {
                        buildSelector.untilEach(1) {
                          return (it.object().status.phase == "Complete")
                        }
                      }
                      echo "Builds have been completed: ${buildSelector.names()}"
                    }
                  }
                }
              }
            }
            stage("Promote Image") {
              steps {
                script {
                  openshift.withCluster() {
                    openshift.withProject() {
                      // Tag the mavenapp:latest image as mavenapp:${devTag}
                      openshift.tag("mavenapp:latest", "mavenapp:${devTag}")
                    }
                  }
                }
              }
            }
          }
        }

Jenkins Pipeline サンプルの実行

oc start-buildによりJenkins Pipelineによるビルドを実行します。

>oc start-build mavenapp-pipeline
build.build.openshift.io/mavenapp-pipeline-1 started

ビルドは数分で完了します。
oc get buildすると作成した2つのビルドが実行されていることがわかります。

>oc get build
NAME                  TYPE              FROM     STATUS     STARTED         DURATION
mavenapp-pipeline-1   JenkinsPipeline            Complete   3 minutes ago
mavenapp-1            Source            Binary   Complete   2 minutes ago   1m38s

Jenkins Pipelineの状況はOpenShift (上) とJenkins (下) のWebコンソールで確認できます。

ビルドの結果、アプリケーション (mavenapp) の新規ImageStreamとImageStreamTagが作成されています。
mavenapp:1.0-1がPipelineの最後のステージでタグ付けされたものになります。

>oc get imagestream
NAME                  IMAGE REPOSITORY                                                                        TAGS           UPDATED
mavenapp              default-route-openshift-image-registry.apps-crc.testing/nkproject/mavenapp              1.0-1,latest   28 seconds ago
wildfly-160-centos7   default-route-openshift-image-registry.apps-crc.testing/nkproject/wildfly-160-centos7   latest         5 minutes ago

>oc get imagestreamtag
NAME                         IMAGE REF                                                                                                                                     UPDATED
mavenapp:1.0-1               image-registry.openshift-image-registry.svc:5000/nkproject/mavenapp@sha256:e5e6894d57bb3cf5ec4e569f10b8ca464f5b62653366061426c993ba13cc3c19   36 seconds ago
mavenapp:latest              image-registry.openshift-image-registry.svc:5000/nkproject/mavenapp@sha256:e5e6894d57bb3cf5ec4e569f10b8ca464f5b62653366061426c993ba13cc3c19   40 seconds ago
wildfly-160-centos7:latest   openshift/wildfly-160-centos7@sha256:d12c6ef86991edf35e51aef804822f76897cbc579f7518ce84eaf55ec8ec6f9d                                         5 minutes ago

イメージのデプロイとアプリの実行

ビルドされたアプリケーションイメージをoc new-appでデプロイします。

>oc new-app -i mavenapp:latest
warning: Cannot find git. Ensure that it is installed and in your path. Git is required to work with git repositories.
--> Found image f419a9c (2 minutes old) in image stream "nkproject/mavenapp" under tag "latest" for "mavenapp:latest"

    WildFly 16.0.0.Final
    --------------------
    Platform for building and running JEE applications on WildFly 16.0.0.Final

    Tags: builder, wildfly, wildfly16

    * This image will be deployed in deployment config "mavenapp"
    * Port 8080/tcp will be load balanced by service "mavenapp"
      * Other containers can access this service through the hostname "mavenapp"

--> Creating resources ...
    deploymentconfig.apps.openshift.io "mavenapp" created
    service "mavenapp" created
--> Success
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose svc/mavenapp'
    Run 'oc status' to view your app.

>oc get pod
NAME                READY   STATUS      RESTARTS   AGE
jenkins-1-64gbk     1/1     Running     0          10m
jenkins-1-deploy    0/1     Completed   0          10m
mavenapp-1-build    0/1     Completed   0          4m56s
mavenapp-1-deploy   0/1     Completed   0          59s
mavenapp-1-jvtpt    1/1     Running     0          50s

デプロイしたアプリケーションにアクセスするためにRouteを作成します。

>oc expose svc/mavenapp
route.route.openshift.io/mavenapp exposed

>oc get route
NAME       HOST/PORT                                                           PATH   SERVICES   PORT       TERMINATION     WILDCARD
jenkins    jenkins-nkproject.2886795308-80-jago05.environments.katacoda.com           jenkins    <all>      edge/Redirect   None
mavenapp   mavenapp-nkproject.2886795308-80-jago05.environments.katacoda.com          mavenapp   8080-tcp                   None

RouteのURLにブラウザでアクセスすると、以下の画面が表示されました。

Jenkins Pipelineの修正と実行

今回作成したJenkins Pipelineにイメージのデプロイを組み込んでみます。
実は先ほどのoc new-appにより作成されるDeploymentConfigにより、指定されたImageStreamに紐づくイメージが更新されると自動で再デプロイされる状態に既になっているのですが、ここではPipelineから明示的にデプロイを行うように変更します。

>oc get deploymentconfig mavenapp
NAME       REVISION   DESIRED   CURRENT   TRIGGERED BY
mavenapp   2          1         1         config,image(mavenapp:latest)

上のDeploymentConfigのトリガーをoc set triggersで変更し、自動で再デプロイしないようにします。

>oc set triggers dc/mavenapp --manual
deploymentconfig.apps.openshift.io/mavenapp triggers updated

Jenkins Pipeline (mavenapp-pipeline) のJenkinsfile部分を修正し、以下のdeployステージをBuild Imageステージの後に追加します。
OpenShiftのWebコンソール上で直接修正することが可能です。

            stage('deploy') {
              steps {
                script {
                  openshift.withCluster() {
                    openshift.withProject() {
                      def rm = openshift.selector("dc", "mavenapp").rollout().latest()
                      timeout(5) {
                        openshift.selector("dc", "mavenapp").related('pods').untilEach(1) {
                          return (it.object().status.phase == "Running")
                        }
                      }
                    }
                  }
                }
              }
            }

Pipelineの実行により、イメージのビルドからデプロイまでがPipeline上で実行されます。

>oc start-build mavenapp-pipeline
build.build.openshift.io/mavenapp-pipeline-5 started

>oc get pod
NAME                READY   STATUS      RESTARTS   AGE
jenkins-1-64gbk     1/1     Running     0          51m
jenkins-1-deploy    0/1     Completed   0          51m
mavenapp-1-build    0/1     Completed   0          45m
mavenapp-1-deploy   0/1     Completed   0          41m
mavenapp-2-build    0/1     Completed   0          32m
mavenapp-2-deploy   0/1     Completed   0          31m
mavenapp-3-5mm76    1/1     Running     0          66s
mavenapp-3-build    0/1     Completed   0          21m
mavenapp-3-deploy   0/1     Completed   0          76s
mavenapp-4-build    0/1     Completed   0          11m
mavenapp-5-build    0/1     Completed   0          2m39s

おわりに

OpenShiftでは予めビルドやデプロイの基本的な仕組みが備わっていますが、Jenkins Pipeline Pluginを使ってJenkins Pipelineとそれらを組み合わせることにより、高度なCI/CDパイプラインをシンプルに実装できます。