Slide 1

Slide 1 text

Configuring Symfony from localhost to High Availability #SymfonyCon @nicolasgrekas

Slide 2

Slide 2 text

@nicolasgrekas @nicolasgrekas https://symfony.com/the-fast-track

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

@nicolasgrekas Configuring a Symfony app •DI parameters and PHP constants •Environment variables •Secrets •(High Availability)

Slide 5

Slide 5 text

What is a Symfony Application? @nicolasgrekas

Slide 6

Slide 6 text

A Symfony app Uses a compiled container @nicolasgrekas

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

@nicolasgrekas config/services.yaml # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices/configuration.html#application-related-configuration parameters: locale: 'en' # This parameter defines the codes of the locales (languages) enabled in the application app_locales: en|fr|de|es|cs|nl|ru|uk|ro|pt_BR|pl|it|ja|id|ca|sl|hr|zh_CN|bg|tr|lt app.notifications.email_sender: [email protected]

Slide 9

Slide 9 text

@nicolasgrekas /** * Gets the private 'App\Command\ListUsersCommand' shared autowired service. * * @return \App\Command\ListUsersCommand */ protected function getListUsersCommandService() { $this->privates['App\\Command\\ListUsersCommand'] = $instance = new \App\Command\ListU ($this->services['swiftmailer.mailer.default'] ?? $this->getSwiftmailer_Mailer_Def '[email protected]', ($this->privates['App\\Repository\\UserRepository'] ?? $this->getUserRepositorySer ); $instance->setName('app:list-users'); return $instance; } var/cache/…/srcApp...Container.php

Slide 10

Slide 10 text

@nicolasgrekas var/cache/…/srcApp...Container.php /** * Gets the private 'App\EventSubscriber\CommentNotificationSubscriber' shared autowired s * * @return \App\EventSubscriber\CommentNotificationSubscriber */ protected function getCommentNotificationSubscriberService() { return $this->privates['App\\EventSubscriber\\CommentNotificationSubscriber'] = new \A ($this->services['swiftmailer.mailer.default'] ?? $this->getSwiftmailer_Mailer_Def ($this->services['router'] ?? $this->getRouterService()), ($this->services['translator'] ?? $this->getTranslatorService()), '[email protected]' ); }

Slide 11

Slide 11 text

@nicolasgrekas Parameters are static! •Updates require recompilation! •Available at built time •Cluster-wide settings Fit configuring client requirements!

Slide 12

Slide 12 text

@nicolasgrekas Parameter examples •Locale(s) •Facebook app id •Google Analytics tracker id •framework.cache.prefix_seed Anything commitable (this excludes secrets!)

Slide 13

Slide 13 text

Best Practice Define business- related settings in config/services.yaml

Slide 14

Slide 14 text

Best Practice Define rarely changing settings in PHP constants

Slide 15

Slide 15 text

No content

Slide 16

Slide 16 text

A Symfony app Can be configured dynamically via env vars @nicolasgrekas

Slide 17

Slide 17 text

@nicolasgrekas config/packages/mailer.yaml framework: mailer: dsn: '%env(MAILER_DSN)%'

Slide 18

Slide 18 text

@nicolasgrekas config/packages/doctrine.yaml doctrine: dbal: url: '%env(resolve:DATABASE_URL)%'

Slide 19

Slide 19 text

@nicolasgrekas var/cache/…/srcApp...Container.php /** * Gets the public 'doctrine.dbal.default_connection' shared service. * * @return \Doctrine\DBAL\Connection */ protected function getDoctrine_Dbal_DefaultConnectionService() { // ... return $this->services['doctrine.dbal.default_connection'] = (new \Doctrine\Bundle\DoctrineBundle\ConnectionFactory([])) ->createConnection([ 'driver' => 'pdo_sqlite', 'charset' => 'utf8mb4', 'url' => $this->getEnv('resolve:DATABASE_URL'), 'host' => 'localhost', 'port' => NULL, 'user' => 'root', 'password' => NULL, 'driverOptions' => [], 'serverVersion' => '3.15',

Slide 20

Slide 20 text

@nicolasgrekas vendor/dependency-injection/… if (isset($_ENV[$name])) { $env = $_ENV[$name]; } elseif (isset($_SERVER[$name]) && 0 !== strpos($name, 'HTTP_')) { $env = $_SERVER[$name]; } elseif (false === ($env = getenv($name)) || null === $env) { // ... Check httpoxy.org getenv() is not thread safe variable_order=EGPCS Access to fastcgi_params, env vars, etc.

Slide 21

Slide 21 text

@nicolasgrekas Environment Variables

Slide 22

Slide 22 text

@nicolasgrekas

Slide 23

Slide 23 text

@nicolasgrekas $ SYMFONY='<3' php -S localhost:8000

Slide 24

Slide 24 text

@nicolasgrekas Anything coming from the runtime environment •OS-level env vars •SAPI-level env vars – e.g. FastCGI parameters •.env files •File contents •FUSE mount points •Key/value services – Consul / DNS

Slide 25

Slide 25 text

@nicolasgrekas Reconfiguring the runtime environment? •Per-process, immutable: restart •Per-vhost/URL: reload the server •Committed: redeploy •Files : reupload •Virtual filesystem: live updates •Network service: live updates

Slide 26

Slide 26 text

@nicolasgrekas Environment variables •Don’t require recompiling the app •Can be updated live •Shouldn’t be used at built time •Can vary by deployment target Fit configuring hosting requirements!

Slide 27

Slide 27 text

Best Practice Define infrastructure- related settings in env vars

Slide 28

Slide 28 text

But how should we configure env vars?! @nicolasgrekas

Slide 29

Slide 29 text

@nicolasgrekas Delegated to devs: for mostly static parts •Commit defaults in .env •rsync .env.local composer dump-env prod (creates .env.local.php)

Slide 30

Slide 30 text

@nicolasgrekas Delegated to ops: enables auto-scaling •Ansile, chef, Dockerfile, etc. •Server vhosts •The web-UI of your hoster •Don’t forget about the CLI! symfony var:set FOO=bar

Slide 31

Slide 31 text

@nicolasgrekas Delegated to bots: unlocks high availability! •File rsync •Virtual filesystems •Key/value stores •Consul <3 (Consul Template too) Next: health checks, routing layer hot- reconfiguration with istio/envoy/traeffic/etc.

Slide 32

Slide 32 text

Best Practice Document all env vars in .env

Slide 33

Slide 33 text

@nicolasgrekas class ConsulEnvVarLoader implements EnvVarLoaderInterface { // ... public function loadEnvVars(): array { // $this->consulUrl = 'http://127.0.0.1:8500/v1/kv/website-config'; $consulVars = $this->httpClient ->request('GET', $this->consulUrl) ->toArray()[0]['value']; return json_decode(base64_decode($consulVars), true); } }

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

Best Practice Increase complexity only when needed!

Slide 36

Slide 36 text

What about secrets? @nicolasgrekas

Slide 37

Slide 37 text

@nicolasgrekas

Slide 38

Slide 38 text

@nicolasgrekas $ SYMFONY='<3' php -S localhost:8000

Slide 39

Slide 39 text

Fact: .env.local.php is more secure than real env vars @nicolasgrekas

Slide 40

Slide 40 text

@nicolasgrekas Security threats – entropy •Remote access •Insecure deployment network •Shared hosting •Bad document root / php.ini •Employees leaving •Technical responsibilities in the wrong hands

Slide 41

Slide 41 text

@nicolasgrekas Security threats mitigation •Rotate secrets •Store and deploy secrets encrypted •Use .env.local.php •Use asymmetric cryptography •Make things convenient

Slide 42

Slide 42 text

@nicolasgrekas Secrets management tools •Pet .env.local file deployed via SSH •bin/console secrets:* •(Consul|Docker|Kubernetes) Vault

Slide 43

Slide 43 text

@nicolasgrekas

Slide 44

Slide 44 text

@nicolasgrekas

Slide 45

Slide 45 text

@nicolasgrekas $ cat config/secrets/dev/dev.GITHUB_API_TOKEN.aebb6a.php

Slide 46

Slide 46 text

@nicolasgrekas

Slide 47

Slide 47 text

@nicolasgrekas class SodiumVault implements EnvVarLoaderInterface public function loadEnvVars(): array { return $this->list(true); }

Slide 48

Slide 48 text

@nicolasgrekas Configure secrets framework: secrets: vault_directory: '%kernel.project_dir%/config/secrets/%kernel.environment%' local_dotenv_file: '%kernel.project_dir%/.env.local' decryption_env_var: 'base64:default::SYMFONY_DECRYPTION_SECRET' export SYMFONY_DECRYPTION_SECRET=$(php -r 'echo base64_encode( \ require "config/secrets/prod/prod.decrypt.private.php" \ );')

Slide 49

Slide 49 text

@nicolasgrekas Deploy secrets •Same workflow as code – git blame <3 •But the decryption key •Share the keys with the appropriate persons •Rotation is yours to build •Decrypt live – or at build time bin/console secrets:decrypt-to-local (populates .env.local)

Slide 50

Slide 50 text

Takeaways? @nicolasgrekas

Slide 51

Slide 51 text

@nicolasgrekas Key takeaways •Use parameters for business requirements •Use env vars for infra-related configuration •Use .env files first, increase complexity later •Use bin/console secrets:* •Use Consul/etc. when the need is here You’re on the path to high-availability!

Slide 52

Slide 52 text

Merci Thank you Gracias ﺍﺮﻜﺷ 多謝 Ευχαριστώ ありがとう 감사합 니다 Спасибо!

Slide 53

Slide 53 text

No content