github.com/secure-build/gitlab-runner@v12.5.0+incompatible/docs/executors/custom_examples/libvirt.md (about) 1 # Using libvirt with the Custom executor 2 3 Using [libvirt](https://libvirt.org/), the Custom executor will create a 4 new disk and VM for every job it executes, after which the disk and VM 5 will be deleted. 6 7 This example is inspired by a Community Contribution 8 [!464](https://gitlab.com/gitlab-org/gitlab-runner/merge_requests/464) 9 to add libvirt as a GitLab Runner executor. 10 11 This document does not try to explain how to set up libvirt, since it's 12 out of scope. However, this executor was tested using [GCP Nested 13 Virtualization](https://cloud.google.com/compute/docs/instances/enable-nested-virtualization-vm-instances), 14 which also has [details on how to setup 15 libvirt](https://cloud.google.com/compute/docs/instances/enable-nested-virtualization-vm-instances#starting_a_private_bridge_between_the_host_and_nested_vms) 16 with bridge networking. This example will use the `default` network that 17 comes with when installing libvirt so make sure it's running. 18 19 This executor requires bridge networking since each VM needs to have 20 it's own dedicated IP address so GitLab Runner can SSH inside of it to 21 run commands. An SSH key can be generated [using the following 22 commands](https://docs.gitlab.com/ee/ssh/#generating-a-new-ssh-key-pair). 23 24 A base disk VM image is created so that dependencies are not downloaded 25 every build. In the following example, 26 [virt-builder](http://libguestfs.org/virt-builder.1.html) is used to 27 create a disk VM image. 28 29 ```sh 30 virt-builder debian-9 \ 31 --size 8G \ 32 --output /var/lib/libvirt/images/gitlab-runner-base.qcow2 \ 33 --format qcow2 \ 34 --hostname gitlab-runner-stretch \ 35 --network \ 36 --install curl \ 37 --run-command "curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | bash" \ 38 --run-command "curl -s https://packagecloud.io/install/repositories/github/git-lfs/script.deb.sh | bash" \ 39 --run-command 'useradd -m -p "" gitlab-runner -s /bin/bash' \ 40 --install gitlab-runner,git,git-lfs,openssh-server \ 41 --run-command "git lfs install --skip-repo" \ 42 --ssh-inject gitlab-runner:file:/root/.ssh/id_rsa.pub \ 43 --run-command "echo 'gitlab-runner ALL=(ALL) NOPASSWD: ALL' >> /etc/sudoers" \ 44 --run-command "sed -E 's/GRUB_CMDLINE_LINUX=\"\"/GRUB_CMDLINE_LINUX=\"net.ifnames=0 biosdevname=0\"/' -i /etc/default/grub" \ 45 --run-command "grub-mkconfig -o /boot/grub/grub.cfg" \ 46 --run-command "echo 'auto eth0' >> /etc/network/interfaces" \ 47 --run-command "echo 'allow-hotplug eth0' >> /etc/network/interfaces" \ 48 --run-command "echo 'iface eth0 inet dhcp' >> /etc/network/interfaces" 49 ``` 50 51 The command above will install all the 52 [prerequisites](../custom.md#prerequisite-software-for-running-a-job) specified 53 earlier. 54 55 `virt-builder` will set a root password automatically which is printed 56 at the end. If you want to specify a password yourself, pass 57 [`--root-password 58 password:$SOME_PASSWORD`](http://libguestfs.org/virt-builder.1.html#setting-the-root-password). 59 60 ## Configuration 61 62 The following is an example of Runner configuration for libvirt: 63 64 ```toml 65 concurrent = 1 66 check_interval = 0 67 68 [session_server] 69 session_timeout = 1800 70 71 [[runners]] 72 name = "libvirt-executor" 73 url = "https://gitlab.com/" 74 token = "xxxxx" 75 executor = "custom" 76 builds_dir = "/home/gitlab-runner/builds" 77 cache_dir = "/home/gitlab-runner/cache" 78 [runners.custom_build_dir] 79 [runners.cache] 80 [runners.cache.s3] 81 [runners.cache.gcs] 82 [runners.custom] 83 prepare_exec = "/opt/libvirt-executor/prepare.sh" # Path to a bash script to create VM. 84 run_exec = "/opt/libvirt-executor/run.sh" # Path to a bash script to run script inside of VM over ssh. 85 cleanup_exec = "/opt/libvirt-executor/cleanup.sh" # Path to a bash script to delete VM and disks. 86 ``` 87 88 ## Base 89 90 Each stage ([prepare](#prepare), [run](#run), and [cleanup](#cleanup)) 91 will use the base script below to generate variables that are used 92 throughout other scripts. 93 94 It's important that this script is located in the same directory as the 95 other scripts, in this case `/opt/libivirt-executor/`. 96 97 ```sh 98 #!/usr/bin/env bash 99 100 # /opt/libivrt-executor/base.sh 101 102 VM_IMAGES_PATH="/var/lib/libvirt/images" 103 BASE_VM_IMAGE="$VM_IMAGES_PATH/gitlab-runner-base.qcow2" 104 VM_ID="runner-$CUSTOM_ENV_CI_RUNNER_ID-project-$CUSTOM_ENV_CI_PROJECT_ID-concurrent-$CUSTOM_ENV_CI_CONCURRENT_PROJECT_ID-job-$CUSTOM_ENV_CI_JOB_ID" 105 VM_IMAGE="$VM_IMAGES_PATH/$VM_ID.qcow2" 106 107 _get_vm_ip() { 108 virsh -q domifaddr "$VM_ID" | awk '{print $4}' | sed -E 's|/([0-9]+)?$||' 109 } 110 ``` 111 112 ## Prepare 113 114 The prepare script: 115 116 - Copies the disk to a new path. 117 - Installs a new VM from the copied disk. 118 - Waits for the VM to get an IP. 119 - Waits for ssh to respond on the VM. 120 121 ```sh 122 #!/usr/bin/env bash 123 124 # /opt/libivrt-executor/prepare.sh 125 126 currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 127 source ${currentDir}/base.sh # Get variables from base script. 128 129 set -eo pipefail 130 131 # trap any error, and mark it as a system failure. 132 trap "exit $SYSTEM_FAILURE_EXIT_CODE" ERR 133 134 # Copy base disk to use for Job. 135 cp "$BASE_VM_IMAGE" "$VM_IMAGE" 136 137 # Install the VM 138 virt-install \ 139 --name "$VM_ID" \ 140 --os-variant debian9 \ 141 --disk "$VM_IMAGE" \ 142 --import \ 143 --vcpus=2 \ 144 --ram=2048 \ 145 --network default \ 146 --graphics none \ 147 --noautoconsole 148 149 # Wait for VM to get IP 150 echo 'Waiting for VM to get IP' 151 for i in $(seq 1 30); do 152 VM_IP=$(_get_vm_ip) 153 154 if [ -n "$VM_IP" ]; then 155 echo "VM got IP: $VM_IP" 156 break 157 fi 158 159 if [ "$i" == "30" ]; then 160 echo 'Waited 30 seconds for VM to start, exiting...' 161 # Inform GitLab Runner that this is a system failure, so it 162 # should be retried. 163 exit "$SYSTEM_FAILURE_EXIT_CODE" 164 fi 165 166 sleep 1s 167 done 168 169 # Wait for ssh to become available 170 echo "Waiting for sshd to be available" 171 for i in $(seq 1 30); do 172 if ssh -i /root/.ssh/id_rsa -o StrictHostKeyChecking=no gitlab-runner@"$VM_IP" >/dev/null 2>/dev/null; then 173 break 174 fi 175 176 if [ "$i" == "30" ]; then 177 echo 'Waited 30 seconds for sshd to start, exiting...' 178 # Inform GitLab Runner that this is a system failure, so it 179 # should be retried. 180 exit "$SYSTEM_FAILURE_EXIT_CODE" 181 fi 182 183 sleep 1s 184 done 185 ``` 186 187 ## Run 188 189 This will run the script generated by GitLab Runner by sending 190 the content of the script to the VM via `STDIN` through SSH. 191 192 ```sh 193 #!/usr/bin/env bash 194 195 # /opt/libivrt-executor/run.sh 196 197 currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 198 source ${currentDir}/base.sh # Get variables from base script. 199 200 VM_IP=$(_get_vm_ip) 201 202 ssh -i /root/.ssh/id_rsa -o StrictHostKeyChecking=no gitlab-runner@"$VM_IP" /bin/bash < "${1}" 203 if [ $? -ne 0 ]; then 204 # Exit using the variable, to make the build as failure in GitLab 205 # CI. 206 exit "$BUILD_FAILURE_EXIT_CODE" 207 fi 208 ``` 209 210 ## Cleanup 211 212 This script removes the VM and deletes the disk. 213 214 ```sh 215 #!/usr/bin/env bash 216 217 # /opt/libivrt-executor/cleanup.sh 218 219 currentDir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" 220 source ${currentDir}/base.sh # Get variables from base script. 221 222 set -eo pipefail 223 224 # Destroy VM. 225 virsh shutdown "$VM_ID" 226 227 # Undefine VM. 228 virsh undefine "$VM_ID" 229 230 # Delete VM disk. 231 if [ -f "$VM_IMAGE" ]; then 232 rm "$VM_IMAGE" 233 fi 234 ```