メインコンテンツへスキップ

Containerlabを試してみた

弊社ではネットワーク系の開発案件が多く、開発中の動作検証環境は複数の仮想マシン(VM)を用いて構築しているのですが、少し前からコンテナで構築することも行っていました。

VMの代わりにコンテナで構築するメリットは次の2点です。

  • 構成変更が設定ファイルで簡単にできる
  • コンテナ間の時刻同期が不要である

sshdのみ動作させたコンテナをノード単位で立ち上げて検証環境を構築するのですが、このような使い方はアンチパターンとされており、あまり情報がありません。さらに、ちょっと特殊な設定をしようとするとDocker Composeでは対応できないケースがあったりと、苦労する部分も少なからずありました。

そんな時、たまたま「Containerlab」なるものを知りました。コンテナをノード単位で立ち上げて検証環境を構築するというコンセプトが利用目的と合致しており、ネットワークインターフェース名の固定もできるようなので、ちょっと触ってみました。

動作環境
#

Ubuntu 24.04を新規構インストールした状態で試してみました。

~$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=24.04
DISTRIB_CODENAME=noble
DISTRIB_DESCRIPTION="Ubuntu 24.04.4 LTS"

インストール
#

以下を実行するだけで、Docker等のContainerlabが依存するパッケージも導入してくれます。

~$ curl -sL https://containerlab.dev/setup | sudo -E bash -s "all"

インストール完了後に次のコマンドを実行してから再ログインします。

~$ sudo usermod -aG clab_admins $USER
~$ sudo usermod -aG docker $USER

インストールが成功しているとこのような表示が出ます。

~$ containerlab version

 ⣴⡾⠛⠛⠖ ⢠⣶⠟⠛⢷⣦ ⢸⣿⣧  ⣿ ⠘⠛⢻⡟⠛⠛  ⣾⣿⡀  ⣿⡇ ⣿⣿⡄ ⢸⡇ ⣿⡟⠛⠛⠃⢸⣿⠛⠛⣷⡄ ⣿⡇      ⣿⡇    
⢸⣿     ⣿⡇   ⣿⡇⢸⣿⠹⣧⡀⣿   ⢸⡇   ⣸⡏⢹⣧  ⣿⡇ ⣿⡏⢿⣄⢸⡇ ⣿⣧⣤⣤ ⢸⣿⣀⣀⣾⠇ ⣿⡇⠐⠟⠛⢿⡆ ⣿⡷⠛⢿⣆ 
⠘⣿⣄  ⡀ ⢻⣧⡀ ⣠⣿⠃⢸⣿ ⠘⣷⣿   ⢸⡇  ⢠⣿⠷⠶⢿⡆ ⣿⡇ ⣿⡇ ⢻⣾⡇ ⣿⡇   ⢸⣿⠉⢻⣧⡀ ⣿⡇⢰⡟⠛⣻⡇ ⣿⡇ ⣸⡿ 
 ⠈⠙⠛⠛⠉  ⠉⠛⠛⠋⠁ ⠘⠛  ⠈⠛   ⠘⠃  ⠚⠃  ⠘⠛ ⠛⠃ ⠛⠃  ⠙⠃ ⠛⠛⠛⠛⠃⠘⠛  ⠙⠓ ⠛⠃⠈⠛⠛⠙⠃ ⠛⠛⠛⠛⠁ 

    version: 0.77.0
     commit: 05c2006e3
       date: 2026-06-28T08:07:27Z
     source: https://github.com/srl-labs/containerlab
 rel. notes: https://containerlab.dev/rn/0.77/

はじめてのContainerlab
#

まずは簡単なサンプルを試してみます。

構成
#

~/clab-test/sample1$ tree .
.
└── sample1.clab.yml # トポロジ定義ファイル

設定
#

Containerlabの設定ファイルは「トポロジ定義ファイル」と呼ばれるYAML形式のテキストファイルです。今回は「sample1.clab.yml」という名前で作成します。

  • 「sample1.clab.yml」
    name: sample1
    
    topology:
      nodes:
        ubuntu1:
          kind: linux
          image: ubuntu:24.04
          exec:
            - apt-get update
            - apt-get install -y iproute2 iputils-ping
            - ip addr add 192.168.100.1/24 dev eth1
            - ip link set eth1 up
    
        ubuntu2:
          kind: linux
          image: ubuntu:24.04
          exec:
            - apt-get update
            - apt-get install -y iproute2 iputils-ping
            - ip addr add 192.168.100.2/24 dev eth1
            - ip link set eth1 up
    
      links:
        - endpoints: ["ubuntu1:eth1", "ubuntu2:eth1"]

nodes」でノード2つ、「links」でノード間のリンクを定義しているというのが直感的にわかる構成ですね。トポロジ定義ファイルの詳細は「Topology definition」を参照してください。

構築
#

作成したトポロジ定義ファイルの環境を構築するには、トポロジ定義ファイルと同じディレクトリで次のコマンドを実行します。

~/clab-test/sample1$ containerlab deploy  

デフォルトではカレントディレクトリの「*.clab.yaml」または「*.clab.yml」をトポロジ定義ファイルとして参照します(「-t xxxx.yml」としてトポロジ定義ファイルを指定することも可能です)。

コマンド実行時のコンソール出力にERRORが含まれず、次のようになっていれば構築成功です。

05:58:47 INFO Containerlab started version=0.77.0
05:58:47 INFO Parsing & checking topology file=sample1.clab.yml
05:58:47 INFO Creating docker network name=clab IPv4 subnet=172.20.20.0/24 IPv6 subnet=3fff:172:20:20::/64 MTU=0
05:58:47 INFO Creating lab directory path=/home/bit/clab-test/sample1/clab-sample1
05:58:47 INFO Creating container name=ubuntu2
05:58:47 INFO Creating container name=ubuntu1
05:58:47 INFO Created link: ubuntu1:eth1 ▪┄┄▪ ubuntu2:eth1
05:59:00 INFO Executed command node=ubuntu2 command="apt-get update"

(snip)

05:59:00 INFO Executed command node=ubuntu1 command="ip addr add 192.168.100.1/24 dev eth1" stdout=""
05:59:00 INFO Executed command node=ubuntu1 command="ip link set eth1 up" stdout=""
05:59:00 INFO Adding host entries path=/etc/hosts
05:59:00 INFO Adding SSH config for nodes path=/etc/ssh/ssh_config.d/clab-sample1.conf
╭──────────────────────┬──────────────┬─────────┬───────────────────╮
│         Name         │  Kind/Image  │  State  │   IPv4/6 Address  │
├──────────────────────┼──────────────┼─────────┼───────────────────┤
│ clab-sample1-ubuntu1 │ linux        │ running │ 172.20.20.2       │
│                      │ ubuntu:24.04 │         │ 3fff:172:20:20::2 │
├──────────────────────┼──────────────┼─────────┼───────────────────┤
│ clab-sample1-ubuntu2 │ linux        │ running │ 172.20.20.3       │
│                      │ ubuntu:24.04 │         │ 3fff:172:20:20::3 │
╰──────────────────────┴──────────────┴─────────┴───────────────────╯
~/clab-test/sample1$ 

Docker上でどう見えるかを確認してみます。

~/clab-test/sample1$ docker ps
CONTAINER ID   IMAGE          COMMAND       CREATED         STATUS         PORTS     NAMES
5276e9a05476   ubuntu:24.04   "/bin/bash"   7 minutes ago   Up 7 minutes             clab-sample1-ubuntu1
1d9c8c600add   ubuntu:24.04   "/bin/bash"   7 minutes ago   Up 7 minutes             clab-sample1-ubuntu2
~/clab-test/sample1$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
0c1fce322d37   bridge    bridge    local
63027d02575d   clab      bridge    local
fba106cdaf33   host      host      local
a648fb69192e   none      null      local
~/clab-test/sample1$ docker network inspect clab -f '{{range .Containers}}{{.Name}} {{.IPv4Address}}
{{end}}'
clab-sample1-ubuntu2 172.20.20.3/24
clab-sample1-ubuntu1 172.20.20.2/24

コンテナと管理ネットワークは見えましたが、コンテナ間のリンクに関する情報は出てきません。

~/clab-test/sample1$ sudo ip netns ls
clab-sample1-ubuntu2 (id: 1)
clab-sample1-ubuntu1 (id: 0)
~/clab-test/sample1$ sudo ip netns exec clab-sample1-ubuntu1 ip r
default via 172.20.20.1 dev eth0 
172.20.20.0/24 dev eth0 proto kernel scope link src 172.20.20.2 
192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.1 
~/clab-test/sample1$ sudo ip netns exec clab-sample1-ubuntu2 ip r
default via 172.20.20.1 dev eth0 
172.20.20.0/24 dev eth0 proto kernel scope link src 172.20.20.3 
192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.2 

netnsでリンクの情報が見えました。つまり、ContainerlabはDocker経由ではなくNetwork Namespaceを直接操作してノード間のリンクを設定することで、トポロジ定義ファイルの記述通りのネットワークインターフェース名をコンテナに付与しているようです。

疎通確認
#

コンテナ間の疎通を試してみます。

~/clab-test/sample1$ docker exec clab-sample1-ubuntu1 ping -c 3 192.168.100.2
PING 192.168.100.2 (192.168.100.2) 56(84) bytes of data.
64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=0.303 ms
64 bytes from 192.168.100.2: icmp_seq=2 ttl=64 time=0.061 ms
64 bytes from 192.168.100.2: icmp_seq=3 ttl=64 time=0.062 ms

--- 192.168.100.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2056ms
rtt min/avg/max/mdev = 0.061/0.142/0.303/0.113 ms
~/clab-test/sample1$ 

次に、ホストOSからコンテナへのSSH接続を試してみます。

~/clab-test/sample1$ ssh clab-sample1-ubuntu1
ssh: connect to host clab-sample1-ubuntu1 port 22: Connection refused

構築時のコンソール出力を見る限り、ホストOSからコンテナ名を用いて管理ネットワーク経由でSSHする設定までしてくれているようだったのですが、今回トポロジ定義ファイルで設定したコンテナイメージにopenssh-serverが含まれていないためにSSHできませんでした。

破棄
#

構築した環境を破棄するには、構築時同様にトポロジ定義ファイルと同じディレクトリで次のコマンドを実行します。

~/clab-test/sample1$ containerlab destroy
06:26:58 INFO Parsing & checking topology file=sample1.clab.yml
06:26:58 INFO Parsing & checking topology file=sample1.clab.yml
06:26:58 INFO Destroying lab name=sample1
06:26:59 INFO Removed container name=clab-sample1-ubuntu2
06:26:59 INFO Removed container name=clab-sample1-ubuntu1
06:26:59 INFO Removing host entries path=/etc/hosts
06:26:59 INFO Removing SSH config path=/etc/ssh/ssh_config.d/clab-sample1.conf
~/clab-test/sample1$ docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
~/clab-test/sample1$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
0c1fce322d37   bridge    bridge    local
fba106cdaf33   host      host      local
a648fb69192e   none      null      local
~/clab-test/sample1$ sudo ip netns ls
~/clab-test/sample1$ 

構築で生成されたDockerのリソースやnetnsが削除されています。

応用編1
#

次は、自分でコンテナイメージを作成して使用するサンプルです。

構成
#

~/clab-test/sample2$ tree .
├── Dockerfile
├── entrypoint.sh
├── sample2.clab.yml
└── scripts/
    └── setup-iface.sh*

設定
#

コンテナ関連
#

  • 「Dockerfile」

    FROM ubuntu:24.04
    
    ENV DEBIAN_FRONTEND=noninteractive
    ENV TZ=Asia/Tokyo
    
    RUN apt-get update && apt-get install -y \
        openssh-server \
        sudo \
        tzdata \
        iproute2 \
        tcpdump \
        iptables \
        net-tools \
        curl \
        netcat-openbsd \
        iputils-ping \
        iperf3 \
        && apt-get clean \
        && rm -rf /var/lib/apt/lists/*
    
    ARG USERNAME=hoge
    ENV USERNAME=${USERNAME}
    
    RUN useradd -m -s /bin/bash ${USERNAME} && \
        usermod -aG sudo ${USERNAME} && \
        echo "${USERNAME} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/90-${USERNAME} && \
        chmod 0440 /etc/sudoers.d/90-${USERNAME}
    
    RUN mkdir /var/run/sshd && \
        sed -i 's/#PubkeyAuthentication yes/PubkeyAuthentication yes/' /etc/ssh/sshd_config && \
        sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config && \
        sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config
    
    COPY entrypoint.sh /entrypoint.sh
    RUN chmod +x /entrypoint.sh
    
    EXPOSE 22
    
    ENTRYPOINT ["/entrypoint.sh"]
    CMD ["/usr/sbin/sshd", "-D"]

ネットワーク検証で使いそうなコマンド類をインストールし、sshdだけ起動するコンテナです。SSH後の操作を簡易にするためにsudo実行時のパスワードも不要にしています。

  • 「entrypoint.sh」
    #!/bin/bash
    set -e
    
    PASSWORD="${PASSWORD:-fugafuga}"
    echo "${USERNAME}:${PASSWORD}" | chpasswd
    ssh-keygen -A
    
    exec "$@"   

DockerのSecretsUsedInArgOrEnv警告への対策と、ホストキーをコンテナ固有とするためのスクリプトです。

  • 「scripts/setup-iface.sh」

    #!/bin/bash
    set -euo pipefail
    
    IFACE="${1:-}"
    IPADDR="${2:-}"
    
    if [ -z "$IFACE" ] || [ -z "$IPADDR" ]; then
      echo "Usage: $0 <interface> <ip/prefix>"
      exit 1
    fi
    
    ip addr add "$IPADDR" dev "$IFACE"
    ip link set "$IFACE" up
    
    echo "configured $IFACE with $IPADDR"

IP設定用のヘルパースクリプトです。

トポロジ定義ファイル
#

  • 「sample2.clab.yml」

    name: sample2
    
    topology:
      defaults:
        kind: linux
        image: myubuntu:latest
        env:
          PASSWORD: fugafuga
        binds:
          - ./scripts:/scripts
    
      nodes:
        ubuntu1:
          exec:
            - /scripts/setup-iface.sh eth1 192.168.100.1/24
    
        ubuntu2:
          exec:
            - /scripts/setup-iface.sh eth1 192.168.100.2/24
    
      links:
        - endpoints: ["ubuntu1:eth1", "ubuntu2:eth1"]

sample1ではノードごとに定義していたimage指定を「defaults」として共通化し、後述の手順でDockerfileからビルドしたコンテナイメージ「myubuntu」を指定します。また、「env」でコンテナ起動時に参照される環境変数を指定し、「binds」でホストOSの「./scripts」をコンテナ上にマウントさせ、各ノードのexecでは「setup-iface.sh」によりIP設定を行えるよう簡易化しています。

コンテナイメージビルド
#

現時点のContainerlabは、コンテナイメージのビルドまでは行ってくれないので、予めビルドしておきます。

~/clab-test/sample2$ docker build -t myubuntu .
~/clab-test/sample2$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
myubuntu     latest    6e0eea4ecb37   7 seconds ago   236MB
ubuntu       24.04     ef91e4b15da8   2 weeks ago     78.1MB
~/clab-test/sample2$ 

構築
#

~/clab-test/sample2$ containerlab deploy  

apt実行済みのビルドイメージを用いているので、sample1よりも早く構築が終わりました。

疎通確認
#

コンテナへのSSH接続とコンテナ同士の疎通を試してみます。

$ ssh hoge@clab-sample2-ubuntu1
(snip)
hoge@ubuntu1:~$ ip r
default via 172.20.20.1 dev eth0 
172.20.20.0/24 dev eth0 proto kernel scope link src 172.20.20.2 
192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.1
hoge@ubuntu1:~$ exit
(snip)
~/clab-test/sample2$ ssh hoge@clab-sample2-ubuntu2
(snip)
hoge@ubuntu2:~$ ip r
default via 172.20.20.1 dev eth0 
172.20.20.0/24 dev eth0 proto kernel scope link src 172.20.20.3 
192.168.100.0/24 dev eth1 proto kernel scope link src 192.168.100.2 
hoge@ubuntu2:~$ ping -c 3 192.168.100.1
PING 192.168.100.1 (192.168.100.1) 56(84) bytes of data.
64 bytes from 192.168.100.1: icmp_seq=1 ttl=64 time=0.238 ms
64 bytes from 192.168.100.1: icmp_seq=2 ttl=64 time=0.050 ms
64 bytes from 192.168.100.1: icmp_seq=3 ttl=64 time=0.048 ms

--- 192.168.100.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2046ms
rtt min/avg/max/mdev = 0.048/0.112/0.238/0.089 ms
hoge@ubuntu2:~$ exit
(snip)
~/clab-test/sample2$ 

ホストOSからコンテナにSSH接続し、コンテナ上でコマンド実行することができました。

破棄
#

~/clab-test/sample2$ containerlab destroy  

応用編2
#

次は、同じネットワークに2台以上のノードが存在するサンプルです。

構成
#

~/clab-test/sample3$ tree .
├── Dockerfile
├── entrypoint.sh
├── sample3.clab.yml
└── scripts/
    └── setup-iface.sh*

「sample3.clab.yml」以外は応用編1と同じです。

設定
#

  • 「sample3.clab.yml」

    name: sample3
    
    topology:
      defaults:
        kind: linux
        image: myubuntu:latest
        env:
          PASSWORD: fugafuga
        binds:
          - ./scripts:/scripts
    
      nodes:
        ubuntu1:
          exec:
            - /scripts/setup-iface.sh eth1 192.168.100.1/24
    
        ubuntu2:
          exec:
            - /scripts/setup-iface.sh eth1 192.168.100.2/24
        ubuntu3:
          exec:
            - /scripts/setup-iface.sh eth1 192.168.100.3/24
        # sudo ip link add br-clab type bridge
        # sudo ip link set br-clab up
        br-clab:
          kind: bridge
    
      links:
        # 全てのノードはbr-clabとP2Pでつながる
        - endpoints: ["ubuntu1:eth1", "br-clab:eth1"]
        - endpoints: ["ubuntu2:eth1", "br-clab:eth2"]
        - endpoints: ["ubuntu3:eth1", "br-clab:eth3"]

links」は"どのノードのどのポート(インターフェース)を、どのノードのどのポートに接続するか“という物理配線を定義するものなので、P2Pの定義しかできません。そのため、2台以上のコンテナを同じネットワークにつなぐにはブリッジを経由する必要があります。

ブリッジ作成
#

Containerlabはブリッジの自動作成に未対応なので、構築前に予めホストOSでブリッジ「br-clab」を作成します。

~/clab-test/sample3$ sudo ip link add br-clab type bridge
~/clab-test/sample3$ sudo ip link set br-clab up
~/clab-test/sample3$ brctl show br-clab
bridge name     bridge id               STP enabled     interfaces
br-clab         8000.76a6a2e783bd       no       

構築
#

~/clab-test/sample3$ containerlab deploy  

ブリッジの状態を見てみると、各コンテナのネットワークインターフェースが接続されていることが確認できます。

~/clab-test/sample3$ brctl show br-clab
bridge name     bridge id               STP enabled     interfaces
br-clab         8000.76a6a2e783bd       no              eth1
                                                        eth2
                                                        eth3

疎通確認
#

コンテナへのSSH接続とコンテナ同士の疎通を試してみます。

~/clab-test/sample3$ ssh hoge@clab-sample3-ubuntu1
(snip)
hoge@ubuntu1:~$ ping -c 3 192.168.100.2
PING 192.168.100.2 (192.168.100.2) 56(84) bytes of data.
64 bytes from 192.168.100.2: icmp_seq=1 ttl=64 time=0.361 ms
64 bytes from 192.168.100.2: icmp_seq=2 ttl=64 time=0.058 ms
64 bytes from 192.168.100.2: icmp_seq=3 ttl=64 time=0.071 ms

--- 192.168.100.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2043ms
rtt min/avg/max/mdev = 0.058/0.163/0.361/0.139 ms
hoge@ubuntu1:~$ ping -c 3 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
64 bytes from 192.168.100.3: icmp_seq=1 ttl=64 time=0.310 ms
64 bytes from 192.168.100.3: icmp_seq=2 ttl=64 time=0.062 ms
64 bytes from 192.168.100.3: icmp_seq=3 ttl=64 time=0.074 ms

--- 192.168.100.3 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2049ms
rtt min/avg/max/mdev = 0.062/0.148/0.310/0.114 ms
hoge@ubuntu1:~$ 

ブリッジ経由でも問題なく疎通できていますね。

破棄
#

~/clab-test/sample3$ containerlab destroy  
~/clab-test/sample3$ sudo ip link del br-clab 

ブリッジ「br-clab」は自動では削除されないため、手動で削除する必要があります。

終わりに
#

今回の構成程度であれば、慣れていればあえてContainerlabを使わずDocker Composeだけで構築するほうが簡単です。しかし、次のようなトポロジで検証を行う場合には、経路設定等のために、リンクごとのネットワークインターフェース名を固定化できたほうが望ましいことがあります。

containerlab graph」でトポロジ定義ファイルからトポロジ図を生成してくれます。

Containerlabは本来、ネットワーク機器の検証環境を構築する目的で開発されたものなので、ノードとしてLinuxを用いる場合に優れた機能が提供されているわけではありませんが、多少の工夫を惜しまないのであればDocker Composeで頑張るよりも各自の望んだ構成をスムーズに組めるかもしれません。

関連記事

CPU Isolation

きょうは Linux カーネルがユーザーランドプロセスにスケジューリングしないCPUを確保しておく方法についてです。これによってコンテナやVMに割り当てるためのCPUが、ホストOSのユーザランドプロセスに使われてしまうことを回避できます。

WOL Configuration on Linux

Wake On LANの設定のメモ。netplanが入っているUbuntu前提だけど、ほかのディストリビューションでも参考になるかもしれない。