使用 Kubernetes 部署分布式集群
在一个实际的大型系统中,微服务架构可能由成千上万个服务组成。在发布一个系统时,如果都单纯地通过打包上传,再发布,工作量无疑是巨大的,也是不可取的。我们现在已经知道了可以通过Jenkins 帮我们自动化完成发布任务。但是一个Java应用其实是比较占用资源的,每个服务都发布到物理宿主机上面,资源开销是巨大的,而且每扩展一台服务器都需要重复部署相同的软件。
容器技术的出现带给了我们新的思路。我们可以将服务打包成镜像,放到容器中,通过容器来运行服务,这样可以很方便地进行分布式管理,同样的服务也可以很方便地进行水平扩展。
Docker是容器技术方面的佼佼者,它是一个开源容器,而Kubernetes (以下简称K8S)是一个分布式集群方案的平台,它和 Docker就是天生的一对。通过K8S和Docker的配合,我们很容易搭建分布式集群环境。下面,我们就来看一下Docker和 K8S的诱人之处。
Docker介绍
Docker是一个开源的容器引擎,我们可以将任何应用移植到Docker容器中,然后发布到任何Linux服务器上,也可以实现虚拟化。在容器技术出现以前,如果我们想要将应用发布到多台物理主机上,需要在每台物理主机上都部署相同的环境;而利用容器技术,我们只需要将环境和应用放到容器中,就可以很方便地发布到任意物理主机上。
由于Docker 底层是基于LXC(即Linux Container )实现的虚拟化技术,所以Docker只能运行在Linux内核操作系统中。尽管macOS基于Unix,Docker依然提供了对macOS的支持,因为 macOS版的Docker采用了虚拟机技术。
Docker安装
Docker的安装非常简单,只需要运行如下命令即可:
代码语言:javascript复制yum install docker -y
安装完成后,运行下面的命令可以启动Docker并设置开机启动:
代码语言:javascript复制chkconfig docker on
service docker start
执行下面的命令可以验证安装是否正确:
代码语言:javascript复制docker run hello-world
如安装正确,你将看到以下信息:
代码语言:javascript复制Unable to find image "hello-world: latest" locally
Trying to pull repository docker.io/library/hello-world ...latest: Pulling from docker.io/library/hello-world
9db2ca6ccaee: Pull complete
Digest: sha256:4b8ff392a12ed9ea17784bd3c9a8b1fa3299cac44aca35a85c90c5e3c7afacdcStatus: Downloaded newer image for docker.io/hello-world:latest
WARNING: IPv4 forwarding is disabled. Networking will not work.
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message,Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
( amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client,which sent it
to your terminal.
To try something more ambitious,you can run an Ubuntu container with:$ docker run -it ubuntu bash
Share images, automate workflows,and more with a free Docker ID:
https://hub. docker.com/
For more examples and ideas, visit:
https://docs.docker.com/engine/userguide/
Docker 镜像
前面讲Docker概念时就提到了,我们可以将环境容器打包到Docker容器中执行,而在容器中执行的载体就是镜像。
1.拉取镜像
通过命令docker pull就可以从 Docker仓库中拉取镜像,Docker 的默认仓库为Docker Hub。那么如何配置国内加速镜像?
(1)修改/etc/docker/daemon.json,加入以下内容R:
代码语言:javascript复制{ "registry-mirrors": ["https : / /9cpn8tt6.mirror.aliyuncs.com"] ]}
(2)重启 Docker。
我们可以举个例子,拉取Java镜像:
代码语言:javascript复制docker pull java
执行该命令后,Docker 会默认从Docker Hub下载最新的Java镜像。由于网络原因,可能需要等一段时间。当出现如下信息后,说明镜像拉取完成:
代码语言:javascript复制Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8cOe9d Status:
Downloaded newer image for docker.io/java: latest
我们还可以指定镜像版本,如:
代码语言:javascript复制docker pull java:7
这样Docker就会下载版本为7的Java镜像。
2.创建镜像
实际生产中,我们要将环境应用部署到 Docker 容器中,这就需要创建它的镜像。镜像的创建主要有3种方式:基于容器、基于本地模板导入和基于Dockerfile文件。
本书主要讲解基于Dockerfile创建镜像,因为实际生产中,我们大多数是通过 Dockerfile来构建应用镜像的。
我们以Nginx为例,从 Docker Hub拉取Nignx镜像并改变首页内容。(1)编写文件命名为Dockerfile,输入如下内容:
代码语言:javascript复制FROM nginx
RUN echo '<h1>Nginx Hello world!</h1>' >/usr/share/nginx/html/index.html
(2)在 Dockerfile文件所在目录执行以下命令:
代码语言:javascript复制docker build -t nginx .
等待一段时间后,看到以下内容:
代码语言:javascript复制Successfully built a3c8cee6148b
说明镜像创建成功。通过docker images命令可以查看刚才创建的镜像:
[root@localhost ~]# docker images
代码语言:javascript复制REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest b8cd84ec56e7 About an hour ago 109MB
3.查看及搜索镜像
查看镜像非常简单,执行以下命令即可:
代码语言:javascript复制docker images
执行后会看到镜像列表,如:
此外,通过命令docker search可以搜索指定镜像,如:
代码语言:javascript复制docker search java
我们将看到Docker Hub包含的所有名为Java的镜像列表:
4.删除镜像
我们可以通过镜像名或镜像ID删除镜像,基本命令为docker rmi。
例如删除java:7的镜像:
代码语言:javascript复制docker rmi docker.io/java:7
或者:
代码语言:javascript复制docker rmi d23bdf5b1b1b
其中 d23bdf5b1b1b为要删除的镜像ID,镜像ID通过docker images获取。
Docker容器
Docker 的另一大核心便是容器,前面我们讲过,创建或拉取的镜像需要放到容器里面才能运行,那么怎么将镜像运行到容器里呢?
1.创建容器和启动容器
容器的创建和启动很简单,通过docker run命令即可,如果输入的容器名称不存在,会自动创建一个容器。如果存在,就会直接启动该容器。例如启动运行上一节构建的Nginx镜像:
代码语言:javascript复制docker run -d -p 91:80 nginx
其中-d表示后台运行,-p用于指定容器运行端口,第一个端口为物理主机的端口,第二个端口为容器的端口。因为外部访问只能访问物理主机的端口,所以我们需要指定它。
启动完成后,通过浏览器访问地址“IP:91”可以看到如图15-1所示的界面。
我们还可以通过docker ps命令查看启动的容器:
此外,通过docker ps -a命令能够查看所有的容器。
2.进入容器和删除容器
容器创建后可以通过docker exec命令进入容器,如:
代码语言:javascript复制docker exec -it 806d1021575d /bin/bash
删除容器也很简单,通过命令docker rm即可:
代码语言:javascript复制docker rm 806d1021575d
其中,806d1021575d为容器ID。注意,启动中的容器是无法删除的,如果提示删除失败,需要先通过命令停止容器: docker stop容器ID,相反,启动容器的命令为: docker start容器ID。
K8S集群环境搭建
从本节开始,我们将进入一个非常神奇的世界,利用K8S快速搭建分布式集群环境,并实现分布式系统的部署。K8S全称Kubernetes,是谷歌开源的一套用于搭建分布式集群应用环境的平台,它基于Docker,和 Docker配合可以很方便地部署分布式应用。在进行K8S分布式集群部署之前,首先应先搭建集群环境。
环境准备
本文集群使用单台虚拟机做演示,即将Master和Node都部署到一台机器上,实际中可以由多台服务器做集群。虚拟机在上一章已经安装完成,采用CentOS 64位操作系统,内存为2GB。由于我们是在个人计算机上安装Linux虚拟机,资源有限,所以用一台虚拟机模拟集群环境,实际中的集群环境搭建和单机模拟是一样的操作。
下面就是本文虚拟机的环境配置。
- IP:172.20.10.2。
- 操作系统:CentOS7.4。
- 内存:2GB。
集群搭建
首先,我们需要安装Docker(前面已经安装了Docker,此处省略)。然后,我们来安装etcdR,执行以下命令:
代码语言:javascript复制yum install etcd -y
启动etcd:
代码语言:javascript复制systemctl start etcd
systemctl enable etcd
输入如下命令查看etcd健康状况:
代码语言:javascript复制etcdctl -C http://localhost:2379 cluster-health
如果出现以下内容,说明etcd没有问题:
代码语言:javascript复制member 8e9e05c52164694d is healthy: got healthy result from http://localhost:2379
cluster is healthy
接着安装K8S,执行命令:
代码语言:javascript复制yum install kubernetes -y
安装好后,编辑文件/etc/kubernetes/apiserver,将KUBE_ADMISSION_CONTROL后面的ServiceAccount去掉,如:
代码语言:javascript复制KUBE_ADMISSION_CONTROL="--admission-control=NamespaceLifecycle,NamespaceExists,
LimitRanger,SecurityContextDeny, ResourceQuota"
接下来分别启动以下程序(Master ):
代码语言:javascript复制systemct1 start kube-apiserver
systemctl enable kube-apiserver
systemctl start kube-controller-manager
systemctl enable kube-controller-manager
systemctl start kube-scheduler
systemctl enable kube-scheduler
最后,启动Node节点的程序:
代码语言:javascript复制systemctl start kubelet
systemctl enable kubelet
systemctl start kube-proxy
systemct1 enable kube-proxy
这样一个简单的K8S集群环境就已经搭建完成了,我们可以运行以下命令来查看集群状态:
代码语言:javascript复制[root@localhost ~]# kubectl get no
NAME STATUS AGE
127.0.8.1 Ready 1h
该集群环境目前还不能很好地工作,因为需要对集群中 pod的网络进行统一管理,所以需要创建覆盖网络flannel。
(1)安装flannel:
代码语言:javascript复制yum install flannel -y
(2)编辑文件/etc/sysconfig/flanneld,增加以下代码:
代码语言:javascript复制FLANNEL_OPTIONS=" --logtostderr=false --log_dir=/var/log/k8s/flannel/--etcd-prefix=/atomic.io/
network --etcd-endpoints=http:/ / localhost:2379 --iface=ens33"
其中--iface对应的是网卡的名字。
(3)配置etcd中关于flanneld 的 key
因为flannel使用etcd进行配置来保证多个flannel实例之间配置的一致性,所以需要在etcd上进行如下配置:
代码语言:javascript复制etcdctl mk /atomic.io/network/config '{"Network" : "10.0.0.0/16" }'
/atomic.io/network/config这个key与上文/etc/sysconfig/flannel中的配置项是相对应的,错误的话启动就会出错。
Network是配置网段,不能和物理机IP冲突,可以任意定义,尽量避开物理机IP段。
(4)启动修改后的flannel,并依次重启Docker和 Kubernetes :
代码语言:javascript复制systemctl enable flanneld
systemctl start flanneldservice docker restart
systemctl restart kube-apiserver
systemctl restart kube-controller-managersystemctl restart kube-scheduler
systemct1 enable flanneld
systemctl start flanneldservice docker restartsystemctl restart kubeletsystemctl restart kube-proxy
这样我们将应用部署到Docker容器中时,就可以通过物理IP访问到容器了。
分布式应用部署
本节中,我们就可以开始部署一个分布式应用了。(实际中的集群是一个Master对应多个Node,通过K8S会通过Master 将 Docker镜像随机分配到不同的Node 中。)
接下来,以注册中心register为例来讲述K8S的应用部署。
1.构建应用镜像
首先将register打包并上传到服务器上,并编写Dockerfile:
代码语言:javascript复制#下载Java 8的镜像FROM java:8
#将本地文件挂到到/tmp目录VOLUME /tmp
#复制文件到容器
ADD register.jar /register.jar#暴露8101端口
EXPOSE 8101
#配置启动容器后执行的命令
ENTRYPOINT ["java", "-jar", " /register.jar"]
然后通过docker build命令创建镜像:
代码语言:javascript复制docker build -t register .
如果构建成功,你将看到以下内容:
代码语言:javascript复制Sending build context to Docker daemon 1.019 GB
Step 1/5 : FROM java: 8
--->d23bdf5b1b1b
Step 2/5: VOLUME /tmp
--->[Warning]IPv4 forwarding is disabled. Networking will not work.--- > Running in 63ddece53c5e
--->015fedfaf379
Removing intermediate container 63ddece53c5eStep 3/5 :ADD register.jar /register.jar---> aaed606aa239
Removing intermediate container 940f70c5bddeStep 4/5 :EXPOSE 8101
--->[warning] IPv4 forwarding is disabled. Networking will not work.---> Running in ca6e30c82996
--->22856e75b953
Removing intermediate container ca6e30c82996Step 5/5 :ENTRYPOINT java -jar /register.jar
--->[Warning]IPv4 forwarding is disabled.Networking will not work.---> Running in60636581cda9
--->901d6123e0b7
Removing intermediate container 60636581cda9
Successfully built 901d6123e8b7
这时执行命令docker images就将看到刚才构建的镜像,如:
代码语言:javascript复制[root@localhost ~]#docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
register latest 9bc4a8542033 About an hour ago 712 MB
2.利用K8S部署应用
(1)创建rc文件register-rc.yaml :
代码语言:javascript复制apiVersion: v1
kind: ReplicationControllermetadata:
name: registerspec:
#节点数,设置为多个可以实现负载均衡效果replicas: 1
selector:
app:registertemplate:
metadata:
labels:
app: register
spec:
containers:
- name: register#镜像名
image: register
#本地有镜像就不会去仓库拉取
imagePullPolicy: IfNotPresentports:
- containerPort: 8101
在上述文件中,image为要拉取的镜像名,意为拉取register镜像; imagePullPolicy为镜像拉取策略,可选值有 Always(每次都从仓库拉取一次镜像,无论镜像是否存在)、Never(不拉取镜像,无论镜像是否存在)、工fNotPresent(本地镜像不存在时才会进行拉取); containerPort为容器内部启动端口。
(2)执行以下命令创建pod:
代码语言:javascript复制[root@localhost ~]# kubectl create -f register-rc.yaml
replicationcontroller "register" created
(3)创建成功后,我们可以查看pod:
代码语言:javascript复制[root@localhost ~]#kubectl get po
NAME READY STATUS RESTARTS AGE
register-lsk2g 0/1 containerCreating 0 3s
ContainerCreating 提示正在创建中,这时运行命令kubectl describe po register-lsk2g可以查看创建日志:
读者请注意看加粗字体部分,提示redhat-cat.crt不存在,我们先通过ll命令查看下该文件:
代码语言:javascript复制[ root@MiNiFi-R3-srV ~]# ll /etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crtlrwxrwXrwx 1 root root 27 7月 31 22:53
/etc/docker/certs.d/registry.access.redhat.com/redhat-ca.crt ->
/etc/rhsm/ca/redhat-uep.pem
可以发现该文件是个链接文件,它指向的是 /etc/rhsm/ca/redhat-uep.pem,而这个文件发现确实不存在,那这个文件又是怎么来的呢?答案就在这个路径里,我们需要安装rhsm这个软件,执行命令安装:
代码语言:javascript复制yum install *rhsm* -y
安装完成后,执行1l命令查看该文件是否存在:
[ root@MiwiFi-R3-srv ~]# ll /etc/rhsm/ca/redhat-uep.pemls:无法访问/etc/rhsm/ca/redhat-uep.pem:没有那个文件或目录
我们发现,依然没有该文件。不过没关系,我们可以手动创建:
touch /etc/rhsm/ca/redhat-uep.pem
执行完以上操作后,我们先将rc删除,再创建:
[root@MiwiFi-R3-srv ~]# kubectl delete rc registerreplicationcontroller "register" deleted
[root@MiwiFi-R3-srv ~]# kubectl create -f register-rc.yamlreplicationcontroller "register" created
等待一段时间后,重新查看po,我们发现已经成功启动:
代码语言:javascript复制[root@MiwiFi-R3-srv ~]# kubectl get po
NAME READY STATUS RESTARTS AGE
register-hdmxs 1/1 Running 0 1m
这时,我们还无法通过局域网访问应用,还需要创建服务:
(1)创建文件register-svc.yaml :
代码语言:javascript复制apiVersion : v1
kind: Servicemetadata:
name: registerspec:
type: NodePortports:
- port: 8101
targetPort: 8101nodePort: 30001selector:
app: register
在上述文件中,nodePort为节点暴露给外部的端口,即外部是通过该端口访问容器的,端口范围为30000~32767,否则无法创建服务;targetPort为目标端口,即外部通过nodePort访问容器内部开启的哪个端口.
(2)执行命令以创建服务:
代码语言:javascript复制[root@MiwiFi-R3-srv ~]# kubectl create -f register-svc.yaml
service "register" created
(3)我们可以查看刚才创建的服务:
代码语言:javascript复制[root@localhost ~]#kubectl get svc
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes 10.254.0.1 <none> 443/TCP 1h
register 10.254.68.212 <nodes> 8101: 30001/TCP 1h
这时,我们就可以通过172.20.10.2:30001访问应用了,如图15-2所示。
如果访问不到,需要关闭防火墙:
代码语言:javascript复制systemctl stop firewalld
iptables -P FORWARD ACCEPT
至此,通过 K8S部署应用就大功告成了。在实际的生产环境中,可能会有一个 Master管理多个Node,和本章讲述的原理一样,只是对应在不同机器上而已。通过 kubectl创建pod和 service,Master会随机分配到不同服务器上,通过K8S来部署分布式应用就变得非常简单。
通过本章的一系列操作,我们可以利用Jenkins实现系统的自动化部署,结合上一章的操作步骤,将本章讲解的K8S发布步骤编写成一个脚本,利用Jenkins自动执行脚本就能完成系统的自动化部署。
小结
通过本章的学习,读者可以了解到 Docker K8S搭建集群环境的全过程,亦可独立完成集群环境的搭建,并能利用K8S部署微服务应用。
本文给大家讲解的内容是springcloud实战:使用Kubernetes部署分布式集群
- 觉得文章不错的朋友可以转发此文关注小编;
- 感谢大家的支持!
本文就是愿天堂没有BUG给大家分享的内容,大家有收获的话可以分享下,想学习更多的话可以到微信公众号里找我,我等你哦。