更新 )

GitLabでCI/CD GitLab Runner 後編

カバー

[!] この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

前回は、GitLabのCI/CDと連携してパイプラインの実行が可能になるGitLab Runnerの構築方法について紹介しました。
今回は、GitLab Runnerの一般的な利用方法について紹介します。

前回構築した環境の構成では、GitLab Runnerの種類はShared Runnerで、executorはDockerになります。

以下の点について説明します。

GitLab Runnerの利用手順

作業端末でソースファイルを修正してGitLabにgit pushすると、GitLab Runnerが動作し修正したソースファイルに対してアクションを起こすことができます。

流れ

このときGitLabのリポジトリの内容がコンテナ内の/builds/配下に展開されます。
コンテナ起動時の作業ディレクトリは/builds/group01/project01/になります。

コンテナ内

実行させるDockerイメージは、利用者がコンテナ内で行いたいことに合わせて独自のDockerイメージを作成して利用することができます。

プロジェクトの作成

GitLab Runnerを説明するため、runnersというグループにprojectjavaというプロジェクトを作成します。

プロジェクト

今回は簡単なJavaのソースファイルを用いて説明していきます。

Dockerイメージの作成・登録

JavaのソースコードをビルドするためのDockerイメージを作成し、GitLab Container Registryに登録します。

  1. 元となるDockerイメージをdocker pullで取得

    # docker image pull docker.io/openjdk:11
    
  2. Dockerイメージを作成

    • docker image tagで作成する場合
    # docker image tag docker.io/openjdk:11 <GitLabのFQDN>:5050/runners/projectjava/openjdk:11
    
    • docker image buildで作成する場合
    # docker image build -t <GitLabのFQDN>:5050/runners/projectjava/openjdk:11 .
    
  3. GitLab Container Registryに登録

    3.1. docker loginでGitLab Container Registryにログイン

    # docker login <GitLabのFQDN>:5050
    Username: <GitLabのアカウント>
    Password: <GitLabのアカウントのパスワード>
    Login Succeeded
    

    3.2. Dockerイメージをdocker image pushで登録

    # docker image push <GitLabのFQDN>:5050/runners/projectjava/openjdk:11
    

    3.3. プロジェクト画面で左ペインの「Packages & Registries」->「コンテナレジストリ」を選択
    コンテナレジストリ画面でDockerイメージが登録されていることを確認します。

    レジストリ

    3.4. GitLab Container Registryからログアウト

    # docker logout <GitLabのFQDN>:5050
    Removing login credentials for <GitLabのFQDN>:5050
    

ソースファイルと設定ファイルの登録

GitLabのリポジトリに登録したJavaのソースファイルを、GitLab Container Registryに登録したDockerイメージを使用してビルドします。
GitLab Runnerを実行するには、.gitlab-ci.ymlをGitLabのリポジトリに登録する必要があります。
Javaのソースファイルと.gitlab-ci.ymlを作成していきます。

ソースファイルの作成

作業端末の作業用ディレクトリにローカルリポジトリを作成します。

$ git clone https://<GitLabのFQDN>/runners/projectjava.git
Cloning into 'projectjava'...
warning: You appear to have cloned an empty repository.

プロジェクト名でディレクトリが作成されているので移動します。

$ cd projectjava

projectjava配下にsrcディレクトリを作成し、src配下にHello.javaファイルを以下の内容で作成します。

import java.lang.*;

public class Hello {
  public static void main(String[] args) {
    try {
      System.out.println("Hello, world.");
    } catch(Exception e) {}
  }
}

.gitlab-ci.ymlの作成

projectjava配下に.gitlab-ci.ymlファイルを以下の内容で作成します。

# ジョブ名
job01:
  # 利用するDockerイメージを記述
  image: <GitLabのFQDN>:5050/runners/projectjava/openjdk:11
  # 実行したい処理を記述
  script:
  # ビルドしたクラスファイルを格納するディレクトリを作成
   - mkdir -p build
  # Javaのサンプルソースファイルをビルド
   - javac -d build src/Hello.java
  # ビルドしたクラスファイルの格納ディレクトリに移動
   - cd build
  # ビルドしたクラスファイルを実行
   - java Hello
  # Shared Runnerのタグを指定
  tags:
    - <Shared Runnerのタグ>

<Shared Runnerのタグ>は前回のGitLab Runnerの登録で登録した値です。

リポジトリへの登録

Javaのソースファイルと.gitlab-ci.ymlをGitLabのリポジトリに登録します。
.gitlab-ci.ymlがリポジトリに登録されると、パイプラインが自動で実行されるようになります。

$ git add .gitlab-ci.yml src/Hello.java
$ git commit -m "初回登録"
$ git push

リポジトリにJavaのソースファイルと.gitlab-ci.ymlが登録されていることを確認します。

  • プロジェクト画面で左ペインの「プロジェクトの概要」->「詳細」を選択

初回登録

パイプライン実行結果の確認

リポジトリの変更によってパイプラインが実行されたかどうか確認します。

  • プロジェクト画面で左ペインの「CI/CD」->「パイプライン」を選択

パイプライン画面を確認すると、パイプラインのステータスに実行結果が表示されています。

初回パイプライン

パイプラインの詳細を確認します。

  • ステータスの「成功」を選択

job01は.gitlab-ci.ymlで設定したジョブ名です。

初回ジョブ

ジョブの詳細を確認します。

  • ジョブ名「job01」を選択

ジョブの詳細画面では.gitlab-ci.ymlのscript:で記述したコマンドの結果等が確認できます。
script:で記述したコマンドの結果は21-25行目です。コンパイルして実行した結果「Hello, world.」が表示されていることが確認できます。

ジョブ詳細

これで次回以降も、リポジトリを変更すると.gitlab-ci.ymlで構成されたパイプラインが自動で実行されます。

パイプラインの書き方

設定パラメータ

.gitlab-ci.ymlの記述方法について、Javaのソースファイルをビルドするサンプルで説明します。

stages:
  - Stage01
job01:
  stage: Stage01
  image: <GitLabのFQDN>:5050/runners/projectjava/openjdk:11
  script:
   - mkdir -p build
   - javac -d build src/Hello.java
  tags:
    - <Shared Runnerのタグ>
キーワード説明
stagesジョブ内でのstageで記載する任意の文字列を記載
記載しない場合、デフォルトでbuild、test、deployがステージ名として利用可能
job01ジョブ名を任意に記載
stagestagesで記載したステージ名を記載
記載しない場合、デフォルトでtestステージとなる
imageコンテナで使用するDockerイメージを記載
記載しない場合、Shared RunnerのデフォルトのDockerイメージが利用される
scriptコンテナ内で実行するコマンドを記載
配列で複数のコマンドを記述することが可能
tagsGitLab Runnerの登録時に入力したShared Runnerのタグを記載

詳しくはGitLab CI/CD パイプライン設定リファレンスを参照してください。


ステージとジョブ

以下の.gitlab-ci.ymlを実行してジョブとステージの関係をみてみます。
3つのステージを定義して、それぞれのステージに2つ、1つ、3つのジョブを設定しています。

image: <GitLabのFQDN>:5050/runners/projectjava/openjdk:11
stages:
 - Stage01
 - Stage02
 - Stage03
job01:
  stage: Stage01
  script:
   - echo hello
  tags:
    - <Shared Runnerのタグ>
job02:
  stage: Stage01
  script:
   - echo hello
  tags:
    - <Shared Runnerのタグ>
job03:
  stage: Stage02
  script:
   - echo hello
  tags:
    - <Shared Runnerのタグ>
job04:
  stage: Stage03
  script:
   - echo hello
  tags:
    - <Shared Runnerのタグ>
job05:
  stage: Stage03
  script:
   - echo hello
  tags:
    - <Shared Runnerのタグ>
job06:
  stage: Stage03
  script:
   - echo hello
  tags:
    - <Shared Runnerのタグ>

実行したパイプラインは以下の通りです。

ステージとジョブ

列がステージになり、行に各ステージのジョブが配置されます。
ジョブの実行は左のステージから右のステージの順番になり、各ステージのジョブは上から処理されます。
同時実行数が3の場合(※)は、まず「job01」と「job02」が並列に処理され、両方が完了した後に「job03」が実行され、「job03」完了後に「job04」と「job05」と「job06」が並列に処理されます。

※同時実行数はGitLab Runnerサーバの/etc/gitlab-runner/config.tomlファイルのconcurrentの値で変更可能

CI/CDで定義済みの環境変数

.gitlab-ci.ymlで利用できる環境変数の一部を紹介します。

環境変数説明
CI_PROJECT_DIRプロジェクトの起点となるディレクトリパス/builds/runners/projectjava
CI_PIPELINE_ID現在のパイプラインのユニークID38
CI_JOB_ID現在のジョブのユニークID128
CI_REGISTRY_IMAGEプロジェクトのContainer Registryパス<GitLabのFQDN>:5050/runners/projectjava

詳しくはGitLab CI/CD 定義済みの変数を参照してください。

静的解析ツールの利用手順

Javaの静的解析ツール「Checkstyle」「PMD」「SpotBugs」を含むDockerイメージを作成し、利用する方法について説明します。

まず、下記のDockerfileでDockerイメージ<GitLabのFQDN>:5050/runners/projectjava/analysis-tool-java:latestを作成し、GitLab Container Registryに登録します。

Dockerイメージ作成に必要なファイルは以下の公式ページからダウンロードします。

FROM openjdk:11
COPY checkstyle-10.1-all.jar /
COPY google_checks.xml /
COPY pmd-bin-6.44.0.zip /
COPY spotbugs-4.6.0.zip /
RUN mkdir /usr/lib/spotbugs \
 && mkdir /usr/lib/pmd \
 && mkdir /usr/lib/checkstyle \
 && mkdir /usr/lib/checkstyle/checkstyle-10.1 \
 && unzip spotbugs-4.6.0.zip -d /usr/lib/spotbugs/ \
 && unzip pmd-bin-6.44.0.zip -d /usr/lib/pmd/ \
 && mv checkstyle-10.1-all.jar /usr/lib/checkstyle/checkstyle-10.1/ \
 && mv google_checks.xml /usr/lib/checkstyle/checkstyle-10.1/ \
 && chmod +x /usr/lib/spotbugs/spotbugs-4.6.0/bin/spotbugs \
 && chmod +x /usr/lib/pmd/pmd-bin-6.44.0/bin/run.sh \
 && echo "if [ \$? -eq 4 ]; then">> /usr/lib/pmd/pmd-bin-6.44.0/bin/run.sh \
 && echo "  exit 0">> /usr/lib/pmd/pmd-bin-6.44.0/bin/run.sh \
 && echo "fi">> /usr/lib/pmd/pmd-bin-6.44.0/bin/run.sh \
 && rm spotbugs-4.6.0.zip \
 && rm pmd-bin-6.44.0.zip
ENV SPOTBUGS_HOME /usr/lib/spotbugs/spotbugs-4.6.0
ENV PATH $PATH:/usr/lib/spotbugs/spotbugs-4.6.0/bin
ENV PATH $PATH:/usr/lib/pmd/pmd-bin-6.44.0/bin
ENV PATH $PATH:/usr/lib/checkstyle/checkstyle-10.1

コンテナ内で実行したコマンドやスクリプトの返却値が0でない場合、ジョブは異常終了します。
/usr/lib/pmd/pmd-bin-6.44.0/bin/run.shは返却値が4になるため、返却値が0になるように追記しています。

それでは、Javaのソースファイルを各ツールで解析してみます。

Checkstyle

Checkstyleは、ソースコードがコーディング規約に則しているかを確認するツールです。
詳しくはStandard Checksを参照してください。
Checkstyleを利用する場合の.gitlab-ci.ymlの記述は以下の通りです。

job01:
  image: $CI_REGISTRY_IMAGE/analysis-tool-java:latest
  script:
   - cd /usr/lib/checkstyle/checkstyle-10.1
   - java -Duser.language=ja -jar checkstyle-10.1-all.jar -c google_checks.xml -o $CI_PROJECT_DIR/result-checkstyle.txt $CI_PROJECT_DIR/src/
  artifacts:
    paths:
      - result-checkstyle.txt
  tags:
    - <Shared Runnerのタグ>
キーワード説明
job01ジョブ名を任意に記載
image$CI_REGISTRY_IMAGE/analysis-tool-java:latestを記載
scriptCheckstyleを実行するためのコマンドを記載
artifactsジョブ内で作成したデータをGitLabに格納しダウンロードが可能
pathsにはGitLabに格納するファイル、ディレクトリを記載
記載したファイル、ディレクトリはartifacts.zipとして格納される
tagsGitLab Runnerの登録時に入力したShared Runnerのタグを記載

Checkstyle実行のコマンド・ライン・オプションの説明は以下の通りです。詳しくはCommand Line Usageを参照してください。

java -Duser.language=ja -jar checkstyle-10.1-all.jar -c google_checks.xml -o <出力ファイルのパス> <ソースファイルのパス>
オプション説明
-Duser.language=ja解析結果を日本語で表示
-cチェックに使用するコーティング規約のファイルパスを記載
今回はGoogleのJavaコーディング規約でチェックする
-o解析結果を指定されたファイルに出力

パイプラインを実行すると、result-checkstyle.txtには以下の解析結果が出力されます。

監査を開始しています...
[WARN] /builds/runners/projectjava/src/Hello.java:1:17: '.*' 形式のインポートの使用は避けるべきです - java.lang.*。 [AvoidStarImport]
[WARN] /builds/runners/projectjava/src/Hello.java:3:1: Javadoc コメントがありません。 [MissingJavadocType]
[WARN] /builds/runners/projectjava/src/Hello.java:4:3: Javadoc コメントがありません。 [MissingJavadocMethod]
[WARN] /builds/runners/projectjava/src/Hello.java:7:7: WhitespaceAround: 'catch' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3) [WhitespaceAround]
[WARN] /builds/runners/projectjava/src/Hello.java:7:26: WhitespaceAround: '{' is not followed by whitespace. Empty blocks may only be represented as {} when not part of a multi-block statement (4.1.3) [WhitespaceAround]
[WARN] /builds/runners/projectjava/src/Hello.java:7:26: 空の catch ブロックです。 [EmptyCatchBlock]
[WARN] /builds/runners/projectjava/src/Hello.java:7:27: WhitespaceAround: '}' is not preceded with whitespace. [WhitespaceAround]
監査が完了しました。

PMD

PMDは、Javaのソースコードを解析し、未使用の変数、空のcatchブロック、不要なオブジェクトの作成などの潜在的な問題を検知するツールです。
詳しくはPMD 6.44.0を参照してください。
PMDを利用する場合の.gitlab-ci.ymlの記述は以下の通りです。

job01:
  image: $CI_REGISTRY_IMAGE/analysis-tool-java:latest
  script:
   - run.sh pmd -d $CI_PROJECT_DIR/src/ -R rulesets/java/quickstart.xml -f html -r $CI_PROJECT_DIR/result-pmd.html
  artifacts:
    paths:
      - result-pmd.html
  tags:
    - <Shared Runnerのタグ>
キーワード説明
job01ジョブ名を任意に記載
image$CI_REGISTRY_IMAGE/analysis-tool-java:latestを記載
scriptPMDを実行するためのコマンドを記載
artifactsジョブ内で作成したデータをGitLabに格納しダウンロードが可能
pathsにはGitLabに格納するファイル、ディレクトリを記載
記載したファイル、ディレクトリはartifacts.zipとして格納される
tagsGitLab Runnerの登録時に入力したShared Runnerのタグを記載

PMD実行のコマンド・ライン・オプションの説明は以下の通りです。詳しくはPMD CLI referenceを参照してください。

run.sh pmd -d <ソースファイルのパス> -R rulesets/java/quickstart.xml -f html -r <出力ファイルのパス>
オプション説明
-dソースファイルのパスを記載
-Rルールセットファイルを記載
-f解析結果の出力形式を記載
-r解析結果を指定されたファイルに出力

パイプラインを実行すると、result-pmd.htmlには以下の解析結果が出力されます。

PMD report

SpotBugs

SpotBugsは、Javaプログラムの中のエラーとなる可能性の高いソースコード(バグパターン)をみつけるツールです。
詳しくは検知可能なバグの詳細を参照してください。
SpotBugsを利用する場合の.gitlab-ci.ymlの記述は以下の通りです。

stages:
  - stage01
  - stage02
cache:
  paths:
    - build/
job01:
  stage: stage01
  image: $CI_REGISTRY_IMAGE/openjdk:11
  script:
   - mkdir -p build
   - javac -d build src/Hello.java
  tags:
    - <Shared Runnerのタグ>
job02:
  stage: stage02
  image: $CI_REGISTRY_IMAGE/analysis-tool-java:latest
  script:
   - spotbugs -textui -html:fancy-hist.xsl -output $CI_PROJECT_DIR/result-spotbugs.html -sourcepath $CI_PROJECT_DIR/src/ -longBugCodes $CI_PROJECT_DIR/build/
  artifacts:
    paths:
      - result-spotbugs.html
  tags:
    - <Shared Runnerのタグ>

SpotBugsはバイナリコードを解析しますので、クラスファイルが対象となります。
ビルドを行いクラスファイルが生成されるのを待ってから、静的解析ツールを実行したいため、ステージを指定してジョブを構成しています。

キーワード説明
stagesジョブ内でのstageで記載する任意の文字列を記載
cacheジョブ間でファイル・ディレクトリを共有するための記載
paths今回はbuildディレクトリを共有するためbuild/を記載
job01で作成したbuildディレクトリがjob02でアクセス可能になる
job01ジョブ名を任意に記載
stagestagesで記載したステージ名を記載
image$CI_REGISTRY_IMAGE/openjdk:11を記載
scriptJavaのソースコートをビルドするためのコマンドを記載
tagsGitLab Runnerの登録時に入力したShared Runnerのタグを記載
job02ジョブ名を任意に記載
stagestagesで記載したステージ名を記載
image$CI_REGISTRY_IMAGE/analysis-tool-java:latestを記載
scriptSpotBugsを実行するためのコマンドを記載
artifactsジョブ内で作成したデータをGitLabに格納しダウンロードが可能
pathsにはGitLabに格納するファイル、ディレクトリを記載
記載したファイル、ディレクトリはartifacts.zipとして格納される
tagsGitLab Runnerの登録時に入力したShared Runnerのタグを記載

SpotBugs実行のコマンド・ライン・オプションの説明は以下の通りです。詳しくはSpotBugsの実行を参照してください。

spotbugs -textui -html:fancy-hist.xsl -output <出力ファイルのパス> -sourcepath <ソースファイルのパス> -longBugCodes <クラスファイルのパス>
オプション説明
-textuiコマンドラインユーザインタフェースの実行
-html:fancy-hist.xsl解析結果をHTMLとして出力
スタイルシートとして以下が指定できる
plain.xsl/fancy.xsl/fancy-hist.xsl
-output解析結果を指定されたファイルに出力
-sourcepath解析対象クラスのソースファイルが保管されているパスを記載
-longBugCodesすべてのバグを報告

パイプラインを実行すると、result-spotbugs.htmlには以下の解析結果が出力されます。

SpotBugs Stats

おわりに

GitLab Runnerの利用について、Javaのソースファイルをビルドする、静的解析ツールを実行するという方法を紹介しましたが、いかがだったでしょうか。
皆さんには、単体テストを自動実行させたり、開発環境へデプロイを行ったりなど、この記事では触れなかったものも踏まえて、よりよいパイプラインの構成を検討して頂ければと思います。
この記事が皆さんのCI/CD導入のお役に立てば幸いです。


TOP
アルファロゴ 株式会社アルファシステムズは、ITサービス事業を展開しています。このブログでは、技術的な取り組みを紹介しています。X(旧Twitter)で更新通知をしています。