dans le développement

et l'intégration continue

@jderusse

SensioLabs

Virtualisation

Projet A

- zend server

- mysql

- php 5.3

Projet B

- 5 × apache2

- 5 × postgresql

- php 5.4

- rabbitmq

Projet C

- apache2

mysql

- php 5.6

- oracle

- solr

- ldap

Solution : VM ?

VM

Container

Concrètement

VirtualBox

docker

Démarrage

 ~ 1 min

~ 0.3 sec

Mémoire

~ 256 Mo

~ 1 Mo

Espace disque

~ 1 Go

~ 100 Ko

Et pour
Windows et OS X ?

  • boot2docker

  • docker-machine

  • Kitematic

$ docker-compose up -d
Creating project_db_1...
Creating project_web_1...

$ docker-compose scale web=3
Starting project_web_2...
Starting project_web_3...

$ docker-compose ps
        Name             Command   State     Ports 
-------------- -----------------------------------
project_web_1  apache2-foreground  Up    80/tcp    
project_web_2  apache2-foreground  Up    80/tcp    
project_web_3  apache2-foreground  Up    80/tcp    
project_db_1   mysqld              Up    3306/tcp  

$ docker-compose logs web
Attaching to project_web_3, project_web_2, project_web_1
app_2 | RUN apache
app_1 | RUN apache
app_3 | RUN apache

docker-compose

# docker-compose.yml

web:
  image: php:5.6-apache
  links:
    - db:db
  volumes:
    - .:/var/www/html

db:
  image: postgres

anciennement fig

Accéder aux containers

Accéder aux containers

Ports

# docker-compose.yml

web:
  image: php:5.6-apache
  ports:
    - "8080:80"
  links:
    - db:db
  volumes:
    - .:/var/www/html

db:
  image: postgres
  ports:
    - "3306:3306"

E_TOO_MANY_PORTS

# A/docker-compose.yml

web:
  image: php:5.6-apache
  ports:
    - "8080:80"
  links:
    - db:db
  volumes:
    - .:/var/www/html

db:
  image: postgres
  ports:
    - "3306:3306"
# B/docker-compose.yml

web:
  image: php:5.6-apache
  ports:
    - "8081:80"
  links:
    - db:db
  volumes:
    - .:/var/www/html

db:
  image: postgres
  ports:
    - "3307:3306"
# C/docker-compose.yml

web:
  image: php:5.6-apache
  ports:
    - "8084:80"
  links:
    - db:db
  volumes:
    - .:/var/www/html

db:
  image: postgres
  ports:
    - "3308:3306"

docker-gen + Dnsmasq

# docker-compose.yml

web:
  image: php:5.6-apache
  environment:
    - DOMAIN_NAME=web.project.docker
  links:
    - db:db
  volumes:
    - .:/var/www/html
$ nslookup web.project.docker
Server:		172.17.42.1
Address:	172.17.42.1#53

Name:	web.project.docker
Address: 172.17.0.193

Launch time?

Solution ?

// app_dev.php

// This check prevents access to debug front controllers that are deploy
// Feel free to remove this, extend it, or make something more sophistic
if (isset($_SERVER['HTTP_CLIENT_IP'])
    || isset($_SERVER['HTTP_X_FORWARDED_FOR'])
    || !(in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', 'fe80::1'
) {
    header('HTTP/1.0 403 Forbidden');
    exit('You are not allowed to access this file. Check '.basename(__FI
}
// app_dev.php

// This check prevents access to debug front controllers that are deploy
// Feel free to remove this, extend it, or make something more sophistic
if (isset($_SERVER['HTTP_CLIENT_IP'])
    || isset($_SERVER['HTTP_X_FORWARDED_FOR'])
    || !(in_array(@$_SERVER['REMOTE_ADDR'], array('127.0.0.1', 'fe80::1'
) {
    // header('HTTP/1.0 403 Forbidden');
    // exit('You are not allowed to access this file. Check '.basename(_
}
<?php //app.php

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Debug\Debug;
use Symfony\Component\ClassLoader\ApcClassLoader;

$env = getenv('SYMFONY_ENV') ?: 'prod';
$debug = 'dev' === $env;

$loader = require_once __DIR__.'/../app/bootstrap.php.cache';
if ($debug) {
    Debug::enable();
}

if ('prod' === $env) {
    $apcLoader = new ApcClassLoader(sha1(__FILE__), $loader);
    $loader->unregister();
    $apcLoader->register(true);
}

require_once __DIR__.'/../app/AppKernel.php';

$kernel = new AppKernel($env, $debug);

$kernel->loadClassCache();
$request = Request::createFromGlobals();
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response);

Utiliser SYMFONY_ENV

Définir SYMFONY_ENV

# docker-compose.yml

web:
  image: php:5.6-apache
  environment:
    - SYMFONY_ENV=dev
  command: /usr/sbin/apache2ctl -D FOREGROUND
  volumes:
    - .:/var/www/html
# vhost.conf

<VirtualHost *:80>
    DocumentRoot /var/www/html

    SetEnv SYMFONY_ENV "dev"

    <Directory /var/www/html>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Boîte à outils

# docker-compose.yml

web:
  image: php:5.6-apache
  volumes:
    - .:/var/www/html

tools:
  build: ./docker/tools
  volumes:
    - .:/var/www/html
  
$ docker-compose run --rm tools bower install
$ docker-compose run --rm tools app/console assetic:dump
# Dockerfile

FROM debian:jessie

RUN apt-get update \
 && apt-get install --no-install-recommends -y \
    nodejs \
    npm \
    php5-cli \
    php5-curl \
    php5-json \
    php5-intl \
 && apt-get clean \
 && rm -r /var/lib/apt/lists/*

RUN ln -sf /usr/bin/nodejs /usr/bin/node \
 && npm install -g bower less

jolicode/phaudit

$ alias phaudit="docker run --rm -ti \
    -v \`pwd\`:/project \
    jolicode/phaudit"
$ phaudit phploc .

$ phaudit pdepend --summary-xml=summary.xml .

$ phaudit phpmd . text naming

$ phaudit phpcs .

$ phaudit phpcbf .

$ phaudit phpcpd .

$ phaudit phpdcd .

$ phaudit phpmetrics --report-cli .

$ phaudit php-cs-fixer fix .

Partager vos évolutions

# docker-compose.yml

web:
  build: docker/app
  links:
    blackfire:blackfire
  volumes:
    - .:/var/www/html

blackfire:
  image: blackfire/blackfire
  env_file:
    - .env
# .env

BLACKFIRE_SERVER_ID=foo
BLACKFIRE_SERVER_TOKEN=bar

Supprimer
les dépendances

ldap

# docker-compose.yml

web:
  image: php:5.6-apache
  links:
    - ldap:ldap
  volumes:
    - .:/var/www/html

ldap:
  build: ./docker/ldap
  
# user.ldiff

dn: cn=services,dc=sensiolabs,dc=com
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: services
description: LDAP administrator
userPassword: NoLongerSecret

dn: ou=people,dc=sensiolabs,dc=com
objectClass: organizationalUnit
ou: people

dn: cn=user1,ou=people,dc=sensiolabs,dc=com
objectClass: inetOrgPerson
cn: user1
sn: user1
uid: user1
mail: user1@sensiolabs.fr
userPassword: user1

Partager son projet

avec docker in docker

ProjectA

# Dockerfile

FROM dind_with_docker_compose

COPY . /app

WORKDIR /app

EXPOSE 80
CMD sh -c "wrapdocker & sleep 5 && docker-compose up"
$ docker build --rm -t private-registry/stub-project-a .
$ docker push private-registry/stub-project-a

ProjectB 

# docker-compose.yml

web:
  image: php:5.6-apache
  links:
    - projectA:A.com
  volumes:
    - .:/var/www/html

projectA:
  image: private-registry/stub-project-a
  privileged: true

Ne plus faire semblant

Ne plus faire semblant

mailcatcher

mailcatcher

# docker-compose.yml

web:
  image: php:5.6-apache
  links:
    - mailcatcher:my_smtp
  volumes:
    - .:/var/www/html

mailcatcher:
  image: jderusse/mailcatcher
# app/config/parameters.yml

parameters:
  mailer_transport: smtp
  mailer_host:      my_smtp
  mailer_user:      ~
  mailer_password:  ~
# behat.yml

default:
  extensions:
    Alex\MailCatcher\Behat\MailCatcherExtension\Extension:
      url: http://my_smtp

Simplifier l'installation

Simplifier l'installation

oracle

# docker-compose.yml

web:
  image: php:5.6-apache
  links:
    - oracle:db
  volumes:
    - .:/var/www/html

oracle:
  image: wnameless/oracle-xe-11g

Selenium

# docker-compose.yml

web:
  image: php:5.6-apache
  volumes:
    - .:/var/www/html

behat:
  build: php:5.6-apache
  links:
    - app:my_app
    - seleniumhub:selenium
  volumes:
    - .:/var/www/html

seleniumhub:
  image: selenium/hub

seleniumnode:
  image: selenium/node-firefox-debug
  # image: selenium/node-firefox
  # image: selenium/node-chrome-debug
  # image: selenium/node-chrome
  links:
    - seleniumhub:hub
    - app:my_app
# behat.yml

docker:
  extensions:
    Behat\MinkExtension\Extension:
      base_url: http://my_app
      browser_name: firefox
      goutte: ~
      selenium2:
        capabilities:
          version: ''
        wd_host: http://selenium:4444/wd/hub
$ docker-compose run --rm behat ./bin/behat

Containers
pré-chargés

Containers
pré-chargés

Switcher simplement

# docker-compose.yml

web:
  image: php:5.6-apache
  links:
    - database
  volumes:
    - .:/var/www/html

database:
  image: db:production
  # image: db:fixture
$ docker-compose up -d

Accélérer les tests

$ time app/console doctrine:fixtures:load
real	0m27.289s
$ time docker-compose up web
real	0m2.815s

VS

// FeatureContext.php

/**
 * @BeforeScenario
 */
public static function restartContainer()
{
    static $dbContainer = null;
    $docker = new Docker\Docker(
        new Docker\Http\DockerClient([], 'unix:///var/run/docker.sock')
    );
    $manager = $docker->getContainerManager();

    if (null !== $dbContainer) {
        $manager
            ->stop($dbContainer)
            ->remove($dbContainer);
    }

    $dbContainer = new Docker\Container(['Image' => 'mysql']);
    $dbContainer->setEnv(['DOMAIN_NAME=mysql.test', 'MYSQL_ROOT_PASSWORD=bar']);
    $manager
        ->create($dbContainer)
        ->start($dbContainer);
}

Accélérer les tests

docker et les tests automatisés

docker et les tests automatisés

Comment ?

$ docker-compose run --rm tools composer install
$ docker-compose run --rm tools build_app


$ docker-compose run --rm web ./bin/phpunit
$ docker-compose run --rm behat ./bin/behat
$ docker run --rm -v `pwd`:/app -w /app php:5.4-cli ./bin/phpunit > log54 &
$ docker run --rm -v `pwd`:/app -w /app php:5.5-cli ./bin/phpunit > log55 &
$ docker run --rm -v `pwd`:/app -w /app php:5.6-cli ./bin/phpunit > log56 &

$ wait

$ echo log54
$ echo log55
$ echo log56

Paralléliser les tests

$ docker-compose -p billing run web ./bin/behat @AcmeBillingBundle
Creating billing_db_1...
Creating billing_web_run_1...
Running suite for AcmeBillingBundle
...
$ docker-compose -p blog run web ./bin/behat @AcmeBlogBundle
Creating blog_db_1...
Creating blog_web_run_1...
Running suite for AcmeBlogBundle
...
$ docker ps
CONTAINER ID  IMAGE                 COMMAND                NAMES
df69bb7ec146  billing_db:latest     "mysqld"               billing_db_1
a2596ec1fe5e  billing_web:latest    "apache2 -DFOREGROUN"  billing_web_run_1
b59c3d362d93  blog_db:latest        "mysqld  "             blog_db_1
d63de3f4cdf8  blog_web:latest       "apache2 -DFOREGROUN"  blog_web_run_1

Outils de build

  • drone.io

  • JoliCi

  • Bamboo

  • GitlabCi

  • Jenkins + docker/mesos

     

  • travis-ci

  • CircleCI

open source

hosting

Conclusion

  • Persévérez
  • Oubliez vos réflexes VM
  • Pensez container

SensioLabs recrute !
job@sensiolabs.com

Merci !

@jderusse

SensioLabs

Docker pour le Dev & CI

By Jérémy DERUSSÉ