How to run a Azure Pipeline agent in Docker container on a Raspberry PI

Would it not be create to have your own Azure Pipeline agent running on your own small board computer (SBC) in Docker. Like the Raspberry PI. In this blog I am going to talk about setting up an Azure Pipeline agent in Docker container on a Raspberry PI 4.


Summary

In this blog we are going to create an Azure Pipele agent docker image. We will do this with a Raspberry PI 4 and with Docker. First we will start preparing to build the docker image, then we are going to build it and we will finish with how to start it up.


Prerequisites

  • Raspberry PI 4 (preferred with 2 GB or 4 GB)
  • Micro SD card with 16GB or more
  • Ubuntu with Docker installed
  • Basic knowledge about:
    • Putty
    • Azure Devops

How to create an Azure Pipeline agent docker image on a Raspberry PI

To create an Azure Devops agent on docker on a Raspberry PI, you need to do the following steps:

  • Open putty and connect to your raspberry pi
  • Create a new directory with the name you like. Example ‘mkdir AzureDevopsAgent’
  • Go into the directory you just created
  • Create the file ‘start.sh’ with the command ‘nano start.sh’ and fill it with the following code:
#!/bin/bash
set -e

if [ -z "$AZP_URL" ]; then
  echo 1>&2 "error: missing AZP_URL environment variable"
  exit 1
fi

if [ -z "$AZP_TOKEN_FILE" ]; then
  if [ -z "$AZP_TOKEN" ]; then
    echo 1>&2 "error: missing AZP_TOKEN environment variable"
    exit 1
  fi

  AZP_TOKEN_FILE=/azp/.token
  echo -n $AZP_TOKEN > "$AZP_TOKEN_FILE"
fi

unset AZP_TOKEN

if [ -n "$AZP_WORK" ]; then
  mkdir -p "$AZP_WORK"
fi

rm -rf /azp/agent
mkdir /azp/agent
cd /azp/agent

export AGENT_ALLOW_RUNASROOT="1"

cleanup() {
  if [ -e config.sh ]; then
    print_header "Cleanup. Removing Azure Pipelines agent..."

    ./config.sh remove --unattended \
      --auth PAT \
      --token $(cat "$AZP_TOKEN_FILE")
  fi
}

print_header() {
  lightcyan='\033[1;36m'
  nocolor='\033[0m'
  echo -e "${lightcyan}$1${nocolor}"
}

# Let the agent ignore the token env variables
export VSO_AGENT_IGNORE=AZP_TOKEN,AZP_TOKEN_FILE

print_header "0. Determining OS version"
AZP_OS=$(uname -i)
if [ "$AZP_OS" = "aarch64" ]
then
  AZP_OS_TYPE='arm64'
else
  AZP_OS_TYPE='arm'
fi

echo "OS TYPE: $AZP_OS_TYPE"

print_header "1. Download and installing .NET CORE"

if [ -d "/usr/share/dotnet/" ]; then
  ### Take action if $DIR exists ###
  echo ".NET CORE already installed. Skipping..."
else
  ###  Control will jump here if $DIR does NOT exists ###
  if [ "$AZP_OS_TYPE" = "arm64" ]
  then
    DOTNETCORE_URL='https://download.visualstudio.microsoft.com/download/pr/fe5c0663-3ed1-4a93-95e1-fd068b89215b/14d1caad8fd2859d5f3514745a9bf6b3/dotnet-sdk-3.1.301-linux-arm64.tar.gz'
  else
    DOTNETCORE_URL='https://download.visualstudio.microsoft.com/download/pr/ccbcbf70-9911-40b1-a8cf-e018a13e720e/03c0621c6510f9c6f4cca6951f2cc1a4/dotnet-sdk-3.1.201-linux-arm.tar.gz'
  fi

  mkdir -p /usr/share/dotnet/

  echo "Start download $DOTNETCORE_URL"
  curl -LsS $DOTNETCORE_URL | tar -xz -C /usr/share/dotnet/ & wait $!
  echo "Finish download"

  ln -s /usr/share/dotnet/dotnet /usr/bin/dotnet
  echo "Created "
fi

print_header "2. Determining matching Azure Pipelines agent..."

AZP_AGENT_RESPONSE=$(curl -LsS \
  -u user:$(cat "$AZP_TOKEN_FILE") \
  -H 'Accept:application/json;api-version=3.0-preview' \
  "$AZP_URL/_apis/distributedtask/packages/agent?platform=linux-$AZP_OS_TYPE")

if echo "$AZP_AGENT_RESPONSE" | jq . >/dev/null 2>&1; then
  AZP_AGENTPACKAGE_URL=$(echo "$AZP_AGENT_RESPONSE" \
    | jq -r '.value | map([.version.major,.version.minor,.version.patch,.downloadUrl]) | sort | .[length-1] | .[3]')
fi

if [ -z "$AZP_AGENTPACKAGE_URL" -o "$AZP_AGENTPACKAGE_URL" == "null" ]; then
  echo 1>&2 "error: could not determine a matching Azure Pipelines agent - check that account '$AZP_URL' is correct and the token is valid for that account"
  exit 1
fi

print_header "3. Downloading and installing Azure Pipelines agent..."

curl -LsS $AZP_AGENTPACKAGE_URL | tar -xz & wait $!

source ./env.sh

trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM

print_header "4. Configuring Azure Pipelines agent..."

./config.sh --unattended \
  --agent "${AZP_AGENT_NAME:-$(hostname)}" \
  --url "$AZP_URL" \
  --auth PAT \
  --token $(cat "$AZP_TOKEN_FILE") \
  --pool "${AZP_POOL:-Default}" \
  --work "${AZP_WORK:-_work}" \
  --replace \
  --acceptTeeEula & wait $!

# remove the administrative token before accepting work
rm $AZP_TOKEN_FILE

print_header "4. Running Azure Pipelines agent..."

# `exec` the node runtime so it's aware of TERM and INT signals
# AgentService.js understands how to handle agent self-update and restart
exec ./externals/node/bin/node ./bin/AgentService.js interactive

  • Close nano (Control + X) and confirm you want to save the file (Y)
  • Create the file ‘dockerfile’ with the command ‘nano dockerfile’ and fill it with the following code:
FROM ubuntu:18.04

#Update and Upgrade to latest version
RUN apt update && apt upgrade -y

#Install required files
RUN DEBIAN_FRONTEND="noninteractive"  apt install curl libunwind8 gettext wget nano docker.io docker-compose -y
RUN apt-get install -y --no-install-recommends ca-certificates curl jq git iputils-ping libcurl4 libicu60 libunwind8 netcat

#Set working directory to /azp
WORKDIR /azp

#Copy start script file and make it executable
COPY ./start.sh .
RUN chmod +x start.sh

#Tell docker to start 'start.sh'
CMD ["./start.sh"]
  • Close nano (Control + X) and confirm you want to save the file (Y)
  • Build the container with the following command “docker build . -t azure-pipeline-agent”. The result will look like:

How to start-up your Azure Pipeline agent container on a Raspberry PI

To register and start-up you image you can run the following command: “docker run -e AZP_URL=’https://{oursubscription}.visualstudio.com’ -e AZP_TOKEN='{ourpattoken}’ -e AZP_AGENT_NAME=azure-pipeline-agent -e AZP_POOL='{yourpoolname}’ -d –restart=always –net=host -v /var/run/docker.sock:/var/run/docker.sock –name azure-pipeline-agent azure-pipeline-agent:latest”.

  • Replace {oursubscription} with your subscription of Azure Devops
  • Replace {ourpattoken} with the PAT token of your subscription of Azure Devops
  • Replace {yourpoolname} with the name of the pool you want to run your device in. You also may leave it out.
  • “–restart=always” will make sure it restarts when it crashes
  • “–net=host” will assign the same IP to the docker container then your host (optional)
  • “-v /var/run/docker.sock:/var/run/docker.sock” to have access to your docker host to build docker images
  • “–name azure-pipeline-agent” set the name of your docker container

You can see and check if your Azure Pipeline agent is up and running with the command “docker ps -a”

You also can login in azure devops and go to your “Organistation settings” –> “Agent pools” –> “WorkerPool”. You then also see there that the agent is up and running:

© 2021 Johan the Developer . Powered by WordPress. Theme by Viva Themes.