こんにちは、テクマトリックスの米田です。
最近、Docker Composeを使ってJenkinsとGitLabを利用したCI環境を構築する機会がありました。これまでDocker Composeを利用してCI環境を構築した経験はありませんでしたが、簡単に構築できて便利だと感じたので、本記事でその手法をまとめます。

Docker Composeとは

Docker Composeは、複数のDockerコンテナを簡単に管理・運用するためのツールです。Dockerfileでは1つのコンテナの設定を記述するのに対して、Composeファイル(docker-compose.yml)を使うと、複数のコンテナ設定をまとめて管理でき、少ないコマンド数で複数のコンテナの一括起動・停止が可能になります。今回構築するようなJenkinsやGitLabなど、複数のサービスが連携する環境の構築に便利です。設定の共有やバージョン管理も楽になり、開発者にとって非常に便利なツールです。

構築後のCI環境全体像

今回構築するCI環境では、修正したソースコードなどをGitLabのリポジトリにコミットすると、WebhookによりJenkinsのパイプラインが自動で実行されます。

構築後のCI環境全体像

構築手順

前提

DockerDocker ComposeがWindows10 Proに既にインストールされている状態を前提とします。今回使用した各ツールのバージョンは以下の通りです。

Docker27.4.0
Docker Composev2.28.1-desktop.1
DockerホストWindows10 Pro
各ツールのバージョン

1.docker-compose.ymlファイルを作成

JenkinsとGitLabを構築するためのdocker-compose.ymlファイルを作成します。ポート番号や環境変数などの細かい設定は、Jenkinsの公式ドキュメントGitLabの公式ドキュメントを参照して適宜変更してください。以下に基本的な構成の例を示しますが、実際の環境に合わせてカスタマイズすることをお勧めします。

services:  

  gitlab:
    image: 'gitlab/gitlab-ce:latest'
    container_name: gitlab
    hostname: 'gitlab'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://gitlab:13000'
        gitlab_rails['time_zone'] = 'Asia/Tokyo'
        gitlab_rails['smtp_enable'] = true
        gitlab_rails['smtp_address'] = "smtp.gmail.com"
        gitlab_rails['smtp_port'] = 587
        gitlab_rails['smtp_domain'] = "smtp.gmail.com"
        gitlab_rails['smtp_authentication'] = "login"
        gitlab_rails['smtp_enable_starttls_auto'] = true
        gitlab_rails['smtp_tls'] = false
        gitlab_rails['smtp_openssl_verify_mode'] = 'peer'
        gitlab_rails['smtp_user_name'] = "example@example.com"
        gitlab_rails['smtp_password'] = ""
    ports:
    - '13000:13000'
    - '2022:22'
    volumes:
    - gitlab-config:/etc/gitlab
    - gitlab-logs:/var/log/gitlab
    - gitlab-data:/var/opt/gitlab
    networks:
      gitlab-jenkins:
        ipv4_address: 172.23.0.2

  jenkins:
    image: jenkins/jenkins:lts
    container_name: jenkins
    user: jenkins
    ports:
      - "8090:8080"
      - "50000:50000"
    volumes:
      - jenkins-home:/var/jenkins_home
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      gitlab-jenkins:
        ipv4_address: 172.23.0.3

  jenkins-agent:
    image: jenkins/inbound-agent:latest
    container_name: jenkins-agent
    environment:
      JENKINS_URL: http://172.23.0.3:8080
      JENKINS_AGENT_NAME: "agent-1"
      JENKINS_AGENT_WORKDIR: /home/jenkins/agent
      JENKINS_SECRET: "★★"  # 手順後半で追記します。
    volumes:
      - agent-home:/home/jenkins/agent
    networks:
      gitlab-jenkins:
        ipv4_address: 172.23.0.4

volumes:
  gitlab-config:
  gitlab-logs:
  gitlab-data:
  jenkins-home:
  agent-home:
  
networks:
  gitlab-jenkins:
    name: gitlab-jenkins
    driver: bridge
    ipam:
      config:
        - subnet: 172.23.0.0/24

2.コンテナの起動

手順1で作成したdocker-compose.ymlを配置しているディレクトリで以下のコマンドを実行し、コンテナを起動します。

$ docker compose up -d

ネットワーク構成は以下のようになります。

コンテナのネットワーク構成

3.JenkinsとGitLabの初期セットアップ

3‐1.GitLab

3-1-1.ブラウザでhttp://localhost:<ポート番号>へアクセスします(今回はhttp://localhost:13000にアクセスします)。

3-1-2.ログイン画面が表示されます。初回ログインではrootユーザーでのログインが必要なので、rootユーザーの初期パスワードを入力してログインします。初期パスワードは/etc/gitlab/initial_root_passwordに格納されています。以下のコマンドでファイルの内容を確認してログイン画面に入力し、ログインします。

$ docker container exec gitlab cat /etc/gitlab/initial_root_password

3‐2.Jenkins

3-2-1.ブラウザでhttp://localhost:<ポート番号>へアクセスします(今回はhttp://localhost:8090にアクセスします)。

3-2-2.docker-compose.ymlを配置しているDockerホストのディレクトリで以下のコマンドを実行し、Jenkinsの初期ロックを解除するためのパスワードを取得した後、ブラウザに入力して初期ロックを解除します。パスワードは/var/jenkins_home/secrets/initialAdminPasswordにあります。

$ docker container exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

3-2-3.プラグインのインストール画面に移ります。推奨プラグインをインストールする”Install suggested plugins”を選択します。

3-2-4.管理者ユーザーの登録画面、JenkinsのURLを入力する画面に移りますが、どちらも適宜入力して、Jenkinsの初期セットアップを完了します。

Jenkinsの初期セットアップ

4.JenkinsとGitLabの連携

4‐1.GitLab

4-1-1.GitLabのネットワーク接続を修正します。GitLabはデフォルトでは、Webhookをはじめとするローカルネットワークからのリクエストが許可されていないため、この設定を変更する必要があります。
GitLabの左サイドメニュー下部にある「Admin」をクリックし、「Settings」>「Network」>「Outbound requests」から「Allow requests to the local network from webhooks and integrations」にチェックを入れ、設定を保存します。

GitLabのネットワーク接続の設定変更

4-1-2.「左上のユーザーアイコン」>「Preferences」>「Access tokens」から「Add new token」を押下します。名前や有効期限を指定し、スコープとして”api”を選択した後、パーソナルアクセストークンを生成し、Jenkinsで利用するためにコピーしておきます。

パーソナルアクセストークンの作成

4‐2.Jenkins

4-2-1.「Jenkinsの管理」>「プラグインの管理」> 「Available plugins」から「GitLab Branch Source」プラグインをインストールします。

インストールする「GitLab Branch Source」プラグイン

4-2-2.GitLabで作成したパーソナルアクセストークンを、認証情報としてJenkinsに登録します。「Jenkinsの管理」>「Credentials」から「Add credentials」を選択し、以下のように値を設定し「Create」を押下して認証情報を登録します。
・種類:GitLab Personal Access Token
・スコープ:グローバル
・Token:GitLabで作成したパーソナルアクセストークン
・ID:適宜入力してください。
・説明:適宜入力してください。

Jenkinsに登録する認証情報

4-2-3.「Jenkinsの管理」>「System」にアクセスし、GitLab Servers欄にGitLabとの連携に必要な情報を入力します。今回は以下の項目に対して入力します。入力後は「Test connection」を押下して、「Credentials verified for user root」と表示され、JenkinsからGitLabへ疎通ができているか確認します。
・Display Name:defaultのまま
・Server URL:http://172.23.0.2:13000
・Credentials:手順4‐2‐2で登録した認証情報
・Web Hook:「Manage Web Hooks」にチェックを入れる
・Root URL for hooks:http://172.23.0.3:8080

Jenkinsの設定画面とGitLabへの疎通確認

5.JenkinsのジョブとGitLabのリポジトリの連携

5‐1.GitLab

5-1-1.Jenkinsと連携させたいリポジトリを作成します。

5-1-2.作成したリポジトリをクローンして、パイプラインで実行したい内容を記述したJenkinsfileなどを格納します。格納したファイルなどをコミットし、作成したリポジトリにプッシュします。

$ git clone http://localhost:13000/root/<リポジトリ名>.git
$ git add .
$ git commit -m "<コミットメッセージ>"
$ git push
リポジトリの作成

5‐2.Jenkins

5-2-1.JenkinsからGitLabへアクセスするための認証情報を登録します。今回はGitLabログイン時に入力するユーザー名とパスワードを登録します。「Jenkinsの管理」>「Credentials」から「Add credentials」を選択し、以下のように値を設定し「Create」を押下して認証情報を登録します。
・種類:ユーザー名とパスワード
・スコープ:グローバル
・ユーザー名:GitLabログイン時のユーザー名(今回はroot)
・パスワード:GitLabログイン時のパスワード
・ID:適宜入力してください。
・説明:適宜入力してください。

認証情報の追加

5-2-2.実行したいパイプラインジョブを作成します。「新規ジョブ作成」からジョブ名を入力し、「Multibranch Pipeline」を選択します。

5-2-3.ジョブ設定画面の「Branch Sources」欄で「Add source」>「GitLab Project」を選択し、以下のように値を入力した後、ページ下部の「保存」をクリックします。
・Server:default(http://172.23.0.2:13000) (自動で設定されます)
・Checkout Credentials:Jenkinsに登録したGitLabにアクセスするための認証情報
・Owner:連携したリポジトリの所有者(今回はroot)
・Projects:連携したいリポジトリを選択(Ownerを入力すると自動で候補が表示されます)

ジョブの設定

5-2-4.「Scan GitLab Project Log」が表示され、GitLabのリポジトリに保存されたJenkinsfileが認識されていることを確認します(コンソール出力下部に「Finished: SUCCESS」と表示されます)。

Scan GitLab Project Logのコンソール出力

5-2-5.GitLabの連携したリポジトリの「Settings」>「Webhooks」にJenkinsへのWebhookが自動で設定されていることを確認します(JenkinsでインストールしたGitLab Branch Sourcesプラグインにより自動で設定されます)。右側の「Tests」から「Push events」を選択し、POSTリクエストが問題なくプッシュできることを確認します。

自動で設定されたWebhook

6.パイプラインの自動実行をテスト

6-1.GitLabに新たに何らかのコミットをプッシュします。

6-2.GitLabに格納しているJenkinsfileを基に、Jenkinsでパイプラインが自動で実行されることを確認します。今回は”Hello World”を出力する簡単なパイプラインを実行します。

pipeline {
    agent any 
    stages {
        stage('Hello World') { 
            steps {
                echo "Hello World"
            }
        }
    }
}
実行されたパイプライン

7.エージェントノードの追加

手順6ではJenkinsに登録されているノードはJenkinsコントローラのみのため、パイプラインはコントローラノード上で実行されます。しかしJenkinsではパイプラインの実行はコントローラノードではなくエージェントノードで行うのが推奨されています。これにより、コントローラノードの負荷を軽減し、システム全体の安定性とパフォーマンスを向上させることができます。手順7ではJenkinsコントローラに対してエージェントノードを追加します。

7‐1.「Jenkinsの管理」>「Nodes」から「+New Node」を選択した後、ノード名を入力し、Permanent Agentを選択して「Create」ボタンを押下します。入力するノード名はdocker-compose.ymlのJENKINS_AGENT_NAMEフィールドに入力した名前と一致させてください。今回は「agent-1」とします。

7‐2.エージェントノードの設定画面には以下のように入力して「保存」を押下します。
・リモートFSルート:/home/jenkins/agent
・ラベル:適宜入力してください。今回はagent-1とします。
・起動方法:Launch agent by connecting it to the controller

エージェントノードの設定

7‐3.作成したエージェントノードを選択します。「-secret」の後ろに記載のシークレット(英数字の羅列)をコピーし、docker-compose.yml の JENKINS_SECRET フィールドに貼り付けます。

エージェントノードが未接続の状態

7‐4.以下のコマンドを実行し、修正した docker-compose.yml ファイルを適用します。エージェントノードが正しく接続されると、Jenkinsに新しいエージェントが表示されます。

$ docker compose up -d
エージェントノードが接続された状態

8.エージェントノードでパイプラインを実行

手順6でコントローラノード上で実行していたパイプラインを、エージェントノード上で実行するように設定します。

8‐1.Jenkinsfileに記述しているagentを「any」から、手順7-2でエージェントノードに付与したラベル名に変更し、GitLabのリポジトリに変更をプッシュします。

pipeline {
    agent {
        label 'agent-1'
    }
    stages {

8‐2.GitLabのリポジトリの変更をトリガーに、パイプラインが指定したエージェントノードで実行されることを確認します。エージェントノードで実行されているかは、実行されたパイプラインの「Console Output」画面で出力される「Runnnig on <ノード名>」で確認することができます。

エージェントノードでパイプラインが実行されていることを示すコンソール出力

以上の手順で、Docker Composeを用いたGitLabとJenkins(コントローラノード、エージェントノード)を構築とそれらの連携をして、エージェントノード上でパイプラインを実行することが出来ました。

まとめ

今回はDocker Composeを利用して、GitLabとJenkins(コントローラノード、エージェントノード)を構築し、それらを連携してパイプラインを実行する手法について説明しました。Docker Composeを利用することで、多くのコマンドを実行することなく、少ない手作業で環境を構築することができます。docker-compose.ymlの記述や各アプリケーションのセットアップなど、参考になれば幸いです。

開発基盤ソリューションチームでは、Jenkinsの構築だけでなく、SCMツールとの連携や、ビルドツールやテストツールと連携したCI/CDパイプラインの作成もサポートしていますので、ぜひお気軽にご相談ください。

By yoneta