There are lots of posts about setting up CD with Jenkins and Kubernetes but I haven't found any describing how to do it with Nomad and Gitlab.
So here's how I did it…
I'm assuming that you've already got a working Gitlab (on premise) and Nomad cluster setup and that you've already got CI (continuous integration) working with them.
Getting this working isn't particularly difficult, the “trick” is
to run a nomad
command that loads an updated job file.
Overall steps are:
- make a docker image with
nomad
in it - update your
.nomad
job file - update your project's
.gitlab-ci.yml
with a deploy step - go have a beverage
Docker in Docker build image
Keeping track of all the images when building can get a bit tricky…
I've made an “image builder” based on alpine linux that has docker
in it. It's called ${docker_registry}/public/alpine:3
in the
examples below.
If you already have a similar builder image then you can skip this step.
FROM gliderlabs/alpine
RUN apk add --update --no-cache docker make bash curl jq
docker build -t ${docker_registry}/public/alpine:3 -F Dockerfile .
docker push ${docker_registry}/public/alpine:3
or have an appropriate .gitlab-ci.yml
do it for you.
Make nomad docker image
Make a new Gitlab project, in my case ops/nomad-deployer
.
- download nomad
- make
Dockerfile
and.gitlab-ci.yml
git commit
andgit push
all the things
Dockerfile
FROM alpine:3.9
RUN apk add --update --no-cache libc6-compat gettext
COPY nomad /usr/local/bin/nomad
.gitlab-ci.yml
variables:
docker_registry: registry.example.com
docker_image: public/nomad-deployer
stages:
- build
build:
stage: build
image: ${docker_registry}/public/alpine:3
script:
- tag=${docker_registry}/${docker_image}:latest
- echo "building and pushing tag $tag"
- docker build --pull -t ${tag} -f Dockerfile .
- docker push ${tag}
Update your project
And now for the fun part!
I'm assuming that you already have a .nomad
file for your service
in the current directory and it's called project.nomad
. Update
code below to match your situation.
The magic happens by having envsubst
substitute CI_COMMIT_SHORT_SHA
with the current git hash. You lose having a nice docker image version
but gain not having to increment anything and always knowing exactly what
code you're running.
project.nomad
job "project" {
...
group "project" {
...
task "project" {
...
driver = "docker"
config {
image = "registry.example.com/example/project:${CI_COMMIT_SHORT_SHA}"
...
} # config
} # task run
} # group
} # job
.gitlab-ci.yml
At the time of writing there is an open
bug where gitlab
doesn't properly expand CI_PROJECT_NAME
but once it's fixed replace
project
with ${CI_PROJECT_NAME}
below for a more generic .gitlab-ci.yml
.
- change
NOMAD_ADDR
to match your situation - change all the
variables
actually… - feel free to delete line
- cat job.nomad
once everything is working - note line
when: manual
, that means you need to manually push a button in gitlab to deploy
variables:
NOMAD_ADDR: http://hashi1.example.com:4646
docker_registry: registry.example.com
docker_image: example/project
stages:
- build
- test
- deploy
build:
stage: build
image: ${docker_registry}/public/alpine:3
script:
- tag=${docker_registry}/${docker_image}:${CI_COMMIT_SHORT_SHA}
- echo "building and pushing tag $tag"
- docker build --pull -t ${tag} -f Dockerfile .
- docker push ${tag}
test:
stage: test
...
deploy:
stage: deploy
image: registry.example.com/public/nomad-deployer:latest
script:
- envsubst '${CI_COMMIT_SHORT_SHA}' < project.nomad > job.nomad
- cat job.nomad
- nomad validate job.nomad
- nomad plan job.nomad || if [ $? -eq 255 ]; then exit 255; else echo "success"; fi
- nomad run job.nomad
environment:
name: production
allow_failure: false
when: manual
bringing it all together
- edit some sweet
project
code git commit
andgit push
it- open gitlab jobs url for your project
- push the play button to deploy your new version
If you have great tests and want to auto deploy then change:
when: manual
to
only:
- master
all done
Hopefully you've now got CD to accompany your CI.
Thanks to @henrikjohansen on https://gitter.im/hashicorp-nomad/Lobby for his code snippet that was the missing piece for me.