Pourquoi votre projet Go réclame du CI/CD comme un chien réclame une promenade
Vous avez écrit un super microservice en Go. Il vole, les tests passent, le code est beau. Mais chaque matin, vous lancez manuellement go build, vous téléversez le binaire sur le serveur via SSH et vous priez pour que rien ne tombe. Ça vous dit quelque chose ? Si oui, j'ai une nouvelle pour vous : vous perdez 15 % de votre vie à faire ce qu'un robot pourrait faire.
Le CI/CD (Intégration Continue / Déploiement Continu) n'est pas qu'un mot à la mode dans les offres d'emploi DevOps. C'est votre assistant personnel qui :
- lance les tests à chaque push dans Git ;
- compile le binaire pour Linux/amd64, même si vous êtes sur Mac ;
- envoie des notifications Telegram si le build échoue ;
- déploie automatiquement sur le serveur lorsque vous fusionnez une PR dans main.
Dans cet article, nous allons configurer un pipeline CI/CD pour Go de zéro. Sans blabla, sans "installez Jenkins sur 20 serveurs". Seulement GitHub Actions, Docker et un peu de magie. C'est parti !
Ce dont nous avons besoin
Pour la configuration, nous aurons besoin de :
- Un dépôt GitHub (vous pouvez en créer un de test tout de suite) ;
- Un projet Go avec des modules (nous allons écrire un simple serveur HTTP) ;
- Un compte Docker Hub (ou tout autre registre) ;
- 5 minutes d'attention et l'envie d'automatiser la routine.
Tout le code que nous allons utiliser est en accès libre. Vous pouvez le copier et l'adapter à votre projet. L'essentiel est de ne pas oublier de remplacer les tokens et les noms de dépôts par les vôtres.
Étape 1 : Écrire une simple application Go
Pour la démonstration, prenons un serveur HTTP minimal. Créez le fichier main.go :
package main
import ( "fmt" "net/http")
func main() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go CI/CD pipeline!") })
http.ListenAndServe(":8080", nil)}Et le fichier main_test.go pour vérifier le bon fonctionnement :
package main
import ( "net/http" "net/http/httptest" "testing")
func TestHelloHandler(t *testing.T) { req := httptest.NewRequest("GET", "/", nil) w := httptest.NewRecorder()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello from Go CI/CD pipeline!") })
http.DefaultServeMux.ServeHTTP(w, req)
if w.Code != http.StatusOK { t.Errorf("expected status 200, got %d", w.Code) }}Initialisons le module :
go mod init github.com/votre-username/go-cicd-examplego mod tidyVérifions que tout fonctionne :
go test ./... -vgo run main.goSi vous voyez ok et un message de bienvenue sur localhost:8080 — vous êtes formidable. Continuons.
Étape 2 : Créer un Dockerfile pour l'application Go
La conteneurisation est une étape obligatoire du déploiement moderne. Nous allons compiler un binaire statique (sans CGO) et l'empaqueter dans une image minimale basée sur scratch ou alpine. Cela nous donnera une image d'environ 10 à 15 Mo au lieu de 300+ Mo.
Créez le fichier Dockerfile à la racine du projet :
FROM golang:1.22-alpine AS builder
WORKDIR /appCOPY go.mod go.sum ./RUN go mod downloadCOPY . .RUN CGO_ENABLED=0 GOOS=linux go build -o main .
FROM alpine:latestRUN apk --no-cache add ca-certificatesWORKDIR /root/COPY --from=builder /app/main .EXPOSE 8080CMD ["./main"]Notez que nous utilisons un build multi-étapes. La première étape est la compilation dans un environnement Go complet. La seconde est une image Alpine minimale, où seul le binaire est copié. C'est la norme de l'industrie pour les applications Go.
Construisons l'image localement et vérifions :
docker build -t go-cicd-example .docker run -p 8080:8080 go-cicd-exampleSi tout va bien, passons à