So you are back for more Automagical goodness? You loved the Poor Mans vRA post but now want to scale out with container goodness and still be the poor man? This post will cover how to setup / build the containers running Ansible for the Automated SOE. (Powershell Host container post to follow soon)
By using containers we can reduce the number of Ubuntu servers we need to deploy but increase the number of parallel jobs + building servers wouldn’t be cool in 2022…. so now we containerize the Devops Agent using docker cli (free tier)
This solution will mount the volume /home/vman/ansible from the Ubuntu host within the containers to have access to the build dependencies like psexec, sysmon, bginfo so that we don’t need to have these dependencies within the container image, in my case I have the servers mount an NFS share so that multiple Ubuntu servers access the same data centrally and its easier to manage. (I don’t cover that in this post)
OK so lets get started!
1).
Start by SSHing into the Ubuntu server built in the previous post, configure the account to have password less sudo (my account is vman).
sudo -i
nano /etc/sudoers.d/vman
Insert the following into nano and save.
Defaults:vman !requiretty
vman ALL=(ALL) NOPASSWD: ALL
Then change the permissions of the file as follows.
chmod 440 /etc/sudoers.d/vman
2).
Install the dependencies
apt install -y software-properties-common, python3-pip, sshpass
, docker.io
3).
Create daemon.json for log rotation
nano /etc/docker/daemon.json
add in the contents below
{
"log-driver": "local",
"log-opts":
{
"max-size": "10m",
"max-file": "3"
}
}
4).
Create the AzureContainerUbuntu folder which will store some of the dependencies needed to create the docker image
mkdir /home/vman/AzureContainerUbuntu
5).
Create the start.sh which will launch when the container starts
nano /home/vman/AzureContainerUbuntu/start.sh
Copy the following into start.sh
#!/bin/bash
set -e
ARCHITECTURE="$(arch)"
PLATFORM=$ARCHITECTURE
if [[ $PLATFORM == x86_64 ]]; then
PLATFORM="linux-x64"
elif [[ $PLATFORM == arm* ]]; then
PLATFORM="linux-arm"
elif [[ $PLATFORM == aarch64 ]]; then
PLATFORM="linux-arm64"
else
echo 1>&2 "Unsupported architecture"
exit 1
fi
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
echo "Setup - Determining matching Azure Pipelines agent..."
AZP_AGENT_RESPONSE=$(curl -LsS \
-u user:$(cat "$AZP_TOKEN_FILE") \
-H 'Accept:application/json' \
"$AZP_URL/_apis/distributedtask/packages/agent?platform=$PLATFORM")
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 "Setup - 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
echo "Setup - Latest agent package will be downloaded from $AZP_AGENTPACKAGE_URL"
echo "Setup - Downloading and unpacking Azure Pipelines agent..."
curl -LsS $AZP_AGENTPACKAGE_URL | tar -xz & wait $!
echo "Setup - Completed download and unpack"
export AGENT_ALLOW_RUNASROOT="1"
cleanup() {
if [ -e config.sh ]; then
print_header "Cleanup. Removing Azure Pipelines agent..."
# If the agent has some running jobs, the configuration removal process will fail.
# So, give it some time to finish the job.
while true; do
./config.sh remove --unattended --auth PAT --token $(cat "$AZP_TOKEN_FILE") && break
echo "Retrying in 30 seconds..."
sleep 30
done
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
source ./env.sh
print_header "1. Configuring Azure Pipelines agent..."
cleanup;
./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 $!
print_header "2. Running Azure Pipelines agent..."
trap 'cleanup; exit 0' EXIT
trap 'cleanup; exit 130' INT
trap 'cleanup; exit 143' TERM
# To be aware of TERM and INT signals call run.sh
# Running it with the --once flag at the end will shut down the agent after the build is executed
./run.sh "$@" &
wait $!
6).
Create the dockerfile
nano /home/vman/AzureContainerUbuntu/
dockerfile
Copy the following into dockerfile
FROM ubuntu:20.04
# To make it easier for build and release pipelines to run apt-get,
# configure apt to not require confirmation (assume the -y argument by default)
ENV DEBIAN_FRONTEND=noninteractive
RUN echo "APT::Get::Assume-Yes \"true\";" > /etc/apt/apt.conf.d/90assumeyes
RUN ln -fs /usr/share/zoneinfo/UTC /etc/localtime
RUN apt-get update && apt-get install -y \
software-properties-common \
tzdata \
python3-pip \
libkrb5-dev \
ca-certificates \
curl \
jq \
git \
iputils-ping \
libcurl4 \
libunwind8 \
netcat \
libssl1.0 \
zip \
unzip \
wget \
apt-transport-https \
sshpass
RUN pip install ansible
RUN pip install requests
RUN pip install pyVim
RUN pip install PyVmomi
RUN pip install pywinrm
RUN pip install pywinrm[credssp]
RUN pip install pywinrm[kerberos]
RUN pip install netaddr
RUN pip install jinja2
RUN rm -rf /var/lib/apt/lists/*
RUN curl -LsS https://aka.ms/InstallAzureCLIDeb | bash \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /azp
COPY ./start.sh .
RUN chmod +x start.sh
CMD ["./start.sh"]
7). Building the Docker Image
Change to the correct directory
cd /home/vman/AzureContainerUbuntu/
Now to build the image type the following command to build the image
docker build -t dockerazureagent:latest .
Once complete we can run the following command to check if the image is ready
sudo docker image list
8). Now that the image is ready, let’s spawn some containers
Run the following command, exchanging your details below, I append _0, _1, etc… for each container so I know which one runs where.
docker run -d –restart unless-stopped –name linsrv01_0 -e AZP_URL=https://dev.azure.com/YOURORGHERE -e AZP_TOKEN=YOURAZUREPATHERE -e AZP_POOL=YOURPOOLNAMEHERE -e AZP_AGENT_NAME=linsrv01_0 -v /home/vman/:/home/vman dockerazureagent:latest
And there you have it, multiple AzureDevops Agents running on the same host allowing for more parallel pipelines without having to build more and more servers.
My post above was heavily influenced by this Microsoft post (give credit where credit is due!)
Hope you found this helpful.
vMan
Recent Comments