
Live blog coding et amusement en golang
Publié le 2025-05-27 par
On dit souvent que pour se motiver à lancer (et surtout finir) un projet, le mieux c’est de le faire en live, avec du public. Apparemment, ça pousse à aller jusqu’au bout. C’est en tout cas ce que je remarque chez pas mal de devs-influenceurs que je suis sur YouTube.
Bon, moi j’ai clairement pas leur audience et spoiler : je compte pas me filmer en train de coder pendant 3 heures.
Du coup, j’ai eu une idée qui me correspond mieux : je vais essayer d’écrire un article en même temps que je bosse sur mon projet. Une sorte de journal de bord, en mode « devlog maison ». On verra bien ce que ça donne !
Le besoin
J’auto-héberge quelques projets persos sur un Raspberry Pi, y compris ce blog d’ailleurs. Franchement, ça marche super bien, et ça me permet de m’éclater un peu avec la gestion de serveur, le déploiement, tout ça.
Le seul vrai souci que j’ai rencontré, c’est cette histoire d’IP dynamique. Alors oui, avec nos box actuelles, les IP ont tendance à rester stables pas mal de temps… mais ce la n'est pas garanti.
Dernièrement, j’ai eu la « chance » d’avoir mon IP changée plusieurs fois en une seule semaine, alors qu'elle était stable depuis des mois. Et devoir mettre à jour les enregistrements DNS à la main, c’est clairement pas fun. Surtout que pour connaître la nouvelle IP, faut être chez soi. Pratique, hein ?
Ce qu’il me faudrait, c’est un petit script qui vérifie régulièrement si mon IP a changé, et qui met à jour mes DNS direct chez le registrar. Automatiser un peu tout ça, quoi.
Par chance, mon registrar est OVH. Et même si pas mal de monde aime râler sur leurs offres ou certaines décisions techniques, personellement je trouve qu’ils font vraiment des efforts pour filer un coup de main aux devs.
Petit flashback : en 2021, quand le data center de Strasbourg a pris feu, plein de sites hébergés chez OVH sont tombés en rade. Je vais pas relancer le débat sur les backups, c’est pas le sujet ici, mais forcément, tout le monde cherchait à remettre son site en ligne au plus vite. Sauf que l’interface web (le fameux manager) était complètement saturée.
C’est là que je suis tombé sur un article de wikitwist.com qui parlait de cet événement... mais sous un angle un peu différent : celui de l’API d’OVH. Et franchement, elle est super bien conçue et très bien documentée. Elle permet, entre autres, de gérer ses enregistrements DNS. Du coup, je me suis dit : pourquoi ne pas en profiter pour automatiser un peu tout ça ?
l'article de wikitwist sur l'API d'OVH
Le plan
Mon FAI ne m’aide pas vraiment à récupérer facilement mon IP publique (en IPv4), du coup j’ai dû trouver une autre solution pour connaître mon adresse depuis l’extérieur. Je pourrais tout simplement interroger un service en ligne qui me retourne mon IP, y’en a plein. Mais bon... les solliciter en boucle, ça me gêne un peu, surtout quand on sait pas trop ce qu’ils font des infos qu’on leur envoie.
Heureusement, j’ai déjà un serveur externe (VPS chez OVH), en dehors de mon réseau local. Et faire un petit script PHP pour qu’il affiche mon IP, franchement, c’est pas plus compliqué que quelques lignes. Du coup, j’ai décidé de créer cette première brique que j’ai appelée "KIP", un petit jeu de mots entre “keep” et “IP”. J’aime bien, ça sonne comme l'acronyme KISS.
Ensuite, la seconde partie : un script qui va chercher cette IP, la compare avec celle qu’il a en mémoire, et si jamais elle a changé, il met à jour les DNS via l’API d’OVH. Et comme j’ai très envie de me mettre sérieusement à Go, je me suis dit que ce serait un projet parfait pour ça. Appelons ce futur script "GoKeep", ça colle bien au concept.
Pour être tout à fait transparent : j’avais déjà créé une première version de ces deux utilitaires en PHP avec Symfony. Mais avec un peu de recul, utiliser Symfony pour quelquechose d'aussi simple, c'est comme ouvrir un vortex par la porte des étoiles pour travailler son swing. Trop lourd, trop contraignant à maintenir. Donc là, on repart sur du plus léger, plus fun à coder, et qui colle mieux à mes envies du moment.
KIP a "simple" API to get infos from your IP
DDOManager is a "small" tool to deal with dynamic IP at home
KIP
Cette fois, je veux que la partie "code" reste aussi simple que possible. L’idée, c’est de renvoyer juste l’essentiel : la date et l’heure actuelles (au fuseau horaire de mon VPS), l’adresse IP, le port utilisé, et pourquoi pas le hostname pour faire joli. Le tout, bien proprement formaté en JSON. Et quand je dis "récupérer", je parle bien du côté utilisateur : aucune donnée n’est stockée côté serveur (à part ce que les logs Apache enregistrent par défaut). Juste une réponse simple, directe, sans prise de tête.
PHP<?php header('Content-Type: application/json'); date_default_timezone_set('Europe/Paris'); $ip = $_SERVER['REMOTE_ADDR']; $hostname = gethostbyaddr($ip); $response = [ 'date' => date('Y-m-d'), 'heure' => date('H:i:s'), 'ip' => $ip, 'port' => $_SERVER['REMOTE_PORT'], 'hostname' => $hostname, 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'Unknown' ]; echo json_encode($response, JSON_PRETTY_PRINT)
Je ne vais pas m’étendre ici sur la mise en ligne du script, car dans mon cas, c’est du très classique : j’ai un utilisateur dédié sur le serveur, le code est dans un dossier public_html à l’intérieur de son $HOME, et j’ai créé un VirtualHost Apache spécifique juste pour ce "mini-site". Le tout passe sous HTTPS grâce à Certbot.
Par contre, qui dit public dit potentiellement abusé. Et même si le script ne conserve rien, je préfère poser une petite limite de requêtes pour éviter qu’un bot ne vienne me ping 100 fois par seconde. Pour ça, rien de plus fiable et direct qu’iptables.
BASHsudo iptables -V
Protégeons l'accès SSH. Parce qu’on ne veut surtout pas se bloquer soi-même en appliquant des règles un peu trop strictes... Non, je ne parle pas d'expérience, voyons 🙃
BASHsudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
Ensuite on ajoute la limite des nouvelles connexions à 1/seconde/ip.
BASHsudo iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW -m hashlimit \ --hashlimit 1/sec --hashlimit-burst 5 --hashlimit-mode srcip \ --hashlimit-name http_limit -j ACCEPT sudo iptables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -m hashlimit \ --hashlimit 1/sec --hashlimit-burst 5 --hashlimit-mode srcip \ --hashlimit-name https_limit -j ACCEPT
Enfin on rejette les excès (et rien d'autre !!!)
BASHsudo iptables -A INPUT -p tcp --dport 80 -j REJECT --reject-with tcp-reset sudo iptables -A INPUT -p tcp --dport 443 -j REJECT --reject-with tcp-reset
On peut vérifier les règles avec la commande
BASHsudo iptables -L -v --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 2519 1055K ACCEPT all -- any any anywhere anywhere ctstate RELATED,ESTABLISHED 2 102 6942 ACCEPT tcp -- any any anywhere anywhere tcp dpt:ssh 3 19 1140 ACCEPT tcp -- any any anywhere anywhere tcp dpt:https ctstate NEW limit: up to 1/sec burst 5 mode srcip 4 3 160 REJECT tcp -- any any anywhere anywhere tcp dpt:http reject-with tcp-reset 5 24 1060 REJECT tcp -- any any anywhere anywhere tcp dpt:https reject-with tcp-reset Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination
Il est important de vérifier que les pages web sont encore accessible à ce stade, sans couper la connexion SSH. Si ce n'est pas le cas, il faut recommencer en supprimant toutes les règles :
BASHsudo iptables -F
Si tout est bon, on installe le paquet pour les garder au redémarrage :
BASHapt install iptables-persistent
L’installeur demande si l'on enregistre les règles actuelles pour IPv4 => Oui. Pour IPv6, chacun son choix (moi j’ai mis Non). Ensuite on vérifie l'enregistrement comme ceci :
BASHcat /etc/iptables/rules.v4
GOKeep
Ça fait des années que j’ai envie de me mettre à un autre langage orienté back-end, sans jamais réussir à trancher. Dans ma liste, il y avait Rust et Golang. Rust, tout le monde en parle comme du messie, même si la courbe d’apprentissage peut faire un peu peur, et toutes les vidéos que j'ai pu voir parlaient de subtilités de gestion de mémoire... Pour un langage moderne (de bas niveau), on se retrouvait avec beaucoup de sujets qui rappellent l’époque du C. Du coup, j’ai opté pour Go. (mais je ne ferme pas la porte à Rust, juste je ne peux pas faire les deux en même temps...)
Pour me faire une idée, j’ai regardé pas mal de vidéos sur le sujet, et deux d'entre elles m'ont aidé dans mon choix.
La première, c’est celle de Grafikart : un comparatif entre Go et JavaScript, très accessible, avec des exemples concrets, sans trop s’attarder sur les limitations.
La seconde, c’est la présentation de Jean-Laurent de Morlhon à DevoxxFR : “Pourquoi Maurice ne doit surtout pas coder en Go”. Là, on entre dans le dur : il liste pas mal de "limitations" du langage — formatage imposé du code, gestion très manuelle des erreurs, syntaxe plutôt verbeuse...
Mais en fait, tous ces points qui sont censés être des défauts, je les ai trouvés... apaisants. Moins de choix, moins de réflexions inutiles... donc moins de charge mentale. Attention, cela ne veut pas dire qu’on ne peut pas être créatif avec Go. C’est un peu comme avec le protocole Gemini : ça paraît simpliste et limitant, mais au final, avoir un cadre rigide pousse à la créativité. Et c’est justement l’objectif de Go : coder plus vite, plus simplement, sans sacrifier la lisibilité ni la performance.
Et si je devais donner un ultime argument : un code compilé en Go donne un unique binaire autonome, qui s'exécute sans aucune dépendance. Même pas besoin d’avoir Go installé pour le lancer. Pour le DevOps, c’est juste royal.
Découverte du langage Go pour les développeurs JavaScript
Pourquoi Maurice ne doit surtout pas coder en GO
Je ne sais pas encore si ce choix s’avérera judicieux sur la durée, mais pour l’instant, il me convient parfaitement.
Structure du script
La structure du projet est assez simple :
- Récupération de l'IP actuelle
- Récupération de l'IP en mémoire
- Si les deux adresses ne sont pas concordantes, alors on récupère la liste des domaines et sous domaines à gérer
- Via l'API d'OVH on vient modifier l'adresse IP des DNS de type A pour chaque sous domaines
- Enregistrement de la nouvelle IP
Le tout avec des logs.
J'aurais également besoin de deux fichiers, le premier qui ne contiendra que l'adresse IP "en mémoire", .ip_history et un second qui contient les informations nécessaires à la gestion de l'API d'OVH, .godnsfile. Ces fichiers doivent être présents à la racine du dossier home de l'utilisateur courant. Voici la structure du fichier .godnsfile :
JSON{ "applicationKey": "votre_app_key", "applicationSecret": "votre_app_secret", "consumerKey": "votre_consumer_key", "domains": [ { "uri": "example.com", "subs": ["www", "api", "blog"] } ] }
Maintenant, on code
On est maintenant prêt à passer à la phase la plus satisfaisante du projet : écrire du code. J’écris finalement cette partie de l’article après avoir terminé le développement de la solution, parce que c’était plus simple pour moi de rester concentré sur la découverte du langage, de faire des allers-retours avec la documentation, et de tester plein de petites choses sans avoir à formuler tout ça proprement en même temps. Bref, j’ai préféré coder d’abord, écrire ensuite. (tout le monde ne sait pas coder et blogguer en même temps...)
On commence par la base : interroger mon service KIP et récupérer mon IP publique.
GOpackage main import ( "fmt" "io" "net/http" ) func main() { resp, err := http.Get("https://kip.lhoir.me") if err != nil { fmt.Println(err) panic(err) } defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { fmt.Println(err) panic(err) } fmt.Println(string(body)) }
Je lance avec `go run main.go` et... magie ! Mon JSON s’affiche dans le terminal. On a notre point de départ. C'est vraiment agréable d'avoir autant d'outils déjà intégré au langage. Finalement, pour des scripts simples, la plus grande difficulté de Go (de ce que j'en vois) est de connaître la bonne librairie de base pour réaliser l'action que l'on souhaite.
Notons l’utilisation de defer : un mot-clé tout simple, mais super pratique. Il permet de différer la fermeture de la connexion HTTP à la fin de la fonction, sans risquer de l’oublier plus tard. Moins d’oublis, moins de bugs.
Plutôt que de bricoler une extraction à coups de regex ou de split, Go propose de typer proprement une réponse JSON via une struct. Je pourrai me contenter de ne déclarer que le champ "ip", mais autant faire les choses proprement.
GOimport ( "encoding/json" ) [...] type ResponseData struct { Date string `json:"date"` Time string `json:"time"` IP string `json:"ip"` Port string `json:"port"` Hostname string `json:"hostname"` UserAgent string `json:"user_agent"` } [...] var data ResponseData err = json.Unmarshal(body, &data) if err != nil { fmt.Println(err) panic(err) } currentIp := data.IP
C’est là qu’on commence à sentir un des aspects les plus décriés de Go : sa gestion des erreurs. Chaque appel potentiellement risqué est suivi d’un if err != nil { ... }. C’est clair, c’est explicite, mais... qu’est-ce que c’est répétitif. J’ai vite trouvé une astuce récurrente dans les vidéos de devs Go : créer une petite fonction utilitaire check() qui encapsule ce comportement, que je veux unique dans mon script : afficher l'erreur et planter comme un lâche. Et hop, on remplace tous les "if err != nil" par de simples appels à "check(err)". On réduit drastiquement le nombre de lignes de notre code.
GOfunc check(e error) { if e != nil { log.Printf("ERROR: %v", e) log.Println("=== End check ===") panic(e) } }
GOimport ( "os" ) [...] home, err := os.UserHomeDir() check(err) // c'est mieux non ? historyPath := home + "/.ip_history" previous, err := os.ReadFile(historyPath) check(err) if string(previous) != currentIp { fmt.Println("Nouvelle IP :", currentIp) } else { fmt.Println("IP inchangée :", currentIp) }
Go n’est pas un langage orienté objet dans le sens classique du terme, mais sa capacité à définir des structures de données typées, propres et claires est franchement plaisante.
Certains parlent même de "POO implicite" en Go, grâce à l’usage de struct et de receiver functions.
La programmation orientée objet dans le langage Go
Bon, je ne vais pas réécrire tout le script ici. La suite consiste à utiliser la lib officielle d’OVH pour identifier les enregistrements DNS correspondants, les mettre à jour, et rafraîchir la zone.
Une fois le script fonctionnel, j'ai remplacé l'affichage des erreurs par un système de log avec "gopkg.in/natefinch/lumberjack.v2. Ce qui me permet d'enchainer sur le système d'import des dépendances.
Un truc que j’ai vraiment apprécié en Go, c’est son système de gestion des dépendances. Contrairement à Composer (PHP) ou Maven (Java), ici pas de fichier de conf à remplir à la main, pas de commandes ésotériques pour initialiser un projet. Tu importes un package, tu fais un go run ou un go build, et Go télécharge tout ce qu’il faut.
BASHgo get github.com/ovh/go-ovh/ovh # Go a automatiquement mis à jour le fichier go.mod et ajouté une entrée dans go.sum pour verrouiller la version.
Et même pour du code local (dans le même projet), il est possible de structurer son application avec des packages maison. Il suffit de les placer dans des sous-dossiers, puis de les importer avec leur chemin relatif:
GOimport "monprojet/utils"
Pas besoin d’autoload, de configuration compliquée ni de réflexion sur l’arborescence du namespace. Autre point fort : Go impose de ne pas avoir de dépendances non utilisées, comme pour les variables. Si tu déclares un import que tu n’utilises pas... le compilateur t’envoie bouler. C’est brutal, mais ça force à garder un code propre. Un peu à la Unix : chaque outil fait une chose, mais il le fait bien.
Conclusion
J'utilise le couple Kip et GoKeep depuis quelques semaines et j'en suis ravi. Ce projet m’a permis de poser mes premières briques en Go, et franchement... j’ai adoré.
C’est un langage qui respire la simplicité sans être simpliste. J’ai à peine effleuré ce qu’il propose : je n’ai pas encore joué avec les pointeurs, ni touché aux goroutines ou aux channels, qui sont pourtant parmi les concepts les plus puissants de Go. Mais rien que ce petit script m’a donné envie d’aller plus loin, de creuser davantage, et surtout de réécrire d'autres outils que j’avais bricolés en PHP ou Bash avec ce nouveau langage. (Le moteur de ce blog ? Tempo ?)
J’ai ensuite demandé à ChatGPT de me générer un joli README.md pour accompagner le projet. Oui j'utilise les outis d'IA (ou LLM) sans avoir honte... cela ferait un bon article de blog.
Le code est maintenant disponible sur mon dépôt GitLab, pour ceux que ça intéresse, ou pour les curieux qui veulent voir un exemple simple et concret d’usage de l’API OVH en Go.
Et toi, quel est le projet, le besoin, qui t'as motivé à apprendre un langage de programmation, voir le langage Golang ?