Pacemaker管理工具中 pcs/pcsd 的关系


在 debug pacemaker/pcs/pcsd 的时候,我们通常需要知道,敲下 `pcs xxxx xxxx` 命令后,发生了什么动作。

在应用 pcs 进行管理的 pacemaker 集群中,每个节点都会启动一个 pcsd 守护进程,监听 2224/tcp 端口。随后,我们可以从任一节点中,通过 pcs 命令管理整个集群。

误解

按照套路,通常这是一种 client/server 架构, pcs 命令行工具向相应节点的 pcsd 发送请求, pcsd 在相应节点完成动作。

然而实际与此有所出入。

实际套路

实际上,真正对 pacemaker 执行操作的,是 pcs 这个命令行工具。pcsd 负责接收来自其他节点的请求,随之调用本地的 pcs 工具,最后由本地的 pcs 执行操作。

本地命令示例

以 `pcs cluster start` 命令为例。在 Node A 中执行 `pcs cluster start`, Node A 本地的 cluster 相关服务将被启动。

在此操作中,不需要经过 pcsd. 即, pcs ---> execute. 具体过程如下。

1. Node A 的 pcs 脚本被调用。

### pcs/pcs
 10 from pcs import app
 11  
 12 app.main(sys.argv[1:])

2. Node A 的 pcs 调起 app.py.

### pcs/app.py
 14 from pcs import (
 15     acl,
 16     cluster,
 17     config,
 18     constraint,
 19     node,
 20     pcsd,
 21     prop,
 22     qdevice,
 23     quorum,
 24     resource,
 25     settings,
 26     status,
 27     stonith,
 28     usage,
 29     utils,
 30     alert,
 31 )

### 找到 cluster 子命令对应的入口,cluster.cluster_cmd.
176     cmd_map = {
177         "resource": resource.resource_cmd,
178         "cluster": cluster.cluster_cmd,
179         "stonith": stonith.stonith_cmd,
180         "property": prop.property_cmd,
181         "constraint": constraint.constraint_cmd,
182         "acl": acl.acl_cmd,
183         "status": status.status_cmd,
184         "config": config.config_cmd,
185         "pcsd": pcsd.pcsd_cmd,
186         "node": node.node_cmd,
187         "quorum": lambda argv: quorum.quorum_cmd(
188             utils.get_library_wrapper(),
189             argv,
190             utils.get_modificators()
191         ),
192         "qdevice": lambda argv: qdevice.qdevice_cmd(
193             utils.get_library_wrapper(),
194             argv,
195             utils.get_modificators()
196         ),
197         "alert": lambda args: alert.alert_cmd(
198             utils.get_library_wrapper(),
199             args,
200             utils.get_modificators()
201         ),
202     }

3. 由于`pcs cluster start`后面没有接节点名或`--all`,所以判断为在本地节点进行操作。

可以看到,这个命令实际会在本地启动 corosync 和 pacemaker 服务。

###pcs/cluster.py

  57 def cluster_cmd(argv):

  86     elif (sub_cmd == "start"):
  87         if "--all" in utils.pcs_options:
  88             start_cluster_all()
  89         else:
  90             start_cluster(argv)

 859 def start_cluster(argv):
 860     wait = False
 861     wait_timeout = None
 862     if "--wait" in utils.pcs_options:
 863         wait_timeout = utils.validate_wait_get_timeout(False)
 864         wait = True
 865  
 866     if len(argv) > 0:     ###### <---------------- For remote node only.
 867         start_cluster_nodes(argv)
 868         if wait:
 869             wait_for_nodes_started(argv, wait_timeout)
 870         return
 871  
 872     print("Starting Cluster...")
 873     if utils.is_rhel6():
 874 #   Verify that CMAN_QUORUM_TIMEOUT is set, if not, then we set it to 0
 875         retval, output = getstatusoutput('source /etc/sysconfig/cman ; [ -z "$CMAN_QUORUM_TIMEOUT" ]')
 876         if retval == 0:
 877             with open("/etc/sysconfig/cman", "a") as cman_conf_file:
 878                 cman_conf_file.write("\nCMAN_QUORUM_TIMEOUT=0\n")
 879  
 880         output, retval = utils.run(["service", "cman","start"])
 881         if retval != 0:
 882             print(output)
 883             utils.err("unable to start cman")
 884     else:
 885         output, retval = utils.run(["service", "corosync","start"]) #####<------ Work Horse 本地执行操作
 886         if retval != 0:
 887             print(output)
 888             utils.err("unable to start corosync")
 889     output, retval = utils.run(["service", "pacemaker", "start"])
 890     if retval != 0:
 891         print(output)
 892         utils.err("unable to start pacemaker")
 893     if wait:                                                                          
 894         wait_for_nodes_started([], wait_timeout)

远程命令示例

以`pcs cluster start --all`为例。在 Node A 执行该命令,会启动所有节点的的 cluster 相关服务。

这个命令会向每个节点的 pcsd 发送请求,随后每个节点的 pcsd 调用本地的 pcs 命令执行相应操作。

即, pcs ----> remote psdc ----> remote pcs ---> execute. 具体过程如下。

1. Node A 中执行 `pcs cluster start --all` 命令。由于存在`--all`参数,进入start_cluster_all()分支。

57 def cluster_cmd(argv):

  86     elif (sub_cmd == "start"):
  87         if "--all" in utils.pcs_options:
  88             start_cluster_all()
  89         else:
  90             start_cluster(argv)
  91     elif (sub_cmd == "stop"):
  92         if "--all" in utils.pcs_options:
  93             stop_cluster_all()
  94         else:
  95             stop_cluster(argv)


 896 def start_cluster_all():                                                                                                                  
 897     wait = False
 898     wait_timeout = None
 899     if "--wait" in utils.pcs_options:
 900         wait_timeout = utils.validate_wait_get_timeout(False)
 901         wait = True
 902  
 903     all_nodes = utils.getNodesFromCorosyncConf()  ######<------获取所有节点名字
 904     start_cluster_nodes(all_nodes)                ######<------在所有节点启动cluster
 905  
 906     if wait:
 907         wait_for_nodes_started(all_nodes, wait_timeout)


 909 def start_cluster_nodes(nodes):
 910     error_list = parallel_for_nodes(utils.startCluster, nodes, quiet=True)
 911     if error_list:
 912         utils.err("unable to start all nodes\n" + "\n".join(error_list))

2. Node A 向各节点的 pcsd 发送 http 请求。

### pcs/utils.py
 246 def startCluster(node, quiet=False):                                                                                                      
 247     return sendHTTPRequest(node, 'remote/cluster_start', None, False, not quiet)

 346 def sendHTTPRequest(host, request, data = None, printResult = True, printSuccess = True):
 347     url = 'https://' + host + ':2224/' + request

3. 各节点(包括A)的 pcsd 接收到请求. 随后在本地执行 `pcs cluster start`. 后面的过程与上述本地的过程一致。

### pcsd/remote.rb

  17 def remote(params, request, auth_user)
  18   remote_cmd_without_pacemaker = {  

  39       :cluster_start => method(:cluster_start),  

 193 def cluster_start(params, request, auth_user)
 194   if params[:name]
 195     code, response = send_request_with_token(
 196       auth_user, params[:name], 'cluster_start', true
 197     )
 198   else
 199     if not allowed_for_local_cluster(auth_user, Permissions::WRITE)
 200       return 403, 'Permission denied'
 201     end
 202     $logger.info "Starting Daemons"
 203     output, stderr, retval = run_cmd(auth_user, PCS, 'cluster', 'start') ####<<<<------Call local pcs
 204     $logger.debug output
 205     if retval != 0
 206       return [400, (output + stderr).join]
 207     else
 208       return output
 209     end
 210   end
 211 end