I’ve recently been making an effort to migrate my server stack to use ansible to manage everything. So I wanted to see if I could compile and deploy my blog using just ansible. (spoiler: I can)
Overview
Turns out if you approach it the correct way, it’s pretty easy. First I made a Dockerfile which first installs cobalt,
then builds my site, and finally uses nginx to actually serve the static files. After that, I used ansible to clone my blog’s repo
, build the docker image, and deploy it.
Creating the Dockerfile
I broke my Dockerfile into 2 sections. The first section (builder) installs cobalt and builds my blog.
FROM rust:1.73 AS builder
RUN cargo install cobalt-bin
COPY . /src
WORKDIR /src
RUN cobalt build
The second section copies the built files from the builder section, and uses nginx to serve the content.
FROM nginx
COPY --from=builder /src/_site /usr/share/nginx/html
In the end, I wound end with this dockerfile.
Using ansible
Now that I had an easy way to build a docker image, I just needed to automate that build process. Since I host my repos on GitLab I could’ve set up a seperate CI/CD pipeline that would push my docker image to GitLab’s container registry. However as my docker image would also include all my static content, I don’t really want to push the image to any registry if I can avoid it.
Since I’m already using ansible to deploy my existing services, I decided I should just try to use ansible. It ended up being simpler than expected.
Build the image
Building the docker image basically just boils down to two steps. First, I clone my blog repo to get the content and Dockerfile. Second, build the image.
Cloning my repo was very simple thanks to the ansible.builtin.git module.
- name: Pull blog repo
ansible.builtin.git:
dest: "your_dest_dir"
repo: "ssh://git@your.git/repo/url"
clone: true
accept_hostkey: true
single_branch: true
version: master
register: _blog
destsets where to clone the repo to. I chose to use a sub-directory of a directory where I store various config files for my other services, but you could also clone it into a temporary directoryrepois the URL to your git repo. I’d recommend using assh://URL instead ofhttps://, especially if your repo is privateclonejust tells ansible that you want to clone the repo if there’s not a repo at thatdestaccept_hostkeyjust tells ansible/SSH to be fine with whatever SSH hostkey the repo returns. This is useful if you’re deploying to a new server that hasn’t connected to your git provider before, but could potentially be dangeroussingle_branchtells ansible to only clone one branch which saves time and bandwith versus downloading the whole repoversiontells anisble which branch to clone
Then building the image was accomplished thanks to the community.docker.docker_image module.
- name: Build docker image
community.docker.docker_image:
build:
path: "your_dest_dir"
pull: true
name: blog
force_source: "{{ _blog.changed }}"
source: build
tag: latest
build.pathshould be the same as thedestoption beforebuild.pulltells ansible to pull the latest images for each image in yourDockerfile. You can feel free to disable that thoughnamewill be the name of the docker imagesourceforces ansible to build the image instead of pulling it or loading it from a local fileforce_sourceis needed as otherwise ansible will only build the image if it doesn’t existtagsets the image tag. It defaults tolatestso you can omit that option
Deployment
Then finally I deploy my blog as a normal docker container thanks to the community.docker.docker_container module.
- name: Deploy blog
community.docker.docker_container:
name: blog
image: blog:latest