カスタムのJenkins agentのイメージを作るときに知っておくと良いこと

お久しぶりです。コンサルタントの森 (@mosuke5)です。
コロナで自宅勤務の人も多いかと思いますが、みなさんどう過ごしていますか?
ずっと家にいると辛いので、定期的に息抜きで家の散歩をしています。桜もちょうどきれいでいい花見?になります。

さて、ここ最近は、OpenShift上でのCI/CDパイプラインの構築をたくさん行ってきたのですが、 よくある質問に「どうやってカスタムのJenkins agentを作ったらいいか」がありました。
まとまった情報もインターネットになかったので今回書いてみました。

デフォルトのJenkins agentイメージ

OpenShiftのインストール後のデフォルトでもmaven環境とnodejs環境のagentのイメージがあります。(OCP 4.3環境)
openshiftプロジェクト内にあるimage streamを確認してみます。

$ oc get is -n openshift | grep jenkins-agent
jenkins-agent-maven   image-registry.openshift-image-registry.svc:5000/openshift/jenkins-agent-maven   latest,v4.0   2 days ago
jenkins-agent-nodejs  image-registry.openshift-image-registry.svc:5000/openshift/jenkins-agent-nodejs  latest,v4.0   2 days ago

こちらのイメージの技術情報についてRed Hat Container Catalogから参照することが可能です。 こちらのイメージでことが足りるようであればもうこのブログは閉じてしまって問題ないです(笑)

Red Hat Container Catalogueでは、コンテナイメージの取得や技術情報の閲覧、ビルドしたDockerfileがみることができるので非常に便利です。 とくにイメージをビルドしたDockerfileを確認できるので、イメージの中身を理解するのに役立ちます。 たとえば、Jenkins Agent nodejsのDockerfileはここから確認できます。

カスタマイズ

しかし、実際の運用環境では、デフォルトのJenkins Agentでは事足りなくカスタマイズしたい、自作したいことが多くあるはずです。 例えば、PostgreSQLをデータベースに使っていて、テスト時にアプリケーションから接続できるようにクライアントソフトウェアをインストールしておきたい、 そもそもRubyやPythonなどのデフォルトで提供されている以外の言語を扱いたいなどさまざまかと思います。

ベースイメージ

カスタマイズしたJenkins agent イメージを作成するためのベースイメージを紹介します。
Jenkins agentとして動作させるPodにはJenkins agentのソフトウェアのインストールが必要です。 この段階から自作するのは少し面倒なので、すでにJenkins agentが導入済みのベースイメージを利用すると便利です。

Jenkins Agent Base というイメージを用意しているのでこちらをぜひ利用しましょう。 もし、デフォルトで用意されているjenkins-agent-mavenかjenkins-agent-nodejsに手を入れたいというのであれば、それをベースイメージにしてももちろん構いません。
Jenkins Agent Baseは、Container CatalogueかQuayからダウンロードできます。

ベースイメージが決まれば、Dockerfileを書いてカスタムのJenkins agentのイメージを簡単に作成できます。

FROM quay.io/openshift/origin-jenkins-agent-base:4.6.0
USER root
RUN yum install -y xxxxx
...
USER 1001

Container Quickstarts

Container QuickstartsというGithubのレポジトリでは、Jenkins agentのサンプル(イメージのビルドサンプル)をいくつか公開しています。 RubyやPythonの環境のイメージを作成するためのテンプレートもありますので参考にしてみるといいです。 このレポジトリのものをそのまま利用しないにしても、Jenkins slaveの作り方について参考になるので、みなさんのアプリケーションにあったSlaveの作成に活用しましょう。

github.com

例えば、Ruby2.5のバージョンのJenkins slaveのイメージ作成したければ、こちらのリポジトリのBuildConfigを利用することで簡単に作成することができます。(Rubyのバージョンもう古いですね。。)

$ git clone https://github.com/redhat-cop/containers-quickstarts
$ cd containers-quickstarts/
$ oc process -f .openshift/templates/jenkins-slave-generic-template.yml \
    -p NAME=jenkins-slave-ruby \
    -p SOURCE_CONTEXT_DIR=jenkins-slaves/jenkins-slave-ruby \
    | oc create -f -

手元のDockerで動作確認する

カスタマイズしたJenkins agentのイメージも、最終的にはBuildConfigを用いてビルドできるようにすることが運用上は望ましいです。 しかし、いきなりBuildConfigを設定してビルドするには、デバッグが大変なことがあります。 なので、まずは手元のPC上のDockerなどで必要なソフトウェアのインストールができるかどうかなどを確認するといいでしょう。

例えば、origin-jenkins-agent-baseは下記のようにDocker上で動かして検証できます。
確認した結果を、最終的にDockerfileに記述できればBuildConfigでの生成もできるようになります。

$ docker run -it --entrypoint="/bin/bash" quay.io/openshift/origin-jenkins-agent-base:4.5
container #
container # useradd xxxx
container # yum install xxxx

注意点として、origin-jenkins-agent-baseはENTRYPOINTに["/usr/bin/go-init", "-main", "/usr/local/bin/run-jnlp-client"]が指定されているので、 docker run -it quay.io/openshift/origin-jenkins-agent-base:4.5 /bin/bash として起動しようとしても、エラーで落ちます。
(DockerのCMDとENTRYPOINTの違いは復習)

Jenkins上での作成したイメージの指定方法

カスタマイズ したJenkins agentのイメージを利用するにはJenkinsfileから指定できます。
JenkinsのGUIから利用するイメージの登録もできるのですが、Jenkinsfileに設定を記述することをおすすめします。 理由は、Jenkins内で起動するPodについてもGit管理の対象とすることができるからです。 なるべくJenkins本体への設定を減らし、コード化してGit管理の対象を増やすことは自動化を進めていく上で便利になります。

JenkinsのKubernetesプラグインを利用して、agentとして起動するPodのマニフェストを指定できます。

pipeline {
  // pipelineを実行するagentの設定。yamlファイルで設定を渡せる
  // 可能な限りJenkinsfileにagentの設定をもたせたほうが自動化とGit管理が進むためおすすめ。
  agent {
    kubernetes {
      cloud 'openshift'
      yamlFile 'jenkins-slave-pod.yaml'
    }
  }
  ...
}

Podのマニフェストの例は以下です。

# jenkins-slave-pod.yaml
apiVersion: v1
kind: Pod
spec:
  serviceAccountName: jenkins
  containers:
    - name: jnlp
      image: image-registry.openshift-image-registry.svc:5000/your-project/custom-jenkins-agent
      args: ['$(JENKINS_SECRET)', '$(JENKINS_NAME)']
      tty: false
      env:
        - name: POSTGRESQL_USER
          value: "postgres"

jenkins-agent-mavenのJavaバージョンについて

jenkins-agent-maven のイメージを利用する際にJavaのバージョンについて注意があります。
OCP 4.3以降のイメージではJava8と11の両方がインストールされるようになり切り替えできるようになりました。 コンテナイメージ内でJavaのバージョンを切り替えるスマートな方法がいまのところあまり用意されていないのですが、 Jenkinsパイプライン内で、JAVA_HOMEの指定やalternativesコマンドを使ってalternatives --set java xxxx 設定することで切り替えできます。

$ alternatives --list
ld  auto    /usr/bin/ld.bfd
libnssckbi.so.x86_64    auto    /usr/lib64/pkcs11/p11-kit-trust.so
java_sdk_11_openjdk auto    /usr/lib/jvm/java-11-openjdk-11.0.6.10-1.el7_7.x86_64
java_sdk_1.8.0_openjdk  auto    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64
java_sdk_openjdk    auto    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64
javadocdir  auto    /usr/share/javadoc/java-11-openjdk-11.0.6.10-1.el7_7.x86_64/api
java_sdk_1.8.0  auto    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64
java_sdk_11 auto    /usr/lib/jvm/java-11-openjdk-11.0.6.10-1.el7_7.x86_64
javac   auto    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64/bin/javac
jre_11  auto    /usr/lib/jvm/java-11-openjdk-11.0.6.10-1.el7_7.x86_64
jre_openjdk auto    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64/jre
jre_11_openjdk  auto    /usr/lib/jvm/jre-11-openjdk-11.0.6.10-1.el7_7.x86_64
jre_1.8.0_openjdk   auto    /usr/lib/jvm/jre-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64
java    auto    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64/jre/bin/java
jre_1.8.0   auto    /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64/jre

例えばJenkinsfileないの環境変数の指定で下記のようにコマンドの結果を設定するといいかもしれません。

environment {
    JAVA_HOME = """${sh(
                returnStdout: true,
                script: 'alternatives --list | grep "java_sdk_1.8.0\\s" | awk \'{print $3}\' | tr -d \'\\n\''
            )}"""
}

さいごに

ひさびさの投稿は、華やかな新しいプロダクトの内容とかでない地味な内容でしたが(笑)、 OpenShift上でCI/CDを行っていくのに欠かせないJenkins agentの作り方についてみてきました。
CI環境は、その後の運用環境で動くアプリケーションのビルドなどをする重要な環境です。 利用するソフトウェアのバージョンやテストを妥協すると、いいCI/CDは実現できません。
OpenShift上でCI/CDパイプラインを整備していきたいみなさんのご参考になればと思っています。

最後に個人的に最近書いたCI/CDに関するブログがまあまあ反響がよかったので、関連ブログとして参考にしてもらえればと思います。

blog.mosuke.tech

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