UMGUM.COM 

Atlassian + Docker ( Развёртывание в среде исполнения "Docker" комплекта web-приложений "Atlassian", таких как "Jira", "Jira Service Desk", "Confluence" и "Crowd". )

24 июня 2019

OS: "Linux Debian 9", "Linux Ubuntu Server 18 LTS".
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".

Учитывая то, что в Апреля 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. Установка и настройка фронтального web-прокси "Nginx";
3. Установка сервера СУБД "PostgreSQL" и создание "баз данных";
4. Создание для web-приложений "Atlassian" специфичных docker-контейнеров;
5. Подготовка конфигурации и пробные запуски web-приложений;
6. Формирование YAML-конфигурации "Docker Compose";
7. Миграция данных из уже работающих инстансов.


Подготовка системного окружения и установка сопутствующего ПО.

Обновляем программное обеспечение и устанавливаем набор полезных утилит:

# apt update && apt full-upgrade
# apt-get install aptitude coreutils sudo acl psmisc net-tools dnsutils host htop iotop bmon tree findutils pigz rsync mc vim pwgen ntpdate dirmngr

Удаляем ненужные нам подсистемы:

# aptitude purge snapd lxd lxcfs open-iscsi mdadm

Обеспечим точность показаний системных даты и времени. Прежде всего установим верный часовой пояс:

# echo "Asia/Novosibirsk" > /etc/timezone
# rm /etc/localtime && ln -sf /usr/share/zoneinfo/Asia/Novosibirsk /etc/localtime

Разово синхронизируемся с каким-нибудь источником точного времени:

# ntpdate 0.asia.pool.ntp.org && hwclock --systohc --utc

Для корректировки отклонений внутренних часов заведём регулярную (через три часа) сверку с общемировым временем:

# vi /etc/crontab

....
0 */3 * * * root ntpdate asia.pool.ntp.org &

Ненужную нам подсистему синхронизации времени "Systemd" отключаем:

# systemctl disable systemd-timesyncd && systemctl stop systemd-timesyncd

Деактивируем и останавливаем ненужную нам службу DNS-кеширования:

# systemctl disable systemd-resolved && systemctl stop systemd-resolved

Подставляем вместо ссылки на конфигурационный файл "Systemd-Resolved" с единственным локальным кеширующим DNS-сервером свой файл, описывая там полноценную DNS-конфигурацию:

# rm /etc/resolv.conf && vi /etc/resolv.conf

nameserver 123.456.789.10
nameserver 8.8.8.8
search example.net

Проверить работу DNS-резольвинга можно элементарно, запросив разрешение в IP-адрес какого-нибудь действительно существующего FQDN посредством следующей команды:

# dig example.net

В ответе утилиты "dig" имеется параметр "SERVER", в котором указано, какой именно DNS-сервер использовался для "резольвинга". Если несколько раз подряд вызвать приведённую выше команду, то в параметре "SERVER" побывают все указанные в локальной DNS-конфигурации "nameserver"-ы - обычно при каждом новом запросе выбирается новый сервер из списка, по алгоритму "round-robin".

Для работы с русскоязычными данными в "Java" и "PostgreSQL", с учётом специфичных для них правил сортировок и форматирования, потребуется явно проконтролировать наличие поддержки несущей операционной системой соответствующих "локалей":

# dpkg-reconfigure locales

Установка системы контейнеризации приложений "Docker".

По состоянию на начало 2019-го несколько поколений "Docker" стали устаревшими и неподдерживаемыми: их дистрибутивы как правило называются "docker", "docker.io" или "docker-engine". Есть смысл профилактически зачистить систему от них:

# apt remove docker docker-engine docker.io containerd runc

Современный "Docker" выпускается в двух вариантах: "Community Edition" (бесплатная) и "Enterprise Edition" (проприетарная, платная). Мы будем работать со свободно распространяемым дистрибутивом, именуемым в APT-системе "docker-ce".

В централизованных репозиториях стабильных версий "Debian/Ubuntu" актуальные пакеты "Docker" отсутствуют - оно и понятно, технология переднего края, непрерывно развивающаяся. Единственный простой способ добычи дистрибутива в подключении репозитория разработчиков и установке оттуда.

Прежде всего обеспечим наличие в системе утилит, необходимых для ручного подключения дополнительных APT-репозиториев:

# apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common

Перед подключением нового APT-репозитория скачаем и применим PGP-ключ, которым подписано содержимое репозитория:

# curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo "$ID")/gpg | sudo apt-key add -

Создаём выделенный конфигурационный файл с описанием подключаемого APT-репозитория:

# echo -e "# Official APT-repository Docker-CE\ndeb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable" >> /etc/apt/sources.list.d/docker.list

Обновляем сведения о доступном программном обеспечении и устанавливаем базовый набор подсистем "Docker":

# apt-get update && apt-get install docker-ce docker-ce-cli containerd.io

Обращаю внимание на то, что в качестве прослойки абстракции связей контейнеров "Docker-CE" с ядром "Linux" сейчас используется открытая реализация "Open Container Initiative (containerd.io)", сменившая использовавшуюся в предыдущем поколении "Docker" самодельную подсистему "docker-containerd".

Установка утилиты автоматизации запуска docker-контейнеров "Docker Compose".

Утилита "Docker Compose" написана на языке "Python" и представляет собой единственный файл-скрипт (размером в 16MB), обрабатывающий передаваемый ему конфигурационный YAML-файл и взаимодействующий с "Docker (engine)". Установка тривиальна и заключается в загрузке файла.

Утилита пока не вышла на уровень, когда какой-то из версий можно задать статус "stable" или "latest" - так что первым делом есть смысл сходить на сайт разработчиков, поискать прямую ссылку на последний проверенный релиз и уже его загружать:

# curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# chmod +x /usr/local/bin/docker-compose

Для совместимости обеспечиваем возможность запуска утилиты из двух типичных месторасположений исполняемых файлов:

# ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

Есть смысл сразу проверить, запускается ли утилита (как минимум убедимся, что интерпретатор "Python" присутствует в операционной системе):

$ docker-compose --version

Подготовка несущей файловой структуры для приложений "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/log/atlassian/jirasd
# 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

Передадим директории файловых ресурсов приложений выделенным для этого пользователям:

# 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

Установка фронтального 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

Сразу слегка преднастроим web-сервер:

# vi /etc/nginx/nginx.conf

user www-data www-data;
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

Настройка фронтального 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;
  }
}

Конфигурации для остальных инстансов аналогичные, различающиеся только обслуживаемыми доменными именами и портами проксирования:

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

Активируем для "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

Установка СУБД "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

Изначально СУБД принимает и обслуживает запросы через локальный "файловый сокет" и сетевое подключение "локальной петли" на принятом по умолчанию адресу порта TCP:5432. Следующими настройками я перевожу сервер "PostgreSQL" на прослушивание иного TCP-порта потому, что перед СУБД будет работать оптимизатор SQL-соединений "PgBouncer", с точкой входа как раз на привычном пользователям TCP:5432. Удобнее наружу показывать то, что не потребует перенастройки клиентских приложений (при этом параметры подключения через локальный "сокет" я менять не буду, для сохранения простоты обращений к БД от имени суперпользователя "postgres", при конфигурировании):

# vi /etc/postgresql/9.6/main/postgresql.conf

....
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
....

Принятие внесённых изменений потребует перезапуска сервера СУБД:

# /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

Учитывая то, что сервер СУБД скорее всего уже имеет набор сертификатов для шифрования соединений с клиентами, при настройке 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

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

В таблице выше заметны пустые поля параметра "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

Создание 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

По правилам сборки 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

# 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" находятся в свободном доступе по следующим адресам:


Ссылки на файлы динамические, так что приводить их здесь нет смысла - но общий принцип выбора и сохранения дистрибутива такой:

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 ./

Заготавливаем унифицированный для всех 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 "$@"

Пишем сценарий для 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"]

# 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"]

# 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"]

# 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

# 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
....

Распределение оперативной памяти в соответствии с потребностями приложений.

По умолчаниям JVM каждому запускаемому java-приложению выделяет 64MB ОЗУ, что на практике чрезвычайно мало. В предустановленной конфигурации для приложений "Atlassian" запрашивается от 384M до 768M, но и этого недостаточно (с таким лимитом стартует и будет полноценно работать разве что свежеустановленная или малоиспользуемая "Jira"). В общем случае Java-приложения являются самым прожорливым потребителем ОЗУ на сервере, так что каждому инстансу есть смысл нарезать памяти по максимуму, оставив до четверти-трети от всего объёма под СУБД и системные нужды.

Будем отталкиваться от того, что у нашего сервера ОЗУ на 32GB, которое нужно поделить между следующими сервисам (перечень по убыванию наблюдаемых требований к ОЗУ). Треть отрежем для СУБД и системных нужд, а 20GB распределим в соответствии с типичной нагрузкой на каждое приложение:

"Jira-SD"    - 10GB,
"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 &

Запускаем 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 &

Запускаем 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 &

Запускаем 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 &

Наблюдать за процессом загрузки и вообще состоянием web-приложений можно через журналы событий выдаваемых через STDOUT docker-контейнеров или непосредственно серверами приложений "Tomcat":

# tail /var/log/atlassian/docker-output-jirasd.log
# 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

Остановка docker-контейнеров элементарна:

# docker stop atlassian-jirasd
# 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

Проще всего будет использовать для единственного в нашей простой схеме конфигурационного файла имя "по умолчанию":

# 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

Запускаем пачку контейнеров, указывая явно имя конфигурационного файла:

# 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":

# docker logs --follow atlassian-jirasd > /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-контейнера наладить съём сообщений с его STDOUT в файл журнала событий:

# docker logs --follow atlassian-jirasd > /var/log/atlassian/docker-output-jirasd.log 2>&1 &

Настройка автозагрузки "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
#
ExecStartPost=/bin/sleep 5
ExecStartPost=-/bin/bash -c '/usr/bin/docker logs --follow atlassian-jirasd > /var/log/atlassian/docker-output-jirasd.log 2>&1 &'
ExecStartPost=-/bin/bash -c '/usr/bin/docker logs --follow atlassian-jira > /var/log/atlassian/docker-output-jira.log 2>&1 &'
ExecStartPost=-/bin/bash -c '/usr/bin/docker logs --follow atlassian-confluence > /var/log/atlassian/docker-output-confluence.log 2>&1 &'
ExecStartPost=-/bin/bash -c '/usr/bin/docker logs --follow atlassian-crowd > /var/log/atlassian/docker-output-crowd.log 2>&1 &'
#
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

Смотрим журнал событий "Systemd" если "что-то пошло не так":

# systemctl status atlassian-apps-docker.service
# 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).

Заранее нужно узнать (например, командой "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)"

Копируем основной объём файлов данных и настроек приложений:

# 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 - )"

Применение данных 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

Опционально создаём "базы данных":

# su - postgres
postgres@pure:~$ 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

Развёртываем в подготовленные пустые БД "логические дампы" (обязательно указывая опцией "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

Заменяем директории файловых ресурсов и явно передаём их во владение выделенных для этого пользователей:

# 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/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/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/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

Подготовка БД для миграции 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>
....

Корректируем параметры подключения к БД "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>
....

Корректируем параметры подключения к БД "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>
....

Корректируем параметры подключения к БД "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>
....

Учитывая то, что точка подключения к СУБД относительно приложений "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

Восстановление административного доступа к уже настроенному 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).


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )