Dockerizing: Magento

A guide on how to run a Magento shop on docker using composer and docker-compose
created by on 2015-04-23

In this guide I am going to walk you through the steps that are required to run all software components of a Magento shop entirely with docker containers.

  1. Motivation
  2. Software Requirements
  3. Components of a Magento installation
  4. Setup of a basic Magento installation with Docker

If you are not (yet) interested in all the details on how this works you can jump ahead to my dockerized-magento project at https://github.com/andreaskoch/dockerized-magento.git:

git clone https://github.com/andreaskoch/dockerized-magento.git
cd dockerized-magento
./magento start

Animatation: Installation and First Start of the Dockerized-Magento project

Motivation

In order to run a Magento shop you need at least

and in most cases

Your options for hosting Magento are

  1. Using tools like MAMP, WAMP or AMPS
  2. Installing, compiling, patching and configuring all the required components yourself

But these tools will only take care of your development environment. And what about Solr and Redis? How do you transfer the setup and configuration of these tools to your co-workers or even different environments? How do you scale and transfer these setups? How do you make sure everything is configured correctly?

So why no try our docker for a change?

Docker offers:

If you you want to learn more about Docker you can have a look at a tutorial on Docker that made a while back: Getting started with Docker

Software Requirements

Install* docker and docker-compose (aka fig):

# Become root
sudo su

# Install Docker
wget -qO- https://get.docker.com/ | sh

# Allow non-root access to docker
sudo usermod -aG docker ubuntu

# Install Docker-Compose
curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

Docker-Compose is the orchestrator utility for docker which allows you to control multiple containers using a single configuration file.

* For this tutorial I will assume that you are using Ubuntu 14.04 but you should also be able to follow this guide with only slight adjustments on other Linux systems and on Windows or Mac OS using Boot2Docker.

Or you can use a Vagrant machine to follow along:

mkdir dockerized-magento
cd dockerized-magento
vagrant init ubuntu/trusty64
vagrant up

Components of a (dockerized) Magento Installation

These are the components that we will need for a basic and advanced Magento installation - no matter if you are using Docker or not:

Basic Magento Installation

  1. Tools
  2. Magento (Community Edition 1.9.x)
  3. PHP (5.5)
  4. Web-Server (we will be using Nginx)
  5. Mysql server

Diagram: Connecting the Components of an advanced Magento Installation with PHP, Nginx and MySQL

See also: System Requirements for Magento Enterprise Edition 1.14.1 and Community Edition 1.9.1

Advanced Magento Installation: Basic + Redis + Solr + Modules

  1. A basic Magento Installation
  2. Some useful Magento modules
  3. Solr for fulltext-search
  4. Redis for caching

Diagram: Connecting the Components of an advanced Magento Installation with PHP, Nginx, Solr, Redis and MySQL

Note: We are not going to cover the advanced setup in detail in this tutorial. But you can have a look at my dockerized-magento project at github.com/andreaskoch/dockerized-magento. This project contains a fully automated Magento installation with PHP, Nginx, Solr, Redis and MySQL.

Setup of a basic Magento installation with Docker

In this section we are going through all steps for creating a basic docker-based Magento Shop with Magento Community Edition 1.9.1, PHP 5.5.x, Nginx 1.7.x and MySQL 5.6.

  1. Project Setup
  2. Composer File Definition
  3. Docker-Compose File Definition

Note: To make things easier for now I will assume that you have composer (and php) installed on your host. You can have a look at the install.sh of my dockerized-magento project if you are interested in moving the installer into docker as well.

Step 1: Project Setup

In this step we lay out the structure for our project which will look like this:

├── composer.json
├── db
├── docker-compose.yml
├── docker-images
│   ├── mysql
│   ├── nginx
│   └── php
└── web

You can create the structure using these commands:

mkdir dockerizing-magento
cd dockerizing-magento

touch composer.json
touch docker-compose.yml
mkdir -p web db docker-images/php docker-images/nginx

We will fill the composer.json and the docker-compose.yml in the next steps.

Step 2: composer.json

The second step is to define the composer.json file which determines which Magento version and modules composer is going to install:

composer.json

{
  "name": "dockerizing-magento",
  "description": "A Dockerized Magento Community Edition",
  "require": {
    "magento-hackathon/magento-composer-installer": "v2.1.1",
    "magento/core": "1.9.1.0-patch1"
  },
  "require-dev": {
  },
  "repositories": [
  ],
  "extra": {
    "magento-root-dir": "web",
    "auto-append-gitignore": true
  },
  "config": {
    "discard-changes": true
  },
  "minimum-stability": "dev",
  "prefer-stable": true,
  "scripts": {
    "post-install-cmd": [
    ],
    "post-update-cmd": [
    ]
  }
}

The above composer.json file contains three noteworthy sections:

  1. the require { ... } section lists all modules which are going to be installed. Here it is magento/core and the magento-hackathon/magento-composer-installer which allows us to install magento and its modules using composer.
  2. the repositories { ... } section states that composer will look at packages.firegento.com for the code of the referenced Magento modules
  3. and the “magento-root-dir” property in the extra { ... } section. The property tells the composer-installer to install Magento and all modules into the web folder of your project.

Once you have placed the JSON into the “composer.json”-file in your project directory you can use composer to install Magento:

composer update

Project Folder Setup & Magento Installation

Screenrecording: Project Setup and composer update

Check your project folder for a web and vendor folder. Both should have been filled by the composer update command.

Step 3: docker-compose.yml

Now that we have the basic project structure set up and installed Magento into our web-folder we can start creating the required docker images and link them together using a docker-compose-file.

Please put this content into your docker-compose.yml:

docker-compose.yml

nginx:
  build: docker-images/nginx
  ports:
    - "80:80"
    - "443:443"
  links:
    - "php:phpfpmupstream"
  volumes_from:
    - php
php:
  build: docker-images/php
  links:
    - "mysql:mysql"
  volumes:
    - .:/var/www/html
mysql:
  image: mysql
  ports:
    - "3306:3306"
  environment:
    MYSQL_ROOT_PASSWORD: pw
    MYSQL_DATABASE: magento
    MYSQL_USER: shop
    MYSQL_PASSWORD: ShopPassword
  volumes:
    - db:/var/lib/mysql

The syntax of the file is YAML and consists of three sections:

Since the php and the nginx containers are referencing custom images we must create docker image defintions and configuration files for both of them.

Customized Nginx Containers

docker-imges/nginx

The Nginx image does not require much customization. We can inherit all functionality from the standard nginx docker image.

We only need to add two things:

  1. ssl-cert for a dummy ssl certificate
  2. a custom virtual-host configuration file for the Magento website

docker-images/nginx/Dockerfile

FROM nginx:latest

# Make snakeoil certificates available
RUN apt-get update && \
	apt-get install -qy ssl-cert

# Add virtual host config for Magento
COPY virtualhost.conf /etc/nginx/conf.d/default.conf

docker-images/nginx/virtualhost.conf

server {
    listen                      80 default_server;
    listen                      443 default_server ssl;

    ssl_certificate             /etc/ssl/certs/ssl-cert-snakeoil.pem;
    ssl_certificate_key         /etc/ssl/private/ssl-cert-snakeoil.key;

    client_max_body_size        10M;

    root                        /var/www/html/web;
    index                       index.php;

    # Serve images directly
    location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|pdf|txt|tar|wav|bmp|rtf|js|flv|swf)$ {
        root /var/www/html/web;
    }

    location / {
        index index.html index.php;
        try_files $uri $uri/ @handler;
        expires 30d;
    }

    location ^~ /app/                       { deny all; }
    location ^~ /includes/                  { deny all; }
    location ^~ /lib/                       { deny all; }
    location ^~ /media/downloadable/        { deny all; }
    location ^~ /pkginfo/                   { deny all; }
    location ^~ /report/config.xml          { deny all; }
    location ^~ /var/                       { deny all; }
    location ^~ /downloader/                { deny all; }
    location /var/export/                   { deny all; }

    location  /. {
        return 404;
    }

    location @handler {
        rewrite / /index.php;
    }

    location ~ \.php/ {
        rewrite ^(.*\.php)/ $1 last;
    }

    location ~ \.php$ {

        ## Catch 404s that try_files miss
        if (!-e $request_filename) {
            rewrite / /index.php last;
        }

        fastcgi_split_path_info         ^(.+\.php)(/.+)$;

        fastcgi_pass                    phpfpmupstream:9000;
        fastcgi_index                   index.php;

        include fastcgi_params;
        fastcgi_param                   SCRIPT_FILENAME $document_root$fastcgi_script_name;

        fastcgi_param                   MAGENTO_ROOT /var/www/html/web;
        fastcgi_param                   MAGE_IS_DEVELOPER_MODE 1;

        fastcgi_param                   REMOTE_ADDR $http_x_real_ip;
        fastcgi_buffer_size             1024k;
        fastcgi_buffers                 500 512k;
        fastcgi_connect_timeout         1200;
        fastcgi_send_timeout            1200;
        fastcgi_read_timeout            1200;

    }

    rewrite ^/minify/([0-9]+)(/.*.(js|css))$ /lib/minify/m.php?f=$2&d=$1 last;
    rewrite ^/skin/m/([0-9]+)(/.*.(js|css))$ /lib/minify/m.php?f=$2&d=$1 last;

    location /lib/minify/ {
        allow all;
    }

    gzip                on;
    gzip_min_length     1000;
    gzip_proxied        any;
}

Customized PHP Container

docker-imges/php

The PHP image can also be completely reused from the standard PHP Docker Image - we only need to add

docker-images/php/Dockerfile

FROM php:5.5-fpm

# Install
RUN buildDeps=" \
        libpng12-dev \
        libjpeg-dev \
        libmcrypt-dev \
        libxml2-dev \
        freetype* \
    "; \
    set -x \
    && apt-get update && apt-get install -y $buildDeps --no-install-recommends && rm -rf /var/lib/apt/lists/* \
    && docker-php-ext-configure \
    gd --with-png-dir=/usr --with-jpeg-dir=/usr --with-freetype-dir \
    && docker-php-ext-install \
    gd \
    mbstring \
    mysqli \
    mcrypt \
    mysql \
    pdo_mysql \
    soap \
    zip \
    && apt-get update -qy && apt-get install -qy git-core \
    && cd /tmp/ && git clone https://github.com/derickr/xdebug.git \
    && cd xdebug && phpize && ./configure --enable-xdebug && make \
    && mkdir /usr/lib/php5/ && cp modules/xdebug.so /usr/lib/php5/xdebug.so \
    && touch /usr/local/etc/php/ext-xdebug.ini \
    && rm -r /tmp/xdebug && apt-get purge -y git-core \
    && apt-get purge -y --auto-remove

# Configure
COPY php.ini /usr/local/etc/php/php.ini
COPY php-fpm.conf /usr/local/etc/
COPY ext-xdebug.ini /usr/local/etc/php/conf.d/ext-xdebug.ini

# Make sure the volume mount point is empty
RUN rm -rf /var/www/html/*

# Install magerun
RUN curl -o magerun https://raw.githubusercontent.com/netz98/n98-magerun/master/n98-magerun.phar && \
    chmod +x ./magerun && \
    cp ./magerun /usr/local/bin/ && \
    rm ./magerun && \
    apt-get update && \
    apt-get install -qy mysql-client

docker-images/php/php.ini

[PHP]

;;;;;;;;;;;;;;;;;;;
; Resource Limits ;
;;;;;;;;;;;;;;;;;;;

max_execution_time = 1800
max_input_time = 60
memory_limit = 512M
upload_max_filesize = 32M

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Error handling and logging ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

error_reporting = E_ALL & ~E_NOTICE | E_DEPRECATED
display_errors = Off
display_startup_errors = Off
log_errors = On
log_errors_max_len = 1024
ignore_repeated_errors = Off
ignore_repeated_source = Off
report_memleaks = On
track_errors = Off
html_errors = On

date.timezone = "UTC"

docker-images/php/php-fpm.conf

; This file was initially adapated from the output of: (on PHP 5.6)
;   grep -vE '^;|^ *$' /usr/local/etc/php-fpm.conf.default

[global]

error_log = /proc/self/fd/2
log_level = warning
daemonize = no

[www]

; if we send this to /proc/self/fd/1, it never appears
access.log = /proc/self/fd/2

user = www-data
group = www-data

listen = [::]:9000

pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3

docker-images/php/ext-xdebug.ini

zend_extension="/usr/lib/php5/xdebug.so"
xdebug.remote_enable=1
xdebug.remote_port=9000
xdebug.overload_var_dump=1
xdebug.default_enable=1
xdebug.remote_connect_back=1

Wrap-Up

If you have created* all of the files mentioned in the steps above (composer.json, docker-compose.yml and the docker-image definitions) you are ready to start you project:

cd dockerizing-magento
composer update
touch web/LICENSE.html
docker-compose up -d

After all containers have been started you can reach your Magento Shop at http://127.0.0.1.

* You can also download the complete project archive here: dockerizing-magento.tar.gz

Screenrecording: Project Start with docker-compose

Conclusion

Docker Rocks! If you are interested in more complex setup have a look at github.com/andreaskoch/dockerized-magento.

Shortlink:
Tags: