Cloud init
clould init is a technology used by cloud provider to setup instance
the config below is the “user data” that will customize an Ubuntu image to a DHIS2 image
what it does
- install tomcat 9
- install postgresql 14
- create 2 databases dhis2_dev and dhis2 and add the postgis extension
- setup a daily backup of the dhis2 database but keep 1 per day for 14 day, 1 per week for 60 day, 1 per month for 1 year then 1 per year
- generate a SSL certificate with lets ’ Encrypt (certbot), the DNS record must be set within 5 min after the first boot on the instance
- configure Tomcat and postgres memory based on availability (~45/50)
- create 2 dhis config folder in /home/dhis/config and /home/dhis/config-dev
- configure the tomcat context so https://PROJ_NAME.DOMAIN leads to prod and https://PROJ_NAME.DOMAIN/dev to dev server
limitations
- both dhis2 instances use the same postgres and tomcat server (on purpose to not “block” resource for dev)
- no backup of dev
- if certbot fails then one must generate the cert manually (without going through nginx which won’t start because of the missing certs)
Utilization
update the content of
- /etc/environment in the write_files section, especially the PROJ_NAME, DOMAIN, MAILADDRESS, DHIS_DB_PW
- replace in the user/ssh-authorized-keys section with you RSA key, if you remove the ssh-authorized-keys section you must add a password and set ssh_pwauth: yes
other
on my version I have also a command line like
- su - dhis -c “ssh-import-id-gh delcroip”
To add my key from github, I removed it to avoid getting access to server of people forgetting to remove that line after a copy paste …
user-data
#cloud-config
hostname: mydomain
timezone: UTC
write_files:
- path: /etc/environment
permissions: '0644'
content: |
PROJ_NAME=subdomain
DOMAIN=domain
MAILADDRESS=YourAdress
DHIS_VERSION=2.39.0.1
POSTGRES_VERSION=14
DHIS_DB_PW=your pass
DHIS_DB_USER=dhis
DHIS_DB_NAME=dhis2
DHIS_DB_NAME_DEV=dhis2_dev
DHIS_HOME=/home/dhis/config
DHIS_HOME_DEV=/home/dhis/config-dev
CATALINA_HOME=/usr/share/tomcat9
CATALINA_BASE=/var/lib/tomcat9
BACKUP_SCRIPT=/opt/database_backup.sh
BACKUP_DIR=/var/backups/dhis2
TOMCAT_CONTEXT=/var/lib/tomcat9/conf/Catalina/localhost
- path: /etc/cron.d/dhis
owner: root:root
content: |
0 7 * * 0 /bin/bash /home/dhis/clean_pg_backups.sh
0 1 * * 0 certbot renew --post-hook "systemctl reload nginx"
0 1 * * * DATE_TIME=`date +%F-%H%M%S`; BACKUPFILENAME="${BACKUP_DIR}/backup-${PROJ_NAME}-v${DHIS_VERSION}-${DATE_TIME}.sql.gz"; pg_dump -T aggregated* -T analytics* -T completeness* -T _* -O $DHIS_DB_NAME | gzip > $BACKUPFILENAME
- path: /var/set-env.sh
owner: root
permissions: '0777'
content: |
#!/bin/bash
export MEMORY=$(( $(awk '/MemTotal:/ { print $2 }' /proc/meminfo)/(1024) ))
export JAVA_MAX=$(( $(awk '/MemTotal:/ { print $2 }' /proc/meminfo) *2 / (1024 *5)))
export JAVA_MIN=$(( $(awk '/MemTotal:/ { print $2 }' /proc/meminfo) / (1024 * 4) ))
export JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom -Xmx${JAVA_MAX}m -Xms${JAVA_MIN}m"
export PSQL_MEM_WORK=$(( $(awk '/MemTotal:/ { print $2 }' /proc/meminfo) / (1024 * 4) ))
export PSQL_MEM_CACHE=$(( $(awk '/MemTotal:/ { print $2 }' /proc/meminfo) / (1024 * 8) ))
export PSQL_MEM_BUFF=$(( $(awk '/MemTotal:/ { print $2 }' /proc/meminfo) / (1024 * 8) ))
- path: /tmp/set-dhis2.sh
permissions: '0777'
content: |
#!/bin/bash
su - postgres -c "psql -c \"CREATE ROLE $D2_DB_USER WITH LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE PASSWORD '$D2_DB_PW';\" -q"
su - postgres -c "psql -U postgres -c 'CREATE DATABASE $D2_DB_NAME OWNER $D2_DB_USER;'"
su - postgres -c "psql -U postgres -d $D2_DB_NAME -c 'CREATE EXTENSION IF NOT EXISTS postgis;'"
mkdir -p $D2_HOME; envsubst < /tmp/dhis.conf.tpl > $D2_HOME/dhis.conf ; chown dhis:tomcat $D2_HOME -R; chmod g+w $D2_HOME -R
- path: /tmp/context.xml.tpl
permissions: '0644'
content: |
<Context>
<Environment name="dhis2-home" value="$D2_HOME"
type="java.lang.String" override="false"/>
</Context>
- path: /home/dhis/clean_pg_backups.sh
permissions: '0744'
content: |
#!/bin/bash
for file in $BACKUP_DIR/*
do
path=$BACKUP_DIR$(basename $file)
created_date='date -r "$path"'
year=$( date -r "$path" '+%Y')
month=$(date -r "$path" +"%B")
month_day=$( date -r "$path" +"%d")
week_day=$(date -r "$path" +"%u")
year_day=$(date -r "$path" +"%j")
data_diff=$(( ( $(date +"%s") - $(date -r "$path" +"%s")) / (60*60*24) ))
# keep 2 weeks of logs everyday
if [ "$data_diff" -gt 14 ] ;then
if [ "$data_diff" -lt 60 ] ;then
if [ "$week_day" -ne 1 ] ; then
rm $file -f
fi
else
if [ "$data_diff" -lt 360 ];then
if [ "$month_day" -ne 1 ] ; then
rm $file -f
fi
else
if [ "$year_day" -ne 1 ] ; then
rm $file -f
fi
fi
fi
fi
done
- path: /tmp/pg_conf/postgresql.conf.tpl
permissions: '0644'
content: |
# dhis2 tuning https://postgresqlco.nf/doc/en
synchronous_commit = off
max_connections = 200
# Total RAM * 0.20
shared_buffers = ${PSQL_MEM_BUFF}MB
# Total RAM * 0.25
effective_cache_size = ${PSQL_MEM_CACHE}MB
# Total RAM * 0.5
work_mem = ${PSQL_MEM_WORK}MB
#Total RAM * 0.05
maintenance_work_mem = 256MB
# WRITE-AHEAD LOG
wal_writer_delay = 10000ms
wal_buffers = 16MB
checkpoint_completion_target = 0.8
default_statistics_target = 100
- path: /tmp/pg_conf/pg_ident.conf
permissions: '0644'
content: |
# MAPNAME SYSTEM-USERNAME PG-USERNAME
local_users postgres postgres
local_users root postgres
local_users root dhis
- path: /tmp/pg_conf/pg_hba.conf
permissions: '0644' ssh-authorized-keys:
content: |
# Allow root to login as postgres or dhis DB user without the need of entering a password.
local all all peer map=local_users
host all all 127.0.0.1/32 md5
host all all ::1/128 md5
- path: /tmp/dhis.conf.tpl
permissions: '0644'
content: |
connection.dialect = org.hibernate.dialect.PostgreSQLDialect
connection.driver_class = org.postgresql.Driver
connection.url = jdbc:postgresql:$D2_DB_NAME
connection.username = $D2_DB_USER
connection.password = $D2_DB_PW
connection.schema = update
- path: /tmp/nginx.conf.tpl
permissions: '0644'
content: |
user www-data;
worker_processes auto;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=dhis:250m inactive=1d;
root /var/lib/tomcat9/webapps/ROOT;
include mime.types;
default_type application/octet-stream;
gzip on; # Enables compression, incl Web API content-types
gzip_types
"application/json;charset=utf-8" application/json
"application/javascript;charset=utf-8" application/javascript text/javascript
"application/xml;charset=utf-8" application/xml text/xml
"text/css;charset=utf-8" text/css
"text/plain;charset=utf-8" text/plain;
server {
listen 80 default_server;
listen [::]:80 default_server;
# Redirect all HTTP requests to HTTPS with a 301 Moved Permanently response.
return 301 https://$host$request_uri;
# enable Let's Encrypt Renewal
location ~ /.well-known {
allow all;
}
}
# HTTPS server
server {
server_name ${PROJ_NAME}.${DOMAIN};
listen 443 ssl;
listen [::]:443 ssl;
# File Max Upload size
client_max_body_size 10M;
ssl_certificate /etc/letsencrypt/live/${PROJ_NAME}.${DOMAIN}/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/${PROJ_NAME}.${DOMAIN}/privkey.pem;
ssl_session_cache shared:SSL:20m;
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers RC4:HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_dhparam /etc/ssl/certs/dhparam.pem;
#add_header X-Frame-Options DENY;
# Disallow Search Engine Crawling
location = /robots.txt {
add_header Content-Type text/plain;
return 200 "User-agent: *\nDisallow: /\n";
}
# Proxy pass to servlet container
location / {
proxy_pass http://localhost:8080/;
proxy_redirect off;
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 https;
proxy_buffer_size 128k;
proxy_buffers 8 128k;
proxy_busy_buffers_size 256k;
proxy_cookie_path ~*^/(.*) "/$1; SameSite=Lax";
proxy_cache dhis;
}
}
}
#--------------------
users:
- name: dhis
groups: tomcat
shell: /bin/bash
sudo: ['ALL=(ALL) NOPASSWD:ALL']
ssh-authorized-keys:
- <YOURKEY>
locale: en_US.UTF-8
manage_etc_hosts: true
packages:
- openjdk-11-jre
- software-properties-common
- unzip
- nginx
- fail2ban
- htop
- tomcat9
- python3-certbot-nginx
- certbot
- postgresql-14
- postgresql-14-postgis-3
- postgresql-client
package_update: true
package_upgrade: true
ssh_pwauth: no
disable_root: true
package_reboot_if_required: true
runcmd:
- set -a; . /etc/environment; set +a; source /var/set-env.sh
#- sed -i "/#\$nrconf{restart} = 'i';/s/.*/\$nrconf{restart} = 'a';/" /etc/needrestart/needrestart.conf
#- apt-get install -y postgresql-14 postgresql-14-postgis-3 postgresql-client
- MAIN_DHIS_VERSION=$(echo $DHIS_VERSION | cut -c1-4); wget https://releases.dhis2.org/${MAIN_DHIS_VERSION}/dhis2-stable-${DHIS_VERSION}.war -O $CATALINA_BASE/webapps/ROOT.war
- cp $CATALINA_BASE/webapps/ROOT.war $CATALINA_BASE/webapps/dev.war
- sed -i 's/Connector port="8080"/& relaxedQueryChars = "[]|{}^\\\`\"\<\>"/' /etc/tomcat9/server.xml
# MAin DB
- D2_HOME=$DHIS_HOME D2_DB_NAME=$DHIS_DB_NAME D2_DB_USER=$DHIS_DB_USER D2_DB_PW=$DHIS_DB_PW /tmp/set-dhis2.sh
- D2_HOME=$DHIS_HOME envsubst < /tmp/context.xml.tpl > $TOMCAT_CONTEXT/ROOT.xml && chown tomcat:tomcat $TOMCAT_CONTEXT/ROOT.xml
# dev db
- D2_HOME=$DHIS_HOME_DEV D2_DB_NAME=$DHIS_DB_NAME_DEV D2_DB_USER=$DHIS_DB_USER D2_DB_PW=$DHIS_DB_PW /tmp/set-dhis2.sh
- D2_HOME=$DHIS_HOME_DEV envsubst < /tmp/context.xml.tpl > $TOMCAT_CONTEXT/dev.xml && chown tomcat:tomcat $TOMCAT_CONTEXT/dev.xml
#cetbot
- sleep 360; certbot --nginx --email $MAILADDRESS -d "${PROJ_NAME}.${DOMAIN}" --non-interactive --agree-tos
# configure postgres
- if [ ! -f /etc/ssl/certs/dhparam.pem ]; then openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048; fi
- su - postgres -c "source /var/set-env.sh; envsubst < /tmp/pg_conf/postgresql.conf.tpl >> /etc/postgresql/$POSTGRES_VERSION/main/postgresql.conf"
- su - postgres -c "cp /tmp/pg_conf/pg_* /etc/postgresql/$POSTGRES_VERSION/main"
#config and restart nginx
- envsubst '${DOMAIN} ${PROJ_NAME}' < /tmp/nginx.conf.tpl > /etc/nginx/nginx.conf && chown www-data:www-data /etc/nginx/nginx.conf
- rm -rf $CATALINA_BASE/webapps/ROOT && service nginx reload