You want to make simple services, that do things. To be able to modify that role out, at code base, at will? Ok, probably interested in Docker or things like it (containers). In this post we will be exploring some of my findings and this will sort of be a notes/detail reference for myself in the future. Thanks me.

Docker Build and Provisioning
Now, there must be a couples files that have been crafted governing the provisioning, and possible creation of your Docker container. This guide assumes you have an average distro and have installed the latest docker environment.
RedHat Enterprise Linux – docker

Ubuntu – docker
As I am getting to understand it, these are:
docker build -t containername .
In this example you use the “Dockerfile” file where you put the actual code the builds your docker container, can be as simple as getting an image, or, can be as complicated as layers and all the rest
docker-compose up -d
When you do this, it uses the “docker-compose.yml” file to provision the Docker container to your host
In most default configurations when you provision some default Docker container image it will network via a Bridge network connection. You will publish port forwards from your host to the docker0 network (in most cases). Some environments alter the default rollout for a standard environment, and to this, Docker can support quite a wide array. You just might need to know how to delivery that arrangement.
Standard deployment
In most cases you would be following a standard deployment suggestion from Docker Hub or someone’s Github project, you probably where told do a ‘docker run -d groupname/containername:version’ and this uses Docker’s bridge and port forward usually. This is the “standard” deployment model.
Here’s a much more filled out example, one that could exist as a script in of itself. This builds a PiHole using an IP that would be assigned on the primary Ethernet controller for the host it is ran on:
build_pihole01.sh
!/bin/bash
docker container stop pihole01
docker container rm pihole01
docker volume rm pihole_data
docker volume create pihole_data
docker volume rm pihole_dnsmasq
docker volume create pihole_dnsmasq
docker pull pihole/pihole:latest
docker run -d \
--name pihole01 \
-p 10.0.1.6:53:53/tcp -p 10.0.1.6:53:53/udp \
-p 10.0.1.6:80:80 \
-p 10.0.1.6:443:443 \
-p [::]:53:53/tcp -p [::]:53:53/udp \
-p [::]:80:80 \
-p [::]:443:443 \
--restart=always \
-e TZ="America/Chicago" \
-v pihole_data:/etc/pihole/ \
-v pihole_dnsmasq:/etc/dnsmasq.d/ \
--dns=10.0.1.1 --dns=8.8.8.8 \
--restart=unless-stopped \
--hostname pihole01 \
-e WEBPASSWORD="random" \
-e VIRTUAL_HOST="pihole01" \
-e PROXY_LOCATION="pihole01" \
-e ServerIP="10.0.1.6" \
pihole/pihole:latest
If you were going admin the PiHole, you would browse to the “10.0.1.6” address and enter your password of “random” to login. When a host resolves a DNS address, it would connect to the “10.0.1.6” IP but, the host running that PiHole Docker Container (whatever IP it might be) would actually reply back to the requester. It can look odd on the network if you were expecting something else to happen.
If you are after a more “local network connection” or standard “service style” arrangement, then you would be interested in Docker’s other features.
You will need to make a new network for this via “docker” commands, you can also do this in a “docker-compose” method too, I will try to start with the first and detail the second. Haven’t actually used the second method yet, so…
Custom Deployment – Bind configurable via Webmin
In this deployment we will be using “docker-compose” and this must be installed and of course as it relies on Python, you will have to have and a series of dependencies installed. Please return once you have this installed, you should not have to do much configuration on a standard flavor of Linux as far as I know.
The first step is creating the new network for the IP resolved Docker Containers
Issue these commands to create your network interface you will deploy against:
docker network create dockerext01 --driver=macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 --ip-range=192.168.1.52/30 -o parent=eth0
sudo ip link set eth0 promisc on
Now you can use the Dockerfile and docker-compose.yml files plus a likely entrypoint.sh file to configure to this environment. At the end we cover how to execute the whole group of files. The bind example is a good strong example in my mind because the original creator built out the entrypoint.sh as such. It helped me learn some of the rest of the instrumentation more easily. Thank you!

A bind configuration (modified from sameersbn/docker-bind):
Dockerfile (copied and modified from source)
FROM ubuntu:focal AS add-apt-repositories
RUN DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y apt-utils \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y ca-certificates wget \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y gnupg \
&& wget -q -O- http://www.webmin.com/jcameron-key.asc | apt-key add \
&& echo "deb http://download.webmin.com/download/repository sarge contrib" >> /etc/apt/sources.list
FROM ubuntu:focal
ENV BIND_USER=bind \
BIND_VERSION=9.16.1 \
WEBMIN_VERSION=1.984 \
DATA_DIR=/data \
HOSTNAME=ns02
LABEL maintainer="email@domain.tld"
COPY --from=add-apt-repositories /etc/apt/trusted.gpg /etc/apt/trusted.gpg
COPY --from=add-apt-repositories /etc/apt/sources.list /etc/apt/sources.list
RUN rm -rf /etc/apt/apt.conf.d/docker-gzip-indexes \
&& DEBIAN_FRONTEND=noninteractive apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y \
bind9=1:${BIND_VERSION}* \
bind9-host=1:${BIND_VERSION}* \
dnsutils \
webmin=${WEBMIN_VERSION}* \
&& rm -rf /var/lib/apt/lists/*
COPY entrypoint.sh /sbin/entrypoint.sh
RUN chmod 755 /sbin/entrypoint.sh
EXPOSE 53/udp 53/tcp 10000/tcp
ENTRYPOINT ["/sbin/entrypoint.sh"]
HEALTHCHECK --interval=12s --timeout=12s --start-period=30s \
CMD [ "/usr/bin/dig", "+short", "+norecurse", "+retry=0", "@192.168.1.53", "ns02.homelab.home", "||", "exit 1" ]
CMD ["/usr/sbin/named"]
docker-compose.yml (created by me, there is a reference but it was quite minimal)
version: "3"
services:
bind:
container_name: ns02
image: bind_homelab:latest
build:
context: .
dockerfile: Dockerfile
hostname: ns02
domainname: homelab.home
extra_hosts:
- "ns02:127.0.0.1"
networks:
dockerext02:
ipv4_address: 192.168.1.53
aliases:
- ns02
dns:
- 127.0.0.1
- 8.8.8.8
ports:
- "53:53/tcp"
- "53:53/udp"
- "10000:10000/tcp"
volumes:
- bind:/data/bind
- webmin:/data/webmin
restart: always
networks:
dockerext02: # externally created network (later in article)
external: true
volumes:
bind:
webmin:
entrypoint.sh (modified from source, only slightly)
#!/bin/bash
set -e
# usage: file_env VAR [DEFAULT]
# ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
local var="$1"
local fileVar="${var}_FILE"
local def="${2:-}"
if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
exit 1
fi
local val="$def"
if [ "${!var:-}" ]; then
val="${!var}"
elif [ "${!fileVar:-}" ]; then
val="$(< "${!fileVar}")"
fi
export "$var"="$val"
unset "$fileVar"
}
file_env 'ROOT_PASSWORD'
ROOT_PASSWORD=${ROOT_PASSWORD:-password}
WEBMIN_ENABLED=${WEBMIN_ENABLED:-true}
WEBMIN_INIT_SSL_ENABLED=${WEBMIN_INIT_SSL_ENABLED:-true}
WEBMIN_INIT_REDIRECT_PORT=${WEBMIN_INIT_REDIRECT_PORT:-10000}
WEBMIN_INIT_REFERERS=${WEBMIN_INIT_REFERERS:-NONE}
BIND_DATA_DIR=${DATA_DIR}/bind
WEBMIN_DATA_DIR=${DATA_DIR}/webmin
create_bind_data_dir() {
mkdir -p ${BIND_DATA_DIR}
# populate default bind configuration if it does not exist
if [ ! -d ${BIND_DATA_DIR}/etc ]; then
mv /etc/bind ${BIND_DATA_DIR}/etc
fi
rm -rf /etc/bind
ln -sf ${BIND_DATA_DIR}/etc /etc/bind
chmod -R 0775 ${BIND_DATA_DIR}
chown -R ${BIND_USER}:${BIND_USER} ${BIND_DATA_DIR}
if [ ! -d ${BIND_DATA_DIR}/lib ]; then
mkdir -p ${BIND_DATA_DIR}/lib
chown ${BIND_USER}:${BIND_USER} ${BIND_DATA_DIR}/lib
fi
rm -rf /var/lib/bind
ln -sf ${BIND_DATA_DIR}/lib /var/lib/bind
}
create_webmin_data_dir() {
mkdir -p ${WEBMIN_DATA_DIR}
chmod -R 0755 ${WEBMIN_DATA_DIR}
chown -R root:root ${WEBMIN_DATA_DIR}
# populate the default webmin configuration if it does not exist
if [ ! -d ${WEBMIN_DATA_DIR}/etc ]; then
mv /etc/webmin ${WEBMIN_DATA_DIR}/etc
fi
rm -rf /etc/webmin
ln -sf ${WEBMIN_DATA_DIR}/etc /etc/webmin
}
disable_webmin_ssl() {
sed -i 's/ssl=1/ssl=0/g' /etc/webmin/miniserv.conf
}
set_webmin_redirect_port() {
echo "redirect_port=$WEBMIN_INIT_REDIRECT_PORT" >> /etc/webmin/miniserv.conf
}
set_webmin_referers() {
echo "referers=$WEBMIN_INIT_REFERERS" >> /etc/webmin/config
}
set_root_passwd() {
echo "root:$ROOT_PASSWORD" | chpasswd
}
create_pid_dir() {
mkdir -p /var/run/named
chmod 0775 /var/run/named
chown root:${BIND_USER} /var/run/named
}
create_bind_cache_dir() {
mkdir -p /var/cache/bind
chmod 0775 /var/cache/bind
chown root:${BIND_USER} /var/cache/bind
}
first_init() {
if [ ! -f /data/webmin/.initialized ]; then
set_webmin_redirect_port
if [ "${WEBMIN_INIT_SSL_ENABLED}" == "false" ]; then
disable_webmin_ssl
fi
if [ "${WEBMIN_INIT_REFERERS}" != "NONE" ]; then
set_webmin_referers
fi
touch /data/webmin/.initialized
fi
}
create_pid_dir
create_bind_data_dir
create_bind_cache_dir
# allow arguments to be passed to named
if [[ ${1:0:1} = '-' ]]; then
EXTRA_ARGS="$*"
set --
elif [[ ${1} == named || ${1} == "$(command -v named)" ]]; then
EXTRA_ARGS="${*:2}"
set --
fi
# default behaviour is to launch named
if [[ -z ${1} ]]; then
if [ "${WEBMIN_ENABLED}" == "true" ]; then
create_webmin_data_dir
first_init
set_root_passwd
echo "Starting webmin..."
/etc/init.d/webmin start
fi
echo "Starting named..."
exec "$(command -v named)" -u ${BIND_USER} -g ${EXTRA_ARGS}
else
exec "$@"
fi
Extra, a script to call the build process
build_bind_ns02.sh (also mine, but incredibly basic, and more to understand additional flags)
#!/bin/bash
docker-compose up -d --force-recreate --always-recreate-deps --build --remove-orphans
After you create all the files in a directory together you would then issue these commands:
chmod +x *.sh
./build_bind_ns02.sh