# sed -i 's/=enforcing/=disabled/g' /etc/sysconfig/selinux # sed -i 's/=enforcing/=disabled/g' /etc/selinux/config # sed -i 's/#PermitRootLogin/PermitRootLogin/g' /etc/ssh/sshd_config # /sbin/chkconfig iptables off # /sbin/chkconfig ip6tables off # /sbin/chkconfig sshd on # reboot
uri ldap://pdc.lab.com/ binddn cn=ldapbind,cn=Users,dc=lab,dc=com bindpw secretone scope sub base cn=users,dc=lab,dc=com scope group sub scope hosts sub pagesize 1000 referrals off
#%PAM-1.0 # This file is auto-generated. # User changes will be destroyed the next time authconfig is run. auth required pam_env.so auth sufficient pam_unix.so nullok try_first_pass auth requisite pam_succeed_if.so uid >= 500 quiet auth sufficient pam_krb5.so use_first_pass auth sufficient pam_ldap.so use_first_pass auth required pam_deny.so
Interact with customers to learn and document requirements that are then used to produce business requirements documents.
Write technical requirements from a critical phase.
Interact with designers to understand software limitations.
Help programmers during system development, ex: provide use cases, flowcharts or even Database design.
Perform system testing.
Deploy the completed system.
Document requirements or contribute to user manuals.
Whenever a development process is conducted, the system analyst is responsible for designing components and providing that information to the developer.
安裝完成後, 關閉 ip6tables, iptables, postfix
# /sbin/service ip6tables off
# /sbin/service iptables off
# /sbin/service postfix off
同時編輯 /etc/selinux/config 把 selinux關掉, 把enforcing 改成 disabled
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing – SELinux security policy is enforced.
# permissive – SELinux prints warnings instead of enforcing.
# disabled – No SELinux policy is loaded.
SELINUX=disabled
# SELINUXTYPE= can take one of these two values:
# targeted – Targeted processes are protected,
# mls – Multi Level Security protection.
SELINUXTYPE=targeted
都設定好之後, 先到 Management Node上啟動ndb_mgmd
# ndb_mgmd -f /var/lib/mysql-cluster/config.ini –initial
MySQL Cluster Management Server mysql-5.1.51 ndb-7.2.0-beta
2011-07-25 23:20:02 [MgmtSrvr] INFO — The default config directory ‘/usr/mysql-cluster’ does not exist. Trying to create it…
2011-07-25 23:20:02 [MgmtSrvr] INFO — Sucessfully created config directory
接著到Data Node啟動ndbd (兩台都要)
# ndbd
2011-07-25 23:21:13 [ndbd] INFO — Angel connected to ‘192.168.0.2:1186’
2011-07-25 23:21:13 [ndbd] INFO — Angel allocated nodeid: 3
最後啟動SQL Node (一樣兩台都要)
# /sbin/service mysql start
Starting MySQL. SUCCESS!
接著就可以到SQL Node上用MySQL Client Command Line去連線並且開始執行SQL指令
# mysql -u root
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 3
Server version: 5.1.51-ndb-7.2.0-devmilestone-cluster-gpl MySQL Cluster Server (GPL)
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license
Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.
mysql>
看到上頭版本是 5.1.51-ndb-7.2.0-devmilestone-cluster-gpl MySQL Cluster Server (GPL)就大功告成啦, 可以開始試著執行指令並看看有沒有自動被同步到另外一台SQL Node上.
要關閉MySQL Cluster時請反過來執行, 先到兩台SQL Node上關掉mysqld
# /sbin/service mysql stop
再到Management Node關閉
# ndb_mgm -e shutdown
這個指令會同時關掉 Data Node 的 ndbd 及Management Node上的ndb_mgmd
Update on 2011/07/17
如果碰到
2011-07-17 02:56:25: (critical) proxy-plugin.c:263: read_query_result() in /etc/mysql-proxy/proxy.lua tries to modify the resultset, but hasn’t asked to buffer it in proxy.query:append(…, { resultset_is_needed = true }). We ignore the change to the result-set.
接著設定成服務, 建立一個檔案/etc/init.d/mysql-proxy, 內容如下
#!/bin/sh
#
# mysql-proxy This script starts and stops the mysql-proxy daemon
#
# chkconfig: – 78 30
# processname: mysql-proxy
# description: mysql-proxy is a proxy daemon to mysql
# Source function library.
. /etc/rc.d/init.d/functions
設定/etc/mysql-proxy/admin.lua, 內容如下
–[[ $%BEGINLICENSE%$
Copyright (c) 2008, 2009, Oracle and/or its affiliates. All rights reserved.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; version 2 of the
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA
$%ENDLICENSE%$ –]]
function set_error(errmsg)
proxy.response = {
type = proxy.MYSQLD_PACKET_ERR,
errmsg = errmsg or “error”
}
end
function read_query(packet)
if packet:byte() ~= proxy.COM_QUERY then
set_error(“[admin] we only handle text-based queries (COM_QUERY)”)
return proxy.PROXY_SEND_RESULT
end
local query = packet:sub(2)
local rows = { }
local fields = { }
if query:lower() == “select * from backends” then
fields = {
{ name = “backend_ndx”,
type = proxy.MYSQL_TYPE_LONG },
{ name = “address”,
type = proxy.MYSQL_TYPE_STRING },
{ name = “state”,
type = proxy.MYSQL_TYPE_STRING },
{ name = “type”,
type = proxy.MYSQL_TYPE_STRING },
{ name = “uuid”,
type = proxy.MYSQL_TYPE_STRING },
{ name = “connected_clients”,
type = proxy.MYSQL_TYPE_LONG },
}
for i = 1, #proxy.global.backends do
local states = {
“unknown”,
“up”,
“down”
}
local types = {
“unknown”,
“rw”,
“ro”
}
local b = proxy.global.backends[i]
rows[#rows + 1] = {
i,
b.dst.name, — configured backend address
states[b.state + 1], — the C-id is pushed down starting at 0
types[b.type + 1], — the C-id is pushed down starting at 0
b.uuid, — the MySQL Server’s UUID if it is managed
b.connected_clients — currently connected clients
}
end
elseif query:lower() == “select * from help” then
fields = {
{ name = “command”,
type = proxy.MYSQL_TYPE_STRING },
{ name = “description”,
type = proxy.MYSQL_TYPE_STRING },
}
rows[#rows + 1] = { “SELECT * FROM help”, “shows this help” }
rows[#rows + 1] = { “SELECT * FROM backends”, “lists the backends and their state” }
else
set_error(“use ‘SELECT * FROM help’ to see the supported commands”)
return proxy.PROXY_SEND_RESULT
end
proxy.response = {
type = proxy.MYSQLD_PACKET_OK,
resultset = {
fields = fields,
rows = rows
}
}
return proxy.PROXY_SEND_RESULT
end
設定/etc/mysql-proxy/proxy.lua, 內容如下
–[[ $%BEGINLICENSE%$
Copyright (C) 2007-2008 MySQL AB, 2008 Sun Microsystems, Inc
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
$%ENDLICENSE%$ –]]
—
— a flexible statement based load balancer with connection pooling
—
— * build a connection pool of min_idle_connections for each backend and maintain
— its size
— *
—
—
local commands = require(“proxy.commands”)
local tokenizer = require(“proxy.tokenizer”)
local lb = require(“proxy.balance”)
local auto_config = require(“proxy.auto-config”)
— config
—
— connection pool
if not proxy.global.config.rwsplit then
proxy.global.config.rwsplit = {
min_idle_connections = 4,
max_idle_connections = 8,
is_debug = false
}
end
—
— read/write splitting sends all non-transactional SELECTs to the slaves
—
— is_in_transaction tracks the state of the transactions
local is_in_transaction = false
— if this was a SELECT SQL_CALC_FOUND_ROWS … stay on the same connections
local is_in_select_calc_found_rows = false
—
— get a connection to a backend
—
— as long as we don’t have enough connections in the pool, create new connections
—
function connect_server()
local is_debug = proxy.global.config.rwsplit.is_debug
— make sure that we connect to each backend at least ones to
— keep the connections to the servers alive
—
— on read_query we can switch the backends again to another backend
if is_debug then
print()
print(“[connect_server] ” .. proxy.connection.client.address)
end
local rw_ndx = 0
— init all backends
for i = 1, #proxy.global.backends do
local s = proxy.global.backends[i]
local pool = s.pool — we don’t have a username yet, try to find a connections which is idling
local cur_idle = pool.users[“”].cur_idle_connections
if is_debug then
print(” [“.. i ..”].connected_clients = ” .. s.connected_clients)
print(” [“.. i ..”].pool.cur_idle = ” .. cur_idle)
print(” [“.. i ..”].pool.max_idle = ” .. pool.max_idle_connections)
print(” [“.. i ..”].pool.min_idle = ” .. pool.min_idle_connections)
print(” [“.. i ..”].type = ” .. s.type)
print(” [“.. i ..”].state = ” .. s.state)
end
— prefer connections to the master
if s.type == proxy.BACKEND_TYPE_RW and
s.state ~= proxy.BACKEND_STATE_DOWN and
cur_idle < pool.min_idle_connections then
proxy.connection.backend_ndx = i
break
elseif s.type == proxy.BACKEND_TYPE_RO and
s.state ~= proxy.BACKEND_STATE_DOWN and
cur_idle < pool.min_idle_connections then
proxy.connection.backend_ndx = i
break
elseif s.type == proxy.BACKEND_TYPE_RW and
s.state ~= proxy.BACKEND_STATE_DOWN and
rw_ndx == 0 then
rw_ndx = i
end
end
if proxy.connection.backend_ndx == 0 then
if is_debug then
print(" [" .. rw_ndx .. "] taking master as default")
end
proxy.connection.backend_ndx = rw_ndx
end
-- pick a random backend
--
-- we someone have to skip DOWN backends
-- ok, did we got a backend ?
if proxy.connection.server then
if is_debug then
print(" using pooled connection from: " .. proxy.connection.backend_ndx)
end
-- stay with it
return proxy.PROXY_IGNORE_RESULT
end
if is_debug then
print(" [" .. proxy.connection.backend_ndx .. "] idle-conns below min-idle")
end
-- open a new connection
end
---
-- put the successfully authed connection into the connection pool
--
-- @param auth the context information for the auth
--
-- auth.packet is the packet
function read_auth_result( auth )
if is_debug then
print("[read_auth_result] " .. proxy.connection.client.address)
end
if auth.packet:byte() == proxy.MYSQLD_PACKET_OK then
-- auth was fine, disconnect from the server
proxy.connection.backend_ndx = 0
elseif auth.packet:byte() == proxy.MYSQLD_PACKET_EOF then
-- we received either a
--
-- * MYSQLD_PACKET_ERR and the auth failed or
-- * MYSQLD_PACKET_EOF which means a OLD PASSWORD (4.0) was sent
print("(read_auth_result) ... not ok yet");
elseif auth.packet:byte() == proxy.MYSQLD_PACKET_ERR then
-- auth failed
end
end
---
-- read/write splitting
function read_query( packet )
local is_debug = proxy.global.config.rwsplit.is_debug
local cmd = commands.parse(packet)
local c = proxy.connection.client
local r = auto_config.handle(cmd)
if r then return r end
local tokens
local norm_query
-- looks like we have to forward this statement to a backend
if is_debug then
print("[read_query] " .. proxy.connection.client.address)
print(" current backend = " .. proxy.connection.backend_ndx)
print(" client default db = " .. c.default_db)
print(" client username = " .. c.username)
if cmd.type == proxy.COM_QUERY then
print(" query = " .. cmd.query)
end
end
if cmd.type == proxy.COM_QUIT then
-- don't send COM_QUIT to the backend. We manage the connection
-- in all aspects.
proxy.response = {
type = proxy.MYSQLD_PACKET_OK,
}
if is_debug then
print(" (QUIT) current backend = " .. proxy.connection.backend_ndx)
end
return proxy.PROXY_SEND_RESULT
end
proxy.queries:append(1, packet)
-- read/write splitting
--
-- send all non-transactional SELECTs to a slave
if not is_in_transaction and
cmd.type == proxy.COM_QUERY then
tokens = tokens or assert(tokenizer.tokenize(cmd.query))
local stmt = tokenizer.first_stmt_token(tokens)
if stmt.token_name == "TK_SQL_SELECT" then
is_in_select_calc_found_rows = false
local is_insert_id = false
for i = 1, #tokens do
local token = tokens[i]
-- SQL_CALC_FOUND_ROWS + FOUND_ROWS() have to be executed
-- on the same connection
-- print("token: " .. token.token_name)
-- print(" val: " .. token.text)
if not is_in_select_calc_found_rows and token.token_name == "TK_SQL_SQL_CALC_FOUND_ROWS" then
is_in_select_calc_found_rows = true
elseif not is_insert_id and token.token_name == "TK_LITERAL" then
local utext = token.text:upper()
if utext == "LAST_INSERT_ID" or
utext == "@@INSERT_ID" then
is_insert_id = true
end
end
-- we found the two special token, we can't find more
if is_insert_id and is_in_select_calc_found_rows then
break
end
end
-- if we ask for the last-insert-id we have to ask it on the original
-- connection
if not is_insert_id then
local backend_ndx = lb.idle_ro()
if backend_ndx > 0 then
proxy.connection.backend_ndx = backend_ndx
end
else
print(” found a SELECT LAST_INSERT_ID(), staying on the same backend”)
end
end
end
— no backend selected yet, pick a master
if proxy.connection.backend_ndx == 0 then
— we don’t have a backend right now
—
— let’s pick a master as a good default
—
proxy.connection.backend_ndx = lb.idle_failsafe_rw()
end
— by now we should have a backend
—
— in case the master is down, we have to close the client connections
— otherwise we can go on
if proxy.connection.backend_ndx == 0 then
return proxy.PROXY_SEND_QUERY
end
local s = proxy.connection.server
— if client and server db don’t match, adjust the server-side
—
— skip it if we send a INIT_DB anyway
if cmd.type ~= proxy.COM_INIT_DB and
c.default_db and c.default_db ~= s.default_db then
print(” server default db: ” .. s.default_db)
print(” client default db: ” .. c.default_db)
print(” syncronizing”)
proxy.queries:prepend(2, string.char(proxy.COM_INIT_DB) .. c.default_db)
end
— send to master
if is_debug then
if proxy.connection.backend_ndx > 0 then
local b = proxy.global.backends[proxy.connection.backend_ndx]
print(” sending to backend : ” .. b.address);
print(” is_slave : ” .. tostring(b.type == proxy.BACKEND_TYPE_RO));
print(” server default db: ” .. s.default_db)
print(” server username : ” .. s.username)
end
print(” in_trans : ” .. tostring(is_in_transaction))
print(” in_calc_found : ” .. tostring(is_in_select_calc_found_rows))
print(” COM_QUERY : ” .. tostring(cmd.type == proxy.COM_QUERY))
end
return proxy.PROXY_SEND_QUERY
end
—
— as long as we are in a transaction keep the connection
— otherwise release it so another client can use it
function read_query_result( inj )
local is_debug = proxy.global.config.rwsplit.is_debug
local res = assert(inj.resultset)
local flags = res.flags
if inj.id ~= 1 then
— ignore the result of the USE
— the DB might not exist on the backend, what do do ?
—
if inj.id == 2 then
— the injected INIT_DB failed as the slave doesn’t have this DB
— or doesn’t have permissions to read from it
if res.query_status == proxy.MYSQLD_PACKET_ERR then
proxy.queries:reset()
proxy.response = {
type = proxy.MYSQLD_PACKET_ERR,
errmsg = “can’t change DB “.. proxy.connection.client.default_db ..
” to on slave ” .. proxy.global.backends[proxy.connection.backend_ndx].address
}
return proxy.PROXY_SEND_RESULT
end
end
return proxy.PROXY_IGNORE_RESULT
end
is_in_transaction = flags.in_trans
local have_last_insert_id = (res.insert_id and (res.insert_id > 0))
if not is_in_transaction and
not is_in_select_calc_found_rows and
not have_last_insert_id then
— release the backend
proxy.connection.backend_ndx = 0
elseif is_debug then
print(“(read_query_result) staying on the same backend”)
print(” in_trans : ” .. tostring(is_in_transaction))
print(” in_calc_found : ” .. tostring(is_in_select_calc_found_rows))
print(” have_insert_id : ” .. tostring(have_last_insert_id))
end
end
—
— close the connections if we have enough connections in the pool
—
— @return nil – close connection
— IGNORE_RESULT – store connection in the pool
function disconnect_client()
local is_debug = proxy.global.config.rwsplit.is_debug
if is_debug then
print(“[disconnect_client] ” .. proxy.connection.client.address)
end
— make sure we are disconnection from the connection
— to move the connection into the pool
proxy.connection.backend_ndx = 0
end
連接上Admin Console
[Proxy] # mysql -u admin -padmin_password -h PROXY_IP -P 4401
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 1
Server version: 5.0.99-agent-admin
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license
Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.
或者是直接用設定在MASTER與SLAVE上相同的mysql使用者帳密透過proxy連接
[Proxy] # mysql -u USERNAME -pPASWORD -h PROXY_IP -P 3306
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 162
Server version: 5.0.77-log Source distribution
Copyright (c) 2000, 2010, Oracle and/or its affiliates. All rights reserved.
This software comes with ABSOLUTELY NO WARRANTY. This is free software,
and you are welcome to modify and redistribute it under the GPL v2 license
Type ‘help;’ or ‘\h’ for help. Type ‘\c’ to clear the current input statement.
設定Replication使用的連線帳號
[Master] # /sbin/service mysqld start
[Master] # mysql -u root
mysql> GRANT REPLICATION SLAVE ON *.* TO repl@SLAVE-IP IDENTIFIED BY ‘repl_password’;
mysql> FLUSH PRIVILEGES;
去libevent網站抓libevent來裝, CentOS附的太舊
# wget http://monkey.org/~provos/libevent-2.0.12-stable.tar.gz
# tar -zxpvf libevent-2.0.12-stable.tar.gz
# cd libevent-2.0.12-stable
# ./configure –prefix=/usr/local
# make all install
去LUA網站抓新的LUA來裝, CentOS附的太舊
# wget http://www.lua.org/ftp/lua-5.1.4.tar.gz
# tar -zxpvf lua-5.1.4.tar.gz
# cd lua-5.1.4
# vi src/Makefile
修改CFLAGS, 在後頭加入 -fPIC
# make linux install
# cp src/lua.pc /usr/local/lib/pkgconfig
終於可以開始build mysql-proxy了, 去 MySQL Proxy網站抓Source, 這裡有Mirror.
# wget http://mysql.cdpa.nsysu.edu.tw/Downloads/MySQL-Proxy/mysql-proxy-0.8.1.tar.gz
# tar -zxpvf mysql-proxy-0.8.1.tar.gz
# cd mysql-proxy-0.8.1
# ./configure –prefix=/usr/local
# make all install