👏

[ Gitlab Runner ] CI / CD パイプラインを構築する①(CI編)

2022/03/30に公開

はじめに

最近の開発現場ではもはや当たり前のものとして整備されつつある、 CI/CDパイプライン ですが、現職の開発現場では私が参画した当初の時点では導入されていませんでした。
CI/CDパイプラインは導入すると様々なメリットがあるので、導入した方が良いと思い、自ら社内の開発現場への導入を進めました。

CI/CDとは

簡単に説明するとアプリケーションのビルド、テスト、デプロイを自動で行い、開発の生産性を向上させるための開発の手法です。

CIのメリット

  • Linterを実行することで規定されたルールに則っていないソースコードを事前に検知できる。
  • Formatterを実行することで、同一のリポジトリ内においてソースコードのフォーマットを強制的に統一することができる。
  • デバッグコードを事前に検知し、本番環境に不要なデバッグコードがリリースされることを未然に防ぐことができる。
  • アプリケーションのビルドが問題なく行われるかどうかのチェックを、本番環境へのリリース前に自動で行うことができる。

補足として、最後にあげたメリットに関しては本番環境をコンテナ基盤で運用している場合に教授できるメリットですがCIのメリットとして挙げられる代表的なものの一つとなるかなと思います。
リポジトリ内に本番環境用の Dockerfile を含めておいて、CIの処理の中で自動でビルドされるようにしておけば、もしビルドが失敗した際、リリース前にアプリケーションに何らかの問題が発生していることを事前に検知できるのかなと思います。

CDのメリット

  • 本番環境へのリリース作業において社内の特定の人物しか把握していないというような状況を防ぐことができる。
  • リリース作業を自動化することで開発者の作業ミスを防ぐことができる。
  • アプリケーションのダウンタイムをゼロにすることができる。

CI/CD導入におけるデメリット

  • 既存の開発現場にCI/CDの仕組みが整備されていない場合、チームの開発者にCI/CDの導入を前提とする開発スタイルに馴染ませる必要がある。
  • CI/CDパイプラインをメンテナンスする開発者が必要になる。
  • CI/CDの仕組みに関して、チームの開発者にキャッチアップを促す必要がある。

https://codezine.jp/article/detail/11083

使用したCIツールとCIを実行する環境

使用しているCIツールは、gitlabでCIを行う際のデファクトスタンダードとなっている、 Gitlab Runner を使用しています。
クラウド上にCI専用のサーバー(CentOS7)を構築して、サーバー上に Gitlab Runner をインストールしてCI環境を整備しています。

※CentOS7に Gitlab Runnerインストールする参考資料
こちらの記事に、gitlab上でCIを行う際の設定について解説されているので参考にしてください。

https://qiita.com/GENO/items/5cc40d32c5c8fcd49254

実際に使用しているCIスクリプト

こちらのリポジトリにもまとめていますので、参考にしてください。

https://github.com/takuyanagai0213/gitlab-runner-pipeline-setting

.gitlab-ci.yml
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検索

見出しのキーワードで検索すると、こんな記事が出てきました。
https://webclass.jp/blog/2021/07/01/dockerコンテナ内で-make-bin-sh-operation-not-permitted/

何か、dockerのversionが関係している可能性があるようです。

2. CIサーバー上のDockerのversionを確認する。

サーバーにインストールされているdockerのバージョンを確認してみましょう。

サーバーにインストールされているdockerのversion
[root@gitlab-runner ~]# docker -v
Docker version 20.10.3, build 48d30b5

なるほど。
サーバーにインストールされているバージョンがわかったところで現在のdocker最新版を確認します。

※docker最新バージョンを確認
https://openstandia.jp/oss_info/docker/version/

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を最新版にするための参考資料
https://qiita.com/okcoder/items/e91f1e339c114e0be129

以上を行った後に、再度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の公式ドキュメント

https://docs.docker.jp/engine/userguide/dockerrepos.html

※参考記事

https://qiita.com/kon_yu/items/7c40f4dfbd1cce006ce7

どうなったか?

CIの処理実行時間を短縮するために、これらの対策を行ったところ、CIの処理実行時間を 1/3 程度にまで短縮することができました。

最後に

gitlabでCIを行おうとすると自前でサーバーを用意して、運用も自分たちで行う必要があるため、少し大変な面もありますが、安定して運用が行えるように今後も環境を整備していきたいと思います。

Discussion