Successfully reported this slideshow.
Your SlideShare is downloading. ×

Managing a shared mysql farm dpc11

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Loading in …3
×

Check these out next

1 of 114 Ad

More Related Content

Slideshows for you (20)

Viewers also liked (20)

Advertisement

Similar to Managing a shared mysql farm dpc11 (20)

More from Combell NV (20)

Advertisement

Recently uploaded (20)

Managing a shared mysql farm dpc11

  1. 1. Managing  a  shared  MySQL  farm Thijs  Feryn Evangelist +32  (0)9  218  79  06 thijs@combellgroup.com Dutch  PHP  Conference Saturday  May  21st  2011 Amsterdam,  The  Netherlands
  2. 2. About  me I’m  an  evangelist  at  Combell
  3. 3. About  me I’m  a  board  member  at  PHPBenelux
  4. 4. I  live  in  the  wonderful  city  of  Bruges MPBecker  -­‐  Bruges  by  Night  hXp://www.flickr.com/photos/galverson2/3715965933
  5. 5. Follow  me  on  TwiXer:  @ThijsFeryn Give  me  feedback:  hXp://joind.in/3247 Read  my  blog:  hXp://blog.feryn.eu
  6. 6. Managing a shared MySQL farm tekst
  7. 7. Provisioning/authentication/ permissions Managing a shared MySQL farm tekst
  8. 8. Several clients/apps connect to it Managing a shared MySQL farm tekst
  9. 9. Multiple servers Managing a shared MySQL farm tekst
  10. 10. The  farm
  11. 11. Managing  the  farm
  12. 12. Managing  the  farm User Permissions Database
  13. 13. Managing  users ✓Create  user ✓Remove  user ✓Enable/disable  user ✓Reset  password
  14. 14. Managing  databases ✓Create  database ✓Remove  database ✓Enable/disable  database ✓Set  quota
  15. 15. Managing  permissions ✓Grant  permissions ✓Revoke  permissions ✓Enable  wricng ✓Disable  wricng
  16. 16. MySQL  authenccacon  &  privileges
  17. 17. MySQL  privilege  system Global  privileges Database  privileges Table  privileges Field  privileges
  18. 18. MySQL  privilege  system Global  privileges mysql.user Database  privileges mysql.db Table  privileges mysql.tables_priv Field  privileges mysql.columns_priv
  19. 19. General  privileges ✓Select ✓Alter ✓Insert ✓Create  tmp  table ✓Update ✓Lock  tables ✓Delete ✓Create  view ✓Create ✓Show  view ✓Drop ✓Create  roucne ✓Grant ✓Alter  roucne ✓References ✓Execute  priv ✓Index
  20. 20. Server  privileges ✓Reload ✓Max  quescons ✓Shutdown ✓Max  updates ✓Process ✓Max  conneccons ✓File ✓Max  user  conneccons ✓Show_db ✓Super
  21. 21. Which  privileges  to  grant?
  22. 22. Which  privileges  to  grant? ✓Select ✓Alter ✓Insert ✓Create  tmp  table ✓Update ✓Lock  tables ✓Reload ✓Delete ✓Create  view ✓Shutdown ✓Create ✓Show  view ✓Process ✓Drop ✓Create  roucne File ✓ ✓Grant ✓Alter  roucne ✓Show_db ✓References ✓Execute  priv ✓Super ✓Index
  23. 23. Manage  privileges ✓CREATE  USER ✓DROP  USER ✓GRANT ✓RENAME  USER ✓REVOKE ✓SET  PASSWORD
  24. 24. Manage  privileges ✓Manually  in  mysql.user ✓Manually  in  mysql.db ✓Manually  in  mysql.tables_priv ✓Manually  in  mysql.columns_priv
  25. 25. Challenges
  26. 26. Challenges ✓Management  across  mulcple  nodes ✓Aggregacng  data  from  mulcple  nodes ✓Name  clashes ✓Quota  management
  27. 27. Solucons
  28. 28. Solucons ✓Centralized  provisioning  database ✓GeXers  on  the  provisioning  database ✓Node  mapper  for  user/db/privilege   management ✓INFORMATION_SCHEMA  for  quota   management ✓Prefixes  to  avoid  name  clashes
  29. 29. Provisioning  plan
  30. 30. User Database ✓Id ✓Id ✓Prefix ✓Node ✓Username ✓Prefix ✓Password ✓Database ✓Enabled ✓Quota ✓DatabaseId ✓Enabled ✓Write ✓Down ✓CreateDate ✓Overquota ✓UpdateDate ✓CreateDate ✓UpdateDate
  31. 31. User Database ✓Id ✓Id Mulcple ✓Prefix ✓Node servers ✓Username ✓Prefix Database   ✓Password ✓Database on  single   ✓Enabled ✓Quota node ✓DatabaseId ✓Enabled ✓Write ✓Down ✓CreateDate ✓Overquota ✓UpdateDate ✓CreateDate ✓UpdateDate
  32. 32. Mapping  uses  cases  to  SQL
  33. 33. ✓Add  user ✓Delete  user ✓Reset  user  password ✓Enable  user ✓Disable  user ✓Get  user
  34. 34. Add  user INSERT INTO `user` (`prefix`,`username`,`password`,`createdate`) VALUES(‘test’,‘test_user’,‘mypass123’,NOW());
  35. 35. 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’;
  36. 36. Reset  user  password UPDATE `user` SET `password` = ‘newpass123’ WHERE `username` = ‘test_user’; UPDATE `mysql`.`user` SET `Password` = PASSWORD (‘newpass123’) WHERE `User`= ‘test_user’;
  37. 37. Enable  user UPDATE `user` SET `enabled` = '1' WHERE `username` = ‘test_user’; UPDATE `mysql`.`user` SET `Host` = ‘%’ WHERE `User`= ‘test_user’
  38. 38. Disable  user UPDATE `user` SET `enabled` = '0' WHERE `username` = ‘test_user’; UPDATE `mysql`.`user` SET `Host` = ‘localhost’ WHERE `User`= ‘test_user’
  39. 39. Get  user SELECT * FROM `user` WHERE `username` = ‘test_user’;
  40. 40. ✓Add  database ✓Delete  database ✓Set  database  quota ✓Enable  database ✓Disable  database ✓Get  database
  41. 41. Add  database INSERT INTO `database` (`node`,`prefix`,`database`,`quota`,`c reatedate`) VALUES(1,‘test’,‘test_db’, 10,NOW()); CREATE DATABASE test_db1;
  42. 42. Delete  database DELETE FROM `database` WHERE `database` = ‘test_db’;
  43. 43. Delete  database Are   linked  to  this   database SELECT u.username FROM `user` u WHERE u.databaseId = 123 GROUP BY u.username; Find   deletable  users  to   delete  from  MySQL     privileges  system
  44. 44. Delete  database DELETE u.*, db.* FROM `user` u LEFT JOIN `db` db ON(db.`User` = u.`User`) WHERE u.`User` IN('test_user’); Delete these  users  from   MySQL    privileges   system
  45. 45. Delete  database DROP DATABASE test_db;
  46. 46. Set  database  quota UPDATE `database` SET `quota` = 100 WHERE `database` = ‘test_db’;
  47. 47. Enable  database UPDATE `database` SET `enabled` = '1' WHERE `database` = ‘test_db’;
  48. 48. Enable  database SELECT u.username, u.write FROM user u WHERE u.databaseId = 123 Find   user  mappings   to  re-­‐enable
  49. 49. Enable  database INSERT INTO `db` (Host,Db,User,Select_priv,Insert_priv, Update_priv,Delete_priv,Create_priv,Drop_pr iv,Grant_priv,References_priv, Index_priv,Alter_priv,Create_tmp_table_priv ,Lock_tables_priv, Create_view_priv,Show_view_priv,Create_rout ine_priv, Alter_routine_priv,Execute_priv)
  50. 50. Write   Enable  database permissions VALUES (‘%’,‘test_db’,‘test_user’,'Y','Y','Y','Y', 'Y','Y','N','Y','Y','Y' ,'Y','Y','Y','Y','Y','Y','Y'); Read-­‐ only   permissions VALUES (‘%’,‘test_db’,‘test_user’,'Y','N','N','N', 'N','N','N','N','N','N','N','N','N','Y','N' ,'N','Y');
  51. 51. Disable  database UPDATE `database` SET `enabled` = '0' WHERE `database` = ‘test_db’; DELETE FROM `db` WHERE db = 'test_db’;
  52. 52. Get  database SELECT * FROM `database` WHERE `database` = ‘test_db’;
  53. 53. ✓Grant  privilege ✓Revoke  privilege ✓Enable  database  wricng ✓Disable  database  wricng
  54. 54. Write   Grant  privilege permissions UPDATE `user` SET `databaseId`=123, `write`='1' WHERE `username`= ‘test_user’; Read-­‐ only   permissions UPDATE `user` SET `databaseId`=123, `write`='0' WHERE `username`= ‘test_user’;
  55. 55. Grant  privilege Try   adding  user  or   catch  duplicate   user  error INSERT INTO `user`(Host,User,Password) VALUES(‘%’,‘test_user’,PASSWORD (‘password’));
  56. 56. Grant  privilege INSERT INTO `db` (Host,Db,User,Select_priv,Insert_priv, Update_priv,Delete_priv,Create_priv,Drop_pr iv,Grant_priv,References_priv, Index_priv,Alter_priv,Create_tmp_table_priv ,Lock_tables_priv, Create_view_priv,Show_view_priv,Create_rout ine_priv, Alter_routine_priv,Execute_priv)
  57. 57. Write   Grant  privilege permissions VALUES (‘%’,‘test_db’,‘test_user’,'Y','Y','Y','Y', 'Y','Y','N','Y','Y','Y' ,'Y','Y','Y','Y','Y','Y','Y'); Read-­‐ only   permissions VALUES (‘%’,‘test_db’,‘test_user’,'Y','N','N','N', 'N','N','N','N','N','N','N','N','N','Y','N' ,'N','Y');
  58. 58. 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’;
  59. 59. Enable  database  wricng UPDATE `user` SET `write`= '1' WHERE `username` = ‘test_user’;
  60. 60. Enable  database  wricng UPDATE `db` SET `Select_priv` = 'Y',`Insert_priv` = 'Y', UPDATE `user`'Y',`Delete_priv` '1' `Update_priv` = SET `write`= = 'Y', WHERE `username` = ‘test_user’; `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’;
  61. 61. Disable  database  wricng UPDATE `user` SET `write`= '0' WHERE `username` = ‘test_user’;
  62. 62. Disable  database  wricng UPDATE `db` SET `Select_priv` = 'Y',`Insert_priv` = 'N', UPDATE `user`'N',`Delete_priv` '1' `Update_priv` = SET `write`= = 'N', WHERE `username` = ‘test_user’; `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’;
  63. 63. Quota  management
  64. 64. Quota  management ✓Limits  in  provisioning  database ✓Current  usage  stored  in   INFORMATION_SCHEMA ✓Raco  calculated  via  cron  task ✓Write  permissions  disabled  while  over  quota
  65. 65. 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
  66. 66. Quota  management UPDATE `database` SET `overquota` = '1' WHERE `database` = ‘test_db’;
  67. 67. 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’
  68. 68. Quota  management UPDATE `database` SET `overquota` = '0' WHERE `database` = ‘test_db’;
  69. 69. 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’
  70. 70. Goals
  71. 71. Single  point  of  management
  72. 72. Single  point  of  conneccon
  73. 73. Replicacon  &  loadbalancing
  74. 74. Replicacon  &  loadbalancing ✓Minimizes  risk ✓Ensures  stability,  scalability  &  performance ✓Copies  databases  across  nodes ✓Doesn’t  parccon/shard  databases ✓Will  require  mulcple  independent  clusters
  75. 75. Proxying  strategies
  76. 76. Server  proxy
  77. 77. Server  proxy MySQL  Proxy  is  a  simple  program  that   sits  between  your  client  and  MySQL   server(s)  that  can  monitor,  analyze  or   transform  their  communicacon.
  78. 78. MySQL  Proxy  features ✓  Load  balancing ✓  Failover ✓  Query  analysis ✓  Query  filtering  and  modificacon
  79. 79. Installacon APT-­‐GET  INSTALL ✓mysql-­‐proxy ✓lua5.1 ✓liblua5.1-­‐0-­‐dev ✓liblua5.1-­‐sql-­‐mysql-­‐2 ✓liblua5.1-­‐memcached0 ✓liblua5.1-­‐md5-­‐0
  80. 80. Startup /usr/bin/mysql-­‐proxy   -­‐-­‐proxy-­‐lua-­‐script=/var/www/mysqlproxy.dev/   proxy.lua  -­‐-­‐proxy-­‐address=:3307     -­‐-­‐proxy-­‐backend-­‐addresses=172.16.26.133:3306   -­‐-­‐proxy-­‐backend-­‐addresses=172.16.26.134:3306   -­‐-­‐lua-­‐path=/usr/share/lua/5.1/?.lua   -­‐-­‐lua-­‐cpath=/usr/lib/lua/5.1/?.so Custom   LUA  library /etc/default/mysql-­‐proxy
  81. 81. Hooks ✓connect_server ✓read_handshake ✓read_auth ✓read_auth_result ✓read_query ✓read_query_result ✓disconnect_client
  82. 82. Goal
  83. 83. Goal ✓  Accept  conneccon  using  the  proxy ✓Hook  into  the  authenccacon ✓Match  user  to  the  provisioning  DB ✓Fetch  node  from  provisioning ✓Switch  to  the  right  node ➡Effeccve  proxying  solucon
  84. 84. Reality
  85. 85. Reality ✓  Accept  conneccon  using  the  proxy ✓Hook  into  the  authenccacon ✓Match  user  to  the  provisioning  DB ✓Fetch  node  from  provisioning ✓Switch  to  the  right  node ➡Effeccve  proxying  solucon
  86. 86. Reality Conneccon  switching  only  happens  in  the   connect_server  hook Auth  info  is  only  available  starcng  from   the  read_auth  hook
  87. 87. Bridge  the  gap
  88. 88. Bridge  the  gap Redirect  to  node  based  on  client  IP
  89. 89. Let’s  see  some  code  !
  90. 90. Code require('luarocks.require') require('md5') require('Memcached') require('luasql.mysql') local  memcache  =  Memcached.Connect() -­‐-­‐-­‐  config local  mysqlhost  =  "localhost" local  mysqluser  =  "myUser" local  mysqlpassword  =  "MyPwDsesd" local  mysqldatabase  =  "test" -­‐-­‐  debug local  debug  =  true Dependencies  &  config
  91. 91. Code function  error_result  (msg)   proxy.response  =  {     type  =  proxy.MYSQLD_PACKET_ERR,     errmsg  =  msg,     errcode  =  7777,     sqlstate  =  'X7777',   }   return  proxy.PROXY_SEND_RESULT end Custom  MySQL  errors
  92. 92. function  node_get(ip)   local  node  =  memcache:get(md5.sumhexa(ip)) Code     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
  93. 93. function  sql_get(ip)     env  =  assert  (luasql.mysql()) Code   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.id end Get  node  from  provisioning  database
  94. 94. 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
  95. 95. Reality MySQL  Proxy  is  not  accvely  supported
  96. 96. Client  proxy
  97. 97. MySQL  Nacve  Driver
  98. 98. MySQL  Nacve  Driver ✓Replacement  for  libmysql ✓Full  client  protocol  as  a  PHP  extension ✓Official  since  PHP  5.3.0 ✓No  API ✓Mysql,  Mysqli  &  PDO  use  it ✓Supports  plugins
  99. 99. MySQL  Nacve  Driver Read   these  blog  posts hXp://blog.ulf-­‐wendel.de/?p=284 hXp://schlueters.de/blog/archives/146-­‐mysqlnd-­‐plugins-­‐ for-­‐PHP-­‐in-­‐praccce.html
  100. 100. MySQL  Nacve  Driver ✓  Accept  conneccon  using  the  proxy ✓Hook  into  the  authenccacon ✓Match  user  to  the  provisioning  DB ✓Fetch  node  from  provisioning ✓Switch  to  the  right  node ✓Doesn’t  work  for  remote  conneccons ➡Effeccve  proxying  solucon
  101. 101. DNS  &  hostnames Hostname  per  account
  102. 102. What  about  PhpMyAdmin?
  103. 103. What  about  PhpMyAdmin? ✓Use  single  signon  auth  module ✓Use  customized  fallback  auth  module ✓Detect  linked  database  &  node ✓Switch  to  node
  104. 104. 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.4'; $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';
  105. 105. scripts/signon.php <?php if (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");     } ...
  106. 106. 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 {         exit();     header('Location: ../index.php?server=1'); }
  107. 107. scripts/signon-­‐logout.php <?php session_set_cookie_params(0, '/', '', 0); $session_name = 'SSOSession'; session_name($session_name); session_start(); session_destroy(); header('Location: ../index.php?server=1');
  108. 108. Customized  fallback  auth  module ✓Copy  of  ./libraries/auth/h>p.auth.lib.php ✓Modify  PMA_auth_set_user()  funccon ✓Implement  deteccon  logic ✓Communicates  with  provisioning  service ✓Retrieves  database  &  node ✓Switches  to  node
  109. 109. libraries/auth/hXpsoap.auth.lib.php <?php function 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;     } ...
  110. 110. libraries/auth/hXpsoap.auth.lib.php if ($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; }
  111. 111. Q&A

×