Optimisation des performances d'un serveur Apache à charge élevée

12

Je cherche à comprendre certains problèmes de performances du serveur que je vois avec un serveur Web (pour nous) lourdement chargé. L'environnement est le suivant:

  • Debian Lenny (tous les packages stables + patché aux mises à jour de sécurité)
  • Apache 2.2.9
  • PHP 5.2.6
  • Grande instance Amazon EC2

Le comportement que nous constatons est que le Web se sent généralement réactif, mais avec un léger retard pour commencer à traiter une demande - parfois une fraction de seconde, parfois 2 à 3 secondes dans nos heures d'utilisation maximales. La charge réelle sur le serveur est signalée comme très élevée - souvent 10.xx ou 20.xx comme indiqué par top. De plus, l'exécution d'autres choses sur le serveur pendant ces périodes (même vi) est très lente, donc la charge est définitivement là-haut. Curieusement, Apache reste très réactif, à part ce délai initial.

Nous avons configuré Apache comme suit, en utilisant prefork:

StartServers          5
MinSpareServers       5
MaxSpareServers      10
MaxClients          150
MaxRequestsPerChild   0

Et KeepAlive en tant que:

KeepAlive On
MaxKeepAliveRequests 100
KeepAliveTimeout 5

En regardant la page d'état du serveur, même en ces temps de forte charge, nous atteignons rarement le plafond du client, servant généralement entre 80 et 100 demandes et bon nombre de celles en état de conservation. Cela me dit d'exclure la lenteur de la demande initiale comme "en attente d'un gestionnaire" mais je peux me tromper.

La surveillance CloudWatch d'Amazon me dit que même lorsque notre système d'exploitation signale une charge> 15, l'utilisation de l'UC de notre instance se situe entre 75 et 80%.

Exemple de sortie de top:

top - 15:47:06 up 31 days,  1:38,  8 users,  load average: 11.46, 7.10, 6.56
Tasks: 221 total,  28 running, 193 sleeping,   0 stopped,   0 zombie
Cpu(s): 66.9%us, 22.1%sy,  0.0%ni,  2.6%id,  3.1%wa,  0.0%hi,  0.7%si,  4.5%st
Mem:   7871900k total,  7850624k used,    21276k free,    68728k buffers
Swap:        0k total,        0k used,        0k free,  3750664k cached

La majorité des processus ressemblent à:

24720 www-data  15   0  202m  26m 4412 S    9  0.3   0:02.97 apache2                                                                       
24530 www-data  15   0  212m  35m 4544 S    7  0.5   0:03.05 apache2                                                                       
24846 www-data  15   0  209m  33m 4420 S    7  0.4   0:01.03 apache2                                                                       
24083 www-data  15   0  211m  35m 4484 S    7  0.5   0:07.14 apache2                                                                       
24615 www-data  15   0  212m  35m 4404 S    7  0.5   0:02.89 apache2            

Exemple de sortie vmstaten même temps que ci-dessus:

procs -----------memory---------- ---swap-- -----io---- -system-- ----cpu----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa
 8  0      0 215084  68908 3774864    0    0   154   228    5    7 32 12 42  9
 6 21      0 198948  68936 3775740    0    0   676  2363 4022 1047 56 16  9 15
23  0      0 169460  68936 3776356    0    0   432  1372 3762  835 76 21  0  0
23  1      0 140412  68936 3776648    0    0   280     0 3157  827 70 25  0  0
20  1      0 115892  68936 3776792    0    0   188     8 2802  532 68 24  0  0
 6  1      0 133368  68936 3777780    0    0   752    71 3501  878 67 29  0  1
 0  1      0 146656  68944 3778064    0    0   308  2052 3312  850 38 17 19 24
 2  0      0 202104  68952 3778140    0    0    28    90 2617  700 44 13 33  5
 9  0      0 188960  68956 3778200    0    0     8     0 2226  475 59 17  6  2
 3  0      0 166364  68956 3778252    0    0     0    21 2288  386 65 19  1  0

Et enfin, sortie d'Apache server-status:

Server uptime: 31 days 2 hours 18 minutes 31 seconds
Total accesses: 60102946 - Total Traffic: 974.5 GB
CPU Usage: u209.62 s75.19 cu0 cs0 - .0106% CPU load
22.4 requests/sec - 380.3 kB/second - 17.0 kB/request
107 requests currently being processed, 6 idle workers

C.KKKW..KWWKKWKW.KKKCKK..KKK.KKKK.KK._WK.K.K.KKKKK.K.R.KK..C.C.K
K.C.K..WK_K..KKW_CK.WK..W.KKKWKCKCKW.W_KKKKK.KKWKKKW._KKK.CKK...
KK_KWKKKWKCKCWKK.KKKCK..........................................
................................................................

De mon expérience limitée, je tire les conclusions / questions suivantes:

  • Nous pouvons accepter beaucoup trop de KeepAlivedemandes

  • Je vois un peu de temps passé à attendre les E / S dans le vmstat, mais pas régulièrement et pas beaucoup (je pense?), Donc je ne suis pas sûr que ce soit une grande préoccupation ou non, je suis moins expérimenté avec vmstat

  • Toujours dans vmstat, je vois dans certaines itérations un certain nombre de processus en attente d'être servis, ce à quoi j'attribue le délai de chargement de page initial sur notre serveur Web, peut-être à tort

  • Nous servons un mélange de contenu statique (75% ou plus) et de contenu de script, et le contenu du script est souvent assez gourmand en processeur, il est donc important de trouver le bon équilibre entre les deux; à long terme, nous voulons déplacer la statique ailleurs pour optimiser les deux serveurs, mais notre logiciel n'est pas prêt pour cela aujourd'hui

Je suis heureux de fournir des informations supplémentaires si quelqu'un a des idées, l'autre note est qu'il s'agit d'une installation de production à haute disponibilité, donc je me méfie de faire des ajustements après ajustements, et c'est pourquoi je n'ai pas joué avec des choses comme la KeepAlivevaleur moi-même encore.

futureal
la source
+1 Grande question sanglante, bien formulée et réfléchie. J'espère que vous avez obtenu la réponse qu'elle mérite!
Dave Rix

Réponses:

7

Je commencerai par admettre que je n'ai pas grand-chose à faire avec des choses dans les nuages ​​- mais d'après mon expérience ailleurs, je dirais que cette configuration de serveur Web reflète un volume de trafic assez faible. Le fait que la file d'attente soit si grande suggère qu'il n'y a tout simplement pas assez de CPU disponible pour y faire face. Qu'y a-t-il d'autre dans la file d'attente?

Nous acceptons peut-être beaucoup trop de demandes KeepAlive

Non - keeplive améliore toujours les performances, les navigateurs modernes sont très intelligents pour savoir quand canaliser et quand exécuter des requêtes en parallèle, bien qu'un délai de 5 secondes soit encore assez élevé, et vous avez BEAUCOUP de serveurs en attente - sauf si vous '' J'ai des problèmes de latence ÉNORMES, je recommanderais de le réduire à 2-3. Cela devrait raccourcir un peu la file d'attente.

Si vous n'avez pas déjà installé mod_deflate sur le serveur Web - alors je vous recommande de le faire - et ajoutez l'ob_gzhandler () à vos scripts PHP. Vous pouvez le faire en tant que pré-ajout automatique:

if(!ob_start("ob_gzhandler")) ob_start();

(Oui, la compression utilise plus de CPU - mais vous devriez économiser le CPU dans l'ensemble en retirant les serveurs de la file d'attente plus rapidement / en gérant moins de paquets TCP - et en bonus, votre site est également plus rapide).

Je recommanderais de fixer une limite supérieure à MaxRequestsPerChild - dites quelque chose comme 500. Cela permet simplement un certain roulement des processus au cas où vous auriez une fuite de mémoire quelque part. Vos processus httpd semblent ÉNORMES - assurez-vous d'avoir supprimé tous les modules apache dont vous n'avez pas besoin et assurez-vous de fournir du contenu statique avec de bonnes informations de mise en cache.

Si vous rencontrez toujours des problèmes, le problème est probablement dans le code PHP (si vous passez à l'utilisation de fastCGI, cela devrait être évident sans pénalité de performance majeure).

mise à jour

Si le contenu statique ne varie pas beaucoup d'une page à l'autre, il peut également être utile d'expérimenter avec:

if (count($_COOKIE)) {
    header('Connection: close');
}

sur les scripts PHP aussi.

symcbean
la source
Parmi une variété de bonnes réponses, je la marque comme acceptée parce que vous avez clairement déclaré qu'il s'agissait d'un problème lié au processeur (en grande partie en raison de la mauvaise application que nous exécutons) et que c'était certainement le cas. J'ai tout redéployé sur des instances EC2 2xlarge (par rapport aux grandes) et la plupart des problèmes ont disparu, bien que de nombreuses autres caractéristiques de performances soient toujours là. Nous n'avons qu'une seule application en cours d'exécution sur ces serveurs, et c'est tout simplement moche.
futureal
4

Vous devriez envisager d'installer un proxy inverse asynchrone, car un certain nombre de processus à l'état W sont également assez élevés. Vos processus Apache semblent passer beaucoup de temps à envoyer du contenu à des clients lents sur le réseau bloqués. Nginx ou lighttpd en tant que frontend pour votre serveur Apache peut réduire considérablement un certain nombre de processus dans l'état W. Et oui, vous devez limiter un certain nombre de requêtes Keepalive. Il vaut probablement la peine d'essayer de désactiver Keepalive.

BTW, 107 processus Apache sont trop élevés pour 22 rps, j'ai pu servir 100-120 rps en utilisant seulement 5 processus Apache. Probablement, la prochaine étape consiste à profiler votre application.

Alex
la source
Oui, tout à fait d'accord pour dire que la demande est une grande partie du problème. Il a été externalisé et a depuis été soumis à de nombreux correctifs et ainsi de suite qui n'ont fait qu'empirer les choses, et un effort de refonte est en cours. J'ai essayé ce soir de désactiver KeepAlive sans aucun effet réel, et ma prochaine étape est d'essayer ce proxy inverse, probablement avec nginx basé sur tout ce que j'ai lu depuis.
futureal
Pour faire le suivi, j'ai commencé à expérimenter avec le proxy inverse et je vais probablement le déployer en production dans un avenir proche. Merci (et aux autres qui l'ont suggérée) pour l'idée, ce n'est pas quelque chose avec lequel j'avais jamais bricolé auparavant, mais je pense que cela aura un impact jusqu'à ce que nous puissions faire une refonte complète.
futureal
1

Vous avez deux lignes dans votre vmstat qui montrent que votre temps d'attente CPU est assez élevé, et autour de celles-ci, vous faites un bon nombre d'écritures (io-bo) et de changement de contexte. Je regarderais ce qui écrit des blocs et comment éliminer cette attente. Je pense que la plus grande amélioration pourrait être trouvée dans l'amélioration de votre E / S de disque. Vérifiez syslog - définissez-le pour écrire asynchrone. Assurez-vous que le cache d'écriture de votre contrôleur fonctionne (vérifiez-le - vous pourriez avoir une mauvaise batterie).

Keepalive ne cause pas votre problème de performance, il vous fait gagner du temps sur la configuration de la connexion si vous n'exécutez pas de cache en face. Vous pourriez bousculer un peu MaxSpareServers de sorte que dans un resserrement, vous n'attendez pas toutes les fourches.

des haricots
la source
Je ne connais pas suffisamment syslog pour savoir comment le configurer pour les écritures asynchrones sous Apache, bien que je vais certainement le rechercher et le rechercher. J'ai fait quelques changements ce soir concernant KeepAlive et MaxSpareServers sans aucun effet réel, je suis d'accord pour laisser plus de pièces de rechange, je l'avais manqué. Une (mauvaise) qualité de notre application est qu'elle écrit beaucoup sur les fichiers de session utilisateur (oui, les fichiers), c'est là que je commence à penser que nous souffrons. J'ai la possibilité de déplacer la gestion des sessions vers la base de données, que je vais probablement essayer ensuite.
futureal
Oui, je conviens que vos écritures de session sont la source du problème.Vous pouvez perdre les écritures de disque de session si vous utilisez des sessions php - installez memcache, et définissez session.save_handler de PHP sur memcache et session.save_path sur tcp : //127.0.0.1: 11211 (ou partout où vous configurez memcache). La journalisation d'Apache est asynchrone par défaut, mais parfois les applications Web peuvent utiliser syslog, ou syslog peut être bavard et il effectue une synchronisation pour chaque ligne. Cela ne semble pas être le problème dans votre cas, après tout. Vous pouvez préfixer les lignes d'entrée de fichier avec '-' dans syslog.conf pour omettre la synchronisation.
haricots
0

vous devriez envisager de désactiver keepalive comme un premier essai ...

avec 107 demandes traitées, je garderais MaxSpareServers plus haut que ce que vous avez défini ...

IMHO dans le nginx à long terme en tant que proxy inverse pour le contenu statique doit être pris en considération

evcz
la source
0

Première suggestion: désactiver les Keepalives. Je n'en ai jamais eu besoin que lorsque j'ai pu identifier une situation spécifique où les performances ont augmenté, mais en général, les requêtes / s ont diminué avec Keepalive activé.

Deuxième suggestion: définissez un MaxRequestsPerChild. Je fais écho à symcbean ici, cela aidera au basculement de processus en cas de fuite de mémoire. 500 est un bon point de départ.

Troisième suggestion: augmenter les MaxClients. Un calcul approximatif pour cela est (mémoire physique - mémoire utilisée par un processus non httpd) / taille de chaque processus httpd. Selon la façon dont httpd a été compilé, ce nombre est au maximum de 255. J'utilise 250 pour mes serveurs publics pour gérer Google / Yahoo / MS en explorant les systèmes.

Quatrième suggestion: augmenter les MaxSpareServers: quelque chose comme 4-5x MinSpareServers.

À moins que ces suggestions échouent, je regarderais l'équilibrage de charge avec reverse-proxy ou memcache pour DB.

Paul S
la source