LXDコンテナの操作を手軽に行うスクリプト6つ

LXD-UIセットアップ

まずはLXD-UIセットアップ部分を。

# LXD インストール
sudo snap install lxd --channel=latest/stable

# 初期化
sudo lxd init --minimal

# HTTPS API を有効化
sudo lxc config set core.https_address :8443

# UI 有効化
sudo snap set lxd ui.enable=true
sudo systemctl reload snap.lxd.daemon

# ユーザーを lxd グループに追加
sudo usermod -aG lxd $USER

LXD-UI https://ホスト名:8443

マウントディレクトリ作成

ホストとLXDコンテナ内にマウントしてホストと共有するディレクトリを作成。これも次のスクリプトにあるので、無理に実施しなくてもOK。

sudo mkdir -p /opt/lxd-data
sudo chown -R 1000:1000 /opt/lxd-data

ここからスクリプトを作成。
全部で6つあります。
最後に、この6つを一気に作成するスクリプトがあるので、個別のは内容だけ確認して、一気に作成したほうが手軽かも。
使用時にTabキーで補完出来るようなファイル名にしています。

lxd-base-minimal作成スクリプト

mkrir -p ~/script/lxd
cd  ~/script/lxd
nano minimal-lxd-base-create.sh
chmod +x minimal-lxd-base-create.sh
./minimal-lxd-base-create.sh

minimal-lxd-base-create.sh

#!/bin/bash

CONTAINER="lxd-base-minimal"
MOUNT_PATH="/opt/lxd-data"

echo "=== lxd-base-minimal コンテナを作成 ==="
lxc launch ubuntu:24.04 "$CONTAINER"

echo "=== $MOUNT_PATH が存在しない場合は作成 ==="
if [ ! -d "$MOUNT_PATH" ]; then
    sudo mkdir -p "$MOUNT_PATH"
    if [ ! -d "$MOUNT_PATH" ]; then
        echo "エラー: $MOUNT_PATH の作成に失敗しました"
        exit 1
    fi
    echo "    $MOUNT_PATH を作成しました"
else
    echo "    $MOUNT_PATH は既に存在します"
fi

echo "=== ホストの $MOUNT_PATH をコンテナに同じパスでマウント ==="
lxc config device add "$CONTAINER" opt-lxd-data disk source="$MOUNT_PATH" path="$MOUNT_PATH"

echo "=== ID マッピング設定を適用 ==="
lxc config set "$CONTAINER" raw.idmap "both 1000 1000"

echo "=== コンテナを再起動します ==="
lxc restart "$CONTAINER"

echo "=== 完了しました ==="

lxd-base-minimalコンテナに入ってセットアップ

lxd-base-minimalコンテナに入って、下記の、アップデートとTailscaleインストールしtailscale up直前までを実行するスクリプト。

sudo apt update
sudo apt upgrade -y
sudo apt install curl
curl -fsSL https://tailscale.com/install.sh | sh

first-setup-minimal-lxd-base.sh

#!/bin/bash
set -euo pipefail

CONTAINER="lxd-base-minimal"

echo "==> コンテナ '${CONTAINER}' に接続してセットアップを開始します..."

lxc exec "${CONTAINER}" -- bash -euo pipefail << 'EOF'
echo "==> apt update"
sudo apt update

echo "==> apt upgrade"
sudo apt upgrade -y

echo "==> curl インストール"
sudo apt install -y curl

echo "==> Tailscale インストール"
curl -fsSL https://tailscale.com/install.sh | sh

echo "==> セットアップ完了"
EOF

echo "==> コンテナをシャットダウンします..."
lxc stop "${CONTAINER}"

echo "==> 完了"

lxd-base-minimalをコピーしてlxd-base-dockerを作るスクリプト

処理の流れ:

  1. lxd-base-minimal をコピーして lxd-base-docker を作成
  2. 停止した状態で raw.idmapsecurity.nesting true(Nesting Allow)を設定
  3. 起動後、マウントディレクトリ(/opt/lxd-data)の権限設定
  4. 再起動して設定を反映
  5. コンテナ内でDockerインストール・/opt/dockerディレクトリ設定
  6. シャットダウン

docker-lxd-base-create.sh

#!/bin/bash
set -euo pipefail

SRC="lxd-base-minimal"
NEW="lxd-base-docker"
MOUNT_PATH="/opt/lxd-data"

echo "=== コンテナをコピー: $SRC → $NEW ==="
lxc copy "$SRC" "$NEW"

echo "=== コンテナを停止します(設定適用のため) ==="
lxc stop "$NEW" 2>/dev/null || true

echo "=== raw.idmap を設定 ==="
lxc config set "$NEW" raw.idmap "both 1000 1000"

echo "=== Nesting を Allow に設定 ==="
lxc config set "$NEW" security.nesting true

echo "=== コンテナを起動 ==="
lxc start "$NEW"

echo "=== マウントディレクトリの権限設定(ホスト側) ==="
sudo chown 1000:1000 "$MOUNT_PATH"
sudo chmod 775 "$MOUNT_PATH"

echo "=== 設定反映のため再起動 ==="
lxc restart "$NEW"

echo "=== ネットワーク疎通を待機中 ==="
for i in $(seq 1 30); do
    if lxc exec "$NEW" -- curl -fsSL --max-time 3 https://get.docker.com -o /dev/null 2>/dev/null; then
        echo "    ネットワーク疎通確認 (${i}秒)"
        break
    fi
    echo "    待機中... (${i}/30秒)"
    sleep 1
    if [ "$i" -eq 30 ]; then
        echo "エラー: ネットワークが30秒以内に疎通しませんでした" >&2
        exit 1
    fi
done

echo "=== コンテナ内でセットアップを実行 ==="
lxc exec "$NEW" -- bash -euo pipefail << 'EOF'

echo "--- Docker インストール ---"
curl -fsSL https://get.docker.com | sh

echo "--- /opt/docker ディレクトリのセットアップ ---"
mkdir -p /opt/docker

if getent group docker > /dev/null 2>&1; then
    chown -R "${USER:-root}:docker" /opt/docker
    chmod -R 775 /opt/docker
    chmod -R g+s /opt/docker
    if [ "${USER:-root}" != "root" ]; then
        usermod -aG docker "$USER"
        echo "グループ変更を反映するには、newgrp docker を実行してください(再起動不要)"
    else
        echo "rootユーザーのため usermod はスキップしました"
    fi
else
    chown -R "${USER:-root}:${USER:-root}" /opt/docker
    chmod -R 755 /opt/docker
    echo "docker グループが存在しないため、オーナー権限のみ設定しました"
fi

echo "/opt/docker のセットアップ完了"
EOF

echo "=== コンテナをシャットダウン ==="
lxc stop "$NEW"

echo "=== 完了: $NEW ==="

コンテナを選択してコピーするスクリプト

lxd-base-minimallxd-base-dockerをコピーしてコンテナを作成する時などに。

copy-lxd-create.sh

#!/bin/bash

# --- コンテナ一覧を取得 ---
containers=($(lxc list -c n --format csv))

echo "=== コピー元のコンテナを選択してください ==="
i=1
for c in "${containers[@]}"; do
    echo "$i) $c"
    ((i++))
done

# --- 番号入力 ---
read -p "番号を入力: " index

if ! [[ "$index" =~ ^[0-9]+$ ]]; then
    echo "エラー: 数字を入力してください" >&2
    exit 1
fi

index=$((index - 1))

if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
    echo "エラー: 不正な番号です" >&2
    exit 1
fi

SRC="${containers[$index]}"
echo "選択されたコピー元: $SRC"

read -p "新しいコンテナ名を入力: " NEW

if [ -z "$NEW" ]; then
    echo "エラー: コンテナ名が空です" >&2
    exit 1
fi

echo "=== コピー開始: $SRC → $NEW ==="
lxc copy "$SRC" "$NEW"

echo "=== コンテナを起動します ==="
lxc start "$NEW"

# (※ raw.idmap を適用するには STOP している必要があるため)
echo "=== 一旦コンテナを停止します(ID マップ適用のため) ==="
lxc stop "$NEW"

echo "=== raw.idmap を設定します ==="
lxc config set "$NEW" raw.idmap "both 1000 1000"

echo "=== コンテナを起動します ==="
lxc start "$NEW"

echo "=== 設定反映のため再起動 ==="
lxc restart "$NEW"

echo "=== コンテナに入ります: $NEW ==="
lxc exec "$NEW" -- bash

スナップショットを作成

容量節約や確実に行うために、一旦停止してスナップショットを作成

snapshot-lxd.sh

#!/bin/bash
set -euo pipefail

echo "=== LXD コンテナ一覧 ==="

# コンテナ名一覧を配列に格納
containers=($(lxc list -c n --format csv))

# 一覧表示
i=1
for c in "${containers[@]}"; do
    echo "$i) $c"
    ((i++))
done

# 番号選択
read -p "番号を入力: " index

# 数字チェック
if ! [[ "$index" =~ ^[0-9]+$ ]]; then
    echo "エラー: 数字を入力してください" >&2
    exit 1
fi

index=$((index - 1))

# 範囲チェック
if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
    echo "エラー: 不正な番号です" >&2
    exit 1
fi

TARGET="${containers[$index]}"
echo "選択されたコンテナ: $TARGET"

# コメント入力
read -p "スナップショットのコメントを入力(任意・空可): " COMMENT

# スペースをハイフンに変換
COMMENT="${COMMENT// /-}"

# スナップショット名(コメントがあれば付加)
SNAP="snap-$(date +%Y%m%d-%H%M%S)"
if [ -n "$COMMENT" ]; then
    SNAP="${SNAP}-${COMMENT}"
fi

echo "=== コンテナ停止: $TARGET ==="
lxc stop "$TARGET" 2>/dev/null || true

echo "=== スナップショット作成: $TARGET/$SNAP ==="
lxc snapshot "$TARGET" "$SNAP"

echo "=== コンテナ起動: $TARGET ==="
lxc start "$TARGET"

echo "=== 完了しました ==="
echo "作成されたスナップショット: $TARGET/$SNAP"

コンテナに入るだけ

コンテナ一覧を表示して選択したコンテナに入る

enter-lxd-container.sh

#!/bin/bash

echo "=== LXD コンテナ一覧 ==="

# コンテナ名の一覧を取得
containers=($(lxc list -c n --format csv))

# 番号付きで表示
i=1
for c in "${containers[@]}"; do
    echo "$i) $c"
    ((i++))
done

# 番号入力
read -p "番号を入力: " index

# 数字チェック
if ! [[ "$index" =~ ^[0-9]+$ ]]; then
    echo "エラー: 数字を入力してください" >&2
    exit 1
fi

index=$((index - 1))

# 範囲チェック
if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
    echo "エラー: 不正な番号です" >&2
    exit 1
fi

TARGET="${containers[$index]}"
echo "選択されたコンテナ: $TARGET"

# 起動状態を確実に判定(Running / RUNNING / Stopped / STOPPED など対応)
STATUS=$(lxc info "$TARGET" | awk '/Status:/ {print tolower($2)}')

if [ "$STATUS" != "running" ]; then
    echo "=== コンテナが停止中です。起動します ==="
    lxc start "$TARGET"
else
    echo "=== コンテナは既に起動中です ==="
fi

echo "=== コンテナに入ります: $TARGET ==="
lxc exec "$TARGET" -- bash

6個のスクリプトを一度に作成して保存するスクリプト

コピーボタンでまとめてコピーして、ターミナルに貼り付けて実行するだけです。
実行後、以下の6ファイルが /opt/script/lxd/ に作成され、実行権限も付与されます。

生成される6つのスクリプトの内容:

ファイル名内容
minimal-lxd-base-create.shコンテナ作成・マウントディレクトリチェック付き
first-setup-minimal-lxd-base.shapt update/upgrade・curl・Tailscaleインストール後シャットダウン
docker-lxd-base-create.shlxd-base-minimalをコピーしてDocker環境構築・シャットダウン
copy-lxd-create.sh既存コンテナをコピーして新規作成
snapshot-lxd.shスナップショット作成
enter-lxd-container.shコンテナ選択して入る

最後の ls -la で作成確認もできます。

#!/bin/bash
set -euo pipefail

sudo mkdir -p /opt/script/lxd

cat > /tmp/minimal-lxd-base-create.sh << 'EOF'
#!/bin/bash
set -euo pipefail

CONTAINER="lxd-base-minimal"
MOUNT_PATH="/opt/lxd-data"

echo "=== lxd-base-minimal コンテナを作成 ==="
lxc launch ubuntu:24.04 "$CONTAINER"

echo "=== $MOUNT_PATH が存在しない場合は作成 ==="
if [ ! -d "$MOUNT_PATH" ]; then
    sudo mkdir -p "$MOUNT_PATH"
    if [ ! -d "$MOUNT_PATH" ]; then
        echo "エラー: $MOUNT_PATH の作成に失敗しました"
        exit 1
    fi
    echo "    $MOUNT_PATH を作成しました"
else
    echo "    $MOUNT_PATH は既に存在します"
fi

echo "=== ホストの $MOUNT_PATH をコンテナに同じパスでマウント ==="
lxc config device add "$CONTAINER" opt-lxd-data disk source="$MOUNT_PATH" path="$MOUNT_PATH"

echo "=== ID マッピング設定を適用 ==="
lxc config set "$CONTAINER" raw.idmap "both 1000 1000"

echo "=== コンテナを再起動します ==="
lxc restart "$CONTAINER"

echo "=== 完了しました ==="
EOF

cat > /tmp/first-setup-minimal-lxd-base.sh << 'EOF'
#!/bin/bash
set -euo pipefail

CONTAINER="lxd-base-minimal"

echo "==> コンテナ '${CONTAINER}' に接続してセットアップを開始します..."

lxc exec "${CONTAINER}" -- bash -euo pipefail << 'INNER'
echo "==> apt update"
sudo apt update

echo "==> apt upgrade"
sudo apt upgrade -y

echo "==> curl インストール"
sudo apt install -y curl

echo "==> Tailscale インストール"
curl -fsSL https://tailscale.com/install.sh | sh

echo "==> セットアップ完了"
INNER

echo "==> コンテナをシャットダウンします..."
lxc stop "${CONTAINER}"

echo "==> 完了"
EOF

cat > /tmp/docker-lxd-base-create.sh << 'EOF'
#!/bin/bash
set -euo pipefail

SRC="lxd-base-minimal"
NEW="lxd-base-docker"
MOUNT_PATH="/opt/lxd-data"

echo "=== コンテナをコピー: $SRC → $NEW ==="
lxc copy "$SRC" "$NEW"

echo "=== コンテナを停止します(設定適用のため) ==="
lxc stop "$NEW" 2>/dev/null || true

echo "=== raw.idmap を設定 ==="
lxc config set "$NEW" raw.idmap "both 1000 1000"

echo "=== Nesting を Allow に設定 ==="
lxc config set "$NEW" security.nesting true

echo "=== コンテナを起動 ==="
lxc start "$NEW"

echo "=== マウントディレクトリの権限設定(ホスト側) ==="
sudo chown 1000:1000 "$MOUNT_PATH"
sudo chmod 775 "$MOUNT_PATH"

echo "=== 設定反映のため再起動 ==="
lxc restart "$NEW"

echo "=== ネットワーク疎通を待機中 ==="
for i in $(seq 1 30); do
    if lxc exec "$NEW" -- curl -fsSL --max-time 3 https://get.docker.com -o /dev/null 2>/dev/null; then
        echo "    ネットワーク疎通確認 (${i}秒)"
        break
    fi
    echo "    待機中... (${i}/30秒)"
    sleep 1
    if [ "$i" -eq 30 ]; then
        echo "エラー: ネットワークが30秒以内に疎通しませんでした" >&2
        exit 1
    fi
done

echo "=== コンテナ内でセットアップを実行 ==="
lxc exec "$NEW" -- bash -euo pipefail << 'INNER'

echo "--- Docker インストール ---"
curl -fsSL https://get.docker.com | sh

echo "--- /opt/docker ディレクトリのセットアップ ---"
mkdir -p /opt/docker

if getent group docker > /dev/null 2>&1; then
    chown -R "${USER:-root}:docker" /opt/docker
    chmod -R 775 /opt/docker
    chmod -R g+s /opt/docker
    if [ "${USER:-root}" != "root" ]; then
        usermod -aG docker "$USER"
        echo "グループ変更を反映するには、newgrp docker を実行してください(再起動不要)"
    else
        echo "rootユーザーのため usermod はスキップしました"
    fi
else
    chown -R "${USER:-root}:${USER:-root}" /opt/docker
    chmod -R 755 /opt/docker
    echo "docker グループが存在しないため、オーナー権限のみ設定しました"
fi

echo "/opt/docker のセットアップ完了"
INNER

echo "=== コンテナをシャットダウン ==="
lxc stop "$NEW"

echo "=== 完了: $NEW ==="
EOF

cat > /tmp/copy-lxd-create.sh << 'EOF'
#!/bin/bash
set -euo pipefail

containers=($(lxc list -c n --format csv))

echo "=== コピー元のコンテナを選択してください ==="
i=1
for c in "${containers[@]}"; do
    echo "$i) $c"
    ((i++))
done

read -p "番号を入力: " index

if ! [[ "$index" =~ ^[0-9]+$ ]]; then
    echo "エラー: 数字を入力してください" >&2
    exit 1
fi

index=$((index - 1))

if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
    echo "エラー: 不正な番号です" >&2
    exit 1
fi

SRC="${containers[$index]}"
echo "選択されたコピー元: $SRC"

read -p "新しいコンテナ名を入力: " NEW

if [ -z "$NEW" ]; then
    echo "エラー: コンテナ名が空です" >&2
    exit 1
fi

echo "=== コピー開始: $SRC → $NEW ==="
lxc copy "$SRC" "$NEW"

echo "=== 一旦コンテナを停止します(ID マップ適用のため) ==="
lxc stop "$NEW" 2>/dev/null || true

echo "=== raw.idmap を設定します ==="
lxc config set "$NEW" raw.idmap "both 1000 1000"

echo "=== コンテナを起動します ==="
lxc start "$NEW"

echo "=== 設定反映のため再起動 ==="
lxc restart "$NEW"

echo "=== コンテナに入ります: $NEW ==="
lxc exec "$NEW" -- bash
EOF

cat > /tmp/snapshot-lxd.sh << 'EOF'
#!/bin/bash
set -euo pipefail

echo "=== LXD コンテナ一覧 ==="

# コンテナ名一覧を配列に格納
containers=($(lxc list -c n --format csv))

# 一覧表示
i=1
for c in "${containers[@]}"; do
    echo "$i) $c"
    ((i++))
done

# 番号選択
read -p "番号を入力: " index

# 数字チェック
if ! [[ "$index" =~ ^[0-9]+$ ]]; then
    echo "エラー: 数字を入力してください" >&2
    exit 1
fi

index=$((index - 1))

# 範囲チェック
if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
    echo "エラー: 不正な番号です" >&2
    exit 1
fi

TARGET="${containers[$index]}"
echo "選択されたコンテナ: $TARGET"

# コメント入力
read -p "スナップショットのコメントを入力(任意・空可): " COMMENT

# スペースをハイフンに変換
COMMENT="${COMMENT// /-}"

# スナップショット名(コメントがあれば付加)
SNAP="snap-$(date +%Y%m%d-%H%M%S)"
if [ -n "$COMMENT" ]; then
    SNAP="${SNAP}-${COMMENT}"
fi

echo "=== コンテナ停止: $TARGET ==="
lxc stop "$TARGET" 2>/dev/null || true

echo "=== スナップショット作成: $TARGET/$SNAP ==="
lxc snapshot "$TARGET" "$SNAP"

echo "=== コンテナ起動: $TARGET ==="
lxc start "$TARGET"

echo "=== 完了しました ==="
echo "作成されたスナップショット: $TARGET/$SNAP"
EOF

cat > /tmp/enter-lxd-container.sh << 'EOF'
#!/bin/bash
set -euo pipefail

echo "=== LXD コンテナ一覧 ==="

containers=($(lxc list -c n --format csv))

i=1
for c in "${containers[@]}"; do
    echo "$i) $c"
    ((i++))
done

read -p "番号を入力: " index

if ! [[ "$index" =~ ^[0-9]+$ ]]; then
    echo "エラー: 数字を入力してください" >&2
    exit 1
fi

index=$((index - 1))

if [ "$index" -lt 0 ] || [ "$index" -ge "${#containers[@]}" ]; then
    echo "エラー: 不正な番号です" >&2
    exit 1
fi

TARGET="${containers[$index]}"
echo "選択されたコンテナ: $TARGET"

STATUS=$(lxc info "$TARGET" | awk '/Status:/ {print tolower($2)}')

if [ "$STATUS" != "running" ]; then
    echo "=== コンテナが停止中です。起動します ==="
    lxc start "$TARGET"
else
    echo "=== コンテナは既に起動中です ==="
fi

echo "=== コンテナに入ります: $TARGET ==="
lxc exec "$TARGET" -- bash
EOF

sudo mv /tmp/minimal-lxd-base-create.sh /opt/script/lxd/
sudo mv /tmp/first-setup-minimal-lxd-base.sh /opt/script/lxd/
sudo mv /tmp/docker-lxd-base-create.sh /opt/script/lxd/
sudo mv /tmp/copy-lxd-create.sh /opt/script/lxd/
sudo mv /tmp/snapshot-lxd.sh /opt/script/lxd/
sudo mv /tmp/enter-lxd-container.sh /opt/script/lxd/

sudo chmod +x /opt/script/lxd/*.sh
sudo chown user:user /opt/script/lxd/*.sh

echo "=== 完了 ==="
ls -la /opt/script/lxd/
タイトルとURLをコピーしました