devopsにコンテナベースの デリバリ・パイプラインを活用 - oracle ·...
TRANSCRIPT
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
22
//microservices and containers /
本記事では、よく使われているJenkinsツール、Dockerコンテナ、クラウドにホストされた Java EEインス
タンスを活用して、DevOpsを実現する方法について説明します。DevOpsは 2009 年に生まれた用語で、
開発者と運用スタッフが連携してアプリケーションの構築やデプロイにあたる手法を指しています。DevOpsでは
一般的に、サイクル時間の短縮が重視されます。すなわち、新しいプロダクトを迅速にリリースしやすくなります。
DevOpsは、他の慣習とどう関係しているのでしょうか。継続的デリバリとは、公開するプロダクトに頻繁に
変更を加えることを言います。一方、継続的デプロイとは、その変更を効率的にエンドユーザーに提供するため
のプロセスを指します。継続的インスペクションと継続的インテグレーションは、継続的デリバリの構成要素とな
るものです。継続的インスペクションは、高い品質を常に課すことを重視する手法です。継続的インテグレーショ
ンは、1日に何度も変更をバージョン管理システムにチェックインし、バージョン管理を行っているコードをいつ
でもチェックアウトでき、ビルドが成功するようにする手法を指します。継続的デリバリは、継続的デプロイの前
提となります。また、DevOpsは継続的デリバリの前提となります。
基本的なユースケース車輪の再発明はしたくないため、ここでは優れた手法や、ツールチェーンによる相乗効果について紹介します。
今回のDevOpsに向けた取り組みに欠かせないのが、集中管理ツールです。中でも重要になるのが、自動化エ
ンジンのJenkinsです。
つまり、バージョン管理システム(今回の例ではGit)に変更をプッシュした瞬間、Jenkinsでテスト、パッケー
ジ化、コンテナ化、ステージング、プロモーションというすべてのプロセスが自動的に実行されます。自動化は
DevOpsの構成要素の1つですが、手動で行う手順がDevOpsと矛盾することはありません。ほとんどの場合、
手動で行う手順には意味があり、欠かせないものである場合もあります。
DevOpsにコンテナベースの デリバリ・パイプラインを活用Jenkinsベースのツールチェーンで、コンテナ化されたJava EEアプリを クラウドに自動配信するパイプラインを実際に作成する
MICHAEL HÜTTERMANN
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
23
//microservices and containers /
今回の例では、Alipine Linux 上のOpenJDKで実行する
Java EE Webアプリケーションをデプロイします。このWebア
プリケーションは、広く使われているApache Tomcat にバン
ドルされた状態でクラウドにデプロイします。このアプリケー
ションは、さまざまなクラウドにデプロイすることができます
が、今回はOracle Cloudで実行します。また、ご想像のとおり、
Dockerfileで生成したDockerコンテナを使用します。
このアプリケーションを実行すると、図1の開始ページが表示されます。次に、これからパイプラインを使って行う変更処理について説明します。
パイプラインからワークフローへ変更したコードは、いくつかのステージの処理を経て、本番環境に向かいます。ステージング(プロモーションと
も呼ばれます)とは、明示された、ソフトウェアのベースラインを、すべての構成項目とともに、整合性のとれた
形であるステージから別のステージに遷移させるアクティビティを指します。ソフトウェアは、リビルドされること
なく、構成に基づいて複数の異なる環境にステージングされます。一連のステージと、ステージ間の遷移ルール
を合わせて、デリバリ・パイプラインと言います。DevOpsの視点から見た場合、パイプラインは組織内の複数
の機能、特にすべての開発機能と運用機能をつなぐかけ橋となるものです。
通常、ある変更は、後続のステージにプルされるまで待機し、その後、遷移ルールに従って処理されます。
遷移ルールは、満たす必要がある明確な要件と一致します。この要件を、クオリティ・ゲートと呼びます。さらに
大規模で複雑な環境では、複数のパイプラインが合わさり、ワークフローを形成します。ワークフローの一部は
互いに結び付いており、自動的に実行されます。その他のパイプラインは、手動で実行します。
図 2に、コードの変更をリリース・ビルドに反映させる典型的なワークフローを示します。ワークフローは、複数のパイプラインで構成され、それぞれの間にクオリティ・ゲート(図の鍵マーク)が存在します。承認された
変更リクエストからワークフローが始まり(図の左上)、エンドユーザーに変更が配信されると、ワークフローが
終わります(図の右下)。変更中のコードは継続的にビルドされます。その変更は、開発ビルドによって、開発版、
リリース候補(RC)版、そして最終的に正式(GA)版へとプロモーションされていきます。主なステークホルダー
には、開発部門(図の左から1~3番目の列の下に記載されたDev)、企業の代表者(Business)、ソフトウェア
構成管理機能(SCM)などがあります。この例については、以降のセクションでさらに詳しく説明します。
図1:本記事で使用するシンプルな アプリケーションの開始ページ
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
24
//microservices and containers /
最初のステージは、開発者のワークスペースです。ここでは、コードの設計、記述、テストを行った後、コー
ドの変更をバージョン管理システムにチェックインします。コードをチェックインすると、下流のJenkins パイプラ
インがトリガーされます。第 2ステージの継続的ビルドは、変更されたコードと既存のコードを合わせてコンパイ
ルできるかどうかと、基本的な単体テストが成功するかどうかを確認するための簡単なジョブです。この最初の2
つのステージが、継続的インテグレーションにあたります。
次のステージは、開発版を生成する「開発ビルド」です。このステージでは、意味のある一連のコミットから、
プロダクトのワーキング実装がビルドされます。ここでは、ビルド、検証、コンテナ内での成果物の作成という
段階すべてが行われます。
今回の例のパイプラインでは、Mavenスナップショットの取得、Mavenベースのリリースの生成、WARファ
イルのパッケージ化、コードの検査(静的コード分析には、SonarQubeを使用します)、生成されたWARファイ
ルがデプロイ可能かどうかの確認、DockerイメージへのWARファイルのパッケージ化、Dockerコンテナが実行
可能かどうか、すなわちWebアプリケーションが起動可能かどうかの確認が行われます。起動可能であると確
認された場合、WARファイルとDockerイメージが筆者の会社のバイナリ・リポジトリ・マネージャに送信されます。
このマネージャは、JFrog Artifactory にホストされています。Artifactory は、Dockerイメージを管理するDocker
レジストリとして動作します。WARファイルとDockerイメージは、Artifactoryのリポジトリに格納されます。トレー
図2:コードの変更をリリース・ビルドに移行するワークフローのパイプライン
ContinuousBuild
Change request
Delivered changeDevDev DevSCM
Business Business
GA BuildPipeline forDev Versions RC Build
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
25
//microservices and containers /
サビリティを実現するため、Jenkins からArtifactory に移動し、そこから戻ることができるようになっています。
リスト1に、Groovyで記述したパイプライン・スクリプトの一部を示します。ここでは、バイナリにコンテキスト情報を追加したうえで、Jenkins からJFrog Artifactory に転送する方法を示しています。詳細については、
Jenkins パイプラインを参照してください。
リスト1:DockerイメージをJFrog Artifactory に移動し、メタデータを使ってラベル付けするパイプライン・スクリプトの抜粋def artDocker = Artifactory.docker server:server, host: "tcp://127.0.0.1:1234"artDocker.addProperty("eat", "pizza").addProperty("drink", "beer")
def dockerInfo = artDocker.push( "$ARTI3REGISTRY/michaelhuettermann/alpine-tomcat7:${version}", "docker-local")buildInfo.append(dockerInfo)server.publishBuildInfo(buildInfo)
DevOpsにおいて、左シフトと右シフトという言葉は、従来のソフトウェア開発のアプローチをパイプラインの早
い段階に移動(左シフト)または遅い段階に移動(右シフト)することを指します。今回の例では、プロセスの早
い段階で本番環境向けのコンテナをパッケージ化することが左シフトにあたり、Dockerfiles およびその他の構成
項目を、同じベースライン・ビジネス・アプリケーションの一部としてバージョン管理を行うことが右シフトにあた
ります。
Jenkins パイプラインは、Jenkins Blue Oceanと呼ばれる、Jenkins パイプラインの視覚化や作成を行う機
能と、Jenkins のドメイン固有言語(DSL)を使って設定します。このかなりの部分が、一般的に継続的インテグレー
ションと呼ばれているものと重複します。SonarQubeもDSLからトリガーします。SonarQubeベースのクオリティ・
ゲートの定義をリスト2に示します。
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
26
//microservices and containers /
リスト2:SonarQubeによる処理の一部であるクオリティ・ゲートtimeout(time: 2, unit:'MINUTES') { def qg = waitForQualityGate() if (qg.status != ‘OK’ ) { error "Pipeline aborted ... ${qg.status}" }}
パイプラインには、DockerイメージがArtifactory にプッシュされる前に行われるテストも含まれています。その
ために、Dockerイメージが実行され、生成されたDockerコンテナでバンドルされたWebアプリケーションが
利用できるようになっています。これは、後ほど本番環境にプロモーションさせたいものにほかなりません。以
上で、Webアプリケーションが正常に起動したかどうか、期待される動作になっているかどうかをSeleniumを使っ
て確認できるようになります。パイプラインの実行を視覚化したものを、オンラインで参照できます。
それでは、次の手順に進みます。次は、作成されたアーティファクトに対して、リリース候補(RC)版を生成
します。
リリース候補版パイプライン 次のパイプラインでは、リリース候補版を作成します。リリース前の最後のステージになるため、ここで気になる
のは、バイナリがパッケージ化され、十分にテストされ、コンテナ化されているかどうかを確認することです。こ
れは、既存の開発版からよいものを選ぶことで行います。通常は前のパイプラインで作成された最新版になりま
すが、最新版を選ばない理由が存在する場合もよくあります。たとえば、リグレッションが起きている場合など
です(言い換えれば、すべての開発版が漏れなくリリース候補版にプロモーションされなければならないわけで
はありません)。大きなプロジェクトや複雑なプロジェクトでは、このステージでマルチコンポーネントの設定やフ
レームワークの開発を行うことが多いでしょう。
今回の例では、RCパイプラインでWARファイルとDockerイメージがArtifactory の専用ステージング・リ
ポジトリにプロモーションされます。多くの場合、このステージでコンテキスト情報をバイナリに追加します。た
とえば、チケット番号や、責任が変更されたことを示す、バイナリの新しい所有者などです。このパイプラインの
Groovyスクリプトを、GitHubで公開しています。
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
27
//microservices and containers /
正式版パイプラインワークフロー全体における次のステージは、正式(GA)版を生成するパイプラインです。ここでも、バイナリを
プロモーションし、さまざまなクオリティ・ゲートを適用し、コンテキスト情報を追加し、さらにテストを行うとい
うのが基本的な考え方になります。実際には、ソフトウェア構成管理チームやリリース・チームがこの作業を担当
することが多いでしょう。今回の例では、オリジナルのバイナリを再度プロモーションしますが、このステージで
は、JFrog Artifactory からJFrog Bintray にプロモーションします。
ここで示しているのは、別のDockerレジストリを利用する方法です。これは、異なる環境が混在した設定
を例示するために含めています。実際に、社内のさまざまな機能や所有権、責任を反映して、多くのプラットフォー
ムやツールが使われています。今回の例では、リリース候補版のDockerレジストリとしてJFrog Artifactoryを、
正式版バイナリのDockerレジストリを提供するツールとしてJFrog Bintrayを使っていることを強調しています
(Jenkins Blue Oceanワークフローの画面をキャプチャした図3をご覧ください)。このパイプラインのスクリプトもGroovyで書かれており、オンラインで公開しています。
DockerイメージがJFrog Bintray にプッシュされると、コンシューマがイメージに対してネイティブのDocker
コマンドを適用できるようになります。これにより、たとえばDockerイメージをプルして使用することができます。
今回の例では、クラウドでコンテナを実行してオーケストレーションさせたいため、コンシューマはOracle Cloud
になります。この点は、次のセクションで説明します。
図3:GA版のパイプライン
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
28
//microservices and containers /
クラウドへのDockerコンテナのプロビジョニングDocker は、アプリケーションを確実に開発環境から本番環境へと移動する方法として、事実上の業界標準になっ
ています。その理由は、Dockerを使うと、明確に定義され、独立したサービスを、オンプレミスのデータセンター
からクラウド環境へというように境界を越えて配信できるからです。さらに、Docker は Puppetなどの構成管理
システムとも簡単に統合できます(筆者による以前の記事をご覧ください)。ただし、現在登場しつつあるコンピュー
ティング・モデルは、Docker のイメージやコンテナだけで完結するものではなく、複数のコンテナのオーケストレー
ション(アプリケーション・サーバーとロードバランサというように、複数の単一コンテナをグループ化したソリュー
ションの作成など)、リソースの管理(ホストやネットワークなど)、一連のコンテナへのライフサイクル操作の提供(障
害時の自動再起動による「自己回復機能」など)、非常に多くの機能の使用(スケーリング、サービス検出、ドキュ
メント化された十分な機能を持つAPIなど)といった点も関係します。
これを実現しつつ、本番環境を含むいくつかの環境をホストできる使いやすいプラットフォームに、Oracle
Cloud Infrastructure Container Service Classic があります。このPlatform-as-a-Service(PaaS)ソフトウェアは、
Oracle Cloud製品の一部で、マネージャ・ノードとワーカー・ノードが基盤となっています。マネージャ・ノード
は、ワーカー・ノードへのコンテナのデプロイをオーケストレーションします。コンテナ・サービスはホスト上で動
作し、複数のホストがさらにリソース・プールとしてまとめられます。リソース・プールはホストの集合体で、そこ
にサービスで使うコンテナを配置します。Oracle Cloud Infrastructure Container Service Classic のコンテナ・サー
ビスでは、Docker サービスの定義と、Docker
イメージの実行やデプロイメント・ルールに必要
な構成設定の定義を行います。Oracle Cloudサー
ビスでは、グラフィカルなウィザード、または
Dockerコマンドを入力できるプレーンテキスト・
フィールドを使って、サービスを実行するコマン
ドを生成できます。このサービスは、内部的にYAMLによる構成を使っているため、YAMLを編集することもで
きます。複数のサービスを連携させて、スタックと呼ばれる、意味付けされたグループを起動することも可能です。
Oracle Cloud Infrastructure Container Service Classicコンソールでは、デフォルトで多くのサービスやスタックが
提供されているため、すばやく簡単に使い始めることができます。
このような環境では、サービスは管理可能でデプロイ可能な単位であり、そこで機能横断的なチームがライ
フサイクルを通じて作業できます。優れたソフトウェア・ソリューションは、基盤となるプラットフォームから分離
されているべきです。そのため、Oracle Cloudサービスをプラットフォームとして使用すれば、すべてのDocker
優れたソフトウェア・ソリューションは、基盤となる
プラットフォームから分離されているべきです。
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
29
//microservices and containers /
コンテナやそれを使って組み立てるソリューションを管理およびオーケストレーションする手段として役立てるこ
とができます。
今回の例では、パッケージ化したテスト済みDockerコンテナを、JFrog Bintray の DockerレジストリにGA
版としてプッシュします。Oracle Cloud Infrastructure Container Service ClassicでこのDockerレジストリを定義
すると、このサービスがレジストリから簡単にイメージをプルできるようになります。
ここでは、説明のために、すでに存在する可能性があるデモ用デプロイを停止して削除し、対応するサービ
スとDockerイメージをOracle Cloud Infrastructure Container Service Classic から削除しています。単に、それ
ぞれの新しいバージョンを提供し、実行中のものを削除するようにソリューションを拡張することも簡単です。パ
イプラインは、後ほど新しいバージョンのイメージをプルし、新しいサービスを作成して、サービスをデプロイす
ることができます。図 4に、このステージに含まれる処理のシーケンスを示します(イメージの削除が行われているときにキャプチャしたものです)。
Jenkins は自動化エンジンであり、Docker オーケストレータではありません。そのため、Oracle Cloud API
を使ってそれぞれの目的を実現します。一連の段階は、Jenkins の資格証明ハンドリングによって保護されており、
一部では Jenkins の共有ライブラリとして管理される一元化されたスクリプトを使っています。Oracle Cloudの
呼出しは、定義されているAPIに基づいてcurl で行います。リスト3に示すコード・スニペットは、Oracle Cloud Infrastructure Container Service Classicに新しいサービスを作成するものです。新しく作成されたイメージに基づ
き、パラメータで指定したバージョンに対応する新しいサービスを作成しています。
図4:クラウドへのデプロイを行うパイプライン(Oracle Cloudで利用する一連の効率的な自動化手順)
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
30
//microservices and containers /
リスト3:新しいサービスの作成(インデントされた行は前の行の続きです)curl -ski -X "POST" -H "Authorization:Bearer ${BEARER}" "https://${CLOUDIP}/api/v2/services/" --data "@${WORKSPACE}/new-service.json"
このコマンドでは、入力として、オンラインで公開しているJSONファイルを使用しています。
サービスを作成した後は、そのサービスから新しいデプロイメントを作成する必要があります。このデプロ
イメントとは、サービスで実行中のインスタンスを指します。Oracle Cloudサービスで新たなデプロイメントを作
成するコマンドをリスト4に示します。
リスト4:新しいデプロイメントの作成(インデントされた行は前の行の続きです)curl -ski -X "POST" -H "Authorization:Bearer ${BEARER}" "https://${CLOUDIP}/api/v2/deployments/" \ --data "@${WORKSPACE}/create-deployment.json"
このコマンドも、一般的な JSONファイルを入力として使います。
パイプラインの処理が完了すると(パイプライン・ファイルの全体を、こちらから参照できます)、新しい
バージョンのDockerイメージに基づいたコンテナが実行され、クラウドのコンソールで確認できるようになります
(図 5参照。名前は気にしないでください。筆者は猫好きです)。以上で変更が反映され、クラウドで利用できる状態になります。ここで、公開Web ページをチェックし、
新しいコンテンツが実際に利用できるかどうかを確認する必要があります。図 6から、正常に動作していることがわかります。
まとめこれで、コードの変更を本番環境にリリースするまでの旅は終わりになります。その道のりでは、DevOpsのコン
セプトやツールに重点を置いて、JenkinsやDockerなどのツールを利用しました。本記事では、典型的なベスト・
プラクティスに従い、パイプラインやステージによるデプロイで変更を遷移させ、その変更をクラウドにデプロイ
する方法について説明しました。</article>
ORACLE.COM/JAVAMAGAZINE ////////////////////////////// MARCH/APRIL 2018
31
//microservices and containers /
Michael Hüttermann:Java Championで、継続的デリバリ、DevOps、ソフトウェア構成管理、アプリケーション・ライフサイクル管理のエキスパート。『DevOps for Developers』(Apress)、『Agile ALM』(Manning
Publications)など、4冊の書籍を執筆。2017年には、Oracle Developer Championに選ばれた。
図6:新しいコンテンツが利用できるかどうかのチェック
図5:Oracle Cloud Infrastructure Container Service Classicコンソールによる、デプロイメントの視覚化と操作