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  ```