検証

【Proxmox】Ubuntu VMでmacvlanコンテナを作成し、物理ルータ経由でVLAN間通信を実現する方法

gumio

導入

仮想マシンだけで検証環境を増やしていくと、個人のラボ環境ではどうしてもリソースが不足しがちです。そこで今回は、より少ないリソースでセグメントを分けつつホストを量産する方法として、macvlanコンテナを使った構成を組んでみました。

本記事ではProxmox上の Ubuntu VMにVLANサブインターフェースを作成し、PodmanでmacvlanコンテナをVLAN100 / VLAN200の別セグメントで動作させ、物理ルータ経由でVLAN間通信を可能にする手順を解説します。

構成イメージは以下の通りです。
Ubuntu VM・コンテナA・コンテナB間でpingによる疎通確認ができる状態をゴールとします。

動作環境

以下の環境で動作を確認をしています。

  • ハイパーバイザー
    • Proxmox VE 9.0.3
  • 物理・仮想ネットワークの接続
    • Proxmoxでデフォルトで用意されているLinux Bridge(vmbr0)を使用
      VLANのタグ処理はルータとUbuntu VMで行うこととする。
  • 仮想マシン
    • Ubuntu Server 24.04.3 LTS
  • コンテナエンジン
    • Podman
  • コンテナ
    • Ubuntu 22.04
  • macvlanモード
    • bridge
  • 物理ルータ
    • C891FJ-K9

物理ルータのインターフェイス設定は以下の通りです。

interface GigabitEthernet6
 description ### Proxmox VE ###
 switchport trunk native vlan 40
 switchport trunk allowed vlan 1,2,40,100,200,1002-1005
 switchport mode trunk
 no ip address
!

手順

必要パッケージのインストール

sudo apt update
sudo apt install -y iproute2 iputils-ping iputils-arping net-tools dnsutils traceroute curl podman chrony rsyslog snmp snmp-mibs-downloader bash-completion zsh

rootユーザーへの切り替え

gumio@ubuntu-server:~$ sudo passwd root
New password:
Retype new password:
passwd: password updated successfully
gumio@ubuntu-server:~$
gumio@ubuntu-server:~$ su
Password:
root@ubuntu-server:/home/gumio#

時刻設定

root@ubuntu-server:/home/gumio# timedatectl set-timezone Asia/Tokyo
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# timedatectl
               Local time: Sat 2025-11-15 20:56:35 JST
           Universal time: Sat 2025-11-15 11:56:35 UTC
                 RTC time: Sat 2025-11-15 11:56:35
                Time zone: Asia/Tokyo (JST, +0900)
System clock synchronized: yes
              NTP service: active
          RTC in local TZ: no
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# chronyc sources -v

  .-- Source mode  '^' = server, '=' = peer, '#' = local clock.
 / .- Source state '*' = current best, '+' = combined, '-' = not combined,
| /             'x' = may be in error, '~' = too variable, '?' = unusable.
||                                                 .- xxxx [ yyyy ] +/- zzzz
||      Reachability register (octal) -.           |  xxxx = adjusted offset,
||      Log2(Polling interval) --.      |          |  yyyy = measured offset,
||                                \     |          |  zzzz = estimated error.
||                                 |    |           \
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
^- prod-ntp-3.ntp4.ps5.cano>     2   6   377    44  +4570us[+4570us] +/-  129ms
^- prod-ntp-5.ntp1.ps5.cano>     2   6   377    43  -7042us[-7042us] +/-  124ms
^- alphyn.canonical.com          2   6   377    45    -10ms[  -10ms] +/-  113ms
^- prod-ntp-4.ntp4.ps5.cano>     2   6   377    46  +3052us[+3052us] +/-  130ms
^* ntp1.hnd.jp.hojmark.net       2   6   377    47  -1443us[-2960us] +/-   24ms
^- vmi2899903.contaboserver>     4   6   353   107  +4820us[+3738us] +/-  176ms
^- 50.7.159.140                  3   7   101   167  +2892us[ +282us] +/-   14ms
^- 138.3.209.153                 2   6   373    47  -1253us[-1253us] +/-  110ms
root@ubuntu-server:/home/gumio#

Netplan設定ファイルを作成・適用

サブインターフェイスを作成するため、Netplan設定ファイルを新規作成します。あわせて、今回は自動生成されているcloud-init用のファイルは無効化します。
cloud-initを無効化する理由については、こちらの過去記事をご参照ください。

root@ubuntu-server:/home/gumio# ls -al /etc/netplan/
total 12
drwxr-xr-x   2 root root 4096 Nov 14 16:15 .
drwxr-xr-x 115 root root 4096 Nov 15 20:55 ..
-rw-------   1 root root  235 Nov 14 16:15 50-cloud-init.yaml
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# mv /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.disabled
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# ls -al /etc/netplan/
total 12
drwxr-xr-x   2 root root 4096 Nov 15 21:10 .
drwxr-xr-x 115 root root 4096 Nov 15 20:55 ..
-rw-------   1 root root  235 Nov 14 16:15 50-cloud-init.yaml.disabled
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# nano /etc/netplan/99-network.yaml
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# ls -al /etc/netplan/
total 16
drwxr-xr-x   2 root root 4096 Nov 15 21:12 .
drwxr-xr-x 115 root root 4096 Nov 15 20:55 ..
-rw-------   1 root root  235 Nov 14 16:15 50-cloud-init.yaml.disabled
-rw-r--r--   1 root root  366 Nov 15 21:12 99-network.yaml
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# netplan apply

** (generate:6504): WARNING **: 21:20:05.154: Permissions for /etc/netplan/99-network.yaml are too open. Netplan configuration should NOT be accessible by others.

** (process:6502): WARNING **: 21:20:05.358: Permissions for /etc/netplan/99-network.yaml are too open. Netplan configuration should NOT be accessible by others.

** (process:6502): WARNING **: 21:20:05.408: Permissions for /etc/netplan/99-network.yaml are too open. Netplan configuration should NOT be accessible by others.
root@ubuntu-server:/home/gumio#

99-network.yamlの中身には以下の内容を記述しています。

network:
  version: 2
  renderer: networkd
  ethernets:
    ens18:
      dhcp6: false
      dhcp4: false
      addresses: [192.168.40.10/24]
      routes:
        - to: default
          via: 192.168.40.99
      nameservers:
        addresses: [8.8.8.8, 1.1.1.1]
  vlans:
    ens18.100:
      id: 100
      link: ens18
    ens18.200:
      id: 200
      link: ens18

コンテナの参照DNSをホストと統一

DNSサーバ確認

■ホストのDNS
root@ubuntu-server:/home/gumio# cat /run/systemd/resolve/resolv.conf
# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 8.8.8.8
nameserver 1.1.1.1
search .
root@ubuntu-server:/home/gumio#

■コンテナのDNS
root@ubuntu-server:/home/gumio# cat /etc/resolv.conf
# This is /run/systemd/resolve/stub-resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients to the
# internal DNS stub resolver of systemd-resolved. This file lists all
# configured search domains.
#
# Run "resolvectl status" to see details about the uplink DNS servers
# currently in use.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 127.0.0.53
options edns0 trust-ad
search .
root@ubuntu-server:/home/gumio#

設定一致

root@ubuntu-server:/home/gumio# ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# cat /etc/resolv.conf
# This is /run/systemd/resolve/resolv.conf managed by man:systemd-resolved(8).
# Do not edit.
#
# This file might be symlinked as /etc/resolv.conf. If you're looking at
# /etc/resolv.conf and seeing this text, you have followed the symlink.
#
# This is a dynamic resolv.conf file for connecting local clients directly to
# all known uplink DNS servers. This file lists all configured search domains.
#
# Third party programs should typically not access this file directly, but only
# through the symlink at /etc/resolv.conf. To manage man:resolv.conf(5) in a
# different way, replace this symlink by a static file or a different symlink.
#
# See man:systemd-resolved.service(8) for details about the supported modes of
# operation for /etc/resolv.conf.

nameserver 8.8.8.8
nameserver 1.1.1.1
search .
root@ubuntu-server:/home/gumio#

macvlanネットワークを作成

作成コマンド

# VLAN100
podman network create \
  --driver macvlan \
  --subnet 192.168.100.0/24 \
  --gateway 192.168.100.99 \
  -o parent=ens18.100 macvlan100

# VLAN200
podman network create \
  --driver macvlan \
  --subnet 192.168.200.0/24 \
  --gateway 192.168.200.99 \
  -o parent=ens18.200 macvlan200

確認コマンド

root@ubuntu-server:/home/gumio# podman network ls
NETWORK ID    NAME        DRIVER
b31d14129146  macvlan100  macvlan
14b0a7d1dcd2  macvlan200  macvlan
2f259bab93aa  podman      bridge
root@ubuntu-server:/home/gumio#

コンテナ作成

作成コマンド

# コンテナA (VLAN100)
podman run -itd --name containerA \
  --network macvlan100 \
  --ip 192.168.100.10 \
  --cap-add=NET_RAW \
  --cap-add=NET_ADMIN \
  docker.io/library/ubuntu:22.04 \
  sleep infinity

# コンテナB (VLAN200)
podman run -itd --name containerB \
  --network macvlan200 \
  --ip 192.168.200.10 \
  --cap-add=NET_RAW \
  --cap-add=NET_ADMIN \
  docker.io/library/ubuntu:22.04 \
  sleep infinity

確認コマンド

root@ubuntu-server:/home/gumio# podman ps -a
CONTAINER ID  IMAGE                           COMMAND         CREATED         STATUS         PORTS       NAMES
87273959f8ff  docker.io/library/ubuntu:22.04  sleep infinity  17 seconds ago  Up 17 seconds              containerA
874bda1cc239  docker.io/library/ubuntu:22.04  sleep infinity  8 seconds ago   Up 8 seconds               containerB
root@ubuntu-server:/home/gumio#

コンテナで必要パッケージをインストール

podman exec -it containerA apt update
podman exec -it containerA apt install -y iproute2 iputils-ping iputils-arping net-tools dnsutils traceroute curl chrony rsyslog snmp snmp-mibs-downloader bash-completion
podman exec -it containerB apt update
podman exec -it containerB apt install -y iproute2 iputils-ping iputils-arping net-tools dnsutils traceroute curl chrony rsyslog snmp snmp-mibs-downloader bash-completion

コンテナからGARP(Gratuitous ARP)を送信

物理ネットワーク上のルータのARPキャッシュを更新するため、コンテナ側からGARPを送信します。

root@ubuntu-server:/home/gumio# podman exec -it containerA arping -A -c 3 -I eth0 192.168.100.10
ARPING 192.168.100.10 from 192.168.100.10 eth0
Sent 3 probes (3 broadcast(s))
Received 0 response(s)
root@ubuntu-server:/home/gumio#
root@ubuntu-server:/home/gumio# podman exec -it containerB arping -A -c 3 -I eth0 192.168.200.10
ARPING 192.168.200.10 from 192.168.200.10 eth0
Sent 3 probes (3 broadcast(s))
Received 0 response(s)
root@ubuntu-server:/home/gumio#

疎通確認

Ubuntuホスト→コンテナA・コンテナB

ping -c5 192.168.100.10
ping -c5 192.168.200.10

コンテナA→Ubuntuホスト・コンテナB

podman exec -it containerA ping -c5 192.168.40.10
podman exec -it containerA ping -c5 192.168.200.10

コンテナB→Ubuntuホスト・コンテナA

podman exec -it containerB ping -c5 192.168.40.10
podman exec -it containerB ping -c5 192.168.100.10

Tips

その他コマンド

コンテナのIPアドレス・MACアドレス・ゲートウェイ・DNSサーバを確認

podman inspect -f '{{.Name}}: {{.NetworkSettings.Networks.macvlan100.IPAddress}}' containerA
podman inspect -f '{{.Name}}: {{.NetworkSettings.Networks.macvlan100.MacAddress}}' containerA
podman inspect -f '{{.NetworkSettings.Networks.macvlan100.Gateway}}' containerA
podman exec -it containerA cat /etc/resolv.conf

コンテナの停止・開始・削除(複数指定可)

podman stop containerA
podman start containerA
podman rm -f containerA containerB containerC ※-fオプション有りの場合、起動中でも削除可能

macvlanの制限事項

macvlanをbridgeモードで使用する場合、ホスト(親インターフェース)とmacvlanコンテナ間では通信できないという制限があります。
今回の構成でいえば、Ubuntu VMのens18.100(親)と、その上に作成したmacvlan100コンテナの間では通信できません。そのため、そもそもサブインターフェイスにはIPアドレスをアサインしていません。

一方で、別のインターフェイス(例:ens18)とmacvlan100の間であれば通信は可能です。そのため、ホストとコンテナ間で相互通信を行いたい場合は、親インターフェースを分けてmacvlanを作成することで問題なく実現できます。

実行コマンドまとめ

sudo apt update
sudo apt install -y iproute2 iputils-ping iputils-arping net-tools dnsutils traceroute curl podman chrony rsyslog snmp snmp-mibs-downloader bash-completion zsh
sudo passwd root
su
timedatectl set-timezone Asia/Tokyo
timedatectl
chronyc sources -v
ls -al /etc/netplan/
mv /etc/netplan/50-cloud-init.yaml /etc/netplan/50-cloud-init.yaml.disabled
ls -al /etc/netplan/
nano /etc/netplan/99-network.yaml

network:
  version: 2
  renderer: networkd
  ethernets:
    ens18:
      dhcp6: false
      dhcp4: false
      addresses: [192.168.40.10/24]
      routes:
        - to: default
          via: 192.168.40.99
      nameservers:
        addresses: [8.8.8.8, 1.1.1.1]
  vlans:
    ens18.100:
      id: 100
      link: ens18
    ens18.200:
      id: 200
      link: ens18

ls -al /etc/netplan/
netplan apply
cat /run/systemd/resolve/resolv.conf
cat /etc/resolv.conf
ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
cat /etc/resolv.conf

# VLAN100
podman network create \
  --driver macvlan \
  --subnet 192.168.100.0/24 \
  --gateway 192.168.100.99 \
  -o parent=ens18.100 macvlan100

# VLAN200
podman network create \
  --driver macvlan \
  --subnet 192.168.200.0/24 \
  --gateway 192.168.200.99 \
  -o parent=ens18.200 macvlan200

podman network ls

# コンテナA (VLAN100)
podman run -itd --name containerA \
  --network macvlan100 \
  --ip 192.168.100.10 \
  --cap-add=NET_RAW \
  --cap-add=NET_ADMIN \
  docker.io/library/ubuntu:22.04 \
  sleep infinity

# コンテナB (VLAN200)
podman run -itd --name containerB \
  --network macvlan200 \
  --ip 192.168.200.10 \
  --cap-add=NET_RAW \
  --cap-add=NET_ADMIN \
  docker.io/library/ubuntu:22.04 \
  sleep infinity

podman ps -a
podman exec -it containerA apt update
podman exec -it containerA apt install -y iproute2 iputils-ping iputils-arping net-tools dnsutils traceroute curl chrony rsyslog snmp snmp-mibs-downloader bash-completion
podman exec -it containerB apt update
podman exec -it containerB apt install -y iproute2 iputils-ping iputils-arping net-tools dnsutils traceroute curl chrony rsyslog snmp snmp-mibs-downloader bash-completion
podman exec -it containerA arping -A -c 3 -I eth0 192.168.100.10
podman exec -it containerB arping -A -c 3 -I eth0 192.168.200.10
ping -c5 192.168.100.10
ping -c5 192.168.200.10
podman exec -it containerA ping -c5 192.168.40.10
podman exec -it containerA ping -c5 192.168.200.10
podman exec -it containerB ping -c5 192.168.40.10
podman exec -it containerB ping -c5 192.168.100.10

まとめ

今回の構成により、以下を実現できました。

  • VLAN100 / VLAN200の独立したネットワークをmacvlanで再現
  • Ubuntu VM 1台で複数セグメントの疎通検証が可能
  • 物理ネットワーク側のL3機器を使ったVLAN間ルーティングが可能
  • VMを増やさないため、全体のリソース消費を抑えられる

実際の現場の検証でも使用している構成のため、今後の検証環境やネットワーク設計においても幅広く応用できる仕組みです。

また、実際にはサーバを用意するというよりも、ノートPC内に仮想環境を構築し、外部ネットワークとトランク接続するケースの方が扱いやすく、より参考になるかと思います。そのため、Windows標準のHyper-Vを利用して同様の構成を構築する方法も別記事にて解説する予定です。

ABOUT ME
空花(そらはな)ぐみを
空花(そらはな)ぐみを
ネットワークエンジニア / 愛猫家
高卒工場勤務を経て31歳でIT業界へ転職し、現在はSIerでネットワークエンジニアの現場リーダーとして設計・構築を担当しています。未経験からキャリアを切り開いた経験を活かし、
・ネットワークエンジニア初級〜中級者がつまずきやすいポイントの解説
・ラボ環境を活用した検証手順
・業務で生かせる実践的なノウハウ
など、インフラエンジニアを目指す方や、現職でスキルを磨きたい方に役立つ情報を発信していきます。
記事URLをコピーしました