Compare commits
6 Commits
french
...
43b2c213e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
43b2c213e4
|
|||
|
b198db2725
|
|||
|
13372066c3
|
|||
|
6d54cbef61
|
|||
|
3062cba541
|
|||
|
f0810eb8e5
|
@@ -1,9 +1,9 @@
|
||||
# Package versions
|
||||
ARG HUGO_VERSION="latest"
|
||||
ARG CADDY_VERSION="2.10.0"
|
||||
ARG HUGO_VERSION="0.152.2"
|
||||
ARG CADDY_VERSION="2.10.2"
|
||||
|
||||
# Stage 1: Build
|
||||
FROM git.brds.ca/d-b.ca/hugo-builder:${HUGO_VERSION} AS builder
|
||||
FROM core.harbor.brds.ca/d-b.ca/hugo-builder:${HUGO_VERSION} AS builder
|
||||
WORKDIR /project
|
||||
COPY . .
|
||||
RUN hugo --minify build
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
disabled = false
|
||||
languageCode = "fr"
|
||||
languageName = "Français"
|
||||
weight = 2
|
||||
title = "Drew Bowering"
|
||||
|
||||
[params]
|
||||
displayName = "FR"
|
||||
isoCode = "fr"
|
||||
rtl = false
|
||||
dateFormat = "2 January 2006"
|
||||
logo = "img/d-bca_logo_light.png"
|
||||
secondaryLogo = "img/d-bca_logo_dark.png"
|
||||
description = "Drew Bowering's personal website"
|
||||
# copyright = "Copy, _right?_ :thinking_face:"
|
||||
|
||||
[params.author]
|
||||
name = "Drew Bowering"
|
||||
# email = "drew@d-b.ca"
|
||||
image = "img/DrewBowering.jpg"
|
||||
# imageQuality = 96
|
||||
headline = "Architecte informatique, Développeur, Canoeiste, Tubiste"
|
||||
# bio = "A little bit about me"
|
||||
links = [
|
||||
{ email = "mailto:drew@d-b.ca" },
|
||||
# { link = "https://link-to-some-website.com/" },
|
||||
# { amazon = "https://www.amazon.com/hz/wishlist/ls/wishlist-id" },
|
||||
# { apple = "https://www.apple.com" },
|
||||
# { blogger = "https://username.blogspot.com/" },
|
||||
# { bluesky = "https://bsky.app/profile/username" },
|
||||
# { codepen = "https://codepen.io/username" },
|
||||
# { dev = "https://dev.to/username" },
|
||||
# { discord = "https://discord.gg/invitecode" },
|
||||
# { dribbble = "https://dribbble.com/username" },
|
||||
{ facebook = "https://www.facebook.com/drew.bowering" },
|
||||
# { flickr = "https://www.flickr.com/photos/username/" },
|
||||
# { foursquare = "https://foursquare.com/username" },
|
||||
{ github = "https://github.com/drewbowering" },
|
||||
# { gitlab = "https://gitlab.com/username" },
|
||||
# { google = "https://www.google.com/" },
|
||||
# { hashnode = "https://username.hashnode.dev" },
|
||||
# { instagram = "https://instagram.com/username" },
|
||||
# { itch-io = "https://username.itch.io" },
|
||||
# { keybase = "https://keybase.io/username" },
|
||||
# { kickstarter = "https://www.kickstarter.com/profile/username" },
|
||||
# { lastfm = "https://lastfm.com/user/username" },
|
||||
{ linkedin = "https://www.linkedin.com/in/drew-bowering/" },
|
||||
# { mastodon = "https://mastodon.instance/@username" },
|
||||
# { medium = "https://medium.com/username" },
|
||||
# { microsoft = "https://www.microsoft.com/" },
|
||||
# { orcid = "https://orcid.org/userid" },
|
||||
# { patreon = "https://www.patreon.com/username" },
|
||||
# { pinterest = "https://pinterest.com/username" },
|
||||
# { reddit = "https://reddit.com/user/username" },
|
||||
# { researchgate = "https://www.researchgate.net/profile/username" },
|
||||
# { slack = "https://workspace.url/team/userid" },
|
||||
# { snapchat = "https://snapchat.com/add/username" },
|
||||
# { soundcloud = "https://soundcloud.com/username" },
|
||||
# { spotify = "https://open.spotify.com/user/userid" },
|
||||
# { stack-overflow = "https://stackoverflow.com/users/userid/username" },
|
||||
# { steam = "https://steamcommunity.com/profiles/userid" },
|
||||
# { telegram = "https://t.me/username" },
|
||||
# { threads = "https://www.threads.net/@username" },
|
||||
# { tiktok = "https://tiktok.com/@username" },
|
||||
# { tumblr = "https://username.tumblr.com" },
|
||||
# { twitch = "https://twitch.tv/username" },
|
||||
# { twitter = "https://twitter.com/username" },
|
||||
# { x-twitter = "https://twitter.com/username" },
|
||||
# { whatsapp = "https://wa.me/phone-number" },
|
||||
# { youtube = "https://youtube.com/username" },
|
||||
# { ko-fi = "https://ko-fi.com/username" },
|
||||
# { codeberg = "https://codeberg.org/username"},
|
||||
]
|
||||
@@ -1,69 +0,0 @@
|
||||
# -- Main Menu --
|
||||
# The main menu is displayed in the header at the top of the page.
|
||||
# Acceptable parameters are name, pageRef, page, url, title, weight.
|
||||
#
|
||||
# The simplest menu configuration is to provide:
|
||||
# name = The name to be displayed for this menu link
|
||||
# pageRef = The identifier of the page or section to link to
|
||||
#
|
||||
# By default the menu is ordered alphabetically. This can be
|
||||
# overridden by providing a weight value. The menu will then be
|
||||
# ordered by weight from lowest to highest.
|
||||
|
||||
[[main]]
|
||||
name = "Posts"
|
||||
pageRef = "posts"
|
||||
weight = 10
|
||||
|
||||
#[[main]]
|
||||
# name = "Parent"
|
||||
# weight = 20
|
||||
|
||||
#[[main]]
|
||||
# name = "example sub-menu 1"
|
||||
# parent = "Parent"
|
||||
# pageRef = "posts"
|
||||
# weight = 20
|
||||
|
||||
#[[main]]
|
||||
# name = "example sub-menu 2"
|
||||
# parent = "Parent"
|
||||
# pageRef = "posts"
|
||||
# weight = 20
|
||||
|
||||
#[[subnavigation]]
|
||||
# name = "An interesting topic"
|
||||
# pageRef = "tags/interesting-topic"
|
||||
# weight = 10
|
||||
|
||||
#[[subnavigation]]
|
||||
# name = "My Awesome Category"
|
||||
# pre = "github"
|
||||
# pageRef = "categories/awesome"
|
||||
# weight = 20
|
||||
|
||||
#[[main]]
|
||||
# name = "Categories"
|
||||
# pageRef = "categories"
|
||||
# weight = 20
|
||||
|
||||
#[[main]]
|
||||
# name = "Tags"
|
||||
# pageRef = "tags"
|
||||
# weight = 30
|
||||
|
||||
|
||||
# -- Footer Menu --
|
||||
# The footer menu is displayed at the bottom of the page, just before
|
||||
# the copyright notice. Configure as per the main menu above.
|
||||
|
||||
|
||||
# [[footer]]
|
||||
# name = "Tags"
|
||||
# pageRef = "tags"
|
||||
# weight = 10
|
||||
|
||||
# [[footer]]
|
||||
# name = "Categories"
|
||||
# pageRef = "categories"
|
||||
# weight = 20
|
||||
BIN
content/posts/new-homenet-architecture/featured.jpg
Executable file
BIN
content/posts/new-homenet-architecture/featured.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 MiB |
4
content/posts/new-homenet-architecture/first_net.svg
Normal file
4
content/posts/new-homenet-architecture/first_net.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 349 KiB |
4
content/posts/new-homenet-architecture/first_new_net.svg
Normal file
4
content/posts/new-homenet-architecture/first_new_net.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 307 KiB |
241
content/posts/new-homenet-architecture/index.md
Normal file
241
content/posts/new-homenet-architecture/index.md
Normal file
@@ -0,0 +1,241 @@
|
||||
---
|
||||
title: "New Home Network Architecture"
|
||||
date: "2025-11-20T07:30:00-07:00"
|
||||
description: "The architecture of my current home network"
|
||||
summary: "I decided to rearchitect my home network, and this is the result."
|
||||
---
|
||||
|
||||
Almost two years ago, I decided to undertake another major overhaul of my home
|
||||
network. It is the latest step in the long evolution of my personal systems,
|
||||
and is now in a state that I'm fairly happy with.
|
||||
|
||||
## History
|
||||
|
||||
I've been networking my computers together for decades. The first example that
|
||||
could reasonably be called a network was around 1996, where I connected my
|
||||
Amiga 1200 and Amiga 3000T together over a null-modem serial cable and ran IP
|
||||
between them. I eventually got some Ethernet hardware and ran mostly simple
|
||||
flat networks for a while.
|
||||
|
||||
### Orthodoxy
|
||||
|
||||
After working professionally designing and building networks and IT systems, I
|
||||
had learned a few rules. Networks in particular always consisted of several
|
||||
key elements:
|
||||
|
||||
- **Three Tiers**. You needed Core, Distribution, and Access switches. This
|
||||
helps to scale the network and keep things well balanced.
|
||||
- **VLANs**. Every packet needs to go through a VLAN. VLANs keep the network
|
||||
segregated for security, and allow for smaller broadcast domains.
|
||||
- **Firewalls**. The network has to protect the vulnerable endpoints by
|
||||
blocking packets that aren't permitted by policy.
|
||||
- **Virtualization**. Virtualize everything to decouple the systems from the
|
||||
underlying infrastructure, keeping them portable.
|
||||
|
||||
Naturally, I took these ideas home and built my personal networks accordingly.
|
||||
|
||||
### Something's not right
|
||||
|
||||
Eventually, I had built myself a network that looked something like the
|
||||
diagram below. I kept to the principles I was familiar with, and this was the
|
||||
result.
|
||||
|
||||

|
||||
|
||||
I had VLANs for everything coming from the VM hosts to the physical switches.
|
||||
Traffic would loop through the tiers (in, out, and back in), routing
|
||||
everything like it's supposed to, but this redundancy introduced unecessary
|
||||
complexity. I had more VMs acting as routers than I had VMs doing productive
|
||||
activity.
|
||||
|
||||
When I decided that I would like to start using IPv6 in my network, everything
|
||||
doubled. I kept the IPv4 and IPv6 traffic on separate VLANs, and had separate
|
||||
routers for everything, doubling what's in that diagram. It didn't take me
|
||||
long to notice that this wasn't working out, and started to think about a new
|
||||
approach.
|
||||
|
||||
## New Design
|
||||
|
||||
When I started to think about what I really needed in my home network, I
|
||||
came up with several principles:
|
||||
|
||||
- **IPv6 First**. Using IPv6 makes so many things simpler. No NAT. Subnets
|
||||
don't run out of addresses. Link-local addressing that works, and is useful.
|
||||
No DHCP. *No NAT!*
|
||||
- **Zero Trust**. I'm a fan of zero-trust architectures. When you place the
|
||||
responsibility for security on the network, it tends to get complicated. You
|
||||
make physical design choices around isolating packets on the right segments
|
||||
and getting them to the right firewalls, where the network should focus on
|
||||
moving traffic through it as quickly as possible. This principle of simply
|
||||
getting packets to where they need to be is how the Internet scales so well.
|
||||
We can keep physical LANs simple and efficient like this, and leave the
|
||||
security concerns to the endpoints. The endpoints need to be secure anyways,
|
||||
and we now have more modern and effective tools to help us.
|
||||
- **Network Fabric**. Rather than redoing the same 3-tier model I was used to,
|
||||
I wanted to do something more efficient. I was inspired by an article
|
||||
written at Facebook about their
|
||||
["data center fabric"](https://engineering.fb.com/2014/11/14/production-engineering/introducing-data-center-fabric-the-next-generation-facebook-data-center-network/)
|
||||
architecture. This is obviously much larger than what anyone needs in a home
|
||||
network, but I thought that these were good ideas that I could use.
|
||||
- **Routing Protocols**. I've traditionally used [OSPF](https://en.wikipedia.org/wiki/Open_Shortest_Path_First)
|
||||
in the networks I've operated. When I decided to implement IPv6 in the
|
||||
network, I was using [Quagga](https://www.nongnu.org/quagga/) for the
|
||||
routing software. It doesn't support OSPF areas in OSPFv3 for IPv6, which
|
||||
made me reconsider. I settled on [IS-IS](https://en.wikipedia.org/wiki/IS-IS),
|
||||
as it supports both IPv4 and IPv6 at the same time, and could do everything
|
||||
that I needed it to do.
|
||||
|
||||
### Refresh, Version 1
|
||||
|
||||
My first refreshed network design looked like this:
|
||||
|
||||

|
||||
|
||||
I did away with all of the traditional complexity, and established two network
|
||||
"fabrics" that all of the traffic would pass through. The fabrics do not
|
||||
connect directly at layer 2, each side is separate. Each fabric is a single
|
||||
flat network, there are no VLANs.
|
||||
|
||||
These were the key design decisions:
|
||||
|
||||
- **Routing over switching**. Every physical device connecting into the fabric
|
||||
switches would conform to a basic set of requirements:
|
||||
1. It will act as a router, running IS-IS to communicate with every other
|
||||
device on the fabric switches.
|
||||
2. Each endpoint uses a single loopback address as its network identity. It
|
||||
advertises this address to the other nodes, as well as the subnets that
|
||||
it can route to.
|
||||
3. Routes are advertised over both fabrics, enabling [ECMP](https://en.wikipedia.org/wiki/Equal-cost_multi-path_routing)
|
||||
for higher availability and bandwidth.
|
||||
- **IPv6 first**. The access routers and Wifi routers only had IPv6 subnets
|
||||
available for client devices. This allowed me to do away with DHCP services
|
||||
on the network, only using [SLAAC](https://en.wikipedia.org/wiki/IPv6_address#Stateless_address_autoconfiguration_(SLAAC)).
|
||||
Access to IPv4-only resources was through the use of
|
||||
[DNS64 and the NAT64 gateway](https://en.wikipedia.org/wiki/IPv6_transition_mechanism#NAT64).
|
||||
|
||||
## Next Generation
|
||||
|
||||
At this point, I was fairly happy with the result. The network was efficient
|
||||
and much easier to maintain. It was faster, thanks to ECMP and having fewer
|
||||
hops. As I was using it however, I started to think about the next set of
|
||||
improvements.
|
||||
|
||||
- **Single point routers**. I had only single devices acting as my edge,
|
||||
access, and Wifi routers. I wanted some redundancy in case one failed, and
|
||||
to make maintenance more transparent with the ability to fail over.
|
||||
- **Virtual Machines**. Most of my workloads were set up as virtual machines.
|
||||
I wanted to migrate to [Kubernetes](https://kubernetes.io/) as everything
|
||||
I was running could be run there, along with many other benefits.
|
||||
- **NAT64**. Here I was running IPv6 to get away from needing NAT, but I still
|
||||
needed NAT. This setup was mostly working fine, but there were a few small
|
||||
irritations:
|
||||
- There are not very many NAT64 implementations. I was using [JooL](https://jool.mx/);
|
||||
it's a non-standard Linux kernel module, and it's not really actively
|
||||
developed anymore.
|
||||
- The path from the NAT64 gateway out the Edge router is still IPv4, and I
|
||||
still need to do NAT for IPv4 at the edge.
|
||||
- Applications connecting directly to an IPv4 address weren't able to do so.
|
||||
I could use [464XLAT](https://en.wikipedia.org/wiki/IPv6_transition_mechanism#464XLAT)
|
||||
on endpoints that supported it, but it's yet another thing to set up.
|
||||
- There's the occasional device that still doesn't support IPv6, or doesn't
|
||||
support it properly.
|
||||
- **BGP**. I was purely using IS-IS throughout the network, but Kubernetes
|
||||
CNIs that work on bare metal systems like mine rely on BGP to advertise
|
||||
routes into the network. I'd have to work out how to incorporate this.
|
||||
- **Easier WiFi**. I was using a WiFi router running [OpenWRT](https://openwrt.org/),
|
||||
connecting to both fabrics and running IS-IS just like everything else.
|
||||
OpenWRT is great, but it is challenging to keep devices up-to-date.
|
||||
- **Load Balancing**. I didn't have any solution for establishing network
|
||||
load balancing for scale and availability.
|
||||
|
||||
### Refresh, Version 2
|
||||
|
||||
Incorporating the improvements I wanted to make, here is the resulting network
|
||||
architecture:
|
||||
|
||||

|
||||
|
||||
The key changes are:
|
||||
|
||||
- **Redundant Routers**. I doubled up the edge and access routers. They can
|
||||
effectively divide the load and fail over when needed.
|
||||
- **Anycast for Load Balancing**. I've standardized on making use of
|
||||
[Anycast](https://en.wikipedia.org/wiki/Anycast) addressing for creating
|
||||
load balanced and redundant network services. I'm using this a few ways:
|
||||
- The API server for my Kubernetes cluster is on an anycast address. This
|
||||
address is advertised from the three control plane nodes.
|
||||
- Kubernetes `LoadBalancer` type services allocate an address from a pool
|
||||
and advertise it out from any node that can accept traffic for the
|
||||
service.
|
||||
- My recursive DNS servers providing DNS lookups for the network are on two
|
||||
anycast addresses. Each edge router runs an instance and advertises one of
|
||||
the addresses; this is so I can "bootstrap" the network from the edge
|
||||
routers. I also run the DNS service under Kubernetes, which advertises the
|
||||
same anycast addresses using ordinary `LoadBalancer` services.
|
||||
- **IS-IS and BGP**. I took a few passes at getting this right. I first tried
|
||||
to move fully from IS-IS to BGP only. This meant setting up peering
|
||||
using IPv6 link local addresses, which worked, but it was a bit flaky under
|
||||
[FRR](https://frrouting.org/). I settled on using IS-IS on the fabric
|
||||
interfaces only to exchange the IPv6 loopback addresses of each node. I use
|
||||
the globally routable loopback addresses for the BGP peering, which is much
|
||||
easier in practice. All of the other routes (access subnets, Kubernetes
|
||||
networks, anycast addresses, defaults from the edge routers) are exchanged
|
||||
using BGP.
|
||||
- **No NAT64**. I decided to do away with NAT64 and provide dual-stack
|
||||
connectivity to the access networks. I set up [Kea](https://www.isc.org/kea/)
|
||||
as a cluster on the two access routers, which is thankfully rather low
|
||||
maintenance.
|
||||
- **BGP Extended-Nexthop**. An added bonus to using BGP the way that I am is
|
||||
that I could make use of the [BGP extended-nexthop](https://datatracker.ietf.org/doc/html/rfc8950)
|
||||
capability. The old network with only IS-IS still required me to define IPv4
|
||||
subnets on the switching fabrics, nodes used IPv4 addresses as the next hop
|
||||
gateway addresses for IPv4 routes. With the extended-nexthop capability in
|
||||
BGP, it uses the IPv6 link-local addresses for the next hop under both IPv4
|
||||
and IPv6.
|
||||
|
||||
### High Availability
|
||||
|
||||
To migrate from single routers to redundant pairs, I needed to figure out a
|
||||
few things.
|
||||
|
||||
#### Default Routes
|
||||
|
||||
With a single edge router, this was easy. With two, it's a bit of a puzzle. My
|
||||
ISP doesn't actually provide fully routed IPv6 connectivity with my class of
|
||||
service. I do get static IPv4 addresses, however. I've been using Hurricane
|
||||
Electric's [tunnel broker](https://tunnelbroker.net/) service to get a routed
|
||||
`/48` IPv6 subnet.
|
||||
|
||||
With a pair of edge routers, I've set them up with four static IPv4 addresses
|
||||
on their Internet-facing interfaces. Each router gets one address. I then have
|
||||
two [VRRP](https://en.wikipedia.org/wiki/Virtual_Router_Redundancy_Protocol)
|
||||
interfaces, one that I use to terminate the IPv6 tunnel, and the other I use
|
||||
for all IPv4 traffic. When both routers are up and running, one will have the
|
||||
IPv6 tunnel and the other will have the IPv4 interface. Each one advertises a
|
||||
default route for the address family it's taking care of. If one goes down,
|
||||
the interface will fail over and everything reconverges rather quickly. IPv6
|
||||
connections are unaffected, as the routing is stateless and traffic continues
|
||||
to flow normally. IPv4 connections may get interrupted as the NAT state is
|
||||
lost.
|
||||
|
||||
#### Access Routers
|
||||
|
||||
The interfaces facing the client machines provide connectivity for both IPv4
|
||||
and IPv6.
|
||||
|
||||
The IPv6 configuration is much simpler. FRR can be configured to send router
|
||||
advertisements to the subnets. Both routers are configured to advertise their
|
||||
presence, as well as the subnet prefixes and DNS information. Client machines
|
||||
will pick these up, and then have both routers as their default gateways.
|
||||
|
||||
While IPv6 configuration is seamless, IPv4 relies on VRRP to share a `".1"`
|
||||
default gateway address, which, though functional, lacks the elegance of
|
||||
IPv6's stateless design.
|
||||
|
||||
## It Works
|
||||
|
||||
After I got this all in place, it was finally possible to build myself a
|
||||
working Kubernetes cluster and migrate all of my old services over to it. The
|
||||
transition to Kubernetes not only streamlined service management but also laid
|
||||
the foundation for future scalability and automation. I'll get into that
|
||||
adventure in the next series of articles.
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 234 KiB |
@@ -1,208 +0,0 @@
|
||||
---
|
||||
title: "Nouveau site web"
|
||||
slug: "nouveau-site-web"
|
||||
date: "2025-06-12T08:00:00-06:00"
|
||||
description: "Il y a enfin un nouveau site web sur d-b.ca."
|
||||
summary: "J'ai publié mon site web personnel. Un aperçu de certaines réalisations passées et quelques détails sur la technologie utilisée pour le nouveau site."
|
||||
---
|
||||
|
||||
J'ai enfin publié un site web correct à [https://d-b.ca/](https://d-b.ca/).
|
||||
La dernière fois que j'avais quelque chose en ligne sur ce domaine remontait à
|
||||
plus de 20 ans, selon le [Wayback Machine](https://web.archive.org/). Pourquoi
|
||||
si longtemps? La vérité est que mes intérêts ont évolué au fil des années, et
|
||||
j'ai beaucoup appris. Ce site, bien qu'utile en lui-même, est en réalité le
|
||||
fruit d'une plateforme personnelle que j'ai développée au fil du temps.
|
||||
|
||||
## Histoire
|
||||
|
||||
Mon premier site web personnel a été développé alors que j'étais étudiant à
|
||||
l'[Université de l'Alberta](https://ualberta.ca), à la fin du siècle dernier.
|
||||
Le web était encore à ses débuts, mais l'université permettait aux étudiants
|
||||
de publier du contenu web. À l'époque, c'était surtout une nouveauté qui n'a
|
||||
pas duré au-delà de mon temps à l'école, mais cela a éveillé mon intérêt pour
|
||||
les technologies internet et leurs applications.
|
||||
|
||||
Ce site initial comprenait une fonction intéressante. J'ai développé un
|
||||
mécanisme pour mettre à jour automatiquement une page chaque fois que je me
|
||||
connectais à l'un des ordinateurs de l'école, afin que mes amis puissent me
|
||||
trouver si nécessaire. À l'époque, les mises à jour dynamiques des sites web
|
||||
étaient difficiles. La méthode la plus courante était d'utiliser des
|
||||
[CGI](https://fr.wikipedia.org/wiki/Common_Gateway_Interface), qui consistait
|
||||
à exécuter un petit programme chaque fois qu'une page était demandée.
|
||||
L'université n'autorisait pas les sites étudiants à utiliser cela. J'ai donc
|
||||
rédigé des scripts shell qui étaient appelés lors de mes scripts de connexion
|
||||
et de déconnexion, générant ainsi un fichier HTML statique et le stockant dans
|
||||
mon répertoire de contenu web. Il fallait gérer des cas comme des connexions
|
||||
multiples, et le script de déconnexion ne s'exécutait pas toujours, donc je
|
||||
devais surveiller les entrées obsolètes.
|
||||
|
||||
### Hébergement autonome
|
||||
|
||||
J'ai toujours été un grand fan de l'hébergement autonome. Apprendre en mettant
|
||||
un système en marche fonctionne bien pour moi. J'apprécie aussi la
|
||||
confidentialité et le contrôle qu'il offre. De plus, c'est bien plus
|
||||
économique à long terme!
|
||||
|
||||
Cela a vraiment commencé lorsque je travaillais chez un fournisseur local
|
||||
d'accès à Internet. J'ai obtenu un bon accord sur une connexion large bande à
|
||||
la maison, qui comprenait un petit bloc réseau (`/28`, comprenant 16 adresses
|
||||
IP) que je pouvais utiliser. J'ai dédié mon ordinateur le plus puissant comme
|
||||
serveur et développé plusieurs services, notamment un nouveau site web.
|
||||
|
||||
À l'époque, mon site web n'était pas très élaboré et était principalement
|
||||
orienté vers l'expérimentation. J'ai développé un système de gestion de
|
||||
contenu simple à partir de zéro en PHP, que j'ai utilisé pour publier un blog.
|
||||
Il s'intégrait également aux listes de diffusion, un domaine que j'explorais
|
||||
à l'époque.
|
||||
|
||||
### D-B.CA
|
||||
|
||||
À l'époque, je n'avais pas encore enregistré de nom de domaine personnel, donc
|
||||
tout se trouvait sous un domaine d'un ami. En fin d'année 2002, j'ai décidé
|
||||
d'enregistrer le mien, principalement pour avoir une adresse e-mail stable.
|
||||
J'ai voulu intégrer les initiales de mon nom, donc j'ai choisi `d-b.ca`.
|
||||
C'était un compromis, car quelqu'un squattait `db.ca` à l'époque, et, à ma
|
||||
connaissance, c'est toujours le cas.
|
||||
|
||||
Au début, j'ai principalement focalisé mon attention sur l'exploitation de mes
|
||||
services de messagerie et d'autres expérimentations, sans trop me soucier de
|
||||
mon site web. Il y avait quelques pages de test de temps en temps, mais rien
|
||||
de substantiel.
|
||||
|
||||
## Technologie moderne
|
||||
|
||||
L'un des projets que je suis depuis un certain temps est
|
||||
[Hugo](https://gohugo.io/). J'ai eu l'occasion de travailler avec divers
|
||||
systèmes de gestion de contenu web, et ils ont souvent semblé encombrants et
|
||||
poser des problèmes de sécurité. Hugo est un exemple de «générateur de sites
|
||||
statiques». Imaginez-le comme suit: au lieu de créer des pages web en temps
|
||||
réel chaque fois qu'une personne les visite, Hugo prend tout le contenu brut
|
||||
et le transforme en un ensemble de fichiers prêts à être servis, de manière
|
||||
similaire à la manière dont un compilateur transforme du code en un programme
|
||||
exécutable. Les ressources statiques résultantes peuvent être servies comme
|
||||
des fichiers réguliers depuis n'importe quel service web, sans avoir besoin de
|
||||
générer dynamiquement du contenu à partir d'une base de données, comme le font
|
||||
les systèmes de gestion de contenu traditionnels.
|
||||
|
||||
Utiliser Hugo est beaucoup plus simple avec un modèle de base solide. Il
|
||||
existe de nombreux modèles à choisir ([voir ici](https://themes.gohugo.io/)),
|
||||
notamment celui que j'ai choisi ici appelé
|
||||
[«Blowfish»](https://blowfish.page/). J'aime l'aspect qu'il a, et il prend
|
||||
bien en charge le style de site que je souhaite.
|
||||
|
||||
Un autre avantage d'un générateur de sites statiques est que l'ensemble des
|
||||
sources du site peut être traité comme du code logiciel, ce qui rend simple
|
||||
l'utilisation d'outils de développement comme [Git](https://git-scm.com/) pour
|
||||
le contrôle de version. Je garde les sources de ce site dans un dépôt public
|
||||
sur mon propre serveur Git. N'hésitez pas à jeter un œil:
|
||||
|
||||
{{< gitea repo="d-b.ca/web" >}}
|
||||
|
||||
### CI/CD
|
||||
|
||||
J'ai également configuré un pipeline CI/CD pour construire et déployer le site
|
||||
chaque fois que des modifications sont apportées au dépôt source. Qu'est-ce
|
||||
que cela signifie ?
|
||||
|
||||
**CI** = *Intégration continue*
|
||||
> C'est la pratique d'intégrer fréquemment les modifications dans un dépôt
|
||||
> source. Les modifications sont vérifiées, assemblées et empaquetées via des
|
||||
> processus automatisés. [Plus d'informations](https://martinfowler.com/articles/continuousIntegration.html)
|
||||
|
||||
**CD** = *Livraison continue*
|
||||
> C'est la capacité à pouvoir prendre de nouvelles modifications (comme les
|
||||
> sorties du processus CI) et les déployer et les exécuter automatiquement.
|
||||
> [Plus d'informations](https://continuousdelivery.com/)
|
||||
|
||||
La partie CI est déclenchée par un push vers le dépôt
|
||||
[web](https://git.brds.ca/d-b.ca/web) . Il exécute un workflow automatisé qui
|
||||
construit le site et empaquette les artefacts résultants dans une image
|
||||
conteneur basée sur le serveur web [Caddy](https://caddyserver.com/).
|
||||
|
||||
Une image conteneur est comme un environnement logiciel préemballé qui assure
|
||||
que le site web fonctionne de manière cohérente, quel que soit
|
||||
l'infrastructure sous-jacente. L'image résultante contient tout ce dont le
|
||||
site a besoin pour fonctionner et peut s'exécuter sur n'importe quelle
|
||||
infrastructure capable de l'héberger, comme mon propre ordinateur portable ou
|
||||
mon cluster de serveurs.
|
||||
|
||||
L'image conteneur utilisée avec Hugo est une autre image, uniquement utilisée
|
||||
pour créer l'image conteneur du site web. Je la maintiens dans ce dépôt:
|
||||
|
||||
{{< gitea repo="d-b.ca/hugo-builder" >}}
|
||||
|
||||
Une fois que le workflow a construit l'image conteneur pour exécuter le site
|
||||
web, il met à jour le dépôt GitOps de livraison continue pour déployer
|
||||
immédiatement cette nouvelle version sur un site de prévisualisation privé.
|
||||
Une autre définition:
|
||||
|
||||
> **GitOps** désigne la pratique de gérer l'automatisation de l'infrastructure
|
||||
> en conservant des descriptions lisibles par machine de l'infrastructure
|
||||
> souhaitée dans un dépôt Git contrôlé en version. Un système de livraison
|
||||
> continue surveille le dépôt pour des changements, ajoutant, modifiant ou
|
||||
> supprimant immédiatement l'infrastructure pour aligner l'état du système
|
||||
> opérationnel sur la description source.
|
||||
|
||||
Il y a de nombreux avantages à gérer l'infrastructure de cette manière. Les
|
||||
changements sont automatiquement traqués, car tout est stocké dans un système
|
||||
de dépôt de code existant conçu spécifiquement pour gérer et suivre les
|
||||
changements. Les changements problématiques peuvent être facilement annulés en
|
||||
annulant le changement dans le dépôt. L'automatisation maintient les choses
|
||||
synchronisées en permanence - tout changement effectué manuellement en dehors
|
||||
de ce système est immédiatement détecté et supprimé.
|
||||
|
||||
Quand je veux publier cette nouvelle version en tant que site web de
|
||||
production, j'utilise mon dépôt GitOps privé régulier pour mettre à jour le
|
||||
tag de version de l'image, et le reste se fait automatiquement. Le dépôt
|
||||
GitOps de livraison continue pour le site de prévisualisation est public, vous
|
||||
êtes les bienvenus pour le consulter ici:
|
||||
|
||||
{{< gitea repo="d-b.ca/db-cd" >}}
|
||||
|
||||
#### Diagramme du pipeline
|
||||
|
||||
{{< mermaid >}}
|
||||
flowchart TB
|
||||
subgraph GIT [Dépôt GIT]
|
||||
WR[(Web)]
|
||||
CDR[(CD)]
|
||||
end
|
||||
|
||||
WP(Envoyer les mises à jour du site web)-->WR
|
||||
WP ~~~ HBI
|
||||
|
||||
subgraph CI [Workflow CI]
|
||||
CIP[Récupérer le dépôt source]-->BWI[Construire l'image web]
|
||||
BWI-->PWI[Envoyer l'image web]
|
||||
PWI-->UCD[Mettre à jour le dépôt CD]
|
||||
end
|
||||
|
||||
PWI-->WI
|
||||
WR-->CIP
|
||||
|
||||
subgraph DOCKER [Dépôt d'images]
|
||||
HBI((Hugo
|
||||
Build))
|
||||
WI((Web))
|
||||
end
|
||||
|
||||
HBI-->BWI
|
||||
|
||||
UCD-->CDP(Envoyer la mise à jour de l'image)
|
||||
CDP-->CDR
|
||||
CDR-->CDPull
|
||||
|
||||
subgraph CD [Processus CD]
|
||||
CDPull[Récupérer le dépôt source]-->DWI[Déployer l'image web]
|
||||
end
|
||||
|
||||
WI-->DWI
|
||||
{{< /mermaid >}}
|
||||
|
||||
## Plateforme sous-jacente
|
||||
|
||||
Dans des articles futurs, je décrirai l'évolution du réseau physique et des
|
||||
systèmes sur lesquels ce site s'exécute, et comment ils m'ont permis de
|
||||
construire un cluster [Kubernetes](https://kubernetes.io/) pour mettre à
|
||||
l'échelle ce site et exécuter d'autres services, tout cela sur du matériel que
|
||||
j'ai assemblé moi-même!
|
||||
4
go.mod
4
go.mod
@@ -1,5 +1,5 @@
|
||||
module git.brds.ca/drew/web
|
||||
|
||||
go 1.24.2
|
||||
go 1.25.4
|
||||
|
||||
require github.com/nunocoracao/blowfish/v2 v2.86.0 // indirect
|
||||
require github.com/nunocoracao/blowfish/v2 v2.92.0 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@@ -1,2 +1,2 @@
|
||||
github.com/nunocoracao/blowfish/v2 v2.86.0 h1:wAnsjubJuoAWgvM0Xgh2H7+4bOBVFAgX3WUvYtXO2k8=
|
||||
github.com/nunocoracao/blowfish/v2 v2.86.0/go.mod h1:4SkMc+Ht8gpQCwArqiHMBDP3soxi2OWuAhVney+cuyk=
|
||||
github.com/nunocoracao/blowfish/v2 v2.92.0 h1:1EgHMRaY6VI438TIAN/5luNx16lg1e0Lrbi+6kDxpdA=
|
||||
github.com/nunocoracao/blowfish/v2 v2.92.0/go.mod h1:4SkMc+Ht8gpQCwArqiHMBDP3soxi2OWuAhVney+cuyk=
|
||||
|
||||
Reference in New Issue
Block a user