Home/Templates/Point Argo CD at a repository
Point Argo CD at a repository
The first Application, which then manages every other application from Git.
Argo CD
Why bother
Once Argo CD is installed it does nothing until you tell it which repository to follow. The usual mistake is adding applications one at a time through the web interface, which puts the list of what should be running in the one place that is not version controlled. One Application that points at a folder of Applications fixes that on day one.
Add a folder to the repository. It appears in Argo CD without anyone running a command.
Set it up
Pick your platform, then open the level you want. Each level is complete on its own. Read the comments in the files as you go. Anything with a real consequence is explained on the line where it happens.
Argo CD. An Application pointing at a folder of Applications
What you need first
- Argo CD already installed in the cluster.
- A repository Argo CD can read, with a protected branch you are happy to have deploy itself.
What it creates
- One Application in the argocd namespace
- An AppProject fencing which repositories, namespaces, and cluster-wide kinds are allowed (Standard)
- An ApplicationSet that turns each folder under apps/ into an application (Standard)
The code
Quick startDraft
# gitops-bootstrap / kubernetes / t0 "Quick start"
#
# One Application that watches a folder of Applications. Add a file to that
# folder in Git and it appears in the cluster. This is usually called the
# app-of-apps pattern.
#
# Change repoURL to your repository, then:
# kubectl apply -f bootstrap.yaml
#
# Verify:
# Add a new Application file to apps/ in the repository and push.
# It shows up in Argo CD within about three minutes, with nobody running
# a command.
#
# Expected repository layout:
# apps/
# monitoring.yaml <- each of these is an Argo CD Application
# logging.yaml
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: bootstrap
namespace: argocd
# Without this finalizer, deleting the bootstrap Application leaves every
# application it created running and unmanaged.
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/your-org/your-cluster-config.git # change me
# Pin to a branch you protect. Pointing at HEAD means any push to any
# branch can change production.
targetRevision: main
path: apps
directory:
recurse: true
destination:
# This exact string means the cluster Argo CD is running in.
server: https://kubernetes.default.svc
namespace: argocd
syncPolicy:
automated:
# Remove from the cluster anything that was deleted from Git.
prune: true
# Undo changes made directly on the cluster. This is the setting that turns
# Argo CD from a deployment tool into a control.
selfHeal: true
syncOptions:
- CreateNamespace=true
StandardDraft
# One definition that creates an Application for every folder under apps/.
# Adding a folder to the repository adds an application. Deleting the folder
# removes it. Nothing is clicked.
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: platform-apps
namespace: argocd
spec:
# This only matters if the ApplicationSet itself is deleted. True means the
# platform services keep running, unmanaged, until someone decides what to do.
# False means deleting one object removes every platform service at once.
syncPolicy:
preserveResourcesOnDeletion: true
generators:
- git:
repoURL: https://github.com/your-org/your-cluster-config.git
revision: main
directories:
- path: apps/*
# Folders starting with an underscore hold shared pieces that other
# applications include, so they get no Application of their own.
- path: apps/_*
exclude: true
template:
metadata:
name: "{{path.basename}}"
finalizers:
- resources-finalizer.argocd.argoproj.io
annotations:
# Only commits touching this application's own folder trigger a
# refresh, so a busy repository does not make every app regenerate.
argocd.argoproj.io/manifest-generate-paths: "."
spec:
project: platform
source:
repoURL: https://github.com/your-org/your-cluster-config.git
targetRevision: main
path: "{{path}}"
destination:
server: https://kubernetes.default.svc
namespace: "platform-{{path.basename}}"
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- ServerSideApply=true
retry:
limit: 5
backoff:
duration: 10s
factor: 2
maxDuration: 5m
# Apply both files together:
# kubectl apply -k .
#
# Order matters. The project has to exist before the ApplicationSet that
# references it, and kustomize keeps them in the order listed here.
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: argocd
resources:
- project.yaml
- applicationset.yaml
# labels with includeSelectors false adds the labels without touching selector
# fields. The older commonLabels rewrites selectors too, and selectors on an
# existing Deployment cannot be changed, so the apply fails.
labels:
- pairs:
app.kubernetes.io/managed-by: argocd
owner: platform-engineering
includeSelectors: false
# gitops-bootstrap / kubernetes / t1 "Standard"
#
# An AppProject is a fence. It says which repositories this set of applications
# may come from, which clusters and namespaces it may write to, and which kinds
# of object it may create. Without one, every Application runs with whatever
# Argo CD itself can do, which is usually everything.
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: platform
namespace: argocd
spec:
description: Platform services owned by the infrastructure team.
# Only these repositories. A compromised repository somewhere else cannot
# deploy into this project.
sourceRepos:
- https://github.com/your-org/your-cluster-config.git
- https://falcosecurity.github.io/charts
destinations:
- server: https://kubernetes.default.svc
namespace: platform-*
- server: https://kubernetes.default.svc
namespace: argocd
# Falco runs in its own namespace, as its chart expects.
- server: https://kubernetes.default.svc
namespace: falco
# Cluster-wide objects this project may create. Anything not listed is denied.
#
# Be clear about what this does and does not buy. ClusterRoleBinding is on the
# list because Falco needs one, and anything allowed to create a
# ClusterRoleBinding can bind itself to cluster-admin. What actually protects
# you here is sourceRepos above: only the listed repositories can put anything
# into this project. If a service does not need cluster-wide RBAC, give it a
# project without these two lines.
clusterResourceWhitelist:
- group: ""
kind: Namespace
- group: rbac.authorization.k8s.io
kind: ClusterRole
- group: rbac.authorization.k8s.io
kind: ClusterRoleBinding
- group: apiextensions.k8s.io
kind: CustomResourceDefinition
namespaceResourceBlacklist:
- group: ""
kind: ResourceQuota
- group: ""
kind: LimitRange
# Who can do what. sync is deliberately separate from override, so the people
# who can deploy are not automatically the people who can bypass Git.
roles:
- name: deployer
description: Can sync, cannot change the definition.
policies:
- p, proj:platform:deployer, applications, sync, platform/*, allow
- p, proj:platform:deployer, applications, get, platform/*, allow
groups:
- your-org:platform-engineers
HardenedPlanned
Without the resources-finalizer, deleting the bootstrap Application leaves every application it created still running and managed by nobody. Both templates set it.
An AppProject narrows what an application can create. It does not make ClusterRoleBinding safe. Anything allowed to create one can bind itself to cluster-admin, and Falco needs one. The Standard project allows it, says so in a comment, and relies on the list of permitted repositories to keep strangers out.
How to undo it
kubectl delete on the Application removes it and, because of that finalizer, everything underneath it. Check that is what you meant before you press return.
What it costs
None beyond what the applications themselves use.