
Personal Dev Environment using Docker
When I was trying to tune my competitive programming setup on Mac, I was bugged by the fact that bits/stdc++.h
is
not natively present on OSX. To workaround this issue, I have to either use nasty hacks like copying the contents
into a local file or symlinking the file in path. In the end, all of those methods will get the work done, but not in the
cleanest way possible.
This is just one example of the many inconsistencies we find when our dev environment is not the one on which we want
to develop. Lately, I have been using VSCode
and found that they have a very cool extension of Remote Docker
which
lets you connect to any Docker host on the system and install VSCode
in that. You can edit the files in that container
without leaving VSCode
, which is awesome.
Though I am not a big VSCode
user and primarily use Neovim
as my PDE. I thought why not replicate the same thing
but for Neovim
instead.
Technically, we can execute any shell program inside Docker container and we will be using this and some other
features which Docker
provides to achieve this.
Step 1: Select Appropriate Image for use
Decide which image you will be using for your dev environment. Generally, one can go for defacto environment
like Ubuntu
or Alpine
. But you are free to use any image of your choice. For the sake of this blog, I will be going
ahead with the Ubuntu system image as it has all the tools required for competitive programming setup.
FROM ubuntu:18.04
Step 2: Install necessary dependencies
After choosing the image, next step is to install the required packages for dev environment, which are not natively present in the image.
In my case, I will be using this dev environment for competitive programming, so latest Clang
should be good. Not to forget
the editor, in my case Neovim
binary.
# Make sure the image is updated, install some prerequisites,
# Download the latest version of Clang (official binary) for Ubuntu
# Extract the archive and add Clang to the PATH
RUN apt-get update && apt-get install -y \
xz-utils \
build-essential \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& curl -SL https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz \
| tar -xJC . && \
mv clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04 clang_10.0.0 && \
echo 'export PATH=/clang_10.0.0/bin:$PATH' >> ~/.bashrc && \
echo 'export LD_LIBRARY_PATH=/clang_10.0.0/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
# Install neovim and other necessary deps for use
RUN apt-get update && apt-get install -y python3.6 git locales neovim python3-neovim
Also, at this stage, we can setup some env variables as well for terminal use.
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
At this stage, your Dockerfile
should see something like this.
FROM ubuntu:18.04
# Make sure the image is updated, install some prerequisites,
# Download the latest version of Clang (official binary) for Ubuntu
# Extract the archive and add Clang to the PATH
RUN apt-get update && apt-get install -y \
xz-utils \
build-essential \
curl \
&& rm -rf /var/lib/apt/lists/* \
&& curl -SL https://github.com/llvm/llvm-project/releases/download/llvmorg-14.0.0/clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04.tar.xz \
| tar -xJC . && \
mv clang+llvm-14.0.0-x86_64-linux-gnu-ubuntu-18.04 clang_10.0.0 && \
echo 'export PATH=/clang_10.0.0/bin:$PATH' >> ~/.bashrc && \
echo 'export LD_LIBRARY_PATH=/clang_10.0.0/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
RUN apt-get update && apt-get install -y python3.6 git locales
RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && \
locale-gen
ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
CMD [ "/bin/bash" ]
Now you can build the Dockerfile
using the following command.
# Make sure to be in the same directory as the `Dockerfile`.
docker build -t competitive .
Step 3: Mount the directories
We will also require a file system so that files can be shared between the host OS and the Docker container. We want to have a bi-directional communication from the host OS to the Docker Container running.
If something changes in the Docker container, it should also be reflected in the main host file-system and vice-versa.
To perform this, we can use Volumes
to attach directories from the host system to the Docker container.
This can be done via the following command, which will run the container from competitive
image.
docker run \
-e TERM=screen-256color \
-v $(pwd):/app/competitive \
-v $HOME/.dotfiles/nvim/.config/nvim:/root/.config/nvim \
--name competitive -dit competitive
Above command will run the image competitive
in a container name competitive
, and attach the directories from the host
operating system to the Docker container.
Step 4: Ready
Now that the setup is almost done, you can exec
into the container and start working on the files. You will get the exact
libs for work which you require from the comfort of different Host operating system.
docker exec -it competitive bash
Gotcha
When you restart the system, Docker containers will be stopped. You need to explicitly start the container using container id next time.
To solve this repetitive action, I created a small script to find the container with the name competitive
and
start the container if ti is not already present.
#!/bin/sh
containerId=`docker ps -a -f 'name=competitive' --format "{{.ID}}"`
if [ ! -z "$containerId" ]; then
echo "Found existing container, Starting it"
docker start $containerId
else
echo "Container not found with name competitive, creating one"
docker run \
-e TERM=screen-256color \
-v $(pwd):/app/competitive \
-v $HOME/.dotfiles/nvim/.config/nvim:/root/.config/nvim \
--name competitive -dit competitive
fi
echo "Starting container"
docker exec -it competitive bash
In the above snippet, we are first checking if there is any container with the name competitive. If there is, then it fetches the
container ID and starts it, else it creates a new container and exec
into it.
Final workflow should be something like following:

I had a great time exploring Docker
and what we can achieve via some simple scripts.