Auto-update blog content from Obsidian: 2026-04-29 19:59:52
Some checks failed
Blog Deployment / Check-Rebuild (push) Successful in 8s
Blog Deployment / Build (push) Successful in 31s
Blog Deployment / Deploy-Staging (push) Successful in 9s
Blog Deployment / Test-Staging (push) Failing after 3s
Blog Deployment / Merge (push) Has been skipped
Blog Deployment / Deploy-Production (push) Has been skipped
Blog Deployment / Test-Production (push) Has been skipped
Blog Deployment / Clean (push) Has been skipped
Blog Deployment / Notify (push) Successful in 3s
Some checks failed
Blog Deployment / Check-Rebuild (push) Successful in 8s
Blog Deployment / Build (push) Successful in 31s
Blog Deployment / Deploy-Staging (push) Successful in 9s
Blog Deployment / Test-Staging (push) Failing after 3s
Blog Deployment / Merge (push) Has been skipped
Blog Deployment / Deploy-Production (push) Has been skipped
Blog Deployment / Test-Production (push) Has been skipped
Blog Deployment / Clean (push) Has been skipped
Blog Deployment / Notify (push) Successful in 3s
This commit is contained in:
170
content/post/16-how-I-deploy-application/index.fr.md
Normal file
170
content/post/16-how-I-deploy-application/index.fr.md
Normal file
@@ -0,0 +1,170 @@
|
||||
---
|
||||
slug: how-I-deploy-application
|
||||
title: Comment je Déploie des Applications Aujourd’hui
|
||||
description: La méthode que j’utilise aujourd’hui pour déployer de nouvelles applications dans mon homelab. Workflow simple tirant parti de Docker Compose dans une VM sur Proxmox VE
|
||||
date: 2026-01-31
|
||||
draft: false
|
||||
tags:
|
||||
- docker
|
||||
- proxmox
|
||||
- opnsense
|
||||
- treafik
|
||||
- gitea
|
||||
categories:
|
||||
- homelab
|
||||
---
|
||||
## Intro
|
||||
|
||||
Dans cet article, je ne vais pas expliquer les bonnes pratiques pour déployer des applications. À la place, je veux documenter comment je déploie actuellement de nouvelles applications dans mon homelab.
|
||||
|
||||
Considérez cet article comme un snapshot. C’est comme ça que les choses fonctionnent vraiment aujourd’hui, sachant que dans un futur proche j’aimerais évoluer vers un workflow plus orienté GitOps.
|
||||
|
||||
La méthode que j’utilise est assez simple. J’ai essayé de la standardiser autant que possible, mais elle implique encore pas mal d’étapes manuelles. J’expliquerai aussi comment je mets à jour les applications, ce qui est, à mon avis, la plus grande faiblesse de cette configuration. À mesure que le nombre d’applications augmente, garder le tout à jour demande de plus en plus de temps.
|
||||
|
||||
|
||||
---
|
||||
## Overview de la Plateforme
|
||||
|
||||
Avant d’entrer dans le workflow, voici un rapide aperçu des principaux composants impliqués.
|
||||
### Docker
|
||||
|
||||
Docker est la base de ma stack applicative. Quand c’est possible, je déploie les applications sous forme de conteneurs.
|
||||
|
||||
J’utilise Docker Compose depuis des années. À l’époque, tout tournait sur un seul serveur physique. Aujourd’hui, mon installation est basée sur des VM, et je pourrais migrer vers Docker Swarm, mais j’ai choisi de ne pas le faire. Cela peut avoir du sens dans certains scénarios, mais ce n’est pas aligné avec là où je veux aller à long terme.
|
||||
|
||||
Pour l’instant, je m’appuie toujours sur une seule VM pour héberger toutes les applications Docker. Cette VM est plus ou moins un clone de mon ancien serveur physique, simplement virtualisé.
|
||||
|
||||
### Proxmox VE
|
||||
|
||||
Cette VM est hébergée sur un cluster Proxmox VE, composé de trois nœuds et utilisant Ceph comme stockage distribué.
|
||||
|
||||
Cela me donne de la haute disponibilité et facilite grandement la gestion des VM, même si le workload Docker n'est pas hautement disponible.
|
||||
|
||||
### Traefik
|
||||
|
||||
Traefik tourne directement sur l’hôte Docker et fait office de reverse proxy.
|
||||
|
||||
Il est responsable d’acheminer le trafic HTTPS vers les bons conteneurs et de gérer automatiquement les certificats TLS via Let’s Encrypt. Cela garde la configuration au niveau des applications simple et centralisée.
|
||||
|
||||
### OPNsense
|
||||
|
||||
OPNsense est mon routeur, pare-feu et agit aussi comme reverse proxy.
|
||||
|
||||
Le trafic HTTPS entrant est transféré vers Traefik en utilisant le plugin Caddy avec des règles Layer 4. Le TLS n’est pas terminé au niveau du pare-feu. Il est transmis à Traefik, qui gère l’émission et le renouvellement des certificats.
|
||||
|
||||
### Gitea
|
||||
|
||||
Gitea est un dépôt Git self-hosted, j’ai une instance qui tourne dans mon homelab.
|
||||
|
||||
Dans Gitea, j’ai un dépôt privé qui contient toutes mes configurations Docker Compose. Chaque application a son propre dossier, ce qui rend le dépôt facile à parcourir et à maintenir.
|
||||
|
||||
---
|
||||
## Déployer une Nouvelle Application
|
||||
|
||||
Pour standardiser les déploiements, j’utilise un template `docker-compose.yml` qui ressemble à ceci :
|
||||
```yml
|
||||
services:
|
||||
NAME:
|
||||
image: IMAGE
|
||||
container_name: NAME
|
||||
volumes:
|
||||
- /appli/data/NAME/:/
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
networks:
|
||||
- web
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.NAME.rule=Host(`HOST.vezpi.com`)
|
||||
- traefik.http.routers.NAME.entrypoints=https
|
||||
- traefik.http.routers.NAME.tls.certresolver=letsencrypt
|
||||
- traefik.http.services.NAME.loadbalancer.server.port=PORT
|
||||
restart: always
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: true
|
||||
```
|
||||
|
||||
Laissez-moi expliquer.
|
||||
|
||||
Pour l’image, selon l’application, le registre utilisé peut varier, mais j’utilise quand même Docker Hub par défaut. Quand j’essaie une nouvelle application, je peux utiliser le tag `latest` au début. Ensuite, si je choisis de la garder, je préfère épingler la version actuelle plutôt que `latest`.
|
||||
|
||||
J’utilise des montages de volumes pour tout ce qui est stateful. Chaque application a son propre dossier dans le filesystem `/appli/data`.
|
||||
|
||||
Quand une application doit être accessible en HTTPS, je relie le conteneur qui sert les requêtes au réseau `web`, qui est géré par Traefik et je lui associe des labels. Les `entrypoint` et `certresolver` sont définis dans ma configuration Traefik. L’URL définie dans `Host()` est celle qui sera utilisée pour accéder à l’application. Elle doit être identique à ce qui est défini dans la route Layer4 du plugin Caddy d’OPNsense.
|
||||
|
||||
Si plusieurs conteneurs doivent communiquer entre eux, j’ajoute un réseau `backend` qui sera créé lors du déploiement de la stack, dédié à l’application. Ainsi, aucun port n’a besoin d’être ouvert sur l’hôte.
|
||||
|
||||
### Étapes de Déploiement
|
||||
|
||||
La plupart du travail est effectué depuis VScode :
|
||||
- Créer un nouveau dossier dans ce dépôt, avec le nom de l’application.
|
||||
- Copier le template ci-dessus dans ce dossier.
|
||||
- Adapter le template avec les valeurs fournies par la documentation de l’application.
|
||||
- Créer un fichier `.env` pour les secrets si nécessaire. Ce fichier est ignoré par `.gitignore`.
|
||||
- Démarrer les services directement depuis VS Code en utilisant l’extension Docker.
|
||||
|
||||
Puis dans l’interface Web OPNsense, je mets à jour 2 routes Layer4 pour le plugin Caddy:
|
||||
- Selon que l’application doit être exposée sur Internet ou non, j’ai une route _Internal_ et une route _External_. J’ajoute l’URL donnée à Traefik dans l’une d’elles.
|
||||
- J’ajoute aussi cette URL dans une autre route pour rediriger le challenge HTTP Let’s Encrypt vers Traefik.
|
||||
|
||||
Une fois terminé, je teste l’URL. Si tout est correctement configuré, l’application devrait être accessible en HTTPS.
|
||||
|
||||
Quand tout fonctionne comme prévu, je commit le nouveau dossier de l’application dans le dépôt.
|
||||
|
||||
---
|
||||
## Mettre à Jour une Application
|
||||
|
||||
Les mises à jour d’applications sont encore entièrement manuelles.
|
||||
|
||||
Je n’utilise pas d’outils automatisés comme Watchtower pour l’instant. Environ une fois par mois, je cherche de nouvelles versions en regardant Docker Hub, les releases GitHub ou la documentation de l’application.
|
||||
|
||||
Pour chaque application que je veux mettre à jour, je passe en revue:
|
||||
- Nouvelles fonctionnalités
|
||||
- Breaking changes
|
||||
- Chemins de mise à niveau si nécessaire
|
||||
|
||||
La plupart du temps, les mises à jour sont simples:
|
||||
|
||||
- Mettre à jour le tag de l’image dans le fichier Docker Compose
|
||||
- Redémarrer la stack.
|
||||
- Vérifier que les conteneurs redémarrent correctement
|
||||
- Consulter les logs Docker
|
||||
- Tester l’application pour détecter des régressions
|
||||
|
||||
Si ça fonctionne, je continue à mettre à niveau étape par étape jusqu’à atteindre la dernière version disponible.
|
||||
|
||||
Sinon, je débogue jusqu’à corriger le problème. Les retours arrière sont pénibles.
|
||||
|
||||
Une fois la dernière version atteinte, je commit les changements dans le dépôt.
|
||||
|
||||
---
|
||||
## Avantages et inconvénients
|
||||
|
||||
Qu’est-ce qui fonctionne bien et qu’est-ce qui fonctionne moins ?
|
||||
|
||||
### Avantages
|
||||
|
||||
- Modèle simple, une VM, un fichier compose par application.
|
||||
- Facile à déployer, idéal pour tester une application.
|
||||
- Emplacement central pour les configurations.
|
||||
|
||||
### Inconvénients
|
||||
|
||||
- La VM Docker unique est un point de défaillance unique.
|
||||
- Les mises à jour manuelles ne passent pas à l’échelle quand le nombre d’applications augmente.
|
||||
- Devoir déclarer l’URL dans Caddy est fastidieux.
|
||||
- Difficile de suivre ce qui est en ligne et ce qui ne l’est pas.
|
||||
- Les secrets dans .env sont pratiques mais basiques.
|
||||
- Pas de moyen rapide de rollback.
|
||||
- Les opérations sur la VM sont critiques.
|
||||
|
||||
---
|
||||
## Conclusion
|
||||
|
||||
Cette configuration fonctionne, et elle m’a bien servi jusqu’ici. Elle est simple et intuitive. Cependant, elle est aussi très manuelle, surtout pour les mises à jour et la maintenance à long terme.
|
||||
|
||||
À mesure que le nombre d’applications augmente, cette approche ne passe clairement pas très bien à l’échelle. C’est l’une des principales raisons pour lesquelles je regarde vers GitOps et des workflows plus déclaratifs pour l’avenir.
|
||||
|
||||
Pour l'instant, cependant, c'est ainsi que je déploie des applications dans mon homelab, et cet article sert de point de référence pour savoir par où j'ai commencé.
|
||||
169
content/post/16-how-I-deploy-application/index.md
Normal file
169
content/post/16-how-I-deploy-application/index.md
Normal file
@@ -0,0 +1,169 @@
|
||||
---
|
||||
slug: how-I-deploy-application
|
||||
title: How Do I Deploy Application Today
|
||||
description: The method I use today to deploy new application in my homelab. Simple workflow taking advantage of Docker Compose in a VM on Proxmox VE
|
||||
date: 2026-01-31
|
||||
draft: false
|
||||
tags:
|
||||
- docker
|
||||
- proxmox
|
||||
- opnsense
|
||||
- treafik
|
||||
- gitea
|
||||
categories:
|
||||
- homelab
|
||||
---
|
||||
## Intro
|
||||
|
||||
In this post, I am not going to explain best practices for deploying applications. Instead, I want to document how I am currently deploying new applications in my homelab.
|
||||
|
||||
Think of this article as a snapshot in time. This is how things really work today, knowing that in the near future I would like to move toward a more GitOps-oriented workflow.
|
||||
|
||||
The method I use is fairly simple. I have tried to standardize it as much as possible, but it still involves quite a few manual steps. I will also explain how I update applications, which is, in my opinion, the biggest weakness of this setup. As the number of applications keeps growing, keeping everything up to date requires more and more time.
|
||||
|
||||
---
|
||||
## Platform Overview
|
||||
|
||||
Before diving into the workflow, here is a quick overview of the main components involved.
|
||||
### Docker
|
||||
|
||||
Docker is the foundation of my application stack. Whenever possible, I deploy applications as containers.
|
||||
|
||||
I have been using Docker Compose for years. At the time, everything was running on a single physical server. Today, my setup is VM-based, and I could migrate to Docker Swarm, but I have chosen not to. It might make sense in some scenarios, but it is not aligned with where I want to go long term.
|
||||
|
||||
For now, I still rely on a single VM to host all Docker applications. This VM is more or less a clone of my old physical server, just virtualized.
|
||||
|
||||
### Proxmox VE
|
||||
|
||||
This VM is hosted on a Proxmox VE cluster, composed of three nodes and uses Ceph as a distributed storage backend.
|
||||
|
||||
This gives me high availability and makes VM management much easier, even though the Docker workloads themselves are not highly available.
|
||||
|
||||
### Traefik
|
||||
|
||||
Traefik runs directly on the Docker host and acts as the reverse proxy.
|
||||
|
||||
It is responsible for routing the HTTPS traffic to the correct containers and for managing TLS certificates automatically using Let’s Encrypt. This keeps application-level configuration simple and centralized.
|
||||
|
||||
### OPNsense
|
||||
|
||||
OPNsense is my router, firewall and also acts as reverse proxy.
|
||||
|
||||
Incoming HTTPS traffic is forwarded to Traefik using the Caddy plugin with Layer 4 rules. TLS is not terminated at the firewall level. It is passed through to Traefik, which handles certificate issuance and renewal.
|
||||
|
||||
### Gitea
|
||||
|
||||
Gitea is a self-hosted Git repository, I have one instance running in my homelab.
|
||||
|
||||
Inside Gitea, I have a private repository that contains all my Docker Compose configurations. Each application has its own folder, making the repository easy to navigate and maintain.
|
||||
|
||||
---
|
||||
## Deploy New Application
|
||||
|
||||
To standardize deployments, I use a `docker-compose.yml` template that looks like this:
|
||||
```yml
|
||||
services:
|
||||
NAME:
|
||||
image: IMAGE
|
||||
container_name: NAME
|
||||
volumes:
|
||||
- /appli/data/NAME/:/
|
||||
environment:
|
||||
- TZ=Europe/Paris
|
||||
networks:
|
||||
- web
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.routers.NAME.rule=Host(`HOST.vezpi.com`)
|
||||
- traefik.http.routers.NAME.entrypoints=https
|
||||
- traefik.http.routers.NAME.tls.certresolver=letsencrypt
|
||||
- traefik.http.services.NAME.loadbalancer.server.port=PORT
|
||||
restart: always
|
||||
|
||||
networks:
|
||||
web:
|
||||
external: true
|
||||
```
|
||||
|
||||
Let me explain.
|
||||
|
||||
For the image, depending on the application, the registry used could differ, but I still the Docker Hub by default. When I try a new application, I might use the `latest` tag at first. Then if I choose to keep the it, I prefer to pin the current version instead of `latest`.
|
||||
|
||||
I use volume binds for everything stateful. Every application got its own folder in the `/appli/data` filesystem.
|
||||
|
||||
When an application needs to be reachable with HTTPS, I link the container serving the requests in the `web` network, which is managed by Traefik and I associate it labels. The `entrypoint` and `certresolver` is defined in my Traefik configuration. The URL defined in `Host()` is the one which will be used to access the application. This needs to be the same as defined in the Layer4 route in the Caddy plugin of OPNsense.
|
||||
|
||||
If several containers need to talk to each other, I add a `backend` network which will be created when the stack will be deployed, dedicated for the application. This way, no ports need to be opened on the host.
|
||||
|
||||
### Steps to Deploy
|
||||
|
||||
Most of the work is done from VScode:
|
||||
- Create a new folder in that repository, with the application name.
|
||||
- Copy the template above inside this folder.
|
||||
- Adapt the template with the values given by the application documentation.
|
||||
- Create a `.env` file for secrets if needed. This file is ignored by `.gitignore`.
|
||||
- Start the services directly from VS Code using the Docker extension.
|
||||
|
||||
|
||||
Then in the OPNsense WebUI, I update 2 Layer4 routes for the Caddy plugin:
|
||||
- Depending if the application should be exposed on the internet or not, I have an *Internal* and *External* route. I add the URL given to Traefik in one of these.
|
||||
- I also add this URL in another route to redirect the Let's Encrypt HTTP challenge to Traefik.
|
||||
|
||||
Once complete, I test the URL. If everything is configured correctly, the application should be reachable over HTTPS.
|
||||
|
||||
When everything works as expected, I commit the new application folder to the repository.
|
||||
|
||||
---
|
||||
## Update Application
|
||||
|
||||
Application updates are still entirely manual.
|
||||
|
||||
I do not use automated tools like Watchtower for now. About once a month, I check for new versions by looking at Docker Hub, GitHub releases, or the application documentation.
|
||||
|
||||
For each application I want to update, I review:
|
||||
- New features
|
||||
- Breaking changes
|
||||
- Upgrade paths if required
|
||||
|
||||
Most of the time, updates are straightforward:
|
||||
- Bump the image tag in the Docker Compose file
|
||||
- Restart the stack.
|
||||
- Verify that the containers restart properly
|
||||
- Check Docker logs
|
||||
- Test the application to detect regressions
|
||||
|
||||
If it works, I continue upgrading step by step until I reach the latest available version.
|
||||
|
||||
If not, I debug until I fix the problem. Rollbacks are painful.
|
||||
|
||||
Once the latest version is reached, I commit the changes to the repository.
|
||||
|
||||
---
|
||||
## Pros and Cons
|
||||
|
||||
What works well and what doesn't?
|
||||
|
||||
### Pros
|
||||
|
||||
- Simple model, one VM, one compose file per application.
|
||||
- Easy to deploy, great to test an application.
|
||||
- Central location for the configurations.
|
||||
|
||||
### Cons
|
||||
|
||||
- Single Docker VM is a single point of failure.
|
||||
- Manual updates don’t scale as the app count grows.
|
||||
- Having to declare the URL on Caddy is boring.
|
||||
- Hard to follow what is up, and what is not.
|
||||
- Secrets in .env are convenient but basic.
|
||||
- No fast way to rollback.
|
||||
- Operations on the VM are critical.
|
||||
|
||||
---
|
||||
## Conclusion
|
||||
|
||||
This setup works, and it has served me well so far. It is simple and intuitive. However, it is also very manual, especially when it comes to updates and long-term maintenance.
|
||||
|
||||
As the number of applications grows, this approach clearly does not scale very well. That is one of the main reasons why I am looking toward GitOps and more declarative workflows for the future.
|
||||
|
||||
For now, though, this is how I deploy applications in my homelab, and this post serves as a reference point for where I started.
|
||||
Reference in New Issue
Block a user