Managing a shared mysql farm dpc11

2,403 views

Published on

Slides for my #dpc11 talk.

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,403
On SlideShare
0
From Embeds
0
Number of Embeds
15
Actions
Shares
0
Downloads
60
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

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:  @ThijsFerynGive  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/permissionsManaging a shared MySQL farm tekst
  8. 8. Several clients/apps connect to itManaging a shared MySQL farm tekst
  9. 9. Multiple serversManaging 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.userDatabase  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  userINSERT INTO `user`(`prefix`,`username`,`password`,`createdate`)VALUES(‘test’,‘test_user’,‘mypass123’,NOW());
  35. 35. Delete  userDELETE FROM `user`WHERE username=‘test_user’;DELETE u.*, db.* FROM `mysql`.`user` uLEFT JOIN `mysql`.`db` dbON(db.`User` = u.`User`)WHERE u.`User` = ‘test_user’;
  36. 36. Reset  user  passwordUPDATE `user`SET `password` = ‘newpass123’WHERE `username` = ‘test_user’;UPDATE `mysql`.`user`SET `Password` = PASSWORD(‘newpass123’)WHERE `User`= ‘test_user’;
  37. 37. Enable  userUPDATE `user`SET `enabled` = 1WHERE `username` = ‘test_user’;UPDATE `mysql`.`user`SET `Host` = ‘%’WHERE `User`= ‘test_user’
  38. 38. Disable  userUPDATE `user`SET `enabled` = 0WHERE `username` = ‘test_user’;UPDATE `mysql`.`user`SET `Host` = ‘localhost’WHERE `User`= ‘test_user’
  39. 39. Get  userSELECT * FROM `user`WHERE `username` = ‘test_user’;
  40. 40. ✓Add  database✓Delete  database✓Set  database  quota✓Enable  database✓Disable  database✓Get  database
  41. 41. Add  databaseINSERT INTO `database`(`node`,`prefix`,`database`,`quota`,`createdate`) VALUES(1,‘test’,‘test_db’,10,NOW());CREATE DATABASE test_db1;
  42. 42. Delete  databaseDELETE FROM `database`WHERE `database` = ‘test_db’;
  43. 43. Delete  database Are   linked  to  this   databaseSELECT u.usernameFROM `user` uWHERE u.databaseId = 123GROUP BY u.username; Find   deletable  users  to   delete  from  MySQL     privileges  system
  44. 44. Delete  databaseDELETE u.*, db.*FROM `user` uLEFT JOIN `db` dbON(db.`User` = u.`User`)WHERE u.`User` IN(test_user’); Delete these  users  from   MySQL    privileges   system
  45. 45. Delete  databaseDROP DATABASE test_db;
  46. 46. Set  database  quotaUPDATE `database`SET `quota` = 100WHERE `database` = ‘test_db’;
  47. 47. Enable  databaseUPDATE `database` SET`enabled` = 1WHERE `database` = ‘test_db’;
  48. 48. Enable  databaseSELECT u.username, u.writeFROM user uWHERE u.databaseId = 123 Find   user  mappings   to  re-­‐enable
  49. 49. Enable  databaseINSERT 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)
  50. 50. Write  Enable  database permissionsVALUES(‘%’,‘test_db’,‘test_user’,Y,Y,Y,Y,Y,Y,N,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y); Read-­‐ only   permissionsVALUES(‘%’,‘test_db’,‘test_user’,Y,N,N,N,N,N,N,N,N,N,N,N,N,Y,N,N,Y);
  51. 51. Disable  databaseUPDATE `database` SET`enabled` = 0WHERE `database` = ‘test_db’;DELETE FROM `db`WHERE db = test_db’;
  52. 52. Get  databaseSELECT * FROM `database`WHERE `database` = ‘test_db’;
  53. 53. ✓Grant  privilege✓Revoke  privilege✓Enable  database  wricng✓Disable  database  wricng
  54. 54. Write  Grant  privilege permissionsUPDATE `user`SET `databaseId`=123, `write`=1WHERE `username`= ‘test_user’; Read-­‐ only   permissionsUPDATE `user`SET `databaseId`=123, `write`=0WHERE `username`= ‘test_user’;
  55. 55. Grant  privilege Try   adding  user  or   catch  duplicate   user  errorINSERT INTO `user`(Host,User,Password)VALUES(‘%’,‘test_user’,PASSWORD(‘password’));
  56. 56. Grant  privilegeINSERT 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)
  57. 57. Write  Grant  privilege permissionsVALUES(‘%’,‘test_db’,‘test_user’,Y,Y,Y,Y,Y,Y,N,Y,Y,Y,Y,Y,Y,Y,Y,Y,Y); Read-­‐ only   permissionsVALUES(‘%’,‘test_db’,‘test_user’,Y,N,N,N,N,N,N,N,N,N,N,N,N,Y,N,N,Y);
  58. 58. Revoke  privilegeUPDATE `user`SET `databaseId`= NULL, `write`= NULLWHERE `user`= ‘test_user’;DELETE u.*, db.* FROM `user` u LEFT JOIN`db` db ON(db.`User` = u.`User`) WHEREu.`User` = ‘test_user’;
  59. 59. Enable  database  wricngUPDATE `user` SET `write`= 1WHERE `username` = ‘test_user’;
  60. 60. Enable  database  wricngUPDATE `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` = YWHERE `db`= ‘test_db’ AND `user` = ‘test_user’;
  61. 61. Disable  database  wricngUPDATE `user` SET `write`= 0WHERE `username` = ‘test_user’;
  62. 62. Disable  database  wricngUPDATE `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` = YWHERE `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  managementSELECT `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  managementUPDATE `database`SET `overquota` = 1WHERE `database` = ‘test_db’;
  67. 67. Quota  managementUPDATE `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  managementUPDATE `database`SET `overquota` = 0WHERE `database` = ‘test_db’;
  69. 69. Quota  managementUPDATE `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  proxyMySQL  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. RealityConneccon  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. 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
  91. 91. Codefunction  error_result  (msg)   proxy.response  =  {     type  =  proxy.MYSQLD_PACKET_ERR,     errmsg  =  msg,     errcode  =  7777,     sqlstate  =  X7777,   }   return  proxy.PROXY_SEND_RESULTend 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  nodeend 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.idend Get  node  from  provisioning  database
  94. 94. Codefunction  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<?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");    }...
  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<?phpsession_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<?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;    }...
  110. 110. libraries/auth/hXpsoap.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;}
  111. 111. Q&A

×