Optimisations BDD: indexes en doublon

Les indexes prennent de l'espace disque et de la mémoire. Les moteurs MySQL/MariaDB/PostgreSQL savent utiliser des indexes plus complexes afin de lire des données nécessitant un seul index donc les champs conditionnant sont inclus dans un index plus complexe.

Exemple ici avec 3 indexes

struct index_a {
 id_1 int,
 id_2 int,
};

struct index_b {
 id_1 int,
};

struct index_c {
 id_2 int,
};

Si nous faisons une requête filtrant sur le champ _id1 uniquement (SELECT field1 FROM table1 where id_1 = 3), le moteur de BDD est capable d'utiliser 2 indexes: _indexb, qui ne contient que _id1 et _indexa qui contient _id1 puis _id2.

En revanche, si nous faisons une requête filtrant sur le champ _id2 uniquement (SELECT field1 FROM table1 where id_2 = 6), le moteur de BDD ne pourra utiliser que _indexc.

Si nous reprenons _indexa, il n'est pas valide car le moteur de BDD lira cet index en trouvant les records discriminants sur _id1 avant les records discriminant sur _id2. Ce n'est pas un chemin optimal pour trouver un résultat dépendant uniquement de _id2. L'ordre des champs d'un index est donc important.

Pour finir, _indexa filtrant tout d'abord sur _id1 avant de filtrer sur _id2, il sait donc filtrer uniquement sur _id1 et donc _indexb est inutile (il prend des ressources disque et mémoire inutiles). Supprimez donc _indexb qui prend de l'espace disque et mémoire pour rien.

PostgreSQL: nettoyer complètement un schéma sans effort

Dans certains cas, comme par exemple dans un environnement d'intégration, il peut être intéressant de nettoyer toutes entrées dans votre BDD PostgreSQL. Lorsque le nombre de tables devient grand et surtout lorsque vous avez construit un schéma robuste à base de clefs étrangères, il peut être fastidieux de nettoyer ce schéma. Heureusement, il est possible de nettoyer votre base en demandant à PostgreSQL de générer la requête qui va lister les tables du schéma et les nettoyer. La requête ci-dessous va générer la requête de nettoyage des tables dans la base courante du schéma public. Elle ne s'éxecutera pas, il s'agit uniquement d'un résultat SQL sous forme de chaîne.

select 'truncate ' || string_agg(nspname || '.' || relname, ',') || ';' from pg_class join pg_namespace on pg_class.relnamespace = pg_namespace.oid where pg_namespace.nspname = 'public' and relkind='r';

Il ne vous reste plus qu'à exécuter la requête générée. Note: Si vous souhaitez filtrer sur le nom des tables il vous suffit de filtrer sur le champ relname.

Apache: obtenir l’IP source depuis un en-tête conditionel dans les logs

Dans le cadre d'un environnement web haute disponibilité il y a généralement des répartiteurs de charge HTTP comme HAproxy ou des reverse proxies comme Apache ou Nginx en frontal qui vont s'occuper de répartir la charge et/ou filtrer une partie des requêtes pour ménager les serveurs d'application de backend.

Le principal problème rencontré dans ce type d'architecture est que, généralement, le service web de frontend va masquer l'IP source du client, réduisant la tracabilité de celui-ci sur les backends. Nous allons ici partir du principe que le service web de frontend retransmet l'IP au backend via un en-tête HTTP nommé X-Real-IP.

L'astuce que nous allons voir ici permet d'afficher l'adresse IP du client via l'en-tête X-Real-IP si celui-ci existe, auquel cas l'adresse IP source.

Dans un premier temps nous allons définir 2 LogFormat, _completerealip et complete, chacun ayant un paramétrage sur le champ 1 différent:

LogFormat "%{X-Real-IP}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{Host}i\" %D" complete_realip
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{Host}i\" %D" complete

Dans un second temps, nous allons nous servir du module _modsetenvif afin de vérifier l'existence de l'en-tête:

SetEnvIf X-Real-IP ^.+$ real_ip_exists

Enfin on définit une condition sur l'environnement afin de basculer le log d'un mode à l'autre suivant la requête entrante:

CustomLog /var/log/apache/myapp_access.log complete_realip env=real_ip_exists
CustomLog /var/log/apache/myapp_access.log complete env=!real_ip_exists

Conclusion

Votre serveur Apache va désormais basculer l'IP présentée dans ses logs dynamiquement en fonction de la présence ou non de l'en-tête X-Real-IP.

Note: Si vous utilisez un haproxy en frontend, la configuration à ajouter à vos backend haproxy est la suivante:

http-request set-header X-Real-IP %[src]

GIT: supprimer un submodule

Git dispose d'une fonctionnalité de sous modules très intéressante quand il s'agit de relier plusieurs projets entre eux. Cela permet ainsi de relier un répertoire à un autre repository git, par exemple pour une librairie C/C++ à intégrer en statique dans un projet principal.

L'intégration du sous module est assez simple en revanche la suppression des traces du sous module n'est pas forcément triviale, git ne disposant pas d'une commande git submodule rm .

Voici donc la solution, pour un sous module installé dans le répertoire _lib/customsubmodule.

git submodule deinit lib/custom_submodule
git rm lib/custom_submodule
rm -Rf .git/modules/lib/custom_submodule

Vous allez ici supprimer le lien vers le submodule du repository et ses références dans le repository local.

Si vous souhaitez intégrer nativement un sous module, copiez au préalable le répertoire lib/custom_submodule et après le git rm, replacez le à l'endroit initial puis ajoutez les fichiers au repository avec git add

Ansible: gérer les clefs SSH de ses serveurs

Ansible est un outil d'industrialisation puissant. Il permet notamment de gérer les clefs publiques SSH de vos utilisateurs en les déployant sur un ensemble de machines.

Nous allons ici étudier le module authorized_key fourni par ansible.

Déploiement de clef SSH

Dans un premier temps nous allons parler de la tâche permettant de déployer les clefs SSH.

Pour déployer une clef SSH dans son plus simple appareil voici la syntaxe à adopter:

- authorized_key: user=root key="ssh-rsa AAAA....= ansible@ansiblehost"

Si vous souhaitez définir que la clef SSH de l'utilisateur est la seule qui doit exister pour cet utilisateur, il suffit d'appeler le paramètre exclusive:

- authorized_key:
  user: "root"
  key: "ssh-rsa AAAA....= ansible@ansiblehost"
  exclusive: yes

Suppression de clef SSH

Pour supprimer une clef SSH, il suffit de définir la state de notre tâche à absent

- authorized_key:
  user: "root"
  key: "ssh-rsa AAAA....= ansible@ansiblehost"
  state: absent

Déploiement de clefs SSH multiples par hôtes/groupes d'hôtes

Nous allons corser un peu la tâche ici en lui ajoutant une notion de boucle afin de définir des ensembles de clefs à déployer sur des hôtes:

- authorized_key:
  user: "{{ item.user }}"
  key: "{{ item.key }}"
  exclusive: "{{ item.exclusive | default('no') }}"
  state: "{{ item.state | default('present') }}"
  with_items: "{{ ssh_authorized_keys }}"

Nous avons ici une boucle lisant la variable ssh_authorized_keys. Chaque élément de ce tableau nécessitera le champ user et key. Les champs exclusive et state sont optionnels avec des valeurs par défaut étant le standard du module.

Admettons maintenant que nous ayons un groupe linux_hosts dans notre inventaire ansible, nous allons éditer le fichier group_vars/linux_hosts.yml afin de lui attribuer des clefs SSH au moyen de notre variable:

ssh_authorized_keys:
  - user: root
    key: "ssh-rsa AAAAB...aB= ansible@bastion"
  - user: cafecreme
    key: "ssh-rsa AAAAA...qpX= cafecreme@restau"

Lorsque nous exécutons notre tâche sur notre groupe linux_hosts, nous allons déployer simultanement les deux clefs. Les hôtes n'étant pas dans ce groupe ne seront pas affectés si on appèle, par exemple, le groupe d'hôtes embarqué all.

Conclusion

Cette méthode est très pratique et permet de faire des changements de clefs SSH autorisées sur un nombre d'hôtes important et de le faire varier par groupe d'hôtes sans se compliquer la tâche.

Linux: Ajout de CPU à chaud

Sur un système Linux récent (noyau > 2.6.24) il est possible de changer le nombre de CPU de la machines à chaud, qu'on soit en machine virtuelle ou physique. Pour vérifier l'état d'activation d'un CPU, il suffit d'afficher le fichier concernant le CPU mentionné

cat /sys/devices/system/cpu/cpu2/online

Si online est à 1 le CPU est activé et si c'est à 0 il est désactivé Pour cela il suffit de s'interfacer avec le sous système /sys pour changer l'état actif/inactif du CPU en poussant 0 ou 1 dans le champ online

echo 1 > /sys/devices/system/cpu/cpu2/online

Pour activer tous les CPU d'un coup, faîtes une boucle:

cd /sys/devices/system/cpu/
for F in cpu* ; do echo 1 > "$F/online"; done

A des fins de tests vous pouvez également désactiver des CPU en spécifiant 0 au lieu de 1, la seule limitation étant que le cpu0 n'est pas désactivable.

Surcharger le DNS utilisé par dhclient

La configuration DNS sous UNIX est située dans le fichier /etc/resolv.conf. Dans une configuration classique pour un serveur ce fichier est statique. Il est créé lors de la première installation en se référant au paramétrage spécifié dans l'installeur. Pour une configuration dynamique en DHCP, généralement un client, c'est le binaire dhclient qui va s'occuper de modifier les lignes de configuration afin de mettre les DNS reçus via DNS ainsi que les zones de recherches et suffixe DNS. Le principal problème que nous allons essayer de résoudre ici est essentiellement une problématique de type cloud. Sur un cloud de type AWS nos serveurs sont tous en DHCP, c'est obligatoire. Pour prendre le cas pratique AWS, gérer son DNS sans serveur local consiste à configurer le service route53 avec ses enregistrements, soit via l'interface, awscli ou encore ansible. Si jamais on souhaite pouvoir interagir avec des DNS personnalisés ce n'est donc pas possible facilement, dhclient remplaçant automatiquement le fichier /etc/resolv.conf à chaque interrogation.

Résolution de la problématique

dhclient contient fort heureusement certaines options de configuration nous permettant de gérer le cas d'un DNS statique avec une IP dynamique, ce sont les directives prepend, append et supersede. Ces options permettent d'ajouter avant (prepend), après (append), ou de remplacer (supersede) des options DHCP reçues par le client avant d'interagir avec le système. Vous pouvez donc avoir 2 cas d'usages intéressants. La configuration suivante sera à insérer dans le fichier /etc/dhcp/dhclient.conf.

1. Remplacer le DNS obtenu par un DNS personnalisé

La directive supersede permet de remplacer complètement les DNS obtenus via DHCP

supersede domain-name-servers 172.0.14.1, 172.0.14.2;
supersede domain-name-servers 172.0.14.1, 172.0.14.2;

2 Utiliser un DNS personnalisé par défaut et les DNS obtenus en fallback

On va ici utiliser la directive prepend, nous permettant d'utiliser les DNS obtenus via DHCP uniquement en cas d'erreur d'accès à nos DNS personnalisés

prepend domain-name-servers 172.0.14.1, 172.0.14.2;

Note: Un effet de bord d'usage de ces directives, vous pouvez ainsi spécifier des DNS si votre DHCP n'en fournit pas.

Conclusion

Lors de la prochaine requête DHCP votre fichier resolv.conf sera ainsi configuré pour contenir les 2 serveurs DNS spécifiés dans dhclient.

Bonus

Puisqu'automatiser est très important, voici quelques lignes de configuration ansible à intégrer à vos rôles d'uniformisation des socles applicatifs

- name: "DHCLIENT | Set static nameservers"
  lineinfile: dest="/etc/dhcp/dhclient.conf" line="supersede domain-name-servers {{ dns_servers | join(', ') }};" state=present
  when: dns_servers is defined
  tags: [dhcp]

Régler un problème de lecture sur une vue MySQL/MariaDB

Suite à un peu de troubleshoot ces derniers jours je suis tombé sur un problème MySQL difficile à comprendre lors d'un requêtage sur mes vues lors de la création d'une instance dupliquée d'une base de production destinée à des tests de charge. L'erreur lors du requêtage sur la vue avec un utilisateur non privilégiée était la suivante:

Access denied for user 'read_user'@'172.1.7.125'

A première vue, il semblerait que notre utilisateur mysql applicatif n'aie pas accès à la base de données avec son mot de passe habituel. Pourtant lorsque l'application requête une table InnoDB sur la même base cela fonctionne. Regardons maintenant les droits de notre utilisateur:

GRANT USAGE ON *.* TO 'read_user'@'172.1.7.125' IDENTIFIED BY PASSWORD '*32F1789A74AA440B2FC136AF791B5C2B8BC7DE2F'
GRANT ALL PRIVILEGES ON `financialdb`.* TO 'read_user'@'172.1.7.125'

D'après ces droits, notre utilisateur a bien le droit de se connecter et possède les droits sur la base financialdb (notre base importée). Si on teste rapidement via un prompt mysql

mysql -u read_user -h localhost -p financialdb
select count(*) from history_records;
+----------+
| count(*) |
+----------+
| 11332692 |
+----------+

select count(*) from view_history_records_filtered;
Access denied for user 'read_user'@'172.1.7.125'

Etrange, nous sommes connectés mais impossible de lire la vue.

show create table view_history_records_filtered
| view_history_records_filtered | CREATE ALGORITHM=UNDEFINED DEFINER=`gildas`@`localhost` SQL SECURITY DEFINER VIEW `view_history_records_filtered` AS select `id` from view_history_records WHERE `p`='134' |

La vue appartient à l'utilisateur 'gildas'@'localhost', il ne s'agit pas de notre utilisateur applicatif. Verifions les droits de cet utilisateur:

show grants for 'gildas'@'localhost';
ERROR 1141 (42000): There is no such grant defined for user 'gildas' on host 'localhost'

Notre utilisateur n'existe pas ou plus, c'est là l'erreur. On recréée la vue avec un utilisateur qui existe, le nôtre par exemple, et on retente la requête

select count(*) from history_records;
+----------+
| count(*) |
+----------+
| 715      |
+----------+

Cela fonctionne ! MySQL parle de droits pour votre utilisateur, alors qu'en fait c'est le definer de la vue qui n'existe pas, le message d'erreur est obscur et dans les logs nous n'avons pas d'entrée évoquant ce problème. Ceci s'explique par le fait que l'utilisateur définissant la vue est une sorte d'utilisateur proxy lisant les données (donc attention à ne pas mettre root !)

Installer et utiliser powerline sur Archlinux et FreeBSD

Powerline est un excellent module qui peut se greffer sur votre shell favori, sur vim et sur tmux. Il permet d'ajouter une couche dynamique intéressante sur les outils précédents en ajoutant de la colorisation et des états à votre shell, tmux ou vim. vim powerline

Installation

Sur votre Archlinux installez les paquets suivants:

pacman -S powerline powerline-vim powerline-fonts

Note: si votre Archlinux est un serveur (ce que je ne recommande pas), n'installez pas les fonts Sur FreeBSD installez les paquets suivants:

pkg install py27-powerline-status powerline-fonts py27-psutils

Note: si votre FreeBSD utilise une version de python par défaut différence, par exemple Python 3.5, changez les noms de paquets par py35-powerline-status et py35-psutils. Si votre FreeBSD est un serveur vous n'avez pas besoin des fonts.

Intégration tmux

tmux est un excellent remplaçant au vénérable screen. Il supporte très bien powerline. Pour activer le support powerline dans tmux, éditez le fichier tmux.conf(~/.tmux.conf pour votre utilisateur uniquement, en global: /etc/tmux.conf sous Linux, /usr/local/etc/tmux.conf sous FreeBSD) et ajoutez la ligne suivante.

Archlinux

source /usr/lib/python3.6/site-packages/powerline/bindings/tmux/powerline.conf

FreeBSD

source /usr/local/lib/python2.7/site-packages/powerline/bindings/tmux/powerline.conf

Note: la version de Python est peut être à changer dans ce chemin, sous FreeBSD cela dépend de la version de Python par défaut sur votre repository, sous Archlinux il se peut que cela devienne Python 3.6 ou plus lorsqu'il sortira. Lancez maintenant un nouveau tmux ou lancez la commande suivante dans un nouveau tmux (après avoir appuyé sur CTRL+B)

:source-file

Résultat:tmux powerlineIntégration ZSH

Pour intégrer powerline sur ZSH il vous faudra simplement sourcer powerline dans le fichier ~/.zshrc de l'utilisateur concerné Sous Archlinux

source /usr/lib/python3.6/site-packages/powerline/bindings/zsh/powerline.zsh

Sous FreeBSD

source /usr/local/lib/python2.7/site-packages/powerline/bindings/zsh/powerline.zsh

Résultat sur un shell local:

powerline-zsh-local

Intégration BASH

Pour bash c'est la même chose, sourcez powerline dans le fichier ~/.bashrc de votre utilisateur: Sous Archlinux:

source /usr/lib/python3.6/site-packages/powerline/bindings/bash/powerline.sh

Sous FreeBSD:

source /usr/local/lib/python2.7/site-packages/powerline/bindings/bash/powerline.sh

Résultat sur un shell distant, en SSH:powerline-bash-ssh

Intégration VIM

Enfin il nous reste à l'implémenter dans vim éditez le fichier ~/.vimrc si vous souhaitez l'activer pour votre utilisateur ou /etc/vim/vimrc (/usr/local/etc/vim/vimrc sous FreeBSD) si vous souhaitez les modifications en global. Ajoutez les lignes suivantes:

set laststatus=2
set t_Co=256

Si vous utilisez un vim compilé pour Python 3, ajoutez la ligne suivante:

let g:powerline_pycmd="py3"

Enfin, uniquement sous FreeBSD vous devrez ajouter la ligne suivante dans votre vimrc:

set rtp+=/usr/local/lib/python2.7/site-packages/powerline/bindings/vim/

AWS + Ansible: scheduler automatisé d’allumage/extinction d’instances

Amazon Web Services (AWS) est très bien interfacé avec Ansible. Nous allons voir dans cet article comment créer quelques économies d'infrastructure en designant un planficateur de démarrage et d'arrêt d'instances. Nous partirons du principe que vous avez déjà utilisé AWS et que vous avez déjà vu les bases d'Ansible. Nous utiliserons ici une syntaxe ansible 2.0

Architecture

L'architecture de notre scheduler est simple, un cron va faire tourner périodiquement ansible pour envoyer des ordres d'arrêt et extinction à la plateforme EC2 d'AWS en fonction d'un tag sur une instance. Les instances qui devront être démarrées à une heure précise auront le tag starthour, les instances qui devront être éteintes à une heure précise le tag stophour

Playbook Ansible

Nous allons créer un playbook qui va se baser sur un rôle que nous nommerons amazon_scheduler. Editons notre fichier de playbook /etc/ansible/playbooks/amazon_scheduler.yml

---
- hosts: localhost
  connection: local
  become: false
  roles:
    - amazon_scheduler

La configuration de ce playbook permet de la lancer avec un utilisateur non privilégié sur la machine locale.

Role Ansible

Créons maintenant notre role ansible

mkdir -p /etc/ansible/roles/amazon_scheduler/{tasks,vars,default}

Variables

Peuplons le fichier de variables par défaut du rôle (default/main.yml) avec une seule variable correspondant à la region AWS sur laquelle nous allons travailler. Note: Vous pourrez bien entendu externaliser cette région afin d'agir sur plusieurs points du globe facilement.

---
region: 'eu-west-1'

Dans le répertoire de variables du rôle, créez un fichier _vars/defaulthost.yml contenant le hash vide suivant:

---
aws_env: {}

Et un fichier au nom d'hôte de la machine faisant tourner le cron contenant les informations d'authentification AWS réelles.

---
 aws_env:  AWS_ACCESS_KEY_ID: KAMLD8ELAM64AD
 AWS_SECRET_ACCESS_KEY=MSLds51A54cdsq+sdf1s+d2d+dqs59

Note: je vous conseille de sceller le fichier contenant les informations AWS dans une vault ansible chiffrée. Cette méthode permettra ainsi d'avoir une configuration statique pour votre runner automatique et, par défaut d'utiliser la configuration de votre machine personnelle pour vos tests ou actions manuelles.

Actions

Editez maintenant le fichier tasks/main.yml et indiquez le contenu suivant:

---
- name: "Initialize some host specific configuration"
  include_vars: "{{ item }}"
  with_first_found:
    - "{{ ansible_hostname }}.yml"
    - "default_host.yml"
  tags: [stop, start]

- include: stop.yml
  environment: "{{ aws_env }}"
  tags:
    - stop

- include: start.yml
  environment: "{{ aws_env }}"
  tags:
    - start

Notre point d'entrée va dans un premier temps charger nos variables dynamiques par host avant de déclarer 2 sous actions, une pour démarrer et une pour stopper des machines. Chaque sous action dispose en prime d'un accès aux variables d'environnement AWS chargées dynamiquement. Passons maintenant à l'action stop (tasks/stop.yml)

---
- name: EC2 | Stopping instances
  ec2:
    instance_ids: []
    instance_tags:
      stophour: "{{ stophour | default(ansible_date_time.hour) }}"
      region: '{{ region }}'
      state: stopped
      wait: True

Cette action est très simple, on demande au module ec2 d'arrêter toutes les machines avec le tag stophour correspondant à une variable ansible stophour arbitraire. Cette action est appliquée sur notre région statique, spécifiée dans default/main.yml. On attend également que les machines soient éteintes avant de rendre la main. De manière analogue,

---
- name: EC2 | Starting instances
  ec2:
    instance_ids: []
    instance_tags:
      starthour: "{{ starthour | default(ansible_date_time.hour) }}"
    region: '{{ region }}'
    state: running
    wait: True

Arborescence Ansible finale

Vous devriez maintenant avoir l'arborescence ansible suivante:

  • playbooks
    • amazon_scheduler.yml
  • roles
    • amazon_scheduler
      • defaults
        • main.yml
      • tasks
        • main.yml
        • start.yml
        • stop.yml
      • vars
        • default_host.yml
        • ansible_launcher_hostname.yml

Test du playbook

Vous pouvez maintenant tester un scheduler avec une heure précise en invoquant le playbook avec une heure précise et une action précise:

ansible-playbook /etc/ansible/playbooks/amazon_scheduler.yml --tags stop -e stophour=21

Planification cron

Vos schedulers ansible sont prêts. Vous n'avez plus qu'à aller sur votre machine hôte de déploiement Ansible et créer le cron suivant:

0 * * * * ansible ansible-playbook /etc/ansible/playbooks/amazon_scheduler.yml

Configuration AWS

Allez maintenant sur votre console AWS et ajoutez les tags stophour et starthour sur les instances que vous souhaitez allumer et éteindres à horaires fixes. AWS EC2 TagsNote: vous pouvez bien entendu utiliser le module _ec2_tags_ d'Ansible pour industrialiser cette action

Conclusion

Vous pouvez désormais économiser vos ressources AWS (et accessoirement faire quelques économies) en planifiant simplement l'allumage et l'extinction de vos machines. Quelques idées intéressantes en vous basant ce scheduler:

  • Allumer des machines uniquement sur des périodes ouvrées pour vos développeurs
  • Activer certaines machines de production à certaines heures afin d'absorber les pics de charge (ce n'est pas la seule solution, loin de là).

Ansible: dump & injection de dump

Ansible est un excellent orchestrateur d'infrastructure agent-less. Nous n'avions pas encore eu le temps de le couvrir sur ce blog, malgré que je l'utilise beaucoup au quotidien pour orchestrer, mettre en production, maintenir une infrastructure de quelques centaines de serveurs.

Ansible est capable de faire des dumps et restaurations de fichiers SQL de façon très simple et, depuis la version 2.0, ceux-ci peuvent être compressés.

Injecter un dump SQL compressé

hosts: mysql_server_01
tasks:
  - mysql_db: name="translations" state=import target="/nfs/backup/translations.sql.gz"

Ansible va ici se connecter sur le serveur mysql_server_01 renseigné dans votre fichier hosts et injecter le fichier translations.sql.gz directement dans la base translations. Si elle n'existe pas il la créera. Si vous utilisez ansible 1.X, vous ne pouvez spécifier qu'un fichier non compressé, il faudra une étape unarchive intermédiaire.

Dumper une base de données en SQL

hosts: mysql_server_01
tasks:
  - mysql_db: name="translations" state=dump target="/nfs/backup/translations.sql.gz"

Ansible exporte ici notre base translations vers le fichier translations.sql.gz

Si vous le souhaitez vous pouvez également utiliser les paramètres _loginhost, _login_password, loginport et _loginhost afin de configurer votre connexion MySQL. Dans le cas contraire le fichier $HOME/.my.cnf sera lu.

Backup multi bases

Voici en extra de cet article, un cas d'usage qui permet de faire un backup multi bases en utilisant une boucle ansible

hosts: mysql_server_01
tasks:
  - mysql_db: name="{{ item }}" state=export target="/nfs/backup/{{ item }}.sql.gz"
    with_items:
      - translations
      - movies
      - mysql
      - owncloud

Optimiser ses images PNG sans perte

Les images PNG sont monnaie courante dans le web. De meilleure qualité que les images JPEG et permettant la transparence, elle prennent aussi plus de place.

Pour l'affichage sur le web il est important d'optimiser ces échanges, que ce soit pour ses utilisateurs ou pour améliorer son ranking.

Pour cela, installez tout d'abord l'outil optipng:

pkg install optipng
apt-get install optipng

Ensuitez tapez la commande suivante et observez vos fichiers réduire de taille:

optipng -o2 /usr/local/www/images/*.png

optipng dispose de 7 niveaux d'optimisations consistant à utiliser plusieurs passes et différents algorithmes. Le niveau 2 est un bon premier compromis mais vous pouvez pousser l'optimisation plus loin. N'oubliez pas de faire un backup de vos images, optipng réécrit les fichiers qu'il optimise, notamment si vous utilisez le niveau maximum de compression.

Si vous avez un blog de type Wordpress, vous pouvez par exemple jouer le cron suivant afin d'optimiser automatiquement vos png:

00 4 * * * optipng -o2 /usr/local/www/wordpress/wp-content/uploads/*/*.png