Jump to content
jean-luc

Running a ProcessWire site with Docker

Recommended Posts

logo.png
 
Docker (http://www.docker.com) is an open platform for building, shipping and running distributed applications.
 
Docker containers are a great way to package a complete application with its specific dependencies in a portable way so that it can easily be deployed on any compatible network or cloud infrastructure.
 
Recently I spent a few days making my ProcessWire site run in a Docker container, and - as I could not find any good tutorial for this - it sounded like a good idea to write one.  :)
 
You will find on the web plenty of presentations and tutorials about Docker, so I won't start with the basic concepts, and this tuto assumes that you have a first understanding of Docker's fundamentals.
 
What we want to do here is to migrate an existing site to a set of docker containers.
 
Therefore, to start with, you should have:
- docker installed on your computer;
- the site directory of your ProcessWIre site
- a backup of your site's MySQL database
 
Let's start.
 
Create a docker container for the site database
 
For several reasons (insulation, security, scalability), it is preferable to host the site database in a separate docker container. 
 
1. Set-up a SQL database with MariaDb or MySQL
$ docker run --name database -e MYSQL_ROOT_PASSWORD=rootdbpassword -d mariadb
Here I choose to use the MariaDB official container in its latest version, but MySQLwould be just fine as well.
 
2. Run a PhpMyAdmin container and create the ProcessWire database  
  
We first select an simple image with PhpMyAdmin on the Docker Hub: nazarpc/phpmyadmin and we create a docker container based on this image. This container will  access the port exposed by the database container via a private networking interface. We specify this with the `--link` option.
 
It can be run temporarily (and exited by ctrl-C):
docker run --rm --link database:mysql -p 8881:80 nazarpc/phpmyadmin
Or it can be run as a daemon in the background:
docker run -d --name phpmyadmin --link database:mysql -p 8881:80 nazarpc/phpmyadmin
From phpmyadmin (accessed from your browser at http://hostaddress:8881) you can now create your ProcessWire database, create a dedicated user for it, and import the database content from a previously saved SQL file.
 
Note: alternatively, you can do all database operations from the command line in the database docker container created during step 1, or use another mysql user interface container if you prefer…
 
3. Update the database parameters in your site configuration
 
In your site's `config.php` file, the sql server name shall be set to `mysql`:
$config->dbHost = 'mysql';
Other `$config->dbXxx` settings shall match the database name, user and password of the just-created database.
 
 
Create a Docker Image for Apache, PHP and the Processwire site
 
1. Create an image-specific directory with the following contents and `cd` to it
bash-3.2$ ls -l . config
.:
total 16
-rw-rw-rw-   1 jean-luc  staff  1163 21 aoû 12:09 Dockerfile
drwxr-xr-x  17 jean-luc  staff   578 17 aoû 12:48 ProcessWire
drwxr-xr-x   7 jean-luc  staff   238 21 aoû 12:07 config
drwxr-xr-x   7 jean-luc  staff   238 20 aoû 18:46 site

config:
total 160
-rw-rw-rw-  1 jean-luc  staff    160 20 aoû 18:28 msmtprc
-rw-rw-rw-  1 jean-luc  staff  72518 20 aoû 18:56 php.ini
where:
2.  Set the `Dockerfile` content
FROM php:5.6-apache

RUN    apt-get update \
&& apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libmcrypt-dev libpng12-dev zziplib-bin msmtp\
&& a2enmod rewrite \
&& a2enmod ssl \
&& docker-php-ext-install mysqli pdo_mysql iconv mcrypt zip \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
&& docker-php-ext-install gd 

EXPOSE 80
EXPOSE 443

# Add a specific php.ini file
COPY config/php.ini /usr/local/etc/php/

# Configure the mail sent utility msmtp (http://msmtp.sourceforge.net) and make it readable only by www-data
COPY config/msmtprc /usr/local/etc/php/
RUN chmod 600 /usr/local/etc/php/msmtprc \
&& chown www-data:www-data /usr/local/etc/php/msmtprc

# Remove all default site files in /var/www/html
RUN rm -fR /var/www/html/*

# Copy ProcessWire core files
COPY ProcessWire/wire /var/www/html/wire
COPY ProcessWire/index.php /var/www/html/index.php
COPY ProcessWire/htaccess.txt /var/www/html/.htaccess

# Copy site-specific files
COPY site /var/www/html/site

# Make www-data the owner of site-specific files
RUN chown -R www-data:www-data /var/www/html/site

VOLUME /var/www/html/site
Based on the official image `php:5.6-apache`, it installs missing packages to the system, adds  mod-rewrite and mod-ssl to Apache, plus a number of PHP modules needed by Processwire (core or modules): mysqli, pdo_mysql, iconv, mcrypt, zip, and gd.
 
Then it copies the site files to the location expected by the Apache server.  Finally it declares a Docker volume `/var/www/html/site` (i.e. the site files and assets), so that it can be shared with other containers.
 
3. Set the msmtp configuration
 
We need to configure a sendmail utility, so that we can send emails from php, for example when a user registers on the website. The simplest way to do it is to rely on an external smtp server to do the actual sending. That's why we use msmtp.
 
- define the desired smtp account in `config/msmtprc`
account celedev-webmaster
tls on
tls_certcheck off
auth on
host smtp.celedev.com
port 587
user webmaster@celedev.com
from webmaster@celedev.com
password thepasswordofwebmasteratceledevdotcom
 
- in `config/php.ini`, configure the sendmail command so it uses msmtp:
sendmail_path = /usr/bin/msmtp -C /usr/local/etc/php/msmtprc --logfile /var/log/msmtp.log -a celedev-webmaster -t
 
4. Build the Docker image
docker build -t php-5.6-pw-celedev .
5. Create a Data-only container for the site files
docker run --name celedev-data php-5.6-pw-celedev echo "Celedev site data-only container"
6. Run the web server container
docker run --name celedev-site -p 8088:80 --link database:mysql --volumes-from celedev-data -d php-5.6-pw-celedev
Note that this container is linked to our database and shares the 'celedev-data' volume created previously
 
During development, it can be convenient to keep an access to the host file system from the container. For this, we can add a shared volume to the previous command:
docker run --name celedev-site -p 8088:80 --link database:mysql -v /Users/jean-luc/web/test-docker:/hostdir --volumes-from celedev-data -d php-5.6-pw-celedev
 
Our ProcessWire website is now up and running and we can test it in our browser at http://hostaddress:8088. Great!
 
What we now have in Docker
bash-3.2$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
php-5.6-pw-celedev    latest              2aaeb241c2e2        3 hours ago         1.149 GB
nazarpc/phpmyadmin    latest              e25cd4fd48b3        8 days ago          521 MB
mariadb               latest              dd208bafcc33        2 weeks ago         302.2 MB
debian                latest              9a61b6b1315e        5 weeks ago         125.2 MB

bash-3.2$ docker ps -a
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS                    PORTS                                         NAMES
68cc5d976f0d        php-5.6-pw-celedev    "apache2-foreground"     20 hours ago        Up 20 hours               443/tcp, 0.0.0.0:8088->80/tcp                 celedev-site
0729fe6d6752        php-5.6-pw-celedev    "echo 'Celedev site d"   20 hours ago        Exited (0) 20 hours ago                                                 celedev-data
e3e9e3a4715c        mariadb               "/docker-entrypoint.s"   3 days ago          Up 3 days                 3306/tcp                                      database
Saving the site data
 
We can create an archive of the site files by running a tar command in a dedicated container:
bash-3.2$ docker run --rm -it --volumes-from celedev-data -v /Users/jean-luc/web/test-docker:/hostdir debian /bin/bash

root@2973c5af3eaf:/# cd /var/www/html/
root@2973c5af3eaf:/var/www/html# tar cvf /hostdir/backup.tar site
root@2973c5af3eaf:exit

bash-3.2$
Tagging and archiving the Docker image
 
We can also add a tag to the docker image that we have created in step 4 (recommended):
bash-3.2$ docker tag 2aaeb241c2e2 php-5.6-pw-celedev:0.11

bash-3.2$ docker images
REPOSITORY            TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
php-5.6-pw-celedev    latest              2aaeb241c2e2        3 hours ago         1.149 GB
php-5.6-pw-celedev    0.11                2aaeb241c2e2        3 hours ago         1.149 GB
nazarpc/phpmyadmin    latest              e25cd4fd48b3        8 days ago          521 MB
mariadb               latest              dd208bafcc33        2 weeks ago         302.2 MB
And we can archive this image locally if we dont want to push it now to the Docker Hub:
bash-3.2$ docker save php-5.6-pw-celedev:0.11 | gzip > php-5.6-pw-celedev-0.11.tar.gz 

And that's it!

You now have a portable image of your ProcessWire website that you can run directly on any docker-compatible system.

  • Like 19

Share this post


Link to post
Share on other sites

I just made a few edits in the tutorial to improve clarity and fix typos...

Share this post


Link to post
Share on other sites

Good work! Regarding best practises, the only suggestion I have is that the tutorial should be changed to use environment variables for the database connection. This has several benefits, for an example

  • It would be very easy to launch a separate development site, which uses a different database, because all you need to change is the environment variable when launching the development web container
  • You could easily use the same config.php as a template for new sites
  • You don't have to store sensitive information in your version control
  • The config.php becomes more cloud compatible (i.e. a twelve-factor app)

Also since the MySQL-container is linked to the web-container, the config.php can directly use the environment variables set by Docker.

An example config.php would be

$config->dbHost = getenv("MYSQL_PORT_3306_TCP_ADDR");
$config->dbName = getenv("MYSQL_DBNAME");
$config->dbUser = getenv("MYSQL_DBUSER");
$config->dbPass = getenv("MYSQL_DBPASS");
$config->dbPort = getenv("MYSQL_PORT_3306_TCP_PORT");

Obviously launching the web container(s) would need to include these variables too, e.g.

docker run -e "MYSQL_DBNAME=mypwdb" -e "MYSQL_DBUSER=mypwdbuser" -e "MYSQL_DBPASS=mypwdbpass" --name celedev-site -p 8088:80 --link database:mysql --volumes-from celedev-data -d php-5.6-pw-celedev

Just my two cents :)

  • Like 7

Share this post


Link to post
Share on other sites

This is brilliant!

I've just started using Docker with a view to testing my PW sites locally, so this couldn't have come at a better time.

Share this post


Link to post
Share on other sites

I play with Rancher environment and custom docker images at the moment and it's fun :)

Created local images and deployed as Rancher stack

  1. caddy webserver (PW forum topic) as revproxy (use docker-gen to generate the caddyfile) and web server for PW.
  2. php5-fpm container (replaced it quickly with php7-fpm for short testing PW 3 with php7)
  3. openssh (used as sftp / scp and optional ssh access to the "webspace" = docker volume)

Works fine with HTTPS (frontend, ssl offloading) and http in the backend / internal container). Tested PW frontend, backend, image upload and content editing ...).

 

Because playing with docker is fun... 

I use RancherOS (OS with docker as pid 1), a custom kernel and custom desktop images (xorg, dbus, volumeicon, pcmanfm, chromium, ...) as "Docker Desktop"... but I should find the way back to PW soon because I'll create a new website in the near future... :lol:

  • Like 1

Share this post


Link to post
Share on other sites

Without any rude intentions: If you can't get ProcessWire to run without docker on EC2 you shouldn't try to add docker to the game as well. It's only going to get more complicated and error prone.

  • Like 1

Share this post


Link to post
Share on other sites

Edit: solved using this gem of knowledge! I had to use "mariadb" as the DB host, because that is how I named (and linked) it in my compose file!

 

I am running the PW installer. PW files are located on my host (Arch Linux). Db files as well.

If I use localhost as the DB host I get this: SQLSTATE[HY000] [2002] Can't connect to local MySQL server through socket '/run/mysqld/mysqld.sock' (2 "No such file or directory")

The solution given to this everywhere is to use 127.0.0.1 instead, because localhost makes it look at my host machine instead of the MariaDB container.

But with 127.0.0.1 I get: SQLSTATE[HY000] [2003] Can't connect to MySQL server on '127.0.0.1' (111 "Connection refused")

I have confirmed inside the database container (using sh) that the user and password are correct.

The web server and database containers are linked. I am using https://github.com/abiosoft/caddy-docker/  and https://github.com/bianjp/docker-mariadb-alpine/

I found some advice to create firewall rules, but they did not help:

iptables -t filter -A INPUT -p tcp -i docker0 --dport 3306 -j ACCEPT

iptables -t filter -A OUTPUT -p tcp -o docker0 --dport 3306 -j ACCEPT

I found advice to add this to my.cnf: bind-address = 0.0.0.0 so it listens to all interfaces. No help.

What to try next?


Here is my docker-compose.yml:

Spoiler

 

version: '3.0'

services:
  caddy:
    image: abiosoft/caddy:php
    volumes:
      - /home/user/Documents/project/docker/Caddyfile:/etc/Caddyfile
      - /home/user/Documents/project/docker/processwire:/srv:Z
    ports:
      - "2015:2015"
    links:
      - mariadb
    restart: always

  mariadb:
    image: bianjp/mariadb-alpine:latest
    environment:
      MYSQL_DATABASE: db
      MYSQL_USER: user
      MYSQL_PASSWORD: pwd
      MYSQL_ROOT_PASSWORD: pwd
    volumes:
      - /home/user/Documents/project/docker/mariadbdata:/var/lib/mysql
    ports:
      - "3306:3306"
    restart: always

 

 

Edited by Beluga
SOLVED

Share this post


Link to post
Share on other sites

Hello MRiza,

On 30/04/2017 at 6:52 AM, MRiza said:

I know this thread was old, but I want to build a web server that is using official package from the distribution repo. So I write this Dockerfile and if anyone wants to test/use it jus look at https://github.com/mriza/docker-processwire

I try to use your docker file (thanks a lot) with my Debian server but i met some difficulties : 

1 : I added a specific user

As root,

useradd -d /home/processwire -m -s /bin/bash processwire
usermod -aG docker processwire

2 : I built a Docker image

As processwire user,

Download the project files to /home/processwire: wget -c https://github.com/mriza/docker-processwire/archive/v0.2.tar.gz
Extract: tar xvzf v0.2.tar.gz
Change to the new directory: cd docker-processwire-v0.2/
Build the image : "docker build -t processwire ."

3 : I wrote my docker-compose file

vi /home/processwire/docker-compose.yml

#******************* docker-compose.yml file ************

processwire:
  image: processwire
  container_name: processwire
  restart: always
  volumes:
    - /home/processwire/docker-processwire-0.2/pw:/var/www/html
  ports:
    - "82:80"

#******************* docker-compose.yml file ************

 

4 : I ran Docker application

As processwire user and from /home/processwire, I ran "docker-compose up -d". Processwire website is now live on port 82, and I can access the server IP address (http://192.168.2.2:82 in my case). But when I try to install the "Site Installation Profile" (Default, intermediate edition) ,it fails (http://192.168.2.2:82/install.php) : 

File system is not writable by this installer. Before continuing, please rename '/site-default' to '/site'

I understand that a user in my Docker Application doesn't have write permissions for my external directory /home/processwire/docker-processwire-0.2/pw, but I don't see who...

 

Any ideas ?

Joël, first steps in the field of ProcessWire CMS

ps. Sorry for my bad english, I 'm French

 

Share this post


Link to post
Share on other sites

Extra informations :

root@CarExpresso:/home/processwire/docker-processwire-0.2# ls -la pw/

total 100
drwxr-xr-x 8 processwire processwire  4096 avril 30 06:36 .
drwxr-xr-x 3 processwire processwire  4096 mai   31 15:51 ..
-rw-r--r-- 1 processwire processwire   537 avril 30 06:36 composer.json
-rw-r--r-- 1 processwire processwire 12223 avril 30 06:36 .htaccess
-rw-r--r-- 1 processwire processwire  2421 avril 30 06:36 index.php
-rw-r--r-- 1 processwire processwire 47540 avril 30 06:36 install.php
drwxr-xr-x 6 processwire processwire  4096 avril 30 06:36 site-beginner
drwxr-xr-x 6 processwire processwire  4096 avril 30 06:36 site-blank
drwxr-xr-x 6 processwire processwire  4096 avril 30 06:36 site-classic
drwxr-xr-x 6 processwire processwire  4096 avril 30 06:36 site-default
drwxr-xr-x 6 processwire processwire  4096 avril 30 06:36 site-languages
drwxr-xr-x 5 processwire processwire  4096 avril 30 06:36 wire



 

Share this post


Link to post
Share on other sites

If I had a test.php file into pw/ directory with "<?php echo exec('whoami');" inside, I can read in my Web browser (http://192.168.2.2:82/test.php) :

www-data

 

Share this post


Link to post
Share on other sites

Help yourself... ;-)

1 - apache user id inside my container

docker exec processwire /bin/sh -c "id www-data"

# uid=33(www-data) gid=33(www-data) groups=33(www-data)

2 - I create my directory site (pw/site)

mkdir site
chown -R 33:33 site
mkdir modules
mkdir assets
chown -R 33:33 modules
chown -R 33:33 assets
cp site-default/config.php site/config.php
chown -R 33:33 site/config.php

cd site-default/
ls
assets      finished.php  install  ready.php
config.php  init.php      modules  templates

cd ..

cp site-default/finished.php site/
cp site-default/ready.php site/
cp site-default/init.php site/
cp -r site-default/install site/
cp -r site-default/templates site/

After that, everythink is ok with http://192.168.2.2:82/install.php

  • Like 2

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By clsource
      Hello, I finished this toy project. A simple API to show data for Chilean Birds.
      Used to flex my PW and React muscles, since a lot of time has passed since making something with those techs.
       

      Code: https://github.com/NinjasCL/chileanbirds-api
      Frontend (React): https://aves.ninjas.cl
      Backend (PW): https://aves.ninjas.cl/api
      Hope you like it 🙂

      Thanks.
       
       
    • By Kiwi Chris
      I don't really have anything public to show, as nearly all the work is back-end, but I thought I'd post here anyway as it's a pretty good example of just how powerful Processwire can be.
      About a year ago, I inherited an incomplete Craft website made by a designer. Apart from the usual company information site, it was supposed to provide a customer portal for clients of a wine bottling company to make bookings for production runs.
      Data for stock levels of goods like bottles and labels was to come from an inventory management system Unleashed https://www.unleashedsoftware.com/
      Unleashed provides a REST API, so I had to integrate with that first by writing an API integration module, and then ended up using Processwire's core lazy cron module to periodically pull data from Unleashed using a custom module.
      The booking forms have a lot of conditional fields, eg if you are bottling a given wine variety, you should only be able to select labels that match that variety. All this conditional stuff was achieved with a lot of additions to ready.php.
      I also needed to be able to created a predefined set of pages when a new user is added if they have a 'client' role. Once again, more hooking in ready.php
      I've used the Admin Restrict Branch module so clients can only see their own records when they're logged in, but staff can see all records.
      Lister Pro provides the ability to search and view completed production runs.
      Part way through the project, as the client was happy with the way things were going, I was asked to add in logistics and dispatch which is provided by another company, which also runs Unleashed with a separate set of data, and with some clients who don't bottle wine, but will end up using the same portal, so using the roles and permissions inherent in Processwire, I set up production templates with separate roles to dispatch templates, so I could easily have clients assigned access to just the templates they need.
      Tracey Debugger got a thorough workout along the way, and the debugger console is an absolute killer tool for making quick changes to data when updating a live site to match changes from the dev site.
      At the start of this project, I'd used Processwire quite a bit, but never dived into module development or hooking, but I've now ended up with a reasonable idea how they work.
      @bernhard has produced some excellent tutorials which I found really helpful figuring out how to create modules, and other people like @Robin S have answered questions when I've got stuck. @ryan himself has been helpful when I've been trying to do things that push either the limits of my knowledge or Processwire or both 😋  .
      Could I have done this with other tools? Depends. Wordpress would have been as useless as using petrol to fight a fire, however something like ASP.Net COULD have done the job but would have probably made things a lot more complicated. In parallel, I've been working on building a REST API with ASP.Net for another client to integrate with an existing SQL Server database, and I've found that Visual Studio is inclined to break projects quite regularly, with dependencies getting messed up, or even whole configuration files getting corrupted when it has a hissy fit, so working with Processwire is a pleasure in comparison.
       
    • By Kevin C. McCarthy
      I am desperate to learn how to you ProcessWire to it's fullest potential, and while the documentation is great and always appreciated, I simply can't follow along because it gets way too technical without really showing how applicable and versatile it can be. Then again, I'm almost a moderate understanding of PHP and no experience with APIs or programming JavaScript—so it's probably leagues ahead of where I am at the moment. That said, I learn best by watching and the doing. Think Codecademy or FreeCodeCamp. I was wonder if there are any video tutorials or walk-through lessons to give me a greater understanding of ProcessWire and how to utilize it effectively.
      For some background, I'm great with WordPress and I'm great with writing websites by hand with Notepad only. The biggest hurdles I have with PW is the phrasing is so far left of WP at times that it's a massive hurdle for me to get over. Like in WP, themes, templates, etc are totally different things. And as someone who builds WP sites for a living, it gets hard to kill those old preconceived meanings.
      I want to start building out PW sites for numerous reasons. For one, most of my clients they would benefit from it vs the Bloated Beast. Two, it would allow me to differentiate me in a market saturated by WP devs. I know I have a long ways to go until I reach that point of considering myself a "PW dev", but I am desperate for resources to help me wrap my head around it.
      I've built my own website in PW but TBH it only handles some of the data while most of the text has been hard-coded into the PHP template files because I couldn't get my my head around the "best practice" of structuring the data.
      Anyway, enough rambling, I'm just hoping those of you in the community can point me to easily-digestible sources out there that can help move me along so I can actually benefit from using the platform. Thank you!
    • By prestoav
      Hi folks,
      I have a marketplace site built on PW which I'd like to tie to a subscription payment system for those selling. The user accounts currently use specific PW user accounts. I'm looking closely at PayWhirl to manage payments and subscriptions as it looks ideal for subscriptions. Their API seems to work well too from initial testing.
      I wondered if anyone else in this super forum had used PayWhirl and had any advice or tips & tricks?
      Thanks.
      https://app.paywhirl.com/
    • By dst81
      Hi,
      I'm a System Administrator responsible for DevOps in our Company. We're trying PW in a single client project atm and experience some glitches with DevOps-/Workflow.
      We have a small team of developers that need to work with the same code base. They all need to be able to develop locally and deploy to a preview/staging environment.
      Our Toolstack contains git for versioning, chef/vagrant or docker for local development/testing and Jenkins for building assets and automatic deployment to the staging-site.
      There's several challenges / glitches in this process that makes me think that ProcessWire hadn't been developed for a use case like ours and is much more intended to be used by single developers that work right on the production system.
      Can you advise me on a suitable workflow?

      There's problems with the assets/files dir that must be shared between the staging website and local environments of our developers.
      We're right now working with symlinks on the staging system that helps to preserve the direcory when deploying from the master branch. but now we tend to use nfs-shares so devs can collaborate with a shared directory.

      The local docker containers can use the same target (the nfs) from inside the containers. But is that the way it needs to be done? Really?
      There's so much work that needs to be done to fit ProcessWire in a DevOps Workflow that we tend to decide to switch to another CMS.
       
      Any suggestions or hints that i might have missed. Am I wrong or is PW really not meant to be used this way. I
×
×
  • Create New...