Jonas Hietala: GitOps with ArgoCD
This is part 5 of the<br>Modern Kubernetes homelab series.
Now we’re getting to the fun stuff: GitOps.<br>The act of pushing beautifully crafted .yaml files and seeing your Kubernetes cluster get red and stall out is surely what life’s all about.
Install ArgoCD
We have our Kubernetes cluster and now we’re going to bootstrap ArgoCD so it starts syncing from our git repository.<br>Using helm:
helm install argocd \
--repo https://argoproj.github.io/argo-helm \
argo-cd \
--namespace argocd \
--create-namespace \
--version 10.1.4
ArgoCD will by default generate an admin password and you can get it with the argocd tool:
# You can use the password to login to the web.
argocd admin initial-password -n argocd
# Make ArgoCD available at localhost:8123
kubectl port-forward -n argocd svc/argocd-server 8123:80
You can use the argocd CLI to manage ArgoCD but I’m going to prefer the declarative way.
Fixed IP
You can use the port forward method above to access ArgoCD but having a fixed IP is nicer, especially if you want to use the argocd CLI (I only used it when trying to get it all working).
I use a separate service for the IP, which allows me to assign an IP outside of the Cilium load balance pool we setup previously (10.1.4.101–10.1.4.255).<br>It looks like this:
apiVersion: v1
kind: Service
metadata:
name: argocd-server-ip
namespace: argocd
spec:
type: ClusterIP
externalIPs:
- 10.1.4.51
selector:
app.kubernetes.io/name: argocd-server
ports:
- name: http
port: 80
targetPort: 8080
It simply routes 10.1.4.51:80 to the IP of argocd-server and port 8080.<br>We need to apply it:
kubectl apply -f gitops/bootstrap/argocd.yaml
And you should see it assigned and able to visit it on the web:
$ kubectl get svc -n argocd argocd-server-ip
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
argocd-server-ip ClusterIP 10.105.209.116 10.1.4.51 80/TCP 3d18h
Repository
I use a self-hosted Forgejo instance (hosted in a Proxmox LXC) but you can use whatever you want.<br>You need an API token with read permissions.
ArgoCD documents a declarative setup that we’ll use.<br>For repositories we need to create a secret:
kubectl create secret generic home-ops-repo \
--namespace argocd \
--from-literal=type=git \
--from-literal=url=repo-url> \
--from-literal=username=username> \
--from-literal=password=token> \
--dry-run=client -o yaml \
| kubeseal --cert infrastructure/sealed-secrets-cert.pem -o yaml \
> gitops/bootstrap/argocd-repo-secret.yaml
We also need to add the repository label to the generated file so it looks something like this:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: home-ops-repo
namespace: argocd
spec:
encryptedData:
password: ...
type: ...
url: ...
username: ...
template:
metadata:
name: home-ops-repo
namespace: argocd
labels:
argocd.argoproj.io/secret-type: repository
Apply it:
kubectl apply -f gitops/bootstrap/argocd-repo-secret.yaml
And the repo should show up in the UI or argocd repo list.
Initial admin password
Instead of letting ArgoCD generate a random password we can set the initial password explicitly.<br>It’s possible to do it either via the Helm chart or the declarative way, which I’ll continue with.
After generating it with kubeseal the secret should look like this:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: argocd-secret
namespace: argocd
spec:
encryptedData:
admin.password: ...
admin.passwordMtime: ...
server.secretkey: ...
template:
metadata:
labels:
app.kubernetes.io/name: argocd-secret
app.kubernetes.io/part-of: argocd
name: argocd-secret
namespace: argocd
With three keys:
password is a bcrypt hash:
argocd account bcrypt --password 'supersecret'
passwordMtime is the plaintext modification time:
date -u +"%Y-%m-%dT%H:%M:%SZ"
secretkey is a random value:
openssl rand -base64 48
ArgoCD uses server.secretkey to sign session and API tokens, and it will regenerate the key if restarted.<br>I kept getting logged out when I was rebuilding things all over the place and it was annoying so I added this to make it stable.
A file structure
ArgoCD is now installed but at this point I think it’s good to take a step back before we steam ahead.
One of the benefits of ArgoCD is that you can create any kind of file structure and organize the repository any way you want.<br>That’s also one of its problems: if you can do anything it’s hard to decide on a plan forward.
I found the blog post How to Structure Your Argo CD Repositories Using Application Sets<br>illuminating and even though their setup is overkill for my homelab, I simplified their ideas into something that works for me.
The basic idea is to have all applications in their own folders under gitops/apps, and place all bootstrap stuff under gitops/bootstrap.<br>Borrowing the “level” terminology from the above post here’s the basic idea:
Level 1/Level 2bootstrap/Level...