I would like to have a single server which serves as Docker host and KVM at the same time using the same network bridges. I am not going to use NAT in Docker and will use the same L2 bridges used by KVM. I am going to use Ubuntu 18.04 LTS
Choose Ubuntu minimal install and only select SSH server to be installed additionally.
We are going to use netplan for network configuration. In our example we have one network interface with multiple VLANs on it. We create bridge interfaces using these VLAN interfaces.
network:
version: 2
renderer: networkd
ethernets:
eno1:
accept-ra: no
dhcp4: no
dhcp6: no
vlans:
vlan8:
id: 8
link: eno1
accept-ra: no
dhcp4: no
dhcp6: no
vlan9:
id: 9
link: eno1
accept-ra: no
dhcp4: no
dhcp6: no
vlan10:
id: 10
link: eno1
accept-ra: no
dhcp4: no
dhcp6: no
bridges:
mgmt:
accept-ra: no
addresses:
- x.x.2.x/24
- x:x:x:2::x/64
gateway4: x.x.2.1
gateway6: x:x:x:2::1
nameservers:
addresses: [ "x.x.x.x", "208.67.222.222" ]
search: [ mgmt ]
interfaces: [eno1]
internet:
accept-ra: no
dhcp4: no
dhcp6: no
interfaces: [vlan8]
dmz:
accept-ra: no
dhcp4: no
dhcp6: no
interfaces: [vlan9]
local:
accept-ra: no
dhcp4: no
dhcp6: no
interfaces: [vlan10]
apt purge snapd
apt install qemu-kvm libvirt-clients libvirt-daemon-system bridge-utils virt-manager systemctl start libvirtd systemctl enable libvirtd
Because we already have the bridges created with netplan, we only need to assign them to networking names in libvirt.
<network> <name>internet</name> <forward mode='bridge'/> <bridge name='internet'/> </network>
<network> <name>dmz</name> <forward mode='bridge'/> <bridge name='dmz'/> </network>
<network> <name>local</name> <forward mode='bridge'/> <bridge name='local'/> </network>
<network> <name>mgmt</name> <forward mode='bridge'/> <bridge name='mgmt'/> </network>
Now we have to activate the xmls
virsh net-destroy default virsh net-undefine default virsh net-define internet.xml virsh net-define dmz.xml virsh net-define local.xml virsh net-define mgmt.xml virsh net-start internet virsh net-start dmz virsh net-start local virsh net-start mgmt virsh net-autostart internet virsh net-autostart dmz virsh net-autostart local virsh net-autostart mgmt
Check if everything is ok
virsh net-list
Name State Autostart Persistent ---------------------------------------------------------- dmz active yes yes internet active yes yes local active yes yes mgmt active yes yes
Because we are going to use Docker with the same bridges, we have to wait for libvirt to start after Docker is started. Else the bridges are not available and autostart of VMs will fail.
cd /etc/systemd/system rm libvirt-bin.service cp /lib/systemd/system/libvirtd.service . vi libvirtd.service
. . . After=network.target After=dbus.service After=iscsid.service After=apparmor.service After=local-fs.target After=remote-fs.target After=systemd-machined.service After=docker.service Documentation=man:libvirtd(8) Documentation=https://libvirt.org [Service] . . .
Now we need to reload systemd
systemctl daemon-reload
apt install docker.io systemctl enable docker
We need docker-compose and are going to install it via pip
apt install python3-pip
Now install docker-compose
pip3 install docker-compose
Docker is using iptables to mitigate traffic between containers. Because we are using VLANs with bridges, we do not want Docker to intervene network traffic.
{
"iptables": false
}
When we stop docker to manipulate iptables it will not add the masquerade rule to the docker0 bridge. We need this iptables rule to make docker build work again. So first we are going to add the iptables rule manually. Find out which subnet is connected to docker0
ip a s docker0
11: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:b5:d4:a8:53 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:b5ff:fed4:a853/64 scope link
valid_lft forever preferred_lft forever
In this case the subnet is 172.17.0.0/16. Add the rule
iptables -t nat -A POSTROUTING -s 172.17.0.0/16 -j MASQUERADE
Now we have the rule added. Make it persistent. First install iptables-persistent
apt install iptables-persistent
It will tell you it created rules in: /etc/iptables/rules.v4 and /etc/iptables/rules.v6. Check the rules.v4 file for the masquerade rule
cat /etc/iptables/rules.v4
# Generated by iptables-save v1.6.1 on Fri Jul 26 18:19:47 2019 *nat :PREROUTING ACCEPT [468:72707] :INPUT ACCEPT [22:10893] :OUTPUT ACCEPT [8:601] :POSTROUTING ACCEPT [8:601] -A POSTROUTING -s 172.17.0.0/16 -j MASQUERADE COMMIT # Completed on Fri Jul 26 18:19:47 2019 # Generated by iptables-save v1.6.1 on Fri Jul 26 18:19:47 2019 *filter :INPUT ACCEPT [2328:191223] :FORWARD ACCEPT [3836:8412380] :OUTPUT ACCEPT [1473:233542] :DOCKER-USER - [0:0] -A FORWARD -j DOCKER-USER -A DOCKER-USER -j RETURN COMMIT # Completed on Fri Jul 26 18:19:47 2019
When you have a ZFS pool you want to use for storage
systemctl stop docker rm -rf /var/lib/docker zfs create -o mountpoint=/var/lib/docker data/docker
{
"storage-driver": "zfs",
"iptables": false
}
systemctl start docker
Check with docker info
docker info
Client: Debug Mode: false Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 19.03.8 Storage Driver: zfs Zpool: data Zpool Health: ONLINE Parent Dataset: data/docker Space Used By Parent: 98304 Space Available: 965292744704 Parent Quota: no Compression: off . . .
In our example we are not going to use containers in the routed subnet bridge called internet
docker network create -d macvlan --subnet=x.x.0.0/24 --gateway=x.x.0.1 --ip-range=x.x.0.128/28 -o parent=local local
docker network create -d macvlan --subnet=x.x.1.0/24 --gateway=x.x.1.1 --ip-range=x.x.1.128/28 --subnet=x:x:x:1::/64 --gateway=x:x:x:1::1 --ip-range=x:x:x:1::128/124 -o parent=dmz --ipv6 dmz
docker network create -d macvlan --subnet=x.x.2.0/24 --gateway=x.x.2.1 --ip-range=x.x.2.128/28 --subnet=x:x:x:2::/64 --gateway=x:x:x:2::1 --ip-range=x:x:x:2::128/124 -o parent=mgmt --ipv6 mgmt
Check it
docker network ls
NETWORK ID NAME DRIVER SCOPE 6ae8a5b99638 bridge bridge local 700edcbcc28b dmz macvlan local 06d5b0706837 host host local cb04700d3481 local macvlan local b0bdff0a6d53 mgmt macvlan local 320e38274915 none null local