🐳 Guide Portainer Complet

📦 PORTAINER CE/EE 2.19+

Installation, configuration et gestion avancée de vos environnements Docker

📅 Dernière mise à jour : Décembre 2024
⏱️ Temps de lecture : 30 min
📊 Niveau : Débutant à Expert
🔧 Docker 20.10+

📖Introduction à Portainer

Portainer est une interface web légère et intuitive pour gérer vos environnements Docker, Docker Swarm et Kubernetes. Il simplifie la gestion des conteneurs, images, volumes, réseaux et bien plus encore, tout en offrant des fonctionnalités avancées pour les environnements de production.

Fonctionnalités principales

🐳
Gestion Docker
Interface complète pour gérer conteneurs, images, volumes et réseaux Docker
🔄
Docker Swarm
Orchestration et gestion de clusters Docker Swarm
☸️
Kubernetes
Support complet de Kubernetes avec interface simplifiée
👥
Multi-utilisateurs
Gestion des utilisateurs, équipes et contrôle d'accès
📊
Monitoring
Surveillance en temps réel des ressources et performances
🔒
Sécurité
Authentification, autorisation et audit complets

Portainer CE vs EE

Fonctionnalité Community Edition (CE) Enterprise Edition (EE)
Gestion Docker/Swarm ✅ Complète ✅ Complète
Support Kubernetes ✅ Basique ✅ Avancé
Authentification LDAP/OAuth
RBAC avancé
Support commercial
Audit logs
💡 Conseil

Portainer CE est parfait pour les environnements de développement et les petites équipes. Pour les environnements de production avec des besoins de sécurité avancés, considérez Portainer EE.

⚙️Prérequis et Préparation

Configuration système requise

512MB
RAM minimum
1GB
Espace disque
9000
Port par défaut
9443
Port HTTPS

Prérequis Docker

Vérification de Docker

Assurez-vous que Docker est installé et fonctionne :

🐳 Vérification Docker
# Vérification de la version Docker
docker --version

# Test de fonctionnement
docker run hello-world

# Vérification des informations système
docker system info

# Vérification de l'espace disque
docker system df

Configuration des ports

Ouvrez les ports nécessaires dans votre pare-feu :

🔥 Configuration UFW
# Port HTTP Portainer
sudo ufw allow 9000/tcp

# Port HTTPS Portainer
sudo ufw allow 9443/tcp

# Port pour agent Portainer (si nécessaire)
sudo ufw allow 9001/tcp

# Vérification des règles
sudo ufw status

Préparation des volumes

Créez les répertoires pour les données persistantes :

📁 Préparation volumes
# Création du répertoire pour les données Portainer
sudo mkdir -p /opt/portainer/data

# Permissions appropriées
sudo chown -R 1000:1000 /opt/portainer/data

# Création du répertoire pour les certificats SSL
sudo mkdir -p /opt/portainer/certs

# Vérification
ls -la /opt/portainer/

🚀Installation Standalone

Installation rapide avec Docker

Installation basique

Installation simple de Portainer CE :

🐳 Installation Portainer CE
# Création du volume pour les données
docker volume create portainer_data

# Lancement de Portainer CE
docker run -d \
  --name portainer \
  --restart=always \
  -p 8000:8000 \
  -p 9000:9000 \
  -p 9443:9443 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest

# Vérification du conteneur
docker ps | grep portainer

Installation avec Docker Compose

Configuration plus avancée avec Docker Compose :

📝 docker-compose.yml
version: '3.8'

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    ports:
      - "8000:8000"
      - "9000:9000"
      - "9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - portainer_data:/data
      - /opt/portainer/certs:/certs:ro
    environment:
      - PORTAINER_ADMIN_PASSWORD_FILE=/run/secrets/portainer_admin_password
    secrets:
      - portainer_admin_password
    command: >
      --admin-password-file /run/secrets/portainer_admin_password
      --ssl
      --sslcert /certs/portainer.crt
      --sslkey /certs/portainer.key
    healthcheck:
      test: ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost:9000/api/status"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

volumes:
  portainer_data:
    driver: local

secrets:
  portainer_admin_password:
    file: ./portainer_admin_password.txt

Configuration avec SSL personnalisé

Génération et configuration des certificats SSL :

🔐 Génération certificats SSL
# Génération de la clé privée
openssl genrsa -out /opt/portainer/certs/portainer.key 4096

# Génération du certificat auto-signé
openssl req -new -x509 -key /opt/portainer/certs/portainer.key \
  -out /opt/portainer/certs/portainer.crt -days 365 \
  -subj "/C=FR/ST=IDF/L=Paris/O=MonEntreprise/CN=portainer.domain.local"

# Permissions sécurisées
chmod 600 /opt/portainer/certs/portainer.key
chmod 644 /opt/portainer/certs/portainer.crt

# Création du fichier de mot de passe admin
echo 'MonMotDePasseSecurise123!' > ./portainer_admin_password.txt
chmod 600 ./portainer_admin_password.txt

# Lancement avec Docker Compose
docker-compose up -d
🎉 Installation réussie !

Portainer est maintenant accessible à l'adresse : https://votre-serveur:9443 ou http://votre-serveur:9000

🔄Installation Docker Swarm

Déploiement en mode Swarm

Initialisation du Swarm

Configuration d'un cluster Docker Swarm :

🔄 Initialisation Swarm
# Initialisation du Swarm (sur le manager principal)
docker swarm init --advertise-addr $(hostname -I | awk '{print $1}')

# Récupération du token pour les workers
docker swarm join-token worker

# Récupération du token pour les managers
docker swarm join-token manager

# Vérification du cluster
docker node ls

Stack Portainer pour Swarm

Déploiement de Portainer en tant que service Swarm :

📝 portainer-swarm-stack.yml
version: '3.8'

services:
  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
      placement:
        constraints: [node.platform.os == linux]
      resources:
        limits:
          memory: 128M
        reservations:
          memory: 64M

  portainer:
    image: portainer/portainer-ce:latest
    command: -H tcp://tasks.agent:9001 --tlsskipverify
    ports:
      - "9000:9000"
      - "9443:9443"
      - "8000:8000"
    volumes:
      - portainer_data:/data
    networks:
      - agent_network
    deploy:
      mode: replicated
      replicas: 1
      placement:
        constraints: [node.role == manager]
      resources:
        limits:
          memory: 512M
        reservations:
          memory: 256M
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
        window: 120s
      update_config:
        parallelism: 1
        delay: 10s
        failure_action: rollback
        monitor: 60s
        max_failure_ratio: 0.3

networks:
  agent_network:
    driver: overlay
    attachable: true

volumes:
  portainer_data:
    driver: local

Déploiement de la stack

🚀 Déploiement Swarm
# Déploiement de la stack Portainer
docker stack deploy -c portainer-swarm-stack.yml portainer

# Vérification du déploiement
docker stack ls
docker stack services portainer
docker service logs portainer_portainer

# Vérification des agents
docker service ps portainer_agent
💡 Architecture Swarm

Dans cette configuration, Portainer Agent s'exécute sur chaque nœud du cluster, permettant à Portainer de gérer l'ensemble du Swarm depuis une interface centralisée.

☸️Installation Kubernetes

Déploiement sur Kubernetes

Installation via Helm

Méthode recommandée avec Helm Chart :

⎈ Installation Helm
# Ajout du repository Helm Portainer
helm repo add portainer https://portainer.github.io/k8s/
helm repo update

# Installation de Portainer
helm install portainer portainer/portainer \
  --create-namespace \
  --namespace portainer \
  --set service.type=LoadBalancer \
  --set tls.force=true

# Vérification du déploiement
kubectl get all -n portainer

# Récupération de l'IP externe
kubectl get svc -n portainer

Installation via manifestes YAML

Déploiement manuel avec des manifestes Kubernetes :

📝 portainer-k8s.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: portainer
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: portainer-sa-clusteradmin
  namespace: portainer
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: portainer-crb-clusteradmin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: portainer-sa-clusteradmin
  namespace: portainer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: portainer-pvc
  namespace: portainer
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: portainer
  namespace: portainer
spec:
  replicas: 1
  selector:
    matchLabels:
      app: portainer
  template:
    metadata:
      labels:
        app: portainer
    spec:
      serviceAccountName: portainer-sa-clusteradmin
      containers:
      - name: portainer
        image: portainer/portainer-ce:latest
        ports:
        - containerPort: 9000
        - containerPort: 9443
        - containerPort: 8000
        volumeMounts:
        - name: data
          mountPath: /data
        resources:
          limits:
            memory: "512Mi"
            cpu: "500m"
          requests:
            memory: "256Mi"
            cpu: "250m"
        livenessProbe:
          httpGet:
            path: /api/status
            port: 9000
          initialDelaySeconds: 30
          periodSeconds: 30
        readinessProbe:
          httpGet:
            path: /api/status
            port: 9000
          initialDelaySeconds: 5
          periodSeconds: 5
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: portainer-pvc
---
apiVersion: v1
kind: Service
metadata:
  name: portainer
  namespace: portainer
spec:
  type: LoadBalancer
  ports:
  - port: 9000
    targetPort: 9000
    name: http
  - port: 9443
    targetPort: 9443
    name: https
  - port: 8000
    targetPort: 8000
    name: edge
  selector:
    app: portainer

Déploiement et configuration

🚀 Déploiement K8s
# Application des manifestes
kubectl apply -f portainer-k8s.yaml

# Vérification du déploiement
kubectl get pods -n portainer
kubectl get svc -n portainer

# Vérification des logs
kubectl logs -n portainer deployment/portainer

# Port-forward pour accès local (si pas de LoadBalancer)
kubectl port-forward -n portainer svc/portainer 9000:9000 9443:9443

⚙️Configuration Initiale

Premier accès et configuration

Création du compte administrateur

Lors du premier accès à Portainer :

⚠️ Important

Vous avez 5 minutes après le démarrage de Portainer pour créer le compte administrateur. Passé ce délai, le conteneur se ferme pour des raisons de sécurité.

  1. Accédez à http://votre-serveur:9000 ou https://votre-serveur:9443
  2. Créez un nom d'utilisateur administrateur
  3. Définissez un mot de passe fort (minimum 12 caractères)
  4. Cliquez sur "Create user"

Configuration de l'environnement

Sélection et configuration de votre environnement :

  • Docker : Pour un environnement Docker local
  • Docker Swarm : Pour un cluster Swarm
  • Kubernetes : Pour un cluster Kubernetes
  • ACI : Pour Azure Container Instances
  • Edge Agent : Pour des environnements distants

Configuration des paramètres globaux

⚙️ Configuration via API
# Script de configuration automatique
#!/bin/bash

PORTAINER_URL="https://localhost:9443"
USERNAME="admin"
PASSWORD="VotreMotDePasse"

# Authentification et récupération du token
TOKEN=$(curl -s -X POST "$PORTAINER_URL/api/auth" \
  -H "Content-Type: application/json" \
  -d "{\"Username\":\"$USERNAME\",\"Password\":\"$PASSWORD\"}" \
  --insecure | jq -r '.jwt')

# Configuration des paramètres globaux
curl -X PUT "$PORTAINER_URL/api/settings" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "LogoURL": "",
    "BlackListedLabels": [],
    "AuthenticationMethod": 1,
    "LDAPSettings": {
      "ReaderDN": "",
      "Password": "",
      "URL": "",
      "TLSConfig": {
        "TLS": false,
        "TLSSkipVerify": false
      },
      "StartTLS": false,
      "SearchSettings": [
        {
          "BaseDN": "",
          "Filter": "",
          "UserNameAttribute": ""
        }
      ],
      "GroupSearchSettings": [
        {
          "GroupBaseDN": "",
          "GroupFilter": "",
          "GroupAttribute": ""
        }
      ]
    },
    "AllowBindMountsForRegularUsers": false,
    "AllowPrivilegedModeForRegularUsers": false,
    "AllowVolumeBrowserForRegularUsers": false,
    "AllowHostNamespaceForRegularUsers": false,
    "AllowStackManagementForRegularUsers": true,
    "AllowDeviceMappingForRegularUsers": false,
    "AllowContainerCapabilitiesForRegularUsers": false,
    "EnableHostManagementFeatures": false,
    "EdgeAgentCheckinInterval": 5
  }' \
  --insecure

🔒Sécurisation Avancée

Configuration SSL/TLS

Certificats Let's Encrypt

Configuration automatique avec Certbot :

🔐 Let's Encrypt + Nginx
# Installation de Certbot
sudo apt install -y certbot python3-certbot-nginx

# Génération du certificat
sudo certbot --nginx -d portainer.votre-domaine.com

# Configuration Nginx pour Portainer
cat > /etc/nginx/sites-available/portainer << 'EOF'
server {
    listen 80;
    server_name portainer.votre-domaine.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name portainer.votre-domaine.com;

    ssl_certificate /etc/letsencrypt/live/portainer.votre-domaine.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/portainer.votre-domaine.com/privkey.pem;
    
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    location / {
        proxy_pass http://localhost:9000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
        proxy_read_timeout 86400;
    }
}
EOF

# Activation du site
sudo ln -s /etc/nginx/sites-available/portainer /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Restriction d'accès par IP

🛡️ Restriction IP
# Configuration Docker avec restriction IP
docker run -d \
  --name portainer \
  --restart=always \
  -p 127.0.0.1:9000:9000 \
  -p 127.0.0.1:9443:9443 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest

# Configuration UFW pour restriction
sudo ufw deny 9000
sudo ufw deny 9443
sudo ufw allow from 192.168.1.0/24 to any port 9000
sudo ufw allow from 192.168.1.0/24 to any port 9443

# Fail2ban pour Portainer
cat > /etc/fail2ban/jail.d/portainer.conf << 'EOF'
[portainer]
enabled = true
port = 9000,9443
filter = portainer
logpath = /var/lib/docker/containers/*/portainer*-json.log
maxretry = 3
bantime = 3600
findtime = 600
EOF

# Filtre Fail2ban
cat > /etc/fail2ban/filter.d/portainer.conf << 'EOF'
[Definition]
failregex = .*Invalid credentials.*.*
ignoreregex =
EOF

sudo systemctl restart fail2ban

Durcissement de la sécurité

🔒 Docker Compose sécurisé
version: '3.8'

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    
    # Sécurité renforcée
    security_opt:
      - no-new-privileges:true
      - apparmor:docker-default
    
    # Utilisateur non-root
    user: "1000:1000"
    
    # Limitations de ressources
    deploy:
      resources:
        limits:
          memory: 512M
          cpus: '0.5'
        reservations:
          memory: 256M
          cpus: '0.25'
    
    # Réseau personnalisé
    networks:
      - portainer_network
    
    ports:
      - "127.0.0.1:9000:9000"
      - "127.0.0.1:9443:9443"
    
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - portainer_data:/data
      - /opt/portainer/certs:/certs:ro
    
    environment:
      - PORTAINER_ADMIN_PASSWORD_FILE=/run/secrets/portainer_admin_password
    
    secrets:
      - portainer_admin_password
    
    command: >
      --admin-password-file /run/secrets/portainer_admin_password
      --ssl
      --sslcert /certs/portainer.crt
      --sslkey /certs/portainer.key
      --hide-label owner=system
      --logo "https://votre-domaine.com/logo.png"

networks:
  portainer_network:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

volumes:
  portainer_data:
    driver: local

secrets:
  portainer_admin_password:
    file: ./portainer_admin_password.txt

🌐Gestion Multi-Environnements

Configuration des environnements distants

Portainer Agent

Installation d'agents sur des serveurs distants :

🤖 Installation Agent
# Sur le serveur distant - Installation de l'agent
docker run -d \
  --name portainer_agent \
  --restart=always \
  -p 9001:9001 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/docker/volumes:/var/lib/docker/volumes \
  portainer/agent:latest

# Vérification de l'agent
docker logs portainer_agent

# Test de connectivité
curl http://serveur-distant:9001/ping

Edge Agent pour environnements isolés

Configuration pour des environnements sans connectivité directe :

🌐 Edge Agent
# Génération de la commande Edge Agent depuis Portainer UI
# Environments > Add environment > Edge Agent

# Exemple de commande générée
docker run -d \
  --name portainer_edge_agent \
  --restart=always \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v /var/lib/docker/volumes:/var/lib/docker/volumes \
  -v /:/host \
  -v portainer_agent_data:/data \
  --label "io.portainer.agent=true" \
  -e EDGE=1 \
  -e EDGE_ID=votre-edge-id \
  -e EDGE_KEY=votre-edge-key \
  -e CAP_HOST_MANAGEMENT=1 \
  portainer/agent:latest

# Configuration du tunnel (si nécessaire)
# Pour les environnements derrière NAT/Firewall

Script d'automatisation multi-environnements

🤖 Script automatisation
#!/bin/bash
# portainer-multi-env-setup.sh

PORTAINER_URL="https://portainer.votre-domaine.com"
USERNAME="admin"
PASSWORD="VotreMotDePasse"

# Liste des environnements à ajouter
declare -A ENVIRONMENTS=(
    ["prod-web"]="tcp://prod-web.domain.com:2376"
    ["staging"]="tcp://staging.domain.com:2376"
    ["dev"]="tcp://dev.domain.com:2376"
)

# Authentification
TOKEN=$(curl -s -X POST "$PORTAINER_URL/api/auth" \
  -H "Content-Type: application/json" \
  -d "{\"Username\":\"$USERNAME\",\"Password\":\"$PASSWORD\"}" \
  --insecure | jq -r '.jwt')

# Fonction pour ajouter un environnement
add_environment() {
    local name=$1
    local url=$2
    
    echo "Ajout de l'environnement: $name ($url)"
    
    curl -s -X POST "$PORTAINER_URL/api/endpoints" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d "{
        \"Name\": \"$name\",
        \"URL\": \"$url\",
        \"EndpointCreationType\": 1,
        \"TLS\": true,
        \"TLSSkipVerify\": true
      }" \
      --insecure
}

# Ajout de tous les environnements
for env_name in "${!ENVIRONMENTS[@]}"; do
    add_environment "$env_name" "${ENVIRONMENTS[$env_name]}"
    sleep 2
done

echo "Configuration multi-environnements terminée!"

👥Authentification LDAP/OAuth

Configuration LDAP (Portainer EE)

💼 Portainer Enterprise

L'authentification LDAP/OAuth n'est disponible que dans Portainer Enterprise Edition. Voici la configuration pour référence.

🔐 Configuration LDAP
# Configuration LDAP via API
curl -X PUT "$PORTAINER_URL/api/settings" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "AuthenticationMethod": 2,
    "LDAPSettings": {
      "ReaderDN": "cn=portainer,ou=services,dc=domain,dc=local",
      "Password": "password-service-account",
      "URL": "ldap://dc.domain.local:389",
      "TLSConfig": {
        "TLS": false,
        "TLSSkipVerify": false
      },
      "StartTLS": false,
      "SearchSettings": [
        {
          "BaseDN": "ou=users,dc=domain,dc=local",
          "Filter": "(&(objectClass=person)(uid={{.Username}}))",
          "UserNameAttribute": "uid"
        }
      ],
      "GroupSearchSettings": [
        {
          "GroupBaseDN": "ou=groups,dc=domain,dc=local",
          "GroupFilter": "(&(objectClass=groupOfNames)(member={{.UserDN}}))",
          "GroupAttribute": "cn"
        }
      ]
    }
  }' \
  --insecure

Configuration OAuth (GitHub/Google)

🔑 Configuration OAuth
# Configuration OAuth GitHub
curl -X PUT "$PORTAINER_URL/api/settings" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "AuthenticationMethod": 3,
    "OAuthSettings": {
      "ClientID": "votre-github-client-id",
      "ClientSecret": "votre-github-client-secret",
      "AuthorizationURI": "https://github.com/login/oauth/authorize",
      "AccessTokenURI": "https://github.com/login/oauth/access_token",
      "ResourceURI": "https://api.github.com/user",
      "RedirectURI": "https://portainer.votre-domaine.com",
      "UserIdentifier": "login",
      "Scopes": "user:email",
      "OAuthAutoCreateUsers": true,
      "DefaultTeamID": 1
    }
  }' \
  --insecure

💾Sauvegarde et Restauration

Stratégie de sauvegarde

Sauvegarde des données Portainer

💾 Script de sauvegarde
#!/bin/bash
# portainer-backup.sh

BACKUP_DIR="/backup/portainer"
DATE=$(date +%Y%m%d_%H%M%S)
CONTAINER_NAME="portainer"

# Création du répertoire de sauvegarde
mkdir -p "$BACKUP_DIR/$DATE"

# Arrêt temporaire de Portainer
echo "Arrêt de Portainer..."
docker stop $CONTAINER_NAME

# Sauvegarde du volume de données
echo "Sauvegarde des données..."
docker run --rm \
  -v portainer_data:/data \
  -v "$BACKUP_DIR/$DATE":/backup \
  alpine tar czf /backup/portainer-data.tar.gz -C /data .

# Sauvegarde de la configuration Docker Compose
if [ -f "docker-compose.yml" ]; then
    cp docker-compose.yml "$BACKUP_DIR/$DATE/"
fi

# Sauvegarde des certificats
if [ -d "/opt/portainer/certs" ]; then
    tar czf "$BACKUP_DIR/$DATE/certs.tar.gz" -C /opt/portainer certs
fi

# Redémarrage de Portainer
echo "Redémarrage de Portainer..."
docker start $CONTAINER_NAME

# Nettoyage des anciennes sauvegardes (garde 30 jours)
find "$BACKUP_DIR" -type d -mtime +30 -exec rm -rf {} \;

echo "Sauvegarde terminée: $BACKUP_DIR/$DATE"

Restauration des données

🔄 Script de restauration
#!/bin/bash
# portainer-restore.sh

BACKUP_PATH=$1
CONTAINER_NAME="portainer"

if [ -z "$BACKUP_PATH" ]; then
    echo "Usage: $0 /path/to/backup/directory"
    exit 1
fi

# Arrêt de Portainer
echo "Arrêt de Portainer..."
docker stop $CONTAINER_NAME
docker rm $CONTAINER_NAME

# Suppression de l'ancien volume
docker volume rm portainer_data

# Création d'un nouveau volume
docker volume create portainer_data

# Restauration des données
echo "Restauration des données..."
docker run --rm \
  -v portainer_data:/data \
  -v "$BACKUP_PATH":/backup \
  alpine tar xzf /backup/portainer-data.tar.gz -C /data

# Restauration des certificats
if [ -f "$BACKUP_PATH/certs.tar.gz" ]; then
    echo "Restauration des certificats..."
    tar xzf "$BACKUP_PATH/certs.tar.gz" -C /opt/portainer/
fi

# Redémarrage de Portainer
echo "Redémarrage de Portainer..."
if [ -f "$BACKUP_PATH/docker-compose.yml" ]; then
    cp "$BACKUP_PATH/docker-compose.yml" .
    docker-compose up -d
else
    docker run -d \
      --name portainer \
      --restart=always \
      -p 9000:9000 \
      -p 9443:9443 \
      -v /var/run/docker.sock:/var/run/docker.sock \
      -v portainer_data:/data \
      portainer/portainer-ce:latest
fi

echo "Restauration terminée!"

Automatisation avec cron

⏰ Configuration cron
# Ajout au crontab
sudo crontab -e

# Sauvegarde quotidienne à 2h du matin
0 2 * * * /usr/local/bin/portainer-backup.sh

# Sauvegarde hebdomadaire avec notification
0 3 * * 0 /usr/local/bin/portainer-backup.sh && echo "Sauvegarde Portainer réussie" | mail -s "Backup Success" admin@domain.com

# Vérification de l'intégrité des sauvegardes
0 4 * * * find /backup/portainer -name "*.tar.gz" -mtime -1 -exec tar -tzf {} \; > /dev/null || echo "Erreur sauvegarde Portainer" | mail -s "Backup Error" admin@domain.com

📊Monitoring et Logs

Monitoring avec Prometheus

📈 Stack monitoring complète
version: '3.8'

services:
  portainer:
    image: portainer/portainer-ce:latest
    container_name: portainer
    restart: unless-stopped
    ports:
      - "9000:9000"
      - "9443:9443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - portainer_data:/data
    networks:
      - monitoring

  prometheus:
    image: prom/prometheus:latest
    container_name: prometheus
    restart: unless-stopped
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
      - prometheus_data:/prometheus
    command:
      - '--config.file=/etc/prometheus/prometheus.yml'
      - '--storage.tsdb.path=/prometheus'
      - '--web.console.libraries=/etc/prometheus/console_libraries'
      - '--web.console.templates=/etc/prometheus/consoles'
      - '--storage.tsdb.retention.time=200h'
      - '--web.enable-lifecycle'
    networks:
      - monitoring

  grafana:
    image: grafana/grafana:latest
    container_name: grafana
    restart: unless-stopped
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin123
    volumes:
      - grafana_data:/var/lib/grafana
    networks:
      - monitoring

  node-exporter:
    image: prom/node-exporter:latest
    container_name: node-exporter
    restart: unless-stopped
    ports:
      - "9100:9100"
    volumes:
      - /proc:/host/proc:ro
      - /sys:/host/sys:ro
      - /:/rootfs:ro
    command:
      - '--path.procfs=/host/proc'
      - '--path.rootfs=/rootfs'
      - '--path.sysfs=/host/sys'
      - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
    networks:
      - monitoring

  cadvisor:
    image: gcr.io/cadvisor/cadvisor:latest
    container_name: cadvisor
    restart: unless-stopped
    ports:
      - "8080:8080"
    volumes:
      - /:/rootfs:ro
      - /var/run:/var/run:rw
      - /sys:/sys:ro
      - /var/lib/docker/:/var/lib/docker:ro
    networks:
      - monitoring

networks:
  monitoring:
    driver: bridge

volumes:
  portainer_data:
  prometheus_data:
  grafana_data:

Configuration Prometheus

📝 prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "portainer_rules.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets:
          - alertmanager:9093

scrape_configs:
  - job_name: 'prometheus'
    static_configs:
      - targets: ['localhost:9090']

  - job_name: 'node-exporter'
    static_configs:
      - targets: ['node-exporter:9100']

  - job_name: 'cadvisor'
    static_configs:
      - targets: ['cadvisor:8080']

  - job_name: 'portainer'
    static_configs:
      - targets: ['portainer:9000']
    metrics_path: '/api/status'
    scrape_interval: 30s

Alertes personnalisées

🚨 portainer_rules.yml
groups:
  - name: portainer_alerts
    rules:
      - alert: PortainerDown
        expr: up{job="portainer"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "Portainer is down"
          description: "Portainer has been down for more than 1 minute."

      - alert: HighContainerCount
        expr: count(container_last_seen) > 100
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High number of containers"
          description: "More than 100 containers are running."

      - alert: HighMemoryUsage
        expr: (node_memory_MemTotal_bytes - node_memory_MemAvailable_bytes) / node_memory_MemTotal_bytes > 0.9
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "High memory usage"
          description: "Memory usage is above 90%."

      - alert: HighDiskUsage
        expr: (node_filesystem_size_bytes{fstype!="tmpfs"} - node_filesystem_free_bytes{fstype!="tmpfs"}) / node_filesystem_size_bytes{fstype!="tmpfs"} > 0.8
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "High disk usage"
          description: "Disk usage is above 80%."

🔧Troubleshooting

Problèmes courants et solutions

Portainer ne démarre pas

❌ Erreur de démarrage

Symptômes : Le conteneur Portainer s'arrête immédiatement

🔍 Diagnostic
# Vérification des logs
docker logs portainer

# Vérification des permissions du socket Docker
ls -la /var/run/docker.sock

# Vérification de l'espace disque
df -h

# Test de connectivité Docker
docker version

# Vérification des ports
netstat -tlnp | grep -E "(9000|9443)"

# Redémarrage avec debug
docker run --rm -it \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest --log-level=DEBUG

Problèmes de connexion aux environnements

⚠️ Connexion impossible

Symptômes : Impossible de se connecter aux environnements distants

🌐 Test connectivité
# Test de connectivité vers l'agent
curl -v http://serveur-distant:9001/ping

# Vérification des certificats TLS
openssl s_client -connect serveur-distant:2376 -showcerts

# Test Docker API
curl -k https://serveur-distant:2376/version

# Vérification des règles de pare-feu
sudo ufw status
sudo iptables -L

# Test depuis le conteneur Portainer
docker exec -it portainer sh
wget -qO- http://serveur-distant:9001/ping

Problèmes de performance

⚡ Optimisation performance
# Vérification des ressources du conteneur
docker stats portainer

# Augmentation des limites de ressources
docker update --memory=1g --cpus=1.0 portainer

# Nettoyage des données inutiles
docker exec -it portainer sh -c "find /data -name '*.log' -mtime +7 -delete"

# Optimisation de la base de données
docker exec -it portainer sh -c "sqlite3 /data/portainer.db 'VACUUM;'"

# Redémarrage avec plus de mémoire
docker stop portainer
docker rm portainer
docker run -d \
  --name portainer \
  --restart=always \
  --memory=1g \
  --cpus=1.0 \
  -p 9000:9000 \
  -p 9443:9443 \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v portainer_data:/data \
  portainer/portainer-ce:latest

Script de diagnostic automatique

🔍 Script diagnostic complet
#!/bin/bash
# portainer-diagnostic.sh

echo "=== DIAGNOSTIC PORTAINER ==="
echo "Date: $(date)"
echo "Hostname: $(hostname)"
echo

# Vérification Docker
echo "=== DOCKER ==="
docker --version
docker info | grep -E "(Server Version|Storage Driver|Logging Driver)"
echo

# Vérification Portainer
echo "=== PORTAINER ==="
if docker ps | grep -q portainer; then
    echo "✅ Portainer est en cours d'exécution"
    docker ps | grep portainer
    echo
    echo "Logs récents:"
    docker logs --tail 10 portainer
else
    echo "❌ Portainer n'est pas en cours d'exécution"
    echo "Derniers logs:"
    docker logs --tail 20 portainer 2>/dev/null || echo "Aucun log disponible"
fi
echo

# Vérification réseau
echo "=== RÉSEAU ==="
echo "Ports en écoute:"
netstat -tlnp | grep -E "(9000|9443|9001)"
echo
echo "Connectivité externe:"
curl -s -o /dev/null -w "%{http_code}" http://localhost:9000 || echo "Erreur connexion HTTP"
curl -s -o /dev/null -w "%{http_code}" https://localhost:9443 -k || echo "Erreur connexion HTTPS"
echo

# Vérification ressources
echo "=== RESSOURCES ==="
echo "Utilisation disque:"
df -h | grep -E "(/$|/var)"
echo
echo "Utilisation mémoire:"
free -h
echo
echo "Charge système:"
uptime
echo

# Vérification volumes
echo "=== VOLUMES ==="
docker volume ls | grep portainer
echo
echo "Taille des volumes:"
docker system df -v | grep portainer
echo

echo "=== FIN DU DIAGNOSTIC ==="