First off, many thanks to everyone who answered
my poll on what the next – so this – episode should be about. Your wish clearly is my command so today we
will talk about docker networking. How are we going to do this ? I thought, rather
than making a complex setup like we did in the last episode or me just reading the docker
network documentation to you – we will take a top down approach. What do I mean by that ? This episode will
be 100% hands on. We will pull one docker image and run it as
a container that gives us information about the network as the docker container sees it
from the inside and then launch that same container with different network types. We will then be able to see three things:
How we can access the container from the outside; what the container sees from the inside and
last but not least how that container can communicate with other containers on the same
network. That should give you guys a good overview
over the different network types that you can use with Docker and at the same time this
is something that you can very easily reproduce, practice and experiment with. Stay tuned. (intro) Standard reminder guys – here is the breakdown
of this episode - use the chapters if you are in a hurry and want to fast forward or
if you want to skip chapter. The chapters are here on the time line and
in the description of the video as usual. All command that I type are in the description. Thank you. The container which we will use for our tests
is the NGINX hello word example container which is nginxdemos/hello. This container provides a simple webserver
that just prints out the IP address of the container itself, so a view from the inside
and a handful of other things such as the host name and the local time. Here is what we need: We will do all this on a Linux machine. If you don’t have Linux on your desktop
then you can run a Linux image in a virtual machine or on a raspberry pi and run Docker
inside that VM or on the Pi. On the linux machine we will of course need
docker – you can install docker on a debian by running sudo apt install docker.io. In order to monitor, create and modify the
docker networks, we will use portainer as a graphical interface. Portainer can be ran directly as a container
with a simple one-liner which you can see here. Don’t worry, everything is in the description. Once we log into portainer we can then go
to the networks section here and we can nicely see the existing docker networks. By default you should see bridge, host and
none. On the command line this can be seen by typing
docker network ls. Cool, that’s all we need. We have a linux host with docker, and we have
portainer in order to visualize the docker environment. Let’s pull the image and start the first
instance of the nginx demo. On the command line you can type docker run
-d nginxdemos/hello. That will launch the container with an automatically
created name and just default settings. We will see what those are in a second. You could do the same in Portainer by selecting
Containers in the left menu, then click on Add container and specify nginxdemos/hello
as the image name. Once you click on “Deploy the container”
then the result is similar. If we browse to the containers menu item then
we can see the container listed here. As you can see, there are no published ports,
so the container would probably not be visible from outside the docker host but we can access
it from the docker host itself. But first we need to know where to find it. Let’s click on the container’s name and
scroll down to the very bottom. Here we can see the network that it joined
and also its IP address. The first thing to note is therefore that
if you launch a container without specifying network parameters, then it will default to
the bridge network and get an IP address out of that network. Here is the IP address that has been assigned
to it. So if I just copy that address and paste it
into a new browser tab, then we should get the NGINX demo page. On that page it shows us what it sees from
the inside, such as its IP address on the bridge plus the Server name which is in fact
the linux hostname of the container which in turn is a shortened version of the container
ID. Copy that name to the clipboard. We will need it in a minute. So far, that was very easy. All we had to do was launching an image with
default values. We have learnt that the default network is
the bridge network, which only exists on the docker host, so it’s not visible or accessible
from the outside world. The hostname of a container on the bridge
by default is a shortened version of the docker container ID. Let’s see how two of those containers can
communicate with each other. In order to do that I need a second instance
of that container. In Portainer that can be done by clicking
on the container name, then clicking on Duplicate/Edit. I change the name of the new container and
untick pull the image – we can use the existing image – and then further down I click on
Deploy the container. On the command line we would just have done
another docker run -d and then the image name. As expected, we have two containers here in
the container section. In Portainer, we can easily execute a shell
or console inside the container from the quick actions column here. The command in this demo container is /bin/ash. On the command line you would do docker exec
-it /bin/ash. Let’s connect. IP addr shows us the IP address of this new
container. It’s on the same bridge of course. So I should be able to ping at least myself
– OK – and the other container – that works as
well. So we do have IP connectivity between containers
that are on the same bridge. Let’s check the default route. IP route shows us that there is a default
route via the docker host’s IP on the bridge so presumably the docker host acts as a gateway
here. Let’s see if we can get to the internet. I can ping google’s name server – so yes,
we have internet access from inside the container. Let’s check name resolution to www.google.com
– that works as well. Awesome – we already know so much about
Docker networking just by looking at the behavior of the default settings. We know that there is a default bridge that
can not be accessed from the outside, but it gives us IP connectivity and name resolution
to the outside. Plus we have IP connectivity between containers
on the same bridge. Before we do a last test with this setup I
want to draw your attention to a very important detail here. That is the hostname of the docker container
depending on how we create it. Let’s browse to the NGINX on the second
container here – and – surprise – it has the same hostname like the first container. That can easily lead to confusion. The reason is that I cloned it in Portainer
from the first one and I did not modify the hostname down in the network section before
I cloned it. So we can’t really check name resolution
between containers here because we would get a false positive as they both have the same
name. Let’s create another instance of the demo
container from scratch without cloning. That one got a new hostname. Back to portainer. Launch a console inside that new container. Quickly check the ip address and the hostname
Now let’s try to ping the host name of the first container. And – that doesn’t work. We have NO name resolution between containers. Perfect – now we have the complete image
of what the default settings and the default bridge network provide us with. Let’s now take this to the next level. So far we can not access the containers from
the outside world. The most common way of making a docker container
visible from the outside is port mapping. Let’s create a new instance of our demo
image here. This time we map a port, let’s say port
81 to port 80 inside the container. We can do this here with this little grey
button. 81 on the host to 80 in the container. Deploy. On the command line you would do this with
the -p option of the docker run or docker start command. The first apparent difference here in the
containers list is that we have an entry in the published ports column. So we can now browse to localhost colon 81
or – from the outside – the IP address of our docker host colon 81 and we will always
get the same result – the NGINX page shows us the IP address of the bridge. Great – accessing a docker container from
the outside can easily be done by mapping a port on the host to a port inside the container. If you have a bit of experience with VMWare
Workstation or Virtualbox then you could say that the docker bridge pretty much acts like
the NAT network in Virtualbox. Mapping a port is hence pretty much like a
port forward on a router to the LAN. In fact the docker host forwards port 81 on
its own network to port 80 on the container’s bridge interface. One thing that is annoying still is the fact
that we have no name resolution between docker containers. If you had for example a web server and a
database server that need to communicate with each other, it would come in handy if they
could access each other by their respective names rather than using IP addresses. Let’s see how we can solve that challenge. The docker bridge network driver provides
name resolution in principle. Just not on the default bridge. Hence, we just create our own. Click on Networks, then click on the blue
“Add Network” button. I call my new network marcsBridge, I use the
bridge driver and assign a new network IP address range to it, for example 172.172.0.0/24. the docker host itself should be reachable
under the 172.172.0.1 address. The IP addresses which it should give to containers
should just be the upper half of that range, so .128/25 at the end. Click on create the network. Back to the containers section. Let’s scrap all the containers which we
don’t need any more. Select all of them and then click on stop
if they are running. Do the same thing and then click on remove. Pay attention to not stop or remove the portainer
container itself. We still need it. OK – nice and clean environment. Now we recreate two NGINX demo containers. But this time with a hostname and on the new
bridge. Add Container
Give it a name – I use nginx1 Type in the image name
Now browse down the Network tab. Open the Network dropdown and select marcsBridge
Important – give it a hostname. I use the same name like the container name. Let’s deploy. Do the same thing again for a second container
which I have called nginx2. Now – if I open a console in one of those
containers and again check the ip address and the host name then we see here the values
which we have defined in portainer. I can of course ping the container itself
with its host name and – that is the big difference between this bridge and the system
bridge – I can also ping the other container with its host name. So Docker does provide name resolution between
containers over a self-defined bridge. Cool – as we could see, a bridge is very
flexible. We can access the outside world, we have full
routing and name resolution between the containers on the same host and bridge and we can access
a container on the bridge from the outside by mapping ports. The only inconvenience here is that if we
want to replicate this setup and automate the deployment then it’s actually a lot
of clicks or commands. There is however a simple solution to this
– and that’s Docker compose. Docker Compose allows us to define multiple
containers as so called services in a stack. That’s just terminology. If you have watched my raspberry pi video
about the IOTStack project – that is one example. For this episode we will just create a very
simple stack of two instances of the nginx demo container. Let’s quickly install a couple of things
here. First we need to install docker-compose by
typing apt install docker-compose in a shell on the host. Second I want a decent editor. For this I will use Microsoft – yes you
heard right – Microsoft Visual Code. I can easily install this by downloading a
deb or rpm file from the visualstudio.com website. The reason why I want to use Visual code here
is that there are loads of extensions available for different source code types. I have installed an extension for docker compose
files here. Now look what happens when I open the folder
where my little example compose file is located. The file is called docker-compose.yml and
visual code has associated it with the extension. Inside the file you can see that I have defined
two services inside that stack. One is called nginx1, the image is the nginxdemo/hello
container and I will be mapping host port 81 to port 80 of the container. Same scenario for service nginx2. Before I bring up this stack I want to quickly
scrap the containers from the last exercise and also the bridge which I had designed. Back to Visual Code. Here I can just do a right click on the yml
file and select compose up. You could also do this on the command line
without visual Code by typing the docker-compose command as it is shown here in the terminal
window. Very convenient, especially if you are writing
docker compose files on your own. Now what has this done in Docker ? Let’s
check in portainer. First the containers. Here we see the two containers which have
been created with the corresponding port mappings. They are both part of the same stack which
by default is the name of the folder where I put the yml file, in my case dockertest. Let me check the properties of that container. If I scroll down to the network part then
I can see that this container uses a bridge which is called dockertest_default. Let’s check it out. Clicking on the network name brings me to
the details page of that network. It is a bridge network and further down we
can see the two containers which are actually using it. In my case nginx1 and nginx2. Please note the little “Leave Network”
button here which is another big advantage of user-defined bridges – you can leave
and join networks on the fly. Let’s go back to the containers and open
the nginx demo pages. Hmmm…. The host name again is that cryptic container
name. Will Name resolution work as desired ? Let’s
check it out. We open a console on nginx1
hostname shows that cryptic docker name ping nginx2 – and it works!!! Just another quick remark on the docker stack
– if you don’t want to use an editor like visual code and you just want to bring up
a docker-compose.yml file, then you can also do this from within portainer. Go to stacks, click on Add stack, give it
a name and copy paste the content of the yml file into that window. Scroll down and click on “Deploy this stack”. Keep in mind that if you are on one single
host only version 2 yml files are supported plus I got that Error message saying the stack
could not be deployed but it looked like everything went OK. Might be a bug or a feature in portainer. Just wanted to point this out quickly. Awesome – guys, when I started writing this
episode I didn’t think that there was so much to show on bridges alone, so let’s
stop here – I think you have a couple of things to play and experiment with – in
the next episode we will have a look at the other network types such as the host or macvlan
network. And then there is still the overlay network
and also maybe we will have a look at how we can import a disk image of a virtual machine
into a docker container so that we can for example run OpenWrt in a container and build
a dockerized version of our test lab network. Until then, many thanks for watching, liking,
commenting and subscribing. Stay safe, stay healthy, bye for now.