refactor(argocd)!: merge bootstrap and system

This is a breaking change and requires cluster rebuild (carefully
replacing the ApplicationSets may should work but I didn't bother at the
current alpha stage):

- ApplicationSets are merged into a single root one
  to use the progressive sync feature when it's ready.
- Switched to server side apply to avoid CRDs not ready issues.

Also replace the apply script with Ansible, since the Ansible Helm
dependency update feature was released.
This commit is contained in:
Khue Doan 2024-04-17 15:21:10 +07:00
parent 6c294a5d6a
commit 8d00d55eb1
18 changed files with 157 additions and 251 deletions

View File

@ -5,7 +5,7 @@
KUBECONFIG = $(shell pwd)/metal/kubeconfig.yaml
KUBE_CONFIG_PATH = $(KUBECONFIG)
default: metal bootstrap external smoke-test post-install clean
default: metal system external smoke-test post-install clean
configure:
./scripts/configure
@ -14,8 +14,8 @@ configure:
metal:
make -C metal
bootstrap:
make -C bootstrap
system:
make -C system
external:
make -C external
@ -48,10 +48,6 @@ test:
clean:
docker compose --project-directory ./metal/roles/pxe_server/files down
dev:
make -C metal cluster env=dev
make -C bootstrap
docs:
mkdocs serve

View File

@ -1,15 +0,0 @@
.POSIX:
default: namespace argocd root
namespace:
kubectl create namespace argocd --dry-run=client --output=yaml \
| kubectl apply -f -
.PHONY: argocd
argocd:
cd argocd && ./apply.sh
.PHONY: root
root:
cd root && ./apply.sh

View File

@ -1,18 +0,0 @@
#!/bin/sh
VALUES="values.yaml"
kubectl get ingress argocd-server --namespace argocd \
|| VALUES="values-seed.yaml"
helm template \
--dependency-update \
--include-crds \
--namespace argocd \
--values "${VALUES}" \
argocd . \
| kubectl apply -n argocd -f -
kubectl -n argocd wait --timeout=60s --for condition=Established \
crd/applications.argoproj.io \
crd/applicationsets.argoproj.io

View File

@ -1,12 +0,0 @@
argo-cd:
server:
metrics: &metrics
enabled: false
serviceMonitor:
enabled: false
controller:
metrics: *metrics
repoServer:
metrics: *metrics
redis:
metrics: *metrics

View File

@ -1,31 +0,0 @@
argo-cd:
global:
domain: argocd.khuedoan.com
configs:
params:
server.insecure: true
controller.diff.server.side: true
cm:
resource.ignoreResourceUpdatesEnabled: true
resource.customizations.ignoreResourceUpdates.all: |
jsonPointers:
- /status
server:
ingress:
enabled: true
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
tls: true
metrics: &metrics
enabled: true
serviceMonitor:
enabled: true
dex:
enabled: false
controller:
metrics: *metrics
repoServer:
metrics: *metrics
redis:
metrics: *metrics

View File

@ -1 +0,0 @@
# TODO convert ./argocd/apply.sh and ./root/apply.sh to Ansible playbook

View File

@ -1,3 +0,0 @@
apiVersion: v2
name: root
version: 0.0.0

View File

@ -1,13 +0,0 @@
#!/bin/sh
VALUES="values.yaml"
kubectl get ingress gitea --namespace gitea \
|| VALUES="values-seed.yaml"
helm template \
--include-crds \
--namespace argocd \
--values "${VALUES}" \
argocd . \
| kubectl apply -n argocd -f -

View File

@ -1,41 +0,0 @@
{{- range $index, $stack := .Values.stacks }}
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: {{ $stack.name }}
namespace: {{ $.Release.Namespace }}
spec:
generators:
- git:
repoURL: {{ $.Values.gitops.repo }}
revision: {{ $.Values.gitops.revision }}
directories:
- path: {{ $stack.name }}/*
template:
metadata:
name: '{{ `{{path.basename}}` }}'
spec:
destination:
name: in-cluster
namespace: '{{ default `{{path.basename}}` $stack.namespace }}'
project: default # TODO
source:
repoURL: {{ $.Values.gitops.repo }}
path: '{{ `{{path}}` }}'
targetRevision: {{ $.Values.gitops.revision }}
syncPolicy:
automated:
prune: true
selfHeal: true
retry:
limit: 10
backoff:
duration: 1m
factor: 2
maxDuration: 16m
syncOptions:
- CreateNamespace=true
- ApplyOutOfSyncOnly=true
- ServerSideApply=true
{{- end }}

View File

@ -1,2 +0,0 @@
gitops:
repo: https://github.com/khuedoan/homelab

View File

@ -1,9 +0,0 @@
gitops:
repo: http://gitea-http.gitea:3000/ops/homelab
revision: master
stacks:
- name: bootstrap
namespace: argocd # Override default value
- name: system
- name: platform
- name: apps

View File

@ -10,8 +10,6 @@
|--------------| +------------+
| ./system |- - - -| ./external |
|--------------| +------------+
| ./bootstrap |
|--------------|
| ./metal |
|--------------|
| HARDWARE |
@ -21,7 +19,6 @@
Main components:
- `./metal`: bare metal management, install Linux and Kubernetes
- `./bootstrap`: GitOps bootstrap with ArgoCD
- `./system`: critical system components for the cluster (load balancer, storage, ingress, operation tools...)
- `./platform`: essential components for service hosting platform (git, build runners, dashboards...)
- `./apps`: user facing applications
@ -41,15 +38,11 @@ Everything is automated, after you edit the configuration files, you just need t
- Create an ephemeral, stateless PXE server
- Install Linux on all servers in parallel
- Build a Kubernetes cluster (based on k3s)
- (2) Build the `./bootstrap` layer:
- Install ArgoCD
- Configure the root app to manage other layers (and also manage itself)
From now on, ArgoCD will do the rest:
- (3) Build the `./system` layer (storage, networking, monitoring, etc)
- (4) Build the `./platform` layer (Gitea, Grafana, SSO, etc)
- (5) Build the `./apps` layer: (Syncthing, Jellyfin, etc)
- (2) Bootstrap the `./system` layer:
- Install ArgoCD and the root app to manage itself and other layers, from now on ArgoCD will do the rest
- Install the remaining components (storage, monitoring, etc)
- (3) Build the `./platform` layer (Gitea, Grafana, SSO, etc)
- (4) Deploy applications in the `./apps` layer
```mermaid
flowchart TD
@ -57,11 +50,8 @@ flowchart TD
pxe[PXE Server] -.-> linux[Fedora Server] --> k3s
end
subgraph bootstrap[./bootstrap]
argocd[ArgoCD] --> rootapp[Root app]
end
subgraph system[./system]
argocd[ArgoCD and root app]
nginx[NGINX]
rook-ceph[Rook Ceph]
cert-manager
@ -79,96 +69,18 @@ flowchart TD
cloudflare -.-> external-dns
cloudflare -.-> cloudflared
subgraph platform
subgraph platform[./platform]
Gitea
Woodpecker
Grafana
end
subgraph apps
subgraph apps[./apps]
homepage[Homepage]
jellyfin[Jellyfin]
matrix[Matrix]
paperless[Paperless]
end
make[Run make] -- 1 --> metal -- 2 --> bootstrap -. 3 .-> system -. 4 .-> platform -. 5 .-> apps
```
Below is the pseudo code for the entire process, you don't have to read it right now, but it will be handy for debugging.
??? detailed "Detailed provisioning flow"
```
Human run make:
build ./metal:
install the OS:
download the installer image and extract it
create a PXE server on the controller using Docker Compose:
DHCP server
TFTP server
HTTP server
create init config for each machine
turn the machines on via WoL
the machines boot:
select network boot automatically
broadcast DHCP request
DHCP server reply:
machine IP
TFTP server (next-server) IP
get boot files from TFTP server
GRUB
GRUB config with URL to init config based on MAC address
kernel
initrd
boot to the kernel
download from HTTP server:
init config from the URL in GRUB config
remaining files required to boot
install the OS based on the init config:
configure the system
remaining files required to install
reboot to the new OS
controller see the machines are ready
build a Kubernetes cluster:
download k3s binary
generate cluster token
copy k3s config files
enable k3s service and form a cluster
create KUBECONFIG file
install Cilium
build ./bootstrap:
install ArgoCD:
apply helm chart
wait for status
install root app:
select values file:
if Gitea unreachable (first install):
get data from GitHub
else:
get data from Gitea
apply helm chart
wait for status
ArgoCD apply the rest:
clone git repo
install components based on directories:
./bootstrap (it manages itself):
argocd
root
./system:
storage
loadbalancer
ingress
etc
./platform (depends on ./system):
git:
migrate the homelab repository from GitHub
ArgoCD switch the source from GitHub to Gitea
ci
dashboards
etc
./apps (depends on ./system and ./platform):
homepage
jellyfin
etc
make[Run make] -- 1 --> metal -- 2 --> system -. 3 .-> platform -. 4 .-> apps
```

3
scripts/configure vendored
View File

@ -57,7 +57,6 @@ def main() -> None:
paths=[
".ci",
"apps",
"bootstrap",
"platform",
"system",
"external"
@ -68,7 +67,7 @@ def main() -> None:
pattern=upstream_config['seed_repo'],
replacement=seed_repo,
paths=[
"bootstrap",
"system",
"platform"
]
)

8
system/Makefile Normal file
View File

@ -0,0 +1,8 @@
.POSIX:
export KUBECONFIG = $(shell pwd)/../metal/kubeconfig.yaml
.PHONY: bootstrap
bootstrap:
ansible-playbook \
bootstrap.yml

View File

@ -5,3 +5,6 @@ dependencies:
- name: argo-cd
version: 6.4.1
repository: https://argoproj.github.io/argo-helm
- name: argocd-apps
version: 2.0.0
repository: https://argoproj.github.io/argo-helm

View File

@ -0,0 +1,28 @@
argo-cd:
server:
metrics: &metrics
enabled: false
serviceMonitor:
enabled: false
controller:
metrics: *metrics
repoServer:
metrics: *metrics
redis:
metrics: *metrics
argocd-apps:
applicationsets:
root:
generators:
- git:
repoURL: &repoURL https://github.com/khuedoan/homelab
revision: &revision master
directories:
- path: system/*
- path: platform/*
- path: apps/*
template:
spec:
source:
repoURL: *repoURL
targetRevision: *revision

69
system/argocd/values.yaml Normal file
View File

@ -0,0 +1,69 @@
argo-cd:
global:
domain: argocd.khuedoan.com
configs:
params:
server.insecure: true
controller.diff.server.side: true
cm:
resource.ignoreResourceUpdatesEnabled: true
resource.customizations.ignoreResourceUpdates.all: |
jsonPointers:
- /status
server:
ingress:
enabled: true
ingressClassName: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
tls: true
metrics: &metrics
enabled: true
serviceMonitor:
enabled: true
dex:
enabled: false
controller:
metrics: *metrics
repoServer:
metrics: *metrics
redis:
metrics: *metrics
argocd-apps:
applicationsets:
root:
namespace: argocd
generators:
- git:
repoURL: &repoURL http://gitea-http.gitea:3000/ops/homelab
revision: &revision master
directories:
- path: system/*
- path: platform/*
- path: apps/*
template:
metadata:
name: '{{path.basename}}'
spec:
destination:
name: in-cluster
namespace: '{{path.basename}}'
project: default # TODO
source:
repoURL: *repoURL
path: '{{path}}'
targetRevision: *revision
syncPolicy:
automated:
prune: true
selfHeal: true
retry:
limit: 10
backoff:
duration: 1m
factor: 2
maxDuration: 16m
syncOptions:
- CreateNamespace=true
- ApplyOutOfSyncOnly=true
- ServerSideApply=true

36
system/bootstrap.yml Normal file
View File

@ -0,0 +1,36 @@
- name: Bootstrapping the cluster
hosts: localhost
tasks:
- name: Create ArgoCD namespace
kubernetes.core.k8s:
api_version: v1
kind: Namespace
name: argocd
state: present
- name: Check if this is the first installation
kubernetes.core.k8s_info:
kind: Pod
label_selectors:
- app.kubernetes.io/instance=gitea
field_selectors:
- status.phase=Running
register: first_install
- name: Render ArgoCD manifests from Helm chart
kubernetes.core.helm_template:
chart_ref: ./argocd
include_crds: true
release_name: argocd
release_namespace: argocd
dependency_update: true
values_files:
- "argocd/{{ (first_install.resources | length == 0) | ternary('values-seed.yaml', 'values.yaml') }}"
register: argocd_manifests
- name: Apply ArgoCD manifests
kubernetes.core.k8s:
resource_definition: "{{ argocd_manifests.stdout }}"
apply: true
server_side_apply:
field_manager: argocd-controller