Exercise 6: Change the order of the pipeline steps¶
Objective¶
Learn how to change the order of pipeline stages, and how to build a parallel pipeline
Change the order of stages¶
Build stages have dependencies, which give them access to the artifacts from other stages, but the order is determined by the stages: declaration in the .gitlab-ci.yml file. After the last exercise, your stages: section will look something like this:
> cat .gitlab-ci.yml | grep --after-context=5 --before-context=1 stages
stages:
- build
- test
- deploy
- test-docker
If you simply swap the order of the test and deploy stages, commit, tag and push your code, you should see that the build pipeline runs in a different order, with the deploy (create and push the docker image) happening before the test (running the executable).
It’s up to you to decide if it makes sense to ‘deploy’ before testing, or after, this simply illustrates how to do it.
Sequential and parallel builds¶
In our example, testing the executable and testing the docker image could, in principle, happen in parallel. If the tests were long-running, and we have enough resources in our gitlab runners, this can save time. We can do this very simply by removing the test-docker stage from the stages: piece and changing the run-docker step to execute the test stage, instead of having it execute the test-docker stage.
In other words, there will be two steps, run and run-docker, both of which run the test stage. They will happen in the correct order, after the deploy stage, but they are eligible to run in parallel if there are sufficient resources. If there are not sufficient resources for the steps to run in parallel, they will simply happen sequentially, in a random order.
Make this change to your YAML file, commit it, tag it, and push it to the server. Take a look at the pipeline page and see how it shows the parallel steps. It should look like this:
Conclusion¶
- You now know how to write pipelines which can run stages sequentially, or in parallel.
- Each stage of a pipeline can have many steps that run that stage.
- Each step in a stage must succeed, or the whole pipeline fails
- Each step in a stage must complete before the next stage is free to start
- Parallel stages can speed up testing, e.g. by running tests in many environments at the same time
Best Practices¶
- Think carefully about what you are trying to achieve in your pipeline. Keep the steps small, and parallelise where it makes sense.
Cheat-sheet¶
In case you get stuck, this is what your .gitlab-ci.yml file should now look like. Don’t peek until you’ve had a go yourself though!
> cat .gitlab-i.yml
variables:
DOCKER_TLS_CERTDIR: ""
GIT_STRATEGY: clone
REGISTRY_USER: tonywildish
APPLICATION: tiny-test
LATEST_IMAGE: $CI_REGISTRY/$REGISTRY_USER/$APPLICATION:latest
RELEASE_IMAGE: $CI_REGISTRY/$REGISTRY_USER/$APPLICATION:$CI_BUILD_REF_NAME
DOCKER_DRIVER: overlay
before_script:
- echo "Starting..."
- export DOCKER_IMAGE=$RELEASE_IMAGE
- if [ "$CI_BUILD_REF_NAME" == "master" ]; then export DOCKER_IMAGE=$LATEST_IMAGE; fi
- echo "Build docker image $DOCKER_IMAGE"
stages:
- build
- deploy
- test
compile:
stage: build
image: gcc:6
services:
- docker:dind
artifacts:
name: "${CI_BUILD_NAME}_${CI_BUILD_REF_NAME}"
untracked: true
expire_in: 1 week
script:
- make
run:
stage: test
dependencies:
- compile
only:
- tags
script:
- echo "Testing application. First, list the files here, to show we have the git repo + the artifacts from the build step"
- ls -l
- echo 'Now try running it'
- ./hello
- echo "If that failed you won't see this because you'll have died already"
install:
stage: deploy
image: docker:latest
services:
- docker:dind
dependencies:
- compile
script:
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
- echo Building $DOCKER_IMAGE
- docker build -t $DOCKER_IMAGE .
- echo Deploying $DOCKER_IMAGE
- docker push $DOCKER_IMAGE
run-docker:
stage: test
image: docker:latest
services:
- docker:dind
script:
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
- docker run $DOCKER_IMAGE
after_script:
- echo "Congratulations, this step succeeded"