Creating a Bridge and Shared Network on Docker

Sometimes I want to use Docker containers like regular VMs, creating a bridge on a Docker host, having containers on the same subnet, and then logging into them via port 22. (No port forwarding, please.) So here's how to do it.

I use Vagrant and VirtualBox on my MacBook in this example, and create containers with IP addresses shown on the table below. Once you go through the steps, you should be able to extend the idea into having Docker containers on your on-premises network.

|Host                | IP Address     |
|:-------------------|:---------------|
| MacBook            | 192.168.33.1   |
| host1(VirtualBox)  | 192.168.33.10  |
| container1         | 192.168.33.11  |
| container2         | 192.168.33.12  |
| container3         | 192.168.33.13  |

## Prerequisites

My OS is Mac OS X 10.11.4, and I use the following applications - versions:

* [VirtualBox - v5.0.16](https://www.virtualbox.org/wiki/Downloads)
* [Vagrant - v1.8.1](https://www.vagrantup.com/downloads.html)
* [Docker Toolbox - v1.10.3](https://www.docker.com/products/docker-toolbox)

## Creating a `Vagrantfile`

You may want to go with `docker-machine create -d virtualbox <hostname>`, but it is important to change the network `Adapter Type` and `Promiscuous Mode` in order to make the network bridge work on VirtualBox. With using `Vagrantfile`, you can make the change easily, which looks like this:

```ruby
Vagrant.configure(2) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.hostname = "host1"
  config.vm.network "private_network", ip: "192.168.33.10"

  config.vm.provider "virtualbox" do |v|
    v.memory = "2048"
    # Change the network adapter type and promiscuous mode
    v.customize ['modifyvm', :id, '--nictype1', 'Am79C973']
    v.customize ['modifyvm', :id, '--nicpromisc1', 'allow-all']
    v.customize ['modifyvm', :id, '--nictype2', 'Am79C973']
    v.customize ['modifyvm', :id, '--nicpromisc2', 'allow-all']
  end

  # Install bridge-utils
  config.vm.provision "shell", inline: <<-SHELL
    apt-get update
    apt-get -y install bridge-utils
  SHELL
end
```

## Creating a Docker machine

```bash
# Create a VM
$ vagrant up

# Setup the VM as your Docker machine
$ docker-machine create \
      --driver "generic" \
      --generic-ip-address 192.168.33.10 \
      --generic-ssh-user vagrant \
      --generic-ssh-key .vagrant/machines/default/virtualbox/private_key \
      --generic-ssh-port 22 \
      host1
```

## Creating a bridge `docker1` and shared network `shared_nw`

```bash
# Log in to the VM via eth0
$ vagrant ssh

# Delete the IP address from eth1
$ sudo ip addr del 192.168.33.10/24 dev eth1

# Create "shared_nw" with a bridge name "docker1"
$ sudo docker network create \
    --driver bridge \
    --subnet=192.168.33.0/24 \
    --gateway=192.168.33.10 \
    --opt "com.docker.network.bridge.name"="docker1" \
    shared_nw

# Add docker1 to eth1
$ sudo brctl addif docker1 eth1
```

## Running Containers

Go back to your MacBook, and launch containers:

```bash
$ eval $(docker-machine env host1) # Setup the environment
$ docker run --name container1 --net shared_nw --ip 192.168.33.11 -dt ubuntu
$ docker run --name container2 --net shared_nw --ip 192.168.33.12 -dt ubuntu
$ docker run --name container3 --net shared_nw --ip 192.168.33.13 -dt ubuntu
```

And now you should be able to reach the containers' IP addresses:

```bash
$ ping -c 3 192.168.33.11
$ ping -c 3 192.168.33.12
$ ping -c 3 192.168.33.13
```

Doesn't it feel great to be able to assign a specific IP address to a container?

## Making the network setting permanent

At this point, the network bridge can be detatched from `eth1` if you reboot the machine. So here are two more things to make the setting permanent.

Update `/etc/network/interfaces` like this:

```
auto eth1
iface eth1 inet manual
   pre-up ifconfig $IFACE up
   post-down ifconfig $IFACE down
```

And add the following command on `/etc/rc.local` before `exit 0`:

```bash
brctl addif docker1 eth1
```

## Cleaning up

After finishing your test, you can remove the containers and the VM:

```bash
$ docker stop $(docker ps -aq) # Stop all containers
$ docker rm $(docker ps -aq) # Remove all containers
$ docker-machine rm host1 # Remove host1 from your machine list
$ vagrant destroy # Remove the VM
```

## Where to go next

You may want to try [Dockerizing an SSH daemon service](https://docs.docker.com/engine/examples/running_ssh_service/).

## References

The following links are really helpful to understand Docker's network, and highly recommended.
* [Docker - Network Configuration](https://docs.docker.com/v1.5/articles/networking/)
* [Docker - network create](https://docs.docker.com/engine/reference/commandline/network_create/)
* [Odd Bits - Four ways to connect a docker container to a local network](http://blog.oddbit.com/2014/08/11/four-ways-to-connect-a-docker/)
* [Github - jpetazzo/pipework](https://github.com/jpetazzo/pipework)
* [Stack Overflow - Assign static IP to Docker container](http://stackoverflow.com/questions/27937185/assign-static-ip-to-docker-container)