I use Docker
to run several microservices across my websites. Generally, they are standalone tools
and demonstrations, mostly made to teach myself how to get these things done in microservices.
I recently came across the concept of utility containers. Up to now, all the containers I run are meant to run all the time, and be easily replaced by spinning up a replacement container. Utility containers, on the other hand, are literally containers that hold the bits needed to process input data into output data, do that process, then exit.
At the same time, I’ve been transitioning from Google’s blogger platform onto my in-home hardware,
using Hugo. I’ve written my own little Docker utility container to hold
the go
language and the Hugo
executable, specifically made to process data checked out from
my hugo-content
and hugo-static
(private) repositories and automatically check the outputs back
into my blog-htdocs repository.
I’m writing this post directly on my gitlab website, and it will be the first post that I’m writing
directly into source control, to be processed by my hugo-builder
container, and posted publicly.
Detail: Dockerfile
# Docker 20.10.16
FROM alpine:latest
MAINTAINER Gary Allen Vollink g.hugo@vollink.com
RUN apk update \
&& apk upgrade \
&& apk add coreutils shadow bash openssh curl go git \
&& mkdir /root/.ssh \
&& ssh-keygen -t ed25519 -f /root/.ssh/id_ed25519 \
-N '' -C 'git@hugobuilder' -q \
&& chmod 700 /root/.ssh \
&& chmod 600 /root/.ssh/id_ed25519 \
&& chmod 644 /root/.ssh/id_ed25519.pub \
&& curl -LOs https://github.com/gohugoio/hugo/releases/download/v0.99.1/hugo_0.99.1_Linux-64bit.tar.gz \
&& cd /usr/local/bin \
&& tar xfz /hugo_0.99.1_Linux-64bit.tar.gz \
&& rm LICENSE README.md \
&& /bin/echo "#######################" \
&& /bin/echo "## Add key to gitlab." \
&& /bin/echo "#######################" \
&& cat /root/.ssh/id_ed25519.pub \
&& /bin/echo "#######################"
COPY src/* /run/
CMD /run/entry.sh
Detail: entry.sh
For now, I’m not sharing this script. The entry.sh
script
is 249 lines long. Here are the key points:
If
/work
exist (if so, it came from-v
on the command line):- Store
/work
owner’s UIDWORK_UID
- Store
/work
group’s GIDWORK_GID
- Store
If
/work
does NOT exist:WORK_UID=33
,WORK_GID=33
(Ubuntu’s www-data u/gids)- Create
/work
, set ownership toWORK_UID:WORK_GID
If
alpine:latest
does not having a group withWORK_GID
- Create a group:
groupadd --gid $WORK_GID hugobuilder
- Create a group:
If
alpine:latest
does not having a user withWORK_UID
- Create a user, hugobuilder, with
WORK_GID
andWORK_UID
- Create a user, hugobuilder, with
If
alpine:latest
did have a matching user or group:- Matching user is
WORK_NAME
orhugobuilder
- Matching group is
GROUP_NAME
orhugobuilder
- Matching user is
Modify
WORK_NAME
:- Add
GROUP_NAME
toWORK_NAME
account - If
WORK_NAME
has no home directory, add/create (not/work
).- This is
WORK_NAME_HOME
- This is
- Set
WORK_NAME
shell to/bin/bash
- Add
Modify
WORK_NAME_HOME
:- If there is somehow an .ssh or .gitconfig already there:
- Back up any .ssh and .gitconfig that are “in the way”
- If a
/work/.ssh
folder exists, copy it toWORK_NAME_HOME
- Else, copy the /root/.ssh to the user folder.
- If there is somehow an .ssh or .gitconfig already there:
Check for
/work/config.yaml
or/work/config.toml
:- Set this in the environment for the next script.
Execute the
go_hugo.sh
(script in next section, below):Cleanup/revert any .ssh changes
Cleanup/revert any .gitconfig changes
Details go_hugo.sh
#!/bin/bash
#############################################################################
VAR_ERROR=""
cd /work
# Read the environment package that entry.sh left us.
if [ -r "$1" ]
then
echo "Reading $1"
eval $(cat "$1")
else
echo "Unable to read $1"
ls -ld "$1"
fi
# Read any environment package that a user put in /work
if [ -r "/work/hugobuilder.env" ]
then
eval $(cat "/work/hugobuilder.env")
fi
# Check for expected variables
# These should all have something, even if left unused.
if [ -z "$GROUP_NAME" ]
then
VAR_ERROR="${VAR_ERROR}GROUP_NAME:"
fi
# SKIPPING THE REST OF THE CHECKS FOR BREVITY
if [ ! -z "$VAR_ERROR" ]
then
echo "ERR: Expected variables missing: ${VAR_ERROR}"
echo "HAS_CONFIG=$HAS_CONFIG"
exit 2
fi
if [ -z "$GIT_SSH_COMMAND" ]
then
# ONLY if the user has not given us a better one.
GIT_SSH_COMMAND="ssh -o UserKnownHostsFile=/dev/null"
GIT_SSH_COMMAND="${GIT_SSH_COMMAND} -o StrictHostKeyChecking=no"
fi
export GIT_SSH_COMMAND
if [ -r "/work/.gitconfig" ]
then
# If it exists, this was already backed up by entry.sh
cp "/work/.gitconfig" "${WORK_HOME}/.gitconfig"
fi
# See if we have the two settings needed for `git commit`
# Assume we don't
_NEED_GCFG_E=1
_NEED_GCFG_N=1
if [ -r "${WORK_HOME}/.gitconfig" ]
then
grep 'user.email' "${WORK_HOME}/.gitconfig" 2>&1 >/dev/null
if [ "0" = "$?" ]
then
# Unless we find it
_NEED_GCFG_E=0
fi
grep 'user.name' "${WORK_HOME}/.gitconfig" 2>&1 >/dev/null
if [ "0" = "$?" ]
then
_NEED_GCFG_N=0
fi
fi
# Add needed git settings.
if [ "1" = "${_NEED_GCFG_E}" ]
then
git config --global user.email "hugobuilder-auto@vollink.com"
fi
if [ "1" = "${_NEED_GCFG_N}" ]
then
git config --global user.name "Hugo Builder Automation"
fi
if [ ! -d "/work/blog/.git" ]
then
git clone --recursive \
ssh://git@gitlab.home.vollink.com:30022/external/blog-htdocs.git \
"/work/blog"
if [ "0" -ne "$?" ]
then
echo "ERROR: git failed."
echo "Was key added to gitlab?"
echo "===>"
cat ${WORK_HOME}/.ssh/id_ed25519.pub
echo "<==="
exit 1
fi
if [ ! -d "/work/blog/htdocs" ]
then
echo "ERROR: git claims success, but blog/htdocs was not created."
exit 1
fi
fi
cd /work
if [ ! -d "/work/hugo-blog/.git" ]
then
git clone --recursive \
ssh://git@gitlab.home.vollink.com:30022/home/web/hugo-blog.git \
"/work/hugo-blog"
if [ "0" -ne "$?" ]
then
echo "ERROR: git failed."
echo "Was key added to gitlab?"
echo "===>"
cat ${WORK_HOME}/.ssh/id_ed25519.pub
echo "<==="
exit 1
fi
if [ ! -d "/work/hugo-blog" ]
then
echo "ERROR: git claims success, but hugo-blog/ was not created."
exit 1
fi
fi
cd /work/hugo-blog
git fetch --all
git pull
git submodule foreach git pull origin master
##
# before running hugo: do I have a config?
if [ -r "${HAS_CONFIG}" ]
then
cp "${HAS_CONFIG}" "/work/hugo-blog/."
fi
/usr/local/bin/hugo --destination "/work/blog/htdocs"
# This is bullshit, by the way... if there is a git FOLDER in the
# destination, hugo will delete it entirely before replacing everything,
# so I've set this up so the destination is one layer deep.
rm /work/blog/htdocs/.git
cd /work/blog
git add .
if [ "0" = "$?" ]
then
git commit -m 'docker hugobuilder automated check-in.'
if [ "0" = "$?" ]
then
git push origin master
if [ "0" -ne "$?" ]
then
exit 1
fi
fi
fi
Update
The page above was run through the hugobuilder
container and deployed using
a git pull
from my web server. With this edit, I’m going to attempt to
let the hugobuilder
and my various crontabs deploy this automatically
(checks are done on a 10 minute schedule).
Update 2
This page is updating from source check-ins alone, so I’m feeling really good. A few things above were updated since the last update, but a lot less has changed than I initially expected.