service discovery and configuration provisioning

Post on 14-Jan-2017

1.213 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Service discovery and configuration provisioning

mariuszgil srcministry

Mariusz Gil

What is an application? Source code?

Is it a complete solution for client?

How to manage the configuration?

Old school approach…

<?php  define('DB_HOST', 'localhost'); define('DB_NAME', 'northwind'); define('DB_USER', 'root'); define('DB_PASS', 'your_password');  // This connection allows your application to be used for multi-lingual websites. function _connect_to_mysql($is_utf8 = null) { $mysql_link = mysql_connect(DB_HOST, DB_USER, DB_PASS) or die("Could not connect to database server"); mysql_select_db(DB_NAME, $mysql_link) or die("Could not select database");   if (is_null($is_utf8)) { /* This sets collation for the connection to utf8_general_ci because it is the default collation for utf8. This enables multi-lingual capability in database. */ mysql_query("SET NAMES 'utf8'"); } return $mysql_link; }

<?php  // ...  // ** MySQL settings - You can get this info from your web host ** // define('DB_NAME', 'database');  /** MySQL database username */ define('DB_USER', 'username');  /** MySQL database password */ define('DB_PASSWORD', 'password');  /** MySQL hostname */ define('DB_HOST', 'localhost');  /** Database Charset to use in creating database tables. */ define('DB_CHARSET', 'utf8');  /** The Database Collate type. Don't change this if in doubt. */ define('DB_COLLATE', '');

<?php /** * The base configuration for WordPress * * The wp-config.php creation script uses this file during the * installation. You don't have to use the web site, you can * copy this file to "wp-config.php" and fill in the values. * * @link https://codex.wordpress.org/Editing_wp-config.php * @package WordPress */  // ** MySQL settings - You can get this info from your web host ** // define('DB_NAME', 'database');  /** MySQL database username */ define('DB_USER', 'username');  /** MySQL database password */ define('DB_PASSWORD', 'password');  /** MySQL hostname */ define('DB_HOST', 'localhost');  /** Database Charset to use in creating database tables. */ define('DB_CHARSET', 'utf8');  /** The Database Collate type. Don't change this if in doubt. */ define('DB_COLLATE', '');

bash-3.2$ ack DB_NAME wp-includes/load.php350: $wpdb = new wpdb( DB_USER, DB_PASSWORD, DB_NAME, DB_HOST ); wp-content/plugins/xcloner-backup-and-restore/restore/XCloner.php580: $config_data = str_replace("define('DB_NAME', '", "define('DB_NAME', ‚".$_REQUEST[mysql_db]."'); #", $config_data) wp-content/plugins/w3-total-cache/lib/W3/Db.php165: parent::__construct(DB_USER, DB_PASSWORD, DB_NAME, DB_HOST);

How we can improve it?

<?php use Pimple\Container; $container = new Container(); $container['db'] = function() { $host = 'localhost'; $dbName = 'wordpress'; $user = 'root'; $pass = ''; return new \PDO("mysql:host={$host};dbname={$dbName}", $user, $pass); };

<?php $dice = new \Dice\Dice(); $rule = [ //Mark the class as shared so the same instance is returned each time 'shared' => true, //The constructor arguments that will be supplied when the instance is created 'constructParams' => [ 'mysql:host=127.0.0.1;dbname=mydb', 'username', 'password', ], ]; //Apply the rule to the PDO class $dice->addRule('PDO', $rule); //Now any time PDO is requested from Dice, the same instance will be returned //And will have been constructed with the arguments supplied in 'constructParams' $pdo = $dice->create('PDO');

<?php $builder = new \DI\ContainerBuilder(); $builder->addDefinitions([ 'foo' => 'hello ', 'bar' => 'world', ]); $builder->addDefinitions([ 'foo' => \DI\decorate(function ($previous, \Interop\Container\ContainerInterface $container) { return $previous . $container->get('bar'); }), ]); $builder->addDefinitions([ 'values' => [ 'value 1', 'value 2', ], ]); $builder->addDefinitions([ // with the same name 'stdClass' => \DI\object('stdClass'), // with name inferred __NAMESPACE__ . '\ObjectDefinition\Class1' => \DI\object(), // with a different name 'object' => \DI\object(__NAMESPACE__ . '\ObjectDefinition\Class1'), ]); $container = $builder->build();

# Swiftmailer Configurationswiftmailer: transport: "%mailer_transport%" host: "%mailer_host%" username: "%mailer_user%" password: "%mailer_password%" spool: { type: memory }

# KNP Menu configurationknp_menu: twig: template: knp_menu.html.twig templating: false default_renderer: twig

# KNP Paginator configurationknp_paginator: page_range: 5 default_options: page_name: page

# LiipImagine configurationliip_imagine: filter_sets: square_200: quality: 100 filters: thumbnail: { size: [200, 200], mode: outbound }

# Configuration parametersparameters: database_host: 127.0.0.1 database_port: null database_name: database database_user: root database_password: null mailer_transport: smtp mailer_host: 127.0.0.1 mailer_user: null mailer_password: null secret: 092402938a039b72278b9b05da3f4d4

Is captcha feature enabled? Is remember-be feature enabled? Is remind-me feature enabled? Is 2-factor authentication enabled? Is brute-force protection enabled? How many attempts per login allowed? What is delay between failed logins? Is hide-errors protection enabled? … Is login feature enabled at all?

What is database host? What is database port? What is database user? What is database password? What is database name? What is database encoding? Is database cache enabled? What is cache host? What is cache port? Where the sessions are stored?

Standard feature Single form Many configuration problems

How to update configs?

mysql> DESC wp_options;+--------------+---------------------+------+-----+---------+----------------+| Field | Type | Null | Key | Default | Extra |+--------------+---------------------+------+-----+---------+----------------+| option_id | bigint(20) unsigned | NO | PRI | NULL | auto_increment || option_name | varchar(64) | NO | UNI | | || option_value | longtext | NO | | NULL | || autoload | varchar(20) | NO | | yes | |+--------------+---------------------+------+-----+---------+----------------+4 rows in set (0.00 sec)

mysql> SELECT * FROM wp_options WHERE option_name LIKE '%enabled%' LIMIT 1;+-----------+----------------------+--------------+----------+| option_id | option_name | option_value | autoload |+-----------+----------------------+--------------+----------+| 87 | link_manager_enabled | 0 | yes |+-----------+----------------------+--------------+----------+1 row in set (0.00 sec)

You need to deploy application to release changes in configuration

host A

host A host B

Another approach to maintain configs

Decouple your application from hardcoded configuration details

by

Service discovery and configuration provisioning tool

Service discovery

Failure detection

Key Value storage

Multi DC ready

host A host B host C host D

192.168.100.56

192.168.100.120

host A host B host C host D

192.168.100.56

192.168.100.120

host A host B host C host D

mysql.service.consul.

redis.service.consul.

agentserver agent agent

host A host B host C host D

mysql.service.consul.

redis.service.consul.

server agent agent agent

Managing services

# curl -X PUT -d '{ "service": { "name": "memcached", "tags": ["cache"], "port": 11211, "check": { "name": "memcached status", "script": "echo stats | nc 127.0.0.1 11211", "interval": "5s" } }}' http://192.168.33.10:8500/v1/catalog/service/memcached

# curl -X GET http://192.168.33.10:8500/v1/catalog/service/memcached[{„Node":"vagrant-ubuntu-trusty-64","Address":"192.168.33.10","ServiceID":"memcached","ServiceName":"memcached","ServiceTags":["cache"],"ServiceAddress":"","ServicePort":11211,"ServiceEnableTagOverride":false,"CreateIndex":19,"ModifyIndex":22}]

<?php

require '../vendor/autoload.php';

$sf = new SensioLabs\Consul\ServiceFactory(array( 'base_url' => 'http://192.168.33.10:8500',));

$catalog = $sf->get('catalog');$response = $catalog->service('memcached')->json();

# Result dataarray ( 0 => array ( 'Node' => 'vagrant-ubuntu-trusty-64', 'Address' => '192.168.33.10', 'ServiceID' => 'memcached', 'ServiceName' => 'memcached', 'ServiceTags' => array ( 0 => 'cache', ), 'ServiceAddress' => '', 'ServicePort' => 11211, 'ServiceEnableTagOverride' => false, 'CreateIndex' => 19, 'ModifyIndex' => 60, ),)

DNS interface for services and tags

<node>.node[.dc].<domain>

[tag.]<service>.service[.dc].<domain>

# dig @192.168.33.10 -p 8600 memcached.service.consul

; <<>> DiG 9.9.5-3ubuntu0.5-Ubuntu <<>> @192.168.33.10 -p 8600 memcached.service.consul; (1 server found);; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 58136;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0;; WARNING: recursion requested but not available

;; QUESTION SECTION:;memcached.service.consul. IN A

;; ANSWER SECTION:memcached.service.consul. 0 IN A 192.168.33.10

;; Query time: 4 msec;; SERVER: 192.168.33.10#8600(192.168.33.10);; WHEN: Fri Jan 29 16:40:37 UTC 2016;; MSG SIZE rcvd: 82

# dig @192.168.33.10 -p 8600 memcached.service.consul SRV

; <<>> DiG 9.9.5-3ubuntu0.5-Ubuntu <<>> @192.168.33.10 -p 8600 memcached.service.consul SRV; (1 server found);; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10299;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1;; WARNING: recursion requested but not available

;; QUESTION SECTION:;memcached.service.consul. IN SRV

;; ANSWER SECTION:memcached.service.consul. 0 IN SRV 1 1 11211 vagrant-ubuntu-trusty-64.node.dc1.consul.

;; ADDITIONAL SECTION:vagrant-ubuntu-trusty-64.node.dc1.consul. 0 IN A 192.168.33.10

;; Query time: 4 msec;; SERVER: 192.168.33.10#8600(192.168.33.10);; WHEN: Fri Jan 29 16:41:06 UTC 2016;; MSG SIZE rcvd: 182

PHP Benelux Fries Example

Service 1 Service 2

# curl -X PUT -d '{ "Datacenter": "dc1", "Node": "vagrant-ubuntu-trusty-64", "Address": "192.168.33.10", "Service": { "ID": "fries-1", "Service": "fries", "Tags": [ "fries" ], "Address": "192.168.33.10", "Port": 1111 }}' http://192.168.33.10:8500/v1/catalog/registertrue

# curl -X PUT -d '{ "Datacenter": "dc1", "Node": "vagrant-ubuntu-trusty-64", "Address": "192.168.33.10", "Service": { "ID": "fries-2", "Service": "fries", "Tags": [ "fries" ], "Address": "192.168.33.10", "Port": 2222 }}' http://192.168.33.10:8500/v1/catalog/registertrue

# dig @192.168.33.10 -p 8600 fries.service.consul SRV

; <<>> DiG 9.9.5-3ubuntu0.5-Ubuntu <<>> @192.168.33.10 -p 8600 fries.service.consul SRV; (1 server found);; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 30707;; flags: qr aa rd; QUERY: 1, ANSWER: 2, AUTHORITY: 0, ADDITIONAL: 2;; WARNING: recursion requested but not available

;; QUESTION SECTION:;fries.service.consul. IN SRV

;; ANSWER SECTION:fries.service.consul. 0 IN SRV 1 1 1111 vagrant-ubuntu-trusty-64.node.dc1.consul.fries.service.consul. 0 IN SRV 1 1 2222 vagrant-ubuntu-trusty-64.node.dc1.consul.

;; ADDITIONAL SECTION:vagrant-ubuntu-trusty-64.node.dc1.consul. 0 IN A 192.168.33.10vagrant-ubuntu-trusty-64.node.dc1.consul. 0 IN A 192.168.33.10

;; Query time: 4 msec;; SERVER: 192.168.33.10#8600(192.168.33.10);; WHEN: Fri Jan 29 23:21:22 UTC 2016;; MSG SIZE rcvd: 310

Storing values

# curl -X GET http://192.168.33.10:8500/v1/kv/foo[{"LockIndex":0,"Key":"foo","Flags":128,"Value":"YmFy","CreateIndex":123,"ModifyIndex":130}]

# curl -X PUT -d 'bar' http://192.168.33.10:8500/v1/kv/footrue

# curl -X GET http://192.168.33.10:8500/v1/kv/foo[{"LockIndex":0,"Key":"foo","Flags":0,"Value":"YmFy","CreateIndex":123,"ModifyIndex":123}]

# curl -X PUT -d 'bar' http://192.168.33.10:8500/v1/kv/foo?flags=128true

<?php require '../vendor/autoload.php'; $sf = new SensioLabs\Consul\ServiceFactory(array('base_url' => 'http://192.168.33.10:8500')); $kv = $sf->get('kv'); $kv->put('foo', 'bar' . time()); $response = $kv->get('foo')->json(); $kv->delete('foo'); // Result data array ( 0 => array ( 'LockIndex' => 0, 'Key' => 'foo', 'Flags' => 0, 'Value' => 'YmFyMTQ1NDA4NTQ3MA==', 'CreateIndex' => 123, 'ModifyIndex' => 147, ), )

Distributed locks

<?php require '../vendor/autoload.php'; $sf = new SensioLabs\Consul\ServiceFactory(array('base_url' => 'http://192.168.33.10:8500')); $session = $sf->get('session'); $kv = $sf->get('kv'); // Start a session $sessionId = $session->create()->json()['ID']; // Lock a key / value with the current session $lockAcquired = $kv->put('session/a-lock', 'a value', ['acquire' => $sessionId])->json(); if (false === $lockAcquired) { $session->destroy($sessionId); echo "The lock is already acquire by another node.\n"; exit(1); } # YOUR CRITICAL SECTION CODE HERE... $kv->delete('session/a-lock'); $session->destroy($sessionId);

Useful to avoid dogpile effect

Even more useful with leader election

Possibilities...

Task #1 Replace one search engine to another Without downtime

<?php $backend = $userId % 100 < $settings->get('search/elastic/access_threshold', 0) ? $container->get('search.elastic') : $container->get('search.sphinx'); $suggestions = $backend->search($query);

# curl -X PUT -d '5' http://192.168.33.10:8500/v1/kv/search/elastic/threshold# curl -X PUT -d '10' http://192.168.33.10:8500/v1/kv/search/elastic/threshold# curl -X PUT -d '15' http://192.168.33.10:8500/v1/kv/search/elastic/threshold# curl -X PUT -d '25' http://192.168.33.10:8500/v1/kv/search/elastic/threshold# curl -X PUT -d '50' http://192.168.33.10:8500/v1/kv/search/elastic/threshold# curl -X PUT -d '75' http://192.168.33.10:8500/v1/kv/search/elastic/threshold# curl -X PUT -d '50' http://192.168.33.10:8500/v1/kv/search/elastic/threshold

<?php $engine = $userId % 100 < $settings->get('search/elastic/access_threshold') ? 'elastic' : 'sphinx'; $backend = $container->get('search.' . $engine); $minimalQueryLength = $settings->get('search.' . $engine . '.minimal_length', 3); $maximalNumberOfSuggestion = $settings->get('search.' . $engine . '.suggestions_limits', 10); if (length($query) >= $minimalQueryLength) { $suggestions = $backend->search($query, $maximalNumberOfSuggestion); }

Task #2 Add CPU/memory to the server In the middle of the night

<?php if ($settings->get('recommendations/enabled')) { # Fetch recommendations for user based on request # ... }

# curl -X PUT -d '0' http://192.168.33.10:8500/v1/kv/recommendations/enabled# halt

# ADD MORE CPU/MEMORY

# curl -X PUT -d '1' http://192.168.33.10:8500/v1/kv/recommendations/enabled

# curl -X PUT -d '{> "Datacenter": "dc1",> "Node": "vagrant-ubuntu-trusty-64",> "ServiceID": "memcached"> }' http://192.168.33.10:8500/v1/catalog/deregistertrue

# curl -X PUT -d '{> "Datacenter": "dc1",> "Node": "vagrant-ubuntu-trusty-64",> "Address": "192.168.33.10",> "Service": {> "ID": "memcached",> "Service": "memcached",> "Tags": [> "cache"> ],> "Address": "192.168.33.10",> "Port": 11211> }> }' http://192.168.33.10:8500/v1/catalog/registertrue

<?php require '../vendor/autoload.php'; $sf = new SensioLabs\Consul\ServiceFactory(array( 'base_url' => 'http://192.168.33.10:8500', )); $catalog = $sf->get('catalog'); $serviceDefinitions = $catalog->service('memcached')->json(); $memcached = new Memcache(); foreach ($serviceDefinitions as $serviceDefinition) { $memcached->addserver($serviceDefinition['ServiceAddress'], $serviceDefinition['ServicePort']); }

Long running background workers

Watch the Watches. React

host A host B host C host D

agentserver

web app

agent

background worker

agent

background worker

watch for event

make changes & fire event

API methods

Agent

/v1/agent/checks /v1/agent/services /v1/agent/members /v1/agent/self /v1/agent/maintenance /v1/agent/join/<address> /v1/agent/force-leave/<node>> /v1/agent/check/register /v1/agent/check/deregister/<checkID> /v1/agent/check/pass/<checkID> /v1/agent/check/warn/<checkID> /v1/agent/check/fail/<checkID> /v1/agent/service/register /v1/agent/service/deregister/<serviceID> /v1/agent/service/maintenance/<serviceID>

Catalog

/v1/catalog/register /v1/catalog/deregister /v1/catalog/datacenters /v1/catalog/nodes /v1/catalog/services /v1/catalog/service/<service> /v1/catalog/node/<node>

Health Checks

/v1/health/node/<node> /v1/health/checks/<service> /v1/health/service/<service> /v1/health/state/<state>

Network coordinates

/v1/coordinate/datacenters /v1/coordinate/nodes

Key / Values

/v1/kv/<key>

Events

/v1/event/fire/<name> /v1/event/list

ACL

/v1/agent/checks /v1/agent/services /v1/agent/members /v1/agent/self /v1/agent/maintenance /v1/agent/join/<address> /v1/agent/force-leave/<node>> /v1/agent/check/register /v1/agent/check/deregister/<checkID> /v1/agent/check/pass/<checkID> /v1/agent/check/warn/<checkID> /v1/agent/check/fail/<checkID> /v1/agent/service/register /v1/agent/service/deregister/<serviceID> /v1/agent/service/maintenance/<serviceID>

Prepared queries

/v1/query /v1/query/<query> /v1/query/<query or name>/execute

Sessions

/v1/session/create /v1/session/destroy/<session> /v1/session/info/<session> /v1/session/node/<node> /v1/session/list /v1/session/renew

Server status

/v1/status/leader /v1/status/peers

Alternative solutions

Choose one or implement your own If you need it. Really need it

Thanks! Enjoy config management! https://joind.in/talk/198e7

mariuszgil srcministry

top related