Compare commits

...

10 commits

31 changed files with 317 additions and 6732 deletions

View file

@ -1,18 +0,0 @@
name: Build
on: [push]
jobs:
build:
runs-on: k8s
steps:
- uses: actions/checkout@v4
- uses: https://git.qcampos.fr/system/actions/setup-kaniko@main
- name: Build and push
uses: docker://gcr.io/kaniko-project/executor:debug
with:
args: >-
--context dir:///github/workspace
--destination git.qcampos.fr/${{ github.repository }}-backend:latest
--destination git.qcampos.fr/${{ github.repository }}-backend:${{ github.sha }}
--skip-tls-verify

View file

@ -1,5 +0,0 @@
FROM python:alpine
RUN ["touch", "barrrrrrr.txt"]
CMD ["python", "-c", "'import this'"]

3
ansible/README.md Normal file
View file

@ -0,0 +1,3 @@
# Ansible Playbook
This playbook is used to boostrap the cluster by installing the cluster and ArgoCD configuration.

192
ansible/bootstrap.yaml Normal file
View file

@ -0,0 +1,192 @@
---
- name: Bootstrap K3s and ArgoCD
hosts: vps
become: yes
vars:
# URL of the official ArgoCD manifest
argocd_manifest_url: "https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml"
# Local path to the root-app.yaml manifest (at the root of the repository)
root_app_path: "../root-app.yaml"
# Tailscale secrets are retrieved from environment variables
tailscale_client_id: "{{ lookup('ansible.builtin.env', 'TAILSCALE_CLIENT_ID') | default('', true) }}"
tailscale_client_secret: "{{ lookup('ansible.builtin.env', 'TAILSCALE_CLIENT_SECRET') | default('', true) }}"
tasks:
# =========================================================================
# INITIAL NODE CONFIGURATION
# =========================================================================
- name: Initial Node Configuration
block:
- name: Install prerequisites
ansible.builtin.apt:
name:
- curl
- git
- ca-certificates
- python3-kubernetes
state: present
update_cache: yes
# =========================================================================
# K3S INSTALLATION AND CONFIGURATION
# =========================================================================
- name: K3s Installation and Configuration
block:
- name: Check if K3s is already installed
ansible.builtin.stat:
path: /usr/local/bin/k3s
register: k3s_bin
- name: Install K3s (default configuration)
ansible.builtin.shell: |
curl -sfL https://get.k3s.io | sh -
when: not k3s_bin.stat.exists
- name: Ensure ~/.kube directory exists
ansible.builtin.file:
path: ~/.kube
state: directory
mode: '0700'
- name: Copy K3s kubeconfig to default root config
ansible.builtin.copy:
src: /etc/rancher/k3s/k3s.yaml
dest: ~/.kube/config
remote_src: yes
owner: root
group: root
mode: '0600'
- name: Also configure kubeconfig for the ansible_user
block:
- name: Ensure non-root user ~/.kube directory exists
ansible.builtin.file:
path: "/home/{{ ansible_user }}/.kube"
state: directory
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0700'
- name: Copy K3s kubeconfig to non-root user config
ansible.builtin.copy:
src: /etc/rancher/k3s/k3s.yaml
dest: "/home/{{ ansible_user }}/.kube/config"
remote_src: yes
owner: "{{ ansible_user }}"
group: "{{ ansible_user }}"
mode: '0600'
when: ansible_user is defined and ansible_user != 'root'
- name: Wait for K3s node to be ready
ansible.builtin.shell: |
kubectl get nodes | grep -q -w "Ready"
register: node_status
until: node_status.rc == 0
retries: 30
delay: 5
changed_when: false
- name: Create tailscale namespace
kubernetes.core.k8s:
definition:
apiVersion: v1
kind: Namespace
metadata:
name: tailscale
state: present
- name: Create "operator-oauth" tailscale secret if credentials are provided
kubernetes.core.k8s:
definition:
apiVersion: v1
kind: Secret
metadata:
name: operator-oauth
namespace: tailscale
type: Opaque
stringData:
client_id: "{{ tailscale_client_id }}"
client_secret: "{{ tailscale_client_secret }}"
state: present
when: tailscale_client_id != "" and tailscale_client_secret != ""
# =========================================================================
# ARGOCD INSTALLATION
# =========================================================================
- name: ArgoCD Installation
block:
- name: Create ArgoCD namespace
kubernetes.core.k8s:
definition:
apiVersion: v1
kind: Namespace
metadata:
name: argocd
state: present
- name: Install ArgoCD (Server-Side Apply)
kubernetes.core.k8s:
src: "{{ argocd_manifest_url }}"
namespace: argocd
state: present
apply: true
server_side_apply:
field_manager: ansible
force_conflicts: true
- name: Wait for ArgoCD Server deployment to be operational
ansible.builtin.shell: |
kubectl rollout status deployment/argocd-server -n argocd --timeout=300s
changed_when: false
# =========================================================================
# SYSTEM BOOTSTRAP (Cert-Manager, ClusterIssuer, Forgejo)
# =========================================================================
- name: System Bootstrap (Cert-Manager, ClusterIssuer, Forgejo)
block:
- name: Install Cert-Manager (from official manifest)
kubernetes.core.k8s:
src: "https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml"
state: present
apply: true
server_side_apply:
field_manager: ansible
force_conflicts: true
- name: Wait for Cert-Manager webhook to be ready
ansible.builtin.shell: |
kubectl rollout status deployment/cert-manager-webhook -n cert-manager --timeout=300s
changed_when: false
- name: Deploy Let's Encrypt ClusterIssuer
kubernetes.core.k8s:
definition: "{{ lookup('file', '../platform/cluster/lets-encrypt.yaml') }}"
state: present
- name: Deploy Forgejo Namespace
kubernetes.core.k8s:
definition: "{{ lookup('file', '../platform/forgejo/namespace.yaml') }}"
state: present
- name: Deploy Forgejo Instance resources
kubernetes.core.k8s:
definition: "{{ lookup('file', '../platform/forgejo/instance/' ~ item) }}"
state: present
loop:
- registry-secret.yaml
- pvc.yaml
- service.yaml
- deployment.yaml
- ingress.yaml
- name: Deploy Forgejo Runner resources
kubernetes.core.k8s:
definition: "{{ lookup('file', '../platform/forgejo/runner/' ~ item) }}"
state: present
loop:
- secret.yaml
- pvc.yaml
- configmap.yaml
- deployment.yaml

3
ansible/inventory.ini Normal file
View file

@ -0,0 +1,3 @@
[vps]
# Main VPS
porygon2 ansible_host=porygon2.qcampos.fr ansible_port=58123 ansible_user=qcampos

2
ansible/requirements.txt Normal file
View file

@ -0,0 +1,2 @@
ansible
kubernetes

1
gitops/README.md Normal file
View file

@ -0,0 +1 @@
Loopback from the App of Apps (`/root-app.yaml`) that adds the manually installed base tools to ArgoCD to be automatically managed and self-healing.

View file

@ -0,0 +1,20 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: argocd-config
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "3"
spec:
project: default
source:
repoURL: 'https://git.qcampos.fr/qcampos/system.git'
path: platform/argocd
targetRevision: main
destination:
server: 'https://kubernetes.default.svc'
namespace: argocd
syncPolicy:
automated:
prune: true
selfHeal: true

View file

@ -0,0 +1,26 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cert-manager
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "1"
spec:
project: default
source:
repoURL: 'https://charts.jetstack.io'
chart: cert-manager
targetRevision: v1.14.5
helm:
parameters:
- name: installCRDs
value: "true"
destination:
server: 'https://kubernetes.default.svc'
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

View file

@ -0,0 +1,20 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cluster-issuer
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "2"
spec:
project: default
source:
repoURL: 'https://git.qcampos.fr/qcampos/system.git'
path: platform/cluster
targetRevision: main
destination:
server: 'https://kubernetes.default.svc'
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: true

24
gitops/forgejo-app.yaml Normal file
View file

@ -0,0 +1,24 @@
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: forgejo
namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "4"
spec:
project: default
source:
repoURL: 'https://git.qcampos.fr/qcampos/system.git'
path: platform/forgejo
targetRevision: main
directory:
recurse: true
destination:
server: 'https://kubernetes.default.svc'
namespace: forgejo
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true

View file

@ -3,11 +3,13 @@ kind: Application
metadata: metadata:
name: tailscale-operator name: tailscale-operator
namespace: argocd namespace: argocd
annotations:
argocd.argoproj.io/sync-wave: "3"
spec: spec:
project: default project: default
source: source:
repoURL: 'https://git.qcampos.fr/qcampos/system.git' repoURL: 'https://git.qcampos.fr/qcampos/system.git'
path: system/tailscale path: platform/tailscale-operator
targetRevision: main targetRevision: main
destination: destination:
server: 'https://kubernetes.default.svc' server: 'https://kubernetes.default.svc'
@ -15,6 +17,6 @@ spec:
syncPolicy: syncPolicy:
automated: automated:
prune: true prune: true
selfHeal: false selfHeal: true
syncOptions: syncOptions:
- CreateNamespace=true - CreateNamespace=true

1
platform/README.md Normal file
View file

@ -0,0 +1 @@
Base tools that are "manually" installed by Ansible to boostrat the cluster.

View file

@ -8,7 +8,7 @@ spec:
service: service:
name: argocd-server name: argocd-server
port: port:
number: 80 number: 443
ingressClassName: tailscale ingressClassName: tailscale
tls: tls:
- hosts: - hosts:

View file

@ -4,7 +4,7 @@ metadata:
name: letsencrypt-http name: letsencrypt-http
spec: spec:
acme: acme:
email: quentin.campos@gmail.com # Ton email pour les alertes de fin de validité email: quentin.campos@gmail.com # Email for end of validity notifications
server: https://acme-v02.api.letsencrypt.org/directory server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef: privateKeySecretRef:
name: letsencrypt-http-issuer-key name: letsencrypt-http-issuer-key

View file

@ -25,18 +25,18 @@ spec:
name: ssh name: ssh
env: env:
- name: FORGEJO__database__DB_TYPE - name: FORGEJO__database__DB_TYPE
value: sqlite3 # Léger pour ton VPS value: sqlite3 # Lightweight for your VPS
- name: FORGEJO__server__DOMAIN - name: FORGEJO__server__DOMAIN
value: git.qcampos.fr value: git.qcampos.fr
- name: FORGEJO__server__ROOT_URL - name: FORGEJO__server__ROOT_URL
value: https://git.qcampos.fr/ value: https://git.qcampos.fr/
# Activation du Registre d'images # Enable package registry
- name: FORGEJO__packages__ENABLED - name: FORGEJO__packages__ENABLED
value: "true" value: "true"
# Required for OCI/container image pushes (chunked upload temp dir) # Required for OCI/container image pushes (chunked upload temp dir)
- name: FORGEJO__packages__CHUNKED_UPLOAD_PATH - name: FORGEJO__packages__CHUNKED_UPLOAD_PATH
value: /data/tmp/package-chunked-upload value: /data/tmp/package-chunked-upload
# Activation des Actions (CI/CD) # Enable Actions (CI/CD)
- name: FORGEJO__actions__ENABLED - name: FORGEJO__actions__ENABLED
value: "true" value: "true"
volumeMounts: volumeMounts:

View file

@ -1,5 +1,5 @@
# ConfigMap contenant la configuration du runner (fichier config.yaml). # ConfigMap containing the runner configuration (config.yaml file).
# Ce fichier est monté dans le pod et passé au runner via --config. # This file is mounted in the pod and passed to the runner via --config.
apiVersion: v1 apiVersion: v1
kind: ConfigMap kind: ConfigMap
metadata: metadata:

View file

@ -13,4 +13,4 @@ metadata:
namespace: forgejo namespace: forgejo
stringData: stringData:
# Remplacer par le token copié depuis l'interface Forgejo. # Remplacer par le token copié depuis l'interface Forgejo.
registration-token: "LOqadesvfA2tjVRoyWUxCnzYjmgMhZqhHHT30nnp" registration-token: "SHdSfwt8mtWNq2RGp0mS0HyuXyhNKu5yHokITMnK"

View file

@ -0,0 +1,10 @@
apiVersion: v2
name: tailscale-operator-wrapper
description: Wrapper Helm chart for the official Tailscale Operator
type: application
version: 1.0.1
appVersion: 1.98.4
dependencies:
- name: tailscale-operator
version: 1.98.4
repository: https://pkgs.tailscale.com/helmcharts

View file

@ -7,7 +7,7 @@ spec:
project: default project: default
source: source:
repoURL: 'https://git.qcampos.fr/qcampos/system.git' repoURL: 'https://git.qcampos.fr/qcampos/system.git'
path: bootstrap path: gitops
targetRevision: main targetRevision: main
destination: destination:
server: 'https://kubernetes.default.svc' server: 'https://kubernetes.default.svc'
@ -15,4 +15,4 @@ spec:
syncPolicy: syncPolicy:
automated: automated:
prune: true prune: true
selfHeal: true selfHeal: true

View file

@ -1,26 +0,0 @@
# For the ingress that connects to the HTTP port of ArgoCD
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-http"
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
ingressClassName: traefik
tls:
- hosts:
- argo.qcampos.fr
secretName: argocd-server-tls
rules:
- host: argo.qcampos.fr
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80

View file

@ -1,67 +0,0 @@
# Values.yaml for the Argo helm chart
global:
hosts:
domain: qcampos.fr
gitlab:
name: git.qcampos.fr
ingress:
configureCertmanager: false
class: traefik
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-http"
traefik.ingress.kubernetes.io/router.tls: "true"
kas:
enabled: false # Désactivation de l'agent K8s interne
# Regroupement de toute la section gitlab
gitlab:
upgradeCheck:
enabled: false
webservice:
workerProcesses: 1
resources:
limits:
memory: 1.5Gi
requests:
memory: 1Gi # On définit un minimum pour éviter que K3s ne le refuse
sidekiq:
concurrency: 1
resources:
limits:
memory: 750Mi # Sidekiq a besoin d'un peu plus de 500Mo pour charger Rails au démarrage
requests:
memory: 500Mi
toolbox:
enabled: true
gitaly: # Ajout crucial : Gitaly est très gourmand par défaut
resources:
limits:
memory: 1Gi
requests:
memory: 500Mi
# Désactivation des services d'observabilité (Lourds)
prometheus:
install: false
gitlab-exporter:
enabled: false
grafana:
install: false
registry:
enabled: false
gitlab-runner:
install: false
# Optimisation des bases de données
postgresql:
resources:
requests:
memory: 256Mi
redis:
resources:
requests:
memory: 128Mi
minio:
resources:
requests:
memory: 128Mi

File diff suppressed because it is too large Load diff