最近公司新接到一个项目,惯例是通过技术架构、业务需求、用户量还有以往的经验大概评估出一份资源配置表格提供给客户,让客户参考采购的服务器资源。但这次客户根本没有根据我们提供的参考表格来,而是直接就提供给我们一些高配置的服务器实例,实例数与我们预期的少了很多,但配置相对的提高了很多,本着客户是上帝的原则,我们只能自己针对服务器做虚拟化。
之前一直是在Windows系统上做虚拟机,在Linux系统上虚拟化服务器还是第一次,更何况这个Linux系统是没有图形界面的。在虚拟化服务器之前我也查询资料做过很多技术比较,最终选择使用Docker来虚拟化服务器,具体总结优势无非以下几点:
1、Docker创建的容器启动速度快,秒级启动
Docker管理容器操作(start,stop,rm,restart等等) 都是以秒或毫秒为单位的。
2、Docker可以基于创建的镜像进行弹性扩展
创建容器并且根据自己的需求配置好容器后提交镜像到仓库,等到需要扩展容器的时候可拉取镜像启动相同的容器。
3、Docker创建的容器较轻量级
我可以在一台服务器上启动很多容器,如果只是用到某个服务的话,你无需虚拟整套系统版本。当然本文目的是虚拟出一整套系统的运行环境,所以就另当别论了。
4、Docker开源免费☺
开源的,免费的,低成本的,这就不用我多说了。
Docker的安装这个其实我不必多说什么,网上一搜一大堆。但是有几点我需要说明下:
1、Docker存储数据位置及镜像存储位置
默认情况下Docker是将数据存储在/var/lib/docker路径下的,如果你系统盘的磁盘空间比较小,那你就需要修改Docker的数据存储路径了,可通过如下命令查看:
sudo docker info | grep "Docker Root Dir"
修改方法就是先停掉Docker服务,然后在/etc/docker/路径下创建一个daemon.json文件,在文件里加入如下文本:
{
"graph": "/home/docker"
}
重启Docker,再次查看Docker存储路径是否已经修改,如果不成功请自行百度查找,修改方法不止这一种。
2、拉取私有仓库镜像
这个私有仓库不是Docker收费版的那个,而是针对自己公司搭建的私有仓库。当你想拉取私有仓库镜像的时候,你需要配置私有仓库的IP和端口号,目的是让Docker信任你的私有仓库。具体步骤还是先关闭Docker服务,然后修改daemon.json文件,如下:
{
"graph": "/home/docker",
"insecure-registries":["192.168.10.123:5000"]
}
重启Docker,此时你就可以拉取你私有仓库的镜像了。
在说这个之前我先大概说下Docker网络配置,Docker安装后会自动创建3种网络:bridge、host、none,这三种网络模式的详细讲解我就不说了,因为太占篇幅,我就大概讲下我的理解吧,如下:
1、bridge模式
我理解就是Docker守护进程启动时会在宿主机上创建一个docker0虚拟网桥,这个网桥的作用就相当于一个交互机,该宿主机上的所有容器都是通过这个虚拟网桥连接外部网络的,docker0虚拟网桥的IP就相当于该宿主机上所有容器的默认网关。
当创建一个新容器时Docker会在宿主机上创建一对虚拟网卡veth pair设备,并且将veth pair设备的一端放在新创建的容器中,并命名为eth0(容器的网卡),另一端放在宿主机中,以vethxxx这样类似的名字命名,并将这个网络设备加入到docker0网桥中。
该模式是Docker创建容器的默认模式,如果想让外部网络访问到容器中的服务,就是需针对端口做映射,归根究底它实际是在iptables做了DNAT规则,实现端口转发功能。本文也是使用的这个模式。
根据我使用这么长时间的经验来看,这种模式的缺点就是需要事先规划好容器哪些端口该做映射。网上也有教在容器创建好后给容器增添映射端口,但是有的不靠谱儿,有的则是需要修改Docker生成的配置文件,但是如果像我这种构建的是整套系统运行环境,修改配置后重启可能有些服务就需要重新配置了,比如集群相关的服务。
2、host模式
这种模式其实就是和宿主机共用一套网路环境,它使用的IP就是宿主机的IP,它使用的端口也是宿主机的端口,网络的隔离性不好,我没有使用它的根本原因也正是它的隔离性,因为我所使用的宿主机上是存在两台系统运行环境的,或多或少的会出现端口冲突问题。
3、none模式
这种模式下的容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。也就是说它没有办法联网,我至今还没有用过,可能这样更安全吧。
以上就是我对Docker安装后创建的三种网络模式的理解,还有两种模式(container模式和user-defined模式)可以自行查找了解。
上图为不同宿主机间容器通信的的原理图,说实话这张图在网上都被用烂了,但是没得办法,谁让咱确实是按照这么做的呢,对于上图的讲解我也是直接照搬网上查到的,如下:
1)数据从源容器中发出后,经由所在主机的docker0虚拟网卡转发到flannel0虚拟网卡,这是个P2P的虚拟网卡,flanneld服务监听在网卡的另外一端。
2)Flannel通过Etcd服务维护了一张节点间的路由表,在稍后的配置部分我们会介绍其中的内容。
3)源主机的flanneld服务将原本的数据内容UDP封装后根据自己的路由表投递给目的节点的flanneld服务,数据到达以后被解包,然后直接进入目的节点的flannel0虚拟网卡,
然后被转发到目的主机的docker0虚拟网卡,最后就像本机容器通信一下的有docker0路由到达目标容器。
这样整个数据包的传递就完成了,这里需要解释三个问题:
1)UDP封装是怎么回事?
在UDP的数据内容部分其实是另一个ICMP(也就是ping命令)的数据包。原始数据是在起始节点的Flannel服务上进行UDP封装的,投递到目的节点后就被另一端的Flannel服务
还原成了原始的数据包,两边的Docker服务都感觉不到这个过程的存在。
2)为什么每个节点上的Docker会使用不同的IP地址段?
这个事情看起来很诡异,但真相十分简单。其实只是单纯的因为Flannel通过Etcd分配了每个节点可用的IP地址段后,偷偷的修改了Docker的启动参数。
在运行了Flannel服务的节点上可以查看到Docker服务进程运行参数(ps aux|grep docker|grep “bip”),例如“–bip=182.48.56.1/24”这个参数,它限制了所在节
点容器获得的IP范围。这个IP范围是由Flannel自动分配的,由Flannel通过保存在Etcd服务中的记录确保它们不会重复。
3)为什么在发送节点上的数据会从docker0路由到flannel0虚拟网卡,在目的节点会从flannel0路由到docker0虚拟网卡?
例如现在有一个数据包要从IP为172.17.18.2的容器发到IP为172.17.46.2的容器。根据数据发送节点的路由表,它只与172.17.0.0/16匹配这条记录匹配,因此数据从docker0出来以后就被投递到了flannel0。同理在目标节点,由于投递的地址是一个容器,因此目的地址一定会落在docker0对于的172.17.46.0/24这个记录上,自然的被投递到了docker0网卡。
(注:点击这里查看以上原理图讲解详细信息)
虽然安装部署方法也是在网上找的,但是网上给出的方法大方向是没问题的就是一些细节讲的还不是很清楚,导致我部署的时候还是出现很多问题。下面我会将我安装部署的过程和遇到的问题整理到本文中。
我准备了四台宿主机,如下:
主机名 | 系统版本 | 宿主机IP | 需安装的中间件 |
---|---|---|---|
master | CentOS7.6 | 192.168.10.1 | docker/etcd/flannel |
slave1 | CentOS7.6 | 192.168.10.2 | docker/flannel |
slave2 | CentOS7.6 | 192.168.10.3 | docker/flannel |
slave3 | CentOS7.6 | 192.168.10.4 | docker/flannel |
四台宿主机都需要设置hosts:
[root@master ~]# vim /etc/hosts
192.168.10.1 master
192.168.10.1 etcd
192.168.10.2 slave1
192.168.10.3 slave2
192.168.10.4 slave3
本来想搭建etcd的多主机集群的,但是想了下只是在预生产使用而且只是处在前期搭建状态还没有正式使用,所以etcd我只在192.168.10.1服务器上搭建了单个主机的,如果后期出现问题,我会修改为多主机集群。
1、安装etcd服务
yum install etcd -y
2、修改etcd配置文件
[root@master ~]# vim /etc/etcd/etcd.conf
#ETCD_CORS=""
ETCD_DATA_DIR="/var/lib/etcd/default.etcd" #数据存放路径
#ETCD_WAL_DIR=""
#ETCD_LISTEN_PEER_URLS="http://localhost:2380"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379,http://0.0.0.0:4001" #监听客户端地址
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
ETCD_NAME="master" #部署节点的名称
#ETCD_SNAPSHOT_COUNT="100000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
#ETCD_QUOTA_BACKEND_BYTES="0"
#ETCD_MAX_REQUEST_BYTES="1572864"
#ETCD_GRPC_KEEPALIVE_MIN_TIME="5s"
#ETCD_GRPC_KEEPALIVE_INTERVAL="2h0m0s"
#ETCD_GRPC_KEEPALIVE_TIMEOUT="20s"
#
#[Clustering]
#ETCD_INITIAL_ADVERTISE_PEER_URLS="http://localhost:2380"
ETCD_ADVERTISE_CLIENT_URLS="http://etcd:2379,http://etcd:4001" #通知客户端地址
#ETCD_DISCOVERY=""
3、启动etcd服务
[root@master ~]# systemctl start etcd.service
[root@master ~]# systemctl enable etcd.service
4、查看etcd服务是否启动成功
[root@master ~]# netstat -tnlp | grep -E "4001|2380"
tcp 0 0 127.0.0.1:2380 0.0.0.0:* LISTEN 22733/etcd
tcp6 0 0 :::4001 :::* LISTEN 22733/etcd
[root@master ~]# ps -ef | grep etcd
etcd 22733 1 1 4月17 ? 03:33:44 /usr/bin/etcd --name=master --data-dir=/var/lib/etcd/default.etcd --listen-client-urls=http://0.0.0.0:2379,http://0.0.0.0:4001
root 162335 1 0 4月22 ? 00:04:37 /usr/bin/flanneld -etcd-endpoints=http://etcd:2379 -etcd-prefix=/atomic.io/network
root 333684 333652 0 19:58 pts/2 00:00:00 vi /etc/etcd/etcd.conf
root 334263 333652 0 20:00 pts/2 00:00:00 vi /etc/etcd/etcd.conf
root 338219 333652 0 20:14 pts/2 00:00:00 grep --color=auto etcd
[root@master ~]# etcdctl -C http://etcd:4001 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://etcd:2379
cluster is healthy
[root@master ~]# etcdctl -C http://etcd:2379 cluster-health
member 8e9e05c52164694d is healthy: got healthy result from http://etcd:2379
cluster is healthy
[root@master ~]#
5、在etcd中配置flannel需要用到的键值
[root@master ~]# etcdctl mk /atomic.io/network/config '{ "Network": "172.18.0.0/16" }'
{ "Network": "172.18.0.0/16" }
flannel是所有宿主机需要安装的中间件,而且前文中提到的hosts文件一定要设置好。
1、安装flannel服务
yum install flannel -y
2、修改flannel配置文件
[root@master ~]# vim /etc/sysconfig/flanneld
# Flanneld configuration options
# etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD_ENDPOINTS="http://etcd:2379" #etcd客户端端口
# etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_PREFIX="/atomic.io/network" #这个参数的值需要是etcd保存的key值,不然flannel启动不了
# Any additional options that you want to pass
#FLANNEL_OPTIONS=""
flanneld配置文件中的FLANNEL_ETCD_PREFIX参数与上文中保存在etcd中的一个键值是相对应的,但要注意FLANNEL_ETCD_PREFIX参数的值没有后边的/config。
3、启动flanneld服务
[root@master ~]# systemctl start flanneld.service
[root@master ~]# systemctl enable flanneld.service
4、查看flanneld服务是否启动成功
[root@master ~]# ps -ef|grep flanneld
root 162335 1 0 4月22 ? 00:04:38 /usr/bin/flanneld -etcd-endpoints=http://etcd:2379 -etcd-prefix=/atomic.io/network
root 345092 333652 0 20:37 pts/2 00:00:00 vim /etc/sysconfig/flanneld
root 345496 333652 0 20:39 pts/2 00:00:00 vim /etc/sysconfig/flanneld
root 345545 333652 0 20:39 pts/2 00:00:00 vim /etc/sysconfig/flanneld
root 347078 333652 0 20:44 pts/2 00:00:00 vim /etc/sysconfig/flanneld
root 350147 333652 0 20:54 pts/2 00:00:00 grep --color=auto flanneld
[root@master ~]# ifconfig flannel0
flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1472
inet 172.18.92.0 netmask 255.255.0.0 destination 172.18.92.0
inet6 fe80::244d:d95c:a79f:af2d prefixlen 64 scopeid 0x20<link>
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC)
RX packets 2691026 bytes 142959067 (136.3 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2690704 bytes 152477868 (145.4 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
此时可看到flannel生成的flannel0网卡的IP是在etcd保存的网段内的,说明flannel网络配置完成。
安装启动flanneld后重启Docker服务,查看Docker服务生成的docker0网桥IP是否已经位于flanneld生成flannel0网卡的网段之中,比如flannel0网卡的IP是:172.18.92.0,docker0的IP是:172.18.92.1,则配置成功。如果IP地址的前三段有一段不同,则说明需要重新对docker0虚拟网桥进行配置,配置方法如下:
1、停掉Docker服务,修改docker.service配置文件
[root@master ~]# systemctl stop docker
[root@master ~]# vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=172.18.92.1/24
如上就是需要在docker.service文件中的ExecStart参数后添加–bip=172.18.92.1/24,bip的值请查看/run/flannel/subnet.env文件的FLANNEL_SUBNET值,这两个需要保持一致。
2、重启Docker
[root@master ~]# systemctl daemon-reload
[root@master ~]# systemctl restart docker
3、docker0是否配置成功
[root@master ~]# ifconfig flannel0
flannel0: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1472
inet 172.18.92.0 netmask 255.255.0.0 destination 172.18.92.0
inet6 fe80::244d:d95c:a79f:af2d prefixlen 64 scopeid 0x20<link>
unspec 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00 txqueuelen 500 (UNSPEC)
RX packets 2701666 bytes 143511551 (136.8 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 2701343 bytes 153069980 (145.9 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[root@master ~]# ifconfig docker0
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 172.18.92.1 netmask 255.255.255.0 broadcast 172.18.92.255
inet6 fe80::42:cff:fe75:d72 prefixlen 64 scopeid 0x20<link>
ether 02:42:0c:75:0d:72 txqueuelen 0 (Ethernet)
RX packets 4010425 bytes 230843638 (220.1 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 3057671 bytes 995135672 (949.0 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
如上docker0处于flannel0的网段内,说明配置已经成功。
如果以上步骤都已配置成功,此时就需要验证不通宿主机间容器是否通信了,验证方法就是在每个宿主机创建个容器,然后在容器中ping其他宿主机中容器的Ip,如果能够ping通,则说明已经完成。如果Ping不通也不要惊慌,因为可能是底层的iptables造成的,所以解决办法是在各宿主机上执行下面操作:
[root@master ~]# iptables -P INPUT ACCEPT
[root@master ~]# iptables -P FORWARD ACCEPT
[root@master ~]# iptables -F
[root@master ~]# iptables -L -n
以上只完成后再次测试容器间是否能够相互ping通,还是不可以的话就需要排查是否那个环节疏忽出现问题了。docker通过Flannel可以实现各容器间的相互通信,即宿主机和容器,容器和容器之间都能相互通信。
1、拉取centos:7.6.1810系统版本的镜像
[root@master ~]# docker pull centos:7.6.1810
2、创建容器
[root@master ~]# docker run -itd --name pre-centos --privileged=true -p 10022:22 centos:7.6.1810 init
我对该命令中的参数具体讲解下:
-i:以交互模式运行容器,通常与 -t 同时使用。
-t:为容器重新分配一个伪输入终端,通常与 -i 同时使用。
-d:后台运行容器,并返回容器ID,如果不加这个参数,当退出容器时,这个容器也就停止了。
–privileged:默认是false,我们这里改成了true,使用该参数容器中的root拥有真正的root权限,否则容器的root用户只是宿主机的一个普通用户权限,有些命令如:systemctl、service是使用不了的。
-p:就是映射端口的作用
还有一个问题需要注意,就是该命令后的init,加上它赋值给容器真正的root权限才会生效,如果使用/bin/bash是不会生效的。
3、进入创建好的容器
[root@master ~]# docker exec -it 1fc31e49daxc bash
1fc31e49daxc 参数为新创建容器的CONTAINER ID。
4、安装启动ssh
[root@1fc31e49daxc ~]# yum -y install openssh-server
[root@1fc31e49daxc ~]# systemctl start sshd
容器中安装配置ssh,就是希望外部工具能够通过容器映射宿主机的10022端口直接访问到容器里。
5、修改ssh配置文件
[root@1fc31e49daxc ~]# vi /etc/ssh/sshd_config
#配置信息去掉如下注释
Port 22ListenAddress 0.0.0.0
ListenAddress ::
permitrootlogin yes
UsePAM yes 改为 UsePAM no
GSSAPICleanupCredentials no 改为 GSSAPICleanupCredentials yes
UsePrivilegeSeparation sandbox 改为 UsePrivilegeSeparation no
6、重启ssh
[root@1fc31e49daxc ~]# /usr/sbin/sshd -D
7、设置linux容器的密码
[root@1fc31e49daxc ~]# passwd root
7、退出容器并将容器提交成镜像
[root@1fc31e49daxc ~]# exit
[root@master ~]# docker commit 1fc31e49daxc pre-centos:1.0
8、将镜像打成标签提交私有仓库
[root@master ~]# docker tag 8a1960ed7c4c 192.168.12.10:5000/yuliang/pre-centos:1.0
[root@master ~]# docker push 8a1960ed7c4c
8a1960ed7c4c 为IMAGE ID,修改标签后的镜像与提交的镜像IMAGE ID是一样的。
9、拉取私有仓库镜像
[root@slave1 ~]# docker pull 192.168.12.10:5000/yuliang/pre-centos:1.0
11、创建新的容器
[root@slave1 ~]# docker run -itd --name pre-centos --privileged=true -m 30G -p 10022:22 -p 3357:3306 -p 8024:8024 -p 8025:8025 192.168.12.10:5000/yuliang/pre-centos:1.0 /usr/sbin/init
其中有一个新的参数-m需要讲一下,其实它就是限制容器使用的内存,默认情况下容器使用的资源是根据宿主机来定的,宿主机有多少资源它就能够用多少资源。容器的磁盘空间除外,容器的磁盘空间是根据Docker存储数据路径所在宿主机的那个磁盘大小定的,也就是说Docker存储数据路径所在磁盘有100G,容器也只能够使用100G的磁盘。而内存和CPU在默认情况下宿主机有多少资源它就能够用多少资源。这个容器我使用了-m来限制容器只能使用30G内存。
(注:再次提醒事先规划好容器使用的端口在创建容器,如果容器已经创建成功后想添加端口请查看这篇文章:为已创建的容器添加映射端口)
12、测试容器是否正常运行
[root@slave1 ~]# docker ps
[root@slave1 ~]# docker exec -it a38321832cfd bash
13、查看容器信息
[root@slave1 ~]# docker inspect a38321832cfd
这条命令执行后显示的信息就是该a38321832cfd容器构建时候的全部信息,包括IP、端口映射等等。
14、访问容器
1、如果是在做过容器间通信的宿主机上连接访问可通过以下命令:
[root@master ~]# ssh [email protected]
注意使用的容器的22端口。
2、如果是在没有做过容器间通信的服务器上连接访问则需要通过以下命令:
[root@ecs-12 ~]# ssh -p 10022 [email protected]
3、如果是使用外部工具访问容器,需要用到也是宿主机的10022端口和宿主机的IP。
(注:上文参考地址:使用Docker虚拟化出一个Centos7操作系统)
以上关于如何做不同宿主机间容器通信和使用Docker创建虚拟机服务器我已经讲解的很清楚了,文中有些我是在网上找来的也添加了出处,在网上找来的内容我也有实际认认真真的研究过并且加以改进我才敢在我们的预生产环境使用。因为现在还没到真正的测试实验阶段,所以我不敢打包票说这些内容一定没有问题。后续我会将遇到的问题整理进这篇文章的,如果我没有添加则说明确实没有问题了。看到这篇文章的人发现文中有问题可随时给我留言。最后说一句话,有些事情看着很容易,实际做起来你会发现也很容易,但得需要你去做啊。