Laravel5.3をGitLab CIでCIしたり、CDしたり
タグ: Laravel5.3 Laravel PHP7 GitLab CI
前回のGitLab CIのチュートリアル翻訳記事でGitLab組み込みのCIを使えるようになった方も、大勢いるかと思います。
なにせこのサイトは、現在Laravel関連の記事が多いので、Laravelの最新版5.3と絡めてみます。
参照
まずは、https://gitlab.com/でアカウント作ってください。古い情報をお読みの方は、自分でGitLabをホストしないと使えないと思っているかもしれません。実はGitHubと同じように、誰でもコードをホストして置けます。
続いてさっそく、プロジェクトを作ってください。リポジトリのことです。これもGitHubと同様に、新しく作成すると、その後に実行すべきコマンドが表示されます。もともと、GitLabはGitHubクローンとして開発されてきているため、似ているのは当然です。しかし、最近は独自機能をいろいろ追加しており、GitLab CIもその一つです。
ローカルに新しいLaravelプロジェクトを作成し、それを作成したプロジェクトにpushしてください。
単体テスト
Laravel5.3にはPHPUnitのテストができています。Composerのパッケージとして指定されているため、プロジェクトルートでvender/bin/phpunit
により実行できます。
これをGitLabへgit push
するたびに実行してくれるようにしましょう。プロジェクトルートに.gitlab-ci.yml
ファイルを以下の内容で作成してください。
image: php:7.1-alpine # ステージの定義 # ステージはシーケンシャルに実行される # 途中のステージが失敗した場合、以降のステージは中止される stages: - unittest # PHPUnitによるテストビルド phpunit: stage: unittest cache: paths: - vendor before_script: - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer script: - mv .env.example .env - composer install --prefer-dist --no-progress --ansi - php artisan key:generate - vendor/bin/phpunit
GitLabのCIは標準でDockerイメージ上で行われます。Dockerイメージもいろいろ用意されていますので、その中から適当なものを見つけるのが最初の作業となります。
最近は、小さいということでAlpineというLinuxが人気です。実際に、公式やユーザが用意したPHPのイメージを試してみましたが、単純にPHPUnitを実行したいだけであれば、Alpineを使うのがスピード的に有利でした。
PHPの公式Dockerイメージに、Alpine上にPHP7を載せたものが用意されていますので、これを使います。imageで指定しているのがそれです。
その後を簡単に説明すると、Composerをまずインストールし、composer installで必要なパッケージをインストールし、やっと最後にPHPUnitを実行しています。
まずは、上記のように.gitlab-ci.yml
を作成し、変更をコミットし、pushしてみてください。それから、上部ナビのPipelinesリンクから、いろいろ確認してください。リアリティがある方が、内容を理解しやすくなります。
YMLの内容
image項目は、既に説明しました通り、Dockerイメージを指定します。
本来、PHPUnitひとつだけを実行するだけでしたら、もっと単純に書けます。のちほど便利にするために少々手順を追加しています。
ステージはGitLabでは、順番に実行されるジョブグループの名前です。今回はunittestというステージをひとつだけ用意しましたが、便利になるのは2つ以上用意した場合です。
ステージは順番に実行されます。どこかのステージに所属するジョブが失敗すると、それ以降のステージの実行はスキップされます。
複数形のstages
でステージを定義します。各ジョブでは定義したステージをstage
という単数形の項目で指定します。
ステージには複数のジョブを含めることができ、各ジョブは並列に実行されます。つまり、それぞれが独立してDocker上で実行されます。(実行順をコントロールするための指定項目もありますが、複雑になるので基本はシーケンシャルに実行されるステージと、パラレルに実行されるステージに所属するジョブで構成することを考えましょう。)
各ジョブのscript
が本体になります。そこにシェルスクリプトを記述していきます。before_script
はそのジョブの始めに実行されます。まあ、script
にまとめて書いても良いのですが、意味的にscript
は各ジョブで本来行う仕事、before_script
はそのための準備を記述するとわかりやすいでしょう。
今回の例の場合、composerコマンドのインストールだけをbefore...
に入れましたが、phpunit
の実行直前までのコードをこちらに含めてもよいかと思います。
多少、難しいのはcache
です。しかし、今回の例はとても理解しやすですよ。
cache
はその通りキャッシュです。実行中のDockerイメージは「コンテナ」と呼ばれます。PHPのクラスファイルが、実行中はインスタンスになるようなものです。コンテナもPHPのインスタンス同様、そのままでは実行後に消えます。(正確に言えば、Docker CIが消しているわけです。)
実行中に生成したファイルなども消えます。PHPの場合、Composerで必要なパッケージ類を取得する作業は時間がかかります。ですから、インストールしたパッケージが保存されるvendor
下をキャッシュして、次回の実行時に再利用できたら、早く実行できるわけです。
ただし、キャッシュの管理はGitLab CIの管理下に置かれており、できるだけ保存しておくという、ベストエフォート形式です。必ずキャッシュされ、次回実行時に利用できるという性格のものではありません。
さて、いくら自動で、GitLabの環境で行われるから、自分の懐もマシンのリソースも傷まないからと言って、毎回テストされるのはうざったいとか、自分やチームのやり方とそぐわない場合もあるでしょう。
採用している、運用フローに従い、いろいろなブランチ名が使用されます。そのブランチ名を条件として各ジョブの実行をコントロールできます。
unittest
ジョブの部分のみ、変更してみます。
phpunit: stage: unittest only: - staging - /^fix(ed)?(-)?(#)?[0-9]+$/ cache: paths: - vendor before_script: - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer script: - mv .env.example .env - composer install --prefer-dist --no-progress --ansi - php artisan key:generate - vendor/bin/phpunit
only
項目を追加しました。どのブランチに対して、このジョブを実行するのかを指定できます。この場合のstaging
のように、普通の文字列で指定すると、そのブランチ名と一致した場合に実行されます。
また、2つ目の/^fix(ed)?(-)?(#)?[0-9]+$/
のように、Rubyの正規表現で指定すると、一致するブランチ名の場合に実行されます。この場合であれば、"fix10"とか"fixed12"、"fix-20"、"fixed#30"、"fix-#44"のような、いかにも「不具合を修正しました」感のあるブランチの場合は、テストを実行するようになります。
スクリプト記述時の注意点
Dockerのイメージは元になるOSにより、デフォルトのシェルが異なります。大抵はLinuxイメージでしょうが、デフォルトのsh
の正体はBashだったり、Ubuntu系のようにDashだったりします。このAlpineのデフォルトシェルはash(Almquist Shell)らしいです。それぞれ多少の違いがあり、そのためあるページの情報が動かないということもあります。
また、コマンド自身もバージョンやOSの違いをうけ、動作が異なる場合もあります。お手軽なのですが、こうした仮想化やLinuxに関わる、はまりがちなトラップには気をつけてください。
デプロイぽいこと
テストが通ったら、デプロイしたくなります。
デプロイ方法もいろいろありますが、いろいろと勉強になるので、今回はrsyncを使ってみましょう。
さっそく、.gitlab-ci.yml
をご覧ください。
image: php:7.1-alpine stages: - テスト - デプロイ PHPUnit単体テスト: stage: テスト only: - staging - /^fix(ed)?-?#?[0-9]+$/ cache: paths: - vendor before_script: - curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer script: - mv .env.example .env - composer install --prefer-dist --no-progress --ansi - php artisan key:generate - vendor/bin/phpunit rsyncによるデプロイ: stage: デプロイ only: - staging before_script: # Alpineのパッケージインストール定石 - echo "ipv6" >> /etc/modules - apk update - apk add openssh-client rsync script: - eval $(ssh-agent -s) - mkdir -p ~/.ssh - chmod 700 ~/.ssh - echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config' - rsync -av -e "ssh -p 256" ./ myname@sample.com:/somewhere/laravel/project/root/directory
デプロイのサンプルでありますが、いくつも重要なポイントを含んでいます。
Alpineのパッケージ
Alpineが小さいのは、余計なものがないからです。逆に言えば、必要に応じてインストールしなければなりません。
before_script
はAlpineのパッケージ追加の定石コードです。apkがパッケージ操作コマンドで、最後のapk add
がパッケージの追加、つまりインストールです。add
の後に、インストールしたいパッケージ名を指定します。
実は、当初はGitLabのブログに書かれている方法でチェレンジしました。ssh-add
を使用する方法です。プライベートキーの内容をssh-add
に流し込むと、キーチェーンに追加される様なコードになっていましたが、このDockerイメージでは動作しなかったため、.ssh下に設置するベタな方法にしています。
openssh-client
はssh-add
などが入っているパッケージで必要ないのですが、複数のパッケージを同時にインストールできることを示すために残しています。
コマンドなどがどのパッケージに入っているかは、Alpineのパッケージ検索ページのcontent
にコマンド名を指定し、検索します。見つからない場合は、用意されていません。
まずひとつ、Alpineのパッケージのインストール方法を学びました。
日本語の使用
ステージ名とジョブ名を日本語にしていることに、気が付かれていることでしょう。
ご覧の通り、使用できます。ジョブ名は文字列ですので、空白を間に入れ、文章のように指定することもできます。
これが2つ目の学習ポイントです。
機密情報
rsyncのようにSSHを利用するコマンドのために、キーペアを用意する必要があります。公開キーは、相手先サーバーのauthorized_keysに登録しておきます。ここらへんはSSHの設定のため省きます。
SSHを利用するため、プライベートキーを保持する必要があります。もちろんGitで管理するファイルに含めることはできません。(プライベートリポなら可能でしょうが…).gitlab-ci.yml
もGit管理されるファイルです。直接ハードコードはできません。
そこで、プロジェクトごとの設定ページの右側、設定ボタンを押すと表示される項目にある、Valiables
を利用します。これにより指定した値は、シェルの環境変数として取得できます。
今回は、SSH_PRIVATE_KEY
という変数名で、プライベートキーの内容を登録してあります。それをDockerコンテナ中からのSSH接続のためのキーとして利用しています。
もう一つ重要なポイントがあります。使用するキーペアは新しく生成したものを利用しましょう。既存のものは使用しないことです。通常、この手のサイトに登録するのは「公開鍵」の方ですが、今回はプライベートキーを登録します。もしGitLabの従業員の中に、悪意を持った人間がいたら悪用されてしまいます。また、中間者攻撃も可能になります。そうした、リスクがありますので、万が一の場合の影響を最低限で済ませるように、キーペアの流用は避け、専用のキーペアを新しく生成しましょう。
SSH接続ができれば、他のデプロイ方法も簡単です。rsync系のコマンドで転送するのではなく、実動機でcomposerやgitで新しいソースを取得して、デプロイもできます。
その他
ランナーを実働機で走らせる方法などもデプロイで使えそうですが、長くなりますので、今回はこの辺で終わります。
現在、たとえばPHPソースコードをphp-cs-fixerにかけ、その結果をpushするとかを簡単に行う方法がまだありません。要望はあり、機能の実現待ちとなっています。