In this exercise we'll learn how to use nx agents and distributed task execution.
In the end we'll have a CI that is able to distribute tasks between multiple self hosted machines with the help of nx.
Open the .github/workflows/ci.yml
file, this will be everything you need
for this exercise.
That's something we have to configure for githubs sake. Add a concurrency
section to your yaml configuration, just below the on
config.
concurrency setting
on:
push:
branches:
- YOUR_BRANCH # 👈️👈️👈️👈️👈️👈️👈️ IMPORTANT !!!
#pull_request:
concurrency:
# Group concurrency on workflow, then:
# - Is merge run? Group on branch name (`refs/heads/main`)
# - Is pull request? Group on pull request branch name, for example `feat/add-awesome-feature`
group: >-
${{ github.workflow }}-${{
github.event_name == 'push'
&& github.ref
|| github.head_ref
}}
# Run merge workflows in sequence to prevent parallel deployments and releases
# Cancel stale pull request runs in progress for the same branch
cancel-in-progress: ${{ github.event_name != 'push' }}
Add a new dte_agents
job section that declares our machines based on a given matrix.
The skeleton should just be able to spawn up to three agents based on the following strategy
The [1, 2]
defines how many agents to spawn. Please don't overdo it here. Keep it small!
name: DTE Agent ${{ matrix.agent }}
strategy:
matrix:
agent: [1, 2]
dte_agents configuration
jobs:
dte_agents:
name: DTE Agent ${{ matrix.agent }}
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
matrix:
agent: [1, 2]
Now we also need to tell the agent what to do. It's quite the same as we do in the main job.
We need to:
checkout
setup-node
npm ci
Plus we have to call npx nx-cloud start-agent
as a last step.
Add the following snippets as steps
section in your agent configuration:
jobs:
dte_agents:
name: DTE Agent ${{ matrix.agent }}
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
matrix:
# Must match the argument passed to `nx-cloud --agent-count` in the main job
agent: [1, 2, 3]
steps:
# 👈️👈️👈️👈️👈️
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Start Nx Cloud DTE Agent
run: npx nx-cloud start-agent
env:
NX_AGENT_NAME: ${{ matrix.agent }}
full dte_agents
jobs:
dte_agents:
name: DTE Agent ${{ matrix.agent }}
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
matrix:
# Must match the argument passed to `nx-cloud --agent-count` in the dte_coordinator job
agent: [1, 2, 3]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Start Nx Cloud DTE Agent
run: npx nx-cloud start-agent
env:
NX_AGENT_NAME: ${{ matrix.agent }}
Now it's time to setup the coordinator, our existing main
step.
In order to make it a coordinator, we need to make it run
npx nx-cloud start-ci-run
before we run our nx affected
commands.
And also make it run npx nx-cloud stop-all-agents
after we've finished the whole process.
We should configure specific NX env variables for this job specifically:
env:
NX_CLOUD_DISTRIBUTED_EXECUTION: true
NX_DISTRIBUTED_TASK_EXECUTION: true
Insert the following step before the existing npx affected
command:
- name: Initialize the Nx Cloud distributed CI run
run: npx nx-cloud start-ci-run --distribute-on="manual"
Insert this snippet after the npx affected
command.
- name: Stop Nx Cloud DTE agents
if: ${{ always() }}
run: npx nx-cloud stop-all-agents
Full `main` -> coordinator job
jobs:
main:
name: DTE Coordinator
runs-on: ubuntu-latest
env:
NX_CLOUD_DISTRIBUTED_EXECUTION: true
NX_DISTRIBUTED_TASK_EXECUTION: true
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Initialize the Nx Cloud distributed CI run
run: npx nx-cloud start-ci-run --distribute-on="manual"
- name: Run commands in parallel
run: npx nx affected -t lint build --base=HEAD^
- name: Stop Nx Cloud DTE agents
if: ${{ always() }}
run: npx nx-cloud stop-all-agents
We need to set the NX_CLOUD_DISTRIBUTED_EXECUTION
to true
& also the
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT
to a number value that tells nx how many
agents it should expect. This value should match the value of the matrix
configured
for the dte_agents
.
Of course, as always. Play around with different change sets and run your newly created wonderful CI environment.
Make it fail, make it pass. Do as you please.
Inspect the detailed outputs of each agent & also watch the nx cloud dashboard giving you information about what happened.
Of course play around with the distributed caching. Make sure to produce the cache locally once and let the CI enjoy having nothing to do :).
name: CI
on:
push:
branches:
- YOUR_BRANCH # 👈️👈️👈️👈️👈️👈️👈️ IMPORTANT !!!
#pull_request:
concurrency:
# Group concurrency on workflow, then:
# - Is merge run? Group on branch name (`refs/heads/main`)
# - Is pull request? Group on pull request branch name, for example `feat/add-awesome-feature`
group: >-
${{ github.workflow }}-${{
github.event_name == 'push'
&& github.ref
|| github.head_ref
}}
# Run merge workflows in sequence to prevent parallel deployments and releases
# Cancel stale pull request runs in progress for the same branch
cancel-in-progress: ${{ github.event_name != 'push' }}
env:
NX_CLOUD_DISTRIBUTED_EXECUTION: true # this enables DTE
#NX_DISTRIBUTED_TASK_EXECUTION: true
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT: 2 # expected number of agents
jobs:
dte_agents:
name: DTE Agent ${{ matrix.agent }}
runs-on: ubuntu-latest
timeout-minutes: 20
# The Free GitHub plan has a limit of 20 concurrent jobs on Ubuntu images
# Reference: https://docs.github.com/en/actions/learn-github-actions/usage-limits-billing-and-administration
# If we need to, we can optimize for 2 simultaneous workflow runs:
# 2 x 1 main job = 2 concurrent jobs
# 2 x 9 agent jobs = 18 concurrent jobs
# Total = 20 concurrent jobs
#
# However, we don't have many projects or targets in this workspace, so we
# lower the number of agents to reduce spent compute time.
strategy:
matrix:
# Must match the argument passed to `nx-cloud --agent-count` in the dte_coordinator job
agent: [1, 2]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Start Nx Cloud DTE Agent
run: npx nx-cloud start-agent
env:
NX_AGENT_NAME: ${{ matrix.agent }}
# We're using Nx Cloud for Distributed Task Execution
# Reference: https://nx.dev/using-nx/dte
#
# The coordinator outputs the combination of task outputs from the agents,
# both terminal and file outputs
main:
name: DTE Coordinator
runs-on: ubuntu-latest
env:
NX_CLOUD_DISTRIBUTED_EXECUTION: true
NX_DISTRIBUTED_TASK_EXECUTION: true
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Initialize the Nx Cloud distributed CI run
run: npx nx-cloud start-ci-run --distribute-on="manual"
- name: Run commands in parallel
run: npx nx affected -t lint build --base=HEAD^
- name: Stop Nx Cloud DTE agents
if: ${{ always() }}
run: npx nx-cloud stop-all-agents