#■これをやった理由
- 担当プロダクトでJenkinsを使ったCIを行っているが、開発用のビルドと本番のバッチ処理のトリガとして混在して動作している状態であり、これを dev(開発) と prod(本番) でちゃんと分けたい。
- Jenkinsによるビルドで開発コードの静的解析(phpmd)、重複コード検知(phpcpd)、Unitテスト実行(phpunit)を行っており、それが1度実行するだけで、20分程度かかってしまい、開発効率の悪い状態があった。
- 負荷テストが実行できる環境が欲しかったが、現在のJenkinsに入れて、本番のバッチ処理になにか影響があると困るために別で作成することにした。
#■GOAL
- Jenkinsが「開発によるビルドや負荷テスト用(dev)」と「本番のバッチ処理のトリガ用(prod)」として分かれていること。
- dev のJenkinsで、これまでと同様に、ChatOpsでのビルドが実行できること。 $\color{red}{\rm ※十分長いですが、さらに長くなるのでここでは書きません。 }$
- ビルド、テストが並列実行され、ビルド完了までの時間が短縮されていること。
- 負荷テストが実行できる環境が、Jmeter?でも良いので実現されていること。
- CIで実行でき、結果がJenkins上で確認できる状態を目指す
#■どういう手順で行ったか。
##既存のJenkinsの移行
-
最初は既存のJenkinsをThinBackupプラグインを使っての設定状態のコピーで実行したが、うまくいかず断念。
-
せっかくの機会なので、AWS ECS と Dockerを使ってJenkinsを立ち上げた。
- 利用したコンテナは、下記のサイトを参考 $\color{red}{\rm ※実はSeleniumのテストもできるようにしようとしてたけど、できなかった }$
-
既存のJenkinsのプラグインを新Jenkinsへもインストール。いくつかのプラグインがない。。。
- 探してみたが、なさそうだったプラグイン
- Duplicate Code Scanner Plug-in
- PMD's Copy Paste Detector (CPD)
- External Monitor Job Type Plugin
- Jenkins build timeout plugin
- Jenkins Mailer Plugin
- Jenkins SSH Slaves plugin
- Jenkins Subversion Plug-in
- LDAP Plugin
- PAM Authentication plugin
- SSH Credentials Plugin
- 不足していたプラグイン
- DRY Plugin
- 探してみたが、なさそうだったプラグイン
-
インストール後、Gitでのソースpullできるか調査、対応 ※ここで少しはまった・・・
- JenkinsのmasterのDockerfile作って、ビルドして実行してみたら、ソースは取れた。
-
Slaveでのジョブ実行をトライ。
- 下記のプラグイン入れてみて、SlaveをDocker HubやECRから引っ張る設定をして、実行までできた。
しかし、Slave上でソースを引っ張るには、また、、、、鍵がSlaveのコンテナに必要なことが判明。。。orz色々と、Slaveへの鍵の配置をトライしたが、なぜか「ビルドできない」、「配置したと思っても、コンテナ起動しなおすと消えてる」ことが起きたので、STOP。- 色々とみていくと、下記プラグインにてソースをSlaveにコピーして解決できることが判明!!!!
- JenkinsのCopy To Slave Pluginを使ってみる - Narrow Escape
- この方も結局は、Slaveでソース引っ張ってないみたいなのでこの方法を参考にしました!w
- Jenkinsとamazon ecsで コンテナCI
-
とりあえずmasterで実行できるジョブから移管開始。
- awsコマンドの必要なジョブもawsコマンドのインストールさせて、移管できた。
-
Copy To Slave PluginでSlaveへのソースのコピーもできるようになったので、実際にphpが動くコンテナを探してみた。
- JenkinsのSlaveとして動くことが必要なので、Javaが入っていて、jenkins-slaveのエージェントが必要なことが判明
- 下記のコンテナが使えそうだったが、php5-curlが入ってなかったので、独自でDockerfileいじって、コンテナ作成
- wilreichert/jnlp-slave-php
- これ以降は全て、独自でDockerfileを作成
##ビルドの並列実行環境の構築
- ECSプラグインにて、Slaveでのジョブ実行可能になったので、並列実行の仕組みを検討
- 既存のビルドジョブは下記の2つのジョブを実行しており、直列のため、$\color{red}{\rm 23分程度}$かかっていた。
-
build-phpmd-cpd
$\color{red}{\rm 8分弱かかる}$ -
build-phpunit
$\color{red}{\rm 15分かかる}$
-
- さらに直列での実行であるため、開発者Aさんがビルド実行中の場合は、開発者Bさんはそのビルド実行待ちの状態となっていた・・・
- 下記の方法がありそうだったので、実装をトライ
- ジョブの同時実行
- ジョブ自体の複数起動
- ジョブ内でさらに並列実行
- 既存のビルドジョブは下記の2つのジョブを実行しており、直列のため、$\color{red}{\rm 23分程度}$かかっていた。
- ジョブの同時実行方法
- Jenkins2.0系から使える「Pipeline」にて、上記ジョブを同時に実行にて実現
- それぞれのジョブはSlaveのコンテナにて、実行するよう設定
- 実際のGroovy Scriptは下記。
- ジョブ自体の複数起動
- 各ジョブの並列実行のため「ビルドを並行実行」のチェックを有効化
- ビルド自体が各Slaveコンテナで動くように「実行するノードを制限」のチェックを有効化
- $\color{red}{\rm ※注意 インスタンス自体のリソースが足りない場合は待たさるみたいでした}$
- $\color{red}{\rm ECSインスタンスを2つにしてみたら、コンテナが4つ同時に動かせた!!!}$
- ジョブ内でさらに並列実行
-
phing
のbuild.xml
にて、並列実行の書き方があったので、それを利用 - さらに、
phpunit
の並列実行のモジュールparatest
を導入してみて、さらなる短縮を試しました。
-
GroovyScript
// flow.groovyファイル 必ず失敗するパターンの確認用
def builds() {
parallel(phing: {
run 'build-phpmd-cpd'
}, phpunit: {
run 'build-phpunit'
}, fail: {
//run 'test-fail'
})
}
def run(name) {
// pcntを指定プロセス数としてパラメタで指定
build job: name, parameters: [string(name: 'repo', value: repo), string(name: 'branch', value: branch), string(name: 'app_name', value: app_name), string(name: 'env', value: 'development'), string(name: 'pcnt', value: pcnt)]
}
stage 'Build and Unit test'
builds()
return this;
build.xml
<!-- # of threads to execute parallel tasks -->
<property name="threads" value="3"/>
<target name="main" depends= "phpcs,phpmd,phpcpd" ></target>
<target name="parallel_main">
<parallel threadCount="${threads}">
<phingcall target="tasks_to_try" />
</parallel>
</target>
<target name="tasks_to_try">
<phingcall target="phpcs" />
<phingcall target="phpmd" />
<phingcall target="phpcpd" />
</target>
結果
- 同時実行+並列処理導入で、$\color{red}{\rm 15分程度に短縮}$ もう少し行きたかったな・・・
- ビルド自体の並列実行が可能となり、後からビルドしたい開発者が待たされることがなくなった
##負荷テスト環境の構築
- まずは、Jenkinsと
JMeter
周りについて調査し、導入をトライしたが、下記の理由で中止。- Antによる実行がうまくいかない・・・
- 調査してると、古い技術に感じたのもあり、使い方を習得して共有してもあまり組織的にメリットがないと思った
- 最近の負荷テストツールである
Gatling
を下記の理由から導入-
Gatling
のインストール、Jenkinsプラグインの導入まで簡単 - レポートのUIがいい感じ
- 流行りのScala(これから学習しないといけないけど・・・)
- こっちの方がScalaも学べる、使い方の共有によるメリットもありそう
-
JMeter
のようにrecorder
によるブラウザの操作にてテスト計画を作成できる
-
#■はまったところ
- Dockerで起動した新Jenkinsにて、プロジェクトのソースを
git pull
するところ - ECSプラグイン入れて、Slaveでジョブが動くところまで確認後、EC2を再起動して、Public IPが変わってしまったら、Slaveでジョブが動かなくなった・・・
- 原因は、Slave起動時の、
tcpSlaveAgentListener
へのアクセスが再起動前のIPアドレスへ接続していたためでした。。。 キャッシュが残っているのか、システム設定にて、JenkinsのURLを再起動後のURLへ変更してもダメでした。。。最終的に一度、Slave Templatesの設定を一度削除 ⇒ 保存 ⇒ 再度、設定 でなおりました。すげー無駄な時間。。。- ECSプラグインの「高度な設定」のURLの変更をしていなかっただけでしたw
- 原因は、Slave起動時の、
- grunt動かすために、nodejs,npm,nvm入れたりと試行錯誤・・・意外とうまくいかず大変だった・・・でも、nvmっていいですね
- phpmd,phpcs,phpcpdのインストールとそれが起動できるまでが大変。。。phpcpdだけ、PEARじゃなくなってるし・・・orz
- むかついたので、Composerでインストールしてやった。
- ECSで登録したインスタンスタイプをEC2のコンソールから勝手に変えると怒られることが判明
- docker-compose.ymlの記述がversion2になることで意味不明になった。。。
- ecs-cliから、死んでてもいいデータコンテナのあるcomposeでの起動をすると、タスクが起動しないことが判明
- ECSのインスタンスのディスクがfull diskになった。。。
- そのデータ食ってる
/var/lib/docker
を削除することができない。。。 - EBS拡張して、なんとか対応w
- そのデータ食ってる
- fullbokを試してみたが、Cloudformationのテンプレが起動できず、あきらめた。。。orz
#■改善点
- まだ15分程度かかってしまっているので、さらなるビルドの高速化方法の検証
-
paratest
時のプロセスを多く与えてみる?(現在は7~8で実行) - phpmd,phpcs,phpcpdのビルドのジョブ自体もそれぞれ別でジョブを作って実行?
- テストコードのボトルネックをチェックして、改修
-
- ECSにて、JenkinsのDocker起動すると、ビルド履歴や設定の保存場所が起動しているインスタンスに限られるため、厳密には自由にスケールできる状態にない。。。
- EFSを利用してのインスタンス間での設定情報の保存場所の共有を検討中
#■その他参考サイト
- AWS上にDocker環境構築を、EC2に直で入れる方法とECS(マネジメントコンソール上で操作)の導入の2つの方法で入れてみた。 - Qiita
- DockerでJenkinsサーバ(master/slave)を構築してみる - knjnameのブログ
- 開発者(個人)のためのJenkins - Slave編 - Qiita
- Dockerfile リファレンス — Docker-docs-ja 1.12.RC ドキュメント
- Dockerでつくる開発環境【nginxコンテナ編】 | フリップフラップ
- コンテナに入りたい?それ docker exec でできるよ - Qiita
- Jenkinsのジョブの実行環境にAmazon EC2 Container Service ( ECS ) を活用 | HAWSクラウドサービス
- Jenkinsとamazon ecsで コンテナCI
- Dockerを用いたJenkinsの運用をしてみた話 – NET BIZ DIV. TECH BLOG
- Deploying Jenkins Docker Image using Amazon EC2 Container and Registry Services · cloudway.io
- Docker で「速くてウマイ」な CI 環境を構築するための 5 つの Tips - ヌーラボ [Nulab Inc.]
- 動作中のDockerコンテナにファイルをコピーする方法
- https://hub.docker.com/r/wilreichert/jnlp-slave-php/
- Upload a file on amazon S3 with PHP SDK - Stack Overflow
- curl - PHP AWS SDK throwing unknown error - Stack Overflow
- docker-composeでPHP5.4〜PHP7.0まで単体テストする - Qiita
- Jenkinsスレーブサーバ作成 - JunxBoxWiki
- Jenkins+Dockerを使用したCIサーバの作成 - JunxBoxWiki
- Jenkins+DockerでPHPユニットテストサンプル · GitHub
- DockerでJenkinsサーバ(master/slave)を構築してみる - knjnameのブログ
- Hudson+phpcpdで継続的に重複コードをチェックする | Ryuzee.com
- nvmでNodejsの管理をしてみたよ | tkd55 blog
- Amazon Linuxに Node.js と npm を入れる - Qiita
- gruntでファイル変更を監視しながらFuelPHPのテストをPHPUnitで実行する - Qiita
- npmでnode.jsのpackageを管理する - Qiita
- Grunt超入門
- はじめに | Grunt 日本語リファレンス | js STUDIO
- CentOS 7で、できるだけ簡単にPHP用のJenkins環境を作る - Qiita
- phpcs/phpmd/phpcpdをJenkinsで試してみる - yk5656 diary
- Phingをjenkinsと連携させてみる | ミラボ
- コードレビューに便利!PHPの重複コードを見つけてくれるPHPCPD|A Day In The Boy's Life
- https://hub.docker.com/r/keysoftware/php/~/dockerfile/
- https://hub.docker.com/r/iliyan/jenkins-ci-php/~/dockerfile/
- Setting up Jenkins for a PHP projects on CentOS 7 — Medium
- SebastianHanke/Jenkins-build-examples: build examples of java and php projects with ant and gradle
- Usine logicielle - Jenkins pour PHP - Blog erlem.fr
- Template for Jenkins Jobs for PHP Projects: Installation
- WordPressのプラグイン開発。PHPのツールとJenkinsプラグインの用意 | Somof blog site
- AWS Developer Forums: Start ECS agent with ECS_CLUSTER fails ...
- コンテナで簡単(かもしれない)クラウド夜逃げの準備と実践 - ようへいの日々精進XP :
- Jenkins2のPipelineとJenkinsfileで複数ブランチの自動テスト | karakaram-blog
- すべてが╭( ・ㅂ・)و ̑̑ グッ ! になる - Jenkinsビルドパイプライン結果をプルリクエストに表示する · THINKING MEGANE
- Jenkinsを使った自動テスト環境を作る(前編) - さくらのナレッジ
- ブログズミ: [Jenkins] Pipeline Plugin を使ってみた
- [Ubuntu][Shell]Ubuntuの/bin/shでsourseコマンドが効かない件 | aoshiman.org
- fullbokにプルリクしてみました | KiQ
- お手軽JMeterクラスター 〜 フルボッコ編|アドカレ2013 : CFn #1 | Developers.IO
- classmethod-aws/fullbok: GUI JMeter cluster on EC2
- SpotInstanceとJMeterを使って400万req/minの負荷試験を行う | Developers.IO
- Performance testing for DevOps | Load Impact
- Continuous Delivery with Docker and Amazon ECS
- https://hub.docker.com/r/jenkinsci/jnlp-slave/~/dockerfile/
- ParaTest と Docker で PHPUnit を並列実行する試み - Qiita
- Jenkins導入を図るなら読んどかないとヤバい、サイバーエージェント、ミクシィ、クルーズ、楽天、Aimingの活用事例まとめ~第6回テックヒルズレポート | イベントカレンダー+ログ
- Using Amazon EFS to Persist Data from Amazon ECS Containers | AWS Compute Blog
- ECS で Amazon CloudWatch Logs にログ出力が出来るようになったのでチュートリアル - ようへいの日々精進XP
- Jenkins+JMeterを用いた性能試験環境の構築 - サーバサイドWiki - Confluence
- JMeter でシナリオを書く時に HTTP プロキシサーバを使えば少し捗るかもしれないメモ - ようへいの日々精進XP
- JMeter with Jenkins: Performance Testing in CI Environment
- Install Jenkins with Jmeter (Performance Plugin)
- HudsonでJMeterを使った負荷テストを定期的に行う - cynipeと読む
- JMeter ブラウザの動きを記憶させる | MISLEAD
- JMeter httpsリクエストを記憶する | MISLEAD
- Jenkins+Gatlingを用いた性能試験環境の構築 - サーバサイドWiki - Confluence
- 便利すぎる負荷試験テストツールGatlingの使い方~自力ソース編~ - Qiita
- 便利すぎる負荷試験テストツールGatlingの使い方~ブラウザ操作記憶編~ - Qiita
- 便利すぎる負荷試験テストツールGatlingの使い方~戻り値チェック編~ - Qiita
- Scala 初心者が Gatling をぶっ放して負荷テストをやってみました - SHANON Engineer's Blog
#■まとめ
###参考サイトだらけですみません。。。 皆さまの知見があって初めてできたことです。ありがとうございます