アーカイブ「2019年03月」

githubが開発しているOrchestoratorについて、

以前の記事で、MariaDBに対応していない?と記載してしまったが、

実際に試してみたところ、MariaDBのGTIDレプリケーションにも対応していたので、

その備忘録を記載します。


なお、検証したバージョンは以下となります。

  • CentOS7.5
  • MariaDB10.3.11
  • Orchestorator3.0.13

1.MariaDBのセットアップ

master、slave×2の準同期レプリケーションを構築します。

構成は以下の通り。

----------------------------------------
master
IP:192.168.35.11
----------------------------------------
     |                       |
     |(semi sync)            |(semi sync)
     |                       |
    --------------------    --------------------
    slave                   slave
    IP:192.168.35.12        IP:192.168.35.13
    --------------------    --------------------

設定は以下の通り。

# マルチソースをやる際に必要かもしれないので、レプリケーションクラスターで共通の数字
gtid_domain_id=11

# レプリケーションクラスター間で一意の数字(IPアドレスの下2桁など)
server_id={{ server_id }}

# 名前解決ができない環境向けに、自身のノードのIPアドレスを設定しておく
report_host={{ ip_addr }}

# とりあえず0だが、動的に変更する
read_only=0

# 安定のrow
binlog_format=row

# 毎コミットごとにバイナリログに書き出す
sync_binlog=1

# フェイルオーバー時にslaveが昇格しても大丈夫なように
log_slave_updates=1

# レプリケーション安全設定
relay_log_recovery=1

# 大きなリレーログへの対応
slave_max_allowed_packet=1G

# タイムアウト長めに
slave_net_timeout=3600

# slaveのSQLスレッドの並列化
slave_parallel_threads={{ cpu_num }}

# 厳格モード
gtid_strict_mode=1

# 以下、準同期設定(MariaDB10.3では、plugin_load_addは不要)
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1500
rpl_semi_sync_slave_enabled=1
rpl_semi_sync_master_wait_point=AFTER_SYNC

そして、レプリケーションを開始しておく。



2.Orchestoratorの設定

IPアドレス192.168.35.10のOrchestorator専用ノードを立てる。

orchestorator.conf.jsonの内容は以下の通り。

{
  "Debug": false,
  "EnableSyslog": false,
  "ListenAddress": ":3000",
  "MySQLTopologyUser": "adminuser",
  "MySQLTopologyPassword": "adminpassword",
  "BackendDB": "sqlite",
  "SQLite3DataFile": "/usr/local/orchestrator/orchestrator.sqlite3",
  "DiscoverByShowSlaveHosts": true,
  "InstancePollSeconds": 5,
  "UnseenInstanceForgetHours": 240,
  "SnapshotTopologiesIntervalHours": 0,
  "InstanceBulkOperationsWaitTimeoutSeconds": 10,
  "HostnameResolveMethod": "default",
  "MySQLHostnameResolveMethod": "@@report_host",
  "SkipBinlogServerUnresolveCheck": true,
  "ExpiryHostnameResolvesMinutes": 60,
  "RejectHostnameResolvePattern": "",
  "ReasonableReplicationLagSeconds": 10,
  "ProblemIgnoreHostnameFilters": [],
  "VerifyReplicationFilters": false,
  "ReasonableMaintenanceReplicationLagSeconds": 20,
  "CandidateInstanceExpireMinutes": 60,
  "DetectClusterAliasQuery": "SELECT @@report_host",
  "PromotionIgnoreHostnameFilters": [],
  "DetectSemiSyncEnforcedQuery": "",
  "URLPrefix": "",
  "StatusEndpoint": "/api/status",
  "StatusSimpleHealth": true,
  "StatusOUVerify": false,
  "AgentPollMinutes": 60,
  "UnseenAgentForgetHours": 6,
  "StaleSeedFailMinutes": 60,
  "SeedAcceptableBytesDiff": 8192,
  "PseudoGTIDPattern": "",
  "PseudoGTIDPatternIsFixedSubstring": false,
  "PseudoGTIDMonotonicHint": "asc:",
  "DetectPseudoGTIDQuery": "",
  "BinlogEventsChunkSize": 10000,
  "SkipBinlogEventsContaining": [],
  "ReduceReplicationAnalysisCount": true,
  "FailureDetectionPeriodBlockMinutes": 60,
  "RecoveryPeriodBlockSeconds": 3600,
  "RecoveryIgnoreHostnameFilters": [],
  "RecoverMasterClusterFilters": [
    "*"
  ],
  "RecoverIntermediateMasterClusterFilters": [
    "*"
  ],
  "OnFailureDetectionProcesses": [],
  "PreFailoverProcesses": [],
  "PostFailoverProcesses": [
    "echo -e 'Failed server is {failedHost}:{failedPort}.\nSuccessed server is {successorHost}:{successorPort}.' | mail -s 'Complete Orchestrator Failover' yourname@sample.com"
  ],
  "PostUnsuccessfulFailoverProcesses": [
    "echo -e 'Failed server is {failedHost}:{failedPort}.' | mail -s 'Failed Orchestrator Failover' yourname@sample.com"
  ],
  "PostMasterFailoverProcesses": [
    "/usr/local/bin/detect_orchestrator_failover.py -u adminuser -p adminpassword -H {successorHost} -P {successorPort}"
  ],
  "PostIntermediateMasterFailoverProcesses": [],
  "CoMasterRecoveryMustPromoteOtherCoMaster": true,
  "DetachLostSlavesAfterMasterFailover": true,
  "ApplyMySQLPromotionAfterMasterFailover": true,
  "MasterFailoverDetachSlaveMasterHost": false,
  "MasterFailoverLostInstancesDowntimeMinutes": 0,
  "PostponeSlaveRecoveryOnLagMinutes": 0
}

※adminuserは、レプリケーションクラスターに対してアクセス可能なsuperユーザとする。


ここでのポイントは、

"MySQLHostnameResolveMethod": "@@report_host"
"DetectClusterAliasQuery": "SELECT @@report_host"

である。

これにより、Orchestoratorはレプリケーションクラスターの状態取得をIPアドレスで行うことができるようになる。

この後、ひとまずOrechstoratorにレプリケーションクラスターのmasterノードを登録する。masterを登録しておけば、slaveも自動的に発見してくれる。

なお、登録方法は、WebGUIやらAPIなどであるが、ここでは割愛。



3.フェイルオーバー時のMariaDBパラメータの動的設定処理の配備

上記の設定の

"PostMasterFailoverProcesses"

の部分の

/usr/local/bin/detect_orchestrator_failover.py

を以下の感じで実装する。

なお、以下の処理は、masterのフェイルオーバー完了時に呼び出される。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import os
import sys
import traceback
import datetime
import re
import argparse
import MySQLdb

parser = argparse.ArgumentParser(description = "detect orchestrator failover.")
parser.add_argument("-u", type=str, help = "(required) mysql user", required=True)
parser.add_argument("-p", type=str, help = "(required) mysql pass", required=True)
parser.add_argument("-H", type=str, help = "(required) master host", required=True)
parser.add_argument("-P", type=str, help = "(required) master port", required=True)
args = parser.parse_args()

mysql_user = args.u
mysql_pass = args.p
master_host = args.H
master_port = args.P

fp = None
master_conn = None
master_c = None
try:
  # log
  log_path = '/path_to_your_dir/detect_orchestrator_failover.log'
  fp = open(log_path, 'a')

  # connect master
  master_conn = MySQLdb.connect(
    user=mysql_user,
    passwd=mysql_pass,
    host=master_host,
    port=int(master_port),
    db='mysql'
  )
  master_c = master_conn.cursor(MySQLdb.cursors.DictCursor)
  fp.write('[{}]new master is {}:{}.\n'.format(datetime.datetime.now(), master_host, master_port))

  # set master
  master_queries = []
  master_queries.append('set global read_only = 0')
  for sql in master_queries:
    master_c.execute(sql)
    fp.write('[{}]new master query is {}.\n'.format(datetime.datetime.now(), sql))

  # get slaves
  sql = "show slave hosts"
  master_c.execute(sql)
  rows = master_c.fetchall()
  if isinstance(rows, tuple):
    for row in rows:
      slave_conn = None
      slave_c = None
      try:
        slave_host = row['Host']
        slave_port = row['Port']

        # connect slave
        slave_conn = MySQLdb.connect(
          user=mysql_user,
          passwd=mysql_pass,
          host=slave_host,
          port=int(slave_port),
          db='mysql'
        )
        slave_c = slave_conn.cursor(MySQLdb.cursors.DictCursor)
        fp.write('[{}]new slave is {}:{}.\n'.format(datetime.datetime.now(), slave_host, slave_port))

        # set slave
        slave_queries = []
        slave_queries.append('set global read_only = 1')
        for sql in slave_queries:
          slave_c.execute(sql)
          fp.write('[{}]new slave query is {}.\n'.format(datetime.datetime.now(), sql))

      except:
        fp.write('[{}]{}\n'.format(datetime.datetime.now(), traceback.format_exc()))
      finally:
          if slave_c is not None:
            slave_c.close()
          if slave_conn is not None:
            slave_conn.close()

except:
  fp.write('[{}]{}\n'.format(datetime.datetime.now(), traceback.format_exc()))
finally:
  if master_c is not None:
    master_c.close()
  if master_conn is not None:
    master_conn.close()
  if fp is not None:
    fp.close()


ここまでやっておくことにより、

  • IPアドレスベースでOrchestorator上からレプリケーションクラスターの認識/操作が行われる
  • フェイルオーバー時にslaveのread_onlyを1に変更できる

ということができる。


ちなみに、Orchestoratorでは、

masterのダウンに対する振舞のみがhookとして設定できるようなので、

slaveを追加した場合に自動的にread_onlyを設定するといったことは出来無いように見えます。(たぶん)


マニュアルが細かいので、利用の際はがんばってマニュアルを読んでみてください。

https://github.com/github/orchestrator/tree/master/docs


(参考)

https://qiita.com/rerorero/items/1f06cc8db9c469191289

https://www.s-style.co.jp/blog/2018/11/2875/

以上

投稿日時:2019年03月03日 01:12   カテゴリー:mariadb   [コメントがあればどうぞ]

MariaDBにてudfを作成したのですが、

生成カラムで利用する際の制限が存在しているようなので記載します。

なお、MariaDBのバージョンは、10.3.13で、

以下のようなudfを作成したとします。

> create function myfunc returns string soname 'myfunc.so';

1.virtualタイプの生成カラム

virtualタイプの生成カラムでは、条件式として使えます。

以下のようなalterをすることができます。

> alter table t1 add column c2 varchar(64) as (myfunc(c1)) virtual;

しかし、c2に対するINDEXを作成することはできません。



2.persistent(stored)タイプの生成カラム

persistentタイプの生成カラムでは、udfを使えません。

当然INDEXも作成できません。



以下のマニュアルにもその旨が記載されていますが、udfを用いたvirtualタイプの生成列にINDEXを作成することができないとは記載されていないような。。

https://mariadb.com/kb/en/library/generated-columns/



実は、この制約は結構痛いなーと感じてます。

udfは、C/C++やらgolangで実装できるので、アプリケーションの処理を通さなければ判定できなかった状態を、データベース側で判定できるようになる、、にも拘わらずINDEXが使えない、つまり高速アクセスできない。。。残念。

また、persistentタイプにも使えないことで、文字列を全文検索用に整形するようなudfを作っても、すごく便利には使えないなどのことが発生してしまう。

(FTSに対応するのは、persistentタイプのみなので)



将来のバージョンアップに期待。



以上


投稿日時:2019年03月02日 23:07   カテゴリー:mariadb   [コメントがあればどうぞ]