Archives de catégorie : Gentoo

Configurer Apache et PHP-FPM sous Gentoo

Après avoir utilisé suEXEC et PHP en fast-CGI; je suis passé a PHP-FPM, maintenant supporté par les dernières versions de PHP. Il s’agit d’un gestionnaire de processus PHP similaire au fast-CGI, mais offrant plus de possibilités de configurations que suEXEC/fastCGI.

Avantages

Lancement de processus PHP avec des utilisateurs différents. Cette fonction est similaire à celle de suEXEC, mais d’une configuration beaucoup plus aisée (évitant notamment les problèmes de droits sur les wrappers CGI).
Définition de plusieurs pools de processus, avec des paramètres différents.
Le processus maître étant une instance de PHP lui-même, les caches d’op-code peuvent être partagés par les différents processus fils.

Installation de PHP-FPM

Il faut tout d’abord ajouter le use flag ‘fpm’ à PHP avant de le recompiler. Il est aussi nécessaire de réinsaller les modules PHP éventuellement utilisés :

# Update /etc/portage/packages.use to add fpm use flag
emerge php
emerge pecl-geoip xcache pecl-memcache suhosin -av

Afin qu’Apache puisse communiquer avec PHP-FPM, il lui faut un module fast CGI. Si mod_fastcgi ou mod_fcgid peuvent faire l’affaire, ils disposent de plus de fonctionnalités que nécessaire et sont donc plus compliqués à configurer. J’ai donc choisi mod_fastcgi_handler :

emerge www-apache/mod_fastcgi_handler -av

Il faut alors modifier le fichier ‘/etc/conf.d/apache2’ pour lui activer ce nouveau module en l’ajoutant à la liste des options APACHE2_OPTS : ‘-D FASTCGI_HANDLER’.

Configuration PHP-FPM

La configuration de la gestion des pools de processus de PHP-FPM est centralisée dans le fichier ‘/etc/php/fpm-php-5.3/php-fpm.conf’. On peut définir plusieurs pools, exécutant PHP avec des utilisateurs différents :

[poirsouille]
listen=/var/run/php-fpm/poirsouille.socket
user=poirsouille
group=poirsouille
pm = dynamic
pm.max_children = 50
pm.min_spare_servers = 5
pm.max_spare_servers = 35

Le mode ‘dynamic’ permet à PHP-FPM d’ajuster automatiquement le nombre d’instances du pool à la demande.
On choisira un socket différent pour chaque pool.

Configuration Apache

Il reste à indiquer à Apache quel script fast CGI utiliser pour traiter les fichiers PHP. J’ai pu remplacer ma configuration suEXEC/mod_fcgid par celle-ci :


        Use VH tech

        # Ancienne configuration
        #SuexecUserGroup poirsouille poirsouille
        #FCGIWrapper /home/poirsouille/public_html/cgi-bin/php-fcgi .php
    
        # PHP-FPM
        AddHandler fcgi:/var/run/php-fpm/poirsouille.socket .php

On peut ainsi utiliser des pools différents selon les répertoires/virtualhosts/etc.

Démarrage

PHP-FPM est un ervice à part, qu’il faut donc ajouter au démarrage du serveur :

rc-update add php-fpm default

On peut ensuite (re)lancer PHP-FPM et Apache :

/etc/init.d/php-fpm restart
/etc/init.d/apache2 restart

Après quelques visites sur les différents sites, on constate l’apparition des processus dans les différents pools :

web ~ # ps aux |grep php-fpm
1002      9224  0.0  2.5 254380 52212 ?        S    00:16   0:02 php-fpm: pool poirsouille
1002      9275  0.0  2.1 246340 43540 ?        S    00:16   0:02 php-fpm: pool poirsouille
1002      9276  0.0  2.2 245420 45980 ?        S    00:16   0:01 php-fpm: pool poirsouille
1002      9331  0.1  2.2 244392 45816 ?        S    00:16   0:04 php-fpm: pool poirsouille
1002      9332  0.0  4.8 286808 100444 ?       S    00:16   0:02 php-fpm: pool poirsouille
root     19808  0.0  0.2 235232  5168 ?        Ss   May27   0:02 php-fpm: master process (/etc/php/fpm-php5.3/php-fpm.conf)
apache   19809  0.0  0.5 238400 11112 ?        S    May27   0:00 php-fpm: pool apache
apache   19810  0.0  0.7 240260 16372 ?        S    May27   0:00 php-fpm: pool apache
apache   19811  0.0  0.5 240184 12172 ?        S    May27   0:00 php-fpm: pool apache
apache   19812  0.0  1.2 248308 26444 ?        S    May27   0:00 php-fpm: pool apache
apache   19813  0.0  0.5 239864 12068 ?        S    May27   0:00 php-fpm: pool apache
apache   19814  0.0  0.7 241912 14476 ?        S    May27   0:00 php-fpm: pool apache
apache   19815  0.0  0.8 241416 16864 ?        S    May27   0:00 php-fpm: pool apache
tibou    19819  0.0  1.2 243724 26372 ?        S    May27   0:00 php-fpm: pool tibou
tibou    19820  0.0  2.7 268464 56912 ?        S    May27   0:01 php-fpm: pool tibou

Nettoyage

Une fois le fonctionnement validé, il est possible de désinstaller les packages devenus inutiles :

emerge --depclean mod_fcgid
#Remove suexec flag and recompile apache.

Références

http://mattmcadoo.com/node/42
http://blog.myprod.net/2010/08/14/apache2-suexec-fastcgi-php-5-3-3-fpm-cache-opcode-apc/

Gentoo : monter un volume DM-crypt/LUKS au boot

DM-crypt et LUKS permettent de créer très simplement un volume totalement crypté. Si l'utilisation de cryptsetup sur Gentoo ne diffère pas de la procédure standard, le montage au boot n'utilise pas le fichier /etc/crypttab mais /etc/conf.d/dmcrypt.

Création du volume crypté

Voici les étapes à suivre pour créer un volume crypté /dev/mapper/data_secured à partir de la partition /dev/xvda2. Pour plus de détails, voir cet article.

# Formatage avec LUKS de la partition
cryptsetup --verbose --verify-passphrase luksFormat /dev/xvda2
# Ouverture de la partition LUKS
cryptsetup luksOpen /dev/xvda2 data_secured
# Création du système de fichiers Ext4
mkfs.ext4 /dev/mapper/data_secured

Le volume /dev/mapper/data_secured peut alors être monté et utilisé comme une partition normale (ne pas oublier le crypsetup luksOpen avant l'appel à mount cependant).

Monter le volume DM-crypt/LUKS au boot

Comme précisé dans le wiki Gentoo, on utilise le fichier /etc/conf.d/dmcrypt plutot que /etc/crypttab :

/etc/conf.d/dmcrypt
target=data_secured
source="/dev/xvda2"

Cela va indiquer aux scripts de boot Gentoo d'ouvrir le volume LUKS avant de monter les systèmes de fichiers (hors root, qui nécessite un initramfs spécifique pour être crypté). Si une passphrase est utilisée pour le volume, elle sera alors demandée au boot sur la console. A éviter donc sur un serveur distant sans accès à la console…

Il suffit ensuite d'ajouter une ligne dans /etc/fstab comme d'ordinaire :

/etc/fstab
/dev/mapper/data_secured        /var/torrent    ext4    errors=remount-ro,noatime,acl   0       2

A chaque reboot, la passphrase est demandée :

 * Setting up dm-crypt mappings ...
 * dm-crypt map data_secured ...
 * cryptsetup will be called with :   luksOpen /dev/xvda2 data_secured
Entrez la phrase de passe pour /dev/xvda2 :
                                                                          [ ok ]
                                                                          [ ok ]
 * Checking all filesystems .../dev/mapper/data_secured : propre, 473/3932160 fichiers, 10663788/15728383 blocs
                                           [ ok ]
 * Mounting local filesystems ...                                         [ ok ]
 * Mounting misc binary format filesystem ...                             [ ok ]

 

Sécuriser PHP avec Suhosin sur Gentoo

Je viens de découvrir Suhosin, un patch et une extension PHP permettant de combler d'éventuelles vulnérabilités (type buffer overflow, etc.).

Son installation sur Gentoo est enfantine :

 
echo "dev-lang/php suhosin" >> /etc/portage/package.use
emerge php
/etc/init.d/apache restart

Et voilà ! Ce n'est certainement pas une solution miracle, mais ça ne peut pas faire de mal.

Incompatibilité avec WordPress 3

Après l'activation de Suhosin, j'ai vu fleurir de jolies alertes dans mes logs :

 

Ceci est un problème connu de WP, qui demande 256Mo de mémoire pour certaines pages (dont l'admin)… Ceci est très bien détaillé ici, et un bug WP a été ouvert.

Unworkaround est de changer la taille mémoire max autorisée par Suhosin à 256 Mo.

Configurer PHP avec suEXEC sous Apache

Après l'installation de ce premier site WordPress, j'ai fait face à quelques soucis lors de l'installation automatique des thèmes et extensions : WP exige en effet que les fichiers appartiennent à l'utilisateur exécutant les scripts PHP. Ce qui n'était pas mon cas…

J'aurais simplement pu faire un "chown -R apache" , mais j'ai préféré en profiter pour me lancer dans la mise en place de suEXEC.

Installation

J'utilisais à l'origine l'association Apache + mod_php disponible par défaut sur ma Gentoo. Cependant, celle-ci ne permet pas d'exécuter les scripts PHP avec un utilisateur différent de celui du serveur web (ou alors peut-être avec le MPM "peruser", à vérifier).

Pour compiler Apache avec le support suEXEC, il suffit d'ajouter le use-flag suivant suexec. La configuration par défaut de suEXEC exige que tous les documents soientt dans /var/www. Dans mon cas, je préfère /home. Il est donc nécessaire d'ajouter la variable SUEXEC_DOCROOT dans /etc/make.conf :

 
echo 'www-servers/apache suexec' >> /etc/portage/package.use
echo 'SUEXEC_DOCROOT="/home"' >> /etc/make.conf
emerge apache

Afin de pouvoir utiliser les modes CGI ou FastCGI, il faut aussi désactiver le mod_php. Cela peut se faire en supprimant la directive "-D PHP5" du fichier /etc/conf.d/apache2. Mais pour faire des tests sans impacter tout le serveur (sur un VirtualHost ou un répertoire par exemple), il est aussi possible de le désactivé depuis les fichiers de configuration apache :

 
<VirtualHost *:80>
  ...
  php_admin_flag engine off
  ...
</VirtualHost>

Mode CGI

Première étape : le mode CGI basique.

 
<VirtualHost *:80>
  ServerName tech.poirsouille.org
  DocumentRoot "/home/poirsouille/public_html/tech"
  CustomLog /var/log/apache2/tech_access_log combined
  ErrorLog /var/log/apache2/tech_error_log

  SuexecUserGroup poirsouille poirsouille
  ScriptAlias /cgi-bin/ "/home/poirsouille/public_html/cgi-bin/"
  php_admin_flag engine off
  AddHandler php-script .php
  Action php-script /cgi-bin/php-cgi

  <Directory "/home/poirsouille/public_html/cgi-bin">
         AllowOverride None
         Options None
         Order allow,deny
         Allow from all
  </Directory>

  <Directory "/home/poirsouille/public_html/tech">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all
  </Directory>
</VirtualHost>

La commande SuexecUserGroup active suEXEC pour les appels CGI. Le ScriptAlias définit un répertoire contenant des cgi exécutables. Celui-ci doit impérativement être contenu dans la DOC_ROOT de suEXEC. AddHandler et AddScript permettent d'associer les fichiers .php au binaire php-cgi.

suEXEC exige que le programme appelé appartienne à l'utilisateur, et qu'il ne soit modifiable que par lui. Les liens symboliques hors de DOC_ROOT ne sont pas supportés. Afin d'éviter de copier le binaire pour chaque utilisateur, le plus simple est d'utiliser un simple wrapper, en veillant à ce qu'il ait des droits corrects :

/home/poirsouille/public_html/cgi-bin/php-cgi
#!/bin/sh
/usr/bin/php-cgi $@

Après un redémarrage d'Apache, les scripts PHP devraient fonctionner correctement.

Cependant, côté serveur, un nouveau processus /usr/bin/php-cgi est créé pour chaque requête, ce qui cause une importante dégradation des performances face à mod_php.

Mode FastCGI

C'est alors qu'intervient FastCGI. Au lieu de créer un processus par requête, il ré-utilise toujours le(s) même(s). La maintenance de ces processus est assurée par Apache (un peu comme pour Apache lui-même avec le MPM prefork).

Voici donc comment utiliser PHP en mode FastCGI. Il existe deux modules Apache pour cela : mod_fstcgi et mod_fcgid. J'ai choisi le second :

 
emerge mod_fcgid

Voici ma configuration FastCGI :

/etc/apache2/modules.d/20_mod_fcgid.conf
<IfDefine FCGID>
  LoadModule fcgid_module modules/mod_fcgid.so
  SocketPath /var/run/fcgidsock
  SharememPath /var/run/fcgid_shm

  FcgidIdleTimeout 120
  FcgidProcessLifeTime 240
  FcgidMaxProcessesPerClass 100
  FcgidConnectTimeout 60
  FcgidIOTimeout 120

  # Ces deux lignes ne sont nécessaire que si mod_php est désactivé totalement
  AddHandler fcgid-script .php
  FCGIWrapper /usr/bin/php-cgi .php
</IfDefine>

Et on modifie le VirtualHost précédent :

 
<VirtualHost *:80>
  ServerName tech.poirsouille.org
  DocumentRoot "/home/poirsouille/public_html/tech"
  CustomLog /var/log/apache2/tech_access_log combined
  ErrorLog /var/log/apache2/tech_error_log

  SuexecUserGroup poirsouille poirsouille
  php_admin_flag engine off
  FCGIWrapper /home/poirsouille/public_html/cgi-bin/php-fcgi .php

  <Directory "/home/poirsouille/public_html/tech">
    Options Indexes FollowSymLinks
    AllowOverride All
    Order allow,deny
    Allow from all

    Options +ExecCGI

  </Directory>
</VirtualHost>

Les directives FCGIWrapper et +ExecCGI font leur apparition. Voici le simple wrapper PHP :

/home/poirsouille/public_html/cgi-bin/php-fcgi
#!/bin/sh

#export PHPRC="/etc/php.ini"
#export PHP_FCGI_CHILDREN=4
exec /usr/bin/php-cgi

On noter l'utilisation de la fonction exec de bash, qui permet de remplacer le processus de bash par celui appelé. Cela évite la création d'un processus fils, qui ne serait pas correctement tué par Apache. Une nouvelle fois, on s'assure que ce script a les droits corrects pour suEXEC :

 
web ~ # chown poirsouille:poirsouille /home/poirsouille/public_html/cgi-bin/php-fcgi
web ~ # chmod 755 /home/poirsouille/public_html/cgi-bin/php-fcgi
web ~ # ll /home/poirsouille/public_html/cgi-bin/php-fcgi
-rwxr-xr-x+ 1 poirsouille poirsouille 91 Oct 10 15:31 /home/poirsouille/public_html/cgi-bin/php-fcgi

Après redémarrage d'Apache on constate que les processus php-cgi survivent aux requêtes. Cela apporte de bien meilleures performances que le mode CGI, au détriment de l'utilisation mémoire (puisque l'interpréteur PHP reste en mémoire en attendant la prochaine requête).

Afin de minimiser la mémoire utilisée, j'ai désactivé mod_php globalement une fois mes tests terminés. Ainsi les processus Apache n'incluent plus mod_php et consomment moins de mémoire.

Performances

Les résultats suivants ont été obtenus en utilisant Apache bench avec les paramètres suivants :

ab -n100 -c10 http://tech.poirsouille.org/

La page d'accueil de ma nouvelle installation de WP m'a servi de référence.

mod_php

Concurrency Level:      10
Time taken for tests:   25.674 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      589000 bytes
HTML transferred:       565100 bytes
Requests per second:    3.89 [#/sec] (mean)
Time per request:       2567.396 [ms] (mean)
Time per request:       256.740 [ms] (mean, across all concurrent requests)
Transfer rate:          22.40 [Kbytes/sec] received

Ma configuration initiale comme référence.

CGI

Concurrency Level:      10
Time taken for tests:   72.616 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      586800 bytes
HTML transferred:       565100 bytes
Requests per second:    1.38 [#/sec] (mean)
Time per request:       7261.626 [ms] (mean)
Time per request:       726.163 [ms] (mean, across all concurrent requests)
Transfer rate:          7.89 [Kbytes/sec] received

En passant PHP en CGI on note une nette dégradation des performances, comme prévu.

CGI + suEXEC

Concurrency Level:      10
Time taken for tests:   72.299 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      586800 bytes
HTML transferred:       565100 bytes
Requests per second:    1.38 [#/sec] (mean)
Time per request:       7229.882 [ms] (mean)
Time per request:       722.988 [ms] (mean, across all concurrent requests)
Transfer rate:          7.93 [Kbytes/sec] received

L'activation de suEXEC en plus de CGI n'influe pratiquement pas sur les performances.

mod_fcgid + suEXEC

Concurrency Level:      10
Time taken for tests:   27.193 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      948188 bytes
HTML transferred:       926054 bytes
Requests per second:    3.68 [#/sec] (mean)
Time per request:       2719.334 [ms] (mean)
Time per request:       271.933 [ms] (mean, across all concurrent requests)
Transfer rate:          34.05 [Kbytes/sec] received

Le passage en mode FastCGI permet de retrouver des performances presque équivalentes à mod_php.

Remarques

L'authentification HTTP de PHP ne fonctionne qu'avec mod_php. Il existe toutefois des workarounds en utilisant mod_rewrite.

Il faudra que je jette un œil a PHP-FPM, qui devrait permettre de se passer de suEXEC.

Références

Gentoo : téléchargements impossibles avec emerge

Récemment, toutes mes mises à jour système se sont mise à planter lors du téléchargement des sources par portage, quel que soit le miroir utilisé :

web ~ # emerge gdb
Calculating dependencies... done!

>>> Verifying ebuild manifests

>>> Emerging (1 of 1) sys-devel/gdb-7.0.1
>>> Downloading 'http://distfiles.gentoo.org/distfiles/gdb-7.0.1.tar.bz2'
>>> Downloading 'ftp://sources.redhat.com/pub/gdb/releases/gdb-7.0.1.tar.bz2'
>>> Downloading 'http://ftp.gnu.org/gnu/gdb/gdb-7.0.1.tar.bz2'
!!! Couldn't download 'gdb-7.0.1.tar.bz2'. Aborting.
 * Fetch failed for 'sys-devel/gdb-7.0.1', Log file:
 *  '/var/tmp/portage/sys-devel/gdb-7.0.1/temp/build.log'

>>> Failed to emerge sys-devel/gdb-7.0.1, Log file:

>>>  '/var/tmp/portage/sys-devel/gdb-7.0.1/temp/build.log'

 * Messages for package sys-devel/gdb-7.0.1:

 * Fetch failed for 'sys-devel/gdb-7.0.1', Log file:
 *  '/var/tmp/portage/sys-devel/gdb-7.0.1/temp/build.log'

Rien d'intéressant dans les logs, et le mode debug d'emerge n'apporte aucune information supplémentaire sur l'erreur de téléchargement. De plus, un wget manuel fonctionne parfaitement :

web ~ # wget http://distfiles.gentoo.org/distfiles/gdb-7.0.1.tar.bz2
--2010-10-13 18:47:22--  http://distfiles.gentoo.org/distfiles/gdb-7.0.1.tar.bz2
Resolving distfiles.gentoo.org... 130.239.17.6, 137.226.34.42
Connecting to distfiles.gentoo.org|130.239.17.6|:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: http://mirrors.kernel.org/gentoo/distfiles/gdb-7.0.1.tar.bz2 [following]
--2010-10-13 18:47:22--  http://mirrors.kernel.org/gentoo/distfiles/gdb-7.0.1.tar.bz2
Resolving mirrors.kernel.org... 130.239.17.6, 199.6.1.174
Reusing existing connection to distfiles.gentoo.org:80.
HTTP request sent, awaiting response... 200 OK
Length: 17614682 (17M) [application/x-bzip2]
Saving to: `gdb-7.0.1.tar.bz2'

100%[===============================================================================================>] 17,614,682  4.66M/s   in 3.9s

2010-10-13 18:47:27 (4.28 MB/s) - `gdb-7.0.1.tar.bz2' saved [17614682/17614682]

J'ai donc passé quelques heures à mettre des print un peu partout dans le code de portage, pour finalement déterminer que le script s'arrêtait violemment lors de l'appel à la fonction os.setgroups par la méthode _exec de process.py :

web ~ # head -380 /usr/lib/portage/pym/portage/process.py | tail -20
 os.close(fd)
 except OSError:
   pass

 # Set requested process permissions.
 if gid:
   os.setgid(gid)
 if groups:
   os.setgroups(groups)
 if uid:
   os.setuid(uid)
 if umask:
   os.umask(umask)
 if pre_exec:
   pre_exec()

 # And switch to the new process.
 os.execve(binary, myargs, env)

Un simple test permet de constater que la méthode os.setgroups() est en cause :

web ~ # python -c "import os; os.setgroups([250])"
Segmentation fault

Le workaround

Ce problème apparait lorsque emerge change son utlisateur et ses groupes avant le téléchargement. Un workaround simple est donc de désactiver cette fonctionalité, en ajoutant la ligne suivante dans le fichier /etc/make.conf :

FEATURES="-userfetch"

Cause et solution

Toutefois, ce problème est apparu récemment, et ne peut être reproduit sur mes autres installations Gentoo. J'ai donc repensé aux modifications que j'avais apportées récemment, et j'ai eu un déclic : j'ai réduit la taille de la pile pour optimiser la mémoire consommée par Apache !

J'avais donc réduit la taille de la pile de 8Mo à 256 ko. En essayant de l'augmenter un peu :

web ~ # ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 16120
max locked memory       (kbytes, -l) 32
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 256
cpu time               (seconds, -t) unlimited
max user processes              (-u) 16120
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited
web ~ # ulimit -s 512
web ~ # python -c "import os; os.setgroups([250])"
web ~ #

C'était donc bien la taille de la pile.

Visiblement ma modification dans le fichier /etc/conf.d/rc n'impactait pas uniquement le lancement des services comme je le pensais… J'ai donc changé la variable RC_ULIMIT :

RC_ULIMIT="-s 512"

Utiliser ccache entre plusieurs VPS OpenVZ Gentoo

J’ai décidé d’utiliser une distribution Gentoo avec un noyau OpenVZ, hébergeant des VPS eux aussi sous Gentoo.

L’utilisation d’une distribution Gentoo sur un serveur animé par un petit Celeron 1.2 GHz pose quelques soucis de temps d’installation / mise à jour des paquets. Ceci est d’autant pire qu’il faut multiplier ce temps par le nombre de VPS…

ccache est un cache de compilateur, qui permet d’accélérer notablement les recompilations futures de sources identiques.  Ceci est déjà fort appréciable pour accélérer les recompilations lors du changement de certains use flags.

Partager le cache entre les VPS

Afin d’éviter de recompiler les même paquets sur chacun des VPS, j’ai décidé de partager le cache de ccache via NFS (comme je le fais pour l’arbre de paquets Gentoo par exemple).

Sur l’hôte

J’utilise donc le serveur NFS déjà configuré sur l’hôte OpenVZ :

mkdir -p /var/tmp/ccache
echo "/var/tmp/ccache 10.0.0.0/255.255.255.0(async,rw,no_subtree_check,insecure,no_root_squash)" >> /etc/exports
exports -r

Sur les VPS

Il suffit d’installer ccache et d’utiliser le partage NFS comme répertoire de cache :

emerge ccache
echo "host:/var/tmp/ccache /var/tmp/ccache nfs rw 0 0" >> /etc/fstab
mkdir /var/tmp/ccache
mount /var/tmp/ccachecd /var/tmp
mkdir ccache
chown portage:portage ccache
chmod 775 ccache
echo "CCACHE_DIR=\"/var/tmp/ccache\"" >> /etc/env.d/99local
env-update
source /etc/profile

Il ne reste alors plus qu’à configurer portage pour utiliser ccache, en ajoutant les lignes suivantes dans le fichier /etc/make.conf :

CCACHE_SIZE="4G"
CCACHE_DIR="/var/tmp/ccache"
CCACHE_COMPILERCHECK="content"
FEATURES="ccache"

La directive CCACHE_COMPILERCHECK permet d’indiquer a ccache d’utiliser le contenu du binaire GCC plutôt que sa date de modification pour identifier la version du compilateur. La configuration par défaut utilise la date de modification du fichier, qui sera forcément différente entre deux VPS utilisant pourtant la même version de GCC.

Contrairement à ce qu’affirme portage à la fin de l’installation de GCC, l’ajout de la variable FEATURES m’a été nécessaire pour qu’il utilise ccache…

Vérification

Il suffit maintenant de compiler le même paquet successivement sur deux VPS et de vérifier que ccache est bien utilisé sur le second.

Après la compilation sur le premier VPS :

web tmp # ccache -s
cache directory                     /var/tmp/ccache
cache hit                              0
cache miss                            31
called for link                        1
autoconf compile/link                 15
no input file                          4
files in cache                        62
cache size                           168 Kbytes
max cache size                       4.0 Gbytes

Après la compilation sur le second VPS (qui a du être beaucoup plus rapide) :

web tmp # ccache -s
cache directory                     /var/tmp/ccache
cache hit                             31
cache miss                            31
called for link                        5
not a C/C++ file                      64
autoconf compile/link                 45
unsupported compiler option           56
no input file                         13
files in cache                        62
cache size                           168 Kbytes
max cache size                       4.0 Gbytes

Référence

Réduire l’utilisation mémoire du MPM Worker d’Apache

Selon la documentation d’Apache, le MPM worker nécessite moins de ressources que l’historique pre-fork. Cherchant à économiser les ressources de mon petit Kimsufi, j’ai voulu essayer ce mode :

echo 'www-servers/apache threads' >> /etc/portage/package.use
echo 'APACHE2_MPMS="worker"' >> /etc/make.conf
emerge apache

Cependant, je fus assez déçu du résultat, car avec les paramètres par défaut plus de RAM était consommée qu’avec le MPM prefork (pour un faible trafic).

apache   27556  0.0  0.7  24352 15448 ?        S    00:54   0:00 /usr/sbin/apache2
apache   27557  0.0  0.7  24984 15312 ?        S    00:54   0:00 /usr/sbin/apache2
apache   27558  0.0  0.8 246632 16252 ?        Sl   00:54   0:00 /usr/sbin/apache2
apache   27561  0.0  0.8 246632 16252 ?        Sl   00:54   0:00 /usr/sbin/apache2

La solution : diminuer la taille de la pile

Après quelques recherche, je suis tombé sur ce post, où un utilisateur avait le même problème, et la solution :

Stack size is counted towards virtual memory
Default stack size is very high on Linux (8MB)
Every thread uses separate stack
=> multi threaded application will use at least number_of_threads * 8MB virtual memory!

Par défaut le MPM worker crée 25 threads par processus, utilisant chacun au moins 8 Mo pour la pile ! En réduisant la taille par défaut de la pile, on peut largement diminuer la mémoire consommée par Apache :

web apache2 # ulimit -s
8192
web apache2 # ulimit -s 256
/etc/init.d/apache2 restart

On constate alors que la mémoire virtuelle utilisée par chacun des processus apache a diminuée drastiquement :

apache   28029  0.0  0.7  24352 15444 ?        S    00:56   0:00 /usr/sbin/apache2
apache   28031  0.0  0.7  24984 15460 ?        S    00:56   0:00 /usr/sbin/apache2
apache   28032  0.0  0.8  32660 17340 ?        Sl   00:56   0:00 /usr/sbin/apache2
apache   28035  0.0  0.8  32500 17320 ?        Sl   00:56   0:00 /usr/sbin/apache2

De plus, ceci est valable pour tous les processus multi-threads, comme fail2ban ou mysqld !

Limiter la taille de la pile au boot

L’appel précédent à ulimit n’est valable que dans le shell courant.

Afin d’appliquer automatiquement cette limite pour tous les services sur Gentoo, il faut ajouter la ligne suivante dans le fichier /etc/conf.d/rc :

RC_ULIMIT="-s 256"

Remarques

J’ai ici utilisé la taille recommandée dans le post cité. J’ai tout de même une légère crainte que le passage de 8192 à 256 ne soit un peu excessif. Je verrai à l’usage s’il est nécessaire d’augmenter cette valeur.

Edit : en effet, il ne m’aura fallu que quelques jours pour détecter un problème… Impossible pour portage de télécharger quoi que soit lors d’un emerge. J’ai donc ré-augmenté un peu la taille de la pile en la passant à 512k.