Créer son propre Manager Scaleway avec Docker

Créer son propre Manager Scaleway avec Docker
Photo by Rubens Nguyen / Unsplash

Ce tutoriel vous guide dans la mise en place d'une interface graphique (Web) permettant de gérer vos zones DNS en masse et de déployer des instances Debian sécurisées avec Docker Compose en un clic.

Prérequis

  • Docker et Docker Compose installés sur votre machine (Windows, Mac ou Linux).
  • Un compte Scaleway avec :
    • Une Access Key et une Secret Key.
    • Un Project ID.
    • Un nom de domaine dont les DNS sont gérés chez Scaleway.

Structure du Projet

Créez un dossier nommé scw-manager et disposez les fichiers suivants à l'intérieur :

scw-manager/
├── .env
├── app.py
├── Dockerfile
└── docker-compose.yml

Le fichier de configuration : .env

Ce fichier stocke vos identifiants de manière sécurisée. Remplacez les valeurs par les vôtres.

SCW_ACCESS_KEY=SCWXXXXXXXXXXXXXXXXX
SCW_SECRET_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
SCW_PROJECT_ID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

L'application : app.py

C'est le cœur du manager (utilisant Python et Streamlit). Il gère l'interface, les appels API et l'injection du Cloud-init.

import streamlit as st
import requests
import pandas as pd
import os

# Configuration de la page en mode large
st.set_page_config(page_title="Scaleway Manager", page_icon="🚀", layout="wide")

# CSS pour maximiser l'espace visuel
st.markdown("<style>.block-container { padding: 1rem; }</style>", unsafe_allow_html=True)

# Récupération des secrets depuis l'environnement
DEFAULT_ACCESS = os.getenv("SCW_ACCESS_KEY", "")
DEFAULT_SECRET = os.getenv("SCW_SECRET_KEY", "")
DEFAULT_PROJECT = os.getenv("SCW_PROJECT_ID", "")

# Barre latérale pour l'authentification
st.sidebar.title("🔐 Authentification")
scw_access = st.sidebar.text_input("Access Key ID", value=DEFAULT_ACCESS)
scw_secret = st.sidebar.text_input("Secret Key", type="password", value=DEFAULT_SECRET)
scw_project = st.sidebar.text_input("Project ID", value=DEFAULT_PROJECT)

HEADERS = {"X-Auth-Token": scw_secret, "Content-Type": "application/json"}

def get_dns_records(zone):
    all_recs = []
    page = 1
    while True:
        url = f"https://api.scaleway.com/domain/v2beta1/dns-zones/{zone}/records?page={page}&page_size=100"
        res = requests.get(url, headers=HEADERS)
        if res.status_code != 200: break
        data = res.json()
        all_recs.extend(data.get("records", []))
        if len(all_recs) >= data.get("total_count", 0): break
        page += 1
    return all_recs

st.title("🛠️ Scaleway Infrastructure Manager")
tab1, tab2 = st.tabs(["🌐 Gestion DNS", "🖥️ Déploiement Instances"])

# --- ONGLET DNS ---
with tab1:
    dns_zone = st.text_input("Zone DNS", placeholder="ex: mon-domaine.fr")
    if st.button("🔍 Charger la zone"):
        recs = get_dns_records(dns_zone)
        if recs:
            st.dataframe(pd.DataFrame(recs)[['name', 'type', 'data', 'ttl', 'id']], use_container_width=True, height=400)

    st.divider()
    c1, c2 = st.columns(2)
    with c1:
        st.subheader("➕ Ajout Individuel")
        with st.form("dns_add"):
            n, t, d = st.text_input("Nom"), st.selectbox("Type", ["A","AAAA","CNAME","TXT","MX"]), st.text_input("Valeur")
            if st.form_submit_button("Ajouter"):
                payload = {"changes": [{"add": {"records": [{"name": n, "type": t, "data": d, "ttl": 3600}]}}]}
                requests.patch(f"https://api.scaleway.com/domain/v2beta1/dns-zones/{dns_zone}/records", json=payload, headers=HEADERS)
                st.success("Record ajouté.")
    with c2:
        st.subheader("📋 Import en Masse")
        bulk = st.text_area("Format: nom type valeur", height=150)
        if st.button("🚀 Synchroniser"):
            lines = [l.split() for l in bulk.split('\n') if len(l.split()) >= 3]
            new_recs = [{"name": p[0], "type": p[1], "data": p[2], "ttl": 3600} for p in lines]
            if new_recs:
                requests.patch(f"https://api.scaleway.com/domain/v2beta1/dns-zones/{dns_zone}/records", json={"changes": [{"set": {"records": new_recs}}]}, headers=HEADERS)
                st.success("Zone mise à jour.")

# --- ONGLET INSTANCES ---
with tab2:
    st.header("🚀 Déploiement Instance + App Docker")
    col_a, col_b = st.columns(2)
    with col_a:
        scw_zone = st.radio("Zone", ["fr-par-1", "fr-par-2"], horizontal=True)
        server_name = st.text_input("Nom du serveur", "srv-docker-app")
        inst_type = st.selectbox("Type", ["PLAY2-PICO", "PLAY2-NANO", "DEV1-S"])
        custom_compose = st.text_area("Contenu docker-compose.yml", height=200, placeholder="services:...")

    with col_b:
        st.info("Inclus : Debian 13 + Docker + UFW + Fail2ban")
        
    if st.button("🔥 CRÉER ET DÉPLOYER"):
        with st.spinner("Déploiement en cours..."):
            payload = {"name": server_name, "commercial_type": inst_type, "image": "debian_trixie", "project": scw_project, "dynamic_ip_required": True}
            url = f"https://api.scaleway.com/instance/v1/zones/{scw_zone}/servers"
            r = requests.post(url, json=payload, headers=HEADERS)
            if r.status_code == 201:
                sid = r.json()['server']['id']
                # Préparation du Cloud-init
                indented_compose = custom_compose.replace("\n", "\n      ")
                cloud_init = f"#cloud-config\npackage_update: true\npackages: [ufw, fail2ban, curl, docker.io, docker-compose]\nwrite_files:\n  - path: /app/docker-compose.yml\n    content: |\n      {indented_compose}\nruncmd:\n  - ufw allow 22,80,443/tcp\n  - ufw --force enable\n  - systemctl enable --now docker\n  - cd /app && docker-compose up -d"
                # Injection User Data
                requests.patch(f"{url}/{sid}/user_data/cloud-init", data=cloud_init, headers={"X-Auth-Token": scw_secret, "Content-Type": "text/plain"})
                # Power On
                requests.post(f"{url}/{sid}/action", json={"action": "poweron"}, headers=HEADERS)
                st.success(f"Instance {server_name} lancée !")
            else:
                st.error(f"Erreur : {r.text}")

st.sidebar.markdown("---")
st.sidebar.caption("Scaleway Manager v2")

Le Dockerfile

Ce fichier définit l'environnement Python nécessaire.

FROM python:3.9-slim
WORKDIR /app
RUN pip install streamlit requests pandas
COPY app.py .
EXPOSE 8501
CMD ["streamlit", "run", "app.py", "--server.address=0.0.0.0"]

Le fichier docker-compose.yml

Pour orchestrer le lancement du manager.

services:
  scw-manager:
    build: .
    ports:
      - "8501:8501"
    env_file:
      - .env
    restart: unless-stopped

Lancement

  1. Ouvrez un terminal dans le dossier du projet.
  2. Accédez à l'interface sur votre navigateur : http://localhost:8501

Lancez la commande :

docker-compose up --build -d

Note : L'onglet DNS vous permet de gérer vos domaines en masse, et l'onglet Instances déploie une Debian 13 avec votre stack Docker prête à l'emploi.