JenkinsでCI環境構築して効率的な開発環境を実現する

Jenkinsロゴ ※Jenkins®のロゴはJenkins Projectに帰属します。

あいさつ

みなさまこんにちは、おさかなです。 現在所属しているチームでWindowsアプリを開発しているのですが、その中でJenkinsを使用してCI環境を構築する機会があったので、今回はCI/CDの紹介とその環境の構築について記事にまとめてみたいと思います。CI/CD構築に興味がある方、これから業務でCI/CDの導入を検討している方などの参考になれば幸いです。

CI/CDとは?

みなさんはCI/CDという言葉はご存知でしょうか。CI/CDは「Continuous Integration/Continuous Delivery」の2つを略したもので、日本語では「継続的インテグレーション/継続的デリバリー」と言われます。これらはソフトウェア開発のプラクティス(開発に用いられる手法)のことです。

CI(継続的インテグレーション)

開発者が書いたコードをメインブランチに頻繁にマージするプラクティスのことを言います。統合の際に変更が多くなってしまうとマージ作業が大変になるため、少ない修正で何度もマージを行うことが重要です。マージ前にコードにバグやエラーが含まれていないか自動的にビルド・テストが実行されます。

CD(継続的デリバリー)

ソフトウェアをいつでもリリース可能な状態に保つためのプラクティスを言います。これは、ソフトウェアの変更(新機能、設定変更、バグ修正、実験などでマージされたコードの変更)が自動的にビルド・テストされ、最終的にステージング環境にデプロイされることを意味します。  

CI/CD概要
CI/CD概要

用語だけだと難しく聞こえますが、CI/CDを一言で簡単にまとめると「コードの追加・修正に応じてビルド・テスト・デプロイを自動化する環境を構築することで開発を効率化すること」といえます。これを実践することで開発者やチーム全体の生産性の向上に効果があると知られています。

一般的にCI/CDを導入することで下記のメリットがあります。

  • バグの早期発見・修正

CI/CDではコードが頻繁にビルドされ、テストが自動的に実行されます。これによってバグを早期に発見でき、修正も容易になります。

  • リリースの高速化

CI/CDは、コード変更を迅速に本番環境にデプロイができます。これにより、新機能のリリースやバグ修正の反映が高速化します。

  • 品質の向上

頻繁なビルドとテストにより、ソフトウェアの品質が継続的にチェックされ向上につながります。

  • 開発者の生産性向上

ビルド、テスト、デプロイを自動化することで、開発者は開発に集中することができます。

また、CI/CDが近年注目される理由としては、アジャイル開発との親和性が挙げられます。アジャイル開発は基本的に開発・リリースのサイクルを短期間で行うため、その他の開発と比べてコード変更やテストの回数が増加してしまうという課題があります。しかし、アジャイル開発でもそれらの工程を自動化することにより、短期間であっても効率的な開発を実現できるのがCI/CDです。現在はCI/CD環境を実現するさまざまなツールが存在し、ユーザーは状況に応じて適切なツールを選択することが求められます。

CI/CDのフロー

それではさらに具体的なCI/CDのイメージをつかむために、図を用いて流れを紹介します。CI/CD の各フェーズを下の図に示します。 まず、最初に開発者はコードを作成します。作成したソースコードをGitなどのコード管理ツールにコミットすると、CIツールがコミットを検知し、自動的にビルド・テストを実行します。このとき、バグなどでビルドエラーやテストに失敗した場合、修正のために開発者に返され、開発者はバグを修正して工程を最初からやり直します。

ビルド、テストに成功すると、コードをリリース用ブランチにマージしてデプロイが行われます。デプロイされたものはすぐに動作確認することができます。リリースブランチでビルドされたものをエンドユーザーのところへデプロイするかどうかは、人が判断して行います。以上がCI/CD の一連の流れです。なお、エンドユーザーへのデプロイまで自動で行うことは「継続的デプロイメント」と呼ばれます。

CI/CDフロー
CI/CDフロー

少人数チームの場合、開発リソースの関係で、上記の工程におけるビルドが終わるとテストを行わず(実装せず)にデプロイを行う開発チームも多いそうですが、テストを行うことは非常に重要です(もちろん本番環境にデプロイする前はテストを行っていると思いますが……)。テストにはいくつかの種類がありますが、いずれもCIのパイプラインに組み込み、自動化することができます。たとえば、結合テストに単体テストを組み合わせて、テストカバレッジを広げることもできます。テストを行うことで、バグの数を減らして高品質なソフトウェアを実現が可能になります。

主要なCI/CDサービス

現在はさまざまな機能を備えたCI/CDツールが存在し、CI/CD環境を構築するためにはそれらのサービスを使用して構築することが一般的です。サービスにはオンプレミス型とクラウド型の2種類が存在します。

  • オンプレミス型

自分でサーバーを構築して保守・運用する必要があるが、拡張性が高い

  • クラウド型

サーバーなどを自前で用意する必要がなくすぐに使用を開始することができるが、拡張性は低い

また、実際にサービスとして提供されている主なCIツールについても下記にまとめます。サービスごとに特徴があり、自分の導入する環境に応じて親和性の高いツールを導入することが重要です。

CI/CDサービス
CI/CDサービス
  今回私がCI/CD環境を導入するにあたり、実現したい内容は以下の通りです。 ・GPUリソースを使用したい ・コストはできるだけ下げたい ・ビルド時はWindows環境を使用したい(Windowsアプリを開発しているため) ・コードベースでリソースを管理したい ・コード管理にはBitbucketを使用したい

これらを踏まえ、サーバーを立てるという手間はあるものの、拡張の自由度が高くコストもコントロールできるオンプレミス型のJenkinsを使用することに決定しました。 Jenkinsはオープンソースのため無償で使用でき、コミュニティが常に新規プラグインを開発して公開しているため容易に拡張が可能です。現在使用しているBitbucketなどのツールにも対応しており、コードでリソースを管理することも可能です。

JenkinsでのCI/CD環境の構築

それでは、今回私が作成した構成図を示します。前節で検討した項目を下記の仕組みで実現することにしました。

  • GPUリソースを使用したい、Windows環境を使用したい

⇒ビルドする環境にはAWSのWindows OSのGPU搭載のEC2を使用する

  • コストはできるだけ下げたい

⇒ビルド環境(EC2)は必要な場合のみ起動し、ジョブがなくなると終了するようにする  Jenkinsサーバーも必要な時間外は自動で停止するようにする

  • コードベースでリソースを管理したい

⇒Jenkinsでジョブ実行時にファイルから設定を読み取って動作するように設定する  (これはJenkinsのデフォルトの機能で実現可能)

  • コード管理にはBitbucketを使用したい

⇒JenkinsのBitbucketのプラグインを使用してBitbucketと接続する

構築したCI構成
構築したCI構成

Jenkinsサーバー、ビルドノードはすべてAWSのEC2上で作成します。BitbucketにソースをpushするとそれをJenkinsが自動で検知し、AMIからビルドノードを立ち上げます。ビルドノードが立ち上がるとBitbucketからソースをクローンし、内部でビルドとテストを実行します。ビルドとテストの結果、バグや問題がなければビルド結果をS3に保存してzipでダウンロードできるようにします。 また、効率的にジョブが実行できるように、ジョブが複数ある場合は自動でEC2をスケーリングするようにします。 (※今回は本番環境へのデプロイまでは行っていません)

ここではEC2環境へのJenkinsのインストールやAWSのネットワーク設定については割愛し、Jenkinsの設定周りにフォーカスして紹介したいと思います。

JenkinsにビルドノードのAMIを登録して自動起動

JenkinsはAWSと紐づけることで、ジョブの中でAMIからEC2インスタンスを起動して処理させることが可能です。

Jenkinsの管理->Clouds->PJName buildnode->Configureを開き、AWSコンソールで作成したビルド用のAMIの登録を行うことができます。これによりビルドジョブが起動するときはAMIからインスタンスが生成されるようになります。

最初にAWS側のIAMユーザーにJenkins用のユーザーを作成します。JenkinsユーザーにはEC2とS3(後にzipファイルをS3に保存する操作を行うため)へのアクセス権を付与しておきます。生成が完了するとJenkinsのConfigure画面に戻り、生成したユーザー情報を入力します。

AMIの登録
AMIの登録

次に、ビルドノードに関する情報を入力します。ビルドノード名、AMI ID、インスタンスタイプ、アベイラビリティゾーン、セキュリティグループなどを入力します。「Remote FS root」ではビルド環境のEC2上でルートディレクトリとなるディレクトリを入力します。 「Number of Executors」は同時にビルドする数を設定でき、効率的にビルドをすることができます。また「Idle termination time」を設定すると、その時間だけビルドノードが使用されていなければインスタンスを自動で終了させることができます。これにより不要なコストの削減も可能です。

AMIの登録
AMIの登録

AMIの登録
AMIの登録3

AMIの登録
AMIの登録4

S3への成果物の保存

JenkinsではS3上にビルド結果の成果物を保存し、ユーザーがブラウザーからダウンロードするように設定することができます。

まずAWSで成果物保存用のS3を作成し、その内部に適当なディレクトリを作成します。次にJenkinsの「Jenkinsの管理->Plugins」を開きます。そしてArtifact Manager on S3をインストールします。

s3のプラグインインストール
s3のプラグインインストール

「Jenkinsの管理->AWS」を開き、Amazon S3 Bucket Access settingsのバケット名、ディレクトリを入力します。設定ができたら「Validate S3 Bucket configuration」を押し、Successが返ってくることを確認します。 ※その際、Credentialの入力も行います

次に、「Jenkinsの管理->System」を開き「Artifact Management for Builds」という項目を探します。そして「Cloud provider」という場所に「Amazon S3」を設定します。これでビルドが成功すると指定したバケットに成果物を保存できるようになります。また、ブラウザーから成果物をzipでダウンロードすることも可能です。

成果物のダウンロード
成果物のダウンロード

JenkinsとBitbucketの接続

Jenkinsはさまざまなコード管理ツールと接続し、任意のアクションを検出して自動でジョブを実行することが可能です。

今回はソース管理ツールとしてBitbucketを使用したので、BitbucketとJenkinsをwebhookで接続する設定をします。まず、Bitbucketの接続したいリポジトリを開き、右メニューの「Repository settings」を開きます。「Add Webhook」を選択し、Title、接続先のURLを入力します。また、TriggersでWebhookのトリガーとなるイベントにチェックを入れます。今回はBitbucketにプッシュしたときにビルドが走るようにしたのでPushのみチェックを入れています。

Bitbucketとの接続
Bitbucketとの接続

次にJenkinsを開き、「Jenkinsの管理->Plugins」を開き、Bitbucketと検索します。すると、BitbucketとJenkinsを接続するためのプラグインが表示されるのでインストールを行います。これでJenkinsとBitbucketが接続されました。 実際にpushを行うとビルドが走り、ジョブが実行されるのが確認できます。

ジョブの実行
ジョブの実行

※AWS側でセキュリティグループにAtlassianのIP、ポートを追加してやる必要はあります

コードベースでリソースの管理

Jenkinsではジョブをコードで制御することができます。これにより環境が変わっても毎回同じジョブを実行することができ、柔軟性の高いジョブを作成することも可能です。

まず、ダッシュボードから「新規ジョブ作成」を押して「Multibranch Pipeline」 を選択してジョブを作成します。

新規ジョブの実行
新規ジョブの作成

すると、「General->Buil Configuration」にModeとScript Pathという領域が表示されます。ここにジョブ実行用のコードを記述したファイル名を指定しておくことができます。そうすると、Jenkinsがそのファイル名を持つブランチを検知したときにそれに対応するジョブを自動で作成するので、ブランチを動的にコードで管理することが可能となります。

※Multibranch Pipelineの中にテストフェーズを追加してユニットテストなどを実施することも可能です。

Jenkinsの起動・終了時間

AWSのCloudWatchとLamdaを使用してインスタンスの管理を行うことが可能です。これによりJenkinsサーバーのインスタンスの開始・停止を制御することができます。

CloudWatchのEventBridgeから曜日と時間による「Startルール」と「Stopルール」を作成し、それをトリガーにLambdaが呼ばれるようにしています。土日および24時~9時まではほとんどJenkinsが使用されることはないと考え、平日9時にJenkinsインスタンスを自動起動、24時にインスタンスを自動停止で運用することにしました。これによりさらなるコストの削減が可能です。

以上の機能を実装することで、Bitbucketにコードがプッシュされると自動でビルド用のEC2インスタンスが起動し、ビルド・テストを実行し、成功すると成果物をzipファイルでダウンロードできるようにしました。また、idle状態が続くとビルド用のEC2インスタンスは自動で終了し、Jenkinsサーバーも付与な時間帯には停止してコストを削減しました。事前に調査した概算ではクラウドを使用した場合の1/3程度のコストにできたのではないかと思います。

まとめ

今回はCI/CDについて概要を説明し、私が業務で作成したCI環境の構成や設定について紹介しました。私が構築した環境もまだ必要最低限の機能を入れて動作させただけで、今後テストやデプロイについても力を入れていく必要があると考えています。 しかしながら、簡単なものでも自動でビルド・テストを行ってくれると、早期にバグが見つかることも多く非常に恩恵を受けています。 Jenkinsではそのほかにも、Doxygenでドキュメントを表示したり、テストカバレッジを表示したりもできるため、機能を追加してさらに使いやすく自由にカスタマイズできます。CI/CDはバージョン管理の観点でも非常に有用なので、多少のメンテナンスのコストはありつつも、開発に応じてこれからもアップデートしていきたいと思います。

それでは次回の記事でお会いしましょう!