Un coup de boost

ajouté le 07.08.2011 dans Documentation • par SwissTenguCommentaires (4)
Tags: nginx memcached optimisation perl python

Mon blog a eu quelques petits hoquets ces dernières heures - c'est normal.
Je me suis amusé à mettre en place diverses choses, dont memcached.

J'ai aussi pris un peu de temps pour mettre en place la "minification" de mes divers javascripts et CSS, ce qui permet d'accélérer un peu le temps de chargement, et de minimiser la taille des fichiers.

Au final, le site devrait aller un poil plus vite, modulo la durée de vie du cache (quelques heures).

Voici comment j'ai pu faire tout ceci:

memcached


J'ai commencé par vouloir employer le décorateur @beaker_cache intégré à Pylons. Mal m'en a pris, les clefs générées dans le cache sont tout bonnement imbuvables, et je n'ai pas trouvé de moyen de sortir les contenus par nginx (ce qui permet donc de passer outre la lenteur de pylons).
Il m'a donc fallu mettre en place une gestion du cache personnalisée. J'ai créé un nouveau fichier lib/cache.py:
Code: python
#-*- coding:utf-8 -*-
import memcache
class Cache(object):
  def __init__(self, url, timeout=None, prefix=None): 
    self.mc = memcache.Client([url]) 
    self.timeout = timeout or 300 
    self.prefix = prefix 

  def get(self, key): 
    return self.mc.get(key) 

  def set(self, key, value, timeout=None): 
    if self.prefix: 
      key = self.prefix + key 
    self.mc.set(key, value, timeout or self.timeout) 

  def revoke(self, key):
    self.mc.delete(key)

import decorator
import app_globals
from pylons.configuration import PylonsConfig
def cache(timeout=None):
  config = PylonsConfig()
  g = app_globals.Globals(config)
  def wrapper(func, *args, **kwargs): 
    request = args[0]._py_object.request 
    content = func(*args, **kwargs) 
    key = request.path_qs 
    g.cache.set(key, str(content.encode('utf-8')), timeout) 
    return content 
  return decorator.decorator(wrapper) 


En gros, je crée une classe "Cache" et un décorateur me permettant d'injecter mes contenus dans le cache - en l’occurrence memcached. La clef est prédictible et simple: le chemin appelé pour accéder au contrôleur, avec les arguments etc.

Ensuite j'ai dû instancier la classe dans lib/app_globals.py:
Code: python
from blogger.lib.cache import Cache
class Globals(object):
    def __init__(self, config):
        self.cache = Cache(url='127.0.0.1:11211')


Après, bin y a plus qu'à employer le décorateur dans les contrôleurs:
Code: python
from blogger.lib.cache import cache
class BlogController(BaseController):
  @cache(600)
  def post(self, id):
    # display a post, with comments
    try:
      c.post = Session.query(Content).filter(Content.id == id).one()
    except:
      redirect( url(controller='blog', action='index') )
    c.title = c.post.title
    c.comments = Session.query(Comment).filter(Comment.commentID == id).order_by(Comment.time.asc()).all()
    return render('/blog/%s/post.html' % self.theme)


Easy ;).

Vient ensuite la partie fun:
dans l'action "post", il affiche les commentaires. Et le formulaire pour en ajouter, avec mon fameux captcha. Ooops, lui, il doit être dynamique !

C'est là que le SSI (Server-Side Inclusion) intervient :).

J'ai donc créé un contrôleur appelé "ssi" de manière à savoir à quoi il sert. Dedans, y a les diverses actions utiles à regénérer le contenu réellement dynamique. Dans mes templates mako, j'ai simplement mis le code permettant à nginx de faire les appels en interne:
Code:
<!--# include file="/ssi/formular/${c.post.id}" -->


Et Voilà, le formulaire est à nouveau dynamique ! Top-moumoute quoi.

Maintenant, reste la configuration nginx. Là, je me suis un peu explosé les dents, principalement à cause de memcached, vu qu'au début je ne savais pas quelles étaient les clefs employées. Et je suis tombé sur ce post. Et j'ai vu la Lumière.
De là, tout a été rapide, et j'ai obtenu cette configuration pour nginx:
Code: perl
server {
# ..... blabla etc
  location /ssi {
    proxy_pass        http://localhost:9090/ssi;
    proxy_set_header  Host $host;
    proxy_set_header  SSL yes ;
    add_header X-Memcached "missed";
  }
  location / {
    proxy_set_header  Host $host;
    proxy_set_header  SSL yes ;
    ssi on;
    if ($request_method = POST) { 
      proxy_pass        http://localhost:9090;
    }
    set $memcached_key $uri;
    memcached_pass 127.0.0.1:11211;
    default_type text/html;
    error_page 404 = @pylons;
  }

  location @pylons {
    ssi on;
    proxy_pass        http://localhost:9090;
    proxy_set_header  Host $host;
    proxy_set_header  SSL yes ;
    add_header X-Memcached "missed";
  }
}


Que de l'amour, cette configuration ;).

Bref. Memcached est mis en place, marche... mon serveur ronronne, et tout va bien.

Mini-lui


Vient ensuite la partie "mes css/js ont des espaces vides, des retours à la ligne inutiles etc... Mais si je les vire, je pourrai plus trop les éditer". Que faire ??
Bah simple:
on va planter un peu de Perl dans la config nginx :)

Pour se faire, il faut ajouter le support Perl dans nginx - je vous laisse vous référer à la doc.
Ensuite, dans la partie http, j'ai ajouté ceci:
Code: perl
http {
# blablab...
  perl_modules perl;
  perl_require JSMinify.pm;
  perl_require CSSMinify.pm;
}


et dans mon vhost:
Code: perl
server {
  # blabla..
  location ~* \.css$ {
    root              /var/www/vhosts/blog.tengu.ch/blogger/blogger/public/;
    expires           30d;
    add_header        Cache-Control '30d, must-revalidate';
    try_files $uri @miniCSS;
  }
  location @miniCSS {
    perl CSSMinify::handler;
  }
  location ~* \.js$ {
    root              /var/www/vhosts/blog.tengu.ch/blogger/blogger/public/;
    expires           30d;
    add_header        Cache-Control '30d, must-revalidate';
    try_files $uri @miniJS;
  }
  location @miniJS {
    perl JSMinify::handler;
  }
  # blabla
}


J'vais être gentil et vous filer le code des deux minifiers - il est vraiment simple:
Code: perl
package CSSMinify;
use nginx;
use CSS::Minifier qw(minify);
sub handler {
  my $r=shift;
  my $cache_dir="/tmp"; # Cache directory where minified file will be kept
  my $cache_file=$r->uri;
  $cache_file=~s!/!_!g;
  $cache_file=join("/", $cache_dir, $cache_file);
  my $uri=$r->uri;
  my $filename=$r->filename;
  local $/=undef;
  return DECLINED unless -f $filename;

  if (! -f $cache_file) {
    open(INFILE, $filename) or die "Error reading file: $!";
    open(OUTFILE, '>' . $cache_file ) or die "Error writting file:
    $! $cache_file\n";
    minify(input => *INFILE, outfile => *OUTFILE);
    close(INFILE);
    close(OUTFILE);
  }

  $r->send_http_header('text/css');
  $r->header_out('Cache-Control', '30d, must-revalidate');
  $r->sendfile($cache_file);
  return OK;
}
1;
__END__

Code: perl
package JSMinify;
use nginx;
use JavaScript::Minifier qw(minify);
sub handler {
  my $r=shift;
  my $cache_dir="/tmp"; # Cache directory where minified file will be kept
  my $cache_file=$r->uri;
  $cache_file=~s!/!_!g;
  $cache_file=join("/", $cache_dir, $cache_file);
  my $uri=$r->uri;
  my $filename=$r->filename;
  return DECLINED unless -f $filename;

  if (! -f $cache_file) {
    open(INFILE, $filename) or die "Error reading file: $!";
    open(OUTFILE, '>' . $cache_file ) or die "Error writting file:
    $!";
    minify(input => *INFILE, outfile => *OUTFILE);
    close(INFILE);
    close(OUTFILE);
  }

  $r->header_out('Cache-Control', '30d, must-revalidate');
  $r->send_http_header('text/css');
  $r->sendfile($cache_file);
  return OK;
}
1;
__END__

(Note: je les ai mis dans /etc/perl)

Il me reste encore à intégrer la compression gzip - là j'ai eu pas mal de problèmes et ce n'est pas encore en place.
Il faut savoir que la méthode sendfile() bypass complètement les filtres gzip et charset (et peut-être encore un autre) - donc il faut faire tout cela dans le module perl, en live... et tenter de balancer le bon header, au bon moment, de la bonne manière... Pas encore gagné :(.

Bref. Avec ces 2-3 améliorations, le blog va un poil plus vite, y a moins de trucs qui transitent, mais c'est pas encore optimisé au max.

Il manque encore:
- arriver à faire passer les infos de cache aux fichiers "minimisés" (actuellement, ça semble pas marcher)
- concaténer tous les CSS et tous les JS en un seul fichier, si possible à la volée
- compresser les CSS et JS
- ajouter un peu de gestion de cache, par exemple lors que j'édite un post depuis l'admin, il devrait s'assurer qu'il est invalidé dans le cache. Actuellement, seul l'ajout de commentaires fait cela...
- voir si je peux aussi optimiser la taille d'une page, avec des filtres pour supprimer les espaces inutiles etc (HTML::Minifier doit sans doute exister :D)

Voilà... J'espère arriver à faire les points ci-dessus, particulièrement la partie gzip des css/js... Si j'y arrive, je ferai évidemment un post avec les explications et le code.

++

T.
 

Proxyfions un peu OSM

ajouté le 11.05.2010 dans Documentation • par SwissTenguCommentaires (0)
Tags: nginx osm cache tiles

Suite à mon post précédent sur OpenLayers et OSM, j'ai continué de jouer un petit peu.
Maintenant, les tiles OSM sont mises en cache sur mon serveur, ce qui permet à l'application d'être très rapide, et de surtout décharger un tout petit peu les serveurs OSM.
Pas que je pense que mon appli va les faire sauter, mais j'me dis que c'est la moindre des choses, vu que tout est gratuit.

Bref. Au début, je pensais prendre un tilecache... Puis j'ai vu qu'il me fallait mapnik et tout le reste, ce qui n'est pas trop possible actuellement (manque de ressources et de place sur le serveur).
Ensuite je suis parti sur un varnish. Et là, je me suis soudain foutu une baffe : nginx fait très bien proxy cache, tout seul comme un grand !

Donc... Un peu de lecture saine, et voici une petite config pas trop moche :

Code: perl
proxy_cache_path  /home/tilecache  levels=1:2:2   keys_zone=tilecache:1536m;

server {
  listen 94.23.198.39:80;
  server_name toto.internux.ch;

  access_log  /var/log/nginx/tiles_access.log main;
  error_log   /var/log/nginx/tiles_error.log info;

  keepalive_timeout 15;
  proxy_ignore_client_abort on;
  proxy_set_header  X-Real-IP  $remote_addr;
  proxy_set_header  X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_set_header "ENABLE_X_ACCEL_REDIRECT" "true";

  location / {
    proxy_pass        http://tile.openstreetmap.org/ ;
    proxy_cache       tilecache;
    proxy_cache_key "$scheme$host$request_uri";
    proxy_cache_valid  200 302  7d;
    proxy_cache_valid  404      1m;
  }
}


Ajouter à cela une petite modification à OpenLayers.js pour faire pointer l'url OSM non pas sur tile.openstreetmap.org mais sur toto.internux.ch... et hop. C'est tout bon !

Je suis assez content, l'application tourne vite et bien. C'est agréable à utiliser là. Vraiment :)

Allez, happy coding !

T.
 

La migration gentoo -> IIS

ajouté le 04.04.2010 dans Geek World • par SwissTenguCommentaires (0)
Tags: gentoo fake nginx

Bon, donc tout le monde l'a compris de suite, c'est faux.

Par contre, niveau obfuscation, c'est pas trop mal.

Pour permettre ce "miracle", j'ai dû employer un module spécial pour nginx. Pour faire cela avec Gentoo, il faut :
- avoir www-servers/nginx-0.8.34-r1 minimum
- avoir téléchargé le module
- avoir édité le /etc/make.conf et ajouté une ligne de ce genre :
Code: bash
NGINX_ADD_MODULES="/usr/src/nginx-modules/headers-more-nginx-module"


Une fois nginx recompilé avec le module, il suffit de mettre ceci dans la config :
Code: perl
  more_set_headers 'Server: Microsoft-IIS/7.5';
  more_set_headers 'X-AspNet-Version: 2.0.50727';
  more_set_headers 'VTag: 438152213400000000';
  more_set_headers 'X-Powered-By: ASP.NET';


Et de redémarrer nginx (attention, reload ne suffit pas !!)

Concernant les pages d'erreur, je me suis simplement permis de copier le code d'une page trouvée sur le net [simple à faire en général], et de modifier le controlleur d'erreur de mon application "blogger".

Et voilà. Bon, reste qu'un nmap donnera un booo linux, mais les bots qui cherchent des pages/dossiers genre phpmyadmin et autres merdes de ce genre risquent de relever un IIS... Inutile, sans doute, mais marrant ;).

Seul point con : netcraft.com va dire que j'ai IIS... donc fausser un peu les stats mondiale ;)

Mais j'dois pas être le seul :D

Allez, enjoy!

Tengu
 

Akemi migre à IIS

ajouté le 31.03.2010 dans News • par SwissTenguCommentaires (0)
Tags: gentoo fake nginx

J'en avais un peu ma claque de ces linux. Après tout, le code étant ouvert et accessible à tous, les failles de sécurité sont nettement plus visibles.

J'ai donc décidé de passer mon serveur à [http://www.microsoft.com/windo... Server 2008 Genuine[/url] (bah ouais, j'fais les choses en grand). Le serveur web quant à lui est passé à IIS 7.5, réputé pour sa solidité et sa stabilité.

J'ai réussi à faire la migration très facilement, grâce à l'excellente documentation Microsoft. Le downtime a été très court, ayant pu tout configurer avant, et n'ayant plus qu'à uploader le nouveau système par-dessus la gentoo d'origine. J'ai, au passage, gagné environ 3G d'espace disque. Le serveur quant à lui est plus rapide, plus stable, et me fait même le thé à la température correcte, c'est dire :)

Pour vous convaincre que non, je ne plaisante pas, vous pouvez faire cette commande dans un de vos linux de paumé :
Code: bash
cedric@gally:pts/4 ~ % curl -I akemi.internux.ch
HTTP/1.1 200 OK        
Date: Wed, 31 Mar 2010 18:54:39 GMT
Content-Type: text/html; charset=utf-8
Connection: keep-alive
Keep-Alive: timeout=20
Pragma: no-cache
Cache-Control: no-cache
Content-Length: 39792
Server: Microsoft-IIS/7.5
X-AspNet-Version: 2.0.50727
VTag: 438152213400000000
X-Powered-By: ASP.NET


Si cela ne suffit pas, tentez d'avoir une page 404 sur ce blog, par exemple :
https://akemi.internux.ch/defw...

Si vous êtes sages, je ferai un petit tuto sur comment migrer d'un système plein de failles visibles par tous à un beau système fermé comme on les aime :)

Enjoy, et vive le closed-source, gage de sécurité de par le simple fait qu'on peut pas voir les problèmes

Tengu, ravi de son IIS rien qu'à lui.

PS: et tentez même pas de perser le firewall, j'ai pensé à en mettre un excellent, recommandé par une professionnelle de la sécurité informatique.
 

nginx & Pylons

ajouté le 03.08.2008 dans Documentation • par SwissTenguCommentaires (0)
Tags: nginx pylons python

Comme dit il y a quelques jours, j'ai opté pour une solution pylons/nginx pour mon pseudo-blog.

Pylons, je ne le présente plus (quoique si, allez : framework web en python, dans le même genre que catalyst ou ror).

Nginx, quant à lui, est un serveur web russe 'achement très puissant et propre. Son principal atout est la simplicité déconcertante de la configuration. Il est de plus très orienté "proxypass" et "load balancer", ce qui le met en première ligne pour héberger du pylons, ror ou encore catalyst.

Voici donc comment faire un truc rapide, propre et simple, pour afficher votre site pylons :

Code: perl
user nginx nginx;
worker_processes 1;
error_log /var/log/nginx/error_log info;
events {
  worker_connections  8192;
  use epoll;
}

http {
  include         /etc/nginx/mime.types;
  default_type    application/octet-stream;
# snip-snip
  server {
    listen 80;
    server_name nimo.internux.ch illyasviel.internux.ch;
    access_log /var/log/nginx/nimo.access main;
    error_log /var/log/nginx/nimo.error info;
    location / {
      proxy_pass h ttp://localhost:9090;
    }
  }
# snip-snip
}


(attention ! la lib que j'emploie pour les bbcode est un peu pourrie, et ne prend pas garde à certains détails.. comme par exemple si je mets une url entre deux balises code -.-' -> j'ai dû tricher et ajouter un espace.)

Simple non? Par rapport à apache... Et encore, c'est vraiment le truc basique de la base, à ce niveau. Après, imaginez mettre non pas 1, mais 2, 3, 4 serveurs pylons, et employer nginx comme load balancer...

A vos claviers !

Liens :
http://nginx.net/
http://www.rkblog.rk.edu.pl/w/...