「Red Hat OpenShift on IBM Cloud」によって、CI/CD(継続的インテグレーション/継続的デリバリー)パイプラインの構築はどれほど簡単になるのか?IBM Champion for Cloud 2019が評価

IBMのソリューションやソフトウェアに対し、そのテクニカルコミュニティーにおいて高度な貢献をしたエンジニアを表彰する制度「IBM Champions」。今回はIBM Champion for Cloud 2019に選出された平岡大祐氏に、その知見・ノウハウを体感できる記事を執筆していただいた。昨今のDXトレンドなどについて聞いたショートインタビューとともに、3回にわたって「Red Hat OpenShift on IBM Cloud」の実践的な活用方法をお届けする。最終回はOpenShiftを使ったCI/CD(継続的インテグレーション/継続的デリバリー)について。

» 2019年12月12日 10時00分 公開
[PR/@IT]
PR

IBMテクノロジーを使ってビジネス、社会をリードするエンジニアに贈られる称号「IBM Champions」

 DX(デジタルトランスフォーメーション)トレンドが進展し、ビジネスが「ITを使った体験価値の競争」に変容して久しい。テクノロジーはビジネスのコアとなり、それを使いこなすエンジニアこそが、ビジネスの推進役となる時代が到来したといえるだろう。

 そうした中、IBMが主催しているのが「IBM Champions」だ。IBMのソリューションやソフトウェアに対し、年間を通してテクニカルコミュニティーに優れた貢献をしたエンジニアを表彰する制度で2008年に創設。2018年までにグローバルで650人、うち日本人は51人が選出されている。開発者、ビジネスリーダー、経営層など、全世界のITに携わる人に向けたインフルエンサーとして認定され、「IBM Analytics Champions」「IBM Cloud Champions」「IBM Collaboration&Talent Champions」「IBM Power Systems Champions」「IBM Storage Champions」「IBM Z Champions」の6部門で表彰。IBMのグローバルイベント、公式サイト、ブログなどを通じた意見発信の機会が与えられる。

 今回はIBM Champion for Cloud 2019に選出された平岡大祐氏に、その知見・ノウハウを体感できる記事を執筆していただいた。昨今のDXトレンドなどについて聞いたショートインタビューとともに、3回にわたって「Red Hat OpenShift on IBM Cloud」の実践的な活用方法をお届けする。手を動かして“最先端の視点”を体感してはいかがだろうか。

IBM Champion for Cloud 2019の平岡大祐氏

平岡大祐

IBM Champion for Cloud 2019

2002年からエンジニアとして、業務系、基幹系システム、プロダクトの設計、開発、運用などさまざま業務に従事。2016年の夏に2カ月でIBM CloudのベアメタルサーバにOpenShift環境を構築し、2017年1月から本番環境で稼働させ、運用する業務を経験。以降、コンテナやOpenShiftの業務を多く経験するようになる。2018年3月刊行の『コンテナ・ベース・オーケストレーション Docker/Kubernetesで作るクラウド時代のシステム基盤』(翔泳社)に、OpenShiftに関する章の共著で参加。Red Hat認定資格「Red Hat Certified Specialist in OpenShift Administration」を取得している。

ショートインタビュー

―― アジャイル、DevOps、CI/CDの必要性とは?

 Red Hat OpenShift on IBM Cloudでのアプリケーション開発の前提となる「コンテナ」は、リリーススピードを速めるアジャイル、DevOps、CI/CDを実践するための一要素です。DXを実現するためには、リリーススピードを速めることが欠かせないのは言うまでもないと思います。

―― では、これまでアジャイル、DevOps、CI/CDを阻んできたものとは何か?

 今までの経験でいうと、外注することによるリードタイムの増加もあると思います。なぜ外注が増えたのかについては、IBMの方が言うには、テクノロジーが分かっているIT部門が、テクノロジーの言葉をビジネス層、経営層に分かる言葉で説明できないから、外注してしまう。外注側はビジネス上ウオーターフォール的な開発体制を取らざるを得ない時代だったということもあるでしょう。でも、リリーススピードと自動化が求められている今の時代は変わらざるを得ないですよね。

―― エンジニアがビジネスに寄与するために必要なこととは?

 アジャイル、DevOps、CI/CDに加え、これまで紹介してきたようなコンテナやマイクロサービスといった技術を活用できることがビジネスへの寄与に必要になると思います。コンテナやマイクロサービスについては、若いエンジニアのものというイメージがあるかもしれませんが、マイクロサービス化に必要なドメイン駆動設計ができる人、つまり業務を整理できるエンジニアは昔からJavaやオブジェクト指向に触れてきた人たちだと思います。40歳を過ぎた人たちこそがヒーローになれるのではないでしょうか。

 今回の記事では、OpenShiftを使ったCI/CD(継続的インテグレーション/継続的デリバリー)について見ていただきたいと思います。


※以下は日本アイ・ビー・エムから提供されたコンテンツを、許諾を得て転載したものです。このため、用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。

 IBM Champion for Cloud 2019の平岡です。3カ月にわたってRed Hat OpenShift on IBM Cloudの実践的な活用方法について連載しました。

 この3回目の記事がこの連載の最後の記事となりますが、今回は、より実践的に開発/検証/本番環境へのアプリケーションのリリースを想定した「OpenShiftでDevOps、CI/CD(継続的インテグレーション/継続的デリバリー)について」です。

 本題に入る前に少し前回の内容に戻ってみましょう。

 前回の記事では私たちはExample HealthのOpenShiftによるJava EEアプリケーションのモダナイゼーションに成功しました。

 これからは、その後の話になります。Red Hat OpenShift on IBM Cloudに移行した結果、Example Healthは容易に機能拡張できるようになりました。そして次々にシステムを拡大して新しいサービスを追加していきます。その一つが健康記録管理者用のPHPアプリケーションのCode Patternsです。 このCode Patternsを自分のパソコンで試すことができるようにQiitaに投稿しました。是非お試しください。

 順風満帆に見えたExample Healthですが、サービスが増えるにつれアプリケーションのリリースサイクルが遅くなっていく問題に直面します。Example Healthではアプリケーションの開発は開発担当、アプリケーションのリリースは運用担当と役割分担があり、運用担当はこれまでアプリケーションのリリースの度に開発担当が書いたExcel手順書に書かれた通りに実行するのが慣例でした。実は運用担当はモダナイゼーションの旅に取り残されていて、サービスが増えれば増えるほど、Excel手順書が増え、リリースのための作業そのものがボトルネックになっていたのです。開発担当、運用担当の間でのExcel手順書のキャッチボールは徐々にフラストレーションがたまり関係が悪化していきます。そして、ある日、運用担当のオペレーションミスでリリース事故が発生するのです。

 このまま運用担当と開発担当の壁がある状態ではExample Healthのモダナイゼーションは成功したとは言えません。アーキテクチャに見合った組織改革も必要ですが、アプリケーションのリリースについてもモダナイゼーションが必要です。

 説明が長くなりましたが、この問題を解決することが今回のメインテーマになります。

今回の概要

 初めに、DevOpsやCI(継続的インテグレーション)をやるために必要なOpenShiftのビルドについて説明します。

3回目記事概要 - (1)DevOpsのイメージ

 次に開発環境でビルドしたコンテナイメージを使って検証/本番環境へのアプリケーションのリリースを、「oc」コマンドを使った手動オペレーションで行う方法、次にJenkinsを使って自動で実施する方法を順に試したいと思います。

3回目記事概要 - (2)CI/CD(継続的インテグレーション/継続的デリバリー)のイメージ

 OpenShiftの標準的な機能のみを使ってDevOpsやCI/CD(継続的インテグレーション/継続的デリバリー)が実現できるってところがポイントです。

 それでは、実際に試していきましょう。

事前準備

 GitHubのリポジトリをクローンします。クローンするとpatientuiフォルダが作成されます。

$ git clone https://github.com/daihiraoka/patientui.git
$ cd patientui
$ ls
README.md  patientui-all.yaml  patientui_dc.yaml  patientui_dc_tok05.yaml
patientui_is.yaml  patientui_svc.yaml

 patientui-all.yamlは、2回目の記事でpatientuiという名称で作成した患者用UI(ユーザーインターフェイス)の定義をエクスポートしたものです。

 エクスポートは「oc get」コマンドを実行して取得できます。

$ oc get --export=true BuildConfig,DeploymentConfig,Service,ImageStream,Route \
patientui -n health -o yaml > patientui-all.yaml

 その他のファイルはpatientui-all.yamlを基に私が作成したこの後使用するテンプレートです。

OpenShiftのビルドの仕組み

Source-to-Image(S2I)

 OpenShiftのビルドの仕組みの一つにSource-to-Image(S2I)というアプリケーションのソースコードを使って新しいコンテナイメージを作成させるフレームワークがあります。

Source-to-Image(S2I)の概要

 Source-to-Image(S2I)はS2I Scriptというソフトウェアが準備されたベース(コンテナ)イメージにアプリケーションのソースコードを挿入し、アプリケーションのコンテナイメージを作成します。このフレームワークの最大のメリットはソースコードとベースイメージが分離されるので、開発担当はソースコードを書くこと、運用担当はベースイメージの保守と役割を明確に分けることができることです。ベースイメージからソースコードが分離していることはバージョンアップやパッチ適用など組み合わせ検証も容易になりますね。

組み合わせ検証の例

  • ソースコード v1 + ベースイメージ RHEL7.7
  • ソースコード v1 + ベースイメージ RHEL7.8
  • ソースコード v2 + ベースイメージ RHEL7.8

 実は、Source-to-Image(S2I)は既に1回目の記事で、ブラウザ操作でPHPのバージョンとアプリケーションのソースコードを指定して「create」ボタンをクリック、

1回目記事の画面

2回目の記事の患者用UIでは、Node.jsのベースイメージとアプリケーションのソースコードを指定して「oc new-app」コマンドを実行した後に始まるビルドの中でSource-to-Image(S2I)を使ってコンテナイメージを生成していました。

$ oc new-app openshift/nodejs-10~https://github.com/daihiraoka/node-s2i-openshift.git
--name='patientui' --context-dir='/site'

 DevOpsも考慮されているSource-to-Image(S2I)はよく考えられてると思いませんか?

Note

 Source-to-Image(S2I)のS2I Scriptの動作はベースイメージの起動(docker run)でS2I Scriptの処理が始まります。

 S2I Scriptはダウンロードしたソースコードをtarでベースイメージに流し込んだものをdocker commit/pushでアプリケーションのコンテナイメージを作成します。S2I Scriptはシェルスクリプトで書かれていて、ダウンロードしたJavaのソースコードをmavenでビルドするなど、みなさんの配信プロセスを追加することもできますよ。

 従来のDockerfileのビルドでは1行が1レイヤーとして積み重ねになるのに対してS2I Scriptを使うことで1つのレイヤーの追加でコンテナイメージが作成されるのでビルドスピードやイメージのサイズが改善されます。tarでソースコードを流し込むのもビルドスピードが早くなる仕組みの一つです。


ビルド設定(BuildConfig)

 次にみなさんの理解を高めるためにSource-to-Image(S2I)のイメージ図とビルド設定(BuildConfig)は同じなのか確認しましょう。

BuildConfig(patientui_bc.yaml)から抜粋1

 (2)sourceセクションでアプリケーションのソースコードを指定します。 患者用UIではGitHubのリポジトリを指定しました。

 (3)fromセクションでベースイメージを指定します。2回目の記事では患者用UIのベースイメージをopenshiftプロジェクトにインポートしたので、ここではopenshiftプロジェクトのnodejs-10:latestを指定しています。

 (1)outputセクションでは、ビルドで生成するコンテナイメージを指定します。 今回のビルドで生成されたコンテナイメージはpatientui:latestで作成されます。

 (2)アプリケーションのソースコードを(3)ベースイメージに挿入して(1)アプリケーションのコンテナイメージを生成になるのでSource-to-Image(S2I)のイメージ図と同じになっていることがわかりますよね。

自動ビルドに必要なトリガー設定

 次に自動でビルドが始まるために必要なトリガーの設定です。

BuildConfig(patientui_bc.yaml)から抜粋2

 (4)GitHub、GitLab、BitbucketまたはGeneric Webhookを使用したWebhookトリガーを定義できます。WebhookのURLが発行されるのは、BuildConfigがOpenShiftに作成された後なので、ここで設定できるのはsecretの鍵のみです。

 (5)type: ConfigChangeはビルド設定(BuildConfig)に変更が発生した場合にビルドが自動開始します。(初回登録を含む)

 type: ImageChangeはfromに定義したベースイメージに変更が起こった場合にビルドを自動開始します。

 OpenShiftにはトリガーがあるので、ソースコードをコミットするとWebhookトリガーで自動的にビルドが始まります。後述のデプロイ設定(DeploymentConfig)にもトリガーがあります。トリガーのおかげでアプリケーション開発者はOpenShiftを触れることなく、ソースコードの変更を確認することができます。この繰り返しはCI(継続的インテグレーション)になります。標準の機能でCIができるってすごくないですか?運用担当者もベースイメージをアップデートするとImageChangeトリガーから自動でビルドが始まり、CIすることができて検証が楽になりますよ。

検証/本番環境へのアプリケーションのリリースの簡素化と自動化

 今回はOpenShiftのイメージプロモーションと呼ばれるImageStreamのタグを使ったアプリケーションのリリースを開発環境の患者用UIアプリケーションを使って検証/本番環境にリリースを行います。初めに「oc」コマンドを使った手動オペレーションで手順を確認します。そして、その手順をJenkins Pipelineにしてリリースを自動化したいと思います。

概要

検証/本番環境へのアプリケーションのリリースの簡素化と自動化概要

 図ではすでに開発環境(healthプロジェクト)は完成しているので、検証環境と本番環境を作成した後に(5)から(10)の手順を行うのみです。

 ここでのポイントは、コンテナイメージのビルドが開発環境にしかないことです。1つのコンテナイメージを開発/検証/本番環境と開発サイクル全体を通して使用することは、各環境間での変化が少ない、もしくはまったくないということです。

 これまで、ある環境ではバグが出力され、他の環境では出力されなくて困った!という経験はみなさん少なからずあると思います。仮想マシンをクローンしたとしても、たとえAnsibleのような自動構成ツールを使って、同じ手順で、各環境でビルド・デプロイしたとしても、ごくわずかなのでしょうがライブラリやバイナリの差が発生するからです。

 上記の問題がなくなるのが、コンテナを使うことのメリットの一つですね。

検証環境へのアプリケーションのリリース

1.検証環境(health-testing)のプロジェクトを作成します。

$ oc new-project health-testing

2.検証環境から開発環境のコンテナイメージを参照できるようにsystem:image-pullerロールを検証環境のdefault(全てのPod)サービスアカウントに追加します。

- 検証環境(health-testting)
$ oc policy add-role-to-user  \
    system:image-puller system:serviceaccount:health-testing:default  \
    -n health 
role "system:image-puller" added: "system:serviceaccount:health-testing:default"

 事前準備で用意したYAMLファイルを使って順番にImageStream、DeploymentConfig、Serviceを作成します。

3.ImageStreamの作成

 まず、開発環境のImageStreamを確認します。

- 開発環境
$ oc get is -n health
NAME       DOCKER REPO                                        TAGS    UPDATED
patientui  docker-registry.default.svc:5000/health/patientui  latest  7 days ago

 patientuiという名前でlatestタグがあることがわかります。このコンテナイメージを使って検証環境へのリリースを行います。

 そして、開発環境のコンテナイメージを格納するために検証環境にImageStreamを作成します。

$ oc create -f patientui_is.yaml 
imagestream.image.openshift.io/patientui created
 
$ oc get is
NAME       DOCKER REPO                                               TAGS  UPDATED
patientui  docker-registry.default.svc:5000/health-testing/patientui     

 patientuiというImageStreamが作成されましたが、この段階では、コンテナイメージは展開されていないのでTAGSとUPDATEが何も記載されていません。patientuiという空っぽの箱が作成されたと考えてください。

ImageStreamのイメージ図(1)

4.デプロイ設定(DeploymentConfig)を登録します。

 GitHubのリポジトリからクローンしたデプロイ設定(DeploymentConfig)はpatientui_dc.yamlとpatientui_dc_tok05.yamlの2つあります。この2つのデプロイ設定(DeploymentConfig)の差分はnodeSelectorの設定がpatientui_dc_tok05.yamlにあることです。

$ diff patientui_dc.yaml patientui_dc_tok05.yaml
10a11,12
>       nodeSelector:
>         failure-domain.beta.kubernetes.io/zone: tok05

 nodeSelectorはPod(コンテナ)を起動するワーカーノードを指定する設定です。「oc get node」コマンドに--show-labelsオプションを追加して実行し出力されたワーカーノードのLABELSを使って設定します。

ワーカーノードの一覧

 検証環境ではPod(コンテナ)が起動するデーターセンター(ゾーン)をtok05のみに指定したかったので図(2)Keyを"failure-domain.beta.kubernetes.io/zone"、Valueを"tok05"の追加したpatientui_dc_tok05.yamlを作成しました。patientui_dc.yamlはnodeSelectorの設定はないので、全てのワーカーノードにPod(コンテナ)は起動します。ちなみに、ワーカーノードを1つ指定したい場合は、図(1)keyを"kubernetes.io/hostname"、Valueを"10.192.17.136"とIPアドレスを指定して設定することができますよ。

 では、patientui_dc_tok05.yamlを登録します。

$ oc create -f patientui_dc_tok05.yaml 
 
$ oc get dc
NAME        REVISION   DESIRED   CURRENT   TRIGGERED BY
patientui   0          3         0         config,image(patientui:latest)

 DeploymentConfigを登録した時点では、Pod(コンテナ)の要求(DESIRED)は3ですが、この時点では検証環境にはコンテナイメージはないため現在動作しているPodの数(CURRENT)は0になっています。TRIGGERED BYの列でimage(patientui:latest)がありますが、patientuiというImageStreamのlatestタグに更新があったらトリガーが発動するという意味です。

 これでImageStreamとDeploymentConfigが作成され、デプロイできる準備ができました。

ImageStreamのタグを使ったアプリケーションのリリース

5.「図:検証/本番環境へのアプリケーションのリリースの簡素化と自動化概要(5)タグ付けの手順」

 oc tag image-name:tag1 image-name:tag2の書式でタグ作成します。今回、検証環境のタグ名は、バージョン管理したいので1.0-1としました。

- 開発環境から検証環境へのタグ付け
$ oc tag health/patientui:latest health-testing/patientui:1.0-1
Tag patientui:1.0-1 set to health/patientui@sha256:803891d56baf51695b9473c948210725c699d9c401a4c602f6233180a457c339.

 「oc tag」コマンドの出力はImageStream: patientuiのタグ:1.0-1はhealthプロジェクトのImageStreamImageであるpatientui@sha256:803891d56baf51695b9473c948210725c699d9c401a4c602f6233180a457c339にセットされたという意味です。

ImageStreamのイメージ図(2)

 ImageStreamImageとはImageStreamでコンテナレジストリにあるコンテナイメージを一意に識別するための名前で<ImageStream名> @ <sha256のハッシュ値> で表現します。sha256のハッシュ値は「docker image --digests」で出力されるDIGEST値を使っています。

 そして検証環境のImageStreamを確認するとTAGSに1.0-1が追加されたことが確認できました。

$ oc get is -n health-testing
NAME      DOCKER REPO                         TAGS  UPDATED
patientui (省略):5000/health-testing/patientui 1.0-1 35 seconds ago

 DeploymentConfigを確認するとImageStreamのpatientui:latestでデプロイが始まるため、現在動作しているPodの数(CURRENT)は0のままです。

$ oc get dc 
NAME        REVISION   DESIRED   CURRENT   TRIGGERED BY
patientui   0          3         0         config,image(patientui:latest)

6.「図:検証/本番環境へのアプリケーションのリリースの簡素化と自動化概要(6)タグ付けの手順」

 それではImageStreamのタグを1.0-1からlatestにタグ付けして、デプロイが実行されることを確認しましょう。

ImageStreamのイメージ図(3)
- 開発環境(1.0-1からlatest)
$ oc tag health-testing/patientui:1.0-1 health-testing/patientui:latest
Tag patientui:latest set to health-testing/patientui@sha256:803891d56baf51695b9473c948210725c699d9c401a4c602f6233180a457c339.
$ oc get is -n health-testing
NAME      DOCKER REPO                         TAGS            UPDATED
patientui (省略):5000/health-testing/patientui latest,1.0-1 14 seconds ago

 ImageStreamにはlatestタグが追加されました。

7.DeploymentConfigを確認するとImageChange(patientui:latest)トリガーが動作して現在動作しているPodの数(CURRENT)は3に変わりました。

$ oc get dc
NAME        REVISION   DESIRED   CURRENT   TRIGGERED BY
patientui   1          3         3         config,image(patientui:latest)

8.Pod(コンテナ)を確認します。

$ oc get pod -o wide
NAME              READY STATUS  RESTARTS AGE IP             NODE         
patientui-1-5rmzv 1/1   Running 0        34s 172.30.138.197 10.193.80.235
patientui-1-fftpr 1/1   Running 0        34s 172.30.138.209 10.193.80.235
patientui-1-rdchs 1/1   Running 0        34s 172.30.138.241 10.193.80.235

 Pod(コンテナ)が3つ、nodeSelectorで指定したTOK05のワーカーノード(IP 10.193.80.235)で起動していることがわかります。

9.外部から接続できるようにServiceとRouteを作成します。

$ oc create -f patientui_svc.yaml
service/patientui created
 
$ oc get svc
NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
service/patientui   ClusterIP   172.21.107.238   <none>        8080/TCP   1m
 
$ oc expose svc/patientui 
route.route.openshift.io/patientui exposed
 
$ oc get route
NAME      HOST/PORT                                      PATH SERVICES  PORT    
patientui patientui-health-testing.<省略>.appdomain.cloud      patientui 8080-tcp

10.ブラウザでホスト名をアクセスすると患者用UIのログイン画面が出力されました。

Example Healthログイン画面

 これまでの手順からOpenShiftを使うとImageStreamのタグの作成だけでコンテナイメージを異なる環境へリリースできることがわかっていただけたと思います。みなさんの従来使っている手順に比べてみると大幅に時間が短縮されませんか?タグの作成だけなので簡単ですし、1回のビルドで生成されたコンテナイメージを他の環境でも使用することができるので各環境間での変化が少ない、もしくはまったくないのもいいですよね。

 ちなみにDockerではタグをつけるのは「docker tag」コマンドですが、タグをつける時は、コンテナイメージを直接操作しています。一方、ImageStreamではコンテナイメージを参照しているだけなので、「oc tag」コマンドでタグをつける時は、ImageStreamの中で解決するのでコンテナイメージを操作していないので変化がありません。こういう所もKubernetesにはないOpenShiftの強みなのです。

イメージの履歴管理とリリース・ロールバック

 開発環境(health)のlatestと検証環境のlatestでタグ付けすれば、タグ付けした瞬間にデプロイが始まって簡単なのですが、latestタグが更新されるだけで、履歴が残りません。そのため、1.0-1のようにバージョンを使って、タグ付けの手順は1つ増えますが、ImageStreamのタグと履歴管理機能を使って、下図のようにリリースする場合は「最新のバージョンのタグをlatestにタグ付け」、ロールバックする場合は「古いバージョンのタグをlatestにタグ付け」とリリース・ロールバックはImageStreamのタグで統一、全てのリリースは記録されるという形にしたのです。

イメージの履歴管理とリリース・ロールバック

本番環境へのアプリケーションのリリース

 本番環境へのデプロイ設定の手順は、ほとんど同じため簡単に紹介します。

1.本番環境(health-production)プロジェクトを作成します。

$ oc new-project health-production

2.本番環境から開発環境のコンテナイメージを参照できるようにsystem:image-pullerロールを本番環境のdefault(全てのPod)サービスアカウントに追加します。

- 本番環境(health-production)
$ oc policy add-role-to-user  \
    system:image-puller system:serviceaccount:health-production:default  \
    -n health 
role "system:image-puller" added: "system:serviceaccount:health-production:default"

3.デプロイ設定(DeploymentConfig)を登録します。

$ oc create -f patientui_dc.yaml

4.「図:検証/本番環境へのアプリケーションのリリースの簡素化と自動化概要(8)タグ付けの手順」

 「oc tag」コマンドを使用して検証環境(health-testing)と本番環境(health-production)のpatientui:1.0-1をタグ付けします。

- 検証環境(patienutui:1.0-1)から本番環境(patienutui:1.0-1)
$ oc tag health-testing/patientui:1.0-1 health-production/patientui:1.0-1
Tag patientui:1.0-1 set to health-testing/patientui@sha256:803891d56baf51695b9473c948210725c699d9c401a4c602f6233180a457c339.

5.「図:検証/本番環境へのアプリケーションのリリースの簡素化と自動化概要(9)タグ付けの手順」

 「oc tag」コマンドを使用して本番環境(health-production)のpatientuiのタグ1.0-1からlatestにタグ付けします。

- 本番環境(patientui:1.0-1からpatientui:latest)
$ oc tag health-production/patientui:1.0-1 health-production/patientui:latest
Tag patientui:latest set to health-production/patientui@sha256:803891d56baf51695b9473c948210725c699d9c401a4c602f6233180a457c339.

ImageStreamのイメージ図(4)

6.DeploymentConfigを確認するとImageChange(patientui:latest)トリガーが動作して現在動作しているPodの数(CURRENT)は3に変わりました。

$ oc get dc
NAME        REVISION   DESIRED   CURRENT   TRIGGERED BY
patientui   1          3         3         config,image(patientui:latest)

7.Pod(コンテナ)を確認します。

$ oc get pod -o wide
NAME              READY STATUS  RESTARTS AGE IP             NODE         
patientui-1-jdt7w 1/1   Running 0        34s 172.30.121.202 10.192.17.136
patientui-1-l968s 1/1   Running 0        34s 172.30.199.21  10.212.36.4
patientui-1-wqg2l 1/1   Running 0        34s 172.30.138.240 10.193.80.235

 Pod(コンテナ)が3つ、今回は NODEのIPアドレスが全て違うことからTOK02、TOK04、TOK05に均等に配置されました。

 これで手動での開発環境、検証環境、本番環境のアプリケーションのリリースが終わりました。最初の一回だけは、ImageStream、DeploymentConfig、Serviceなどオブジェクトを作成する必要があるので手順は多いですが、一回作ると開発でビルドした後は、ImageStreamのタグ付けだけでリリースは終わってしまいます。簡単過ぎるくらい簡単ですよね。

Jenkinsを使ったリリースの自動化と可視化

 OpenShiftには、JenkinsをOpenShiftで使うためにカスタマイズされたJenkinsのイメージが標準で用意されています。これまで手作業で実施した開発環境から本番環境までのアプリケーションのリリース手順をJenkins Pipelineに変えてリリースを自動化したいと思います。Jenkins Pipelineの詳細についてはOpenShift 4.2の製品ドキュメント 5.4. PIPELINE ビルドがわかりやすかったので参考にしてください。

事前準備

1.GitHubのリポジトリをクローンします。クローンするとpatientui-pipelineフォルダが作成されます。

$ git clone https://github.com/daihiraoka/patientui-pipeline.git
$ cd patientui-pipeline
$ ls 
README.md  patientui-pipeline.yaml

 patientui-pipeline.yamlが、今回使用するテンプレートファイルです。

 テンプレートファイルは、Jenkins Pipelineビルドの設定と、healthプロジェクトの患者用UIアプリケーションの設定を参考にして作ったテンプレートを記述しています。

patientui-pipeline.yamlの構成

 今回、このテンプレートファイルを使って、新たにhealth-pipelineプロジェクトを作成し、このプロジェクトにJenkinsと患者用UIのアプリケーションを立ち上げます。

 このプロジェクトでビルドした患者用UIのコンテナイメージを検証環境(health-testing)、本番環境(health-production)へJenkinsのPipelineを使ってリリースを行います。

処理概要

 下図では、上部が「oc」コマンドを使用して手動で行なっていたフロー、下部が、Jenkins Pipelineで行う場合のフローになります。

Pipelineの設計

 今回のPipelineは5つのステージで構成されています。

  1. ビルド(開発環境)
  2. デプロイ(開発環境)
  3. タグ付け(開発環境)履歴管理
  4. タグ付け(検証環境)タグ付けによりイメージ更新で自動デプロイ
  5. タグ付け(本番環境)タグ付けによりイメージ更新で自動デプロイ

 environmentにはJenkinsfile内で利用する変数を定義します。

environment {
  version = "2.0"
  devTag = "${version}-${BUILD_NUMBER}"
}

 今回はイメージのタグ付けを行うためにdevTagの変数を使います。versionは任意の数字、手動で1.0をつけたので、今回は2.0を設定、BUILD_NUMBERはJenkins Jobのビルド番号が格納されるデフォルトの環境変数です。

 Jenkins Pipelineが実行されるとdevTagは2.0-1、2.0-2、2.0-3とBUILD_NUMBERが+1加算されるのを利用して履歴管理に使います。

1.ビルド(開発環境)

stage("Build Image") {
  steps {
    script {
      openshift.withCluster() { 
         openshift.withProject() { 
            def bld = openshift.startBuild('patientui')
            bld.untilEach { 
              return it.object().status.phase == "Complete"
            }

 このステージでは開発環境(patientui-pipeline)でビルドが始まりSource-to-Image(S2I)でコンテナイメージを作成します。

 スクリプトの中ではdef bld = openshift.startBuild('patientui')でpatientuiのビルドが開始し、bld.untilEach {} で ビルドのステータスが"Complete"になるまでループしてビルドが終わるのを待っています。

2.デプロイ(開発環境)

stage("Deploy Image") {
  steps {
    script {
      openshift.withCluster() { 
        openshift.withProject() { 
          def dc = openshift.selector('dc', 'patientui')
          dc.rollout().latest() 

 開発環境で作成したコンテナイメージを開発環境にデプロイします。

3.タグ付け(開発環境)履歴管理

stage("Tagging Image Development latest to devTag") {
  steps {
    script {
      openshift.withCluster() {
        openshift.withProject() {
          // Tag the patientui:latest image as patientui:${devTag}
          openshift.tag("patientui:latest", "patientui:${devTag}")

 openshift.tagでImageStreamのタグを作成します。ここではImageStreamに履歴を残すために現在動作している最新のコンテナイメージであるlatestをpatientui:${devTag}でタグ付けします。

4.検証環境にデプロイ

stage("Promote Image Development to Testing") {
  steps {
    script {
      openshift.withCluster() {
        openshift.withProject() {
          // Tag the patientui:${devTag} image as health-testing/patientui:${devTag}
          openshift.tag("patientui:${devTag}", "health-testing/patientui:${devTag}")
          openshift.tag("health-testing/patientui:${devTag}", "health-testing/patientui:latest")

 開発環境(patientui-pipeline)のpatientui:${devTag}のImageStreamを検証環境(health-testing)のpatientui:${devTag}へタグ付けして履歴管理します。

 そして、検証環境へのデプロイは、ImageStreamのタグを${devTag}からlatestに移動することでイメージが更新され、自動的に新しいデプロイが開始します。

5.本番環境にデプロイ

stage("Promote Image Testing to Production") {
  steps {
    script {
      openshift.withCluster() {
        openshift.withProject('health-testing') {
          // Tag the patientui:${devTag} image as health-testing/patientui:${devTag}
          openshift.tag("patientui:${devTag}", "health-production/patientui:${devTag}")
          openshift.tag("health-production/patientui:${devTag}", "health-production/patientui:latest")

 検証環境と似ていますが、本番環境はopenshift.withProject('health-testing')と検証環境(health-testingプロジェクト)に移動してタグ付けを行なっています。ちなみに検証環境のようにopenshift.withProject()をプロジェクト指定しない場合はJenkinsがデプロイされているプロジェクトで実行されます。

 本番環境も検証環境と同様にImageStreamのタグを${devTag}からlatestに移動することでイメージが更新され、自動的に新しいデプロイが開始します。

 これで準備が整いましたので、Pipelineから新しいデプロイを実行できるようにJenkinsインスタンスを起動します。

手順

1.Jenkins Pipeline用のプロジェクトpatientui-pipelineを作成します。

$ oc new-project patientui-pipeline

2.「oc new-app」コマンドを使用してテンプレートからJenkins Pipelineと患者用UIのアプリケーションを作成します。

$ oc new-app -f patientui-pipeline.yaml

3.Podの起動を確認すると多少時間はかかりますが、しばらくするとJenkinsが起動しているのがわかります。

$ oc get pod
NAME               READY     STATUS    RESTARTS   AGE
jenkins-1-bqnn2    1/1       Running   0          6m

4.ビルド設定(BuildConfig)を確認します。

$ oc get bc
NAME                 TYPE              FROM      LATEST
patientui            Source            Git       0
patientui-pipeline   JenkinsPipeline             0

 BuildConfigにはJenkins Pipelineのpatientui-pipelineと患者用UIのpatientuiができています。

5.ImageStreamには、まだコンテナイメージがないため、TAGSは空白です。

$ oc get is -n health-testing
NAME        DOCKER REPO                                                 TAGS      UPDATED
patientui   docker-registry.default.svc:5000/health-pipeline/patientui     

Jenkinsへの権限作成

6.開発環境で動作しているJenkinsが検証環境、本番環境のような他のプロジェクトに対して実行できるようにするには、検証環境、本番環境の編集(edit)ロールをpatientui-pipelineプロジェクトのjenkinsサービスアカウントに追加します。

- 検証環境(health-testing)
$ oc policy add-role-to-user  \
    edit system:serviceaccount:patientui-pipeline:jenkins -n health-testing 
role "edit" added: "system:serviceaccount:patientui-pipeline:jenkins"
 
- 本番環境(health-production)
$ oc policy add-role-to-user  \
    edit system:serviceaccount:patientui-pipeline:jenkins -n health-production
role "edit" added: "system:serviceaccount:patientui-pipeline:jenkins"

7.検証環境、本番環境から開発環境のImageStreamのイメージを参照できるようにsystem:image-pullerロールをdefault(全てのPod)サービスアカウントに追加します。

- 検証環境(health-testing)
$ oc policy add-role-to-user  \
    system:image-puller system:serviceaccount:health-testing:default  \
    -n patientui-pipeline 
role "system:image-puller" added: "system:serviceaccount:health-testing:default"
 
- 本番環境(health-production)
$ oc policy add-role-to-user  \
    system:image-puller system:serviceaccount:health-production:default  \
    -n patientui-pipeline 
role "system:image-puller" added: "system:serviceaccount:health-production:default"

8.GitHubでnode-s2i-openshiftリポジトリの/site/public/login.htmlファイルに[ patient pipeline test-1] という文字列を追加して、Pipelineビルドが終わった後に各環境間で同じ文字列が表示されるか確認したいと思います。

Pipelineビルドの実行

9.Pipelineビルドを開始します。

$ oc start-build patientui-pipeline
build.build.openshift.io/patientui-pipeline-1 started

10.ビルドの進行状況を確認するとpatientui-pipeline-1がRunning(進行中)であることが確認できます。

$ oc get build
NAME                 TYPE            FROM STATUS  STARTED DURATION
patientui-pipeline-1  JenkinsPipeline       Running 5 seconds ago

11.OpenShiftのWebコンソールにログインし、patientui-pipelineプロジェクトのjenkins-ephemeralアプリケーションのFQDNをクリックします。

OpenShift Webコンソール画面

12.Jenkins Pipelineビルドは、「Build Image」ステージで、コンテナイメージをビルドしています。

Jenkins Pipeline(1)

 上図の「Stage View」では各ステージの処理の内容についてはわかりませんが、各ステージのコンソールの出力は記録されていて参照することができます。「Build Image」ステージのログを確認するとGitHubリポジトリからソースコードを取得してビルドを行い、patientui:latestにpushしていることがわかります。

Jenkins Pipeline(2)

13.Jenkins Pipelineのジョブが終了すると各ステージの処理結果と各ステージの所要時間が表示されます。

Jenkins Pipeline(3)

14.次に、Jenkins Pipelineビルドによって作成されたImageStreamのタグを確認します。「oc get is」コマンドでそれぞれImageStreamを確認するとバージョン(2.0-1)でタグ付けされていることがわかります。

- 開発環境
$ oc get is -n patientui-pipeline
 
NAME       DOCKER REPO                            TAG          UPDATED
patientu   (省略):5000/patientui-pipeline/patientu 2.0-1,latest About a minute ago
 
- 検証環境
$ oc get is -n health-testing
NAME       DOCKER REPO                           TAG                           UPDATED
patientu   (省略):5000/patientui-testing/patientu 2.0-1,latest,1.0-2 + 1 more.. About a minute ago
 
- 本番環境
$ oc get is -n health-production
NAME       DOCKER REPO                           TAG                           UPDATED
patientu   (省略):5000/patientui-production/patientu 2.0-1,latest,1.0-2 + 1 more.. About a minute ago

15.最後に各環境にデプロイしたアプリケーションを確認すると文字列の変更が全ての環境で反映していることがわかります。

患者用UIログイン画面

新しいJenkins Pipelineビルドの開始

 新しいJenkins Pipelineビルドを始めるためには、開発担当のアプリケーションのソースコードの変更がリポジトリにプッシュした後、Jenkins Pipelineビルドを開始するだけです。ビルドにWebhookトリガーを有効にすれば、この手順も必要ないですね。

$ oc start-build patientui-pipeline
build "patientui-pipeline-2" started

 これで開発環境のアプリケーションのビルド、検証環境と本番環境のアプリケーションのデプロイをJenkins Pipelineビルドを使って展開することができました。Jenkinsを使って全てのステップを自動化、可視化することで、アプリケーションのリリースが短縮できますし、オペレーションミスなど意図しない変更が発生しなくなります。

 みなさんがそれぞれの要件で配信プロセスを作るときは前述のOpenShift 4.2の製品ドキュメント 5.4. PIPELINE ビルドに加えてOpenShiftの開発者ガイド「2.3. 環境全体におけるアプリケーションのプロモート」も参考にするといいと思います。

Note

 自分のパソコンで今回の手順を試すことができるようにQiitaに「OpenShiftによるJava EEアプリケーションのモダナイゼーションをやってみた(4)」を投稿しました。是非お試しください。


最後に

 IBM CloudのマネージドサービスであるRed Hat OpenShift on IBM Cloudは、ブラウザ操作で簡単に短時間で、シングル構成から耐障害性のある複数のデータセンターによるマルチゾーン構成までOpenShiftクラスターを入手することができます。これまで、自分で構築するのが難しかった、インフラ担当がいなかったなど、障壁のあった企業にとっては朗報ですね。Red Hat OpenShiftは、Kubernetesを含むOpenShiftのコンポーメント全てがサポート対象であることが最大の利点だと思います。(Kubernetesはソフトウェアなのでバグは必ず発生します。)さらに今回の記事で紹介しましたがDevOpsやCI/CD(継続的インテグレーション/継続的デリバリー)を始めるための必要なコンポーネントが標準で用意されていて、OpenShiftの製品ドキュメントにユースケースが用意されているので、そのまま乗っかる形でDevOpsやCI/CDが始められるのが素晴らしいと思います。

 OpenShiftの学習としてはExample HealthのOpenShiftによるJava EEアプリケーションのモダナイゼーション以外にもCode Patterns記事が用意されているので、ぜひ試されるといい経験になると思います。OpenShiftの製品ドキュメントの開発者ガイドも参考になると思います。また、IBM Developer DojoではOpenShift(Minishift)のハンズオンセミナーを定期的に開始しています。(2019年12月時点)自分で始めるのが難しい方は、そこで、サポートを受けながら自分のパソコンにOpenShift(Minishift)をインストールして始めることができるのでいい機会だと思います。

最後になりますが、お読みいただいた方へ

 これまで3回の記事にお付き合いいただきまして、本当にありがとうございました。

 私のこれまでの経験に基づいて書き綴ってみましたが、みなさんにとって何か一つでも参考になれば嬉しく思います。

 ありがとうございました。

イベントのご案内

IBM Developer Dojo Online 2019 #11 OpenShift

本イベントでは、DojoのコンテンツをWebinar形式でオンラインで実施いたします。

Copyright © ITmedia, Inc. All Rights Reserved.


提供:日本アイ・ビー・エム株式会社
アイティメディア営業企画/制作:@IT 編集部/掲載内容有効期限:2020年1月7日

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。