项目作者: ondrejsika

项目描述 :
Skoleni Gitlab CI (CI/CD)
高级语言: Go
项目地址: git://github.com/ondrejsika/gitlab-ci-training.git
创建时间: 2019-07-24T07:00:15Z
项目社区:https://github.com/ondrejsika/gitlab-ci-training

开源协议:

下载


Ondrej Sika (sika.io) | ondrej@sika.io | join slack, open slack

Gitlab CI Training

About Course

Any Questions?

Write me mail to ondrej@sika.io

Course

About Me - Ondrej Sika

Freelance DevOps Engineer, Consultant & Lecturer

  • Complete DevOps Pipeline
  • Open Source / Linux Stack
  • Cloud & On-Premise
  • Technologies: Git, Gitlab, Gitlab CI, Docker, Kubernetes, Terraform, Prometheus, ELK / EFK, Rancher, Proxmox

Star, Create Issues, Fork, and Contribute

Feel free to star this repository or fork it.

If you found bug, create issue or pull request.

Also feel free to propose improvements by creating issues.

DevOps Live (Czech only)

https://devopslive.cz

DevOps Kniha (Czech only)

https://kniha.sika.io

Chat

For sharing links & “secrets”.

Agenda

What is CI

Git

Git is a version control system for tracking changes in computer files and coordinating work on those files among multiple people.

Gitlab

From project planning and source code management to CI/CD and monitoring, GitLab is a complete DevOps platform, delivered as a single application.

CI

In software engineering, continuous integration is the practice of merging all developer working copies to a shared mainline several times a day.

Usage of CI

Automatization of:

  • build process
  • testing - unit tests, integration tests, linting, formating
  • deployment - dev, staging, production

Gitlab CI Features

  • Integration into Gitlab (CE & EE)
  • Versioned (YAML file in repository)
  • Easy to scale
  • Docker & Kubernetes support

Gitlab CI Architecture

gitlab-ci-architecture

Gitlab Runner

Runs on every platform - Linux, Mac, Windows, Docker, Kubernetes

How to install & configure:

My scripts to bootstrap Gitlab Runner in Docker: https://github.com/ondrejsika/ondrejsika-gitlab-runner

Setup Gitlab CI

Demo Gitlab

Setup

You can setup Gitlab on Digital Ocean & Cloudflare using Terraform

https://github.com/ondrejsika/terraform-demo-gitlab

Login

SSH Password

Password is asdfasdf1234 for both: Girlab on root@gitlab.sikademo.com and Runner on root@runner.sikademo.com

Install Runners into Gitlab

Go to Admin -> Runners

And Set up a shared Runner manually

Setup Gitlab Runner using slu

  1. ssh root@runner.sikademo.com

Install slu

  1. curl -fsSL https://raw.githubusercontent.com/sikalabs/slu/master/install.sh | sudo sh

From Vault

  1. slu login --url https://vault-slu.sikalabs.io --user gitlab-ci-sikademo --password gitlab-ci-sikademo
  2. slu gitlab-ci setup-runner --gitlab sikademo

From CLI arguments

  1. slu gitlab-ci setup-runner --gitlab-url https://gitlab.sikademo.com --token <token>

[DEPRECATED] Setup runner by shell scripts

  1. ssh root@runner.sikademo.com
  1. git clone https://github.com/sikalabs/sikalabs-gitlab-runner
  2. cd sikalabs-gitlab-runner
  3. ./create-runner.sh
  4. ./register-runner.sh https://gitlab.sikademo.com/ h94VrzQzZnJ_va3hxGGW
  5. ./set-concurrency.sh

Gitlab Workflow (CI) VS Code Extension

https://marketplace.visualstudio.com/items?itemName=GitLab.gitlab-workflow

CI Jobs

Gitlab CI YAML

Configuration of everything aroud Gitlab CI is stored inside Git repository in file .gitlab-ci.yml

If you don’t know YAML format, check out this simple YAML tutorial - https://learnxinyminutes.com/docs/yaml

Here is a Gitlab CI YAML reference - https://docs.gitlab.com/ee/ci/yaml

First Job

Create new projet and create there file .gitlab-ci.yml with following content:

  1. # .gitlab-ci.yml
  2. job:
  3. script: echo Hello World!

Push to Gitlab and check it out.

Job

Jobs are smalles units which can be executed by Gitlab CI. Here are samples of common job configurations.

Jobs are top level object in Gitlab CI YAML files instead of few keywords. Keywords are: image, services, stages, types, before_script, after_script, variables, cache

Script

Every job require script - it’s a shell scrip which will be executed by job. Script can be string or list of strings.

  1. # .gitlab-ci.yml
  2. job1:
  3. script: echo Hello World!
  4. job2:
  5. script:
  6. - echo Hello World!
  7. - echo Ahoj Svete!

Tags

You can choose specific set of runners to execute the job

  1. # .gitlab-ci.yml
  2. build:
  3. tags:
  4. - build
  5. script: echo Build!
  6. deploy:
  7. tags:
  8. - kubernetes
  9. script: echo Deploy!

Stages

You can define order of jobs by stages. You can define stages and their order. Jobs in same stage run in parallel and after CI finishes all job in stage, then start jobs from next.

  1. # .gitlab-ci.yml
  2. stages:
  3. - build
  4. - test
  5. build:
  6. stage: build
  7. script: echo Build!
  8. test1:
  9. stage: test
  10. script: echo Test1!
  11. test2:
  12. stage: test
  13. script: echo Test2!

.pre, .post stages

Default stages which runs before and after our user-defined stages. No need to specify them in stages.

  1. stages:
  2. - build
  3. - test
  4. pre:
  5. stage: .pre
  6. script: echo Pre ...
  7. build:
  8. stage: build
  9. script: echo Build!
  10. test:
  11. stage: test
  12. script: echo Test!
  13. post:
  14. stage: .post
  15. script: echo Post ...

Example with include

  1. include: https://raw.githubusercontent.com/ondrejsika/gitlab-ci-training/refs/heads/master/examples/library/hello/hello.gitlab-ci.yml
  2. pre:
  3. stage: .pre
  4. script: echo Pre ...
  5. post:
  6. stage: .post
  7. script: echo Post ...

Needs

  1. # .gitlab-ci.yml
  2. stages:
  3. - build
  4. - test
  5. - deploy
  6. linux build:
  7. stage: build
  8. script: sleep 10 && echo Done
  9. mac build:
  10. stage: build
  11. script: sleep 20 && echo Done
  12. lint:
  13. stage: test
  14. needs: []
  15. script: echo Done
  16. linux unit tests:
  17. stage: test
  18. needs:
  19. - linux build
  20. script: echo Done
  21. linux e2e tests:
  22. stage: test
  23. needs:
  24. - linux build
  25. script: sleep 10 && echo Done
  26. mac unit tests:
  27. stage: test
  28. needs:
  29. - mac build
  30. script: sleep 5 && echo Done
  31. mac e2e tests:
  32. stage: test
  33. needs:
  34. - mac build
  35. script: sleep 30 && echo Done
  36. release linux:
  37. stage: deploy
  38. script: "echo Done"
  39. needs:
  40. - lint
  41. - linux unit tests
  42. - linux e2e tests
  43. release mac:
  44. stage: deploy
  45. script: "echo Done"
  46. needs:
  47. - lint
  48. - mac unit tests
  49. - mac e2e tests

Needs in same stage

  1. stages:
  2. - test
  3. test1:
  4. stage: test
  5. script: echo Test1!
  6. test2:
  7. stage: test
  8. script: echo Test2!
  9. needs:
  10. - test1

Before & After Script

You can define script which will be executed befor and after job script. You can define those script globally or per job.

  1. # .gitlab-ci.yml
  2. before_script:
  3. - echo Global before
  4. after_script:
  5. - echo Global after
  6. job1:
  7. script: echo Job1!
  8. job2:
  9. script: echo Job2! && false
  10. job3:
  11. before_script:
  12. - echo Local before
  13. after_script: []
  14. script: echo Job3!

Cleanup after script

  1. # .gitlab-ci.yml
  2. job1:
  3. script:
  4. - echo deploy!
  5. - echo test! && false
  6. - echo cleanup!
  7. job2:
  8. script:
  9. - echo deploy!
  10. - echo test! && false
  11. after_script:
  12. - echo cleanup!

Job status in after script

  1. job1:
  2. script:
  3. - echo deploy!
  4. - echo test!
  5. after_script:
  6. - echo $CI_JOB_STATUS
  7. job2:
  8. script:
  9. - echo deploy!
  10. - echo test! && false
  11. after_script:
  12. - echo $CI_JOB_STATUS

CI_JOB_STATUS

When

You can control when do you want run your jobs. By default, jobs are executed automatically when previous stage succeed. You can specify another condition, you can run jobs manually, always or on error.

  1. # .gitlab-ci.yml
  2. stages:
  3. - build
  4. - test
  5. build:
  6. stage: build
  7. script: echo Run build ...
  8. # script: echo Run build ... && false
  9. test:
  10. stage: test
  11. script: echo Run test ...
  12. deploy:
  13. stage: test
  14. script: echo Run deploy ...
  15. when: manual
  16. diagnostics:
  17. stage: test
  18. script: echo Run diagnostics ...
  19. when: on_failure
  20. reporting:
  21. stage: test
  22. script: echo Run CI reporting ...
  23. when: always

Example with following jobs after manual job

  1. stages:
  2. - deploy prod
  3. - test prod
  4. deploy prod:
  5. stage: deploy prod
  6. when: manual
  7. allow_failure: false
  8. # allow_failure: true
  9. script:
  10. - echo Deploy ...
  11. test prod:
  12. stage: test prod
  13. script:
  14. - echo Test ...

Allow Failure

You can specify flag allow_failure to true, job can fail but pipeline will succeed.

  1. # .gitlab-ci.yml
  2. test:
  3. script: echo test ... && false
  4. allow_failure: true

Manual jobs are allowed to fail by default, if you want to disallow failure, you have to set allow_failure to false.

  1. # .gitlab-ci.yml
  2. test:
  3. when: manual
  4. script: echo test ... && false
  5. test2:
  6. when: manual
  7. script: echo test ... && false
  8. allow_failure: false

Retry

  1. # .gitlab-ci.yml
  2. image: sikalabs/ci
  3. test1:
  4. script: '[ "$(slu random int --max 2)" == "0" ]'
  5. retry: 2
  6. test2:
  7. script: '[ "$(slu random int --max 2)" == "0" ]'
  8. retry: 2
  9. test3:
  10. script: '[ "$(slu random int --max 2)" == "0" ]'
  11. retry: 2
  12. test4:
  13. script: slu chaos-monkey random-status-code --verbose
  14. retry: 2
  15. test5:
  16. script: slu chaos-monkey random-status-code --verbose
  17. retry: 2
  18. test6:
  19. script: slu chaos-monkey random-status-code --verbose
  20. retry: 2

Variables

Gitlab CI offers you lots of usable variables like:

  • CI
  • CI_PROJECT_PATH_SLUG, CI_COMMIT_REF_SLUG
  • CI_COMMIT_SHORT_SHA
  • CI_PIPELINE_SOURCE
  • CI_COMMIT_TAG, CI_COMMIT_BRANCH, CI_DEFAULT_BRANCH
  • CI_PIPELINE_ID, CI_JOB_ID
  • CI_REGISTRY, CI_REGISTRY_USER, CI_REGISTRY_PASSWORD
  • CI_JOB_STATUS

See all varibles: https://docs.gitlab.com/ce/ci/variables/predefined_variables.html#variables-reference

You can define own variables in:

  • Predefined CI Variables
  • Group CI Settings
  • Project CI Setting
  • Globally in CI YAML
  • In job in CI YAML

You can define varible in Settings -> CI / CD -> Variables. Same for project and group. You can define for example connection to your Kubernetes cluster, AWS credentials, …

Variables can be defined as:

  • Masked - Value is hidden from the CI output. You probably dont want to show any credential, even development one.
  • Protected - Protected variable appears only in jobs on protected branches. If developers can’t push to protected branches, there have no chance to get production deployment keys or deploy to production. After code has been merged to master (protected), then protected variables appears and you can deploy to production.

Example job:

  1. # .gitlab-ci.yml
  2. variables:
  3. XXX_GLOBAL: global
  4. job1:
  5. script: env | grep XXX
  6. job2:
  7. variables:
  8. XXX_LOCAL: local
  9. script: env | grep XXX
  10. job3:
  11. variables:
  12. XXX_FOO: foo
  13. XXX_BAR: bar
  14. XXX_FOO_BAR: $XXX_FOO-$XXX_BAR
  15. script: env | grep XXX
  16. job4:
  17. variables:
  18. TAG: $CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-$CI_PIPELINE_ID
  19. IMAGE: $CI_REGISTRY_IMAGE:$TAG
  20. BASE_HOST: sikademo.com
  21. HOST: $CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG.$BASE_HOST
  22. script:
  23. - echo docker build . -t $IMAGE
  24. - echo helm install ... --set host=$HOST

Variables with Terraform

You can manage project or group variables using Terraform.

  1. resource "gitlab_project_variable" "base_host" {
  2. project = local.project_id
  3. key = "BASE_HOST"
  4. value = "k8s.sikademo.com
  5. }

See example ./terraform-gitlab-ci-variables

You can manage entire Gitlab with Terraform ;)

Rules

You can specify another condition when you can run jobs. You can specify branches and tags on which you want to run your jobs or not.

Simple example:

  1. # .gitlab-ci.yml
  2. push:
  3. rules:
  4. - if: $CI_PIPELINE_SOURCE == "push"
  5. script:
  6. - echo push
  7. run:
  8. rules:
  9. - if: $CI_PIPELINE_SOURCE == "web"
  10. script:
  11. - echo run

More advanced example:

  1. # .gitlab-ci.yml
  2. stages:
  3. - unit_test
  4. - integration_test
  5. - build
  6. - deploy
  7. unit_test:
  8. stage: unit_test
  9. script: echo Unit Test ...
  10. rules:
  11. - if: $CI_COMMIT_BRANCH
  12. - if: $CI_COMMIT_TAG
  13. - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  14. integration_test:
  15. stage: integration_test
  16. script: echo Integration Test ...
  17. rules:
  18. - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  19. build:
  20. stage: build
  21. script: echo Build ...
  22. rules:
  23. - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  24. - if: $CI_COMMIT_TAG
  25. deploy:
  26. stage: deploy
  27. script: echo Deploy ...
  28. rules:
  29. - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  30. when: manual
  31. allow_failure: false
  32. - if: $CI_COMMIT_TAG

Rules Changes

You can run job when are changes is some files. That’s great for monorepos.

  1. # .gitlab-ci.yml
  2. Build Frontend:
  3. script: echo Build Frontend ...
  4. rules:
  5. - changes:
  6. - frontend/**
  7. Build Backend:
  8. script: echo Build Backend ...
  9. rules:
  10. - changes:
  11. - backend/**

If && Changes Together

  1. Build Frontend:
  2. script: echo Build Frontend ...
  3. rules:
  4. - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_MERGE_REQUEST_ID
  5. changes:
  6. - frontend/**/*
  7. Build Backend:
  8. script: echo Build Backend ...
  9. rules:
  10. - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_MERGE_REQUEST_ID
  11. changes:
  12. - backend/**/*

Merge Request Pipelines

  1. stages:
  2. - build
  3. - test
  4. - deploy
  5. build:
  6. stage: build
  7. script: echo build
  8. rules:
  9. - if: $CI_COMMIT_BRANCH
  10. - if: $CI_MERGE_REQUEST_ID
  11. test:
  12. stage: test
  13. script: echo test
  14. rules:
  15. - if: $CI_MERGE_REQUEST_ID
  16. deploy:
  17. stage: deploy
  18. script: echo deploy
  19. rules:
  20. - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

Workflow

Workflow Name

  1. # .gitlab-ci.yml
  2. workflow:
  3. name: Pipeline $CI_COMMIT_REF_NAME $CI_COMMIT_SHORT_SHA $CI_COMMIT_TITLE

Cache

Cache is used to specify a list of files and directories which should be cached between jobs. You can only use paths that are within the project workspace.

If cache is defined outside the scope of jobs, it means it is set globally and all jobs will use that definition.

You can specify cache key for better caching of different branches

  1. # .gitlab-ci.yml
  2. job:
  3. cache:
  4. key: ${CI_COMMIT_REF_SLUG}
  5. paths:
  6. - now
  7. script:
  8. - date >> now
  9. - cat now

More about caches: https://docs.gitlab.com/ce/ci/caching/index.html

Git Clean Flags

Instead of cache, you can use faster option how to keep some data in repository. If you are using cache, your repository is cleaned by git clean -ffdx, which means remove all ignored & untracked files and directories. Then your cache is downloaded and extracted into your working directory. This way is not fastest way if you have thousands files in cache (like node modules, for example).

Instead of cache, you can specify variable GIT_CLEAN_FLAGS and use own git clean arguments. If you want to ignore node modules from git clean, you can set -ffdx -e .yarn-cache.

  1. # .gitlab-ci.yml
  2. variables:
  3. GIT_CLEAN_FLAGS: -ffdx -e .yarn-cache
  4. job:
  5. script:
  6. - yarn install --frozen-lockfile --cache-folder .yarn-cache --prefer-offline
  7. - yarn build

Artifacts

Artifacts is used to specify a list of files and directories which should be attached to the job when it succeeds, fails, or always.

The artifacts will be sent to GitLab after the job finishes and will be available for download in the GitLab UI.

Artifact are also distributed to jobs in next stages.

  1. # .gitlab-ci.yml
  2. stages:
  3. - build
  4. - test
  5. build:
  6. stage: build
  7. script:
  8. - mkdir -p out
  9. - echo '<h1>Hello World!</h1>' > out/index.html
  10. artifacts:
  11. paths:
  12. - out
  13. test:
  14. stage: test
  15. script:
  16. - cat out/index.html

When the job succed, you can browse and download job from Gitlab.

More about artifacts: https://docs.gitlab.com/ce/user/project/pipelines/job_artifacts.html

Gitlab Pages

Enable Gitlab Pages in /etc/gitlab/gitlab.rb

  1. pages_external_url "http://pages.sikademo.com"

Gitlab pages MUST be on http (NOT https) or you have to provide wildcard certificate for *.pages.sikademo.com. See: https://gitlab.com/gitlab-org/gitlab/-/issues/376130

Artifacts Dependencies

By default, all artifacts will be passed to jobs in following stages. If you want artifact only from specific jobs, you can use dependencies to choose which articact you want.

Fists usage is when artifact are on same path and there will be colision, see:

  1. # .gitlab-ci.yml
  2. stages:
  3. - build
  4. - test
  5. build_A:
  6. stage: build
  7. script: mkdir -p out && echo '<h1>Hello from Project A!</h1>' > out/index.html
  8. artifacts:
  9. paths:
  10. - out
  11. build_B:
  12. stage: build
  13. script: mkdir -p out && echo '<h1>Hello from Project B!</h1>' > out/index.html
  14. artifacts:
  15. paths:
  16. - out
  17. test A:
  18. stage: test
  19. script: cat out/index.html
  20. dependencies:
  21. - build_A
  22. test B:
  23. stage: test
  24. script: cat out/index.html
  25. dependencies:
  26. - build_B

Or you can use dependencies when you have lots of artifact and dont want to slow down your jobs by downloading unnecessary artifacts.

JUnit Test Reports

Docs

  1. # test_example.py
  2. def test_ok():
  3. assert True
  4. def test_err():
  5. assert False
  1. # .gitlab-ci.yml
  2. test:
  3. image: ondrejsika/pytest
  4. script:
  5. - pytest --junitxml=report.xml
  6. artifacts:
  7. reports:
  8. junit: report.xml

Docker

  • Fully supported
  • Easiest way how to create build environment
  • Easiest way how to run and distribute your software

If you have a Docker or Kubernetes executor (we have) you can define image where you want to run your job.

You can specify image globally or in job:

  1. # .gitlab-ci.yml
  2. image: sikalabs/ci
  3. build:
  4. image: node
  5. script:
  6. - yarn
  7. - yarn run build
  8. deploy:
  9. script:
  10. - kubectl apply -f kubernetes.yml

You can also run docker commands from job because we have added Docker socket there. See here
and here.

  1. # .gitlab-ci.yml
  2. variables:
  3. TAG: $CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-$CI_PIPELINE_ID
  4. IMAGE: $CI_REGISTRY_IMAGE:$TAG
  5. job:
  6. script:
  7. - docker login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
  8. - docker build -t $IMAGE .
  9. - docker push $IMAGE

Docker Image sikalabs/ci

That is my image which I use for most of CI jobs.

It contains lots of common tools like git, zip, curl, wget, Docker client, Docker Compose, Kubernetes client, …

You can see the repository on Github - https://github.com/sikalabs/sikalabs-container-images/tree/master/ci

Kaniko

https://github.com/GoogleContainerTools/kaniko

  1. variables:
  2. GIT_CLEAN_FLAGS: -ffdx -e .kaniko-cache
  3. TAG: $CI_COMMIT_REF_SLUG-$CI_COMMIT_SHORT_SHA-$CI_PIPELINE_ID
  4. IMAGE: $CI_REGISTRY_IMAGE:$TAG
  5. job:
  6. image:
  7. name: gcr.io/kaniko-project/executor:v1.19.2-debug
  8. entrypoint: [""]
  9. script:
  10. - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
  11. - /kaniko/executor
  12. --context .
  13. --dockerfile ./Dockerfile
  14. --destination $IMAGE
  15. --cache=true
  16. --cache-dir .kaniko-cache
  17. --cache-repo $CI_REGISTRY_IMAGE/cache

matrix

  1. # .gitlab-ci.yml
  2. image: golang:1.22-bullseye
  3. build:
  4. parallel:
  5. matrix:
  6. - GOOS:
  7. - linux
  8. - darwin
  9. - windows
  10. GOARCH:
  11. - amd64
  12. - arm64
  13. artifacts:
  14. paths:
  15. - hello-${GOOS}-${GOARCH}
  16. script:
  17. - go build -o hello-${GOOS}-${GOARCH} main.go
  1. // main.go
  2. package main
  3. import "fmt"
  4. func main() {
  5. fmt.Println("Hello World")
  6. }

Environments

Environment is used to define that a job deploys to a specific environment.

If environment is specified and no environment under that name exists, a new one will be created automatically.

  1. # .gitlab-ci.yml
  2. variables:
  3. BASE_HOST: dev.company.com
  4. HOST: $CI_PROJECT_PATH_SLUG-$CI_COMMIT_REF_SLUG.$BASE_HOST
  5. deploy:
  6. script: echo 'Deploy!'
  7. environment:
  8. name: $CI_COMMIT_REF_SLUG
  9. url: https://$HOST

Stop Environmet

You can define stop job for the environment which you can stop manually from Gitlab. If stop job is defined, Gitlab automatically stop unnecessary environments, like envirments created from merged or deleted branches.

  1. # .gitlab-ci.yml
  2. deploy:
  3. script: echo Deploy!
  4. environment:
  5. name: $CI_COMMIT_REF_SLUG
  6. url: https://$CI_COMMIT_REF_SLUG.dev.company.com
  7. on_stop: stop
  8. stop:
  9. variables:
  10. GIT_STRATEGY: none
  11. script: echo Stop!
  12. when: manual
  13. environment:
  14. name: $CI_COMMIT_REF_SLUG
  15. action: stop

Environment: Auto Stop In

https://docs.gitlab.com/ee/ci/yaml/#environmentauto_stop_in

  1. deploy_review:
  2. script: echo Deploy for review!
  3. environment:
  4. name: $CI_COMMIT_REF_SLUG
  5. url: https://$CI_COMMIT_REF_SLUG.dev.company.com
  6. on_stop: stop_rewiew
  7. auto_stop_in: 1 day

Extends

  1. # .gitlab-ci.yml
  2. stages:
  3. - deploy dev
  4. - deploy prod
  5. .dev:
  6. stage: deploy dev
  7. variables:
  8. ENV: dev
  9. .prod:
  10. stage: deploy prod
  11. variables:
  12. ENV: prod
  13. .deploy:
  14. script: echo Deploy $ENV
  15. deploy dev:
  16. extends:
  17. - .deploy
  18. - .dev
  19. deploy prod:
  20. extends:
  21. - .deploy
  22. - .prod

Includes

Docs

  1. # .gitlab-ci.yml
  2. stages:
  3. - build
  4. - deploy
  5. include:
  6. - .gitlab-ci-build.yml
  7. - .gitlab-ci-deploy.yml
  1. # .gitlab-ci-build.yml
  2. build:
  3. stage: build
  4. script: echo build
  1. # .gitlab-ci-deploy.yml
  2. deploy:
  3. stage: deploy
  4. script: echo deploy

Child Pipelines

Docs

  1. # .gitlab-ci.yml
  2. pipeline_a:
  3. trigger:
  4. include: a/.gitlab-ci.yml
  5. strategy: depend
  6. rules:
  7. - changes:
  8. - a/**
  9. pipeline_b:
  10. trigger:
  11. include: b/.gitlab-ci.yml
  12. strategy: depend
  13. rules:
  14. - changes:
  15. - b/**
  1. # a/.gitlab-ci.yml
  2. stages:
  3. - build
  4. - deploy
  5. build:
  6. stage: build
  7. script: echo Build service A
  8. deploy:
  9. stage: deploy
  10. script: echo Deploy service A
  1. # b/.gitlab-ci.yml
  2. stages:
  3. - build
  4. - deploy
  5. build:
  6. stage: build
  7. script: echo Build service B
  8. deploy:
  9. stage: deploy
  10. script: echo Deploy service B

Generated Pipelines

  1. # .gitlab-ci.yml
  2. stages:
  3. - generate
  4. - pipeline
  5. generate:
  6. stage: generate
  7. image: python:3.7-slim
  8. script: python generate-gitlab-ci.py
  9. artifacts:
  10. paths:
  11. - .gitlab-ci.generated.yml
  12. pipeline:
  13. stage: pipeline
  14. trigger:
  15. include:
  16. - artifact: .gitlab-ci.generated.yml
  17. job: generate
  18. strategy: depend
  1. # generate-gitlab-ci.py
  2. import json
  3. SERVICES = (
  4. "foo",
  5. "bar",
  6. "baz",
  7. )
  8. def make_service(name):
  9. return {
  10. "build-%s" % name: {
  11. "stage": "build",
  12. "script":[
  13. "echo Build %s" % name,
  14. ]
  15. },
  16. "deploy-%s" % name: {
  17. "stage": "deploy",
  18. "script":[
  19. "echo Deploy %s" % name,
  20. ]
  21. }
  22. }
  23. with open(".gitlab-ci.generated.yml", "w") as f:
  24. pipeline = {}
  25. pipeline.update({
  26. "stages": ["build", "deploy"]
  27. })
  28. for service in SERVICES:
  29. pipeline.update(make_service(service))
  30. f.write(json.dumps(pipeline))

Multi Project Pipelines

Docs

Project foo depends on library bar. If you make any change in library bar you have to trigger pipeline in project foo. ondrejsika/foo is full path to project where you want to run pipeline.

  1. # .gitlab-ci.yml (library bar)
  2. job:
  3. script: Do something
  4. triger-pipelines:
  5. trigger: ondrejsika/foo

or

  1. # .gitlab-ci.yml (library bar)
  2. job:
  3. script: Do something
  4. triger-pipelines:
  5. trigger:
  6. project: ondrejsika/foo
  7. branch: $CI_COMMIT_REF_NAME%

Terraform Pipeline

  1. image: registry.gitlab.com/gitlab-org/terraform-images/stable:latest
  2. variables:
  3. TF_ROOT: ${CI_PROJECT_DIR}
  4. TF_ADDRESS: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/terraform/state/${CI_PROJECT_NAME}
  5. cache:
  6. paths:
  7. - ${TF_ROOT}/.terraform
  8. before_script:
  9. - cd ${TF_ROOT}
  10. stages:
  11. - validate
  12. - plan
  13. - apply
  14. validate:
  15. stage: validate
  16. script:
  17. - gitlab-terraform init
  18. - gitlab-terraform validate
  19. plan:
  20. stage: plan
  21. script:
  22. - gitlab-terraform plan
  23. - gitlab-terraform plan-json
  24. artifacts:
  25. name: plan
  26. paths:
  27. - ${TF_ROOT}/plan.cache
  28. reports:
  29. terraform: ${TF_ROOT}/plan.json
  30. apply:
  31. stage: apply
  32. script:
  33. - gitlab-terraform apply
  34. dependencies:
  35. - plan
  36. rules:
  37. - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
  38. when: manual
  39. allow_failure: false

Inspired by https://gitlab.com/gitlab-org/configure/examples/gitlab-terraform-aws/-/blob/master/.gitlab-ci.yml

Scheduled Pipelines

You can schedule pipeline.

  1. nightly:
  2. script: echo Nightly build ...
  3. rules:
  4. - if: $CI_PIPELINE_SOURCE == "schedule"
  5. when: never

Schedule setting in gitlab.rb

If you want to run scheduled pipelines in less than hour, you have to set custom pipeline_schedule_worker_cron in /etc/gitlab/gitlab.rb

  1. gitlab_rails['pipeline_schedule_worker_cron'] = "*/5 * * * *"

Scheduled Pipelines using Terraform

  1. resource "gitlab_pipeline_schedule" "nightly" {
  2. project = local.project_id
  3. description = "nightly builds"
  4. ref = "master"
  5. cron = "0 0 * * *"
  6. }
  7. resource "gitlab_pipeline_schedule_variable" "nightly" {
  8. project = gitlab_pipeline_schedule.nightly.project
  9. pipeline_schedule_id = gitlab_pipeline_schedule.nightly.id
  10. key = "NIGHTLY"
  11. value = "yes"
  12. }

Gitlab Runner in Kubernetes using Operator and Helm

Using sikalabs/gitlab-runner chart.

Install OLM

  1. curl -sL https://github.com/operator-framework/operator-lifecycle-manager/releases/download/v0.23.1/install.sh | bash -s v0.23.1

Install Gitlab Runner Operator

  1. kubectl create -f https://operatorhub.io/install/gitlab-runner-operator.yaml

Create Runner

  1. helm upgrade --install \
  2. gitlab-runner \
  3. --repo https://helm.sikalabs.io gitlab-runner \
  4. --namespace gitlab-runner \
  5. --create-namespace \
  6. --set gitlabUrl=https://gitlab.sikademo.com \
  7. --set runnerRegistrationToken=h94VrzQzZnJ_va3hxGGW

Git Tag from CI

  1. tag:
  2. when: manual
  3. script:
  4. - slu git tag-next-calver
  5. - git remote set-url origin https://token:$COMMIT_TOKEN@$CI_SERVER_HOST/$CI_PROJECT_PATH.git
  6. - git push origin `slu git get-last-calver`

Gitlab Feature Flags

See example in ondrejsika/golang-examples.

Resources

Docs:

Examples

Thank you! & Questions?

That’s it. Do you have any questions? Let’s go for a beer!

Do you like the course? Tweet about it <3

Please, tweet something with @ondrejsika. Thanks :)

Ondrej Sika

Next time?

Wanna to go for a beer or do some work together? Just book me :)

Training Sessions

2024-11-21 O2

2024-02-12 Alpiq

2023-06-19

2023-05-15 Tieto

2023-02-27 Siemens