Tame the Cat

Einen eigenen Tomcat Server zu betreiben, kann unter gewissen Umständen ein aufwendiges und mühsames Unterfangen sein.
Daher haben wir mithilfe von Puppet ein eigenes Tomcat-Hosting-Profil entwickelt. Dabei greifen wir auf das bestehende Puppet Modul von Puppetlabs zurück. Ziel dieses Puppetmodules ist es, in kurzer Zeit einen Tomcatserver bereitzustellen, der alle nötigen Konfigurationen beinhaltet sowie in einer einheitlichen und standardisierten Art und Weise aufgesetzt ist. So soll ein Update der Tomcat Version einfach und schnell möglich sein.

In diesem Blog-Post erläutern wir kurz, was unser Puppet-Modul alles kann und zeigen auf, wie einfach es ist, eine neue Tomcat Instanz aufzusetzen.

Architektur

Bevor wir zur Beschreibung des eigentlichen Moduls kommen, zeigen wir kurz auf, welche Komponenten installiert und konfiguriert werden.

Im oben stehenden Diagramm sehen wir die Grundarchitektur eines klassischen Tomcathostings.

Wir verwenden für unser Setup:

  • ein Nginx als Reverse-Proxy für die SSL-Terminierung
  • optional Varnish für das Caching
  • Mysql/Mariadb und/oder PostgreSQL als Datenbank-Server

Der komplette Server lässt sich dabei in einem yaml-file konfigurieren:

---
classes:
  - profile_tomcathosting

profile_tomcathosting::servers:
  'tomcat8.5.15':
    source_url: 'http://www-eu.apache.org/dist/tomcat/tomcat-8/v8.5.15/bin/apache-tomcat-8.5.15.tar.gz'
  'tomcat7':
    source_url: 'http://www-eu.apache.org/dist/tomcat/tomcat-7/v7.0.78/bin/apache-tomcat-7.0.78.tar.gz'

profile_tomcathosting::db_root_pw: 'mySuperS3cretR00tPw'
profile_tomcathosting::varnish_version: '4.1'
profile_tomcathosting::varnish_template: 'profile_tomcathosting/varnish/defaultv4.vcl.erb'
profile_tomcathosting::tomcat_base: '/home'

tomcathosting_sites:
  'mytomcathosting':
    server_name: 'mytomcathosting.com'
    db_password: 'anothergre@tp@$$w0rd'
    use_static: true
    use_varnish: true
    use_mariadb: true
    manage_tls: 'letsencrypt'
    offset: 1
    suppress_version: false
    tomcat_server: 'tomcat8.5.15'
  'legacy_tomcathosting':
    server_name: 'legacy-tomcathosting.com'
    db_password: 'myother_dbP@$swort'
    use_static: false
    use_varnish: false
    use_mariadb: false
    use_postgresql: true
    manage_tls: 'letsencrypt'
    offset: 2
    suppress_version: true
    tomcat_server: 'tomcat7'

Dies reicht schon, um einen Server mit Tomcat7 sowie Tomcat8 zu installieren, sowie zwei Instanzen, die jeweils einen der beiden installierten Tomcats verwendet.

Die Konfiguration im oben stehenden Listing installiert unter /opt/tomcat8.5.15 sowie unter /opt/tomcat7 jeweils einen Tomcat. So werden zwei neue User angelegt (mytomcathosting und legacy_tomcathosting) mit dem Homeverzeichnis unter /home (default ist /var/lib/tomcat, wenn tomcat_base nicht angegeben wird). Dort wird von Puppet im Unterordner tomcat/ die Verzeichnisstruktur für eine Tomcatinstanz eingerichtet. Des Weiteren erstellt Puppet auch die Konfigurationsdateien von Tomcat im Ordner: tomcat/config/. Für mytomcathosting wird zusätzlich ein Ordner static im Homeverzeichnis angelegt.

Wenn wir uns die generierte nginx config genauer ansehen, sehen wir, dass für mytomcathosting eine zusätzliche Location für static content konfiguriert wurde. Zudem leitet die proxy_pass Direktive nicht zur Tomcatinstanz, sondern zum installierten Varnish. Damit Varnish die verschiedenen vhosts unterscheiden kann, setzten wir zusätzlich den varnish_host Header:

mytomcathosting.com Nginx config server

{
  listen       *:443 ssl ;
  server_name  mytomcathosting.com;

  ssl on;

  ssl_certificate           /etc/ssl/certs/mytomcathosting.com-chained.pem;
  ssl_certificate_key       /etc/ssl/private/mytomcathosting.com-key.pem;
  ssl_dhparam               /etc/ssl/dh4096.pem;
  [...]
  add_header              Strict-Transport-Security max-age=31536000;

  location /static/ {

    root      /home/mytomcathosting/;
    index     index.html index.htm index.php;
  }

  location / {
    proxy_pass            ;
    proxy_read_timeout    90;
    proxy_connect_timeout 90;
    proxy_redirect        off;
    proxy_set_header      varnish_host mytomcathosting;
    proxy_set_header      Host mytomcathosting.com;
    proxy_set_header      X-Scheme $host;
    proxy_set_header      X-Forwarded-Host $host;
    proxy_set_header      X-Forwarded-Server $host;
    proxy_set_header      X-Forwarded-Server-IP $server_addr;
    proxy_set_header      X-Forwarded-Server-Port $server_port;
    proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header      X-Real-IP $remote_addr;
    proxy_set_header      X-Forwarded-Client-IP $remote_addr;
    proxy_set_header      Proxy "";
  }
}

legacy-tomcathosting.com Nginx config server

{
  listen       *:443 ssl ;
  server_name  legacy-tomcathosting.com;

  ssl on;

  ssl_certificate           /etc/ssl/certs/legacy-tomcathosting.com-chained.pem;
  ssl_certificate_key       /etc/ssl/private/legacy-tomcathosting.com-key.pem;
  ssl_dhparam               /etc/ssl/dh4096.pem;
  [...]
  add_header              Strict-Transport-Security max-age=31536000;

  location / {

    proxy_pass            ;
    proxy_read_timeout    90;
    proxy_connect_timeout 90;
    proxy_redirect        off;
    proxy_set_header      Host legacy-tomcathosting.com;
    proxy_set_header      X-Scheme $host;
    proxy_set_header      X-Forwarded-Host $host;
    proxy_set_header      X-Forwarded-Server $host;
    proxy_set_header      X-Forwarded-Server-IP $server_addr;
    proxy_set_header      X-Forwarded-Server-Port $server_port;
    proxy_set_header      X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header      X-Real-IP $remote_addr;
    proxy_set_header      X-Forwarded-Client-IP $remote_addr;
    proxy_set_header      Proxy "";
  }
}

Die angegebenen Zertifikate wurden via Let’s Encrypt erstellt und werden automatisch erneuert.

Zusätzlich wird für mytomcathosting eine mariadb Datenbank und für legacy-tomcathosting eine PostgresSQL Datenbank konfiguriert. Damit sichergestellt ist, dass die Tomcat Instanzen auch laufen, erstellt unser Puppet Modul Systemd Unit-Files. Dabei wird als CATALINA_HOME der Pfad zum ausgewählten Tomcatserver verwendet und als CATALINA_BASE der Pfad zum Tomcatverzeichnis der Instanz.

Das Unitfile für mytomcathosting sieht daher wie folgt aus:

mytomcathosting Unitfile

[Unit]
Description=tomcat-mytomcathosting Service
[Service]
ExecStart=/opt/tomcat8.5.15/bin/catalina.sh start
ExecStop=/opt/tomcat8.5.15/bin/catalina.sh stop
Restart=always
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=tomcat-mytomcathosting
User=%i
Type=forking
Environment="CATALINA_HOME=/opt/tomcat8.5.15" "CATALINA_BASE=/home/mytomcathosting/tomcat"

[Install]
WantedBy=multi-user.target

Backup und Monitoring

Backup und Monitoring gehören bei uns zur Selbstverständlichkeit. So wird jede konfigurierte Instanz automatisch ins Backup aufgenommen (User Homeverzeichnis, sowie DB Dumps). Monitoringchecks, die überprüfen, ob die Tomcat Instanzen laufen, werden automatisch eingerichtet. Ebenfalls vollautomatisch wird geprüft, ob der konfigurierte Servername erreichbar ist und einen validen HTTP Code zurückgibt.

Tomcat aktualisieren

Eine bestehende Instanz auf eine neuere Tomcatversion zu aktualisieren ist sehr einfach und in wenigen Minuten erledigt. Dabei muss nur im profile_tomcathosting::servers Hash ein neuer Server mit der gewünschten Version installiert werden und dann in der zu aktualisierenden Instanz im tomcat_serverFeld referenziert werden. Sollte ein Tomcat Server nicht mehr verwendet werden, kann dieser durch das Setzen des ensure: ‚absent‘ Parameters im profile_tomcathosting::servers Hash einfach entfernt werden.

Fazit

Der Betrieb einer Tomcatumgebung ist dank unserem Puppet Modul sehr einfach, schnell und zuverlässig. Dank dem modularen Aufbau der Puppet Module, können wir mit sehr geringem Aufwand verschiedene Tomcat Szenarien abdecken. In nur 15 Minuten können wir eine neue VM (z.b. bei Cloudscale.ch) hochfahren und mit Puppet so konfigurieren, dass wir eine einsatzbereite Tomcatumgebung haben.