0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Docker Swarm + GPU + GlusterFS で AI推論クラスタを構築する

0
Last updated at Posted at 2026-03-16

はじめに

GPU を搭載した物理サーバー3台で AI推論クラスタ を構築する機会がありました。

要件は以下の通りです。

  • 複数のAI推論サービスをコンテナで動かしたい
  • GPU リソースを効率的に使いたい
  • ノード間でモデルファイルや設定を共有したい
  • ロードバランサーで冗長化し、障害時にも自動復旧したい
  • WAF でアプリケーション層のセキュリティを確保したい

運用コストの点からKubernetesではなく、Docker Swarm をオーケストレーションに採用し、GlusterFS でストレージ共有、Traefik でリバースプロキシ、ModSecurity で WAF を構成しました。

本記事では、構築の全体像と各ステップのポイントを紹介します。

構成概要

アーキテクチャ図

ノード構成

ノード 役割 GPU OS メモリ
node1 フロント + マネージャ なし Ubuntu 24.04 250GB
node2 AI推論 + ワーカー あり Ubuntu 24.04 1.0TB
node3 AI推論 + ワーカー あり Ubuntu 24.04 1.0TB

主要コンポーネント

コンポーネント 役割
Docker Swarm コンテナオーケストレーション・自己修復
NVIDIA Container Toolkit GPU 対応コンテナ実行環境
GlusterFS ノード間共有ストレージ (replica 3)
Traefik v3 ロードバランサー + HTTPS 終端
Nginx + ModSecurity + OWASP CRS WAF (アプリケーション層防御)
Let's Encrypt SSL 証明書の自動取得・更新

STEP 1: Docker のインストール (全ノード)

# 公式スクリプトでインストール
curl -fsSL https://get.docker.com | sudo sh

# 自動起動設定
sudo systemctl enable docker
sudo systemctl start docker

# 現在のユーザーを docker グループに追加
sudo usermod -aG docker $USER
newgrp docker

# 確認
docker version

STEP 2: Docker Swarm クラスタ構成

クラスタ初期化 (node1)

# node1 で実行
docker swarm init --advertise-addr <node1のIP>

出力されるトークンを控えておきます。

ワーカーノードの参加 (node2, node3)

# node2, node3 で実行
docker swarm join --token <取得したトークン> <node1のIP>:2377

ノードラベルの付与

ラベルにより、サービスの配置先を制御します。

# node1 で実行
docker node update --label-add role=frontend node1
docker node update --label-add role=frontend node2
docker node update --label-add role=gpu     node2
docker node update --label-add role=gpu     node3
  • role=frontend: Traefik (ロードバランサー) を配置するノード
  • role=gpu: AI推論コンテナを配置するノード

ポイント: hostname の事前設定

Swarm に参加後はノード名が変更できません。docker swarm joinに hostname を設定しておきます。

# 各ノードで実行
sudo hostnamectl set-hostname node1  # node2, node3 も同様

確認

docker node ls
ID             HOSTNAME   STATUS   AVAILABILITY   MANAGER STATUS
xxxxx *        node1      Ready    Active         Leader
yyyyy          node2      Ready    Active
zzzzz          node3      Ready    Active

STEP 3: GPU ドライバ + NVIDIA Container Toolkit (node2, node3)

NVIDIA ドライバのインストール

sudo apt update && sudo apt install -y nvidia-driver-550
sudo reboot

再起動後、nvidia-smi でドライバが認識されていることを確認します。

NVIDIA Container Toolkit のインストール

# GPG キーの追加
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add -

# リポジトリ追加
distribution=$(. /etc/os-release; echo $ID$VERSION_ID)
curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-docker.list

# インストール
sudo apt update && sudo apt install -y nvidia-docker2
sudo systemctl restart docker

Docker の GPU デフォルトランタイム設定

AI 推論ノードでは、Docker のデフォルトランタイムを NVIDIA に設定しておくと便利です。

/etc/docker/daemon.json:

{
  "default-runtime": "nvidia",
  "runtimes": {
    "nvidia": {
      "path": "nvidia-container-runtime",
      "runtimeArgs": []
    }
  }
}
sudo systemctl restart docker

動作確認

docker run --rm --gpus all nvidia/cuda:12.4.0-base-ubuntu22.04 nvidia-smi

nvidia-smi の出力が表示されれば成功です。

STEP 4: GlusterFS (共有ストレージ)

3ノードで replica 3 のボリュームを構成し、モデルファイル・設定・ログを共有します。

インストール (全ノード)

sudo apt update
sudo apt install -y glusterfs-server
sudo systemctl enable --now glusterd

ピア接続 (node1 から実行)

gluster peer probe node2
gluster peer probe node3
gluster peer status

事前に /etc/hosts または DNS でノード名が解決できるよう設定しておく必要があります。

ボリューム作成 (node1)

sudo mkdir -p /gluster/brick1

sudo gluster volume create gv0 replica 3 transport tcp \
  node1:/gluster/brick1 \
  node2:/gluster/brick1 \
  node3:/gluster/brick1 force

sudo gluster volume start gv0

マウント (全ノード)

sudo mkdir -p /mnt/gv0
sudo mount -t glusterfs node1:/gv0 /mnt/gv0

/etc/fstab に追記して永続化:

node1:/gv0 /mnt/gv0 glusterfs defaults,_netdev 0 0

_netdev を忘れると、ネットワーク起動前にマウントしようとして起動が失敗します。

ディレクトリ構成例

GlusterFS 上に以下のようなディレクトリ構成を作り、全ノードで共有します。

/mnt/gv0/
├─ service/
│  ├─ node1/
│  │  ├─ traefik/          # Traefik 設定ファイル
│  │  ├─ log/traefik/      # Traefik ログ
│  │  └─ ssl/              # SSL 証明書関連
│  ├─ node2/
│  └─ node3/
├─ app/
│  ├─ web/                 # Web サービス
│  └─ ai/                  # AI アプリ・モデルファイル
└─ docker-images/          # Docker イメージの共有用

STEP 5: Nginx + WAF + Traefik (リバースプロキシ構成)

本構成では、Nginx (ModSecurity)Traefik を同一ノード上に配置し、以下の流れでリクエストを処理します。

Client → Nginx (WAF + SSL終端) → Traefik (ルーティング) → APP コンテナ

Traefik 単体には WAF 機能がないため、Nginx + ModSecurity を前段に置いてアプリケーション層の防御を行います。

5-1. Nginx + ModSecurity v3 のセットアップ

ModSecurity v3 のビルド (Ubuntu 24.04)

# 依存パッケージ
sudo apt install -y git build-essential libpcre3 libpcre3-dev \
    libxml2 libxml2-dev libyajl-dev curl pkg-config libtool \
    automake autoconf zlib1g-dev libcurl4-openssl-dev libgeoip-dev \
    liblmdb-dev libpcre++-dev doxygen nginx libssl-dev

# ModSecurity v3 ビルド
cd /usr/local/src
sudo git clone --depth 1 -b v3/master https://github.com/SpiderLabs/ModSecurity
cd ModSecurity
sudo git submodule init && sudo git submodule update
sudo ./build.sh && sudo ./configure && sudo make && sudo make install

# Nginx 用コネクタ
cd /usr/local/src
sudo git clone https://github.com/SpiderLabs/ModSecurity-nginx.git

# Nginx 動的モジュールとしてビルド
NGINX_VERSION=$(nginx -v 2>&1 | grep -o '[0-9.]*')
sudo apt source nginx
cd nginx-$NGINX_VERSION
sudo ./configure --with-compat --add-dynamic-module=../ModSecurity-nginx
sudo make modules
sudo cp objs/ngx_http_modsecurity_module.so /etc/nginx/modules

ModSecurity 設定

sudo mkdir -p /etc/nginx/modsec
cd /etc/nginx/modsec
sudo wget https://raw.githubusercontent.com/SpiderLabs/ModSecurity/v3/master/modsecurity.conf-recommended \
  -O modsecurity.conf

# 検知モードから遮断モードへ変更
sudo sed -i 's/SecRuleEngine DetectionOnly/SecRuleEngine On/' modsecurity.conf

OWASP CRS を導入して、SQLi・XSS 等のアプリケーション層攻撃を検知・遮断します。

Nginx 設定

/etc/nginx/nginx.conf の先頭に追加:

load_module modules/ngx_http_modsecurity_module.so;

バーチャルホスト設定例 (/etc/nginx/sites-available/your-domain):

server {
    listen 443 ssl;
    server_name your-domain.example.com;

    ssl_certificate     /etc/nginx/ssl/your-domain.crt;
    ssl_certificate_key /etc/nginx/ssl/your-domain.key;

    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;

    location / {
        proxy_pass http://localhost:8080;  # Traefik へ転送
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto https;
    }
}
sudo ln -s /etc/nginx/sites-available/your-domain /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

ログローテーション

/etc/logrotate.d/modsecurity:

/var/log/modsecurity/audit.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 640 root adm
    sharedscripts
    postrotate
        systemctl reload nginx > /dev/null 2>&1 || true
    endscript
}

5-2. Traefik のセットアップ

Overlay ネットワークの作成

docker network create --driver=overlay traefik-net
docker network create --driver=overlay app-net

Traefik 静的設定 (traefik.yaml)

entryPoints:
  web:
    address: ":8080"

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
  file:
    filename: "/traefik-dynamic.yaml"
    watch: true

log:
  level: INFO
  filePath: "/var/log/traefik/traefik.log"

accessLog:
  filePath: "/var/log/traefik/access.log"
  bufferingSize: 100

Nginx が SSL 終端とポート 443 を担当するため、Traefik は 8080 番で HTTP のみ受け付けます。

Traefik 動的設定 (traefik-dynamic.yaml)

http:
  routers:
    frontend:
      rule: "Host(`your-domain.example.com`)"
      service: frontend-service
      entryPoints:
        - web
  services:
    frontend-service:
      loadBalancer:
        servers:
          - url: "http://<APPコンテナのIP>:<ポート>"

Swarm Stack 定義 (compose.yaml)

version: "3.8"

services:
  traefik:
    image: traefik:v3
    ports:
      - "8080:8080"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "/mnt/gv0/service/node1/traefik/traefik.yaml:/traefik.yaml:ro"
      - "/mnt/gv0/service/node1/traefik/traefik-dynamic.yaml:/traefik-dynamic.yaml:ro"
      - "/mnt/gv0/service/node1/log/traefik:/var/log/traefik"
    networks:
      - traefik-net
      - app-net
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.labels.role == frontend

networks:
  traefik-net:
    external: true
  app-net:
    external: true
docker stack deploy -c compose.yaml traefik

STEP 6: AI/APP コンテナのデプロイ

Docker イメージの配布 (GlusterFS 経由)

プライベートレジストリを立てなくても、GlusterFS 共有ディレクトリを中継してイメージを配布できます。

# node1: イメージを tar で保存
sudo docker save -o /mnt/gv0/docker-images/ai-app.tar ai-app:latest

# node2, node3: イメージをロード
sudo docker load -i /mnt/gv0/docker-images/ai-app.tar

docker save はイメージサイズによって10分程度かかることがあります。

GPU 対応サービスの Stack 定義

services:
  ai-service:
    image: ai-app:latest
    deploy:
      replicas: 2
      placement:
        constraints:
          - node.labels.role == gpu
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    volumes:
      - type: bind
        source: /mnt/gv0/app/ai
        target: /app/models
    networks:
      - app-net
docker stack deploy -c ai-stack.yaml ai
  • node.labels.role == gpu の制約で GPU ノードにのみ配置
  • resources.reservations.devices で GPU リソースを明示的に予約
  • GlusterFS マウントによりモデルファイルを全ノードで共有

運用で学んだこと・ハマりポイント

1. Swarm 参加前に hostname を設定する

Swarm に参加後はノード名が変更できません。docker swarm join の前に hostnamectl set-hostname を実行しておかないと、後から管理が煩雑になります。

2. Docker イメージの配布に GlusterFS が使える

プライベートレジストリを立てなくても、docker save → GlusterFS 共有 → docker load のワークフローで十分に運用できました。ただし大きなイメージだと保存に時間がかかるため、並行作業の計画が必要です。

3. ModSecurity のソースビルドは依存関係が多い

Ubuntu 24.04 でのビルドは依存パッケージが多く、手動構築は時間がかかります。再現性を確保するためにスクリプト化しておくのがおすすめです。

4. GlusterFS の fstab には _netdev が必須

_netdev を忘れると、ネットワーク起動前にマウントしようとして起動が失敗します。

5. daemon.json の設定を忘れると GPU コンテナが動かない

GPU ノードでは /etc/docker/daemon.json に NVIDIA ランタイムを設定した上で Docker を再起動する必要があります。これを忘れると docker load したイメージが GPU を認識しません。

まとめ

項目 採用技術
オーケストレーション Docker Swarm
GPU コンテナ NVIDIA Container Toolkit
共有ストレージ GlusterFS (replica 3)
ロードバランサー Traefik v3
WAF + リバースプロキシ Nginx + ModSecurity v3 + OWASP CRS
SSL Let's Encrypt

Docker Swarm は Kubernetes に比べて学習コストが低く、3ノード程度の小〜中規模クラスタには十分な機能を提供します。GlusterFS との組み合わせにより、プライベートレジストリなしでもイメージ配布やモデル共有が実現でき、実用的な AI 推論基盤を構築できました。

同様の構成を検討されている方の参考になれば幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?