В предыдущей части публикации я рассказал, как запустить демонстрационный Web-отчет FastReport.Mono. Сейчас же, предлагаю перейти на новый уровень и «завернуть» все необходимые компоненты в Docker-контейнеры.
Пойми, на небесах только и говорят, что о море. Как оно бесконечно прекрасно…
Последние годы все и везде говорят о Docker, ведь контейнеры это стильно, модно и
Как уже отмечалось, для корректной работы FastReport.Mono нам может потребоваться 2 контейнера:
- контейнер с Apache 2.4
- контейнер с X-сервером
Оба контейнера создаем на базе образа debian:stretch из репозитория Docker.
Контейнер FRMono
Первый контейнер, как уже упоминалось, будет содержать в себе Apache 2.4. Кроме того, при сборке сразу же добавим туда Mono и FastRepor.
В принципе, ни чего не мешает расширить Dockerfile и организовать автоматическую установку еще и какого-нибудь Oracle Instant Client, но т.к. статья посвящена именно демонстрационному Web-отчету — нам это не нужно.
Dockerfile
FROM debian:stretch
ENV TZ Europe/Moscow
ENV DEBIAN_FRONTEND noninteractive
ENV DISPLAY :1
WORKDIR /
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN echo "nameserver 8.8.8.8" > /etc/resolv.conf
RUN ["mkdir", "-p", "/opt/fastreport/htdocs/bin"]
RUN apt-get update && apt-get -y install apt-utils && apt-get -y install unzip && apt-get -y install mono-complete mono-xsp apache2 libapache2-mod-mono
# Apache setup
COPY 001-mono.conf /etc/apache2/sites-available/001-mono.conf
RUN /usr/sbin/update-rc.d apache2 disable && /usr/sbin/a2dismod mod_mono_auto && /usr/sbin/a2dissite 000-default && /usr/sbin/a2ensite 001-mono
# FastReport.Mono setup
# Download official Demo
ADD https://www.fastreport.ru/public_download/frmono_demo.zip /tmp/frmono_demo.zip
# Or add from local filesystem
#COPY frmono_demo.zip /tmp/frmono_demo.zip
# Extract and copy to destinations
RUN ["unzip", "/tmp/frmono_demo.zip", "-d", "/tmp/frmono.demo"]
RUN cp -rp /tmp/frmono.demo/Demos/C#/Web/* /opt/fastreport/htdocs
RUN cp /tmp/frmono.demo/FastReport.*.dll /opt/fastreport/htdocs/bin
RUN chown -R www-data:www-data /opt/fastreport
# Volume for X11 unix socket
VOLUME /tmp/.X11-unix
# Start Apache in foreground to prevent container exit
CMD ["/usr/sbin/apachectl", "-DFOREGROUND"]
Собственно, ничего сложного или хитрого в настройке нет. Главное, на что стоит обратить внимание: не нужно запускать Apache как фоновый процесс, необходимо выставить его на передний план. Иначе контейнер завершит свою работу сразу после запуска.
Конфигурационный файл виртуального хоста для Apache, в принципе, является урезанной версией конфига, приведенного в первой части публикации. Однако, есть и некоторые отличия. Это связано с тем, что будет использоваться немного иная структура каталогов (с размещением библиотек в директории bin).
Какой-либо особой разницы в данных подходах, лично я не вижу. Кроме того, в контейнере можно смело использовать 80 порт.
001-mono.conf
<VirtualHost *:80>
DocumentRoot "/opt/fastreport/htdocs"
<IfModule mod_mono.c>
MonoUnixSocket FrSite /tmp/.mod_mono_server
MonoServerPath FrSite /usr/bin/mod-mono-server4
MonoPath FrSite /usr/lib/mono/4.5:/usr/lib:/usr/lib/mono/4.0
AddMonoApplications FrSite "/:/opt/fastreport/htdocs"
MonoAutoApplication Disabled
MonoDocumentRootDir /opt/fastreport/htdocs
MonoDebug false
MonoSetEnv FrSite DISPLAY=:1;HOME=/opt/fastreport
AddHandler mono .aspx .ascx .asax .ashx .config .cs .asmx .axd
</IfModule>
<Directory "/opt/fastreport/htdocs">
Require all granted
Options Indexes FollowSymLinks MultiViews
AllowOverride All
<IfModule mod_mono.c>
SetHandler mono
MonoSetServerAlias FrSite
DirectoryIndex Default.aspx
</IfModule>
</Directory>
<Directory "/opt/fastreport/htdocs/bin">
Require all denied
</Directory>
</VirtualHost>
Запускаем процесс сборки:
docker build -t debian-stretch-mono:latest .
Контейнер X11Dummy
В первой части этой серии публикаций я уже упоминал, что для использования System.Windows.Forms необходимо наличие X-сервера. Т.к. в контейнере с Apache, Mono и FastReport «иксов» нет, нам надо их откуда-нибудь взять.
Возможны два варианта:
- использовать X-сервер, который запущен в ОС хоста
- использовать отдельный контейнер с X-сервером
Первый вариант потребует различной настройки, в зависимости от используемой ОС. Ведь в Linux это может быть, например, X.org, в Mac OS — XQuartz, а в Windows придется доставлять Xming
Второй вариант более соответствует концепции Docker и позволяет создать легко переносимую и не зависящую от ОС хоста систему. Взаимодействие между контейнерами может быть осуществлено через tcp или unix-сокет. На мой взгляд, вариант с использованием unix-сокета отлично подходит для решения данной задачи.
Dockerfile
FROM debian:stretch
ENV TZ Europe/Moscow
ENV DEBIAN_FRONTEND noninteractive
ENV DISPLAY :1
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
RUN echo "nameserver 8.8.8.8" > /etc/resolv.conf
RUN apt-get update && apt-get -y install apt-utils && apt-get -y install xserver-xorg-video-dummy x11-apps
VOLUME /tmp/.X11-unix
COPY xorg.mini.conf /etc/X11/xorg.conf
CMD /usr/bin/Xorg -noreset +extension GLX +extension RANDR +extension RENDER -logfile /tmp/xdummy.log -config /etc/X11/xorg.conf $DISPLAY
Если внимательно изучить Docerfile под спойлером, то стоит обратить внимание на два момента. Первый — использование видеодрайвера из пакета xserver-xorg-video-dummy. «Пустой» драйвер, который сообщает X-серверу, что выводит изображение, но на самом деле ничего не делает.
Второй — установка переменной окружения DISPLAY в значение, отличное от привычного по всевозможным инструкциям :0. Исключительно мера предосторожности, если вдруг решите выставить UNIX-сокет через /tmp ОС хоста.
Конфигурационный файл X-сервера, это вариация на тему вот этого конфига, гуляющего с различными изменениями по всей сети. Мне попалась очень длинная версия с кучей Modeline'ов (увы, не смог вспомнить источник) и я ее постарался максимально ужать (хотя, думаю еще можно).
xorg.mini.conf
Section "ServerFlags"
Option "DontVTSwitch" "true"
Option "AllowMouseOpenFail" "true"
Option "PciForceNone" "true"
Option "AllowEmptyInput" "true"
Option "AutoEnableDevices" "false"
Option "AutoAddDevices" "false"
EndSection
Section "Device"
Identifier "dummy_videocard"
Driver "dummy"
DacSpeed 600
Option "ConstantDPI" "true"
VideoRam 256000
EndSection
Section "Monitor"
Identifier "dummy_monitor"
HorizSync 1.0 - 2000.0
VertRefresh 1.0 - 200.0
Modeline "1920x1080" 23.53 1920 1952 2040 2072 1080 1106 1108 1135
Modeline "1280x1024" 31.50 1280 1312 1424 1456 1024 1048 1052 1076
Modeline "1280x720" 59.42 1280 1312 1536 1568 720 735 741 757
Modeline "1024x768" 18.71 1024 1056 1120 1152 768 786 789 807
EndSection
Section "Screen"
Identifier "dummy_screen"
Device "dummy_videocard"
Monitor "dummy_monitor"
DefaultDepth 24
SubSection "Display"
Viewport 0 0
Depth 8
Modes "1920x1080" "1280x1024" "1280x800" "1024x768"
Virtual 8192 4096
EndSubSection
SubSection "Display"
Viewport 0 0
Depth 16
Modes "1920x1080" "1280x1024" "1280x800" "1024x768"
Virtual 8192 4096
EndSubSection
SubSection "Display"
Viewport 0 0
Depth 24
Modes "1920x1080" "1280x1024" "1280x800" "1024x768"
Virtual 8192 4096
EndSubSection
SubSection "Display"
Viewport 0 0
Depth 30
Modes "1920x1080" "1280x1024" "1280x800" "1024x768"
Virtual 8192 4096
EndSubSection
EndSection
Section "ServerLayout"
Identifier "dummy_layout"
Screen "dummy_screen"
EndSection
Собираем:
docker build -t debian-stretch-x11dummy:latest .
Запуск при помощи Docker-compose
Для простоты управления сервисами вполне подойдет docker-compose. В процессе запуска нам необходимо:
- выставить «наружу» Apache из контейнера с FastReport.Mono
- «пробросить» unix-сокет X-сервера из контейнера x11dummy в контейнер frmono
Исходя из вышеизложенных требований, получаем минимально необходимый docker-compose.yml.
docker-compose.yml
version: "2"
services:
fastreport:
container_name: frmono
image: debian-stretch-mono:latest
volumes:
- ./.x11-unixsoc:/tmp/.X11-unix
ports:
- "127.0.0.1:8085:80"
xorg:
container_name: x11dummy
image: debian-stretch-x11dummy:latest
volumes:
- ./.x11-unixsoc:/tmp/.X11-unix
N.B. Если вы не знакомы с YAML, обратите внимание, что форматирование осуществляется пробелами.
Т.к. unix-сокет, это, по сути, файл, то используем подключаемые тома — возможность взаимодействия контейнера с хостом. При таком подходе, оба контейнера для обмена данными будут использовать каталог .x11-unixsoc, находящийся в директории запуска.
Запускаем и проверяем:
docker-compose up
И, как любит говорить один мой знакомый, «вишенка на торте»: автоматизация запуска контейнеров при старте ОС с использованием Systemd. Все осуществляется в два несложных действия. Во-первых, необходимо и достаточно, создать файл /etc/system.d/system/docker-frmono.service, приблизительное содержание которого приведено под спойлером. Почему приблизительное? Как минимум, необходимо изменить пути, в соответствии с выбранным вами расположением конфигурационных файлов и местом установки docker-compose.
docker-frmono.service
[Unit]
Description=FastReport Docker
Requires=docker.service
After=docker.service
[Service]
Restart=always
ExecStart=/usr/local/bin/docker-compose -f /opt/frdocker/docker-compose.yml start
ExecStop=/usr/local/bin/docker-compose -f /opt/frdocker/docker-compose.yml stop
[Install]
WantedBy=multi-user.target
Во-вторых, зарегистрировать сервис в системе и «включить» его:
systemctl daemon-reload
systemctl enable docker-frmono
P.S. Вполне возможно, что в процессе экспериментов, у вас скопилось некоторое количество «кривых» образов. Вычистить можно следующим образом:
docker images | grep "<none>" | awk '{split($0,a," ");print a[3];}' | xargs -I{} docker rmi "{}"
Все использованные в статье конифигурационные файлы доступны на github
moropsk
Почистить «хвосты» можно еще так
docker system prune -a