Notes et Astuces
En guise d’introduction
Awk pour les fichiers csv
Exemple pour compter le nombre de valeurs uniques du quatrième champs d’un
fichier csv
, dont le séparateur est un ;
:
cat Customer.csv | awk -F ';' '{print $4}'| sort -u | wc -l
Awk pour docker
Pour afficher la liste des container en filtrant sur la commande exécutée:
docker ps -a | awk '$3 ~ /docker-php-entry/ '
Git
Lister les fichiers modifiés entre deux commits, ou deux branches (ici master
et develop
).
git diff-tree --no-commit-id --name-only -r master..develop
Ansible
J’ai eu du mal à trouver comment exécuter en local_action
une commande du
module shell. La solution: utiliser _raw_params
comme ci-dessous:
- name: Build frontend local_action: module: shell _raw_params: grunt build args: chdir: "{{ build_root_path }}/src/views/"
Hardware
Quand votre station d’accueil dell buggue, la solution est le reset de tous les périphériques usb:
find /sys/bus/usb/devices/*/authorized -exec sh -c 'echo 0 > ${0}; echo 1 > ${0}' {} \;
(la vrai solution reste de remplacer la station d’accueil par un hub usb)
Mysql
Quelques commandes en vrac, suffisamment explicites pour ne pas avoir à les commenter, à garder sous le coude:
SHOW FULL PROCESSLIST; SET GLOBAL general_log = 1; SHOW TABLE LIKE 'prefix%';
Composer
Après la résolution d’un conflit dans git, il peut arriver que vous ayez
manuellement modifier le contenu du fichier composer.lock
. Il est alors
nécessaire de mettre à jour les champs qui contiennent les champs de
contrôle:
composer update nothing
Visualiser correctement les journaux contenant des \n
tail -f oxideshop.log | sed 's/\\n/\ /g'
Configuration
Je ne suis pas un fanatique de la configuration à outrance des logiciels utilisables dans un terminal. J’essaie généralement de me contenter de la configuration par défaut, afin de ne pas être pris au dépourvu ensuite lorsque je dois utiliser ces mêmes logiciels sur un serveur distant, où ces personnalisations seront absentes.
Dernièrement j’ai fait une exception pour tmux
. Dans 90% des cas, lorsque
je découpe une fenêtre, je souhaite rester dans le même répertoire duquel
j’ai lancé la découpe. En revanche, lorsque j’ouvre une nouvelle fenêtre, je
souhaite retourner dans mon HOME
, ce qui est le comportement par
défault. C’est très pratique sur mon ordinateur personnel, et si je n’ai pas
cette fonctionnalité sur un serveur, je peux m’en passer aisément. J’ai donc
créé un fichier ~/.tmux.conf
avec la configuration suivante:
bind '"' split-window -c "#{pane_current_path}" bind % split-window -h -c "#{pane_current_path}"
Je n’utilise emacs
que sur mon ordinateur personnel. Pour me me connecter
à une base de données mariadb
(ou mysql
) en utilisant un autre port que
le port standard, ce qui arrive systématiquement en utilisant des base de
données hébergé dans des containers, j’ai dû ajouter la ligne suivante:
(setq sql-mysql-login-params (append sql-mysql-login-params '(port)))(with-eval-after-load "sql" (setq sql-mysql-login-params (append sql-mysql-login-params '(port))) )
Explorer une application php avec php-spx
Flame Graph pour Php
Nous avons vu précédemment comment générer des graphes de flammes pour analyser son système GNU/Linux. Dans cet article nous allons voir comment générer ce type de graphe pour une application php.
Grâce à l’extension php-spx ce n’est pas simplement une image svg qui va nous fournir des indications sur les fonctions les plus gourmandes de notre programme php, mais c’est une application web dédiée, beaucoup plus riche en terme d’information qui sera à notre disposition.
Comment installer php-spx
Je ne vais détailler ce qui est déjà écrit dans la documentation du projet. Je vais simplement expliquer les modifications que j’effectue sur mon container docker php-fpm, lancé par docker-compose, qui contient aussi un serveur web et une base de donnée (ces autres containers ne seront pas décrit ici).
Par défaut mon Dockerfile
copie déjà des fichiers de configuration ou bien des fichiers à exécuter dans le container. J’ajoute donc deux fichiers supplémentaires pour l’extension php-spx:
diff --git a/docker/php/Dockerfile b/docker/php/Dockerfile COPY ./xdebug.sh /tmp/xdebug.sh +COPY ./spx.sh /tmp/spx.sh +COPY ./spx.ini /tmp/spx.ini USER ${CONTAINER_UID}:${CONTAINER_GID} COPY --chown=${CONTAINER_UID}:${CONTAINER_GID} ./install.sh /tmp/install.sh
install.sh
est le fichier qui est exécuté par le container avec CMD ["/bin/bash","/tmp/install.sh"]
, et bien évidement la dernière ligne de ce fichier est php-fpm
diff --git a/docker/php/install.sh b/docker/php/install.sh sudo bash /tmp/xdebug.sh + sudo bash /tmp/spx.sh if [ -f "source/index.php.installing.tmp" ] then
Voici maintenant le contenu des deux nouveaux fichiers. Tout d’abord la configuration de l’extension:
diff --git a/docker/php/spx.ini b/docker/php/spx.ini new file mode 100644 +extension=spx.so +spx.http_enabled=1 +spx.http_key="dev" +spx.http_ip_whitelist="127.0.0.1,{EXTERNAL_IP}"
Et ensuite le fichier qui va installer l’extension dans le container:
diff --git a/docker/php/spx.sh b/docker/php/spx.sh new file mode 100644 +cd /tmp +apt-get update && apt-get install zlib1g-dev +git clone https://github.com/NoiseByNorthwest/php-spx.git +cd php-spx +phpize +./configure +make +make install +cp /tmp/spx.ini /usr/local/etc/php/conf.d/spx.ini +EXTERNAL_IP=$(/sbin/ip route|awk '/default/ { print $3 }') +sed -i "s/{EXTERNAL_IP}/${EXTERNAL_IP}/" /usr/local/etc/php/conf.d/spx.ini
Et comment ça marche
Maintenant, en allant sur l’url de l’application et en y ajoutant le
suffixe, /?SPX_UI_URI=/
vous devriez accéder à l’interface de gestion de
php-spx.
Nous allons activer l’enregistrement des évènements en cliquant sur la checkbox. Ensuite nous pouvons aller visiter une ou plusieurs pages de notre application avant de revenir à cette page.
Nous avons maintenant dans le bas de la page la liste des urls que nous avons visitées et en cliquant sur un des liens, nous pouvons enfin analyser l’exécution du code de notre application.
La ligne de temps, en haut de la page, montre chaque fonction appelée. La vue globale est au dessus et la vue détaillée juste en dessous. Plus la fonction prend du temps à s’exécuter, plus elle apparaît large. Lorsque la la barre représentant une fonction est cliquée (depuis la vue détaillée ou globale), s’affiche le détail de la fonction:
- son nom
- la profondeur
- le temps d’exécution
En bas à droite s’affiche un résumé du graphe de flammes: toutes les stacks (chaînes de fonctions qui s’appellent) identiques sont regroupés ensemble. Pour information, cet affichage qui regroupe les stacks identiques est le type d’affichage par défaut présenté précédemment.
Le tableau à gauche montre toutes les fonctions. Il est possible de trier le
tableau par chaque colonne. Les informations les plus importantes sont les colonnes
Inc
et Exc
:
-
Inc
: est le temps passé au total dans la fonction -
Exc
: est le temps passé dans la fonction elle même, soit le temps total, moins le temps passé dans les autres fonctions appelées - exemple:
index.php
a une valeurinc
de 100%, mais généralement de 0% pourexc
, puisqu’il ne se passe rien dans ce fichier même.
La vue globale est extrêmement pratique, parce qu’elle permet de zoomer à sur une période de temps très très précise. Toutes les informations de la page se mettent à jour pour correspondre à ce qui se passe pendant l’intervalle de temps zoomé: vue détaillée, tableau et graphe de flammes.
Et en ligne de commande
Pour finir
Php-spx est une extension incroyable et très pratique, pour analyser la performance d’une page php, mais aussi pour apprendre le fonctionnement des frameworks php en examinant en live ce qu’ils font.
La seule limite est qu’elle ne fournit pas les arguments des fonctions, ce qui peut-être une limite parfois, comme par exemple pour trouver les requêtes SQL les plus lentes.
Enregistrer toutes les commandes lancées par bash
Présentation succincte de bpftrace
Je suis en train d’apprendre à utiliser bpftrace. Bpftrace est une interface à eBPF, sous la forme d’un nouveau langage inspiré de la syntaxe d’awk, qui permet d’observer un système GNU/Linux.
Comme awk, il est possible de créer des commandes d’une seule ligne (des
unilignes), à lancer depuis un terminal, ou bien de créer des scripts en
utilisant un shebang spécifique: #!/usr/bin/env bpftrace
Surveiller bash
Le premier exemple du projet est un programme qui affiche la liste des commande lancées par tous les shells interactifs bash.
Mais ce script est incomplet lorsqu’on le compare au besoin réel d’un hébergeur.
Il n’affiche que le pid
du script et la valeur de retour: printf("%-6d %s\n", pid, str(retval));
.
On peut afficher très facilement l’id et le nom de l’utilisateur:
printf("%-6d %s %5d (%s) - ", pid, str(retval), uid, username);
.
On peut aussi vouloir afficher certaines variables d’environnement. Si on
peut accéder à toutes les variables d’environnement, en utilisant le fichier
/proc/pid/environ
, malheureusement bpftrace ne proprose pas d’outil
complexe pour manipuler les chaînes de caractères et ne supporte les boucles
que depuis la version 5.3 du noyau. Dès lors je n’ai pas cherché à aller
plus loin pour extraire les valeurs dont on a besoin.
Au final on obtient l’uniligne suivant:
bpftrace -e 'uretprobe:/bin/bash:readline { time("%H:%M:%S "); printf("%-6d %s %5d (%s) -", pid, str(retval), uid, username); cat("/proc/%d/environ", pid) ; printf("\n")}'
Il est possible de transformer les lignes produites par cet outil, afin d’extraire les variables d’environnement que l’on veut sauvegarder.
Comment mieux faire
La sonde utilisée ici est uretprobe:/bin/bash:readline
: on récupère la
valeur retour de la fonction readline appelée par bash. Un utilisateur qui
veut être discret pourra utiliser un autre shell, comme dash, pour lancer
d’autres commandes. Il faudrait donc créer une sonde différente pour chaque
type de shell, ou bien trouver une autre sonde commune à tous les shells.
Quoi qu’il en soit, bpftrace est un outil surpuissant pour surveiller ce qui se passe sur un serveur. La liste des exemples fournit une base solide pour développer ses propres sondes de surveillance.
Enflammer son GNU/Linux
De quoi parle-t-on?
Les graphes de flammes (traduction plus qu’approximative de flam graphs) sont une représentation graphiques des appels de fonctions d’un serveur ou d’un programme, qui permet de visualiser:
- l’empilement des appels de fonctions (quelle fonction appelle qui);
- la durée (ou une autre quantité comme la mémoire) occupée par chaque fonction.
Comme une image vaut mieux qu’un long discours voici un exemple:
Ceci est une image statique. Mais l’image originale permet de zoomer à volonté.
Premières flammes
Pour créer un tel graphe, trois étapes théoriques sont nécessaires:
- collecter les données avec l’outil perf (
apt install linux-perf
sous Debian pour l’obtenir); - mettre en forme les données;
- générer l’image.
J’ai écrit en théorie ci-dessus, parce qu’en pratique l’étape deux et trois sont généralement effectuées en même temps.
La génération des données s’effectue donc avec la commande perf
. Par exemple:
perf record -F 99 -a -g -- sleep 20
Cette commande collecte pendant 20 secondes tous les appels de fonctions à la fréquence 99. Elle génère un fichier perf.data
dans le répertoire où elle a été lancée.
La première transformation que l’on va apporter à ce fichier s’effectue aussi avec la commande perf
1:
perf script --header -i perf.data > out.perf
Cela génère un fichier texte, auquel vous pouvez jeter un œil. C’est lisible, mais pas très pratique. À la limite, pour lire le contenu des données collectée, la commande report
est plus adaptée: perf report -n --stdio
.
Pour la suite, vous allez avoir besoin de FlameGraph. Il suffit de récupérer le dépôt, et d’appeler les commandes suivantes depuis le répertoire où est téléchargé le dépôt:
./stackcollapse-perf.pl ../out.perf > out.folded
./flamegraph.pl --title "Server On Fire" out.folded > image.svg
Et voilà, vous avez généré votre premier graphe de flammes!
On peut résumer les dernières commandes en une seule ligne:
perf script --header -i ../path-to/perf.data | ./stackcollapse-perf.pl | ./flamegraph.pl --title "Title" > en-flamme.svg
Plus de précisions
Parfois il y a beaucoup de unknown
qui apparaissent comme nom de
fonction. Cela signifie que perf
n’arrive pas à retrouver la fonction
appelée. Généralement en remplaçant -g
par --call-graph dwarf
cela
résout le problème. J’ai aussi rencontré une situation où la solution a été
d’effacer le contenu de mon répertoire ~/.debug/
qui contenait des
fichiers obsolètes.
Jusqu’à présent, nous avons obtenu une image globale de notre système. Mais il est aussi possible de surveiller un ou plusieurs processus, grâce à l’argument -p
:
record -F 99 -a --call-graph dwarf -p 469325,521774 -- sleep 10
De même les arguments -t
et -u
filtrent le premier sur les thread id, et
le second sur les processus appartenant à un utilisateur donné.
Il est aussi possible d’observer un container.
perf record -F 99 -a --call-graph dwarf -e instructions --cgroup=docker/424d518a79c2bf2bed363a32ef0ceee3e9ec47e34020eaff41bf0b09fba84251 -- sleep 10
Quelques explications s’imposent:
- il faut fournir le nom complet du
cgroup
du container, préfixé pardocker/
. Le nom complet s’obtient avecdocker inspect --format={{.Id}} nom-du-container
- il faut spécifier un
event
.Perf
, qui sert à beaucoup d’autres choses que produire des graphes de flammes, permet d’observer uniquement certains évènement. L’évènementinsstructions
est suffisant pour notre besoin.
Références
Cet article n’est qu’une légère introduction.
Quelques références, toutes en anglais, pour aller plus loin:
- Tout sur les FlamGraph, par Brendan Gregg, qui est à l’origine du concept: https://github.com/brendangregg/FlameGraph
- Dépôt git pour générer les images: https://github.com/brendangregg/FlameGraph
- Sur la commande
perf
:- Page web de Brendan Gregg qui présente l’outil: http://www.brendangregg.com/perf.html
- Présentation au format PDF) du même auteur avec des informations supplémentaires: http://www.brendangregg.com/Slides/SCALE2015_Linux_perf_profiling.pdf
- Tutorial: https://perf.wiki.kernel.org/index.php/Tutorial#Sampling_with_perf_record
L’argument -i
, qui indique le fichier à traiter, est inutile dans le cas présent, puisque perf.data
est le fichier par défaut. Je ne l’ai mis ici uniquement pour vous évitez de le rechercher dans la documentation (perf help script
).
Monitorer des certificats avec Bash
Origine
Après avoir lu un article sur comment monitorer des certificats avec Perl, je n’ai pas pu m’empêcher de penser: «mais perl n’est pas nécessaire!». En effet, openssl est un véritable couteau suisse pour manipuler les certificats.
Voici donc un petit script, qui prend en argument une liste de domaines et de ports, et qui retourne le nombre de jours avant leurs expirations.
Le script
#!/bin/bash get_date () { local host=$1 local port=$2 text_date=$(echo - | openssl s_client -ign_eof -showcerts -servername $host -connect $host:$port 2>/dev/null | openssl x509 -inform pem -noout --enddate|cut -f 2 -d =) echo $text_date } get_nb_days () { local date=$1 now=$(date +"%s") expire=$(date --date="${date}" --utc +"%s") seconds_expire=$(($expire-$now)) days_expire=$((${seconds_expire}/$((60*60*24)))) echo $days_expire } main () { for var in "$@" do text_date=$(get_date ${var/:/ }) nb_days=$(get_nb_days "$text_date") echo $var $nb_days done } main "$@"
Explications et limites
Ce script utilise essentiellement:
- la commande
s_client
d’OpenSSL; - la commande
date
, version GNU (j’ai lu quelque part que l’argument+"%s"
ne marchait pas avec la version fournie par MacOS)
Le programme boucle sur les arguments passés, qui doivent être de la forme: host:port
. En sortie il affiche chaque host:port
suivi du nombre de jours avant l’expiration du certificat.
Les deux fonctions get_date
et get_nb_days
peuvent être re-utilisées pour d’autres scripts, par exemple pour n’afficher que les certificats qui expirent dans 15 jours ou dans 7 jours.