Deploy a zriz runner with Docker
Build the runner image from its source, give it a token and a config file, and start it with Docker. The work is done when a run has status pass on this runner.
A runner is a program in your network. It makes the calls of a run and sends back only what the checks need.
The runner is open source. The source is in the ZrizTech/runner repository.
It holds each secret. Security tells what stays in your network.
Do you need one?
Yes. Each run needs a runner. For a first test, start one on this machine: A runner on this machine.
Deploy a runner when:
- the app or the database under test is not reachable from your machine,
- you want runs from CI or on a schedule against a shared environment,
- you use
browserorcliresources.
How it connects
The runner makes outbound connections only: to https://zriz.io, and to the resources in its config. Nothing listens. You open no port and change no firewall rule.
Before you start
- Docker is installed on the host of the runner:
docker --version gitis installed on that host:git --versionzzis logged in:zz runners- The project has the sample pipeline:
zz validate hello
1. Make a token
Do
<runner>is a name that you give to the runner. Example:ci.<env>is the name of the environment that the runner serves. Example:staging.
zz runners token create <runner> --env <env>
Why
The token fixes the environment of the runner. The token is data.token of the reply: store it as a secret.
You should see
{"ok":true,"command":"runners",
"token":"zrt_
If not
not logged in: Dozz login. Then do the command again.
2. Write the config
Do
Make the file config.json on the host of the runner. The sample pipeline hello calls the resource target, so the config names target.
<base url>is the URL of your app, as the runner reaches it. Example:https://staging.shop.example.
Common mistake:
localhostin<base url>. In a container,localhostis the container itself.
config.json:
{
"cloud": { "url": "https://zriz.io", "token-env": "ZRIZ_RUNNER_TOKEN" },
"resources": {
"target": { "type": "http", "base-url": "<base url>" }
}
}
grep '"token-env"' config.json
Why
The resources map is the allowlist: the runner calls no other target. The id of a resource is the name of its file in .zriz/resources, without .json.
You should see
"token-env": "ZRIZ_RUNNER_TOKEN"
If not
No such file or directory: Make the file in the current folder. Then do the command again.
3. Start the runner
Do
Do the commands in the folder that has config.json. The build compiles the runner, so it takes some minutes.
<token>isdata.tokenfrom step 1.
Make the file runner.env with an editor, not with a command. A command puts the token in the shell history. The file has one line:
ZRIZ_RUNNER_TOKEN=<token>
chmod 600 runner.env
git clone https://github.com/ZrizTech/runner
docker build -t zriz-runner runner
docker run -d --name zriz-runner \
--env-file runner.env \
-e ZRIZ_RUNNER_CONFIG=/config/config.json \
-v "$PWD/config.json:/config/config.json:ro" \
zriz-runner
sleep 5
docker logs zriz-runner
Why
The runner is open source, so you build the image from code that you can read. The file runner.env keeps the token out of the shell history and out of config.json.
You should see
runner connected
If not
started: The runner is not connected yet. Wait 10 seconds. Then dodocker logs zriz-runneragain.poll refused: The cloud refused the token. Do step 1 again, and put the new token inrunner.env. Then dodocker rm -f zriz-runneranddocker runagain.poll failed: The runner cannot reachhttps://zriz.io. Permit outbound HTTPS from the host.environment variable ZRIZ_RUNNER_TOKEN is not set:runner.envmust have the line of the token.docker runmust have--env-file runner.env.missing environment variable: A${NAME}of the config has no value. Add its line torunner.env.config:: The line names the fault inconfig.json. Correct the file.is already in use by container: Dodocker rm -f zriz-runner. Then dodocker runagain.
4. Check that a run uses it
Do
Do the command in the folder of your service. The project must have the file .zriz/environments/<env>.json.
<env>is the environment from step 1.
zz run hello --env <env>
Why
zz run needs a runner that serves the environment of the run.
You should see
"status":"pass"
If not
no environment file: Make the file that the message names. Pipelines gives its format.runner-unavailable: No runner serves this environment. Dozz runners, and compareenvof each runner.no runner connected: The runner is down. Dodocker logs zriz-runner."status":"fail": Do the command inhintsof the reply. It shows the failed step.
A runner on this machine
zz runners init writes the files of a runner for this machine. With --start, it also starts the runner. Docker must be installed.
zz runners init --start
The command has these options:
--env <env>is the environment that the runner serves. The default isdefault-envof.zriz/project.json.--name <name>is the name of the runner. The default is the project name.--startstarts the runner with Docker Compose and waits until it is connected. It waits 60 seconds at most.
The command writes three files in the folder .zriz/runner/:
| File | Content |
|---|---|
config.json |
The cloud URL, the name of the token variable, and one entry for each http, sql, and browser resource. |
runner.env |
The runner token and each value that the env file lists in sensitive. The file mode is 600. |
compose.yml |
The service of the runner. The service of the worker, when the project has a browser resource. |
The command adds the line runner/ to .zriz/.gitignore. The folder belongs to one machine and is not committed.
The command follows these rules:
- The command makes the token with the call of
zz runners token create. The token goes only intorunner.env. The output never shows it. - The command writes only a file that does not exist. It never changes a file that exists.
- The command makes a token only when it writes
runner.env. A second call writes nothing and makes no token. - The command never prompts.
- A
cliresource is not inconfig.json, because the runner config needs the path of each command. A warning names the resource. - A
browserresource takes its URL from the env value<RESOURCE>_URL. The name is in capitals, with_for-. Example:SHOP_WEB_URLfor the resourceshop-web. - A host
localhostor127.0.0.1becomeshost.docker.internalinconfig.json. A warning tells you that the service must listen on an address that a container can get to.
The image tag in compose.yml is fixed in the zz release. This release writes ghcr.io/zriztech/runner:0.2.0, and ghcr.io/zriztech/worker:0.2.0 for a browser resource.
Check that the tag exists before you rely on it. The runner README tells how to build the images from source.
To start the runner without --start, do this:
docker compose -f .zriz/runner/compose.yml up -d
To stop the runner, do this:
docker compose -f .zriz/runner/compose.yml down
If the start fails, the error is runner-start-failed. The files stay, and a second call does not write them again. The field data.step tells which part failed:
data.step |
Meaning | Fix |
|---|---|---|
docker-missing |
The system has no program docker. |
Install Docker. Then do zz runners init --start. Or start the runner by hand with the commands above. |
compose-failed |
docker compose ended with an exit code that is not 0. |
Do docker compose -f .zriz/runner/compose.yml logs. Correct the fault. Then do zz runners init --start. |
not-connected |
The runner did not connect in 60000 ms. | Do docker compose -f .zriz/runner/compose.yml logs runner. Check the token and the network. Then do zz runners. |
Secrets and databases in the config
The config holds the names of secrets, not their values. This example has an HTTP resource with one secret, and a Postgres database.
config.json:
{
"cloud": { "url": "https://zriz.io", "token-env": "ZRIZ_RUNNER_TOKEN" },
"resources": {
"shop": {
"type": "http",
"base-url": "https://staging.shop.example",
"secrets": ["SHOP_API_KEY"]
},
"shop-db": {
"type": "sql",
"connection": "postgres://<user>:${SHOP_DB_PASSWORD}@<host>:5432/<database>",
"read-only": true
}
}
}
- The runner fills
${NAME}incloud.url,base-url,connection, andoriginsfrom its environment at the start. Put each value inrunner.env, one line for each name. secretslists names of environment variables. A step can use them as${NAME}in the headers, the body, and the query parameters of that HTTP resource.- Do not put the token variable in
secrets. The runner refuses that config. cloud.urlmust behttps.- All the projects of an org share its runner. Thus a resource id must be unique in the org:
ms-a-db, notdb. - For a database, use a user that can only read: Create a read-only Postgres user.
| Database | Form of connection |
|---|---|
| Postgres | postgres://user:pass@host:5432/db |
| MySQL | user:pass@tcp(host:3306)/db |
What can the runner do? gives the rules that the runner obeys.
Browser and command-line tests
A browser resource drives a web page. A cli resource runs a command that the config permits. The two types run in the worker, a second container that runs next to the runner.
The worker holds no secret of the runner. The file ops/docker-compose.yml of the runner repository starts the runner and the worker together.
The runner README gives the config keys of the two types.