Galera Cluster に Asynchronous replicateion ノードを追加する
こんばんは。
Galera Clusterを構成しているCluster nodeにはMySQLレプリケーションノードが追加できます。
以下はパラメータの設定例ですが log_slave_updates
は必ず有効にしておかないと、
SlaveノードでCHANGE MASTER TOを実行したノードからのトランザクションしかレプリケーションされません。
server_id=<unique> binlog_format=ROW log_bin=binlog log_slave_updates=1 relay_log=relay-bin expire_logs_days=8
さっきかなり痛い目にあった...
んでスレーブを追加する利点ですが(AWS的に)
- 別AZへのバックアップが可能(GaleraのWANモードでもいいよねというのもあるけど...)
- スレーブでクッソ重いクエリを思い思いに実行可能
MySQL/MariaDB 5.6/10.0系以上ならGTID使えるから破損してもレプリ元のノードを気にしなくてよくなるよ。という資料を見たのですが忘れてしまった...
Infrastructure as 脳筋のためのchef recipe tips. HAProxyの例
chef-zeroのC/S wrapper knife-zero使ってますか?
とても便利です。重宝しています。
higanworks/knife-zero · GitHub
個人的にchefの真髄であるsearchが利用できるため、chef-soloと違って関連nodeの情報をattributeに記述すること無くrecipeを記述でき、
管理の幅を広げてくれるツールなので是非使いましょうと思っている次第です(がまだ横展開しきれず)
今回はHAproxyを用いてGalera Cluster MariaDBへの接続を行うための定義を簡単に書くためのtipsを残したいと思います。
EnvironmentとRoleを使い、HAProxyのロードバランス対象となるノードをnode情報から自動判別してHAProxyの設定を生成します。
chef repositoryで利用するEnvironmentとRole
- Environment
chef_environment | 意味 |
---|---|
production | 本番環境 |
- Role
role | 意味 |
---|---|
galera_master | Galera Cluster初期ノード |
galera_member | Galera Clusterメンバーノード |
DBサーバとなるノードにはrole[galera_master]またはrole{galera_member]を割り当ててknife zeroを実行しておきます。(実行しておかないとnode.jsonに対象ノードの情報が集約されない)
recipe
- haproxy/recipes/default.rb
Galera Clusterを構成するノードの情報をsearchで取得してtemplate variablesに渡します
if node.chef_environment.include?("production") cookbook_file "/root/haproxy-1.5.9-2.amzn1.x86_64.rpm" do source "haproxy-1.5.9-2.amzn1.x86_64.rpm" owner "root" group "root" mode 0644 end package "haproxy" do action :install provider Chef::Provider::Package::Rpm source "/root/haproxy-1.5.9-2.amzn1.x86_64.rpm" end template "/etc/haproxy/haproxy.cfg" do source "haproxy.cfg.erb" owner "root" group "root" mode 0644 notifies :restart, "service[haproxy]" variables( :galerahosts => search(:node,"roles:galera* AND chef_environment:#{node.chef_environment}") ) end service "haproxy" do action :nothing end end
- haproxy/template/haproxy.cfg.erb(一部抜粋)
template variablesから渡された情報をeachでぶん回します。
listen haproxy-galera bind *:33306 mode tcp timeout client 28800s timeout server 28800s balance roundrobin option httpchk option allbackups <% @galerahosts.each do |host| %> server <%= host.ipaddress %> <%= host.ipaddress %>:3306 check <% end %>
apply後
このようにHAProxyを導入するサーバにrecipeを適用します(chef-repo毎にbundler使ってるのでbundle execかいてます)
bundle exec knife zero chef_client search "role:app AND chef_environment production" -x ec2-user --sudo --attribute name
実行後は対象ノードに以下のようなhaproxy.cfgが作成されます。
..省略.. listen haproxy-galera bind *:33306 mode tcp timeout client 28800s timeout server 28800s balance roundrobin option httpchk option allbackups server 192.168.1.100 192.168.1.100:3306 check server 192.168.1.101 192.168.1.101:3306 check server 192.168.1.102 192.168.1.102:3306 check
attributeを使用せずroleとchef_environmentの検索でロードバランス対象ノードのIPアドレスを設定することが出来ました。
search最高。
ちなみに自分はknife zeroのコマンドラインを実行するのが面倒なので 簡単なRakefileを作成して、search条件だけ渡してknife zeroを実行しています。
適用するroleやenvironment毎にtaskを作成したらもっと簡単にかつsearchのミス無く適用することができると思います。
require 'rake' desc "run, bundle exec knife <type> list" task :list, [:type] do |t,args| command = %(bundle exec knife #{args[:type]} #{t} ) puts command Bundler.clean_system command end desc "run, bundle exec knife search <hint>" task :search, [:hint] do |t, args| command = %(bundle exec knife #{t} "#{args[:hint]}") puts command Bundler.clean_system command end knife_args = "-x ec2-user --sudo --attribute name" desc "run, bundle exec knife zero chef_client --why-run" task "dry-run", [:hint] do |t,args| command = %(bundle exec berks vendor cookbooks;bundle exec knife zero chef_client "#{args[:hint]}" #{knife_args} --why-run) puts command Bundler.clean_system command end desc "run, bundle exec knife zero chef_client." task :apply, [:hint] do |t,args| command = %(bundle exec berks vendor cookbooks;bundle exec knife zero chef_client "#{args[:hint]}" #{knife_args}) puts command Bundler.clean_system command end
- Rake実行例
why-run
bundle exec rake "dry-run[(roles:galera* AND chef_environment:production)]"
apply
bundle exec rake "apply[(roles:galera* AND chef_environment:production)]"
Rake saikou.
Infrastructure as 脳筋のためのterraform tips
terraform始めました。
使っていてこれはどーすりゃよいのかしらと思っていたことが実装できたのでメモを残します。
(1)AWS Security Groupを用いて内部通信ツーツーの設定を作りたい
Security GroupにAll trafficを許可するInboundルールを書くことがあると思います。
Management Consoleから作成したAll Traffic
を選択してセキュリティグループのIDを登録するだけで良いです。
ではterraformで実装する時はaws_security_group
リソースのingressにselfを指定すると自身のセキュリティグループのIDを登録できますが、
All trafficはどう指定したらよいのかとドキュメント読んでも見当たらなかったのでソースを眺めた所
protocolがstring型で定義されていたぐらいなので、コードの中で指定可能な値を定義しているところはありませんでした。
ということはprotocolで指定した値をそのままAWSのAPIサーバへPOSTしているだけであれば
CloudformationドキュメントのIpProtocol
に指定可能なパラメータは指定できると思い、
下記の様なresource
を記述してterraform apply
を実行すると上手く出来ました。
resource "aws_security_group" "common" { name = "common-sg" description = "common security group" vpc_id = "${aws_vpc.hogehoge.id}" ingress { from_port = 0 to_port = 65535 protocol = "-1" self = true } }
(2) 起動するinstance数を指定したい
同一のロールをもつインスタンスが複数台あるとき(たとえばwwwとか) resource "aws_instance"
を並べるのは苦行以外の何物でもありません
ドキュメントには記載されていませんがcountが指定できます。
resource "aws_instance" "hogehoge" { ami = "ami-1234" instance_type = "t2.micro" count = 10 tags { Name = "hogehoge" } }
(3) terraform apply実行時にThrottlingエラー
ひたすらAPIサーバへPOSTしている模様なのでresourceが多いと頻発する。 terraform applyをリトライしたらよい
Error applying plan: 1 error(s) occurred: * Error retrieving ELB: Throttling: Rate exceeded Terraform does not automatically rollback in the face of errors. Instead, your Terraform state file has been partially updated with any resources that successfully completed. Please address the error above and apply again to incrementally change your infrastructure.
HAProxy経由でGalera Cluster MariaDBに接続する
oreですこんばんは。
アプリケーションサーバからGalera Clusterへの接続をどうやって行うか考えており、HAProxyで実装してみました。
以下動作確認結果です-
環境
- OS
- ミドル
ミドル | バージョン |
---|---|
ロードバランサ | HAProxy 1.5.9 |
データベース | MariaDB-Galera-server-5.5.40-1.el6.x86_64 |
レプリケーションライブラリ | galera-25.3.5-1.rhel6.x86_64 |
- サーバ一覧
サーバ | IP |
---|---|
HAProxy | 10.0.1.50 |
Galera Cluster#1 | 10.0.0.187 |
Galera Cluster#2 | 10.0.0.97 |
Galera Cluster#3 | 10.0.0.246 |
HAProxy 1.5.9のビルド
Amazon Linuxが標準で提供しているHAProxyは1.4.22だったのでHAProxy 1.5.9をrpmbuildしました。
mockを利用してhaproxyのrpmを作成するスクリプトを公開している方がいたので、こちらを参考にrpmbuildでrpmを作成しました。
boogieshafer/haproxy-rpm · GitHub
※このスクリプトをそのまま使えたらよかったのですがmockが上手く動かず断念.
- build-haproxy失敗ログ
[ec2-user@ip-10-0-1-50 haproxy-rpm]$ bash build-haproxy.1.5.x.sh Downloading sources... Building initial source rpm... 書き込み完了: /usr/src/rpm/SRPMS/haproxy-1.5.9-2.src.rpm Using mock to build rpm... INFO: mock.py version 1.1.41 starting... Start: init plugins INFO: selinux disabled Finish: init plugins Start: run INFO: Start(/usr/src/rpm/SRPMS/haproxy-1.5.9-2.src.rpm) Config(amzn-x86_64) Start: lock buildroot Start: clean chroot INFO: chroot (/var/lib/mock/amzn-x86_64) unlocked and deleted Finish: clean chroot Finish: lock buildroot Start: chroot init Start: lock buildroot Mock Version: 1.1.41 INFO: Mock Version: 1.1.41 INFO: calling preinit hooks INFO: enabled root cache INFO: enabled yum cache Start: cleaning yum metadata Finish: cleaning yum metadata INFO: enabled ccache Start: device setup Finish: device setup Start: yum update ERROR: Exception(/usr/src/rpm/SRPMS/haproxy-1.5.9-2.src.rpm) Config(amzn-x86_64) 0 minutes 0 seconds INFO: Results and/or logs in: /var/lib/mock/amzn-x86_64/result ERROR: Command failed: # ['/usr/bin/yum', '--installroot', '/var/lib/mock/amzn-x86_64/root/', 'install', '@buildsys-build'] Configuration file /var/lib/mock/amzn-x86_64/root/etc/yum/pluginconf.d/update-motd.conf not found Unable to find configuration file for plugin update-motd Configuration file /var/lib/mock/amzn-x86_64/root/etc/yum/pluginconf.d/upgrade-helper.conf not found Unable to find configuration file for plugin upgrade-helper Group buildsys-build does not exist. Error: Nothing to do Cleaning up...
HAProxyのインストール
$ cd /usr/src/rpm/RPMS/x86_64 $ sudo yum localinstall haproxy-1.5.9-2.amzn1.x86_64.rpm
HAProxyの設定
とりあえずほぼデフォルトで。
- /etc/haproxy.cfg
global log 127.0.0.1 local0 log 127.0.0.1 local1 notice #log loghost local0 info maxconn 1024 chroot /var/lib/haproxy user haproxy group haproxy daemon defaults log global mode http option httplog option dontlognull retries 3 redispatch maxconn 2000 contimeout 5000 clitimeout 50000 srvtimeout 50000 listen galera_clister 0.0.0.0:3306 mode tcp balance roundrobin option tcpka option mysql-check user haproxy server galera01 10.0.0.187:3306 check weight 1 server galera02 10.0.0.97:3306 check weight 1 server galera03 10.0.0.246:3306 check weight 1
DBにcheck用ユーザの作成
ヘルスチェック用のユーザをデータベース上に作成します。適当なGalera Clusterノードでmysql-checkを実行するユーザを作成します。
GRANT USAGE ON *.* TO haproxy@'%' FLUSH PRIVILEGES;
接続テスト
HAProxyサーバから行ないます。ラウンドロビンで接続できています。
[root@ip-10-0-1-50 haproxy]# mysql -uroot -proot -h127.0.0.1 -e "show variables like 'wsrep_node_name' ;" +-----------------+------------+ | Variable_name | Value | +-----------------+------------+ | wsrep_node_name | 10.0.0.187 | +-----------------+------------+ [root@ip-10-0-1-50 haproxy]# mysql -uroot -proot -h127.0.0.1 -e "show variables like 'wsrep_node_name' ;" +-----------------+-----------+ | Variable_name | Value | +-----------------+-----------+ | wsrep_node_name | 10.0.0.97 | +-----------------+-----------+ [root@ip-10-0-1-50 haproxy]# mysql -uroot -proot -h127.0.0.1 -e "show variables like 'wsrep_node_name' ;" +-----------------+------------+ | Variable_name | Value | +-----------------+------------+ | wsrep_node_name | 10.0.0.246 | +-----------------+------------+
とりあえず負荷分散するだけ!なら簡単でした。
次はノード障害テストやらないと。
その他
Galera Load Balancerというのもあるみたいです。気が向いたら試そうかな...
Galera Cluster MariaDBエラーtips
oreが遭遇したエラーtipsまとめ
(1) Percona Xtrabackupインストールしてないのにwsrep_sst_method=xtrabackup奴wwww
Galera Clusterの検証環境をつくろうと思って、既にある環境からmy.cnfを丸コピしてきたものを使って、
初期ノード起動後、2台目以降のGalera Clusterを起動しようとした時に発生したエラー
- エラー内容
ログファイルより抜粋
141230 21:41:42 [Note] WSREP: Running: 'wsrep_sst_xtrabackup --role 'joiner' --address '10.0.0.97' --auth 'root' --datadir '/var/lib/mysql/' --defaults-file '/etc/my.cnf' --parent '4906'' which: no innobackupex in (/usr/sbin:/sbin:/usr//bin:/sbin:/usr/sbin:/bin:/usr/bin:/usr/bin) WSREP_SST: [ERROR] innobackupex not in path: /usr/sbin:/sbin:/usr//bin:/sbin:/usr/sbin:/bin:/usr/bin:/usr/bin (20141230 21:41:42.953) 141230 21:41:42 [ERROR] WSREP: Failed to read 'ready <addr>' from: wsrep_sst_xtrabackup --role 'joiner' --address '10.0.0.97' --auth 'root' --datadir '/var/lib/mysql/' --defaults-file '/etc/my.cnf' --parent '4906' Read: '(null)' 141230 21:41:42 [ERROR] WSREP: Process completed with error: wsrep_sst_xtrabackup --role 'joiner' --address '10.0.0.97' --auth 'root' --datadir '/var/lib/mysql/' --defaults-file '/etc/my.cnf' --parent '4906': 2 (No such file or directory) 141230 21:41:42 [ERROR] WSREP: Failed to prepare for 'xtrabackup' SST. Unrecoverable. 141230 21:41:42 [ERROR] Aborting
- 原因
wsrep_sst_method=xtrabackup
だったため
wsrep_sst_methodはGalera Cluster起動時にマスタデータベース・サーバから同期する手段を指定するパラメータで、デフォルトがrsyncなのですが既にあった環境にはxtrabackupを指定していました。
つくりたてほやほやぁのサーバにはPercona XtrabackupをインストールしていなかったためGalera Cluster起動時に同期ができず失敗した模様です。
- Percona XtraBackupのインストール
$ yum install http://www.percona.com/downloads/percona-release/redhat/0.1-3/percona-release-0.1-3.noarch.rpm $ yum install xtrabackup
- Galera Clusterの起動
Galera Cluster
$ service mysql start
また wsrep_sst_method=xtrabackup
としたときは wsrep_sst_auth=<認証ユーザ>:<password>
も設定しておくことを忘れずに。
(2) Clusterジョイン後に、参加したノードのシーケンス番号がClusterよりも高いため同期に失敗して起動しない
- ログ抜粋
150108 0:45:22 [ERROR] WSREP: Local state seqno (12) is greater than group seqno (10): states diverged. Aborting to avoid potential data loss. Remove '/storage/data/mysql//grastate.dat' file and restart if you wish to continue. (FATAL) at galera/src/replicator_str.cpp:state_transfer_required():33
- 原因
まだ調べてない..
- 対応方法
起動にfailしたgrastate.datを削除する.
- ログ詳細
150108 0:45:22 [Note] WSREP: STATE EXCHANGE: sent state msg: a097fc2f-96cf-11e4-9c92-e32d0822faad 150108 0:45:22 [Note] WSREP: STATE EXCHANGE: got state msg: a097fc2f-96cf-11e4-9c92-e32d0822faad from 0 (10.0.0.53) 150108 0:45:22 [Note] WSREP: STATE EXCHANGE: got state msg: a097fc2f-96cf-11e4-9c92-e32d0822faad from 2 (10.0.0.31) 150108 0:45:22 [Note] WSREP: STATE EXCHANGE: got state msg: a097fc2f-96cf-11e4-9c92-e32d0822faad from 1 (10.0.0.120) 150108 0:45:22 [Note] WSREP: Quorum results: version = 3, component = PRIMARY, conf_id = 14, members = 2/3 (joined/total), act_id = 10, last_appl. = -1, protocols = 0/5/3 (gcs/repl/appl), group UUID = 2a245897-968f-11e4-bc67-13c84f4a5ebb 150108 0:45:22 [Note] WSREP: Flow-control interval: [28, 28] 150108 0:45:22 [Note] WSREP: Shifting OPEN -> PRIMARY (TO: 10) 150108 0:45:22 [Note] WSREP: Closing send monitor... 150108 0:45:22 [Note] WSREP: Closed send monitor. 150108 0:45:22 [Note] WSREP: gcomm: terminating thread 150108 0:45:22 [Note] WSREP: gcomm: joining thread 150108 0:45:22 [Note] WSREP: gcomm: closing backend 150108 0:45:22 [Note] WSREP: view(view_id(NON_PRIM,213ef9d9-96ce-11e4-b33f-0756a3a1e93e,15) memb { a05cd71b-96cf-11e4-98d8-561067f120d0,0 } joined { } left { } partitioned { 213ef9d9-96ce-11e4-b33f-0756a3a1e93e,0 f6e4b3c1-96cd-11e4-ab41-5f43f8aae952,0 }) 150108 0:45:22 [Note] WSREP: view((empty)) 150108 0:45:22 [Note] WSREP: gcomm: closed 150108 0:45:22 [Note] WSREP: New COMPONENT: primary = no, bootstrap = no, my_idx = 0, memb_num = 1 150108 0:45:22 [Note] WSREP: Flow-control interval: [16, 16] 150108 0:45:22 [Note] WSREP: Received NON-PRIMARY. 150108 0:45:22 [Note] WSREP: Shifting PRIMARY -> OPEN (TO: 10) 150108 0:45:22 [Note] WSREP: Received self-leave message. 150108 0:45:22 [Note] WSREP: Flow-control interval: [0, 0] 150108 0:45:22 [Note] WSREP: Received SELF-LEAVE. Closing connection. 150108 0:45:22 [Note] WSREP: Shifting OPEN -> CLOSED (TO: 10) 150108 0:45:22 [Note] WSREP: RECV thread exiting 0: Success 150108 0:45:22 [Note] WSREP: recv_thread() joined. 150108 0:45:22 [Note] WSREP: Closing replication queue. 150108 0:45:22 [Note] WSREP: Closing slave action queue. ☆☆150108 0:45:22 [ERROR] WSREP: Local state seqno (12) is greater than group seqno (10): states diverged. Aborting to avoid potential data loss. Remove '/storage/data/mysql//grastate.dat' file and restart if you wish to continue. (FATAL) at galera/src/replicator_str.cpp:state_transfer_required():33 150108 0:45:22 [Note] WSREP: applier thread exiting (code:8) 150108 0:45:22 [ERROR] Aborting 150108 0:45:24 [Note] WSREP: Closing send monitor... 150108 0:45:24 [Note] WSREP: Closed send monitor. 150108 0:45:24 [Note] WSREP: Service disconnected. 150108 0:45:24 [Note] WSREP: rollbacker thread exiting 150108 0:45:25 [Note] WSREP: Some threads may fail to exit. 150108 0:45:25 [Note] /usr/sbin/mysqld: Shutdown complete Error in my_thread_global_end(): 1 threads didn't exit 150108 00:45:30 mysqld_safe mysqld from pid file /storage/data/mysql//ip-10-0-0-120.pid ended 150108 00:46:50 mysqld_safe Starting mysqld daemon with databases from /storage/data/mysql/ 150108 00:46:50 mysqld_safe WSREP: Running position recovery with --log_error='/storage/data/mysql//wsrep_recovery.uw41rR' --pid-file='/storage/data/mysql//ip-10-0-0-120-recover.pid' 150108 00:46:53 mysqld_safe WSREP: Recovered position 2a245897-968f-11e4-bc67-13c84f4a5ebb:12
(3) wsrep_sst_method=xtrabackup指定していて2台目以降のGalera Clusterを起動すると[[Warning] WSREP: Gap in state sequence. Need state transfer.]がログに出力されてClusterが起動しない
現状出くわしたケースとしてgalera cluster -> replication slaveを構成しているDBで replication slaveで実行したinnobackupexのbackup fileをリストアすると2台目以降のmysql起動時に発生する。
力技だけど2台目以降のmy.cnfに wsrep_sst_method=rsync
を指定して起動する。
起動完了後はwsrep_sst_method=xtrabackupに戻してmysqlを再起動したらよい。
なーんでこれがでるかなあ。わかってない件
EBSスナップショットでMySQL用データディスクをバックアップ
突貫版を直したもの。
EBSスナップショットでMySQLデータディスクをバックアップします。
スレーブDBで実行することを想定してます。
jq必要です(入ってないならインストールするを追加しておこう..あとで間違いなく忘れる)
service mysqld stop
してるけどFLUSH TABLES WITH READ LOCK
でもよいかも。
毎日cronやjenkinsおじさんで動くものと想定して、取得後に前日分のSnapshot/Volumeを削除してます。 世代残すなら2 days agoのほうがいいね。
某コピペみたいに小さな音がすべてに響くみたいな。
だめだけど。いいんです。
ELBでSSL TerminationしてるとPHPMyAdminが辛かった件
ドはまりした。
助かりましたほんと..