Docker quickstart
In this quick start guide, we will write our first script ran from a Docker container.
Windmill natively supports Python, TypeScript, Go, PHP, Bash or SQL. In some cases where your task requires a complex set of dependencies or is implemented in a non-supported language, Windmill allows running any Docker container through its Bash support.
As a pre-requisite, the host docker daemon needs to be mounted into the worker container. This is done through a simple volume mount: /var/run/docker.sock:/var/run/docker.sock
.
This tutorial covers how to create a simple script through Windmill web IDE. See the dedicated page to Develop Scripts Locally.
Scripts are the basic building blocks in Windmill. They can be run and scheduled as standalone, chained together to create Flows or displayed with a personalized User Interface as Apps.
Scripts consist of 2 parts:
- Code.
- Settings: settings & metadata about the Script such as its path, summary, description, jsonschema of its inputs (inferred from its signature).
When stored in a code repository, those 2 parts are stored separately at <path>.docker
and <path>.script.yaml
.
Below is a simple example of a script built using Bash to run Docker containers from Windmill:
# shellcheck shell=bash
# Bash script that calls docker as a client to the host daemon
# See documentation: https://www.windmill.dev/docs/advanced/docker
msg="${1:-world}"
IMAGE="alpine:latest"
COMMAND="/bin/echo Hello $msg"
# ensure that the image is up-to-date
docker pull $IMAGE
docker run --rm $IMAGE $COMMAND
Kubernetes
If you use kubernetes and would like to run your docker file directly on the kubernetes host, use the following script:
# shellcheck shell=bash
# Bash script that calls docker as a client to the host daemon
# See documentation: https://www.windmill.dev/docs/advanced/docker
msg="${1:-world}"
IMAGE="docker/whalesay:latest"
COMMAND=(sh -c "cowsay $msg")
APISERVER=https://kubernetes.default.svc
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
TOKEN=$(cat ${SERVICEACCOUNT}/token)
CACERT=${SERVICEACCOUNT}/ca.crt
KUBECONFIG_TMP_DIR="$(mktemp -d)"
export KUBECONFIG="${KUBECONFIG_TMP_DIR}/kubeconfig"
trap "rm -rfv ${KUBECONFIG_TMP_DIR}" EXIT
kubectl config set-cluster local --server="${APISERVER}" --certificate-authority="${CACERT}"
kubectl config set-credentials local --token="${TOKEN}"
kubectl config set-context local --cluster=local --user=local --namespace="${NAMESPACE}"
kubectl config use-context local
kubectl run task -it --rm --restart=Never --image="$IMAGE" -- "${COMMAND[@]}"
and use the following additional privileges
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: windmill
name: pod-management
rules:
- apiGroups: ['']
resources: ['pods']
verbs: ['get', 'list', 'watch', 'create', 'update', 'patch', 'delete']
- apiGroups: ['']
resources: ['pods/log']
verbs: ['get', 'list', 'watch']
- apiGroups: ['']
resources: ['pods/attach']
verbs: ['get', 'list', 'watch', 'create', 'update', 'patch', 'delete']
- apiGroups: ['']
resources: ['events']
verbs: ['get', 'list', 'watch']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-management
namespace: windmill
subjects:
- kind: ServiceAccount
name: windmill-chart
namespace: windmill
roleRef:
kind: Role
name: pod-management
apiGroup: rbac.authorization.k8s.io
Setup
To set-up Docker, see:
Use
From the Home page of Windmill App, click +Script
. This will take you to the
first step of script creation: Metadata.
Settings
As part of the settings menu, each script has metadata associated with it, enabling it to be defined and configured in depth.
- Path is the Script's unique identifier that consists of the script's owner, and the script's name. The owner can be either a user, or a group (folder).
- Summary (optional) is a short, human-readable summary of the Script. It
will be displayed as a title across Windmill. If omitted, the UI will use the
path
by default. - Language of the script.
- Description is where you can give instructions through the auto-generated UI to users on how to run your Script. It supports markdown.
- Script kind: Action (by default), Trigger, Approval or Error handler. This acts as a tag to filter appropriate scripts from the flow editor.
This menu also has additional settings on Runtime, Generated UI and Triggers.
Now click on the code editor on the left side.
Code
Windmill provides an online editor to work on your Scripts. The left-side is the editor itself. The right-side previews the UI that Windmill will generate from the Script's signature - this will be visible to the users of the Script. You can preview that UI, provide input values, and test your script there.
As we picked Docker
for this example, Windmill provided some Bash
boilerplate. Let's take a look:
# shellcheck shell=bash
# Bash script that calls docker as a client to the host daemon
# See documentation: https://www.windmill.dev/docs/advanced/docker
msg="${1:-world}"
IMAGE="alpine:latest"
COMMAND="/bin/echo Hello $msg"
# ensure that the image is up-to-date
docker pull $IMAGE
docker run --rm $IMAGE $COMMAND
msg
is just a normal Bash variable. It can be used to pass arguments to the script. This syntax is the standard Bash one to assign default values to parameters.
docker run --rm <image> <command>
--rm is so that the container dispose itself after being executed. It helps unpollute the host.
The image is the docker image to run. It can be any image available on docker hub or any private registry. It can also be a local image.
The command is the command to run inside the container. It can be any command available in the image.
It is just a Bash script so it will behave exactly the same as a local command or if running this as an ssh command on the host. As a consequence, you can use any strategy to cache docker images or handle authentication.
Do not use the daemon mode -d
otherwise the script will immediately return while the container continue to run in the background. However, in some cases, that might be what you want.
Like any Bash script, it will return the last line of the stdout. So be sure to print the return value from your command if you'd like to use it as a result.
Instant preview & testing
Look at the UI preview on the right: it was updated to match the input
signature. Run a test (Ctrl
+ Enter
) to verify everything works.
Now let's go to the last step: the "Generated UI" settings.
Generated UI
From the Settings menu, the "Generated UI" tab lets you customize the script's arguments.
The UI is generated from the Script's main function signature, but you can add additional constraints here. For example, we could use the Customize property
: add a regex by clicking on Pattern
to make sure users are providing a name with only alphanumeric characters: ^[A-Za-z0-9]+$
. Let's still allow numbers in case you are some tech billionaire's kid.
We're done! Save your script. Note that Scripts are versioned in Windmill, and each script version is uniquely identified by a hash.
Run!
Now let's look at what users of the script will do. Click on the Deploy button to load the script. You'll see the user input form we defined earlier.
Fill in the input field, then hit "Run". You should see a run view, as well as your logs. All script runs are also available in the Runs menu on the left.
You can also choose to run the script from the CLI with the pre-made Command-line interface call.
JSON result
The last line returned by the script will be the string result. To use a json result instead, output your result in ./result.json
and it will be automatically picked-up and considered as the JSON result for Bash and Powershell scripts.
What's next?
This script is a minimal working example, but there's a few more steps that can be useful in a real-world use case:
- Pass variables and secrets to a script.
- Connect to resources.
- Trigger that script in many ways.
- Compose scripts in Flows or Apps.
- You can share your scripts with the community on Windmill Hub. Once submitted, they will be verified by moderators before becoming available to everyone right within Windmill.
Scripts are immutable and there is an hash for each deployment of a given script. Scripts are never overwritten and referring to a script by path is referring to the latest deployed hash at that path.
For each script, a UI is autogenerated from the jsonchema inferred from the script signature, and can be customized further as standalone or embedded into rich UIs using the App builder.
In addition to the UI, sync and async webhooks are generated for each deployment.