Apps: "Nginx", "PostgreSQL", "Java", "Docker", "Docker Compose".
Задача: развернуть в среде исполнения "Docker" комплект web-приложений производства "Atlassian", таких как "Jira", "Jira Service Desk", "Confluence" и "Crowd".
Для обеспечения работы "Atlassian Jira/Jira-SD/Confluence/Crowd" нам потребуется следующий минимальный набор приложений:
1. "Oracle/Sun JDK/JRE v1.8+" ("Jira" не поддерживает пока "OpenJDK");
2. СУБД "PostgreSQL 9.x";
3. Фронтальный web-прокси "Nginx".
2. СУБД "PostgreSQL 9.x";
3. Фронтальный web-прокси "Nginx".
Учитывая то, что в Апреля 2019-го лицензионное соглашение "Oracle/Sun JDK/JRE" требует денег за коммерческое использование, я пробовал перейти на "OpenJDK" и столкнулся с проблемами совместимости - для работы "Atlassian Jira/Jira-SD" требуется java-класс "sun.awt.X11FontManager", отсутствующий в "OpenJDK" - пришлось пока остаться с "Oracle Java".
Приложения мы будем запускать в docker-контейнерах, а данные хранить на несущем сервере.
Сервер приложений "Tomcat v8" уже встроен в дистрибутив всех развёртываемых здесь продуктов "Atlassian" - так что его инсталлировать не нужно (WAR-версии web-приложений отдельно более не поставляются).
Последовательность дальнейших действий такова:
1. Подготовка системного окружения (отдельная инструкция);
2. Установка системы контейнеризации "Docker" (отдельная инструкция);
3. Установка и настройка фронтального web-прокси "Nginx";
4. Установка сервера СУБД "PostgreSQL" и создание "баз данных";
5. Создание для web-приложений "Atlassian" специфичных docker-контейнеров;
6. Подготовка конфигурации и пробные запуски web-приложений;
7. Формирование YAML-конфигурации "Docker Compose";
8. Миграция данных из уже работающих инстансов.
2. Установка системы контейнеризации "Docker" (отдельная инструкция);
3. Установка и настройка фронтального web-прокси "Nginx";
4. Установка сервера СУБД "PostgreSQL" и создание "баз данных";
5. Создание для web-приложений "Atlassian" специфичных docker-контейнеров;
6. Подготовка конфигурации и пробные запуски web-приложений;
7. Формирование YAML-конфигурации "Docker Compose";
8. Миграция данных из уже работающих инстансов.
Подготовка несущей файловой структуры для приложений "Atlassian".
Продукты "Atlassian" представляют собой классическую мешанину из Java-классов, C-бинарников, shell-скриптов, разномастных конфигурационных файлов и всевозможных статичных данных, разделению по типам слабо поддающуюся. В docker-контейнерах ядро приложения мы будем размещать в директории "/opt/atlassian/application", а вот загружаемые пользователями данные и конфигурационные файлы лучше вынести в более подобающее место:
# mkdir -p /var/atlassian/appdata/jirasd
# mkdir -p /var/atlassian/appdata/jira
# mkdir -p /var/atlassian/appdata/confluence
# mkdir -p /var/atlassian/appdata/crowd
# mkdir -p /var/atlassian/appdata/jira
# mkdir -p /var/atlassian/appdata/confluence
# mkdir -p /var/atlassian/appdata/crowd
Заранее предусмотрим место для группировки журналов событий:
# mkdir -p /var/log/atlassian/jirasd
# mkdir -p /var/log/atlassian/jira
# mkdir -p /var/log/atlassian/confluence
# mkdir -p /var/log/atlassian/crowd
# mkdir -p /var/log/atlassian/jira
# mkdir -p /var/log/atlassian/confluence
# mkdir -p /var/log/atlassian/crowd
Создание пользователей и условий для усечения привилегий приложений "Atlassian".
Для синхронизации GID и UID несущей системы и контейнеров при создании задаём явно их численные значения:
# groupadd --system --gid 777 atlassian
# useradd --system --home-dir /opt/atlassian/jirasd --shell /bin/false --gid atlassian --uid 771 jirasd
# useradd --system --home-dir /opt/atlassian/jira --shell /bin/false --gid atlassian --uid 772 jira
# useradd --system --home-dir /opt/atlassian/confluence --shell /bin/false --gid atlassian --uid 773 confluence
# useradd --system --home-dir /opt/atlassian/crowd --shell /bin/false --gid atlassian --uid 774 crowd
# useradd --system --home-dir /opt/atlassian/jirasd --shell /bin/false --gid atlassian --uid 771 jirasd
# useradd --system --home-dir /opt/atlassian/jira --shell /bin/false --gid atlassian --uid 772 jira
# useradd --system --home-dir /opt/atlassian/confluence --shell /bin/false --gid atlassian --uid 773 confluence
# useradd --system --home-dir /opt/atlassian/crowd --shell /bin/false --gid atlassian --uid 774 crowd
Передадим директории файловых ресурсов приложений выделенным для этого пользователям:
# chown root:atlassian /opt/atlassian /var/atlassian /var/log/atlassian
# chown -R jirasd:atlassian /opt/atlassian/jirasd /var/atlassian/appdata/jirasd /var/log/atlassian/jirasd
# chown -R jira:atlassian /opt/atlassian/jira /var/atlassian/appdata/jira /var/log/atlassian/jira
# chown -R confluence:atlassian /opt/atlassian/confluence /var/atlassian/appdata/confluence /var/log/atlassian/confluence
# chown -R crowd:atlassian /opt/atlassian/crowd /var/atlassian/appdata/crowd /var/log/atlassian/crowd
# chmod -R o-rwx /opt/atlassian /var/atlassian /var/log/atlassian
# chown -R jirasd:atlassian /opt/atlassian/jirasd /var/atlassian/appdata/jirasd /var/log/atlassian/jirasd
# chown -R jira:atlassian /opt/atlassian/jira /var/atlassian/appdata/jira /var/log/atlassian/jira
# chown -R confluence:atlassian /opt/atlassian/confluence /var/atlassian/appdata/confluence /var/log/atlassian/confluence
# chown -R crowd:atlassian /opt/atlassian/crowd /var/atlassian/appdata/crowd /var/log/atlassian/crowd
# chmod -R o-rwx /opt/atlassian /var/atlassian /var/log/atlassian
Установка фронтального web-сервера "Nginx".
Встроенный в развёртываемые web-приложения "Apache Tomcat" предназначен не только для запуска java-сервлетов, но и выступает в роли web-сервера. Однако на практике для приёма и первичной обработки клиентских подключений удобнее использовать более легковесный web-сервер. Я предпочитаю "Nginx":
# aptitude install nginx-light
Для последующего включения в "Nginx" современного HTTPv2 генерируем DH-сертификат:
# mkdir -p /etc/ssl/nginx && chown -R root:root /etc/ssl/nginx
# openssl dhparam -out /etc/ssl/nginx/dhparam.2048.pem 2048
# openssl dhparam -out /etc/ssl/nginx/dhparam.2048.pem 2048
Сразу слегка преднастроим web-сервер:
# vi /etc/nginx/nginx.conf
user www-data www-data;
worker_processes auto;
....
http {
....
# Велим Nginx не выдавать сведения о номере своей версии
server_tokens off;
# Отключаем ограничение размера тела обрабатываемых клиентских запросов
client_max_body_size 0;
....
worker_processes auto;
....
http {
....
# Велим Nginx не выдавать сведения о номере своей версии
server_tokens off;
# Отключаем ограничение размера тела обрабатываемых клиентских запросов
client_max_body_size 0;
....
Удаляем конфигурацию "сайта по умолчанию" и перезапускаем web-сервер (на этом этапе он не должен прослушивать TCP-порты, кстати):
# rm /etc/nginx/sites-enabled/default
# nginx -t && /etc/init.d/nginx restart
# nginx -t && /etc/init.d/nginx restart
Настройка фронтального web-прокси для docker-контейнеров.
Конфигурация принимающего подключения пользователей web-сервера "Nginx" проста и сводится к описанию параметров "проксирования" всех запросов нижележащим инстансам "Tomcat", запущенным внутри docker-контейнеров:
# vi /etc/nginx/sites-available/jirasd.example.net.conf
# Блок перехвата обращений посредством открытого HTTP и перенаправления таковых на HTTPS
server {
listen 80;
server_name jirasd.example.net;
location / { rewrite ^(.+)$ https://$host$1 permanent; }
#include /etc/nginx/snippets/letsencrypt.conf;
}
# Блок описания web-сервиса приёма, терминирования SSL/TLS запросов и проксирования их нижележащему Tomcat
server {
listen 443 ssl http2;
server_name jirasd.example.net;
access_log /var/log/nginx/jirasd.example.net_access.log;
error_log /var/log/nginx/jirasd.example.net_error.log;
# Явно указываем обслуживать здесь только SSL/TLS подключения
ssl on;
# Описываем параметры установления соединений SSL/TLS
ssl_dhparam /etc/ssl/nginx/dhparam.2048.pem;
ssl_certificate /etc/ssl/nginx/wildcard.example.net.crt;
ssl_certificate_key /etc/ssl/nginx/wildcard.example.net.key.decrypt;
ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Отправляем все запросы на обработку в docker-контейнер
location / {
proxy_pass http://localhost:8081;
proxy_set_header Host $http_host;
proxy_set_header X-Scheme https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-NginX-Proxy true;
# (включение поддержки HTTPv1.1 и WebSocket)
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 240;
proxy_send_timeout 240;
proxy_read_timeout 240;
}
}
server {
listen 80;
server_name jirasd.example.net;
location / { rewrite ^(.+)$ https://$host$1 permanent; }
#include /etc/nginx/snippets/letsencrypt.conf;
}
# Блок описания web-сервиса приёма, терминирования SSL/TLS запросов и проксирования их нижележащему Tomcat
server {
listen 443 ssl http2;
server_name jirasd.example.net;
access_log /var/log/nginx/jirasd.example.net_access.log;
error_log /var/log/nginx/jirasd.example.net_error.log;
# Явно указываем обслуживать здесь только SSL/TLS подключения
ssl on;
# Описываем параметры установления соединений SSL/TLS
ssl_dhparam /etc/ssl/nginx/dhparam.2048.pem;
ssl_certificate /etc/ssl/nginx/wildcard.example.net.crt;
ssl_certificate_key /etc/ssl/nginx/wildcard.example.net.key.decrypt;
ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Отправляем все запросы на обработку в docker-контейнер
location / {
proxy_pass http://localhost:8081;
proxy_set_header Host $http_host;
proxy_set_header X-Scheme https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-NginX-Proxy true;
# (включение поддержки HTTPv1.1 и WebSocket)
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "upgrade";
proxy_connect_timeout 240;
proxy_send_timeout 240;
proxy_read_timeout 240;
}
}
Конфигурации для остальных инстансов аналогичные, различающиеся только обслуживаемыми доменными именами и портами проксирования:
jirasd.example.net -> "proxy_pass" port: 8081
jira.example.net -> "proxy_pass" port: 8082
confluence.example.net -> "proxy_pass" port: 8083
crowd.example.net -> "proxy_pass" port: 8084
jira.example.net -> "proxy_pass" port: 8082
confluence.example.net -> "proxy_pass" port: 8083
crowd.example.net -> "proxy_pass" port: 8084
Активируем для "Nginx" новые конфигурации, проверяем их и запускаем в работу:
# ln -s /etc/nginx/sites-available/jirasd.example.net.conf /etc/nginx/sites-enabled/jirasd.example.net.conf
# ln -s /etc/nginx/sites-available/jira.example.net.conf /etc/nginx/sites-enabled/jira.example.net.conf
# ln -s /etc/nginx/sites-available/confluence.example.net.conf /etc/nginx/sites-enabled/confluence.example.net.conf
# ln -s /etc/nginx/sites-available/crowd.example.net.conf /etc/nginx/sites-enabled/crowd.example.net.conf
# nginx -t && /etc/init.d/nginx reload
# ln -s /etc/nginx/sites-available/jira.example.net.conf /etc/nginx/sites-enabled/jira.example.net.conf
# ln -s /etc/nginx/sites-available/confluence.example.net.conf /etc/nginx/sites-enabled/confluence.example.net.conf
# ln -s /etc/nginx/sites-available/crowd.example.net.conf /etc/nginx/sites-enabled/crowd.example.net.conf
# nginx -t && /etc/init.d/nginx reload
Установка СУБД "PostgreSQL" и мультиплексора "PgBouncer".
СУБД "PostgreSQL" рекомендуется к применению для большинства продуктов "Atlassian" разработчиками таковых. Для неё в дистрибутивных наборах web-приложения уже заложены "java-коннекторы", так что настроить к ней подключение будет проще всего. В используемой здесь "Ubuntu Linux 18 LTS" по умолчанию поставляется "PostgreSQL v10", в то время как разработчики приложений "Atlassian Jira/Jira-SD" гарантируют поддержку только для "PostgreSQL v9x". Придерживаясь рекомендаций подключим дополнительные репозитории и установим необходимую версию СУБД:
# wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
# echo -e "# Official APT-repository PostgreSQL\ndeb [arch=amd64] http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" >> /etc/apt/sources.list.d/postgresql.list
# apt-get update && apt-get install postgresql-9.6 postgresql-contrib-9.6 pgtop pgbouncer
# echo -e "# Official APT-repository PostgreSQL\ndeb [arch=amd64] http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main" >> /etc/apt/sources.list.d/postgresql.list
# apt-get update && apt-get install postgresql-9.6 postgresql-contrib-9.6 pgtop pgbouncer
Изначально СУБД принимает и обслуживает запросы через локальный "файловый сокет" и сетевое подключение "локальной петли" на принятом по умолчанию адресу порта TCP:5432. Следующими настройками я перевожу сервер "PostgreSQL" на прослушивание иного TCP-порта потому, что перед СУБД будет работать оптимизатор SQL-соединений "PgBouncer", с точкой входа как раз на привычном пользователям TCP:5432. Удобнее наружу показывать то, что не потребует перенастройки клиентских приложений (при этом параметры подключения через локальный "сокет" я менять не буду, для сохранения простоты обращений к БД от имени суперпользователя "postgres", при конфигурировании):
# vi /etc/postgresql/9.6/main/postgresql.conf
....
listen_addresses = 'localhost'
....
#port = 5432
port = 5433
....
listen_addresses = 'localhost'
....
#port = 5432
port = 5433
....
В конфигурационном файле правил аутентификации велим СУБД даже для локальных подключений требовать предоставления пароля в виде MD5-хеша, дабы не смешивать системных пользователей и тех, что мы заведём внутри СУБД. Кроме того, добавим в разрешения возможность подключений с выделенного для docker-контейнеров диапазона IP-адресов:
# vi /etc/postgresql/9.6/main/pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
# --------------------------------------
....
# "local" is for Unix socket connections
local all all md5
....
# IPv4 almost local connections:
host all all 100.127.254.0/24 md5
host all all 100.127.255.0/24 md5
....
# --------------------------------------
....
# "local" is for Unix socket connections
local all all md5
....
# IPv4 almost local connections:
host all all 100.127.254.0/24 md5
host all all 100.127.255.0/24 md5
....
Принятие внесённых изменений потребует перезапуска сервера СУБД:
# /etc/init.d/postgresql restart
Проверяем, получилось ли у нас перенести "PostgreSQL" на отличный от принятого по умолчанию TCP-порт:
# netstat -apn | grep -i tcp | grep -i postgres
"PgBouncer" - это программа-мультиплексор, управляющая пулом соединений "PostgreSQL". Любое конечное приложение может подключиться к мультиплексору, как если бы это был непосредственно сервер СУБД, и "PgBouncer" создаст подключение к реальному серверу, либо задействует одно из ранее установленных подключений. Основное предназначение "PgBouncer" - минимизировать издержки на установление новых подключений к СУБД.
Корректируем единственный файл настройки "PgBouncer", приводя его параметры к следующему виду (деактивируя всё остальное символом комментирования ";"):
# vi /etc/pgbouncer/pgbouncer.ini
;; Описываем параметры подключения к СУБД (переопределяя пользовательские только в критичных местах)
[databases]
* = port=5433 auth_user=postgres
;; Описываем конфигурацию мультиплексора как такового
[pgbouncer]
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid
;; Выдвигаем PgBouncer на передний план, принимать подключения пользователей на принятом по умолчанию TCP-порту PostgreSQL
listen_addr = 0.0.0.0
listen_port = 5432
;; Нормально мультиплексор будет принимать клиентские запросы через сетевое TCP-подключение, но для упрощения процедуры подтверждения подлинности посредством запросов через суперпользователя "postgres" нужно указать, где расположен локальный "файловый сокет" целевой СУБД
unix_socket_dir = /var/run/postgresql
;; Задаём параметры SSL/TLS-шифрования соединений между клиентом и мультиплексором
;client_tls_sslmode = require
client_tls_sslmode = prefer
client_tls_ciphers = normal
client_tls_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
client_tls_cert_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
;client_tls_ca_file = root.crt
;; Явно указываем пропускать только пользователей прошедших аутентификацию с паролем зашифрованным посредством MD5
auth_type = md5
;; Предварительную аутентификацию проводить, загружая реальные данные подключающегося пользователя из таблицы с паролями целевой БД
auth_query = SELECT usename, passwd FROM pg_shadow WHERE usename=$1
;; Выбираем режим мультиплексирования соединений (pooling) и параметры очистки ресурсов соединения по завершению запроса
pool_mode = session
server_reset_query = DISCARD ALL
;; JDBC (Java-коннектору) требуется для работы особый параметр конфигурирования соединия с СУБД - разрешаем мультиплексору его пропускать
ignore_startup_parameters = extra_float_digits
;; Задаём параметры количества обслуживаемых клиентов
max_client_conn = 500
default_pool_size = 64
[databases]
* = port=5433 auth_user=postgres
;; Описываем конфигурацию мультиплексора как такового
[pgbouncer]
logfile = /var/log/postgresql/pgbouncer.log
pidfile = /var/run/postgresql/pgbouncer.pid
;; Выдвигаем PgBouncer на передний план, принимать подключения пользователей на принятом по умолчанию TCP-порту PostgreSQL
listen_addr = 0.0.0.0
listen_port = 5432
;; Нормально мультиплексор будет принимать клиентские запросы через сетевое TCP-подключение, но для упрощения процедуры подтверждения подлинности посредством запросов через суперпользователя "postgres" нужно указать, где расположен локальный "файловый сокет" целевой СУБД
unix_socket_dir = /var/run/postgresql
;; Задаём параметры SSL/TLS-шифрования соединений между клиентом и мультиплексором
;client_tls_sslmode = require
client_tls_sslmode = prefer
client_tls_ciphers = normal
client_tls_key_file = /etc/ssl/private/ssl-cert-snakeoil.key
client_tls_cert_file = /etc/ssl/certs/ssl-cert-snakeoil.pem
;client_tls_ca_file = root.crt
;; Явно указываем пропускать только пользователей прошедших аутентификацию с паролем зашифрованным посредством MD5
auth_type = md5
;; Предварительную аутентификацию проводить, загружая реальные данные подключающегося пользователя из таблицы с паролями целевой БД
auth_query = SELECT usename, passwd FROM pg_shadow WHERE usename=$1
;; Выбираем режим мультиплексирования соединений (pooling) и параметры очистки ресурсов соединения по завершению запроса
pool_mode = session
server_reset_query = DISCARD ALL
;; JDBC (Java-коннектору) требуется для работы особый параметр конфигурирования соединия с СУБД - разрешаем мультиплексору его пропускать
ignore_startup_parameters = extra_float_digits
;; Задаём параметры количества обслуживаемых клиентов
max_client_conn = 500
default_pool_size = 64
Учитывая то, что сервер СУБД скорее всего уже имеет набор сертификатов для шифрования соединений с клиентами, при настройке PgBouncer (пример конфигурации выше) проще всего указать на таковые. Посмотреть текущую SSL/TLS конфигурацию "PostgreSQL" можно так:
# cat /etc/postgresql/9.6/main/postgresql.conf | grep -i ssl | grep -v "^#"
На всякий случай (хотя это уже сделано, как правило) разрешаем доступ к используемым по умолчанию сертификатам пользователю, от имени которого работает СУБД:
# usermod --append --groups ssl-cert postgres
На этом настройка "PgBouncer" завершена и для принятия в работу изменений конфигурации его потребуется перезапустить:
# /etc/init.d/pgbouncer restart
Удостоверимся, что мультиплексор заработал и прослушивает заданный TCP-порт:
# netstat -apn | grep -i tcp | grep -i pgbouncer
tcp ... 0.0.0.0:5432 ... LISTEN .../pgbouncer
Создание БД приложений "Atlassian" в СУБД "PostgreSQL".
Самым простым и незамысловатым образом создаём пользователей и заготовки для "баз данных" приложений "Atlassian":
# su -l postgres
$ psql
$ psql
postgres=# CREATE USER jirasd WITH PASSWORD 'jirasdPassword';
postgres=# DROP DATABASE IF EXISTS jirasd;
postgres=# CREATE DATABASE jirasd WITH OWNER jirasd ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# CREATE USER jira WITH PASSWORD 'jiraPassword';
postgres=# DROP DATABASE IF EXISTS jira;
postgres=# CREATE DATABASE jira WITH OWNER jira ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# CREATE USER confluence WITH PASSWORD 'confluencePassword';
postgres=# DROP DATABASE IF EXISTS confluence;
postgres=# CREATE DATABASE confluence WITH OWNER confluence ENCODING 'UTF8' LC_COLLATE 'ru_RU.UTF-8' LC_CTYPE 'ru_RU.UTF-8' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# CREATE USER crowd WITH PASSWORD 'crowdPassword';
postgres=# DROP DATABASE IF EXISTS crowd;
postgres=# CREATE DATABASE crowd WITH OWNER crowd ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# \list
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+------------+----------+-------------+-------------+-------------------
confluence | confluence | UTF8 | ru_RU.UTF-8 | ru_RU.UTF-8 |
crowd | crowd | UTF8 | C | C |
jira | jira | UTF8 | C | C |
jirasd | jirasd | UTF8 | C | C |
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
....
postgres=# \q
postgres=# DROP DATABASE IF EXISTS jirasd;
postgres=# CREATE DATABASE jirasd WITH OWNER jirasd ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# CREATE USER jira WITH PASSWORD 'jiraPassword';
postgres=# DROP DATABASE IF EXISTS jira;
postgres=# CREATE DATABASE jira WITH OWNER jira ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# CREATE USER confluence WITH PASSWORD 'confluencePassword';
postgres=# DROP DATABASE IF EXISTS confluence;
postgres=# CREATE DATABASE confluence WITH OWNER confluence ENCODING 'UTF8' LC_COLLATE 'ru_RU.UTF-8' LC_CTYPE 'ru_RU.UTF-8' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# CREATE USER crowd WITH PASSWORD 'crowdPassword';
postgres=# DROP DATABASE IF EXISTS crowd;
postgres=# CREATE DATABASE crowd WITH OWNER crowd ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# \list
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+------------+----------+-------------+-------------+-------------------
confluence | confluence | UTF8 | ru_RU.UTF-8 | ru_RU.UTF-8 |
crowd | crowd | UTF8 | C | C |
jira | jira | UTF8 | C | C |
jirasd | jirasd | UTF8 | C | C |
postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 |
....
postgres=# \q
В таблице выше заметны пустые поля параметра "Access privileges" - это потому, что во всех случаях создания БД для web-приложений "Atlassian" единственный пользователь БД объявлен её владельцем и в дополнительных разрешениях не нуждается. Если потребуется дать доступ к БД иному пользователю, на то есть соответствующая SQL-команда, исполняемая в консоли "psql":
postgres=# GRANT ALL PRIVILEGES ON DATABASE jirasd TO jirasduser;
Если потребуется сменить пароль пользователя, то делаем это там же, в консоли утилиты "psql":
postgres=# ALTER USER jirasduser WITH ENCRYPTED PASSWORD 'jirasduserPassword';
Сразу проверяем возможность соединения от имени созданных для инстансов "Atlassian" пользователей с сервером БД через мультиплексор (явно подключаясь на обслуживаемый им сетевой порт):
# sudo -u jirasd psql -h localhost -p 5432 -U jirasd -W -d jirasd
jirasddb=> \conninfo
You are connected to database "jirasd" as user "jirasd" on host "localhost" at port "5432".
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
jirasd=> \q
You are connected to database "jirasd" as user "jirasd" on host "localhost" at port "5432".
SSL connection (protocol: TLSv1.2, cipher: ECDHE-RSA-AES256-GCM-SHA384, bits: 256, compression: off)
jirasd=> \q
Создание docker-образа с "Oracle Java SE JDK v8".
Прежде всего необходимо располагать дистрибутивом "Oracle Java SDK". Со второго квартала 2019-го этот программный продукт стал платным для коммерческого использования, но в целях разработки или сугубо личных можно получить его бесплатно. Загружаем с сайта "Oracle" последний стабильный релиз "Java SE JDK v8", укладывая его в типовую директорию "/usr/src".
Создаём выделенную под конфигурации docker-образов директорию и переходим в неё:
# mkdir -p /usr/local/etc/devops/images/atlassian
# cd /usr/local/etc/devops/images/atlassian
# cd /usr/local/etc/devops/images/atlassian
По правилам сборки docker-образа включаемые в него файлы должны находиться в той же директории, где расположен сценарий сборки:
# cp /usr/src/jdk-8u211-linux-x64.tar.gz ./
Пишем сценарий для docker-образа "Oracle Java SE JDK v8":
# vi ./Dockerfile-java-8-211
FROM ubuntu:bionic
LABEL maintainer="NSU, Andrey Narozhniy"
ENV HOME /root
ENV DEBIAN_FRONTEND noninteractive
COPY jdk-8u211-linux-x64.tar.gz /usr/src/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y tzdata \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& mkdir -p /usr/lib/jvm && cd /usr/lib/jvm \
&& tar -zxf /usr/src/jdk-8u211-linux-x64.tar.gz \
&& rm -f /usr/src/jdk-8u211-linux-x64.tar.gz \
&& ln -s /usr/lib/jvm/jdk1.8.0_211/bin/java /bin/java \
&& ln -s /usr/lib/jvm/jdk1.8.0_211/bin/javac /bin/javac \
&& echo "JAVA_HOME=\"/usr/lib/jvm/jdk1.8.0_211\"" >> /etc/environment \
&& echo "JAVA_VERSION=8u211-b12" >> /etc/environment
LABEL maintainer="NSU, Andrey Narozhniy"
ENV HOME /root
ENV DEBIAN_FRONTEND noninteractive
COPY jdk-8u211-linux-x64.tar.gz /usr/src/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y tzdata \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& mkdir -p /usr/lib/jvm && cd /usr/lib/jvm \
&& tar -zxf /usr/src/jdk-8u211-linux-x64.tar.gz \
&& rm -f /usr/src/jdk-8u211-linux-x64.tar.gz \
&& ln -s /usr/lib/jvm/jdk1.8.0_211/bin/java /bin/java \
&& ln -s /usr/lib/jvm/jdk1.8.0_211/bin/javac /bin/javac \
&& echo "JAVA_HOME=\"/usr/lib/jvm/jdk1.8.0_211\"" >> /etc/environment \
&& echo "JAVA_VERSION=8u211-b12" >> /etc/environment
# docker build --no-cache --tag selfmade:java-8-211 --file ./Dockerfile-java-8-211 . >> /var/log/atlassian/build-java-8-211.log
Создание docker-образов с web-приложениями "Atlassian" внутри.
Дистрибутивы приложений "Atlassian" находятся в свободном доступе по следующим адресам:
https://www.atlassian.com/software/jira/service-desk/download
https://www.atlassian.com/software/jira/download
https://www.atlassian.com/software/confluence/download
https://www.atlassian.com/software/crowd/download/data-center
https://www.atlassian.com/software/jira/download
https://www.atlassian.com/software/confluence/download
https://www.atlassian.com/software/crowd/download/data-center
Ссылки на файлы динамические, так что приводить их здесь нет смысла - но общий принцип выбора и сохранения дистрибутива такой:
Download Page -> All Server versions, "TAR.GZ Archive" -> "/usr/src".
Переходим в выделенную под конфигурации docker-образов директорию:
# cd /usr/local/etc/devops/images/atlassian
По правилам сборки docker-образа включаемые в него файлы должны находиться в той же директории, где расположен сценарий сборки:
# tar -xf /usr/src/atlassian-servicedesk-4.2.2.tar.gz -C ./
# tar -xf /usr/src/atlassian-jira-software-8.2.2.tar.gz -C ./
# tar -xf /usr/src/atlassian-confluence-6.15.6.tar.gz -C ./
# tar -xf /usr/src/atlassian-crowd-3.4.5.tar.gz -C ./
# tar -xf /usr/src/atlassian-jira-software-8.2.2.tar.gz -C ./
# tar -xf /usr/src/atlassian-confluence-6.15.6.tar.gz -C ./
# tar -xf /usr/src/atlassian-crowd-3.4.5.tar.gz -C ./
Создаём директорию и складываем в неё набор x.509-сертификатов, которые требуется объявить "доверенными корневыми" для java-приложений внутри docker-контейнера:
# mkdir /usr/local/etc/devops/images/atlassian/cacerts
# cp ./example-rootCA.crt /usr/local/etc/devops/images/atlassian/cacerts
# cp ./example-rootCA.crt /usr/local/etc/devops/images/atlassian/cacerts
Заготавливаем унифицированный для всех web-приложений "Atlassian" скрипт предварительного формирования переменных окружения:
# vi ./docker-entrypoint.sh
#!/bin/bash
set -euo pipefail
shopt -s nullglob
: ${APP_OPTS:=}
: ${CATALINA_OPTS:=}
: ${JAVA_OPTS:=}
# Declare a time zone variable
TZ="`cat /etc/timezone`" && export TZ
# Add to list of directories "APR based Apache Tomcat Native LifecycleEvent library" location
JAVA_OPTS="${JAVA_OPTS} -Djava.library.path=/lib:/lib64:/usr/lib:/usr/lib64:/usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu"
# Typical general "Atlassian" application settings
JAVA_OPTS="${JAVA_OPTS} -Duser.timezone=Asia/Novosibirsk -Duser.language=ru -Duser.country=RU -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8"
# Declare a Java options variable
JAVA_OPTS="${JAVA_OPTS} ${APP_OPTS}"
export JAVA_OPTS
exec "$@"
set -euo pipefail
shopt -s nullglob
: ${APP_OPTS:=}
: ${CATALINA_OPTS:=}
: ${JAVA_OPTS:=}
# Declare a time zone variable
TZ="`cat /etc/timezone`" && export TZ
# Add to list of directories "APR based Apache Tomcat Native LifecycleEvent library" location
JAVA_OPTS="${JAVA_OPTS} -Djava.library.path=/lib:/lib64:/usr/lib:/usr/lib64:/usr/java/packages/lib/amd64:/usr/lib/x86_64-linux-gnu"
# Typical general "Atlassian" application settings
JAVA_OPTS="${JAVA_OPTS} -Duser.timezone=Asia/Novosibirsk -Duser.language=ru -Duser.country=RU -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8"
# Declare a Java options variable
JAVA_OPTS="${JAVA_OPTS} ${APP_OPTS}"
export JAVA_OPTS
exec "$@"
Пишем сценарий для docker-образа "Atlassian Jira Service-Desk (Jira-SD)":
# vi ./Dockerfile-java-8-jirasd-4.2.2
FROM selfmade:java-8-211
LABEL maintainer="NSU, Andrey Narozhniy"
ENV DEBIAN_FRONTEND noninteractive
COPY docker-entrypoint.sh /usr/local/bin/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y perl ttf-dejavu libtcnative-1 libapr1 \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& chmod 755 usr/local/bin/docker-entrypoint.sh && ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh \
&& groupadd --system --gid 777 atlassian \
&& useradd --system --home-dir /opt/atlassian/jirasd --shell /bin/false --gid atlassian --uid 771 jirasd \
&& mkdir -p /opt/atlassian/jirasd && chown -R jirasd:atlassian /opt/atlassian/jirasd
COPY --chown=jirasd:atlassian atlassian-jira-servicedesk-4.2.2-standalone/ /opt/atlassian/jirasd
# Enable custom configuration "Nginx HTTPS -> Proxy -> Tomcat HTTP"
RUN sed -i -e "s/port=\"8080\"/port=\"8080\"\n\t\t proxyName=\"jirasd.example.net\" proxyPort=\"443\" scheme=\"https\"\n\t\t/gI; s/redirectPort=\"8443\"//gI" /opt/atlassian/jirasd/conf/server.xml
# Blocking clumsy logic in web application initialization script
RUN sed -i -e "s/^JVM_MINIMUM_MEMORY/# (bypassed) JVM_MINIMUM_MEMORY/gI; s/^JVM_MAXIMUM_MEMORY/# (bypassed) JVM_MAXIMUM_MEMORY/gI" /opt/atlassian/jirasd/bin/setenv.sh
WORKDIR /var/atlassian/appdata/jirasd
EXPOSE 8080
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/opt/atlassian/jirasd/bin/start-jira.sh", "-fg"]
LABEL maintainer="NSU, Andrey Narozhniy"
ENV DEBIAN_FRONTEND noninteractive
COPY docker-entrypoint.sh /usr/local/bin/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y perl ttf-dejavu libtcnative-1 libapr1 \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& chmod 755 usr/local/bin/docker-entrypoint.sh && ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh \
&& groupadd --system --gid 777 atlassian \
&& useradd --system --home-dir /opt/atlassian/jirasd --shell /bin/false --gid atlassian --uid 771 jirasd \
&& mkdir -p /opt/atlassian/jirasd && chown -R jirasd:atlassian /opt/atlassian/jirasd
COPY --chown=jirasd:atlassian atlassian-jira-servicedesk-4.2.2-standalone/ /opt/atlassian/jirasd
# Enable custom configuration "Nginx HTTPS -> Proxy -> Tomcat HTTP"
RUN sed -i -e "s/port=\"8080\"/port=\"8080\"\n\t\t proxyName=\"jirasd.example.net\" proxyPort=\"443\" scheme=\"https\"\n\t\t/gI; s/redirectPort=\"8443\"//gI" /opt/atlassian/jirasd/conf/server.xml
# Blocking clumsy logic in web application initialization script
RUN sed -i -e "s/^JVM_MINIMUM_MEMORY/# (bypassed) JVM_MINIMUM_MEMORY/gI; s/^JVM_MAXIMUM_MEMORY/# (bypassed) JVM_MAXIMUM_MEMORY/gI" /opt/atlassian/jirasd/bin/setenv.sh
WORKDIR /var/atlassian/appdata/jirasd
EXPOSE 8080
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/opt/atlassian/jirasd/bin/start-jira.sh", "-fg"]
# docker build --no-cache --tag selfmade:java-8-jirasd-4.2.2 --file ./Dockerfile-java-8-jirasd-4.2.2 . >> /var/log/atlassian/build-java-8-jirasd-4.2.2.log
Пишем сценарий для docker-образа "Atlassian Jira Software":
# vi ./Dockerfile-java-8-jira-8.2.2
FROM selfmade:java-8-211
LABEL maintainer="NSU, Andrey Narozhniy"
ENV DEBIAN_FRONTEND noninteractive
COPY docker-entrypoint.sh /usr/local/bin/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y perl ttf-dejavu libtcnative-1 libapr1 \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& chmod 755 usr/local/bin/docker-entrypoint.sh && ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh \
&& groupadd --system --gid 777 atlassian \
&& useradd --system --home-dir /opt/atlassian/jira --shell /bin/false --gid atlassian --uid 772 jira \
&& mkdir -p /opt/atlassian/jira && chown -R jira:atlassian /opt/atlassian/jira
COPY --chown=jira:atlassian atlassian-jira-software-8.2.2-standalone/ /opt/atlassian/jira
# Enable custom configuration "Nginx HTTPS -> Proxy -> Tomcat HTTP"
RUN sed -i -e "s/port=\"8080\"/port=\"8080\"\n\t\t proxyName=\"jira.example.net\" proxyPort=\"443\" scheme=\"https\"\n\t\t/gI; s/redirectPort=\"8443\"//gI" /opt/atlassian/jira/conf/server.xml
# Blocking clumsy logic in web application initialization script
RUN sed -i -e "s/^JVM_MINIMUM_MEMORY/# (bypassed) JVM_MINIMUM_MEMORY/gI; s/^JVM_MAXIMUM_MEMORY/# (bypassed) JVM_MAXIMUM_MEMORY/gI" /opt/atlassian/jira/bin/setenv.sh
WORKDIR /var/atlassian/appdata/jira
EXPOSE 8080
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/opt/atlassian/jira/bin/start-jira.sh", "-fg"]
LABEL maintainer="NSU, Andrey Narozhniy"
ENV DEBIAN_FRONTEND noninteractive
COPY docker-entrypoint.sh /usr/local/bin/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y perl ttf-dejavu libtcnative-1 libapr1 \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& chmod 755 usr/local/bin/docker-entrypoint.sh && ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh \
&& groupadd --system --gid 777 atlassian \
&& useradd --system --home-dir /opt/atlassian/jira --shell /bin/false --gid atlassian --uid 772 jira \
&& mkdir -p /opt/atlassian/jira && chown -R jira:atlassian /opt/atlassian/jira
COPY --chown=jira:atlassian atlassian-jira-software-8.2.2-standalone/ /opt/atlassian/jira
# Enable custom configuration "Nginx HTTPS -> Proxy -> Tomcat HTTP"
RUN sed -i -e "s/port=\"8080\"/port=\"8080\"\n\t\t proxyName=\"jira.example.net\" proxyPort=\"443\" scheme=\"https\"\n\t\t/gI; s/redirectPort=\"8443\"//gI" /opt/atlassian/jira/conf/server.xml
# Blocking clumsy logic in web application initialization script
RUN sed -i -e "s/^JVM_MINIMUM_MEMORY/# (bypassed) JVM_MINIMUM_MEMORY/gI; s/^JVM_MAXIMUM_MEMORY/# (bypassed) JVM_MAXIMUM_MEMORY/gI" /opt/atlassian/jira/bin/setenv.sh
WORKDIR /var/atlassian/appdata/jira
EXPOSE 8080
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/opt/atlassian/jira/bin/start-jira.sh", "-fg"]
# docker build --no-cache --tag selfmade:java-8-jira-8.2.2 --file ./Dockerfile-java-8-jira-8.2.2 . >> /var/log/atlassian/build-java-8-jira-8.2.2.log
Пишем сценарий для docker-образа "Atlassian Confluence":
# vi ./Dockerfile-java-8-confluence-6.15.6
FROM selfmade:java-8-211
LABEL maintainer="NSU, Andrey Narozhniy"
ENV DEBIAN_FRONTEND noninteractive
COPY docker-entrypoint.sh /usr/local/bin/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y perl ttf-dejavu libtcnative-1 libapr1 \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& chmod 755 usr/local/bin/docker-entrypoint.sh && ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh \
&& groupadd --system --gid 777 atlassian \
&& useradd --system --home-dir /opt/atlassian/confluence --shell /bin/false --gid atlassian --uid 773 confluence \
&& mkdir -p /opt/atlassian/confluence && chown -R confluence:atlassian /opt/atlassian/confluence
COPY --chown=confluence:atlassian atlassian-confluence-6.15.6/ /opt/atlassian/confluence
# Enable custom configuration "Nginx HTTPS -> Proxy -> Tomcat HTTP"
RUN sed -i -e "s/port=\"8090\"/port=\"8090\"\n\t\t proxyName=\"confluence.example.net\" proxyPort=\"443\" scheme=\"https\"\n\t\t/gI; s/redirectPort=\"8443\"//gI" /opt/atlassian/confluence/conf/server.xml
# Blocking clumsy logic in web application initialization script
RUN sed -i -e "s/^CATALINA_OPTS=\"-Xms/# (bypassed) CATALINA_OPTS=\"-Xms/gI" /opt/atlassian/confluence/bin/setenv.sh
WORKDIR /var/atlassian/appdata/confluence
EXPOSE 8090
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/opt/atlassian/confluence/bin/start-confluence.sh", "-fg"]
LABEL maintainer="NSU, Andrey Narozhniy"
ENV DEBIAN_FRONTEND noninteractive
COPY docker-entrypoint.sh /usr/local/bin/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y perl ttf-dejavu libtcnative-1 libapr1 \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& chmod 755 usr/local/bin/docker-entrypoint.sh && ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh \
&& groupadd --system --gid 777 atlassian \
&& useradd --system --home-dir /opt/atlassian/confluence --shell /bin/false --gid atlassian --uid 773 confluence \
&& mkdir -p /opt/atlassian/confluence && chown -R confluence:atlassian /opt/atlassian/confluence
COPY --chown=confluence:atlassian atlassian-confluence-6.15.6/ /opt/atlassian/confluence
# Enable custom configuration "Nginx HTTPS -> Proxy -> Tomcat HTTP"
RUN sed -i -e "s/port=\"8090\"/port=\"8090\"\n\t\t proxyName=\"confluence.example.net\" proxyPort=\"443\" scheme=\"https\"\n\t\t/gI; s/redirectPort=\"8443\"//gI" /opt/atlassian/confluence/conf/server.xml
# Blocking clumsy logic in web application initialization script
RUN sed -i -e "s/^CATALINA_OPTS=\"-Xms/# (bypassed) CATALINA_OPTS=\"-Xms/gI" /opt/atlassian/confluence/bin/setenv.sh
WORKDIR /var/atlassian/appdata/confluence
EXPOSE 8090
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/opt/atlassian/confluence/bin/start-confluence.sh", "-fg"]
# docker build --no-cache --tag selfmade:java-8-confluence-6.15.6 --file ./Dockerfile-java-8-confluence-6.15.6 . >> /var/log/atlassian/build-java-8-confluence-6.45.6.log
Пишем сценарий для docker-образа "Atlassian Crowd":
# vi ./Dockerfile-java-8-crowd-3.4.5
FROM selfmade:java-8-211
LABEL maintainer="NSU, Andrey Narozhniy"
ENV DEBIAN_FRONTEND noninteractive
COPY docker-entrypoint.sh /usr/local/bin/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y perl ttf-dejavu libtcnative-1 libapr1 \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& chmod 755 usr/local/bin/docker-entrypoint.sh && ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh \
&& groupadd --system --gid 777 atlassian \
&& useradd --system --home-dir /opt/atlassian/crowd --shell /bin/false --gid atlassian --uid 774 crowd \
&& mkdir -p /opt/atlassian/crowd && chown -R crowd:atlassian /opt/atlassian/crowd
COPY --chown=crowd:atlassian atlassian-crowd-3.4.5/ /opt/atlassian/crowd
# Add a set of CA-certificates to the Java keystore
COPY cacerts/ /etc/ssl/atlassian
RUN JAVA_HOME=$(java -XshowSettings:properties -version 2>&1 >/dev/null | grep -i "java.home" | awk -F '=' '{print $2}' | sed 's/^\s//;s/\s$//') && for I in /etc/ssl/atlassian/* ; do ${JAVA_HOME}/bin/keytool -import -noprompt -trustcacerts -alias "Added CA certificat #${I}" -file "${I}" -storetype JKS -keystore ${JAVA_HOME}/lib/security/cacerts -keypass changeit -storepass changeit ; done
# Enable custom configuration "Nginx HTTPS -> Proxy -> Tomcat HTTP"
RUN sed -i -e "s/port=\"8095\"/port=\"8095\" proxyName=\"crowd.example.net\" proxyPort=\"443\" scheme=\"https\"/gI; s/redirectPort=\"8443\"//gI" /opt/atlassian/crowd/apache-tomcat/conf/server.xml
# Blocking clumsy logic in web application initialization script
RUN sed -i -e "s/^JAVA_OPTS/# (bypassed) JAVA_OPTS/gI; s/^export.*JAVA_OPTS/# (bypassed) export JAVA_OPTS/gI" /opt/atlassian/crowd/apache-tomcat/bin/setenv.sh
WORKDIR /var/atlassian/appdata/crowd
EXPOSE 8095
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/opt/atlassian/crowd/start_crowd.sh", "-fg"]
LABEL maintainer="NSU, Andrey Narozhniy"
ENV DEBIAN_FRONTEND noninteractive
COPY docker-entrypoint.sh /usr/local/bin/
RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y apt-utils ca-certificates \
&& apt-get install --no-install-recommends -y perl ttf-dejavu libtcnative-1 libapr1 \
&& apt-get remove apt-utils ca-certificates -y \
&& apt-get purge --auto-remove -y \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& chmod 755 usr/local/bin/docker-entrypoint.sh && ln -s usr/local/bin/docker-entrypoint.sh /docker-entrypoint.sh \
&& groupadd --system --gid 777 atlassian \
&& useradd --system --home-dir /opt/atlassian/crowd --shell /bin/false --gid atlassian --uid 774 crowd \
&& mkdir -p /opt/atlassian/crowd && chown -R crowd:atlassian /opt/atlassian/crowd
COPY --chown=crowd:atlassian atlassian-crowd-3.4.5/ /opt/atlassian/crowd
# Add a set of CA-certificates to the Java keystore
COPY cacerts/ /etc/ssl/atlassian
RUN JAVA_HOME=$(java -XshowSettings:properties -version 2>&1 >/dev/null | grep -i "java.home" | awk -F '=' '{print $2}' | sed 's/^\s//;s/\s$//') && for I in /etc/ssl/atlassian/* ; do ${JAVA_HOME}/bin/keytool -import -noprompt -trustcacerts -alias "Added CA certificat #${I}" -file "${I}" -storetype JKS -keystore ${JAVA_HOME}/lib/security/cacerts -keypass changeit -storepass changeit ; done
# Enable custom configuration "Nginx HTTPS -> Proxy -> Tomcat HTTP"
RUN sed -i -e "s/port=\"8095\"/port=\"8095\" proxyName=\"crowd.example.net\" proxyPort=\"443\" scheme=\"https\"/gI; s/redirectPort=\"8443\"//gI" /opt/atlassian/crowd/apache-tomcat/conf/server.xml
# Blocking clumsy logic in web application initialization script
RUN sed -i -e "s/^JAVA_OPTS/# (bypassed) JAVA_OPTS/gI; s/^export.*JAVA_OPTS/# (bypassed) export JAVA_OPTS/gI" /opt/atlassian/crowd/apache-tomcat/bin/setenv.sh
WORKDIR /var/atlassian/appdata/crowd
EXPOSE 8095
ENTRYPOINT ["docker-entrypoint.sh"]
CMD ["/opt/atlassian/crowd/start_crowd.sh", "-fg"]
# docker build --no-cache --tag selfmade:java-8-crowd-3.4.5 --file ./Dockerfile-java-8-crowd-3.4.5 . >> /var/log/atlassian/build-java-8-crowd-3.4.5.log
Просматриваем список задействуемых docker-образов:
# docker images
REPOSITORY TAG ... SIZE
selfmade java-8-crowd-3.4.5 ... 960MB
selfmade java-8-confluence-6.15.6 ... 1.36GB
selfmade java-8-jira-8.2.2 ... 1.12GB
selfmade java-8-jirasd-4.2.2 ... 1.14GB
selfmade java-8-211 ... 669MB
ubuntu bionic ... 64.2MB
....
selfmade java-8-crowd-3.4.5 ... 960MB
selfmade java-8-confluence-6.15.6 ... 1.36GB
selfmade java-8-jira-8.2.2 ... 1.12GB
selfmade java-8-jirasd-4.2.2 ... 1.14GB
selfmade java-8-211 ... 669MB
ubuntu bionic ... 64.2MB
....
Распределение оперативной памяти в соответствии с потребностями приложений.
По умолчаниям JVM каждому запускаемому java-приложению выделяет 64MB ОЗУ, что на практике чрезвычайно мало. В предустановленной конфигурации для приложений "Atlassian" запрашивается от 384M до 768M, но и этого недостаточно (с таким лимитом стартует и будет полноценно работать разве что свежеустановленная или малоиспользуемая "Jira"). В общем случае Java-приложения являются самым прожорливым потребителем ОЗУ на сервере, так что каждому инстансу есть смысл нарезать памяти по максимуму, оставив до четверти-трети от всего объёма под СУБД и системные нужды.
Будем отталкиваться от того, что у нашего сервера ОЗУ на 32GB, которое нужно поделить между следующими сервисам (перечень по убыванию наблюдаемых требований к ОЗУ). Треть отрежем для СУБД и системных нужд, а 20GB распределим в соответствии с типичной нагрузкой на каждое приложение:
"Jira-SD" - 10GB,
"Jira" - 6GB,
"Confluence" - 3GB,
"Crowd" - 1GB.
OS & "PostgreSQL" - 25%.
"Jira" - 6GB,
"Confluence" - 3GB,
"Crowd" - 1GB.
OS & "PostgreSQL" - 25%.
Пробные запуски docker-контейнеров с web-приложениями.
Вначале можно поупражняться в подборе подходящей конфигурации web-приложений индивидуальным запуском docker-контейнеров - но в последующем мы задачу развёртывания сервисов отдадим "Docker Compose".
Создаём виртуальную сеть для docker-контейнеров, с IP-адресацией из "shared address space" (IANA), наименее вероятно конфликтующую с уже имеющимися сетевыми сервисами:
# docker network create --driver bridge --subnet 100.127.254.0/24 atlassian
Запускаем docker-контейнер с web-приложением "Atlassian Jira-SD":
# docker run --rm --name atlassian-jirasd \
--network-alias atlassian-jirasd \
--env JVM_MINIMUM_MEMORY="5g" --env JVM_MAXIMUM_MEMORY="10g" \
--env APP_OPTS="-Djira.home=/var/atlassian/appdata/jirasd -Djava.io.tmpdir=/opt/atlassian/jirasd/temp \
-v /var/atlassian/appdata/jirasd:/var/atlassian/appdata/jirasd:rw \
-v /var/log/atlassian/jirasd:/opt/atlassian/jirasd/logs:rw \
--workdir /var/atlassian/appdata/jirasd \
--tmpfs /run --tmpfs /var/atlassian/appdata/jirasd/temp:uid=771 \
--user jirasd:atlassian \
--network atlassian \
--publish 127.0.0.1:8081:8080 \
--detach \
selfmade:java-8-jirasd-4.2.2 > /dev/null \
&& chown -R jirasd:atlassian /var/log/atlassian/jirasd \
&& docker logs --follow atlassian-jirasd > /var/log/atlassian/docker-output-jirasd.log 2>&1 &
--network-alias atlassian-jirasd \
--env JVM_MINIMUM_MEMORY="5g" --env JVM_MAXIMUM_MEMORY="10g" \
--env APP_OPTS="-Djira.home=/var/atlassian/appdata/jirasd -Djava.io.tmpdir=/opt/atlassian/jirasd/temp \
-v /var/atlassian/appdata/jirasd:/var/atlassian/appdata/jirasd:rw \
-v /var/log/atlassian/jirasd:/opt/atlassian/jirasd/logs:rw \
--workdir /var/atlassian/appdata/jirasd \
--tmpfs /run --tmpfs /var/atlassian/appdata/jirasd/temp:uid=771 \
--user jirasd:atlassian \
--network atlassian \
--publish 127.0.0.1:8081:8080 \
--detach \
selfmade:java-8-jirasd-4.2.2 > /dev/null \
&& chown -R jirasd:atlassian /var/log/atlassian/jirasd \
&& docker logs --follow atlassian-jirasd > /var/log/atlassian/docker-output-jirasd.log 2>&1 &
Запускаем docker-контейнер с web-приложением "Atlassian Jira":
# docker run --rm --name atlassian-jira \
--network-alias atlassian-jira \
--env JVM_MINIMUM_MEMORY="3g" --env JVM_MAXIMUM_MEMORY="6g" \
--env APP_OPTS="-Djira.home=/var/atlassian/appdata/jira -Djava.io.tmpdir=/opt/atlassian/jira/temp" \
-v /var/atlassian/appdata/jira:/var/atlassian/appdata/jira:rw \
-v /var/log/atlassian/jira:/opt/atlassian/jira/logs:rw \
--workdir /var/atlassian/appdata/jira \
--tmpfs /run --tmpfs /var/atlassian/appdata/jira/temp:uid=772 \
--user jira:atlassian \
--network atlassian \
--publish 127.0.0.1:8082:8080 \
--detach \
selfmade:java-8-jira-8.2.2 > /dev/null \
&& chown -R jira:atlassian /var/log/atlassian/jira \
&& docker logs --follow atlassian-jira > /var/log/atlassian/docker-output-jira.log 2>&1 &
--network-alias atlassian-jira \
--env JVM_MINIMUM_MEMORY="3g" --env JVM_MAXIMUM_MEMORY="6g" \
--env APP_OPTS="-Djira.home=/var/atlassian/appdata/jira -Djava.io.tmpdir=/opt/atlassian/jira/temp" \
-v /var/atlassian/appdata/jira:/var/atlassian/appdata/jira:rw \
-v /var/log/atlassian/jira:/opt/atlassian/jira/logs:rw \
--workdir /var/atlassian/appdata/jira \
--tmpfs /run --tmpfs /var/atlassian/appdata/jira/temp:uid=772 \
--user jira:atlassian \
--network atlassian \
--publish 127.0.0.1:8082:8080 \
--detach \
selfmade:java-8-jira-8.2.2 > /dev/null \
&& chown -R jira:atlassian /var/log/atlassian/jira \
&& docker logs --follow atlassian-jira > /var/log/atlassian/docker-output-jira.log 2>&1 &
Запускаем docker-контейнер с web-приложением "Atlassian Confluence":
# docker run --rm --name atlassian-confluence \
--network-alias atlassian-confluence \
--env APP_OPTS="-Xms1g -Xmx3g -Dconfluence.home=/var/atlassian/appdata/confluence -Djava.io.tmpdir=/opt/atlassian/confluence/temp" \
-v /var/atlassian/appdata/confluence:/var/atlassian/appdata/confluence:rw \
-v /var/log/atlassian/confluence:/opt/atlassian/confluence/logs:rw \
--workdir /var/atlassian/appdata/confluence \
--tmpfs /run --tmpfs /var/atlassian/appdata/confluence/temp:uid=773 --tmpfs /opt/atlassian/confluence/temp:uid=773 \
--user confluence:atlassian \
--network atlassian \
--publish 127.0.0.1:8083:8090 \
--ulimit nofile=32768:32768 \
--detach \
selfmade:java-8-confluence-6.15.6 > /dev/null \
&& chown -R confluence:atlassian /var/log/atlassian/confluence \
&& docker logs --follow atlassian-confluence > /var/log/atlassian/docker-output-confluence.log 2>&1 &
--network-alias atlassian-confluence \
--env APP_OPTS="-Xms1g -Xmx3g -Dconfluence.home=/var/atlassian/appdata/confluence -Djava.io.tmpdir=/opt/atlassian/confluence/temp" \
-v /var/atlassian/appdata/confluence:/var/atlassian/appdata/confluence:rw \
-v /var/log/atlassian/confluence:/opt/atlassian/confluence/logs:rw \
--workdir /var/atlassian/appdata/confluence \
--tmpfs /run --tmpfs /var/atlassian/appdata/confluence/temp:uid=773 --tmpfs /opt/atlassian/confluence/temp:uid=773 \
--user confluence:atlassian \
--network atlassian \
--publish 127.0.0.1:8083:8090 \
--ulimit nofile=32768:32768 \
--detach \
selfmade:java-8-confluence-6.15.6 > /dev/null \
&& chown -R confluence:atlassian /var/log/atlassian/confluence \
&& docker logs --follow atlassian-confluence > /var/log/atlassian/docker-output-confluence.log 2>&1 &
Запускаем docker-контейнер с web-приложением "Atlassian Crowd":
# docker run --rm --name atlassian-crowd \
--network-alias atlassian-crowd \
--env APP_OPTS="-Xms512m -Xmx1g -Dcrowd.home=/var/atlassian/appdata/crowd -Djava.io.tmpdir=/opt/atlassian/crowd/temp" \
-v /var/atlassian/appdata/crowd:/var/atlassian/appdata/crowd:rw \
-v /var/log/atlassian/crowd:/opt/atlassian/crowd/apache-tomcat/logs:rw \
--workdir /var/atlassian/appdata/crowd \
--tmpfs /run --tmpfs /var/atlassian/appdata/crowd/temp:uid=774 \
--user crowd:atlassian \
--network atlassian \
--publish 127.0.0.1:8084:8095 \
--detach \
selfmade:java-8-crowd-3.4.5 > /dev/null \
&& chown -R crowd:atlassian /var/log/atlassian/crowd \
&& docker logs --follow atlassian-crowd > /var/log/atlassian/docker-output-crowd.log 2>&1 &
--network-alias atlassian-crowd \
--env APP_OPTS="-Xms512m -Xmx1g -Dcrowd.home=/var/atlassian/appdata/crowd -Djava.io.tmpdir=/opt/atlassian/crowd/temp" \
-v /var/atlassian/appdata/crowd:/var/atlassian/appdata/crowd:rw \
-v /var/log/atlassian/crowd:/opt/atlassian/crowd/apache-tomcat/logs:rw \
--workdir /var/atlassian/appdata/crowd \
--tmpfs /run --tmpfs /var/atlassian/appdata/crowd/temp:uid=774 \
--user crowd:atlassian \
--network atlassian \
--publish 127.0.0.1:8084:8095 \
--detach \
selfmade:java-8-crowd-3.4.5 > /dev/null \
&& chown -R crowd:atlassian /var/log/atlassian/crowd \
&& docker logs --follow atlassian-crowd > /var/log/atlassian/docker-output-crowd.log 2>&1 &
Наблюдать за процессом загрузки и вообще состоянием web-приложений можно через журналы событий выдаваемых через STDOUT docker-контейнеров или непосредственно серверами приложений "Tomcat":
# tail /var/log/atlassian/docker-output-jirasd.log
# tail /var/log/atlassian/jirasd/catalina.out
# tail /var/log/atlassian/jirasd/catalina.out
После запуска java-приложений нелишним будет проконтролировать, в предусмотренные ли нами директории указывают параметры месторасположения исполняемых файлов, данных приложения и временных ресурсов (иногда настройки перекрываются и приходится поразбираться, что к чему). Например, для "Jira-SD" желаемый результат проверки таков:
# ps -wax | grep -i java | grep -i jirasd
... -Dcatalina.base=/opt/atlassian/jirasd -Dcatalina.home=/opt/atlassian/jirasd -Djava.io.tmpdir=/opt/atlassian/jirasd/temp ...
В итоге, после запуска всех четырёх приложений в перечне активных docker-контейнеров будет наблюдаться следующее:
# docker ps
IMAGE ... PORTS NAMES
selfmade:java-8-jirasd-4.2.2 ... 127.0.0.1:8081->8080/tcp atlassian-jirasd
selfmade:java-8-jira-8.2.2 ... 127.0.0.1:8082->8080/tcp atlassian-jira
selfmade:java-8-confluence-6.15.6 ... 127.0.0.1:8083->8090/tcp atlassian-confluence
selfmade:java-8-crowd-3.4.5 ... 127.0.0.1:8084->8095/tcp atlassian-crowd
selfmade:java-8-jirasd-4.2.2 ... 127.0.0.1:8081->8080/tcp atlassian-jirasd
selfmade:java-8-jira-8.2.2 ... 127.0.0.1:8082->8080/tcp atlassian-jira
selfmade:java-8-confluence-6.15.6 ... 127.0.0.1:8083->8090/tcp atlassian-confluence
selfmade:java-8-crowd-3.4.5 ... 127.0.0.1:8084->8095/tcp atlassian-crowd
Остановка docker-контейнеров элементарна:
# docker stop atlassian-jirasd
# docker stop atlassian-jira
# docker stop atlassian-confluence
# docker stop atlassian-crowd
# docker stop atlassian-jira
# docker stop atlassian-confluence
# docker stop atlassian-crowd
Реализация пакетного запуска посредством "Docker Compose".
Создаём директорию для размещения конфигурационных файлов "Docker Compose":
# mkdir /usr/local/etc/devops/compose
# cd /usr/local/etc/devops/compose
# cd /usr/local/etc/devops/compose
Проще всего будет использовать для единственного в нашей простой схеме конфигурационного файла имя "по умолчанию":
# vi ./docker-compose.yml
version: "3"
services:
atlassian-jirasd:
container_name: atlassian-jirasd
image: selfmade:java-8-jirasd-4.2.2
user: "jirasd:atlassian"
networks:
atlassian:
aliases:
- "atlassian-jirasd"
ports:
- "127.0.0.1:8081:8080"
environment:
JVM_MINIMUM_MEMORY: "5g"
JVM_MAXIMUM_MEMORY: "10g"
APP_OPTS: "-Djira.home=/var/atlassian/appdata/jirasd -Djava.io.tmpdir=/opt/atlassian/jirasd/temp"
working_dir: "/var/atlassian/appdata/jirasd"
volumes:
- "/var/atlassian/appdata/jirasd:/var/atlassian/appdata/jirasd:rw"
- "/var/log/atlassian/jirasd:/opt/atlassian/jirasd/logs:rw"
tmpfs:
- "/run"
- "/var/atlassian/appdata/jirasd/temp:uid=771"
atlassian-jira:
container_name: atlassian-jira
image: selfmade:java-8-jira-8.2.2
user: "jira:atlassian"
networks:
atlassian:
aliases:
- "atlassian-jira"
ports:
- "127.0.0.1:8082:8080"
environment:
JVM_MINIMUM_MEMORY: "3g"
JVM_MAXIMUM_MEMORY: "6g"
APP_OPTS: "-Djira.home=/var/atlassian/appdata/jira -Djava.io.tmpdir=/opt/atlassian/jira/temp"
working_dir: "/var/atlassian/appdata/jira"
volumes:
- "/var/atlassian/appdata/jira:/var/atlassian/appdata/jira:rw"
- "/var/log/atlassian/jira:/opt/atlassian/jira/logs:rw"
tmpfs:
- "/run"
- "/var/atlassian/appdata/jira/temp:uid=772"
atlassian-confluence:
container_name: atlassian-confluence
image: selfmade:java-8-confluence-6.15.6
user: "confluence:atlassian"
networks:
atlassian:
aliases:
- "atlassian-confluence"
ports:
- "127.0.0.1:8083:8090"
environment:
APP_OPTS: "-Xms1g -Xmx3g -Dconfluence.home=/var/atlassian/appdata/confluence -Djava.io.tmpdir=/opt/atlassian/confluence/temp"
working_dir: "/var/atlassian/appdata/confluence"
volumes:
- "/var/atlassian/appdata/confluence:/var/atlassian/appdata/confluence:rw"
- "/var/log/atlassian/confluence:/opt/atlassian/confluence/logs:rw"
tmpfs:
- "/run"
- "/var/atlassian/appdata/confluence/temp:uid=773"
ulimits:
nofile:
soft: 32768
hard: 32768
atlassian-crowd:
container_name: atlassian-crowd
image: selfmade:java-8-crowd-3.4.5
user: "crowd:atlassian"
networks:
atlassian:
aliases:
- "atlassian-crowd"
ports:
- "127.0.0.1:8084:8095"
environment:
APP_OPTS: "-Xms512m -Xmx1g -Dcrowd.home=/var/atlassian/appdata/crowd -Djava.io.tmpdir=/opt/atlassian/crowd/temp"
working_dir: "/var/atlassian/appdata/crowd"
volumes:
- "/var/atlassian/appdata/crowd:/var/atlassian/appdata/crowd:rw"
- "/var/log/atlassian/crowd:/opt/atlassian/crowd/apache-tomcat/logs:rw"
tmpfs:
- "/run"
- "/var/atlassian/appdata/crowd/temp:uid=774"
networks:
atlassian:
driver: bridge
internal: false
ipam:
driver: default
config:
- subnet: 100.127.255.0/24
services:
atlassian-jirasd:
container_name: atlassian-jirasd
image: selfmade:java-8-jirasd-4.2.2
user: "jirasd:atlassian"
networks:
atlassian:
aliases:
- "atlassian-jirasd"
ports:
- "127.0.0.1:8081:8080"
environment:
JVM_MINIMUM_MEMORY: "5g"
JVM_MAXIMUM_MEMORY: "10g"
APP_OPTS: "-Djira.home=/var/atlassian/appdata/jirasd -Djava.io.tmpdir=/opt/atlassian/jirasd/temp"
working_dir: "/var/atlassian/appdata/jirasd"
volumes:
- "/var/atlassian/appdata/jirasd:/var/atlassian/appdata/jirasd:rw"
- "/var/log/atlassian/jirasd:/opt/atlassian/jirasd/logs:rw"
tmpfs:
- "/run"
- "/var/atlassian/appdata/jirasd/temp:uid=771"
atlassian-jira:
container_name: atlassian-jira
image: selfmade:java-8-jira-8.2.2
user: "jira:atlassian"
networks:
atlassian:
aliases:
- "atlassian-jira"
ports:
- "127.0.0.1:8082:8080"
environment:
JVM_MINIMUM_MEMORY: "3g"
JVM_MAXIMUM_MEMORY: "6g"
APP_OPTS: "-Djira.home=/var/atlassian/appdata/jira -Djava.io.tmpdir=/opt/atlassian/jira/temp"
working_dir: "/var/atlassian/appdata/jira"
volumes:
- "/var/atlassian/appdata/jira:/var/atlassian/appdata/jira:rw"
- "/var/log/atlassian/jira:/opt/atlassian/jira/logs:rw"
tmpfs:
- "/run"
- "/var/atlassian/appdata/jira/temp:uid=772"
atlassian-confluence:
container_name: atlassian-confluence
image: selfmade:java-8-confluence-6.15.6
user: "confluence:atlassian"
networks:
atlassian:
aliases:
- "atlassian-confluence"
ports:
- "127.0.0.1:8083:8090"
environment:
APP_OPTS: "-Xms1g -Xmx3g -Dconfluence.home=/var/atlassian/appdata/confluence -Djava.io.tmpdir=/opt/atlassian/confluence/temp"
working_dir: "/var/atlassian/appdata/confluence"
volumes:
- "/var/atlassian/appdata/confluence:/var/atlassian/appdata/confluence:rw"
- "/var/log/atlassian/confluence:/opt/atlassian/confluence/logs:rw"
tmpfs:
- "/run"
- "/var/atlassian/appdata/confluence/temp:uid=773"
ulimits:
nofile:
soft: 32768
hard: 32768
atlassian-crowd:
container_name: atlassian-crowd
image: selfmade:java-8-crowd-3.4.5
user: "crowd:atlassian"
networks:
atlassian:
aliases:
- "atlassian-crowd"
ports:
- "127.0.0.1:8084:8095"
environment:
APP_OPTS: "-Xms512m -Xmx1g -Dcrowd.home=/var/atlassian/appdata/crowd -Djava.io.tmpdir=/opt/atlassian/crowd/temp"
working_dir: "/var/atlassian/appdata/crowd"
volumes:
- "/var/atlassian/appdata/crowd:/var/atlassian/appdata/crowd:rw"
- "/var/log/atlassian/crowd:/opt/atlassian/crowd/apache-tomcat/logs:rw"
tmpfs:
- "/run"
- "/var/atlassian/appdata/crowd/temp:uid=774"
networks:
atlassian:
driver: bridge
internal: false
ipam:
driver: default
config:
- subnet: 100.127.255.0/24
Запускаем пачку контейнеров, указывая явно имя конфигурационного файла:
# docker-compose -f /usr/local/etc/devops/compose/docker-compose.yml up --remove-orphans --build --force-recreate -d
Если используется имя конфигурационного файла "docker-compose.yml" и мы находимся в одной с ним директории, то строка запуска будет проще:
# docker-compose up --remove-orphans --build --force-recreate -d
Подключаемся к выдаваемому через STDOUT выводу docker-контейнера посредством "Docker Copmose":
# docker-compose logs --follow > /var/log/atlassian/docker-output-jirasd.log 2>&1 &
Останавливаем пачку docker-контейнеров и деактивируем виртуальную сеть, описанные в конфигурации "Docker Compose":
# docker-compose down
Примеры индивидуального управления контейнерами "Docker Compose".
Остановка контейнера ("сервиса" в терминологии "Docker Compose"):
# docker-compose stop atlassian-jirasd
Удаление остановленного контейнера и его ресурсов:
# docker-compose rm -f -v atlassian-jirasd
Опциональное обновление конфигурации контейнера с его пересборкой в случае изменения зависимостей:
# docker-compose up --no-start atlassian-jirasd
Запуск контейнера:
# docker-compose start atlassian-jirasd
Настройка автозагрузки "Docker Compose" посредством "Systemd".
Создаём файл описания параметров запуска и остановки пачки docker-контейнеров посредством "Docker Compose" в роли короткоживущего сервиса "Systemd":
# vi /etc/systemd/system/atlassian-apps-docker.service
[Unit]
Description=Atlassian Application in Docker Compose Service
Requires=network.target docker.service containerd.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/usr/local/etc/devops/compose
ExecStartPre=-/bin/bash -c 'chown -R jirasd:atlassian /var/log/atlassian/jirasd'
ExecStartPre=-/bin/bash -c 'chown -R jira:atlassian /var/log/atlassian/jira'
ExecStartPre=-/bin/bash -c 'chown -R confluence:atlassian /var/log/atlassian/confluence'
ExecStartPre=-/bin/bash -c 'chown -R crowd:atlassian /var/log/atlassian/crowd'
ExecStartPre=/usr/local/bin/docker-compose -f docker-compose.yml down --remove-orphans
ExecStart=/usr/local/bin/docker-compose -f /usr/local/etc/devops/compose/docker-compose.yml up --remove-orphans --build --force-recreate --detach
#
ExecStop=/usr/local/bin/docker-compose down
[Install]
WantedBy=multi-user.target
Description=Atlassian Application in Docker Compose Service
Requires=network.target docker.service containerd.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/usr/local/etc/devops/compose
ExecStartPre=-/bin/bash -c 'chown -R jirasd:atlassian /var/log/atlassian/jirasd'
ExecStartPre=-/bin/bash -c 'chown -R jira:atlassian /var/log/atlassian/jira'
ExecStartPre=-/bin/bash -c 'chown -R confluence:atlassian /var/log/atlassian/confluence'
ExecStartPre=-/bin/bash -c 'chown -R crowd:atlassian /var/log/atlassian/crowd'
ExecStartPre=/usr/local/bin/docker-compose -f docker-compose.yml down --remove-orphans
ExecStart=/usr/local/bin/docker-compose -f /usr/local/etc/devops/compose/docker-compose.yml up --remove-orphans --build --force-recreate --detach
#
ExecStop=/usr/local/bin/docker-compose down
[Install]
WantedBy=multi-user.target
Указываем "Systemd" перечитать и принять новую конфигурацию, а также явно активируем и запускаем новый сервис:
# systemctl daemon-reload
# systemctl enable atlassian-apps-docker.service
# systemctl start atlassian-apps-docker
# systemctl enable atlassian-apps-docker.service
# systemctl start atlassian-apps-docker
Смотрим журнал событий "Systemd" если "что-то пошло не так":
# systemctl status atlassian-apps-docker.service
# journalctl -xe
# journalctl -xe
Об эксплуатации и лицензировании.
Уже на этом этапе мы должны получить в распоряжение вполне работоспособные web-приложения "Atlassian Jira/Jira-SD/Confluence/Crowd". Их настройка практически полностью реализована через web-интерфейс и рассматриваться здесь не будет.
В процессе первичной конфигурации потребуется предоставить лицензионный ключ, который на сайте производителя запросто выдаётся на тестовый период до одного месяца или приобретается. Идентификатор "Server ID", на основе которого генерируется лицензионный ключ, можно найти в журнале web-приложения (например "/var/log/atlassian/jirasd/catalina.out").
При желании идентификатор продукта "Server ID" можно выяснить запросом к его "базе данных":
jirasd=# SELECT * FROM propertystring WHERE id IN (SELECT id FROM propertyentry WHERE property_key='jira.sid.key');
На практике после покупки лицензии её можно использовать неограниченно долго, переезжая с сервера на сервер и обновляя компоненты вручную, а вот возможность простого обновления посредством web-интерфейса ограничена сроком действия лицензии.
Выборка данных web-приложений "Atlassian" для переноса на новую площадку.
Прежде всего настоятельно рекомендую создать или убедиться в наличии пользователя, аутентифицируемого локально, средствами самого web-приложения "Atlassian", а не путём запроса сторонних сервисов вроде LDAP или AD - следует учитывать, что связь web-приложения с внешними сервисами аутентификации на новом месте запуска может оказаться разорванной и потребуется доступ к настройкам, для чего в обязательном порядке необходимо подтверждение подлинности и прав доступа.
При миграции переносится на новый сервер (и обязательно сохраняется в виде резервной копии) следующее:
1. SQL-дамп БД (в нашем случае это "PostgreSQL");
2. Файлы данных приложения (/var/atlassian/appdata/jirasd).
2. Файлы данных приложения (/var/atlassian/appdata/jirasd).
Заранее нужно узнать (например, командой "du -sh ./"), насколько объёмны исходные данные и приготовить для них достаточно места на целевом сервере.
Выгружаем "логический дамп" БД с сохранением его прямо на целевом сервере, используя для этого SSH-туннель:
# sudo -u postgres pg_dump --verbose --no-owner --no-acl --dbname=jirasd | sed -E "s#^(DROP\ EXTENSION|CREATE\ EXTENSION|COMMENT\ ON\ EXTENSION)#-- \1#gI" | sed -E "s#^(DROP\ SCHEMA|CREATE\ SCHEMA|COMMENT\ ON\ SCHEMA|GRANT)#-- \1#gI" | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data; cat > /var/tmp/atlassian_data/jirasd.sql)"
# sudo -u postgres pg_dump --verbose --no-owner --no-acl --dbname=jira | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data; cat > /var/tmp/atlassian_data/jira.sql)"
# sudo -u postgres pg_dump --verbose --no-owner --no-acl --dbname=confluence | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data; cat > /var/tmp/atlassian_data/confluence.sql)"
# sudo -u postgres pg_dump --verbose --no-owner --no-acl --dbname=crowd | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data; cat > /var/tmp/atlassian_data/crowd.sql)"
# sudo -u postgres pg_dump --verbose --no-owner --no-acl --dbname=jira | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data; cat > /var/tmp/atlassian_data/jira.sql)"
# sudo -u postgres pg_dump --verbose --no-owner --no-acl --dbname=confluence | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data; cat > /var/tmp/atlassian_data/confluence.sql)"
# sudo -u postgres pg_dump --verbose --no-owner --no-acl --dbname=crowd | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data; cat > /var/tmp/atlassian_data/crowd.sql)"
Копируем основной объём файлов данных и настроек приложений:
# cd /var/atlassian/appdata/jirasd ; tar cfv - * | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data/jirasd; cd /var/tmp/atlassian_data/jirasd; tar xf - )"
# cd /var/atlassian/appdata/jira ; tar cfv - * | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data/jira; cd /var/tmp/atlassian_data/jira; tar xf - )"
# cd /var/atlassian/appdata/confluence ; tar cfv - * | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data/confluence; cd /var/tmp/atlassian_data/confluence; tar xf - )"
# cd /var/atlassian/appdata/crowd ; tar cfv - * | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data/crowd; cd /var/tmp/atlassian_data/crowd; tar xf - )"
# cd /var/atlassian/appdata/jira ; tar cfv - * | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data/jira; cd /var/tmp/atlassian_data/jira; tar xf - )"
# cd /var/atlassian/appdata/confluence ; tar cfv - * | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data/confluence; cd /var/tmp/atlassian_data/confluence; tar xf - )"
# cd /var/atlassian/appdata/crowd ; tar cfv - * | ssh user@target.example.net "(mkdir -p /var/tmp/atlassian_data/crowd; cd /var/tmp/atlassian_data/crowd; tar xf - )"
Применение данных web-приложений "Atlassian" на новой площадке.
Прежде всего останавливаем docker-контейнер с целевым приложением "Atlassian", чтобы оно преждевременно не начало влиять на вносимые в его хранилища данные, например:
# docker-compose -f /usr/local/etc/devops/compose/docker-compose.yml stop atlassian-jirasd
...или так:
# cd /usr/local/etc/devops/compose
# docker-compose stop atlassian-jira
# docker-compose stop atlassian-confluence
# docker-compose stop atlassian-crowd
# docker-compose stop atlassian-jira
# docker-compose stop atlassian-confluence
# docker-compose stop atlassian-crowd
Опционально создаём "базы данных":
# su - postgres
postgres@jira:~$ psql
postgres@jira:~$ psql
postgres=# SELECT pg_terminate_backend (pg_stat_activity.pid) FROM pg_stat_activity WHERE datname = 'jirasd';
postgres=#
postgres=# DROP DATABASE IF EXISTS jirasd;
postgres=# CREATE DATABASE jirasd WITH OWNER jirasd ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# DROP DATABASE IF EXISTS jira;
postgres=# CREATE DATABASE jira WITH OWNER jira ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# DROP DATABASE IF EXISTS confluence;
postgres=# CREATE DATABASE confluence WITH OWNER confluence ENCODING 'UTF8' LC_COLLATE 'ru_RU.UTF-8' LC_CTYPE 'ru_RU.UTF-8' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# DROP DATABASE IF EXISTS crowd;
postgres=# CREATE DATABASE crowd WITH OWNER crowd ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=# \q
postgres=#
postgres=# DROP DATABASE IF EXISTS jirasd;
postgres=# CREATE DATABASE jirasd WITH OWNER jirasd ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# DROP DATABASE IF EXISTS jira;
postgres=# CREATE DATABASE jira WITH OWNER jira ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# DROP DATABASE IF EXISTS confluence;
postgres=# CREATE DATABASE confluence WITH OWNER confluence ENCODING 'UTF8' LC_COLLATE 'ru_RU.UTF-8' LC_CTYPE 'ru_RU.UTF-8' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=#
postgres=# DROP DATABASE IF EXISTS crowd;
postgres=# CREATE DATABASE crowd WITH OWNER crowd ENCODING 'UTF8' LC_COLLATE 'C' LC_CTYPE 'C' TEMPLATE template0 CONNECTION LIMIT -1;
postgres=# \q
Развёртываем в подготовленные пустые БД "логические дампы" (обязательно указывая опцией "ON_ERROR_STOP" прерывать развёртывание на первой же ошибке, чтобы можно было её сразу увидеть):
# sudo -u jirasd psql -v ON_ERROR_STOP=1 jirasd < /var/tmp/atlassian_data/jirasd.sql >> /var/tmp/atlassian_data/psql-jirasd.log 2>&1
# sudo -u jira psql -v ON_ERROR_STOP=1 < /var/tmp/atlassian_data/jira.sql >> /var/tmp/atlassian_data/psql-jira.log 2>&1
# sudo -u confluence psql -v ON_ERROR_STOP=1 confluence < /var/tmp/atlassian_data/confluence.sql >> /var/tmp/atlassian_data/psql-confluence.log 2>&1
# sudo -u crowd psql -v ON_ERROR_STOP=1 crowd < /var/tmp/atlassian_data/crowd.sql >> /var/tmp/atlassian_data/psql-crowd.log 2>&1
# sudo -u jira psql -v ON_ERROR_STOP=1 < /var/tmp/atlassian_data/jira.sql >> /var/tmp/atlassian_data/psql-jira.log 2>&1
# sudo -u confluence psql -v ON_ERROR_STOP=1 confluence < /var/tmp/atlassian_data/confluence.sql >> /var/tmp/atlassian_data/psql-confluence.log 2>&1
# sudo -u crowd psql -v ON_ERROR_STOP=1 crowd < /var/tmp/atlassian_data/crowd.sql >> /var/tmp/atlassian_data/psql-crowd.log 2>&1
Заменяем директории файловых ресурсов и явно передаём их во владение выделенных для этого пользователей:
# mv /var/atlassian/appdata/jirasd /var/atlassian/appdata/jirasd_backup
# mv /var/tmp/atlassian_data/jirasd /var/atlassian/appdata/jirasd
# chown -R jirasd:atlassian /var/atlassian/appdata/jirasd
# chmod -R o-rw /var/atlassian/appdata/jirasd
# mv /var/tmp/atlassian_data/jirasd /var/atlassian/appdata/jirasd
# chown -R jirasd:atlassian /var/atlassian/appdata/jirasd
# chmod -R o-rw /var/atlassian/appdata/jirasd
# mv /var/atlassian/appdata/jira /var/atlassian/appdata/jira_backup
# mv /var/tmp/atlassian_data/jira /var/atlassian/appdata/jira
# chown -R jira:atlassian /var/atlassian/appdata/jira
# chmod -R o-rw /var/atlassian/appdata/jira
# mv /var/tmp/atlassian_data/jira /var/atlassian/appdata/jira
# chown -R jira:atlassian /var/atlassian/appdata/jira
# chmod -R o-rw /var/atlassian/appdata/jira
# mv /var/atlassian/appdata/confluence /var/atlassian/appdata/confluence_backup
# mv /var/tmp/atlassian_data/confluence /var/atlassian/appdata/confluence
# chown -R confluence:atlassian /var/atlassian/appdata/confluence
# chmod -R o-rw /var/atlassian/appdata/confluence
# mv /var/tmp/atlassian_data/confluence /var/atlassian/appdata/confluence
# chown -R confluence:atlassian /var/atlassian/appdata/confluence
# chmod -R o-rw /var/atlassian/appdata/confluence
# mv /var/atlassian/appdata/crowd /var/atlassian/appdata/crowd_backup
# mv /var/tmp/atlassian_data/crowd /var/atlassian/appdata/crowd
# chown -R crowd:atlassian /var/atlassian/appdata/crowd
# chmod -R o-rw /var/atlassian/appdata/crowd
# mv /var/tmp/atlassian_data/crowd /var/atlassian/appdata/crowd
# chown -R crowd:atlassian /var/atlassian/appdata/crowd
# chmod -R o-rw /var/atlassian/appdata/crowd
Подготовка БД для миграции web-приложений "Atlassian".
Наверняка при миграции понадобиться изменить параметры подключения приложений к СУБД.
Корректируем параметры подключения к БД "Jira-SD":
# vi /var/atlassian/appdata/jirasd/dbconfig.xml
....
<!-- Database connection pool recommended 30 connections. -->
<pool-max-size>30</pool-max-size>
....
<url>jdbc:postgresql://pgbd.example.net:5432/jirasd</url>
<username>jirasd</username>
<password>jirasdPassword</password>
....
<!-- Database connection pool recommended 30 connections. -->
<pool-max-size>30</pool-max-size>
....
<url>jdbc:postgresql://pgbd.example.net:5432/jirasd</url>
<username>jirasd</username>
<password>jirasdPassword</password>
....
Корректируем параметры подключения к БД "Jira":
# vi /var/atlassian/appdata/jira/dbconfig.xml
....
<!-- Database connection pool recommended 30 connections. -->
<pool-max-size>30</pool-max-size>
....
<url>jdbc:postgresql://pgdb.example.net:5432/jira</url>
<username>jira</username>
<password>jiraPassword</password>
....
<!-- Database connection pool recommended 30 connections. -->
<pool-max-size>30</pool-max-size>
....
<url>jdbc:postgresql://pgdb.example.net:5432/jira</url>
<username>jira</username>
<password>jiraPassword</password>
....
Корректируем параметры подключения к БД "Confluence":
# vi /var/atlassian/appdata/confluence/confluence.cfg.xml
....
<!-- Database connection pool recommended 60 connections. -->
<property name="hibernate.c3p0.max_size">60</property>
....
<property name="hibernate.connection.url">jdbc:postgresql://pgdb.example.net:5432/confluence</property>
<property name="hibernate.connection.username">confluence</property>
<property name="hibernate.connection.password">confluencePassword</property>
....
<!-- Database connection pool recommended 60 connections. -->
<property name="hibernate.c3p0.max_size">60</property>
....
<property name="hibernate.connection.url">jdbc:postgresql://pgdb.example.net:5432/confluence</property>
<property name="hibernate.connection.username">confluence</property>
<property name="hibernate.connection.password">confluencePassword</property>
....
Корректируем параметры подключения к БД "Crowd":
# vi /var/atlassian/appdata/crowd/crowd.cfg.xml
....
<property name="hibernate.connection.url">jdbc:postgresql://pgdb.example.net:5432/crowd</property>
<property name="hibernate.connection.username">crowd</property>
<property name="hibernate.connection.password">crowdPassword</property>
....
<property name="hibernate.connection.url">jdbc:postgresql://pgdb.example.net:5432/crowd</property>
<property name="hibernate.connection.username">crowd</property>
<property name="hibernate.connection.password">crowdPassword</property>
....
Учитывая то, что точка подключения к СУБД относительно приложений "Atlassian" изменилась - они теперь в docker-контейнерах - имя сервера "localhost" потребуется изменить на FQDN БД, например "pgdb.example.net".
После размещения данных на новой площадке и корректировки параметров подключения к СУБД можно попробовать запустить контейнеры с web-приложениями:
# docker-compose -f /usr/local/etc/devops/compose/docker-compose.yml start atlassian-jirasd
...или так:
# cd /usr/local/etc/devops/compose
# docker-compose start atlassian-jira
# docker-compose start atlassian-confluence
# docker-compose start atlassian-crowd
# docker-compose start atlassian-jira
# docker-compose start atlassian-confluence
# docker-compose start atlassian-crowd
Восстановление административного доступа к уже настроенному web-приложению.
Если, несмотря на предупреждение о необходимости иметь локального суперпользователя выше, аутентифицироваться в перенесённом на новое место web-приложении "Atlassian Jira/Jira-SD" не получается (недоступен внешний сервис вроде LDAP или AD, например), возможно придётся добираться до требуемого путём прямых манипуляций с содержимым "базы данных".
В "Jira/Jira-SD" версии более "7.0" предусмотрена возможность запуска в режиме восстановления, когда автоматически создаётся виртуальный суперпользователь "recovery_admin", от имени которого можно произвести все необходимые корректировки конфигурации.
Для этого достаточно в строке запуска web-приложения добавить параметр "-Datlassian.recovery.password=tempPassword", задающий пароль виртуального пользователя. Перезапускаем web-приложение и в общей форме аутентифицирцемся от имени пользователя "recovery_admin" с указанным в параметре "atlassian.recovery.password" паролем. После входа можно завести полноценного пользователя в локальной базе и ввести его в желаемые группы (например: "jira-administrators", "confluence-administrators", "crowd-administrators").
Резервное копирование.
Если получилось развернуть сервис следуя этой инструкции, очевидно для последующего восстановления его достаточно сохранять все затрагиваемые здесь ресурсы. На практике можно ограничиться нижеследующим перечнем:
1. Дамп БД (в нашем случае это "PostgreSQL");
2. Файлы данных приложения (/var/atlassian/appdata/jirasd);
3. Конфигурационные файлы docker-образов и "Docker Compose" (/usr/local/etc/devops);
4. Общесистемные конфигурационные файлы (/etc).
2. Файлы данных приложения (/var/atlassian/appdata/jirasd);
3. Конфигурационные файлы docker-образов и "Docker Compose" (/usr/local/etc/devops);
4. Общесистемные конфигурационные файлы (/etc).