Deploying Simplicité with Docker images tutorial

For this tutorial we use an out-of-the-box CentOS 7, Centos 8 or Debian host but this can be transposed to any other host OS.

By following the steps below you should have an easily-maintainable production-grade deployment of Simplicité up and running in a few minutes.

Installation

System upgrade

This step is required to make sure your host system is up-to-date.

Update system on CentOS 7:

sudo yum update -y && sudo yum clean all
sudo reboot

Update system on CentOS 8:

sudo dnf update -y && sudo dnf clean all
sudo reboot

Update system on Debian:

sudo apt-get update && sudo apt-get upgrade
sudo reboot

Firewall setup

Although it is highly recommended, this step is not required if you have an external firewall which already filters the access to you host.

Install and enable firewall on CentOS 7:

sudo yum install -y firewalld && sudo yum clean all
sudo systemctl enable firewalld
sudo systemctl start firewalld

Install and enable firewall on CentOS 8:

sudo dnf install -y firewalld && sudo dnf clean all

Important: Change the default backend to iptables in /etc/firewalld/firewalld.conf: replace FirewallBackend=nftables by FirewallBackend=iptables. This is required at that stage because CentOS 8 default backend (nftables) is not yet compatible with Docker networking

sudo systemctl enable firewalld
sudo systemctl start firewalld

Install firewall on Debian:

sudo apt update
sudo apt -y install firewalld

Configure firewall:

sudo firewall-cmd --add-service=ssh --permanent
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

Optional useful packages

This step is not required but provides various useful tools and aliases on your host.

Install other useful packages on CentOS 7:

sudo yum install -y vim-enhanced git wget curl zip unzip && sudo yum clean all
sudo yum install -y epel-release && sudo yum install -y certbot htop && sudo yum clean all

Install other useful packages on CentOS 8:

sudo dnf install -y vim-enhanced git wget curl zip unzip && sudo dnf clean all
sudo dnf install -y epel-release && sudo dnf install -y certbot htop && sudo dnf clean all

Add these useful aliases to ~/.bashrc:

cat << EOF >> .bashrc
alias vi=vim
alias rm='rm -i'
alias cp='cp -i'
alias mv='mv -i'
alias dir='ls -alF'
alias d='sudo docker'
alias dv='sudo docker volume'
alias dc='sudo /usr/bin/docker-compose'
alias k='sudo kubectl'
EOF
. .bashrc

Optional database clients packages

This step is not required if you don't plan to connect to the deployed databases from your host.

Install database clients on CentOS 7:

sudo yum install -y mariadb postgresql && sudo yum clean all

Install database clients on CentOS 8:

sudo dnf install -y mariadb postgresql && sudo dnf clean all

Install database clients on Debian:

sudo apt install mariadb-client postgresql-client

Docker

Install Docker on CentOS 7:

sudo yum install -y docker && sudo yum clean all
sudo systemctl enable docker
sudo systemctl start docker

Install Docker on CentOS 8:

sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install docker-ce --nobest -y && sudo dnf clean all
sudo docker --version
sudo systemctl enable docker
sudo systemctl start docker

Install Docker on Debian:

sudo apt update && sudo apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
sudo apt update && sudo apt install docker-ce
sudo docker --version
sudo systemctl status docker

Note: by default Docker is allowed to change the firewall rules to open exposed ports. To disable this default behavior you need to edit /usr/lib/systemd/system/docker.service cartridge file and add --iptables=false to the ExecStart command.

Sign-in to Docker Hub:

sudo docker login

To test if everything works fine you can try starting a Tomcat-only ephemereal container with:

sudo docker run -it --rm -p 80:8080 simplicite/server:latest

Docker compose

Install the Docker compose tool on CentOS 7:

sudo yum install -y epel-release && sudo yum install -y docker-compose && sudo yum clean all

Install the Docker compose tool on CentOS 8 or Debian:

Check the x.y.z version of the latest version on https://github.com/docker/compose/releases/latest.

sudo curl -L "https://github.com/docker/compose/releases/download/x.y.z/docker-compose-$(uname -s)-$(uname -m)" -o /usr/bin/docker-compose
sudo chmod +x /usr/bin/docker-compose
sudo docker-compose --version

Docker compose descriptors

The sample docker-compose.yml files below are using different databases.

Create a folder for your instance (e.g. myinstance), all following files must be created in this folder.

Note: Alternatively you can add --project-name=myinstance to all docker-compose command invokations.

Embedded HSQL database (1 container)

version: "3"
services:
  simplicite:
    image: simplicite/platform:latest
    restart: always
    container_name: myinstance-hsqldb
    ports:
      - 80:8080
    volumes:
      - myinstance-hsqldb-db:/usr/local/tomcat/webapps/ROOT/WEB-INF/db
      - myinstance-hsqldb-dbdoc:/usr/local/tomcat/webapps/ROOT/WEB-INF/dbdoc
      - myinstance-hsqldb-git:/usr/local/tomcat/webapps/ROOT/WEB-INF/git
volumes:
  myinstance-hsqldb-db:
  myinstance-hsqldb-dbdoc:
  myinstance-hsqldb-git:

MySQL database (2 containers)

version: "3"
services:
  db:
    image: mysql:latest
    restart: always
    container_name: myinstance-mysql-database
    command: --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: "simplicite"
      MYSQL_DATABASE: "simplicite"
      MYSQL_USER: "simplicite"
      MYSQL_PASSWORD: "simplicite"
    ports:
    - 127.0.0.1:3306:3306
    volumes:
    - myinstance-mysql-db:/var/lib/mysql
  simplicite:
    image: simplicite/platform:latest
    restart: always
    container_name: myinstance-mysql-webapp
    environment:
      DB_SETUP: "true"
      DB_VENDOR: "mysql"
      DB_HOST: db
      DB_USER: "simplicite"
      DB_PASSWORD: "simplicite"
      DB_NAME: "simplicite"
      DB_WAIT: 10
    ports:
     - 80:8080
    volumes:
    - myinstance-mysql-git:/usr/local/tomcat/webapps/ROOT/WEB-INF/git
    depends_on:
    - db
volumes:
  myinstance-mysql-db:
  myinstance-mysql-git:

Note thate the DB_HOST environment variable of the simplicite is using the name of the db service as hostname.

PostgreSQL database (2 containers)

version: "3"
services:
  db:
    image: postgres:latest
    restart: always
    container_name: myinstance-postgres-database
    environment:
      POSTGRES_USER: "simplicite"
      POSTGRES_PASSWORD: "simplicite"
      POSTGRES_DB: "simplicite"
    ports:
    - 127.0.0.1:5432:5432
    volumes:
    - myinstance-postgres-db:/var/lib/postgresql/data
  simplicite:
    image: simplicite/platform:latest
    restart: always
    container_name: myinstance-postgres-webapp
    environment:
      DB_SETUP: "true"
      DB_VENDOR: "postgresql"
      DB_HOST: db
      DB_USER: "simplicite"
      DB_PASSWORD: "simplicite"
      DB_NAME: "simplicite"
      DB_WAIT: 10
    ports:
     - 80:8080
    volumes:
    - myinstance-postgres-git:/usr/local/tomcat/webapps/ROOT/WEB-INF/git
    depends_on:
    - db
volumes:
  myinstance-postgres-db:
  myinstance-postgres-git:

Note thate the DB_HOST environment variable of the simplicite is using the name of the db service as hostname.

Start/stop/upgrade

To start the container(s):

sudo docker-compose up [-d]

To stop the container(s):

sudo docker-compose down

To update the images (after stopping the containers(s)):

sudo docker-compose pull

[Optional] Add an SSL-enabled NGINX reverse proxy

To add an NGINX reverse proxy to expose your instances over SSL do the following changes to the docker-compose.yml file:

services:
(...)
  nginx:
    image: nginx:latest
    restart: always
    container_name: myinstance-reverseproxy
    ports:
      - 80:80
      - 443:443
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./server.crt:/etc/ssl/server.crt
      - ./server.key:/etc/ssl/server.key
(...)

And, of course, remove the exposed 80 and/or 443 ports from the simplicite service.

Where nginx.conf is like:

events {
}
http {
    gzip on;
    gzip_types text/plain text/css text/javascript text/xml application/json application/javascript application/x-javascript;
    server_names_hash_bucket_size 128;
    client_max_body_size 0;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;

        location / {
            return 301 https://$http_host$request_uri;
        }
    }

    server {
        listen       443 ssl http2 default_server;
        listen       [::]:443 ssl http2 default_server;
        server_name  _;

        ssl_certificate "/etc/ssl/server.crt";
        ssl_certificate_key "/etc/ssl/server.key";

        location / {
            # Uncomment this block if you need to enable CORS
            #if ($request_method = 'OPTIONS') {
            #    add_header Access-Control-Allow-Origin $http_origin;
            #    add_header Access-Control-Allow-Credentials true;
            #    add_header Access-Control-Allow-Headers Content-Type,Authorization,X-Requested-With,X-HTTP-Method-Override,X-Simplicite-Authorization;
            #    add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,HEAD,OPTIONS;
            #    add_header Access-Control-Max-Age 1728000;
            #    add_header Content-Type text/plain;
            #    add_header Content-Length 0;
            #    return 204;
            #}
            if ($request_method ~ '(GET|POST)') {
            #    Uncomment these 3 lines if you need to enable CORS
            #    add_header Access-Control-Allow-Origin $http_origin always;
            #    add_header Access-Control-Allow-Credentials true always;
            #    add_header Access-Control-Expose-Headers X-Simplicite-SessionID;
            #    Uncomment these 3 lines if you need to enable CSP and other security directives (make sure to add specific directives for external services your application may use e.g. Google Maps, ..)
            #    add_header Content-Security-Policy "default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' blob:; style-src 'self' 'unsafe-inline'; font-src 'self' data:" always;
            #    add_header X-Frame-Options SAMEORIGIN always;
            #    add_header X-XSS-Protection "1; mode=block" always;
            }
            proxy_redirect off;
            proxy_buffering off;
            proxy_read_timeout 86400s;
            proxy_send_timeout 86400s;
            proxy_set_header Host $http_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 https;
            proxy_pass http://simplicite:8443;
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    }
}

Note thate the proxy_pass statement is using the name of the simplicite service as hostname and the secured 8443 port (not the unsecured 8080 port)

The mapped /etc/sslserver.crt and /etc/sslserver.key are the SSL certificate and corresponding key that you have obtained/bought for your hostname(s).

GZIP compression

In all cases you should configure GZIP compression. Depending on your deployment strategy it can be done at different level.

Generate a self-signed certificate

You can generate them as a self-signed certficate with the following commands:

openssl genrsa -des3 -out server.key 1024
openssl req -new -key server.key -out server.csr
cp server.key server-pwd.key
openssl rsa -in server-pwd.key -out server.key
openssl x509 -req -days 3650 -in server.csr -signkey server.key -out server.crt

Note: a self-signed certificate will generate warnings in your browser

Use a LetsEncrypt certificate

You can obtain a free signed certificate from the LetsEncrypt service, there are some steps to go thru:

  1. Add this to the HTTP server config in nginx.conf (befor the location / {...} block):
(...)
        location ^~ /.well-known/acme-challenge/ {
            root /usr/local/tomcat/webapps/ROOT;
        }
(...)
  1. Comment the HTTPS config in the nginx.conf file.

  2. Create a .well-known dir:

mkdir .well-known
  1. Add a new volume mapping to the simplicite service in the docker-compose.yml file:
(...)
      - ./.well-known:/usr/local/tomcat/webapps/ROOT/.well-known
(...)
  1. Restart

  2. Request the LetEncrypt certificate by:

sudo certbot certonly --webroot -w . -d <myhostname>
  1. Uncomment the HTTPS config in the nginx.conf file.

  2. Change the certificat and key volume mappings in the docker-compose.yml file to:

(...)
      - /etc/letsencrypt/live/<myhostname>/fullchain.pem:/etc/ssl/server.crt
      - /etc/letsencrypt/live/<myhostname>/privkey.pem:/etc/ssl/server.key
(...)
  1. Restart.

Note: the above step are not required when you renew the certificate by sudo certbot renew.

Connecting to a running container

List the running containers with:

sudo docker ps

Look for the container ID or name of the Simplicité container and execute a bash shell into this container:

sudo docker exec -it <container ID or name> bash

Inside the container the Tomcat base folder is /usr/local/tomcat.

Reviewing logs of a running container

List the running containers with:

sudo docker ps

Look for the container ID or name of the Simplicité container and execute standard Docker logs commands such as:

sudo docker logs -f <container ID or name>