Bonnes pratiques Portainer
Objectifs du chapitre
- Sécuriser votre installation Portainer
- Optimiser les performances
- Configurer la haute disponibilité
- Mettre en place le monitoring
1 - Sécurité
Sécuriser l'accès
HTTPS obligatoire
# Avec certificat Let's Encrypt via Traefik
docker run -d \
--name portainer \
-v portainer_data:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
portainer/portainer-ce:latest
# Ou avec certificat personnalisé
docker run -d \
-p 9443:9443 \
-v /path/to/certs:/certs \
-v portainer_data:/data \
-v /var/run/docker.sock:/var/run/docker.sock \
portainer/portainer-ce:latest \
--ssl --sslcert /certs/cert.pem --sslkey /certs/key.pem
Firewall
# Limiter l'accès au port Portainer
ufw allow from 192.168.1.0/24 to any port 9443
ufw allow from 10.0.0.0/8 to any port 9001 # Agent
Protection du socket Docker
┌─────────────────────────────────────────────────────────────────┐
│ Options de sécurité socket │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Option 1: Socket direct (développement) │
│ -v /var/run/docker.sock:/var/run/docker.sock │
│ ⚠️ Accès root complet au daemon Docker │
│ │
│ Option 2: Agent Portainer (recommandé production) │
│ Agent sur chaque hôte, socket isolé │
│ ✅ Meilleure isolation │
│ │
│ Option 3: TLS mutual auth │
│ Socket exposé via TCP avec certificats │
│ ✅ Authentification forte │
│ │
└─────────────────────────────────────────────────────────────────┘
Politique de mots de passe
| Critère | Recommandation |
|---|---|
| Longueur | Minimum 12 caractères |
| Complexité | Majuscules, minuscules, chiffres, symboles |
| Expiration | Rotation tous les 90 jours |
| Historique | Interdire les 5 derniers mots de passe |
2 - Configuration de production
Variables d'environnement
docker run -d \
--name portainer \
-p 9443:9443 \
-e HIDE_CONFIGURATION_FIELDS=enable \
-e NO_ANALYTICS=1 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v portainer_data:/data \
portainer/portainer-ce:latest
| Variable | Description |
|---|---|
NO_ANALYTICS | Désactiver la télémétrie |
HIDE_CONFIGURATION_FIELDS | Cacher les mots de passe |
ADMIN_PASSWORD | Définir le mot de passe admin au démarrage |
Limites de ressources
# docker-compose.yml
services:
portainer:
image: portainer/portainer-ce:latest
deploy:
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.25'
memory: 128M
restart: unless-stopped
3 - Haute disponibilité
Architecture HA
┌─────────────────────────────────────────────────────────────────┐
│ High Availability Setup │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ │
│ │ Load Balancer │ │
│ │ (HAProxy/ │ │
│ │ Traefik) │ │
│ └─────── ─┬────────┘ │
│ │ │
│ ┌──────────────┼──────────────┐ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Portainer │ │ Portainer │ │ Portainer │ │
│ │ Node 1 │ │ Node 2 │ │ Node 3 │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
│ └──────────────┼──────────────┘ │
│ │ │
│ ┌───────▼───────┐ │
│ │ Shared Volume │ │
│ │ (NFS/EFS) │ │
│ └───────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Déploiement Swarm HA
# portainer-ha.yml
version: "3.9"
services:
portainer:
image: portainer/portainer-ce:latest
command: -H tcp://tasks.agent:9001 --tlsskipverify
volumes:
- portainer_data:/data
networks:
- agent_network
deploy:
mode: replicated
replicas: 1
placement:
constraints:
- node.role == manager
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
agent:
image: portainer/agent:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /var/lib/docker/volumes:/var/lib/docker/volumes
networks:
- agent_network
deploy:
mode: global
networks:
agent_network:
driver: overlay
attachable: true
volumes:
portainer_data:
driver: local
driver_opts:
type: nfs
o: addr=nfs-server,rw
device: ":/portainer"
4 - Backup et restore
Sauvegarder les données
# Arrêter Portainer
docker stop portainer
# Sauvegarder le volume
docker run --rm \
-v portainer_data:/data \
-v $(pwd):/backup \
alpine tar czf /backup/portainer-backup-$(date +%Y%m%d).tar.gz /data
# Redémarrer Portainer
docker start portainer
Script de backup automatique
#!/bin/bash
# backup-portainer.sh
BACKUP_DIR=/backups/portainer
RETENTION_DAYS=30
DATE=$(date +%Y%m%d_%H%M%S)
# Créer le backup
docker run --rm \
-v portainer_data:/data \
-v $BACKUP_DIR:/backup \
alpine tar czf /backup/portainer-$DATE.tar.gz /data
# Nettoyer les anciens backups
find $BACKUP_DIR -name "portainer-*.tar.gz" -mtime +$RETENTION_DAYS -delete
echo "Backup completed: portainer-$DATE.tar.gz"
Restaurer
# Arrêter Portainer
docker stop portainer
# Supprimer les anciennes données
docker run --rm -v portainer_data:/data alpine rm -rf /data/*
# Restaurer
docker run --rm \
-v portainer_data:/data \
-v $(pwd):/backup \
alpine tar xzf /backup/portainer-backup.tar.gz -C /
# Redémarrer
docker start portainer
5 - Monitoring de Portainer
Healthcheck
services:
portainer:
image: portainer/portainer-ce:latest
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:9000/api/status"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Métriques Prometheus
Portainer expose des métriques sur /api/endpoints/{id}/docker/info :
# prometheus.yml
scrape_configs:
- job_name: 'portainer'
static_configs:
- targets: ['portainer:9000']
metrics_path: /api/system/status
bearer_token: 'your-api-token'
Dashboard Grafana
┌─────────────────────────────────────────────────────────────────┐
│ Portainer Monitoring Dashboard │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Environments │ │ Containers │ │ Stacks │ │
│ │ 3 │ │ 47 │ │ 12 │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Portainer Uptime │ │
│ │ ████████████████████████████████████████████ 99.9% │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ API Response Time │ │
│ │ ▁▂▃▂▁▂▃▄▃▂▁▂▃▂▁ avg: 45ms │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
6 - API et automatisation
Générer un token API
- Settings > API > Add access token
- Nommer le token
- Copier et stocker de manière sécurisée
Utilisation de l'API
# Obtenir la liste des environnements
curl -s -H "X-API-Key: ptr_xxxx" \
https://portainer.example.com/api/endpoints
# Lister les conteneurs
curl -s -H "X-API-Key: ptr_xxxx" \
https://portainer.example.com/api/endpoints/1/docker/containers/json
# Démarrer un conteneur
curl -X POST -H "X-API-Key: ptr_xxxx" \
https://portainer.example.com/api/endpoints/1/docker/containers/abc123/start
Script d'automatisation
#!/usr/bin/env python3
import requests
PORTAINER_URL = "https://portainer.example.com"
API_KEY = "ptr_xxxxxxxxxxxx"
headers = {"X-API-Key": API_KEY}
# Lister les conteneurs arrêtés
response = requests.get(
f"{PORTAINER_URL}/api/endpoints/1/docker/containers/json",
headers=headers,
params={"all": True, "filters": '{"status": ["exited"]}'}
)
stopped = response.json()
print(f"Found {len(stopped)} stopped containers")
# Nettoyer les conteneurs arrêtés depuis plus de 7 jours
for container in stopped:
# ... logique de nettoyage
pass
7 - Checklist de production
Avant le déploiement
☐ HTTPS configuré avec certificat valide
☐ Mot de passe admin fort défini
☐ Authentification externe configurée (OAuth/LDAP)
☐ Équipes et permissions définies
☐ Firewall configuré (ports 9443, 9001)
☐ Backups automatisés
☐ Monitoring en place
Configuration
☐ Variables d'environnement de production
☐ Limites de ressources définies
☐ Restart policy configurée
☐ Logs centralisés
☐ Analytics désactivé (NO_ANALYTICS=1)
Sécurité
☐ Socket Docker protégé ou agent utilisé
☐ Edge agents pour environnements distants
☐ Audit logs activé (Business Edition)
☐ Sessions timeout configuré
☐ 2FA activé si disponible
8 - Dépannage courant
Portainer ne démarre pas
# Vérifier les logs
docker logs portainer
# Vérifier les permissions du socket
ls -la /var/run/docker.sock
# Réinitialiser les données (perd la configuration)
docker volume rm portainer_data
docker volume create portainer_data
Connexion refusée
# Vérifier que le conteneur tourne
docker ps | grep portainer
# Vérifier les ports
docker port portainer
# Tester la connectivité locale
curl -k https://localhost:9443
Réinitialiser le mot de passe
docker stop portainer
docker run --rm \
-v portainer_data:/data \
portainer/helper-reset-password
docker start portainer
Agent ne se connecte pas
# Vérifier que l'agent tourne
docker ps | grep portainer_agent
# Vérifier les logs de l'agent
docker logs portainer_agent
# Vérifier la connectivité réseau
telnet <portainer_ip> 9001
Résumé
| Aspect | Recommandation |
|---|---|
| HTTPS | Obligatoire avec certificat valide |
| Auth | OAuth/LDAP en production |
| Socket | Utiliser l'agent plutôt que le socket direct |
| Backup | Quotidien avec rétention 30 jours |
| Monitoring | Healthcheck + métriques Prometheus |
| API | Tokens pour l'automatisation |
Points clés
- Sécurisez toujours l'accès avec HTTPS
- Utilisez l'agent pour les environnements de production
- Automatisez les backups
- Mettez en place le monitoring
- Utilisez l'API pour l'automatisation
Exercices pratiques
- Configurez HTTPS avec un certificat Let's Encrypt
- Mettez en place un backup automatique quotidien
- Configurez le healthcheck pour monitoring
- Créez un script API pour lister les ressources