Episode 0x0D: Argo Workflows!
Table of Contents
NOTE: Many commands in this post make use of specific constants tied to my own setup. Make sure to tailor these to your own needs. These examples should serve as a guide, not as direct instructions to copy and paste.
NOTE: Check out the final code at homelab repo on my Github account.
Introduction
In the previous episode, we created our own private container registry. Now, our next step is to install Argo Workflows, a container-native workflow engine designed for orchestrating jobs on Kubernetes. We’ll utilize this for our CI pipeline.
Installation
ArgoCD Application
Similar to other applications, we can install Argo Workflows as an ArgoCD application. However, this time we will use community-managed Helm charts. Add the following configuration to your apps.yaml
file:
- apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: argo-workflows
namespace: argocd
spec:
destination:
namespace: argo
server: https://kubernetes.default.svc
project: default
source:
chart: argo-workflows
repoURL: https://argoproj.github.io/argo-helm
targetRevision: 0.41.8
helm:
releaseName: argo-workflows
values: |
server:
ingress:
enabled: true
hosts:
- argo.<your-domain>.com
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
acme.cert-manager.io/http01-edit-in-place: "true"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
tls:
- hosts:
- argo.<your-domain>.com
secretName: argo.<your-domain>.com-tls
syncPolicy:
automated:
prune: true
syncOptions:
- CreateNamespace=true
Apply these configurations with:
kubectl apply -f app.yaml
This command installs Argo Workflows for you and sets up the necessary ingress configuration. Ensure you create an A
record in your domain’s DNS for argo.<your-domain>.com
.
Accessing UI
Navigate to argo.<your-domain>.com
in your browser. You should see the login page:
To log in, you need a Bearer token. Create one by applying the following secret configuration:
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
namespace: argo
name: argo.service-account-token
annotations:
kubernetes.io/service-account.name: argo-workflows-server
type: kubernetes.io/service-account-token
EOF
Retrieve the token with:
ARGO_TOKEN="Bearer $(kubectl get secret argo.service-account-token -n argo -o=jsonpath='{.data.token}' | base64 --decode)"
echo $ARGO_TOKEN
Using this token, you should be able to log in to the Argo Workflows UI.
Argo Workflows Configuration
Argo Workflows comes with a CLI, which is the preferred method for interaction. Install it by following the official documentation.
Use Minio As Artifact Repository
Workflow steps in Argo can produce output artifacts. We can store these artifacts in our Minio object storage.
First, log into Minio and create an access token by navigating to the access keys section:
Add the access key to the mc
CLI using:
mc alias set workflow-ak http://minio.api.<your-domain>.com <access-key> <secret>
Create a bucket named artifacts-repo
using the Minio UI:
mc mb workflow-ak/artifacts-repo
Define this repository configuration in Argo Workflows:
k apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
namespace: argo
name: artifacts-repository
annotations:
workflows.argoproj.io/default-artifact-repository: default-v1-s3-artifact-repository
data:
default-v1-s3-artifact-repository: |
s3:
bucket: artifacts-repo
endpoint: minio-svc.minio:9000
insecure: true
accessKeySecret:
name: minio-workflow-ak-sec
key: accessKey
secretKeySecret:
name: minio-workflow-ak-sec
key: secretKey
region: us-east-1
EOF
Refer to Storing Artifacts for subsequent use of this artifact repository in workflows.
NOTE the annotation in the above config is how we introduce this artifact repository to Argo Workflows.
Test Argo Workflows
To test Argo Workflows, start by creating a test namespace and set your current context to it:
kubectl create namespace test-workflows
kubectl config set-context --current --namespace=test-workflows
Then follow the next sub section to setup a service account
Configuring Workflow Service Account
By default, workflows in a namespace use the default service account, which generally lacks sufficient privileges. Define a role with the necessary permissions:
kubectl create role argo-workflows-admin \
--namespace test-workflows \
--verb=list --verb=get --verb=watch --verb=patch --verb=create --verb=update --verb=delete \
--resource=workflowtaskresults.argoproj.io
Bind this role to the default service account:
kubectl create rolebinding default-argo-workflows-admin-rb --role=argo-workflows-admin --serviceaccount=test-workflows:default -n test-workflows
Running a “Hello World” Workflow
Deploy your first simple workflow:
kubectl apply -f - << EOF
apiVersion: argoproj.io/v1alpha1
kind: Workflow # new type of k8s spec
metadata:
namespace: test-workflows
name: first-workflow
spec:
entrypoint: whalesay # invoke the whalesay template
templates:
- name: whalesay # name of the template
container:
image: docker/whalesay
command: [ cowsay ]
args: [ "hello world" ]
resources: # limit the resources
limits:
memory: 32Mi
cpu: 100m
EOF
Check the workflow logs:
argo logs -n test-workflows @latest --follow
# Output will look like:
# first-workflow: _____________
# first-workflow: < hello world >
# first-workflow: -------------
# first-workflow: \
# first-workflow: \
# first-workflow: \
# first-workflow: ## .
# first-workflow: ## ## ## ==
# first-workflow: ## ## ## ## ===
# first-workflow: /""""""""""""""""___/ ===
# first-workflow: ~~~ {~~ ~~~~ ~~~ ~~~~ ~~ ~ / ===- ~~~
# first-workflow: \______ o __/
# first-workflow: \ \ __/
# first-workflow: \____\______/
# first-workflow: time="2024-07-04T21:58:04.984Z" level=info msg="sub-process exited" argo=true error="<nil>"
Also from UI:
Storing Artifacts
To use the artifact repository in every workflow namespace:
Create the
minio-workflow-ak-sec
Secret:k create secret generic -n test-workflows minio-workflow-ak-sec --dry-run=client --from-literal="accessKey=<minio-access-key>" --from-literal="secretKey=<minio-secret-key>" --output=yaml | kubeseal -o yaml | k apply -f -
Create a workflow using this configuration (save it as
test.yaml
):
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
namespace: test-workflows
generateName: test-artifacts-
spec:
artifactRepositoryRef:
configMap: artifacts-repository
entrypoint: artifact-example
templates:
- name: artifact-example
steps:
- - name: generate-artifact
template: whalesay
- - name: consume-artifact
template: print-message
arguments:
artifacts:
# bind message to the hello-art artifact
# generated by the generate-artifact step
- name: message
from: "{{steps.generate-artifact.outputs.artifacts.hello-art}}"
- name: whalesay
container:
image: docker/whalesay:latest
command: [sh, -c]
args: ["cowsay hello world | tee /tmp/hello_world.txt"]
outputs:
artifacts:
# generate hello-art artifact from /tmp/hello_world.txt
# artifacts can be directories as well as files
- name: hello-art
path: /tmp/hello_world.txt
- name: print-message
inputs:
artifacts:
# unpack the message input artifact
# and put it at /tmp/message
- name: message
path: /tmp/message
container:
image: alpine:latest
command: [sh, -c]
args: ["cat /tmp/message"]
NOTE: To reference the artifact repository in your workflows this part is essential
spec:
artifactRepositoryRef:
configMap: artifact-repositories
Submit the workflow with:
argo submit -n test-workflows test.yaml
You can verify the workflow execution from the UI:
Conclusion
We have successfully installed and set up Argo Workflows. However, you should explore it further by creating various types of workflows, templates, and cron workflows. I highly recommend following the official walkthrough to deepen your understanding. Take at least two days of practice here.
In the next episode, we will explore building a static blog with GoHugo! Stay tuned! 🚀