Blogging with Docker

One issue with the previous approach to blogging is the requirement of installing packages to build the blog. Virtual machines can be installed to alleviate this matter, but at the cost of manually installing an OS and the corresponding packages. Furthermore, the virtual machine needs its own hacks and a lot of computational resources to work properly.

Docker CE is the latest solution to this problem. Installing it is very simple and you only need to run

sudo usermod -aG docker ${USER}

afterwards if you would like to use Docker as a non-root user. Make sure to reboot to apply the new permissions.

Docker itself only needs a Dockerfile, but this post will include additional items in order to replace the older blogging methods:

# Use an official Python runtime as a parent image
FROM python:3-slim

# Set the working directory to /app
ENV DEV=/workspace/dev
ENV DEV_DATA=/workspace/data

# Copy the current directory contents into the container at ${DEV}
WORKDIR ${DEV}
COPY . .

ENV TMP=/tmp

ENV ANACONDA_VERSION="https://repo.anaconda.com/archive/Anaconda3-2018.12-Linux-x86_64.sh"
ENV ANACONDA_PATH=${TMP}/anaconda.sh
ENV ANACONDA_ROOT=${DEV}/anaconda
ENV ANACONDA_BIN=${ANACONDA_ROOT}/bin

ENV GCLOUD_SDK_VERSION="https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-237.0.0-linux-x86_64.tar.gz"
ENV GCLOUD_SDK_PATH=gcloud_sdk.tar.gz
ENV GCLOUD_SDK_ROOT=google-cloud-sdk
ENV GCLOUD_SDK_BIN=google-cloud-sdk/bin

ENV LILYPOND_VERSION="http://lilypond.org/download/binaries/linux-64/lilypond-2.20.0-1.linux-64.sh"
ENV LILYPOND_PATH=${TMP}/lilypond.sh
ENV LILYPOND_ROOT=${DEV}/lilypond
ENV LILYPOND_BIN=${LILYPOND_ROOT}/bin

RUN apt-get update --fix-missing \
  && apt-get install -y \
       wget bzip2 ca-certificates libglib2.0-0 libxext6 libsm6 libxrender1 \
       asymptote texlive-font-utils pdf2svg \
       silversearcher-ag mercurial less vim \
  && wget --quiet ${ANACONDA_VERSION} -O ${ANACONDA_PATH} \
  && /bin/bash ${ANACONDA_PATH} -b -p ${ANACONDA_ROOT} \
  && rm ${ANACONDA_PATH} \
  && echo "export PATH=${ANACONDA_ROOT}/bin:$PATH" >> ~/.bashrc \
  && wget --quiet ${LILYPOND_VERSION} -O ${LILYPOND_PATH} \
  && /bin/bash ${LILYPOND_PATH} --prefix ${LILYPOND_ROOT} \
  && rm ${LILYPOND_PATH} \
  && rm -rf /var/lib/apt/lists/* \
  && echo "export PATH=${LILYPOND_ROOT}/bin:$PATH" >> ~/.bashrc \
  && wget --quiet ${GCLOUD_SDK_VERSION} -O ${GCLOUD_SDK_PATH} \
  && tar xvf ${GCLOUD_SDK_PATH} \
  && rm -rf ${GCLOUD_SDK_PATH} \
  && ${GCLOUD_SDK_ROOT}/install.sh --quiet --usage-reporting False --command-completion True --path-update True \
  && cp hgrc ~/.hgrc \
  && cp inputrc ~/.inputrc \
  && ${GCLOUD_SDK_BIN}/gcloud components update --quiet

# Install any needed packages specified in requirements.txt
RUN ${ANACONDA_BIN}/pip install --trusted-host pypi.python.org -r ${DEV}/requirements.txt

# Make ports available to the world outside this container
EXPOSE 80 8000 8888

# Run a shell when the container launches
CMD ["/bin/bash"]

The requirements.txt is to support data analysis:

sphinx
sphinxcontrib-bibtex
nbsphinx
sphinx_bootstrap_theme
sphinx_sitemap
sphinx-autobuild
pymprog
graphviz

The files .gitconfig

[core]
    editor = vi

[user]
    name = alphaXomega
    email = alpha.x.omega@outlook.com

and .inputrc

"\e[A": history-search-backward
"\e[B": history-search-forward
"\e[C": forward-char
"\e[D": backward-char

and .vimrc

set tabstop=4
set expandtab

are more for convenience.

Copy Data Between Docker Volume and Host

Suppose you have a volume created by

docker volume create blog_data

Any data transfer between host and docker volumes must happen via a container. As an example, suppose we need to generate an SSH key to connect to BitBucket:

mkdir .ssh
cd .ssh
ssh-keygen -f id_rsa
docker container create --name dummy --mount source=blog_data,target=/workspace/data python:3-slim
cd ..
docker cp .ssh dummy:/workspace/data/.ssh
docker rm dummy

Running a Docker Container

Suppose the blog uses the provided Dockerfile to build an image as follows:

docker build -t blogger .

The blog needs three processes to fully operate:

gnome-terminal --tab -- docker run --name sphinx_autobuild --rm -it --mount source=blog_data,target=/workspace/data blogger
echo -e 'Run this for container sphinx_autobuild (1):\n\neval `ssh-agent`; cd ../data/allthingsphi; sphinx-autobuild -b html source/ gae/www\n'

gnome-terminal --tab -- docker run --name python_server --rm -it -p 8000:8000 --mount source=blog_data,target=/workspace/data blogger
echo -e 'Run this for container python_server (2):\n\ncd ../data/allthingsphi; pushd gae/www/; python3 -m http.server\n'

gnome-terminal --tab -- docker run --name jupyter_notebook --rm -it -p 8888:8888 --mount source=blog_data,target=/workspace/data blogger
echo -e 'Run this for container jupyter_notebook (3):\n\ncd ../data/; jupyter notebook --allow-root --ip=0.0.0.0 --no-browser\n'
  • sphinx_autobuild is responsible for looking at the blog data for changes and rebuilding the site.

    • Before running the related commands, you should clone the repository.

      eval `ssh-agent`
      hg clone ssh://hg@bitbucket.org/alphaXomega/allthingsphi
      
  • python_server tests the blog with a local web server. Remember that this process will push data to Google Cloud, make sure to initialize it.

    gcloud init
    
  • jupyter_notebook allows editing of blog content. It must use the following settings in order to run inside a container.

    jupyter notebook --allow-root --ip=0.0.0.0 --no-browser