FastReport.Mono. Part 2: Web Reporting in a Docker Container

  • Tutorial


In the previous part of the publication, I talked about how to run the FastReport.Mono demo Web report. Now, I propose moving to a new level and “wrapping” all the necessary components in Docker containers.


Understand, in heaven they only say that about the sea. How infinitely beautiful it is ...

In recent years, everyone has been talking about Docker everywhere, because containers are stylish, fashionable and sexually youthful. Many developers and administrators use Docker for business and for no apparent reason. But, if you really need it and it is interesting - welcome under cat. Unlike the first part, there will be a maximum of text and a minimum of images.

As already noted, for the correct operation of FastReport.Mono, we may need 2 containers:

  • container with Apache 2.4
  • X server container

We create both containers based on the debian: stretch image from the Docker repository.

FRMono container


The first container, as already mentioned, will contain Apache 2.4. In addition, when assembling, we immediately add Mono and FastRepor there.

In principle, nothing prevents you from expanding the Dockerfile and organizing an automatic installation of some Oracle Instant Client, but since the article is devoted specifically to the demo Web-report - we do not need this.

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

Actually, there is nothing complicated or tricky to configure. The main thing you should pay attention to: you do not need to run Apache as a background process, you need to put it in the foreground. Otherwise, the container will complete its work immediately after launch.

The virtual host configuration file for Apache, in principle, is a stripped-down version of the config given in the first part of the publication. However, there are some differences. This is due to the fact that a slightly different directory structure will be used (with the placement of libraries in the bin directory).

I don’t see any particular difference in these approaches. In addition, the container can safely use port 80.

001-mono.conf

DocumentRoot "/opt/fastreport/htdocs"
    
          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
    
         Require all granted
         Options Indexes FollowSymLinks MultiViews
         AllowOverride All
          
                 SetHandler mono
                 MonoSetServerAlias FrSite
                 DirectoryIndex Default.aspx
           
        Require all denied
    


We start the assembly process:

docker build -t debian-stretch-mono:latest .

X11Dummy Container


In the first part of this series of publications, I already mentioned that an X server is required to use System.Windows.Forms. Because there are no "X" in the container with Apache, Mono and FastReport, we need to get them from somewhere.

Two options are possible:

  • use an X server that is running on the host OS
  • use a separate container with the X server

The first option will require different settings, depending on the OS used. Indeed, in Linux it can be, for example, X.org , in Mac OS - XQuartz , and in Windows it is necessary to deliver Xming. The
second option is more consistent with the Docker concept and allows you to create an easily portable and host-system-independent system. Interaction between containers can be done via tcp or unix socket. In my opinion, the option of using a unix socket is great for solving this problem.

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

If you carefully study the Docerfile under the spoiler, then you should pay attention to two points. The first is the use of the video driver from the xserver-xorg-video-dummy package . An “empty” driver that tells the X server that it is displaying an image, but actually does nothing.

The second one is setting the DISPLAY environment variable to a value different from the usual one according to various instructions: 0. An exceptional precaution, if you suddenly decide to set the UNIX socket through the / tmp host OS.

X server configuration file, this is a variation on thisconfig walking with various changes throughout the network. I came across a very long version with a bunch of Modeline'ov (alas, I could not remember the source) and I tried to squeeze it as much as possible (although, I think it is still possible).

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


We collect:

docker build -t debian-stretch-x11dummy:latest .

Launch with Docker-compose


For ease of service management, docker-compose is fine . In the launch process, we need:

  • set Apache out of the container with FastReport.Mono
  • “Forward” the unix-socket of the X-server from the container x11dummy to the container frmono

Based on the above requirements, we get the minimum required 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


NB If you are not familiar with YAML , note that formatting is done with spaces .

Because unix-socket, this is essentially a file, then we use plug-in volumes - the ability of the container to interact with the host. With this approach, both containers will use the .x11-unixsoc directory located in the startup directory to exchange data.

We launch and check:

docker-compose up

And, as one friend of mine likes to say, “cherry on the cake”: automation of container launch at OS startup using Systemd. Everything is carried out in two simple steps. Firstly, it is necessary and sufficient to create the file /etc/system.d/system/docker-frmono.service, the approximate contents of which are given under the spoiler. Why is it approximate? At a minimum, you need to change the paths in accordance with the location of the configuration files of your choice and the installation location of 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


Secondly, register the service in the system and “enable” it:

systemctl daemon-reload
systemctl enable docker-frmono


PS It is quite possible that during the experiments, you have accumulated a certain number of "crooked" images. You can clean as follows:

docker images | grep "" | awk '{split($0,a," ");print a[3];}' | xargs -I{} docker rmi "{}"

All configuration files used in the article are available on github

Also popular now: