GitLabCI(3/4) パイプライン変更
パイプライン作成
はじめに
前回からの続きです。前回はパイプラインを作成して実際にアプリのデプロイができるまでを確認しました。
 今回は、前回作成したパイプラインを改善し、より効率的で管理しやすい形にしていきます。
システム構成
| # | 項目 | 内容 | 
|---|---|---|
| 1 | GitLab のバージョン | 16.8.1 | 
| # | 項目 | 内容 | 
|---|---|---|
| 1 | GitLab Runner を構築した OS | Amazon Linux 2023 | 
| 2 | GitLab Runner のバージョン | 16.8.1 | 
| 3 | GitLab Runner のタイプ | shell | 
システム概略図
※GitLab はすでに構築済みのものを使用
- Runner がパイプラインを確認(これは定期的に行われている)
 - GitLabへpushするタイミングでRunnerがExecutorにパイプライン実行を指示
 - Executorがジョブを実行
 

ディレクトリ構成
root
├── app
│   ├── backend
│   │   └── main
│   │       ├── Dockerfile
│   │       ├── app
│   │       │   └── bnbwebapi-0.0.1-SNAPSHOT.jar
│   │       └── db
│   │           ├── Dockerfile
│   │           ├── compose.yaml
│   │           └── data
│   │               └── data.sql
│   └── frontend
│       ├── compose.yaml
│       └── main
│           ├── Dockerfile
│           └── app
│               └── ...
└── bnbwebapi
    └── ...
パイプライン作成
今回は以下のjobを実行します。
| # | 項目 | stage | 内容 | 
|---|---|---|---|
| 1 | jar-build-job | build | mavenを実行しjarファイルを作成 | 
| 2 | image-build-job | build | docker imageをビルド | 
| 3 | deploy-job | deploy | ビルドしたdocker imageをコンテナレジストリにプッシュ | 
| 4 | cleanup-job | cleanup | 不要な Docker オブジェクトを削除 | 
パイプライン編集
ここまででGitLab-Runnerを使ってアプリのデプロイを実施できました。ここからはオプションを使ってパイプラインを編集していきます。
①CIの実行条件を設定する
やりたいこと:
- variablesで定義しているTAGをソースコードPUSH時のタグを取得するようにしたい。
 - タグが追加された時のみCIを実行するようにしたい。
 - jar-build-job → image-build-job → deploy-jobと順番に実行されるようにし、前のジョブが失敗した場合には次のジョブは実行されないようにしたい。
 - cleanup-job はタグが追加された場合に、他のジョブの成功にかかわらず実行するようにしたい。
 
ソースコードPUSH時のタグをCIで取得するには定義済み CI/CD 変数の中から CI_COMMIT_TAG を使います。この変数はPUSH時にタグがある場合はタグが入っており、ない場合は空になります。
⚠ 注意:
 定義済み変数には CI_COMMIT_BRANCH というブランチ名を取得するタグも存在します。当初は、「指定のブランチでタグが追加されたらCIを実行する」という実行条件にしたかったのですが、 CI_COMMIT_TAG と CI_COMMIT_BRANCH は同時に使えない仕様でした。(タグが追加されると CI_COMMIT_BRANCH が空になる)testジョブを作成し、タグの有無でそれぞれ検証した結果がこちらです。
test-job:
  stage: test
  script:
    - echo $CI_COMMIT_BRANCH
    - echo $CI_COMMIT_TAG
タグ無し
 
タグ有り
 
この変数を使って、タグが追加された時のみにCIを実行するために、すべてのジョブで適用される共通ルールを設定します。 .gitlab-ci.yml に以下を追加します。
.default_rules:
  rules:
    # $CI_COMMIT_TAGがnullの時はジョブを実行しない
    - if: '$CI_COMMIT_TAG == null'
      when: never
共通ルールをジョブに適用させるため、各ジョブに以下を追記します。
rules:
    - !reference [.default_rules, rules]
これで、すべてのジョブがタグが追加された時のみ実行するようになりました。次にビルドジョブ、デプロイジョブに順序を付けていきます。
前のジョブが完了してから次のジョブを実行するにはrulesオプションで実行条件を設定します。
rules:
    - !reference [.default_rules, rules]
    - when: on_success
when: on_successを追記することで前のジョブが成功した時のみ実行されるようになります。
 同じステージ内のジョブに順序をつけるには加えてneedsオプションをつかいます。
needs: ["jar-build-job"]
これをimage-build-jobに追記することで、jar-build-job成功時のみジョブが実行されるようになります。
cleanup-jobは他のジョブの成功にかかわらず実行したいのでwhen: alwaysを使います。
rules:
    - !reference [.default_rules, rules]
    - when: always
②共通処理
GitLab Runnerではextendsオプションを使って重複処理を共通化することができます。今回は重複処理はありませんがECRにログインする操作を共通化してみようと思います。
# 共通ジョブ
.login-ecr:
  script:
    - aws ecr get-login-password | docker login --username AWS --password-stdin https://$AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
・・・
image-build-job:
  stage: build
  needs: ["jar-build-job"]
  extends:
    - .login-ecr
  script:
    - cd ./app/frontend
    - docker compose build
    - docker image tag backend $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/backend-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
    - docker image tag backend-db $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/backend-db-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
    - docker image tag frontend $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/frontend-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
  rules:
    - !reference [.default_rules, rules]
    - when: on_success
先頭がドット(.)から始まるジョブは、無効化されそのジョブ自体は処理されなくなりますが、他のジョブのベースとすることができます。extendsで.login-ecrジョブを呼び出すことで他のジョブでECRにログインすることが可能になります。
③ジョブの共通ファイル化
cleanup-jobのように他のCIでも使えるような汎用ジョブはincludeを使って共通ファイルにして呼ぶことができます。
.gitlab-ci.yml
include: - local: .gitlab-ci/test.yml - local: .gitlab-ci/cleanup.yml
cleanup.yml
cleanup-job:
  stage: cleanup
  script:
    - docker system prune -f
    - docker volume prune -f
  rules:
    - !reference [.default_rules, rules]
    - when: always
test.yml
test-job:
  stage: test
  script:
    - echo $CI_COMMIT_BRANCH
    - echo $CI_COMMIT_TAG
  rules:
    - !reference [.default_rules, rules]
    - when: always
これで一通りの修正ができました。修正後の.gitlab-ci.ymlはこちらです。
image: maven:3.9.9-jdk-21
.default_rules:
  rules:
    - if: '$CI_COMMIT_TAG == null'
      when: never
.login-ecr:
  script:
    - aws ecr get-login-password | docker login --username AWS --password-stdin https://$AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
stages:
  - build
  - deploy
  - cleanup
  - test
include:
 - local: .gitlab-ci/test.yml
 - local: .gitlab-ci/cleanup.yml
jar-build-job:
  stage: build
  script:
    - cd ./bnbwebapi
    - mvn clean package -DskipTests=true
    - cp ./target/bnbwebapi-0.0.1-SNAPSHOT.jar ../app/backend/main/app/
  artifacts:
    paths:
      - ./bnbwebapi/target/bnbwebapi-0.0.1-SNAPSHOT.jar
    expire_in: "1 day"
  rules:
    - !reference [.default_rules, rules]
    - when: always
image-build-job:
  stage: build
  needs: ["jar-build-job"]
  extends:
    - .login-ecr
  script:
    - cd ./app/frontend
    - docker compose build
    - docker image tag backend $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/backend-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
    - docker image tag backend-db $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/backend-db-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
    - docker image tag frontend $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/frontend-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
  rules:
    - !reference [.default_rules, rules]
    - when: on_success
deploy-job:
  stage: deploy
  script:
    - docker image push $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/backend-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
    - docker image push $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/backend-db-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
    - docker image push $AWS_ACCOUNT.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/app/frontend-$AWS_IAM_USER_ID:$CI_COMMIT_TAG
  rules:
    - !reference [.default_rules, rules]
    - when: on_success
終わりに
今回の改善によって、パイプラインの柔軟性とメンテナンス性が向上しました。実行条件を適切に設定することで不要なジョブの実行を防ぎ、共通処理や共通ファイルを活用することで、設定の重複を減らし管理しやすくなりました。
 次回は、GitLab Runner の Executor を Docker に変更し、Docker-in-Docker(dind)環境を構築していきます。
※GITLAB is a trademark of GitLab Inc. in the United States and other countries and regions.