Skip to content

Commit 1f1a939

Browse files
feat(instances): add GitLab runner Terraform tutorial ext-add-instances
1 parent dd5ae74 commit 1f1a939

File tree

1 file changed

+325
-0
lines changed
  • tutorials/terraform-dynamic-gitlab-runner

1 file changed

+325
-0
lines changed
Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
---
2+
meta:
3+
title: Dynamically create and destroy GitLab runner using Terraform
4+
description: How to dynamically create and destroy GitLab runner using Scaleway Terraform provider
5+
content:
6+
h1: Dynamically create and destroy GitLab runner using Terraform
7+
paragraph: How to dynamically create and destroy GitLab runner using Scaleway Terraform provider
8+
tags: automation terraform instances GitLab ci-cd
9+
categories:
10+
- instances
11+
- devtools
12+
---
13+
14+
import Requirements from '@macros/iam/requirements.mdx'
15+
16+
## Gitlab runner overview
17+
18+
GitLab runners are the components that execute the jobs defined in your GitLab CI/CD pipelines. They can be installed on various operating systems and can run jobs on different platforms. Runners can be configured to be specific to a project or shared across multiple projects.
19+
With terraform Scaleway provider and GitLab one, runners can be dynamically created and destroyed.
20+
21+
## Before you start
22+
23+
To complete the actions presented below, you must have:
24+
25+
- A Scaleway account logged into the [console](https://console.scaleway.com)
26+
- [Owner](/iam/concepts/#owner) status or [IAM permissions](/iam/concepts/#permission) allowing you to perform actions in the intended Organization
27+
- [Created IAM API key](/iam/how-to/create-api-keys/)
28+
- A GitLab account and project
29+
30+
## Provision a GitLab runner on a Scaleway instance with terraform.
31+
32+
### Gitlab setup
33+
34+
he following GitLab CI/CD needs 3 secret variables :
35+
- GITLAB_TOKEN
36+
- SCW_SECRET_KEY
37+
- SCW_ACCES_KEY
38+
39+
The `GITLAB_TOKEN` needs to be a GitLab personal access token with the Owner role, and create_runner scope.
40+
Create the environment variables in your [project cicd settings](https://docs.gitlab.com/ci/variables/)
41+
42+
### terraform setup
43+
44+
First we have to provide a basic terraform setup :
45+
46+
1. Create the main.tf file :
47+
```hcl
48+
//providers
49+
terraform {
50+
required_providers {
51+
scaleway = {
52+
source = "scaleway/scaleway"
53+
}
54+
gitlab = {
55+
source = "gitlabhq/gitlab"
56+
}
57+
}
58+
required_version = ">= 0.13"
59+
}
60+
61+
provider "gitlab" {
62+
token = var.gitlab_token
63+
}
64+
65+
provider "scaleway" {
66+
zone = "fr-par-1" //change to the zone you want to use
67+
region = "fr-par"
68+
access_key = var.access_key
69+
secret_key = var.secret_key
70+
}
71+
72+
resource "scaleway_instance_ip" "main" {
73+
tags = ["cicd"] //or any tag you want to apply to all ci/cd instances
74+
project_id = var.project_id
75+
}
76+
77+
resource "scaleway_instance_server" "main" {
78+
type = var.instance_type
79+
image = "debian_bookworm"
80+
81+
project_id = var.project_id
82+
83+
ip_id = scaleway_instance_ip.main.id
84+
85+
tags = ["ci-cd", gitlab_user_runner.project_runner.id, var.gitlab_project_name, var.gitlab_commit_branch]
86+
87+
root_volume {
88+
size_in_gb = var.volume_size
89+
sbs_iops = 15000
90+
}
91+
92+
user_data = {
93+
cloud-init = local.cloud_init
94+
}
95+
}
96+
97+
resource "gitlab_user_runner" "project_runner" {
98+
runner_type = "project_type"
99+
project_id = var.gitlab_project_id
100+
untagged = false
101+
tag_list = var.runner_tags
102+
maximum_timeout = var.job_timeout
103+
}
104+
105+
locals {
106+
cloud_init = templatefile("${path.module}/cloud-init.yml", {
107+
token = gitlab_user_runner.project_runner.token,
108+
tags = join(",", var.runner_tags)
109+
})
110+
}
111+
112+
113+
output "runner_id" {
114+
value = gitlab_user_runner.project_runner.id
115+
}
116+
```
117+
2. Create the var.tf file :
118+
```hcl
119+
variable "project_id" {
120+
type = string
121+
description = "Your project ID."
122+
}
123+
124+
variable "scw_org_id" {
125+
type = string
126+
description = "Your scaleway organisation ID"
127+
}
128+
129+
variable "secret_key" {
130+
type = string
131+
sensitive = true
132+
}
133+
134+
variable "access_key" {
135+
type = string
136+
}
137+
138+
variable "gitlab_token" {
139+
type = string
140+
sensitive = true
141+
}
142+
143+
variable "gitlab_project_id" {
144+
type = string
145+
}
146+
147+
variable "gitlab_project_name" {
148+
type = string
149+
}
150+
151+
variable "gitlab_commit_branch" {
152+
type = string
153+
}
154+
155+
variable "runner_tags" {
156+
type = list(string)
157+
default = []
158+
}
159+
160+
variable "instance_type" {
161+
type = string
162+
default = "DEV1-S"
163+
}
164+
165+
variable "job_timeout" {
166+
type = number
167+
default = 3600
168+
}
169+
170+
variable "volume_size" {
171+
type = number
172+
default = 50
173+
description = "volume in Gb"
174+
}
175+
```
176+
3. Create the cloud-init.yml file :
177+
```yml
178+
#cloud-config
179+
package_update: true
180+
package_upgrade: false
181+
182+
runcmd:
183+
- sudo apt install -y curl
184+
- curl -fsSL https://get.docker.com | sudo sh
185+
- curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
186+
- apt install -y gitlab-runner
187+
- |
188+
gitlab-runner register -n \
189+
--url "https://gitlab.com/" \
190+
--registration-token ${token} \
191+
--executor docker \
192+
--description "My Docker Runner" \
193+
--docker-image "docker:24.0.5" \
194+
--docker-privileged \
195+
--tag-list "${tags}" \
196+
--docker-volumes "/certs/client"
197+
```
198+
199+
### Gitlab setup
200+
201+
1. Create the .gitlab-ci.yml :
202+
```yml
203+
stages:
204+
- provision
205+
- work
206+
- clean
207+
208+
.default_env_vars: &default_env_vars |
209+
cat <<EOF > env.tfvars
210+
scw_org_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
211+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
212+
access_key = "$SCW_ACCES_KEY"
213+
secret_key = "$SCW_SECRET_KEY"
214+
gitlab_token = "$GITLAB_TOKEN"
215+
gitlab_project_id = "$CI_PROJECT_ID"
216+
runner_tags = [ "custom" ]
217+
instance_type = "PLAY2-PICO"
218+
job_timeout = 1200
219+
volume_size = 20
220+
gitlab_project_name="${CI_PROJECT_NAMESPACE_SLUG}-${CI_PROJECT_NAME}"
221+
gitlab_commit_branch="$CI_COMMIT_BRANCH"
222+
223+
EOF
224+
225+
provision:
226+
stage: provision
227+
image: alpine
228+
before_script:
229+
- apk update
230+
- apk add --no-cache wget unzip bash curl openssl libc6-compat jq
231+
- wget https://releases.hashicorp.com/terraform/1.6.6/terraform_1.6.6_linux_amd64.zip
232+
- unzip terraform_1.6.6_linux_amd64.zip
233+
- mv terraform /usr/local/bin/
234+
- terraform -version
235+
script:
236+
- *default_env_vars
237+
- terraform init -var-file="env.tfvars"
238+
- terraform apply -var-file="env.tfvars" -auto-approve
239+
- bash ./look.bash "$(terraform output -raw runner_id)" "$GITLAB_TOKEN" 600
240+
artifacts:
241+
paths:
242+
- "*.tfstate*"
243+
when: always
244+
access: none
245+
246+
work:
247+
stage: work
248+
image: alpine
249+
tags:
250+
- custom
251+
needs:
252+
- provision
253+
script:
254+
- echo "hello-world"
255+
256+
clean:
257+
stage: clean
258+
image: alpine
259+
when: always
260+
needs:
261+
- job: work
262+
optional: true
263+
artifacts: false
264+
- provision
265+
before_script:
266+
- apk update
267+
- apk add --no-cache wget unzip bash curl openssl libc6-compat
268+
- wget https://releases.hashicorp.com/terraform/1.6.6/terraform_1.6.6_linux_amd64.zip
269+
- unzip terraform_1.6.6_linux_amd64.zip
270+
- mv terraform /usr/local/bin/
271+
- terraform -version
272+
script:
273+
- *default_env_vars
274+
- terraform init -var-file="env.tfvars"
275+
- terraform apply -destroy -var-file="env.tfvars" -auto-approve
276+
```
277+
2. Create the look.bash file :
278+
```bash
279+
#!/bin/bash
280+
281+
URL="https://gitlab.com/api/v4/runners/$1"
282+
TOKEN="$2"
283+
TIMEOUT=$3
284+
START_TIME=$(date +%s)
285+
286+
while true; do
287+
CURRENT_TIME=$(date +%s)
288+
ELAPSED_TIME=$((CURRENT_TIME - START_TIME))
289+
290+
if [ $ELAPSED_TIME -ge $TIMEOUT ]; then
291+
echo "Timeout: Runner did not come online within 5 minutes."
292+
exit 1 # Exit with error code
293+
fi
294+
295+
STATUS=$(curl --silent --header "PRIVATE-TOKEN: $TOKEN" "$URL" | jq .status)
296+
297+
if [ "$STATUS" == "\"online\"" ]; then
298+
echo "Runner is online."
299+
break
300+
else
301+
echo "Runner status: $STATUS. Retrying in 5 seconds..."
302+
sleep 5
303+
fi
304+
done
305+
```
306+
307+
With this setup the Gitlab-CI/CD create an instance and register it as a GitLab runner, run the work job on the instance, then delete the instance and the runner.
308+
This setup allows you to dynamically provide high computation runners in a cost-effective way. You can also customize the cloud init to fit your business needs, such as connecting to your company VPN.
309+
310+
## Going further
311+
312+
If you want to use a custom instance image, you can edit the `main.tf` file as bellow :
313+
314+
```hcl
315+
// ... previous code
316+
317+
data "scaleway_instance_image" "my_image" {
318+
image_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
319+
}
320+
321+
resource "scaleway_instance_server" "main" {
322+
type = var.instance_type
323+
image = data.scaleway_instance_image.my_image.id
324+
325+
// ...

0 commit comments

Comments
 (0)