Mediawiki-Docker on Apple M1

2021-02-16 09:23:05 +0100 +0100

In a previous post I wrote about speeding up Docker for Mac by giving up on Docker for Mac (tl;dr SSH tunnels and PhpStorm’s deploy feature to work on a remote server).

There are aspects to that workflow that aren’t great, so I’ve spent some time working on a config for working locally with ARM images that overriode the images used in the default MediaWiki-Docker config.

Here’s where I ended up.

Override file

version: '3.7'
services:
  mediawiki-web:
    user: "root"
    build: docker/apache/
  mediawiki-jobrunner:
    user: "root"
    build: docker/php/
  mediawiki:
    working_dir: /var/www/html/w
    user: "root"
    build: docker/php/
    volumes:
        - ./:/var/www/html/w:cached
        - ../localsettings:/localsettings
  mariadb:
    image: arm64v8/mariadb
    ports:
      - 3306:3306
    volumes:
      - mariadbdata:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root
  elasticsearch:
    ports:
      - "9200:9200"
      - "9300:9300"
    #image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2-arm64
    image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.14
    volumes:
      - esdata:/usr/share/elasticsearch/data
    environment:
      - discovery.type=single-node
      - bootstrap.system_call_filter=false
  redis:
    image: arm64v8/redis
volumes:
  mariadbdata:
    driver: local
  esdata:
    driver: local

I’m using custom Dockerfiles for the web/php containers, which I’ll get to in a second. The main thing to note is that most images extend from arm64v8 variants of the official images, e.g. arm64v8/redis instead of redis.

While there is a arm64 version of ElasticSearch, it’s currently just for version 7 and CirrusSearch uses version 6 still. Note the bootstrap.system_call_filter=false line which is needed to avoid startup errors on a host ARM system.

PHP Dockerfile

Pretty basic:

FROM arm64v8/php:7.3-fpm
ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/
RUN chmod +x /usr/local/bin/install-php-extensions && sync && \
    install-php-extensions intl gd opcache
RUN docker-php-ext-install mbstring mysqli && docker-php-ext-enable mbstring mysqli opcache gd intl
RUN pecl install redis \
    && pecl install xdebug \
    && docker-php-ext-enable redis xdebug
RUN echo xdebug.client_host=host.docker.internal >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
RUN echo xdebug.mode=debug >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
RUN apt-get update && apt-get install -y default-mysql-client

In the future I’ll probably tune this so it matches what is in the releng/dev-images repo.

Apache Dockerfile

Like the releng/dev-images repo, I’m extending Debian Stretch to build this. In the official PHP Docker Hub images (https://hub.docker.com/_/php) I think there is a vanilla Apache image designed to work with php-fpm, but I couldn’t find the equivalent in the arm64v8 repo, so that’s why I’ve gone with Stretch as the base.

FROM arm64v8/debian:stretch
RUN apt-get update && apt-get install -y apache2 \
    && rm /var/www/html/index.html \
    && a2enmod proxy_fcgi \
    && a2enmod mpm_event \
    && a2enmod rewrite \
    && a2enmod http2 \
    && a2enmod cache
COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
RUN echo "Listen 8080" > /etc/apache2/ports.conf
EXPOSE 8080
ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]

And the conf file, which is basically the same as what is in releng/dev-images:

<VirtualHost *:8080>
	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/html
    <Directory /var/www/html>
        Require all granted
    </Directory>
    <FilesMatch "\.php$">
        SetHandler "proxy:fcgi://mediawiki:9000/"
    </FilesMatch>
	# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
	# error, crit, alert, emerg.
	# It is also possible to configure the loglevel for particular
	# modules, e.g.
	#LogLevel info ssl:warn

	ErrorLog ${APACHE_LOG_DIR}/error.log
	CustomLog ${APACHE_LOG_DIR}/access.log combined

	# For most configuration files from conf-available/, which are
	# enabled or disabled at a global level, it is possible to
	# include a line for only one particular virtual host. For example the
	# following line enables the CGI configuration for this host only
	# after it has been globally disabled with "a2disconf".
	#Include conf-available/serve-cgi-bin.conf
    ## Support /wiki style URLs
    RewriteEngine On
    RewriteRule ^/?wiki(/.*)?$ %{DOCUMENT_ROOT}/w/index.php [L]
    RewriteRule ^/?$ %{DOCUMENT_ROOT}/w/index.php [L]

    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
    RewriteRule ^/?images/thumb/[0-9a-f]/[0-9a-f][0-9a-f]/([^/]+)/([0-9]+)px-.*$ %{DOCUMENT_ROOT}/w/thumb.php?f=$1&width=$2 [L,QSA,B]

    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
    RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-d
    RewriteRule ^/?images/thumb/archive/[0-9a-f]/[0-9a-f][0-9a-f]/([^/]+)/([0-9]+)px-.*$ %{DOCUMENT_ROOT}/w/thumb.php?f=$1&width=$2&archived=1 [L,QSA,B]

    # VisualEditor support. T262392
    AllowEncodedSlashes NoDecode
</VirtualHost>

Benchmarks

With the arm64v8 images:

ab -c 4 -n 100 http://localhost:8080/wiki/Special:BlankPage
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        Apache/2.4.25
Server Hostname:        localhost
Server Port:            8080

Document Path:          /wiki/Special:BlankPage
Document Length:        31525 bytes

Concurrency Level:      4
Time taken for tests:   21.235 seconds
Complete requests:      100
Failed requests:        99
   (Connect: 0, Receive: 0, Length: 99, Exceptions: 0)
Total transferred:      3142418 bytes
HTML transferred:       3097218 bytes
Requests per second:    4.71 [#/sec] (mean)
Time per request:       849.403 [ms] (mean)
Time per request:       212.351 [ms] (mean, across all concurrent requests)
Transfer rate:          144.51 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   323  832 731.4    788    5743
Waiting:      323  832 731.5    788    5743
Total:        323  833 731.4    788    5743

Percentage of the requests served within a certain time (ms)
  50%    788
  66%    838
  75%    860
  80%    872
  90%    967
  95%   1078
  98%   5714
  99%   5743
 100%   5743 (longest request)

With the amd64 default images:

ab -c 4 -n 100 http://localhost:8080/wiki/Special:BlankPage
This is ApacheBench, Version 2.3 <$Revision: 1879490 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient).....done


Server Software:        Apache/2.4.25
Server Hostname:        localhost
Server Port:            8080

Document Path:          /wiki/Special:BlankPage
Document Length:        16792 bytes

Concurrency Level:      4
Time taken for tests:   44.441 seconds
Complete requests:      100
Failed requests:        98
   (Connect: 0, Receive: 0, Length: 98, Exceptions: 0)
Total transferred:      1702201 bytes
HTML transferred:       1657001 bytes
Requests per second:    2.25 [#/sec] (mean)
Time per request:       1777.636 [ms] (mean)
Time per request:       444.409 [ms] (mean, across all concurrent requests)
Transfer rate:          37.40 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.0      0       0
Processing:   862 1729 763.5   1724    7215
Waiting:      862 1728 763.5   1724    7215
Total:        862 1729 763.5   1724    7215

Percentage of the requests served within a certain time (ms)
  50%   1724
  66%   1761
  75%   1790
  80%   1800
  90%   1862
  95%   1875
  98%   6201
  99%   7215
 100%   7215 (longest request

Conclusion

p50 of 788 ms / p95 of 1.07 seconds is good enough for now, but would be great to get this lower. Still, it’s a huge improvement over p50 of 1724ms / p95 of 1.8 seconds.