ProxySQLは、v1.4.2以降ネイティブクラスタリングをサポートしています。これは、複数のProxySQLインスタンスがクラスター対応であることを意味します。彼らは互いの状態を認識しており、構成バージョン、タイムスタンプ、およびチェックサム値に基づいて最新の構成に同期することにより、構成の変更を自動的に処理できます。 ProxySQLのクラスタリングサポートを構成する方法と、その動作を期待できる方法を示すこのブログ投稿を確認してください。
ProxySQLは分散型プロキシであり、アプリケーションの近くにデプロイすることをお勧めします。このアプローチは、実行時に簡単に再構成できるように設計されているため、数百ノードまでもかなり適切に拡張できます。複数のProxySQLノードを効率的に管理するには、ノードの1つで実行された変更が、ファーム内のすべてのノードに適用されることを確認する必要があります。ネイティブクラスタリングを使用しない場合は、構成を手動でエクスポートして他のノードにインポートする必要があります(ただし、これは自分で自動化できます)。
前回のブログ投稿では、KubernetesConfigMapを介したProxySQLクラスタリングについて説明しました。このアプローチは、ConfigMapの集中型構成アプローチで多かれ少なかれ効率的です。 ConfigMapにロードされたものはすべて、ポッドにマウントされます。構成の更新は、バージョン管理(proxysql.cnfコンテンツを変更し、別の名前でConfigMapにロード)を介して実行し、展開方法のスケジューリングと更新戦略に応じてポッドにプッシュできます。
ただし、急速に変化する環境では、このConfigMapアプローチはおそらく最善の方法ではありません。これは、新しい構成をロードするために、ConfigMapボリュームを再マウントするためにポッドの再スケジュールが必要であり、これによりProxySQLサービス全体が危険にさらされる可能性があるためです。たとえば、私たちの環境では、厳格なパスワードポリシーにより、MySQLユーザーパスワードの有効期限が7日ごとに強制される必要があります。これにより、新しいパスワードのProxySQLConfigMapを毎週更新し続ける必要があります。補足として、ProxySQL内のMySQLユーザーは、バックエンドMySQLサーバー上のものと一致するユーザーとパスワードを必要とします。そこで、KubernetesでProxySQLネイティブクラスタリングのサポートを利用して、ConfigMapのバージョン管理やポッドの再スケジュールに煩わされることなく構成の変更を自動的に適用する必要があります。
このブログ投稿では、Kubernetesでヘッドレスサービスを使用してProxySQLネイティブクラスタリングを実行する方法を紹介します。私たちの高レベルのアーキテクチャは、次のように説明できます。
ClusterControlによって展開および管理されるベアメタルインフラストラクチャで実行されている3つのGaleraノードがあります:
- 192.168.0.21
- 192.168.0.22
- 192.168.0.23
私たちのアプリケーションはすべて、Kubernetes内でポッドとして実行されています。アイデアは、アプリケーションとデータベースクラスターの間に2つのProxySQLインスタンスを導入して、リバースプロキシとして機能させることです。次に、アプリケーションはKubernetesサービスを介してProxySQLポッドに接続します。このサービスは、負荷分散され、多数のProxySQLレプリカ間でフェイルオーバーされます。
以下は、Kubernetesのセットアップの概要です。
[email protected]:~# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kube1 Ready master 5m v1.15.1 192.168.100.201 <none> Ubuntu 18.04.1 LTS 4.15.0-39-generic docker://18.9.7
kube2 Ready <none> 4m1s v1.15.1 192.168.100.202 <none> Ubuntu 18.04.1 LTS 4.15.0-39-generic docker://18.9.7
kube3 Ready <none> 3m42s v1.15.1 192.168.100.203 <none> Ubuntu 18.04.1 LTS 4.15.0-39-generic docker://18.9.7
ConfigMapを介したProxySQLの構成
まず、ConfigMapにロードされる基本構成を準備しましょう。 proxysql.cnfというファイルを作成し、次の行を追加します。
datadir="/var/lib/proxysql"
admin_variables=
{
admin_credentials="proxysql-admin:adminpassw0rd;cluster1:secret1pass"
mysql_ifaces="0.0.0.0:6032"
refresh_interval=2000
cluster_username="cluster1"
cluster_password="secret1pass"
cluster_check_interval_ms=200
cluster_check_status_frequency=100
cluster_mysql_query_rules_save_to_disk=true
cluster_mysql_servers_save_to_disk=true
cluster_mysql_users_save_to_disk=true
cluster_proxysql_servers_save_to_disk=true
cluster_mysql_query_rules_diffs_before_sync=3
cluster_mysql_servers_diffs_before_sync=3
cluster_mysql_users_diffs_before_sync=3
cluster_proxysql_servers_diffs_before_sync=3
}
mysql_variables=
{
threads=4
max_connections=2048
default_query_delay=0
default_query_timeout=36000000
have_compress=true
poll_timeout=2000
interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
default_schema="information_schema"
stacksize=1048576
server_version="5.1.30"
connect_timeout_server=10000
monitor_history=60000
monitor_connect_interval=200000
monitor_ping_interval=200000
ping_interval_server_msec=10000
ping_timeout_server=200
commands_stats=true
sessions_sort=true
monitor_username="proxysql"
monitor_password="proxysqlpassw0rd"
monitor_galera_healthcheck_interval=2000
monitor_galera_healthcheck_timeout=800
}
mysql_galera_hostgroups =
(
{
writer_hostgroup=10
backup_writer_hostgroup=20
reader_hostgroup=30
offline_hostgroup=9999
max_writers=1
writer_is_also_reader=1
max_transactions_behind=30
active=1
}
)
mysql_servers =
(
{ address="192.168.0.21" , port=3306 , hostgroup=10, max_connections=100 },
{ address="192.168.0.22" , port=3306 , hostgroup=10, max_connections=100 },
{ address="192.168.0.23" , port=3306 , hostgroup=10, max_connections=100 }
)
mysql_query_rules =
(
{
rule_id=100
active=1
match_pattern="^SELECT .* FOR UPDATE"
destination_hostgroup=10
apply=1
},
{
rule_id=200
active=1
match_pattern="^SELECT .*"
destination_hostgroup=20
apply=1
},
{
rule_id=300
active=1
match_pattern=".*"
destination_hostgroup=10
apply=1
}
)
mysql_users =
(
{ username = "wordpress", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 },
{ username = "sbtest", password = "passw0rd", default_hostgroup = 10, transaction_persistent = 0, active = 1 }
)
proxysql_servers =
(
{ hostname = "proxysql-0.proxysqlcluster", port = 6032, weight = 1 },
{ hostname = "proxysql-1.proxysqlcluster", port = 6032, weight = 1 }
)
上記の構成行の一部は、以下のセクションごとに説明されています。
admin_variables
admin_credentialsに注意してください 「proxysql-admin」であるデフォルト以外のユーザーを使用した変数。 ProxySQLは、ローカルホストのみを介したローカル接続用にデフォルトの「admin」ユーザーを予約します。したがって、ProxySQLインスタンスにリモートでアクセスするには、他のユーザーを使用する必要があります。そうしないと、次のエラーが発生します:
ERROR 1040 (42000): User 'admin' can only connect locally
cluster_usernameも追加しました およびcluster_password admin_credentialsの値 自動同期を可能にするためにセミコロンで区切られた行。 cluster_ *で始まるすべての変数 ProxySQLネイティブクラスタリングに関連しており、自明です。
mysql_galera_hostgroups
これは、ProxySQL 2.xに導入された新しいディレクティブです(ProxySQLイメージは2.0.5で実行されています)。 ProxySQL 1.xで実行する場合は、この部分を削除して、代わりにスケジューラテーブルを使用してください。構成の詳細については、このブログ投稿「DockerでMySQLGaleraCluster用にProxySQL2.0を実行および構成する方法」の「ProxySQL2.xSupportforGaleraCluster」ですでに説明しました。
mysql_servers
すべての行は自明です。これは、ClusterControlから取得した次のトポロジのスクリーンショットに要約されているように、MySQLGaleraClusterで実行されている3つのデータベースサーバーに基づいています。
proxysql_servers
ここでは、ProxySQLピアのリストを定義します。
- hostname-ピアのホスト名/IPアドレス
- ポート-ピアの管理ポート
- 重量-現在は使用されていませんが、将来の機能強化のロードマップに含まれています
- コメント-自由形式のコメントフィールド
Docker / Kubernetes環境では、コンテナのホスト名またはIPアドレスを検出してリンクし、ConfigMap、手動挿入、entrypoint.shスクリプト、環境変数、またはその他の手段を使用して、これらをこのテーブルに挿入する方法が複数あります。 Kubernetesでは、使用するReplicationControllerまたはDeploymentメソッドに応じて、StatefulSetで実行している場合を除き、ポッドの解決可能なホスト名を事前に推測するのはやや注意が必要です。
作成されたポッドに安定した解決可能なホスト名を提供するStatefulStateポッド序数インデックスに関するこのチュートリアルを確認してください。これをヘッドレスサービス(さらに下で説明)と組み合わせると、解決可能なホスト名の形式は次のようになります。
{app_name}-{index_number}。{service}
ここで、{service}はヘッドレスサービスであり、「proxysql-0.proxysqlcluster」と「proxysql-1.proxysqlcluster」の由来を説明しています。 3つ以上のレプリカが必要な場合は、StatefulSetアプリケーション名に関連する昇順のインデックス番号を追加して、それに応じてエントリを追加します。
これで、構成ファイルをConfigMapにプッシュする準備が整いました。これは、展開中にすべてのProxySQLポッドにマウントされます。
$ kubectl create configmap proxysql-configmap --from-file=proxysql.cnf
ConfigMapが正しくロードされているかどうかを確認します:
$ kubectl get configmap
NAME DATA AGE
proxysql-configmap 1 7h57m
ProxySQL監視ユーザーの作成
展開を開始する前の次のステップは、データベースクラスターにProxySQL監視ユーザーを作成することです。 Galeraクラスターで実行しているため、Galeraノードの1つで次のステートメントを実行します。
mysql> CREATE USER 'proxysql'@'%' IDENTIFIED BY 'proxysqlpassw0rd';
mysql> GRANT USAGE ON *.* TO 'proxysql'@'%';
MySQLユーザーを作成していない場合(上記のmysql_usersセクションで指定)、それらも作成する必要があります:
mysql> CREATE USER 'wordpress'@'%' IDENTIFIED BY 'passw0rd';
mysql> GRANT ALL PRIVILEGES ON wordpress.* TO 'wordpress'@'%';
mysql> CREATE USER 'sbtest'@'%' IDENTIFIED BY 'passw0rd';
mysql> GRANT ALL PRIVILEGES ON sbtest.* TO 'proxysql'@'%';
それでおしまい。これで、展開を開始する準備が整いました。
StatefulSetの導入
まず、StatefulSetを使用して2つのProxySQLインスタンス、または冗長性を目的としたレプリカを作成します。
まず、proxysql-ss-svc.ymlというテキストファイルを作成し、次の行を追加しましょう。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: proxysql
labels:
app: proxysql
spec:
replicas: 2
serviceName: proxysqlcluster
selector:
matchLabels:
app: proxysql
tier: frontend
updateStrategy:
type: RollingUpdate
template:
metadata:
labels:
app: proxysql
tier: frontend
spec:
restartPolicy: Always
containers:
- image: severalnines/proxysql:2.0.4
name: proxysql
volumeMounts:
- name: proxysql-config
mountPath: /etc/proxysql.cnf
subPath: proxysql.cnf
ports:
- containerPort: 6033
name: proxysql-mysql
- containerPort: 6032
name: proxysql-admin
volumes:
- name: proxysql-config
configMap:
name: proxysql-configmap
---
apiVersion: v1
kind: Service
metadata:
annotations:
labels:
app: proxysql
tier: frontend
name: proxysql
spec:
ports:
- name: proxysql-mysql
nodePort: 30033
port: 6033
protocol: TCP
targetPort: 6033
- name: proxysql-admin
nodePort: 30032
port: 6032
protocol: TCP
targetPort: 6032
selector:
app: proxysql
tier: frontend
type: NodePort
上記の定義には、StatefulSetとServiceの2つのセクションがあります。 StatefulSetは、ポッドまたはレプリカの定義であり、proxysql-configmapからロードされるConfigMapボリュームのマウントポイントです。次のセクションはサービス定義です。ここでは、ポッドを公開して内部ネットワークまたは外部ネットワークにルーティングする方法を定義します。
ProxySQLステートフルセットとサービスを作成します:
$ kubectl create -f proxysql-ss-svc.yml
ポッドとサービスの状態を確認します:
$ kubectl get pods,svc
NAME READY STATUS RESTARTS AGE
pod/proxysql-0 1/1 Running 0 4m46s
pod/proxysql-1 1/1 Running 0 2m59s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10h
service/proxysql NodePort 10.111.240.193 <none> 6033:30033/TCP,6032:30032/TCP 5m28s
ポッドのログを見ると、次の警告が殺到していることがわかります。
$ kubectl logs -f proxysql-0
...
2019-08-01 19:06:18 ProxySQL_Cluster.cpp:215:ProxySQL_Cluster_Monitor_thread(): [WARNING] Cluster: unable to connect to peer proxysql-1.proxysqlcluster:6032 . Error: Unknown MySQL server host 'proxysql-1.proxysqlcluster' (0)
上記は、proxysql-0が「proxysql-1.proxysqlcluster」を解決して接続できなかったことを意味します。これは、ProxySQL間通信に必要となるDNSレコード用のヘッドレスサービスを作成していないためです。
Kubernetesヘッドレスサービス
ProxySQLポッドが予想されるFQDNを解決して直接接続できるようにするには、解決プロセスで、仮想IPアドレスではなく、割り当てられたターゲットポッドのIPアドレスを検索できる必要があります。ここでヘッドレスサービスが登場します。 「clusterIP=None」を設定してヘッドレスサービスを作成する場合、負荷分散は構成されず、このサービスにクラスターIP(仮想IP)は割り当てられません。 DNSのみが自動的に構成されます。ヘッドレスサービスのDNSクエリを実行すると、ポッドのIPアドレスのリストが表示されます。
「proxysqlcluster」のヘッドレスサービスDNSレコードを検索すると、次のようになります(この例では、3つのProxySQLインスタンスがあります):
$ host proxysqlcluster
proxysqlcluster.default.svc.cluster.local has address 10.40.0.2
proxysqlcluster.default.svc.cluster.local has address 10.40.0.3
proxysqlcluster.default.svc.cluster.local has address 10.32.0.2
次の出力は、clusterIPに解決される「proxysql」と呼ばれる標準サービスのDNSレコードを示しています。
$ host proxysql
proxysql.default.svc.cluster.local has address 10.110.38.154
ヘッドレスサービスを作成してポッドにアタッチするには、StatefulSet宣言内でServiceNameを定義する必要があり、以下に示すように、サービス定義には「clusterIP=None」が必要です。 proxysql-headless-svc.ymlというテキストファイルを作成し、次の行を追加します。
apiVersion: v1
kind: Service
metadata:
name: proxysqlcluster
labels:
app: proxysql
spec:
clusterIP: None
ports:
- port: 6032
name: proxysql-admin
selector:
app: proxysql
ヘッドレスサービスを作成する:
$ kubectl create -f proxysql-headless-svc.yml
確認のため、この時点で、次のサービスが実行されています。
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8h
proxysql NodePort 10.110.38.154 <none> 6033:30033/TCP,6032:30032/TCP 23m
proxysqlcluster ClusterIP None <none> 6032/TCP 4s
次に、ポッドのログの1つを確認します:
$ kubectl logs -f proxysql-0
...
2019-08-01 19:06:19 ProxySQL_Cluster.cpp:215:ProxySQL_Cluster_Monitor_thread(): [WARNING] Cluster: unable to connect to peer proxysql-1.proxysqlcluster:6032 . Error: Unknown MySQL server host 'proxysql-1.proxysqlcluster' (0)
2019-08-01 19:06:19 [INFO] Cluster: detected a new checksum for mysql_query_rules from peer proxysql-1.proxysqlcluster:6032, version 1, epoch 1564686376, checksum 0x3FEC69A5C9D96848 . Not syncing yet ...
2019-08-01 19:06:19 [INFO] Cluster: checksum for mysql_query_rules from peer proxysql-1.proxysqlcluster:6032 matches with local checksum 0x3FEC69A5C9D96848 , we won't sync.
Clusterコンポーネントは、「proxysqlcluster」と呼ばれるヘッドレスサービスを介して、ポート6032上の他のピアproxysql-1.proxysqlclusterからの新しいチェックサムを解決、接続、および検出できることに気付くでしょう。このサービスはKubernetesネットワーク内でのみポート6032を公開するため、外部からは到達できないことに注意してください。
この時点で、展開は完了です。
ProxySQLへの接続
ProxySQLサービスに接続する方法はいくつかあります。負荷分散されたMySQL接続は、Kubernetesネットワーク内からポート6033に送信し、クライアントが外部ネットワークから接続している場合はポート30033を使用する必要があります。
外部ネットワークからProxySQL管理インターフェースに接続するには、NodePortセクションで定義されたポート30032に接続できます(192.168.100.203はホストkube3.localのプライマリIPアドレスです):
$ mysql -uproxysql-admin -padminpassw0rd -h192.168.100.203 -P30032
Kubernetesネットワーク内の他のポッドからアクセスする場合は、ポート6032でclusterIP 10.110.38.154(「proxysql」サービスで定義)を使用します。
次に、必要に応じてProxySQL構成の変更を実行し、それらをランタイムにロードします。
mysql> INSERT INTO mysql_users (username,password,default_hostgroup) VALUES ('newuser','passw0rd',10);
mysql> LOAD MYSQL USERS TO RUNTIME;
ポッドの1つに、構成の同期が完了したことを示す次の行が表示されます。
$ kubectl logs -f proxysql-0
...
2019-08-02 03:53:48 [INFO] Cluster: detected a peer proxysql-1.proxysqlcluster:6032 with mysql_users version 2, epoch 1564718027, diff_check 4. Own version: 1, epoch: 1564714803. Proceeding with remote sync
2019-08-02 03:53:48 [INFO] Cluster: detected peer proxysql-1.proxysqlcluster:6032 with mysql_users version 2, epoch 1564718027
2019-08-02 03:53:48 [INFO] Cluster: Fetching MySQL Users from peer proxysql-1.proxysqlcluster:6032 started
2019-08-02 03:53:48 [INFO] Cluster: Fetching MySQL Users from peer proxysql-1.proxysqlcluster:6032 completed
自動同期は、ProxySQLランタイムで構成が変更された場合にのみ発生することに注意してください。したがって、アクションを表示する前に、「LOAD...TORUNTIME」ステートメントを実行することが重要です。永続性のために、ProxySQLの変更をディスクに保存することを忘れないでください:
mysql> SAVE MYSQL USERS TO DISK;
制限
ProxySQLは、永続性のためにConfigMapにロードするために後で使用できるテキスト構成ファイルへのアクティブな構成の保存/エクスポートをサポートしていないため、このセットアップには制限があることに注意してください。これには機能リクエストがあります。その間、変更をConfigMapに手動でプッシュできます。そうしないと、ポッドが誤って削除された場合、新しいポッドがConfigMapで定義されたものによってブートストラップされるため、現在の構成が失われます。
このブログ投稿のアイデアを生み出し、ユースケースと実装に関する洞察を提供してくれたSampathKamineniに特に感謝します。