feat(kubernetes): add initial setup for ArgoCD, Cert-Manager, MetalLB, and Traefik

Signed-off-by: Tuan-Dat Tran <tuan-dat.tran@tudattr.dev>
This commit is contained in:
Tuan-Dat Tran
2025-07-13 14:25:53 +02:00
parent 4aa939426b
commit 76000f8123
30 changed files with 416 additions and 69 deletions

View File

@@ -6,9 +6,9 @@
- name: Setup DNS on Netcup
community.general.netcup_dns:
api_key: "{{ k3s_loadbalancer_netcup_api_key }}"
api_password: "{{ k3s_loadbalancer_netcup_api_password }}"
customer_id: "{{ k3s_loadbalancer_netcup_customer_id }}"
api_key: "{{ netcup_api_key }}"
api_password: "{{ netcup_api_password }}"
customer_id: "{{ netcup_customer_id }}"
domain: "{{ domain }}"
name: "k3s"
type: "A"

View File

@@ -1,7 +1,3 @@
k3s_loadbalancer_nginx_config_path: "/etc/nginx/nginx.conf"
k3s_loadbalancer_netcup_api_key: "{{ netcup_api_key }}"
k3s_loadbalancer_netcup_api_password: "{{ netcup_api_password }}"
k3s_loadbalancer_netcup_customer_id: "{{ netcup_customer_id }}"
domain: "{{ internal_domain }}"

View File

@@ -0,0 +1,4 @@
---
argocd_version: stable
argocd_namespace: argocd
argocd_repo: "https://raw.githubusercontent.com/argoproj/argo-cd/refs/tags/{{ argocd_version }}/manifests/ha/install.yaml"

View File

@@ -0,0 +1,10 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cmd-params-cm
labels:
app.kubernetes.io/name: argocd-cmd-params-cm
app.kubernetes.io/part-of: argocd
data:
redis.server: "argocd-redis-ha-haproxy:6379"
server.insecure: "true"

View File

@@ -0,0 +1,50 @@
---
- name: Install ArgoCD
block:
- name: Create ArgoCD namespace
kubernetes.core.k8s:
name: "{{ argocd_namespace }}"
api_version: v1
kind: Namespace
state: present
- name: Apply ArgoCD manifests
kubernetes.core.k8s:
src: "{{ argocd_repo }}"
state: present
namespace: "{{ argocd_namespace }}"
register: apply_manifests
until: apply_manifests is not failed
retries: 5
delay: 10
- name: Wait for ArgoCD server to be ready
kubernetes.core.k8s_info:
api_version: apps/v1
kind: Deployment
name: argocd-server
namespace: "{{ argocd_namespace }}"
register: rollout_status
until: rollout_status.resources[0].status.readyReplicas is defined and rollout_status.resources[0].status.readyReplicas == rollout_status.resources[0].spec.replicas
retries: 30
delay: 10
- name: Apply ArgoCD Ingress
kubernetes.core.k8s:
definition: "{{ lookup('ansible.builtin.template', 'ingress.yml.j2') | from_yaml }}"
state: present
namespace: "{{ argocd_namespace }}"
register: apply_manifests
until: apply_manifests is not failed
retries: 5
delay: 10
- name: Apply ArgoCD CM
kubernetes.core.k8s:
src: "files/argocd-cmd-params-cm.yaml"
state: present
namespace: "{{ argocd_namespace }}"
register: apply_manifests
until: apply_manifests is not failed
retries: 5
delay: 10

View File

@@ -0,0 +1,19 @@
---
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: "{{ item.name }}"
namespace: "{{ argocd_namespace }}"
spec:
project: default
source:
repoURL: "{{ item.repo_url }}"
targetRevision: "{{ item.target_revision }}"
path: "{{ item.path }}"
destination:
server: "{{ item.destination_server }}"
namespace: "{{ item.destination_namespace }}"
syncPolicy:
automated:
prune: true
selfHeal: true

View File

@@ -0,0 +1,26 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-ingress
namespace: argocd
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
cert-manager.io/cluster-issuer: "{{ argocd_cert_resolver }}"
traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
spec:
rules:
- host: {{ argocd_hostname }}
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
tls:
- hosts:
- {{ argocd_hostname }}
secretName: argocd-tls-secret

View File

@@ -0,0 +1,5 @@
cert_manager_version: "v1.18.2"
cert_manager_email: "mail@example.com"
cert_manager_manifest: "https://github.com/cert-manager/cert-manager/releases/download/{{ cert_manager_version }}/cert-manager.yaml"
cert_manager_issuer_name: "letsencrypt-prod"
cert_manager_issuer_env: "staging"

View File

@@ -0,0 +1,69 @@
---
- name: Ensure cert-manager namespace exists
kubernetes.core.k8s:
name: cert-manager
api_version: v1
kind: Namespace
state: present
tags:
- cert_manager
- namespace
- name: Create netcup-secret
kubernetes.core.k8s:
namespace: cert-manager
definition: "{{ lookup('ansible.builtin.template', 'netcup.yml.j2') | from_yaml }}"
- name: Add a repository
kubernetes.core.helm_repository:
name: cert-manager-webhook-netcup
repo_url: https://aellwein.github.io/cert-manager-webhook-netcup/charts/
- name: Install NetCup Webhook
kubernetes.core.helm:
name: my-cert-manager-webhook-netcup
chart_ref: cert-manager-webhook-netcup/cert-manager-webhook-netcup
release_namespace: cert-manager
create_namespace: true
- name: Download cert-manager manifest
ansible.builtin.get_url:
url: "{{ cert_manager_manifest }}"
dest: "/tmp/cert-manager.yaml"
mode: "0644"
validate_certs: true
tags:
- cert_manager
- download
- name: Apply cert-manager core manifests
kubernetes.core.k8s:
src: "/tmp/cert-manager.yaml"
state: present
tags:
- cert_manager
- apply_manifest
- name: Wait for cert-manager deployments to be ready
kubernetes.core.k8s_info:
api_version: apps/v1
kind: Deployment
namespace: cert-manager
name: "{{ item }}"
wait: true
wait_timeout: 300
loop:
- cert-manager
- cert-manager-cainjector
- cert-manager-webhook
tags:
- cert_manager
- wait_ready
- name: Create Let's Encrypt ClusterIssuer
kubernetes.core.k8s:
state: present
definition: "{{ lookup('ansible.builtin.template', 'clusterissuer.yml.j2') | from_yaml }}"
tags:
- cert_manager
- cluster_issuer

View File

@@ -0,0 +1,18 @@
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
spec:
# For staging: https://acme-staging-v02.api.letsencrypt.org/directory
# For production: https://acme-v02.api.letsencrypt.org/directory
server: "{% if cert_manager_issuer_env == 'production' %}https://acme-v02.api.letsencrypt.org/directory{% else %}https://acme-staging-v02.api.letsencrypt.org/directory{% endif %}"
email: "{{ cert_manager_email }}"
privateKeySecretRef:
name: "{{ cert_manager_issuer_name }}-account-key"
solvers:
- dns01:
webhook:
groupName: com.netcup.webhook
solverName: netcup
config:
secretRef: netcup-secret
secretNamespace: cert-manager

View File

@@ -0,0 +1,11 @@
---
apiVersion: v1
kind: Secret
metadata:
name: netcup-secret
type: Opaque
data:
customer-number: {{ netcup_customer_id | b64encode }}
api-key: {{ netcup_api_key |b64encode }}
api-password: {{ netcup_api_password | b64encode }}

View File

@@ -0,0 +1,4 @@
---
metallb_version: v0.15.2
metallb_ip_range: "192.168.178.200-192.168.178.220"
metallb_manifest_url: "https://raw.githubusercontent.com/metallb/metallb/{{ metallb_version }}/config/manifests/metallb-native.yaml"

View File

@@ -0,0 +1,62 @@
---
- name: Ensure metallb-system namespace exists
kubernetes.core.k8s:
name: metallb-system
api_version: v1
kind: Namespace
state: present
tags:
- metallb
- namespace
- name: Download MetalLB manifest
ansible.builtin.get_url:
url: "{{ metallb_manifest_url }}"
dest: "/tmp/metallb.yaml"
mode: "0644"
validate_certs: true
run_once: true
tags:
- metallb
- download
- name: Apply MetalLB core manifests
kubernetes.core.k8s:
src: "/tmp/metallb.yaml"
state: present
namespace: metallb-system
tags:
- metallb
- apply_manifest
- name: Create IPAddressPool for MetalLB
kubernetes.core.k8s:
state: present
namespace: metallb-system
definition: "{{ lookup('ansible.builtin.template', 'ipaddresspool.yml.j2') | from_yaml }}"
tags:
- metallb
- ip_pool
- name: Create L2Advertisement for MetalLB
kubernetes.core.k8s:
state: present
namespace: metallb-system
definition: "{{ lookup('ansible.builtin.template', 'l2advertisement.yml.j2') | from_yaml }}"
tags:
- metallb
- l2_advertisement
- name: Setup DNS on Netcup
community.general.netcup_dns:
api_key: "{{ netcup_api_key }}"
api_password: "{{ netcup_api_password }}"
customer_id: "{{ netcup_customer_id }}"
domain: "{{ domain }}"
name: "{{ service.name }}.k3s"
type: "A"
value: "{{ service.ip }}"
loop: "{{ services }}"
loop_control:
loop_var: service
delegate_to: localhost

View File

@@ -0,0 +1,9 @@
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: default-pool
namespace: metallb-system
spec:
addresses:
- "{{ metallb_ip_range }}"

View File

@@ -0,0 +1,9 @@
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: default-l2advertisement
namespace: metallb-system
spec:
ipAddressPools:
- default-pool

View File

@@ -0,0 +1,3 @@
---
traefik_dashboard_hostname: "traefik.example.com"
traefik_cert_resolver: "cert_resolver-prod"

View File

@@ -0,0 +1,12 @@
---
# roles/traefik/tasks/main.yml
- name: "Traefik | Enable dashboard"
kubernetes.core.k8s:
template: "helmchartconfig.yaml.j2"
state: present
- name: "Traefik | Create dashboard ingress"
kubernetes.core.k8s:
template: "ingress.yaml.j2"
state: present

View File

@@ -0,0 +1,15 @@
---
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
dashboard:
enabled: true
ingressRoute: false
ports:
websecure:
tls:
enabled: true

View File

@@ -0,0 +1,25 @@
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: traefik-dashboard
namespace: kube-system
annotations:
kubernetes.io/ingress.class: traefik
cert-manager.io/cluster-issuer: "{{ traefik_cert_resolver }}"
spec:
rules:
- host: "{{ traefik_dashboard_hostname }}"
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: traefik
port:
name: traefik
tls:
- hosts:
- "{{ traefik_dashboard_hostname }}"
secretName: traefik-dashboard-tls

View File

@@ -1,9 +1,9 @@
---
- name: Setup DNS on Netcup
community.general.netcup_dns:
api_key: "{{ reverse_proxy_netcup_api_key }}"
api_password: "{{ reverse_proxy_netcup_api_password }}"
customer_id: "{{ reverse_proxy_netcup_customer_id }}"
api_key: "{{ netcup_api_key }}"
api_password: "{{ netcup_api_password }}"
customer_id: "{{ netcup_customer_id }}"
domain: "{{ domain }}"
name: "{{ service.name }}"
type: "A"

View File

@@ -6,7 +6,3 @@ reverse_proxy_custom_caddy_dest_path: "/usr/bin/caddy.custom"
reverse_proxy_diverted_caddy_path: "/usr/bin/caddy.default"
reverse_proxy_alternatives_link: "/usr/bin/caddy"
reverse_proxy_alternatives_name: "caddy"
reverse_proxy_netcup_api_key: "{{ netcup_api_key }}"
reverse_proxy_netcup_api_password: "{{ netcup_api_password }}"
reverse_proxy_netcup_customer_id: "{{ netcup_customer_id }}"