One of the reasons I moved to GitLab is that it provides free private repos as well as free CI. It’s CI is based around Docker images which makes it dead easy to build any sort of project from GitLab.
I thought I’d share with you here the simple configuration I use to build things (such as this blog) and put it on to my web-hosting.
To tell GitLab you want to use their CI, you create a file
.gitlab-ci.yml in the root of your
repo. If this file starts with
image: then GitLab knows it’s a Docker build and will use the
appropriate runner. However, this is a general file and you can use any the various runners for
doing your builds (see
but Docker is as general as it gets.
You must specify a Docker image from The Docker Hub although you can also use a local GitLab repo with your own images if you want. As the example I’m showing here is for this Jekyll-based blog (which is Ruby-based), we use a Ruby image:
The next section contains some script-wide variables that can be used for keeping your scripts tidy. So, I ensure that Jekyll is going to be in production mode:
variables: JEKYLL_ENV: production
Next comes some bigger sections. The
build section tells GitLab how to build your code after it has
checked out your repo into the container. The
deploy section tells GitLab how to deploy your code
after it has checked it out into a container. Each of these sections runs as a separate docker process
in GitLab and they do not share the same container.
So, the first section is this:
1 2 3 4 5 6 7 8 9 10 11 build pages: stage: build before_script: - bundle install script: - bundle exec jekyll build artifacts: paths: - _site only: - master
We give the build a name:
pages and it’s the
build stage. In the
before_script section (line 3), each
line gives a shell command to run before we do the build. Here’s we’re ensuring we have all our gems.
Each line in the
script section (line 5) performs the build.
One of the most important parts here is the
artifacts section (line 7) which allows us to pass the result of
the build on to another stage of the pipeline. If we don’t do this, when the Docker container shuts down
we lose everything. Here we tell it to remember the contents of
_site, which is where Jekyll outputs
the fixed HTML to.
So that’s the build done. Now we need to deploy that to my hosting. If you’re hosting on GitLab or GitHub,
moving this output to
public and committing the result is enough.
However, I need to SSH to my own hosting provider to put my blog up there. Doing that requires us to set some SSH keys in the server and GitLab to ensure that the container can talk to my hosting.
To do this, generate a key-pair on your server (on the hosting box) with:
ssh-keygen -t rsa -C "email@example.com" -b 4096
Grab the contents of the
~/.ssh/id_rsa file and in GitLab’s settings for your project,
Settings > Pipelines add a new variable called
SSH_PRIVATE_KEY and paste the contents of
that file as the value. The environment can stay
*. This lets GitLab log into your host without
requiring a password; but only if the server is trusted.
To allow the server to be trusted, you also need to generate a value for
add that as a variable. You can do that with:
Paste the result of that command as the value of the new variable.
Alone, these two variables do nothing, but in our deploy script we update the container with these keys so that it has access to the host. These keys are stored securely in GitLab, behind your username and password, and the Docker container is only running for the duration of the build.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 deploy to site: stage: deploy variables: GIT_STRATEGY: none before_script: # Install ssh-agent if not already installed, it is required by Docker. - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )' # Run ssh-agent - eval $(ssh-agent -s) # Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store - ssh-add <(echo "$SSH_PRIVATE_KEY") # In order to properly check the server's host key, assuming you created the # SSH_SERVER_HOSTKEYS variable previously, uncomment the following two lines # instead. - mkdir -p ~/.ssh - '[[ -f /.dockerenv ]] && echo "$SSH_SERVER_HOSTKEYS" > ~/.ssh/known_hosts' script: - scp -r _site/* <username>@blog.dupplaw.uk:<path in my hosting> dependencies: - build pages
There is more about the SSH process on GitLab’s documentation.
Once all the SSH keys are set up, a simple
scp (line 21) is all that’s needed to get the files on to my hosting.
One interesting variable in the deploy script, is the
GIT_STRATEGY variable defined on line 4. This tells
GitLab not to bother checking out your repo into the container. The deployment is only using the result
of the previous stage, so it doesn’t need the repo. This saves time during the build.
GitLab’s CI is incredible. It’s free and easy to use and supported by Digital Ocean, one of the big Docker hosting suppliers. I’m using it for lots of my personal projects now. How are you using it?