- Home /
- Resources /
- Learning center /
- CI/CD Guide - Depl...
CI/CD Guide - Deploy Equinix Metal servers within your GitHub Actions workflow
Deploy Equinix Metal servers within your GitHub Actions workflow.
On this page
Introduction to GitHub Actions
GitHub Actions, like all CI/CD systems, run their jobs on runners, either virtual machines or containers. Like all CI/CD systems, the description of the actions to take as part of the pipeline is defined in a configuration file. In the case of GitHub Actions, these are yaml files.
If GitHub Actions are new to you, we highly recommend you read the official GitHub Actions documentation.
If you already are familiar, it is helpful to keep the yaml reference handy.
Each yaml file contains a named workflow. Each workflow consists of one or more named jobs. Each job, in turn, consists of a sequence of one or more tasks called steps.
Here is an example of a simple GitHub Actions workflow that runs on a Linux runner and prints a message. This example is modified from the official GitHub Actions documentation.
name: In Flow Testing
on: push
jobs:
my-job:
name: My Job
runs-on: ubuntu-latest
steps:
- name: Print a greeting
env:
MY_VAR: Hi there! My name is
FIRST_NAME: Equinix
LAST_NAME: Metal
run: |
echo $MY_VAR $FIRST_NAME $LAST_NAME.
In this case, the single step provided just runs a shell command to print a message.
In addition, steps can run pre-packaged activities, called "actions". Actions are reusable units of code that can be shared with the community. You can find actions in the GitHub Marketplace.
Here is an example of a step that uses an action to check out the repository.
- name: Checkout code
uses: actions/checkout@v2
There isn't any code you have to write. It all is packaged up inside the checkout
action.
Deploying Equinix Metal servers in a GitHub Actions workflow
There are several methods to deploy a server on Equinix Metal as part of a GitHub Actions workflow.
Deploying a server using the Equinix Metal CLI
One option is to install the metal CLI, authenticate, and run the metal
commands directly in the workflow.
Here is an example of a job that installs the metal CLI, authenticates, and creates a server.
name: In Flow Testing
on: push
jobs:
my-job:
name: My Job
runs-on: ubuntu-latest
steps:
- name: Install metal CLI
run: |
curl -sL https://github.com/equinix/metal-cli/releases/download/v0.22.0/metal-linux-amd64 -o metal
chmod +x metal
sudo mv metal /usr/local/bin/metal
- name: Create Metal project
id: create-project
run: |
OUTPUT=$(metal project create --name ci-cd-project --organization-id $METAL_ORG --output json)
METAL_PROJECT_ID=$(echo $OUTPUT | jq -r '.id')
echo projectId=$METAL_PROJECT_ID >> $GITHUB_OUTPUT
- name: Create Metal device
run: |
OUTPUT=$(metal --project-id ${{ steps.create-project.outputs.projectId }} device create --hostname my-server --plan c3.small.x86 --metro da --operating-system ubuntu_20_04 --output json)
METAL_DEVICE_ID=$(echo $OUTPUT | jq -r '.id')
echo deviceId=$METAL_DEVICE_ID >> $GITHUB_OUTPUT
- name: Do testing tasks
run: |
# do some testing tasks
- name: Remove metal device
run: |
metal device delete --id ${{ steps.create-device.outputs.deviceId }} --force
- name: Remove metal project
run: |
metal project delete --id ${{ steps.create-project.outputs.projectId }} --force
The above works reasonably well, but it has some drawbacks:
- The metal CLI is not pre-installed on the GitHub Actions runners. You have to install it yourself.
- The metal CLI requires some steps to manage the output and wrangle it into a useful format.
- You need to manage getting that useful output into a store that can be used by subsequent steps.
- The steps are written as scripts. The inputs are messy interspersed into the code itself. And this is before pulling out some of the hard-coded values into environment variables or options for the run.
This can be better, thanks to the hard-working staff of Equinix Metal.
Deploying a server using Equinix Metal Actions
Recall the first example, with a single action that handles all of the cloning of the repository. Equinix Metal has pre-packaged Actions available to handle all of the work you did above.
The Actions available are:
- metal-project-action to create a project
- metal-device-action to create a device
- metal-sweeper-action to clean up artifacts, like devices and projects
Convert the scripts into a workflow that uses those actions.
name: In Flow Testing
on: push
jobs:
my-job:
name: My Job
runs-on: ubuntu-latest
steps:
- name: Create temporary project
id: metal-project
uses: equinix-labs/metal-project-action@v0.14.1
with:
userToken: ${{ secrets.METAL_AUTH_TOKEN }}
- name: Create device in temporary project
uses: equinix-labs/metal-device-action@v0.2.1
continue-on-error: true
with:
metal_auth_token: ${{ steps.metal-project.outputs.projectToken }}
metal_project_id: ${{ steps.metal-project.outputs.projectID }}
metro: da
plan: m3.small.x86
os: ubuntu_22_04
- name: Do testing tasks
run: |
# do some testing tasks
- name: Delete temporary project & device
uses: equinix-labs/metal-sweeper-action@v0.6.1
with:
authToken: ${{ secrets.METAL_AUTH_TOKEN }}
projectID: ${{ steps.metal-project.outputs.projectID }}
This code is much cleaner. The inputs are all in the with
section, which are where the parameters
are passed to the actions.
The outputs are all in the outputs
section. There are no scripts that pertain to creating or deploying
resources, and no ugly lines that have to be structured just right.
Take one more step to make this even cleaner: put the common parameters into the env
section.
name: In Flow Testing
on: push
env:
PLAN: m3.small.x86
OS: ubuntu_22_04
METRO: da
jobs:
my-job:
name: My Job
runs-on: ubuntu-latest
steps:
- name: Create temporary project
id: metal-project
uses: equinix-labs/metal-project-action@v0.14.1
with:
userToken: ${{ secrets.METAL_AUTH_TOKEN }}
- name: Create device in temporary project
uses: equinix-labs/metal-device-action@v0.2.1
continue-on-error: true
with:
metal_auth_token: ${{ steps.metal-project.outputs.projectToken }}
metal_project_id: ${{ steps.metal-project.outputs.projectID }}
metro: ${{ env.METRO }}
plan: ${{ env.PLAN }}
os: ${{ env.OS }}
- name: Do testing tasks
run: |
# do some testing tasks
- name: Delete temporary project & device
uses: equinix-labs/metal-sweeper-action@v0.6.1
with:
authToken: ${{ secrets.METAL_AUTH_TOKEN }}
projectID: ${{ steps.metal-project.outputs.projectID }}
The pre-packaged Equinix Metal Actions have one other distinct benefit. Normally, when you create
a device in Metal - whether using the Web UI, API or metal
CLI - it does not create the device,
but rather creates the request to create the device. You then need to check back to see if the device
is ready or not.
The equinix-device-action
comes with a built-in timeout. It waits until the device is ready before
proceeding to the next step. However, this means that the step might take longer to complete. If you
need to deploy multiple servers, we recommend using GitHub Actions' parallel execution within a single
job for deployment, and then another job for running tests.
name: In Flow Testing
on: push
env:
PLAN: m3.small.x86
OS: ubuntu_22_04
METRO: da
jobs:
create-project:
name: Create Project
runs-on: ubuntu-latest
outputs:
projectID: ${{ steps.metal-project.outputs.projectID }}
projectToken: ${{ steps.metal-project.outputs.projectToken }}
steps:
- name: Create temporary project
id: metal-project
uses: equinix-labs/metal-project-action@v0.14.1
with:
userToken: ${{ secrets.METAL_AUTH_TOKEN }}
deploy-servers:
name: Deploy Servers
runs-on: ubuntu-latest
needs: create-project
strategy:
matrix:
count: [1, 2, 3]
outputs:
deviceid_1: ${{ steps.create-device.outputs.deviceid_1 }}
ipaddress_1: ${{ steps.create-device.outputs.ipaddress_1 }}
deviceid_2: ${{ steps.create-device.outputs.deviceid_2 }}
ipaddress_2: ${{ steps.create-device.outputs.ipaddress_2 }}
deviceid_3: ${{ steps.create-device.outputs.deviceid_3 }}
ipaddress_3: ${{ steps.create-device.outputs.ipaddress_3 }}
steps:
- name: Create device in temporary project
id: create-device
uses: equinix-labs/metal-device-action@v0.2.1
continue-on-error: true
with:
metal_auth_token: ${{ needs.create-project.outputs.projectToken }}
metal_project_id: ${{ needs.create-project.outputs.projectID }}
metro: ${{ env.METRO }}
plan: ${{ env.PLAN }}
os: ${{ env.OS }}
- name: Set device ID output
id: outputs
run: |
echo "deviceid_${{ matrix.count }}=${{ steps.create-device.outputs.deviceid }}" >> $GITHUB_OUTPUTS
echo "ipaddress_${{ matrix.count }}=${{ steps.create-device.outputs.ipaddress }}" >> $GITHUB_OUTPUTS
test-servers:
name: Run Tests
runs-on: ubuntu-latest
needs: deploy-servers
steps:
- name: Do testing tasks
run: |
# do some testing tasks
# to access individual server ipaddress and deviceid, use ${{ needs.deploy-servers.outputs.ipaddress_1 }} and ${{ needs.deploy-servers.outputs.deviceid_1 }} etc.
clean-up:
name: Clean Up
runs-on: ubuntu-latest
needs: [test-servers,create-project]
steps:
- name: Delete temporary project & devices
uses: equinix-labs/metal-sweeper-action@v0.6.1
with:
authToken: ${{ secrets.METAL_AUTH_TOKEN }}
projectID: ${{ needs.create-project.outputs.projectID }}
This is looking pretty good.
- You have a
create-project
job that creates a project. - You have a
deploy-servers
job that creates servers in that project. Because of thematrix
, it creates three of them in parallel. Any one failure will stop the entire job and workflow, while they are deployed in parallel. - You have a
test-servers
job that runs tests on the servers. It only runs if thedeploy-servers
job is successful. - You have a
clean-up
job that deletes the project and all the servers. It runs even if the tests fail. - The common parameters are extracted into
env
.
One last piece is missing. Notice the reference to secrets.METAL_AUTH_TOKEN
? We need to set that up in the GitHub
repository. When you go to your repository, you will be able to add secrets in the "Settings" tab. This workflow
expects to find it there. Do not store the secret hard-coded into the workflow.
Apply this all together.
- Go to GitHub and create a new repository, or, if you already have one you want to use, skip this step.
- Enable Actions in the repository in "Settings".
- Create a secret in the repository called
METAL_AUTH_TOKEN
with the value of your Equinix Metal API token. - Create a new file in the
.github/workflows
directory calledmetal.yml
, of the filename of your choice. - Copy the above workflow into the file.
- Edit the workflow as you see fit.
Some things you may want to edit in the workflow:
- The values of
PLAN
,OS
, andMETRO
in theenv
section. - The number of servers to deploy in the
matrix
section. - The tests. In this guide, the tests are left empty - customize it by adding the tests that best suit your project.
Now you can deploy Equinix Metal projects and servers, and clean them up, as part of your GitHub Actions workflows!
Last updated
03 June, 2024Category
Tagged
ArticleYou may also like
Dig deeper into similar topics in our archivesConfiguring BGP with BIRD 2 on Equinix Metal
Set up BGP on your Equinix Metal server using BIRD 2, including IP configuration, installation, and neighbor setup to ensure robust routing capabilities between your server and the Equinix M...
Configuring BGP with FRR on an Equinix Metal Server
Establish a robust BGP configuration on your Equinix Metal server using FRR, including setting up network interfaces, installing and configuring FRR software, and ensuring secure and efficie...
Crosscloud VPN with WireGuard
Learn to establish secure VPN connections across cloud environments using WireGuard, including detailed setups for site-to-site tunnels and VPN gateways with NAT on Equinix Metal, enhancing...
Deploy Your First Server
Learn the essentials of deploying your first server with Equinix Metal. Set up your project & SSH keys, provision a server and connect it to the internet.