В предыдущей части публикации я рассказал, как запустить демонстрационный Web-отчет FastReport.Mono. Сейчас же, предлагаю перейти на новый уровень и «завернуть» все необходимые компоненты в Docker-контейнеры.


Пойми, на небесах только и говорят, что о море. Как оно бесконечно прекрасно…

Последние годы все и везде говорят о 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

Комментарии (1)


  1. moropsk
    25.03.2018 23:44

    Почистить «хвосты» можно еще так
    docker system prune -a