k8s学习笔记-14-服务发现Service

[TOC]

一、Service的概念

  运行在Pod中的应用是向客户端提供服务的守护进程,比如,nginx、tomcat、etcd等等,它们都是受控于控制器的资源对象,存在生命周期,我们知道Pod资源对象在自愿或非自愿终端后,只能被重构的Pod对象所替代,属于不可再生类组件。而在动态和弹性的管理模式下,Service为该类Pod对象提供了一个固定、统一的访问接口和负载均衡能力。

  其实,就是说Pod存在生命周期,有销毁,有重建,无法提供一个固定的访问接口给客户端。并且为了同类的Pod都能够实现工作负载的价值,由此Service资源出现了,可以为一类Pod资源对象提供一个固定的访问接口和负载均衡,类似于阿里云的负载均衡或者是LVS的功能。

  但是要知道的是,Service和Pod对象的IP地址,一个是虚拟地址,一个是Pod IP地址,都仅仅在集群内部可以进行访问,无法接入集群外部流量。而为了解决该类问题的办法可以是在单一的节点上做端口暴露(hostPort)以及让Pod资源共享工作节点的网络名称空间(hostNetwork)以外,还可以使用NodePort或者是LoadBalancer类型的Service资源,或者是有7层负载均衡能力的Ingress资源。

  Service是Kubernetes的核心资源类型之一,Service资源基于标签选择器将一组Pod定义成一个逻辑组合,并通过自己的IP地址和端口调度代理请求到组内的Pod对象,如下图所示,它向客户端隐藏了真实的,处理用户请求的Pod资源,使得从客户端上看,就像是由Service直接处理并响应一样,是不是很像负载均衡器呢!

img

  Service对象的IP地址也称为Cluster IP,它位于为Kubernetes集群配置指定专用的IP地址范围之内,是一种虚拟的IP地址,它在Service对象创建之后保持不变,并且能够被同一集群中的Pod资源所访问。Service端口用于接受客户端请求,并将请求转发至后端的Pod应用的相应端口,这样的代理机制,也称为端口代理,它是基于TCP/IP 协议栈的传输层。

二、Service的实现模型

  在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。kube-proxy 负责为 Service 实现了一种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起,默认就是 iptables 代理。在Kubernetes v1.8.0-beta.0中,添加了ipvs代理。在 Kubernetes v1.0 版本,Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了 Ingress API(beta 版),用来表示 “7层”(HTTP)服务。

kube-proxy 这个组件始终监视着apiserver中有关service的变动信息,获取任何一个与service资源相关的变动状态,通过watch监视,一旦有service资源相关的变动和创建,kube-proxy都要转换为当前节点上的能够实现资源调度规则(例如:iptables、ipvs)

img

2.1、userspace代理模式

  这种模式,当客户端Pod请求内核空间的service iptables后,把请求转到给用户空间监听的kube-proxy 的端口,由kube-proxy来处理后,再由kube-proxy将请求转给内核空间的 service ip,再由service iptalbes根据请求转给各节点中的的service pod。

  由此可见这个模式有很大的问题,由客户端请求先进入内核空间的,又进去用户空间访问kube-proxy,由kube-proxy封装完成后再进去内核空间的iptables,再根据iptables的规则分发给各节点的用户空间的pod。这样流量从用户空间进出内核带来的性能损耗是不可接受的。在Kubernetes 1.1版本之前,userspace是默认的代理模型。

img

2.2、 iptables代理模式

  客户端IP请求时,直接请求本地内核service ip,根据iptables的规则直接将请求转发到到各pod上,因为使用iptable NAT来完成转发,也存在不可忽视的性能损耗。另外,如果集群中存在上万的Service/Endpoint,那么Node上的iptables rules将会非常庞大,性能还会再打折扣。iptables代理模式由Kubernetes 1.1版本引入,自1.2版本开始成为默认类型。

img

2.3、ipvs代理模式

  Kubernetes自1.9-alpha版本引入了ipvs代理模式,自1.11版本开始成为默认设置。客户端IP请求时到达内核空间时,根据ipvs的规则直接分发到各pod上。kube-proxy会监视Kubernetes Service对象和Endpoints,调用netlink接口以相应地创建ipvs规则并定期与Kubernetes Service对象和Endpoints对象同步ipvs规则,以确保ipvs状态与期望一致。访问服务时,流量将被重定向到其中一个后端Pod。

与iptables类似,ipvs基于netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着ipvs可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs为负载均衡算法提供了更多选项,例如:

  • rr:轮询调度
  • lc:最小连接数
  • dh:目标哈希
  • sh:源哈希
  • sed:最短期望延迟
  • nq:不排队调度

注意: ipvs模式假定在运行kube-proxy之前在节点上都已经安装了IPVS内核模块。当kube-proxy以ipvs代理模式启动时,kube-proxy将验证节点上是否安装了IPVS模块,如果未安装,则kube-proxy将回退到iptables代理模式。

img

如果某个服务后端pod发生变化,标签选择器适应的pod有多一个,适应的信息会立即反映到apiserver上,而kube-proxy一定可以watch到etc中的信息变化,而将它立即转为ipvs或者iptables中的规则,这一切都是动态和实时的,删除一个pod也是同样的原理。如图:

img

三、Service的定义

3.1、清单创建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
25
26
27
28
29
30
31
32
33
34
35
 1 [root@k8s-master ~]# kubectl explain svc
2 KIND: Service
3 VERSION: v1
4
5 DESCRIPTION:
6 Service is a named abstraction of software service (for example, mysql)
7 consisting of local port (for example 3306) that the proxy listens on, and
8 the selector that determines which pods will answer requests sent through
9 the proxy.
10
11 FIELDS:
12 apiVersion <string>
13 APIVersion defines the versioned schema of this representation of an
14 object. Servers should convert recognized schemas to the latest internal
15 value, and may reject unrecognized values. More info:
16 https://git.k8s.io/community/contributors/devel/api-conventions.md#resources
17
18 kind <string>
19 Kind is a string value representing the REST resource this object
20 represents. Servers may infer this from the endpoint the client submits
21 requests to. Cannot be updated. In CamelCase. More info:
22 https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds
23
24 metadata <Object>
25 Standard object's metadata. More info:
26 https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata
27
28 spec <Object>
29 Spec defines the behavior of a service.
30 https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status
31
32 status <Object>
33 Most recently observed status of the service. Populated by the system.
34 Read-only. More info:
35 https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status

其中重要的4个字段:
apiVersion:
kind:
metadata:
spec:
  clusterIP: 可以自定义,也可以动态分配
  ports:(与后端容器端口关联)
  selector:(关联到哪些pod资源上)
  type:服务类型

3.2、service的类型

对一些应用(如 Frontend)的某些部分,可能希望通过外部(Kubernetes 集群外部)IP 地址暴露 Service。

Kubernetes ServiceTypes 允许指定一个需要的类型的 Service,默认是 ClusterIP 类型。

Type 的取值以及行为如下:

  • **ClusterIP:**通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType
  • **NodePort:**通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。
  • **LoadBalancer:**使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。
  • **ExternalName:**通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如, foo.bar.example.com)。 没有任何类型代理被创建,这只有 Kubernetes 1.7 或更高版本的 kube-dns 才支持。

3.2.1、ClusterIP的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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[root@k8s-master mainfests]# cat redis-svc.yaml 
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: default
spec:
selector:  #标签选择器,必须指定pod资源本身的标签
app: redis
role: logstor
type: ClusterIP  #指定服务类型为ClusterIP
ports:   #指定端口
- port: 6379  #暴露给服务的端口
- targetPort: 6379  #容器的端口
[root@k8s-master mainfests]# kubectl apply -f redis-svc.yaml
service/redis created
[root@k8s-master mainfests]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36d
redis ClusterIP 10.107.238.182 <none> 6379/TCP 1m

[root@k8s-master mainfests]# kubectl describe svc redis
Name: redis
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"redis","namespace":"default"},"spec":{"ports":[{"port":6379,"targetPort":6379}...
Selector: app=redis,role=logstor
Type: ClusterIP
IP: 10.107.238.182  #service ip
Port: <unset> 6379/TCP
TargetPort: 6379/TCP
Endpoints: 10.244.1.16:6379  #此处的ip+端口就是pod的ip+端口
Session Affinity: None
Events: <none>

[root@k8s-master mainfests]# kubectl get pod redis-5b5d6fbbbd-v82pw -o wide
NAME READY STATUS RESTARTS AGE IP NODE
redis-5b5d6fbbbd-v82pw 1/1 Running 0 20d 10.244.1.16 k8s-node01

从上演示可以总结出:service不会直接到pod,service是直接到endpoint资源,就是地址加端口,再由endpoint再关联到pod。

service只要创建完,就会在dns中添加一个资源记录进行解析,添加完成即可进行解析。资源记录的格式为:SVC_NAME.NS_NAME.DOMAIN.LTD.
默认的集群service 的A记录:svc.cluster.local.
redis服务创建的A记录:redis.default.svc.cluster.local.

3.2.2、NodePort的service类型演示:

  NodePort即节点Port,通常在部署Kubernetes集群系统时会预留一个端口范围用于NodePort,其范围默认为:30000~32767之间的端口。定义NodePort类型的Service资源时,需要使用.spec.type进行明确指定。

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
[root@k8s-master mainfests]# kubectl get pods --show-labels |grep myapp-deploy
myapp-deploy-69b47bc96d-4hxxw 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary
myapp-deploy-69b47bc96d-95bc4 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary
myapp-deploy-69b47bc96d-hwbzt 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary
myapp-deploy-69b47bc96d-pjv74 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary
myapp-deploy-69b47bc96d-rf7bs 1/1 Running 0 12m app=myapp,pod-template-hash=2560367528,release=canary

[root@k8s-master mainfests]# cat myapp-svc.yaml #为myapp创建service
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
selector:
app: myapp
release: canary
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
[root@k8s-master mainfests]# kubectl apply -f myapp-svc.yaml
service/myapp created
[root@k8s-master mainfests]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36d
myapp NodePort 10.101.245.119 <none> 80:30080/TCP 5s
redis ClusterIP 10.107.238.182 <none> 6379/TCP 28m

[root@k8s-master mainfests]# while true;do curl http://192.168.56.11:30080/hostname.html;sleep 1;done
myapp-deploy-69b47bc96d-95bc4
myapp-deploy-69b47bc96d-4hxxw
myapp-deploy-69b47bc96d-pjv74
myapp-deploy-69b47bc96d-rf7bs
myapp-deploy-69b47bc96d-95bc4
myapp-deploy-69b47bc96d-rf7bs
myapp-deploy-69b47bc96d-95bc4
myapp-deploy-69b47bc96d-pjv74
myapp-deploy-69b47bc96d-4hxxw
myapp-deploy-69b47bc96d-pjv74
myapp-deploy-69b47bc96d-pjv74
myapp-deploy-69b47bc96d-4hxxw
myapp-deploy-69b47bc96d-pjv74
myapp-deploy-69b47bc96d-pjv74
myapp-deploy-69b47bc96d-pjv74
myapp-deploy-69b47bc96d-95bc4
myapp-deploy-69b47bc96d-hwbzt

[root@k8s-master mainfests]# while true;do curl http://192.168.56.11:30080/;sleep 1;done
Hello MyApp | Version: v1 | Pod Name
Hello MyApp | Version: v1 | Pod Name
Hello MyApp | Version: v1 | Pod Name
Hello MyApp | Version: v1 | Pod Name
Hello MyApp | Version: v1 | Pod Name
Hello MyApp | Version: v1 | Pod Name

从以上例子,可以看到通过NodePort方式已经实现了从集群外部端口进行访问,访问链接如下:http://192.168.56.11:30080/。实践中并不鼓励用户自定义使用节点的端口,因为容易和其他现存的Service冲突,建议留给系统自动配置。

3.2.3、Pod的会话保持

  Service资源还支持Session affinity(粘性会话)机制,可以将来自同一个客户端的请求始终转发至同一个后端的Pod对象,这意味着它会影响调度算法的流量分发功用,进而降低其负载均衡的效果。因此,当客户端访问Pod中的应用程序时,如果有基于客户端身份保存某些私有信息,并基于这些私有信息追踪用户的活动等一类的需求时,那么应该启用session affinity机制。

  Service affinity的效果仅仅在一段时间内生效,默认值为10800秒,超出时长,客户端再次访问会重新调度。该机制仅能基于客户端IP地址识别客户端身份,它会将经由同一个NAT服务器进行原地址转换的所有客户端识别为同一个客户端,由此可知,其调度的效果并不理想。Service 资源 通过. spec. sessionAffinity 和. spec. sessionAffinityConfig 两个字段配置粘性会话。 spec. sessionAffinity 字段用于定义要使用的粘性会话的类型,它仅支持使用“ None” 和“ ClientIP” 两种属性值。如下:

1
2
3
4
5
6
7
8
9
10
11
[root@k8s-master mainfests]# kubectl explain svc.spec.sessionAffinity
KIND: Service
VERSION: v1

FIELD: sessionAffinity <string>

DESCRIPTION:
Supports "ClientIP" and "None". Used to maintain session affinity. Enable
client IP based session affinity. Must be ClientIP or None. Defaults to
None. More info:
https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies

sessionAffinity支持ClientIP和None 两种方式,默认是None(随机调度) ClientIP是来自于同一个客户端的请求调度到同一个pod中

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
[root@k8s-master mainfests]# vim myapp-svc.yaml 
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: default
spec:
selector:
app: myapp
release: canary
sessionAffinity: ClientIP
type: NodePort
ports:
- port: 80
targetPort: 80
nodePort: 30080
[root@k8s-master mainfests]# kubectl apply -f myapp-svc.yaml
service/myapp configured
[root@k8s-master mainfests]# kubectl describe svc myapp
Name: myapp
Namespace: default
Labels: <none>
Annotations: kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"myapp","namespace":"default"},"spec":{"ports":[{"nodePort":30080,"port":80,"ta...
Selector: app=myapp,release=canary
Type: NodePort
IP: 10.101.245.119
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30080/TCP
Endpoints: 10.244.1.18:80,10.244.1.19:80,10.244.2.15:80 + 2 more...
Session Affinity: ClientIP
External Traffic Policy: Cluster
Events: <none>
[root@k8s-master mainfests]# while true;do curl http://192.168.56.11:30080/hostname.html;sleep 1;done
myapp-deploy-69b47bc96d-hwbzt
myapp-deploy-69b47bc96d-hwbzt
myapp-deploy-69b47bc96d-hwbzt
myapp-deploy-69b47bc96d-hwbzt
myapp-deploy-69b47bc96d-hwbzt
myapp-deploy-69b47bc96d-hwbzt
myapp-deploy-69b47bc96d-hwbzt
myapp-deploy-69b47bc96d-hwbzt

也可以使用打补丁的方式进行修改yaml内的内容,如下:

1
2
3
kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"ClusterIP"}}'  #session保持,同一ip访问同一个pod

kubectl patch svc myapp -p '{"spec":{"sessionAffinity":"None"}}' #取消session

四、Headless Service

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 "None" 来创建 Headless Service

这个选项允许开发人员自由寻找他们自己的方式,从而降低与 Kubernetes 系统的耦合性。 应用仍然可以使用一种自注册的模式和适配器,对其它需要发现机制的系统能够很容易地基于这个 API 来构建。

对这类 Service 并不会分配 Cluster IP,kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由。 DNS 如何实现自动配置,依赖于 Service 是否定义了 selector。

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
(1)编写headless service配置清单
[root@k8s-master mainfests]# cp myapp-svc.yaml myapp-svc-headless.yaml
[root@k8s-master mainfests]# vim myapp-svc-headless.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: default
spec:
selector:
app: myapp
release: canary
clusterIP: "None"  #headless的clusterIP值为None
ports:
- port: 80
targetPort: 80

(2)创建headless service
[root@k8s-master mainfests]# kubectl apply -f myapp-svc-headless.yaml
service/myapp-headless created
[root@k8s-master mainfests]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36d
myapp NodePort 10.101.245.119 <none> 80:30080/TCP 1h
myapp-headless ClusterIP None <none> 80/TCP 5s
redis ClusterIP 10.107.238.182 <none> 6379/TCP 2h

(3)使用coredns进行解析验证
[root@k8s-master mainfests]# dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10

; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> -t A myapp-headless.default.svc.cluster.local. @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 62028
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 5, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myapp-headless.default.svc.cluster.local. IN A

;; ANSWER SECTION:
myapp-headless.default.svc.cluster.local. 5 IN A 10.244.1.18
myapp-headless.default.svc.cluster.local. 5 IN A 10.244.1.19
myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.15
myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.16
myapp-headless.default.svc.cluster.local. 5 IN A 10.244.2.17

;; Query time: 4 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Thu Sep 27 04:27:15 EDT 2018
;; MSG SIZE rcvd: 349

[root@k8s-master mainfests]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 36d

[root@k8s-master mainfests]# kubectl get pods -o wide -l app=myapp
NAME READY STATUS RESTARTS AGE IP NODE
myapp-deploy-69b47bc96d-4hxxw 1/1 Running 0 1h 10.244.1.18 k8s-node01
myapp-deploy-69b47bc96d-95bc4 1/1 Running 0 1h 10.244.2.16 k8s-node02
myapp-deploy-69b47bc96d-hwbzt 1/1 Running 0 1h 10.244.1.19 k8s-node01
myapp-deploy-69b47bc96d-pjv74 1/1 Running 0 1h 10.244.2.15 k8s-node02
myapp-deploy-69b47bc96d-rf7bs 1/1 Running 0 1h 10.244.2.17 k8s-node02

(4)对比含有ClusterIP的service解析
[root@k8s-master mainfests]# dig -t A myapp.default.svc.cluster.local. @10.96.0.10

; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> -t A myapp.default.svc.cluster.local. @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 50445
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;myapp.default.svc.cluster.local. IN A

;; ANSWER SECTION:
myapp.default.svc.cluster.local. 5 IN A 10.101.245.119

;; Query time: 1 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Thu Sep 27 04:31:16 EDT 2018
;; MSG SIZE rcvd: 107

[root@k8s-master mainfests]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 36d
myapp NodePort 10.101.245.119 <none> 80:30080/TCP 1h
myapp-headless ClusterIP None <none> 80/TCP 11m
redis ClusterIP 10.107.238.182 <none> 6379/TCP 2h

从以上的演示可以看到对比普通的service和headless service,headless service做dns解析是直接解析到pod的,而servcie是解析到ClusterIP的,那么headless有什么用呢?这将在statefulset中应用到,这里暂时仅仅做了解什么是headless service和创建方法。

k8s学习笔记-12-Pod控制器--ReplicaSet、Deployment

[TOC]

一、Pod控制器及其功用

Pod控制器是用于实现管理pod的中间层,确保pod资源符合预期的状态,pod的资源出现故障时,会尝试 进行重启,当根据重启策略无效,则会重新新建pod的资源。

pod控制器有多种类型:
ReplicaSet: 代用户创建指定数量的pod副本数量,确保pod副本数量符合预期状态,并且支持滚动式自动扩容和缩容功能。
ReplicaSet主要三个组件组成:
  (1)用户期望的pod副本数量
  (2)标签选择器,判断哪个pod归自己管理
  (3)当现存的pod数量不足,会根据pod资源模板进行新建
帮助用户管理无状态的pod资源,精确反应用户定义的目标数量,但是RelicaSet不是直接使用的控制器,而是使用Deployment。
**Deployment:**工作在ReplicaSet之上,用于管理无状态应用,目前来说最好的控制器。支持滚动更新和回滚功能,还提供声明式配置。
**DaemonSet:**用于确保集群中的每一个节点只运行特定的pod副本,通常用于实现系统级后台任务。比如ELK服务
特性:服务是无状态的
服务必须是守护进程
**Job:**只要完成就立即退出,不需要重启或重建。
**Cronjob:**周期性任务控制,不需要持续后台运行,
**StatefulSet:**管理有状态应用

二、ReplicaSet控制器

ReplicationController用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收。

在新版本的Kubernetes中建议使用ReplicaSet来取代ReplicationController。ReplicaSet跟ReplicationController没有本质的不同,只是名字不一样,并且ReplicaSet支持集合式的selector。

虽然ReplicaSet可以独立使用,但一般还是建议使用 Deployment 来自动管理ReplicaSet,这样就无需担心跟其他机制的不兼容问题(比如ReplicaSet不支持rolling-update但Deployment支持)。

ReplicaSet示例:

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
43
44
45
46
(1)命令行查看ReplicaSet清单定义规则
[root@k8s-master ~]# kubectl explain rs
[root@k8s-master ~]# kubectl explain rs.spec
[root@k8s-master ~]# kubectl explain rs.spec.template

(2)新建ReplicaSet示例
[root@k8s-master ~]# vim rs-demo.yaml

apiVersion: apps/v1  #api版本定义
kind: ReplicaSet  #定义资源类型为ReplicaSet
metadata:  #元数据定义
name: myapp
namespace: default
spec:  #ReplicaSet的规格定义
replicas: 2  #定义副本数量为2个
selector:    #标签选择器,定义匹配pod的标签
matchLabels:
app: myapp
release: canary
template:  #pod的模板定义
metadata:  #pod的元数据定义
name: myapp-pod   #自定义pod的名称 
labels:   #定义pod的标签,需要和上面定义的标签一致,也可以多出其他标签
app: myapp
release: canary
environment: qa
spec:  #pod的规格定义
containers:  #容器定义
- name: myapp-container  #容器名称
image: ikubernetes/myapp:v1  #容器镜像
ports:  #暴露端口
- name: http
containerPort: 80

(3)创建ReplicaSet定义的pod
[root@k8s-master ~]# kubectl create -f rs-demo.yaml
[root@k8s-master ~]# kubectl get pods  #获取pod信息
[root@k8s-master ~]# kubectl describe pods myapp-***  #查看pod详细信息

(4)修改pod的副本数量
[root@k8s-master ~]# kubectl edit rs myapp
replicas: 5
[root@k8s-master ~]# kubectl get rs -o wide

(5)修改pod的镜像版本
[root@k8s-master ~]# kubectl edit rs myapp
1
image: ikubernetes/myapp:v2  
1
2
[root@k8s-master ~]# kubectl delete pods myapp-***   #修改了pod镜像版本,pod需要重建才能达到最新版本
[root@k8s-master ~]# kubectl create -f rs-demo.yaml

三、Deployment控制器

Deployment为Pod和ReplicaSet(下一代Replication Controller)提供声明式更新。

只需要在 Deployment 中描述想要的目标状态是什么,Deployment controller 就会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态。也可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。

img

典型的用例如下:

  • (1)使用Deployment来创建ReplicaSet。ReplicaSet在后台创建pod。检查启动状态,看它是成功还是失败。
  • (2)然后,通过更新Deployment的PodTemplateSpec字段来声明Pod的新状态。这会创建一个新的ReplicaSet,Deployment会按照控制的速率将pod从旧的ReplicaSet移动到新的ReplicaSet中。
  • (3)如果当前状态不稳定,回滚到之前的Deployment revision。每次回滚都会更新Deployment的revision。
  • (4)扩容Deployment以满足更高的负载。
  • (5)暂停Deployment来应用PodTemplateSpec的多个修复,然后恢复上线。
  • (6)根据Deployment 的状态判断上线是否hang住了。
  • (7)清除旧的不必要的 ReplicaSet。

1、解析Deployment Spec

首先看一个官方的nginx-deployment.yaml的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
apiVersion: v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80

在所有的 Kubernetes 配置中,Deployment 也需要apiVersionkindmetadata这些配置项。如下:

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
[root@k8s-master ~]# kubectl explain deployment
KIND: Deployment
VERSION: extensions/v1beta1

DESCRIPTION:
DEPRECATED - This group version of Deployment is deprecated by
apps/v1beta2/Deployment. See the release notes for more information.
Deployment enables declarative updates for Pods and ReplicaSets.

FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#resources

kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds

metadata <Object>
Standard object metadata.

spec <Object>
Specification of the desired behavior of the Deployment.

status <Object>
Most recently observed status of the Deployment.

使用kubectl explain deployment.spec查看具体Deployment spec的配置选项,解析如下:

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
43
44
45
46
47
48
49
50
51
[root@k8s-master ~]# kubectl explain deployment.spec
KIND: Deployment
VERSION: extensions/v1beta1

RESOURCE: spec <Object>

DESCRIPTION:
Specification of the desired behavior of the Deployment.

DeploymentSpec is the specification of the desired behavior of the
Deployment.

FIELDS:
minReadySeconds <integer>
Minimum number of seconds for which a newly created pod should be ready
without any of its container crashing, for it to be considered available.
Defaults to 0 (pod will be considered available as soon as it is ready)

paused <boolean>
Indicates that the deployment is paused and will not be processed by the
deployment controller.

progressDeadlineSeconds <integer>
The maximum time in seconds for a deployment to make progress before it is
considered to be failed. The deployment controller will continue to process
failed deployments and a condition with a ProgressDeadlineExceeded reason
will be surfaced in the deployment status. Note that progress will not be
estimated during the time a deployment is paused. This is not set by
default.

replicas <integer>
Number of desired pods. This is a pointer to distinguish between explicit
zero and not specified. Defaults to 1.

revisionHistoryLimit <integer>
The number of old ReplicaSets to retain to allow rollback. This is a
pointer to distinguish between explicit zero and not specified.

rollbackTo <Object>
DEPRECATED. The config this deployment is rolling back to. Will be cleared
after rollback is done.

selector <Object>
Label selector for pods. Existing ReplicaSets whose pods are selected by
this will be the ones affected by this deployment.

strategy <Object>
The deployment strategy to use to replace existing pods with new ones.

template <Object> -required-
Template describes the pods that will be created.

Replicas(副本数量):
  .spec.replicas 是可以选字段,指定期望的pod数量,默认是1。

Selector(选择器):
  .spec.selector是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围。如果被指定, .spec.selector 必须匹配 .spec.template.metadata.labels,否则它将被API拒绝。如果 .spec.selector 没有被指定, .spec.selector.matchLabels 默认是.spec.template.metadata.labels。

  在Pod的template跟.spec.template不同或者数量超过了.spec.replicas规定的数量的情况下,Deployment会杀掉label跟selector不同的Pod。

Pod Template(Pod模板):
  .spec.template 是 .spec中唯一要求的字段。

  .spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion 和 kind字段。

  另外为了划分Pod的范围,Deployment中的pod template必须指定适当的label(不要跟其他controller重复了,参考selector)和适当的重启策略。

  .spec.template.spec.restartPolicy 可以设置为 Always , 如果不指定的话这就是默认配置。

strategy(更新策略):
  .spec.strategy 指定新的Pod替换旧的Pod的策略。 .spec.strategy.type 可以是”Recreate“或者是 “RollingUpdate“。“RollingUpdate”是默认值

  Recreate: 重建式更新,就是删一个建一个。类似于ReplicaSet的更新方式,即首先删除现有的Pod对象,然后由控制器基于新模板重新创建新版本资源对象。

  rollingUpdate:滚动更新,简单定义 更新期间pod最多有几个等。可以指定maxUnavailable 和 **maxSurge **来控制 rolling update 进程。

  maxSurge:.spec.strategy.rollingUpdate.maxSurge 是可选配置项,用来指定可以超过期望的Pod数量的最大个数。该值可以是一个绝对值(例如5)或者是期望的Pod数量的百分比(例如10%)。当MaxUnavailable为0时该值不可以为0。通过百分比计算的绝对值向上取整。默认值是1。

  **例如,**该值设置成30%,启动rolling update后新的ReplicatSet将会立即扩容,新老Pod的总数不能超过期望的Pod数量的130%。旧的Pod被杀掉后,新的ReplicaSet将继续扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻所有的Pod数量和不会超过期望Pod数量的130%。

  maxUnavailable:.spec.strategy.rollingUpdate.maxUnavailable 是可选配置项,用来指定在升级过程中不可用Pod的最大数量。该值可以是一个绝对值(例如5),也可以是期望Pod数量的百分比(例如10%)。通过计算百分比的绝对值向下取整。 如果.spec.strategy.rollingUpdate.maxSurge 为0时,这个值不可以为0。默认值是1。

  **例如,**该值设置成30%,启动rolling update后旧的ReplicatSet将会立即缩容到期望的Pod数量的70%。新的Pod ready后,随着新的ReplicaSet的扩容,旧的ReplicaSet会进一步缩容确保在升级的所有时刻可以用的Pod数量至少是期望Pod数量的70%。

img

PS:maxSurge和maxUnavailable的属性值不可同时为0,否则Pod对象的副本数量在符合用户期望的数量后无法做出合理变动以进行更新操作。

在配置时,用户还可以使用Deployment控制器的spec.minReadySeconds属性来控制应用升级的速度。新旧更替过程中,新创建的Pod对象一旦成功响应就绪探测即被认为是可用状态,然后进行下一轮的替换。而spec.minReadySeconds能够定义在新的Pod对象创建后至少需要等待多长的时间才能会被认为其就绪,在该段时间内,更新操作会被阻塞。

revisionHistoryLimit(历史版本记录):
  Deployment revision history存储在它控制的ReplicaSets中。默认保存记录10个  

  .spec.revisionHistoryLimit 是一个可选配置项,用来指定可以保留的旧的ReplicaSet数量。该理想值取决于心Deployment的频率和稳定性。如果该值没有设置的话,默认所有旧的Replicaset或会被保留,将资源存储在etcd中,是用kubectl get rs查看输出。每个Deployment的该配置都保存在ReplicaSet中,然而,一旦删除的旧的RepelicaSet,Deployment就无法再回退到那个revison了。

  如果将该值设置为0,所有具有0个replica的ReplicaSet都会被删除。在这种情况下,新的Deployment rollout无法撤销,因为revision history都被清理掉了。

PS:为了保存版本升级的历史,需要再创建Deployment对象时,在命令中使用”–record”选项

rollbackTo:    

  .spec.rollbackTo 是一个可以选配置项,用来配置Deployment回退的配置。设置该参数将触发回退操作,每次回退完成后,该值就会被清除。

   revision:.spec.rollbackTo.revision是一个可选配置项,用来指定回退到的revision。默认是0,意味着回退到上一个revision。

progressDeadlineSeconds:  

  .spec.progressDeadlineSeconds 是可选配置项,用来指定在系统报告Deployment的failed progressing——表现为resource的状态中type=ProgressingStatus=FalseReason=ProgressDeadlineExceeded前可以等待的Deployment进行的秒数。Deployment controller会继续重试该Deployment。未来,在实现了自动回滚后, deployment controller在观察到这种状态时就会自动回滚。

  如果设置该参数,该值必须大于 .spec.minReadySeconds

paused:

.spec.paused是可以可选配置项,boolean值。用来指定暂停和恢复Deployment。Paused和没有paused的Deployment之间的唯一区别就是,所有对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout。Deployment被创建之后默认是非paused。 **

2、创建Deployment

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
[root@k8s-master ~]# vim deploy-demo.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: myapp
release: canary
template:
metadata:
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
ports:
- name: http
containerPort: 80

[root@k8s-master ~]# kubectl apply -f deploy-demo.yaml
[root@k8s-master ~]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
myapp-deploy 2 0 0 0 1s
[root@k8s-master ~]# kubectl get rs

输出结果表明我们希望的repalica数是2(根据deployment中的.spec.replicas配置)当前replica数( .status.replicas)是0, 最新的replica数(.status.updatedReplicas)是0,可用的replica数(.status.availableReplicas)是0。过几秒后再执行get命令,将获得如下输出:

1
2
3
[root@k8s-master ~]# kubectl get deploy
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
myapp-deploy 2 2 2 2 10s

我们可以看到Deployment已经创建了2个 replica,所有的 replica 都已经是最新的了(包含最新的pod template),可用的(根据Deployment中的.spec.minReadySeconds声明,处于已就绪状态的pod的最少个数)。执行kubectl get rskubectl get pods会显示ReplicaSet(RS)和Pod已创建。

1
2
3
[root@k8s-master ~]# kubectl get rs
NAME DESIRED CURRENT READY AGE
myapp-deploy-2035384211 2 2 0 18s

ReplicaSet 的名字总是<Deployment的名字>-<pod template的hash值>

1
2
3
4
[root@k8s-master ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myapp-deploy-2035384211-7ci7o 1/1 Running 0 10s app=myapp,release=canary,pod-template-hash=2035384211
myapp-deploy-2035384211-kzszj 1/1 Running 0 10s app=myapp,release=canary,pod-template-hash=2035384211

刚创建的Replica Set将保证总是有2个myapp的 pod 存在。

注意: 在 Deployment 中的 selector 指定正确的 pod template label(在该示例中是 app = myapp,release=canary),不要跟其他的 controller 的 selector 中指定的 pod template label 搞混了(包括 Deployment、Replica Set、Replication Controller 等)。

上面示例输出中的 pod label 里的 pod-template-hash label。当 Deployment 创建或者接管 ReplicaSet 时,Deployment controller 会自动为 Pod 添加 pod-template-hash label。这样做的目的是防止 Deployment 的子ReplicaSet 的 pod 名字重复。通过将 ReplicaSet 的PodTemplate 进行哈希散列,使用生成的哈希值作为 label 的值,并添加到 ReplicaSet selector 里、 pod template label 和 ReplicaSet 管理中的 Pod 上。

3、Deployment更新升级

(1)通过直接更改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
[root@k8s-master ~]# vim deploy-demo.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deploy
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: myapp
release: canary
template:
metadata:
labels:
app: myapp
release: canary
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v2
ports:
- name: http
containerPort: 80
[root@k8s-master ~]# kubectl apply -f deploy.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
[root@k8s-master ~]# kubectl get pods -l app=myapp -w
NAME READY STATUS RESTARTS AGE
myapp-deploy-f4bcc4799-cs5xc 1/1 Running 0 23m
myapp-deploy-f4bcc4799-cwzd9 1/1 Running 0 14m
myapp-deploy-f4bcc4799-k4tq5 1/1 Running 0 23m
myapp-deploy-f4bcc4799-shbmb 1/1 Running 0 14m
myapp-deploy-f4bcc4799-vtk6m 1/1 Running 0 14m


myapp-deploy-f4bcc4799-shbmb 1/1 Terminating 0 16m
myapp-deploy-869b888f66-mv5c6 0/1 Pending 0 0s
myapp-deploy-869b888f66-mv5c6 0/1 Pending 0 0s
myapp-deploy-869b888f66-vk9j6 0/1 Pending 0 0s
myapp-deploy-869b888f66-vk9j6 0/1 Pending 0 0s
myapp-deploy-869b888f66-mv5c6 0/1 ContainerCreating 0 0s
myapp-deploy-869b888f66-r57t5 0/1 Pending 0 0s
myapp-deploy-869b888f66-r57t5 0/1 Pending 0 0s
myapp-deploy-869b888f66-vk9j6 0/1 ContainerCreating 0 1s
myapp-deploy-869b888f66-r57t5 0/1 ContainerCreating 0 1s
myapp-deploy-869b888f66-r57t5 0/1 ContainerCreating 0 1s
myapp-deploy-869b888f66-mv5c6 0/1 ContainerCreating 0 1s
myapp-deploy-869b888f66-vk9j6 0/1 ContainerCreating 0 2s
myapp-deploy-f4bcc4799-shbmb 0/1 Terminating 0 16m
myapp-deploy-f4bcc4799-shbmb 0/1 Terminating 0 16m
myapp-deploy-869b888f66-r57t5 1/1 Running 0 4s
myapp-deploy-f4bcc4799-vtk6m 1/1 Terminating 0 16m
myapp-deploy-869b888f66-rxzbb 0/1 Pending 0 1s
myapp-deploy-869b888f66-rxzbb 0/1 Pending 0 1s
myapp-deploy-869b888f66-rxzbb 0/1 ContainerCreating 0 1s
myapp-deploy-869b888f66-vk9j6 1/1 Running 0 5s
myapp-deploy-f4bcc4799-cwzd9 1/1 Terminating 0 16m
myapp-deploy-869b888f66-vvwwv 0/1 Pending 0 0s
myapp-deploy-869b888f66-vvwwv 0/1 Pending 0 0s
.....

查看一下 rs的情况,以下可以看到原的rs作为备份,而现在是启动新的rs

1
2
3
4
[root@k8s-master ~]# kubectl get rs -o wide
NAME DESIRED CURRENT READY AGE CONTAINER(S) IMAGE(S) SELECTOR
myapp-deploy-869b888f66 2 2 2 3m myapp-containers ikubernetes/myapp:v2 app=myapp,pod-template-hash=4256444922,release=canary
myapp-deploy-f4bcc4799 0 0 0 29m myapp-containers ikubernetes/myapp:v1 app=myapp,pod-template-hash=906770355,release=canary

(2)通过set 命令直接修改image的版本进行升级,如下:

1
[root@k8s-master ~]# kubectl set image deployment/myapp-deploy myapp=ikubernetes/myapp:v2

4、Deployment扩容

(1)使用以下命令扩容 Deployment:

1
[root@k8s-master ~]# kubectl scale deployment myapp-deploy --replicas 5

(2)直接修改yaml文件的方式进行扩容:

1
2
3
4
5
[root@k8s-master ~]# vim demo.yaml
修改.spec.replicas的值
spec:
replicas: 5
[root@k8s-master ~]# kubectl apply -f demo.yaml

(3)通过打补丁的方式进行扩容:

1
2
[root@k8s-master ~]# kubectl patch deployment myapp-deploy -p '{"spec":{"replicas":5}}'
[root@k8s-master ~]# kuebctl get pods

5、修改滚动更新策略

可以通过打补丁的方式进行修改更新策略,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@k8s-master ~]# kubectl patch deployment myapp-deploy -p '{"spec":{"strategy":{"rollingupdate":{"maxsurge“:1,"maxUnavailable":0}}}}'
[root@k8s-master ~]# kubectl describe deploy myapp-deploy
Name: myapp-deploy
Namespace: default
CreationTimestamp: Tue, 28 Aug 2018 09:52:03 -0400
Labels: app=myapp
release=canary
Annotations: deployment.kubernetes.io/revision=4
kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{},"name":"myapp-deploy","namespace":"default"},"spec":{"replicas":3,"selector":{...
Selector: app=myapp,release=dev
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 0 max unavailable, 1 max surge
Pod Template:
....

6、金丝雀发布

Deployment控制器支持自定义控制更新过程中的滚动节奏,如“暂停(pause)”或“继续(resume)”更新操作。比如等待第一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布(Canary Release),如下命令演示:

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
43
44
45
46
47
48
49
50
51
(1)更新deployment的v3版本,并配置暂停deployment
[root@k8s-master ~]# kubectl set image deployment myapp-deploy myapp=ikubernetes/myapp:v3 && kubectl rollout pause deployment myapp-deploy
deployment "myapp-deploy" image updated
deployment "myapp-deploy" paused
[root@k8s-master ~]# kubectl rollout status deployments myapp-deploy  #观察更新状态

(2)监控更新的过程,可以看到已经新增了一个资源,但是并未按照预期的状态去删除一个旧的资源,就是因为使用了pause暂停命令
[root@k8s-master ~]# kubectl get pods -l app=myapp -w
NAME READY STATUS RESTARTS AGE
myapp-deploy-869b888f66-dpwvk 1/1 Running 0 24m
myapp-deploy-869b888f66-frspv 1/1 Running 0 24m
myapp-deploy-869b888f66-sgsll 1/1 Running 0 24m
myapp-deploy-7cbd5b69b9-5s4sq 0/1 Pending 0 0s
myapp-deploy-7cbd5b69b9-5s4sq 0/1 Pending 0 0s
myapp-deploy-7cbd5b69b9-5s4sq 0/1 ContainerCreating 0 1s
myapp-deploy-7cbd5b69b9-5s4sq 0/1 ContainerCreating 0 2s
myapp-deploy-7cbd5b69b9-5s4sq 1/1 Running 0 19s

(3)确保更新的pod没问题了,继续更新
[root@k8s-master ~]# kubectl rollout resume deploy myapp-deploy

(4)查看最后的更新情况
[root@k8s-master ~]# kubectl get pods -l app=myapp -w
NAME READY STATUS RESTARTS AGE
myapp-deploy-869b888f66-dpwvk 1/1 Running 0 24m
myapp-deploy-869b888f66-frspv 1/1 Running 0 24m
myapp-deploy-869b888f66-sgsll 1/1 Running 0 24m
myapp-deploy-7cbd5b69b9-5s4sq 0/1 Pending 0 0s
myapp-deploy-7cbd5b69b9-5s4sq 0/1 Pending 0 0s
myapp-deploy-7cbd5b69b9-5s4sq 0/1 ContainerCreating 0 1s
myapp-deploy-7cbd5b69b9-5s4sq 0/1 ContainerCreating 0 2s
myapp-deploy-7cbd5b69b9-5s4sq 1/1 Running 0 19s
myapp-deploy-869b888f66-dpwvk 1/1 Terminating 0 31m
myapp-deploy-7cbd5b69b9-p6kzm 0/1 Pending 0 1s
myapp-deploy-7cbd5b69b9-p6kzm 0/1 Pending 0 1s
myapp-deploy-7cbd5b69b9-p6kzm 0/1 ContainerCreating 0 1s
myapp-deploy-7cbd5b69b9-p6kzm 0/1 ContainerCreating 0 2s
myapp-deploy-869b888f66-dpwvk 0/1 Terminating 0 31m
myapp-deploy-869b888f66-dpwvk 0/1 Terminating 0 31m
myapp-deploy-869b888f66-dpwvk 0/1 Terminating 0 31m
myapp-deploy-7cbd5b69b9-p6kzm 1/1 Running 0 18s
myapp-deploy-869b888f66-frspv 1/1 Terminating 0 31m
myapp-deploy-7cbd5b69b9-q8mvs 0/1 Pending 0 0s
myapp-deploy-7cbd5b69b9-q8mvs 0/1 Pending 0 0s
myapp-deploy-7cbd5b69b9-q8mvs 0/1 ContainerCreating 0 0s
myapp-deploy-7cbd5b69b9-q8mvs 0/1 ContainerCreating 0 1s
myapp-deploy-869b888f66-frspv 0/1 Terminating 0 31m
myapp-deploy-869b888f66-frspv 0/1 Terminating 0 31m
myapp-deploy-869b888f66-frspv 0/1 Terminating 0 31m
myapp-deploy-869b888f66-frspv 0/1 Terminating 0 31m
......

7**、Deployment版本回退**

  默认情况下,kubernetes 会在系统中保存前两次的 Deployment 的 rollout 历史记录,以便可以随时回退(您可以修改revision history limit来更改保存的revision数)。

  注意: 只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如.spec.template)被更改,例如更新template 中的 label 和容器镜像时,就会创建出一个新的 revision。

其他的更新,比如扩容 Deployment 不会创建 revision——因此我们可以很方便的手动或者自动扩容。这意味着当您回退到历史 revision 时,只有 Deployment 中的 Pod template 部分才会回退。

1
2
3
4
5
6
7
[root@k8s-master ~]#  kubectl rollout history deploy  myapp-deploy  #检查Deployment升级记录
deployments "myapp-deploy"
REVISION CHANGE-CAUSE
0 <none>
3 <none>
4 <none>
5 <none>

这里在创建deployment时没有增加–record参数,所以并不能看到revision的变化。在创建 Deployment 的时候使用了--record参数可以记录命令,就可以方便的查看每次 revision 的变化。

查看单个revision 的详细信息:

1
[root@k8s-master ~]# kubectl rollout history deployment/myapp-deploy --revision=2

回退历史版本,默认是回退到上一个版本:

1
2
[root@k8s-master ~]# kubectl rollout undo deployment/myapp-deploy
deployment "myapp-deploy" rolled back

也可以使用 --revision参数指定某个历史版本:

1
2
[root@k8s-master ~]# kubectl rollout undo deployment/myapp-deploy --to-revision=2
deployment "myapp-deploy" rolled back

k8s学习笔记-11-Pod状态和生命周期管理

[TOC]

一、什么是Pod?

Pod是kubernetes中你可以创建和部署的最小也是最简的单位。一个Pod代表着集群中运行的一个进程。

Pod中封装着应用的容器(有的情况下是好几个容器),存储、独立的网络IP,管理容器如何运行的策略选项。Pod代表着部署的一个单位:kubernetes中应用的一个实例,可能由一个或者多个容器组合在一起共享资源。

在Kubrenetes集群中Pod有如下两种使用方式:

  • 一个Pod中运行一个容器。“每个Pod中一个容器”的模式是最常见的用法;在这种使用方式中,你可以把Pod想象成是单个容器的封装,kuberentes管理的是Pod而不是直接管理容器。
  • 在一个Pod中同时运行多个容器。一个Pod中也可以同时封装几个需要紧密耦合互相协作的容器,它们之间共享资源。这些在同一个Pod中的容器可以互相协作成为一个service单位——一个容器共享文件,另一个“sidecar”容器来更新这些文件。Pod将这些容器的存储资源作为一个实体来管理。

Pod中共享的环境包括Linux的namespace,cgroup和其他可能的隔绝环境,这一点跟Docker容器一致。在Pod的环境中,每个容器中可能还有更小的子隔离环境。

Pod中的容器共享IP地址和端口号,它们之间可以通过localhost互相发现。它们之间可以通过进程间通信,需要明白的是同一个Pod下的容器是通过lo网卡进行通信。例如SystemV信号或者POSIX共享内存。不同Pod之间的容器具有不同的IP地址,不能直接通过IPC通信。

Pod中的容器也有访问共享volume的权限,这些volume会被定义成pod的一部分并挂载到应用容器的文件系统中。

就像每个应用容器,pod被认为是临时实体。在Pod的生命周期中,pod被创建后,被分配一个唯一的ID(UID),调度到节点上,并一致维持期望的状态直到被终结(根据重启策略)或者被删除。如果node死掉了,分配到了这个node上的pod,在经过一个超时时间后会被重新调度到其他node节点上。一个给定的pod(如UID定义的)不会被“重新调度”到新的节点上,而是被一个同样的pod取代,如果期望的话甚至可以是相同的名字,但是会有一个新的UID(查看replication controller获取详情)。

二、Pod中如何管理多个容器?

Pod中可以同时运行多个进程(作为容器运行)协同工作。同一个Pod中的容器会自动的分配到同一个 node 上。同一个Pod中的容器共享资源、网络环境和依赖,它们总是被同时调度。

注意在一个Pod中同时运行多个容器是一种比较高级的用法。只有当你的容器需要紧密配合协作的时候才考虑用这种模式。例如,你有一个容器作为web服务器运行,需要用到共享的volume,有另一个“sidecar”容器来从远端获取资源更新这些文件,如下图所示:

img

Pod中可以共享两种资源:网络和存储。

  • 网络

  每个Pod都会被分配一个唯一的IP地址。Pod中的所有容器共享网络空间,包括IP地址和端口。Pod内部的容器可以使用localhost互相通信。Pod中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)。

  • 存储:

  可以Pod指定多个共享的Volume。Pod中的所有容器都可以访问共享的volume。Volume也可以用来持久化Pod中的存储资源,以防容器重启后文件丢失。

三、使用Pod

通常把Pod分为两类:

  • **自主式Pod :**这种Pod本身是不能自我修复的,当Pod被创建后(不论是由你直接创建还是被其他Controller),都会被Kuberentes调度到集群的Node上。直到Pod的进程终止、被删掉、因为缺少资源而被驱逐、或者Node故障之前这个Pod都会一直保持在那个Node上。Pod不会自愈。如果Pod运行的Node故障,或者是调度器本身故障,这个Pod就会被删除。同样的,如果Pod所在Node缺少资源或者Pod处于维护状态,Pod也会被驱逐。
  • **控制器管理的Pod:**Kubernetes使用更高级的称为Controller的抽象层,来管理Pod实例。Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。虽然可以直接使用Pod,但是在Kubernetes中通常是使用Controller来管理Pod的。如下图:

每个Pod都有一个特殊的被称为“根容器”的Pause 容器。 Pause容器对应的镜像属于Kubernetes平台的一部分,除了Pause容器,每个Pod还包含一个或者多个紧密相关的用户业务容器。

img

Kubernetes设计这样的Pod概念和特殊组成结构有什么用意?????

原因一:在一组容器作为一个单元的情况下,难以对整体的容器简单地进行判断及有效地进行行动。比如,一个容器死亡了,此时是算整体挂了么?那么引入与业务无关的Pause容器作为Pod的根容器,以它的状态代表着整个容器组的状态,这样就可以解决该问题。

原因二:Pod里的多个业务容器共享Pause容器的IP,共享Pause容器挂载的Volume,这样简化了业务容器之间的通信问题,也解决了容器之间的文件共享问题。

四、Pod的持久性和终止

(1)Pod的持久性

Pod在设计支持就不是作为持久化实体的。在调度失败、节点故障、缺少资源或者节点维护的状态下都会死掉会被驱逐

通常,用户不需要手动直接创建Pod,而是应该使用controller(例如Deployments),即使是在创建单个Pod的情况下。Controller可以提供集群级别的自愈功能、复制和升级管理。

(2)Pod的终止

因为Pod作为在集群的节点上运行的进程,所以在不再需要的时候能够优雅的终止掉是十分必要的(比起使用发送KILL信号这种暴力的方式)。用户需要能够放松删除请求,并且知道它们何时会被终止,是否被正确的删除。用户想终止程序时发送删除pod的请求,在pod可以被强制删除前会有一个宽限期,会发送一个TERM请求到每个容器的主进程。一旦超时,将向主进程发送KILL信号并从API server中删除。如果kubelet或者container manager在等待进程终止的过程中重启,在重启后仍然会重试完整的宽限期。

示例流程如下:

  1. 用户发送删除pod的命令,默认宽限期是30秒;
  2. 在Pod超过该宽限期后API server就会更新Pod的状态为“dead”;
  3. 在客户端命令行上显示的Pod状态为“terminating”;
  4. 跟第三步同时,当kubelet发现pod被标记为“terminating”状态时,开始停止pod进程:
    1. 如果在pod中定义了preStop hook,在停止pod前会被调用。如果在宽限期过后,preStop hook依然在运行,第二步会再增加2秒的宽限期;
    2. 向Pod中的进程发送TERM信号;
  5. 跟第三步同时,该Pod将从该service的端点列表中删除,不再是replication controller的一部分。关闭的慢的pod将继续处理load balancer转发的流量;
  6. 过了宽限期后,将向Pod中依然运行的进程发送SIGKILL信号而杀掉进程。
  7. Kublete会在API server中完成Pod的的删除,通过将优雅周期设置为0(立即删除)。Pod在API中消失,并且在客户端也不可见。

删除宽限期默认是30秒。kubectl delete 命令支持 —grace-period=<seconds> 选项,允许用户设置自己的宽限期。如果设置为0将强制删除pod。在kubectl>=1.5版本的命令中,你必须同时使用 --force--grace-period=0 来强制删除pod。

Pod的强制删除是通过在集群和etcd中将其定义为删除状态。当执行强制删除命令时,API server不会等待该pod所运行在节点上的kubelet确认,就会立即将该pod从API server中移除,这时就可以创建跟原pod同名的pod了。这时,在节点上的pod会被立即设置为terminating状态,不过在被强制删除之前依然有一小段优雅删除周期。

五、Pause容器

Pause容器,又叫Infra容器。我们检查node节点的时候会发现每个node上都运行了很多的pause容器,例如如下。

1
2
3
4
5
6
7
[root@k8s-node01 ~]# docker ps |grep pause
0cbf85d4af9e k8s.gcr.io/pause:3.1 "/pause" 7 days ago Up 7 days k8s_POD_myapp-848b5b879b-ksgnv_default_0af41a40-a771-11e8-84d2-000c2972dc1f_0
d6e4d77960a7 k8s.gcr.io/pause:3.1 "/pause" 7 days ago Up 7 days k8s_POD_myapp-848b5b879b-5f69p_default_09bc0ba1-a771-11e8-84d2-000c2972dc1f_0
5f7777c55d2a k8s.gcr.io/pause:3.1 "/pause" 7 days ago Up 7 days k8s_POD_kube-flannel-ds-pgpr7_kube-system_23dc27e3-a5af-11e8-84d2-000c2972dc1f_1
8e56ef2564c2 k8s.gcr.io/pause:3.1 "/pause" 7 days ago Up 7 days k8s_POD_client2_default_17dad486-a769-11e8-84d2-000c2972dc1f_1
7815c0d69e99 k8s.gcr.io/pause:3.1 "/pause" 7 days ago Up 7 days k8s_POD_nginx-deploy-5b595999-872c7_default_7e9df9f3-a6b6-11e8-84d2-000c2972dc1f_2
b4e806fa7083 k8s.gcr.io/pause:3.1 "/pause" 7 days ago Up 7 days k8s_POD_kube-proxy-vxckf_kube-system_23dc0141-a5af-11e8-84d2-000c2972dc1f_2

kubernetes中的pause容器主要为每个业务容器提供以下功能:

  • 在pod中担任Linux命名空间共享的基础;
  • 启用pid命名空间,开启init进程。

如图:

img

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@k8s-node01 ~]# docker run -d --name pause -p 8880:80 k8s.gcr.io/pause:3.1
d3057ceb54bc6565d28ded2c33ad2042010be73d76117775c130984c3718d609
[root@k8s-node01 ~]# cat <<EOF >> nginx.conf
> error_log stderr;
> events { worker_connections 1024; }
> http {
> access_log /dev/stdout combined;
> server {
> listen 80 default_server;
> server_name example.com www.example.com;
> location / {
> proxy_pass http://127.0.0.1:2368;
> }
> }
> }
> EOF
[root@k8s-node01 ~]# docker run -d --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause nginx
d04f848b7386109085ee350ebb81103e4efc7df8e48da18404efb9712f926082
[root@k8s-node01 ~]# docker run -d --name ghost --net=container:pause --ipc=container:pause --pid=container:pause ghost
332c86a722f71680b76b3072e85228a8d8e9608456c653edd214f06c2a77f112

现在访问http://192.168.56.12:8880/ 就可以看到ghost博客的界面了。

解析

pause容器将内部的80端口映射到宿主机的8880端口,pause容器在宿主机上设置好了网络namespace后,nginx容器加入到该网络namespace中,我们看到nginx容器启动的时候指定了--net=container:pause,ghost容器同样加入到了该网络namespace中,这样三个容器就共享了网络,互相之间就可以使用localhost直接通信,--ipc=contianer:pause --pid=container:pause就是三个容器处于同一个namespace中,init进程为pause,这时我们进入到ghost容器中查看进程情况。

1
2
3
4
5
6
7
8
9
[root@k8s-node01 ~]# docker exec -it ghost /bin/bash
root@d3057ceb54bc:/var/lib/ghost# ps axu
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 1012 4 ? Ss 03:48 0:00 /pause
root 6 0.0 0.0 32472 780 ? Ss 03:53 0:00 nginx: master process nginx -g daemon off;
systemd+ 11 0.0 0.1 32932 1700 ? S 03:53 0:00 nginx: worker process
node 12 0.4 7.5 1259816 74868 ? Ssl 04:00 0:07 node current/index.js
root 77 0.6 0.1 20240 1896 pts/0 Ss 04:29 0:00 /bin/bash
root 82 0.0 0.1 17496 1156 pts/0 R+ 04:29 0:00 ps axu

在ghost容器中同时可以看到pause和nginx容器的进程,并且pause容器的PID是1。而在kubernetes中容器的PID=1的进程即为容器本身的业务进程。

六、init容器

Pod 能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的 Init 容器。

Init 容器与普通的容器非常像,除了如下两点:

  • Init 容器总是运行到成功完成为止。
  • 每个 Init 容器都必须在下一个 Init 容器启动之前成功完成。

如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,如果 Pod 对应的 restartPolicy 为 Never,它不会重新启动。

七、Pod的生命周期

(1)Pod phase(Pod的相位)

Pod 的 status 在信息保存在 PodStatus 中定义,其中有一个 phase 字段。

Pod 的相位(phase)是 Pod 在其生命周期中的简单宏观概述。该阶段并不是对容器或 Pod 的综合汇总,也不是为了做为综合状态机。

Pod 相位的数量和含义是严格指定的。除了本文档中列举的状态外,不应该再假定 Pod 有其他的 phase值。

下面是 phase 可能的值:

  • 挂起(Pending):API Server创建了Pod资源对象并已经存入了etcd中,但是它并未被调度完成,或者仍然处于从仓库下载镜像的过程中。
  • 运行中(Running):Pod已经被调度到某节点之上,并且所有容器都已经被kubelet创建完成。
  • 成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启。
  • 失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容器以非0状态退出或者被系统终止。
  • 未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败。

下图是Pod的生命周期示意图,从图中可以看到Pod状态的变化。

img

(2)Pod的创建过程

Pod是Kubernetes的基础单元,了解其创建的过程,更有助于理解系统的运作。

①用户通过kubectl或其他API客户端提交Pod Spec给API Server。

②API Server尝试将Pod对象的相关信息存储到etcd中,等待写入操作完成,API Server返回确认信息到客户端。

③API Server开始反映etcd中的状态变化。

④所有的Kubernetes组件通过”watch”机制跟踪检查API Server上的相关信息变动。

⑤kube-scheduler(调度器)通过其”watcher”检测到API Server创建了新的Pod对象但是没有绑定到任何工作节点。

⑥kube-scheduler为Pod对象挑选一个工作节点并将结果信息更新到API Server。

⑦调度结果新消息由API Server更新到etcd,并且API Server也开始反馈该Pod对象的调度结果。

⑧Pod被调度到目标工作节点上的kubelet尝试在当前节点上调用docker engine进行启动容器,并将容器的状态结果返回到API Server。

⑨API Server将Pod信息存储到etcd系统中。

⑩在etcd确认写入操作完成,API Server将确认信息发送到相关的kubelet。

(3)Pod的状态

Pod 有一个 PodStatus 对象,其中包含一个 PodCondition 数组。 PodCondition 数组的每个元素都有一个 type 字段和一个 status 字段。type 字段是字符串,可能的值有 PodScheduled、Ready、Initialized 和 Unschedulable。status 字段是一个字符串,可能的值有 True、False 和 Unknown。

(4)Pod存活性探测

在pod生命周期中可以做的一些事情。主容器启动前可以完成初始化容器,初始化容器可以有多个,他们是串行执行的,执行完成后就退出了,在主程序刚刚启动的时候可以指定一个post start 主程序启动开始后执行一些操作,在主程序结束前可以指定一个 pre stop 表示主程序结束前执行的一些操作。在程序启动后可以做两类检测 liveness probe(存活性探测) 和 readness probe(就绪性探测)。如下图:

img

探针是由 kubelet 对容器执行的定期诊断。要执行诊断,kubelet 调用由容器实现的Handler。其存活性探测的方法有以下三种:

  • **ExecAction:**在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
  • **TCPSocketAction:**对指定端口上的容器的 IP 地址进行 TCP 检查。如果端口打开,则诊断被认为是成功的。
  • **HTTPGetAction:**对指定的端口和路径上的容器的 IP 地址执行 HTTP Get 请求。如果响应的状态码大于等于200 且小于 400,则诊断被认为是成功的。

设置exec探针举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness-exec
name: liveness-exec
spec:
containers:
- name: liveness-exec-demo
image: busybox
args: ["/bin/sh","-c","touch /tmp/healthy;sleep 60;rm -rf /tmp/healthy;"sleep 600]
livenessProbe:
exec:
command: ["test","-e","/tmp/healthy"]

上面的资源清单中定义了一个Pod 对象, 基于 busybox 镜像 启动 一个 运行touch/ tmp/ healthy; sleep 60; rm- rf/ tmp/ healthy; sleep 600 命令 的 容器, 此 命令 在 容器 启动 时 创建/ tmp/ healthy 文件, 并于 60 秒 之后 将其 删除。 存活性探针运行“ test -e/ tmp/ healthy” 命令 检查/ tmp/healthy 文件 的 存在 性, 若 文件 存在 则 返回 状态 码 0, 表示 成功 通过 测试。

设置HTTP探针举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness-http
name: liveness-http
spec:
containers:
- name: liveness-http-demo
image: nginx:1.12-alpine
ports:
- name: http
containerPort: 80
lifecycle:
postStart:
exec:
command: ["/bin/sh","-c","echo healthy > /usr/share/nginx/html/healthy"]
livenessProbe:
httpGet:
path: /healthy
port: http
scheme: HTTP

上面 清单 文件 中 定义 的 httpGet 测试 中, 请求 的 资源 路径 为“/ healthy”, 地址 默认 为 Pod IP, 端口 使用 了 容器 中 定义 的 端口 名称 HTTP, 这也 是 明确 为 容器 指明 要 暴露 的 端口 的 用途 之一。

设置TCP探针举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness-tcp
name: liveness-tcp
spec:
containers:
- name: liveness-tcp-demo
image: nginx:1.12-alpine
ports:
- name: http
containerPort: 80
livenessProbe:
tcpSocket:
port: http

上面的资源清单文件,向Pod IP的80/tcp端口发起连接请求,并根据连接建立的状态判断Pod存活状态。

每次探测都将获得以下三种结果之一:

  • 成功:容器通过了诊断。
  • 失败:容器未通过诊断。
  • 未知:诊断失败,因此不会采取任何行动。

Kubelet 可以选择是否执行在容器上运行的两种探针执行和做出反应:

  • **livenessProbe:**指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将受到其 重启策略 的影响。如果容器不提供存活探针,则默认状态为 Success
  • **readinessProbe:**指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容器不提供就绪探针,则默认状态为 Success

(5)livenessProbe和readinessProbe使用场景

如果容器中的进程能够在遇到问题或不健康的情况下自行崩溃,则不一定需要存活探针; kubelet 将根据 Pod 的restartPolicy 自动执行正确的操作。

如果希望容器在探测失败时被杀死并重新启动,那么请指定一个存活探针,并指定restartPolicy 为 Always 或 OnFailure。

如果要仅在探测成功时才开始向 Pod 发送流量,请指定就绪探针。在这种情况下,就绪探针可能与存活探针相同,但是 spec 中的就绪探针的存在意味着 Pod 将在没有接收到任何流量的情况下启动,并且只有在探针探测成功后才开始接收流量。

如果您希望容器能够自行维护,您可以指定一个就绪探针,该探针检查与存活探针不同的端点。

请注意,如果您只想在 Pod 被删除时能够排除请求,则不一定需要使用就绪探针;在删除 Pod 时,Pod 会自动将自身置于未完成状态,无论就绪探针是否存在。当等待 Pod 中的容器停止时,Pod 仍处于未完成状态。

(6)Pod的重启策略

PodSpec 中有一个 restartPolicy 字段,可能的值为Always、OnFailure 和 Never。默认为 Always。 restartPolicy 适用于 Pod 中的所有容器。restartPolicy 仅指通过同一节点上的 kubelet 重新启动容器。失败的容器由 kubelet 以五分钟为上限的指数退避延迟(10秒,20秒,40秒…)重新启动,并在成功执行十分钟后重置。pod一旦绑定到一个节点,Pod 将永远不会重新绑定到另一个节点。

(7)Pod的生命

一般来说,Pod 不会消失,直到人为销毁他们。这可能是一个人或控制器。这个规则的唯一例外是成功或失败的 phase 超过一段时间(由 master 确定)的Pod将过期并被自动销毁。

有三种可用的控制器:

  • 使用 Job 运行预期会终止的 Pod,例如批量计算。Job 仅适用于重启策略为 OnFailureNever 的 Pod。

  • 对预期不会终止的 Pod 使用 ReplicationControllerReplicaSetDeployment ,例如 Web 服务器。 ReplicationController 仅适用于具有 restartPolicy 为 Always 的 Pod。

  • 提供特定于机器的系统服务,使用 DaemonSet 为每台机器运行一个 Pod 。

所有这三种类型的控制器都包含一个 PodTemplate。建议创建适当的控制器,让它们来创建 Pod,而不是直接自己创建 Pod。这是因为单独的 Pod 在机器故障的情况下没有办法自动复原,而控制器却可以。

如果节点死亡或与集群的其余部分断开连接,则 Kubernetes 将应用一个策略将丢失节点上的所有 Pod 的 phase 设置为 Failed。

(8)livenessProbe解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@k8s-master ~]# kubectl explain pod.spec.containers.livenessProbe

KIND: Pod
VERSION: v1

RESOURCE: livenessProbe <Object>

exec command 的方式探测 例如 ps 一个进程

failureThreshold 探测几次失败 才算失败 默认是连续三次

periodSeconds 每次的多长时间探测一次 默认10s

timeoutSeconds 探测超市的秒数 默认1s

initialDelaySeconds 初始化延迟探测,第一次探测的时候,因为主程序未必启动完成

tcpSocket 检测端口的探测

httpGet http请求探测

举个例子:定义一个liveness的pod资源类型,基础镜像为busybox,在busybox这个容器启动后会执行创建/tmp/test的文件啊,并删除,然后等待3600秒。随后定义了存活性探测,方式是以exec的方式执行命令判断/tmp/test是否存在,存在即表示存活,不存在则表示容器已经挂了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@k8s-master ~]# vim liveness.yaml

apiVersion: v1
kind: Pod
metadata:
name: liveness-exec-pod
namespace: default
labels:
name: myapp
spec:
containers:
- name: livess-exec
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","touch /tmp/test; sleep 30; rm -f /tmp/test; sleep 3600"]
livenessProbe:
exec:
command: ["test","-e","/tmp/test"]
initialDelaySeconds: 1
periodSeconds: 3
[root@k8s-master ~]# kubectl apply -f lineness.yaml

(9)资源需求和资源限制

在Docker的范畴内,我们知道可以对运行的容器进行请求或消耗的资源进行限制。而在Kubernetes中,也有同样的机制,容器或Pod可以进行申请和消耗的资源就是CPU和内存。CPU属于可压缩型资源,即资源的额度可以按照需求进行收缩。而内存属于不可压缩型资源,对内存的收缩可能会导致无法预知的问题。

资源的隔离目前是属于容器级别,CPU和内存资源的配置需要Pod中的容器spec字段下进行定义。其具体字段,可以使用”requests”进行定义请求的确保资源可用量。也就是说容器的运行可能用不到这样的资源量,但是必须确保有这么多的资源供给。而”limits”是用于限制资源可用的最大值,属于硬限制。

在Kubernetes中,1个单位的CPU相当于虚拟机的1颗虚拟CPU(vCPU)或者是物理机上一个超线程的CPU,它支持分数计量方式,一个核心(1core)相当于1000个微核心(millicores),因此500m相当于是0.5个核心,即二分之一个核心。内存的计量方式也是一样的,默认的单位是字节,也可以使用E、P、T、G、M和K作为单位后缀,或者是Ei、Pi、Ti、Gi、Mi、Ki等形式单位后缀。

资源需求举例:

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx
resources:
requests:
memory: "128Mi"
cpu: "200m"

上面的配置清单中,nginx请求的CPU资源大小为200m,这意味着一个CPU核心足以满足nginx以最快的方式运行,其中对内存的期望可用大小为128Mi,实际运行时不一定会用到这么多的资源。考虑到内存的资源类型,在超出指定大小运行时存在会被OOM killer杀死的可能性,于是该请求值属于理想中使用的内存上限。

资源限制举例:

容器的资源需求只是能够确保容器运行时所需要的最少资源量,但是并不会限制其可用的资源上限。当应用程序存在Bug时,也有可能会导致系统资源被长期占用的情况,这就需要另外一个limits属性对容器进行定义资源使用的最大可用量。CPU是属于可压缩资源,可以进行自由地调节。而内存属于硬限制性资源,当进程申请分配超过limit属性定义的内存大小时,该Pod将会被OOM killer杀死。如下:

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
[root@k8s-master ~]# vim memleak-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: memleak-pod
labels:
app: memleak
spec:
containers:
- name: simmemleak
image: saadali/simmemleak
resources:
requests:
memory: "64Mi"
cpu: "1"
limits:
memory: "64Mi"
cpu: "1"

[root@k8s-master ~]# kubectl apply -f memleak-pod.yaml
pod/memleak-pod created
[root@k8s-master ~]# kubectl get pods -l app=memleak
NAME READY STATUS RESTARTS AGE
memleak-pod 0/1 OOMKilled 2 12s
[root@k8s-master ~]# kubectl get pods -l app=memleak
NAME READY STATUS RESTARTS AGE
memleak-pod 0/1 CrashLoopBackOff 2 28s

Pod资源默认的重启策略为Always,在memleak因为内存限制而终止会立即重启,此时该Pod会被OOM killer杀死,在多次重复因为内存资源耗尽重启会触发Kunernetes系统的重启延迟,每次重启的时间会不断拉长,后面看到的Pod的状态通常为”CrashLoopBackOff”。

这里还需要明确的是,在一个Kubernetes集群上,运行的Pod众多,那么当节点都无法满足多个Pod对象的资源使用时,是按照什么样的顺序去终止这些Pod对象呢??

Kubernetes是无法自行去判断的,需要借助于Pod对象的优先级进行判定终止Pod的优先问题。根据Pod对象的requests和limits属性,Kubernetes将Pod对象分为三个服务质量类别:

  • Guaranteed:每个容器都为CPU和内存资源设置了相同的requests和limits属性的Pod都会自动归属于该类别,属于最高优先级。
  • Burstable:至少有一个容器设置了CPU或内存资源的requests属性,单不满足Guaranteed类别要求的资源归于该类别,属于中等优先级。
  • BestEffort:未对任何容器设置requests属性和limits属性的Pod资源,自动归于该类别,属于最低级别。

顾名思义,最低级别,死得越快!

k8s学习笔记-10-资源清单定义

[TOC]

一、Kubernetes常用资源

以下列举的内容都是 kubernetes 中的 Object,这些对象都可以在 yaml 文件中作为一种 API 类型来配置。

类别 名称
工作负载型资源对象 Pod Replicaset ReplicationController Deployments StatefulSets Daemonset Job CronJob
服务发现及负载均衡 Service Ingress
配置与存储 Volume、Persistent Volume、CSl 、 configmap、 secret
集群资源 Namespace Node Role ClusterRole RoleBinding ClusterRoleBinding
元数据资源 HPA PodTemplate LimitRang

二、理解Kubernetes中的对象

在 Kubernetes 系统中,Kubernetes 对象 是持久化的条目。Kubernetes 使用这些条目去表示整个集群的状态。特别地,它们描述了如下信息:

  • 什么容器化应用在运行(以及在哪个 Node 上)
  • 可以被应用使用的资源
  • 关于应用如何表现的策略,比如重启策略、升级策略,以及容错策略

Kubernetes 对象是 “目标性记录” —— 一旦创建对象,Kubernetes 系统将持续工作以确保对象存在。通过创建对象,可以有效地告知 Kubernetes 系统,所需要的集群工作负载看起来是什么样子的,这就是 Kubernetes 集群的 期望状态。

与 Kubernetes 对象工作 —— 是否创建、修改,或者删除 —— 需要使用 Kubernetes API。当使用 kubectl 命令行接口时,比如,CLI 会使用必要的 Kubernetes API 调用,也可以在程序中直接使用 Kubernetes API。

三、对象的Spec和状态

每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置:对象 spec 和 对象 statusspec 必须提供,它描述了对象的 期望状态—— 希望对象所具有的特征。status 描述了对象的 实际状态,它是由 Kubernetes 系统提供和更新。在任何时刻,Kubernetes 控制平面一直处于活跃状态,管理着对象的实际状态以与我们所期望的状态相匹配。

例如,Kubernetes Deployment 对象能够表示运行在集群中的应用。当创建 Deployment 时,可能需要设置 Deployment 的 spec,以指定该应用需要有 3 个副本在运行。Kubernetes 系统读取 Deployment spec,启动我们所期望的该应用的 3 个实例 —— 更新状态以与 spec 相匹配。如果那些实例中有失败的(一种状态变更),Kubernetes 系统通过修正来响应 spec 和状态之间的不一致 —— 这种情况,启动一个新的实例来替换。

四、Pod的配置格式

当创建 Kubernetes 对象时,必须提供对象的 spec,用来描述该对象的期望状态,以及关于对象的一些基本信息(例如,名称)。当使用 Kubernetes API 创建对象时(或者直接创建,或者基于kubectl),API 请求必须在请求体中包含 JSON 格式的信息。更常用的是,需要在 .yaml 文件中为 kubectl 提供这些信息。 kubectl 在执行 API 请求时,将这些信息转换成 JSON 格式。查看已经部署好的pod的资源定义格式:

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
[root@k8s-master ~]# kubectl get pod myapp-848b5b879b-5f69p -o yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: 2018-08-24T07:40:57Z
generateName: myapp-848b5b879b-
labels:
pod-template-hash: "4046164356"
run: myapp
name: myapp-848b5b879b-5f69p
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: myapp-848b5b879b
uid: caf2ec54-a76f-11e8-84d2-000c2972dc1f
resourceVersion: "90507"
selfLink: /api/v1/namespaces/default/pods/myapp-848b5b879b-5f69p
uid: 09bc0ba1-a771-11e8-84d2-000c2972dc1f
spec:
containers:
- image: ikubernetes/myapp:v1
imagePullPolicy: IfNotPresent
name: myapp
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: default-token-j5pf5
readOnly: true
dnsPolicy: ClusterFirst
nodeName: k8s-node01
priority: 0
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
serviceAccount: default
serviceAccountName: default
terminationGracePeriodSeconds: 30
tolerations:
- effect: NoExecute
key: node.kubernetes.io/not-ready
operator: Exists
tolerationSeconds: 300
- effect: NoExecute
key: node.kubernetes.io/unreachable
operator: Exists
tolerationSeconds: 300
volumes:
- name: default-token-j5pf5
secret:
defaultMode: 420
secretName: default-token-j5pf5
status:
conditions:
- lastProbeTime: null
lastTransitionTime: 2018-08-24T07:40:57Z
status: "True"
type: Initialized
- lastProbeTime: null
lastTransitionTime: 2018-08-24T07:40:59Z
status: "True"
type: Ready
- lastProbeTime: null
lastTransitionTime: null
status: "True"
type: ContainersReady
- lastProbeTime: null
lastTransitionTime: 2018-08-24T07:40:57Z
status: "True"
type: PodScheduled
containerStatuses:
- containerID: docker://a5e7004f45b1ec4a4297e50db6d0b5b11573e36ed8de814ea8b6cdacd13b8f9a
image: ikubernetes/myapp:v1
imageID: docker-pullable://ikubernetes/myapp@sha256:9c3dc30b5219788b2b8a4b065f548b922a34479577befb54b03330999d30d513
lastState: {}
name: myapp
ready: true
restartCount: 0
state:
running:
startedAt: 2018-08-24T07:40:58Z
hostIP: 192.168.56.12
phase: Running
podIP: 10.244.1.11
qosClass: BestEffort
startTime: 2018-08-24T07:40:57Z

创建资源的方法:

  • apiserver在定义资源时,仅接收JSON格式的资源定义;
  • yaml格式提供配置清单,apiservere可自动将其转为json格式,而后再提交;

大部分资源的配置清单格式都由5个一级字段组成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: group/version  指明api资源属于哪个群组和版本,同一个组可以有多个版本
$ kubectl api-versions

kind: 资源类别,标记创建的资源类型,k8s主要支持以下资源类别
Pod,ReplicaSet,Deployment,StatefulSet,DaemonSet,Job,Cronjob

metadata:元数据,主要提供以下字段
name:同一类别下的name是唯一的
namespace:对应的对象属于哪个名称空间
labels:标签,每一个资源都可以有标签,标签是一种键值数据
annotations:资源注解

每个的资源引用方式(selflink):
/api/GROUP/VERSION/namespace/NAMESPACE/TYPE/NAME

spec: 定义目标资源的期望状态(disired state),资源对象中最重要的字段

status: 显示资源的当前状态(current state),本字段由kubernetes进行维护

K8s存在内嵌的格式说明,可以使用kubectl explain 进行查看,如查看Pod这个资源的定义:

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
[root@k8s-master ~]# kubectl explain pods
KIND: Pod
VERSION: v1

DESCRIPTION:
Pod is a collection of containers that can run on a host. This resource is
created by clients and scheduled onto hosts.

FIELDS:
apiVersion <string>
APIVersion defines the versioned schema of this representation of an
object. Servers should convert recognized schemas to the latest internal
value, and may reject unrecognized values. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#resources

kind <string>
Kind is a string value representing the REST resource this object
represents. Servers may infer this from the endpoint the client submits
requests to. Cannot be updated. In CamelCase. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#types-kinds

metadata <Object>
Standard object's metadata. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata

spec <Object>
Specification of the desired behavior of the pod. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status

status <Object>
Most recently observed status of the pod. This data may not be up to date.
Populated by the system. Read-only. More info:
https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status

从上面可以看到apiVersion,kind等定义的键值都是,而metadata和spec看到是一个,当看到存在的提示,说明该字段可以存在多个二级字段,那么可以使用如下命令继续查看二级字段的定义方式:

1
2
[root@k8s-master ~]# kubectl explain pods.metadata
[root@k8s-master ~]# kubectl explain pods.spec

二级字段下,每一种字段都有对应的键值类型,常用类型大致如下:

**<[]string>:**表示是一个字串列表,也就是字串类型的数组

**:**表示是可以嵌套的字段

**<map[string]string>:**表示是一个由键值组成映射

**<[]Object>:**表示是一个对象列表

**<[]Object> -required-:**required表示该字段是一个必选的字段

五、使用配置清单创建自主式Pod资源

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
[root@k8s-master ~]# mkdir mainfests
[root@k8s-master ~]# cd mainfrests
[root@k8s-master mainfrests]# vim pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name:myapp
image: ikubernetes/myapp:v1
- name: busybox
image: busybox:latest
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
[root@k8s-master mainfrests]# kubectl create -f pod-demo.yaml
[root@k8s-master mainfrests]# kubectl get pods
[root@k8s-master mainfrests]# kubectl describe pods pod-demo  #获取pod详细信息
[root@k8s-master mainfrests]# kubectl logs pod-demo myapp
[root@k8s-master mainfrests]# kubectl exec -it pod-demo -c myapp -- /bin/sh

六、Pod资源下spec的containers必需字段解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
[root@k8s-master ~]# kubectl explain pods.spec.containers

name <string> -required- #containers 的名字
image <string> #镜像地址
imagePullPolicy <string> #如果标签是latest 就是Always(总是下载镜像) IfNotPresent(先看本地是否有此镜像,如果没有就下载) Never (就是使用本地镜像)

ports <[]Object> #是给对象列表 可以暴露多个端口 可以对每个端口的属性定义 例如:(名称(可后期调用)端口号 协议 暴露在的地址上) 暴露端口只是提供额外信息的,不能限制系统是否真的暴露

   - containerPort 容器端口

    hostIP 主机地址(基本不会使用)

    hostPort 节点端口

    name 名称

    protocol (默认是TCP)

args <[]string> 传递参数给command 相当于docker中的CMD

command <[]string> 相当于docker中的ENTRYPOINT

如果Pod不提供commandargs使用Container,则使用Docker镜像中的cmd或者ENTRYPOINT。

如果Pod提供command但不提供args,则仅使用提供 command的。将忽略Docker镜像中定义EntryPoint和Cmd。

如果Pod中仅提供args,则args将作为参数提供给Docker镜像中EntryPoint。

如果提供了commandargs,则Docker镜像中的ENTRYPOINT和CMD都将不会生效,Pod中的args将作为参数给command运行

七、标签及标签选择器

1、标签

key=value

  • key:只能使用 字母 数字 _ - . (只能以字母数字开头,不能超过63给字符)
  • value: 可以为空 只能使用 字母 数字开头
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
[root@k8s-master mainfests]# kubectl get pods --show-labels  #查看pod标签
NAME READY STATUS RESTARTS AGE LABELS
pod-demo 2/2 Running 0 25s app=myapp,tier=frontend

[root@k8s-master mainfests]# kubectl get pods -l app  #过滤包含app的标签
NAME READY STATUS RESTARTS AGE
pod-demo 2/2 Running 0 1m
[root@k8s-master mainfests]# kubectl get pods -L app
NAME READY STATUS RESTARTS AGE APP
pod-demo 2/2 Running 0 1m myapp

[root@k8s-master mainfests]# kubectl label pods pod-demo release=canary  #给pod-demo打上标签
pod/pod-demo labeled
[root@k8s-master mainfests]# kubectl get pods -l app --show-labels
NAME READY STATUS RESTARTS AGE LABELS
pod-demo 2/2 Running 0 1m app=myapp,release=canary,tier=frontend

[root@k8s-master mainfests]# kubectl label pods pod-demo release=stable --overwrite  #修改标签
pod/pod-demo labeled
[root@k8s-master mainfests]# kubectl get pods -l release
NAME READY STATUS RESTARTS AGE
pod-demo 2/2 Running 0 2m
[root@k8s-master mainfests]# kubectl get pods -l release,app
NAME READY STATUS RESTARTS AGE
pod-demo 2/2 Running 0 2m

2、标签选择器

  • 等值关系标签选择器:=, == , != (kubectl get pods -l app=test,app=dev)
  • 集合关系标签选择器: KEY in (v1,v2,v3), KEY notin (v1,v2,v3) !KEY (kubectl get pods -l “app in (test,dev)”)

许多资源支持内嵌字段

  • matchLabels: 直接给定建值
  • matchExpressions: 基于给定的表达式来定义使用标签选择器,{key:”KEY”,operator:”OPERATOR”,values:[V1,V2,….]}
  • 操作符: in notin:Values字段的值必须是非空列表 Exists NotExists: Values字段的值必须是空列表

3、节点标签选择器

1
2
3
4
5
6
7
8
9
10
11
[root@k8s-master mainfests]# kubectl explain pod.spec
nodeName <string>
NodeName is a request to schedule this pod onto a specific node. If it is
non-empty, the scheduler simply schedules this pod onto that node, assuming
that it fits resource requirements.

nodeSelector <map[string]string>
NodeSelector is a selector which must be true for the pod to fit on a node.
Selector which must match a node's labels for the pod to be scheduled on
that node. More info:
https://kubernetes.io/docs/concepts/configuration/assign-pod-node/

nodeSelector可以限定pod创建在哪个节点上,举个例子,给节点k8s-node01打上标签disktype=ssd,让pod-demo指定创建在k8s-node01上

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
43
44
45
46
47
48
49
(1)给k8s-node01节点打标签
[root@k8s-master mainfests]# kubectl label nodes k8s-node01 disktype=ssd
node/k8s-node01 labeled

[root@k8s-master mainfests]# kubectl get nodes --show-labels
NAME STATUS ROLES AGE VERSION LABELS
k8s-master Ready master 10d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8s-master,node-role.kubernetes.io/master=
k8s-node01 Ready <none> 10d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,disktype=ssd,kubernetes.io/hostname=k8s-node01
k8s-node02 Ready <none> 9d v1.11.2 beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=k8s-node02

(2)修改yaml文件,增加标签选择器
[root@k8s-master mainfests]# cat pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-demo
namespace: default
labels:
app: myapp
tier: frontend
spec:
containers:
- name: myapp
image: ikubernetes/myapp:v1
- name: busybox
image: busybox:latest
command:
- "/bin/sh"
- "-c"
- "sleep 3600"
nodeSeletor:
disktype: ssd

(3)重新创建pod-demo,可以看到固定调度在k8s-node01节点上
[root@k8s-master mainfests]# kubectl delete -f pod-demo.yaml
pod "pod-demo" deleted

[root@k8s-master mainfests]# kubectl create -f pod-demo.yaml
pod/pod-demo created
[root@k8s-master mainfests]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-demo 2/2 Running 0 20s 10.244.1.13 k8s-node01
[root@k8s-master mainfests]# kubectl describe pod pod-demo
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 42s default-scheduler Successfully assigned default/pod-demo to k8s-node01
......

annotations:

  与label不同的地方在于,annotations不能用于挑选资源对象,仅用于为对象提供”元数据”,没有键值长度限制。

k8s学习笔记-09-kubernetes命令式快速创建应用

[TOC]

1、使用命令kubectl run创建应用

1
2
3
语法:
kubectl run NAME --image=image [--env="key=value"] [--port=port] [--replicas=replicas] [--dry-run=bool]
[--overrides=inline-json] [--command] -- [COMMAND] [args...] [options]

实用举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@k8s-master ~]# kubectl run nginx-deploy --image=nginx:1.14-alpine --port=80 --replicas=1    #创建一个nginx的应用,副本数为1
deployment.apps/nginx-deploy created

[root@k8s-master ~]# kubectl get deployment  #获取应用信息,查看应用是否符合预期状态
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deploy 1 1 1 1 40s

[root@k8s-master ~]# kubectl get pods  #获取pod信息
NAME READY STATUS RESTARTS AGE
nginx-deploy-5b595999-44zwq 1/1 Running 0 1m


[root@k8s-master ~]# kubectl get pods -o wide #查看pod运行在哪个节点上
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deploy-5b595999-44zwq 1/1 Running 0 1m 10.244.2.2 k8s-node02

从上面创建的应用可以得知,nginx-deploy应用的pod的ip为10.244.2.2,这是一个pod ip,仅仅可以在集群内部访问,如下:

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
[root@k8s-master ~]# curl 10.244.2.2 -I
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Thu, 28 Feb 2019 06:13:03 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 06 Jul 2018 16:53:43 GMT
Connection: keep-alive
ETag: "5b3f9e97-264"
Accept-Ranges: bytes

[root@k8s-node01 ~]# curl 10.244.2.2 -I
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Thu, 28 Feb 2019 06:12:04 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 06 Jul 2018 16:53:43 GMT
Connection: keep-alive
ETag: "5b3f9e97-264"
Accept-Ranges: bytes

[root@k8s-node02 ~]# curl 10.244.2.2 -I
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Thu, 23 Aug 2018 09:22:18 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 06 Jul 2018 16:53:43 GMT
Connection: keep-alive
ETag: "5b3f9e97-264"
Accept-Ranges: bytes

这里要注意的是pod的客户端有2类,1类是其他pod,1类是集群外部客户端,那么集群外部的客户端如何访问到pod呢?pod的地址是随时变化的,假设先删除创建的pod:

1
2
[root@k8s-master ~]# kubectl delete pods nginx-deploy-5b595999-44zwq
pod "nginx-deploy-5b595999-44zwq" deleted

要明白pod是通过控制器进行管理的,当控制器发现pod的状态不满足预期的状态时,将会重新创建一个pod

1
2
3
4
5
6
[root@k8s-master ~]# kubectl get pods -o wide    #由于在node01节点上没有镜像,需要重新下载
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deploy-5b595999-872c7 0/1 ContainerCreating 0 24s <none> k8s-node01
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deploy-5b595999-872c7 1/1 Running 0 56s 10.244.1.2 k8s-node01

此时可以看到新建的pod的ip地址已经更改了,并且本次创建的pod是在node01节点上,这样就需要提供一个固定端点,给集群外部客户端进行访问。这个固定端点就是service:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
语法如下:
kubectl expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name]
[--name=name] [--external-ip=external-ip-of-service] [--type=type] [options]

[root@k8s-master ~]# kubectl expose deployment nginx-deploy --name=nginx --port=80 --target-port=80 --protocol=TCP  #创建一个nginx的service
service/nginx exposed

[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1d
nginx ClusterIP 10.106.162.254 <none> 80/TCP 19s

[root@k8s-master ~]# curl 10.106.162.254 -I  #通过ClusterIP进行访问nginx pod
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Thu, 23 Aug 2018 09:38:09 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Fri, 06 Jul 2018 16:53:43 GMT
Connection: keep-alive
ETag: "5b3f9e97-264"
Accept-Ranges: bytes

10.106.162.254这网段依然是集群内部的网段,只能被集群内部所能访问,外部是无法通过service的ip进行访问的。那么针对pod的客户端除了通过service ip访问还可以通过service的名称进行访问,但是前提是需要对service的名称能够进行解析。而解析时是依赖coredns服务的,而我们本地的dns指向并非coredns,如下:

1
2
3
4
5
6
[root@k8s-master ~]# curl nginx
curl: (6) Could not resolve host: nginx; Unknown error
[root@k8s-master ~]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8
nameserver 114.114.114.114

下面查看一下coredns的ip地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@k8s-master ~]# kubectl get pods -n kube-system -o wide 
NAME READY STATUS RESTARTS AGE IP NODE
coredns-78fcdf6894-nmcmz 1/1 Running 0 1d 10.244.0.3 k8s-master
coredns-78fcdf6894-p5pfm 1/1 Running 0 1d 10.244.0.2 k8s-master
etcd-k8s-master 1/1 Running 1 1d 192.168.56.11 k8s-master
kube-apiserver-k8s-master 1/1 Running 8 1d 192.168.56.11 k8s-master
kube-controller-manager-k8s-master 1/1 Running 4 1d 192.168.56.11 k8s-master
kube-flannel-ds-n5c86 1/1 Running 0 1d 192.168.56.11 k8s-master
kube-flannel-ds-nrcw2 1/1 Running 0 5h 192.168.56.13 k8s-node02
kube-flannel-ds-pgpr7 1/1 Running 1 1d 192.168.56.12 k8s-node01
kube-proxy-glzth 1/1 Running 0 5h 192.168.56.13 k8s-node02
kube-proxy-rxlt7 1/1 Running 1 1d 192.168.56.11 k8s-master
kube-proxy-vxckf 1/1 Running 0 1d 192.168.56.12 k8s-node01
kube-scheduler-k8s-master 1/1 Running 2 1d 192.168.56.11 k8s-master

而一般,也不会直接通过coredns的这个pod ip地址进行访问,而是通过service进行访问,查看一下coredns的service:

1
2
3
[root@k8s-master ~]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP 1d

那么就可以通过这个service ip:10.96.0.10进行解析上面的nginx服务,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@k8s-master ~]# yum install -y bind-utils

[root@k8s-master ~]# dig -t A nginx.default.svc.cluster.local @10.96.0.10  #这里需要使用完整的服务名称,否则会因为dns搜索域的问题而导致无法解析成功

; <<>> DiG 9.9.4-RedHat-9.9.4-61.el7 <<>> -t A nginx.default.svc.cluster.local @10.96.0.10
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 78
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;nginx.default.svc.cluster.local. IN A

;; ANSWER SECTION:
nginx.default.svc.cluster.local. 5 IN A 10.106.162.254  #这样就可以正常解析出nginx的service ip了

;; Query time: 155 msec
;; SERVER: 10.96.0.10#53(10.96.0.10)
;; WHEN: Thu Aug 23 05:40:22 EDT 2018
;; MSG SIZE rcvd: 107

那么再演示通过pod 客户端进行访问:

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
[root@k8s-master ~]# kubectl run client --image=busybox --replicas=1 -it --restart=Never  #创建pod
[root@k8s-master ~]# kubectl exec -it client /bin/sh #首次创建如果没进入到容器,可以使用这命令进入

/ # cat /etc/resolv.conf   #查看dns,这里就是自动指向coredns
nameserver 10.96.0.10
search default.svc.cluster.local svc.cluster.local cluster.local
options ndots:5

/ # wget -O - -q http://nginx:80  #请求解析nginx
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

这就是service提供给pod的固定访问端点的使用,而pod的增删改查,并不会影响通过service进行访问,可以通过以下命令来查看service的详细信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@k8s-master ~]# kubectl describe svc nginx
Name: nginx
Namespace: default
Labels: run=nginx-deploy
Annotations: <none>
Selector: run=nginx-deploy
Type: ClusterIP
IP: 10.106.162.254
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.5:80  #pod 的ip,会根据资源变化改变,但是实际访问的service 依旧有效
Session Affinity: None
Events: <none>

那么pod的增删改,service又是如何确定对pod的访问呢?这就需要通过标签选择器进行选定,无论pod的ip如何变化,但是标签不会变化,从而达到固定端点的访问效果,查看一下pod的标签:

1
2
3
4
[root@k8s-master ~]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
client 1/1 Running 0 21h run=client
nginx-deploy-5b595999-872c7 1/1 Running 2 22h pod-template-hash=16151555,run=nginx-deploy

run=nginx-deploy就是这个应用的标签,所以当pod的改变,并不会影响service的访问。

2、应用副本的动态伸缩

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
语法如下:
kubectl scale [--resource-version=version] [--current-replicas=count] --replicas=COUNT (-f FILENAME | TYPE NAME)

(1)创建应用myapp
[root@k8s-master ~]# kubectl run myapp --image=ikubernetes/myapp:v1 --replicas=2
deployment.apps/myapp created

[root@k8s-master ~]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
myapp 2 2 2 1 15s
nginx-deploy 1 1 1 1 22h

(2)查看pod详细信息
[root@k8s-master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE
client 1/1 Running 0 21h 10.244.2.3 k8s-node02
client2 1/1 Running 0 48m 10.244.1.6 k8s-node01
client3 1/1 Running 0 27m 10.244.2.4 k8s-node02
myapp-848b5b879b-bdp7t 1/1 Running 0 26s 10.244.1.7 k8s-node01
myapp-848b5b879b-swt2c 0/1 ErrImagePull 0 26s 10.244.2.5 k8s-node02
nginx-deploy-5b595999-872c7 1/1 Running 2 22h 10.244.1.5 k8s-node01

(3)配置service端点
[root@k8s-master ~]# kubectl expose deployment myapp --name=myapp --port=80
service/myapp exposed

(4)查看服务信息
[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d
myapp ClusterIP 10.106.67.242 <none> 80/TCP 14s
nginx ClusterIP 10.106.162.254 <none> 80/TCP 21h

(5)Pod客户端访问
/ # wget -O - -q http://myapp:80
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

(6)副本增加到5
[root@k8s-master ~]# kubectl scale --replicas=5 deployment myapp
deployment.extensions/myapp scaled

[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client 1/1 Running 0 21h
client2 1/1 Running 0 51m
client3 1/1 Running 0 30m
myapp-848b5b879b-6p6ml 1/1 Running 0 1m
myapp-848b5b879b-7xmnj 0/1 ImagePullBackOff 0 1m
myapp-848b5b879b-bdp7t 1/1 Running 0 3m
myapp-848b5b879b-swt2c 0/1 ImagePullBackOff 0 3m
myapp-848b5b879b-zlvl2 1/1 Running 0 1m
nginx-deploy-5b595999-872c7 1/1 Running 2 22h

(7)副本收缩到3
[root@k8s-master ~]# kubectl scale --replicas=3 deployment myapp
deployment.extensions/myapp scaled

3、应用的版本升级

1
2
语法如下:
kubectl set image (-f FILENAME | TYPE NAME) CONTAINER_NAME_1=CONTAINER_IMAGE_1 ... CONTAINER_NAME_N=CONTAINER_IMAGE_N
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
(1)版本升级为v2
[root@k8s-master ~]# kubectl set image deployment myapp myapp=ikubernetes/myapp:v2
deployment.extensions/myapp image updated

(2)查看升级过程
[root@k8s-master ~]# kubectl rollout status deployment myapp #查看更新过程
Waiting for deployment "myapp" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "myapp" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "myapp" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "myapp" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "myapp" rollout to finish: 1 old replicas are pending termination...
deployment "myapp" successfully rolled out

(3)获取pod信息
[root@k8s-master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
client 1/1 Running 0 21h
client2 1/1 Running 0 53m
client3 1/1 Running 0 33m
myapp-74c94dcb8c-2djgg 1/1 Running 0 1m
myapp-74c94dcb8c-92d9p 1/1 Running 0 28s
myapp-74c94dcb8c-nq7zt 1/1 Running 0 25s
nginx-deploy-5b595999-872c7 1/1 Running 2 22h

[root@k8s-master ~]# kubectl describe pods myapp-74c94dcb8c-2djgg

(4)pod客户端测试访问,可以看到是v2版本
/ # wget -O - -q http://myapp:80
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>

4、应用的版本回滚

语法如下:
kubectl rollout undo (TYPE NAME | TYPE/NAME) [flags][options]

1
2
3
4
5
[root@k8s-master ~]# kubectl rollout undo deployment myapp    #不指定版本直接回滚到上一个版本
deployment.extensions/myapp

/ # wget -O - -q http://myapp:80
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>

5、实现外部访问service

1
2
3
4
5
6
7
8
9
[root@k8s-master ~]# kubectl edit svc myapp
TYPE:CLUSTER-IP改为
TYPE:NodePort

[root@k8s-master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d
myapp NodePort 10.106.67.242 <none> 80:32432/TCP 18m
nginx ClusterIP 10.106.162.254 <none> 80/TCP 22h

这里再查看service信息,可以看到myapp进行了端口映射,将myapp的80端口映射到本地32432端口,则可以使用http://192.168.56.11:32432 进行访问。如图:

img

k8s学习笔记-08-Kubeadm部署集群

[TOC]

一、环境说明

节点名称 ip地址 部署说明 Pod 网段 Service网段 系统说明
k8s-master 192.168.56.11 docker、kubeadm、kubectl、kubelet 10.244.0.0/16 10.96.0.0/12 Centos 7.4
k8s-node01 192.168.56.12 docker、kubeadm、kubelet 10.244.0.0/16 10.96.0.0/12 Centos 7.4
k8s-node02 192.168.56.13 docker、kubeadm、kubelet 10.244.0.0/16 10.96.0.0/12 Centos 7.4

(1)配置源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@k8s-master ~]# cd /etc/yum.repos.d/

配置阿里云的源:https://opsx.alibaba.com/mirror

[root@k8s-master yum.repos.d]# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo  #配置dokcer源

[root@k8s-master ~]# cat <<EOF > /etc/yum.repos.d/kubernetes.repo  #配置kubernetes源
> [kubernetes]
> name=Kubernetes
> baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
> enabled=1
> gpgcheck=1
> repo_gpgcheck=1
> gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
> EOF

[root@k8s-master yum.repos.d]# yum repolist #查看可用源

将源拷贝到node01和node02节点

1
2
3
4
5
6
[root@k8s-master yum.repos.d]# scp kubernetes.repo docker-ce.repo k8s-node1:/etc/yum.repos.d/
kubernetes.repo 100% 276 276.1KB/s 00:00
docker-ce.repo 100% 2640 1.7MB/s 00:00
[root@k8s-master yum.repos.d]# scp kubernetes.repo docker-ce.repo k8s-node2:/etc/yum.repos.d/
kubernetes.repo 100% 276 226.9KB/s 00:00
docker-ce.repo 100% 2640 1.7MB/s 00:00

(2)安装docker、kubelet、kubeadm、还有命令行工具kubectl

1
2
3
[root@k8s-master yum.repos.d]# yum install -y docker-ce kubelet kubeadm kubectl 
[root@k8s-master ~]# systemctl start docker
[root@k8s-master ~]# systemctl enable docker

启动docker,docker需要到自动到docker仓库中所依赖的镜像文件,这些镜像文件会因为在国外仓库而下载无法完成,所以最好预先下载镜像文件,kubeadm也可以支持本地私有仓库进行获取镜像文件。

(3)预先下载镜像

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
在master节点上使用docker pull拉取镜像,再通过tag打标签
docker pull xiyangxixia/k8s-proxy-amd64:v1.11.1
docker tag xiyangxixia/k8s-proxy-amd64:v1.11.1 k8s.gcr.io/kube-proxy-amd64:v1.11.1
docker pull xiyangxixia/k8s-scheduler:v1.11.1
docker tag xiyangxixia/k8s-scheduler:v1.11.1 k8s.gcr.io/kube-scheduler-amd64:v1.11.1
docker pull xiyangxixia/k8s-controller-manager:v1.11.1
docker tag xiyangxixia/k8s-controller-manager:v1.11.1 k8s.gcr.io/kube-controller-manager-amd64:v1.11.1
docker pull xiyangxixia/k8s-apiserver-amd64:v1.11.1
docker tag xiyangxixia/k8s-apiserver-amd64:v1.11.1 k8s.gcr.io/kube-apiserver-amd64:v1.11.1
docker pull xiyangxixia/k8s-etcd:3.2.18
docker tag xiyangxixia/k8s-etcd:3.2.18 k8s.gcr.io/etcd-amd64:3.2.18
docker pull xiyangxixia/k8s-coredns:1.1.3
docker tag xiyangxixia/k8s-coredns:1.1.3 k8s.gcr.io/coredns:1.1.3
docker pull xiyangxixia/k8s-pause:3.1
docker tag xiyangxixia/k8s-pause:3.1 k8s.gcr.io/pause:3.1
docker pull xiyangxixia/k8s-flannel:v0.10.0-s390x
docker tag xiyangxixia/k8s-flannel:v0.10.0-s390x quay.io/coreos/flannel:v0.10.0-s390x
docker pull xiyangxixia/k8s-flannel:v0.10.0-ppc64le
docker tag xiyangxixia/k8s-flannel:v0.10.0-ppc64le quay.io/coreos/flannel:v0.10.0-ppc64l
docker pull xiyangxixia/k8s-flannel:v0.10.0-arm
docker tag xiyangxixia/k8s-flannel:v0.10.0-arm quay.io/coreos/flannel:v0.10.0-arm
docker pull xiyangxixia/k8s-flannel:v0.10.0-amd64
docker tag xiyangxixia/k8s-flannel:v0.10.0-amd64 quay.io/coreos/flannel:v0.10.0-amd64

node节点上拉取的镜像

docker pull xiyangxixia/k8s-pause:3.1
docker tag xiyangxixia/k8s-pause:3.1 k8s.gcr.io/pause:3.1
docker pull xiyangxixia/k8s-proxy-amd64:v1.11.1
docker tag xiyangxixia/k8s-proxy-amd64:v1.11.1 k8s.gcr.io/kube-proxy-amd64:v1.11.1
docker pull xiyangxixia/k8s-flannel:v0.10.0-amd64
docker tag xiyangxixia/k8s-flannel:v0.10.0-amd64 quay.io/coreos/flannel:v0.10.0-amd64

(4)kubeadm初始化集群

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
[root@k8s-master ~]# vim /etc/sysconfig/kubelet   #修改kubelet禁止提示swap警告
KUBELET_EXTRA_ARGS="--fail-swap-on=false" #如果配置了swap不然提示出错信息
更改kubelet配置,不提示swap警告信息,最好关闭swap
[root@k8s-master ~]# swapoff -a  #关闭swap

[root@k8s-master ~]# kubeadm init --kubernetes-version=v1.11.1 --pod-network-cidr=10.244.0.0/16 --service-cidr=10.96.0.0/12 --ignore-preflight-errors=Swap   #初始化
[init] using Kubernetes version: v1.11.1
[preflight] running pre-flight checks
I0821 18:14:22.223765 18053 kernel_validator.go:81] Validating kernel version
I0821 18:14:22.223894 18053 kernel_validator.go:96] Validating kernel config
[WARNING SystemVerification]: docker version is greater than the most recently validated version. Docker version: 18.06.0-ce. Max validated version: 17.03
[preflight/images] Pulling images required for setting up a Kubernetes cluster
[preflight/images] This might take a minute or two, depending on the speed of your internet connection
[preflight/images] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[preflight] Activating the kubelet service
[certificates] Generated ca certificate and key.
[certificates] Generated apiserver certificate and key.
[certificates] apiserver serving cert is signed for DNS names [k8s-master kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.56.11]
[certificates] Generated apiserver-kubelet-client certificate and key.
[certificates] Generated sa key and public key.
[certificates] Generated front-proxy-ca certificate and key.
[certificates] Generated front-proxy-client certificate and key.
[certificates] Generated etcd/ca certificate and key.
[certificates] Generated etcd/server certificate and key.
[certificates] etcd/server serving cert is signed for DNS names [k8s-master localhost] and IPs [127.0.0.1 ::1]
[certificates] Generated etcd/peer certificate and key.
[certificates] etcd/peer serving cert is signed for DNS names [k8s-master localhost] and IPs [192.168.56.11 127.0.0.1 ::1]
[certificates] Generated etcd/healthcheck-client certificate and key.
[certificates] Generated apiserver-etcd-client certificate and key.
[certificates] valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/admin.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/scheduler.conf"
[controlplane] wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/manifests/kube-apiserver.yaml"
[controlplane] wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/manifests/kube-controller-manager.yaml"
[controlplane] wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/manifests/kube-scheduler.yaml"
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/manifests/etcd.yaml"
[init] waiting for the kubelet to boot up the control plane as Static Pods from directory "/etc/kubernetes/manifests"
[init] this might take a minute or longer if the control plane images have to be pulled
[apiclient] All control plane components are healthy after 51.033696 seconds
[uploadconfig] storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.11" in namespace kube-system with the configuration for the kubelets in the cluster
[markmaster] Marking the node k8s-master as master by adding the label "node-role.kubernetes.io/master=''"
[markmaster] Marking the node k8s-master as master by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "k8s-master" as an annotation
[bootstraptoken] using token: dx7mko.j2ug1lqjra5bf6p2
[bootstraptoken] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

kubeadm join 192.168.56.11:6443 --token dx7mko.j2ug1lqjra5bf6p2 --discovery-token-ca-cert-hash sha256:93fe958796db44dcc23764cb8d9b6a2e67bead072e51a3d4d3c2d36b5d1007cf

如果是普通用户部署,要使用kubectl,需要配置kubectl的环境变量,这些命令也是kubeadm init输出的一部分:

1
2
3
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

如果是使用root用户部署,可以使用export进行定义环境变量

1
[root@k8s-master ~]# export KUBECONFIG=/etc/kubernetes/admin.conf

此处也需要记录输出的kubeadm join命令,后面的node节点加入到集群就需要用到此命令:

1
kubeadm join 192.168.56.11:6443 --token dx7mko.j2ug1lqjra5bf6p2 --discovery-token-ca-cert-hash sha256:93fe958796db44dcc23764cb8d9b6a2e67bead072e51a3d4d3c2d36b5d1007cf

该令牌用于主节点和加入节点之间的相互认证。这里包含的令牌是秘密的。保持安全,因为拥有此令牌的任何人都可以向集群添加经过身份验证的节点。可以使用该kubeadm token命令列出,创建和删除这些令牌。到此,集群的初始化已经完成,可以使用kubectl get cs进行查看集群的健康状态信息:

1
2
3
4
5
6
7
8
[root@k8s-master ~]# kubectl get cs
NAME STATUS MESSAGE ERROR
controller-manager Healthy ok
scheduler Healthy ok
etcd-0 Healthy {"health": "true"}
[root@k8s-master ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master NotReady master 43m v1.11.2

从上面的结果可以看到,master的组件controller-manager、scheduler、etcd都处于正常状态。那么apiserver到哪去了?要知道kubectl是通过apiserver进行通信,从而在etcd中获取到集群的状态信息,所以可以获取到集群的状态信息,即表示apiserver是处于正常运行的状态。使用kubectl get node获取节点信息,可以看到master节点的状态是NotReady,这是因为还没有部署好Pod网络。

(5)安装网络插件CNI

安装Pod网络插件,用于保证Pod之间的相互通信。在每个集群当中只能有一个Pod网络,在部署flannel之前需要更改的内核参数将桥接的IPv4的流量进行转发给iptables链,这是CNI插件的运行的前提要求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@k8s-master ~]# cat /proc/sys/net/bridge/bridge-nf-call-iptables
1
[root@k8s-master ~]# cat /proc/sys/net/bridge/bridge-nf-call-ip6tables
1
[root@k8s-master ~]# kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/c5d10c8/Documentation/kube-flannel.yml
clusterrole.rbac.authorization.k8s.io/flannel created
clusterrolebinding.rbac.authorization.k8s.io/flannel created
serviceaccount/flannel created
configmap/kube-flannel-cfg created
daemonset.extensions/kube-flannel-ds created

[root@k8s-master ~]# kubectl get node  #再查看master节点的状态信息,就是已经是Ready状态了
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 3h v1.11.2

(6)node01节点加入集群

1
2
3
4
5
6
7
8
9
10
11
12
[root@k8s-node01 ~]# yum install -y docker kubeadm kubelet
[root@k8s-node01 ~]# systemctl enable dokcer kubelet
[root@k8s-node01 ~]# systemctl start docker

这里需要提前拉取好镜像,如果docker配置了代理另说,按本次方法,提前拉取node节点所需要的镜像。

[root@k8s-node01 ~]# kubeadm join 192.168.56.11:6443 --token dx7mko.j2ug1lqjra5bf6p2 --discovery-token-ca-cert-hash sha256:93fe958796db44dcc23764cb8d9b6a2e67bead072e51a3d4d3c2d36b5d1007cf

[root@k8s-master ~]# kubectl get node  
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 7h v1.11.2
k8s-node01 NotReady <none> 3h v1.11.2

加入集群后,查看节点状态信息,看到node01节点的状态为NotReady,是因为node01节点上还没有镜像或者是还在拉取镜像。等待拉取完镜像就会启动对应的Pod。

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@k8s-node01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
5502c29b43df f0fad859c909 "/opt/bin/flanneld -…" 3 minutes ago Up 3 minutes k8s_kube-flannel_kube-flannel-ds-pgpr7_kube-system_23dc27e3-a5af-11e8-84d2-000c2972dc1f_1
db1cc0a6fec4 d5c25579d0ff "/usr/local/bin/kube…" 3 minutes ago Up 3 minutes k8s_kube-proxy_kube-proxy-vxckf_kube-system_23dc0141-a5af-11e8-84d2-000c2972dc1f_0
bc54ad3399e8 k8s.gcr.io/pause:3.1 "/pause" 9 minutes ago Up 9 minutes k8s_POD_kube-proxy-vxckf_kube-system_23dc0141-a5af-11e8-84d2-000c2972dc1f_0
cbfca066b71d k8s.gcr.io/pause:3.1 "/pause" 10 minutes ago Up 10 minutes k8s_POD_kube-flannel-ds-pgpr7_kube-system_23dc27e3-a5af-11e8-84d2-000c2972dc1f_0

[root@k8s-master ~]# kubectl get pods -n kube-system -o wide
NAME READY STATUS RESTARTS AGE IP NODE
coredns-78fcdf6894-nmcmz 1/1 Running 0 1d 10.244.0.3 k8s-master
coredns-78fcdf6894-p5pfm 1/1 Running 0 1d 10.244.0.2 k8s-master
etcd-k8s-master 1/1 Running 1 1d 192.168.56.11 k8s-master
kube-apiserver-k8s-master 1/1 Running 8 1d 192.168.56.11 k8s-master
kube-controller-manager-k8s-master 1/1 Running 4 1d 192.168.56.11 k8s-master
kube-flannel-ds-n5c86 1/1 Running 0 1d 192.168.56.11 k8s-master
kube-flannel-ds-pgpr7 1/1 Running 1 1d 192.168.56.12 k8s-node01
kube-proxy-rxlt7 1/1 Running 1 1d 192.168.56.11 k8s-master
kube-proxy-vxckf 1/1 Running 0 1d 192.168.56.12 k8s-node01
kube-scheduler-k8s-master 1/1 Running 2 1d 192.168.56.11 k8s-master

[root@k8s-master ~]# kubectl get node  #此时再查看状态已经变成Ready
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 1d v1.11.2
k8s-node01 Ready <none> 1d v1.11.2

(7)增加集群的节点node02

1
2
3
[root@k8s-node02 ~]# yum install -y docker kubeadm kubelet
[root@k8s-node02 ~]# systemctl enable docker kubelet
[root@k8s-node02 ~]# systemctl start docker

同样预先拉取好node节点所需镜像,在此处犯错的还有,根据官方说明tonken的默认有效时间为24h,由于时间差,导致这里的token失效,可以使用kubeadm token list查看token,发现之前初始化的tonken已经失效了。

1
2
3
4
[root@k8s-master ~]# kubeadm token list
TOKEN TTL EXPIRES USAGES DESCRIPTION EXTRA GROUPS

dx7mko.j2ug1lqjra5bf6p2 <invalid> 2018-08-22T18:15:43-04:00 authentication,signing The default bootstrap token generated by 'kubeadm init'. system:bootstrappers:kubeadm:default-node-token

那么此处需要重新生成token,生成的方法如下:

1
2
[root@k8s-master ~]# kubeadm token create
1vxhuq.qi11t7yq2wj20cpe

如果没有值–discovery-token-ca-cert-hash,可以通过在master节点上运行以下命令链来获取:

1
2
3
4
[root@k8s-master ~]# openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'

8cb2de97839780a412b93877f8507ad6c94f73add17d5d7058e91741c9d5ec78

此时,再运行kube join命令将node02加入到集群当中,此处的–discovery-token-ca-cert-hash依旧可以使用初始化时的证书

1
2
3
4
5
6
7
[root@k8s-node02 ~]# kubeadm join 192.168.56.11:6443 --token 1vxhuq.qi11t7yq2wj20cpe --discovery-token-ca-cert-hash sha256:93fe958796db44dcc23764cb8d9b6a2e67bead072e51a3d4d3c2d36b5d1007cf

[root@k8s-master ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 1d v1.11.2
k8s-node01 Ready <none> 1d v1.11.2
k8s-node02 Ready <none> 2h v1.11.2

如果在集群安装过程中有遇到其他问题,可以使用以下命令进行重置:

1
2
3
4
$ kubeadm reset
$ ifconfig cni0 down && ip link delete cni0
$ ifconfig flannel.1 down && ip link delete flannel.1
$ rm -rf /var/lib/cni/

k8s学习笔记-07-Coredns和Dashboard部署

[TOC]

一、CoreDNS部署

在 Cluster 中,除了可以通过 Cluster IP 访问 Service,Kubernetes 还提供了更为方便的 DNS 访问。

(1)编辑coredns.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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
[root@linux-node1 ~]# vim coredns.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
name: coredns
namespace: kube-system
labels:
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
labels:
kubernetes.io/bootstrapping: rbac-defaults
addonmanager.kubernetes.io/mode: Reconcile
name: system:coredns
rules:
- apiGroups:
- ""
resources:
- endpoints
- services
- pods
- namespaces
verbs:
- list
- watch
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
labels:
kubernetes.io/bootstrapping: rbac-defaults
addonmanager.kubernetes.io/mode: EnsureExists
name: system:coredns
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:coredns
subjects:
- kind: ServiceAccount
name: coredns
namespace: kube-system
---
apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
labels:
addonmanager.kubernetes.io/mode: EnsureExists
data:
Corefile: |
.:53 {
errors
health
kubernetes cluster.local. in-addr.arpa ip6.arpa {
pods insecure
upstream
fallthrough in-addr.arpa ip6.arpa
}
prometheus :9153
proxy . /etc/resolv.conf
cache 30
}
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: coredns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "CoreDNS"
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1
selector:
matchLabels:
k8s-app: coredns
template:
metadata:
labels:
k8s-app: coredns
spec:
serviceAccountName: coredns
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
- key: "CriticalAddonsOnly"
operator: "Exists"
containers:
- name: coredns
image: coredns/coredns:1.0.6
imagePullPolicy: IfNotPresent
resources:
limits:
memory: 170Mi
requests:
cpu: 100m
memory: 70Mi
args: [ "-conf", "/etc/coredns/Corefile" ]
volumeMounts:
- name: config-volume
mountPath: /etc/coredns
ports:
- containerPort: 53
name: dns
protocol: UDP
- containerPort: 53
name: dns-tcp
protocol: TCP
livenessProbe:
httpGet:
path: /health
port: 8080
scheme: HTTP
initialDelaySeconds: 60
timeoutSeconds: 5
successThreshold: 1
failureThreshold: 5
dnsPolicy: Default
volumes:
- name: config-volume
configMap:
name: coredns
items:
- key: Corefile
path: Corefile
---
apiVersion: v1
kind: Service
metadata:
name: coredns
namespace: kube-system
labels:
k8s-app: coredns
kubernetes.io/cluster-service: "true"
addonmanager.kubernetes.io/mode: Reconcile
kubernetes.io/name: "CoreDNS"
spec:
selector:
k8s-app: coredns
clusterIP: 10.1.0.2
ports:
- name: dns
port: 53
protocol: UDP
- name: dns-tcp
port: 53
protocol: TCP

(2)创建coredns

1
2
3
4
5
6
7
[root@linux-node1 ~]# kubectl create -f coredns.yaml 
serviceaccount "coredns" created
clusterrole.rbac.authorization.k8s.io "system:coredns" created
clusterrolebinding.rbac.authorization.k8s.io "system:coredns" created
configmap "coredns" created
deployment.extensions "coredns" created
service "coredns" created

(3)查看coredns服务

1
2
3
4
5
6
7
8
9
10
11
12
[root@linux-node1 ~]# kubectl get deployment -n kube-system
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
coredns 2 2 2 0 1m

[root@linux-node1 ~]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
coredns ClusterIP 10.1.0.2 <none> 53/UDP,53/TCP 1m

[root@linux-node1 ~]# kubectl get pod -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-77c989547b-d84n8 1/1 Running 0 2m
coredns-77c989547b-j4ms2 1/1 Running 0 2m

(4)Pod容器中进行域名解析测试

1
2
3
4
5
6
7
8
9
10
11
12
[root@linux-node1 ~]# kubectl run alpine --rm -ti --image=alpine -- /bin/sh
If you don't see a command prompt, try pressing enter.

/ # nslookup httpd-svc
nslookup: can't resolve '(null)': Name does not resolve

Name: httpd-svc
Address 1: 10.1.230.129

/ # wget httpd-svc:8080
Connecting to httpd-svc:8080 (10.1.230.129:8080)
index.html 100% |********************************************************************************************************************************************| 45 0:00:00 ETA

二、Dashboard部署

从github上下载dashboard的yaml文件:https://github.com/unixhot/salt-kubernetes

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
[root@linux-node1 dashboard]# ll
total 20
-rw-r--r-- 1 root root 357 Aug 22 09:26 admin-user-sa-rbac.yaml
-rw-r--r-- 1 root root 4901 Aug 22 09:26 kubernetes-dashboard.yaml
-rw-r--r-- 1 root root 458 Aug 22 09:26 ui-admin-rbac.yaml
-rw-r--r-- 1 root root 477 Aug 22 09:26 ui-read-rbac.yaml

[root@linux-node1 dashboard]# kubectl create -f .
serviceaccount "admin-user" created
clusterrolebinding.rbac.authorization.k8s.io "admin-user" created
secret "kubernetes-dashboard-certs" created
serviceaccount "kubernetes-dashboard" created
role.rbac.authorization.k8s.io "kubernetes-dashboard-minimal" created
rolebinding.rbac.authorization.k8s.io "kubernetes-dashboard-minimal" created
deployment.apps "kubernetes-dashboard" created
service "kubernetes-dashboard" created
clusterrole.rbac.authorization.k8s.io "ui-admin" created
rolebinding.rbac.authorization.k8s.io "ui-admin-binding" created
clusterrole.rbac.authorization.k8s.io "ui-read" created
rolebinding.rbac.authorization.k8s.io "ui-read-binding" created

[root@linux-node1 dashboard]# kubectl get pods -o wide -n kube-system
NAME READY STATUS RESTARTS AGE IP NODE
coredns-77c989547b-d84n8 1/1 Running 0 55m 10.2.99.7 192.168.56.13
coredns-77c989547b-j4ms2 1/1 Running 0 55m 10.2.76.6 192.168.56.12
kubernetes-dashboard-66c9d98865-mps22 1/1 Running 0 4m 10.2.76.12 192.168.56.12

[root@linux-node1 dashboard]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
coredns ClusterIP 10.1.0.2 <none> 53/UDP,53/TCP 56m
kubernetes-dashboard NodePort 10.1.234.201 <none> 443:38974/TCP 5m

从上可以看到kubernetes的dashboard服务的ip为:10.1.234.201,其映射到宿主机的端口为38974,由于master上没有部署kube-porxy,所以需要直接访问https://192.168.56.12:38974,如图:

选择令牌登陆,获取令牌的方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@linux-node1 dashboard]# kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep admin-user | awk '{print $1}')
Name: admin-user-token-mz7p9
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name=admin-user
kubernetes.io/service-account.uid=c2a85113-acc9-11e8-a800-000c29ce4fa7

Type: kubernetes.io/service-account-token

Data
====
namespace: 11 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IiJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLW16N3A5Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJjMmE4NTExMy1hY2M5LTExZTgtYTgwMC0wMDBjMjljZTRmYTciLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.V4aEkKDBcK4RkuXRzwdAyoJRBrxAnc8axLLxGCGiduwv5Qa0HFe2WQWtny6FI-MpUP-dzrxahWSwaFcKKvVdzfBuXTbnPDBkhcrpAuzDsL0vo-GwHAAl88n8yZ67QmBwPVWH2CBrrTwWqALAfR2wNKtrUEigg-qbTQ05slP8WmbeckfzHTeZpQqegO3fz0BNBrJqi2TFDaftPm_vWSEsPWzWE9AyvfiVwGrfc_mmzHpOyxXAQXQLxJunfklwt0kuENO6sRRJ2HGvZ6HnCGZYZj0p-kjh5uAv-q_X2cMPIAhXgH7gHdYeiSXvEGA2Qz6tBE2pgN6S4F_xj6b4JT7kAQ
ca.crt: 1359 bytes

img

点击登录后的界面如下:

img

k8s学习笔记-06-创建K8S应用

[TOC]

一、Deployment的概念

K8S本身并不提供网络的功能,所以需要借助第三方网络插件进行部署K8S中的网络,以打通各个节点中容器的互通。
POD,是K8S中的一个逻辑概念,K8S管理的是POD,一个POD中包含多个容器,容器之间通过localhost互通。而POD需要ip地址。每个POD都有一个标签

POD–>RC–>RS–>Deployment (发展历程)

Deployment,表示用户对K8S集群的一次更新操作。Deployment是一个比RS应用模式更广的API对象。用于保证Pod的副本的数量。

可以是创建一个新的服务,更新一个新的服务,也可以是滚动升级一个服务。滚动升级一个服务。实际是创建一个新的RS,然后将新的RS中副本数增加到理想状态,将旧的RS中的副本数减小到0的复合操作; 这样的一个复合操作用一个RS是不太好描述的,所以用一个更通用的Deployment来描述。

RC、RS和Deployment只是保证了支撑服务的POD数量,但是没有解决如何访问这些服务的问题。一个POD只是一个运行服务的实例,随时可以能在一个节点上停止,在另一个节点以一个新的IP启动一个新的POD,因此不能以确定的IP和端口号提供服务。

要稳定地提供服务需要服务发现和负载均衡能力。服务发现完成的工作,是针对客户端访问的服务,找到对应的后端服务实例。

在K8S的集中当中,客户端需要访问的服务就是Service对象。每个Service会对应一个集群内部有效的虚拟IP,集群内部通过虚拟IP访问一个服务。

二、创建K8S的第一个应用

1
2
3
4
5
6
7
8
9
[root@linux-node1 ~]# kubectl run net-test --image=alpine --replicas=2 sleep 36000  #创建名称为net-test的应用,镜像指定为alpine,副本数为2个
deployment.apps "net-test" created
[root@linux-node1 ~]# kubectl get pod -o wide  #查看pod的状态信息,此时是API Server从etcd中读取这些数据
NAME READY STATUS RESTARTS AGE IP NODE
net-test-7b949fc785-2v2qz 1/1 Running 0 56s 10.2.87.2 192.168.56.120
net-test-7b949fc785-6nrhm 0/1 ContainerCreating 0 56s <none> 192.168.56.130
[root@linux-node1 ~]# kubectl get deployment net-test
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
net-test 2 2 2 2 22h
1
kubectl get deployment命令可以查看net-test的状态,输出显示两个副本正常运行。还可以在创建的过程中,通过kubectl describe deployment net-test了解详细的信息。  
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
[root@linux-node1 ~]# kubectl describe deployment net-test
Name: net-test
Namespace: default
CreationTimestamp: Thu, 16 Aug 2018 15:41:29 +0800
Labels: run=net-test
Annotations: deployment.kubernetes.io/revision=1
Selector: run=net-test
Replicas: 2 desired | 2 updated | 2 total | 2 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 1 max unavailable, 1 max surge
Pod Template:
Labels: run=net-test
Containers:
net-test:
Image: alpine
Port: <none>
Host Port: <none>
Args:
sleep
360000
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: <none>
NewReplicaSet: net-test-5767cb94df (2/2 replicas created)
Events: <none>  

Events是Deployment的日志,记录整个RelicaSet的启动过程,从上面的创建过程,可以看到Deployment是通过ReplicaSet来管理Pod。

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
[root@linux-node1 ~]# kubectl get replicaset  #获取副本集信息
NAME DESIRED CURRENT READY AGE
net-test-5767cb94df 2 2 2 23h

[root@linux-node1 ~]# kubectl describe replicaset net-test-5767cb94df  #查看副本集的详细信息
Name: net-test-5767cb94df
Namespace: default
Selector: pod-template-hash=1323765089,run=net-test
Labels: pod-template-hash=1323765089
run=net-test
Annotations: deployment.kubernetes.io/desired-replicas=2
deployment.kubernetes.io/max-replicas=3
deployment.kubernetes.io/revision=1
Controlled By: Deployment/net-test  #指明ReplicaSet是由Deployment net-test创建
Replicas: 2 current / 2 desired
Pods Status: 3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
Labels: pod-template-hash=1323765089
run=net-test
Containers:
net-test:
Image: alpine
Port: <none>
Host Port: <none>
Args:
sleep
360000
Environment: <none>
Mounts: <none>
Volumes: <none>
Events: <none>  #Events可以查看到两个副本Pod的创建过程

[root@linux-node1 ~]# kubectl get pod  #获取Pod信息,可以看到2个副本都处于Running状态
NAME READY STATUS RESTARTS AGE
net-test-5767cb94df-djt98 1/1 Running 0 22h
net-test-5767cb94df-zb8m4 1/1 Running 0 23h

[root@linux-node1 ~]# kubectl describe pod net-test-5767cb94df-djt98 #查看pod的详细信息
Name: net-test-5767cb94df-djt98
Namespace: default
Node: 192.168.56.13/192.168.56.13
Start Time: Thu, 16 Aug 2018 15:53:00 +0800
Labels: pod-template-hash=1323765089
run=net-test
Annotations: <none>
Status: Running
IP: 10.2.73.3
Controlled By: ReplicaSet/net-test-5767cb94df
Containers:
net-test:
Container ID: docker://c8e267326ed80f3cbe8111377c74dd1f016beaef513196b941165e180a5d5733
Image: alpine
Image ID: docker-pullable://alpine@sha256:7043076348bf5040220df6ad703798fd8593a0918d06d3ce30c6c93be117e430
Port: <none>
Host Port: <none>
Args:
sleep
360000
State: Running
Started: Thu, 16 Aug 2018 15:53:06 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-mnqx5 (ro)
Conditions:
Type Status
Initialized True
Ready True
PodScheduled True
Volumes:
default-token-mnqx5:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-mnqx5
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: <none>
Events: <none>

Controlled By 指明此 Pod 是由 ReplicaSet/net-test-5767cb94df 创建。Events 记录了 Pod 的启动过程。如果操作失败(比如 image 不存在),也能在这里查看到原因。

总结创建的过程:

(1)用户通过kubectl创建Deployment

(2)Deployment创建ReplicaSet

(3)ReplicaSet创建Pod

如图:

img

三、K8S创建资源的两种方式

Kubernetes 支持两种方式创建资源:

(1)用kubectl命令直接创建,在命令行中通过参数指定资源的属性。此方式简单直观,比较适合临时测试或实验使用。

1
kubectl run net-test --image=alpine --replicas=2 sleep 36000

(2)通过配置文件和kubectl create创建。在配置文件中描述了应用的信息和需要达到的预期状态。

1
kubectl create -f nginx-deployment.yaml

四、以Deployment YAML方式创建Nginx服务

1、创建deployment

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
[root@linux-node1 ~]# vim nginx-deployment.yaml  #使用yaml的方式进行创建应用
apiVersion: apps/v1  #apiVersion是当前配置格式的版本
kind: Deployment    #kind是要创建的资源类型,这里是Deploymnet
metadata:        #metadata是该资源的元数据,name是必须的元数据项
name: nginx-deployment
labels:
app: nginx
spec:          #spec部分是该Deployment的规则说明
replicas: 3      #relicas指定副本数量,默认为1
selector:
matchLabels:
app: nginx
template:      #template定义Pod的模板,这是配置的重要部分
metadata:     #metadata定义Pod的元数据,至少要顶一个label,label的key和value可以任意指定
labels:
app: nginx
spec:       #spec描述的是Pod的规则,此部分定义pod中每一个容器的属性,name和image是必需的
containers:
- name: nginx
image: nginx:1.13.12
ports:
- containerPort: 80

[root@linux-node1 ~]# kubectl create -f nginx-deployment.yaml #创建nginx-deployment应用
deployment.apps "nginx-deployment" created

2、查看deployment

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
[root@linux-node1 ~]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
net-test 2 2 2 2 32m
nginx-deployment 3 3 3 0 10s

[root@linux-node1 ~]# kubectl describe deployment nginx-deployment #查看deployment详情
Name: nginx-deployment
Namespace: default
CreationTimestamp: Thu, 16 Aug 2018 16:13:37 +0800
Labels: app=nginx
Annotations: deployment.kubernetes.io/revision=1
Selector: app=nginx
Replicas: 3 desired | 3 updated | 3 total | 0 available | 3 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx
Containers:
nginx:
Image: nginx:1.13.12
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
Conditions:
Type Status Reason
---- ------ ------
Available False MinimumReplicasUnavailable
Progressing True ReplicaSetUpdated
OldReplicaSets: <none>
NewReplicaSet: nginx-deployment-6c45fc49cb (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 1m deployment-controller Scaled up replica set nginx-deployment-6c45fc49cb to 3

3、查看Pod

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@linux-node1 ~]# kubectl get pod    #查看pod在状态,正在创建中,此时应该正在拉取镜像
NAME READY STATUS RESTARTS AGE
net-test-5767cb94df-djt98 1/1 Running 0 22m
net-test-5767cb94df-hcwv7 1/1 Unknown 0 34m
net-test-5767cb94df-zb8m4 1/1 Running 0 34m
nginx-deployment-6c45fc49cb-dmc22 0/1 ContainerCreating 0 2m
nginx-deployment-6c45fc49cb-fd8xm 0/1 ContainerCreating 0 2m
nginx-deployment-6c45fc49cb-sc8sh 0/1 ContainerCreating 0 2m

[root@linux-node1 ~]# kubectl describe pod nginx-deployment-6c45fc49cb-dmc22 #查看具体某个pod的状态信息

[root@linux-node1 ~]# kubectl get pod -o wide  #创建成功,状态为Running
NAME READY STATUS RESTARTS AGE IP NODE
net-test-5767cb94df-djt98 1/1 Running 0 24m 10.2.73.3 192.168.56.13
net-test-5767cb94df-hcwv7 1/1 Unknown 0 36m 10.2.10.2 192.168.56.12
net-test-5767cb94df-zb8m4 1/1 Running 0 36m 10.2.73.2 192.168.56.13
nginx-deployment-6c45fc49cb-dmc22 1/1 Running 0 4m 10.2.73.6 192.168.56.13
nginx-deployment-6c45fc49cb-fd8xm 1/1 Running 0 4m 10.2.73.4 192.168.56.13
nginx-deployment-6c45fc49cb-sc8sh 1/1 Running 0 4m 10.2.73.5 192.168.56.13

Deployment、ReplicaSet、Pod 都已经就绪。如果要删除这些资源,执行 kubectl delete deployment nginx-deployment 或者 kubectl delete -f nginx-deployment.yaml。

4、测试Pod访问

1
2
3
4
5
6
7
8
9
10
[root@linux-node1 ~]# curl --head http://10.2.73.6
HTTP/1.1 200 OK
Server: nginx/1.13.12
Date: Thu, 16 Aug 2018 08:18:14 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Mon, 09 Apr 2018 16:01:09 GMT
Connection: keep-alive
ETag: "5acb8e45-264"
Accept-Ranges: bytes

5、更新Deployment

1
2
3
4
5
6
7
[root@linux-node1 ~]# kubectl set image deployment/nginx-deployment nginx=nginx:1.15.2 --record    #nginx的版本升级,由1.13.2升级为1.15.2,记录需要加参数--record
deployment.apps "nginx-deployment" image updated

[root@linux-node1 ~]# kubectl get deployment -o wide  #查看更新后的deployment,可以看到当前4个副本,说明还在滚动升级中
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
net-test 2 2 2 2 39m net-test alpine run=net-test
nginx-deployment 3 4 1 3 6m nginx nginx:1.15.2 app=nginx

6、查看更新历史

1
2
3
4
5
[root@linux-node1 ~]# kubectl rollout history deployment/nginx-deployment #查看更新历史记录
deployments "nginx-deployment"
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment/nginx-deployment nginx=nginx:1.15.2 --record=true

7、查看具体某一个版本的升级历史

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@linux-node1 ~]# kubectl rollout history deployment/nginx-deployment --revision=1
deployments "nginx-deployment" with revision #1
Pod Template:
Labels: app=nginx
pod-template-hash=2701970576
Containers:
nginx:
Image: nginx:1.13.12
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>

8、查看更新后的Deployment,并进行访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@linux-node1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
net-test-5767cb94df-djt98 1/1 Running 0 30m 10.2.73.3 192.168.56.13
net-test-5767cb94df-hcwv7 1/1 Unknown 0 42m 10.2.10.2 192.168.56.12
net-test-5767cb94df-zb8m4 1/1 Running 0 42m 10.2.73.2 192.168.56.13
nginx-deployment-64749d4b59-djttr 1/1 Running 0 37s 10.2.73.8 192.168.56.13
nginx-deployment-64749d4b59-jp7fw 1/1 Running 0 3m 10.2.73.7 192.168.56.13
nginx-deployment-64749d4b59-q4fsn 1/1 Running 0 33s 10.2.73.9 192.168.56.13
[root@linux-node1 ~]# curl --head http://10.2.73.7
HTTP/1.1 200 OK
Server: nginx/1.15.2  #版本已经升级为1.15.2
Date: Thu, 16 Aug 2018 08:24:09 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 24 Jul 2018 13:02:29 GMT
Connection: keep-alive
ETag: "5b572365-264"
Accept-Ranges: bytes

9、快速回滚到上一个版本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@linux-node1 ~]# kubectl rollout undo deployment/nginx-deployment   #回滚上一个版本
deployment.apps "nginx-deployment"

[root@linux-node1 ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE
net-test-5767cb94df-djt98 1/1 Running 0 32m 10.2.73.3 192.168.56.13
net-test-5767cb94df-hcwv7 1/1 Unknown 0 43m 10.2.10.2 192.168.56.12
net-test-5767cb94df-zb8m4 1/1 Running 0 43m 10.2.73.2 192.168.56.13
nginx-deployment-6c45fc49cb-b9h84 1/1 Running 0 24s 10.2.73.11 192.168.56.13
nginx-deployment-6c45fc49cb-g4mrg 1/1 Running 0 26s 10.2.73.10 192.168.56.13
nginx-deployment-6c45fc49cb-k29kq 1/1 Running 0 21s 10.2.73.12 192.168.56.13
[root@linux-node1 ~]# curl --head http://10.2.73.10
HTTP/1.1 200 OK
Server: nginx/1.13.12
Date: Thu, 16 Aug 2018 08:25:35 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Mon, 09 Apr 2018 16:01:09 GMT
Connection: keep-alive
ETag: "5acb8e45-264"
Accept-Ranges: bytes

回滚完成,每一次更新或者回滚ip都会变化,所以需要通过vip进行访问,这就引入了service

10、使用service的vip进行访问应用

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
43
44
[root@linux-node1 ~]# vim nginx-service.yaml  #使用yaml方式创建service
kind: Service
apiVersion: v1
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80

[root@linux-node1 ~]# kubectl create -f nginx-service.yaml   #创建service
service "nginx-service" created

[root@linux-node1 ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 4h
nginx-service ClusterIP 10.1.213.126 <none> 80/TCP 15s  #这个就是vip

[root@linux-node2 ~]# curl --head http://10.1.213.126  #在node2节点上进行访问vip测试,在node1上无法访问是因为没有安装kube-proxy导致无法访问
HTTP/1.1 200 OK
Server: nginx/1.13.12
Date: Thu, 16 Aug 2018 08:30:08 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Mon, 09 Apr 2018 16:01:09 GMT
Connection: keep-alive
ETag: "5acb8e45-264"
Accept-Ranges: bytes

[root@linux-node2 ~]# ipvsadm -Ln  
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.1.0.1:443 rr persistent 10800
-> 192.168.56.11:6443 Masq 1 0 0
TCP 10.1.213.126:80 rr
-> 10.2.73.10:80 Masq 1 0 1
-> 10.2.73.11:80 Masq 1 0 1
-> 10.2.73.12:80 Masq 1 0 0

查看LVS状态可以看到,当访问VIP:10.1.213.126时,会进行负载均衡到各个pod

11、扩容到5个节点

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
[root@linux-node1 ~]# kubectl scale deployment nginx-deployment --replicas 5  #对应用的副本数进行扩容,直接指定副本数为5
deployment.extensions "nginx-deployment" scaled

[root@linux-node1 ~]# kubectl get pod  #查看pod状态,可以看到已经增加到5个副本
NAME READY STATUS RESTARTS AGE
net-test-5767cb94df-djt98 1/1 Running 0 38m
net-test-5767cb94df-hcwv7 1/1 Unknown 0 50m
net-test-5767cb94df-zb8m4 1/1 Running 0 50m
nginx-deployment-6c45fc49cb-b9h84 1/1 Running 0 6m
nginx-deployment-6c45fc49cb-g4mrg 1/1 Running 0 7m
nginx-deployment-6c45fc49cb-k29kq 1/1 Running 0 6m
nginx-deployment-6c45fc49cb-n9qkx 1/1 Running 0 24s
nginx-deployment-6c45fc49cb-xpx9s 1/1 Running 0 24s

[root@linux-node2 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.1.0.1:443 rr persistent 10800
-> 192.168.56.11:6443 Masq 1 0 0
TCP 10.1.213.126:80 rr
-> 10.2.73.10:80 Masq 1 0 0
-> 10.2.73.11:80 Masq 1 0 0
-> 10.2.73.12:80 Masq 1 0 1
-> 10.2.73.13:80 Masq 1 0 0
-> 10.2.73.14:80 Masq 1 0 0

k8s学习笔记-05-Flannel部署

[TOC]

一、K8S的ip地址

**Node IP:**节点设备的IP,如物理机,虚拟机等容器宿主的实际IP。

**Pod IP:**Pod的IP地址,是根据docker0网络IP段进行分配的。

**Cluster IP:**Service的IP,是一个虚拟IP,仅作用于service对象,由K8S管理和分配,需要结合service port才能使用,单独的IP没有通信功能,集群外访问需要一些修改。

在K8S集群内部,node ip、pod ip、clustere ip的通信机制是由k8s指定的路由规则,不是IP路由。

1
2
3
[root@linux-node1 ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.1.0.1 <none> 443/TCP 3h

二、Flannel网络部署

(1)为Flannel生成证书

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@linux-node1 ssl]# vim flanneld-csr.json
{
"CN": "flanneld",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}

(2)生成证书

1
2
3
4
5
6
7
8
9
[root@linux-node1 ssl]# cfssl gencert -ca=/opt/kubernetes/ssl/ca.pem \
> -ca-key=/opt/kubernetes/ssl/ca-key.pem \
> -config=/opt/kubernetes/ssl/ca-config.json \
> -profile=kubernetes flanneld-csr.json | cfssljson -bare flanneld
[root@linux-node1 ssl]# ll flannel*
-rw-r--r-- 1 root root 997 May 31 11:13 flanneld.csr
-rw-r--r-- 1 root root 221 May 31 11:13 flanneld-csr.json
-rw------- 1 root root 1675 May 31 11:13 flanneld-key.pem
-rw-r--r-- 1 root root 1391 May 31 11:13 flanneld.pem

(3)分发证书

1
2
3
4
5
6
7
[root@linux-node1 ssl]# cp flanneld*.pem /opt/kubernetes/ssl/
[root@linux-node1 ssl]# scp flanneld*.pem 192.168.56.120:/opt/kubernetes/ssl/
flanneld-key.pem 100% 1675 127.2KB/s 00:00
flanneld.pem 100% 1391 308.3KB/s 00:00
[root@linux-node1 ssl]# scp flanneld*.pem 192.168.56.130:/opt/kubernetes/ssl/
flanneld-key.pem 100% 1675 291.1KB/s 00:00
flanneld.pem 100% 1391 90.4KB/s 00:00

(4)下载Flannel软件包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@linux-node1 ~]# cd /usr/local/src
# wget
https://github.com/coreos/flannel/releases/download/v0.10.0/flannel-v0.10.0-linux-amd64.tar.gz
[root@linux-node1 src]# tar zxf flannel-v0.10.0-linux-amd64.tar.gz
[root@linux-node1 src]# cp flanneld mk-docker-opts.sh /opt/kubernetes/bin/
复制到linux-node2和linux-node3节点
[root@linux-node1 src]# scp flanneld mk-docker-opts.sh 192.168.56.120:/opt/kubernetes/bin/
[root@linux-node1 src]# scp flanneld mk-docker-opts.sh 192.168.56.130:/opt/kubernetes/bin/

复制对应脚本到/opt/kubernetes/bin目录下。
[root@linux-node1 ~]# cd /usr/local/src/kubernetes/cluster/centos/node/bin/
[root@linux-node1 bin]# cp remove-docker0.sh /opt/kubernetes/bin/
[root@linux-node1 bin]# scp remove-docker0.sh 192.168.56.120:/opt/kubernetes/bin/
[root@linux-node1 bin]# scp remove-docker0.sh 192.168.56.130:/opt/kubernetes/bin/

(5)配置Flannel

1
2
3
4
5
6
7
8
9
[root@linux-node1 ~]# vim /opt/kubernetes/cfg/flannel
FLANNEL_ETCD="-etcd-endpoints=https://192.168.56.110:2379,https://192.168.56.120:2379,https://192.168.56.130:2379"
FLANNEL_ETCD_KEY="-etcd-prefix=/kubernetes/network"
FLANNEL_ETCD_CAFILE="--etcd-cafile=/opt/kubernetes/ssl/ca.pem"
FLANNEL_ETCD_CERTFILE="--etcd-certfile=/opt/kubernetes/ssl/flanneld.pem"
FLANNEL_ETCD_KEYFILE="--etcd-keyfile=/opt/kubernetes/ssl/flanneld-key.pem"
复制配置到其它节点上
[root@linux-node1 ~]# scp /opt/kubernetes/cfg/flannel 192.168.56.120:/opt/kubernetes/cfg/
[root@linux-node1 ~]# scp /opt/kubernetes/cfg/flannel 192.168.56.130:/opt/kubernetes/cfg/

(6)设置Flannel系统服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@linux-node1 ~]# vim /usr/lib/systemd/system/flannel.service
[Unit]
Description=Flanneld overlay address etcd agent
After=network.target
Before=docker.service

[Service]
EnvironmentFile=-/opt/kubernetes/cfg/flannel
ExecStartPre=/opt/kubernetes/bin/remove-docker0.sh
ExecStart=/opt/kubernetes/bin/flanneld ${FLANNEL_ETCD} ${FLANNEL_ETCD_KEY} ${FLANNEL_ETCD_CAFILE} ${FLANNEL_ETCD_CERTFILE} ${FLANNEL_ETCD_KEYFILE}
ExecStartPost=/opt/kubernetes/bin/mk-docker-opts.sh -d /run/flannel/docker

Type=notify

[Install]
WantedBy=multi-user.target
RequiredBy=docker.service
复制系统服务脚本到其它节点上
# scp /usr/lib/systemd/system/flannel.service 192.168.56.120:/usr/lib/systemd/system/
# scp /usr/lib/systemd/system/flannel.service 192.168.56.130:/usr/lib/systemd/system/

三、Flannel CNI集成

(1)下载CNI插件

1
2
3
4
5
6
7
8
https://github.com/containernetworking/plugins/releases
wget https://github.com/containernetworking/plugins/releases/download/v0.7.1/cni-plugins-amd64-v0.7.1.tgz
[root@linux-node1 ~]# mkdir /opt/kubernetes/bin/cni
[root@linux-node2 ~]# mkdir /opt/kubernetes/bin/cni
[root@linux-node3 ~]# mkdir /opt/kubernetes/bin/cni
[root@linux-node1 src]# tar zxf cni-plugins-amd64-v0.7.1.tgz -C /opt/kubernetes/bin/cni
[root@linux-node1 src]# scp -r /opt/kubernetes/bin/cni/* 192.168.56.120:/opt/kubernetes/bin/cni/
[root@linux-node1 src]# scp -r /opt/kubernetes/bin/cni/* 192.168.56.130:/opt/kubernetes/bin/cni/

(2)创建Etcd的key

此步的操作是为了创建POD的网段,并在ETCD中存储,而后FLANNEL从ETCD中取出并进行分配

1
2
3
[root@linux-node1 src]# /opt/kubernetes/bin/etcdctl --ca-file /opt/kubernetes/ssl/ca.pem --cert-file /opt/kubernetes/ssl/flanneld.pem --key-file /opt/kubernetes/ssl/flanneld-key.pem \
--no-sync -C https://192.168.56.110:2379,https://192.168.56.120:2379,https://192.168.56.130:2379 \
mk /kubernetes/network/config '{ "Network": "10.2.0.0/16", "Backend": { "Type": "vxlan", "VNI": 1 }}' >/dev/null 2>&1

(3)启动flannel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@linux-node1 ~]# systemctl daemon-reload
[root@linux-node1 ~]# systemctl enable flannel
[root@linux-node1 ~]# chmod +x /opt/kubernetes/bin/*
[root@linux-node1 ~]# systemctl start flannel

[root@linux-node2 ~]# systemctl daemon-reload
[root@linux-node2 ~]# systemctl enable flannel
[root@linux-node2 ~]# chmod +x /opt/kubernetes/bin/*
[root@linux-node2 ~]# systemctl start flannel

[root@linux-node3 ~]# systemctl daemon-reload
[root@linux-node3 ~]# systemctl enable flannel
[root@linux-node3 ~]# chmod +x /opt/kubernetes/bin/*
[root@linux-node3 ~]# systemctl start flannel

可以看到每个节点上会多出一个flannel.1的网卡,不同的节点都在不同网段。

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
[root@linux-node1 ~]# ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.2.46.0 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::f4e6:1aff:fe7e:575b prefixlen 64 scopeid 0x20<link>
ether f6:e6:1a:7e:57:5b txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0

[root@linux-node2 ~]# ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.2.87.0 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::d4e5:72ff:fe3e:7309 prefixlen 64 scopeid 0x20<link>
ether d6:e5:72:3e:73:09 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0

[root@linux-node3 ~]# ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.2.33.0 netmask 255.255.255.255 broadcast 0.0.0.0
ether be:cd:5a:4f:6b:d1 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 1 overruns 0 carrier 0 collisions 0

(4)遇到的问题:Flannel无法启动

img

img

检查/opt/kubernetes/cfg/etcd.conf配置文件中的ETCD_LISTEN_CLIENT_URLS是否配置监听127.0.0.1:2379。依旧无法启动flannel,重新输入了一遍,正常了,暂时没发现其他原因,至于etcdctl无法获取key值,有待研究!!!

四、配置Docker使用Flannel

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
[root@linux-node1 ~]# vim /usr/lib/systemd/system/docker.service
[Unit] #在Unit下面修改After和增加Requires
After=network-online.target firewalld.service flannel.service #让docker在flannel网络后面启动
Wants=network-online.target
Requires=flannel.service

[Service] #增加EnvironmentFile=-/run/flannel/docker
Type=notify
EnvironmentFile=-/run/flannel/docker #加载环境文件,设置docker0的ip地址为flannel分配的ip地址
ExecStart=/usr/bin/dockerd $DOCKER_OPTS
[root@linux-node1 ~]# systemctl daemon-reload
[root@linux-node1 ~]# systemctl restart docker
[root@linux-node1 ~]# ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.2.46.1 netmask 255.255.255.0 broadcast 0.0.0.0
ether 02:42:1f:ef:9f:b5 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@linux-node2 ~]# ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.2.87.1 netmask 255.255.255.0 broadcast 0.0.0.0
ether 02:42:8a:a5:42:d7 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

[root@linux-node3 ~]# ifconfig docker0
docker0: flags=4099<UP,BROADCAST,MULTICAST> mtu 1500
inet 10.2.33.1 netmask 255.255.255.0 broadcast 0.0.0.0
ether 02:42:57:90:05:47 txqueuelen 0 (Ethernet)
RX packets 0 bytes 0 (0.0 B)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 0 bytes 0 (0.0 B)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0

总结

kubectl get node时,会看到节点的状态READY,如果状态为NotReady,可以查看节点上的kubelet是否已经启动,如果未启动,进行启动。kubelet无法启动,要进行查看systemctl status kubeletjournalctl -xe看看是什么原因导致无法启动。遇到的一种情况是依赖docker,查看docker无法启动。再进一步排查docker无法启动的原因。

k8s学习笔记-04-Node节点二进制部署

[TOC]

K8S Node节点部署

1、部署kubelet

(1)二进制包准备
1
2
3
4
[root@linux-node1 ~]# cd /usr/local/src/kubernetes/server/bin/
[root@linux-node1 bin]# cp kubelet kube-proxy /opt/kubernetes/bin/
[root@linux-node1 bin]# scp kubelet kube-proxy 192.168.56.120:/opt/kubernetes/bin/
[root@linux-node1 bin]# scp kubelet kube-proxy 192.168.56.130:/opt/kubernetes/bin/
(2)创建角色绑定

kubelet启动时会向kube-apiserver发送tsl bootstrap请求,所以需要将bootstrap的token设置成对应的角色,这样kubectl才有权限创建该请求。

1
2
[root@linux-node1 ~]# kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap
clusterrolebinding "kubelet-bootstrap" created
(3)创建 kubelet bootstrapping kubeconfig 文件 设置集群参数
1
2
3
4
5
6
7
[root@linux-node1 ~]# cd /usr/local/src/ssl
[root@linux-node1 ssl]# kubectl config set-cluster kubernetes \
--certificate-authority=/opt/kubernetes/ssl/ca.pem \
--embed-certs=true \
--server=https://192.168.56.110:6443 \
--kubeconfig=bootstrap.kubeconfig
Cluster "kubernetes" set.
(4)设置客户端认证参数
1
2
3
4
[root@linux-node1 ssl]# kubectl config set-credentials kubelet-bootstrap \
--token=ad6d5bb607a186796d8861557df0d17f \
--kubeconfig=bootstrap.kubeconfig
User "kubelet-bootstrap" set.
(5)设置上下文参数
1
2
3
4
5
[root@linux-node1 ssl]# kubectl config set-context default \
--cluster=kubernetes \
--user=kubelet-bootstrap \
--kubeconfig=bootstrap.kubeconfig
Context "default" created.
(6)选择默认上下文
1
2
3
4
5
[root@linux-node1 ssl]# kubectl config use-context default --kubeconfig=bootstrap.kubeconfig
Switched to context "default".
[root@linux-node1 ssl]# cp bootstrap.kubeconfig /opt/kubernetes/cfg
[root@linux-node1 ssl]# scp bootstrap.kubeconfig 192.168.56.120:/opt/kubernetes/cfg
[root@linux-node1 ssl]# scp bootstrap.kubeconfig 192.168.56.130:/opt/kubernetes/cfg

2、部署kubelete 1.设置CNI支持

(1)配置CNI
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@linux-node2 ~]# mkdir -p /etc/cni/net.d
[root@linux-node2 ~]# vim /etc/cni/net.d/10-default.conf
{
"name": "flannel",
"type": "flannel",
"delegate": {
"bridge": "docker0",
"isDefaultGateway": true,
"mtu": 1400
}
}
[root@linux-node3 ~]# mkdir -p /etc/cni/net.d
[root@linux-node2 ~]# scp /etc/cni/net.d/10-default.conf 192.168.56.130:/etc/cni/net.d/10-default.conf
(2)创建kubelet数据存储目录
1
2
[root@linux-node2 ~]# mkdir /var/lib/kubelet
[root@linux-node3 ~]# mkdir /var/lib/kubelet
(3)创建kubelet服务配置
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
[root@linux-node2 ~]# vim /usr/lib/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/lib/kubelet
ExecStart=/opt/kubernetes/bin/kubelet \
--address=192.168.56.120 \
--hostname-override=192.168.56.120 \
--pod-infra-container-image=mirrorgooglecontainers/pause-amd64:3.0 \
--experimental-bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \
--kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \
--cert-dir=/opt/kubernetes/ssl \
--network-plugin=cni \
--cni-conf-dir=/etc/cni/net.d \
--cni-bin-dir=/opt/kubernetes/bin/cni \
--cluster-dns=10.1.0.2 \
--cluster-domain=cluster.local. \
--hairpin-mode hairpin-veth \
--allow-privileged=true \
--fail-swap-on=false \
--logtostderr=true \
--v=2 \
--logtostderr=false \
--log-dir=/opt/kubernetes/log
Restart=on-failure
RestartSec=5



[root@linux-node3 ~]# vim /usr/lib/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/lib/kubelet
ExecStart=/opt/kubernetes/bin/kubelet \
--address=192.168.56.130 \
--hostname-override=192.168.56.130 \
--pod-infra-container-image=mirrorgooglecontainers/pause-amd64:3.0 \
--experimental-bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig \
--kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig \
--cert-dir=/opt/kubernetes/ssl \
--network-plugin=cni \
--cni-conf-dir=/etc/cni/net.d \
--cni-bin-dir=/opt/kubernetes/bin/cni \
--cluster-dns=10.1.0.2 \
--cluster-domain=cluster.local. \
--hairpin-mode hairpin-veth \
--allow-privileged=true \
--fail-swap-on=false \
--logtostderr=true \
--v=2 \
--logtostderr=false \
--log-dir=/opt/kubernetes/log
Restart=on-failure
RestartSec=5
(4)启动Kubelet
1
2
3
4
5
6
7
8
9
[root@linux-node2 ~]# systemctl daemon-reload
[root@linux-node2 ~]# systemctl enable kubelet
[root@linux-node2 ~]# systemctl start kubelet
[root@linux-node2 kubernetes]# systemctl status kubelet

[root@linux-node3 ~]# systemctl daemon-reload
[root@linux-node3 ~]# systemctl enable kubelet
[root@linux-node3 ~]# systemctl start kubelet
[root@linux-node3 kubernetes]# systemctl status kubelet

在查看kubelet的状态,发现有如下报错Failed to get system container stats for "/system.slice/kubelet.service": failed to...此时需要调整kubelet的启动参数。

解决方法:
/usr/lib/systemd/system/kubelet.service[service]新增: Environment="KUBELET_MY_ARGS=--runtime-cgroups=/systemd/system.slice --kubelet-cgroups=/systemd/system.slice"
修改ExecStart: 在末尾新增$KUBELET_MY_ARGS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@linux-node2 system]# systemctl status kubelet
● kubelet.service - Kubernetes Kubelet
Loaded: loaded (/usr/lib/systemd/system/kubelet.service; static; vendor preset: disabled)
Active: active (running) since 四 2018-05-31 16:33:17 CST; 16h ago
Docs: https://github.com/GoogleCloudPlatform/kubernetes
Main PID: 53223 (kubelet)
CGroup: /system.slice/kubelet.service
└─53223 /opt/kubernetes/bin/kubelet --address=192.168.56.120 --hostname-override=192.168.56.120 --pod-infra-container-image=mirrorgooglecontainers/pause-amd64:3.0 --experiment...

6月 01 08:51:09 linux-node2.example.com kubelet[53223]: E0601 08:51:09.355765 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:51:19 linux-node2.example.com kubelet[53223]: E0601 08:51:19.363906 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:51:29 linux-node2.example.com kubelet[53223]: E0601 08:51:29.385439 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:51:39 linux-node2.example.com kubelet[53223]: E0601 08:51:39.393790 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:51:49 linux-node2.example.com kubelet[53223]: E0601 08:51:49.401081 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:51:59 linux-node2.example.com kubelet[53223]: E0601 08:51:59.407863 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:52:09 linux-node2.example.com kubelet[53223]: E0601 08:52:09.415552 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:52:19 linux-node2.example.com kubelet[53223]: E0601 08:52:19.425998 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:52:29 linux-node2.example.com kubelet[53223]: E0601 08:52:29.443804 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
6月 01 08:52:39 linux-node2.example.com kubelet[53223]: E0601 08:52:39.450814 53223 summary.go:102] Failed to get system container stats for "/system.slice/kubelet.service": failed to...
Hint: Some lines were ellipsized, use -l to show in full.
(5)查看csr请求 注意是在linux-node1上执行。
1
2
3
4
[root@linux-node1 ssl]# kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr-6Wc7kmqBIaPOw83l2F1uCKN-uUaxfkVhIU8K93S5y1U 1m kubelet-bootstrap Pending
node-csr-fIXcxO7jyR1Au7nrpUXht19eXHnX1HdFl99-oq2sRsA 1m kubelet-bootstrap Pending
(6)批准kubelet 的 TLS 证书请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@linux-node1 ssl]#  kubectl get csr|grep 'Pending' | awk 'NR>0{print $1}'| xargs kubectl certificate approve
certificatesigningrequest.certificates.k8s.io "node-csr-6Wc7kmqBIaPOw83l2F1uCKN-uUaxfkVhIU8K93S5y1U" approved
certificatesigningrequest.certificates.k8s.io "node-csr-fIXcxO7jyR1Au7nrpUXht19eXHnX1HdFl99-oq2sRsA" approved

[root@linux-node1 ssl]# kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr-6Wc7kmqBIaPOw83l2F1uCKN-uUaxfkVhIU8K93S5y1U 2m kubelet-bootstrap Approved,Issued
node-csr-fIXcxO7jyR1Au7nrpUXht19eXHnX1HdFl99-oq2sRsA 2m kubelet-bootstrap Approved,Issued

执行完毕后,查看节点状态已经是Ready的状态了
[root@linux-node1 ssl]# kubectl get node
NAME STATUS ROLES AGE VERSION
192.168.56.120 Ready <none> 50m v1.10.1
192.168.56.130 Ready <none> 46m v1.10.1

3、部署Kubernetes Proxy

(1)配置kube-proxy使用LVS
1
2
[root@linux-node2 ~]# yum install -y ipvsadm ipset conntrack
[root@linux-node3 ~]# yum install -y ipvsadm ipset conntrack
(2)创建 kube-proxy 证书请求
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@linux-node1 ~]# cd /usr/local/src/ssl/
[root@linux-node1 ssl]# vim kube-proxy-csr.json
{
"CN": "system:kube-proxy",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}
(3)生成证书
1
2
3
4
[root@linux-node1~]# cfssl gencert -ca=/opt/kubernetes/ssl/ca.pem \
-ca-key=/opt/kubernetes/ssl/ca-key.pem \
-config=/opt/kubernetes/ssl/ca-config.json \
-profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
(4)分发证书到所有Node节点
1
2
3
[root@linux-node1 ssl]# cp kube-proxy*.pem /opt/kubernetes/ssl/
[root@linux-node1 ssl]# scp kube-proxy*.pem 192.168.56.120:/opt/kubernetes/ssl/
[root@linux-node1 ssl]# scp kube-proxy*.pem 192.168.56.120:/opt/kubernetes/ssl/
(5)创建kube-proxy配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@linux-node1 ssl]# kubectl config set-cluster kubernetes \
--certificate-authority=/opt/kubernetes/ssl/ca.pem \
--embed-certs=true \
--server=https://192.168.56.110:6443 \
--kubeconfig=kube-proxy.kubeconfig
Cluster "kubernetes" set.

[root@linux-node1 ssl]# kubectl config set-credentials kube-proxy \
--client-certificate=/opt/kubernetes/ssl/kube-proxy.pem \
--client-key=/opt/kubernetes/ssl/kube-proxy-key.pem \
--embed-certs=true \
--kubeconfig=kube-proxy.kubeconfig
User "kube-proxy" set.

[root@linux-node1 ssl]# kubectl config set-context default \
--cluster=kubernetes \
--user=kube-proxy \
--kubeconfig=kube-proxy.kubeconfig
Context "default" created.

[root@linux-node1 ssl]# kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
Switched to context "default".
(6)分发kubeconfig配置文件
1
2
3
[root@linux-node1 ssl]# cp kube-proxy.kubeconfig /opt/kubernetes/cfg/
[root@linux-node1 ssl]# scp kube-proxy.kubeconfig 192.168.56.120:/opt/kubernetes/cfg/
[root@linux-node1 ssl]# scp kube-proxy.kubeconfig 192.168.56.130:/opt/kubernetes/cfg/
(7)创建kube-proxy服务配置
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
[root@linux-node1 ssl]# mkdir /var/lib/kube-proxy
[root@linux-node2 ssl]# mkdir /var/lib/kube-proxy
[root@linux-node3 ssl]# mkdir /var/lib/kube-proxy

[root@linux-node1 ~]# vim /usr/lib/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target

[Service]
WorkingDirectory=/var/lib/kube-proxy
ExecStart=/opt/kubernetes/bin/kube-proxy \
--bind-address=192.168.56.120 \
--hostname-override=192.168.56.120 \
--kubeconfig=/opt/kubernetes/cfg/kube-proxy.kubeconfig \
--masquerade-all \
--feature-gates=SupportIPVSProxyMode=true \
--proxy-mode=ipvs \
--ipvs-min-sync-period=5s \
--ipvs-sync-period=5s \
--ipvs-scheduler=rr \
--logtostderr=true \
--v=2 \
--logtostderr=false \
--log-dir=/opt/kubernetes/log

Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

[root@linux-node1 ssl]# scp /usr/lib/systemd/system/kube-proxy.service 192.168.56.120:/usr/lib/systemd/system/kube-proxy.service
kube-proxy.service 100% 701 109.4KB/s 00:00
[root@linux-node1 ssl]# scp /usr/lib/systemd/system/kube-proxy.service 192.168.56.130:/usr/lib/systemd/system/kube-proxy.service
kube-proxy.service 100% 701 34.9KB/s 00:00
(8)启动Kubernetes Proxy
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
[root@linux-node2 ~]# systemctl daemon-reload
[root@linux-node2 ~]# systemctl enable kube-proxy
[root@linux-node2 ~]# systemctl start kube-proxy
[root@linux-node2 ~]# systemctl status kube-proxy

[root@linux-node3 ~]# systemctl daemon-reload
[root@linux-node3 ~]# systemctl enable kube-proxy
[root@linux-node3 ~]# systemctl start kube-proxy
[root@linux-node3 ~]# systemctl status kube-proxy

检查LVS状态,可以看到已经创建了一个LVS集群,将来自10.1.0.1:443的请求转到192.168.56.110:6443,而6443就是api-server的端口
[root@linux-node2 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.1.0.1:443 rr persistent 10800
-> 192.168.56.110:6443 Masq 1 0 0

[root@linux-node3 ~]# ipvsadm -Ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 10.1.0.1:443 rr persistent 10800
-> 192.168.56.110:6443 Masq 1 0 0

如果你在两台实验机器都安装了kubelet和proxy服务,使用下面的命令可以检查状态:

[root@linux-node1 ssl]# kubectl get node
NAME STATUS ROLES AGE VERSION
192.168.56.120 Ready <none> 22m v1.10.1
192.168.56.130 Ready <none> 3m v1.10.1

到此,K8S的集群就部署完毕,由于K8S本身不支持网络,需要借助第三方网络才能进行创建Pod,将在下一节学习Flannel网络为K8S提供网络支持。

(9)遇到的问题:kubelet无法启动,kubectl get node 提示:no resource found
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
[root@linux-node1 ssl]#  kubectl get node
No resources found.

[root@linux-node3 ~]# systemctl status kubelet
● kubelet.service - Kubernetes Kubelet
Loaded: loaded (/usr/lib/systemd/system/kubelet.service; static; vendor preset: disabled)
Active: activating (auto-restart) (Result: exit-code) since Wed 2018-05-30 04:48:29 EDT; 1s ago
Docs: https://github.com/GoogleCloudPlatform/kubernetes
Process: 16995 ExecStart=/opt/kubernetes/bin/kubelet --address=192.168.56.130 --hostname-override=192.168.56.130 --pod-infra-container-image=mirrorgooglecontainers/pause-amd64:3.0 --experimental-bootstrap-kubeconfig=/opt/kubernetes/cfg/bootstrap.kubeconfig --kubeconfig=/opt/kubernetes/cfg/kubelet.kubeconfig --cert-dir=/opt/kubernetes/ssl --network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/kubernetes/bin/cni --cluster-dns=10.1.0.2 --cluster-domain=cluster.local. --hairpin-mode hairpin-veth --allow-privileged=true --fail-swap-on=false --logtostderr=true --v=2 --logtostderr=false --log-dir=/opt/kubernetes/log (code=exited, status=255)
Main PID: 16995 (code=exited, status=255)

May 30 04:48:29 linux-node3.example.com systemd[1]: Unit kubelet.service entered failed state.
May 30 04:48:29 linux-node3.example.com systemd[1]: kubelet.service failed.
[root@linux-node3 ~]# tailf /var/log/messages
......
May 30 04:46:24 linux-node3 kubelet: F0530 04:46:24.134612 16207 server.go:233] failed to run Kubelet: failed to create kubelet: misconfiguration: kubelet cgroup driver: "cgroupfs" is different from docker cgroup driver: "systemd"
提示kubelet使用的cgroup驱动类型和docker的cgroup驱动类型不一致。进行查看docker.service

[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.com
After=network.target
Wants=docker-storage-setup.service
Requires=docker-cleanup.timer

[Service]
Type=notify
NotifyAccess=all
KillMode=process
EnvironmentFile=-/etc/sysconfig/docker
EnvironmentFile=-/etc/sysconfig/docker-storage
EnvironmentFile=-/etc/sysconfig/docker-network
Environment=GOTRACEBACK=crash
Environment=DOCKER_HTTP_HOST_COMPAT=1
Environment=PATH=/usr/libexec/docker:/usr/bin:/usr/sbin
ExecStart=/usr/bin/dockerd-current \
--add-runtime docker-runc=/usr/libexec/docker/docker-runc-current \
--default-runtime=docker-runc \
--exec-opt native.cgroupdriver=systemd \ ###修改此处"systemd"为"cgroupfs"
--userland-proxy-path=/usr/libexec/docker/docker-proxy-current \
$OPTIONS \
$DOCKER_STORAGE_OPTIONS \
$DOCKER_NETWORK_OPTIONS \
$ADD_REGISTRY \
$BLOCK_REGISTRY \
$INSECURE_REGISTRY
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=1048576
LimitNPROC=1048576
LimitCORE=infinity
TimeoutStartSec=0
Restart=on-abnormal
MountFlags=slave

[Install]
WantedBy=multi-user.target
[root@linux-node3 ~]# systemctl daemon-reload
[root@linux-node3 ~]# systemctl restart docker.service
[root@linux-node3 ~]# systemctl restart kubelet