[ Gitlab Runner ] CI / CD パイプラインを構築する①(CI編)
はじめに
最近の開発現場ではもはや当たり前のものとして整備されつつある、 CI/CDパイプライン
ですが、現職の開発現場では私が参画した当初の時点では導入されていませんでした。
CI/CDパイプライン
は導入すると様々なメリットがあるので、導入した方が良いと思い、自ら社内の開発現場への導入を進めました。
CI/CDとは
簡単に説明するとアプリケーションのビルド、テスト、デプロイを自動で行い、開発の生産性を向上させるための開発の手法です。
CIのメリット
- Linterを実行することで規定されたルールに則っていないソースコードを事前に検知できる。
- Formatterを実行することで、同一のリポジトリ内においてソースコードのフォーマットを強制的に統一することができる。
- デバッグコードを事前に検知し、本番環境に不要なデバッグコードがリリースされることを未然に防ぐことができる。
- アプリケーションのビルドが問題なく行われるかどうかのチェックを、本番環境へのリリース前に自動で行うことができる。
補足として、最後にあげたメリットに関しては本番環境をコンテナ基盤で運用している場合に教授できるメリットですがCIのメリットとして挙げられる代表的なものの一つとなるかなと思います。
リポジトリ内に本番環境用の Dockerfile
を含めておいて、CIの処理の中で自動でビルドされるようにしておけば、もしビルドが失敗した際、リリース前にアプリケーションに何らかの問題が発生していることを事前に検知できるのかなと思います。
CDのメリット
- 本番環境へのリリース作業において社内の特定の人物しか把握していないというような状況を防ぐことができる。
- リリース作業を自動化することで開発者の作業ミスを防ぐことができる。
- アプリケーションのダウンタイムをゼロにすることができる。
CI/CD導入におけるデメリット
- 既存の開発現場にCI/CDの仕組みが整備されていない場合、チームの開発者にCI/CDの導入を前提とする開発スタイルに馴染ませる必要がある。
- CI/CDパイプラインをメンテナンスする開発者が必要になる。
- CI/CDの仕組みに関して、チームの開発者にキャッチアップを促す必要がある。
使用したCIツールとCIを実行する環境
使用しているCIツールは、gitlabでCIを行う際のデファクトスタンダードとなっている、 Gitlab Runner
を使用しています。
クラウド上にCI専用のサーバー(CentOS7)を構築して、サーバー上に Gitlab Runner
をインストールしてCI環境を整備しています。
※CentOS7に Gitlab Runner
インストールする参考資料
こちらの記事に、gitlab上でCIを行う際の設定について解説されているので参考にしてください。
実際に使用しているCIスクリプト
こちらのリポジトリにもまとめていますので、参考にしてください。
image: php:7.4-fpm
cache:
key: one-key-to-rule-them-all
paths:
- vendor/
- node_modules/
before_script:
- apt-get update
- apt-get install -y git libzip-dev libpng-dev
- docker-php-ext-install zip
- curl --silent --show-error https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
- mkdir ~/.ssh
- chmod 700 ~/.ssh
- echo "$KNOWN_HOSTS" >> ~/.ssh/known_hosts
- echo "$SSH_KEY" >> ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- eval "$(ssh-agent)"
- ssh-add ~/.ssh/id_rsa
- ssh-add -l
- echo "$SSH_CONFIG" >> ~/.ssh/config
- git remote set-url --push origin ssh://git@255.255.255.255:65535/web/sample_project.git
- git remote -v
- git remote update
- git config --global user.name gitlab_runner
- git config --global user.email gitlab.runner@example.com
- if [[ `git branch | grep $CI_COMMIT_REF_NAME` ]]; then
- git branch -D $CI_COMMIT_REF_NAME
- fi
- if [[ ! `git branch | grep $CI_COMMIT_REF_NAME` ]]; then
- git branch $CI_COMMIT_REF_NAME origin/$CI_COMMIT_REF_NAME
- fi
- git branch
stages:
- build
- formatter
formatter:
stage: formatter
script:
- git checkout $CI_COMMIT_REF_NAME
- apt-get install -y lsof nodejs npm
- npm install
- pecl install mongodb
- docker-php-ext-enable mongodb
- git checkout $CI_COMMIT_REF_NAME
- ssh -t -t web_app_db -f -N
- lsof -i
- composer install
- cp .env.example .env
- echo "$ENV_FILE" >> .env
- php artisan key:generate
- vendor/bin/phpunit tests/Feature/api_test.php
- vendor/bin/phpunit tests/Feature/Api
- SYNTAX_CHECK=0
- for FILE in `git diff --name-only origin/master | grep -E '*php'` ; do
- echo $FILE
- ./vendor/bin/php-cs-fixer fix $FILE
- echo 'PHPMDで未使用変数などのチェック'
- if ! ./vendor/bin/phpmd $FILE text ruleset.xml; then
- SYNTAX_CHECK=1
- fi
- done
- IS_ERROR=0
- for FILE in `git diff --name-only origin/master | grep -E '*js'` ; do
- if [ $FILE = '.eslintrc.json' ]; then
- echo $FILE
- echo "skip check"
- continue
- fi
- if [ $FILE = 'composer.json' ]; then
- echo $FILE
- echo "skip check"
- continue
- fi
- if [ $FILE = 'package-lock.json' ]; then
- echo $FILE
- echo "skip check"
- continue
- fi
- if [ $FILE = 'package.json' ]; then
- echo $FILE
- echo "skip check"
- continue
- fi
- if [ $FILE = WebGL_Release.framework.js ]; then
- echo $FILE
- echo "skip check"
- continue
- fi
- if [[ -n `./node_modules/.bin/eslint --fix $FILE` ]]; then
- ./node_modules/.bin/eslint --fix $FILE
- IS_ERROR=1
- fi
- done
- git add -A
- git status
- if [[ -n "`git status --porcelain`" ]];then
- git commit -m '[ci skip]Push by GitLab runner'
- git push origin $CI_COMMIT_REF_NAME
- fi
- IS_ERROR_LOG_REMAIN_ERROR=0
- for FILE in `git diff --name-only origin/master | grep -E '*php'`; do
- echo $FILE
- if [[ -n `git grep error_log -- $FILE` ]]; then
- echo -e "\e[31;43m デバッグコードが残っている可能性があります。'\n'削除してください \e[m"
- IS_ERROR_LOG_REMAIN_ERROR=1
- else
- IS_ERROR_LOG_REMAIN_ERROR=0
- fi
- done
- if [ $SYNTAX_CHECK -eq 0 -a $IS_ERROR_LOG_REMAIN_ERROR -eq 0 ]; then
- exit 0
- else
- echo -e "\e[31;43m 修正を行った上で再度コミットしてください \e[m"
- exit 1
- fi
except:
- master
CI運用時に発生した事象
今まで問題なく動いていたCIが、ある時を境に全プロジェクトで一斉にこけるという事態が発生しました。
その際に発生した問題と、実際に解決を図ったプロセスについて説明を行いたいと思います。
発生した問題①
CIの処理実行時に、以下の処理の部分でCIがこけてしまっていますね。
$ docker-php-ext-install zip
Configuring for:
PHP Api Version: 20190902
Zend Module Api No: 20190902
Zend Extension Api No: 320190902
ls: cannot access '.': Operation not permitted
configure: error: working directory cannot be determined
Cleaning up file based variables
ERROR: Job failed: exit code 1
解決手順
1. ls: cannot access '.': Operation not permitted gitlab でgoogle検索
見出しのキーワードで検索すると、こんな記事が出てきました。
何か、dockerのversionが関係している可能性があるようです。
2. CIサーバー上のDockerのversionを確認する。
サーバーにインストールされているdockerのバージョンを確認してみましょう。
[root@gitlab-runner ~]# docker -v
Docker version 20.10.3, build 48d30b5
なるほど。
サーバーにインストールされているバージョンがわかったところで現在のdocker最新版を確認します。
※docker最新バージョンを確認
3. インストール済みのdockerをremove
既存のdockerを一旦アンインストールします。
yum remove docker-ce docker-selinux docker-engine-selinux
4. docker最新版をインストール
sudo yum install docker-ce -y
4. docker再起動
systemctl start docker
※dockerを最新版にするための参考資料
以上を行った後に、再度CIを走らせると処理が止まらずに無事にCIを実行することができました。
発生した問題➁
CIの処理完了までの時間が長すぎる。
解決手段①
composerとnpmを使用してインストールされるパッケージはyamlファイルの中で以下の設定を行い、CIのjob間でキャッシュを利用できるようにする。
cache:
key: one-key-to-rule-them-all
paths:
- vendor/
- node_modules/
解決手段➁
CIを実行するたびに、諸々のパッケージをインストールしているため、ローカル環境であらかじめ必要なパッケージをインストールしたうえでビルドしたdocker imageをdocker hubにpushしておく。
※dockerの公式ドキュメント
※参考記事
どうなったか?
CIの処理実行時間を短縮するために、これらの対策を行ったところ、CIの処理実行時間を 1/3 程度にまで短縮することができました。
最後に
gitlabでCIを行おうとすると自前でサーバーを用意して、運用も自分たちで行う必要があるため、少し大変な面もありますが、安定して運用が行えるように今後も環境を整備していきたいと思います。
Discussion