普段はエンタープライズのお客様と一緒に仕事しているのですが、 ビルドやデプロイのパイプラインには既存資産や知見を使いまわせるJenkinsを使いたいというニーズは未だに根強いと感じています。
しかしコンテナイメージの形でビルドツールやアプリケーションサーバなどが配布されているこの時代、 Jenkinsからもこれらのコンテナを使えると便利です。 少なくともかつてのようにController (Master) にいちいちインストールしたくない。
そこでこの記事ではOpenShift上にJenkinsをデプロイし、AgentもOpenShiftのPodで動かします。
検証環境
- OpenShift 4.10.3 (Developer Sandboxを利用しています)
- Jenkins 2.319.2
サンプル構成
今回組み立てる構成は以下の通りです。
- Jenkins ControllerとAgentはOpenShiftのPodで立てます。
- AgentのPodから外部のGitリポジトリ(GitHub)からソースコードをクローンします。
- Pod内のMavenコンテナでJavaのアプリケーションをビルドします。
- ビルド済みの成果物 (WARファイル) はJenkins Controllerにアーカイブします。
OpenShiftでJenkins Controllerを動かす
OpenShiftでは、Jenkins Controllerを簡単にプロビジョニングするためのテンプレートが用意されています。 テンプレートを開発者カタログから起動します。 今回は簡易のためにPVを使わないテンプレート ("Ephemeral" とあるもの) を選択します。
テンプレートから起動すると、5分程度でJenkins ControllerのPodが立ち上がります。 Topology画面か、Routeの設定からJenkinsのUIにアクセスできます。
初回アクセス時はOpenShiftの認証情報でSSOするための同意画面が表示されますが、"Login" "OK" と続けてボタンを押せばOKです。
Jenkins Controllerを起動する手順は以上です。
Jenkins Agentのパイプラインスクリプトを記述する
OpenShift (またはKubernetes) でJenkins AgentのPodを動かすためには、Kubernetes Pluginを使用します。 OpenShiftのテンプレートから起動したJenkins ControllerにはこのPluginは既にインストール・設定済みなので、 今回は特にPluginに関する作業は不要です。
Kubernetes Pluginの仕組みとしては、Pipelineの定義にYAMLを埋め込むかYAMLファイルを読み込ませることで一時的なPodを立ち上げます。 この一時的なPodをAgentとして利用します。Pipelineジョブ実行後はPodは自動的に削除されます。
早速Declarative Pipelineを記述していきます。
Declarative Pipeline that use OpenShift/Kubernete…
Agentの定義は、pipeline > agent > kubernetes に定義しています。
ビルドに使用するコンテナとして別途 maven
コンテナを定義しています。
このコンテナ内でPipelineのstepを実行するため、defaultContainer 'maven'
を指定しています。
また、Agent用に作成されるPodにはJenkins Controllerと疎通するための jnlp
というコンテナが自動的に作成されます。
jnlp
コンテナは特に指定せずともAgent定義に勝手に追加される仕組みになっていますが、設定を上書きする場合は記述が必要です。
上記のサンプルで最も重要な設定は、コンテナのホームディレクトリの指定です。
Kubernetes PluginにおいてはPodのホームディレクトリがデフォルト /
となるため、OpenShiftではこのままではファイルの書き込みができません。回避策はKubernetes Pluginのドキュメント「Running on OpenShift」に記載されている以下の2つのいづれかを実施する必要があります。
- OpenShift用のJenkins Agentのベースイメージからビルドされたコンテナイメージを利用する。
- 別途 volume を作成して各コンテナの
/home/jenkins
にマウント & 環境変数HOME
にもこのパスで上書き指定する。
今回のサンプル構成では (2) を採用しています。
volumes
セクションにemptyDirでhome-volumeを作成し、jnlp
と maven
の両方のコンテナにマウントしています。
パイプラインジョブを作成する
パイプラインスクリプトを記述したら、パイプラインジョブを作成します。 今回は「OpenShift Agent Sample」という名前を付けて作成します。
JenkinsのUIから 新規ジョブ作成 > パイプラインを選択します。
パイプラインジョブの設定後半に「パイプライン」のセクションがあるので、ここに先ほどのパイプラインスクリプトを貼り付けて保存します。
なお、今回はパイプラインスクリプトを直書きしましたが、プロダクション環境で動かすつもりであれば Jenkinsfile として別ファイルに外出ししてGitリポジトリから取得することが推奨です。(参考:「Top 10 Best Practices for Jenkins Pipeline Plugin」)
パイプラインジョブを動かしてみる
作成したパイプラインジョブを実行します。 パイプラインジョブを開き、「ビルドを実行」でジョブを開始します。
ジョブが開始されると、パイプライン名をプレフィックスとした名前のPodが立ち上がります。
ビルドが無事完了すれば、「ビルド成果物」にWARファイルが登録されています。 また、実行ログは「コンソール出力」画面から参照できます。
うまくいかない例:ホームディレクトリを上書きしない場合
パイプラインスクリプトにおける最重要設定としてホームディレクトリの指定がありました。
もしホームディレクトリを上書きしない場合、ホームディレクトリが /
となっているワーニングメッセージが表示されます。
また、ファイルの書き込みができないのでジョブが落ちます。
ホームディレクトリを上書きしない場合のログは以下の通りです。
[Pipeline] Start of Pipeline (...中略...) [WARNING] HOME is set to / in the jnlp container. You may encounter troubles when using tools or ssh client. This usually happens if the uid doesnt have any entry in /etc/passwd. Please add a user to your Dockerfile or set the HOME environment variable to a valid directory in the pod template definition. Running on openshift-agent-sample-1-aaaaaa-bbbbb-ccccc in /home/jenkins/agent/workspace/OpenShift Agent Sample [Pipeline] { [Pipeline] writeFile [Pipeline] writeFile [Pipeline] container [Pipeline] { [Pipeline] sh + mvn clean package -B -ntp -DskipTests [ERROR] Could not create local repository at /.m2/repository -> [Help 1] (...中略...) [Pipeline] // container [Pipeline] } [Pipeline] // node [Pipeline] } [Pipeline] // podTemplate [Pipeline] End of Pipeline ERROR: script returned exit code 1 Finished: FAILURE
以上。