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:
-
コンテナ起動後、初期
rootパスワードを取得する(/etc/gitlab/initial_root_passwordに24時間のみ保存される)docker compose exec gitlab cat /etc/gitlab/initial_root_password -
ログイン後、直ちにパスワードを変更する。組織で SSO(SAML/OIDC/LDAP)を使う場合は Admin Area > Settings > General > Sign-in restrictions 等で ID 連携を設定する
Jenkins:
-
初回起動時に生成される初期管理者パスワードを取得する
docker compose exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword -
セットアップウィザードで推奨プラグインに加え、GitLab Plugin / GitLab Branch Source Plugin をインストール
-
最初の管理者ユーザーを作成した後、「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 セットアップは不要でこの節から開始できる。
- プロジェクトを作成(Private を維持。公開する積極的な理由がない限り Public/Internal にしない)
- Settings > Repository > Protected branches で
mainを保護(Allowed to push を Maintainer 以上、または No one に制限) - Settings > Merge requests で Approval rules と「Pipelines must succeed」を有効化
- CI 専用のデプロイキーを発行し、公開鍵をプロジェクトの Settings > Repository > Deploy keys に Write 権限で登録する(個人のSSH鍵を使い回さない)
- Jenkins → GitLab の Webhook 用に Secret Token を生成し控える(Settings > Webhooks で使用)
- 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 側の準備
- Manage Jenkins > Credentials > System > Global credentials に、デプロイキーの秘密鍵を
SSH Username with private key(Username:git)として登録 - GitLab の Project/Group Access Token(
apiscope)をSecret textとして登録(commit status 書き戻し・パッケージアップロード用) - Manage Jenkins > System > GitLab connection で GitLab サーバーの URL と上記 API トークンを紐付ける
- 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のバックアップが構成されている