Skip to content

Instantly share code, notes, and snippets.

@vmlinz
Created October 8, 2015 13:35
Show Gist options
  • Save vmlinz/26cde25c5703d911d313 to your computer and use it in GitHub Desktop.
Save vmlinz/26cde25c5703d911d313 to your computer and use it in GitHub Desktop.
LNMP + docker compose split

Docker Compose up to three containers - Bolt.cm example

Make sure to check out ross's approach!

Docker let's you overcome hypervisors. So let's check it out:

## docker-compose.yml
php:
  image: doerty/fpm  # see below
  ports:
    - "mysql:3306:3306"
  expose:
    - 9000
  volumes:
    - ./html:/var/www/html            # The webdir containing the actual CMS
    - ./fpm.conf:/root/php-fpm.conf   # FPM Config because we might need to change something
    - ./php.ini:/root/php.ini         # PHP Config because the unchanged timezone error is annoying
  command: /usr/local/sbin/php-fpm --fpm-config /root/php-fpm.conf -c /root --force-stderr

mysql:
  image: mysql:latest
  environment:
    MYSQL_ROOT_PASSWORD: root
    MYSQL_DATABASE: asdf
    MYSQL_USER: asdf
    MYSQL_PASSWORD: asdf
  expose:
    - 3306
  volumes:
    - ./db/:/var/lib/mysql/  # I think having this on a per-project basis is kinda neat

nginx:
  image: nginx:latest
  links:
   - php
  expose:
   - 80
  ports:
   - "8080:80"
  volumes:
   - ./html:/usr/share/nginx/html         # The actual webdir, see above
   - ./nginx.local:/etc/nginx/nginx.conf  # The (full) nginx config

Okay so... why overwrite the default PHP/FPM Container? There are a couple'a caveats here:

## doerty/fpm/Dockerfile

FROM php:5.6-fpm

# This is the user FPM will run as. Your machine (if linux) will probably run as UID/GID 1000, and www-data had the uid of 33 
# when i first booted the thing. So instead of fiddling around with guids you could just as well create a new user for FPM to
# run as. Also: Create a tmp dir for given user.
RUN groupadd -g 1000 fpm \
 && adduser --disabled-login --disabled-password --gecos "" --home /var/www/html --shell /bin/false --uid 1000 --ingroup fpm fpm \
 && mkdir /var/www/tmp && chown fpm:fpm /var/www/tmp

# Install modules. [The PHP container's manual](https://github.com/docker-library/docs/tree/master/php) teaches us how to install
# PHP modules via Dockerfile, so let's make use of that in order to get rid of mbstring / gd missing warnings. Afterwards, because
# Docker, clear the apt dir to shorten the Container size.
RUN apt-get update && apt-get install -y \
        apt-utils \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libmcrypt-dev \
        libpng12-dev \
    && docker-php-ext-install mcrypt mbstring \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install gd \
    && rm -rf /var/lib/apt/lists/*

docker build doerty/fpm, docker tag $yourtag doerty/fpm and we're good to go.

FPM Config: change the user / group to "fpm", change the access.log to "/proc/self/fd/2" in order to make them pop up @ your STDOUT docker-compose terminal, chroot into /var/www/html/, php_admin_value[error_log] = "/proc/self/fd/2" done (optional, if you skip the php.ini).

PHP Config: set the timezone, session.save_path = "/var/www/tmp", switch from displaying the errors to logging mode.

Done. Hf.

One last note: If the PHP Container just won't connect to MySQL

If mysql just won't let you connect directly you can use a dirty hack: PHP's ini configuration allows you to set default DSNs for PDO to use! So share a common php.orig.ini into the php container, in which you basically say:

pdo.dsn.myboltinstall = "mysql:host=%%ENV_MYSQL_HOST%%;dbname=myboltinstall"

Then as a starting command you'll fire off a bashscript which reads the freshly created mysql-container's IP from the environment vars and replaces the ENV_MYSQL_HOST directive like:

ip=$(env  | grep MYSQL_PORT_3306_TCP_ADDR | grep -oe '[^=]*$')
/bin/sed -e "s/%%ENV_MYSQL_HOST%%/$ip/g" /root/php.orig.ini > /root/php.ini
/usr/local/sbin/php-fpm --fpm-config /root/php-fpm.conf -c /root --force-stderr

Easy enough. What PHP's PDO constructor now allows you to do is:

$pdo = new PDO("myboltinstall", 'username', 'password', array(PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''));

When you omit the driver, the dbname etc you can use your newly created DSN string from the PHP config. How would that look like in the app/config.yml?

database:
    dsn: 'myboltinstall'
    username: 'username'
    password: 'password'

Why would we do such a thing? Docker provides all the neccessary informations as environment variables. In some cases you just can't connect to MySQL using the linked port, even if you expose it. When some day I know why I'll drop a note here. In order to use environment variables inside the app/config.yml you'd have to install a symfony plugin (iirc, correct me if I'm wrong). Notice though: this is a hack! Containercluster setups probably have problems here. Also with every IP change you'd have to re-write the php.ini. But when you use said bash script as a container start command you can simply destroy the container and re-create it. Again: THIS IS A HACK! But it'll get you working if you really need it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment