摘要
本文是《Kubernetes权威指南:从Docker到Kubernetes实践全接触》的实践篇,把该书的第一个案例在环境上完整跑起来,补充一些比较的信息,便于学习者参考。
实践内容
本示例是一个运行在Tomcat里的Web App,如图1.1所示,JSP页面通过JDBC直接访问MySQL数据库并展示数据。出于演示和简化的目的,只要程序正确连接到了数据库,就会自动完成对应的Table的创建与初始化数据的准备工作。所以,当我们通过浏览器访问此应用时,就会显示一个表格的页面,数据则来自数据库。
2.1.2.1.1 MySQL容器
(1)下载docker镜像
# 本案例有一个大坑,kubeguide/mysql-master:latest对应的是mysql8.0的版本,跟java无法匹配。
# 需要采用5.7的版本。
#docker pull kubeguide/mysql-master:latest
$ docker pull mysql:5.7
(2)创建编辑RC资源文件
mysql-rc.yaml:
apiVersion: v1
kind: ReplicationController
metadata:
name: mysql
spec:
replicas: 1
selector:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
以上YAML定义文件中的kind属性用来表明此资源对象的类型,比如这里的值为ReplicationController,表示这是一个RC;
在spec一节中是RC的相关属性定义,比如spec.selector是RC的Pod标签选择器,即监控和管理拥有这些标签的Pod实例,确保在当前集群中始终有且仅有replicas个Pod实例在运行,这里设置replicas=1,表示只能运行一个MySQL Pod实例。
当在集群中运行的Pod数量少于replicas时,RC会根据在spec.template一节中定义的Pod模板来生成一个新的Pod实例,spec.template.metadata. labels指定了该Pod的标签,需要特别注意的是:这里的labels必须匹配之前的spec.selector,否则此RC每创建一个无法匹配Label的Pod,就会不停地尝试创建新的Pod,陷入恶性循环中。
(3)发布到kubernetes集群中:
$ kubectl create -f mysql-rc.yaml
(4)查看资源创建情况
用kubectl命令查看刚刚创建的RC
$ kubectl get rc
NAME DESIRED CURRENT READY AGE
mysql 1 1 0 9s
查看Pod的创建情况时,可以运行下面的命令:
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-dcr5z 1/1 Running 0 114s 10.244.2.2 k8s-node1
可以看到其在k8s-node1节点运行。在k8s-node1节点,我们通过docker ps指令查看正在运行的容器:
$ docker ps |grep mysql
167e968bd621 mysql "docker-entrypoint.s…" 2 minutes ago Up 2 minutes k8s_mysql_mysql-dcr5z_default_55c116e6-aa2c-402e-87dd-2fcbc4877010_0
51ae0f275c3c k8s.gcr.io/pause:3.2 "/pause" 3 minutes ago Up 3 minutes k8s_POD_mysql-dcr5z_default_55c116e6-aa2c-402e-87dd-2fcbc4877010_0
(4)创建ubernetes Service
创建一个与之关联的Kubernetes Service——MySQL的定义文件,完整的内容和解释如下:
mysql-svc.yaml :
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
(5)运行kubectl命令,创建Service:
$ kubectl create -f mysql-svc.yaml
(6)查看创建的Service情况
运行kubectl命令查看刚刚创建的Service:
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 22h
mysql ClusterIP 10.107.219.146 3306/TCP 14s
可以发现,MySQL服务被分配了一个值为10.107.219.146的Cluster IP地址。随后,Kubernetes集群中其他新创建的Pod就可以通过Service的Cluster IP+端口号3306来连接和访问它了。通常,Cluster IP是在Service创建后由Kubernetes系统自动分配的,其他Pod无法预先知道某个Service的Cluster IP地址,因此需要一个服务发现机制来找到这个服务。为此,最初时,Kubernetes巧妙地使用了Linux环境变量(Environment Variable)来解决这个问题,后面会详细说明其机制。现在只需知道,根据Service的唯一名称,容器可以从环境变量中获取Service对应的Cluster IP地址和端口,从而发起TCP/IP连接请求。
(7)其他Kubectl命令测试
我们可以执行下述命令查看在集群中有多少个Node:
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready control-plane,master 22h v1.20.5
k8s-node1 Ready worker 14h v1.20.5
k8s-node2 Ready worker 22h v1.20
通过kubectl describe node
$ kubectl describe node k8s-master
可以用kubectl describepod xxxx来查看它的描述信息,以定位问题的成因:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-dcr5z 1/1 Running 0 7m3s
$ kubectl describe pod mysql-dcr5z
Name: mysql-dcr5z
Namespace: default
Priority: 0
Node: k8s-node1/192.168.0.3
Start Time: Tue, 17 Aug 2021 11:39:20 +0800
Labels: app=mysql
Annotations:
Status: Running
...
可以通过执行kubectl scale命令来一键完成:
$ kubectl scale rc mysql --replicas=3
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-4gnp5 1/1 Running 0 60s 10.244.2.4 k8s-node1
mysql-7pv7f 1/1 Running 0 60s 10.244.2.3 k8s-node1
mysql-dcr5z 1/1 Running 0 10m 10.244.2.2 k8s-node1
可知,这3个pods都被k8s自动分配到k8s-node1节点了。
需要注意的是,删除RC并不会影响通过该RC已创建好的Pod。为了删除所有Pod,可以设置replicas的值为0,然后更新该RC。另外,kubectl提供了stop和delete命令来一次性删除RC和RC控制的全部Pod。
2.1.2.2.2 Tomcat应用
(1)下载镜像:
$ docker search kubeguide
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
kubeguide/redis-master redis-master with "Hello World!" 33
kubeguide/tomcat-app Tomcat image for Chapter 1 30 kubeguide/guestbook-php-frontend Guestbook PHP website 25
kubeguide/guestbook-redis-slave Guestbook redis slave 23
kubeguide/mysql-master mysql master 6 [OK]
kubeguide/hadoop 5
kubeguide/centos7-ansible ansible with sshpass tool installed and sshk… 4
$ docker pull kubeguide/tomcat-app:v2
(2)创建对应的RC文件myweb-rc.yaml:
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 3
selector:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: myweb
image: kubeguide/tomcat-app:v1
ports:
- containerPort: 8080
env:
- name: MYSQL_SERVICE_HOST
value: 'mysql'
- name: MYSQL_SERVICE_PORT
value: '3306'
注意:在Tomcat容器内,应用将使用环境变量MYSQL_SERVICE_HOST的值连接MySQL服务。更安全可靠的用法是使用服务的名称mysql。
(3)发布到集群中:
$ kubectl create -f myweb-rc.yaml
(4)创建对应的Service(myweb-svc.yaml):
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8080
nodePort: 31330
selector:
app: myweb
type=NodePort和nodePort=31330的两个属性表明此Service开启了NodePort方式的外网访问模式。在Kubernetes集群之外,比如在本机的浏览器里,可以通过31330这个端口访问myweb(对应到8080的虚端口上)。
采用netstat -nltp|grep 31330 命令来确认在service运行前,端口是否已被占用。
(5)运行kubectl create命令进行创建:
$ kubectl create -f myweb-svc.yaml
注意:记得防火墙打开31330端口。
(6)运行kubectl命令,查看创建的Service:
$ kubectl get services -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kubernetes ClusterIP 10.96.0.1 443/TCP 23h
mysql ClusterIP 10.107.219.146 3306/TCP 12m app=mysql
myweb NodePort 10.109.12.156 8080:31330/TCP 36s app=myweb
查看pods情况:
$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mysql-4gnp5 1/1 Running 0 8m5s 10.244.2.4 k8s-node1
mysql-7pv7f 1/1 Running 0 8m5s 10.244.2.3 k8s-node1
mysql-dcr5z 1/1 Running 0 17m 10.244.2.2 k8s-node1
myweb-b2kfl 1/1 Running 0 3m46s 10.244.1.4 k8s-node2
myweb-nblv5 1/1 Running 0 3m46s 10.244.1.3 k8s-node2
myweb-rxws6 1/1 Running 0 3m46s 10.244.1.2 k8s-node2
可知,有3个mysql分布在k8s-node1上,3个myweb分布在k8s-node2上。
2.1.2.2.3 测试
请确认对应的网络安全组已打开端口31330. 例如在windows下采用 psping 114.67.107.240:31330 确认端口已打开。
在浏览器输入http://虚拟机IP(外网):31330/demo/。比如虚拟机IP为114.67.107.240 (可以通过#ip a命令进行查询),在浏览器里输入地址http://114.67.107.240:31330/demo/后,可以看到如图1.2所示的网页界面。
问题描述:
先测试下服务是否联通。
# 测试获取web服务是否已启动。
curl http://192.168.0.3:31330/demo/
...
Error:com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
此时表示无法跟MySql服务器链接。查看pods的打印信息:
p# kubectl get pods
NAME READY STATUS RESTARTS AGE
mysql-ppzgm 1/1 Running 0 48m
myweb-87j84 1/1 Running 0 28m
myweb-drqg9 1/1 Running 0 28m
myweb-dtfdt 1/1 Running 0 28m
myweb-gcfh5 1/1 Running 0 28m
myweb-kzcxc 1/1 Running 0 28m
root@k8s-master:/data/k8s/JavaWebApp# kubectl logs myweb-87j84
30-Jul-2021 06:33:35.868 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version: Apache Tomcat/8.0.35
30-Jul-2021 06:33:35.869 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: May 11 2016 21:57:08 UTC
...
30-Jul-2021 06:33:45.065 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 746 ms
Connecting to database...
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Could not create connection to database server.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
... 39 more
Goodbye!
说明:
从github issue:https://github.com/kubeguide/K8sDefinitiveGuide-V4-Sourcecode/issues/7 找到原因:
1.3.3 myweb-rc.yaml 镜像用的mysql-connector-java-5.1.37.jar,不兼容1.3.2 mysql-rc.yaml默认镜像 mysql 8,导致连接失败,需要将1.3.2 mysql-rc.yaml 指定mysql5.7版本。
# 需要采用5.7的版本。
docker pull mysql:5.7
# 删除已运行的RC
kubectl delete -f mysql-rc.yaml
# 删除已运行的RC
kubectl delete -f myweb-svc.yaml
参考
(1)本书示例中的Docker镜像下载地址为:
https://hub.docker.com/u/kubeguide/
(2)https://github.com/kubeguide/K8sDefinitiveGuide-V4-Sourcecode