Building a CI/CD pipeline in GitLab
You can build a continuous integration/continuous delivery (CI/CD) pipeline using serverless products.
As an example, we will use a Django
The project uses two environments:
prod
: Production environment available to users.testing
: Test environment for testing the app before the release.
For each environment, there is an individual Yandex Cloud folder and separate static resources, e.g., the DB and service accounts. This isolates the environments from each other at the Yandex Identity and Access Management settings level.
There is also a common infra
folder with a registry in Yandex Container Registry containing all app’s Docker images. The system publishes the images under the builder
service account. The prod
and testing
environment service accounts have restricted permissions in the infra
folder limited to pulling Docker images.
To build a CI/CD pipeline with serverless products:
- Create a GitLab instance.
- Configure GitLab.
- Create a GitLab Runner.
- Upload files to the GitLab repository.
- Create GitLab environment variables.
- Create the CI script configuration file.
- Check the result.
If you no longer need the resources you created, delete them.
Getting started
Download a project
Clone the yc-serverless-gitlab-ci-cd repository
git clone https://github.com/yandex-cloud-examples/yc-serverless-gitlab-ci-cd.git
Install additional dependencies
Install the following tools in the local environment:
-
Python libraries listed in the
application/requirements.txt
project file:python -m pip install -r ./application/requirements.txt
Prepare the infrastructure
-
Navigate to the project directory; run the
bootstrap.sh
script and specify your cloud ID:YC_CLOUD_ID=<cloud_ID> ./bootstrap.sh
The script will deploy the basic infrastructure and create YAML resource description files in the
config
directory. You can edit the script to create additional resource folders. For example, this way you can add another test environment.Warning
Once completed, the script will display GitLab environment variables and their values. Save them, as you will need them later.
-
- Service account for resources with the editor role for the Yandex Managed Service for Kubernetes cluster folder. This service account will be used to create Managed Service for Kubernetes cluster resources.
- Service account for nodes with the container-registry.images.puller role for the Docker image registry folder. Nodes will pull the required Docker images from the registry on behalf of this account.
You can use the same service account for both operations.
-
Create a Managed Service for Kubernetes cluster and a node group. When creating the cluster, specify the previously created service accounts for the resources and nodes.
-
Configure security groups for the Managed Service for Kubernetes cluster.
-
Configure a security group for the Yandex Managed Service for GitLab instance.
Create a GitLab instance
Create either a Managed Service for GitLab instance or a VM with a GitLab image on the same cloud network as the Managed Service for Kubernetes cluster.
Create a Managed Service for GitLab instance by following this guide.
Launch GitLab on a VM with a public IP.
-
On the folder page in the management console
, click Create resource and selectVirtual machine instance
. -
Under Boot disk image, in the Product search field, enter
Gitlab
and select a public GitLab image. -
Under Location, select an availability zone to place your VM in. If you do not know which availability zone you need, leave the default one.
-
Under Computing resources, navigate to the
Custom
tab and specify the required platform, number of vCPUs, and the amount of RAM:- Platform:
Intel Ice Lake
. - vCPU:
4
. - Guaranteed vCPU performance:
100%
. - RAM:
8 GB
.
- Platform:
-
Under Network settings:
- In the Subnet field, select the network and subnet to connect your VM to. If the required network or subnet is not listed, create it.
- Under Public IP address, keep
Auto
to assign your VM a random external IP address from the Yandex Cloud pool or select a static address from the list if you reserved one in advance.
-
Under Access, select SSH key and specify the VM access data:
- Under Login, enter the username. Do not use
root
or other names reserved by the OS. To perform operations requiring superuser permissions, use thesudo
command. -
In the SSH key field, select the SSH key saved in your organization user profile.
If there are no saved SSH keys in your profile, or you want to add a new key:
- Click Add key.
- Enter a name for the SSH key.
- Upload or paste the contents of the public key file. You need to create a key pair for the SSH connection to a VM yourself.
- Click Add.
The SSH key will be added to your organization user profile.
If users cannot add SSH keys to their profiles in the organization, the added public SSH key will only be saved to the user profile of the VM being created.
- Under Login, enter the username. Do not use
-
Under General information, specify the VM name:
ci-tutorial-gitlab
. -
Click Create VM.
It may take a few minutes to create the VM. When the VM status changes to RUNNING
and GitLab starts, configure its settings.
Configure GitLab
To configure GitLab and enable Continuous Integration (CI), create a new project and enter the CI authorization parameters:
-
Log in to the Managed Service for GitLab instance web interface.
-
Click Create a project.
-
Click Create blank project.
-
Fill out the fields below:
- Project name:
gitlab-test
. - Project URL: Select the administrator user in the field next to the Managed Service for GitLab instance FQDN.
Leave the other fields unchanged.
- Project name:
-
Click Create project.
-
On the Yandex Compute Cloud page, select the created VM and copy its public IP.
-
Connect to the VM via SSH.
-
Get the GitLab administrator password using the following VM command:
sudo cat /etc/gitlab/initial_root_password
-
Copy the password (without spaces) from the
Password
row to the clipboard or a separate file. -
Open
http://<VM_public_IP_address>
in your browser. This will take you to the GitLab web interface. -
Log in using the administrator account:
- Username or email:
root
- Password: Password you copied earlier
If you are unable to log in, reset the administrator account password
. - Username or email:
-
Log in to the system again using the administrator account and the new password.
-
Select Create a project.
-
Set the project name:
gitlab-test
. -
Click Create project.
Create a GitLab Runner
To run build tasks in the Yandex Managed Service for Kubernetes cluster, create a GitLab Runner
Once it is installed, you can run automated builds inside your Managed Service for Kubernetes cluster.
For more information about installing and running GitLab Runner, see the GitLab documentation
Upload files to the GitLab repository
-
Clone the
gitlab-test
repository over SSH. -
Copy all files from the
yc-serverless-gitlab-ci-cd
repository togitlab-test
. -
Navigate to the
gitlab-test
directory. -
Index the new files:
git add .
-
Commit the files:
git commit -m "Add project files"
-
Push changes to the
gitlab-test
repository:git push
Create GitLab environment variables
-
In GitLab, navigate to Settings in the left-hand panel and select CI/CD from the drop-down list.
-
Click Expand next to Variables.
-
Add environment variables with the protection option disabled. The
bootstrap.sh
script will display all the required variables and their values:cloud_id
: Your cloud ID.CI_REGISTRY
: Container Registry-based registry ID with thecr.yandex/
prefix in theinfra
folder.CI_REGISTRY_KEY
: Thebuilder
service account key.cart_prod
: Yandex Cloud production folder name.DOCAPI_ENDPOINT_prod
: Document API endpoint of the Yandex Managed Service for YDB database in theprod
folder.PROD_SA_ID
:deployer
service account ID in theprod
folder.SA_PROD_DEPLOYER_PRIVATE_KEY
:deployer
service account key in theprod
folder.prod_container_id
: Serverless Containers-based container ID in theprod
folder.cart_testing
: Yandex Cloud test folder name.DOCAPI_ENDPOINT_testing
: Document API endpoint of the Managed Service for YDB database in thetesting
folder.TESTING_SA_ID
: Thedeployer
service account ID in thetesting
folder.SA_TESTING_DEPLOYER_PRIVATE_KEY
: Thedeployer
service account key in thetesting
folder.
To add a variable:
- Click Add variable.
- In the window that opens, specify the variable name in the Key field and its value in the Value field.
- Disable the Protect variable option.
- Click Add variable.
Create the CI script configuration file
-
Open the
gitlab-test
project. -
Click
in the repository navigation bar and select New file from the drop-down menu. -
Name the file as
.gitlab-ci.yml
and add the following build stages to it:.gitlab-ci.yml
stages: - build - deploy-test-env - test - delete-test-env - release # Building a container image. build-job: stage: build # Using `kaniko` to create a container inside another container for enhanced security. image: name: gcr.io/kaniko-project/executor:debug entrypoint: [""] script: - mkdir -p /kaniko/.docker # Upload the container image to the registry. The image is tagged with the commit hash. - echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n "json_key:${CI_REGISTRY_KEY}" | base64 | tr -d '\n' )\"}}}" > /kaniko/.docker/config.json - >- /kaniko/executor --context "${CI_PROJECT_DIR}/application" --dockerfile "${CI_PROJECT_DIR}/application/Dockerfile" --destination "${CI_REGISTRY}/${CI_PROJECT_PATH}:${CI_COMMIT_SHORT_SHA}" # Deploying a test environment using a built-in container image. deploy-test-env-job: stage: deploy-test-env image: alpine:3.15 script: # Installing tools. - apk add -q --no-cache bash curl jq gettext - apk add yq --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community - curl --fail -silent --location --remote-name https://storage.yandexcloud.net/yandexcloud-yc/install.sh - bash install.sh -i /usr/local/yandex-cloud -n - ln -s /usr/local/yandex-cloud/bin/yc /usr/local/bin/yc # Authentication with a service account key. - echo "$SA_TESTING_DEPLOYER_PRIVATE_KEY" > key.json - yc config profile create sa-profile - yc config set service-account-key key.json # Setting up the API Gateway variables and creating a container. - export sa_id=$TESTING_SA_ID - export container_id=$(yc serverless container create --name testing --cloud-id ${cloud_id} --folder-name ${cart_testing} | yq .id) # Deploying a revision. - yc serverless container revision deploy --container-name testing --image "${CI_REGISTRY}/${CI_PROJECT_PATH}:${CI_COMMIT_SHORT_SHA}" --cores 1 --memory 512mb --concurrency 1 --execution-timeout 10s --cloud-id ${cloud_id} --folder-name ${cart_testing} --service-account-id ${TESTING_SA_ID} --environment DOCAPI_ENDPOINT=${DOCAPI_ENDPOINT_testing},DB_TABLE_PREFIX='' --secret environment-variable=AWS_ACCESS_KEY_ID,name=cart-app,key=AWS_ACCESS_KEY_ID --secret environment-variable=AWS_SECRET_ACCESS_KEY,name=cart-app,key=AWS_SECRET_ACCESS_KEY --secret environment-variable=SECRET_KEY,name=cart-app,key=SECRET_KEY # Setting up a template and deploying API Gateway. - (cat ${CI_PROJECT_DIR}/apigw.yaml.j2 | envsubst) > apigw.yaml - cat apigw.yaml - yc serverless api-gateway create --name testing --spec=apigw.yaml --description "created from gitlab CI" --cloud-id ${cloud_id} --folder-name ${cart_testing} - mkdir output - export gwdomain=$(yc serverless api-gateway get testing --cloud-id ${cloud_id} --folder-name ${cart_testing} | yq .domain) - echo "https://"$gwdomain>output/gwurl artifacts: paths: [output/] e2e-test-job: stage: test image: alpine:3.15 script: - apk add -q --no-cache bash curl - apk add yq --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community - cat output/gwurl - export gwurlvar=$(cat output/gwurl) - curl $gwurlvar load-test-job: stage: test image: alpine:3.15 script: - echo "Here goes load testing commands" - echo "Might even invoke bash with prepared bash script" - echo "Hello!" delete-test-env-job: stage: delete-test-env image: alpine:3.15 script: - apk add -q --no-cache bash curl jq gettext yq - curl --fail --silent --location --remote-name https://storage.yandexcloud.net/yandexcloud-yc/install.sh - bash install.sh -i /usr/local/yandex-cloud -n - ln -s /usr/local/yandex-cloud/bin/yc /usr/local/bin/yc - echo "$SA_TESTING_DEPLOYER_PRIVATE_KEY" > key.json - yc config profile create sa-profile - yc config set service-account-key key.json - yc serverless api-gateway delete testing --cloud-id ${cloud_id} --folder-name ${cart_testing} - yc serverless container delete testing --cloud-id ${cloud_id} --folder-name ${cart_testing} release-job: stage: release image: alpine:3.15 script: - apk add -q --no-cache bash curl jq gettext - curl --fail --silent --location --remote-name https://storage.yandexcloud.net/yandexcloud-yc/install.sh - bash install.sh -i /usr/local/yandex-cloud -n - ln -s /usr/local/yandex-cloud/bin/yc /usr/local/bin/yc - echo "$SA_PROD_DEPLOYER_PRIVATE_KEY" > key.json - yc config profile create sa-profile - yc config set service-account-key key.json - yc serverless container revision deploy --container-name prod --image "${CI_REGISTRY}/${CI_PROJECT_PATH}:${CI_COMMIT_SHORT_SHA}" --cores 1 --memory 512mb --concurrency 1 --execution-timeout 10s --cloud-id ${cloud_id} --folder-name ${cart_prod} --service-account-id ${PROD_SA_ID} --environment DOCAPI_ENDPOINT=${DOCAPI_ENDPOINT_prod},DB_TABLE_PREFIX='' --secret environment-variable=AWS_ACCESS_KEY_ID,name=cart-app,key=AWS_ACCESS_KEY_ID --secret environment-variable=AWS_SECRET_ACCESS_KEY,name=cart-app,key=AWS_SECRET_ACCESS_KEY --secret environment-variable=SECRET_KEY,name=cart-app,key=SECRET_KEY - container_id=${prod_container_id} # Creating a production environment. environment: name: production/$CI_COMMIT_SHORT_SHA
-
Add a comment in the Commit message field:
CI scripts
. -
Click Commit changes.
The .gitlab-ci.yml
file contains the following steps of the CI script:
- build: Building a Docker image with
Dockerfile
and pushing the image to Container Registry. - deploy-test-env: App test deployment. Additionally, we described the artifacts
feature to transfer data from one stage to another that we are not going to use here. You can configure it, if required. - test: Testing the app. The tests involve E2E simulation and load testing. You can describe and set up custom tests as well.
- delete-test-env: Deleting the test app.
- release: Deploying the app in production. This stage also uses deployment environments
. The system creates and saves them each time the pipeline is run successfully. Use them to restore and deploy the previous app version.
After you save the .gitlab-ci.yml
configuration file, the build script will start.
Check the result
To check the build script results, select Build on the left-hand panel in the gitlab-test
project, and then Pipelines from the drop-down menu. Make sure all five stages complete successfully.
You can access the app at the API Gateway service domain URL in the prod
folder. You can find this URL in the management consoledomain
field of the bootstrap.sh
script runtime log.
Delete the resources you created
Some resources are not free of charge. To avoid paying for them, delete the resources you no longer need:
- Delete the
prod
,testing
, andinfra
folders with their content. - Delete the Managed Service for GitLab instance or the GitLab image VM.
- Delete the Managed Service for Kubernetes cluster.
- Delete the cluster public static IP address if you reserved one.
- Delete the service accounts.