Bamboo remote agent の実行環境に関する改善 part1

Bamboo remote agent の実行環境を改善する

oginoNovember 22, 2024

はじめに

当社では、CI/CD ジョブの実行環境の一部として、AWS EC2 インスタンス上で稼働する Bamboo remote agent (以下 remote agent) を管理しています。 今回の記事から 2 回に分けて、当社で実践した remote agent の実行環境に対する改善についてご紹介します。

改善前の実行環境について

元々、当社の環境では、以下の構成で remote agent を管理していました。

  • remote agent が実行される環境として EC2 インスタンスを 1 台起動している
  • remote agent は、上記インスタンスの中で 5 つ起動している
    • remote agent とは、ひとつの Java プロセスに相当します

このような構成において、以下の課題を抱えていました。

  • サーバーのセットアップ処理が自動化されていない
    • サーバーを再構築した場合に、セットアップに時間を要してしまう
    • remote agent が自動起動されないため、スケールアウトが困難である
  • remote agent が 1 台の EC2 インスタンスに集中している
    • 利用可能な全ての remote agent が 1 台の EC2 インスタンスの中で実行されている
      • EC2 インスタンスを停止すると、全ての remote agent がオフラインになってしまう
      • 同時に実行されるジョブが多い場合に、EC2 インスタンス側のリソースが飽和してしまう
  • サーバーを再構築した場合に、過去に実行された CI/CD ジョブで蓄積されたキャッシュ資材を持ち越すことができない
  • サーバーの費用が最適化されていない

以降の記事では、上記の課題に対してどのように取り組み、解決したのかについてご紹介します。

今回の記事のスコープ

今回の記事では、先ほど挙げた課題のうち、サーバーのセットアップ処理の自動化と、remote agent の分散の取り組みについてご紹介します。

改善の詳細

いくつかの課題がある中で、サーバーのセットアップ処理の自動化と、remote agent の分散から取り組んだことには理由があります。

まず、サーバーのセットアップ処理の自動化については、サーバーの運用を安定させたい、スケールアウトを見据えた場合の阻害要因を取り除きたい、という狙いがありました。remote agent の分散については、上記の取り組みの過程で無理なく導入ができると考えたため、同時期に作業を実施しています。

サーバーのセットアップ処理を自動化する手段はいくつか挙げられると思いますが、今回は実装スピードを優先して、EC2 インスタンスにユーザーデータを設定する方針としました。

今回の環境は Terraform の ec2-instance モジュール を使用して環境を構築していたため、当該モジュールに対してユーザーデータを挿入します。

まず、以下の内容で bamboo-agent-userdata.tpl を作成します。なお、以下のユーザーデータは、本記事に関係が深い部分のみ抜粋して掲載しています。実際のファイルの内容とは異なるため、注意してください。

#!/bin/bash
 
_bamboo_agent_token=${BAMBOO_AGENT_TOKEN}
 
# mount
device_name="/dev/nvme1n1"
filesystem=$(blkid -o value -s TYPE $${device_name})
if [ "$${filesystem}" != "xfs" ]; then
  mkfs -t xfs $${device_name}
fi
until ls -l /dev/disk/by-uuid | grep $${device_name##*/}; do
  echo "not detected uuid of the $${device_name##*/} device, sleeping and retrying..."
  sleep 10s
done
disk_uuid=$(lsblk -f | grep $${device_name##*/} | awk '{ print $3 }')
mkdir /data
tee -a /etc/fstab <<EOF
UUID=$${disk_uuid}   /data  xfs  defaults,nofail  0  2
EOF
mount -a
 
# package
# https://github.com/amazonlinux/amazon-linux-2023/issues/397
for package in git docker java-17-amazon-corretto; do
  until dnf install -y "$${package}"; do
    echo "dnf $${package} install failed, sleeping and retrying..."
    sleep 10s
  done
done
systemctl enable docker
systemctl start docker
 
# remote-agent
data_home="/data/home"
bamboo_home_dir="$${data_home}/bamboo"
useradd -s /sbin/nologin -G docker -d "$${bamboo_home_dir}" bamboo
chown -R bamboo:bamboo "$${data_home}"
 
bamboo_version="n.n.n"
base_url="https://packages.atlassian.com/content/groups/public/com/atlassian/bamboo/atlassian-bamboo-agent-installer"
filename="atlassian-bamboo-agent-installer-$${bamboo_version}.jar"
sudo -u bamboo wget \
  -O "$${bamboo_home_dir}/atlassian-bamboo-agent-installer.jar" \
  "$${base_url}/$${bamboo_version}/$${filename}"
 
agent_number=${AGENT_NUMBERS_PER_INSTANCE}
for i in $(seq 1 $${agent_number}); do
  agent_name=$(printf bamboo-remote-agent-%02d "$${i}")
  agent_home="/data/$${agent_name}"
  mkdir -p "$${agent_home}"
  chown -R bamboo:bamboo "$${agent_home}"
  sudo -u bamboo /usr/bin/java \
    -Dbamboo.home="$${agent_home}" \
    -jar "$${bamboo_home_dir}/atlassian-bamboo-agent-installer.jar" \
    https://<your-bamboo-server-domain>/agentServer/ \
    install \
    -t "$${_bamboo_agent_token}"
  tee -a "/etc/systemd/system/$${agent_name}.service"<<EOF
[Unit]
Description=Bamboo Remote Agent $${i}
After=network.target
 
[Service]
User=bamboo
ExecStart=/usr/bin/java \
-Dbamboo.home="$${agent_home}" \
-jar "$${bamboo_home_dir}/atlassian-bamboo-agent-installer.jar" \
https://<your-bamboo-server-domain>/agentServer/
Restart=always
 
[Install]
WantedBy=multi-user.target
EOF
  systemctl daemon-reload
  systemctl enable "$${agent_name}"
  systemctl start "$${agent_name}"
done

上記のファイルをテンプレートとして、ユーザーデータを設定します。なお、ユーザーデータ内で /data にマウントしている EBS は、ブロックデバイスマッピングで /dev/sdf として定義します。

module "agent_server" {
  source = "terraform-aws-modules/ec2-instance/aws"
  root_block_device = [
    {
      volume_type = "gp3"
      volume_size = 1000
    }
  ]
  ebs_block_device = [
    {
      device_name = "/dev/sdf"
      volume_type = "gp3"
      volume_size = 1000
    }
  ]
  user_data_base64 = base64encode(templatefile("${path.module}/bamboo-agent-userdata.tpl", {
    AGENT_NUMBERS_PER_INSTANCE = 2
    BAMBOO_AGENT_TOKEN         = var.bamboo_agent_token
  }))

当社の環境では remote agent の認証にセキュリティトークンを有効にしているため、BAMBOO_AGENT_TOKEN 変数でセキュリティトークンを埋め込んでいます。

また、AGENT_NUMBERS_PER_INSTANCE 変数で、ひとつの EC2 インスタンスにインストールされる remote agent の数を調整可能とし、このタイミングでひとつの EC2 インスタンスの中で起動される remote agent の数を 2 つに変更しています。

上記の agent_serverterraform apply して起動される EC2 インスタンスは、ユーザーデータで実行される処理を介して remote agent として動作するために必要なセットアップ処理が自動的に実行されます。

当社の環境では、上記の構成の EC2 インスタンスを 2 台起動させ、従来 remote agent が実行されていた EC2 インスタンスとの入れ替えを行いました。また、新しい EC2 インスタンスは既存の EC2 インスタンスのひとつ下のインスタンスタイプを選択しています。これは、最大同時実行ジョブが半分以下になったため、リソースに余裕が生まれることを見込んでの変更です。

得られた効果

サーバーのセットアップ処理を自動化することで、以下の効果が得られました。

  1. サーバーの再構築作業の安定化 & 高速化
  2. スケールアウトに必要な最低限の準備が整う

また、remote agent がひとつの EC2 インスタンスに集中しなくなったことで、以下の効果が得られました。

  1. 可用性の向上
  2. remote agent にフォーカスしたスケールアップの余地の獲得
  3. 同時実行可能なジョブが制限されたことによるサーバーリソースの余剰確保

おわりに

今回の記事では、Bamboo remote agent の実行環境となる EC2 インスタンスのセットアップ処理の自動化と、remote agent を分散させる取り組みについてご紹介しました。

次回の記事では、パフォーマンス改善や費用最適化に繋がる取り組みについてご紹介します。