Docker Compose : Go + PostgreSQL
Pourquoi Docker Compose : Go + PostgreSQL ?
Le développement moderne est souvent basé sur des systèmes distribués et complexes. Les applications modernes nécessitent souvent plusieurs services, chacun avec ses propres dépendances et environnements de développement. C'est là que Docker Compose entre en jeu.
Docker Compose est un outil qui permet de définir et d'exécuter des applications multi-conteneurs à l'aide d'un simple fichier YAML. Il simplifie le processus de configuration et de démarrage des services, en vous permettant de décrire toutes les configurations nécessaires dans un seul endroit.
Un cas concret est le développement d'une application Go qui nécessite une base de données PostgreSQL pour stocker ses données. Avec Docker Compose, nous pouvons facilement configurer un conteneur pour l'application Go et un autre pour la base de données PostgreSQL, tout en les liant ensemble simplement avec quelques commandes.
Prerequis
Voici les connaissances nécessaires et les outils à installer :
Connaissances :
- Base de développement Go
- Connaissance de Docker et des conteneurs
- Familiarité avec le langage YAML
Outils à installer :
- Docker (version >= 20.10)
- Docker Compose (version >= 1.29)
Concepts fondamentaux
1. Fichiers docker-compose.yml
Ce fichier est le cœur de Docker Compose. Il définit les services, les configurations réseau et autres options pour chaque conteneur.
## docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://user:password@db:5432/dbname
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=dbname
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
2. Services
Dans le fichier docker-compose.yml, nous définissons deux services : app et db. Chaque service peut avoir sa propre configuration.
services:
app:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://user:password@db:5432/dbname
depends_on:
- db
3. Images et Build
Nous pouvons utiliser des images prédéfinies (postgres:13) ou construire nos propres images à partir de Dockerfiles.
services:
app:
build: .
4. Ports et Environnements
Nous pouvons exposer les ports pour que l'application soit accessible depuis le hôte, et définir des variables d'environnement.
services:
app:
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://user:password@db:5432/dbname
5. Dépendances et Volumes
Nous pouvons définir des dépendances entre les services et des volumes pour persister les données.
services:
app:
depends_on:
- db
db:
volumes:
- db_data:/var/lib/postgresql/data
Mise en pratique : projet fil rouge
Étape 1 : Créer le projet Go
Créez un nouveau répertoire pour votre projet et initialisez-le comme un module Go.
mkdir go-postgres-app
cd go-postgres-app
go mod init go-postgres-app
Étape 2 : Créer le fichier main.go
Ce fichier contiendra le code principal de notre application Go.
// main.go
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
log.Fatal(err)
}
fmt.Println("Successfully connected to the database!")
}
Étape 3 : Créer le fichier docker-compose.yml
Comme mentionné précédemment, ce fichier est utilisé pour définir les services de notre application.
## docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "8080:8080"
environment:
- DATABASE_URL=postgres://user:password@db:5432/dbname
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=dbname
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:
Étape 4 : Créer le Dockerfile
Ce fichier est utilisé pour construire l'image de notre application Go.
## Dockerfile
FROM golang:1.17 as builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/app .
CMD ["./app"]
Étape 5 : Construire et exécuter l'application
Utilisez Docker Compose pour construire et exécuter votre application.
docker-compose up --build
Étape 6 : Vérifier la connexion à la base de données
Une fois que le service app est démarré, il devrait se connecter à la base de données PostgreSQL. Vous pouvez vérifier cela en regardant les journaux du conteneur app.
docker-compose logs app
Erreurs frequentes et debugging
1. Erreur : database/sql: dsn string is empty
Cela signifie que l'URL de la base de données est vide.
## ❌ Mauvais
dsn := ""
## ✅ Correct
dsn := os.Getenv("DATABASE_URL")
2. Erreur : psql: could not connect to server: connection refused
Cela signifie que le service PostgreSQL ne peut pas être atteint.
## docker-compose.yml
services:
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=dbname
3. Erreur : error while opening browser: exec: "xdg-open": executable file not found in $PATH
Cela signifie que Docker Compose essaye d'ouvrir un navigateur pour afficher les journaux, mais ne trouve pas le binaire.
## Remplacez par une commande qui fonctionne sur votre système
docker-compose logs -f app
Pour aller plus loin
1. Utiliser des fichiers .env
Vous pouvez utiliser un fichier .env pour stocker les variables d'environnement.
## .env
DATABASE_URL=postgres://user:password@db:5432/dbname
2. Ajouter des tests unitaires
Ajoutez des tests unitaires à votre application Go pour vous assurer qu'elle fonctionne correctement.
// main_test.go
package main
import (
"testing"
)
func TestConnection(t *testing.T) {
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
t.Fatal(err)
}
defer db.Close()
err = db.Ping()
if err != nil {
t.Fatal(err)
}
t.Log("Successfully connected to the database!")
}
3. Utiliser des logs personnalisés
Ajoutez des logs personnalisés à votre application Go pour mieux comprendre ce qui se passe.
// main.go
package main
import (
"database/sql"
"fmt"
"log"
_ "github.com/lib/pq"
)
func main() {
db, err := sql.Open("postgres", os.Getenv("DATABASE_URL"))
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer db.Close()
err = db.Ping()
if err != nil {
log.Fatalf("Failed to ping database: %v", err)
}
fmt.Println("Successfully connected to the database!")
}
Défi pratique
Créez une application Go simple qui utilise Docker Compose et PostgreSQL. L'application devrait permettre d'ajouter, de lire et de supprimer des tâches dans la base de données.