Apache Tomcat — Guide hyper complet (Production / DevOps)
Tomcat est un Servlet Container (serveur web applicatif Java) : léger, performant, omniprésent (Spring / APIs REST), et très utilisé en production derrière un reverse-proxy. Ce guide est conçu pour être opérationnel : architecture interne, fichiers de conf, déploiement WAR, sécurité, performance, logs/monitoring, Docker/Kubernetes, troubleshooting, et checklists de prod.
Panorama & concepts
Tomcat vs WebLogic/JBoss, modes de déploiement, composants internes, vocabulaire essentiel.
PositionnementServletWARArchitecture interne
Catalina / Coyote / Jasper, pipeline de requêtes, Engine/Host/Context, valves/filters.
CatalinaConnectorsValvesInstall & layout
Installation Linux, arborescence, variables, systemd, upgrades propres, multi-instances.
LinuxsystemdUpgradeConfiguration core
server.xml, context.xml, conf/Catalina, resources JNDI, UTF-8, compression, headers.
server.xmlJNDIHTTPDéploiement & CI/CD
WAR, manager (à éviter), rolling/blue-green, Jenkins, artefacts, stratégies de rollback.
CI/CDRollbackBlue/GreenSécurité & hardening
HTTPS/TLS, suppression apps default, manager/host-manager, headers, secrets, permissions.
TLSHardeningHeadersPerformance & tuning
Threads, pools, keep-alive, NIO/NIO2, compression, caches, JVM/GC, sizing & tests.
ThreadsJVMGCReverse proxy
Nginx/Apache, AJP (prudence), sticky sessions, headers X-Forwarded, websockets.
NginxAJPX-ForwardedHA & clustering
Sessions (replication vs sticky), stateless, cache externalisé, multi-nodes, limites.
HASessionsStatelessLogs & observabilité
Access logs, JULI, logrotate, JMX, Prometheus, Grafana, métriques essentielles.
JMXPrometheusGrafanaDocker & Kubernetes
Images, multi-stage, conf externalisée, probes, resources, Helm patterns, secrets.
DockerK8sHelmTroubleshooting
Erreurs fréquentes, OOM, threads bloqués, timeouts, TLS, 502/504, checklist prod.
DebugOOMChecklist1) Panorama & concepts — où se place Tomcat ?
Tomcat = Servlet Container / Web Application Server
Tomcat exécute des Servlets, JSP, WebSockets, et sert de runtime web Java pour des applis (souvent Spring). Il est léger et très stable, mais n’embarque pas nativement toute la pile Java EE / Jakarta EE (EJB/JTA/JMS enterprise intégrés) comme WebLogic/WebSphere.
- Déploiement :
.war(classique) ou.jar(Spring Boot embedded, sans Tomcat externe). - Topologie : standalone / derrière Nginx / multi-nœuds + load balancer.
- Patterns : stateless + cache externe (Redis) = HA “propre”.
Tomcat vs WebLogic/JBoss (très résumé)
- Tomcat : web runtime (Servlet/JSP), simple, rapide, coût = 0, très populaire.
- WebLogic/WebSphere : app servers enterprise, features intégrées (JTA/EJB/JMS etc.), plus lourds, souvent licenciés.
- JBoss/WildFly : app server open-source enterprise, mais “plus lourd” qu’un Tomcat.
Vocabulaire essentiel
- Connector : endpoint réseau (HTTP/HTTPS/AJP) géré par Coyote.
- Engine : “moteur” servlet global. Contient des Host.
- Host : hôte virtuel (nom de domaine). Contient des Context.
- Context : application web (un WAR déployé) + paramètres.
- Valve : composant de pipeline (logs, contrôle, réécriture, sécurité).
- Realm : auth (JDBC/LDAP/JAAS) côté Tomcat.
- JNDI Resource : datasource pool DB exposé à l’application.
“Schéma mental” d’une requête
Client -> Reverse Proxy (optionnel) -> Tomcat Connector -> Engine -> Host -> Context -> Filters -> Servlet -> Response
Les points d’optimisation/contrôle : connector (threads, timeouts), valves (logs), app (pool DB), JVM (GC).
2) Architecture interne — Catalina / Coyote / Jasper
Composants
- Coyote : layer réseau (HTTP/1.1, HTTP/2 selon config, NIO/NIO2).
- Catalina : container Servlet (Engine/Host/Context, pipeline, sessions).
- Jasper : compilation/exécution JSP (souvent à éviter en prod si possible).
- JULI : système de logs Tomcat (java.util.logging optimisé).
- JMX : MBeans pour monitoring / introspection.
Pipeline & points d’extension
- Filters (app) : auth, CORS, gzip, tracing… au niveau application.
- Valves (container) : accès logs, restrictions IP, remoteip, etc.
- Listeners : hooks au démarrage/arrêt, lifecycle.
<Host ...> <Valve className="org.apache.catalina.valves.AccessLogValve" ... /> <Valve className="org.apache.catalina.valves.RemoteIpValve" ... /> </Host>
Valves = excellent endroit pour standardiser les logs et la “réalité” des IP client derrière proxy.
3) Installation & layout — Linux, systemd, upgrades propres
Arborescence (rappel)
bin/ start/stop scripts conf/ server.xml, context.xml, web.xml, logging.properties lib/ libs partagées (attention !) webapps/ WARs déployés (auto-deploy possible) logs/ catalina.out + logs JULI temp/ fichiers temporaires work/ compilation JSP, caches
CATALINA_BASE (instance) et CATALINA_HOME (binaire). Ça facilite les upgrades et les multi-instances.systemd (exemple)
[Unit] Description=Tomcat After=network.target [Service] Type=forking User=tomcat Group=tomcat Environment="JAVA_HOME=/usr/lib/jvm/java-17" Environment="CATALINA_HOME=/opt/tomcat" Environment="CATALINA_BASE=/srv/tomcat-instance01" Environment="CATALINA_OPTS=-Xms2g -Xmx2g -XX:+UseG1GC" ExecStart=/opt/tomcat/bin/startup.sh ExecStop=/opt/tomcat/bin/shutdown.sh Restart=on-failure RestartSec=5 [Install] WantedBy=multi-user.target
Adapte les paths/permissions. En prod, externalise les logs et pense aux ulimits (nofile).
4) Configuration core — server.xml, context.xml, JNDI, HTTP
Connector HTTP (NIO) — base solide
<Connector port="8080"
protocol="org.apache.coyote.http11.Http11NioProtocol"
maxThreads="300"
acceptCount="100"
connectionTimeout="20000"
maxKeepAliveRequests="200"
keepAliveTimeout="15000"
compression="on"
compressibleMimeType="text/html,text/xml,text/plain,text/css,application/json,application/javascript"
URIEncoding="UTF-8" />Datasource JNDI (pool) — exemple robuste
<Resource name="jdbc/app"
auth="Container"
type="javax.sql.DataSource"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="org.postgresql.Driver"
url="jdbc:postgresql://db:5432/app"
username="app"
password="CHANGE_ME"
maxActive="60"
maxIdle="20"
minIdle="10"
maxWait="10000"
testOnBorrow="true"
validationQuery="SELECT 1"
validationInterval="30000"
removeAbandoned="true"
removeAbandonedTimeout="60"
logAbandoned="true" />UTF-8 & compression
- URIEncoding en UTF-8 sur le connector.
- Côté app : s’assurer que les filtres encodage (ou Spring) forcent UTF-8.
- Compression : utile pour JSON/HTML, à benchmarker (CPU vs bande passante).
# Exemple côté app (Spring Boot) server.servlet.encoding.charset=UTF-8 server.servlet.encoding.enabled=true server.servlet.encoding.force=true
Timeouts : l’arme anti “prod qui freeze”
- connectionTimeout : handshake initial.
- keepAliveTimeout : connexions persistantes.
- proxy_read_timeout côté Nginx : doit être cohérent avec ton app.
- DB timeouts : au niveau driver/pool (maxWait).
Auto-deploy : prudence en prod
Tomcat peut auto-déployer depuis webapps/ (war/dir). En prod, préfère des déploiements explicites (CI/CD) et désactive l’auto-reload si tu veux de la stabilité.
<Host ... autoDeploy="false" deployOnStartup="false"> ... </Host>
Pour du rolling/blue-green, pilote le LB/proxy plutôt que le hot-deploy.
5) Déploiement & CI/CD — WAR, rollback, blue/green
Stratégies de déploiement (classiques)
- WAR atomique : publier un
.warversionné (artifact repository), déployer via script. - Rolling : 2+ instances derrière LB, mise à jour nœud par nœud.
- Blue/Green : deux pools complets, switch LB (rollback instantané).
- Canary : trafic partiel sur nouvelle version (observabilité forte).
Jenkins (pseudo pipeline)
stage('Build') { sh 'mvn -DskipTests clean package' }
stage('Publish') { sh 'curl -T target/app.war $NEXUS_URL' }
stage('Deploy') {
sh '''
ssh tomcat01 "deploy_war.sh app.war"
ssh tomcat02 "deploy_war.sh app.war"
'''
}
stage('Smoke') { sh 'curl -f https://app/health' }Le “deploy_war.sh” gère backup, stop/start, healthcheck, rollback si KO.
6) Sécurité & hardening — TLS, apps par défaut, headers
Checklist hardening (indispensable)
- Supprimer docs, examples, host-manager, manager si inutile.
- Si manager requis : IP allowlist + auth forte + pas exposé Internet.
- Mettre Tomcat derrière Nginx/Apache (WAF/rate-limit/headers/TLS centralisé).
- Mettre des permissions strictes sur
conf/et secrets (pas dans le WAR). - Mettre à jour Tomcat/Java régulièrement (CVE).
/manager sur Internet. C’est l’une des causes historiques de compromissions.Headers sécurité (proxy ou app)
add_header X-Frame-Options "DENY" always; add_header X-Content-Type-Options "nosniff" always; add_header Referrer-Policy "no-referrer" always; add_header Content-Security-Policy "default-src 'self'" always; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Adapter CSP selon tes besoins (scripts externes, CDN, etc.).
7) Performance & tuning — threads, keepalive, JVM/GC
Tuning Tomcat (pragmatique)
- maxThreads : nombre de requêtes simultanées par connector.
- acceptCount : backlog en attente si threads saturés.
- keepAlive : réduit le coût TCP/TLS mais peut retenir des sockets.
- compression : réduit bandwidth mais augmente CPU.
- pool DB : dimensionner selon DB + latence + transactions.
JVM flags (base saine Java 17)
-Xms4g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/tomcat/heapdumps -Djava.security.egd=file:/dev/urandom
Toujours valider par tests de charge + monitoring GC (pas au feeling).
maxThreads sans augmenter la capacité DB/cache peut empirer la situation (effet “thundering herd”).8) Reverse proxy — Nginx/Apache, X-Forwarded, WebSockets
Nginx (exemple)
location / {
proxy_pass http://tomcat_upstream;
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_read_timeout 60s;
}Pour WebSockets : ajouter Upgrade/Connection headers.
RemoteIpValve (Tomcat) — IP client réelle
<Valve className="org.apache.catalina.valves.RemoteIpValve"
internalProxies="10\.\d+\.\d+\.\d+|192\.168\.\d+\.\d+"
remoteIpHeader="x-forwarded-for"
proxiesHeader="x-forwarded-by"
protocolHeader="x-forwarded-proto" />9) HA & clustering — sessions, stateless, limites
Approches HA (du plus propre au plus “legacy”)
- Stateless + JWT/OAuth2 + cache externalisé (Redis) => HA simple, scalable.
- Sticky sessions au LB (affinité) => simple mais failover limité.
- Session replication Tomcat => possible, mais coûteux/complexe (latence/serialization).
Notes techniques (sessions)
- La session doit être sérialisable (objets Java).
- Risque : bloat mémoire, GC, latence réseau.
- Sur K8s : préférer externaliser l’état plutôt que de compter sur sticky.
# Pattern recommandé App stateless Redis (session/cache) DB (source of truth) LB (round-robin)
10) Logs & observabilité — AccessLog, JMX, Prometheus, Grafana
Access logs (Valve)
<Valve className="org.apache.catalina.valves.AccessLogValve"
directory="logs"
prefix="access_log"
suffix=".log"
pattern="%h %l %u %t "%r" %s %b %D"
rotatable="true" />%D = durée microsecondes (très utile). Coupler avec RemoteIpValve derrière proxy.
JMX / métriques clés
- Threads : currentThreadCount, currentThreadsBusy
- Connexions : keepalive, erreurs, timeouts
- JVM : heap used, GC pauses, OOM
- App : pool DB, latences endpoints, taux d’erreur
11) Docker & Kubernetes — images, conf externalisée, probes, Helm
Dockerfile (simple)
FROM tomcat:9-jdk17 # enlever les apps par défaut RUN rm -rf /usr/local/tomcat/webapps/* COPY target/app.war /usr/local/tomcat/webapps/ROOT.war # conf externalisée possible via volume / env EXPOSE 8080
Kubernetes (pattern)
- Readiness : endpoint
/health(app) ou TCP check. - Liveness : évite les false positives (GC long). Préférer une probe moins agressive.
- Resources : requests/limits cohérents avec heap JVM (ne pas OOM-kill).
- Config : ConfigMaps/Secrets + volumes.
- Helm : values.yaml (env, probes, resources, ingress, autoscaling).
# Règle pratique : heap Xmx <= ~70% du memory limit # ex: limit 2Gi => Xmx ~1.3Gi (à valider)
12) Troubleshooting — erreurs fréquentes + checklist prod
Symptômes -> causes probables
- 502/504 : timeouts proxy <-> tomcat <-> app/DB incohérents.
- Latence qui explose : DB lente, GC, threads saturés, locks.
- OOM : heap trop petit, fuite mémoire, cache session, trop d’objets en session.
- CPU 100% : boucle app, JSP compile, GC thrash, regex logs, compression trop agressive.
- Threads busy : endpoint lent + pool DB saturé + acceptCount qui gonfle.
Checklist production (rapide)
- Apps par défaut supprimées / manager non exposé.
- TLS géré au proxy + HSTS + headers de sécurité.
- RemoteIpValve + access logs avec durée.
- JVM flags + heapdump OOM + logs GC.
- Logrotate / externalisation logs.
- Monitoring JMX + alerting (threads busy, heap, 5xx).
- CI/CD immuable + rollback.
- Config externalisée + secrets.
