Argo Workflows doesn’t actually "run" scripts in the way you might think; it orchestrates containers that execute them.
Let’s watch a simple script execute.
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: inline-script-example-
spec:
entrypoint: main
templates:
- name: main
dag:
tasks:
- name: hello-world
template: hello-template
- name: goodbye
template: goodbye-template
dependencies: [hello-world]
- name: hello-template
script:
image: alpine:latest
command: [sh]
source: |
echo "Hello, world!"
sleep 5
- name: goodbye-template
script:
image: alpine:latest
command: [sh]
source: |
echo "Goodbye, cruel world!"
When this workflow runs, Argo spins up an alpine:latest container for the hello-template task. Inside that container, it executes the sh command, piping the source script into its standard input. The echo "Hello, world!" command prints to the container’s logs, and sleep 5 keeps the container alive for a few seconds before it exits. Once hello-world completes successfully, the goodbye-template task starts, performing a similar sequence for its script.
The core problem Argo Workflows solves here is the management and orchestration of containerized tasks. Instead of manually building Docker images for every small script or shell command, you can define them directly within the workflow YAML. This dramatically reduces overhead for repetitive or small, self-contained operations. The script template type is a specialized form of the container template, designed to simplify the process of running shell scripts.
Internally, when Argo encounters a script template, it constructs a container template behind the scenes. It takes the image, command, and args you provide, and then it automatically adds the script source to the container’s command arguments. For example, if your script is echo "hello", Argo effectively tells the container runtime to execute sh -c 'echo "hello"' (or similar, depending on the specified command). The command field specifies the executable (like sh or bash), and the source is passed as an argument to that executable, often via the -c flag.
The source field is where the magic happens. It’s a multi-line string containing your script. Argo takes this string and typically writes it to a temporary file within the container, then executes that file. Alternatively, for simpler commands, it might pipe the script directly into the standard input of the interpreter specified in command. The command field is crucial; it dictates how your script is executed. If you specify command: [python], Argo will expect your source to be Python code. If you specify command: [bash], it expects Bash. If you omit command, Argo defaults to sh.
One subtle but powerful aspect is how Argo handles dependencies and context. You can pass environment variables from one step to another, or even use the output of a previous script as input to a later one. For instance, if your hello-template script produced an artifact or a specific output value, you could configure goodbye-template to read that value. This is achieved through output parameters and artifact passing mechanisms, allowing complex data flow between script steps.
The resources field within a script template is also directly passed to the underlying container. This means you can specify CPU and memory limits for your script execution, ensuring that even simple scripts don’t consume excessive cluster resources. For example, resources: { limits: { cpu: "100m", memory: "128Mi" } } ensures the container running your script adheres to these constraints.
The next concept to explore is how to manage more complex dependencies between script steps, particularly when dealing with parallel execution and conditional logic.