Gitlab CI: Pipelines, CI CD and DevOps for Beginners - p-patel/software-engineer-knowledge-base GitHub Wiki
https://www.udemy.com/course/gitlab-ci-pipelines-ci-cd-and-devops-for-beginners/
- Analogous to a car assembly line (ordered process, output of one step input of the next, some processes can be completed in parallel) - chasis -> engine -> wheels
- Similarly Gitlab is a tool that helps us to build and release a product from the 'factory'
- 2 large stages - Build -> Test
-
.gitlab-ci.ymlspecify jobs, the job is run using the 'gitlab runner' tool - gitlab will jobs in parallel by default, define 'stages' to order jobs
- jobs are independent and do not share data unless explicitly specified using
artifacts: - artifacts from any step of any build are available in GitLab to download
- Add an SSH key to the project to push changes remotely to the rep
- Gitlab server, Gitlab runners (provides scalability)
...
- Web app using Gatsby
-
npm -i g gatsby-cliinstall Gatsby -
gatsby new static-websitecreate new web app
-
gatsby buildGatsby production build - generates JavaScript, minify files, see /public dir - automate this step using Gitlab
- Containers that allow virtualisation
- Build images containing code and all dependencies, when executed it becomes a container similar to a virtual machine
- "Old-fashioned CI Server" - install all dependencies (all tools, packages) which does not allow two versions of the same framework (e.g. NodeJS) on the same CI Server. Docker enables each app to use a custom configuration of dependencies (via a custom Docker image)
- Add
gitlab-ci.ymlto repo -
image: nodeuse Docker image that includes Node - use
artifacts:to specify build artifacts path(s) to retain
- Check job success/error status using command exit status (0/non-0)
-
echo $?check result of last command - Add
test artifact:step as part of stage 'test' - Example: Use
grepto check production index.html file to check for text (during development also check for absence of text to confirm the test is working!)
- Use
image: aplinefor a 5Mb image -
image: node stage: test script: - npm i - npm i -g gatsby-cli - gatsby serve - curl "http://localost:9000" | grep -q "Gatsby"
- `test artifact` and `test website` jobs will run in parallel by default as they are both part of the `test:` stage
- using alpine Docker image will speed up jobs considerably
## Troubleshooting gatsby serve
- NB. may need to also publish './.cache' artifact as part of the Gatsby app production build for the 'test websites' job to succeed
## Running jobs in the background
- running `gatsby serve` will block the command line, Gitlab will kill jobs after a 1 hour timeout (can also be cancelled from the Gitlab UI)
- run command in background using `&` to run commands after `gatsby serve` (a simple temporary fix is to add a `sleep 3` after the serve command to allow the server time to initialise
- Pipelines can run in parallel for multiple simultaneous commits
- Can cancel pipelines or individual pipeline jobs from the Gitlab UI
- Build fail error "Failed writing body", fix by using `curl "http://localhost:9000" | tac | tac | grep -q "Gatsby"`
## Deployment using surge.sh
- deploys using serverless model
- `npm i -g surge` install surge
- `surge` setup surge inc. create account
- run from '/public' dir and surge will detect production build dir and use for the surge project path
- surge generates domain
- surge finally deploys web app to cloud
## Using Environment variables for managing secrets
- define env vars to avoid keeping secrets, credentials, usernames etc in pipeline or project files
- use for example the credentials required for surge account credentials during deployment (use token instead of login credentials for authentication)
- `surge token` generates surge authentication token
- Settings | CD/CD | Environment Variables - define env vars made available in Gitlab runner
- define 'SURGE_LOGIN' and 'SURGE_TOKEN' env vars which the surge command will look for on execution
## Deploying the project using Gitlab CI
- `deploy to surge:` define deployment job
- define `image` globally which will used by each job unless over-riddent
- ```script:
- npm i -g surge
- surge --project ./public --domain <something>.surge.sh
- jobs will be added to 'test' stage by default unless explicitly defined, e.g.
stage: Deploy
Let's extend the pipeline and add a new stage.
== DO ==
-
add a new stage called "deployment tests".
-
create a new job called "test deployment"
-
download the first page of the website and search for a specific string (you can use the curl command)
== QUESTION ==
How does your .gitlab-ci.yml file look like?
== QUESTION ==
What was the execution time for the "test deployment" job?
Let's find a way to improve the execution time.
== DO ==
Use the Docker alpine image for this job.
Hint: curl is not installed on the alpine image and you need to install it on your own using a tool called Alpine Linux package management (apk). https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management#Add_a_Package
== QUESTION ==
What was the execution time for the "test deployment" job now?
- Execution speed, git workflow, environments/jobs, manual steps
- Improve code creation and deployment
- Retry failed pipelines/jobs, schedule jobs, triggering pipelines, caching pipelines and Docker image considerations to optimise build speed
- Predefined vars inc. CI_COMMIT_SHORT_SHA
- Add build version to site production files using fill-point e.g. %%VERSION%% in build website job
-
sed -i 's/word1/word2/g inputfilestream editor used to replace fill-point with env var - in
test deployment:job, check for the version number on the deployed page usingcurl | grep
- Click retry button on job or pipeline if a temporary problem is suspected
- 'Run Pipeline' button to re-run pipeline without any new code changes
- Can schedule daily/weekly builds under CI/CD | Schedule in Gitlab
- Downloading dependencies in Gitlab runner's container takes significant amount of time
- Common steps to run a job:
- Delegate job to a Gitlab runner
- Download & start Docker image
- Clone the repo
- Install any required dependencies
- Run the actual step
- Save the result (if needed)
- Instruct Gitlab to maintain a cache of files that will be reused
-
key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/
- in logs 'FATAL: file does not exist, Failed to extract cache' on first attempt to use cache, then 'Creating cache master...'
- each that uses a cache will both use it and update it
- clear cache in Gitlab using 'Clear Runner Caches' button
- Gitlab searches for caches using cache keys
## Assignment 3: Assignment: Improving build speed by using caches
Questions for this assignment
== OBSERVE ==
Take a look at the jobs in the pipeline. You should notice that some jobs now have an overhead of downloading and uploading the caches, even though they do not use caches.
== QUESTION ==
Which jobs do not use project npm dependencies and have no need for the cache?
== LEARN ==
Gitlab offers the possibility of disabling the cache for the jobs that do not need it.
This can be done by using the configuration cache: {} inside the jobs that you don't want to use cache.
== OBSERVE ==
Write down the current total time for running the pipeline.
== DO ==
Disable the usage of cache for the jobs that do not need it.
== QUESTION ==
What was your before and after total time for running the pipeline?
== LEARN ==
The default cache behaviour in Gitlab CI is to download the files at the start of the job execution (pull), and to re-upload them at the end (push). This allows any changes made by the job to be persisted for future runs (known as the pull-push cache policy).
Gitlab offers the possibility of defining how a job should work with cache by setting a policy. So if we want to skip uploading cache file, we can use the setting in the cache configuration
policy: pull
== OBSERVE ==
While downloading the cache (pull) saves us time, we still have a few jobs that do redundant work: updating the cache after each run (push). This still does cost us time and we really do not need this behaviour, as the cache remains the same. The chance of a npm dependency changing during the execution of the pipeline is very small.
== DO ==
Amend the global cache configuration by specifying the pull policy.
== QUESTION ==
What was your before and after total time for running the pipeline?
== OBSERVE ==
Now we are no longer updating the cache. While this is not a problem right now (as we anyway run npm install which will always check for updates), on the long run it is faster to have an up-to-date cache.
== LEARN ==
In Gitlab CI it is possible to create jobs that are only executed when a specific condition is fulfilled. For example if we want to run a job only when the pipeline is triggered by a schedule, we can configure it with:
only:
- schedules
The same goes the other way around. If you don't want to run a job when the pipeline is triggered by a scheduled run, simply add to the respective jobs:
except:
- schedules
== DO ==
Let's create a job that runs only once per day and updates the cache. The job will not need to download the caches (pull). It only needs to create new caches (push).
For this do the following:
- create a stage called "cache"
- create a job called "update cache"
- make sure the job does runs the command npm install (to install all dependencies)
- add the following cache policy to the job: policy: push. Note that you will need to define the entire cache configuration (as you have done globally). You can not override only the policy.
- make sure the "update cache" job only runs when the pipeline is triggered by a schedule
- make sure that all other jobs DO NOT RUN when the pipeline is triggered by a schedule
- create a new scheduled pipeline run and make it run once per day
- manually run the scheduled pipeline and inspect the pipeline (only one job should be displayed)
- manually the pipeline (the "update cache" job should not appear in the pipeline)
== QUESTION ==
How does your .gitlab-ci.yml look like?
== NOTE ==
Please note that the results of this assignment will not be included in the further lectures (in order not to confuse students who have skipped the assignment).
You are free to keep the work that you have done. Thank you for understanding.
== Conclusion / Feedback ==
I hope you have enjoyed this assignment and that it was allowed you to practice your skills.
Please let me know how did you like it, if you feel you have learned something new. Any feedback on how to improve is welcome!
## Cache vs Artifacts
- Artifact is usually the output of a build tool or a build step that is input into the next build step to be augmented
- Cache should only be used a temporary storage for project dependencies
- https://docs.gitlab.com/ee/ci/caching/#cache-vs-artifacts
## Environments
- E.g. addition of staging env to the production env
- Update stages to include 'deploy staging' and 'deploy production'
- ```deploy staging:
stage: deploy staging
script:
- npm i -g surge
- surge --project ./public --domain <something-staging>.surge.sh
- Acceptance/integration tests can be run against staging prior to deployment to production
- ```environment:
name: staging
url: <something-staging>.surge.sh
- environment similarly defined for production env
- Operations | Environments to view defined environments
- Define in Gitlab in the same way as the env vars were defined earlier
- variables can be defined globally or locally within a job as suitable
STAGING_DOMAIN: instazone-staging.surge.sh
PRODUCTION_DOMAIN: instazone.surge.sh
-
url: http://$STAGING_DOMAINusing variable
-
when: manualsets a job to only run when manually run from Gitlab (e.g. 'deploy production' job) - steps after the manual step still run automatically but may fail if reliant on the manual steps output (e.g. Production tests reliant on Deploy Production)
-
allow_failure: falseon the 'deploy production' job does not run subsequent jobs until the manual job has been run - http://docs.gitlab.com/ee/ci/yaml/#allow_failure
- http://docs.gitlab.com/ee/ci/yaml/#whenmanual
- Avoid breaking the master / an undeployable master branch
- Ensures that CD is always possible
- feature/task/bugfix branches, once completed it can be merged back into master
- Branching models: e.g. GitFlow, just avoid using only one branch
-
- master
- merges into a feature branch will then run a pipeline containing jobs relevant to feature branches
## Merge requests - What is a Merge Request?
- Visualise new changes that are about to be made in master
- See the status of the pipeline for a specific branch
- Give other developers to give their feedback regarding a feature/fix before it gets merged into master
## Merge Requests - Configuring Gitlab
- Settings | Repository - 'Protected branches' (includes option allow to push: no one)
- Settings | General - 'Merge Requests' use Fast-forward merge for cleaner commit history, can enable 'Pipelines must succeed' and 'All discussions must be resolved'
...
## Merge requests - Your first merge request
- Create feature branch (e.g. new title), commit new title change, submit MR (inc. optionally delete source branch)
- Select 'Merge when pipeline succeeds'
- On merge, master branch pipeline will run
only: - master - merge_requests
Marks jobs for the build & test stages (make it explicit which cases/branches the job will be executed
## Assignment: Working with Merge Requests
== IMPORTANT! ==
Before starting this assignment, you first need request access to the following project with your Gitlab CI project.
Please note that this is a manual process and may take up to 48 hours to complete. I am sorry for the delay. Thank you for your understanding.
https://gitlab.com/gitlab-course-public/my-static-website
Questions for this assignment
== TODO ==
Reviewing changes others have made
Note: This exercise depends on other students and may not always be available. Feel free to invite friends / colleagues to help you.
Go to this repository are review any open merge requests:
https://gitlab.com/gitlab-course-public/my-static-website/merge_requests
Try at least one of the following:
- giving the merge request a thumbs up ? showing that you agree with the changes proposed
- writing a comment / following the discussion on previous comments
- merge a Merge Request that is still open. The merge request may need a specific number of thumbs up before merging is possible.
What you have just did is called a Code Review and is a common best-practice while writing code. If you are new to code review, consider reading the following first: https://www.atlassian.com/agile/software-development/code-reviews
== QUESTION ==
Which Merge Request did you review? Paste the URL below.
== OBSERVE ==
Broken merge request
Please look into this merge request
https://gitlab.com/gitlab-course-public/my-static-website/merge_requests/2
== QUESTION ==
Why cannot this Merge Request be merged?
== Making a small change ==
Clone the repository and make a new branch.
Inside the branch implement a small feature, whatever you like.
For example:
- change a template and add a new paragraph of text
== QUESTION ==
What is the name of your branch?
== Opening a merge request ==
Go ahead an open a new merge request with your new feature.
== QUESTION ==
What is the URL to your merge request?
== Conclusion / Feedback ==
It might take a few days until your Merge Request has been reviewed by others / merged.
I hope you have enjoyed this assignment and that it was allowed you to practice your skills.
Please let me know how did you like it, if you feel you have learned something new. Any feedback on how to improve is welcome!
## Dynamic environments
- Staging, prod are for the master branch
- To deploy a branch to an environment we need another environment 'review'
- Working with multiple branches at a time we need to dynamically create envs for each branch to review the changes made, optionally run additional tests and reviewed by non-developers (e.g. testers, PO/PM, domain experts etc.)
- create 'deploy review' stage
deploy review: stage: deploy review only: - merge_requests
- environment:
name: review/$CI_COMMIT_REF_NAME
url: https://instazone-$CI_ENVIRONMENT_SLUG.surge.sh
script:
- npm i -g surge
- surge --project ./public --domain $STAG?ING_DOMAIN-$CI_ENVIRONMENT_SLUG.surge.sh
- MR of a feature branch will trigger its deployment to new dynamic environment
## Destroying environments (Clean-up after the Merge Request)
- Dynamic envs need to be cleaned up after MR has been accepted
- `surge teardown vancouver.surge.sh` deletes surge deployment
- ```
stop review:
stage: deploy reviewer
only:
- merge_requests
variables:
GIT_STRATEGY: none
script:
- npm i -g surge
- surge teardown instazone-$CI_ENVIRONMENT_SLUG.surge.sh
when: manual
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
deploy review:
...
environment:
...
on_stop: stop review
...
- stop review is triggered on merge
-
when: manualalso allows Gitlab to trigger the job -
GIT_STRATEGY: noneprevents a git checkout
-
before_scriptruns before the normal script block - can be defined globally or locally within a specific job
-
... before_script:
- echo "Deployming to production" ...
- use cases: install dependencies for a specific job, instead of muddying the `script:` block
- `after_script` local dir has been set back to default, commands are executed in a separate context from `before_script` and `script`
- https://docs.gitlab.com/ee/ci/yaml/#before_script-and-after_script
## Recap & conclusion
- MRs, environments, track version in each env
- Env vars (inc. pre-defined env vars)
- Caches, Docker images
# YAML Basics
## Overview
- Understand existing pipelines from a YAML perspective
- Important YAML language features
## Understanding YAML