メインコンテンツまでスキップ

GitLab + Jenkins

GitLab + Jenkins によるパイプライン構成の本番運用ガイドラインです。GitHub Actions 版(GitHub Actions)と対になる内容で、CI/CDの考え方(ブランチ運用・品質ゲート・成果物配布)は共通です。

推奨 DevOps アーキテクチャ​

ブランチ運用​

  • main: リリース可能状態。すべての変更は Merge Request 経由で main に取り込む

このガイドでは、CI は main 専用で運用します。

パイプライン分割​

  • CI(Merge Request / push)
    • 依存解決
    • 検証(XML/フォーマット等)
    • ビルド
    • 成果物保存
  • Release(タグ)
    • CI と同等の再ビルド
    • zip の GitLab Release 添付

品質ゲート​

  • Settings > Merge requests > Merge checks で「Pipelines must succeed」を有効化し、Jenkins のビルド結果が成功していない Merge Request をマージ不可にする
  • Jenkins の GitLab Plugin で commit status を GitLab へ書き戻し、Jenkins のパイプライン結果を Merge Request 上のステータスチェックとして表示させる(Commit Status API 経由で登録した外部ステータスが「Pipelines must succeed」の判定対象になることを、導入時に使用する GitLab のバージョン/エディションで実機確認すること)
  • Protected branches で main への直接 push を禁止し、Merge Request 経由のみ許可する
  • Approval rules(最低1名の承認)を設定し、可能なら Code Owners 機能を併用する

事前準備​

ローカル開発環境​

  • Bun 1.3.14 以上
  • JDK 17 推奨
  • Maven 3.9.14 以上
  • Git / VSCode
  • bun install 実行後に生成される bun.lock をリポジトリにコミットしておくこと(CI は bun install --frozen-lockfile を使うため、lockfile が無い/最新でないと失敗する)

GitLab + Jenkins 基盤のセットアップ(docker-compose)​

小〜中規模チーム向けに、GitLab CE と Jenkins を1つの docker-compose で構築する手順です。複数ノード・外部データストア・HA構成が必要な規模の場合は GitLab 公式の Reference Architecture に従ってください。

配置先: 本番ホスト上の任意ディレクトリ(例: /opt/gitlab-jenkins/)

services:
gitlab:
image: gitlab/gitlab-ce:latest
container_name: gitlab
hostname: gitlab.example.com
restart: unless-stopped
shm_size: '256m'
environment:
GITLAB_OMNIBUS_CONFIG: |
external_url 'https://gitlab.example.com'
gitlab_rails['gitlab_shell_ssh_port'] = 2224
letsencrypt['enable'] = true
letsencrypt['contact_emails'] = ['ops@example.com']
ports:
- "443:443"
- "80:80"
- "2224:22"
volumes:
- gitlab_config:/etc/gitlab
- gitlab_logs:/var/log/gitlab
- gitlab_data:/var/opt/gitlab
deploy:
resources:
limits:
memory: 8g

jenkins:
build:
context: ./jenkins
container_name: jenkins
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
volumes:
- jenkins_home:/var/jenkins_home
deploy:
resources:
limits:
memory: 4g

volumes:
gitlab_config:
gitlab_logs:
gitlab_data:
jenkins_home:

本番向け設定のポイント:

  • external_url は実ドメインの https:// を指定する。GitLab Omnibus 内蔵の letsencrypt 設定で証明書を自動取得するか、外部のリバースプロキシ/ロードバランサでTLS終端する場合は nginx['listen_port'] 等の omnibus 設定を別途調整する
  • Jenkins はセットアップウィザードをスキップしない。JAVA_OPTS に -Djenkins.install.runSetupWizard=false を指定して admin/admin を自動作成するような init スクリプトは評価専用であり、本番では使用しない(後述の「Jenkins 環境構築」で SSO 連携に置き換える)
  • Jenkins のポートはホストの全インターフェースへ公開せず 127.0.0.1:8080:8080 に限定し、リバースプロキシ経由でのみ外部公開する
  • リモート Agent(inbound TCP経由)を追加しない限り 50000 (JNLP) ポートは公開不要。Docker/Kubernetes Plugin でリモート Agent を追加する段階で、必要な範囲に限定して開放する
  • メモリ上限は想定ユーザー数・ビルド並列度に応じて引き上げる(目安として GitLab 8GB以上、Jenkins 4GB以上)
  • 本構成は単一ノードであり、可用性はホスト1台に依存する。定期バックアップ(後述)と合わせて、許容できるダウンタイム要件かを事前に確認すること
cd /opt/gitlab-jenkins
docker compose build jenkins # カスタムJenkinsイメージのビルド(初回・Dockerfile変更時)
docker compose up -d # 起動
docker compose stop # 停止(データは保持)

Jenkins にはビルドツールが必要なため、jenkins/Dockerfile でベースイメージに Maven/Bun を追加します。

FROM jenkins/jenkins:lts-jdk17

USER root

RUN apt-get update \
&& apt-get install -y --no-install-recommends maven \
&& rm -rf /var/lib/apt/lists/*

ENV BUN_INSTALL=/usr/local/bun
RUN curl -fsSL https://bun.sh/install | bash -s "bun-v1.3.14" \
&& ln -s /usr/local/bun/bin/bun /usr/local/bin/bun
ENV PATH="${BUN_INSTALL}/bin:${PATH}"

USER jenkins
  • ベースイメージのタグは lts-jdk17 のような移動タグではなく、既知の具体的なバージョン(例: jenkins/jenkins:2.492.1-lts-jdk17)にピン留めし、意図しない自動更新を避ける
  • Maven は apt-get install maven で導入(特定バージョン固定が必要な場合は tarball 展開方式に変更する)
  • Bun は公式インストールスクリプトを使用し、バージョンを引数で固定(docker-compose.yml 側のツールバージョンと揃える)
  • USER root で apt-get/curl を実行し、最後に USER jenkins へ戻す(rootのまま起動しない)

初回セットアップ​

GitLab:

  1. コンテナ起動後、初期 root パスワードを取得する(/etc/gitlab/initial_root_password に24時間のみ保存される)

    docker compose exec gitlab cat /etc/gitlab/initial_root_password
  2. ログイン後、直ちにパスワードを変更する。組織で SSO(SAML/OIDC/LDAP)を使う場合は Admin Area > Settings > General > Sign-in restrictions 等で ID 連携を設定する

Jenkins:

  1. 初回起動時に生成される初期管理者パスワードを取得する

    docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword
  2. セットアップウィザードで推奨プラグインに加え、GitLab Plugin / GitLab Branch Source Plugin をインストール

  3. 最初の管理者ユーザーを作成した後、「Jenkins 環境構築」節の手順で組織の SSO と連携するセキュリティレルムに切り替える

バックアップ​

  • GitLab: docker compose exec gitlab gitlab-backup create を定期実行し、生成物を外部ストレージへ退避する。/etc/gitlab/gitlab-secrets.json と /etc/gitlab/gitlab.rb はバックアップの対象外のため別途保管する
  • Jenkins: jenkins_home ボリューム全体(ジョブ設定・Credentials・ビルド履歴)を定期バックアップする

GitLab リポジトリ設定​

以下はリポジトリ側の設定。GitLab.com(SaaS)を利用する場合は、上記の docker-compose セットアップは不要でこの節から開始できる。

  1. プロジェクトを作成(Private を維持。公開する積極的な理由がない限り Public/Internal にしない)
  2. Settings > Repository > Protected branches で main を保護(Allowed to push を Maintainer 以上、または No one に制限)
  3. Settings > Merge requests で Approval rules と「Pipelines must succeed」を有効化
  4. CI 専用のデプロイキーを発行し、公開鍵をプロジェクトの Settings > Repository > Deploy keys に Write 権限で登録する(個人のSSH鍵を使い回さない)
  5. Jenkins → GitLab の Webhook 用に Secret Token を生成し控える(Settings > Webhooks で使用)
  6. GitLab は SSRF 対策でプライベートIP/ローカルネットワーク宛の Webhook をデフォルトでブロックする。Jenkins が社内ネットワークの正規ホスト名で到達可能であれば通常は該当しないが、該当する場合のみ Admin Area > Settings > Network > Outbound requests で必要最小限の範囲を許可する

Jenkins 環境構築​

  • 認証: セットアップウィザードを省略した固定 admin/admin アカウントは評価専用であり、本番では絶対に使用しない。組織の LDAP/SAML/OIDC と連携するセキュリティレルムを設定し、matrix-based security(または Role-based Authorization Strategy)で最小権限を割り当てる
  • Credentials: SSH秘密鍵や API トークンは Manage Jenkins > Credentials に登録し、可能なら Credentials Provider 経由で外部シークレットマネージャ(HashiCorp Vault Plugin 等)と連携する。Jenkinsfile や SCM にトークン・鍵をハードコードしない
  • エージェント: コントローラー上で直接ビルドを実行せず、Docker Plugin / Kubernetes Plugin 等でビルド専用の Agent に分離する
  • プラグイン: GitLab Plugin、GitLab Branch Source Plugin、Pipeline: Multibranch、Credentials Binding を導入し、定期的なプラグイン更新ポリシーを定める
  • バックアップ: JENKINS_HOME(ジョブ設定・Credentials・ビルド履歴を含む)を定期バックアップする
  • 公開方法: Jenkins はリバースプロキシ(nginx等)で TLS 終端した HTTPS 経由で公開する

具体手順​

この手順で作成する Jenkinsfile は、ドキュメント管理用リポジトリではなく、package.json / pom.xml / module.xml を持つモジュールプロジェクトのルート直下に配置します。

手順1: GitLab 側の準備​

上記「GitLab リポジトリ設定」の1〜5を実施済みであること。

手順2: Jenkins 側の準備​

  1. Manage Jenkins > Credentials > System > Global credentials に、デプロイキーの秘密鍵を SSH Username with private key(Username: git)として登録
  2. GitLab の Project/Group Access Token(api scope)を Secret text として登録(commit status 書き戻し・パッケージアップロード用)
  3. Manage Jenkins > System > GitLab connection で GitLab サーバーの URL と上記 API トークンを紐付ける
  4. Multibranch Pipeline ジョブを作成し、Branch Source に GitLab Project を指定。Webhook の Secret Token を設定し、ブランチ・Merge Request・タグを自動検出させる

手順3: Jenkinsfile​

作成先: Jenkinsfile(モジュールプロジェクトルート直下)

pipeline {
agent any

options {
timeout(time: 20, unit: 'MINUTES')
}

environment {
// GitLab CI(.gitlab-ci.yml)と異なり、Jenkins には CI_API_V4_URL / CI_PROJECT_ID は
// 自動注入されない。プロジェクトごとの実値をここで明示する。
GITLAB_API_URL = 'https://gitlab.example.com/api/v4'
GITLAB_PROJECT_ID = '123'
}

stages {
stage('Checkout') {
steps { checkout scm }
}

stage('Install') {
steps { sh 'bun install --frozen-lockfile' }
}

stage('Validate') {
steps { sh 'bun run validate' }
}

stage('Build') {
steps { sh 'bun run build' }
}

stage('Archive') {
steps { archiveArtifacts artifacts: 'target/*.zip', fingerprint: true }
}

stage('Publish latest (main only)') {
when { branch 'main' }
steps {
withCredentials([string(credentialsId: 'gitlab-api-token', variable: 'GITLAB_API_TOKEN')]) {
sh '''
curl --fail --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \
--upload-file target/*.zip \
"${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/packages/generic/module/latest/module.zip"
'''
}
}
}
}

post {
always {
updateGitlabCommitStatus name: 'jenkins', state: currentBuild.currentResult == 'SUCCESS' ? 'success' : 'failed'
}
}
}

ポイント:

  • bun run build は内部で mvn -s settings.xml clean package を実行
  • 成果物 target/*.zip を必ずアーカイブして追跡性を確保
  • Merge Request 作成・更新時に Jenkins が自動でビルドを実行し、updateGitlabCommitStatus で結果を GitLab 側の Merge Request に反映する
  • main への push 成功時のみ GitLab Generic Package Registry の latest パッケージへ成果物をアップロードし、最終成功ビルドの固定配布先として利用する
  • 通常ビルドと配布用アップロードを同一ステージに混在させず when { branch 'main' } で分離し、Merge Request のビルドではアップロードが走らないようにする
  • 同一ブランチへの連続 push で古いビルドが残らないよう、Multibranch Pipeline のジョブ設定で「Discard old builds」と "abort previous builds" 相当のオプション(Build Discarder / Disable Concurrent Builds)を有効化する
  • agent any はコントローラー(docker-compose の jenkins コンテナ)上で直接実行する設定。ビルド量が増えて Agent を分離する場合は、Docker Plugin / Kubernetes Plugin 等で追加した Agent にラベル(例: build)を付与し、agent { label 'build' } に変更する

手順4: タグリリース​

Multibranch Pipeline はタグも自動検出するため、同じ Jenkinsfile にタグ用ステージを追加します。

stage('Create GitLab Release') {
when { tag 'v*' }
steps {
withCredentials([string(credentialsId: 'gitlab-api-token', variable: 'GITLAB_API_TOKEN')]) {
sh '''
curl --fail --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \
--upload-file target/*.zip \
"${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/packages/generic/module/${TAG_NAME}/module.zip"

curl --fail --header "PRIVATE-TOKEN: ${GITLAB_API_TOKEN}" \
--data "name=${TAG_NAME}" \
--data "tag_name=${TAG_NAME}" \
--data "assets[links][][name]=module.zip" \
--data "assets[links][][url]=${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/packages/generic/module/${TAG_NAME}/module.zip" \
"${GITLAB_API_URL}/projects/${GITLAB_PROJECT_ID}/releases"
'''
}
}
}

運用:

  • v0.1.0 のようなタグを push すると、同じ Jenkinsfile の Validate → Build を再実行したうえでリリースを作成する
  • タグ固有の成果物を Generic Package Registry に保存し、GitLab Release の asset link として紐付ける(Release 自体が成果物の保管場所を兼ねる)

手順5: 最終成功ビルドの配布先​

  • GitLab Generic Package Registry の latest パッケージ(packages/generic/module/latest/module.zip)を、Jenkins の lastSuccessfulBuild 相当の固定配布先として利用する
  • main への push 成功後に自動更新されるため、利用者は毎回同じ URL から取得できる

手順6: Merge Request テンプレート(任意)​

.gitlab/merge_request_templates/Default.md に、最低限以下を含めるとレビュー品質が安定します。

  • 変更したモジュールID/バージョン
  • module.xml 影響有無
  • 依存変更(pom.xml)
  • bun run validate / bun run build 実行結果

手順7: 依存関係ポリシーを明文化​

pom.xml 運用ルール:

  • jp.co.intra_mart:* は原則 <version> を書かない(BOM 管理を優先)
  • parent の suffix(例 2026-spring)を組織標準で固定
  • バージョン更新は Merge Request で一括実施し、パイプライン成功を必須化

最小導入チェックリスト​

  • Protected branch(main)が設定されている
  • Merge request approval rule と「Pipelines must succeed」が有効
  • Jenkins の Multibranch Pipeline が GitLab Branch Source 経由でブランチ/Merge Request/タグを自動検出している
  • Webhook が Secret Token で保護されている
  • commit status が Merge Request に反映される
  • target/*.zip が Jenkins のビルドアーティファクトとして取得できる
  • push to main 後、Generic Package Registry の latest パッケージが更新される
  • タグ push で GitLab Release に zip が添付される
  • Jenkins の認証が組織の SSO と連携済みで、固定 admin/admin アカウントを使用していない
  • JENKINS_HOME のバックアップが構成されている