Story
For the past few weeks we’ve been designing and building a project in Lua. To speed up our work flow, we needed to set up CI pipelines. Unfortunately available Lua images on docker hub are mostly unmaintained and out of date.
That is why we’ve built our own image, and decided to document the process!
Crafting Dockerfile
Let’s start with base image. Alpine has a small size and great package manager, so it seemed like a perfect choice.
FROM alpine:latest
A good idea is to start with Alpine, then upgrade to something more feature full (like Debian or Ubuntu), in case Alpine is missing features we need.
Next, we define “ARG” variables for lua and luarocks versions. ARG instructions define variables that are passed at build-time. They will not be available in the resulting container.
ARG LUA_VERSION
ARG LUA_ROCKS_VERSION
We use these later to download specific versions of all binaries.
Some utilities to build our binaries:
RUN apk add make gcc musl-dev openssl wget unzip
Then we download, build and install our chosen Lua version:
RUN wget http://www.lua.org/ftp/lua-${LUA_VERSION}.tar.gz && \
tar xf lua-${LUA_VERSION}.tar.gz && \
cd lua-${LUA_VERSION} && \
make all test && \
ln -s /lua-${LUA_VERSION}/src/lua /usr/bin/lua
Same idea for luarocks:
RUN wget https://luarocks.org/releases/luarocks-${LUA_ROCKS_VERSION}.tar.gz && \
tar zxpf luarocks-${LUA_ROCKS_VERSION}.tar.gz && \
cd luarocks-${LUA_ROCKS_VERSION} && \
./configure --with-lua-include=/lua-${LUA_VERSION}/src/ && \
make bootstrap
We end our container with a default command. Technically this is not required, but we consider it a good practice.
CMD ["lua", "-v"]
Here is the final Dockerfile. It should easily allow us to build any Lua version we need.
FROM alpine:latest
ARG LUA_VERSION
ARG LUA_ROCKS_VERSION
RUN apk add make gcc musl-dev openssl wget unzip
RUN wget http://www.lua.org/ftp/lua-${LUA_VERSION}.tar.gz && \
tar xf lua-${LUA_VERSION}.tar.gz && \
cd lua-${LUA_VERSION} && \
make all test && \
ln -s /lua-${LUA_VERSION}/src/lua /usr/bin/lua
RUN wget https://luarocks.org/releases/luarocks-${LUA_ROCKS_VERSION}.tar.gz && \
tar zxpf luarocks-${LUA_ROCKS_VERSION}.tar.gz && \
cd luarocks-${LUA_ROCKS_VERSION} && \
./configure --with-lua-include=/lua-${LUA_VERSION}/src/ && \
make bootstrap
CMD ["lua", "-v"]
To make sure it works, save it as “Dockerfile”, then run docker build -t .
Crafting CI pipelines
We are ready to define pipelines that will build our image. Then it will tag the image, and push it to docker hub.
Since we need docker for container operations, set base image as “docker:latest” and set up “dind” service (docker in docker).
image: docker:latest
services:
- docker:dind
Most important part of the pipelines is our variables. Specifically LUA_VERSION and LUA_ROCKS_VERSION. They will allow us to define versions of the binaries inside the container. Additionally LUA_VERSION will allow us to tag our container when we push it to docker hub.
variables:
CI_REGISTRY: "docker.io"
CI_REGISTRY_IMAGE: "index.docker.io/antfarminteractive/lua"
LUA_VERSION: "5.4.2"
LUA_ROCKS_VERSION: "3.4.0"
We add “build” stage to build our container, tag it, login and push it to
dockerhub. Below is the complete .gitlab-ci.yaml
. Notice there are two build
stages. test-build
only builds a container (doesn’t tag or push it), and is
used for merge requests.
image: docker:latest
services:
- docker:dind
variables:
CI_REGISTRY: "docker.io"
CI_REGISTRY_IMAGE: "index.docker.io/antfarminteractive/lua"
LUA_VERSION: "5.4.2"
LUA_ROCKS_VERSION: "3.4.0"
test-build:
stage: build
script:
- docker build --build-arg LUA_VERSION=$LUA_VERSION --build-arg LUA_ROCKS_VERSION=$LUA_ROCKS_VERSION -t "$CI_REGISTRY_IMAGE:$LUA_VERSION" .
except:
- master
build:
stage: build
script:
- docker build --build-arg LUA_VERSION=$LUA_VERSION --build-arg LUA_ROCKS_VERSION=$LUA_ROCKS_VERSION -t "$CI_REGISTRY_IMAGE:$LUA_VERSION" .
- docker tag "$CI_REGISTRY_IMAGE:$LUA_VERSION" "$CI_REGISTRY_IMAGE:latest"
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker image push --all-tags $CI_REGISTRY_IMAGE
only:
- master
There is one last step to complete the puzzle. We need to create an access token on dockerhub, so gitlab runner is allowed to login and push our container. Here are instructions from docker docs for managing access tokens.
Go to Repository > Settings > CI / CD > Variables
and add:
- CI_REGISTRY_USER - Put your dockerhub username here.
- CI_REGISTRY_PASSWORD - Put your newly created access token.
Conclusion
With this simple setup, we’ve created a pipeline. It builds a docker container for specific version of Lua interpreter (and luarocks as a bonus).
When new version of Lua is released, LUA_VERSION update will build a new container and automatically push it to dockerhub.
Latest code for all files in this post can be found at: https://gitlab.com/antfarm-interactive/lua-container.