〇、使用Dockers swarm构建多主机docker群集
在Docker上倒腾多了,很快就会遇到跨物理机通信的问题。尽管使用Docker部署很简单,但一口气把那么多服务部署在一台机器上也并非我们本意。几个zookeeper和clickhouse实例在一台机器上跑,多好的机器也得憋死。所以既要发挥docker-compose的一键部署作用,又要跨多物理机部署的既要又要需求就出来了。当然,可以通过在每台物理机上构建并配置docker网络,然后通过路由表方式将它们连接起来(参考Docker容器实现跨物理机通信_跨物理网络发布_唐伯虎点蚊香dw的博客-CSDN博客),但总是还是太麻烦。幸好,好有个swarm可用。
一、集群ssh免密登录
使用swarm之前,我们需要把将要加入swarm集群的主机配置为可以SSH免密登录的。这个可以参考我们之前配置Hadoop集群时的配置方法,此处不赘述:
(25条消息) CENTOS上的网络安全工具(十二)走向Hadoop(4) Hadoop 集群搭建-CSDN博客
1. 设置主机名
[root@localhost etc]# hostnamectl set-hostname pig1 --static
2. 编辑/etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
192.168.119.101 pig1
192.168.119.102 pig2
192.168.119.103 pig3
3. 在pig1上生成SSH指纹
[root@pig1 ~]# ssh pig1
The authenticity of host 'pig1 (192.168.119.101)' can't be established.
ECDSA key fingerprint is SHA256:…….
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'pig1,192.168.119.101' (ECDSA) to the list of known hosts.
root@pig1's password:
Activate the web console with: systemctl enable --now cockpit.socket
Last login: Mon Mar 20 23:44:50 2023
[root@pig1 ~]# cd .ssh/
[root@pig1 .ssh]# ls
known_hosts
[root@pig1 .ssh]# cat known_hosts
pig1,192.168.119.101 ecdsa-sha2-nistp256 AAAA……MeLFns=
4. 生成密钥并配置免密
[root@pig1 .ssh]# ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
Generating public/private rsa key pair.
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
SHA256:…… root@pig1
The key's randomart image is:
+---[RSA 3072]----+
| .oo.o ..+o|
……
| . + . . |
| o.. |
+----[SHA256]-----+
[root@pig1 .ssh]# ls
id_rsa id_rsa.pub known_hosts
[root@pig1 .ssh]# ssh-copy-id pig1
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
root@pig1's password:
Number of key(s) added: 1
Now try logging into the machine, with: "ssh 'pig1'"
and check to make sure that only the key(s) you wanted were added.
[root@pig1 .ssh]# ls
authorized_keys id_rsa id_rsa.pub known_hosts
这里我们尝试新的方法,毕竟如果集群数量很多,使用ssh-copy-id交叉拷贝会非常麻烦。
在authorized_keys中,将pig1的公钥拷贝至集群中主机数量,并且修改后面的主机名,这样实际所有的主机使用相同的公钥:
[root@pig1 .ssh]# cat authorized_keys
ssh-rsa AAAAB……= root@pig1
ssh-rsa AAAAB……= root@pig2
ssh-rsa AAAAB……= root@pig3
然后将它和私钥文件分发到所有的主机上,当然如果主机多的话,可以考虑用pdsh去做这件事。
[root@pig1 .ssh]# scp authorized_keys root@pig2:/root/.ssh/.
[root@pig1 .ssh]# scp id_rsa root@pig2:/root/.ssh/.
二、Swarm集群构建
Swarm集群由manager和worker组成。每个集群可以有多个manager和多个worker。构建时,首先在作为leader(也是一个manager)的主机上创建swarm集群,然后在需要加入到集群的其他主机执行join命令即可:
1. 初始化swarm集群
使用docker swam init命令可以初始化Swarm集群。我们有3台主机pig1、pig2、pig3,其中pig1我们作为leader/manager使用,pig2和pig3作为worker。Swarm集群在Leader上创建:
在pig1上执行:
[root@localhost ~]# docker swarm init
Swarm initialized: current node (ujkxo7uk1j7img0g1buav3tsj) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-1ekofn4i5p671l3tpsd4xgmltk200zcgzth4h41qlej1e0586w-cxbj4hx4lic70gzt9tnf9ctm1 192.168.119.101:2377
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
[root@localhost ~]#
2.向swarm集群加入节点
根据在pig1上init的执行结果提示,可以将worker节点加入到swarm集群中,也可以根据指示添加manger节点。docker swarm使用raft协议选举manger,所以如果要确保高可靠性需要保证manager节点数大于3,且同时存活的manager数大于2。
不过测试过程中我们有一个manager足够。把中间这一句复制出来,在pig2、pig3上执行:
[root@pig2 .ssh]# docker swarm join --token SWMTKN-1-1ekofn4i5p671l3tpsd4xgmltk200zcgzth4h41qlej1e0586w-cxbj4hx4lic70gzt9tnf9ctm1 192.168.119.101:2377
This node joined a swarm as a worker.
[root@pig3 .ssh]# docker swarm join --token SWMTKN-1-1ekofn4i5p671l3tpsd4xgmltk200zcgzth4h41qlej1e0586w-cxbj4hx4lic70gzt9tnf9ctm1 192.168.119.101:2377
This node joined a swarm as a worker.
3. 使用node命令查看
swarm构建完成后,可以使用node命令查看加入到集群中的节点的状态
[root@pig1 ~]# docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
ujkxo7uk1j7img0g1buav3tsj * pig1 Ready Active Leader 23.0.1
mbi5br36m00el05xe9i6bgpu0 pig2 Ready Active 23.0.1
wz9rdesxa45571yic7kl78c5y pig3 Ready Active 23.0.1
三、stack的任务编排
docker stack的任务编排和docker compose的任务编排非常像,都是用yml文件进行描述,部分字段还是通用的。不过compose文件的版本号大于2就够,但用于stack的compose文件要求版本号大于3。
任务编排的部署文件编写方法可以参考官网上的指南撰写文件部署参考 (docker.com)。这里简要记录几个常用的、且和compose不太一样的配置项
1. service-deploy-placement
提供部署位置的约束。对于需要分区分片部署的数据库来说,为了确保swarm重启服务的时候容器仍然保持在对应节点上,避免数据错位,- node选项应该是最为重要的一个:
deploy:
placement:
constraints:
- node.hostname==pig1
如果没有设置主机名,也可以使用node.id,这个ID出现在如上的node ls命令返回结果中。
如果只是希望指定角色,可以使用node.role==manager/worker。
2. service-deploy-replicas
指定服务可以启动多个副本,一般来说对可灵活扩缩的分布式服务比较有用,需要对应节点配置的就不大敢用了。
services:
frontend:
image: awesome/webapp
deploy:
mode: replicated
replicas: 6
3. service-deploy-restart_policy:
该选项指定服务重启的条件,默认是反复重启,也可指定重启的次数。注意这个和compose的restart选项不同。
deploy:
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
4. service-deploy-resource
指定服务可以消耗的资源。limit指定消耗的上限,resourse指定给与的下限。
services:
frontend:
image: awesome/webapp
deploy:
resources:
limits:
cpus: '0.50'
memory: 50M
pids: 1
reservations:
cpus: '0.25'
memory: 20M
四、 zookeeper部署
现在,我们就可以尝试使用swarm来搭建zookeeper集群了。
1. 部署文件编写
docker hub上的zookeeper官方指南给出了一个在单主机上部署多个zookeeper容器的部署文件示例:
我们直接拿来改装一下就好:
version: '3.1'
services:
zoo1:
image: zookeeper
deploy:
restart_policy:
condition: on-failure
hostname: zoo1
ports:
- 2181:2181
environment:
ZOO_MY_ID: 1
ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181
networks:
- pignet
zoo2:
image: zookeeper
deploy:
restart_policy:
condition: on-failure
hostname: zoo2
ports:
- 2182:2181
environment:
ZOO_MY_ID: 2
ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181
networks:
- pignet
zoo3:
image: zookeeper
deploy:
restart_policy:
condition: on-failure
hostname: zoo3
ports:
- 2183:2181
environment:
ZOO_MY_ID: 3
ZOO_SERVERS: server.1=zoo1:2888:3888;2181 server.2=zoo2:2888:3888;2181 server.3=zoo3:2888:3888;2181
networks:
- pignet
networks:
pignet:
主要的改造部分就是改掉不兼容的restart选项,增加一个默认的networks,pignet。因为stack启动服务的时候会自动创建这个网络。
编辑完成后我们可以将该文件命令为zk.yml。
2. 部署
直接使用stack deploy指令进行部署,指定stack名为pig:
[root@localhost teststack]# docker stack deploy -c zk.yml pig
Creating network pig_pignet
Creating service pig_zoo2
Creating service pig_zoo3
Creating service pig_zoo1
可以看到docker帮助我们直接创建了一个名为pig_pignet的网络。这个其实是“stack名_网络名”的方式。
(1)使用stack ls和service ls 查看服务
[root@localhost ~]# docker stack ls
NAME SERVICES
pig 3
[root@localhost ~]# docker service ls
ID NAME MODE REPLICAS IMAGE PORTS
pitt5al5dmj3 pig_zoo1 replicated 1/1 zookeeper:latest *:2181->2181/tcp
udtauhhtli71 pig_zoo2 replicated 1/1 zookeeper:latest *:2182->2181/tcp
v0565sechmd4 pig_zoo3 replicated 1/1 zookeeper:latest *:2183->2181/tcp
[root@localhost ~]#
可以看到服务名称就是“stack名_服务名”的形式。
(2)使用service ps 服务名查看
可以发现,在自动负载均衡下,被分布到了3个节点
[root@localhost ~]# docker service ps pig_zoo1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
ce40t71o3e26 pig_zoo1.1 zookeeper:latest pig1 Running Running 22 hours ago
[root@localhost ~]# docker service ps pig_zoo2
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
8k3nn7srrlax pig_zoo2.1 zookeeper:latest pig2 Running Running 22 hours ago
[root@localhost ~]# docker service ps pig_zoo3
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
tv737w4woq38 pig_zoo3.1 zookeeper:latest pig2 Running Running 40 minutes ago
(3)使用container ps查看
可以发现swarm是如何通过容器的方式将服务部署下去的。比如在pig3上的zookeeper3
[root@pig3 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
98bb9dd1f62c zookeeper:latest "/docker-entrypoint.…" 22 hours ago Exited (143) 42 minutes ago pig_zoo3.1.8ew3t29qjki6i2fqzqsq6hduz
[root@pig3 ~]#
由于我们在部署文件中只指定了hostname,没有指定container_name,所有自动生成的容器名略有些复杂……
(4)使用stack rm命令停止所有服务
可以使用stack rm命令方便的删除我们创建的所有服务:
[root@localhost teststack]# docker stack rm pig
Removing service pig_zoo1
Removing service pig_zoo2
Removing service pig_zoo3
Removing network pig_pignet
3.测试
比如,我们可以找到pig1上运行zookeeper的那个容器:
[root@localhost ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7e1a75015ba8 zookeeper:latest "/docker-entrypoint.…" 22 hours ago Up 22 hours 2181/tcp, 2888/tcp, 3888/tcp, 8080/tcp pig_zoo1.1.ce40t71o3e26z0jzqb57nhkg9
通过exec命令挂进去:
[root@localhost ~]# docker exec -it 7e1a75015ba8 bash
root@zoo1:/apache-zookeeper-3.8.1-bin# ls
bin conf docs lib LICENSE.txt NOTICE.txt README.md README_packaging.md
然后在zookeeper的bin目录下执行客户端程序:
root@zoo1:/apache-zookeeper-3.8.1-bin# bin/zkCli.sh
Connecting to localhost:2181
………………
………………
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
[zk: localhost:2181(CONNECTED) 0] ls /
[clickhouse, zookeeper]
[zk: localhost:2181(CONNECTED) 1] ls /zookeeper
[config, quota]
[zk: localhost:2181(CONNECTED) 2]
如是,zookeeper是可以正常使用的。
五、clickhouse部署
接下来,我们可以继续以这种方式来进行clickhouse的部署。然而开始前,需要注意到的是,clickhouse的部署需要在部署环境中读取配置文件。然而在容器部署情况下,尤其是swarm批量部署的情况下,我们无法事先控制镜像中的配置文件。所以解决的方法,是对每个节点都事先写好配置文件,然后将他们拷贝到每个节点上,然后让镜像加载时直接将配置文件映射到clickhouse-server的config.d目录中去。
1. config.xml文件的编写
我们计划在两个工作节点上部署clickhouse,参考之前的配置文件CENTO OS上的网络安全工具(十九)ClickHouse集群部署_lhyzws的博客-CSDN博客进行修改:
<clickhouse>
<!--允许从主机网络或其他容器进行远程连接-->
<listen_host>::</listen_host>
<listen_host>0.0.0.0</listen_host>
<listen_try>1</listen_try>
<!--记录日志-->
<logger>
<console>1</console>
</logger>
<!--ZooKeeper集群配置-->
<zookeeper>
<node>
<host>zoo1</host>
<port>2181</port>
</node>
<node>
<host>zoo2</host>
<port>2181</port>
</node>
<node>
<host>zoo3</host>
<port>2181</port>
</node>
</zookeeper>
<!--clickhouse集群设置-->
<remote_servers>
<!--集群名称,创建分布式表时需要使用-->
<pig_cluster>
<!--分片1-->
<shard>
<replica>
<host>ck1</host>
<port>9000</port>
</replica>
<!--分片1的副本1-->
<replica>
<host>ck2</host>
<port>9000</port>
</replica>
</shard>
<!--分片2-->
<shard>
<replica>
<host>ck3</host>
<port>9000</port>
</replica>
<!--分片2的副本1-->
<replica>
<host>ck4</host>
<port>9000</port>
</replica>
</shard>
</pig_cluster>
</remote_servers>
<!--分片与副本标识宏定义,创建复制表时需要使用-->
<!--ck1:第1分片的第0副本,即原数据-->
<macros>
<shard>01</shard>
<replica>pig_cluster-01-0</replica>
</macros>
</clickhouse>
这里需要设置的,首先是我们之前启动的zookeeper的节点的主机名和端口;其次,是指定承载clickhouse节点的主机名和端口,如上为ck1、ck2、ck3、ck4;最后,是指定该配置文件所在主机节点的分片及副本标识。如ck1,在1分片,为主数据,所以shard设为01,replica设为pig_cluster-01-0,其中pig_cluster为remote_servers的名称。如ck4,在2分片,为副本数据,shard设为02,replica设为pig_cluster-01-1。
这里的问题在于,每个clickhouse节点上的配置文件的分片及副本标识宏定义是不一样的,所以,对应节点号,我们编辑了4个配置文件,将其拷贝到每个主机上。后面我们将利用volume映射,对每个节点映射其对应的配置文件。
[root@localhost teststack]# scp /root/ckconfig/*.xml root@pig2:/root/ckconfig/.
pigck1.xml 100% 2387 754.8KB/s 00:00
pigck2.xml 100% 2387 1.1MB/s 00:00
pigck3.xml 100% 2387 1.1MB/s 00:00
pigck4.xml 100% 2387 788.1KB/s 00:00
[root@localhost teststack]# scp /root/ckconfig/*.xml root@pig3:/root/ckconfig/.
pigck1.xml 100% 2387 479.6KB/s 00:00
pigck2.xml 100% 2387 631.8KB/s 00:00
pigck3.xml 100% 2387 457.5KB/s 00:00
pigck4.xml 100% 2387 244.7KB/s 00:00
2. 编辑stack部署文件
version: '3'
services:
clickhouse1:
image: yandex/clickhouse-server
deploy:
restart_policy:
condition: on-failure
placement:
constraints:
# 将ck1的部署限定在pig2主机上
- node.hostname==pig2
hostname: ck1
networks:
- pignet
ports:
- "8123:8123"
- "9000:9000"
volumes:
# 映射对应节点的配置文件
- /root/ckconfig/pigck1.xml:/etc/clickhouse-server/config.d/docker_related_config.xml:rw
# 映射数据目录及日志目录到主机上
- /data0:/var/lib/clickhouse:rw
- /data0/log:/var/log/clickhouse-server:rw
clickhouse2:
image: yandex/clickhouse-server
deploy:
restart_policy:
condition: on-failure
placement:
constraints:
# 将ck2的部署限定在pig2主机上
- node.hostname==pig2
hostname: ck2
networks:
- pignet
ports:
- "8124:8123"
- "9001:9000"
volumes:
# 映射对应节点的配置文件
- /root/ckconfig/pigck2.xml:/etc/clickhouse-server/config.d/docker_related_config.xml:rw
# 映射数据目录及日志目录到主机上
- /data1:/var/lib/clickhouse:rw
- /data1/log:/var/log/clickhouse-server:rw
clickhouse3:
image: yandex/clickhouse-server
deploy:
restart_policy:
condition: on-failure
placement:
constraints:
# 将ck3的部署限定在pig3主机上
- node.hostname==pig3
hostname: ck3
networks:
- pignet
ports:
- "8125:8123"
- "9002:9000"
volumes:
# 映射对应节点的配置文件
- /root/ckconfig/pigck3.xml:/etc/clickhouse-server/config.d/docker_related_config.xml:rw
# 映射数据目录及日志目录到主机上
- /data0:/var/lib/clickhouse:rw
- /data0/log:/var/log/clickhouse-server:rw
clickhouse4:
image: yandex/clickhouse-server
deploy:
restart_policy:
condition: on-failure
placement:
constraints:
# 将ck4的部署限定在pig3主机上
- node.hostname==pig3
hostname: ck4
networks:
- pignet
ports:
- "8126:8123"
- "9003:9000"
volumes:
# 映射对应节点的配置文件
- /root/ckconfig/pigck4.xml:/etc/clickhouse-server/config.d/docker_related_config.xml:rw
# 映射数据目录及日志目录到主机上
- /data1:/var/lib/clickhouse:rw
- /data1/log:/var/log/clickhouse-server:rw
networks:
pignet:
由于我们计划将ck1、ck2部署在pig2上,ck3、ck4部署在pig3上,所以还需要规定好他们的安装节点,并且实现在节点上建立好映射目录。
注意data0和data1的区分,因为有两个ck节点被放在一台主机上,不加以区分的话可能会造成启动失败,或者文件混淆,总之不要冒险:P
3. 启动clickhouse
另一个需要注意的问题是,stack下,配置文件不支持注入depends_on、container_name等配置项。所以,控制服务的启动顺序是一个问题。好在在stack中我们可以先启动zookeeper,指定stack名称为pig;然后再启动clickhouse,指定同样的stack名称pig,就可以将所有的服务放在一个stack中了。
[root@localhost teststack]# docker stack deploy -c zk.yml pig
Creating network pig_pignet
Creating service pig_zoo2
Creating service pig_zoo3
Creating service pig_zoo1
[root@localhost teststack]# docker stack deploy -c ck.yml pig
Creating service pig_clickhouse1
Creating service pig_clickhouse2
Creating service pig_clickhouse3
Creating service pig_clickhouse4
检查一下clickhouse的启动情况,如我们所愿被部署在了pig2和pig3上:
[root@localhost teststack]# docker service ps pig_clickhouse1
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
n5a38jw8vxoz pig_clickhouse1.1 yandex/clickhouse-server:latest pig2 Running Running about a minute ago
[root@localhost teststack]# docker service ps pig_clickhouse2
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
7un6h8m3rlbf pig_clickhouse2.1 yandex/clickhouse-server:latest pig2 Running Running 57 seconds ago
[root@localhost teststack]# docker service ps pig_clickhouse3
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
kqq2xnxyfnsc pig_clickhouse3.1 yandex/clickhouse-server:latest pig3 Running Running 44 seconds ago
[root@localhost teststack]# docker service ps pig_clickhouse4
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
kfsjhjcfta3d pig_clickhouse4.1 yandex/clickhouse-server:latest pig3 Running Running 31 seconds ago
[root@localhost teststack]#
检查pig2下的data0目录,也有了东西:
[root@pig2 /]# cd data0
[root@pig2 data0]# ls
access data dictionaries_lib flags format_schemas log metadata metadata_dropped preprocessed_configs status store tmp user_defined user_files user_scripts uuid
4. 测试clickhouse
由于yandex/clickhouse-client镜像安装起来总是存在问题,所以我放弃了。不过可以简述一下问题原因和预想的解决思路:
这个镜像载入为容器是需要提供“ --hostname clickhouse-server主机名”这样的参数的,但是我在compose的配置项中没有找到设置这样的参数的方法(不过可以查看默认的entrypoint,直接用command把初始化进程和参数一并写出来,应该可以);另一方面,可以手动启动这个容器并将其加入到stack启动的pignet中,不过由于pignet是stack默认创建的,禁止手工附加,所以也没有成功(也许可以在stack deploy之前手工建设pig_pignet,指定--attached参数,没尝试)。
所以用了简单的办法,在windows中装了个dbeaver,进行连接:
选择clickhouse的驱动:
由于我们将clickhouse服务器装到了pig2和pig3主机上,其中pig2主机的IP地址是192.168.119.102,所以此处对这个IP进行连接:
测试连接一般都会成功,点击完成即可。如果是在虚拟机上,一个比较大的坑是:一定要ifconfig检查一下docker0网桥是否有IP,因为虚拟机恢复状态网桥可能会挂,然后无论如何从主机就连不到容器了。如果挂了,systemctl restart docker.service重启一下即可。
在建立好的连接上右键选择SQL编辑器,打开编辑窗口
如下,在编辑窗口中输入select * from system.clusters,并运行:可以看到我们的4个clickhouse-server节点确实都在运行了:
大功告成!