Docker is one of my favorite tool in IT – it gives me really fast access to already configured&installed tools. Using that feature I can cover many cases using simple docker container and some magic around. In this article series I’m going to show you how to use docker in creative way and how to debug problems inside docker containers.
Today something about CMD, magic in runtime and docker commit.
I have assumption that you have basic knowledge about docker and some experience in using that great tool – if not I suggest to check that site: https://docs.docker.com/engine/docker-overview/
My environment in this article:
- Docker version 17.12.0-ce, build c97c6d6
- Ubuntu 17.10
- zsh 5.2 (x86_64-ubuntu-linux-gnu)
- tmux 2.5
Everything should also work in docker on windows (this one using hyper-v) – if there is problem somewhere please contact me on my linkedin
Seed your container in runtime
Dockerhub provides many images (as mentioned here in 2016 was 400k) with useful defaults but these defaults rarely meet our requirements. To deal with that situation we can take many strategies but one of my favorite is overriding default docker command with some magic.
Let’s say that we want to host jakubbujny.com blog in docker container and switch my site logo to
just for fun!
- Start container
- Download jakubbujny.com site content recursive
- Start web hosting
- Change image
We need to start with some static web content hosting like https://hub.docker.com/_/nginx/
We have command from this site (with port publish):
docker run -p 8080:80 --name some-nginx -v /some/content:/usr/share/nginx/html:ro -d nginx
Ok but after staring this container we can see only welcome site from nginx – how to inject content there? There are 2 ways:
- Add content to docker image – useful if we want to use that image many times
- Override run command and download content before web server start – our approach because we are fast&furious
So what’s default command in nginx? To check that we should go to origin Dockerfile like this and at end we see:
CMD ["nginx", "-g", "daemon off;"]
Cool, let’s try this way:
docker run -it -p 8080:80 nginx bash -c "cd /usr/share/nginx/html && wget --recursive --no-parent --no-check-certificate https://jakubbujny.com ; nginx -g 'daemon off;'"
What’s happening there:
- docker – 😉
- run – start new container
- -it – interactive (get stdout, pass stdin)
- -p 8080:80 – publish internal nginx 80 port onto our machine 8080 so we can access nginx using localhost:8080
- bash -c ” ” – that’s little magic to pass some long command with &&, ; – bash man says:
-c string If the -c option is present, then commands are read from string. If there are arguments after the string, they are assigned to the positional parameters, starting with $0.
- cd… – sure
- wget – we need to download recursive my site
- nginx -g – start nginx (use default command from Dockerfile)
Ok so let’s run it… woooops!!! Something is not working ;(
bash: wget: command not found
Please remember that every docker container is isolated virtual OS (so actually it’s not but you can think about docker in this way 😉 ) – it means that every docker image has different tools installed. Sadly nginx image doesn’t contain wget tool but okey dokey – we can install wget using apt-get inside container, no worries:
docker run -it -p 8080:80 nginx bash -c "apt-get update && apt-get install --no-install-recommends --no-install-suggests -y wget && cd /usr/share/nginx/html && wget --recursive --no-parent --no-check-certificate https://jakubbujny.com ; nginx -g 'daemon off;'"
What’s happening there:
- sudo not needed because inside container we are root
- apt-get update – very important! Please remember to run that command before any apt-get install inside containers to get valid result
- apt-get install – here -y is important because that command will run in non-interactive mode what means that we need auto-confirm that we are sure to install following package
So after running this command everything seems fine – wget is downloading my site and nginx is starting but at localhost:8080 I see:
Damn that’s not cool….
To debug that situation we need to get into container and see whats happening in file system after wget command. To do that we need to start with:
The most important there is Container Id – we’re going to use that ID to get into container, open new console and try (change container id to yours because it’s random string):
docker exec -it 69cb502ab0d1 bash
After that command we are in new shell process inside container in interactive mode. Let’s check what’s under /usr/share/nginx/html:
drwxr-xr-x 1 root root 4.0K Feb 18 11:20 . drwxr-xr-x 1 root root 4.0K Dec 26 18:16 .. -rw-r--r-- 1 root root 537 Dec 26 11:11 50x.html -rw-r--r-- 1 root root 612 Dec 26 11:11 index.html drwxr-xr-x 7 root root 4.0K Feb 18 11:20 jakubbujny.com
Ok so wget downloaded my site into jakubbujny.com directory – try in web browser:
You should see my blog 🙂
Stay please in our shell process inside container. Changing site logo is so easy, just type: vim /usr/share/nginx/html/jakubbujny.com/index.html omg there is no vim…
<disclaimer> Many popular images don’t have basic tools like vim or even bash because every additional tool not needed at runtime means bigger image size to download for users </disclaimer>
apt-get install vim
I believe that you are VIM WIZARD LVL 300 but if not just type:
ESC /site-logo ENTER
To find my logo section and change next <img> tag src to (should help: ESC a ) : https://jakubbujny.files.wordpress.com/2018/02/57761.png
ESC :wq ENTER
Go to web browser and hit F5:
That was so easy, was it?
Commit container into image
Last step is to convert container into image.
PLEASE DO NOT USE THAT IN PRODUCTION – FOLLOW INFRASTRUCTURE AS CODE PATTERN
It means that we can say to docker: hey! I have here some container where I installed some tools and modified some state, please convert that container into image so I can reuse that manual configuration many times
docker commit 69cb502ab0d1 my-awesome-image:1.0
So again we are using Container ID and docker is creating for us image with name my-awesome-image:1.0 – let’s check if that works:
docker run -it -p 8081:80 my-awesome-image:1.0 bash -c "nginx -g 'daemon off;'"
- Run container from committed image
- Override command again – docker set my-awesome-image’s cmd to that one with wget but we don’t need that here! We committed whole container so jakubbujny.com site is already in image – if we don’t need latest version we can override again cmd to start only web server
And you should see my blog and docker logo instead mine.
As you can see docker is quite powerful – when using with care you can really do magic cases. I hope that was helpful for you – I decided to divide docker article into series because I have many more examples how to use that tool in creative way. See you in next article!