84
Managing a shared MySQL farm Thijs Feryn Evangelist +32 (0)9 218 79 06 [email protected] phpDay Friday May 13th 2011 Verona, Italy

Managing a shared_mysql_farm_phpday2011

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Managing a shared_mysql_farm_phpday2011

Managing  a  shared  MySQL  farmThijs  FerynEvangelist+32  (0)9  218  79  [email protected]

phpDayFriday  May  13th  2011Verona,  Italy

Page 2: Managing a shared_mysql_farm_phpday2011

About  me

I’m  an  evangelist  at  Combell

Page 3: Managing a shared_mysql_farm_phpday2011

About  me

I’m  a  board  member  at  PHPBenelux

Page 4: Managing a shared_mysql_farm_phpday2011

I  live  in  the  wonderful  city  of  Bruges

MPBecker  -­‐  Bruges  by  Night  hYp://www.flickr.com/photos/galverson2/3715965933

Page 5: Managing a shared_mysql_farm_phpday2011

Give  me  feedback:  hYp://joind.in/3003

Read  my  blog:  hYp://blog.feryn.eu

Follow  me  on  TwiYer:  @ThijsFeryn

Page 6: Managing a shared_mysql_farm_phpday2011
Page 7: Managing a shared_mysql_farm_phpday2011

Challenges

Page 8: Managing a shared_mysql_farm_phpday2011

Challenges

✓Management  across  mulaple  nodes✓Aggregaang  data  from  mulaple  nodes✓Name  clashes✓Quota  management

Page 9: Managing a shared_mysql_farm_phpday2011

Soluaons

Page 10: Managing a shared_mysql_farm_phpday2011

Soluaons

✓Centralized  provisioning  database✓GeYers  on  the  provisioning  database✓Node  mapper  for  user/db/privilege  management✓INFORMATION_SCHEMA  for  quota  management✓Prefixes  to  avoid  name  clashes

Page 11: Managing a shared_mysql_farm_phpday2011

Provisioning  plan

Page 12: Managing a shared_mysql_farm_phpday2011

User✓Id✓Prefix✓Username✓Password✓Enabled✓DatabaseId✓Write✓CreateDate✓UpdateDate

Database✓Id✓Node✓Prefix✓Database✓Quota✓Enabled✓Overquota✓CreateDate✓UpdateDate

Page 13: Managing a shared_mysql_farm_phpday2011
Page 14: Managing a shared_mysql_farm_phpday2011

Mapping  uses  cases  to  SQL

Page 15: Managing a shared_mysql_farm_phpday2011

MySQL  authenacaaon  &  privilege  system

Page 16: Managing a shared_mysql_farm_phpday2011

✓Add  user✓Delete  user✓Reset  user  password✓Disable  user✓Enable  user

Page 17: Managing a shared_mysql_farm_phpday2011

Add  user

INSERT INTO `user`(`prefix`,`username`,`password`,`createdate`) VALUES(‘test’,‘test_user’,‘mypass123’,NOW());

Page 18: Managing a shared_mysql_farm_phpday2011

Delete  user

DELETE FROM `user` WHERE username=‘test_user’;

DELETE u.*, db.* FROM `mysql`.`user` u LEFT JOIN `mysql`.`db` db ON(db.`User` = u.`User`) WHERE u.`User` = ‘test_user’;

Page 19: Managing a shared_mysql_farm_phpday2011

Reset  user  password

UPDATE `user` SET `password` = ‘newpass123’ WHERE `username` = ‘test_user’;

UPDATE `mysql`.`user` SET `Password` = PASSWORD(‘newpass123’) WHERE `User`= ‘test_user’;

Page 20: Managing a shared_mysql_farm_phpday2011

Disable  user

UPDATE `user` SET `enabled` = '0' WHERE `username` = ‘test_user’;

UPDATE `mysql`.`user` SET `Host` = ‘localhost’ WHERE `User`= ‘test_user’

Page 21: Managing a shared_mysql_farm_phpday2011

Enable  user

UPDATE `user` SET `enabled` = '1' WHERE `username` = ‘test_user’;

UPDATE `mysql`.`user` SET `Host` = ‘%’ WHERE `User`= ‘test_user’

Page 22: Managing a shared_mysql_farm_phpday2011

✓Add  database✓Delete  database✓Set  database  quota✓Disable  database✓Enable  database

Page 23: Managing a shared_mysql_farm_phpday2011

Add  database

INSERT INTO `database`(`node`,`prefix`,`database`,`quota`,`createdate`) VALUES(1,‘test’,‘test_db’,10,NOW());

CREATE DATABASE test_db1;

Page 24: Managing a shared_mysql_farm_phpday2011

Delete  database

DELETE FROM `database` WHERE `database` = ‘test_db’;

Page 25: Managing a shared_mysql_farm_phpday2011

Delete  database

SELECT u.usernameFROM `user` uWHERE u.databaseId = 123GROUP BY u.username; Find  

deletable  users  to  delete  from  MySQL    privileges  system

Are  linked  to  this  database

Page 26: Managing a shared_mysql_farm_phpday2011

Delete  database

DELETE u.*, db.* FROM `user` u LEFT JOIN `db` db ON(db.`User` = u.`User`) WHERE u.`User` IN('test_user’);

Deletethese  users  from  MySQL    privileges  

system

Page 27: Managing a shared_mysql_farm_phpday2011

Delete  database

DROP DATABASE test_db;

Page 28: Managing a shared_mysql_farm_phpday2011

Set  database  quota

UPDATE `database` SET `quota` = 100WHERE `database` = ‘test_db’;

Page 29: Managing a shared_mysql_farm_phpday2011

Disable  database

UPDATE `database` SET `enabled` = '0' WHERE `database` = ‘test_db’;

DELETE FROM `db` WHERE db = 'test_db’;

Page 30: Managing a shared_mysql_farm_phpday2011

Enable  database

UPDATE `database` SET `enabled` = '1' WHERE `database` = ‘test_db’;

Page 31: Managing a shared_mysql_farm_phpday2011

Enable  database

SELECT u.username, u.writeFROM user uWHERE u.databaseId = 123 Find  

user  mappings  to  re-­‐enable

Page 32: Managing a shared_mysql_farm_phpday2011

Enable  database

INSERT INTO `db`(Host,Db,User,Select_priv,Insert_priv, Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,References_priv,Index_priv,Alter_priv,Create_tmp_table_priv,Lock_tables_priv, Create_view_priv,Show_view_priv,Create_routine_priv, Alter_routine_priv,Execute_priv)

Page 33: Managing a shared_mysql_farm_phpday2011

Enable  database

VALUES(‘%’,‘test_db’,‘test_user’,'Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');

VALUES(‘%’,‘test_db’,‘test_user’,'Y','N','N','N','N','N','N','N','N','N','N','N','N','Y','N','N','Y');

Write  permissions

Read-­‐only  

permissions

Page 34: Managing a shared_mysql_farm_phpday2011

✓Grant  privilege✓Revoke  privilege✓Enable  database  wriang✓Disable  database  wriang

Page 35: Managing a shared_mysql_farm_phpday2011

Grant  privilege

UPDATE `user` SET `databaseId`=123, `write`='1' WHERE `username`= ‘test_user’;

UPDATE `user` SET `databaseId`=123, `write`='0' WHERE `username`= ‘test_user’;

Write  permissions

Read-­‐only  

permissions

Page 36: Managing a shared_mysql_farm_phpday2011

Grant  privilege

INSERT INTO `user`(Host,User,Password) VALUES(‘%’,‘test_user’,PASSWORD(‘password’));

Try  adding  user  or  catch  duplicate  user  error

Page 37: Managing a shared_mysql_farm_phpday2011

Grant  privilege

INSERT INTO `db`(Host,Db,User,Select_priv,Insert_priv, Update_priv,Delete_priv,Create_priv,Drop_priv,Grant_priv,References_priv,Index_priv,Alter_priv,Create_tmp_table_priv,Lock_tables_priv, Create_view_priv,Show_view_priv,Create_routine_priv, Alter_routine_priv,Execute_priv)

Page 38: Managing a shared_mysql_farm_phpday2011

Grant  privilege

VALUES(‘%’,‘test_db’,‘test_user’,'Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');

VALUES(‘%’,‘test_db’,‘test_user’,'Y','N','N','N','N','N','N','N','N','N','N','N','N','Y','N','N','Y');

Write  permissions

Read-­‐only  

permissions

Page 39: Managing a shared_mysql_farm_phpday2011

Revoke  privilege

UPDATE `user` SET `databaseId`= NULL, `write`= NULL WHERE `user`= ‘test_user’;

DELETE u.*, db.* FROM `user` u LEFT JOIN `db` db ON(db.`User` = u.`User`) WHERE u.`User` = ‘test_user’;

Page 40: Managing a shared_mysql_farm_phpday2011

Enable  database  wriang

UPDATE `user` SET `write`= '1' WHERE `username` = ‘test_user’;

Page 41: Managing a shared_mysql_farm_phpday2011

Enable  database  wriang

UPDATE `user` SET `write`= '1' WHERE `username` = ‘test_user’;

UPDATE `db` SET`Select_priv` = 'Y',`Insert_priv` = 'Y',`Update_priv` = 'Y',`Delete_priv` = 'Y',`Create_priv` = 'Y',`Drop_priv` = 'Y',`Grant_priv` = 'N',`References_priv` = 'Y',`Index_priv` = 'Y',`Alter_priv` = 'Y',`Create_tmp_table_priv`='Y',`Lock_tables_priv` = 'Y',`Create_view_priv` = 'Y',`Show_view_priv` = 'Y',`Create_routine_priv` = 'Y',`Alter_routine_priv` = 'Y',`Execute_priv` = 'Y'WHERE `db`= ‘test_db’ AND `user` = ‘test_user’;

Page 42: Managing a shared_mysql_farm_phpday2011

Disable  database  wriang

UPDATE `user` SET `write`= '0' WHERE `username` = ‘test_user’;

Page 43: Managing a shared_mysql_farm_phpday2011

Disable  database  wriang

UPDATE `user` SET `write`= '1' WHERE `username` = ‘test_user’;

UPDATE `db` SET`Select_priv` = 'Y',`Insert_priv` = 'N',`Update_priv` = 'N',`Delete_priv` = 'N',`Create_priv` = 'N',`Drop_priv` = 'N',`Grant_priv` = 'N',`References_priv` = 'N',`Index_priv` = 'N',`Alter_priv` = 'N',`Create_tmp_table_priv`='N',`Lock_tables_priv` = 'N',`Create_view_priv` = 'N',`Show_view_priv` = 'Y',`Create_routine_priv` = 'N',`Alter_routine_priv` = 'N',`Execute_priv` = 'Y'WHERE `db`= ‘test_db’ AND `user` = ‘test_user’;

Page 44: Managing a shared_mysql_farm_phpday2011

Quota  management

Page 45: Managing a shared_mysql_farm_phpday2011

Quota  management

✓Limits  in  provisioning  database✓Current  usage  stored  in  INFORMATION_SCHEMA✓Raao  calculated  via  cron  task✓Write  permissions  disabled  while  over  quota

Page 46: Managing a shared_mysql_farm_phpday2011

Quota  management

SELECT `database`,`quota` FROM `database`

SELECT TABLE_SCHEMA as `database`,ROUND(SUM(DATA_LENGTH + INDEX_LENGTH)/1048576,2) as `usage`FROM `information_schema`.`TABLES`GROUP BY TABLE_SCHEMA

Page 47: Managing a shared_mysql_farm_phpday2011

Quota  management

UPDATE `database` SET `overquota` = '1' WHERE `database` = ‘test_db’;

Page 48: Managing a shared_mysql_farm_phpday2011

Quota  management

UPDATE `db` SET`Select_priv` = 'Y',`Insert_priv` = 'N',`Update_priv` = 'N',`Delete_priv` = 'Y',`Create_priv` = 'N',`Drop_priv` = 'Y',`Grant_priv` = 'N',`References_priv` = 'N',`Index_priv` = 'N',`Alter_priv` = 'N',`Create_tmp_table_priv` = 'N',`Lock_tables_priv` = 'N',`Create_view_priv` = 'N',`Show_view_priv` = 'Y',`Create_routine_priv` = 'N',`Alter_routine_priv` = 'N',`Execute_priv` = 'Y' WHERE `db`= ‘test_database’

Page 49: Managing a shared_mysql_farm_phpday2011

Quota  management

UPDATE `database` SET `overquota` = '0' WHERE `database` = ‘test_db’;

Page 50: Managing a shared_mysql_farm_phpday2011

Quota  management

UPDATE `db` SET`Select_priv` = 'Y',`Insert_priv` = 'Y',`Update_priv` = 'Y',`Delete_priv` = 'Y',`Create_priv` = 'Y',`Drop_priv` = 'Y',`Grant_priv` = 'N',`References_priv` = 'Y',`Index_priv` = 'Y',`Alter_priv` = 'Y',`Create_tmp_table_priv`='Y',`Lock_tables_priv` = 'Y',`Create_view_priv` = 'Y',`Show_view_priv` = 'Y',`Create_routine_priv` = 'Y',`Alter_routine_priv` = 'Y',`Execute_priv` = 'Y' WHERE `db`= ‘test_db’

Page 51: Managing a shared_mysql_farm_phpday2011

Goals

Page 52: Managing a shared_mysql_farm_phpday2011

Single  point  of  management

Page 53: Managing a shared_mysql_farm_phpday2011

Single  point  of  connecaon

Page 54: Managing a shared_mysql_farm_phpday2011

Replicaaon  &  loadbalancing

Page 55: Managing a shared_mysql_farm_phpday2011

Proxying  strategies

Page 56: Managing a shared_mysql_farm_phpday2011

Server  proxy

Page 57: Managing a shared_mysql_farm_phpday2011

Goal

Page 58: Managing a shared_mysql_farm_phpday2011

Goal

✓  Accept  connecaon  using  the  proxy✓Hook  into  the  authenacaaon✓Match  user  to  the  provisioning  DB✓Fetch  node  from  provisioning✓Switch  to  the  right  node

➡Effecave  proxying  soluaon

Page 59: Managing a shared_mysql_farm_phpday2011

Reality

Page 60: Managing a shared_mysql_farm_phpday2011

Reality

✓  Accept  connecaon  using  the  proxy✓Hook  into  the  authenacaaon✓Match  user  to  the  provisioning  DB✓Fetch  node  from  provisioning✓Switch  to  the  right  node

➡Effecave  proxying  soluaon

Page 61: Managing a shared_mysql_farm_phpday2011

Reality

Connecaon  switching  only  happens  in  the  connect_server  hook

Auth  info  is  only  available  starang  from  the  read_auth  hook

Page 62: Managing a shared_mysql_farm_phpday2011

Bridge  the  gap

Redirect  to  node  based  on  client  IP

Page 63: Managing a shared_mysql_farm_phpday2011

Let’s  see  some  code  !

Page 64: Managing a shared_mysql_farm_phpday2011

Coderequire('luarocks.require')require('md5')require('Memcached')require('luasql.mysql')local  memcache  =  Memcached.Connect()-­‐-­‐-­‐  configlocal  mysqlhost  =  "localhost"local  mysqluser  =  "myUser"local  mysqlpassword  =  "MyPwDsesd"local  mysqldatabase  =  "test"-­‐-­‐  debuglocal  debug  =  true

Dependencies  &  config

Page 65: Managing a shared_mysql_farm_phpday2011

Code

function  error_result  (msg)   proxy.response  =  {     type  =  proxy.MYSQLD_PACKET_ERR,     errmsg  =  msg,     errcode  =  7777,     sqlstate  =  'X7777',   }   return  proxy.PROXY_SEND_RESULTend

Custom  MySQL  errors

Page 66: Managing a shared_mysql_farm_phpday2011

Codefunction  node_get(ip)   local  node  =  memcache:get(md5.sumhexa(ip))     if  not  node  ==  nil  then        return  loadstring('return  '..memcache:get(md5.sumhexa(ip)))()       end     node  =  sql_get(ip)   if  node  ==  nil  then          return  nil   end

     memcache:set(md5.sumhexa(ip),  node,  3600)        return  node

end

Get  node  from  cache  or  database

Page 67: Managing a shared_mysql_farm_phpday2011

Codefunction  sql_get(ip)     env  =  assert  (luasql.mysql())   con  =  assert  (env:connect(mysqldatabase,mysqluser,mysqlpassword,mysqlhost))   cur  =  assert  (con:execute(string.format("SELECT  n.`id`  FROM  `accesslist`  a  JOIN  `node`  n  ON(n.id=a.node)  WHERE  a.`ip`  =  '%s'",ip)))     row  =  cur:fetch  ({},  "a")   if  cur:numrows()  ==  0  then     return  nil   end   cur:close()   con:close()   env:close()   return  row.idend

Get  node  from  provisioning  database

Page 68: Managing a shared_mysql_farm_phpday2011

Code

function  connect_server()        selectedNode  =  node_get(proxy.connection.client.src.address)

       if  selectedNode  ==  nil  then                return  error_result(string.format("No  info  found  in  the  cluster  for  IP  '%s'",proxy.connection.client.src.address))        end

       proxy.connection.backend_ndx  =  selectedNode    end

Retrieve  and  switch  to  node

Page 69: Managing a shared_mysql_farm_phpday2011

Reality

MySQL  Proxy  is  not  acavely  supported

Page 70: Managing a shared_mysql_farm_phpday2011

Client  proxy

Page 71: Managing a shared_mysql_farm_phpday2011

MySQL  Naave  Driver  Plugins

Page 72: Managing a shared_mysql_farm_phpday2011

MySQL  Naave  Driver  Plugins

✓  Accept  connecaon  using  the  proxy✓Hook  into  the  authenacaaon✓Match  user  to  the  provisioning  DB✓Fetch  node  from  provisioning✓Switch  to  the  right  node✓Doesn’t  work  for  remote  connecaons

➡Effecave  proxying  soluaon

Page 73: Managing a shared_mysql_farm_phpday2011

DNS  &  hostnames

Hostname  per  account

Page 74: Managing a shared_mysql_farm_phpday2011

What  about  PhpMyAdmin?

Page 75: Managing a shared_mysql_farm_phpday2011

What  about  PhpMyAdmin?

✓Use  single  signon  auth  module✓Use  customized  fallback  auth  module✓Detect  linked  database  &  node✓Switch  to  node

Page 76: Managing a shared_mysql_farm_phpday2011

config.inc.php

<?php$cfg['Servers'][1]['auth_type'] = 'httpsoap';$cfg['Servers'][1]['host'] = '1.2.3.4';$cfg['Servers'][1]['connect_type'] = 'tcp';$cfg['Servers'][1]['compress'] = false;$cfg['Servers'][1]['extension'] = 'mysql';$cfg['Servers'][1]['AllowNoPassword'] = false;$cfg['Servers'][2]['auth_type'] = 'httpsoap';$cfg['Servers'][2]['host'] = '1.2.3.5';$cfg['Servers'][2]['connect_type'] = 'tcp';$cfg['Servers'][2]['compress'] = false;$cfg['Servers'][2]['extension'] = 'mysql';$cfg['Servers'][2]['AllowNoPassword'] = false;$cfg['Servers'][3]['extension'] = 'mysql';$cfg['Servers'][3]['auth_type'] = 'signon';$cfg['Servers'][3]['SignonSession'] = 'SSOSession';$cfg['Servers'][3]['SignonURL'] = 'scripts/signon.php';$cfg['Servers'][3]['LogoutURL'] = 'scripts/signon-logout.php';

Page 77: Managing a shared_mysql_farm_phpday2011

scripts/signon.php

<?phpif (isset($_REQUEST['user'])) {    try{        $soap = new SoapClient('http://my.soap-webservice.net/?WSDL');        $user = $soap->user_getByUsername($_REQUEST['user']);        if(!isset($_REQUEST['hash'])){           die("No hash submitted");        }        if(sha1($user->username.$user->password.'azertyuiop') !== $_REQUEST['hash']){            die("Invalid hash");        }    } catch (Exception $e){        die("No such user");    }...

Page 78: Managing a shared_mysql_farm_phpday2011

scripts/signon.php

session_set_cookie_params(0, '/', '', 0);    $session_name = 'SSOSession';    session_name($session_name);    session_start();    $_SESSION['PMA_single_signon_user'] = $user->username;    $_SESSION['PMA_single_signon_password'] = $user->password;    $_SESSION['PMA_single_signon_host'] = $user->node;    $_SESSION['PMA_single_signon_port'] = '3306';    $id = session_id();    session_write_close();    header('Location: ../index.php?server=3');} else {    header('Location: ../index.php?server=1'); exit();}

Page 79: Managing a shared_mysql_farm_phpday2011

scripts/signon-­‐logout.php

<?phpsession_set_cookie_params(0, '/', '', 0);$session_name = 'SSOSession';session_name($session_name);session_start();session_destroy();header('Location: ../index.php?server=1');

Page 80: Managing a shared_mysql_farm_phpday2011

Customized  fallback  auth  module

✓Copy  of  ./libraries/auth/h4p.auth.lib.php✓Modify  PMA_auth_set_user()  funcaon✓Implement  detecaon  logic✓Communicates  with  provisioning  service✓Retrieves  database  &  node✓Switches  to  node

Page 81: Managing a shared_mysql_farm_phpday2011

libraries/auth/hYpsoap.auth.lib.php<?phpfunction PMA_auth_set_user(){    global $cfg, $server;    global $PHP_AUTH_USER, $PHP_AUTH_PW;    try{        $soap = new SoapClient('http://my.soap-webservice.net/?WSDL');        $user = $soap->user_getByUsername($PHP_AUTH_USER);        $cfg['Server']['host'] = $user->node;    } catch (Exception $e){        PMA_auth();        return true;    }...

Page 82: Managing a shared_mysql_farm_phpday2011

libraries/auth/hYpsoap.auth.lib.phpif ($cfg['Server']['user'] != $PHP_AUTH_USER) { $servers_cnt = count($cfg['Servers']);  for ($i = 1; $i <= $servers_cnt; $i++) {   if (isset($cfg['Servers'][$i])    && ($cfg['Servers'][$i]['host'] == $cfg['Server']['host'] && $cfg['Servers'][$i]['user'] == $PHP_AUTH_USER)) {     $server = $i;                $cfg['Server'] = $cfg['Servers'][$i];                break;            }        }    }    $cfg['Server']['user']     = $PHP_AUTH_USER;    $cfg['Server']['password'] = $PHP_AUTH_PW;    return true;}

Page 83: Managing a shared_mysql_farm_phpday2011
Page 84: Managing a shared_mysql_farm_phpday2011

Q&A