はじめに
この記事では、OpenShift 4.8のOpenShift GitOpsオペレーターの「Argo CD」機能、OpenShift Pipelinesオペレーターの「トリガー」機能と「パイプライン」機能を使用して下図に示す環境を構築します。sourceリポジトリが変更された場合、OpenShift Pipelinesによって変更内容が反映されたコンテナイメージが作成され、OpenShift GitOpsにより自動的にデプロイされます。
「Argo CD」と「パイプライン」は一般公開機能、「トリガー」はテクノロジープレビューです。
環境やアプリケーションについては以下の記事を参照ください。
■ GitBucket環境
■ Spring Bootアプリケーション
1. オペレーターの導入
OpenShiftコンソールのOperatorHubからオペレーターを導入します。
■ Red Hat OpenShift GitOpsオペレーター
■ Red Hat OpenShift Pipelinesオペレーター
2. Red Hat OpenShift GitOpsオペレーターの利用
GitOpsオペレーター(Argo CD)はgitサーバー上のマニフェストを指定したネームスペースに適用します。事前準備としてコンテナイメージを作成し、同コンテナイメージからデプロイメントを作成するマニフェスト「spring-liberty.yaml」をGitBucketのmanifestリポジトリに保管します。
2.1. コンテナイメージの作成
OpenShiftでコンテナイメージ(イメージストリーム)を作成します。コンテナイメージはOpen Libertyではなく、Universal Base ImageベースのAdoptOpenJDKを元にしました。前者はUbuntuベースですが、apt-getコマンドに必要なファイルが削除されているらしく、追加パッケージ導入を行いたい場合に不便なためです。
ls -l
### 標準出力↓
-rw-r--r--. 1 root root 709 7月 21 23:15 Dockerfile
-rw-r--r--. 1 root root 90051554 7月 8 21:15 openliberty-webProfile8-21.0.0.7.zip
-rw-r--r--. 1 root root 669 7月 16 20:47 server.xml
-rw-r--r--. 1 root root 13138959 7月 16 20:26 spring-liberty-plain.war
oc new-project spring-liberty
oc new-build --name=spring-liberty --strategy=docker --binary
oc start-build spring-liberty --from-dir=. --follow
### 標準出力↓
Caching blobs under "/var/cache/blobs".
Pulling image adoptopenjdk/openjdk11:ubi ...
Getting image source signatures
Copying blob sha256:a50df8fd88fecefc26fd331f832672108deb08cf9d2b303a5b86156a7f51b5d8
Copying blob sha256:1cadda38f72dece653de82063e3c8e910265fe7a342ec2fb73ad8e540c47e1f7
Copying blob sha256:55856c01f309dc2b4236d868a57af0d1fdaeed19a8a30d859885994cb716811a
Copying blob sha256:51ca547cb0d95827734417e81c9460d237e576f948eb5dde6fe7985814811678
Copying config sha256:5a656a98d5affc32fb7fdf1fc24210978d7d06200c4ce68040a4d65d9aa5cc7e
Writing manifest to image destination
Storing signatures
Adding transient rw bind mount for /run/secrets/rhsm
STEP 1: FROM adoptopenjdk/openjdk11:ubi
STEP 2: ENV PATH=/opt/ol/wlp/bin:$PATH LOG_DIR=/logs
--> ede80b38f8e
STEP 3: RUN mkdir /opt/ol /logs
--> 48ae27ea010
STEP 4: COPY openliberty-webProfile8-21.0.0.7.zip /opt/ol/
--> e2c5428489f
STEP 5: WORKDIR /opt/ol
--> f81995989a1
STEP 6: RUN /opt/java/openjdk/bin/jar -xf openliberty-webProfile8-21.0.0.7.zip && rm -f openliberty-webProfile8-21.0.0.7.zip && chmod +x /opt/ol/wlp/bin/server && /opt/ol/wlp/bin/server create && ln -s /opt/ol/wlp/usr/servers/defaultServer /config
Server defaultServer created.
--> 241d11d5490
STEP 7: COPY server.xml /config/
--> 52014da9879
STEP 8: COPY spring-liberty-plain.war /config/apps/
--> e3ae7198e68
STEP 9: RUN chmod -R g+rw /opt/ol/wlp && chmod -R g+rw /logs
--> 39c92498925
STEP 10: EXPOSE 9080
--> b9187b0ed67
STEP 11: CMD ["/opt/ol/wlp/bin/server","run","defaultServer"]
--> 27ac34fba56
STEP 12: ENV "OPENSHIFT_BUILD_NAME"="spring-liberty-1" "OPENSHIFT_BUILD_NAMESPACE"="spring-liberty"
--> d6cb92453ae
STEP 13: LABEL "io.openshift.build.name"="spring-liberty-1" "io.openshift.build.namespace"="spring-liberty"
STEP 14: COMMIT temp.builder.openshift.io/spring-liberty/spring-liberty-1:4c475cdd
--> 3ac5f861cbb
3ac5f861cbb75a8725d8d371ed178fe52eb8fc3ee3f910b58d0fc614daadfcb2
Pushing image image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty:latest ...
・・・
Successfully pushed image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty@sha256:0d58eabe3907412d77e4951d672ec3baf1b6b1bdbc8a79f7e573ad2a85ebb4cb
Push successful
oc get is
### 標準出力↓
NAME IMAGE REPOSITORY TAGS UPDATED
spring-liberty image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty latest About a minute ago
FROM adoptopenjdk/openjdk11:ubi
ENV PATH=/opt/ol/wlp/bin:$PATH \
LOG_DIR=/logs
RUN mkdir /opt/ol /logs
COPY openliberty-webProfile8-21.0.0.7.zip /opt/ol/
WORKDIR /opt/ol
RUN /opt/java/openjdk/bin/jar -xf openliberty-webProfile8-21.0.0.7.zip \
&& rm -f openliberty-webProfile8-21.0.0.7.zip \
&& chmod +x /opt/ol/wlp/bin/server \
&& /opt/ol/wlp/bin/server create \
&& ln -s /opt/ol/wlp/usr/servers/defaultServer /config
COPY server.xml /config/
COPY spring-liberty-plain.war /config/apps/
RUN chmod -R g+rw /opt/ol/wlp \
&& chmod -R g+rw /logs
EXPOSE 9080
CMD ["/opt/ol/wlp/bin/server", "run", "defaultServer"]
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
<!-- Enable features -->
<featureManager>
<feature>jsp-2.3</feature>
</featureManager>
<!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
<httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" accessLoggingRef="accessLogging" />
<httpAccessLogging id="accessLogging" filePath="/logs/http_access.log"/>
<!-- Automatically expand WAR files and EAR files -->
<applicationManager autoExpand="true"/>
<webApplication contextRoot="/" location="spring-liberty-plain.war" />
</server>
2.2. GitBucketへのマニフェストの保管
GitBucketのmanifestリポジトリにマニフェスト「spring-liberty.yaml」を保管します。
apiVersion: apps/v1
kind: Deployment
metadata:
name: spring-liberty
labels:
app: spring-liberty
spec:
replicas: 2
selector:
matchLabels:
app: spring-liberty
template:
metadata:
labels:
app: spring-liberty
spec:
containers:
- name: spring-liberty
image: image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty
ports:
- containerPort: 9080
readinessProbe:
httpGet:
path: /healthz
port: 9080
---
apiVersion: v1
kind: Service
metadata:
name: spring-liberty
spec:
selector:
app: spring-liberty
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 9080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: spring-liberty
spec:
rules:
- host: spring-liberty.apps.ocp.cloud.vpc
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: spring-liberty
port:
number: 80
2.3. GitOpsオペレーター(Argo CD)によるコンテナのデプロイ
GitOpsオペレーターのカスタムリソース、または、argocdコマンドを使用してGitBucketのmanifestリポジトリに保管されている「spring-liberty.yaml」を適用します。GitBucketはOpenShiftクラスター内にデプロイされており、ネームスペース「gitbucket」のサービス「gitbucket」でアクセス可能であるため、下記の内部アドレスからマニフェストを取得します。
http://gitbucket.gitbucket.svc.cluster.local/git/ocp/manifest.git
端末からブラウザでアクセスする際は下記の外部アドレスを使用します。
http://gitbucket.apps.ocp.cloud.vpc/git/ocp/manifest.git
■ GitOpsオペレーターのカスタムリソースによるArgo CDの設定
# マニフェスト適用の許可
oc adm policy add-role-to-user admin \
system:serviceaccount:openshift-gitops:openshift-gitops-argocd-application-controller \
-n spring-liberty
# GitOpsオペレーターのカスタムリソースによるArgo CDの設定
oc apply -f application.yaml
# adminユーザーパスワード取得
oc extract secret/openshift-gitops-cluster -n openshift-gitops --to=-
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: spring-liberty
namespace: openshift-gitops
spec:
project: default
source:
repoURL: http://gitbucket.gitbucket.svc.cluster.local/git/ocp/manifest.git
targetRevision: HEAD
path: spring-liberty
destination:
server: https://kubernetes.default.svc
namespace: spring-liberty
syncPolicy:
automated:
prune: true
selfHeal: true
■ argocdコマンドによるArgo CDの設定
# argocdコマンド取得
wget https://github.com/argoproj/argo-cd/releases/download/v2.0.4/argocd-linux-amd64
mv argocd-linux-amd64 /usr/local/bin/argocd
chmod +x /usr/local/bin/argocd
# マニフェスト適用の許可
oc adm policy add-role-to-user admin \
system:serviceaccount:openshift-gitops:openshift-gitops-argocd-application-controller \
-n spring-liberty
# ログイン
password=`oc get secret openshift-gitops-cluster -n openshift-gitops -ojsonpath='{.data.admin\.password}' | base64 -d`
argocd login openshift-gitops-server-openshift-gitops.apps.ocp.cloud.vpc \
--username admin --password ${password} --insecure
# argocdコマンドによるArgo CDの設定
argocd app create spring-liberty --repo http://gitbucket.gitbucket.svc.cluster.local/git/ocp/manifest.git \
--path spring-liberty --dest-server https://kubernetes.default.svc \
--dest-namespace spring-liberty --sync-policy automated
# アプリケーション一覧
argocd app list
### 標準出力↓
NAME CLUSTER NAMESPACE PROJECT STATUS HEALTH SYNCPOLICY CONDITIONS REPO
PATH TARGET
spring-liberty https://kubernetes.default.svc spring-liberty default Synced Healthy Auto <none> http://gitbucket.gitbucket.svc.cluster.local/git/ocp/manifest.git spring-liberty
なお、argocdコマンドの場合、GitOpsオペレーター画面にApplicationインスタンスが表示されません。
設定完了後、下記URLからArgo CDにログインすると、デプロイされたことを確認できます。
https://openshift-gitops-server-openshift-gitops.apps.ocp.cloud.vpc/
3. Red Hat OpenShift Pipelinesオペレーターの利用
3.1. パイプライン処理用コンテナイメージ作成
下表のパイプライン『source-pipeline』がコンパイル・war作成とGitBucketリポジトリ「container」の更新を行い、パイプライン『container-pipeline』がコンテナイメージ作成とGitBucketリポジトリ「manifest」の更新を行います。
パイプライン | タスク | ステップ | 実行コマンド |
---|---|---|---|
source-pipeline | build-source | java-build git-push-war |
gradle build git clone / push |
container-pipeline | build-container | container-build git-push-manifest |
oc new-build / start-build git clone / push |
パイプライン処理の前提として、gradleやocコマンドの実行が可能なコンテナのイメージを作成します。
wget https://services.gradle.org/distributions/gradle-7.1.1-bin.zip
wget https://mirror.openshift.com/pub/openshift-v4/clients/ocp/4.8.2/openshift-client-linux-4.8.2.tar.gz
ls -l
### 標準出力↓
-rw-r--r--. 1 root root 478 7月 20 20:59 Dockerfile
-rw-r--r--. 1 root root 52029653 7月 20 20:29 caches.tar.gz
-rw-r--r--. 1 root root 53 7月 22 23:03 git.store
-rw-r--r--. 1 root root 95 7月 22 23:04 gitconfig
-rw-r--r--. 1 root root 112231447 7月 2 21:29 gradle-7.1.1-bin.zip
-rw-r--r--. 1 root root 49717645 6月 30 02:58 openshift-client-linux-4.8.2.tar.gz
oc project spring-liberty
oc new-build --name=pipeline-operator --strategy=docker --binary
oc start-build pipeline-operator --from-dir=. --follow
oc get is
### 標準出力↓
NAME IMAGE REPOSITORY TAGS UPDATED
pipeline-operator image-registry.openshift-image-registry.svc:5000/spring-liberty/pipeline-operator latest 23 seconds ago
spring-liberty image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty latest 15 minutes ago
「java-build」ステップ実行の度にgradleやキャッシュをダウンロードしたくないため、コンテナイメージに追加しています。キャッシュは端末で『gradle build』した際にダウンロードされたファイルを圧縮したものです。
FROM registry.access.redhat.com/ubi8/ubi:latest
ADD openshift-client-linux-4.8.0.tar.gz /usr/bin/
ADD caches.tar.gz /tmp
COPY gradle-7.1.1-bin.zip /usr/local/
COPY gitconfig /etc/
COPY git.store /tmp/
RUN dnf install -y git \
&& dnf install -y java-11-openjdk-devel.x86_64 \
&& cd /usr/local; jar -xvf /usr/local/gradle-7.1.1-bin.zip \
&& chmod +x /usr/local/gradle-7.1.1/bin/gradle
ENV PATH $PATH:/usr/local/gradle-7.1.1/bin
ENV GRADLE_USER_HOME /tmp
「git-push-war」や「git-push-manifest」ステップで必要なGitBucket認証は下記ファイルで行います。
[user]
email = ocp@example.com
name = ocp
[credential]
helper = store --file /tmp/git.store
http://ocp:ocp@gitbucket.gitbucket.svc.cluster.local
3.2. GitBucketへのソースコードとコンテナ資材の保管
GitBucketのsourceリポジトリにソースコードを保管します。ソースコードについてはこちらの記事を参照ください。Spring BootとOpen Libertyによるシンプルなアプリケーションです。
GitBucketのcontainerリポジトリにコンテナ資材を保管します。本記事でspring-libertyアプリケーションのコンテナイメージ作成に使用したものです。
sourceリポジトリ、containerリポジトリそれぞれにwebhook設定を追加します。webhook送信先のリスナーPodは後続の作業で起動します。
3.3. トリガー機能、パイプライン機能マニフェストの適用
webhookをトリガーとして利用するために「binding.yaml」と「listener.yaml」を適用し、トリガーを契機にパイプラインを実行するために「source-pipeline.yaml」と「container-pipeline.yaml」を適用します。各ファイルでは以下のカスタムリソースを作成しています。
oc project spring-liberty
# トリガーバインディング
oc apply -f binding.yaml
# イベントリスナーPod起動
oc apply -f listener.yaml
# パイプライン処理
oc apply -f source-pipeline.yaml
oc apply -f container-pipeline.yaml
oc get pod,ing
### 標準出力↓
NAME READY STATUS RESTARTS AGE
pod/el-listener-665ccf8947-lhljc 1/1 Running 3 52s
pod/pipeline-operator-1-build 0/1 Completed 0 6m29s
pod/spring-liberty-1-build 0/1 Completed 0 20m
pod/spring-liberty-84c44ffc5b-46p99 1/1 Running 0 12m
pod/spring-liberty-84c44ffc5b-46p99 1/1 Running 0 12m
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
route.route.openshift.io/el-listener el-listener-spring-liberty.apps.ocp.cloud.vpc el-listener http-listener None
route.route.openshift.io/spring-liberty-mp6m5 spring-liberty.apps.ocp.cloud.vpc / spring-liberty <all> None
トリガーバインディングは、トリガーテンプレートに渡すwebhookのパラメータを定義しています。
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
name: binding
spec:
params:
- name: gitrevision
value: $(body.head_commit.id)
- name: gitrepositoryurl
value: $(body.repository.clone_url)
イベントリスナーは、GitBucketのリポジトリ名を判別してトリガーテンプレートを実行します。sourceリポジトリとcontainerリポジトリのディレクトリ名を取得するために、GitBucketリポジトリにプッシュされた1つ目のファイル名を『gitmodified』として取得しています(トリガーバインディングで定義できませんでした)。イベントリスナーのマニフェストを適用するとトリガーPodが起動されます。トリガーPodはwebhookを受信しパイプライン処理用Podを起動します。
apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
name: listener
spec:
serviceAccountName: pipeline
triggers:
- name: source
interceptors:
- cel:
filter: "body.repository.name in ['source']"
overlays:
- key: gitmodified
expression: body.head_commit.modified[0]
bindings:
- name: gitmodified
value: $(extensions.gitmodified)
- ref: binding
template:
ref: source-template
- name: container
interceptors:
- cel:
filter: "body.repository.name in ['container']"
overlays:
- key: gitmodified
expression: body.head_commit.modified[0]
bindings:
- name: gitmodified
value: $(extensions.gitmodified)
- ref: binding
template:
ref: container-template
トリガーテンプレートはパイプラインを、パイプラインはタスクを実行します。いずれのパイプラインも単一のタスクで構成され、タスクはシェルスクリプトとして実装しています。
パイプライン | タスク | ステップ | 実行コマンド |
---|---|---|---|
source-pipeline | build-source | java-build git-push-war |
gradle build git clone / push |
container-pipeline | build-container | container-build git-push-manifest |
oc new-build / start-build git clone / push |
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
name: source-template
spec:
params:
- name: gitrevision
- name: gitrepositoryurl
- name: gitmodified
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: source-pipeline-run-
spec:
pipelineRef:
name: source-pipeline
params:
- name: gitmodified
value: $(tt.params.gitmodified)
resources:
- name: git-source
resourceSpec:
type: git
params:
- name: revision
value: $(tt.params.gitrevision)
- name: url
value: $(tt.params.gitrepositoryurl)
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: source-pipeline
spec:
params:
- name: gitmodified
resources:
- name: git-source
type: git
tasks:
- name: build-source
taskRef:
kind: Task
name: build-source
params:
- name: gitmodified
value: $(params.gitmodified)
resources:
inputs:
- name: git-source
resource: git-source
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: build-source
spec:
params:
- name: gitmodified
resources:
inputs:
- name: git-source
type: git
steps:
- name: java-build
image: image-registry.openshift-image-registry.svc:5000/spring-liberty/pipeline-operator:latest
script: |
#!/usr/bin/env bash
set -x
target=`echo $(params.gitmodified) | awk -F"/" '{print $1}'`
cd git-source/${target}
gradle build --no-daemon
- name: git-push-war
image: image-registry.openshift-image-registry.svc:5000/spring-liberty/pipeline-operator:latest
script: |
#!/usr/bin/env bash
set -x
target=`echo $(params.gitmodified) | awk -F"/" '{print $1}'`
git clone http://gitbucket.gitbucket.svc.cluster.local/git/ocp/container.git
cd container
cp -p /workspace/git-source/${target}/build/libs/*.war ./${target}/
git commit -a -m "update war file"
git push -u origin master
apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
name: container-template
spec:
params:
- name: gitrevision
- name: gitrepositoryurl
- name: gitmodified
resourcetemplates:
- apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: container-pipeline-run-
spec:
pipelineRef:
name: container-pipeline
params:
- name: gitmodified
value: $(tt.params.gitmodified)
resources:
- name: git-source
resourceSpec:
type: git
params:
- name: revision
value: $(tt.params.gitrevision)
- name: url
value: $(tt.params.gitrepositoryurl)
---
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: container-pipeline
spec:
params:
- name: gitmodified
resources:
- name: git-source
type: git
tasks:
- name: build-container
taskRef:
kind: Task
name: build-container
params:
- name: gitmodified
value: $(params.gitmodified)
resources:
inputs:
- name: git-source
resource: git-source
---
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: build-container
spec:
params:
- name: gitmodified
resources:
inputs:
- name: git-source
type: git
steps:
- name: container-build
image: image-registry.openshift-image-registry.svc:5000/spring-liberty/pipeline-operator:latest
script: |
#!/usr/bin/env bash
set -x
# 修正対象ディレクトリ名=BuildConfig名=ImageStream名
target=`echo $(params.gitmodified) | awk -F"/" '{print $1}'`
yyyymmddhhmm=`date +%Y%m%d%H%M`
cd git-source
oc -n spring-liberty delete bc ${target}
oc -n spring-liberty new-build --name=${target} --strategy=docker --binary --to=${target}:${yyyymmddhhmm}
oc -n spring-liberty start-build ${target} --from-dir=${target} --follow
echo `oc -n spring-liberty get is ${target} | tail -1 | awk '{print $2}'`:${yyyymmddhhmm} > ../tag
- name: git-push-manifest
image: image-registry.openshift-image-registry.svc:5000/spring-liberty/pipeline-operator:latest
script: |
#!/usr/bin/env bash
set -x
target=`echo $(params.gitmodified) | awk -F"/" '{print $1}'`
image=`cat tag`
git clone http://gitbucket.gitbucket.svc.cluster.local/git/ocp/manifest.git
cd manifest
cp -p ${target}/spring-liberty.yaml .
sed '/.*image.*/c\ image: '$image'' spring-liberty.yaml > ${target}/spring-liberty.yaml
rm -f spring-liberty.yaml
git commit -a -m "update image"
git push -u origin master
※「container-build」ステップと「git-push-manifest」ステップ間でイメージストリームのタグの文字列(yyyymmddhhmm)をファイルで受け渡しています。タスク間でのファイル受け渡す場合はworkspacesを使う必要があります。
3.4. sourceリポジトリ更新を契機としたパイプライン処理の実行
sourceリポジトリを更新するとGitBucketはwebhookをトリガーPodに送信し以下が実行されます。
パイプラインはこちらで作成したpipeline-operatorが実行します。
■ パイプライン「source-pipeline」
(1) sourceリポジトリをクローン。
(2) ソースコードのコンパイルとwar作成を実行(java-buildステップ)。
(3) warファイルをcontainerリポジトリにプッシュ(git-push-warステップ)。
containerリポジトリ更新に伴い、GitBucketはwebhookをトリガーPodに送信し以下が実行されます。
パイプラインはこちらで作成したpipeline-operatorが実行します。
■ パイプライン「container-pipeline」
(1) containerリポジトリをクローン。
(2) コンテナイメージを作成(container-buildステップ)。
(3) manifestリポジトリのマニフェストのイメージタグを修正しプッシュ(git-push-manifestステップ)。
パイプライン | タスク | ステップ | 実行コマンド |
---|---|---|---|
source-pipeline | build-source | java-build git-push-war |
gradle build git clone / push |
container-pipeline | build-container | container-build git-push-manifest |
oc new-build / start-build git clone / push |
※ 「spring-liberty.yaml」に差分がある場合は自動適用するようにArgo CDを設定しました。これによりsourceリポジトリへの変更が反映されたコンテナが自動的にデプロイされます。
パイプラインが実行されたことはOpenShiftコンソールで確認することができます。
パイプラインはPodとして実行されるため、コマンドラインで確認することもできます。
oc get pod
### 標準出力↓
NAME READY STATUS RESTARTS AGE
container-pipeline-run-4cq6k-build-container-2zh4t-pod-tv675 0/3 Completed 0 3m1s
el-listener-665ccf8947-lhljc 1/1 Running 0 9m41s
source-pipeline-run-tk5jw-build-source-sb6k2-pod-v8v5x 0/3 Completed 0 5m57s
spring-liberty-1-build 0/1 Completed 0 2m47s
spring-liberty-84c44ffc5b-46p99 1/1 Running 0 3h50m
spring-liberty-84c44ffc5b-mb675 1/1 Running 0 3h50m
パイプラインPodの各コンテナのログは下記の通りです。
oc logs source-pipeline-run-tk5jw-build-source-sb6k2-pod-v8v5x -c step-git-source-git-source-hfbpm
### 標準出力↓
{"level":"info","ts":1628424362.5006046,"caller":"git/git.go:169","msg":"Successfully cloned http://gitbucket.apps.ocp.cloud.vpc/git/ocp/source.git @ 27e474577c9ebadcf152d104758c77681ef45405 (grafted, HEAD) in path /workspace/git-source"}
{"level":"info","ts":1628424362.9797778,"caller":"git/git.go:207","msg":"Successfully initialized and updated submodules in path /workspace/git-source"}
oc logs source-pipeline-run-tk5jw-build-source-sb6k2-pod-v8v5x -c step-java-build
### 標準出力↓
++ echo spring-liberty/src/main/resources/templates/index.html
++ awk -F/ '{print $1}'
+ target=spring-liberty
+ cd git-source/spring-liberty
+ gradle build --no-daemon
Welcome to Gradle 7.1.1!
Here are the highlights of this release:
- Faster incremental Java compilation
- Easier source set configuration in the Kotlin DSL
For more details see https://docs.gradle.org/7.1.1/release-notes.html
To honour the JVM settings for this build a single-use Daemon process will be forked. See https://docs.gradle.org/7.1.1/userguide/gradle_daemon.html#sec:disabling_the_daemon.
Daemon will be stopped at the end of the build
> Task :compileJava
> Task :processResources
> Task :classes
> Task :bootWarMainClassName
> Task :bootWar
> Task :war
> Task :assemble
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
> Task :check
> Task :build
BUILD SUCCESSFUL in 13s
7 actionable tasks: 7 executed
oc logs source-pipeline-run-tk5jw-build-source-sb6k2-pod-v8v5x -c step-git-push-war
### 標準出力↓
++ echo spring-liberty/src/main/resources/templates/index.html
++ awk -F/ '{print $1}'
+ target=spring-liberty
+ git clone http://gitbucket.gitbucket.svc.cluster.local/git/ocp/container.git
Cloning into 'container'...
+ cd container
+ cp -p /workspace/git-source/spring-liberty/build/libs/spring-liberty-plain.war /workspace/git-source/spring-liberty/build/libs/spring-liberty.war ./spring-liberty/
+ git commit -a -m 'update war file'
[master 6fea6d6] update war file
1 file changed, 0 insertions(+), 0 deletions(-)
+ git push -u origin master
remote: Updating references: 100% (1/1)
To http://gitbucket.gitbucket.svc.cluster.local/git/ocp/container.git
2f2eced..6fea6d6 master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
oc logs container-pipeline-run-4cq6k-build-container-2zh4t-pod-tv675 -c step-git-source-git-source-p6jlz
### 標準出力↓
{"level":"info","ts":1628424399.2615502,"caller":"git/git.go:169","msg":"Successfully cloned http://gitbucket.gitbucket.svc.cluster.local/git/ocp/container.git @ 6fea6d6c6038737bb2ea2506887f506822486011 (grafted, HEAD) in path /workspace/git-source"}
{"level":"info","ts":1628424399.3050532,"caller":"git/git.go:207","msg":"Successfully initialized and updated submodules in path /workspace/git-source"}
oc logs container-pipeline-run-4cq6k-build-container-2zh4t-pod-tv675 -c step-container-build
### 標準出力↓
++ echo spring-liberty/spring-liberty-plain.war
++ awk -F/ '{print $1}'
+ target=spring-liberty
++ date +%Y%m%d%H%M
+ yyyymmddhhmm=202108081206
+ cd git-source
+ oc -n spring-liberty delete bc spring-liberty
buildconfig.build.openshift.io "spring-liberty" deleted
+ oc -n spring-liberty new-build --name=spring-liberty --strategy=docker --binary --to=spring-liberty:202108081206
* A Docker build using binary input will be created
* The resulting image will be pushed to image stream tag "spring-liberty:202108081206"
* A binary build was created, use 'oc start-build --from-dir' to trigger a new build
--> Creating resources with label build=spring-liberty ...
imagestreamtag.image.openshift.io "spring-liberty:202108081206" created
buildconfig.build.openshift.io "spring-liberty" created
--> Success
+ oc -n spring-liberty start-build spring-liberty --from-dir=spring-liberty --follow
Uploading directory "spring-liberty" as binary input for the build ...
.
Uploading finished
build.build.openshift.io/spring-liberty-1 started
Receiving source from STDIN as archive ...
Caching blobs under "/var/cache/blobs".
Pulling image adoptopenjdk/openjdk11:ubi ...
Getting image source signatures
Copying blob sha256:a50df8fd88fecefc26fd331f832672108deb08cf9d2b303a5b86156a7f51b5d8
Copying blob sha256:85467c3c6735e1bdeba615c5811c3bfa4b7e42f387093ba9c929902a923f9ef7
Copying blob sha256:f87862706a80347fea48f6831e2d1716630d49ec2a800a35488870774e6e3924
Copying blob sha256:1cadda38f72dece653de82063e3c8e910265fe7a342ec2fb73ad8e540c47e1f7
Copying config sha256:6ab8439568645d0182c301fb0fec38766809eac20ddba195c053d97eb77fb2aa
Writing manifest to image destination
Storing signatures
Adding transient rw bind mount for /run/secrets/rhsm
STEP 1: FROM adoptopenjdk/openjdk11:ubi
STEP 2: ENV PATH=/opt/ol/wlp/bin:$PATH LOG_DIR=/logs
--> 35fa6734584
STEP 3: RUN mkdir /opt/ol /logs
--> 280ade3aa67
STEP 4: COPY openliberty-webProfile8-21.0.0.7.zip /opt/ol/
--> 3dcef6b1390
STEP 5: WORKDIR /opt/ol
--> 587003315d9
STEP 6: RUN /opt/java/openjdk/bin/jar -xf openliberty-webProfile8-21.0.0.7.zip && rm -f openliberty-webProfile8-21.0.0.7.zip && chmod +x /opt/ol/wlp/bin/server && /opt/ol/wlp/bin/server cr
eate && ln -s /opt/ol/wlp/usr/servers/defaultServer /config
Server defaultServer created.
--> 7cbc69b3c97
STEP 7: COPY server.xml /config/
--> 7d5221ee65f
STEP 8: COPY spring-liberty-plain.war /config/apps/
--> a87032b7b63
STEP 9: RUN chmod -R g+rw /opt/ol/wlp && chmod -R g+rw /logs
--> ccc05ca86b9
STEP 10: EXPOSE 9080
--> fdc0f5b0ccb
STEP 11: CMD ["/opt/ol/wlp/bin/server","run","defaultServer"]
--> bbc3f1f2add
STEP 12: ENV "OPENSHIFT_BUILD_NAME"="spring-liberty-1" "OPENSHIFT_BUILD_NAMESPACE"="spring-liberty"
--> a952995f435
STEP 13: LABEL "io.openshift.build.name"="spring-liberty-1" "io.openshift.build.namespace"="spring-liberty"
STEP 14: COMMIT temp.builder.openshift.io/spring-liberty/spring-liberty-1:008481a8
--> 97cccbf39b7
97cccbf39b75b66e912963ce70bc33c3750c9ff0ba8833be8b6506859bdb0643
Pushing image image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty:202108081206 ...
Getting image source signatures
Copying blob sha256:6ff9fc7fbdf59d5c6697ebe16d89c43e9edeefd77761be6794d328b6e71384a1
Copying blob sha256:1672952b6d300fc188b0668944ae42fdbb0af36c61f71e58f16b6f91087da702
Copying blob sha256:85467c3c6735e1bdeba615c5811c3bfa4b7e42f387093ba9c929902a923f9ef7
Copying blob sha256:f87862706a80347fea48f6831e2d1716630d49ec2a800a35488870774e6e3924
Copying blob sha256:1cadda38f72dece653de82063e3c8e910265fe7a342ec2fb73ad8e540c47e1f7
Copying blob sha256:afb6362017366010f205e31757be2056dbcb226ed2cbba684420d16bbd44879a
Copying blob sha256:72bf331dd109ae0916c4323808ece74e3bb70352cd0335779b51bda458e8d4dd
Copying blob sha256:a50df8fd88fecefc26fd331f832672108deb08cf9d2b303a5b86156a7f51b5d8
Copying blob sha256:a669218bc263dcdc2db78074a354f7c66d61f8c14882228596996055538c0e89
Copying blob sha256:a70f15e7dd8ab97adbc7b1af812b4d6a21cc5b6c3aad35e97e8c3e70bba2e832
Copying config sha256:97cccbf39b75b66e912963ce70bc33c3750c9ff0ba8833be8b6506859bdb0643
Writing manifest to image destination
Storing signatures
Successfully pushed image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty@sha256:102c010db289c0ffc76cc182cf66bd394a3b6198113a0ac54e63ab63a23e319c
Push successful
++ oc -n spring-liberty get is spring-liberty
++ tail -1
++ awk '{print $2}'
+ echo image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty:202108081206
oc logs container-pipeline-run-4cq6k-build-container-2zh4t-pod-tv675 -c step-git-push-manifest
### 標準出力↓
++ echo spring-liberty/spring-liberty-plain.war
++ awk -F/ '{print $1}'
+ target=spring-liberty
++ cat tag
+ image=image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty:202108081206
+ git clone http://gitbucket.gitbucket.svc.cluster.local/git/ocp/manifest.git
Cloning into 'manifest'...
+ cd manifest
+ cp -p spring-liberty/spring-liberty.yaml .
+ sed '/.*image.*/c\ image: image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty:202108081206' spring-liberty.yaml
+ rm -f spring-liberty.yaml
+ git commit -a -m 'update image'
[master 54dc17a] update image
1 file changed, 1 insertion(+), 1 deletion(-)
+ git push -u origin master
remote: Updating references: 100% (1/1)
To http://gitbucket.gitbucket.svc.cluster.local/git/ocp/manifest.git
154ac78..54dc17a master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.
4. GitOpsオペレーターによるコンテナの自動更新
パイプライン『source-pipeline』とパイプライン『container-pipeline』が実行され、最終的にGitBucketのmanifestリポジトリの「spring-liberty.yaml」のイメージタグ部分が更新されました。
OpenShift GitOpsオペレーターはマニフェスト更新を検知し、spring-libertyコンテナをデプロイしました(Pod名がspring-liberty-84c44ffc5b~からspring-liberty-75cd7848b6~に更新)。以下はArgo CD管理画面のスクリーンショットです。
おわりに
OpenShift 4.8のOpenShift GitOpsオペレーターの「Argo CD」機能、OpenShift Pipelinesオペレーターの「トリガー」機能と「パイプライン」機能を使用して、souceリポジトリの更新を契機にコンテナが自動的にデプロイされる環境を構築しました。
Openshift Pipelinesのカスタムリソースのパラメータ定義は冗長に感じますが、タスク自体はシェルスクリプトで実装できるため、比較的扱い易い印象を受けました。それでもマニフェストのイメージタグ部分を更新するのは煩雑なので、OpenShift GitOpsオペレーターにイメージ更新を検知する「Argo CD Image Updater」が取り込まれることを期待しています。
spring-libertyコンテナをOpenShift独自のDeploymentConfigで作成すればで対応できますが、「oc new-app」コマンドで作成されるアプリケーションのデフォルトがDeploymentConfigからDeploymentに変更されていることもあり、今回は後者を採用しました。以下は「oc new-app -h」の抜粋です。
Options:
--as-deployment-config=false:
If true create this application as a deployment config, which allows for hooks and custom strategies.