写在前面

接下来我们尝试在完整集群上部署微服务,首先来复习之前的一些命令以及学习一个新的命令,了解这些对于学习和熟悉Kubernetes有非常大的帮助。

新的命令

首先查看一下当前其他节点的运行状态信息,可以看到它们都是Ready状态:

1
2
3
4
[root@server02 ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
192.168.51.121 Ready <none> 4h v1.9.0
192.168.51.123 Ready <none> 4h v1.9.0

再来查看一下services,可以看到这里有一个Kubernetes默认的services:

1
2
3
[root@server02 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 1d

再来查看一下pods,正常应该就是没有的:

1
2
[root@server02 ~]# kubectl get pods
No resources found.

接下来我们尝试通过kubectl run命令在k8s集群中部署一个命名为kubernetes-bootcamp的应用,Docker镜像通过--image指定,--port设置应用对外服务的端口,使用的命令如下:

1
2
[root@server02 ~]# kubectl run kubernetes-bootcamp --image=jocatalin/kubernetes-bootcamp:v1 --port=8080
deployment "kubernetes-bootcamp" created

然后我们查看一下当前所有的deployments:

1
2
3
[root@server02 ~]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1 1 1 1 41s

才是再来查看一下pods,可以发现这里就创建了一个pods:

1
2
3
[root@server02 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-6b7849c495-ssf78 1/1 Running 0 1m

然后我们尝试查看一下某个pods中的运行日志信息,可使用kubectl logs +pods名称命令来进行查看:

1
2
[root@server02 ~]# kubectl logs kubernetes-bootcamp-6b7849c495-ssf78
Kubernetes Bootcamp App Started At: 2021-09-04T13:12:56.352Z | Running On: kubernetes-bootcamp-6b7849c495-ssf78

也可以借鉴docker logs命令,在上述kubectl logs命令后面添加-f参数来跟踪日志,这样一旦有刷新日志就会实时的刷新出来:

1
[root@server02 ~]# kubectl logs kubernetes-bootcamp-6b7849c495-ssf78 -f

我们再来查看一下这个pods的详细信息,如下所示:

1
[root@server02 ~]# kubectl describe pods kubernetes-bootcamp-6b7849c495-ssf78

可以看到它多了一个Mounts的配置信息,值为/var/run/secrets/kubernetes.io/serviceaccount,然后我们可以使用类似于docker中的exec命令来以终端方式进入到该pods中:

1
2
3
4
5
6
7
8
9
10
[root@server02 ~]# kubectl exec -it kubernetes-bootcamp-6b7849c495-ssf78 bash
root@kubernetes-bootcamp-6b7849c495-ssf78:/# ls -l /var/run/secrets/kubernetes.io/serviceaccount
total 0
lrwxrwxrwx 1 root root 13 Sep 4 13:12 ca.crt -> ..data/ca.crt
lrwxrwxrwx 1 root root 16 Sep 4 13:12 namespace -> ..data/namespace
lrwxrwxrwx 1 root root 12 Sep 4 13:12 token -> ..data/token
root@kubernetes-bootcamp-6b7849c495-ssf78:/# cd /var/run/secrets/kubernetes.io/serviceaccount
root@kubernetes-bootcamp-6b7849c495-ssf78:/var/run/secrets/kubernetes.io/serviceaccount# ls
ca.crt namespace token
root@kubernetes-bootcamp-6b7849c495-ssf78:/var/run/secrets/kubernetes.io/serviceaccount#

可以看到这个/var/run/secrets/kubernetes.io/serviceaccount其实是一个目录,里面有三个文件,ca.crt是根证书文件,namespace的值为default,以及一个token文件。这些值是怎么来的呢?通过前面的学习可以知道它是与serviceaccount相关联的值。接下来我们看看当前是否存在serviceaccount,使用命令如下:

1
2
3
[root@server02 ~]# kubectl get serviceaccount
NAME SECRETS AGE
default 1 1d

也可以使用简写命令:

1
2
3
[root@server02 ~]# kubectl get sa
NAME SECRETS AGE
default 1 1d

可以看到这里有一个默认的名为default的namespace,可以使用如下命令来查看它的详细信息,其中-o指令用于指定输出的格式,这里我们让信息以yaml格式进行输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@server02 ~]# kubectl get sa -o yaml
apiVersion: v1
items:
- apiVersion: v1
kind: ServiceAccount
metadata:
creationTimestamp: 2022-09-03T10:56:39Z
name: default
namespace: default
resourceVersion: "142"
selfLink: /api/v1/namespaces/default/serviceaccounts/default
uid: 16487d73-2b77-11ed-85ff-000c29e3dc2b
secrets:
- name: default-token-tq5m2
kind: List
metadata:
resourceVersion: ""
selfLink: ""

你要是以json格式进行输出,这也是可以的。可以看到上面有一个名为secrets.name的配置项,它的值为default-token-tq5m2。开发者也可以使用如下命令查看一下当前所有的secrets:

1
2
3
[root@server02 ~]# kubectl get secrets
NAME TYPE DATA AGE
default-token-tq5m2 kubernetes.io/service-account-token 3 1d

然后我们查看一下这个secrets的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@server02 ~]# kubectl get secrets -o yaml
apiVersion: v1
items:
- apiVersion: v1
data:
ca.crt: ca.crt文件的内容
namespace: ZGVmYXVsdA==
token: token的值
kind: Secret
metadata:
annotations:
kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: 16487d73-2b77-11ed-85ff-000c29e3dc2b
creationTimestamp: 2021-09-03T10:56:39Z
name: default-token-tq5m2
namespace: default
resourceVersion: "139"
selfLink: /api/v1/namespaces/default/secrets/default-token-tq5m2
uid: 164a148f-2b77-11ed-85ff-000c29e3dc2b
type: kubernetes.io/service-account-token
kind: List
metadata:
resourceVersion: ""
selfLink: ""

这其实就和我们之前在/var/run/secrets/kubernetes.io/serviceaccount目录中所看到的三个文件是一一对应的,相当于将这个secret赋给了每一个pod。这就说明如果APIServer的kube-apiserver.service文件中的admission-control配置项,如果配置了ServiceAccount,那么它就会在default这个namespace下创建一个默认的serviceaccount,然后每个pod在启动的时候会将这个service中的secret以文件的形式挂载到pod中。有了这些挂载的文件之后,我们的pod就可以通过https的方式去访问APIServer,也就是说让pod可以通过APIServer的认证。

接下来继续学习Kubernetes,回到/home/envy/services目录,可以看到里面有三个文件,这是之前我们创建的三个用于配置对应信息的文件:

1
2
3
4
[root@server02 services]# ls -l /home/envy/services/
-rw-r--r-- 1 root root 274 8月 31 21:51 nginx-deployment.yaml
-rw-r--r-- 1 root root 151 8月 31 21:24 nginx-pod.yaml
-rw-r--r-- 1 root root 174 9月 2 21:47 nginx-service.yaml

之前我们创建对应的deployment、pod和service时,使用了kubectl create命令,接下来我们尝试使用新的命令kubectl apply。我们利用kubectl apply来创建一个pod:

1
2
[root@server02 services]# kubectl apply -f nginx-pod.yaml 
pod "nginx" created

再来看一下当前所有的pod:

1
2
3
4
[root@server02 services]# kubectl get pods
NAME READY STATUS RESTARTS AGE
kubernetes-bootcamp-6b7849c495-ssf78 1/1 Running 0 1h
nginx 1/1 Running 0 4m

然后查看一下名为nginx的pod的详细信息:

1
[root@server02 services]# kubectl describe pods nginx

可以看到相比于kubectl create命令,它多了一个Annotations,里面记录的是最近一次的修改,会将修改的内容添加到里面,你会发现这个命令并没有将完整的修改内容都显示全,如果你想显示完全,可以使用如下命令:

1
2
3
4
5
[root@server02 ~]# kubectl get pods nginx -o json
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.7.9\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80}]}]}}\n"
},
......

可以看到这里面是我们上一次应用的完整配置,它起到了一个记录的功能,会将最近一次文件的配置内容放到annotations中进行存储,这样当应用出现问题的时候,它作为应用回滚的依据。还有一个区别,就是在使用kubectl create命令的时候,如果应用已经存在,那么必须将该应用删除,才能重新创建。而kubectl apply命令并不会重新去创建应用,而是会在之前应用的基础上进行修改。

举个例子,我们想创建一个基于nginx1.13版本的nginx-pod,此时该如何操作呢?可以使用kubectl apply命令,首先将nginx-pod.yaml文件中的nginx镜像版本由1.7.9修改为1.13:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.13
ports:
- containerPort: 80

接着执行如下命令来创建nginx-pod:

1
2
[root@server02 services]# kubectl apply -f nginx-pod.yaml 
pod "nginx" configured

然后使用如下命令来查看一下是否真的更新成功:

1
2
3
4
5
6
[root@server02 services]# kubectl get pods nginx -o json
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"annotations\":{},\"name\":\"nginx\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"image\":\"nginx:1.13\",\"name\":\"nginx\",\"ports\":[{\"containerPort\":80}]}]}}\n"
},
......

当然了,更新镜像还有之前使用的方式:

1
2
[root@server02 services]# kubectl set image pods nginx nginx=nginx:1.7.9
pod "nginx" image updated

再来看一下容器中的镜像,可以看到此时容器中的镜像已经变成1.7.9,但是Annotations中显示的镜像版本还是1.13,这是因为这个Annotations中始终保持的是配置文件中最新的配置,而无论开发者使用命令做任何修改,Annotations中始终都不会进行显示,这里只会显示配置文件中的修改。

接下来我们使用kubectl apply来创建nginx-deployment和nginx-service这两个:

1
2
3
4
[root@server02 services]# kubectl apply -f nginx-deployment.yaml 
deployment "nginx-deployment" created
[root@server02 services]# kubectl apply -f nginx-service.yaml
service "nginx-service" created

接下来我们尝试使用NodeIP+NodePort方式来访问其他的Node节点,这是可以访问的:

1
2
3
4
[root@server02 services]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 2d
nginx-service NodePort 10.68.199.24 <none> 8080:20000/TCP 4m

然后我们使用另一种方式,就是通过进入到容器中使用CLUSTER_IP+ServicePort这一方式来访问。由于这里需要进入到一个容器中,如果现在没有容器,可以使用kubectl run busybox --rm=true --image=busybox --restart=Never --tty -i来启动一个busybox镜像,这个镜像是专门用于测试,它启动完之后提供一些命令,就相当于运行在这个集群中的一个沙盒。 --rm=true参数表示在结束的时候会自动删除,--image=busybox用于指定镜像为busybox,--restart=Never表示重启方案,此处设置从来不重启,即退出后就不重启。--tty表示开启一个伪终端, -i表示获取到标准输入。

1
2
3
4
5
6
[root@server02 services]# kubectl run busybox --rm=true --image=busybox --restart=Never --tty -i
If you don't see a command prompt, try pressing enter.
/ #
/ #
/ # ls
bin dev etc home proc root sys tmp usr var

这样我们就进入了这个容器,我们可以使用wget命令,将wget输出定位到标准输出中,然后我们使用CLUSTER_IP+ServicePort这一方式来访问:

1
/ # wget -qO - 10.68.199.24:8080

如果出现nginx相关的页面信息,这就说明kube-proxy是正常的,然后我们将上面的IP修改为services的命令看看是否也能成功访问:

1
/ # wget -qO - nginx-service:8080

如果也出现nginx相关的页面信息,这就说明kube-dns也是正常的。

微服务项目部署

思路分析

  • 消息服务:message-service
  • 课程dubbo服务:course-dubbo-service
  • 课程web服务:course-edge-service
  • 用户thrift服务:user-thrift-service
  • 用户web服务:user-edge-service
  • API网关:api-gateway

如果将它们放到kunernetes集群中运行,我们需要考虑哪些问题呢?
(1)哪些服务适合单独成一个pod?哪些服务适合在一个pod中?
在Kubernetes中,最小的单元是pod,一个pod中可以包含一个或者多个容器,因此我们需要考虑我们有哪些服务是适合放在同一个pod中,还有哪些是适合单独成一个pod。如果将多个服务放在一个pod中,会有什么特征呢?毫无疑问两者之间的访问效率肯定是很高的,它们之间通过本机localhost就可以访问到处于同一个pod中的服务。

消息服务与其他的服务关联程度不大,而且所有的服务都可能去调用消息服务,因此它和谁放在一起都不太合适,可以将其单独做成一个pod。然后课程的dubbo服务和web服务这两个是紧密相关的,因为课程的dubbo服务大部分都是由web服务去调用的,两者的交换频率肯定会很高。因此课程的dubbo服务和web服务这两个可以放在同一个pod中。同理用户的thrift服务和web服务也应该放在同一个pod中。api网关可能会调用很多的服务,因此它并不适合和其他的服务放在一起,所以将其放在一个单独的pod中。

(2)在一个pod里面的服务如何彼此访问?它们的服务如何对外提供服务?
举个例子,如课程的dubbo服务和web服务,它们都在同一个pod中,两者是如何进行彼此通信的呢?可以通过本机的端口直接去访问,这是没问题的。在前面我们学习过service,知道我们的服务可以通过CLUSTER_IP或者通过kube-dns根据名字来进行解析,让我们的服务可以在集群内部进行访问,并且在一个service中可以同时定义多个端口,其实就是允许我们同时对外提供多个服务。这一点从技术上来说也是没有任何问题的。

(3)单独的pod如何对外提供服务?
单独的pod其实就更简单了,直接通过CLUSTER_IP或者通过kube-dns根据名字来进行解析,就可以直接提供集群内的服务了。

(4)哪个服务作为整个服务的入口,入口服务如何对外提供服务?
最后比较特殊的就是API网关,它作为整个服务的入口,为了对外提供服务,它需要提供一个NodePort,这样就让我们在集群外,也可以访问到这个服务,这就是api-gateway需要对外提供服务的方式,即它需要使用到NodePort。

搞定配置

配置的模板已经设置好了,就是按照前面所说的服务划分,但是开发者还需要做一些配置,将这些配置文件中的变量进行配置。进入到主节点中,我们先进入到如下目录:

1
$ cd /home/envy/kubernetes-starter/service-config/

然后查看一下当前的目录文件信息:

1
2
3
4
5
$ ls
api-gateway.yaml message-service.yaml
course-service.yaml user-service.yaml
#替换变量 - (hub.envyzhan.com:9080是我的环境的镜像仓库地址,大家修改为各自的仓库)
$ sed -i 's/{{HUB}}/hub.envyzhan.com:9080/g' *

接下来查看各个文件的信息,api-gateway.yaml配置文件内容如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
apiVersion: v1
kind: Service
metadata:
labels:
app: api-gateway
name: api-gateway
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8080
nodePort: 80
selector:
app: api-gateway
type: NodePort
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: api-gateway-deployment
spec:
replicas: 1
template:
metadata:
labels:
app: api-gateway
spec:
containers:
- name: api-gateway
image: hub.envyzhan.com:9080/micro-service/api-gateway-zuul:latest
ports:
- containerPort: 8080

接着我们再来看一下message-service.yaml配置文件的内容,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
apiVersion: v1
kind: Service
metadata:
labels:
app: message-service
name: message-service
spec:
ports:
- port: 9090
protocol: TCP
targetPort: 9090
selector:
app: message-service
type: ClusterIP
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: message-service-deployment
spec:
replicas: 1
template:
metadata:
labels:
app: message-service
spec:
containers:
- name: message-service
image: hub.envyzhan.com:9080/micro-service/message-service:latest
ports:
- containerPort: 9090

可以看到这个Service的类型是ClusterIP,因为它不需要对外提供服务,只需在集群内提供服务。

再来看一下course-service.yaml配置文件的内容,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
apiVersion: v1
kind: Service
metadata:
labels:
app: course-service
name: course-service
spec:
ports:
- port: 8081
protocol: TCP
targetPort: 8081
selector:
app: course-service
type: ClusterIP
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: course-service-deployment
spec:
replicas: 1
template:
metadata:
labels:
app: course-service
spec:
containers:
- name: course-service
image: hub.envyzhan.com:9080/micro-service/course-service:latest
ports:
- containerPort: 20880
- name: course-edge-service
image: hub.envyzhan.com:9080/micro-service/course-edge-service:latest
ports:
- containerPort: 8081

可以看到里面有两个容器,一个是course-service,容器端口为20880,另一个则是course-edge-service,容器端口为8081,这个与名为course-service的service所指定的service端口是一致的。这是因为这个8081是在集群内提供给其他服务所使用的,而这个20880它只需要给这个course-edge-service使用就可以,因为别的服务并没有使用到它,也没有依赖它,因此上面我们只需指定一个端口。

最后再来看一下这个user-service.yaml配置文件的内容,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
apiVersion: v1
kind: Service
metadata:
labels:
app: user-service
name: user-service
namespace: default
spec:
ports:
- name: user-edge-service-port
port: 8082
protocol: TCP
targetPort: 8082
- name: user-service-port
port: 7911
protocol: TCP
targetPort: 7911
selector:
app: user-service
sessionAffinity: None
type: ClusterIP
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
name: user-service-deployment
spec:
replicas: 1
template:
metadata:
labels:
app: user-service
spec:
containers:
- name: user-service
image: hub.envyzhan.com:9080/micro-service/user-service:latest
ports:
- containerPort: 7911
- name: user-edge-service
image: hub.envyzhan.com:9080/micro-service/user-edge-service:latest
ports:
- containerPort: 8082

可以看到这个名为user-service的service里面提供了两个port,一个是user-edge-service-port,端口号为8082;另一个则是user-service-port,端口号为7911,注意定义多个port时就需要开发者指定名称。一个是方便我们记忆,另一个则是方便后来维护的人,这些port分别用于做什么事情。这就表明这两个port在集群中,都是提供给集群内其他port来使用的。后面的user-service-deployment中也是有两个容器,一个是user-service,另一个则是user-edge-service。

可以看到user-service和course-service这两个service的唯一区别在于,course-service只定义了一个端口用于对外提供服务,而user-service却需要提供两个端口,且这两个端口都需要对外提供服务。

启动服务

(1)启动Harbor。由于在前面我们已经将harbor设置为开机自启动,因此这里会自己启动。
(2)启动MySQL。进入到/opt/mysql目录,在里面执行其中的start.sh脚本文件即可启动MySQL。
(3)启动Redis。进入到/opt/redis目录,在里面执行其中的start.sh脚本文件即可启动Redis。
(4)启动Zookeeper。由于在前面我们已经将Zookeeper设置为开机自启动,因此这里会自己启动。

梳理代码

(1)api-gateway-zuul微服务配置信息改造。api-gateway-zuul微服务原有的配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 服务名称和端口号
server:
name: gateway-zuul-service
port: 8080
# zuul相关配置信息
zuul:
routes:
course:
path: /course/**
url: http://course-edge-service:8023/course/
user:
path: /user/**
url: http://user-edge-service:8022/user/

修改之后的application.yml配置文件的信息为:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 服务名称和端口号
server:
name: gateway-zuul-service
port: 8080
# zuul相关配置信息
zuul:
routes:
course:
path: /course/**
url: http://course-service:8081/course/
user:
path: /user/**
url: http://user-service:8082/user/

(2)course-dubbo-service微服务配置信息改造。course-dubbo-service微服务原有的配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 用户服务的IP和端口号
thrift:
user:
ip: user-service
port: 7911
# 数据源的配置信息
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.51.100:3306/db_course
username: root
password: envy123
# dubbo相关配置
dubbo:
application:
name: course-service
registry:
address: zookeeper://192.168.51.100:2181
protocol:
name: dubbo
port: 20880
# host: 192.168.51.6
scan:
base-packages: com.envy.course

通过对比发现这个服务的配置信息无需进行修改。
(3)course-edge-service微服务配置信息改造。course-edge-service微服务原有的配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 服务名称和端口号
server:
name: course-edge-service
port: 8023
# dubbo相关配置
dubbo:
application:
name: course-service
registry:
address: zookeeper://${zookeeper.url}:2181
protocol:
name: dubbo
port: 20880
# host: 192.168.51.6
scan:
base-packages: com.envy.course

修改之后的application.yml配置文件的信息为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 服务名称和端口号
server:
name: course-edge-service
port: 8023
# dubbo相关配置
dubbo:
application:
name: course-service
registry:
address: zookeeper://192.168.51.100:2181
protocol:
name: dubbo
port: 20880
scan:
base-packages: com.envy.course
user:
edge:
service:
addr: user-service:8082

(4)user-edge-service微服务配置信息改造。user-edge-service微服务原有的配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 服务名称和端口号
server:
name: user-edge-service
port: 8022

# 用户服务的IP和端口号
thrift:
user:
ip: user-service
port: 7911
# 短信服务的IP和端口号
message:
ip: message-service
port: 9090

# Redis配置信息
spring:
redis:
host: ${redis.url}
port: 6379
timeout: 30000

修改之后的application.yml配置文件的信息为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 服务名称和端口号
server:
name: user-edge-service
port: 8022

# 用户服务的IP和端口号
thrift:
user:
ip: user-service
port: 7911
# 短信服务的IP和端口号
message:
ip: message-service
port: 9090

# Redis配置信息
spring:
redis:
host: 192.168.51.100
port: 6379
timeout: 30000

(5)user-thrift-service微服务配置信息改造。user-thrift-service微服务原有的配置信息如下:

1
2
3
4
5
6
7
8
9
10
11
# 服务名称和端口号
server:
name: user-thrift-service
port: 7911
# 数据源的配置信息
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://${mysql.url}:3306/db_user
username: root
password: envy123

修改之后的application.yml配置文件的信息为:

1
2
3
4
5
6
7
8
9
10
11
# 服务名称和端口号
server:
name: user-thrift-service
port: 7911
# 数据源的配置信息
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://192.168.51.100:3306/db_user
username: root
password: envy123

重新打包镜像

将下来我们需要将之前我们修改过配置文件的服务,都重新打包成镜像,这个过程在前面都已经进行了介绍,这里就不过多介绍了。

删除现有的服务

首先进入到主节点,查看一下当前所有的服务:

1
2
3
4
[root@server02 service-config]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 3d
nginx-service NodePort 10.68.199.24 <none> 8080:20000/TCP 1d

考虑到后面这个nginx-service可能会影响到现有的服务,这里我们将这个服务给删除掉:

1
2
[root@server02 service-config]# kubectl delete svc nginx-service
service "nginx-service" deleted

同理查看一下当前所有的deployments:

1
2
3
4
[root@server02 service-config]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
kubernetes-bootcamp 1 1 1 1 2d
nginx-deployment 2 2 2 2 1d

删除上述两个deployment:

1
2
3
4
[root@server02 service-config]# kubectl delete deploy nginx-deployment
deployment "nginx-deployment" deleted
[root@server02 service-config]# kubectl delete deploy kubernetes-bootcamp
deployment "kubernetes-bootcamp" deleted

再来查看一下当前所有的pods:

1
2
3
[root@server02 service-config]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx 1/1 Running 0 1d

删除这个名为nginx的pod:

1
2
[root@server02 service-config]# kubectl delete pod nginx
pod "nginx" deleted

接着进入到主节点所在机器,我们需要在它的/etc/hosts文件中新增一条映射信息:

1
192.168.51.100 hub.envyzhan.com

然后我们进入到/home/envy/kubernetes-starter/service-config目录,开始根据对应的配置文件创建对应的Service、Deployment。注意它们是有依赖关系的,因此创建顺序不能搞错,必须是【message-service.yaml】–>【user-service.yaml】–>【course-service.yaml】–>【api-gateway.yaml】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@server02 service-config]# kubectl apply -f message-service.yaml 
service "message-service" created
deployment "message-service-deployment" created

[root@server02 service-config]# kubectl apply -f user-service.yaml
service "user-service" created
deployment "user-service-deployment" created

[root@server02 service-config]# kubectl apply -f course-service.yaml
service "course-service" created
deployment "course-service-deployment" created

[root@server02 service-config]# kubectl apply -f api-gateway.yaml
deployment "api-gateway-deployment" created
The Service "api-gateway" is invalid: spec.ports[0].nodePort: Invalid value: 80: provided port is not in the valid range. The range of valid ports is 20000-40000

可以看到我们在创建api-gateway这一NodePort时出现问题,原因是我们在APIServer中规定了节点端口必须在2万-4万之间,而此处的80端口是不在里面,所以就抛出异常。此时我们可以修改APIServer的配置信息:

1
[root@server02 service-config]# vi /lib/systemd/system/kube-apiserver.service

我们可以将其中的2万-4万修改为80-4万,这样就好了,当然你可以让api-gateway它的NodePort修改为2万-4万之间的值,只是此时用起来就不太方便了。之后依次执行如下命令来重启APIServer:

1
2
[root@server02 service-config]# systemctl daemon-reload
[root@server02 service-config]# systemctl restart kube-apiserver

之后我们再次重新执行上述kubectl apply -f api-gateway.yaml命令,可以看到此时就不抛出异常了:

1
2
3
[root@server02 service-config]# kubectl apply -f api-gateway.yaml 
service "api-gateway" created
deployment "api-gateway-deployment" unchanged

接着我们查看一下当前所有的service信息:

1
2
3
4
5
6
7
[root@server02 service-config]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
api-gateway NodePort 10.68.180.221 <none> 80:80/TCP 22s
course-service ClusterIP 10.68.54.94 <none> 8081/TCP 7m
kubernetes ClusterIP 10.68.0.1 <none> 443/TCP 3d
message-service ClusterIP 10.68.24.72 <none> 9090/TCP 7m
user-service ClusterIP 10.68.77.158 <none> 8082/TCP,7911/TCP 7m

再来查看一下当前所有的deployments:

1
2
3
4
5
6
[root@server02 service-config]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
api-gateway-deployment 1 1 1 0 8m
course-service-deployment 1 1 1 0 9m
message-service-deployment 1 1 1 0 9m
user-service-deployment 1 1 1 0 9m

可以看到现在AVAILABLE的个数为0,表示一个都无法使用。那我们查看当前所有的pod信息:

1
2
3
4
5
6
[root@server02 service-config]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
api-gateway-deployment-68f4694877-2s8nv 0/1 ImagePullBackOff 0 14m 172.20.188.9 192.168.51.121
course-service-deployment-ff6f7d4b8-snm7l 0/2 ImagePullBackOff 0 14m 172.20.40.198 192.168.51.123
message-service-deployment-64c49f8ff8-48r8g 0/1 ImagePullBackOff 0 14m 172.20.188.7 192.168.51.121
user-service-deployment-8674c798b7-8bbck 0/2 ImagePullBackOff 0 14m 172.20.188.8 192.168.51.12

可以看到原来它们都在拉取镜像,这个速度有点慢。为了保险起见,接下来我们查看一下日志:

1
2
[root@server02 service-config]# kubectl logs -f api-gateway-deployment-68f4694877-2s8nv
Error from server (BadRequest): container "api-gateway" in pod "api-gateway-deployment-68f4694877-2s8nv" is waiting to start: trying and failing to pull image

可以看到镜像拉取失败,我们尝试登陆到harbor镜像仓库上docker login http://hub.envyzhan.com:9080,可以看到错误信息如下:

1
Error response from daemon: Get "https://hub.envyzhan.com:9080/v2/": http: server gave HTTP response to HTTPS client

在主节点上的/etc/docker/daemon.json文件中新增一个不安全镜像仓库的配置信息,注意值必须是数组形式:

1
2
3
{"registry-mirrors": ["http://f1361db2.m.daocloud.io"],
"insecure-registries": ["hub.envyzhan.com:9080"]
}

然后我们依次执行如下命令来重新启动docker:

1
2
[root@server02 docker]# systemctl daemon-reload
[root@server02 docker]# systemctl restart docker

之后我们再重新执行上述kubectl apply -f命令。