There is usually a problem with finding a container that does exactly what you want it to do. It's either missing something completely or needs some level of modification in order to work correctly. My current gripe is with the Nginx container used as the reverse proxy for the security camera project. It should be able to proxy the Real Time Streaming Protocol (RTSP). Unfortunately it is missing the codecs and module to allow this capability in Nginx. The good news is that we can add it ourselves.
I have spent a bit of time tracking down vendors that could teach a "containers from scratch" course. Mostly because documentation was lacking, or was written for a very specific version of Docker. This was when Docker docs was still coming up, and basically contained what you could find in the man pages. The few blogs that contained information that was relevant were assuming some basic level of knowledge that hadn't really been included in their own documentation. I had a few questions, and now I can provide answers.
My top three questions:
How do you build a base image? You don't. It takes too much effort and we are at a point where you can get your image from a pull very fast. Yes, even for the security minded, you can pull faster than build. There are verified/certified registries out there with guaranteed clean images. Vendors like RedHat tend to cost a little bit of money, or the CERN scientific images can be pulled for free. Although I use Debian, Ubuntu, and BSD at home, I have always worked with RPM based distros professionally. For the standard home user, Ubuntu has the best documentation and therefore has better troubleshooting options. Keep this in mind when you pull.
What is a base image and what can I do with it? A base image is any image you pull. The OS base images are just environments that operate like an empty operating system for a server. No GUI, no applications, just a small environment of directories required to run the container. To make it useful, you will need to connect to it and add applications. The base image for an application is the OS base image plus the application and whatever is required to run it. Let's say you pull a MySQL base image. The dockerfile or registry will tell you what OS image it is running on (Ubuntu, Alpine, CentOS), and then list what else was added so the application can run. Since you will need to modify the Database for it to work, you will need to connect to it to modify it. Once all modifications and additions are complete, you commit the image and deploy it.
How do I migrate an image to a new environment? This is a big one for me. There are environments where you absolutely cannot pull a docker image. Or, you can't pull the image that you created off of your preferred registry/account. So, how do you add a container to these environments? Start with a system that you can use to pull. You can then use docker export or docker save to create a tarball and then docker import to recover. I'm seriously disappointed that they couldn't come up with something clever about "port" in the import/export commands.
While I was getting started with containers, I was under the impression that you would have to:
Build a virtual machine
Install docker
Pull images that have everything you need
Export the virtual machine to a new environment
That's almost completely wrong.
As long as you are running an OS that can run docker, all you need to do is pull an image and modify it locally, or move it and modify it at the new location. Pulling patches and applications before you move it can cut down on the amount of dependency hell and source code downloads/moves that are required to complete the configuration.
Let's go ahead and review the questions again, fixing Nginx as an example. Because we need to compile modules into Nginx, we cannot start from the Nginx base image, we have to build from a Base OS. We start by pulling the Base OS image. I'm using Ubuntu for this example.
Next, run the container "docker run -ti --name nginxfix ubuntu:latest". This will start the container, open a bash shell on the container, and allow you to issue commands inside of the container. You can now pull patches, install applications, and get source code. For containers with an application already installed, you may just need to start them and perform "docker exec -t -i container_name /bin/bash" to get to a shell. Base OS images tend to die quickly when you start them, since they don't have any applications to run.
Aggregate 1
Getting a shell on a Base OS container
Aggregate 2
Getting a shell on an application container
The actual package downloads and installs are a little intimidating. We start with using apt to install required packages:
"apt-get install ffmpeg liblivemedia-dev git wget gcc libpcre3-dev libssl-dev make libaio1 libaio-dev vim"
Next, we pull the source:
"wget www.nginx.org/download/latest_version_tarball". Unzip and extract the source. Then, for the RTMP module, "git clone https://github.com/arut/nginx-rtmp-module.git". Now we look at the instructions to build with the modules.
Aggregate 3
Build Nginx
The actual command to build Nginx so that it is very similar to the official source looks like this:
./configure --add-module=/opt/nginx-rtmp-module --with-http_ssl_module --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic'
And yes, that is all one command.
We need to create the service. "vi /lib/systemd/system/nginx.service"
Insert:
[Unit]
Description=nginx - high performance web server
Documentation=http://nginx.org/en/docs/
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target
[Service]
Type=forking
PIDFile=/var/run/nginx.pid
ExecStart=/usr/sbin/nginx -c /etc/nginx/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
[Install]
WantedBy=multi-user.target
Now enable the service by using "systemctl enable nginx.service
Aggregate 4
It might also be a good idea to compare the /etc/nginx/nginx.conf file from an existing Nginx base container and modify as necessary. Last, we add the nginx user "useradd -s /bin/false nginx"
Now we can commit and test. I am concerned about the container collapsing when I log out of it, so I just opened a new terminal and listed running containers, then performed a docker commit.
Because I put a little bit of effort into building this image as close as possible to the official Nginx container, we should be able to review the official Dockerfile to see what kind of settings we need to make it run. At this point, any errors are going to be configuration errors. We can now prepare to move it.
You can save the image or export it. Exporting retains changes made to the container. Once it is exported, copy it to a disk, and move it to where it needs to go. The commands are listed in the following aggregate and are much easier to understand after reading through it.
Aggregate 5
Import and Export
Once I have sorted out any configuration changes required in the modified Nginx container, I will replace the existing Nginx reverse proxy that I have and test out the RTSP streaming. If everything goes well, I will post about uploading to Dockerhub, local registries, and creating a Dockerfile.