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