GitLab is a great tool for using continuous integration and deployment.
- Build a bare-minimum Alpine Docker image that will be built and hosted by GitLab in a separate project
- Run a Pipeline on each push that deploys your project using Edeliver.
I will assume that you …
- … have a production server up and running (We’re using DigitalOcean)
- … that is already deployed using edeliver from your local machine
1. Build a Docker Image
Jobs in a GitLab pipeline runs in a Docker container. That means we can use a custom Docker image for building and deploying our project.
Let’s get to it.
We will use the official elixir repository as a base and install the necessary applications to use Edeliver. The Dockerfile for this docker image will look like this:
FROM elixir:1.6-alpine RUN \ apk update && \ apk --no-cache --update add \ bash openssh-client make git ncurses-libs RUN mix local.hex --force && \ mix local.rebar --force
You should be able to build this Dockerfile using the command
docker build. You could push this image to Docker hub and call it a day.
But we wanted to make use of GitLab’s Registry feature and host this image in a separate project. And we want this image to build and publish automatically when we update our Dockerfile. So let’s add a
.gitlab-ci file to the project so that GitLab will know what to do when we push changes to this repository.
This file says that we should build a docker image with the tag
:latest every time we push to the master branch of the repository. For every other branch we tag the image with the branch- or tag name.
image: docker:latest services: - docker:dind before_script: - docker login -u gitlab-ci-token -p $CI_BUILD_TOKEN $CI_REGISTRY build-master: stage: build script: - docker build --pull -t "$CI_REGISTRY_IMAGE:latest" . - docker push "$CI_REGISTRY_IMAGE:latest" only: - master build-other: stage: build script: - docker build --pull -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME" . - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME" except: - master
Our repository is public if you’d like to have a look. But please resist the temptation to use this repository in your build step. It might change without warning.
2. Deploy automatically on every push
Edeliver uses ssh and git to deploy code to your server. Make sure your server is setup to use SSH key-based authentication and not password authentication.
Add your ssh private key to your GitLab project as a secret variable: in:
Project Settings -> Secret variables -> Add a variable.
SSH_PRIVATE_KEY and make sure to include the lines
-----BEGIN RSA PRIVATE KEY----- and
-----END RSA PRIVATE KEY-----.
Let’s put our Docker image to good use. Create a
.gitlab-ci.yml file in your elixir project. It will use the new Docker image; add the ssh key; upgrade production and finally perform migrations.
image: registry.GitLab.com/robin-good/docker-build-image:latest deploy: stage: production only: - master environment: production before_script: - eval $(ssh-agent) - echo "$SSH_PRIVATE_KEY" | ssh-add - - mkdir -p ~/.ssh - echo -e "StrictHostKeyChecking no" >> ~/.ssh/config - mix deps.get script: - mix edeliver upgrade production - mix edeliver migrate production
It will probably take a few minutes to build. The time it takes depends on how fast your build server is. But one thing that improves on the speed is to cache the build.
cache: paths: - _build - deps key: "$CI_JOB_NAME" untracked: true
I’ve had mixed results with caching in GitLab pipelines. Sometimes it takes ages to download and extract the cache. It seems to be a known issue.
Bam! You’re “done”.
You’ve got the bare minimum up and running. Your next step should be to add a testing stage to your pipeline so that your deploy job only runs when all the tests passes.
I might do a post on our complete setup in the future. Including how to get up and running with edeliver and phoenix. Give me a shout-out on Twitter if that’s something you’d like to read.