Pipelines
Pipelines in Gitlab are defined in the .gitlab-ci.yml file that lives in the main folder of your repo.
.gitlab-ci.yml is a YAML file that provides instructions to GitLab CI/CD, in this file you define the structure and order of jobs that the runner should execute. Conditionals enable the runner to make decisions and jobs themselves execute a set of specified commands. If any job fails (exit status != 0), then the whole pipeline will fail and the following jobs, such as deployment, will simply not execute.
For Platon PaaS this configuration file is crucial and used to build your project on GitLab and then deploy the application to the cluster.
By default, there are three shared GitLab CI/CD runners configured and these are capable of docker-in-docker abbreviated as dind. If you have a large and frequent workload, or high security requirements consider setting up a dedicated runner for just the specific project.
Make sure to read Getting started first to get an idea of how to best structure your project.
You may also check out the README.md of the hello-world-cicd-components example.
Official quick start guide:
https://docs.gitlab.com/ee/ci/quick_start/index.html
YAML keyword reference:
https://docs.gitlab.com/ee/ci/yaml/
Collection of examples:
https://docs.gitlab.com/ee/ci/examples/index.html
.gitlab-ci.yml walkthrough
A fully functional .gitlab-ci.yml configuration can be found in one of the example projects here: .gitlab-ci.yml
The .gitlab-ci.yml configuration file is enough to deploy to PaaS 2.0 on its own, but an advanced walkthrough of the contents can be found below:
include
GitLab CI/CD components is the successor of gitlab-ci-helpers
. It is recommended to use CI/CD components in your pipeline since it will receive new features and be maintained by Platon. The gitlab-ci-helpers
will be deprecated and removed in the future.
Read more about CI/CD components: CI/CD Components
If you are migrating from gitlab-ci-helpers
, please refer to the migration chapter.
The first part of this file uses the "include"-keyword, which imports YAML from an external YAML file into your local CI/CD configuration.
It is recommended that you use components published to the CI/CD Catalog as your base configuration as this configuration will be maintained by Platon.
# This is a global keyword and needs to be located at the top-level scope
include:
- component: $CI_SERVER_FQDN/platon/ci-components/docker/docker@1.0.2
- component: $CI_SERVER_FQDN/platon/ci-components/imagescan/imagescan@1.0.0
- component: $CI_SERVER_FQDN/platon/ci-components/deploy/deploy@1.0.0
$CI_SERVER_FQDN
here is the hostname of the GitLab instance. You can only reference components in the same GitLab instance as your project.
variables
Secondly, we have some crucial variables that need to be defined. The variables-keyword is used to define environment variables for running CI-jobs.
# This is a global keyword and needs to be located at the top-level scope
variables:
KUBE_PROD_DOMAIN: hello.paas2.uninett.no
KUBE_TEST_ID: hello
HTTP_PORT: '80' # The port number of the web server.
REPLICAS: '1'
These variables create the Ingress configuration that maps a unique full qualified domain name to your application, which will make it possible to access it from the internet.
- KUBE_PROD_DOMAIN: This variable will be the final FQDN for the production version of your application.
- KUBE_TEST_ID: This variable is used to generate test instances of your application. By default, only staging is configured, and in this case the FQDN of staging would be: $KUBE_TEST_ID-staging.paas2.uninett.no. Please note that testing instances of your application are not available to the public.
- HTTP_PORT: This variable defines what port web requests should be routed to by the load balancer running outside of the Kubernetes cluster. TLS/HTTPS is terminated by the Ingress-controller/load balancer, so you only need to accept HTTP requests on this port.
- REPLICAS: This variable is used in the deployment.yaml file to define the amount of replication you want for your application. Typically, this is set to 1 for testing, but there is an override in place which sets replicas to 2 for the production deployment. See stage production for more details.
stages
Thirdly, we have the stages-keyword which defines the order of which GitLab CI/CD should execute the jobs when it runs a pipeline from your commit. It is important to note that all jobs in a single stage can be run in parallel, but that jobs in a stage must be completed before jobs in the next stage are started.
# This is a global keyword and needs to be located at the top-level scope
stages:
- build
- test
- review
- staging
- production
In this case we will first run the build stage, which will build the Docker container. After this is finished, we run the test stage which runs some important testing of the containers before continuing. When this is done there are only deployment steps left: review, staging and production.
At every step in our example, we import a template from CI/CD components, which implies that you need to understand the contents of the component in order to know what is running in each step. In this case, it is very simplified to make it easy for anyone who only wants to deploy their application and does not worry about the nitty-gritty details. The CI/CD components are managed by Platon which means they will be updated over time without the need for any changes in the individual projects.
build
build:
extends: .docker-build
stage: build
In the first stage: build, we run the build job which extends the .docker-build template from the docker component imported at the very top. This job runs on every commit to any branch.
test
imagescan:
extends: .imagescan
stage: test
The imagescan job runs during the test stage to make a static scan of the newly built Docker image for vulnerabilities. This job extends the imagescan component. Like the build job it runs on every commit to any branch.
staging
# This is a job and needs to be placed at the top-level scope
staging:
extends: .staging
stage: staging
rules:
- if: $CI_COMMIT_BRANCH = $CI_DEFAULT_BRANCH
script:
- deploy deployment.yaml
The staging job runs during the staging stage and deploys the testing version of the application. This job extends the .staging job from deploy component.
Implicit here is that the staging job only runs on the master branch. It will be deployed continuously if all stages before it has passed successfully.
One important thing to note here is that the script-keyword configures what commands the job should run. This is the first time we explicitly define what command is being run, and in this case, it is being used to execute the deploy-script from asm/kubernetes-deploy using the contents that have been populated into the deployment.yaml file in the project.
production
# This is a job and needs to be placed at the top-level scope
production:
extends: .production
stage: production
variables:
REPLICAS: "2"
rules:
- if: $CI_COMMIT_BRANCH = $CI_DEFAULT_BRANCH
script:
- deploy deployment.yaml
The production job runs during the production stage and deploys the application to the production environment. This job extends the .production job from deploy component.
Implicit here is that the production job only runs on the master or main branch. It is also configured with the manual option, meaning that there is a need for manual intervention on the pipeline from the end-user to trigger the actual deployment. Hence there is no continuous deployment to production by default.
There is a hard-coded REPLICAS: "2" variable set to the production job which overrides the default replicas specification and ensures there are two copies of the web-application once it is deployed to production. This ensures high availability (HA) for the production-level application.
review
# This is a job and needs to be placed at the top-level scope
review:
extends: .review
stage: review
script:
- deploy deployment.yaml
rules:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
The review job runs during the review stage and makes sure a review copy of the application will be deployed when working in a branch separate from master. This job extends the .review job from deploy component.
Implicit here is that the review job will run on any branch, except main or master, and it will do continuous deployment to the given review environment if all stages before it has passed successfully.
This is essentially what lets you test changes without affecting staging or production. The FQDN of the staging environment will be created by a combination of $KUBE_TEST_ID set as an environment variable at the very top of the file and the branch name as given by the environment from the variable $CI_ENVIRONMENT_SLUG.
stop_review
# This is a job and needs to be placed at the top-level scope
stop_review:
extends: .stop_review
stage: review
The stop_review job runs upon manual intervention during the review stage and makes sure that the created review copy of the application will be deleted when there is no longer any use for it. In order to manually trigger the job click the stop-button inside the review stage from pipeline of the commit that triggered it, which will delete the review copy. The job extends the .stop_review template from deploy component.
When a branch is deleted, for example upon a successful merge request, the review copy environment will be deleted from Platon PaaS.
Additional configuration
Manual destroy action for staging and production
Normally there would be no reason to stop the staging and the production environment for your application, but sometimes an application gets scheduled for removal. For example, when it is no longer necessary, superseded or you have discovered a critical security vulnerability.
A manual intervention to destroy the staging and production environments can be added through importing the relevant templates from deploy component. This is not enabled by default to prevent the accidental deletion of the production application.
# This is a job and needs to be placed at the top-level scope
stop_staging:
extends: .stop_staging
stage: staging
stop_production:
extends: .stop_production
stage: production
Once the pipeline from your last commit has finished running a stop-button should be visible in the staging and production stage of your pipeline. This works in the exact same way as the stop_review job does for review copies and clicking the stop button will destroy the deployment in that environment.
CI/CD Components
A CI/CD component is a reusable single pipeline configuration unit. The concept of components introduces versioning which allows for safer and easier updates to the component itself. The CI/CD components are very similar to the gitlab-ci-helpers defined as YAML file imported with include
keyword at the very top of a .gitlab-ci.yml
file. Components can be listed in CI/CD Catalog for easier discovery.
The components were written to directly replace gitlab-ci-helpers meaning that the components should work with the same variables as gitlab-ci-helpers with exception for only/except
keywords. For some components the default variables can be overwritten by specifying the inputs. The optional parameters are listed in the docs for each component and are used with inputs
keyword.
For fully functional example project using CI/CD components see: hello-world-cicd-components.
Example:
include:
- component: https://gitlab.sikt.no/platon/ci-components/imagescan/imagescan@0.1.10
# Optional inputs
inputs:
image: myimage
image_tag: mytag
Pin components version
The CI/CD components are versioned with semantic versioning model. The semantic versioning consists of major.minor.patch component. One can decide to pin a component to a specific major version. For example:
include:
- component: $CI_SERVER_FQDN/platon/ci-components/deploy/deploy@1
This will import all components published to the CI/CD Catalog with a major version of 1.
More information about pinning component to specific version can be found in official documentation
.
Rules instead of only/except
The components use GitLab rules instead of only/except. Using rules is recommended by GitLab since only/except was deprecated. Detailed documentation with examples can be found here: GitLab rules docs
Example:
only:
- branches
except:
- master
- main
# Translates to:
rules:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
Rules are more flexible, but the flexibility comes with price. One has to be more explicit when the job should be included in the pipeline. For the example above the job will be added to pipeline when merge request is created. To prevent this one has to edit the rule like this:
rules:
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE != "merge_request_event"
Migrating to CI/CD Components from gitlab-ci-helpers
The CI/CD components are based on the legacy gitlab-ci-helpers. The components split the gitlab-ci-helpers into smaller units.
To migrate to CI/CD components you need to rewrite the include
section of your .gitlab-ci.yml file. You need to review which jobs from the gitlab-ci-helpers is your pipeline using and include the component for it.
For example to migrate a pipeline which builds docker image with .docker-build
, scans it with .imagescan
job and deploys manifests to PaaS with .review
and .production
one just needs to replace this:
include:
- project: 'asm/gitlab-ci-helpers'
file: '/gitlab-ci-helpers.yml'
with this:
include:
- component: $CI_SERVER_FQDN/platon/ci-components/docker/docker@1.0.2
- component: $CI_SERVER_FQDN/platon/ci-components/imagescan/imagescan@1.0.0
- component: $CI_SERVER_FQDN/platon/ci-components/deploy/deploy@1.0.0
All other pipeline configuration can stay the same, they are compatible. For more details check the example project using CI/CD components: hello-world-cicd-components.
Writing your own CI/CD component
One can create a custom CI/CD component tailored to specific use. GitLab has solid documentation on how to write CI/CD components: GitLab CI/CD components. If you write your own component, please consider publishing it to the CI/CD Catalog so other developers can use it as well.