52.1.1Installing Smile CDR, NGINX and PostgreSQL as a Docker Stack

 

If you are installing Smile CDR in a Docker container, this page shows how you can also deploy NGINX and PostgreSQL in Docker containers and link all three together in a Docker stack.

The basic steps to creating a Smile CDR Docker stack are as follows:

  1. Initial setup of Docker and context that will be used to build the Smile CDR stack.
  2. Prepare configuration for Smile CDR Docker service.
  3. Prepare configuration for NGINX Docker service.
  4. Prepare configuration for PostgreSQL Docker service.
  5. Create Docker compose file for Smile CDR Docker stack.
  6. Build and launch Smile CDR stack.

The following sections will provide more details about each of the steps above.

52.1.2Initial Setup of Docker and Context

 

Before proceeding with the configuration of the Docker containers that will make up the Smile CDR stack, it is a good idea to first review and/or complete a few preparatory steps.

52.1.2.11. Install Docker if necessary.

If not already installed, the following link provides instructions for downloading and installing Docker Community Edition in a variety of environments including Windows, OSX, Linux and Cloud (e.g. AWS).

52.1.2.22. Determine the port numbers that will be used to access Smile CDR (if not using the default values).

The port numbers will be needed for both the Smile CDR and NGINX configuration files and will also be referenced in the docker compose file used to build the Smile CDR stack.

52.1.2.33. Determine the DB settings that will be used for PostgreSQL

When initializing PostgreSQL, you will want to specify:

  • Name for default PostgreSQL database if necessary (out-of-box default DB name is "postgres").
  • DB superuser ID and password (defaults are "postgres" and blank respectively),
  • Name(s) for the Smile CDR database(s)
  • Userid(s) and password(s) for Smile CDR database(s)

This information will be needed for the Smile CDR configuration file and for environment settings used to initialize PostgreSQL in a Docker container.

52.1.2.44. Create an empty folder named "docker"

Docker requires that you specify a single "context" path which contains all of the configuration and other files that are referenced when building the containers and stack. This path can have any name or location, but for the purposes of this document, it will be assumed that a folder named "docker" will be used for this purpose.

52.1.3Prepare Configuration for Smile CDR Docker Service

 

In this step we will prepare Docker to create a container for Smile CDR.

52.1.3.11. Load Smile CDR Image.

docker image load --input="/path/to/smilecdr-2019.05.R01-image.tar.bz2"

52.1.3.22. Prepare cdr-config-Master.properties file.

The Smile CDR image is delivered with a default cdr-config-Master.properties configured to use a H2 database and http protocol. As such an updated properties file will be required for use in the Smile CDR container.

Copy the default cdr-config-Master.properties file to the "docker" context folder, renaming it to cdr-config-Master-postgresql.properties. Modify the property values as shown below:

module.clustermgr.config.db.driver                                                          =POSTGRES_9_4
module.persistence.config.db.driver                                                         =POSTGRES_9_4
  • Change the DB driver parameters to be for PostgreSQL
module.clustermgr.config.db.url                                                             =jdbc:postgresql://postgresdb:5432/cdr
module.persistence.config.db.url                                                            =jdbc:postgresql://postgresdb:5432/cdr
  • Note the hostname "postgresdb" above. This name must match the name of the PostgreSQL service that we define in the Docker compose file.
  • The last portion of the urls, "cdr" refers to the name of the database. Change this value if needed.
module.clustermgr.config.db.username                                                        =cdr
module.clustermgr.config.db.password                                                        =somepassword
module.persistence.config.db.username                                                       =cdr
module.persistence.config.db.password                                                       =somepassword
  • Change username and passwords if needed.
module.fhir_endpoint.config.port                                                            =8000
module.fhir_endpoint.config.base_url.fixed                                                  =https://localhost:8000
module.admin_json.config.port                                                               =9000
module.admin_web.config.port                                                                =9100
module.fhirweb_endpoint.config.port                                                         =8001
module.smart_auth.config.port                                                               =9200
module.smart_app_demo_host.config.port                                                      =9201
  • Set the port numbers according to the choices made earlier.
  • For the base_url.fixed parameter, make sure to set the protocol portion of the url to "https" and the hostname portion to the actual hostname used for the endpoint.

52.1.3.33. Create a Dockerfile for Smile CDR

Create a file named "Dockerfile" in the "docker" context folder with the following contents:

# Use base Smile CDR image as parent image
FROM smilecdr

# Set working directory
WORKDIR /home/smile/smilecdr/classes

# Copy the customized properties file from the context folder to the WORKDIR in the container.
copy ./cdr-config-Master-postgresql.properties ./cdr-config-Master.properties

The above Dockerfile will essentially instruct Docker to create a new image based on the "smilecdr" image loaded previously copying the properties file from the docker context folder to the "/home/smile/smilecdr/classes" folder inside the new image.

52.1.4Prepare Configuration for NGINX Docker Service

 

52.1.4.11. Create a sub-folder "ssl" in the "docker" context folder

The ssl folder will be used to hold certificate, key and Diffie-Hellman group files that will be used by NGINX for TLS/SSL encryption.

52.1.4.22. Create certificate and key.

When configuring a secure proxy, NGINX requires a certificate and key file in PEM format.

There are multiple ways to create a certificate and key file. You can find instructions on this page for creating a certificate and key for Ubuntu environments using letsencrypt.

Note the names of the certificate and key files and copy the files to the docker/ssl folder.

52.1.4.33. Generate a Diffie-Hellman group.

For additional security, also create a Diffie-Hellman group file and copy this to the docker/ssl folder. Note the name of the resulting file.

$ sudo mkdir -p /etc/nginx/ssl/
$ sudo openssl dhparam -out /path/to/docker/ssl/dhparams.pem 2048

52.1.4.44. Create common settings for reverse proxy ports.

Create a new file in the docker folder named "proxy.conf" and add the following (replace [fullchain.pem], [privkey.pem], and [dhparams.pem] with your actual certificate file, key file and Diffie-Hellman group file names respectively):

server_name localhost;

ssl_certificate /etc/nginx/ssl/[fullchain.pem];
ssl_certificate_key /etc/nginx/ssl/[privkey.pem];

ssl_stapling on;
ssl_stapling_verify on;

ssl_dhparam /etc/nginx/ssl/[dhparams.pem];
ssl_ciphers 'kEECDH+ECDSA+AES128 kEECDH+ECDSA+AES256 kEECDH+AES128 kEECDH+AES256 kEDH+AES128 kEDH+AES256 DES-CBC3-SHA +SHA !aNULL !eNULL !LOW !kECDH !DSS !MD5 !EXP !PSK !SRP !CAMELLIA !SEED !3DES';

access_log  /var/log/nginx/access.log;

52.1.4.55. Create a site server file for nginx to reverse proxy the various Smile CDR ports

Create a new file in the docker folder, "smilecdr_server.conf", and add the following:

#######################################
# Redirect http to https
#######################################
server {
    server_name localhost;
    listen 80;
	 include proxy.conf;
    return 301 https://$host$request_uri;
}

#######################################
# FHIR Endpoint
#######################################
server {
    server_name localhost;
    listen 8000 ssl default_server;
    include proxy.conf;
    location / {
        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-Host   $host:8000;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   8000;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://smilecdr:8000/;
    }
}

#######################################
# FHIRWeb Console
#######################################
server {
    server_name localhost;
    listen 8001 ssl default_server;
    include proxy.conf;
    location / {
        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-Host   $host:8001;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   8001;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://smilecdr:8001/;
    }
}


#######################################
# Web Admin Console
#######################################
server {
    server_name localhost;
    listen 443 ssl default_server;
    include proxy.conf;
    location / {
        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-Host   $host:443;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   443;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://smilecdr:9100/;
    }
}
server {
    server_name localhost;
    listen 9100 ssl default_server;
    include proxy.conf;
    location / {
        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-Host   $host:9100;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   9100;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://smilecdr:9100/;
    }
}


#######################################
# JSON Admin API
#######################################
server {
    server_name localhost;
    listen 9000 ssl default_server;
    include proxy.conf;
    location / {
        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-Host   $host:9000;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   9000;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://smilecdr:9000/;
    }
}

#######################################
# SMART OAuth2 / OpenID Connect Server
#######################################
server {
    server_name localhost;
    listen 9200 ssl default_server;
    include proxy.conf;
    location / {
        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-Host   $host:9200;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   9200;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://smilecdr:9200/;
    }
}

#######################################
# SMART App Host
#######################################
server {
    server_name localhost;
    listen 9201 ssl default_server;
    include proxy.conf;
    location / {
        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-Host   $host:9201;
        proxy_set_header    X-Forwarded-Server $host;
        proxy_set_header    X-Forwarded-Port   9201;
        proxy_set_header    X-Forwarded-Proto  https;
        proxy_pass          http://smilecdr:9201/;
    }
}

NOTES:

  • Review and update the port numbers if necessary. The port numbers below are based on the out-of-the-box configuration and may not be applicable to your configuration.
  • Port #443 must be proxied to the port number configured for web admin (port #9100 in the out-of-the-box configuration).
  • The host names specified in the "proxy_pass" configuration, "smilecdr", must match the service name specified for the Smile CDR service in the docker compose file.
  • Respect Forward Headers should be enabled in the module config for any modules which are being proxied by nginx in order to ensure that Smile CDR is aware of the correct source IP for incoming requests.

52.1.4.66. Create a Dockerfile for NGINX

In the docker folder create a Dockerfile for NGINX named "Dockerfile_NGINX" with contents set as shown below. This Dockerfile will be used to create a container for NGINX with the configurations created in the previous steps.

# Extend nginx image
FROM nginx

# Set the default NGINX folder as working directory
WORKDIR /etc/nginx

# Copy ssl files to nginx folder
COPY ./ssl ./ssl

# Copy common settings to nginx folder
COPY ./proxy.conf ./

# Copy server block file to sites-enabled folder
COPY ./smilecdr_server.conf ./conf.d/default.conf

# Make Smile CDR ports available outside this container
EXPOSE 80 443 8000 8001 9000 9100 9200 9201

NOTE: Review and update the port numbers listed in the "EXPOSE" instruction if necessary. The port numbers below are based on the out-of-the-box configuration and may not be applicable to your configuration.

52.1.5Prepare Configuration for PostgreSQL Docker Service

 

52.1.5.11. Create initialization shell script for PostgreSQL

Create a shell script, init_postgresdb.sh, in the docker folder which will be used to create the CDR database(s), userid(s) and password(s) in PostgreSQL as shown below.

#!/bin/bash
set -e

#POSTGRES_USER, POSTGRES_DB and POSTGRES_PASSWORD are environment variables defined by the parent postgres image.
#POSTGRES_USER is the DB superuser, POSTGRES_PASSWORD is superuser's password and POSTGRES_DB is the default DB.
#POSTGRES_USER and POSTGRES_DB both default to "postgres". POSTGRES_PASSWORD is blank by default.
#All three variables can be overridden by environment settings.
#The script below creates only a single DB for Smile CDR with name $CDR_DB_NAME with a single user ID and password "$CDR_USER_LOGIN" and "$CDR_PASSWORD"
#Additional CREATE and GRANT statements can be included if additional databases and/or user ids are required.

psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<- EOSQL
   CREATE ROLE $CDR_USER LOGIN password '$CDR_PASSWORD';
   CREATE DATABASE $CDR_DB_NAME;
EOSQL

# Starting Postgres 15, users needs explicit permission to public SCHEMA.
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$CDR_DB_NAME" <<- EOSQL
    GRANT ALL PRIVILEGES ON DATABASE $CDR_DB_NAME TO $CDR_USER;
    GRANT ALL PRIVILEGES ON SCHEMA public TO $CDR_USER;
EOSQL

Values for the environment variables will be set when the Docker stack is launched.

52.1.5.22. Create a Dockerfile for PostgreSQL.

In the docker folder create a Dockerfile for PostgreSQL named "Dockerfile_PostgreSQL" with contents set as shown below. This Dockerfile will be used to create a container for PostgreSQL initialized using the shell script above.

# Extend the postgres image provided by docker hub
FROM postgres:alpine
ADD ./init_postgresdb.sh /docker-entrypoint-initdb.d/init_postgresdb.sh

52.1.6Create Docker compose file for Smile CDR Docker stack

 

In the docker folder create a Docker compose file named "compose_smilecdr.yml" as follows:

version: "3.7"
services:
  smilecdr:
    container_name: smileCDR
    build:
      context: .
    image: smilecdr_pg
    depends_on:
      - postgresdb
    volumes:
      - log:/home/smile/smilecdr/log
      - mq:/home/smile/smilecdr/activemq-data
      - lucene:/home/smile/smilecdr/database/lucene_fhir_persistence
    restart: unless-stopped
  postgresdb:
    container_name: postgres_cdr
    build:
      context: .
      dockerfile: Dockerfile_PostgreSQL
    image: postgres_cdr
    ports:
      - "5432:5432"
    environment:
      - POSTGRES_USER=${DB_SUPER_USER_ID}
      - POSTGRES_PASSWORD=${DB_SUPER_USER_PASSWORD}
      - CDR_DB_NAME=${DB_CDR_NAME}
      - CDR_USER=${DB_CDR_USERID}
      - CDR_PASSWORD=${DB_CDR_PASSWORD}
    volumes:
      - db:/var/lib/postgresql/data
    restart: unless-stopped
  nginx:
    container_name: nginx_cdr
    build:
      context: .
      dockerfile: Dockerfile_NGINX
    image: nginx_cdr
    depends_on:
      - smilecdr
    ports:
      - "80:80"
      - "443:443"
      - "8000:8000"
      - "9000:9000"
      - "9100:9100"
      - "8001:8001"
      - "9200:9200"
      - "9201:9201"
    restart: unless-stopped
volumes:
  db:
  mq:
  log:
  lucene:

NOTES:

  • Review and update the port numbers listed in the "ports" sections if necessary. The port numbers below are based on the out-of-the-box configuration and may not be applicable to your configuration.
  • The service names used in the Docker compose file ("smilecdr", "postgresdb", and "nginx" below) must match the corresponding host names used in the NGINX configuration and Smile CDR properties files.
  • The environment settings included below with the "postgresdb" service must match the environment variables referenced by the init_postgresdb.sh script created above.

52.1.7Build and Launch Smile CDR Stack

 

52.1.7.11. Set environment variables needed for PostgreSQL database initialization, e.g.:

$ export DB_SUPER_USER_ID=postgres
$ export DB_SUPER_USER_PASSWORD=MySecretPassword
$ export DB_CDR_NAME=cdr
$ export DB_CDR_USERID=cdr
$ export DB_CDR_PASSWORD=somepassword

52.1.7.22. Build and start the Smile CDR Docker stack:

docker-compose -f /path/to/docker/compose_smilecdr.yml -p smilecdr up -d

52.1.7.33. To monitor the stdout of the launch:

docker-compose -f /path/to/docker/compose_smilecdr.yml -p smilecdr logs -f smilecdr

52.1.7.44. To stop the Smile CDR Docker stack:

docker-compose -f /path/to/docker/compose_smilecdr.yml -p smilecdr stop

52.1.7.55. To remove the Smile CDR Docker stack:

docker-compose -f /path/to/docker/compose_smilecdr.yml -p smilecdr down --rmi all -v