Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Deploy your application in a box - DPC2016
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Willem-Jan Zijderveld
June 24, 2016
Programming
240
0
Share
Deploy your application in a box - DPC2016
Willem-Jan Zijderveld
June 24, 2016
More Decks by Willem-Jan Zijderveld
See All by Willem-Jan Zijderveld
Deploy je applicatie als een pakketje
wjzijderveld
0
94
Deploy your application in a box - 010PHP
wjzijderveld
0
230
Let's write some history - PHPDay 2016
wjzijderveld
1
280
Let's write some History - PHPBenelux 2016
wjzijderveld
1
310
Event Sourcing in practice @ Qandidate.com
wjzijderveld
1
400
Let's write some history - DPC 2015
wjzijderveld
1
290
Symfony CMF - A Decoupled Content Management Framework
wjzijderveld
0
130
PHPCR: A (better) way to structure content
wjzijderveld
0
190
Other Decks in Programming
See All in Programming
Vibe하게 만드는 Flutter GenUI App With ADK , 박제창, BWAI Incheon 2026
itsmedreamwalker
0
540
Running Swift without an OS
kishikawakatsumi
0
320
forteeの改修から振り返るPHPerKaigi 2026
muno92
PRO
3
240
Symfony + NelmioApiDocBundle を使った スキーマ駆動開発 / Schema Driven Development with NelmioApiDocBundle
okashoi
0
270
AIと共にエンジニアとPMの “二刀流”を実現する
naruogram
0
130
夢の無限スパゲッティ製造機 -実装篇- #phpstudy
o0h
PRO
0
190
実践ハーネスエンジニアリング #MOSHTech
kajitack
7
5.9k
Symfonyの特性(設計思想)を手軽に活かす特性(trait)
ickx
0
130
安いハードウェアでVulkan
fadis
1
920
KagglerがMixSeekを触ってみた
morim
0
370
「効かない!」依存性注入(DI)を活用したAPI Platformのエラーハンドリング奮闘記
mkmk884
0
310
Mastering Event Sourcing: Your Parents Holidayed in Yugoslavia
super_marek
0
150
Featured
See All Featured
The Curse of the Amulet
leimatthew05
1
11k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.3k
Into the Great Unknown - MozCon
thekraken
40
2.3k
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
150
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.8k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
64
53k
The untapped power of vector embeddings
frankvandijk
2
1.7k
Un-Boring Meetings
codingconduct
0
260
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
64
54k
Paper Plane (Part 1)
katiecoart
PRO
0
6.4k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
160
Navigating the Design Leadership Dip - Product Design Week Design Leaders+ Conference 2024
apolaine
0
260
Transcript
Deploy your application in a box @willemjanz joind.in/talk/73636
Willem-Jan Zijderveld @willemjanz github.com/wjzijderveld
Don't wait till the last slide with questions
Deployment With the usual disclaimer
It works So why change it?
Can you deploy whenever you want?
Are you ever hesitant to deploy?
"We can't deploy, Robin is sick today"
Do users notice when you deploy?
Things break If you like it or not
Deployments fail • Network issues • Migrations • Broken configuration
Downtime
Missed revenue
Angry customers
Customers losing faith
Scared to deploy
Deployments get postponed
Reserving dedicated time to deploy
Deploying outside office hours...
So what would be a more optimal method?
Less humans
No sweat
More often
0 downtime
Repeatable
Let's get started
Less humans Automate your deploy process to a single step
Possible deployment Migrate database Update code on server (FTP, SSH)
Update code dependencies (composer,npm,bower) Clear cache Reload webserver
> ssh your-server.com > sudo su - > cd /var/www/
> git pull > php composer.phar install > npm install > rm -rf app/cache/* > # Some permission commands > service php-fpm reload Example: before
Example: after > ssh
[email protected]
> ./deploy.sh * Pulling changes
* Updating dependencies * Migrating database * Clearing cache * DEPLOYED NEW VERSION
No sweat Lose the fear of deploying
Testing
Seriously, test your code
New code should have tests
Legacy code? Test the critical parts - Unit tests for
important (domain) logic - Functional tests - Monitor production
Continuous Integration
More often
Smaller changes
6 month projects Deliver something every day Don't wait with
deploying until your feature is ready and approved.
Short lived branches • Allows for rapid deployment • Prevents
huge merge conflicts • Speeds up overall development
Feature toggles
Example: recommendations <?php // BasketController.php function actionBasket(User $user) { $basket
= $this->getBasketOfUser($user); $response = ['basket' => $basket->toArray()]; if ($this->featureIsActive('recommendations')) { $response['recommendations'] = $this->getRecommendations($basket); } return new JsonResponse($response); }
Example: recommendations <?php // BasketController.php function actionBasket(User $user) { $basket
= $this->getBasketOfUser($user); $response = ['basket' => $basket->toArray()]; if ($this->featureIsActive('recommendations')) { $response['recommendations'] = $this->getRecommendations($basket); } return new JsonResponse($response); }
Give some users access
Conditional toggle Check the context against the conditions of the
toggle
Example: Conditional toggle <?php $toggle = new Toggle( 'recommendations', ['user_id'
=> GreaterThan(42)] ); $toggle->active(['user_id' => 21]); // true $toggle->active(['user_id' => 1337]); // false
Example: Conditional toggle <?php $toggle = new Toggle( 'recommendations', ['user_id'
=> GreaterThan(42)] ); $toggle->active(['user_id' => 21]); // true $toggle->active(['user_id' => 1337]); // false
Example: Conditional toggle <?php $toggle = new Toggle( 'recommendations', ['user_id'
=> GreaterThan(42)] ); $toggle->active(['user_id' => 21]); // true $toggle->active(['user_id' => 1337]); // false
Example: Conditional toggle <?php $toggle = new Toggle( 'recommendations', ['user_id'
=> GreaterThan(42)] ); $toggle->active(['user_id' => 21]); // true $toggle->active(['user_id' => 1337]); // false
Feature management GUI API Redis http://labs.qandidate.com/blog/2014/08/19/open-sourcing-our-feature-toggle-api-and-ui/
More often - Smaller iterations - Don't wait for the
whole feature to complete - Make use of feature toggles
0 downtime
Prepare your deployment
Deployment Webserver Application - version 1
Deployment Webserver Application - version 1 Application - version 2
Deployment Webserver Application - version 1 Application - version 2
Deployment Webserver Application - version 1
Deployment Webserver Application - version 1 Install code
Deployment Webserver Application - version 1 Install code Migrations
Deployment Webserver Application - version 1 Install code Migrations Warmup
cache
Deployment Webserver Application - version 1 Install code Migrations Warmup
cache Application - version 2
Deployment Webserver Application - version 1 Install code Migrations Warmup
cache Application - version 2
Example /var /project /releases /20160608-1.0 /20160609-1.1 /current -> /var/project/releases/20160608-1.0
0 downtime - Don't touch the current version - Abort
when any of the steps fail - Make your migrations backwards compatible
Put it in a box
Repeatable
Environment independent
The box In it's simplest form: just a zip
Independent
Reusable On all environments
Some challenges
Filesystem
<!-- services.xml --> <services> <service id="uploader" class="AppBundle\Uploader"> <argument>/var/www/uploads/</argument> </service> </services>
Flysystem Abstract the filesystem - Makes it easier to test
- Makes your code independent of the filesystem, f.e.: - Production uses S3 - Development uses local files
<!-- services.xml --> <services> <service id="uploader" class="AppBundle\Uploader"> <argument type="service" id="oneup_flysystem.private_filesystem"
/> </service> </services>
# config.yml oneup_flysystem: adapters: private_s3: awss3v2: client: aws_s3_client bucket: %s3_private_files_bucket%
filesystem: private: adapter: private_s3
<?php // Uploader.php function uploadAvatar($filename) { $stream = fopen($filename, 'r+');
$this->filesystem ->writeStream('avatars/' . $filename, $stream); if (is_resource($stream)) { fclose($stream); } }
File is now at S3: bucket/avatars/filename. ext
Configuration
# config_prod.yml imports: - { resource: config.yml } doctrine: dbal:
default_connection: default connections: default: driver: pdo_mysql host: 192.168.1.10 dbname: acme_prod user: acme_prod password: SuperSecure
What about staging and acceptance?
Give control to the environment
# config.yml imports: - { resource: parameters.yml } - {
resource: parameters.php } doctrine: dbal: default_connection: default connections: default: driver: pdo_mysql host: %db.hostname% dbname: %db.name% user: %db.user% password: %db.password%
# config.yml imports: - { resource: parameters.yml } - {
resource: parameters.php } doctrine: dbal: default_connection: default connections: default: driver: pdo_mysql host: %db.hostname% dbname: %db.name% user: %db.user% password: %db.password%
# config.yml imports: - { resource: parameters.yml } - {
resource: parameters.php } doctrine: dbal: default_connection: default connections: default: driver: pdo_mysql host: %db.hostname% dbname: %db.name% user: %db.user% password: %db.password%
<?php // parameters.php $parameterMapping = [ 's3_private_files_bucket' => 'MYAPP_S3_PRIVATE_FILES_BUCKET', ];
foreach ($parameterMapping as $parameter => $env) { if (false !== ($value = getenv($env))) { $container->setParameter($parameter, $value); } }
$_ENV['SYMFONY__*'] But those values get overwritten by parameters defined in
config.yml / parameters.yml
PHP dot env https://github.com/vlucas/phpdotenv
<?php // web/index.php $dotenv = new Dotenv\Dotenv(__DIR__ . '/../'); $dotenv->load();
Load .env # .env MYAPP_S3_PRIVATE_FILES_BUCKET=yourbucket
Building the box - Should be reusable - Eliminate hard
dependencies on environment - Give control to the environment
Summary - Less manual steps, eliminate the humans - Lose
the fear: test your stuff - Deploy more often, don't postpone - Deployment shouldn't cause downtime - A deployment should be repeatable
Other questions? @willemjanz Please leave some feedback! joind.in/talk/73636