This article is published under a SIGNATURE 4.0 International (CC BY 4.0) license. Signature 4.0 International (CC BY 4.0)

Author: Su Yang

Creation time: July 16, 2019 statistical word count: 10682 words reading time: 22 minutes to read this article links: soulteary.com/2019/07/16/…


Build Flarum light Forum application with Docker and Traefik

Recently, I was working on a community-type project. Considering the cost of subsequent recruitment from the market, I had to choose PHP, which is less difficult to recruit and train in the market, and I chose this software which is still in beta.

The software has been in Beta for almost 5 years and has nearly 10,000 stars on GitHub. The first submission record was v0.1.0-Beta release in late 2014.

This article will show you how to build Flarum quickly and easily using containers. If you are not familiar with Traefik, check out the previous article.

Writing in the front

About the selection of concerns, I think you read the article at the moment, must also consider.

As mentioned earlier, the software is still in beta and is not in a particularly stable state. A few days ago, major contributors posted a message on the forum announcing “Farewell”. It seems that the situation is not particularly rosy, so why choose it besides the recruitment and maintenance costs?

  • The interactive experience and the project shelf is not bad, the fundamentals are still ok.
  • Iterated for five years as an open source application, with a record of submissions and open code, the application is not particularly complex and relatively easy to trace and troubleshoot.
  • The project documentation is incomplete, but the basics are pretty much there.
  • The app market is sparse, but the plug-in mechanism is already well established and the functionality is relatively easy to extend.
  • Two powerful domestic software programs implemented in the same language, a forum shut down, a plan reconfigured (for reasons you know), obviously not worth trusting at this point in time.
  • WordPress’s BuddyPress ecosystem can be considered, but doing the same needs to change more, because there is more redundant content.

Building a PHP container

The official installation documentation for the environment is this

  • Rewrite: Apache, Nginx…
  • PHP 7.1+ as welldom,gd,json,mbstring,openssl,,pdo_mysql,tokenizerComponents.
  • MySQL 5.6 +MariaDB 10.0.5 +
  • Composer

Therefore, the PHP image provided by Docker Hub by default cannot be used. Additional configuration is required to install the software required above. By default, we do not allow plug-in uninstallation (removing part of the program files) in already running software, so the composer mentioned in the last point can be installed locally or not installed at all.

Take phP-FTM-alpine 7.3.2 as an example:

FROM PHP :7.3.2-fpm-alpine ENV LANG en_us.utf-8 ENV LANGUAGE en_us.utf-8 ENV LC_ALL= en_US.utf-8 RUNecho ' ' > /etc/apk/repositories && \
    echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.9/main"         >> /etc/apk/repositories && \
    echo "https://mirror.tuna.tsinghua.edu.cn/alpine/v3.9/community"    >> /etc/apk/repositories && \
    echo "Asia/Shanghai" > /etc/timezone

RUN apk --no-cache --no-progress update && \
    apk --no-cache --no-progress upgrade

RUN apk add libpng libpng-dev libjpeg-turbo-dev libwebp-dev zlib-dev libxpm-dev

RUN docker-php-ext-install pdo pdo_mysql mbstring gd

ENTRYPOINT ["docker-php-entrypoint"]

STOPSIGNAL SIGQUIT

EXPOSE 9000
CMD ["php-fpm"]
Copy the code

Use the build command to build the container:

Docker build - t PHP - FPM - flarum: 7.3.2-f Dockerfile .
Copy the code

Use Docker Images to view the built PHP image, which is just over 100 megabytes.

REPOSITORY TAG IMAGE ID CREATED SIZE PHP-PMM-flarum 7.3.2ef5ad124a35d 3 minutes ago 105MBCopy the code

Set up a database

As mentioned in the previous section, the official database requirements are: MySQL 5.6+ or MariaDB 10.0.5+, so you can do it according to your preference. If you have performance requirements, it is recommended to use RDS products from cloud vendors.

Here is an example of a database choreographer file:

version: '3.6'

services:

  database:
    image: ${DB_IMAGE}
    restart: always
    container_name: ${DB_HOST}
    ports:
      - 3306:3306
    networks:
      - traefik
    environment:
      MYSQL_DATABASE: ${DB_NAME}
      MYSQL_USER: ${DB_USER}
      MYSQL_PASSWORD: ${DB_PASS}
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
    volumes:
      - ./data:/var/lib/mysql

networks:
  traefik:
    external: true
Copy the code

Save the above configuration as docker-comemage.yml and proceed to write the.env file required for the configuration. In order to facilitate the test, I use weak passwords. In actual use, I should change to complex passwords with higher security.

DB_IMAGE=mysql:5.7.26 DB_NAME=flarum DB_USER=flarum DB_PASS=flarum DB_ROOT_PASS=flarumCopy the code

Docker-compose up -D is now up and running.

Build an application running framework

Today, the official installation program has changed from a traditional software package to a simple command:

composer create-project flarum/flarum . --stability=beta
Copy the code

However, this is not friendly to the development and deployment experience of continuous iterative projects. Even with version locking, the project will take longer to build, and the code will become cumbersome and cumbersome in containers, resulting in the fragmentation of the same application code, and high maintenance costs.

There will also be changes to the framework and merging with future releases. It’s not just a matter of installing and using, so you’ll need to store the application code.

Install Composer PHP package management software

Because the software distribution pattern changes, we need to use Composer to download the software package (the PHP environment installation is not described here).

wget -O composer-setup.php https://getcomposer.org/installer

php composer-setup.php --install-dir=bin --filename=composer
Copy the code

Composer download is slow in China, here you can choose to use ali Cloud accelerated mirror, the method is very simple, just a command.

composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/
Copy the code

Download the Flarum program code

Then use the following command to download the software to the Flarum directory.

`composer create-project flarum/flarum ./flarum --stability=beta`
Copy the code

The directory structure after installation looks like this:

Flag School - - Flag School - - Flag School - - Flag School - - Flag School - - Flag School - - Flag School - - Flag School - - Flag School - - Flag School Public │ ├ ─ ─ assets │ └ ─ ─ index. The PHP ├ ─ ─ storage │ ├ ─ ─ cache │ ├ ─ ─ the formatter │ ├ ─ ─ less │ ├ ─ ─ the locale │ ├ ─ ─ logs │ ├ ─ ─ Sessions │ ├── ├─ ├─Copy the code

Now the software is not running, we need to continue to configure the application running environment.

Configure the software running environment

After all the above operations are ready, we can build the program running environment. Nginx is used as the front end of PHP, and the whole environment is very simple to build.

version: "3.6"

services:

  nginx:
    image: ${DOCKER_NGINX_IMAGE}
    restart: always
    expose:
      - 80
    volumes:
      - ./logs:/var/log/nginx
      - ./conf/docker-nginx.conf:/etc/nginx/nginx.conf
      - ./wwwroot:/wwwroot
    links:
      - php:php
    extra_hosts:
      - "${DOCKER_DOMAIN_NAME}: 127.0.0.1"
    networks:
      - traefik
    labels:
      - "traefik.enable=true"
      - "traefik.port=80"
      - "traefik.frontend.rule=Host:${DOCKER_DOMAIN_NAME}"
      - "traefik.frontend.entryPoints=https,http"
    healthcheck:
      test: ["CMD-SHELL"."wget -q --spider --proxy off http://${DOCKER_DOMAIN_NAME}/get-health || exit 1"]
      interval: 5s
      retries: 12
    logging:
        driver: "json-file"
        options:
            max-size: "100m"

  php:
    image: ${DOCKER_PHP_IMAGE}
    restart: always
    expose:
      - 9000
    volumes:
      - ./logs:/var/log
      - ./wwwroot:/wwwroot
    extra_hosts:
      - "${DOCKER_DOMAIN_NAME}: 127.0.0.1"
    networks:
      - traefik
    healthcheck:
      test: ["CMD-SHELL"."pidof php-fpm"]
      interval: 5s
      retries: 12
    logging:
      driver: "json-file"
      options:
        max-size: "100m"

networks:
  traefik:
    external: true
Copy the code

Env can be written like this:

DOCKER_DOMAIN_NAME=flarum.lab.com DOCKER_PHP_IMAGE = PHP - FPM - flarum: 7.3.2 DOCKER_NGINX_IMAGE = nginx: 1.17.1 - alpineCopy the code

Nginx configuration can refer to the following files:

user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events { worker_connections 1024; }

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    log_format main '$http_x_forwarded_for - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"';
    access_log off;
    sendfile on;
    keepalive_timeout 65;

    server {
        listen 80;
        server_name flarum.lab.com;
        server_tokens off;
        access_log /var/log/nginx/docker-access.log;
        error_log /var/log/nginx/docker-error.log;
        root /wwwroot/public;
        index index.php index.html;

        location ~ \.php$ {
            try_files $uri= 404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass php:9000; fastcgi_index index.php; include fastcgi_params; fastcgi_param SCRIPT_FILENAME$document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $fastcgi_path_info;
        }

        location / {
            try_files $uri $uri/ /index.php?$query_string;
        }
        # The following directives are based on best practices from H5BP Nginx Server Configs
        # https://github.com/h5bp/server-configs-nginx
        # Expire rules for static contentlocation ~* \.(? :manifest|appcache|html? |xml|json)$ { add_header Cache-Control"max-age=0"; } location ~* \.(? :rss|atom)$ { add_header Cache-Control"max-age=3600"; } location ~* \.(? :jpg|jpeg|gif|png|ico|cur|gz|svg|mp4|ogg|ogv|webm|htc)$ { add_header Cache-Control"max-age=2592000"; access_log off; } location ~* \.(? :css|js)$ { add_header Cache-Control"max-age=31536000"; access_log off; } location ~* \.(? :ttf|ttc|otf|eot|woff|woff2)$ { add_header Cache-Control"max-age=2592000";
            access_log off;
        }
        location = /get-health {
            access_log off;
            default_type text/html;
            return 200 'alive';
        }
        Consider handing it to a later application, such as Traefik...
        # Gzip compressiongzip on; gzip_comp_level 5; gzip_min_length 256; gzip_proxied any; gzip_vary on; gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml application/vnd.geo+json application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/bmp image/svg+xml image/x-icon text/cache-manifest text/css text/plain text/vcard text/vnd.rim.location.xloc text/vtt text/x-component text/x-cross-domain-policy; }}Copy the code

Install flarum by placing the flarum program in the wwwroot directory.

Run the application with docker-compose up -d and visit flarum.lab.com to configure the application.

Install the application

After simply filling in the required elements on the installation interface, click the install button.

Moments later, the installation is complete, and you can see that the interface is still very clean.

The management background is relatively simple, it is difficult to meet our daily use requirements, but fortunately now we can write plug-ins to enhance the function.

Optimize the program running framework

The installation part of the program is over, but we still need to do some extra work for subsequent maintenance.

Let the application adapt to the operating environment

After the application is installed, we look at the application directory again and find an extra config.php file in the directory.

Wwwroot ├─ Changelo.md ├─ LICENSE ├─ readme.md ├─ composer.json ├─ config.php ├─ extend.php ├─ Flarum ├ ─ ─ public │ ├ ─ ─ assets │ └ ─ ─ index. The PHP ├ ─ ─ storage │ ├ ─ ─ cache │ ├ ─ ─ the formatter │ ├ ─ ─ less │ ├ ─ ─ the locale │ ├ ─ ─ │ ├─ ├─ exercises │ ├─ exercises │ ├─ exercises │ ├─Copy the code

The content of the file is as follows, recording the program operation configuration:

<? phpreturn array (
  'debug'= >false.'database' =>
  array (
    'driver'= >'mysql'.'host'= >'flarum'.'port'= > 3306,'database'= >'flarum'.'username'= >'flarum'.'password'= >'flarum'.'charset'= >'utf8mb4'.'collation'= >'utf8mb4_unicode_ci'.'prefix'= >'flarum_'.'strict'= >false.'engine'= >'InnoDB'.'prefix_indexes'= >true,),'url'= >'https://flarum.lab.com'.'paths' =>
  array (
    'api'= >'api'.'admin'= >'admin',),);Copy the code

Considering that the program may be running in different environments (production, test, development), database, website address and other information may change, if it can automatically read environment variables can eliminate the hassle of maintaining multiple configurations, and simplify the distribution process.

Make a simple change to it:

<? phpreturn array (
  'debug'= > ($_SERVER['FLARUM_APP_DEBUG'= = ='true'),
  'database' =>
  array (
    'driver'= >'mysql'.'host'= >$_SERVER['FLARUM_DB_HOST'].'database'= >$_SERVER['FLARUM_DB_NAME'].'username'= >$_SERVER['FLARUM_DB_USER'].'password'= >$_SERVER['FLARUM_DB_PASS'].'charset'= >'utf8mb4'.'collation'= >'utf8mb4_unicode_ci'.'prefix'= >'flarum_'.'port'= >'3306'.'strict'= >false,),'url'= >$_SERVER['FLARUM_APP_URL'].'paths' =>
  array (
    'api'= >'api'.'admin'= >'admin',),);Copy the code

Also make a simple change to docker-comemage.yml to “inject”.env into the application as an environment variable.

  php:
    image: ${DOCKER_PHP_IMAGE}
    restart: always
    expose:
      - 9000
    env_file: .env
Copy the code

Finally, declare the variable names required by the above configuration file in.env.

DOCKER_DOMAIN_NAME=flarum.lab.com DOCKER_PHP_IMAGE = PHP - FPM - flarum: 7.3.2 DOCKER_NGINX_IMAGE = nginx: 1.17.1 - alpine FLARUM_DB_HOST=flarum FLARUM_DB_NAME=flarum FLARUM_DB_USER=flarum FLARUM_DB_PASS=flarum FLARUM_APP_DEBUG=true
FLARUM_APP_URL=//flarum.lab.com
Copy the code

Continue to break down the project structure

As mentioned above, if we want to continuously modify and improve the software in beta state, we need to preserve and maintain its code. However, if the environment and code are placed at the same place, the efficiency of modification and debugging will be too low.

Therefore, we can simplify the operation of the above application directory, and split the “application code” and “base environment”, so that the future debugging release only needs to update the file, instead of redeploying the environment, restart and other heavy operations.

If you have similar requirements, you can also use the following figure to split the application’s base environment and Vendor code.

The simplified directory is as follows:

. ├ ─ ─ LICENSE ├ ─ ─ the README. Md ├ ─ ─ the env ├ ─ ─ the conf │ └ ─ ─ docker - nginx. Conf ├ ─ ─ logs ├ ─ ─ the update. The sh └ ─ ─ below ├ ─ ─ config. PHP ├ ─ ─ the extend. PHP ├ ─ ─ flarum ├ ─ ─ public │ └ ─ ─ index. The PHP ├ ─ ─ storage └ ─ ─ vendorCopy the code

As you can see, the wwwroot directory has been simplified to just four PHP files and a few empty directories.

To modify and publish the program, you can use CI and update.sh update script to synchronize the vendor-dependent files required by the program. The update script can be referred to as follows:

#! /usr/bin/env bash
# service shutdown
echo "stop services."
docker-compose down --remove-orphans

Make sure the container image exists
cat .env | grep _IMAGE | cut -d '=' -f2 | while read image ; do
  if test -z "$(docker images -q $image)"; then
    echo "prepare docker images."
    docker pull $image
  fi
done

Clean up the existing history files and synchronize the new files to the execution directory
if [ -d "wwwroot/vendor" ]; then
    echo "cleanup wwwroot/vendor."
    rm -rf wwwroot/vendor
fi
if [ -d "/data/forum-test-vendor" ]; then
    echo "update vendor."
    cp -r /data/forum-test-vendor/vendor/ wwwroot/
    cp /data/forum-test-vendor/composer.* wwwroot/
fi


# clean up & rebuild the directory
echo 'cleanup directory'
rm -rf ./wwwroot/storage
mkdir -p ./wwwroot/storage/cache
mkdir -p ./wwwroot/storage/formatter
mkdir -p ./wwwroot/storage/less
mkdir -p ./wwwroot/storage/locale
mkdir -p ./wwwroot/storage/logs
mkdir -p ./wwwroot/storage/sessions
mkdir -p ./wwwroot/storage/tmp
mkdir -p ./wwwroot/storage/views

# restart service
echo 'restart services.'
docker-compose up -d

# Modify file permissions
docker ps -q -f status=running -f name=flarum_php |  while read container ; do
   echo "fix container $container own && mod."
   docker exec $container chown -R www-data:www-data /wwwroot
   docker exec $container chmod -R 755 /wwwroot/storage
done
Copy the code

After directory splitting, the test release time of the project can be reduced from the previous minute level to 5 to 10 seconds.

The last

There are a lot of details about this software, and then slowly write it out.

To end with a quote from my moments a while ago:

The cooperative unit cancelled the cooperation due to force majeure at the critical moment, the machine deleted the code and the database went offline on Friday. Fifty days of busy work by all of us went to waste, and the project went online as planned.

I had to rewrite my plan and start with MVP. Had to turn into a firefighter again 👩🚒.

It took almost three days to clear up the remaining issues, design the new architecture, set up various environments and infrastructure, and adjust the basic flow of the back end. It looks like another week will be enough! 🎉

Thanks to open source software, the problem can be solved quickly and I still have time to do “big things” this month.


I now have a small toss group, which gathered some like to toss small partners.

In the case of no advertisement, we will talk about software, HomeLab and some programming problems together, and also share some technical salon information in the group from time to time.

Like to toss small partners welcome to scan code to add friends. (Please specify source and purpose, otherwise it will not be approved)

All this stuff about getting into groups