Add bilingual French/English support with Hugo multilingual config
Configure Hugo multilingual mode (fr default, en secondary) with Hextra's language-switch menu entry, and translate all existing content pages to English using Hugo's per-file language suffix convention (.en.md alongside .fr.md).
This commit is contained in:
5
content/blog/_index.en.md
Normal file
5
content/blog/_index.en.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: "Blog"
|
||||
---
|
||||
|
||||
My articles and experience feedback on networking, systems, and DevOps.
|
||||
111
content/blog/aiops-observabilite-reseau-ia/index.en.md
Normal file
111
content/blog/aiops-observabilite-reseau-ia/index.en.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
title: "AIOps and Network Observability: Preparing the Ground for AI"
|
||||
date: 2026-07-14
|
||||
authors:
|
||||
- name: Damien
|
||||
link: https://gitea.arnodo.fr/Damien
|
||||
tags:
|
||||
- AIOps
|
||||
- Observability
|
||||
- Network Automation
|
||||
- AI Agents
|
||||
---
|
||||
|
||||
I just finished building a full observability stack on my EVPN/VXLAN lab: topology, telemetry, and near real-time visualization. While building it, I realized I wasn't just doing monitoring — I was preparing a network to one day be "seen" by an AI. This first article sets the frame for a five-part series: why observability is the mandatory prerequisite before plugging an agent into your infrastructure.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## What I've actually just built
|
||||
|
||||
Over the past few months, I've set up a Containerlab EVPN/VXLAN lab with 28 Arista EOS devices, spread across three sites (campus, core, datacenter). Beyond BGP EVPN itself, I wanted this lab to be **observable**: to be able to say, at any moment, what the real state of the network is. Not just "does it ping," but what the actual topology is, which interfaces are up, and which counters are drifting.
|
||||
|
||||
The result is a stack combining four building blocks:
|
||||
|
||||
- a network discovery and assurance tool that serves as the **topological source of truth** (who is connected to whom, which VLANs, which VRFs);
|
||||
- **telemetry collection via gNMI** directly from the EOS devices;
|
||||
- a **time-series database** for metric storage;
|
||||
- a **visualization layer** automatically regenerated by cross-referencing topology and metrics.
|
||||
|
||||
It works today. But it's not the stack itself that made me write this article: it's a realization I had while building it. I wasn't just setting up monitoring — I was laying the foundations of a pipeline that could, one day, allow an AI agent to reason about my network. And that has a name.
|
||||
|
||||
## AIOps, not MLOps
|
||||
|
||||
Let's clear up an ambiguity right away, because the two terms are often conflated.
|
||||
|
||||
**MLOps** (Machine Learning Operations) is the discipline of operationalizing the lifecycle of machine learning models: training, dataset versioning, deployment, drift monitoring. That's not what I'm doing here. I'm not training any model, I'm not deploying any.
|
||||
|
||||
What I'm doing falls under **AIOps**: making an infrastructure observable and usable by automated systems, and eventually by an AI. The center of gravity isn't the model, it's the **infrastructure data**: its freshness, its consistency, its structure. That's exactly the scope of this series.
|
||||
|
||||
I still cite MLOps once, as a sister discipline, because it's the one that normalized an idea AIOps directly inherits: **observability isn't an option you bolt on afterward, it's the foundation everything else rests on.** The ML community learned this the hard way: a large share of projects that work in a notebook never survive the move to production, for lack of monitoring. The lesson applies to any system we claim to delegate to a machine. Including a network.
|
||||
|
||||
## The blind agent
|
||||
|
||||
Here's the central thesis of this article, and the thread running through the whole series.
|
||||
|
||||
An AI agent without observability is a blind agent. It only "sees" what it's exposed to. And a blind person given nothing to see doesn't stop moving for that reason: they keep going, with the same confidence as if they could see clearly.
|
||||
|
||||
Take a concrete case. I migrated a server from one VLAN to another last weekend. The topological source of truth hasn't yet gone through its discovery cycle: it still describes the old attachment. I open a conversation with an agent that's supposed to help me: "this server has stopped responding since this morning, where's the problem?"
|
||||
|
||||
The agent queries what it's been exposed to, reads the old topology, and answers confidently: "The server is attached to leaf-3 on VLAN 20, the uplink is nominal, check its IP configuration instead." The answer is coherent, well phrased, well argued. It's also entirely wrong: the server has been on leaf-7 since Saturday. The agent didn't lie, it didn't hallucinate in the strict sense: it reasoned correctly over stale data. And at no point did it flag that it might be wrong, because at no point was it aware that it couldn't see.
|
||||
|
||||
That's the real danger. An experienced human compensates for lack of observability with intuition, tribal memory, historical context ("that link has always been flaky, don't worry about it"). An agent has none of that. Partial data, stale data, or an inconsistency between the declared topology and the real state doesn't make it cautious: it makes it wrongly confident. The assurance of the answer generated in natural language masks the poverty of what it actually perceived.
|
||||
|
||||
I should note that no agent runs on this lab today: that will be precisely the subject of the last article in this series.
|
||||
|
||||
Observability, then, is not an end in itself. It's **the condition of possibility** for an agent to one day see the network before acting on it. Without it, you're not building an assistant: you're building a confidently blind one.
|
||||
|
||||
## A layered reading grid
|
||||
|
||||
What helped me structure all this was thinking in stacked layers, and accepting that you don't skip a step:
|
||||
|
||||
1. **Infrastructure first.** Is the base data reliable, complete, up to date? Topology, metrics, logs.
|
||||
2. **The model next.** What's built on top: a correlation rule, a model, an LLM — does it receive quality inputs, and can its relevance be measured over time?
|
||||
3. **Agent behavior last.** Can its answers and actions be trusted, and can they be audited?
|
||||
|
||||
The order isn't negotiable. An agent built on top of a shaky layer 1 inherits all its weaknesses in cascade, with the added bonus of the illusion of reliability that comes from an assertive answer by construction. You can't make up for a poorly instrumented infrastructure with a better prompt.
|
||||
|
||||
Applied to the network, this gives a very concrete order of priorities: before thinking "AI," you need reliable telemetry, an up-to-date topological source of truth, and usable logs. That's layer 1. It's what I've been working on for months, and it's what this series is going to detail.
|
||||
|
||||
## What "seeing" means for a network
|
||||
|
||||
Concretely, giving a network agent sight means bringing together three things:
|
||||
|
||||
- **An up-to-date topological source of truth.** Who is connected to what, which VLANs, which VRFs, which site. Without it, the agent doesn't even know what it's talking about: this is the core of the next article.
|
||||
- **Real-time telemetry.** The living state of the network: interfaces up/down, counters, utilization, drift. Topology tells the structure; telemetry tells the behavior.
|
||||
- **Usable logs.** The narrative of events: who changed what, when, and which errors were raised.
|
||||
|
||||
Of these three pillars, two are in place on my lab. The third, logs, remains a **work item I haven't tackled yet**. I say this honestly, because it's the kind of gap you're tempted to sweep under the rug: "I have the topology and the metrics, that's already good." Except an agent deprived of logs sees the present state without understanding how it got there. It's a prerequisite, not a bonus.
|
||||
|
||||
There's a principle running through this entire layer 1, one I've set for myself as a rule: **standardization over adaptability.** When naming conventions are respected everywhere: interfaces, hostnames, labels, no code has to compensate for discrepancies. It's a human who fixes the configuration upstream, not a script that guesses. This choice has a cost, ongoing rigor, but a direct benefit: clean, predictable data. And clean data is precisely what allows an agent to avoid building sound reasoning on false facts.
|
||||
|
||||
## Seeing isn't enough: you have to make it visible
|
||||
|
||||
Gathering the data: topology, telemetry, logs, isn't enough: it still needs to be structured and intelligently exposed to the agent, rather than dumped on it wholesale, at the risk of saturating its context window and getting answers just as confident but just as unreliable. This is where two concepts worth remembering come in, *context engineering* and the **Model Context Protocol (MCP)**, which I'll cover in detail in the fifth and final article of this series.
|
||||
|
||||
## The series
|
||||
|
||||
This series will follow the order in which I built (and am still building) this stack:
|
||||
|
||||
1. **(this article) AIOps and the importance of network observability**: the general framework.
|
||||
2. **Topology as source of truth**: using network discovery and assurance to drive the rest of the stack.
|
||||
3. **Real-time telemetry**: the mechanics of metrics and time series, from device to dashboard.
|
||||
4. **Log management**: the piece still missing from my stack, which I need to tackle before going further.
|
||||
5. **Making the network "visible" to an AI agent**: context engineering, MCP, and the early steps of AIOps applied to my lab.
|
||||
|
||||
## Conclusion
|
||||
|
||||
None of this is revolutionary in isolation: network telemetry has been around for a long time. What changes is the frame. As long as you instrument a network for humans, missing data translates into a question mark, someone notices the gap and goes looking for the info elsewhere. The day you plug in an agent, that same gap no longer produces doubt: it produces a wrong answer, stated with confidence.
|
||||
|
||||
That's why observability comes first, and the agent comes last: not because AI is a side matter, but because it's blind by default, and you don't hand a system its eyes before building what it's supposed to see. If you're building something similar, I'd be curious to compare notes on your architecture choices.
|
||||
|
||||
## Resources
|
||||
|
||||
### Concepts
|
||||
- [What is AIOps?](https://www.ibm.com/think/topics/aiops) — definition and origin of the term (Gartner)
|
||||
- [Model Context Protocol — official documentation](https://modelcontextprotocol.io/docs/getting-started/intro)
|
||||
- [Introducing the Model Context Protocol](https://www.anthropic.com/news/model-context-protocol) — the original announcement, which frames well the problem MCP solves
|
||||
- [Effective context engineering for AI agents](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) — the reference article on context engineering, including the phenomenon of context window saturation
|
||||
- *Observability Engineering* (O'Reilly, 2nd edition), Charity Majors, Liz Fong-Jones, George Miranda: the field's reference book, with a chapter dedicated to the rise of agents and LLMs
|
||||
|
||||
### My repo
|
||||
- [arista-evpn-vxlan-clab](https://gitea.arnodo.fr/Damien/arista-evpn-vxlan-clab)
|
||||
602
content/blog/migration-gitlab-gitea/index.en.md
Normal file
602
content/blog/migration-gitlab-gitea/index.en.md
Normal file
@@ -0,0 +1,602 @@
|
||||
---
|
||||
title: "Self-Hosting and Hybrid Cloud: My Personal Infrastructure with Gitea and Scaleway"
|
||||
date: 2025-11-20
|
||||
authors:
|
||||
- name: Damien
|
||||
link: https://gitea.arnodo.fr/Damien
|
||||
tags:
|
||||
- Homelab
|
||||
- DevOps
|
||||
- Self-Hosting
|
||||
- Network Engineering
|
||||
---
|
||||
|
||||
A write-up on building my personal infrastructure: migrating from GitHub to self-hosted Gitea, and deploying ephemeral network labs on Scaleway. All powered by Proxmox and Wireguard.
|
||||
|
||||
<!--more-->
|
||||
|
||||
## Context and Motivations
|
||||
|
||||
As a network engineer working in automation and orchestration, I've always needed an environment to experiment and learn in. Until recently, I used GitHub for my code and ContainerLab locally/on AWS via [DevPod](/documentation/devpod/) for my network simulations.
|
||||
|
||||
But several needs emerged:
|
||||
|
||||
- **Learning**: Deploying and managing a full Git server with CI/CD
|
||||
- **Sovereignty**: Hosting my data in France (or at least in Europe), controlling my infrastructure
|
||||
- **Flexibility**: Being able to spin up network labs on demand without saturating my local machine
|
||||
- **Automation**: Scripting the complete provisioning of my environments
|
||||
|
||||
## The Overall Architecture
|
||||
|
||||

|
||||
|
||||
### Tech Stack
|
||||
|
||||
**Homelab**:
|
||||
- **Proxmox VE**: Hypervisor for VMs and LXC
|
||||
- **LXC Containers**: Gitea, runners, various services
|
||||
- **Ansible**: Configuration management and updates
|
||||
- **Grafana/Prometheus/Loki**: Monitoring and observability
|
||||
|
||||
**Public exposure**:
|
||||
- **Scaleway Dedibox**: Dedicated instance with a fixed IP
|
||||
- **Nginx Proxy Manager**: Reverse proxy with automatic SSL
|
||||
- **Wireguard VPN**: Secure tunnel between Scaleway and the homelab
|
||||
|
||||
**Scaleway Cloud**:
|
||||
- **Object Storage**: Hosting for the Hugo blog (S3-compatible)
|
||||
- **Instances**: Ephemeral network labs provisioned on demand
|
||||
- **Scaleway CLI**: Full automation
|
||||
|
||||
## Part 1: Migrating from GitHub to Self-Hosted Gitea
|
||||
|
||||
### Why Gitea?
|
||||
|
||||
[Gitea](https://gitea.io/) is a lightweight Git server, perfect for self-hosting:
|
||||
|
||||
- **Lightweight**: Ideal for an LXC on Proxmox
|
||||
- **GitHub Actions compatible**: Migrate workflows without rewriting them
|
||||
- **Full-featured**: Issues, PRs, CI/CD, webhooks
|
||||
- **Open-source**: Active community
|
||||
|
||||
### Deployment with Proxmox Helper Scripts
|
||||
|
||||
Rather than configuring everything manually, I use the excellent [Proxmox Helper Scripts](https://community-scripts.github.io/ProxmoxVE/), which automate the creation of preconfigured LXCs.
|
||||
|
||||
**Installing Gitea**:
|
||||
|
||||
```bash
|
||||
# On the Proxmox node, run the script
|
||||
bash -c "$(wget -qLO - https://github.com/community-scripts/ProxmoxVE/raw/main/ct/gitea.sh)"
|
||||
```
|
||||
|
||||
The script:
|
||||
- Creates a Debian 12 LXC
|
||||
- Installs Gitea and sqlite
|
||||
- Configures the systemd services
|
||||
- Sets up the environment with the correct permissions
|
||||
|
||||
**Post-installation configuration**:
|
||||
- Web access: `http://<IP_LXC>:3000`
|
||||
- Initial setup via the interface
|
||||
- Site URL: `https://gitea.arnodo.fr`
|
||||
|
||||
### Network Architecture: Wireguard + Nginx Proxy Manager
|
||||
|
||||
**The problem**: Gitea runs inside my homelab (private IP), but I want to access it from the Internet.
|
||||
|
||||
**The solution**:
|
||||
1. A **Scaleway Dedibox** with a fixed public IP and Nginx Proxy Manager
|
||||
2. A **Wireguard VPN** between the Dedibox and the homelab
|
||||
3. The reverse proxy routes `gitea.arnodo.fr` to the LXC's private IP through the tunnel
|
||||
|
||||
**Wireguard configuration (homelab side)**:
|
||||
|
||||
```ini
|
||||
[Interface]
|
||||
Address = 10.0.0.1/24
|
||||
PrivateKey = <private_key>
|
||||
ListenPort = 51820
|
||||
|
||||
[Peer]
|
||||
# Dedibox Scaleway
|
||||
PublicKey = <dedibox_public_key>
|
||||
AllowedIPs = 10.0.0.2/32
|
||||
Endpoint = <dedibox_public_ip>:51820
|
||||
PersistentKeepalive = 25
|
||||
```
|
||||
|
||||
**Nginx Proxy Manager**:
|
||||
- Proxy Host: `gitea.arnodo.fr`
|
||||
- Forward Hostname/IP: `10.0.0.x` (Gitea LXC IP over Wireguard)
|
||||
- Forward Port: `3000`
|
||||
- SSL: automatic Let's Encrypt
|
||||
- Websockets: Enabled
|
||||
|
||||
### Management with Ansible
|
||||
|
||||
To keep Gitea up to date and manage configurations, I use Ansible.
|
||||
|
||||
**Update playbook** (`update-gitea.yml`):
|
||||
|
||||
```yaml
|
||||
---
|
||||
- name: Update Gitea
|
||||
hosts: gitea
|
||||
become: yes
|
||||
tasks:
|
||||
- name: Update apt cache
|
||||
apt:
|
||||
update_cache: yes
|
||||
cache_valid_time: 3600
|
||||
|
||||
- name: Upgrade Gitea and system packages
|
||||
apt:
|
||||
upgrade: dist
|
||||
autoremove: yes
|
||||
autoclean: yes
|
||||
|
||||
- name: Restart Gitea service
|
||||
systemd:
|
||||
name: gitea
|
||||
state: restarted
|
||||
enabled: yes
|
||||
|
||||
- name: Check Gitea version
|
||||
command: gitea --version
|
||||
register: gitea_version
|
||||
|
||||
- debug:
|
||||
msg: "{{ gitea_version.stdout }}"
|
||||
```
|
||||
|
||||
**Execution**:
|
||||
|
||||
```bash
|
||||
ansible-playbook -i inventory.ini update-gitea.yml
|
||||
```
|
||||
|
||||
### Monitoring with Grafana
|
||||
|
||||
Gitea exposes Prometheus metrics (https://docs.gitea.com/administration/config-cheat-sheet#metrics-metrics)
|
||||
Configuration:
|
||||
|
||||
**In Gitea (`app.ini`)**:
|
||||
|
||||
```ini
|
||||
[metrics]
|
||||
ENABLED = true
|
||||
TOKEN = <secret_token>
|
||||
```
|
||||
|
||||
**Prometheus scrape config**:
|
||||
|
||||
```yaml
|
||||
scrape_configs:
|
||||
- job_name: 'gitea'
|
||||
metrics_path: /metrics
|
||||
bearer_token: '<secret_token>'
|
||||
static_configs:
|
||||
- targets: ['<gitea_lxc_ip>:3000']
|
||||
```
|
||||
|
||||
**Grafana dashboard** (https://grafana.com/docs/grafana-cloud/monitor-infrastructure/integrations/integration-reference/integration-gitea/#gitea-integration-for-grafana-cloud):
|
||||
- Number of repositories, users
|
||||
- HTTP requests (rate, latency)
|
||||
- CI/CD runner status
|
||||
- LXC CPU/RAM usage
|
||||
|
||||
### Migrating Code from GitHub
|
||||
|
||||
Simple and fast:
|
||||
Use Gitea's import feature (Settings > New Migration > GitHub), which also migrates issues and releases.
|
||||
|
||||
### CI/CD: Deploying Hugo to Scaleway Object Storage
|
||||
|
||||
My Hugo blog deploys automatically to Scaleway Object Storage on every push.
|
||||
|
||||
**Installing the Gitea Runner** (https://docs.gitea.com/usage/actions/act-runner)
|
||||
|
||||
Create the system user for the runner:
|
||||
|
||||
```bash
|
||||
useradd -r -m -d /var/lib/gitea-runner -s /bin/bash gitea-runner
|
||||
```
|
||||
|
||||
Here's a small script that can help install the runner directly inside an LXC:
|
||||
|
||||
```bash
|
||||
sudo apt install -y jq curl tar # si pas déjà
|
||||
|
||||
LATEST=$(curl -s 'https://gitea.com/api/v1/repos/gitea/act_runner/releases' | jq -r '.[0].tag_name')
|
||||
echo "Latest act_runner: $LATEST"
|
||||
|
||||
# construire URL de binaire (nommage used: act_runner-<os>-<arch>)
|
||||
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
|
||||
ARCH=$(uname -m)
|
||||
# Certains serveurs distribuent binaire sans tar; adapter si archive
|
||||
URL="https://gitea.com/gitea/act_runner/releases/download/${LATEST}/act_runner-${LATEST#v}-${OS}-${ARCH}"
|
||||
|
||||
# essayer télécharger binaire
|
||||
curl -fL "$URL" -o /tmp/act_runner || {
|
||||
echo "Téléchargement direct échoué — vérifier le nom exact sur la page release." >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
sudo mv /tmp/act_runner /usr/local/bin/act_runner
|
||||
sudo chmod +x /usr/local/bin/act_runner
|
||||
```
|
||||
|
||||
and to verify:
|
||||
|
||||
```bash
|
||||
/usr/local/bin/act_runner --version
|
||||
```
|
||||
|
||||
>[!NOTE]
|
||||
> It's important to register the runner so it's recognized by Gitea.
|
||||
> For more information on configuring the runner, check the official Gitea documentation.
|
||||
> https://docs.gitea.io/fr/docs/usage/actions/runner/
|
||||
|
||||
**Gitea Actions workflow** (`.gitea/workflows/deploy.yml`):
|
||||
|
||||
```yaml
|
||||
name: Build and Deploy Hugo
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build_and_deploy:
|
||||
if: github.event.pull_request.merged == true
|
||||
name: Deploy Hugo Website
|
||||
runs-on: self-hosted
|
||||
|
||||
container:
|
||||
image: debian:bookworm-slim
|
||||
|
||||
steps:
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y git curl ca-certificates wget
|
||||
|
||||
- name: Install Hugo
|
||||
run: |
|
||||
wget https://github.com/gohugoio/hugo/releases/download/v0.152.2/hugo_extended_withdeploy_0.152.2_linux-amd64.deb -O /tmp/hugo.deb
|
||||
dpkg -i /tmp/hugo.deb
|
||||
|
||||
- name: Checkout code
|
||||
run: |
|
||||
git clone --recurse-submodules https://gitea.arnodo.fr/Damien/Notebook.git /tmp/workspace
|
||||
cd /tmp/workspace
|
||||
git checkout ${{ github.sha }}
|
||||
|
||||
- name: Build site
|
||||
run: /usr/local/bin/hugo
|
||||
working-directory: /tmp/workspace
|
||||
|
||||
- name: Deploy to Scaleway
|
||||
run: /usr/local/bin/hugo deploy --force --maxDeletes -1
|
||||
working-directory: /tmp/workspace
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.SCW_ACCESS_KEY }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.SCW_SECRET_KEY }}
|
||||
|
||||
```
|
||||
|
||||
**Hugo configuration** (`hugo.yaml`):
|
||||
|
||||
```yaml
|
||||
deployment:
|
||||
targets:
|
||||
- name: "notebook-arnodo-fr"
|
||||
URL: "s3://notebook-arnodo-fr?endpoint=https://s3.fr-par.scw.cloud®ion=fr-par"
|
||||
```
|
||||
|
||||
**Scaleway Object Storage configuration**:
|
||||
1. Create a `notebook-arnodo-fr` bucket
|
||||
2. Enable "Static Website Hosting" mode
|
||||
3. Generate API credentials (Access Key + Secret Key)
|
||||
4. Add them as secrets in Gitea (Settings > Secrets > Actions)
|
||||
|
||||
**Deployment**:
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "New blog post"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
The workflow triggers automatically, Hugo builds the site, and deploys it to Scaleway Object Storage. The site is instantly available via the CDN.
|
||||
|
||||
## Part 2: Network Labs on Scaleway
|
||||
|
||||
### The Problem
|
||||
|
||||
Running ContainerLab with several Arista EOS instances locally is:
|
||||
- **Resource-hungry**: 4-8 GB RAM per cEOS container
|
||||
- **Local-only**: No access from outside
|
||||
- **Conflict-prone**: With other Docker/K8s services
|
||||
|
||||
### The Solution: On-Demand Scaleway Instances
|
||||
|
||||
**Concept**:
|
||||
- Spin up a Scaleway instance whenever I need a lab
|
||||
- Automatically install ContainerLab/the VPN via cloud-init
|
||||
- Destroy the instance after use
|
||||
- Billed by the hour (< €1 for a few hours of lab time)
|
||||
|
||||
### Automation Script: Scaleway CLI
|
||||
|
||||
I built a Bash script that manages the entire lifecycle of a lab instance.
|
||||
|
||||
**Features**:
|
||||
- **Creation**: Instance + Security Group (SSH from my IP only)
|
||||
- **Start/Stop**: Instance management
|
||||
- **Deletion**: Full cleanup (instance, volumes, IP, SG)
|
||||
|
||||
**Script structure** (`scaleway-instance.sh`):
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Configuration
|
||||
INSTANCE_NAME="NetLab"
|
||||
ZONE="fr-par-1"
|
||||
IMAGE="debian_bookworm"
|
||||
VOLUME_SIZE=20 # GB
|
||||
USER_DATA_FILE="user_data.txt"
|
||||
SECURITY_GROUP_NAME="${INSTANCE_NAME}-SG"
|
||||
|
||||
# Détecte l'IP publique actuelle
|
||||
get_public_ip() {
|
||||
curl -4 -s ifconfig.me
|
||||
}
|
||||
|
||||
# Crée un Security Group limitant SSH à l'IP publique
|
||||
create_or_update_security_group() {
|
||||
PUBLIC_IP=$(get_public_ip)
|
||||
# Crée le SG avec inbound SSH uniquement depuis PUBLIC_IP/32
|
||||
# ...
|
||||
}
|
||||
|
||||
# Actions : create, start, stop, delete
|
||||
case "$1" in
|
||||
start)
|
||||
# Démarre l'instance existante
|
||||
scw instance server start "$INSTANCE_ID" --wait
|
||||
;;
|
||||
stop)
|
||||
# Arrête l'instance
|
||||
scw instance server stop "$INSTANCE_ID" --wait
|
||||
;;
|
||||
delete)
|
||||
# Supprime instance + volumes + IP + SG
|
||||
scw instance server terminate "$INSTANCE_ID" --with-ip --with-block
|
||||
scw instance security-group delete "$SG_ID"
|
||||
;;
|
||||
*)
|
||||
# Crée une nouvelle instance
|
||||
create_instance "$1" # Type d'instance (DEV1-S, GP1-XS, ...)
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
### Cloud-Init: Automatic Configuration
|
||||
|
||||
The `user_data.txt` file contains the cloud-init instructions to automatically provision the instance.
|
||||
|
||||
**Example** (`user_data.txt`):
|
||||
|
||||
```yaml
|
||||
#cloud-config
|
||||
package_update: true
|
||||
package_upgrade: true
|
||||
|
||||
packages:
|
||||
- git
|
||||
- curl
|
||||
- docker.io
|
||||
- docker-compose
|
||||
|
||||
runcmd:
|
||||
# Installation de ContainerLab
|
||||
- bash -c "$(curl -sL https://get.containerlab.dev)"
|
||||
|
||||
# Clone d'un repo avec des topologies
|
||||
- git clone https://gitea.arnodo.fr/Damien/network-labs.git /root/labs
|
||||
|
||||
# Démarrage d'une topologie par défaut
|
||||
- cd /root/labs && containerlab deploy -t spine-leaf.clab.yml
|
||||
```
|
||||
|
||||
### Practical Usage
|
||||
|
||||
**Create a lab**:
|
||||
|
||||
```bash
|
||||
# Crée une instance DEV1-S avec 20 GB de stockage
|
||||
./scaleway-instance.sh DEV1-S 20
|
||||
|
||||
# Attend quelques minutes pour cloud-init
|
||||
# Récupère l'IP publique
|
||||
scw instance server list name=NetLab -o json | jq -r '.servers[0].public_ip.address'
|
||||
|
||||
# SSH vers l'instance
|
||||
ssh root@<IP_PUBLIQUE>
|
||||
|
||||
# ContainerLab est déjà lancé !
|
||||
containerlab inspect
|
||||
```
|
||||
|
||||
**Destroy the lab**:
|
||||
|
||||
```bash
|
||||
./scaleway-instance.sh delete
|
||||
```
|
||||
|
||||
### Raycast Integration
|
||||
|
||||
To make things even simpler, I created a Raycast script that lets me manage my instances directly from my Mac.
|
||||
|
||||
**Raycast script**:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# @raycast.schemaVersion 1
|
||||
# @raycast.title Scaleway Instance
|
||||
# @raycast.mode silent
|
||||
# @raycast.icon 🖥️
|
||||
# @raycast.argument1 { "type": "text", "placeholder": "Action or instance type" }
|
||||
# @raycast.argument2 { "type": "text", "placeholder": "Volume size", "optional": true }
|
||||
# @raycast.packageName NetLab
|
||||
|
||||
/path/to/scaleway-instance.sh "$1" "$2"
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
- `⌘ + Space` → "Scaleway Instance DEV1-S" → Creates the instance
|
||||
- `⌘ + Space` → "Scaleway Instance delete" → Deletes the instance
|
||||
|
||||
### Use Case: BGP/EVPN Lab with Arista
|
||||
|
||||
**ContainerLab topology** (`spine-leaf.clab.yml`):
|
||||
|
||||
```yaml
|
||||
name: evpn-lab
|
||||
|
||||
topology:
|
||||
nodes:
|
||||
spine1:
|
||||
kind: ceos
|
||||
image: ceos:latest
|
||||
spine2:
|
||||
kind: ceos
|
||||
image: ceos:latest
|
||||
leaf1:
|
||||
kind: ceos
|
||||
image: ceos:latest
|
||||
leaf2:
|
||||
kind: ceos
|
||||
image: ceos:latest
|
||||
|
||||
links:
|
||||
- endpoints: ["spine1:eth1", "leaf1:eth1"]
|
||||
- endpoints: ["spine1:eth2", "leaf2:eth1"]
|
||||
- endpoints: ["spine2:eth1", "leaf1:eth2"]
|
||||
- endpoints: ["spine2:eth2", "leaf2:eth2"]
|
||||
```
|
||||
|
||||
**Workflow**:
|
||||
1. Create the Scaleway instance
|
||||
2. Cloud-init deploys the topology
|
||||
3. Configure BGP/EVPN via Ansible or manually
|
||||
4. Test, experiment
|
||||
5. Destroy the instance
|
||||
|
||||
**Cost**: DEV1-S instance (2 vCPU, 2GB) = ~€0.015/hour. 4 hours of lab time = €0.06.
|
||||
|
||||
## Digital Sovereignty: Why It Matters
|
||||
|
||||
This hybrid infrastructure reflects a personal conviction about digital sovereignty.
|
||||
|
||||
### The Context
|
||||
|
||||
In my work as a network engineer, I see how important it is to be in control of your own infrastructure.
|
||||
|
||||
Choosing Scaleway (part of the Iliad group, French) and self-hosting Gitea means:
|
||||
- **Supporting the European tech ecosystem**
|
||||
- **Ensuring GDPR compliance**: French jurisdiction
|
||||
- **Reducing latency**: Datacenters in Paris
|
||||
- **Understanding**: Owning your entire chain end-to-end
|
||||
|
||||
### Learning by Doing
|
||||
|
||||
As a network professional (Arista, BGP/EVPN, automation), self-hosting lets me:
|
||||
- Apply Infrastructure as Code principles
|
||||
- Deeply understand CI/CD mechanics
|
||||
- Experiment without limits
|
||||
- Reproduce professional environments
|
||||
|
||||
## Assessment
|
||||
|
||||
### What Works Well
|
||||
|
||||
**Self-hosted Gitea**:
|
||||
- Very fast and stable
|
||||
- Proxmox Helper Scripts = 5-minute install
|
||||
- Ansible handles updates cleanly
|
||||
- Grafana monitors everything
|
||||
|
||||
**Wireguard + Nginx Proxy Manager**:
|
||||
- Secure exposure of the homelab
|
||||
- Excellent performance
|
||||
- Simple configuration
|
||||
|
||||
**Scaleway labs**:
|
||||
- Provisioning in 3 minutes
|
||||
- Total flexibility (size, duration)
|
||||
- Predictable costs (hourly billing)
|
||||
|
||||
**CI/CD Hugo → Scaleway Object Storage**:
|
||||
- Push-to-deploy in 2 minutes
|
||||
- Free (a few cents/month for storage)
|
||||
- Built-in CDN = ultra-fast site
|
||||
|
||||
### The Challenges
|
||||
|
||||
**Initial complexity**:
|
||||
- Wireguard + reverse proxy = learning curve
|
||||
- First Proxmox/LXC setup = a few hours
|
||||
|
||||
**Maintenance**:
|
||||
- Responsibility for updates (thankfully Ansible helps!)
|
||||
- Monitoring to set up yourself
|
||||
- Backups to automate
|
||||
|
||||
**Dependencies**:
|
||||
- If the Dedibox goes down, Gitea becomes unreachable
|
||||
- Solution: failover with a second Dedibox or VPS (coming soon)
|
||||
|
||||
## Next Steps
|
||||
|
||||
- **High availability**: A second Dedibox for failover
|
||||
- **Automatic backup**: Scripts to back up Gitea to Scaleway Object Storage
|
||||
- **More automation**: Terraform to provision the entire Scaleway infrastructure
|
||||
- **Arista MCP**: Building an MCP server to interact with network equipment via local LLMs
|
||||
- **Netbox integration**: Webhook from Netbox to a network validation pipeline
|
||||
|
||||
## Conclusion
|
||||
|
||||
This hybrid infrastructure (Proxmox homelab + Scaleway cloud) offers the best of both worlds:
|
||||
|
||||
- **Control**: Sensitive data (code, configurations) stays in the homelab
|
||||
- **Flexibility**: Cloud resources for one-off needs
|
||||
- **Learning**: A complete environment to experiment in
|
||||
- **Sovereignty**: Everything hosted in France, with European providers
|
||||
|
||||
Self-hosting isn't just about costs (spoiler: I pay about as much as before, if not more), but about learning, mastery, and a deep understanding of systems.
|
||||
|
||||
For a network or DevOps engineer, it's the ideal environment to reproduce professional use cases and build up skills.
|
||||
|
||||
## Resources
|
||||
|
||||
### Documentation
|
||||
- [Gitea](https://docs.gitea.io/)
|
||||
- [Proxmox Helper Scripts](https://community-scripts.github.io/ProxmoxVE/)
|
||||
- [Scaleway CLI](https://www.scaleway.com/en/cli/)
|
||||
- [ContainerLab](https://containerlab.dev/)
|
||||
- [Nginx Proxy Manager](https://nginxproxymanager.com/)
|
||||
|
||||
### My Repos
|
||||
- [Blog Hugo](https://gitea.arnodo.fr/Damien/blog)
|
||||
- [Network Labs](https://gitea.arnodo.fr/Damien/arista-evpn-vxlan-clab) (ContainerLab topologies)
|
||||
- [Scaleway Scripts](https://gitea.arnodo.fr/Damien/scaleway-automation)
|
||||
|
||||
### Community
|
||||
- [Proxmox Forum](https://forum.proxmox.com/)
|
||||
- [r/homelab](https://reddit.com/r/homelab)
|
||||
- [ContainerLab Slack](https://containerlab.dev/)
|
||||
Reference in New Issue
Block a user