-
Création d'une Virtual Machnine (lxc) sur un Serveur physique dédié.
-
Installation d'Ubuntu 18.04 sur la VM.
-
Configuration un utilisateur différent de root avec droit sudo avec mot de pass, de manière à ce que toutes actions importantes d'administrations de ce système soient faites volontairement.
-
Modification du fichier sshd.config pour suspendre root de la connexion à la VM.
-
Mise à jour des paquets , installation de python postgres...
-
Création du dépot git dans /home/aurelia/pur_beurre/
-
Création de l'environnement virtuel
-
Rappatriement des données de l'application
git pull https://github.com/horlas/OC_Project11
Installation de l'environnement :pip install -r requirements.txt
-
Création de base de données :
$sudo -u postgres createdb quality
. Création de l'utilisateur$sudo -u postgres createuser --interactive
.... -
Installation des la base de données : deux dumps: table utilisateur dans quality/dumps/user.json et données dans quality.json.
(venv)$python manage.py loaddata *fichier*
-
Ajout des variables d’environnement ( DJANGO_SETTINGS_MODULE, SECRET_KEY) au chargement de la session utilisateur aurelia: ajout dans /home/aurelia/.bashrc des deux lignes suivantes:
export SECRET_KEY="XXXXX"
export DJANGO_SETTINGS_MODULE=pur-beurre.prod_settings
Une fois le fichier prod_settings.py rapatrié , on l'enlève du dépôt local en l’ajoutant dans le .gitignore
$git rm --cached prod_settings .gitignore
pour la prise en considération de la desindextion de prod_settings.py
$git add .gitignore
puis:
$git push origin master
sur le serveur:
git pull <depot>
Le fichier prod_settings.py est à présent uniquement présent sur le serveur mais plus sur github.
sudo apt-get install nginx
La configuration se trouve dans etc/nginx/sites-available/
Création d'un fichier de configuration touch etc/nginx/sites-available/pur_beurre
Edition et ecriture avec vim sudo vi etc/nginx/sites-available/pur_beurre
Dans ce fichier de configuration les 'directives' sont:
- listen : écouter le trafic sur le port 80
- server_name : notre nom de domaine
- root : l'endroit où se trouve l'application sur le serveur
- location /static : où se trouvent les fichiers statiques
- location / : redirige les URL vers 127.0.0.1:8000
- proxy_set_header reecrit les headers de la requête HTTP.
- X-Forwarded-For transmet l'adresse IP du client.
Création du lien vers etc/nginx/sites_enabled
pour activer le vhost
aurelia@projet-aurelia:/etc/nginx$ ln -s sites-available/pur_beurre sites-enabled/
Nous rechargeons la configuration du serveur
$sudo service nginx reload
Supervisor est un système qui permet de contrôler des processus dans un environnement Linux. Ici nous l'utilisons pour contrôler et redémarrer au besoin Gunicorn (serveur HTTP Python qui utilise les spécifications WSGI, utilisé par Django).
sudo apt-get install supervisor
Ajout d'un nouveau processus dans la configuration de supervisor
$cd etc/supervisor/conf.d/
/etc/supervisor/conf.d$ sudo vi pur_beurre-gunicorn.conf
Voici la configuration mise en place
A noter que nous pouvons transmettre via supervisor des variables environnement , nous les avons donc supprimées de notre .bashrc . (SECRET_KEY, DJANGO_SETTINGS_MODULE)
La commande supervisorctl
offre plein de possibilités
Elle s'execute avec les droits sudo
Ici nous disons à supervisor de prendre en considération les changements de configuration et d'ajouter le processus.
$sudo supervisorctl reread
pur-beurre-gunicorn: available
$sudo supervisorctl update
pur-beurre-gunicorn: added process group
Affiche le statut
$sudo supervisorctl status
pur_beurre-gunicorn RUNNING pid 126, uptime 15:27:20
Redémarre le processus
$sudo supervisorctl restart pur_beurre-gunicorn
pur_beurre-gunicorn: stopped
pur_beurre-gunicorn: started
Travis est un service d'automatisation. Travis crée un environnement équivalent à celui de notre application et y fait tourner les tests.
Nous l'interfaçons avec notre dépôt Github. Il surveille dans notre cas la branche staging
où désormais toutes les modifications de l'application seront faites. A chaque git push origin staging
, Travis lance l'environnement et fait tourner les tests. Si tout se déroule bien, nous pourrons faire un merge sur la branche master. Nous appelons ça de l'intégration continue.
Création d'un compte. Ajout sur la dashboard de Travis de notre dépôt Github:
Sur notre environnement local , au même niveau que manage.py , nous créons un nouveau fichier travis.yml
Nous sommes avec la version de Django 2.1.3 qui fonctionne uniquement avec une version de Postgres supérieur à 9.4 , or Travis par défaut utilise une version de Postgres 9.2 . C'est pour cela nous avons rajouté les lignes suivantes:
addons:
postgres: '9.5'
Pour 'forcer' la version de Postgresql.
Newrelic Infrastructure offre la possibilité de monitorer notre serveur : Etat de la CPU , de la mémoire vive, le load average etc....
- Création d'un compte sur New Relic
- Sur le site Infrastructure/All Host/Add host
Suivre les instructions:
- Mise en place d'une clé de licence dans un fichier de configuration Newrelic
- Installation de Newrelic avec cette nouvelle lincence: de charger le dépôt d'installation de Newrelic, et installation du paquet.
- Dorénavant le monitoring de notre système peut se surveiller depuis la plateforme New Relic :
- Création d'un compte, création d'un projet Django nommé pur-beurre.
- Suivre les instructions de configuration:
Cette documentation est dépréciée .
Nous avons suivi les instructions de celle-ci Nous avons choisi de le faire sur notre environnement de développement et mettre à jour notre fichier settings.py dont le fichier prod_settings hérite. Pour ainsi bénéficier de Sentry sur tous nos environnements. (Dev, Test, Prod)
$pip install --upgrade sentry-sdk==0.5.5
Ne pas oublier de mettre à jour requirements.tx
$pip freeze > requirements.txt
Dans notre cas nous avons rajouté ces quelques lignes à notre fichier de configuration settings.py
Puis mise à jour du dépôt distant et de l'application en production sur la VM.
Dorénavant les erreurs liées à l'application seront reportées ici
Sur la plateforme les
-
Erreur générée sur l'envirronement de test Travis lorsque Postgres n'était pas dans la bonne version: ici
-
Erreur générée lors de l'oubli de la mise à jour des requirements.txt sur l'environnement de Production : ici
Notre client virtuel Pur-Beurre peut vouloir mettre à jour sa base de données régulièrement avec des nouvelles données importées d'OpenFood Facts.
Une bonne idée pourrait être de choisir les catégories comportant le moins de produits pour enrichir ces dernières.
D'un autre côté la base OpenFood Facts présente des incohérences : de nombreuses catégories n'ont qu'un seul produit. Ce paramètre ne permet pas à notre application (qui, pour mémoire, fait une recherche de substituts basée sur le nom de la catégorie de fonctionner correctement).
Nous pensons que de supprimer régulièrement les catégories contenant un seul produit permettrait un fonctionnement optimisé.
Le programme est une tâche personnalisée Django : elle se lance donc avec la commande suivante:
(env)$python manage.py update_database
-
recherche des 10 catégories contenant le moins de produits et chargement de 60 produits en partant de ces catégories:
Pour cela nous réutilisons notre fonction fill_database() développée pour le Projet 11 . Et nous supprimons bien évidement les doublons.
Extrait de code :
def handle(self , *args , **options):
query=Product.objects.all().values('category').annotate(total=Count('category')).order_by('total')
# just display the ten smallest categories
self.stdout.write(self.style.SUCCESS('How many products in the last ten category ? : '))
for c in query[:10]:
self.stdout.write(
self.style.NOTICE('category : {} nb_products : {} '.format(c['category'], c['total'])))
for c in query[:10]:
# we get c['category']
category = c['category']
nb_products = fill_database(category)
- Chargement des produits en base: création d'une fixture et appel de la commande
loaddata
de Django. Un compte des produits chargé en base est tracé.
Extrait de code :
# test the fixture is well created
content = os.path.getsize('quality/fixtures/dugras_data.json')
if content != 0:
self.stdout.write(
self.style.SUCCESS('Successfully created fixtures for the category {} '.format(category)))
else:
self.stdout.write(self.style.WARNING('Le fichier de fixture est vide'))
self.stdout.write(self.style.NOTICE('You are preparing to load {} products in data base'.format(nb_products)))
#test if products well loaded in database:
count_before = Product.objects.count()
call_command('loaddata', 'dugras_data.json', verbosity=0)
count_after = Product.objects.count()
if count_before + nb_products == count_after:
self.stdout.write(
self.style.SUCCESS('Successfully loaded fixtures for the category {} '.format(category)))
else :
self.stdout.write(self.style.WARNING('A problem occurs'))
- Les produits en doublons sont supprimés
Extrait de code :
self.stdout.write(self.style.NOTICE('Deleting products with the same name'))
duplicates = Product.objects.values('name').order_by().annotate(max_id=Max('id') ,
count_id=Count('id')).filter(count_id__gt=1)
fields =['name']
for duplicate in duplicates:
Product.objects.filter(**{x: duplicate[x] for x in fields}).exclude(id=duplicate['max_id']).delete()
count_after_delete_duplicates = Product.objects.count()
self.stdout.write(self.style.SUCCESS('{} products have been deleted'.format((count_after - count_after_delete_duplicates))))
- Et enfin, les catégories contenant un seul produit sont supprimées.
Extrait de code :
# after uploading smallest categories we delete orphan product
query_after = Product.objects.all().values('category').annotate(total=Count('category')).filter(total=1)
# just display the ten smallest categories before delete
self.stdout.write(self.style.NOTICE('How many category contains just one product : {}'.format(len(query_after))))
# delete products
for product in query_after:
Product.objects.get(category=product['category']).delete()
L'intégralité de ce programme se trouve ici.
Comme le programme renvoie un certain nombre d'informations, il est intéressant de sauver les logs de son déroulement. C'est pour cela que, dans la tâche planifiée, nous créons aussi un fichier de logs update_database.log qui se trouve dans ~/var/log/pur_beurre/
Création du fichier de sauvegarde :
/$ mkdir var/log/pur_beurre
/$ touch var/log/pur_beurre/update_database.log
Changement des droits d'accès :
:/var/log/pur_beurre# chown aurelia update_database.log
$ cd /usr/local/bin
$ sudo -s
$ vim update_database.sh
1 #!/bin/bash
2 cd ~/pur_beurre/
3 source venv/bin/activate
4 python manage.py update_database > /var/log/pur_beurre/update_database.log
5 deactivate
6 exit
~
~
~
-- :wq! 7 7,0-1 Tout
Rendre le script exécutable :
/usr/local/bin# chmod +x update_database.sh
Créer la tâche planifiée :
$crontab -e
Ajout des deux lignes suivantes
PATH=/usr/local/bin
00 00 * * 7 update_database.sh