
В предыдущей части публикации я рассказал, как запустить демонстрационный 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
