Configuration des serveurs de production

Zeste de Savoir est installé en 1 tiers (sur un seul serveur donc).

Paramètres

Paramètres généraux

Paramètre Valeur
OS Linux, Debian 8 “Jessie”
Serveurs web Nginx
Moteur WSGI Gunicorn
SGBD MySQL
Contrôle des process systemd
Surveillance Munin

Paramètres spécifiques

  Préproduction Production
Nom beta.zestedesavoir.com zestedesavoir.com
IPv4 176.31.187.88 92.243.26.160
IPv6 x Pas encore disponible…
Identifiant Demande privée Demande privée
Mot de passe Demande privée Demande privée

Premier déploiement

Toute la procédure suppose un déploiement dans /opt/zds.

Utilisateur local

Zeste de Savoir tourne sous l’utilisateur zds et le groupe root.

Installation des outils

  • python
  • virtualenv (dans /opt/zds/zdsenv/)
  • git

Clone du repo et configuration de prod

cd /opt/ && mkdir zds && chown zds:zds -R zds
su zds

cd /opt/zds
mkdir -p data/tutoriels-private data/tutoriels-private data/tutoriels-public data/articles-data
git clone https://github.com/zestedesavoir/zds-site.git

cd zds-site
make install-debian

cd /opt/zds
pip install --user virtualenv # Ajout du module virtualenv
virtualenv zdsenv --python=python2 # Création du répertoire "zdsenv"

cd zds-site
vim zds/settings_prod.py

Dans settings_prod.py, remplacez toutes les valeurs to-fill:

# coding: utf-8

# Zeste de Savoir settings file for production
# WARNING: this file MUST be secret !!

import os

from raven import Client
from zds.utils.context_processor import get_git_version

from settings import ABSOLUTE_URL_OVERRIDES, AUTHENTICATION_BACKENDS, BASE_DIR
from settings import CORS_ALLOW_HEADERS, CORS_ALLOW_METHODS, CORS_EXPOSE_HEADERS
from settings import CORS_ORIGIN_ALLOW_ALL, CRISPY_TEMPLATE_PACK, FILE_UPLOAD_HANDLERS
from settings import GEOIP_PATH
from settings import INSTALLED_APPS, LANGUAGES, LANGUAGE_CODE, LOCALE_PATHS
from settings import LOGIN_REDIRECT_URL, LOGIN_URL, MEDIA_URL, MESSAGE_TAGS
from settings import MIDDLEWARE_CLASSES, OAUTH2_PROVIDER, REST_FRAMEWORK
from settings import REST_FRAMEWORK_EXTENSIONS, ROOT_URLCONF, SERVE, SITE_ID
from settings import STATICFILES_DIRS, STATICFILES_FINDERS, STATIC_URL, SWAGGER_SETTINGS
from settings import THUMBNAIL_ALIASES, THUMBNAIL_PRESERVE_EXTENSIONS, THUMBNAIL_QUALITY
from settings import TIME_ZONE, USE_I18N, USE_TZ, WSGI_APPLICATION, ZDS_APP
from settings import ES_ENABLED, ES_CONNECTIONS, ES_SEARCH_INDEX

##### Django settings #####

# NEVER set this True !!
DEBUG = False

USE_L10N = True

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'zdsdb',
        'USER': 'zds',
        'PASSWORD': 'to-fill',  # KEEP THIS SECRET !!
        'HOST': 'localhost',
        'PORT': '',
        'CONN_MAX_AGE': 600,
        'OPTIONS': {'charset': 'utf8mb4'},
    }
}

# KEEP THIS SECRET !!
SECRET_KEY = 'to-fill'

INSTALLED_APPS += (
    # Sentry client (errors logs)
    'raven.contrib.django.raven_compat',
)

ALLOWED_HOSTS = [
    'zdsappserver',
    'gandi.zestedesavoir.com',
    'gandi.zestedesavoir.com.',
    '.zestedesavoir.com',
    '.zestedesavoir.com.',
    '127.0.0.1',
    'localhost',
]

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_USE_TLS = False
EMAIL_HOST = 'localhost'
EMAIL_PORT = 25
#EMAIL_HOST_USER = 'site@zestedesavoir.com'
#EMAIL_HOST_PASSWORD = 'to-fill'

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:11211',
    }
}

SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
SESSION_COOKIE_AGE = 60 * 60 * 24 * 7 * 4

MEDIA_ROOT = '/opt/zds/data/media'

STATIC_ROOT = '/opt/zds/data/static'
STATICFILES_STORAGE = "django.contrib.staticfiles.storage.CachedStaticFilesStorage"

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'),
        ],
        'OPTIONS': {
            'loaders': [
                ('django.template.loaders.cached.Loader', [
                    'django.template.loaders.filesystem.Loader',
                    'django.template.loaders.app_directories.Loader',
                ]),
            ],
            'context_processors': [
                # Default context processors
                'django.contrib.auth.context_processors.auth',
                'django.template.context_processors.debug',
                'django.template.context_processors.i18n',
                'django.template.context_processors.media',
                'django.template.context_processors.static',
                'django.template.context_processors.request',
                'django.template.context_processors.tz',
                'django.contrib.messages.context_processors.messages',
                'social.apps.django_app.context_processors.backends',
                'social.apps.django_app.context_processors.login_redirect',
                # ZDS context processors
                'zds.utils.context_processor.app_settings',
                'zds.utils.context_processor.git_version',
            ],
            'debug': DEBUG,
        }
    },
]

# Sentry (+ raven, the Python Client)
# https://docs.getsentry.com/hosted/clients/python/integrations/django/
RAVEN_CONFIG = {
    'dsn': 'to-fill'
    'release': get_git_version()['name'],
}

LOGGING = {
   'version': 1,
   'disable_existing_loggers': True,
   'formatters': {
       'verbose': {
           'format': '[%(levelname)s] -- %(asctime)s -- %(module)s : %(message)s'
       },
       'simple': {
           'format': '[%(levelname)s] %(message)s'
       },
   },
   'handlers': {
        'django_log':{
            'level': 'WARNING',
            'class': 'logging.FileHandler',
            'filename': '/var/log/zds/logging.django.log',
            'formatter': 'verbose'
        },
        'debug_log':{
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': '/var/log/zds/debug.django.log',
            'formatter': 'verbose'
        },
        'generator_log':{
            'level': 'WARNING',
            'class': 'logging.FileHandler',
            'filename': '/var/log/zds/generator.log',
            'formatter': 'verbose'
        },
        'mail_admins': {
            'level': 'ERROR',
            'class': 'django.utils.log.AdminEmailHandler',
        },
       'sentry': {
            'level': 'ERROR',  # For beta purpose it can be lowered to WARNING
            'class': 'raven.handlers.logging.SentryHandler',
            'dsn': 'to-fill'
        },
   },
   'loggers': {
        'django': {
            'handlers': ['django_log'],
            'propagate': True,
            'level': 'WARNING',
        },
        'zds': {
            'handlers': ['django_log', 'sentry'],
            'propagate': True,
            'level': 'WARNING',
        },
        'django.request': {
            'handlers': ['mail_admins', 'django_log'],
            'level': 'ERROR',
            'propagate': False,
        },
        'generator': {
            'handlers': ['generator_log'],
            'level': 'WARNING',
        }
    }
}

# https://docs.djangoproject.com/en/stable/ref/settings/#secure-proxy-ssl-header
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTOCOL', 'https')


##### Dependencies settings #####


# easy-thumbnails
# http://easy-thumbnails.readthedocs.io/en/2.1/ref/settings/
THUMBNAIL_OPTIMIZE_COMMAND = {
    'png': '/usr/bin/optipng {filename}',
    'gif': '/usr/bin/optipng {filename}',
    'jpeg': '/usr/bin/jpegoptim {filename}'
}

# Pandoc settings
PANDOC_LOC = '/usr/local/bin/'
PANDOC_LOG = '/var/log/zds/pandoc.log'
PANDOC_LOG_STATE = True
# fix for Gandi
PANDOC_PDF_PARAM = ("--latex-engine=xelatex "
                    "--template={} -s -S -N "
                    "--toc -V documentclass=scrbook -V lang=francais "
                    "-V mainfont=Merriweather -V monofont=\"SourceCodePro-Regular\" "
                    "-V fontsize=12pt -V geometry:margin=1in ".format('/opt/zds/zds-site/assets/tex/template.tex'))

# python-social-auth
# http://psa.matiasaguirre.net/docs/configuration/django.html
SOCIAL_AUTH_PIPELINE = (
    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',
    'social.pipeline.social_auth.social_user',
    'social.pipeline.user.get_username',
    'social.pipeline.social_auth.associate_by_email',
    'social.pipeline.user.create_user',
    'zds.member.models.save_profile',
    'social.pipeline.social_auth.associate_user',
    'social.pipeline.social_auth.load_extra_data',
    'social.pipeline.user.user_details'
)
# KEEP THIS SECRET !!
SOCIAL_AUTH_ACEBOOK_KEY = 'to-fill'
SOCIAL_AUTH_ACEBOOK_SECRET = 'to-fill'
SOCIAL_AUTH_WITTER_KEY = 'to-fill'
SOCIAL_AUTH_WITTER_SECRET = 'to-fill'
SOCIAL_AUTH_OOGLE_OAUTH2_KEY = 'to-fill'
SOCIAL_AUTH_OOGLE_OAUTH2_SECRET = 'to-fill'
SOCIAL_AUTH_ANITIZE_REDIRECTS = 'to-fill'

# Django reCAPTCHA
# https://github.com/praekelt/django-recaptcha
USE_CAPTCHA = False
NOCAPTCHA = True   # Use the "No Captcha engine"
RECAPTCHA_USE_SSL = True
# KEEP THIS SECRET !!
RECAPTCHA_UBLIC_KEY = 'to-fill'
RECAPTCHA_RIVATE_KEY = 'to-fill'


##### ZdS settings #####

# added in v20
ZDS_APP['site']['secure_url'] = 'https://zestedesavoir.com'

# added in v21
ZDS_APP['display_search_bar'] = True

# forum
ZDS_APP['forum']['beta_forum_id'] = 23

# member
ZDS_APP['member']['anonymous_account'] = 'anonyme'
ZDS_APP['member']['external_account'] = 'Auteur externe'
ZDS_APP['member']['bot_account'] = 'Clem'

# site
ZDS_APP['site']['url'] = 'https://zestedesavoir.com'
ZDS_APP['site']['association']['email'] = 'communication@zestedesavoir.com'
ZDS_APP['site']['association']['fee'] = u'(montant en attente de procédures administratives)'
ZDS_APP['site']['googleAnalyticsID'] = 'to-fill'
ZDS_APP['site']['googleTagManagerID'] = 'to-fill'

# content
#ZDS_APP['content']['build_pdf_when_published'] = False
ZDS_APP['article']['repo_path'] = '/opt/zds/data/articles-data'
ZDS_APP['content']['repo_private_path'] = '/opt/zds/data/contents-private'
ZDS_APP['content']['repo_public_path'] = '/opt/zds/data/contents-public'
ZDS_APP['content']['extra_content_generation_policy'] = 'WATCHDOG'

# enable ping!
ZDS_APP['comment']['enable_pings'] = False

# Vote anonymisation - cf v18 : https://goo.gl/L6X4hw
VOTES_ID_LIMIT = 131319

# tags in menu
TOP_TAG_MAX = 5

# path for old tutorials for Site du Z&ro
SDZ_TUTO_DIR = '/home/zds/tutos_sdzv3/Sources_md'

# Force HTTPS for logged users
FORCE_HTTPS_FOR_MEMBERS = True
ENABLE_HTTPS_DECORATOR = True

# visual changes
#ZDS_APP['visual_changes'] = ['snow', 'clem-christmas']
#ZDS_APP['visual_changes'] = ['clem-halloween']

ES_SEARCH_INDEX['shards'] = 3

Il est possible de personnaliser ZdS pour n’importe quel site communautaire de partage. Un ensemble de paramètres est disponible dans le fichier settings.py via le dictionnaire ZDS_APP. Vous pourrez donc écraser ces variables par défaut dans votre fichier settings_prod.py, comme illustré dans le fichier ci-dessus.

Installation de l’application de base

Suivre l’installation complète sous Linux en tenant compte des subtilités suivantes :

  • Installer les outils front n’est pas nécessaire, le front étant packagé par Travis.
  • Ne pas lancer le serveur à la fin de l’étape “Lancer ZdS”
  • Installer toutes les dépendances requises à l’étape “Aller plus loin”
  • Installer MySQL5.6 depuis backports: sudo apt -t jessie-backports install mysql-server mysql-client libmysqlclient-dev
  • Installer les dépendances de production avec pip install --upgrade -r requirements-prod.txt

Outils spécifiques à un serveur de run

Gunicorn

command = '/opt/zds/zdsenv/bin/gunicorn'
pythonpath = '/opt/zds/zds-site'
bind = 'unix:/opt/zds/zdsenv/bin/gunicorn.sock'
workers = 7
user = 'zds'
group = 'zds'
errorlog = '/var/log/zds/gunicorn_error.log'
loglevel = 'info'
pid = '/opt/zds/gunicorn.pid'

Nginx

Installer nginx.

Une version récente de nginx est nécessaire pour utiliser HTTP/2. Si la version installée est inférieure à la version 1.9.5 il faut la mettre à jour avec celle des dépot nginx. Toutefois, HTTP/2 n’est pas nécessaire au bon fonctionnement de Zeste de Savoir, pensez juste à adapter sites-available/zestedesavoir.

La configuration nginx de Zeste de Savoir est séparée en plusieurs fichiers, en plus des quelques fichiers de configuration par défaut de nginx:

/etc/nginx
|- nginx.conf # Fichier de configuration principal
|- sites-available/
|  |- prod-redirect # Redirection de www.zestedesavoir.com -> zestedesavoir.com en prod
|  \- zestedesavoir # Configuration propre au site
\- snippets/ # Dossier contenant des configurations incluses dans différents fichiers
   |- antispam.conf # Incluse dans zestedesavoir
   |- headers.conf # Headers type HSTS, CSP (et X-Robots-Tags sur la beta)
   |- ssl.conf # Définition des protocoles SSL à utiliser + conf. des certificats
   |- gzip.conf # Configuration pour la compression
   |- proxy.conf # Headers à passer au proxy (gunicorn)
   \- static-cache.conf # Headers à rajouter pour les contenus statiques (cache)
user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
	worker_connections 768;
	# multi_accept on;
}

http {
	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	# server_tokens off;

	# server_names_hash_bucket_size 64;
	# server_name_in_redirect off;

	include mime.types;
	default_type application/octet-stream;

	##
	# Logging Settings
	##

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	##
	# Virtual Host Configs
	##
	include sites-enabled/*;

	include snippets/ssl.conf;
	include snippets/gzip.conf;
}
server {
	listen 80;
	listen [::]:80;
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name
		www.zestedesavoir.com
		ftp.zestedesavoir.com
		gandi.zestedesavoir.com
		postfix.zestedesavoir.com
		smtp.zestedesavoir.com
		uploads.zestedesavoir.com
	;

	access_log /var/log/zds/nginx-redirect-access.log;
	error_log /var/log/zds/nginx-redirect-error.log;

	server_tokens off;
	ssl_certificate /etc/letsencrypt/live/zestedesavoir.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/zestedesavoir.com/privkey.pem;



        root /var/www/html/;

        location ~ /.well-known {
                allow all;
        }
	rewrite ^(.*) $scheme://zestedesavoir.com$1 permanent;
}
upstream zdsappserver {
	server unix:/opt/zds/zdsenv/bin/gunicorn.sock fail_timeout=0;
}

# Active le header X-Robots-Tag sur la beta pour éviter d'indexer la beta
map $http_host $robots_tag_header {
	hostnames;
	default '';
	beta.zestedesavoir.com 'none';
}

server {
	listen 80 default_server;
	listen [::]:80 default_server;
	server_name zestedesavoir.com;
	return 301 https://$server_name$request_uri;
}

server {
	listen 443 ssl http2 default_server;
	listen [::]:443 ssl http2 default_server;

	server_name zestedesavoir.com;

	try_files $uri @proxy;
	server_tokens off;
	client_max_body_size 100M;

	ssl_certificate /etc/letsencrypt/live/zestedesavoir.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/zestedesavoir.com/privkey.pem;
	include snippets/ssl.conf;

	# Logging
	access_log /var/log/zds/nginx-access.log;
	error_log /var/log/zds/nginx-error.log;

	# Rewrite de l'ancienne page de teasing
	rewrite ^/teasing/$ / permanent;

	root /opt/zds/zdsenv/webroot;

	location ~ /.well-known {
		allow all;
	}

	location @proxy {
		# 503 si la maintenance est active
		if (-f $document_root/maintenance.html) {
			return 503;
		}

		# Ajout d'un trailing slash sur les URLs
		if ($uri !~ (\.|^/api)) {
			rewrite ^(.*[^/])$ $1/ permanent;
		}

		include snippets/proxy.conf;
		proxy_pass http://zdsappserver;
	}

	location @maintenance {
		rewrite ^(.*)$ /maintenance.html break;
	}

	# Cache headers on static resources
	location ~* ^/(static|media|errors)/ {
		include snippets/static-cache.conf;
	}

	# Error pages
	error_page 500 502 504 /errors/500.html;
	error_page 503 @maintenance;

	include snippets/headers.conf;
	include snippets/antispam.conf;
}
# Conf anti-exploit, source : https://www.howtoforge.com/nginx-how-to-block-exploits-sql-injections-file-injections-spam-user-agents-etc
#
## Block spam
set $block_spam 0;
if ($query_string ~ "\b(ultram|unicauca|valium|viagra|vicodin|xanax|ypxaieo)\b") {
    set $block_spam 1;
}
if ($query_string ~ "\b(erections|hoodia|huronriveracres|impotence|levitra|libido)\b") {
    set $block_spam 1;
}
if ($query_string ~ "\b(ambien|blue\spill|cialis|cocaine|ejaculation|erectile)\b") {
    set $block_spam 1;
}
if ($query_string ~ "\b(lipitor|phentermin|pro[sz]ac|sandyauer|tramadol|troyhamby)\b") {
    set $block_spam 1;
}
if ($block_spam = 1) {
    return 403;
}

## Block user agents
set $block_user_agents 0;

# Don't disable wget if you need it to run cron jobs!
#if ($http_user_agent ~ "Wget") {
#    set $block_user_agents 1;
#}

# Disable Akeeba Remote Control 2.5 and earlier
if ($http_user_agent ~ "Indy Library") {
    set $block_user_agents 1;
}

# Common bandwidth hoggers and hacking tools.
if ($http_user_agent ~ "libwww-perl") {
    set $block_user_agents 1;
}
if ($http_user_agent ~ "GetRight") {
    set $block_user_agents 1;
}
if ($http_user_agent ~ "GetWeb!") {
    set $block_user_agents 1;
}
if ($http_user_agent ~ "Go!Zilla") {
    set $block_user_agents 1;
}
if ($http_user_agent ~ "Download Demon") {
    set $block_user_agents 1;
}
if ($http_user_agent ~ "Go-Ahead-Got-It") {
    set $block_user_agents 1;
}
if ($http_user_agent ~ "TurnitinBot") {
    set $block_user_agents 1;
}
if ($http_user_agent ~ "GrabNet") {
    set $block_user_agents 1;
}
# SpaceFox: adds HTTrack
if ($http_user_agent ~ "HTTrack") {
    set $block_user_agents 1;
}


if ($block_user_agents = 1) {
    return 403;
}
# GZIP compression
gzip on;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript;
gzip_proxied any;
gzip_vary on;
# CSP
# NOTE: CSP is not ready yet, see https://github.com/zestedesavoir/zds-site/issues/3242
# add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline' https://www.gstatic.com https://www.google.com https://cdn.mathjax.org https://www.google-analytics.com https://www.googletagmanager.com; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; img-src * ; font-src 'self' https://fonts.googleapis.com https://fonts.gstatic.com; connect-src 'self' ; media-src 'none' ; object-src 'none' ; child-src https://www.google.com; upgrade-insecure-requests ;";

# P3P
add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';

# HSTS
# This forces the browsers to use HTTPS
# DO NOT use this on a subdomain without a valid certificate
add_header Strict-Transport-Security max-age=15768000;

# CORS
# add_header Access-Control-Allow-Origin *;

add_header X-Clacks-Overhead "GNU Terry Pratchett";

# Robots
add_header X-Robots-Tag $robots_tag_header;
# Paramètres du proxy vers gunicorn
proxy_read_timeout 1000s;
proxy_connect_timeout 1000s;
proxy_redirect     off;
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_intercept_errors on;
# SSL configuration
ssl_session_timeout 1d;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:ECDHE-RSA-AES128-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA128:DHE-RSA-AES128-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA128:ECDHE-RSA-AES128-SHA384:ECDHE-RSA-AES128-SHA128:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA128:DHE-RSA-AES128-SHA:DHE-RSA-AES128-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA384:AES128-GCM-SHA128:AES128-SHA128:AES128-SHA128:AES128-SHA:AES128-SHA:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!MD5:!PSK:!RC4";
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
ssl_dhparam /etc/ssl/dhparam.pem;

# Hardening security settings
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload";
add_header X-Xss-Protection "1" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options SAMEORIGIN;
# Headers de cache pour les contenus statics
expires 1y;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
include snippets/headers.conf;

TLS : Let’s Encrypt

Le site est servi en https avec un certificat Let’s Encrypt install avec certbot.

  • Installer certbot:
    • sudo apt-get install certbot -t jessie-backports
  • Générer le nécessaire:
    • certbot certonly --webroot -w /opt/zds/zdsenv/webroot -d zestedesavoir.com -d www.zestedesavoir.com
  • Mettre un cron pour régénérer les certificats, avec l’utilisateur root:
    • 23 */2 * * * certbot renew --quiet --post-hook "service nginx reload"

MySQL

Zeste de Savoir nécessite MySQL 5.6. Voici la configuration de production :

#
# The MySQL database server configuration file.
#
# You can copy this to one of:
# - "/etc/mysql/my.cnf" to set global options,
# - "~/.my.cnf" to set user-specific options.
# 
# One can use all long options that the program supports.
# Run program with --help to get a list of available options and with
# --print-defaults to see which it would actually understand and use.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

#
# * IMPORTANT: Additional settings that can override those from this file!
#   The files must end with '.cnf', otherwise they'll be ignored.
#

!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
#
# The MySQL database server configuration file.
#
# You can copy this to one of:
# - "/etc/mysql/my.cnf" to set global options,
# - "~/.my.cnf" to set user-specific options.
#
# One can use all long options that the program supports.
# Run program with --help to get a list of available options and with
# --print-defaults to see which it would actually understand and use.
#
# For explanations see
# http://dev.mysql.com/doc/mysql/en/server-system-variables.html

# This will be passed to all mysql clients
# It has been reported that passwords should be enclosed with ticks/quotes
# escpecially if they contain "#" chars...
# Remember to edit /etc/mysql/debian.cnf when changing the socket location.
[client]
port            = 3306
socket          = /var/run/mysqld/mysqld.sock
default-character-set = utf8mb4

# Here is entries for some specific programs
# The following values assume you have at least 32M ram

# This was formally known as [safe_mysqld]. Both versions are currently parsed.
[mysqld_safe]
socket          = /var/run/mysqld/mysqld.sock
nice            = 0

[mysqld]
#
# * Basic Settings
#
user            = mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
skip-name-resolve
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
bind-address            = 127.0.0.1
#
# * Fine Tuning
#
key_buffer              = 16M
max_allowed_packet      = 16M
thread_stack            = 256K
thread_cache_size       = 8
# This replaces the startup script and checks MyISAM tables if needed
# the first time they are touched
myisam-recover         = BACKUP
max_connections        = 60
#table_cache            = 64
#thread_concurrency     = 10
#
# * Query Cache Configuration
#
# query_cache_limit       = 1M

query_cache_size = 256M
query_cache_type = 1
query_cache_limit = 512K
query_cache_min_res_unit = 512

#
# * Logging and Replication
#
# Both location gets rotated by the cronjob.
# Be aware that this log type is a performance killer.
# As of 5.1 you can enable the log at runtime!
#general_log_file        = /var/log/mysql/mysql.log
#general_log             = 1
#
# Error log - should be very few entries.
#
log_error = /var/log/mysql/error.log
#
# Here you can see queries with especially long duration
slow_query_log_file = /var/log/mysql/mysql-slow.log
slow_query_log = 1
long_query_time = 2
#log-queries-not-using-indexes
#
# The following can be used as easy to replay backup logs or for replication.
# note: if you are setting up a replication slave, see README.Debian about
#       other settings you may need to change.
#server-id              = 1
#log_bin                        = /var/log/mysql/mysql-bin.log
expire_logs_days        = 10
max_binlog_size         = 100M
#binlog_do_db           = include_database_name
#binlog_ignore_db       = include_database_name
#
# * InnoDB
#
# InnoDB is enabled by default with a 10MB datafile in /var/lib/mysql/.
# Read the manual for more InnoDB related options. There are many!
#
# * Security Features
#
# Read the manual, too, if you want chroot!
# chroot = /var/lib/mysql/
#
# For generating SSL certificates I recommend the OpenSSL GUI "tinyca".
#
# ssl-ca=/etc/mysql/cacert.pem
# ssl-cert=/etc/mysql/server-cert.pem
# ssl-key=/etc/mysql/server-key.pem
innodb_file_per_table=on
innodb_file_format=barracuda
innodb_large_prefix=on
innodb_buffer_pool_size=2G
innodb_buffer_pool_instances=1
tmp_table_size=128M
max_heap_table_size=128M
sort_buffer_size=4M
join_buffer_size=256K
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci


[mysqldump]
quick
quote-names
max_allowed_packet      = 16M

[mysql]
#no-auto-rehash # faster start of mysql but no tab completition

[isamchk]
key_buffer_size              = 16M
[mysqldump]
quick
quote-names
max_allowed_packet	= 16M
[mysql]
default-character-set = utf8mb4

Supervisor

Installer supervisor.

Créer deux configurations :

Configuration ZdS

Les confs dans /etc/systemd/system/zds.service et /etc/systemd/system/zds.socket permettent de lancer le serveur applicatif de Zeste de Savoir (Gunicorn) à l’aide de systemctl start zds.{service,socket} et l’arrêter avec systemctl stop zds.{service,socket}.

zds.service nécessite la création manuelle de /run/gunicorn appartenant à zds : sudo mkdir /run/gunicorn && sudo chown zds /run/gunicorn.

[Unit]
Description=Zeste de Savoir
Requires=zds.socket
After=network.target

[Service]
PIDFile=/run/gunicorn/pid
User=zds
Group=zds
WorkingDirectory=/opt/zds/zdsenv
ExecStart=/opt/zds/zdsenv/bin/gunicorn --pid /run/gunicorn/pid -c /opt/zds/gunicorn_config.py zds.wsgi
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target
[Unit]
Description=ZdS Gunicorn socket

[Socket]
#ListenStream=/run/gunicorn/socket
ListenStream=/opt/zds/zdsenv/bin/gunicorn.sock
ListenStream=0.0.0.0:9000
ListenStream=[::]:8000

[Install]
WantedBy=sockets.target
Configuration Solr

La conf dans /etc/systemd/system/solr.service permet de lancer Solr à l’aide de systemctl start solr et l’arrêter avec systemctl stop solr.

[Unit]
Description=SolR ZdS
After=syslog.target network.target remote-fs.target nss-lookup.target

[Service]
PIDFile=/run/solrzds/pid
WorkingDirectory=/opt/zds/solr-4.9.1/example/
ExecStart=/usr/bin/java -jar start.jar
User=zds
Group=zds
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s QUIT $MAINPID
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Munin

Configuration générale

Installer le noeud Munin : apt-get install munin-node.

On obtient les suggestions de plugins à installer avec munin-node-configure --suggest et les commandes à lancer pour les activer via munin-node-configure --shell.

Pour l’instant le serveur de graphe est fourni par SpaceFox et est visible ici. Seul SpaceFox peut mettre à jour cette configuration. Le serveur de graphe accède au serveur en SSH avec cette clé publique, placée dans le home de l’utilisateur munin (sous debian, l’utilisateur créé par le packet munin a son home dans /var/lib/munin, donc sa clé doit être dans /var/lib/munin/.ssh/authorized_keys) :

ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBsfYaz5d4wtyTM0Xx1TjpJt0LuZ2Il9JZD2+s4hNQToNBaqT3aafG1SuHuQkqjvIQrI28NEkjALQIp4zD7BOeeW9QlAwE7uiebi3FcwLfaPFwq5qvnpyOSmbktCjHX24a14ozgDPY5diPkOsyMdEYz/KTybSvFvgUjzUSCLBQ2EWj0CBktY6cFC45pvVCsdd/ToDsEVbhixyNmlOMc+FB/oT8CC6ZoDezSXQGaO51/zLS8l4ieBIcB4tK3JdJI+fFv5FJsfgMK+DbNV4pikw9qEZJlASQCU69L+YR7MxTXNCqRyQ1Z4qxH4ZdPELmNOoMB8dHxxBX7TGP+Hvpm3AH munin@Yog-Sothoth
Configuration spécifique à ZdS

Créer les liens vers le plugin Django-Munin :

ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_active_sessions
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_active_users
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_db_performance
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_total_articles
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_total_mps
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_total_posts
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_total_sessions
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_total_topics
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_total_tutorials
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_total_users
ln -s /usr/share/munin/plugins/django.py /etc/munin/plugins/zds_total_tribunes

Créer le fichier /etc/munin/plugin-conf.d/zds.conf et y ajouter la config des graphes propres à ZdS :

[zds_db_performance]
env.url https://zestedesavoir.com/munin/db_performance/
env.graph_category zds

[zds_total_users]
env.url https://zestedesavoir.com/munin/total_users/
env.graph_category zds

[zds_active_users]
env.url https://zestedesavoir.com/munin/active_users/
env.graph_category zds

[zds_total_sessions]
env.url https://zestedesavoir.com/munin/total_sessions/
env.graph_category zds

[zds_active_sessions]
env.url https://zestedesavoir.com/munin/active_sessions/
env.graph_category zds

[zds_total_topics]
env.url https://zestedesavoir.com/munin/total_topics/
env.graph_category zds

[zds_total_posts]
env.url https://zestedesavoir.com/munin/total_posts/
env.graph_category zds

[zds_total_mps]
env.url https://zestedesavoir.com/munin/total_mps/
env.graph_category zds

[zds_total_tutorials]
env.url https://zestedesavoir.com/munin/total_tutorials/
env.graph_category zds

[zds_total_articles]
env.url https://zestedesavoir.com/munin/total_articles/
env.graph_category zds

[zds_total_tribunes]
env.url https://zestedesavoir.com/munin/total_opinions/
env.graph_category zds

Mise à jour d’une instance existante

Allez jeter un coup d’oeil à notre script de déploiement ! ;) (lequel appelle le véritable script de déploiement).