First day with Docker
by Sebastien Mirolo on Sun, 14 May 2017(This post was updated for running Docker on Fedora 25) Since Docker was released as open-source, it has spread like wildfire. Both Amazon and Google have been quick to support Docker on their respective cloud.
Docker is great because it implements something akin to the old times chroot, jail or LXC containers (albeit a lot more powerful), bundled with something akin to pip (or rubygem) for virtual machines.
What we are looking to achieve Today is deploy a Django webapp in a Docker container on a Fedora 25 system. The Fedora 25 system is itself boot up from a stock Fedora image on a VMware Fusion virtual machine. We will also want to reconfigure the nginx front-end load balancer to redirect requests to the Django webapp.
Enough presentation, let's dive into the setup.
Installing Docker
Following How to run Docker containers on CentOS or Fedora, the first steps are straightforward.
$ sudo groupadd docker $ sudo dnf install docker $ sudo systemctl start docker.service $ sudo systemctl enable docker.service $ sudo usermod -a -G docker $USER
Then logout and log back in.
Building a Docker container
Dockerizing a Python Web App is a little more complex than it could for a tutorial starter, yet still a good place to begin.
We will prefer to start a Django project from scratch here.
$ sudo dnf install python-virtualenv $ virtualenv dockerized-webapp $ mkdir -p dockerized-webapp/reps $ cd dockerized-webapp/reps $ source ../bin/activate $ pip install Django $ django-admin.py startproject myapp $ cd myapp $ cat Dockerfile FROM ubuntu:12.10 # Install Python Django RUN apt-get install -y python-virtualenv RUN pip install Django # Bundle app source ADD . /src # Expose Expose 8000 # Run CMD ["python", "/src/manage.py", "runserver", "0.0.0.0:8000"] $ docker build -t myapp .
First Hick-up
Apt-get would not be able to access the Internet from with the container. There are reference to this kind of issues here and here. Debugging turned out into one of these "try random things until it works" session. It didn't help that ping was not available in the base container. Among other things I created a /etc/default/docker file as follows:
$ cat /etc/default/docker DOCKER_OPTS="--dns 8.8.8.8 --dns 8.8.4.4"
Updated the iptables rules on the Fedora system,
Also rebooted the virtual machine ... twice.
Miraculously the container started to be able to access the Internet.
Running a Docker container
Running the docker container at this point is pretty straightforward:
$ docker run -d -p 8000:8000 myapp
I can access the myapp Django starting page through the web browser on the Mac at this point by typing the url: http://<vm_ip_addr>:8000/.
Before moving, time to familiarize ourselves with a few useful docker commands:
$ docker run -i -t myapp /bin/bash $ docker ps -a $ docker images $ docker stop [container-id]
Automated Nginx Reverse Proxy for Docker
This post and the associated tools developed for it are perfect for the job.
First install and start nginx,
$ sudo dnf install nginx $ sudo systemctl start nginx.service $ sudo systemctl enable nginx.service
Then download the docker-gen tool
$ wget https://github.com/jwilder/docker-gen/releases/download/0.3.1/docker-gen-linux-amd64-0.3.1.tar.gz $ tar xvzf docker-gen-linux-amd64-0.3.1.tar.gz
And create a template file
$ cat nginx.tmpl {{ range $host, $containers := groupBy $ "Env.VIRTUAL_HOST" }} upstream {{ $host }} { {{ range $index, $value := $containers }} {{ with $address := index $value.Addresses 0 }} server {{ $address.IP }}:{{ $address.Port }}; {{ end }} {{ end }} } server { listen 80; server_name {{ $host }}; location / { proxy_pass http://{{ $host }}; include /etc/nginx/proxy_params; } } {{ end }}
We also need to create a /etc/nginx/proxy_params file here since we are not on Ubuntu. Fedora configures nginx quite differently here.
$ cat /etc/nginx/proxy_params proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; # proxy_redirect default; proxy_redirect off;
Second Hick-up
docker-gen: invalid cross-device link
Here docker-gen assumes the tempdir is on the same device as the final target file. The standard Fedora installation will put tempdir on its own partition. Fortunately docker-gen uses the standard Go util to create a temporary file and the Go library respects the TMPDIR shell variable.
We get it to work like this:
$ sudo sh -c 'TMPDIR=/etc/nginx/tmp ./docker-gen -only-exposed -watch \ -notify "/etc/init.d/nginx reload" \ nginx.tmpl /etc/nginx/conf.d/default.conf'
We start the docker container this way to trigger a new nginx default.conf file
$ docker run -e VIRTUAL_HOST=demo.localhost.localdomain -t myapp
We are almost there. Since we are not running any DNS server on our Fedora virtual machine, we first update /etc/hosts so we can find demo.localhost
$ diff -u prev /etc/hosts -127.0.0.1 localhost localhost.localdomain -::1 localhost localhost.localdomain +127.0.0.1 demo.localhost localhost localhost.localdomain +::1 demo.localhost localhost localhost.localdomain
Everything should be in place to be able to access the webapp within the docker container. We run the following command from within the Fedora virtual machine.
$ wget http://demo.localhost/
On a side note, looking around we found the following post to trigger the docker API while it is running on a unix socket.
echo -e "GET /images/json HTTP/1.0\r\n" | nc -U /var/run/docker.sock
Pretty awesome!
Third Hick-up
At this point we get a "502 Bad Gateway" error. The nginx logs show a permission denied.
$ tail /var/log/nginx/error.log ... (13: Permission denied) while connecting to upstream
Updating SELinux policies
Albeit in a different context, there is a great article that explains what is going on.
SELinux prevents nginx to connect to the docker container. Let's not forget that the docker containers run on a different subnet.
$ sudo dnf install policycoreutils-devel $ sudo sh -c 'grep nginx /var/log/audit/audit.log | audit2allow -M nginx' $ sudo semodule -i nginx.pp
Conclusion
That's it. We can now start to bring containers up and down and get the nginx redirect updated on the fly. So cool!
More to read
If you are looking for related posts, Docker on Elastic Beanstalk and building a Docker container and pushing it to Amazon EC2 Container Registry are good reads.
More technical posts are also available on the DjaoDjin blog. For fellow entrepreneurs, business lessons learned running a subscription hosting platform are also available.