feat: translate the site in french
This commit is contained in:
parent
3e98ac15b6
commit
b47b193b20
81 changed files with 1327 additions and 251 deletions
|
@ -1,10 +1,11 @@
|
|||
---
|
||||
title: Setup Nginx for Mediawiki
|
||||
date: "2010-09-15T00:00:00+02:00"
|
||||
tags:
|
||||
- Nginx
|
||||
- Mediawiki
|
||||
slug: 1-setup-nginx-for-mediawiki
|
||||
date: "2010-09-15T00:00:00+02:00"
|
||||
categories: [DevOps]
|
||||
tags:
|
||||
- mediawiki
|
||||
- nginx
|
||||
summary: >
|
||||
A simple configuration to serve Mediawiki with Nginx and FastCGI
|
||||
---
|
47
content/blog/001-setup-nginx-for-mediawiki/index.fr.md
Normal file
47
content/blog/001-setup-nginx-for-mediawiki/index.fr.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
title: Configurer Nginx pour Mediawiki
|
||||
slug: 1-configurer-nginx-pour-mediawiki
|
||||
date: "2010-09-15T00:00:00+02:00"
|
||||
categories: [DevOps]
|
||||
tags:
|
||||
- mediawiki
|
||||
- nginx
|
||||
summary: >
|
||||
Une configuration simple de Nginx pour servir Mediawiki en FastCGI
|
||||
---
|
||||
|
||||
Il y a deux semaines, j'ai migré un serveur d'Apache/mod_php vers nginx/php-fpm.
|
||||
Ce n'est qu'aujourd'hui que j'ai réussi à éliminer tous les effets secondaires.
|
||||
Le dernier en date :
|
||||
|
||||
Les fichiers statiques ne doivent pas passer par php-fpm, mais un simple test
|
||||
sur les extensions est inefficace, car les URL comme
|
||||
`http://serveur/File:nom_du_fichier.png` doivent être traitées par PHP.
|
||||
|
||||
Voici ma configuration finale, qui corrige toutes les erreurs que j'ai rencontrées :
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name server_name;
|
||||
index index.php;
|
||||
root /path/to/www/;
|
||||
|
||||
# Serve static files with a far future expiration
|
||||
# date for browser caches
|
||||
location ^~ /images/ {
|
||||
expires 1y;
|
||||
}
|
||||
location ^~ /skins/ {
|
||||
expires 1y;
|
||||
}
|
||||
|
||||
# Pass the request to php-cgi
|
||||
location / {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root/index.php;
|
||||
fastcgi_index index.php;
|
||||
include fastcgi_params;
|
||||
}
|
||||
}
|
||||
```
|
|
@ -1,10 +1,13 @@
|
|||
---
|
||||
title: Build the latest PgPool-II on Debian Etch
|
||||
date: "2010-12-14T00:00:00+01:00"
|
||||
tags: [Debian, PgPool-II]
|
||||
slug: 2-build-pgpool-on-debian
|
||||
slug: 2-build-pgpool-on-debian-etch
|
||||
categories: [DevOps]
|
||||
tags:
|
||||
- Debian
|
||||
- PgPool-II
|
||||
summary: >
|
||||
Building PgPool-II on RHEL 5.5 to avoid the "libpq is not installed or
|
||||
Building PgPool-II on Debian Etch and avoid the "libpq is not installed or
|
||||
libpq is old" error
|
||||
---
|
||||
|
35
content/blog/002-build-pgpool-on-debian/index.fr.md
Normal file
35
content/blog/002-build-pgpool-on-debian/index.fr.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
---
|
||||
title: Compiler le dernier PgPool-II sur Debian Etch
|
||||
slug: 2-compiler-pgpool-sur-debian-etch
|
||||
date: "2010-12-14T00:00:00+01:00"
|
||||
categories: [DevOps]
|
||||
tags:
|
||||
- Debian
|
||||
- PgPool-II
|
||||
summary: >
|
||||
Compilation de PgPool-II sur RHEL 5.5 pour éviter l'erreur "libpq is not
|
||||
installed or libpq is old"
|
||||
---
|
||||
|
||||
Après avoir compilé PgPool-II sur Red Hat Enterprise Linux 5.5 sans aucun
|
||||
problème, j'ai essayé de le compiler sur un nouveau Debian Etch. Seulement, je
|
||||
ne voulais pas installer PostgreSQL 9.0, mais simplement l'extraire des
|
||||
paquets binaires fournis par Entreprisedb (avec l'option `--extract-only 1`).
|
||||
Quelles que soient les options que je passais à `./configure`, cela résultait en
|
||||
la même erreur :
|
||||
|
||||
{{< highlight text >}}
|
||||
checking for PQexecPrepared in -lpq... no
|
||||
configure: error: libpq is not installed or libpq is old
|
||||
{{< /highlight >}}
|
||||
|
||||
Voici la réponse : le paquet binaire contient la libpq avec le nom
|
||||
`libcrypto.so.0.9.8` (le nom RHEL) lorsque pgpool recherche `libcrypto.so.6` sur
|
||||
Debian. La même chose s'applique à `libssl`. Donc un simple
|
||||
|
||||
{{< highlight bash >}}
|
||||
ln -s libcrypto.so.0.9.8 libcrypto.so.0.9.8
|
||||
ln -s libssl.so.0.9.8 libssl.so.6
|
||||
{{< /highlight >}}
|
||||
|
||||
avant votre `./configure` résoudra le problème !
|
|
@ -1,7 +1,11 @@
|
|||
---
|
||||
slug: 3-aptana-eclipse-and-xulrunner
|
||||
title: Aptana Studio/Eclipse and Xulrunner
|
||||
tags: [Aptana Studio, Eclipse, Xulrunner, Arch Linux]
|
||||
categories: [Tooling]
|
||||
tags:
|
||||
- Aptana Studio
|
||||
- Eclipse
|
||||
- Xulrunner
|
||||
date: "2011-12-16T00:00:00+01:00"
|
||||
summary: >
|
||||
How to solve the "Unhandled event loop exception" error in Aptana Studio and Eclipse 3.7 with Xulrunner
|
93
content/blog/003-aptana-eclipse-and-xulrunner/index.fr.md
Normal file
93
content/blog/003-aptana-eclipse-and-xulrunner/index.fr.md
Normal file
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
slug: 3-aptana-eclipse-et-xulrunner
|
||||
title: Aptana Studio/Eclipse et Xulrunner
|
||||
categories: [Outils]
|
||||
tags:
|
||||
- Aptana Studio
|
||||
- Eclipse
|
||||
- Xulrunner
|
||||
date: "2011-12-16T00:00:00+01:00"
|
||||
summary: >
|
||||
Comment résoudre l'erreur "Unhandled event loop exception" dans Aptana Studio
|
||||
et Eclipse 3.7 avec Xulrunner
|
||||
---
|
||||
|
||||
Depuis quelques mois, j'ai une erreur gênante dans Aptana Studio et Eclipse 3.7
|
||||
(les paquets autonomes, pas ceux des dépôts) chaque fois que j'ai essayé de
|
||||
faire une action git ou hg.
|
||||
|
||||
J'ai pu vivre avec jusqu'à maintenant, mais aujourd'hui, cela me dérangeait
|
||||
vraiment.
|
||||
|
||||
L'erreur est :
|
||||
|
||||
{{< highlight text >}}
|
||||
Unhandled event loop exception
|
||||
No more handles [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)]
|
||||
{{< /highlight >}}
|
||||
|
||||
Le fichier log montrait la trace suivante :
|
||||
|
||||
{{< highlight text >}}
|
||||
!ENTRY org.eclipse.ui 4 0 2011-12-16 17:17:30.825
|
||||
!MESSAGE Unhandled event loop exception
|
||||
!STACK 0
|
||||
org.eclipse.swt.SWTError: No more handles [Unknown Mozilla path (MOZILLA_FIVE_HOME not set)]
|
||||
at org.eclipse.swt.SWT.error(SWT.java:4109)
|
||||
at org.eclipse.swt.browser.Mozilla.initMozilla(Mozilla.java:1739)
|
||||
at org.eclipse.swt.browser.Mozilla.create(Mozilla.java:656)
|
||||
at org.eclipse.swt.browser.Browser.<init>(Browser.java:119)
|
||||
at com.aptana.git.ui.internal.actions.CommitDialog.createDiffArea(CommitDialog.java:237)
|
||||
at com.aptana.git.ui.internal.actions.CommitDialog.createDialogArea(CommitDialog.java:158)
|
||||
[...]
|
||||
at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:620)
|
||||
at org.eclipse.equinox.launcher.Main.basicRun(Main.java:575)
|
||||
at org.eclipse.equinox.launcher.Main.run(Main.java:1408)
|
||||
at org.eclipse.equinox.launcher.Main.main(Main.java:1384)
|
||||
{{< /highlight >}}
|
||||
|
||||
Pour faire court, après avoir lu
|
||||
[beaucoup](https://bugs.archlinux.org/task/5149)
|
||||
[de](https://bugs.archlinux.org/task/27130)
|
||||
[posts](https://github.com/eclipse-color-theme/eclipse-color-theme/issues/50)
|
||||
[à ce sujet](https://bbs.archlinux.org/viewtopic.php?id=129982)
|
||||
[sur](http://forums.gentoo.org/viewtopic-t-827838-view-previous.html?sid=546c5717e2167c45d9b02f9f20ab36f4)
|
||||
[ce](http://stackoverflow.com/questions/1017945/problem-with-aptana-studio-xulrunner-8-1)
|
||||
[problème](http://www.eclipse.org/swt/faq.php#gtk64), il semblait qu'il
|
||||
suffisait de donner le chemin vers Xulrunner à Aptana.
|
||||
|
||||
Sur mon Arch Linux, c'était :
|
||||
|
||||
{{< highlight bash >}}
|
||||
export MOZILLA_FIVE_HOME=/usr/lib/xulrunner-8.0
|
||||
{{< /highlight >}}
|
||||
|
||||
En essayant de démarrer Aptana Studio, j'ai eu une nouvelle erreur. Elle disait
|
||||
simplement :
|
||||
|
||||
{{< highlight text >}}
|
||||
XPCOM error -2147467261
|
||||
{{< /highlight >}}
|
||||
|
||||
La solution est qu'Aptana Studio ne peut pas fonctionner avec la version de
|
||||
Xulrunner dans les dépôts Arch Linux car elle est trop récente.
|
||||
|
||||
Pour résoudre ce problème, j'ai dû installer xulrunner 1.9.2 depuis AUR :
|
||||
|
||||
{{< highlight bash >}}
|
||||
yaourt -S xulrunner192
|
||||
{{< /highlight >}}
|
||||
|
||||
Le PKGBUILD était cassé ce matin et se terminait par une erreur 404 lors de la
|
||||
récupération des sources. Si vous avez le même problème, voici un PKGBUILD mis à
|
||||
jour : [PKGBUILD mis à jour](https://gist.github.com/1486851).
|
||||
|
||||
Enfin, j'ai mis :
|
||||
|
||||
{{< highlight bash >}}
|
||||
-Dorg.eclipse.swt.browser.XULRunnerPath=/usr/lib/xulrunner-1.9.2
|
||||
{{< /highlight >}}
|
||||
|
||||
à la fin du fichier `AptanaStudio3.ini` dans le dossier d'Aptana Studio. Pour le
|
||||
paquet dans les dépôts Arch Linux, ce fichier est
|
||||
`/usr/share/aptana/AptanaStudio3.ini`.
|
|
@ -1,8 +1,11 @@
|
|||
---
|
||||
tags: [Python, Buzhug, Database, Locks]
|
||||
slug: 4-locking-buzhug
|
||||
title: Locking Buzhug
|
||||
date: "2012-02-07T00:00:00+01:00"
|
||||
categories: [Dev]
|
||||
tags:
|
||||
- Python
|
||||
- Buzhug
|
||||
summary: >
|
||||
How to implement a cross-process, system-wide lock for Buzhug
|
||||
---
|
||||
|
@ -58,7 +61,7 @@ As it only has to work on Linux, the
|
|||
Vmfarms](http://blog.vmfarms.com/2011/03/cross-process-locking-and.html) fits
|
||||
perfectly. Here is a version slightly modified to make it a context manager :
|
||||
|
||||
{{< highlight python >}}
|
||||
```python
|
||||
import fcntl
|
||||
|
||||
class PsLock:
|
||||
|
@ -87,83 +90,83 @@ class PsLock:
|
|||
|
||||
def __enter__(self):
|
||||
self.acquire()
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
The second step is to define a new class that inheritates from `buzhug.Base`
|
||||
that uses `PsLock` (inspired by `TS_Base`):
|
||||
|
||||
{{< highlight python >}}
|
||||
```python
|
||||
import buzhug
|
||||
|
||||
_lock = PsLock("/tmp/buzhug.lck")
|
||||
|
||||
class PS_Base(buzhug.Base):
|
||||
|
||||
def create(self,*args,**kw):
|
||||
def create(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.create(self,*args,**kw)
|
||||
res = buzhug.Base.create(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def open(self,*args,**kw):
|
||||
def open(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.open(self,*args,**kw)
|
||||
res = buzhug.Base.open(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def close(self,*args,**kw):
|
||||
def close(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.close(self,*args,**kw)
|
||||
res = buzhug.Base.close(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def destroy(self,*args,**kw):
|
||||
def destroy(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.destroy(self,*args,**kw)
|
||||
res = buzhug.Base.destroy(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def set_default(self,*args,**kw):
|
||||
def set_default(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.set_default(self,*args,**kw)
|
||||
res = buzhug.Base.set_default(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def insert(self,*args,**kw):
|
||||
def insert(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.insert(self,*args,**kw)
|
||||
res = buzhug.Base.insert(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def update(self,*args,**kw):
|
||||
def update(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.update(self,*args,**kw)
|
||||
res = buzhug.Base.update(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def delete(self,*args,**kw):
|
||||
def delete(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.delete(self,*args,**kw)
|
||||
res = buzhug.Base.delete(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def cleanup(self,*args,**kw):
|
||||
def cleanup(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.cleanup(self,*args,**kw)
|
||||
res = buzhug.Base.cleanup(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def commit(self,*args,**kw):
|
||||
def commit(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.commit(self,*args,**kw)
|
||||
res = buzhug.Base.commit(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def add_field(self,*args,**kw):
|
||||
def add_field(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.add_field(self,*args,**kw)
|
||||
res = buzhug.Base.add_field(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def drop_field(self,*args,**kw):
|
||||
def drop_field(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.drop_field(self,*args,**kw)
|
||||
res = buzhug.Base.drop_field(self, *args, **kw)
|
||||
return res
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
Now I just use
|
||||
|
||||
{{< highlight python >}}
|
||||
```python
|
||||
database = PS_Base( ... )
|
||||
{{< /highlight >}}
|
||||
```
|
||||
|
||||
And all the errors have vanished.
|
177
content/blog/004-locking-buzhug/index.fr.md
Normal file
177
content/blog/004-locking-buzhug/index.fr.md
Normal file
|
@ -0,0 +1,177 @@
|
|||
---
|
||||
slug: 4-verrouiller-buzhug
|
||||
title: Verrouiller de Buzhug
|
||||
date: "2012-02-07T00:00:00+01:00"
|
||||
categories: [Dev]
|
||||
tags:
|
||||
- Python
|
||||
- Buzhug
|
||||
summary: >
|
||||
Comment implémenter un verrou inter-processus et système pour Buzhug
|
||||
---
|
||||
|
||||
J'ai récemment décidé d'utiliser [Buzhug] pour un projet. À ce que je puisse en
|
||||
juger, il s'est avéré efficace, rapide, facile à utiliser et à maintenir.
|
||||
Cependant, j'ai rencontré quelques problèmes.
|
||||
|
||||
[Buzhug]: http://buzhug.sourceforge.net
|
||||
|
||||
## Les solutions simples sont souvent les meilleures
|
||||
|
||||
J'en suis venu à utiliser Buzhug pour les raisons suivantes :
|
||||
|
||||
- J'avais besoin d'une seule table
|
||||
- Je ne voulais pas ajouter de dépendances supplémentaires au projet
|
||||
- La taille de la table serait en moyenne de 5K entrées (sans dépasser
|
||||
10k entrées en pic)
|
||||
|
||||
Et une raison supplémentaire (personnelle) :
|
||||
|
||||
- Je ne voulais pas me soucier de SQL. Vraiment pas. Pas question !
|
||||
|
||||
Cela ne me laissait qu'une option : une base de données embarquée en pur Python.
|
||||
|
||||
Après avoir considéré quelques bibliothèques, j'ai été séduit par la manière
|
||||
dont l'interface de Buzhug est proche de la manipulation d'objets Python. Et les
|
||||
benchmarks semblaient montrer qu'il est assez performant pour ce projet.
|
||||
|
||||
Après un rapide prototypage (1 jour), le choix était fait.
|
||||
|
||||
Puis vinrent quelques semaines de développement et les premiers tests de
|
||||
charge...
|
||||
|
||||
## Et la réalité est revenue rapidement
|
||||
|
||||
Plusieurs fois par jour, l'application soutenue par cette base de données est
|
||||
intensément utilisée :
|
||||
|
||||
- Elle peut être exécutée jusqu'à 50 fois simultanément dans des processus
|
||||
Python séparés
|
||||
- Chaque exécution effectue une opération de lecture et une opération
|
||||
d'écriture/suppression
|
||||
|
||||
Cela provoque une condition de course sur les fichiers utilisés pour stocker les
|
||||
données, et les écritures concurrentes corrompent la base de données.
|
||||
|
||||
L'utilisation de `buzhug.TS_Base` au lieu de `buzhug.Base` n'a rien résolu, car
|
||||
le problème n'est pas lié aux threads, mais aux processus. Ce dont j'ai besoin
|
||||
est un verrou inter-processus.
|
||||
|
||||
## Voici la solution
|
||||
|
||||
La première étape a été de trouver comment implémenter un verrou inter-processus
|
||||
et système.
|
||||
|
||||
Comme cela doit seulement fonctionner sur Linux, la
|
||||
[classe Lock donnée par Chris de
|
||||
Vmfarms](http://blog.vmfarms.com/2011/03/cross-process-locking-and.html) convient
|
||||
parfaitement. Voici une version légèrement modifiée pour en faire un gestionnaire de contexte :
|
||||
|
||||
```python
|
||||
import fcntl
|
||||
|
||||
class PsLock:
|
||||
"""
|
||||
Adapté de :
|
||||
http://blog.vmfarms.com/2011/03/cross-process-locking-and.html
|
||||
"""
|
||||
def __init__(self, filename):
|
||||
self.filename = filename
|
||||
self.handle = open(filename, 'w')
|
||||
|
||||
# Faites un OR bit à bit avec fcntl.LOCK_NB si vous avez besoin d'un verrou non bloquant
|
||||
def acquire(self):
|
||||
fcntl.flock(self.handle, fcntl.LOCK_EX)
|
||||
|
||||
def release(self):
|
||||
fcntl.flock(self.handle, fcntl.LOCK_UN)
|
||||
|
||||
def __del__(self):
|
||||
self.handle.close()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if exc_type is None:
|
||||
pass
|
||||
self.release()
|
||||
|
||||
def __enter__(self):
|
||||
self.acquire()
|
||||
```
|
||||
|
||||
La deuxième étape consiste à définir une nouvelle classe qui hérite de
|
||||
`buzhug.Base` et qui utilise `PsLock` (inspiré de `TS_Base`) :
|
||||
|
||||
```python
|
||||
import buzhug
|
||||
|
||||
_lock = PsLock("/tmp/buzhug.lck")
|
||||
|
||||
class PS_Base(buzhug.Base):
|
||||
def create(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.create(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def open(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.open(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def close(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.close(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def destroy(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.destroy(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def set_default(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.set_default(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def insert(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.insert(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def update(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.update(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def delete(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.delete(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def cleanup(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.cleanup(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def commit(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.commit(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def add_field(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.add_field(self, *args, **kw)
|
||||
return res
|
||||
|
||||
def drop_field(self, *args, **kw):
|
||||
with _lock:
|
||||
res = buzhug.Base.drop_field(self, *args, **kw)
|
||||
return res
|
||||
```
|
||||
|
||||
Maintenant, j'utilise simplement
|
||||
|
||||
```python
|
||||
database = PS_Base( ... )
|
||||
```
|
||||
|
||||
Et toutes les erreurs ont disparu.
|
|
@ -1,8 +1,12 @@
|
|||
---
|
||||
tags: [Sublime Text 2]
|
||||
slug: 5-automatically-open-sublime-text-projects-in-a-directory
|
||||
title: Automatically open Sublime Text projects in a directory
|
||||
slug: 5-automatically-open-sublime-text-projects-in-a-directory
|
||||
date: "2013-05-15T00:00:00+02:00"
|
||||
categories:
|
||||
- Tooling
|
||||
tags:
|
||||
- Sublime Text
|
||||
- Bash
|
||||
summary: >
|
||||
How to automatically open Sublimetext with a file, a project or the current
|
||||
directory according to the context.
|
||||
|
@ -17,19 +21,19 @@ It ends up with one of the following commands :
|
|||
- `subl .`
|
||||
- `subl my-project.sublime-project`
|
||||
|
||||
Here is the snippet I added to my .bashrc file to have the `subl`
|
||||
Here is the snippet I added to my `.bashrc` file to have the `subl`
|
||||
command automatically "guess" what I want. It does the following:
|
||||
|
||||
- If a path is given (subl "my/file.txt"), it opens the file.
|
||||
- If nothing is given and a .sublime-project file exists in the current
|
||||
- If a path is given (`subl "my/file.txt"`), it opens the file.
|
||||
- If nothing is given and a `.sublime-project` file exists in the current
|
||||
directory, it opens it
|
||||
- If nothing is given and no .sublime-project file has been found, it
|
||||
- If nothing is given and no `.sublime-project` file has been found, it
|
||||
opens the folder.
|
||||
|
||||
{{< highlight bash >}}
|
||||
```bash
|
||||
function project_aware_subl {
|
||||
project_file=$(ls *.sublime-project 2>/dev/null | head -n 1)
|
||||
subl ${*:-${project_file:-.}}
|
||||
}
|
||||
alias subl="project_aware_subl"
|
||||
{{< /highlight >}}
|
||||
```
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
title: Ouvrir automatiquement les projets Sublime Text dans un répertoire
|
||||
slug: 5-ouvrir-automatiquement-les-projets-sublime-text-dans-un-repertoire
|
||||
date: "2013-05-15T00:00:00+02:00"
|
||||
categories:
|
||||
- Outils
|
||||
tags:
|
||||
- Sublime Text
|
||||
- Bash
|
||||
summary: >
|
||||
Comment ouvrir automatiquement Sublime Text avec un fichier, un projet ou le
|
||||
répertoire courant selon le contexte.
|
||||
---
|
||||
|
||||
J'ai l'habitude de lancer Sublime Text 2 depuis la ligne de commande pour
|
||||
travailler, selon le cas, sur le contenu d'un répertoire ou sur un projet
|
||||
(matérialisé par un fichier `*.sublime-project`).
|
||||
|
||||
J'utilise l'une des commandes suivantes :
|
||||
|
||||
- `subl .`
|
||||
- `subl mon-projet.sublime-project`
|
||||
|
||||
Voici la fonction que j'ai ajoutée à mon fichier `.bashrc` pour que la commande
|
||||
`subl` "devine" automatiquement ce que je veux. Il fait ce qui suit :
|
||||
|
||||
- Si un chemin est donné (`subl "mon/fichier.txt"`), il ouvre le fichier.
|
||||
- Si rien n'est donné et qu'un fichier `.sublime-project` existe dans le
|
||||
répertoire courant, il l'ouvre.
|
||||
- Si rien n'est donné et qu'aucun fichier `.sublime-project` n'a été trouvé, il
|
||||
ouvre le dossier.
|
||||
|
||||
```bash
|
||||
function project_aware_subl {
|
||||
project_file=$(ls *.sublime-project 2>/dev/null | head -n 1)
|
||||
subl ${*:-\${project_file:-.}}
|
||||
}
|
||||
alias subl="project_aware_subl"
|
||||
```
|
|
@ -2,8 +2,13 @@
|
|||
title: Discourse without Docker
|
||||
slug: 6-discourse-without-docker
|
||||
date: "2016-06-27T00:00:00+02:00"
|
||||
tags: [discourse, docker]
|
||||
summary: Detailed instructions on how to install Discourse and plugins without Docker.
|
||||
categories:
|
||||
- DevOps
|
||||
tags:
|
||||
- discourse
|
||||
- docker
|
||||
summary: >
|
||||
Detailed instructions on how to install Discourse and plugins without Docker.
|
||||
---
|
||||
|
||||
{{< warning >}}
|
||||
|
@ -277,8 +282,7 @@ WantedBy=multi-user.target
|
|||
### Discourse
|
||||
|
||||
For Discourse, just create the service unit for Puma. Create the file
|
||||
`/etc/systemd/system/discourse.service` with the
|
||||
following content:
|
||||
`/etc/systemd/system/discourse.service` with the following content:
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
|
@ -325,13 +329,13 @@ Restart Discourse:
|
|||
systemctl restart discourse
|
||||
```
|
||||
|
||||
What can go wrong? If if I do not give any solution here, it is always
|
||||
What can go wrong? If I do not give any solution here, it is always
|
||||
recoverable (hence the backups!).
|
||||
|
||||
- The database migration failed (restore the database with your backup,
|
||||
fix the problem and try again!)
|
||||
- The plugins are not compatible with the latest version (rollback to
|
||||
the previous working solution and wit for them to be compatible)
|
||||
the previous working version and wait for them to be compatible)
|
||||
|
||||
## Plugins
|
||||
|
402
content/blog/006-discourse-without-docker/index.fr.md
Normal file
402
content/blog/006-discourse-without-docker/index.fr.md
Normal file
|
@ -0,0 +1,402 @@
|
|||
---
|
||||
title: Discourse sans Docker
|
||||
slug: 6-discourse-sans-docker
|
||||
date: "2016-06-27T00:00:00+02:00"
|
||||
categories:
|
||||
- DevOps
|
||||
tags:
|
||||
- discourse
|
||||
- docker
|
||||
summary: >
|
||||
Instructions détaillées sur la façon d'installer Discourse et ses plugins sans
|
||||
Docker.
|
||||
---
|
||||
|
||||
{{< warning >}}
|
||||
La seule méthode officielle est [avec Docker]. Vous pourriez ne pas obtenir de
|
||||
support de Discourse en suivant cette méthode.
|
||||
[avec Docker]: http://blog.discourse.org/2014/04/install-discourse-in-under-30-minutes/
|
||||
{{< /warning >}}
|
||||
|
||||
L'équipe derrière [Discourse] a choisi de ne publier que des images Docker de
|
||||
leur logiciel. La raison derrière cela est : il est plus facile de ne supporter
|
||||
qu'une seule configuration. Je ne discuterai pas de cela. C'est leur choix.
|
||||
|
||||
Cependant, je n'aime pas utiliser Docker pour déployer des applications en
|
||||
production. Vraiment pas. Si vous êtes comme moi, voici les étapes que
|
||||
j'ai utilisées pour l'installer et le configurer.
|
||||
|
||||
J'utilise des serveurs Debian en production, donc les étapes ci-dessous sont
|
||||
toutes orientées Debian.
|
||||
|
||||
{{< note >}}
|
||||
Ceci n'est pas destiné à être un guide complet. Beaucoup de commandes et
|
||||
fichiers de configuration pourraient avoir besoin d'être adaptés à votre
|
||||
environnement.
|
||||
|
||||
Il ne traite même pas de sujets importants en production tels que
|
||||
la sécurité. Cela est laissé comme exercice au lecteur.
|
||||
{{< /note >}}
|
||||
|
||||
## Installation
|
||||
|
||||
Discourse est une application Rails. Elle peut être installée comme
|
||||
n'importe quelle autre application Rails :
|
||||
|
||||
Tout d'abord, Discourse utilise Redis et PostgreSQL (ou du moins,
|
||||
je préfère utiliser Postgres). J'utilise également Nginx comme proxy pour
|
||||
l'application. Installez les dépendances externes :
|
||||
|
||||
```sh
|
||||
# Ajoutez le dépôt pour Redis
|
||||
echo "deb http://packages.dotdeb.org jessie all" > /etc/apt/sources.list.d/dotdeb.list
|
||||
wget https://www.dotdeb.org/dotdeb.gpg -O - | apt-key add -
|
||||
|
||||
# Ajoutez le dépôt pour PostgreSQL :
|
||||
echo "deb http://apt.postgresql.org/pub/repos/apt/ jessie-pgdg main" > /etc/apt/sources.list.d/postgresql.list
|
||||
wget -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
||||
|
||||
apt-get update
|
||||
apt-get install postgresql-9.5 redis-server nginx
|
||||
```
|
||||
|
||||
Ensuite, créez une base de données pour l'application. Entrez dans l'interface
|
||||
de commande de postgres :
|
||||
|
||||
```sh
|
||||
su - postgres -c psql
|
||||
```
|
||||
|
||||
et entrez les commandes suivantes :
|
||||
|
||||
```sql
|
||||
CREATE DATABASE discourse;
|
||||
CREATE USER discourse;
|
||||
ALTER USER discourse WITH ENCRYPTED PASSWORD 'password';
|
||||
ALTER DATABASE discourse OWNER TO discourse;
|
||||
\connect discourse
|
||||
CREATE EXTENSION hstore;
|
||||
CREATE EXTENSION pg_trgm;
|
||||
```
|
||||
|
||||
Ensuite, vous pouvez cloner le code de Discourse :
|
||||
|
||||
```sh
|
||||
git clone https://github.com/discourse/discourse.git /chemin/vers/discourse
|
||||
#
|
||||
# Optionnellement, basculez sur une étiquette spécifique
|
||||
cd /chemin/vers/discourse
|
||||
git checkout v1.5.3
|
||||
```
|
||||
|
||||
Ensuite, allez dans le répertoire principal de l'application, et configurez-la
|
||||
comme n'importe quelle application Rails :
|
||||
|
||||
```bash
|
||||
# Optionnellement, configurez rvm avec ruby 1.9.3 minimum (j'utilise 2.3.0)
|
||||
rvm install 2.3.0
|
||||
rvm use 2.3.0
|
||||
|
||||
# installez les dépendances
|
||||
cd /chemin/vers/discourse
|
||||
RAILS_ENV=production bundle install
|
||||
```
|
||||
|
||||
Il est temps de configurer l'application.
|
||||
|
||||
Ici, Discourse a une petite particularité : la configuration de production se
|
||||
trouve dans le fichier `./config/discourse.conf`.
|
||||
|
||||
Créez ce fichier :
|
||||
|
||||
```bash
|
||||
cp config/discourse_defaults.conf config/discourse.conf
|
||||
```
|
||||
|
||||
Et modifiez-le avec votre configuration. Les principales zones d'intérêt sont
|
||||
la configuration pour la base de données et pour le serveur de messagerie :
|
||||
|
||||
```ini
|
||||
# adresse hôte pour le serveur de base de données
|
||||
# Ceci est défini à vide pour qu'il essaie d'utiliser les sockets en premier
|
||||
db_host = localhost
|
||||
|
||||
# port du serveur de base de données, pas besoin de le définir
|
||||
db_port = 5432
|
||||
|
||||
# nom de la base de données exécutant discourse
|
||||
db_name = discourse
|
||||
|
||||
# nom d'utilisateur accédant à la base de données
|
||||
db_username = discourse
|
||||
|
||||
# mot de passe utilisé pour accéder à la base de données
|
||||
db_password = password
|
||||
```
|
||||
|
||||
et pour le serveur SMTP (dans cet exemple, nous utilisons Gmail) :
|
||||
|
||||
```ini
|
||||
# adresse du serveur smtp utilisé pour envoyer des emails
|
||||
smtp_address = smtp.gmail.com
|
||||
|
||||
# port du serveur smtp utilisé pour envoyer des emails
|
||||
smtp_port = 587
|
||||
|
||||
# domaine passé au serveur smtp
|
||||
smtp_domain = gmail.com
|
||||
|
||||
# nom d'utilisateur pour le serveur smtp
|
||||
smtp_user_name = votre-adresse@gmail.com
|
||||
|
||||
# mot de passe pour le serveur smtp
|
||||
smtp_password = password
|
||||
|
||||
# mécanisme d'authentification smtp
|
||||
smtp_authentication = plain
|
||||
|
||||
# activer le chiffrement TLS pour les connexions smtp
|
||||
smtp_enable_start_tls = true
|
||||
```
|
||||
|
||||
Maintenant, nous pouvons préparer Discourse pour la production :
|
||||
|
||||
```bash
|
||||
RAILS_ENV=production bundle exec rake db:migrate
|
||||
RAILS_ENV=production bundle exec rake assets:precompile
|
||||
```
|
||||
|
||||
Il est temps de démarrer l'application. J'utilise généralement Puma pour
|
||||
déployer les applications Rails.
|
||||
|
||||
Créez le fichier `config/puma.rb` dans le répertoire de Discourse. Le contenu
|
||||
suivant devrait suffire (pour plus d'informations, voir
|
||||
[la documentation de Puma]) :
|
||||
|
||||
```ruby
|
||||
#!/usr/bin/env puma
|
||||
|
||||
application_path = '/home/discuss.waarp.org/discourse'
|
||||
directory application_path
|
||||
environment 'production'
|
||||
daemonize false
|
||||
pidfile "#{application_path}/tmp/pids/puma.pid"
|
||||
state_path "#{application_path}/tmp/pids/puma.state"
|
||||
bind "unix://#{application_path}/tmp/sockets/puma.socket"
|
||||
```
|
||||
|
||||
À partir de là, l'application peut être exécutée avec la commande suivante :
|
||||
|
||||
```bash
|
||||
bundle exec puma -C config/puma.rb
|
||||
```
|
||||
|
||||
Enfin, configurez Nginx pour transférer les requêtes à Discourse. Créez le
|
||||
fichier `/etc/nginx/conf.d/discourse.conf` avec le contenu suivant :
|
||||
|
||||
```nginx
|
||||
upstream discourse {
|
||||
server unix:/chemin/vers/discourse/tmp/sockets/puma.socket;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name example.com;
|
||||
|
||||
location / {
|
||||
try_files $uri @proxy;
|
||||
}
|
||||
|
||||
location @proxy {
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_pass http://discourse;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Votre forum avec Discourse est configuré !
|
||||
|
||||
## Gestion des services
|
||||
|
||||
Selon vos habitudes de travail, vous pouvez ajouter des unités systemd pour
|
||||
exécuter Discourse.
|
||||
|
||||
Il nécessite au moins deux service :
|
||||
|
||||
1. Sidekiq, qui est utilisé pour traiter les tâches de fond asynchrones
|
||||
2. Rails, pour Discourse lui-même.
|
||||
|
||||
Avec les services configurés, les services peuvent être démarrés/arrêtés/activés
|
||||
avec les commandes `systemctl`.
|
||||
|
||||
Mais avant cela, si vous utilisez RVM, vous devez créer un wrapper pour
|
||||
l'environnement (ruby local, et gemset optionnel) utilisé par Discourse :
|
||||
|
||||
```bash
|
||||
rvm wrapper 2.3.0 systemd bundle
|
||||
```
|
||||
|
||||
Cela crée un exécutable dans `$rvm_bin_path` que vous pouvez appeler
|
||||
à la place de bundle, et qui chargera automatiquement le bon environnement.
|
||||
|
||||
### Sidekiq
|
||||
|
||||
Tout d'abord, créez une configuration pour Sidekiq. Créez le fichier
|
||||
`config/sidekiq.yml` dans votre projet Discourse avec le contenu
|
||||
suivant (pour plus d'informations, voir [la documentation de Sidekiq]) :
|
||||
|
||||
```yaml
|
||||
---
|
||||
:concurrency: 5
|
||||
:pidfile: tmp/pids/sidekiq.pid
|
||||
staging:
|
||||
:concurrency: 10
|
||||
production:
|
||||
:concurrency: 20
|
||||
:queues:
|
||||
- default
|
||||
- critical
|
||||
- low
|
||||
```
|
||||
|
||||
Ensuite, créez l'unité de service pour Sidekiq. Créez le fichier
|
||||
`/etc/systemd/system/discourse-sidekiq.service` avec le
|
||||
contenu suivant :
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=service sidekiq de discourse
|
||||
After=multi-user.target
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/chemin/vers/discourse
|
||||
Environment=RAILS_ENV=production
|
||||
ExecStart=/chemin/vers/rvm/.rvm/bin/systemd_bundle exec sidekiq -C config/sidekiq.yml
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### Discourse
|
||||
|
||||
Pour Discourse, créez simplement l'unité de service pour Puma. Créez le fichier
|
||||
`/etc/systemd/system/discourse.service` avec le contenu suivant :
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=service discourse
|
||||
After=discourse-sidekiq.service
|
||||
Requires=discourse-sidekiq.service
|
||||
|
||||
[Service]
|
||||
WorkingDirectory=/chemin/vers/discourse
|
||||
Environment=RAILS_ENV=production
|
||||
ExecStart=/chemin/vers/rvm/.rvm/bin/systemd_bundle exec puma -C config/puma.rb
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
## Mises à jour
|
||||
|
||||
Les mises à jour sont encore plus faciles :
|
||||
|
||||
Lisez d'abord les notes de version.
|
||||
Faites ensuite des sauvegardes du code et de la base de données.
|
||||
Maintenant, vous pouvez basculer vers la nouvelle version :
|
||||
|
||||
```bash
|
||||
cd /chemin/vers/discourse
|
||||
git checkout vX.X.X
|
||||
```
|
||||
|
||||
Installez les nouvelles dépendances, exécutez les migrations et reconstruisez les
|
||||
assets :
|
||||
|
||||
```bash
|
||||
RAILS_ENV=production bundle install
|
||||
RAILS_ENV=production bundle exec rake db:migrate
|
||||
RAILS_ENV=production bundle exec rake assets:precompile
|
||||
```
|
||||
|
||||
Redémarrez Discourse :
|
||||
|
||||
```bash
|
||||
systemctl restart discourse
|
||||
```
|
||||
|
||||
Que peut-il mal se passer ? Si je ne donne aucune solution ici, c'est toujours
|
||||
récupérable (d'où les sauvegardes !).
|
||||
|
||||
- La migration de la base de données a échoué (restaurez la base de données avec
|
||||
votre sauvegarde, corrigez le problème et réessayez !)
|
||||
- Les plugins ne sont pas compatibles avec la dernière version (revenez à
|
||||
la solution précédente fonctionnelle et attendez qu'ils soient compatibles)
|
||||
|
||||
## Plugins
|
||||
|
||||
Les plugins Discourse peuvent être gérés de la même manière.
|
||||
|
||||
### Installation de plugins
|
||||
|
||||
Installez le plugin avec l'URL de son dépôt :
|
||||
|
||||
```bash
|
||||
cd /chemin/vers/discourse
|
||||
RAILS_ENV=production bundle exec rake plugin:install[URL]
|
||||
```
|
||||
|
||||
Installez les nouvelles dépendances, exécutez les migrations et reconstruisez les
|
||||
assets :
|
||||
|
||||
```bash
|
||||
RAILS_ENV=production bundle install
|
||||
RAILS_ENV=production bundle exec rake db:migrate
|
||||
RAILS_ENV=production bundle exec rake assets:precompile
|
||||
```
|
||||
|
||||
Redémarrez Discourse :
|
||||
|
||||
```bash
|
||||
systemctl restart discourse
|
||||
```
|
||||
|
||||
### Mise à jour
|
||||
|
||||
Pour mettre à jour un plugin spécifique, utilisez la commande suivante :
|
||||
|
||||
```bash
|
||||
RAILS_ENV=production bundle exec rake plugin:update[ID]
|
||||
```
|
||||
|
||||
Vous pouvez également mettre à jour tous les plugins en une seule fois avec la commande :
|
||||
|
||||
```bash
|
||||
RAILS_ENV=production bundle exec rake plugin:update_all
|
||||
```
|
||||
|
||||
Ensuite, installez les nouvelles dépendances, exécutez les migrations et reconstruisez les
|
||||
assets :
|
||||
|
||||
```bash
|
||||
RAILS_ENV=production bundle install
|
||||
RAILS_ENV=production bundle exec rake db:migrate
|
||||
RAILS_ENV=production bundle exec rake assets:precompile
|
||||
```
|
||||
|
||||
et redémarrez Discourse :
|
||||
|
||||
```bash
|
||||
systemctl restart discourse
|
||||
```
|
||||
|
||||
[Discourse]: http://www.discourse.org/
|
||||
[Documentation de Sidekiq]: https://github.com/mperham/sidekiq/wiki/Advanced-Options
|
||||
[Documentation de Puma]: https://github.com/puma/puma
|
|
@ -2,7 +2,11 @@
|
|||
title: 'Prepare for the Next Internet Outage'
|
||||
slug: '7-prepare-for-the-next-internet-outage'
|
||||
date: '2025-06-14T04:05:48+02:00'
|
||||
tags: [architecture, cloud]
|
||||
categories:
|
||||
- DevOps
|
||||
tags:
|
||||
- architecture,
|
||||
- cloud
|
||||
summary: >
|
||||
A reflection on recent internet outages and my takeways to build more
|
||||
resilient web services.
|
||||
|
@ -59,7 +63,7 @@ least one of these vendors.
|
|||
|
||||
The cloud and API world we live in is great. It allows us to build fast, iterate
|
||||
quickly, test things and improve our solutions. You need authentication, use
|
||||
Subabase or Auth0. Online payment? There is Stripe or Paypal. Transactional
|
||||
Supabase or Auth0. Online payment? There is Stripe or Paypal. Transactional
|
||||
emails? Sendgrid and MailChimp. Search? Algolia. The list can be long, but now,
|
||||
you can work on creating value.
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
---
|
||||
title: Préparez-vous pour la prochaine panne d'Internet
|
||||
slug: '7-preparez-vous-pour-la-prochaine-panne-d-internet'
|
||||
date: '2025-06-14T04:05:48+02:00'
|
||||
categories:
|
||||
- DevOps
|
||||
tags:
|
||||
- architecture
|
||||
- cloud
|
||||
summary: >
|
||||
Une réflexion sur les récentes pannes d'Internet et les enseignements que j'en
|
||||
tire pour construire des services web plus résilients.
|
||||
---
|
||||
|
||||
Jeudi dernier, [Internet est tombé en panne](https://mashable.com/article/google-down-cloudflare-twitch-character-ai-internet-outage).
|
||||
Encore une fois. Oui, les médias ont transformé une panne de deux heures en une
|
||||
crise mondiale pour faire du putaclic.
|
||||
|
||||
Ce qui a rendu cet incident significatif, ce n'est pas seulement la perturbation
|
||||
de Google Cloud, mais aussi les centaines de sites web et d'applications qui
|
||||
sont tombés en panne en même temps. Cela incluait certains grands noms comme
|
||||
Cloudflare, qui utilise GCP pour certains de ses services. Cloudflare étant un
|
||||
CDN, un cache et un proxy largement répandus, cela a créé un effet domino et a,
|
||||
à son tour, fait tomber d'innombrables sites web.
|
||||
|
||||
Cela nous rappelle la fragile interconnection de notre monde numérique. Je ne
|
||||
veux pas pointer du doigt, mais plutôt tirer des leçons de cet incident. Ce
|
||||
n'était pas juste un incident ; cela a mis en lumière des principes
|
||||
fondamentaux que, à l'ère du "tout en tant que service", nous avons peut-être
|
||||
involontairement négligés.
|
||||
|
||||
Voici mes principaux enseignements.
|
||||
|
||||
## Ne mettez pas tous vos œufs dans le même panier de fournisseurs
|
||||
|
||||
Le cloud est devenu synonyme d'une mise à l'échelle infinie, d'un stockage
|
||||
infini, d'une puissance de calcul infinie, d'une flexibilité infinie. Il est
|
||||
construit sur la promesse de réduire les coûts (ce qui peut être vrai lorsqu'il
|
||||
est utilisé correctement). Cependant, cela cache une vérité souvent négligée,
|
||||
qui est aussi son plus grand risque : la dépendance à un seul fournisseur. La
|
||||
récente panne a montré comment une panne d'un seul fournisseur, ou même d'un
|
||||
composant de leur infrastructure, peut avoir un effet en cascade sur la plupart
|
||||
des services.
|
||||
|
||||
Ajoutons à cela que
|
||||
[AWS, Azure et Google Cloud Platform ont une part de marché combinée de 63 % en valeur](https://www.crn.com/news/cloud/2025/cloud-market-share-q1-2025-aws-dips-microsoft-and-google-show-growth?page=1&itc=refresh).
|
||||
Même si votre entreprise n'utilise pas directement ces fournisseurs
|
||||
d'infrastructure, il est probable que vous utilisiez des fournisseurs qui
|
||||
dépendent d'eux, ou des fournisseurs qui pourraient dépendre d'eux. Oui, il est
|
||||
probable que votre application SaaS dépende d'au moins l'un de ces fournisseurs.
|
||||
|
||||
**Ce que vous pouvez faire** :
|
||||
|
||||
* *Cartographiez vos dépendances* : Connaissez-vous vraiment tous les services
|
||||
sur lesquels repose votre produit, directement et indirectement ? Quels IaaS,
|
||||
PaaS, API, CDN, etc., utilisez-vous ? Lesquels ces services utilisent-ils à
|
||||
leur tour ? Dépendez-vous de NpmJS pour construire votre produit ? Votre
|
||||
application est-elle déployée avec une action Github ? Plus vous en savez,
|
||||
mieux vous êtes préparé.
|
||||
* *Faites une évaluation approfondie de vos fournisseurs* : Les garanties de
|
||||
disponibilité (3 ? 4 ? 5 neufs ?) ne sont que du marketing. Prenez-les comme
|
||||
tel. Quelle est l'architecture de votre fournisseur ? Son plan de continuité ?
|
||||
Sa transparence sur les incidents ? Ce sont des critères bien plus importants.
|
||||
* *Envisagez des stratégies multi-cloud* : Vous ne mettriez pas tous vos
|
||||
serveurs dans le même centre de données ? Alors ne mettez pas toute votre
|
||||
infrastructure chez le même fournisseur IaaS ! (Si vous le feriez, vous
|
||||
devriez faire quelque chose à ce sujet !)
|
||||
|
||||
## Contrôlez vos données, contrôlez votre entreprise
|
||||
|
||||
Le monde du cloud et des API dans lequel nous vivons est formidable. Il nous
|
||||
permet de construire rapidement, d'itérer rapidement, de tester des choses et
|
||||
d'améliorer nos solutions. Vous avez besoin d'une authentification, utilisez
|
||||
Supabase ou Auth0. Paiement en ligne ? Il y a Stripe ou Paypal. E-mails
|
||||
transactionnels ? Sendgrid et MailChimp. Recherche ? Algolia. La liste peut être
|
||||
longue, mais maintenant, vous pouvez travailler à créer de la valeur.
|
||||
|
||||
Pourtant, comme l'a montré cette panne, si ces services deviennent
|
||||
indisponibles, vos utilisateurs pourraient être bloqués, ou votre application
|
||||
pourrait cesser de fonctionner, indépendamment de la santé de votre propre
|
||||
infrastructure. Cela peut entraîner une perte significative de contrôle sur les
|
||||
opérations commerciales de base et l'accès aux données. Les services tiers SONT
|
||||
des points de défaillance uniques !
|
||||
|
||||
**Ce que vous pouvez faire** :
|
||||
|
||||
* *Mécanismes de repli pour les services principaux* : Si un service devient
|
||||
indisponible, comment le remplacez-vous ? Pouvez-vous développer une
|
||||
alternative de repli ?
|
||||
* *Mirroring de données robuste* : Assurez-vous d'avoir des sauvegardes
|
||||
régulières et accessibles de vos données critiques, même si elles résident
|
||||
principalement chez un tiers. Pouvez-vous les restaurer rapidement dans un
|
||||
environnement différent si nécessaire ?
|
||||
|
||||
## Construire pour la résilience
|
||||
|
||||
La résilience a toujours été une conséquence de la redondance. Vous devriez
|
||||
toujours avoir un système de secours qui peut assurer le service pendant que
|
||||
votre système principal est en panne.
|
||||
|
||||
Mais il ne suffit pas d'avoir de la redondance. Votre application doit également
|
||||
être conçue pour être tolérante aux pannes et utiliser tout ou partie du système
|
||||
de secours lorsque nécessaire. Au moins, elle doit garantir que l'impact pour
|
||||
vos utilisateurs soit le moindre possible : l'impossibilité d'envoyer un e-mail
|
||||
ne doit jamais bloquer toute votre application.
|
||||
|
||||
**Ce que vous pouvez faire** :
|
||||
|
||||
* *Architectures distribuées* : Concevez vos systèmes avec des architectures de
|
||||
type microservice. Déployez vos services sur plusieurs fournisseurs IaaS.
|
||||
Répliquez les données critiques sur plusieurs fournisseurs. L'objectif est de
|
||||
limiter l'impact de toute défaillance d'un seul composant.
|
||||
* *Systèmes auto-cicatrisants* : Mettez en place des mécanismes qui peuvent
|
||||
détecter automatiquement les défaillances, réacheminer le trafic ou redémarrer
|
||||
les services sans intervention humaine. Plus votre système peut réagir
|
||||
rapidement, moins une panne aura d'impact.
|
||||
* *Concevoir pour l'échec* : N'attendez pas qu'un événement externe expose vos
|
||||
faiblesses. Il sera trop tard. Ajoutez quelques tests de défaillance
|
||||
automatisés à votre pipeline CI : que se passe-t-il si le client a une latence
|
||||
de 5 secondes avec votre serveur ? Que se passe-t-il si la base de données est
|
||||
indisponible ? Que se passe-t-il si un paiement ne peut pas être traité
|
||||
immédiatement ? Quelle est l'*expérience utilisateur* lorsque quelque chose ne
|
||||
va pas ? Ces problèmes ARRIVERONT.
|
||||
|
||||
## Conclusion
|
||||
|
||||
La prochaine panne arrivera. C'est certain. Peut-être pas aussi importante, mais
|
||||
il y en aura qui affecteront votre entreprise.
|
||||
|
||||
Soyez préparé :
|
||||
|
||||
* Connaissez votre infrastructure, vos fournisseurs, leurs fournisseurs, etc.
|
||||
* Évaluez les risques régulièrement. Votre application évolue, vos fournisseurs
|
||||
aussi. Ce qui est vrai à un moment ne l'est plus au suivant.
|
||||
* Prévoyez le pire des cas. Les incidents arriveront. Votre travail est de faire
|
||||
en sorte que l'expérience utilisateur ne soit pas impactée.
|
|
@ -1,6 +0,0 @@
|
|||
+++
|
||||
title = 'Blog'
|
||||
date = 2025-06-04T23:00:00Z
|
||||
draft = false
|
||||
+++
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue