As I was going through the excellent short course called Azure Infrastructure as Code with GitHub (by fellow MVP, Barbara Forbes), a thought appeared – what do I need to do to use my custom runner machine inside a pipeline for… I don’t know… security/privacy concerns, isolation, special requirements, different OS, control, price… or just to complicate things a bit?
Of course, GitHub supports this and it’s called a self-hosted runner.
So, what do I need to do to use this self-hosted runner with my GitHub Actions?
It’s relatively simple – there is an application package, which will be installed on your runner machine, and which will listen for and eventually do all the work defined in your workflow!
But first, let’s introduce my environment.
I have a simple GitHub Action (workflow), which creates a simple storage account on my Azure environment (there is actually no need to convert Bicep to ARM before deployment, but it seemed cool 😀). It’s currently using the „ubuntu-latest“ runner, provided by GitHub… which has also all the needed components inside (like Azure CLI, Azure PowerShell, …).
And it works fine. When there is a push to my GitHub repository, GitHub Actions starts and does what is needed on my Azure environment via this workflow:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
name: deploy-to-azure on: push: branches: [ "main" ] workflow_dispatch: env: resourceGroupName: SimpleAzureRG jobs: deploy-to-azure: runs-on: ubuntu-latest steps: - name: "Checkout the code from GitHub" uses: actions/checkout@v3 - name: "Build Bicep code into ARM (JSON)" uses: Azure/cli@v1 with: inlineScript: az bicep build --file ./main.bicep --outfile ./main.json - name: "Login to Azure" with: creds: ${{ secrets.AZURE_CREDENTIALS }} enable-AzPSSession: true - name: "Create the resource group, if needed" uses: Azure/powershell@v1 with: inlineScript: New-AzResourceGroup -Name ${{ env.resourceGroupName }} -location WestEurope -Force azPSVersion: latest - name: "Deploy the ARM template" uses: Azure/[email protected] with: scope: resourcegroup resourceGroupName: ${{ env.resourceGroupName }} template: main.json parameters: storageAccountPrefix=simplegh deploymentName: "gh${{ github.run_id }}" |
And the mighty Bicep file (😀) it’s using for the deployment is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@maxLength(11) param storageAccountPrefix string = 'bicep' param location string = resourceGroup().location var sta = '${storageAccountPrefix}${uniqueString(subscription().id)}' resource storageaccount 'Microsoft.Storage/storageAccounts@2021-02-01' = { name: sta location: location kind: 'StorageV2' sku: { name: 'Standard_LRS' } } |
Of course, this runs just fine on a standard (hosted) runner:
To run this workflow (successfully) not that much is needed.
First, I’ve created a new virtual machine (I’ll use a simple Ubuntu Hyper-V VM, no autoscaling, no… nothing) called hermes (god of speed 😀), with freshly installed Ubuntu 22.04.1-LTS (minimized).
After that, I went to the Settings of my GitHub repository and got the download and install scripts for the x64 Linux runner:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
### get and install the runner application (script provided by GitHub) ## download the runner application # create a runner folder mkdir actions-runner && cd actions-runner # download the latest runner package curl -o actions-runner-linux-x64-2.298.2.tar.gz -L https://github.com/actions/runner/releases/download/v2.298.2/actions-runner-linux-x64-2.298.2.tar.gz # (optional) validate the hash echo "0bfd792196ce0ec6f1c65d2a9ad00215b2926ef2c416b8d97615265194477117 actions-runner-linux-x64-2.298.2.tar.gz" | shasum -a 256 -c # extract the installer tar xzf ./actions-runner-linux-x64-2.298.2.tar.gz ## configure the runner application # create the runner and start the configuration experience ./config.sh --url https://github.com/TomicaKaniski/AzureIaCGH --token "<<--TOKEN_GOES_HERE!-->>" # last step, run it! (we will actually run it via crontab at machine restart!) # ./run.sh |
As you can see, I’ll be using crontab later to automatically (re)start my self-hosted runner.
If everything went well, you should see your runner “up and running” (😀) in the GitHub portal:
Next, I’ll use the following script to install all prerequisites for my workflow (like Azure CLI, Azure PowerShell, etc. – it really depends on your workflow and things you use):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
### install (my) workflow prerequisites on self-runner machine sudo su - ## install Azure CLI (https://learn.microsoft.com/en-us/cli/azure/install-azure-cli-linux?pivots=apt) curl -sL https://aka.ms/InstallAzureCLIDeb | bash ## install PowerShell (https://learn.microsoft.com/en-us/powershell/scripting/install/install-ubuntu?view=powershell-7.2#installation-via-direct-download) wget -q "https://github.com/PowerShell/PowerShell/releases/download/v7.2.6/powershell-lts_7.2.6-1.deb_amd64.deb" && dpkg -i powershell-lts_7.2.6-1.deb_amd64.deb ## install Bicep az bicep install ## install other useful things apt install apt-utils cron vim -y ## install Docker (https://cloudcone.com/docs/article/how-to-install-docker-on-ubuntu-22-04-20-04/) # install prerequisites apt install apt-transport-https curl gnupg-agent ca-certificates software-properties-common -y # add GPG key curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # add repository add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable" -y # install docker apt install docker-ce docker-ce-cli containerd.io -y # create docker group newgrp docker exit # add current user (tomica) to docker group sudo usermod -aG docker $USER # configure Docker autostart sudo systemctl enable docker sudo systemctl restart docker sudo systemctl status docker ## install Az module for PowerShell (https://learn.microsoft.com/en-us/powershell/azure/install-az-ps?view=azps-9.0.0) pwsh Install-Module Az -Repository PSGallery -Confirm:$false -Force exit ### configure ./run.sh (GitHub runner application) to start automatically crontab -e # add the following line (uncommented, of course; and check if the path is right!): # @reboot /home/tomica/actions-runner/run.sh sudo reboot |
Once this is done, my self-hosted runner hermes should be ready to run the workflow.
To try this, I need to make a slight update to my workflow file – line 12 inside the job configuration should be updated from “runs-on: ubuntu-latest” to “runs-on: self-hosted“.
So, my workflow YAML file now looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
name: deploy-to-azure on: push: branches: [ "main" ] workflow_dispatch: env: resourceGroupName: SimpleAzureRG jobs: deploy-to-azure: runs-on: self-hosted steps: - name: "Checkout the code from GitHub" uses: actions/checkout@v3 - name: "Build Bicep code into ARM (JSON)" uses: Azure/cli@v1 with: inlineScript: az bicep build --file ./main.bicep --outfile ./main.json - name: "Login to Azure" with: creds: ${{ secrets.AZURE_CREDENTIALS }} enable-AzPSSession: true - name: "Create the resource group, if needed" uses: Azure/powershell@v1 with: inlineScript: New-AzResourceGroup -Name ${{ env.resourceGroupName }} -location WestEurope -Force azPSVersion: latest - name: "Deploy the ARM template" uses: Azure/[email protected] with: scope: resourcegroup resourceGroupName: ${{ env.resourceGroupName }} template: main.json parameters: storageAccountPrefix=simplegh deploymentName: "gh${{ github.run_id }}" |
And once I push the configuration to my GitHub, my workflow automatically starts and runs on hermes, my self-hosted runner:
If we prepared our runner right, all is good! 😊
Of course, our resources are deployed successfully:
So, this is how you can use your own, self-hosted runner, to execute your GitHub Actions (workflows).
Cheers!