How’s it going, good I hope!, Hope this year is a blast for you.

What is this?

Creating a preconfigured container for a precompiled binary utilizing docker.

Reason / Motivation:

I’ve been using hugo, even since my blog migration post, the one problem that I am facing with hugo is that,

  • whenever I run update os host system,hugo version also updates, the problem with that is some features are deprecated in the new release, my site design depends upon those features. so what I was doing is constantly degrading the hugo version back to old version, before I write a post everytime.
  • I work in new environments all the time, so downloading the appropriate version and installing takes time.

goal

At the end of this venture, I should be able to

  • create a image from scratch
  • install a required binary with its dependencies, either using source or binary distribution
  • create a container from the image and export it
  • upload the container image to a docker hub or something similar
  • setup an environment using the container
  • link the local binary executable to the binary inside the container
    • whenever I run the binary locally, the binary inside the container should be executed on the files from the host system’s current path.
    • I should be able to run the binary, with or without its parameters and environment options

Is this even possible?

what I am trying to achieve is completely possible using docker, its a feature completely baked into the docker core, using ‘–rm’ with ‘docker -run’

Lets start

Choosing the best base image, I recommend alpine.

why?

Alpine Linux is a Linux distribution built around musl libc and BusyBox. The image is only 5 MB in size and has access to a package repository

Set up a container from alpine


aghontpi@elitebook:~$ docker run --name testalpine -d -it alpine:latest  /bin/sh
1be3fb1ba1c46786dc5034745dcdab31c6588627c086c08f3c0ff8493d270544

aghontpi@elitebook:~$ docker ps
CONTAINER ID   IMAGE           COMMAND     CREATED         STATUS         PORTS     NAMES
1be3fb1ba1c4   alpine:latest   "/bin/sh"   4 seconds ago   Up 3 seconds             testalpine

Login to the shell to install your binary

aghontpi@elitebook:~$ docker exec -it testalpine /bin/sh
/ # whoami
root

/ # apk update
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
v3.13.2-8-g7bb1c88019 [https://dl-cdn.alpinelinux.org/alpine/v3.13/main]
v3.13.2-9-gd3529c068e [https://dl-cdn.alpinelinux.org/alpine/v3.13/community]
OK: 13884 distinct packages available

/ # apk list vim
vim-8.2.2320-r0 x86_64 {vim} (Vim)

/ # apk add --no-cache vim
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/main/x86_64/APKINDEX.tar.gz
fetch https://dl-cdn.alpinelinux.org/alpine/v3.13/community/x86_64/APKINDEX.tar.gz
(1/5) Installing xxd (8.2.2320-r0)
(2/5) Installing lua5.3-libs (5.3.6-r0)
(3/5) Installing ncurses-terminfo-base (6.2_p20210109-r0)
(4/5) Installing ncurses-libs (6.2_p20210109-r0)
(5/5) Installing vim (8.2.2320-r0)
Executing busybox-1.32.1-r0.trigger
OK: 25 MiB in 19 packages

/ # vim test

/ # cd home

/home # wget https://github.com/gohugoio/hugo/releases/download/v0.56.3/hugo_0.56.3_Linux-64bit.tar.gz
Connecting to github.com (13.234.210.38:443)
Connecting to github-releases.githubusercontent.com (185.199.108.154:443)
saving to 'hugo_0.56.3_Linux-64bit.tar.gz'
hugo_0.56.3_Linux-64 100% |*********************************************************************************************************************************************************************************************************************************| 11.4M  0:00:00 ETA
'hugo_0.56.3_Linux-64bit.tar.gz' saved

/home # ls
hugo_0.56.3_Linux-64bit.tar.gz

/home # tar -xf hugo_0.56.3_Linux-64bit.tar.gz

/home # ls
LICENSE                         README.md                       hugo                            hugo_0.56.3_Linux-64bit.tar.gz

/home # cp hugo /usr/bin/

/home # mkdir test

/home # cd test/hugo

/home # ls

LICENSE                         README.md                       hugo                            hugo_0.56.3_Linux-64bit.tar.gz  test

/home # cd test/

/home/test # hugo
Building sites … WARN 2021/02/19 16:52:24 found no layout file for "HTML" for "taxonomyTerm": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.
WARN 2021/02/19 16:52:24 found no layout file for "HTML" for "home": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.
WARN 2021/02/19 16:52:24 found no layout file for "HTML" for "taxonomyTerm": You should create a template file which matches Hugo Layouts Lookup Rules for this combination.

                   | EN
+------------------+----+
  Pages            |  3
  Paginator pages  |  0
  Non-page files   |  0
  Static files     |  0
  Processed images |  0
  Aliases          |  0
  Sitemaps         |  1
  Cleaned          |  0

Total in 4 ms

/home/test # ls
public     resources

/home/test # cd ..

/home # ls
LICENSE                         README.md                       hugo                            hugo_0.56.3_Linux-64bit.tar.gz  test

/home # rm -rf ./*

/home # exit

aghontpi@elitebook:~$

aghontpi@elitebook:~$ docker container stop testalpine
testalpine

aghontpi@elitebook:~$ docker commit testalpine testalpinewithhugo/0.1
sha256:c305edf7f82a912801ae44dfe6ffe9e834e0541728295c2968ef69ad2ba9e90f

aghontpi@elitebook:~$ docker image ls testalpinewithhugo/0.1
REPOSITORY               TAG       IMAGE ID       CREATED          SIZE
testalpinewithhugo/0.1   latest    c305edf7f82a   14 seconds ago   60.1MB

Host the image remotely in a service of your choice.

Then use services like docker-hub to your private repo, it will look like below.

Shell script to orchestrate the whole thing.

Didn’t want to brushup my sheel script so wrote a python script and used it as executable instead.

link to the following code

#! /usr/bin/python3

import os

calling_directory = os.getcwd()

cmd =  f"docker run --rm -it \
    --user $(id -u):$(id -g) \
    -v '/etc/group:/etc/group:ro' \
    -v '/etc/passwd:/etc/passwd:ro' \
    -v '/etc/shadow:/etc/shadow:ro'
    -v {calling_directory}/:/tmp/ \
    -e HUGO_ENV \
    -p 80:1313 \
    bluepie/hugo:0.1 \
    /bin/sh -c 'cd /tmp/ && hugo " + ' '.join(sys.argv[1:])  +" '"

print(cmd)
os.system(cmd)

Finally Installation script

The code is verbose, so check it out through the link.

link to installation script

what the script does..

  • check if hugo and docker is already installed
  • create .bin directory inside $HOME
  • downloads the code and places it as executable inside $HOME/.bin/hugo
  • adds the path $HOME/.bin to .bashrc

Script in action


aghontpi@elitebook:/tmp$ ./install-script.py

> docker-hugo custom binary script  v0.0.1

> docker is installed proceeding..

> hugo does not exists, initiating docker pull and installation

> authenticating /w docker, enter credential when asked

Authenticating with existing credentials...
WARNING! Your password will be stored unencrypted in /home/aghontpi/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

> pulling from docker repo

0.1: Pulling from bluepie/hugo
Digest: sha256:db54d94cfb7ad12726b1ee9f5fa7b2a2ad9d7e567021886fe2dd74e4262a43ea
Status: Image is up to date for bluepie/hugo:0.1
docker.io/bluepie/hugo:0.1

> getting the custom script

> writing to disc

> setting permission

> backup cmd -> cp /home/aghontpi/.bashrc /home/aghontpi/.bashrc_bkp_19022021-21:20:15

> modifying bashrc of user

> installation complete

---> use a new terminal to test <---

aghontpi@elitebook:/tmp$

End

I learned a lot through this little experiment, It saves a ton of my time.