the way to hassle free docker php web stack deployments

by Sandro Keil

IT-Consultant / Senior Software Developer @prooph_software

Author, Blogger, Open Source Evangelist

https://sandro-keil.de

Require
ments

Know current
released version

Immutable Infrastructure



Never touch a running system

Build every version

every time

Application provisioning
only once

Single source
of truth

Auto tagging of Docker images

depending on application version

PHP Web Stack

Build Strategy

Every service has an
own Docker image

Docker images with application code in
one repository



Dockerfiles not in application repository

Different
Docker Compose configurations

for different
environments

$ export COMPOSE_FILE=docker-compose.yml:docker-compose-dev.yml
$ docker-compose up -d --no-recreate
                    # docker-compose.yml
version: '2'
services:
  php:
    image: sake-php-fpm:1.0.0
    restart: "always"
    networks:
      - backend

# docker-compose-dev.yml
version: '2'
services:
  php:
    image: sake-php-fpm-dev:1.0.0
    restart: "no"
    volumes:
      - ./:/var/www

Application configuration
via ENV
variables



The Twelve-Factor App
# docker-compose.yml
version: '2'
services:
  php:
    image: sake-php-fpm:1.0.0
    restart: "always"
    environment:
      - MONGODB_DATABASE=${MONGODB_DATABASE}
    networks:
      - backend
                
                    <?php
// mongodb.global.php
return [
    'mongodb' => [
        'dbname' => getenv('MONGODB_DATABASE'),
    ],
];
                

Use own
Docker Registry

Build Implemen
tation

infra-app repository

├── config/
│   ├── ext/
│   │   ├── mongodb.ini
│   │   └── opcache.ini
│   ├── ext-dev/
│   │   └── xdebug.ini
│   ├── fpm/
│   ├── nginx/
│   ├── nginx-dev/
│   └── php.ini
├── composer
├── nginx
├── nginx-dev
├── php-app
├── php-base
├── php-fpm
├── php-fpm-dev
├── php-worker
├── php-worker-dev
└── www/

PHP-BASE Dockerfile

FROM php:7.0.11-fpm
WORKDIR /var/www

RUN apt-get update \
    && apt-get install -y \
        libicu-dev \
        libmcrypt-dev \
        libssl-dev \
    && rm -r /var/lib/apt/lists/* \
    && pecl install mongodb \
    && docker-php-ext-configure opcache --enable-opcache \
    && docker-php-ext-configure intl --enable-intl \
    && docker-php-ext-install intl opcache \
    && rm -rf /var/www \
    && mkdir -p /var/www \
    && rm -rf /usr/local/etc/php-fpm.d \
    && rm -rf /usr/local/etc/php-fpm.conf

COPY config/ext $PHP_INI_DIR/conf.d/
COPY config/php.ini $PHP_INI_DIR/

CMD ["php", "-v"]

PHP-BASE build

$ echo "Docker login on Docker registry"

$ echo "Build php-base image"

$ docker build -t sake-php-base:${TAG} -f php-base .

$ docker tag sake-php-base:${TAG} myregistry.com/sake-php-base:${TAG} \
    && docker push myregistry.com/sake-php-base:${TAG}

COMPOSER Dockerfile

FROM myregistry.com/sake-php-base:1.0.0

ENV COMPOSER_HOME /root/composer
ENV COMPOSER_VERSION master

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get install -y \
        libbz2-dev \
        libzip-dev \
        git \
        unzip \
    && rm -rf /var/lib/apt/lists/* \
    && rm -rf /var/www \
    && docker-php-ext-install zip bz2 \
    && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \
    && sed -i 's/memory_limit="128M"/memory_limit=-1/g' $PHP_INI_DIR/php.ini

VOLUME ["/app"]
WORKDIR /app

CMD ["-"]
ENTRYPOINT ["composer", "--ansi"]

Composer build

$ echo "Replace image version with ${TAG} in composer file"

$ echo "Build composer image"

$ docker build -t sake-composer:${TAG} -f composer .

$ docker tag sake-composer:${TAG} myregistry.com/sake-composer:${TAG} \
    && docker push myregistry.com/sake-composer:${TAG}

PHP-APP Dockerfile

FROM myregistry.com/sake-php-base:1.0.0

COPY www /var/www

RUN rm -rf /var/www/public/assets \
    && chown www-data:www-data /var/www -R \
    && find /var/www/ -type d -exec chmod 755 {} \; \
    && find /var/www/ -type f -exec chmod 644 {} \;

PHP-APP build

$ git clone --branch ${TAG} git@github.org:sandrokeil/php-demo.git www

$ cd www

$ docker run -i -v $(pwd):/app myregistry.com/sake-composer:${TAG} \
    install --no-interaction --no-dev --optimize-autoloader

$ cd ..

$ echo "Replace image version with ${TAG} in php-app file"

$ echo "Build php-app image"

$ docker build -t sake-php-app:${TAG} -f php-app .

$ docker tag sake-php-app:${TAG} myregistry.com/sake-php-app:${TAG} \
    && docker push myregistry.com/sake-php-app:${TAG}

php-worker dockerfile

FROM myregistry.com/sake-php-app:1.0.0

RUN sed -i 's/memory_limit="128M"/memory_limit=-1/g' \
    $PHP_INI_DIR/php.ini

COPY worker/docker-entrypoint.sh /usr/local/bin/
RUN ln -s /usr/local/bin/docker-entrypoint.sh / # backwards compat

CMD ["list"]
ENTRYPOINT ["docker-entrypoint.sh"]

php-worker entrypoint

#!/bin/bash

# Define defaults
WORKER="${WORKER:-1}"
SLEEP="${SLEEP:-5}"

until [  $WORKER -lt 1 ]; do
    let WORKER-=1
    exec php /var/www/public/index.php "$@" &
    sleep $SLEEP
done

wait

docker-compose.yml

version: '2'
services:
    sake-php-negotiation-dequeue:
        image: myregistry.com/sake-php-worker:1.0.0
        restart: always
        environment:
          - WORKER=12
          - SLEEP=5
        command: sake:negotiation-queue:dequeue

nginx Dockerfile

FROM nginx:1.11.1

RUN rm -rf /var/www \
    && mkdir -p /var/www

COPY www/public/assets /var/www
COPY config/nginx /etc/nginx/

WORKDIR /var/www

nginx build

$ echo "Build nginx image"

$ docker build -t sake-nginx:${TAG} -f nginx .

$ docker tag sake-nginx:${TAG} myregistry.com/sake-nginx:${TAG} \
    && docker push myregistry.com/sake-nginx:${TAG}

Pitfalls

Docker Registry
Authentication

data
Persistence



Docker named volumes

Composer
and
private
Repositories

syslog driver
TCP logging



TCP syslog issue

PHP-FPM
splits and truncates logs



PHP-FPM logging issue


Questions ?

Visit My Website