Add docker image building

This commit is contained in:
2025-12-22 19:23:15 +01:00
parent c3588de340
commit 222b026ac5
9 changed files with 681 additions and 0 deletions

70
.dockerignore Normal file
View File

@@ -0,0 +1,70 @@
# Build artifacts
target/
**/target/
# Webapp build artifacts and dependencies
pmoapp/webapp/node_modules/
pmoapp/webapp/dist/
**/node_modules/
# Git
.git/
.gitignore
.jj/
# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
*~
# macOS
.DS_Store
# Cache directories
cache/
.pmomusic/
# Log files
*.log
# Documentation
doc/
*.md
!Readme.md
# Test files
test_upnp/
examples/
# Temporary files
*.tmp
*.temp
*.pcap
# Database files
db/
# Old code and backups
old_code/
*.txt
!Cargo.lock
# Development tools
tools/
gupnp-tools/
# Build scripts (we have them in the Dockerfile)
Makefile
setup-deps.sh
setup-env.sh
# SVG and other assets not needed for runtime
*.svg
# Audio test files
*.ogg
*.flac
*.wav
*.mp3

302
DOCKER.md Normal file
View File

@@ -0,0 +1,302 @@
# Docker Deployment Guide for PMOMusic
Ce guide explique comment construire et déployer PMOMusic avec Docker.
## Architecture
Le Dockerfile utilise une approche multi-stage pour créer une image minimale :
1. **Stage 1 (webapp-builder)** : Compile l'application Vue.js avec Node.js
2. **Stage 2 (rust-builder)** : Compile le binaire Rust avec toutes ses dépendances
3. **Stage 3 (runtime)** : Image finale minimale Debian Slim avec uniquement le binaire et les bibliothèques runtime
### Avantages
- **Binaire auto-contenu** : L'application web est embarquée dans le binaire Rust
- **Image minimale** : ~200-300MB (vs plusieurs GB pour les images de build)
- **Sécurité** : Exécution en tant qu'utilisateur non-root
- **Reproductibilité** : Build complet et déterministe
## Build de l'image
### Option 1 : Build manuel avec Docker
```bash
# Build l'image
docker build -t pmomusic:latest .
# Le build prend environ 10-15 minutes selon votre machine
```
### Option 2 : Build avec docker-compose
```bash
# Build et démarre le conteneur
docker-compose up --build
# Ou juste build
docker-compose build
```
### Build optimisé avec cache
Pour accélérer les builds successifs, Docker réutilise les couches en cache :
```bash
# Build avec cache
docker build -t pmomusic:latest .
# Build sans cache (force rebuild complet)
docker build --no-cache -t pmomusic:latest .
```
## Exécution du conteneur
### Option 1 : Avec docker-compose (recommandé)
```bash
# Démarrer en arrière-plan
docker-compose up -d
# Voir les logs
docker-compose logs -f
# Arrêter
docker-compose down
# Redémarrer
docker-compose restart
```
### Option 2 : Avec docker run
```bash
# Run en mode interactif
docker run -it --rm \
--name pmomusic \
--network host \
-v $(pwd)/config:/home/pmomusic/.pmomusic \
-v $(pwd)/cache:/home/pmomusic/cache \
pmomusic:latest
# Run en mode détaché
docker run -d \
--name pmomusic \
--network host \
--restart unless-stopped \
-v $(pwd)/config:/home/pmomusic/.pmomusic \
-v $(pwd)/cache:/home/pmomusic/cache \
pmomusic:latest
```
## Configuration
### Ports
Par défaut, PMOMusic écoute sur le port **8080**. Vous pouvez modifier cela :
- Dans `docker-compose.yml` : modifier la section `ports`
- Avec `docker run` : utiliser `-p 8080:8080`
### Volumes
Deux volumes sont recommandés pour la persistance :
- **Configuration** : `/home/pmomusic/.pmomusic` - Fichiers de configuration
- **Cache** : `/home/pmomusic/cache` - Cache audio et métadonnées
### Variables d'environnement
Configurable via `docker-compose.yml` ou `-e` avec `docker run` :
```bash
# Niveau de logs Rust
RUST_LOG=debug
# Autres variables (selon votre configuration)
# ...
```
### Réseau
Pour UPnP/DLNA, utilisez **network_mode: host** pour permettre :
- La découverte multicast
- La communication avec les devices UPnP sur le réseau local
**Note** : Le mode `host` ne fonctionne que sur Linux. Sur macOS/Windows avec Docker Desktop, utilisez le mapping de ports standard.
## Gestion de l'image
### Taille de l'image
```bash
# Voir la taille de l'image
docker images pmomusic:latest
# Résultat attendu : ~200-300MB
```
### Nettoyage
```bash
# Supprimer l'image
docker rmi pmomusic:latest
# Nettoyer les images de build intermédiaires
docker builder prune
# Nettoyer tous les caches Docker (libère beaucoup d'espace)
docker system prune -a
```
## Build multi-plateforme
Pour builder pour différentes architectures (ARM64, AMD64) :
```bash
# Créer un builder multi-plateforme
docker buildx create --name multiarch --use
# Build pour AMD64 et ARM64
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t pmomusic:latest \
--push \
.
# Note : nécessite un registry Docker (Docker Hub, GHCR, etc.)
```
## Déploiement en production
### 1. Avec docker-compose (simple)
```bash
# Sur le serveur de production
git clone <votre-repo>
cd pmomusic
docker-compose up -d
```
### 2. Avec un registry Docker (recommandé)
```bash
# Sur votre machine de dev
docker build -t yourregistry.com/pmomusic:v1.0.0 .
docker push yourregistry.com/pmomusic:v1.0.0
# Sur le serveur de production
docker pull yourregistry.com/pmomusic:v1.0.0
docker run -d ... yourregistry.com/pmomusic:v1.0.0
```
### 3. Avec un orchestrateur (Kubernetes, Docker Swarm)
Créer un fichier de déploiement approprié selon votre orchestrateur.
## Debugging
### Logs du conteneur
```bash
# Logs en temps réel
docker logs -f pmomusic
# Logs avec docker-compose
docker-compose logs -f
```
### Entrer dans le conteneur
```bash
# Shell interactif (bash n'est pas disponible, utiliser sh)
docker exec -it pmomusic sh
# Vérifier les processus
docker exec -it pmomusic ps aux
# Vérifier les fichiers
docker exec -it pmomusic ls -la /home/pmomusic
```
### Health check
Le conteneur inclut un health check. Vérifier l'état :
```bash
# Voir l'état de santé
docker inspect --format='{{.State.Health.Status}}' pmomusic
```
## Troubleshooting
### Le build échoue
1. **Erreur de dépendances npm** :
- Vérifier que `pmoapp/webapp/package.json` est correct
- Essayer `docker build --no-cache`
2. **Erreur de compilation Rust** :
- Vérifier que tous les fichiers Cargo.toml sont présents
- Vérifier les dépendances système (libsoxr, libasound2)
3. **Out of memory** :
- Augmenter la mémoire allouée à Docker Desktop (settings)
- Utiliser `--memory` pour limiter la mémoire du build
### Le conteneur ne démarre pas
1. **Port déjà utilisé** :
```bash
# Vérifier quel processus utilise le port 8080
sudo lsof -i :8080
```
2. **Permissions** :
- Vérifier les permissions des volumes montés
- Le conteneur s'exécute en tant qu'utilisateur `pmomusic` (UID 1000)
3. **Configuration manquante** :
- Créer les répertoires de configuration avant de démarrer :
```bash
mkdir -p config cache
```
### UPnP ne fonctionne pas
1. **Network mode** :
- Sur Linux : utiliser `network_mode: host`
- Sur macOS/Windows : UPnP peut ne pas fonctionner correctement avec Docker Desktop
2. **Firewall** :
- Vérifier que les ports UPnP ne sont pas bloqués
- Autoriser le multicast sur le réseau
## Performance
### Optimisations du build
1. **Build cache** : Docker réutilise les couches en cache
2. **Multi-stage build** : Réduit la taille de l'image finale
3. **Strip des symboles** : Le binaire est strippé pour réduire sa taille
### Optimisations runtime
1. **Resource limits** : Définir des limites CPU/mémoire dans docker-compose.yml
2. **Volumes** : Utiliser des volumes pour les données persistantes
3. **Logs** : Configurer la rotation des logs Docker
## Sécurité
- ✅ Exécution en tant qu'utilisateur non-root
- ✅ Image minimale (surface d'attaque réduite)
- ✅ Pas de secrets dans l'image
- ✅ Health checks activés
- ✅ Certificats CA inclus pour HTTPS
## Références
- [Dockerfile](./Dockerfile)
- [docker-compose.yml](./docker-compose.yml)
- [.dockerignore](./.dockerignore)
- [Documentation Rust](./Readme.md)

108
Dockerfile Normal file
View File

@@ -0,0 +1,108 @@
# ===================================
# Stage 1: Build Vue.js webapp
# ===================================
FROM node:22-alpine AS webapp-builder
WORKDIR /webapp
# Copy webapp package files
COPY pmoapp/webapp/package*.json ./
# Install dependencies
RUN npm ci --production=false
# Copy webapp source
COPY pmoapp/webapp/ ./
# Build the webapp
RUN npm run build
# ===================================
# Stage 2: Build Rust binary
# ===================================
FROM rustlang/rust:nightly-bookworm AS rust-builder
WORKDIR /build
# Install system dependencies for building
RUN apt-get update && apt-get install -y \
libsoxr-dev \
libasound2-dev \
pkg-config \
cmake \
&& rm -rf /var/lib/apt/lists/*
# Copy Cargo workspace files
COPY Cargo.toml Cargo.lock ./
# Copy all crates
COPY PMOMusic/ ./PMOMusic/
COPY pmoupnp/ ./pmoupnp/
COPY pmomediarenderer/ ./pmomediarenderer/
COPY pmomediaserver/ ./pmomediaserver/
COPY pmoconfig/ ./pmoconfig/
COPY pmoutils/ ./pmoutils/
COPY pmodidl/ ./pmodidl/
COPY pmoserver/ ./pmoserver/
COPY pmocache/ ./pmocache/
COPY pmocovers/ ./pmocovers/
COPY pmoaudiocache/ ./pmoaudiocache/
COPY pmoaudio/ ./pmoaudio/
COPY pmoqobuz/ ./pmoqobuz/
COPY pmoparadise/ ./pmoparadise/
COPY pmosource/ ./pmosource/
COPY pmoplaylist/ ./pmoplaylist/
COPY pmoflac/ ./pmoflac/
COPY pmometadata/ ./pmometadata/
COPY pmocontrol/ ./pmocontrol/
COPY pmoaudio-ext/ ./pmoaudio-ext/
COPY pmoapp/ ./pmoapp/
# Copy the webapp dist from previous stage
COPY --from=webapp-builder /webapp/dist ./pmoapp/webapp/dist/
# Build the Rust binary in release mode
RUN cargo build --release --bin PMOMusic
# Strip debug symbols to reduce binary size
RUN strip /build/target/release/PMOMusic
# ===================================
# Stage 3: Minimal runtime image
# ===================================
FROM debian:bookworm-slim
# Install only runtime dependencies
RUN apt-get update && apt-get install -y \
libsoxr0 \
libasound2 \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Create a non-root user
RUN useradd -m -u 1000 pmomusic
# Copy the binary from builder
COPY --from=rust-builder /build/target/release/PMOMusic /usr/local/bin/PMOMusic
# Set ownership
RUN chown pmomusic:pmomusic /usr/local/bin/PMOMusic
# Switch to non-root user
USER pmomusic
# Create directories for configuration and cache
RUN mkdir -p /home/pmomusic/.pmomusic
# Set working directory
WORKDIR /home/pmomusic
# Expose default port (adjust if needed)
EXPOSE 8080
# Health check (adjust the URL if needed)
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD ["/usr/local/bin/PMOMusic", "--help"] || exit 1
# Run the binary
ENTRYPOINT ["/usr/local/bin/PMOMusic"]

135
docker-build.sh Executable file
View File

@@ -0,0 +1,135 @@
#!/bin/bash
# Script de build Docker pour PMOMusic
# Usage: ./docker-build.sh [OPTIONS]
set -e
# Couleurs pour l'affichage
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m' # No Color
# Configuration par défaut
IMAGE_NAME="pmomusic"
TAG="latest"
NO_CACHE=false
PUSH=true
REGISTRY="niepce.petite-maison-orange.fr/public"
# Fonction d'aide
show_help() {
echo "Usage: $0 [OPTIONS]"
echo ""
echo "Options:"
echo " -t, --tag TAG Tag de l'image (défaut: latest)"
echo " -r, --registry URL Registry Docker (ex: ghcr.io/user)"
echo " -n, --no-cache Build sans cache"
echo " -p, --push Push l'image vers le registry"
echo " -h, --help Affiche cette aide"
echo ""
echo "Exemples:"
echo " $0 # Build local avec tag 'latest'"
echo " $0 -t v1.0.0 # Build avec tag 'v1.0.0'"
echo " $0 -t v1.0.0 -r ghcr.io/user -p # Build et push vers GHCR"
echo " $0 -n # Build sans cache"
exit 0
}
# Parsing des arguments
while [[ $# -gt 0 ]]; do
case $1 in
-t|--tag)
TAG="$2"
shift 2
;;
-r|--registry)
REGISTRY="$2"
shift 2
;;
-n|--no-cache)
NO_CACHE=true
shift
;;
-p|--push)
PUSH=true
shift
;;
-h|--help)
show_help
;;
*)
echo -e "${RED}Erreur: Option inconnue '$1'${NC}"
show_help
;;
esac
done
# Construire le nom complet de l'image
if [ -n "$REGISTRY" ]; then
FULL_IMAGE_NAME="$REGISTRY/$IMAGE_NAME:$TAG"
else
FULL_IMAGE_NAME="$IMAGE_NAME:$TAG"
fi
# Afficher la configuration
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}Build Docker PMOMusic${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""
echo "Image: $FULL_IMAGE_NAME"
echo "No cache: $NO_CACHE"
echo "Push: $PUSH"
echo ""
# Construire la commande Docker
DOCKER_CMD="docker build"
if [ "$NO_CACHE" = true ]; then
DOCKER_CMD="$DOCKER_CMD --no-cache"
fi
DOCKER_CMD="$DOCKER_CMD -t $FULL_IMAGE_NAME ."
# Exécuter le build
echo -e "${YELLOW}→ Démarrage du build...${NC}"
echo "Commande: $DOCKER_CMD"
echo ""
if eval "$DOCKER_CMD"; then
echo ""
echo -e "${GREEN}✓ Build réussi !${NC}"
# Afficher la taille de l'image
IMAGE_SIZE=$(docker images "$FULL_IMAGE_NAME" --format "{{.Size}}")
echo "Taille de l'image: $IMAGE_SIZE"
# Push si demandé
if [ "$PUSH" = true ]; then
echo ""
echo -e "${YELLOW}→ Push de l'image vers le registry...${NC}"
if docker push "$FULL_IMAGE_NAME"; then
echo -e "${GREEN}✓ Image pushée avec succès !${NC}"
else
echo -e "${RED}✗ Erreur lors du push${NC}"
exit 1
fi
fi
echo ""
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN}Build terminé avec succès !${NC}"
echo -e "${GREEN}========================================${NC}"
echo ""
echo "Pour lancer le conteneur:"
echo " docker run -it --rm --network host $FULL_IMAGE_NAME"
echo ""
echo "Ou avec docker-compose:"
echo " docker-compose up -d"
else
echo ""
echo -e "${RED}✗ Erreur lors du build${NC}"
exit 1
fi

39
docker-compose.yml Normal file
View File

@@ -0,0 +1,39 @@
version: '3.8'
services:
pmomusic:
build:
context: .
dockerfile: Dockerfile
image: pmomusic:latest
container_name: pmomusic
# Port mapping (adjust to your needs)
ports:
- "8080:8080"
# Volume for persistent configuration
volumes:
- ./config:/home/pmomusic/.pmomusic
- ./cache:/home/pmomusic/cache
# Environment variables (adjust as needed)
environment:
- RUST_LOG=info
# Add other environment variables here
# Network mode for UPnP/DLNA discovery (host mode for multicast)
network_mode: host
# Restart policy
restart: unless-stopped
# Resource limits (optional)
deploy:
resources:
limits:
cpus: '2.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 256M

6
package-lock.json generated Normal file
View File

@@ -0,0 +1,6 @@
{
"name": "pmomusic",
"lockfileVersion": 3,
"requires": true,
"packages": {}
}

View File

@@ -18,6 +18,7 @@
},
"devDependencies": {
"@types/dompurify": "^3.0.5",
"@types/node": "^22.0.0",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.8.1",
"typescript": "~5.8.3",
@@ -851,6 +852,17 @@
"dev": true,
"license": "MIT"
},
"node_modules/@types/node": {
"version": "22.19.3",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz",
"integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
@@ -1502,6 +1514,13 @@
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
},
"node_modules/vite": {
"version": "7.1.7",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.7.tgz",

View File

@@ -18,6 +18,7 @@
"vue-virtual-scroller": "^2.0.0-beta.8"
},
"devDependencies": {
"@types/node": "^22.0.0",
"@types/dompurify": "^3.0.5",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/tsconfig": "^0.8.1",

1
version.txt Normal file
View File

@@ -0,0 +1 @@
0.10