6 min read | by Jordi Prats
Argo Workflows is a open-source container-native workflow engine designed to run containerized jobs in Kubernetes clusters, similar to tekton.
It uses DAGs (Directed Acyclic Graphs) or step-based workflows that will run each in a container.
First, we need to set the version we want to install, we can do it by setting the environment variable ARGO_WORKFLOWS_VERSION to the desired version:
export ARGO_WORKFLOWS_VERSION="v3.5.11"
Then we can install it to the argo namespace using the following commands:
kubectl create namespace argo kubectl apply -n argo -f "https://github.com/argoproj/argo-workflows/releases/download/${ARGO_WORKFLOWS_VERSION}/quick-start-minimal.yaml"
We can now wait for the pods to be up and running, keep an eye on the argo-server pod:
$ kubectl get pods NAME READY STATUS RESTARTS AGE argo-server-76c65cd446-2lc82 0/1 Running 0 30s httpbin-7b48b49985-fg7mb 1/1 Running 0 30s minio-68dc5544c4-7lbsl 1/1 Running 0 30s workflow-controller-68d7b854cc-6spmj 1/1 Running 0 30s $ get pods NAME READY STATUS RESTARTS AGE argo-server-76c65cd446-2lc82 1/1 Running 0 96s httpbin-7b48b49985-fg7mb 1/1 Running 0 96s minio-68dc5544c4-7lbsl 1/1 Running 0 96s workflow-controller-68d7b854cc-6spmj 1/1 Running 0 96s
We can access the Argo Workflows GUI by port-forwarding the argo-server service:
kubectl -n argo port-forward service/argo-server 2746:2746
We'll be able to access the GUI by visiting the localhost on port 2746. Make sure to explicitly set the protocol to https. So, navigate to https://localhost:2746 in your browser.
We are going to create a simple workflow that will run a container that will print hello world using the docker/whalesay image. This would be the yaml definition for the workflow:
apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: generateName: whalesay- spec: entrypoint: whalesay templates: - name: whalesay container: image: docker/whalesay:latest command: [cowsay] args: ["hello world"]
Bear in mind that since we want to be able to run the workflow multiple times, we need to set the generateName field in the metadata to a value that will be unique for each run. This also means that we'll need to use kubectl create instead of kubectl apply to create the workflow:
$ kubectl create -f hello-world.yaml ; kubectl get workflow -w workflow.argoproj.io/whalesay-gsmmr created NAME STATUS AGE MESSAGE whalesay-gsmmr Running 1s whalesay-gsmmr Succeeded 10s
As soon as it finishes, we can check the logs of the pod to see the output:
$ kubectl get pods NAME READY STATUS RESTARTS AGE argo-server-76c65cd446-2lc82 1/1 Running 0 49m httpbin-7b48b49985-fg7mb 1/1 Running 0 49m minio-68dc5544c4-7lbsl 1/1 Running 0 49m whalesay-gsmmr 0/2 Completed 0 28s workflow-controller-68d7b854cc-6spmj 1/1 Running 0 49m $ kubectl logs whalesay-gsmmr time="2024-10-21T04:04:24.975Z" level=info msg="capturing logs" argo=true _____________ < hello world > ------------- \ \ \ ## . ## ## ## == ## ## ## ## === /""""""""""""""""___/ === ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~ \______ o __/ \ \ __/ \____\______/ time="2024-10-21T04:04:25.976Z" level=info msg="sub-process exited" argo=true error="<nil>"
We are now going to create a multi-step workflow that will generate a random number and create a configmap with that number.
To do so, first we'll need to assign additional permissions to the default service account in the argo namespace. We can do so by creating the following role and role binding that will grant the necessary permissions:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: argo name: configmap-manager rules: - apiGroups: [""] resources: ["configmaps"] verbs: ["create", "get", "list", "watch", "update", "delete"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: configmap-manager-binding namespace: argo subjects: - kind: ServiceAccount name: default namespace: argo roleRef: kind: Role name: configmap-manager apiGroup: rbac.authorization.k8s.io
We can apply the above yaml file to create both objects:
$ kubectl apply -f sa-cm.yaml role.rbac.authorization.k8s.io/configmap-manager created rolebinding.rbac.authorization.k8s.io/configmap-manager-binding created
Once we have this in place, we can create the workflow that will generate a random number and create a configmap with that number. Please notice how we are using the generate-and-create entrypoint that will run two steps: generate-random and create-configmap. The generate-random step will run a python script that will generate a random number between 1 and 100, while the create-configmap step will create a configmap with that number:
apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: generateName: random-configmap- spec: entrypoint: generate-and-create templates: - name: generate-and-create steps: - - name: generate-random-number template: generate-random - - name: create-configmap template: create-configmap arguments: parameters: - name: random-number value: "{{steps.generate-random-number.outputs.result}}" - name: generate-random script: image: python:3.9 command: [python] source: | import random random_number = random.randint(1, 100) print(random_number) outputs: result: "{{outputs.result}}" - name: create-configmap inputs: parameters: - name: random-number container: image: bitnami/kubectl:latest command: [sh, -c] args: - | kubectl create configmap random-configmap \ --from-literal=random-number={{inputs.parameters.random-number}} \ -n argo
We can now create the workflow using the following command:
$ kubectl create -f random-configmap.yaml ; kubectl get workflow -w workflow.argoproj.io/random-configmap-kg8xk created NAME STATUS AGE MESSAGE whalesay-gsmmr Succeeded 20m random-configmap-kg8xk Running 0s random-configmap-kg8xk Running 10s random-configmap-kg8xk Succeeded 20s
Once it finishes, we can check the configmap to see the random number that was generated:
$ kubectl get cm random-configmap NAME DATA AGE random-configmap 1 40s $ kubectl get cm random-configmap -o yaml apiVersion: v1 data: random-number: "34" kind: ConfigMap metadata: creationTimestamp: "2024-10-21T04:24:36Z" name: random-configmap namespace: argo resourceVersion: "424750" uid: 6e00ce29-b3b9-4c43-a3e2-33f12106d743
Checking the pods, we can see how it ran the two steps in different Pods:
$ kubectl get pods NAME READY STATUS RESTARTS AGE argo-server-76c65cd446-2lc82 1/1 Running 0 71m httpbin-7b48b49985-fg7mb 1/1 Running 0 71m minio-68dc5544c4-7lbsl 1/1 Running 0 71m random-configmap-kg8xk-create-configmap-2480388875 0/2 Completed 0 2m39s random-configmap-kg8xk-generate-random-1513880492 0/2 Completed 0 2m49s whalesay-gsmmr 0/2 Completed 0 22m workflow-controller-68d7b854cc-6spmj 1/1 Running 0 71m
In the status field of the workflow, we can see the inputs and outputs of the steps that were run:
apiVersion: argoproj.io/v1alpha1 kind: Workflow metadata: annotations: workflows.argoproj.io/pod-name-format: v2 creationTimestamp: "2024-10-21T04:24:23Z" generateName: random-configmap- generation: 4 labels: workflows.argoproj.io/completed: "true" workflows.argoproj.io/phase: Succeeded name: random-configmap-kg8xk namespace: argo resourceVersion: "424770" uid: 7488c865-e03d-449a-bef0-d9d27669c529 spec: (...) status: (...) finishedAt: "2024-10-21T04:24:43Z" nodes: random-configmap-kg8xk: (...) random-configmap-kg8xk-1513880492: (...) outputs: artifacts: - name: main-logs s3: key: random-configmap-kg8xk/random-configmap-kg8xk-generate-random-1513880492/main.log exitCode: "0" result: "34" (...) random-configmap-kg8xk-2480388875: (...) inputs: parameters: - name: random-number value: "34" (...) random-configmap-kg8xk-3320070688: (...) random-configmap-kg8xk-3387034069: (...) phase: Succeeded progress: 2/2 resourcesDuration: cpu: 0 memory: 4 startedAt: "2024-10-21T04:24:23Z" taskResultsCompletionStatus: random-configmap-kg8xk-1513880492: true random-configmap-kg8xk-2480388875: true
Posted on 22/10/2024