一、简介
1、基本概念
kubernetes,简称 K8s,是用 8 代替 8 个字符“ubernete”而成的缩写。是一个开源 的,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化的应用简单并且高效,Kubernetes 提供了应用部署,规划,更新,维护的一种机制。
1.1 传统的应用部署方式
通过插件或脚本来安装应用。这样做的缺点是应用的运行、配 置、管理、所有生存周期将与当前操作系统绑定,这样做并不利于应用的升级更新/回滚等操作,当然也可以通过创建虚拟机的方式来实现某些功能,但是虚拟机非常重,并不利于 可移植性。
1.2 新的应用部署方式(部署容器)
通过部署容器方式实现,每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。相对于虚拟机,容器能快速部署, 由于容器与底层设施、机器文件系统解耦的,所以它能在不同云、不同版本操作系统间进行迁移。
容器占用资源少、部署快,每个应用可以被打包成一个容器镜像,每个应用与容器间成一对一关系也使容器有更大优势,使用容器可以在 build 或 release 的阶段,为应用创建容器镜像,因为每个应用不需要与其余的应用堆栈组合,也不依赖于生产环境基础结构, 这使得从研发到测试、生产能提供一致环境。类似地,容器比虚拟机轻量、更“透明”, 这更便于监控和管理。
1.3 容器编排工具中的战斗机--------Kubernetes
Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、 应用容器化管理。在生产环境中部署一个应用程序时,通常要部署该应用的多个实例以便 对应用请求进行负载均衡。 在 Kubernetes 中,我们可以创建多个容器,每个容器里面运行一个应用实例,然后通过内置的负载均衡策略,实现对这一组应用实例的管理、发现、访问,而这些细节都不需要运维人员去进行复杂的手工配置和处理。
2、Kubernetes核心概念
2.1 Master
Master主要负责资源调度,控制副本,和提供统一访问集群的入口。核心节点也是管理节点
2.2 Node
Node是Kubernetes集群架构中运行Pod的服务节点(亦叫agent或minion)。Node是Kubernetes集群操作的单元,用来承载被分配Pod的运行,是Pod运行的宿主机,由Master管理,并汇报容器状态给Master,同时根据Master要求管理容器生命周期。
Node IP
Node节点的IP地址,是Kubernetes集群中每个节点的物理网卡的IP地址,是真是存在的物理网络,所有属于这个网络的服务器之间都能通过这个网络直接通信;
2.3 Pod
-
Pod直译是豆荚,可以把容器想像成豆荚里的豆子,把一个或多个关系紧密的豆子包在一起就是豆荚(一个Pod)。在k8s中我们不会直接操作容器,而是把容器包装成Pod再进行管理 -
运行于Node节点上 ,若干相关容器的组合。Pod内包含的容器运行在同一宿主机上,使用相同的网络命名空间、IP地址和端口,能够通过localhost进行通信。Pod是k8s进行创建、调度和管理的最小单位,它提供了比容器更高层次的抽象,使得部署和管理更加灵活。一个Pod可以包含一个容器或者多个相关容器。 Pod 就是 k8s 世界里的"应用";而一个应用,可以由多个容器组成。
(1)pause容器
- 每个Pod中都有一个pause容器,pause容器做为Pod的网络接入点,Pod中其他的容器会使用容器映射模式启动并接入到这个pause容器。
- 属于同一个Pod的所有容器共享网络的namespace。
- 如果Pod所在的Node宕机,会将这个Node上的所有Pod重新调度到其他节点上
(2)Pod Volume
(3)资源限制
? 每个Pod可以设置限额的计算机资源有CPU和Memory;
Pod IP
Pod的IP地址,是Docker Engine根据docker0网桥的IP地址段进行分配的,通常是一个虚拟的二层网络,位于不同Node上的Pod能够彼此通信,需要通过Pod IP所在的虚拟二层网络进行通信,而真实的TCP流量则是通过Node IP所在的物理网卡流出的
2.4 Namespace(命名空间)
命名空间将资源对象逻辑上分配到不同Namespace,可以是不同的项目、用户等区分管理,并设定控制策略,从而实现多租户。命名空间也称为虚拟集群。
2.5 Label(标签)
Kubernetes中的任意API对象都是通过Label进行标识,Label的实质是一系列的K/V键值对。Label是Replication Controller和Service运行的基础,二者通过Label来进行关联Node上运行的Pod。
? 一个label是一个被附加到资源上的键/值对,譬如附加到一个Pod上,为它传递一个用户自定的并且可识别的属性.Label还可以被应用来组织和选择子网中的资源
2.6 RC-Replication Controller
Replication Controller用来管理Pod的副本,保证集群中存在指定数量的Pod副本。集群中副本的数量大于指定数量,则会停止指定数量之外的多余pod数量,反之,则会启动少于指定数量个数的容器,保证数量不变。Replication Controller是实现弹性伸缩、动态扩容和滚动升级的核心。
部署和升级Pod,声明某种Pod的副本数量在任意时刻都符合某个预期值;
? ? Pod期待的副本数;
? ? 用于筛选目标Pod的Label Selector;
? ? 当Pod副本数量小于预期数量的时候,用于创建新Pod的Pod模板(template);
2.7 Deployment
-
Deployment 是在 Pod 这个抽象上更为上层的一个抽象,它可以定义一组 Pod 的副本数目、以及这个 Pod 的版本。一般用Deployment 来做应用的真正的管理,而 Pod 是组成 Deployment 最小的单元。 -
Kubernetes 是通过 kube-controller去维护 Deployment 中 Pod 的数目,它也会去帮助 Deployment 自动恢复失败的 Pod。
比如说我可以定义一个 Deployment,这个 Deployment 里面需要两个 Pod,当一个 Pod 失败的时候,控制器就会监测到,它重新把 Deployment 中的 Pod 数目从一个恢复到两个,通过再去新生成一个 Pod。通过控制器,我们也会帮助完成发布的策略。比如说进行滚动升级,进行重新生成的升级,或者进行版本的回滚。
2.8 Service
Service定义了Pod的逻辑集合和访问该集合的策略,是真实服务的抽象。Service提供了一个统一的服务访问入口以及服务代理和发现机制,用户不需要了解后台Pod是如何运行。
一个service定义了访问pod的方式,就像单个固定的IP地址和与其相对应的DNS名之间的关系。
如图示,每个Pod都提供了一个独立的Endpoint(Pod IP+ContainerPort)以被客户端访问,多个Pod副本组成了一个集群来提供服务,一般的做法是部署一个负载均衡器来访问它们,为这组Pod开启一个对外的服务端口如8000,并且将这些Pod的Endpoint列表加入8000端口的转发列表中,客户端可以通过负载均衡器的对外IP地址+服务端口来访问此服务。运行在Node上的kube-proxy其实就是一个智能的软件负载均衡器,它负责把对Service的请求转发到后端的某个Pod实例上,并且在内部实现服务的负载均衡与会话保持机制。Service不是共用一个负载均衡器的IP地址,而是每个Servcie分配一个全局唯一的虚拟IP地址,这个虚拟IP被称为Cluster IP。
(1)Cluster IP
Service的IP地址,特性:
-
?仅仅作用于Kubernetes Servcie这个对象,并由Kubernetes管理和分配IP地址; -
?无法被Ping,因为没有一个"实体网络对象"来响应; -
?只能结合Service Port组成一个具体的通信端口;
?Node IP网、Pod IP网域Cluster IP网之间的通信,采用的是Kubernetes自己设计的一种编程方式的特殊的路由规则,与IP路由有很大的不同
(2)Endpoint(IP+Port)
标识服务进程的访问点;
注:Node、Pod、Replication Controller和Service等都可以看作是一种"资源对象",几乎所有的资源对象都可以通过Kubernetes提供的kubectl工具执行增、删、改、查等操作并将其保存在etcd中持久化存储。
3、Kubernetes组件组成
Kubernetes主要组件有 API Server、Controller Manager、Scheduler、kubelet、kube-proxy,其中前三者运行于集群的Master节点,后两者运行于集群的Slave节点。接着描述了一下用于存储Kubernetes集群信息的Etcd,它是一个高可用、强一致性的服务发现存储仓库。
- mater节点: API Server、Controller Manager、Scheduler、etcd
- node节点: kubelet、kube-proxy
3.1 Master节点
-
etcd:etcd存放的就是集群的状态,一般把所有的集群信息都存放到etcd当中,etcd不属于Kubernetes的某一个部分,而是单独集群部署的,API Server是它唯一的入口,可以直接访问etcd。 -
API Server:API Server提供了操作资源的唯一入口,例如认证、授权、访问控制、注册或者发现,都是通过API Server来完成的。 -
Controller Manager:负责管理集群各种资源,保证资源处于预期的状态。Controller Manager由多种controller组成,包括replication controller、endpoints controller、namespace controller、serviceaccounts controller等 。由控制器完成的主要功能主要包括生命周期功能和API业务逻辑,具体如下:
- 生命周期功能:包括Namespace创建和生命周期、Event垃圾回收、Pod终止相关的垃圾回收、级联垃圾回收及Node垃圾回收等。
- API业务逻辑:例如,由ReplicaSet执行的Pod扩展等。
-
Scheduler:Scheduler调度控制器负责整个集群的资源调度,按照默认或者指定的调度策略将Pod调度到符合要求的Node节点上运行
3.2 Node节点
-
Kubelet:Kubelet维护整个容器的生命周期,API Server创建Pod,Scheduler将Pod调度到符合要求的Node节点上,该节点上的Kubelet就会去运行Pod以及Docker,Pod的存储以及网络都是Kubelet进行管理的 -
Docker:Docker负责镜像的管理,例如镜像的拉取、启动容器等 -
Kube-proxy:Kube-proxy主要是提供整个集群内部Service的负载均衡和服务发现
3.3 组件之间的通信
API Server是etcd访问的唯一入口,只有API Server才能访问和操作etcd集群;API Server对内和对外都提供了统一的REST API,其他组件都是通过API Server进行通信的
用户使用kubectl命令来请求API Server接口完成相应操作
Kubernetes内部组件都是通过一种watch机制去监控API Server中的资源变化,然后对其做一些相应的操作
二、Kubernetes集群部署方式
1、常用方式
2、二进制方式部署k8s集群
由于之前总结过了,这里就不过多阐述了(点击链接,进行查看)
3、kubeadm方式部署k8s集群
由于之前总结过了,这里就不过多阐述了(点击链接,进行查看)
4、部署Harbor仓库
下面一步需要翻墙(用的1.8.0版本的harbor)
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
我们选择了上传这两个软件包,所以要更改名称,移动路径
[root@k8s-master ~]
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
http访问方式的配置:
[root@kub-k8s-master harbor]
hostname: 192.168.246.166
[root@kub-k8s-master harbor]
如果安装失败,重启docker服务,重新安装即可;
浏览器访问测试:
http://192.168.246.166
4.1配置https访问
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
Generating RSA private key, 2048 bit long modulus
............................................+++
............+++
e is 65537 (0x10001)
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
[root@kub-k8s-master harbor]
重启
[root@kub-k8s-master harbor]
prepare base dir is set to /root/harbor
Clearing the configuration file: /config/log/logrotate.conf
Clearing the configuration file: /config/nginx/nginx.conf
Clearing the configuration file: /config/core/env
Clearing the configuration file: /config/core/app.conf
Clearing the configuration file: /config/registry/config.yml
Clearing the configuration file: /config/registry/root.crt
Clearing the configuration file: /config/registryctl/env
Clearing the configuration file: /config/registryctl/config.yml
Clearing the configuration file: /config/db/env
Clearing the configuration file: /config/jobservice/env
Clearing the configuration file: /config/jobservice/config.yml
Generated configuration file: /config/log/logrotate.conf
Generated configuration file: /config/nginx/nginx.conf
Generated configuration file: /config/core/env
Generated configuration file: /config/core/app.conf
Generated configuration file: /config/registry/config.yml
Generated configuration file: /config/registryctl/env
Generated configuration file: /config/db/env
Generated configuration file: /config/jobservice/env
Generated configuration file: /config/jobservice/config.yml
loaded secret from file: /secret/keys/secretkey
Generated configuration file: /compose_location/docker-compose.yml
Clean up the input dir
如果出错,重启Docker服务,再次执行./prepare
[root@kub-k8s-master harbor]
Stopping nginx ... done
Stopping harbor-portal ... done
Stopping harbor-jobservice ... done
Stopping harbor-core ... done
Stopping harbor-db ... done
Stopping redis ... done
Stopping registryctl ... done
Stopping registry ... done
Stopping harbor-log ... done
Removing nginx ... done
Removing harbor-portal ... done
Removing harbor-jobservice ... done
Removing harbor-core ... done
Removing harbor-db ... done
Removing redis ... done
Removing registryctl ... done
Removing registry ... done
Removing harbor-log ... done
Removing network harbor_harbor
[root@kub-k8s-master harbor]
访问:
客户端配置(每个访问harbor的机器上都要配置)
在node上面操作:
[root@kub-k8s-node1 ~]
{
"insecure-registries": ["192.168.246.166"]
}
[root@kub-k8s-node1 ~]
创建仓库
创建账号
项目授权
测试
1.登录
[root@kub-k8s-node1 ~]
Username: soso
Password:
Login Succeeded
2.下载一个测试的镜像
[root@kub-k8s-node1 ~]
3.查看
[root@kub-k8s-node1 ~]
REPOSITORY TAG IMAGE ID CREATED SIZE
daocloud.io/library/nginx latest 98ebf73aba75 3 months ago 109MB
4.打个tag
[root@kub-k8s-node1 ~]
5.上传到仓库
[root@kub-k8s-node1 ~]
The push refers to repository [192.168.246.166/jenkins/nginx]
589561a3ffb4: Pushed
ef7dbb0cfc81: Pushed
d56055da3352: Pushed
latest: digest: sha256:f83b2ffd963ac911f9e638184c8d580cc1f3139d5c8c33c87c3fb90aebdebf76 size: 948
在web界面中查看镜像是否被上传到仓库中
三、集群操作
1、查看集群信息
1.查看集群信息:
[root@kub-k8s-master ~]
NAME STATUS ROLES AGE VERSION
kub-k8s-master Ready master 16h v1.16.1
kub-k8s-node1 Ready <none> 15h v1.16.1
kub-k8s-node2 Ready <none> 15h v1.16.1
2.删除节点(无效且显示的也可以删除)
[root@kub-k8s-master ~]
3.查看某一个节点(节点名称可以用空格隔开写多个)
[root@kub-k8s-master ~]
NAME STATUS ROLES AGE VERSION
kub-k8s-node1 Ready <none> 15h v1.16.1
4.查看一个 API 对象的细节
[root@kub-k8s-master ~]
注意:Events(事件) 值得你特别关注
在 Kubernetes 执行的过程中,对 API 对象的所有重要操作,都会被记录在这个对象的 Events 里,并且显示在 kubectl describe 指令返回的结果中。
比如,对于这个 Pod,我们可以看到它被创建之后,被调度器调度(Successfully assigned)到了 node-1,拉取了指定的镜像(pulling image),然后启动了 Pod 里定义的容器(Started container)。
这个部分正是我们将来进行 Debug 的重要依据。如果有异常发生,一定要第一时间查看这些 Events,往往可以看到非常详细的错误信息。
查看node的详细信息
[root@kub-k8s-master ~]
Name: kub-k8s-node1
Roles: <none>
...
-------- -------- ------
cpu 100m (2%) 100m (2%)
memory 50Mi (1%) 50Mi (1%)
ephemeral-storage 0 (0%) 0 (0%)
Events: <none>
查看各组件信息:
查看service的信息:
[root@kub-k8s-master ~]
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 22h
在不同的namespace里面查看service:
[root@kub-k8s-master ~]
-n:namespace名称空间
查看所有名称空间内的资源:
[root@kub-k8s-master ~]
同时查看多种资源信息:
[root@kub-k8s-master ~]
查看主节点:
[root@k8s-master prome]
api查询:
[root@kub-k8s-master ~]
2、创建名称空间
1. 编写yaml文件
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Namespace
metadata:
name: ns-monitor
labels:
name: ns-monitor
2. 创建资源
[root@kub-k8s-master prome]
namespace/ns-monitor created
3. 查看资源
[root@kub-k8s-master prome]
NAME STATUS AGE
default Active 22h
kube-node-lease Active 22h
kube-public Active 22h
kube-system Active 22h
ns-monitor Active 34s
4.查看某一个namespace
[root@kub-k8s-master prome]
5.查看某个namespace的详细信息
[root@kub-k8s-master prome]
6.删除名称空间
[root@kub-k8s-master prome]
namespace "ns-monitor" deleted
[root@k8s-master prome]
namespace "ns-monitor" deleted
3、发布第一个容器化应用
什么才是 Kubernetes 项目能"认识"的方式?
就是使用 Kubernetes 的必备技能:编写配置文件。
这些配置文件可以是 YAML 或者 JSON 格式的。
Kubernetes 跟 Docker 等很多项目最大的不同,就在于它不推荐你使用命令行的方式直接运行容器(虽然 Kubernetes 项目也支持这种方式,比如:kubectl run),而是希望你用 YAML 文件的方式,即:把容器的定义、参数、配置,统统记录在一个 YAML 文件中,然后用这样一句指令把它运行起来:
好处:
- 你会有一个文件能记录下 Kubernetes 到底"run"了什么,方便自身以后查看记录
使用YAML创建Pod
- YAML文件,对应到k8s中,就是一个API Object(API 对象)。当你为这个对象的各个字段填好值并提交给k8s之后,k8s就会负责创建出这些对象所定义的容器或者其他类型的API资源。
编写yaml文件内容如下:
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
spec:
containers:
- name: test-website
image: daocloud.io/library/nginx
ports:
- containerPort: 80
创建pod
[root@kub-k8s-master prome]
pod/website created
查看pod
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
website 1/1 Running 0 74s
=============================================================================
各字段含义:
NAME: Pod的名称
READY: Pod的准备状况,右边的数字表示Pod包含的容器总数目,左边的数字表示准备就绪的容器数目
STATUS: Pod的状态
RESTARTS: Pod的重启次数
AGE: Pod的运行时间
pod的准备状况指的是Pod是否准备就绪以接收请求,Pod的准备状况取决于容器,即所有容器都准备就绪了,Pod才准备就绪。这时候kubernetes的代理服务才会添加Pod作为分发后端,而一旦Pod的准备状况变为false(至少一个容器的准备状况为false),kubernetes会将Pod从代理服务的分发后端移除,即不会分发请求给该Pod。
一个pod刚被创建的时候是不会被调度的,因为没有任何节点被选择用来运行这个pod。调度的过程发生在创建完成之后,但是这个过程一般很快,所以你通常看不到pod是处于unscheduler状态的除非创建的过程遇到了问题。
pod被调度之后,分配到指定的节点上运行,这时候,如果该节点没有所需要的image,那么将会自动从默认的Docker Hub上pull指定的image,一切就绪之后,看到pod是处于running状态了
3.1 查看pod信息
查看pod运行在哪台机器上
[root@kub-k8s-master prome]
可以测试访问:
[root@kub-k8s-master prome]
能访问到!!
查看pods定义的详细信息
查看pod的详细信息----指定pod名字
[root@kub-k8s-master prome]
-o:output
yaml:yaml格式也可以是json格式
查看kubectl describe 支持查询Pod的状态和生命周期事件
[root@kub-k8s-master prome]
Name: website
Namespace: default
Priority: 0
Node: kub-k8s-node1/192.168.246.167
Start Time: Thu, 17 Oct 2019 22:31:16 +0800
Labels: app=website
...
1.各字段含义:
Name: Pod的名称
Namespace: Pod的Namespace。
Image(s): Pod使用的镜像
Node: Pod所在的Node。
Start Time: Pod的起始时间
Labels: Pod的Label。
Status: Pod的状态。
Reason: Pod处于当前状态的原因。
Message: Pod处于当前状态的信息。
IP: Pod的PodIP
Replication Controllers: Pod对应的Replication Controller。
===============================
2.Containers:Pod中容器的信息
Container ID: 容器的ID
Image: 容器的镜像
Image ID:镜像的ID
State: 容器的状态
Ready: 容器的准备状况(true表示准备就绪)。
Restart Count: 容器的重启次数统计
Environment Variables: 容器的环境变量
Conditions: Pod的条件,包含Pod准备状况(true表示准备就绪)
Volumes: Pod的数据卷
Events: 与Pod相关的事件列表
=====
生命周期:指的是status通过
生命周期包括:running、Pending、completed、
3.2 生命周期的介绍
-
Pending:此状态表示Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中(准备状态)。但这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。 -
Running:此状态表示Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。 -
Succeeded:此状态表示 Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。 -
Failed:此状态表示 Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。 -
Unknown:这是一个异常状态(未知状态),表示 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题
其他状态
CrashLoopBackOff: 容器退出,kubelet正在将它重启
InvalidImageName: 无法解析镜像名称
ImageInspectError: 无法校验镜像
ErrImageNeverPull: 策略禁止拉取镜像
ImagePullBackOff: 正在重试拉取
RegistryUnavailable: 连接不到镜像中心
ErrImagePull: 通用的拉取镜像出错
CreateContainerConfigError: 不能创建kubelet使用的容器配置
CreateContainerError: 创建容器失败
m.internalLifecycle.PreStartContainer 执行hook报错
RunContainerError: 启动容器失败
PostStartHookError: 执行hook报错
ContainersNotInitialized: 容器没有初始化完毕
ContainersNotReady: 容器没有准备完毕
ContainerCreating:容器创建中
PodInitializing:pod 初始化中
DockerDaemonNotReady:docker还没有完全启动
NetworkPluginNotReady: 网络插件还没有完全启动
3.3 进入Pod对应的容器内部
通过pod名称
[root@kub-k8s-master prome]
root@website:/
3.4 删除pod
[root@kub-k8s-master prome]
[root@kub-k8s-master prome]
举例:
[root@kub-k8s-master prome]
pod "website" deleted
[root@kub-k8s-master prome]
pod "website" deleted
3.5 创建pod
[root@kub-k8s-master prome]
[root@k8s-master prome]
重新启动基于yaml文件的应用(这里并不是重新启动服务)
扩展
create与apply的区别:
create创建的应用如果需要修改yml文件,必须先指定yml文件删除,在创建新的pod。
如果是apply创建的应用可以直接修改yml文件,继续apply创建,不用先删掉。
四、Yaml文件语法解析
除了某些强制性的命令,如:kubectl run或者expose等,k8s还允许通过配置文件的方式来创建这些操作对象。
通常,使用配置文件的方式会比直接使用命令行更可取,因为这些文件可以进行版本控制,而且文件的变化和内容也可以进行审核,当使用及其复杂的配置来提供一个稳健、可靠和易维护的系统时,这些点就显得非常重要。
在声明定义配置文件的时候,所有的配置文件都存储在YAML或者JSON格式的文件中并且遵循k8s的资源配置方式。
YAML是专门用来写配置文件的语言,非常简洁和强大,使用比json更方便。它实质上是一种通用的数据串行化格式。
kubernetes中用来定义YAML文件创建Pod和创建Deployment等资源。
使用YAML用于K8s的定义的好处:
便捷性:不必添加大量的参数到命令行中执行命令
可维护性:YAML文件可以通过源头控制,跟踪每次操作
灵活性:YAML可以创建比命令行更加复杂的结构
YAML语法规则:
1. 大小写敏感
2. 使用缩进表示层级关系
3. 缩进时不允许使用Tab键,只允许使用空格
4. 缩进的空格数不重要,只要相同层级的元素左侧对齐即可
5. " 表示注释,从这个字符一直到行尾,都会被解析器忽略
在 k8s 中,只需要知道两种结构类型:
1.Lists
2.Maps
字典
a={key:value, key1:{key2:{value2}}, key3:{key4:[1,{key5:value5},3,4,5]}}
key: value
key1:
key2: value2
key3:
key4:
- 1
- key5: value5
- 3
- 4
- 5
YAML Maps
Map指的是字典,即一个Key:Value 的键值对信息。
例如:
---
apiVersion: v1
kind: Pod
注:--- 为可选的分隔符 ,当需要在一个文件中定义多个结构的时候需要使用。上述内容表示有两个键apiVersion和kind,分别对应的值为v1和Pod。
Maps的value既能够对应字符串也能够对应一个Maps。
例如:
---
apiVersion: v1
kind: Pod
metadata:
name: kube100-site
labels:
app: web
{apiVersion:v1,kind:Pod,Metadata:{name:kube100-site,labels:{app:web}}}
注:上述的YAML文件中,metadata这个KEY对应的值为一个Maps,而嵌套的labels这个KEY的值又是一个Map。实际使用中可视情况进行多层嵌套。
YAML处理器根据行缩进来知道内容之间的关联。上述例子中,使用两个空格作为缩进,但空格的数据量并不重要,只是至少要求一个空格并且所有缩进保持一致的空格数 。例如,name和labels是相同缩进级别,因此YAML处理器知道他们属于同一map;它知道app是lables的值因为app的缩进更大。
注意:在YAML文件中绝对不要使用tab键
YAML Lists
List即列表,就是数组
例如:
args:
- beijing
- shanghai
- shenzhen
- guangzhou
可以指定任何数量的项在列表中,每个项的定义以连字符(-)开头,并且与父元素之间存在缩进。
在JSON格式中,表示如下:
{
"args": ["beijing", "shanghai", "shenzhen", "guangzhou"]
}
五、Pod API属性详解
Pod API 对象
Pod是 k8s 项目中的最小编排单位。将这个设计落实到 API 对象上,容器(Container)就成了 Pod 属性里一个普通的字段。
问题:
? 通过yaml文件创建pod的时候里面有容器,这个文件里面到底哪些属性属于 Pod 对象,哪些属性属于 Container?
解决:
Pod 扮演的是传统环境里"虚拟机"的角色。是为了使用户从传统环境(虚拟机环境)向 k8s(容器环境)的迁移,更加平滑。
把 Pod 看成传统环境里的"机器"、把容器看作是运行在这个"机器"里的"用户程序",那么很多关于 Pod 对象的设计就非常容易理解了。 凡是调度、网络、存储,以及安全相关的属性,基本上是 Pod 级别的
? 共同特征是,它们描述的是"机器"这个整体,而不是里面运行的"程序"。
比如:
配置这个"机器"的网卡(即:Pod 的网络定义)
配置这个"机器"的磁盘(即:Pod 的存储定义)
配置这个"机器"的防火墙(即:Pod 的安全定义)
这台"机器"运行在哪个服务器之上(即:Pod 的调度)
kind:指定了这个 API 对象的类型(Type),是一个 Pod,根据实际情况,此处资源类型可以是Deployment、Job、Ingress、Service等。
metadata:包含Pod的一些meta信息,比如名称、namespace、标签等信息.
spec:specification of the resource content 指定该资源的内容,包括一些container,storage,volume以及其他Kubernetes需要的参数,以及诸如是否在容器失败时重新启动容器的属性。可在特定Kubernetes API找到完整的Kubernetes Pod的属性。
specification----->[spes?f??ke??n]
容器可选的设置属性:
除了上述的基本属性外,还能够指定复杂的属性,包括容器启动运行的命令、使用的参数、工作目录以及每次实例化是否拉取新的副本。 还可以指定更深入的信息,例如容器的退出日志的位置。
容器可选的设置属性包括:
"name"、"image"、"command"、"args"、"workingDir"、"ports"、"env"、resource、"volumeMounts"、livenessProbe、readinessProbe、livecycle、terminationMessagePath、"imagePullPolicy"、securityContext、stdin、stdinOnce、tty
跟"机器"相关的配置
[root@kub-k8s-master ~]
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
website 1/1 Running 0 2d23h
[root@kub-k8s-master prome]
六、Pod调度
nodeSelector
nodeName
这两个属性的功能是一样的都是用于人工干预调度器
1、指定node节点的名称(nodeName)
将node1上面的pod删除掉
[root@kub-k8s-master prome]
pod "website" deleted
===========================================
nodeName:是一个供用户将 Pod 与 Node 进行绑定的字段,用法:
现在指定将pod创在node2上面:
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
spec:
containers:
- name: test-website
image: daocloud.io/library/nginx
ports:
- containerPort: 80
nodeName: kub-k8s-node2
创建
[root@kub-k8s-master prome]
pod/website created
NodeName:
- 一旦 Pod 的这个字段被赋值,k8s就会被认为这个 Pod 已经经过了调度,调度的结果就是赋值的节点名字。这个字段一般由调度器负责设置,用户也可以设置它来"骗过"调度器,这个做法一般是在测试或者调试的时候才会用到。
2、指定node标签(nodeSelector)
首先我们需要知道node2上面你的标签有哪些?
1.查看node2上面的标签
[root@kub-k8s-master prome]
1.重新创建一个新的pod
"nodeSelector:是一个供用户将 Pod 与 Node 进行绑定的字段",,通过指定标签来指定
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: tomcat
labels:
app: tomcat
spec:
containers:
- name: test-tomcat
image: daocloud.io/library/tomcat:8
ports:
- containerPort: 8080
nodeSelector:
kubernetes.io/hostname: kub-k8s-node2
2.创建pod
[root@kub-k8s-master prome]
pod/tomcat created
注:表示这个 Pod 永远只能运行在携带了"kubernetes.io/hostname: kub-k8s-node2"标签(Label)的节点上;否则,它将调度失败。
七、域名解析
设置pod容器里面的hosts文件内容,也是做本地解析
HostAliases:定义 Pod 的 hosts 文件(比如 /etc/hosts)里的内容,用法:
1.首先先将刚创建的pod删除掉
[root@kub-k8s-master prome]
pod "tomcat" deleted
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: tomcat
labels:
app: tomcat
spec:
hostAliases:
- ip: "192.168.246.113"
hostnames:
- "foo.remote"
- "bar.remote"
containers:
- name: test-tomcat
image: daocloud.io/library/tomcat:8
ports:
- containerPort: 8080
2.创建pod
[root@kub-k8s-master prome]
pod/tomcat created
3.连接pod
[root@kub-k8s-master prome]
root@tomcat:/usr/local/tomcat
注意:
- 在 k8s 中,如果要设置 hosts 文件里的内容,一定要通过这种方法。否则,如果直接修改了 hosts 文件,在 Pod 被删除重建之后,kubelet 会自动覆盖掉被修改的内容。
八、进程共享
凡是跟容器的 Linux Namespace 相关的属性,也一定是 Pod 级别的
原因:Pod 的设计,就是要让它里面的容器尽可能多地共享 Linux Namespace,仅保留必要的隔离和限制能力。这样,Pod 模拟出的效果,就跟虚拟机里程序间的关系非常类似了。
举例,一个 Pod 定义 yaml 文件如下:
[root@kub-k8s-master prome]
pod "website" deleted
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
spec:
shareProcessNamespace: true
containers:
- name: test-web
image: daocloud.io/library/nginx
ports:
- containerPort: 80
- name: busybos
image: daocloud.io/library/busybox
stdin: true
tty: true
2.创建
[root@kub-k8s-master prome]
pod/website created
1. 定义了 shareProcessNamespace=true
表示这个 Pod 里的容器要共享进程(PID Namespace)如果是false则为不共享。
2. 定义了两个容器:
一个 nginx 容器
一个开启了 tty 和 stdin 的 busybos 容器
在 Pod 的 YAML 文件里声明开启它们俩,等同于设置了 docker run 里的 -it(-i 即 stdin,-t 即 tty)参数。
可以直接认为 tty 就是 Linux 给用户提供的一个常驻小程序,用于接收用户的标准输入,返回操作系统的标准输出。为了能够在 tty 中输入信息,需要同时开启 stdin(标准输入流)。
此 Pod 被创建后,就可以使用 shell 容器的 tty 跟这个容器进行交互了。
查看运行在那台机器上面:
我们登录node1的机器连接busybox的容器
[root@kub-k8s-node1 ~]
在容器里不仅可以看到它本身的 ps 指令,还可以看到 nginx 容器的进程,以及 Infra 容器的 /pause 进程。也就是说整个 Pod 里的每个容器的进程,对于所有容器来说都是可见的:它们共享了同一个 PID Namespace。
[root@kub-k8s-master prome]
[root@kub-k8s-master prome]
将shareProcessNamespace=true修改为false
[root@kub-k8s-master prome]
pod/website created
验证:
凡是 Pod 中的容器要共享宿主机的 Namespace,也一定是 Pod 级别的定义。
刚才的都是pod里面容器的Namespace,并没有和本机的Namespace做共享,接下来我们可以做与本机的Namespace共享,可以在容器里面看到本机的进程。
[root@kub-k8s-master prome]
pod "website" deleted
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: website
labels:
app: website
spec:
hostNetwork: true
hostIPC: true
hostPID: true
containers:
- name: test-web
image: daocloud.io/library/nginx
ports:
- containerPort: 80
- name: busybos
image: daocloud.io/library/busybox
stdin: true
tty: true
创建pod
[root@kub-k8s-master prome]
pod/website created
验证:
定义了共享宿主机的 Network、IPC 和 PID Namespace。这样,此 Pod 里的所有容器,会直接使用宿主机的网络、直接与宿主机进行 IPC 通信、看到宿主机里正在运行的所有进程。
九、容器属性
Pod 里最重要的字段"Containers":
- "Containers"和"Init Containers"这两个字段都属于 Pod 对容器的定义,内容也完全相同,只是 Init Containers 的生命周期,会先于所有的 Containers,并且严格按照定义的顺序执行.
k8s 对 Container 的定义,和 Docker 相比并没有什么太大区别。
- Docker中Image(镜像)、Command(启动命令)、workingDir(容器的工作目录)、Ports(容器要开发的端口),以及 volumeMounts(容器要挂载的 Volume)都是构成 k8s 中 Container 的主要字段。
十、镜像拉取策略
ImagePullPolicy :定义镜像的拉取策略。之所以是一个 Container 级别的属性,是因为容器镜像本来就是 Container 定义中的一部分。
策略 | 作用 |
---|
Never | 只使用本地image | Always | 每次都下载镜像 | IfNotPresent | 优先使用本地image,本地没有再去下载 |
默认值: Always:表示每次创建 Pod 都重新拉取一次镜像。
- 镜像存在而且已经是最新版本就不在拉取镜像
- 如果不存在下载镜像
- 如果镜像存在但是版本不是新版本也会下载镜像
避免:不用latest,下载镜像的时候直接指定版本
注:但是有bug,当容器的镜像是类似于 nginx 或者 nginx:latest 这样的名字时,ImagePullPolicy 也会被认为 Always。
十一、pod的生命周期管理
1、简介
Lifecycle :定义 Container Lifecycle Hooks。作用是在容器状态发生变化时触发一系列"钩子"。
lifecycle有两种回调函数:
- PostStart:容器创建成功后,运行前的任务,用于资源部署、环境准备等。
- PreStop:在容器被终止前的任务,用于优雅关闭应用程序、通知其他系统等等。
postStart:是在容器启动后,立刻执行一个指定的操作。
注意:postStart 定义的操作,虽然是在 Docker 容器 ENTRYPOINT 执行之后,但它并不严格保证顺序。也就是说,在 postStart 启动时,ENTRYPOINT 有可能还没有结束。 如果 postStart执行超时或者错误,k8s 会在该 Pod 的 Events 中报出该容器启动失败的错误信息,导致 Pod 也处于失败的状态。
preStop:是容器被杀死之前(比如,收到了 SIGKILL 信号)。
注意:preStop 操作的执行,是同步的。 所以,它会阻塞当前的容器杀死流程,直到这个 Hook 定义操作完成之后,才允许容器被杀死,这跟 postStart 不一样。
2、生命周期
一个Pod 对象在 Kubernetes 中的生命周期:Pod 生命周期的变化,主要体现在 Pod API 对象的Status 部分,这是除了 Metadata 和 Spec 之外的第三个重要字段。
其中,phase表示一个Pod处于其生命周期的哪个阶段,一共有以下5个可能的取值
-
Pending:此状态表示Pod 的 YAML 文件已经提交给了 Kubernetes,API 对象已经被创建并保存在 Etcd 当中(准备状态)。但这个 Pod 里有些容器因为某种原因而不能被顺利创建。比如,调度不成功。 -
Running:此状态表示Pod 已经调度成功,跟一个具体的节点绑定。它包含的容器都已经创建成功,并且至少有一个正在运行中。 -
Succeeded:此状态表示 Pod 里的所有容器都正常运行完毕,并且已经退出了。这种情况在运行一次性任务时最为常见。 -
Failed:此状态表示 Pod 里至少有一个容器以不正常的状态(非 0 的返回码)退出。这个状态的出现,意味着你得想办法 Debug 这个容器的应用,比如查看 Pod 的 Events 和日志。 -
Unknown:这是一个异常状态(未知状态),表示 Pod 的状态不能持续地被 kubelet 汇报给 kube-apiserver 这很有可能是主从节点(Master 和 Kubelet)间的通信出现了问题
3、案例
这是 k8s 官方文档的一个 Pod YAML 文件
在这个例子中,容器成功启动之后,在 /usr/share/message 里写入了一句"欢迎信息"(即 postStart 定义的操作)。而在这个容器被删除之前,我们则先调用了 nginx 的退出指令(即 preStop 定义的操作),从而实现了容器的"优雅退出"。
步骤:
- 定义了一个 nginx 镜像的容器
- 设置了一个 postStart 和 preStop 参数
[root@kub-k8s-master prome]
pod "website" deleted
[root@kub-k8s-master prome]
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: lifecycle-demo
spec:
containers:
- name: lifecycle-demo-container
image: daocloud.io/library/nginx
lifecycle:
postStart:
exec:
command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
preStop:
exec:
command: ["/usr/sbin/nginx","-s","quit"]
验证:
[root@kub-k8s-node1 ~]
root@lifecycle-demo:~
Hello from the postStart handler
4、扩展:
Pod 对象的 Status 字段,还可以再细分出一组 Conditions:
这些细分状态的值包括:
- PodScheduled
- Ready
- Initialized
- Unschedulable
它们主要用于描述造成当前 Status 的具体原因是什么。
-
比如, Pod 当前的 Status 是 Pending,对应的 Condition 是 Unschedulable,这表示它的调度出现了问题。 -
比如, Ready 这个细分状态表示 Pod 不仅已经正常启动(Running 状态),而且已经可以对外提供服务了。这两者之间(Running 和 Ready)是有区别的,仔细思考一下。
Pod 的这些状态信息,是判断应用运行情况的重要标准,尤其是 Pod 进入了非"Running"状态后,一定要能迅速做出反应,根据它所代表的异常情况开始跟踪和定位,而不是去手忙脚乱地查阅文档。
十二、Projected Volume
1、什么是Projected Volume
在 k8s 中,有几种特殊的 Volume,它们的意义不是为了存放容器里的数据,也不是用来进行容器和宿主机之间的数据交换。而是为容器提供预先定义好的数据。
从容器的角度来看,这些 Volume 里的信息仿佛是被 k8s “投射”(Project)进入容器当中的。
k8s 支持的 Projected Volume 一共有四种:
- Secret
- ConfigMap
- Downward API
- ServiceAccount Token
2、Secret详解
secret用来保存小片敏感数据的k8s资源,例如密码,token,或者秘钥。这类数据当然也可以存放在Pod或者镜像中,但是放在Secret中是为了更方便的控制如何使用数据,并减少暴露的风险。
Pod有2种方式来使用secret:
- 作为volume的一个域被一个或多个容器挂载
- 在拉取镜像的时候被kubelet引用。
用户可以创建自己的secret,系统也会有自己的secret。 Pod需要先引用才能使用某个secret。
2.1 命令方式创建secret
假如某个Pod要访问数据库,需要用户名密码,分别存放在2个文件中:username.txt,password.txt
例子:
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
kubectl create secret 指令将用户名密码写到secret中,并在apiserver创建Secret
[root@kub-k8s-master ~]
secret/db-user-pass created
查看创建结果:
[root@kub-k8s-master ~]
NAME TYPE DATA AGE
db-user-pass Opaque 2 54s
default-token-6svwp kubernetes.io/service-account-token 3 4d11h
注: opaque:英[???pe?k] 美[o??pe?k] 模糊
查看详细信息:
[root@kub-k8s-master ~]
Name: db-user-pass
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
password.txt: 12 bytes
username.txt: 5 bytes
get或describe指令都不会展示secret的实际内容,这是出于对数据的保护的考虑,如果想查看实际内容使用命令:
[root@kub-k8s-master ~]
base64解码:
[root@kub-k8s-master ~]
1f2d1e2e67df
2.2 yaml方式创建Secret
创建一个secret.yaml文件,内容用base64编码:明文显示容易被别人发现,这里先转码。
[root@kub-k8s-master ~]
YWRtaW4=
[root@kub-k8s-master ~]
MWYyZDFlMmU2N2Rm
创建一个secret.yaml文件,内容用base64编码
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
创建:
[root@kub-k8s-master prome]
secret/mysecret created
解析Secret中内容,还是经过编码的—需要解码
[root@kub-k8s-master ~]
NAME TYPE DATA AGE
default-token-7vc82 kubernetes.io/service-account-token 3 30h
mysecret Opaque 2 6s
[root@kub-k8s-master prome]
apiVersion: v1
data:
password: MWYyZDFlMmU2N2Rm
username: YWRtaW4=
kind: Secret
metadata:
creationTimestamp: "2019-10-21T03:07:56Z"
name: mysecret
namespace: default
resourceVersion: "162855"
selfLink: /api/v1/namespaces/default/secrets/mysecret
uid: 36bcd07d-92eb-4755-ac0a-a5843ed986dd
type: Opaque
2.3 使用Secret
一个Pod中引用Secret的例子:
[root@kub-k8s-master prome]
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: testredis
image: daocloud.io/library/redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
创建:
[root@kub-k8s-master prome]
pod/mypod created
[root@kub-k8s-master prome]
root@mypod:/data
root@mypod:/etc/foo
password username
root@mypod:/etc/foo
1f2d1e2e67df
注:
- 每一个被引用的Secret都要在
spec.volumes 中定义 - 如果Pod中的多个容器都要引用这个Secret那么每一个容器定义中都要指定自己的volumeMounts,但是Pod定义中声明一次spec.volumes就好了。
2.4 映射secret key到指定的路径
[root@kub-k8s-master prome]
pod "mypod" deleted
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: testredis
image: daocloud.io/library/redis
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
2.创建
[root@kub-k8s-master prome]
pod/mypod created
3.从volume中读取secret的值
[root@kub-k8s-master prome]
root@mypod:/data
root@mypod:/etc/foo/my-group
my-username
root@mypod:/etc/foo/my-group
admin
root@mypod:/etc/foo/my-group
注:
- username被映射到了文件
/etc/foo/my-group/my-username 而不是/etc/foo/username
被挂载的secret内容自动更新
也就是如果修改一个Secret的内容,那么挂载了该Secret的容器中也将会取到更新后的值,但是这个时间间隔是由kubelet的同步时间决定的。
1.设置base64加密
[root@kub-k8s-master prome]
cWlhbmZlbmcK
2.将admin替换成qianfeng
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: cWlhbmZlbmcK
password: MWYyZDFlMmU2N2Rm
1.创建
[root@kub-k8s-master prome]
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
secret/mysecret configured
2.连接pod容器
[root@kub-k8s-master prome]
root@mypod:/data
root@mypod:/etc/foo/my-group
my-username
root@mypod:/etc/foo/my-group
qianfeng
2.5 以环境变量的形式使用Secret
[root@kub-k8s-master prome]
pod "mypod" deleted
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: mypod
spec:
containers:
- name: testredis
image: daocloud.io/library/redis
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
2.创建使用secret的pod容器
[root@kub-k8s-master prome]
pod/mypod created
3.连接
[root@kub-k8s-master prome]
root@mypod:/data
qianfeng
2.6 实验
1. 通过编写 YAML 文件的方式来创建这个 Secret 对象
Secret 对象要求这些数据必须是经过 Base64 转码的,以免出现明文密码的安全隐患。
转码操作:
[root@kub-k8s-master ~]
YWRtaW4=
[root@kub-k8s-master ~]
MWYyZDFlMmU2N2Rm
注意:像这样创建的 Secret 对象,它里面的内容仅仅是经过了转码,并没有被加密。生产环境中,需要在 Kubernetes 中开启 Secret 的加密插件,增强数据的安全性。
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Secret
metadata:
name: mysecret-01
type: Opaque
data:
user: YWRtaW4=
pass: MWYyZDFlMmU2N2Rm
[root@kub-k8s-master prome]
secret/mysecret-01 created
[root@kub-k8s-master prome]
用yaml方式创建的secret调用方法如下:
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: test-projected-volume
spec:
containers:
- name: test-secret-volume
image: daocloud.io/library/nginx
volumeMounts:
- name: mysql-cred
mountPath: "/projected-volume"
readOnly: true
volumes:
- name: mysql-cred
secret:
secretName: mysecret-01
创建这个 Pod:
[root@kub-k8s-master prome]
pod/test-projected-volume1 created
验证这些 Secret 对象是不是已经在容器里了:
[root@kub-k8s-master prome]
root@test-projected-volume:/
bin dev home lib64 mnt proc root sbin sys usr
boot etc lib media opt projected-volume run srv tmp var
root@test-projected-volume:/
pass user
root@test-projected-volume:/
1f2d1e2e67df
root@test-projected-volume:/
admin
root@test-projected-volume:/
注意:
如果报错:上面这条命令会报错如下
error: unable to upgrade connection: Forbidden (user=system:anonymous, verb=create, resource=nodes, subresource=proxy)
解决:绑定一个cluster-admin的权限
clusterrolebinding.rbac.authorization.k8s.io/system:anonymous created
结果中看到,保存在 Etcd 里的用户名和密码信息,已经以文件的形式出现在了容器的 Volume 目录里。而这个文件的名字,就是 kubectl create secret 指定的 Key,或者说是 Secret 对象的 data 字段指定的 Key。
2、ConfigMap详解
ConfigMap 与 Secret 类似,用来存储配置文件的kubernetes资源对象,所有的配置内容都存储在etcd中。
与 Secret 的区别:
2.1 创建ConfigMap的方式
创建ConfigMap的方式有4种:
命令行方式
- 方式1:通过直接在命令行中指定configmap参数创建,即–from-literal
- 方式2:通过指定文件创建,即将一个配置文件创建为一个ConfigMap,–from-file=<文件>
- 方式3:通过指定目录创建,即将一个目录下的所有配置文件创建为一个ConfigMap,–from-file=<目录>
- 方式4:事先写好标准的configmap的yaml文件,然后kubectl create -f 创建
2.1.1 通过命令行参数–from-literal创建
创建命令:
[root@kub-k8s-master prome]
configmap/test-configmap created
结果如下面的data内容所示:
[root@kub-k8s-master prome]
apiVersion: v1
data:
pass: "1122334"
user: admin
kind: ConfigMap
metadata:
creationTimestamp: "2019-10-21T07:48:15Z"
name: test-configmap
namespace: default
resourceVersion: "187590"
selfLink: /api/v1/namespaces/default/configmaps/test-configmap
uid: 62a8a0d0-fab9-4159-86f4-a06aa213f4b1
2.1.2 通过指定文件创建
编辑配置文件app.properties内容如下:
[root@kub-k8s-master prome]
property.1 = value-1
property.2 = value-2
property.3 = value-3
property.4 = value-4
[mysqld]
!include /home/wing/mysql/etc/mysqld.cnf
port = 3306
socket = /home/wing/mysql/tmp/mysql.sock
pid-file = /wing/mysql/mysql/var/mysql.pid
basedir = /home/mysql/mysql
datadir = /wing/mysql/mysql/var
创建(可以有多个–from-file):
[root@kub-k8s-master prome]
configmap/test-config2 created
结果如下面data内容所示:
[root@kub-k8s-master prome]
apiVersion: v1
data:
app.properties: |
property.1 = value-1
property.2 = value-2
property.3 = value-3
property.4 = value-4
[mysqld]
!include /home/wing/mysql/etc/mysqld.cnf
port = 3306
socket = /home/wing/mysql/tmp/mysql.sock
pid-file = /wing/mysql/mysql/var/mysql.pid
basedir = /home/mysql/mysql
datadir = /wing/mysql/mysql/var
kind: ConfigMap
metadata:
creationTimestamp: "2019-10-21T08:01:43Z"
name: test-config2
namespace: default
resourceVersion: "188765"
selfLink: /api/v1/namespaces/default/configmaps/test-config2
uid: 790fca12-3900-4bf3-a017-5af1070792e5
通过指定文件创建时,configmap会创建一个key/value对,key是文件名,value是文件内容。
2.1.3 指定目录创建
configs 目录下的config-1和config-2内容如下所示:
[root@kub-k8s-master prome]
[root@kub-k8s-master prome]
[root@kub-k8s-master config]
aaa
bbb
c=d
[root@kub-k8s-master config]
eee
fff
h=k
创建:
[root@kub-k8s-master config]
[root@kub-k8s-master prome]
configmap/test-config3 created
结果下面data内容所示:
[root@kub-k8s-master prome]
apiVersion: v1
data:
config1: |
aaa
bbb
c=d
config2: |
eee
fff
h=k
kind: ConfigMap
metadata:
creationTimestamp: "2019-10-21T08:20:42Z"
name: test-config3
namespace: default
resourceVersion: "190420"
selfLink: /api/v1/namespaces/default/configmaps/test-config3
uid: 6e00fded-80a8-4297-aeb3-4c48795e6eb9
指定目录创建时,configmap内容中的各个文件会创建一个key/value对,key是文件名,value是文件内容。
2.1.4 通过标准yaml文件创建
yaml文件内容如下: 注意其中一个key的value有多行内容时的写法
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: ConfigMap
metadata:
name: test-config4
namespace: default
data:
cache_host: memcached-gcxt
cache_port: "11211"
cache_prefix: gcxt
my.cnf: |
[mysqld]
log-bin = mysql-bin
haha = hehe
创建:
[root@kub-k8s-master prome]
configmap/test-config4 created
结果如下面data内容所示:
[root@kub-k8s-master prome]
apiVersion: v1
data:
cache_host: memcached-gcxt
cache_port: "11211"
cache_prefix: gcxt
my.cnf: |
[mysqld]
log-bin = mysql-bin
haha = hehe
kind: ConfigMap
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","data":{"cache_host":"memcached-gcxt","cache_port":"11211","cache_prefix":"gcxt","my.cnf":"[mysqld]\nlog-bin = mysql-bin\nhaha = hehe\n"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"test-config4","namespace":"default"}}
creationTimestamp: "2019-10-21T08:30:24Z"
name: test-config4
namespace: default
resourceVersion: "191270"
selfLink: /api/v1/namespaces/default/configmaps/test-config4
uid: 2a8cd6e7-db2c-4781-b005-e0b76d26394b
查看configmap的详细信息:
[root@kub-k8s-master prome]
2.2 使用ConfigMap
使用ConfigMap有三种方式,
- 通过环境变量的方式,直接传递pod
- 通过在pod的命令行下运行的方式,
- 使用volume的方式挂载入到pod内
示例ConfigMap文件:
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: ConfigMap
metadata:
name: config-map
namespace: default
data:
special.how: very
special.type: charm
创建
[root@kub-k8s-master prome]
configmap/config-map created
2.2.1 通过环境变量使用
(1) 使用valueFrom、configMapKeyRef、name、key指定要用的key:
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: daocloud.io/library/nginx
env:
- name: SPECIAL_LEVEL_KEY
valueFrom:
configMapKeyRef:
name: config-map
key: special.how
- name: SPECIAL_TYPE_KEY
valueFrom:
configMapKeyRef:
name: config-map
key: special.type
restartPolicy: Never
创建pod
[root@kub-k8s-master prome]
pod/dapi-test-pod created
测试:
[root@kub-k8s-master prome]
root@dapi-test-pod:/
charm
(2) 通过envFrom、configMapRef、name使得configmap中的所有key/value对儿 都自动变成环境变量:
[root@kub-k8s-master prome]
pod "dapi-test-pod" deleted
[root@kub-k8s-master prome]
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: daocloud.io/library/nginx
envFrom:
- configMapRef:
name: config-map
restartPolicy: Never
这样容器里的变量名称直接使用configMap里的key名:
[root@kub-k8s-master prome]
pod/dapi-test-pod created.
[root@kub-k8s-master prome]
root@dapi-test-pod:/
HOSTNAME=dapi-test-pod
NJS_VERSION=0.3.3
NGINX_VERSION=1.17.1
KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PKG_RELEASE=1~stretch
KUBERNETES_PORT=tcp://10.96.0.1:443
PWD=/
special.how=very
HOME=/root
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBERNETES_PORT_443_TCP_PORT=443
KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
TERM=xterm
SHLVL=1
KUBERNETES_SERVICE_PORT=443
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
special.type=charm
KUBERNETES_SERVICE_HOST=10.96.0.1
_=/usr/bin/env
2.2.2 作为volume挂载使用
(1) 把1.4中test-config4所有key/value挂载进来:
[root@kub-k8s-master prome]
pod "dapi-test-pod" deleted
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-configmap
spec:
containers:
- name: nginx-configmap
image: daocloud.io/library/nginx
volumeMounts:
- name: config-volume4
mountPath: "/tmp/config4"
volumes:
- name: config-volume4
configMap:
name: test-config4
创建pod
[root@kub-k8s-master prome]
pod/nginx-configmap created
进入容器中/tmp/config4查看:
[root@kub-k8s-master prome]
root@nginx-configmap:/
cache_host cache_port cache_prefix my.cnf
root@nginx-configmap:/
memcached-gcxt
root@nginx-configmap:/
可以看到,在config4文件夹下以每一个key为文件名value为值创建了多个文件。
3、Downward API
Downward API 用于在容器中获取 POD 的基本信息,kubernetes原生支持
Downward API提供了两种方式用于将 POD 的信息注入到容器内部:
- 环境变量:用于单个变量,可以将 POD 信息和容器信息直接注入容器内部。
- Volume挂载:将 POD 信息生成为文件,直接挂载到容器内部中去。
3.1 环境变量的方式
通过Downward API来将 POD 的 IP、名称以及所对应的 namespace 注入到容器的环境变量中去,然后在容器中打印全部的环境变量来进行验证
使用fieldRef获取 POD 的基本信息:
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: test-env-pod
namespace: kube-system
spec:
containers:
- name: test-env-pod
image: daocloud.io/library/nginx
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
注意: POD 的 name 和 namespace 属于元数据,是在 POD 创建之前就已经定下来了的,所以使用 metadata 获取就可以了,但是对于 POD 的 IP 则不一样,因为POD IP 是不固定的,POD 重建了就变了,它属于状态数据,所以使用 status 去获取:
所有基本信息可以使用下面的方式去查看(describe方式看不出来):
创建上面的 POD:
[root@kub-k8s-master prome]
pod/test-env-pod created
POD 创建成功后,查看:
[root@kub-k8s-master prome]
root@test-env-pod:/
POD_NAME=test-env-pod
POD_NAMESPACE=kube-system
POD_IP=10.244.1.35
root@test-env-pod:/
3.2 Volume挂载
通过Downward API将 POD 的 Label、Annotation 等信息通过 Volume 挂载到容器的某个文件中去,然后在容器中打印出该文件的值来验证。
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: test-volume-pod
namespace: kube-system
labels:
k8s-app: test-volume
node-env: test
spec:
containers:
- name: test-volume-pod-container
image: daocloud.io/library/nginx
volumeMounts:
- name: podinfo
mountPath: /etc/podinfo
volumes:
- name: podinfo
downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
将元数据 labels 和 annotaions 以文件的形式挂载到了/etc/podinfo目录下,创建上面的 POD :
[root@kub-k8s-master prome]
pod/test-volume-pod created
[root@kub-k8s-master prome]
[root@k8s-master prome]
root@test-volume-pod:/
root@test-volume-pod:/etc/podinfo
labels
root@test-volume-pod:/etc/podinfo
k8s-app="test-volume"
node-env="test"
在实际应用中,如果你的应用有获取 POD 的基本信息的需求,就可以利用Downward API来获取基本信息,然后编写一个启动脚本或者利用initContainer将 POD 的信息注入到容器中去,然后在自己的应用中就可以正常的处理相关逻辑了。
目前 Downward API 支持的字段:
1. 使用 fieldRef 可以声明使用:
spec.nodeName - 宿主机名字
status.hostIP - 宿主机 IP
metadata.name - Pod 的名字
metadata.namespace - Pod 的 Namespace
status.podIP - Pod 的 IP
spec.serviceAccountName - Pod 的 Service Account 的名字
metadata.uid - Pod 的 UID
metadata.labels['<KEY>'] - 指定 <KEY> 的 Label 值
metadata.annotations['<KEY>'] - 指定 <KEY> 的 Annotation 值
metadata.labels - Pod 的所有 Label
metadata.annotations - Pod 的所有 Annotation
上面这个列表的内容,随着 Kubernetes 项目的发展肯定还会不断增加。所以这里列出来的信息仅供参考,在使用 Downward API 时,还是要记得去查阅一下官方文档。
Secret、ConfigMap,以及 Downward API 这三种 Projected Volume 定义的信息,大多还可以通过环境变量的方式出现在容器里。但是,通过环境变量获取这些信息的方式,不具备自动更新的能力。一般情况下,建议使用 Volume 文件的方式获取这些信息。
4、ServiceAccount
官方文档地址:https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/
k8s中提供了良好的多租户认证管理机制,如RBAC、ServiceAccount还有各种Policy等。
4.1 什么是 Service Account
当用户访问集群(例如使用kubectl命令)时,apiserver 会将用户认证为一个特定的 User Account(目前通常是admin,除非系统管理员自定义了集群配置)。
Pod 容器中的进程也可以与 apiserver 联系。 当它们在联系 apiserver 的时候,它们会被认证为一个特定的Service Account(例如default)。
使用场景:
- Service Account它并不是给kubernetes集群的用户使用的,而是给pod里面的进程使用的,它为pod提供必要的身份认证。----专门为pod里面的进程和apiserver通信提供认证的。
4.2 Service account与User account区别
1. User account是为用户设计的,而service account则是为Pod中的进程调用Kubernetes API或其他外部服务而设计的
2. User account是跨namespace的,而service account则是仅局限它所在的namespace;
3. 每个namespace都会自动创建一个default service account
4. Token controller检测service account的创建,并为它们创建secret
5. 开启ServiceAccount Admission Controller后:
5.1 每个Pod在创建后都会自动设置spec.serviceAccount为default(除非指定了其他ServiceAccout)
5.2 验证Pod引用的service account已经存在,否则拒绝创建
5.3 如果Pod没有指定ImagePullSecrets,则把service account的ImagePullSecrets加到Pod中
5.4 每个container启动后都会挂载该service account的token和ca.crt到/run/secrets/kubernetes.io/serviceaccount/
每一个pod启动之后都会有一个和认证相关的东西存在pod里面,,存在到哪里呢?
查看系统的config配置:
这里用到的token就是被授权过的SeviceAccount账户的token,集群利用token来使用ServiceAccount账户
[root@kub-k8s-master prome]
4.3 Service Account应用示例
Service Account(服务账号)测试示例
因为平时系统会使用默认service account,我们不需要自己创建,感觉不到service account的存在,本实验是使用自己手动创建的service account
1、创建serviceaccount
[root@kub-k8s-master ~]
serviceaccount/mysa created
2、查看mysa
[root@kub-k8s-master ~]
Name: mysa
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: mysa-token-cknwf
Tokens: mysa-token-cknwf
Events: <none>
3、查看mysa自动创建的secret
[root@kub-k8s-master ~]
NAME TYPE DATA AGE
db-user-pass Opaque 2 11h
default-token-6svwp kubernetes.io/service-account-token 3 4d23h
mysa-token-cknwf kubernetes.io/service-account-token 3 76s
mysecret Opaque 2 11h
mysecret-01 Opaque 2 6h58m
pass Opaque 1 7h6m
user Opaque 1 7h7m
4、使用mysa的sa资源配置pod
[root@kub-k8s-master ~]
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
labels:
app: my-pod
spec:
containers:
- name: my-pod
image: daocloud.io/library/nginx
ports:
- name: http
containerPort: 80
serviceAccountName: mysa
5、导入
[root@kub-k8s-master prome]
pod/nginx-pod created
6、查看
[root@kub-k8s-master prome]
7、查看使用的token和secret(使用的是mysa的token)
[root@kub-k8s-master prome]
[map[name:mysa-token-cknwf secret:map[defaultMode:420 secretName:mysa-token-cknwf]]]
[root@kub-k8s-master prome]
4.4 RBAC 详解(基于角色的访问控制)
4.4.1 什么是RBAC
Service Account为服务提供了一种方便的认证机制,但它不关心授权的问题。可以配合RBAC来为Service Account鉴权
在Kubernetes中,授权有ABAC(基于属性的访问控制)、RBAC(基于角色的访问控制)、Webhook、Node、AlwaysDeny(一直拒绝)和AlwaysAllow(一直允许)这6种模式。
在RBAC API中,通过如下的步骤进行授权:
- 定义角色:在定义角色时会指定此角色对于资源的访问控制的规则;
- 绑定角色:将主体与角色进行绑定,对用户进行访问授权。
4.4.2 Role与ClusterRole
- Role:角色可以由命名空间内的Role对象定义,一个Role对象只能用于授予对某一单一命名空间中资源的访问权限。
- ClusterRole:整个Kubernetes集群范围内有效的角色则通过ClusterRole对象实现。
简介
role:
1、允许的操作,如get,list等
2、允许操作的对象,如pod,service等
rolebinding:
将哪个用户绑定到哪个role或clusterrole上
clusterrole:(集群角色)
clusterrolebinding:(绑定到s集群)
如果使用rolebinding绑定到clusterrole上,表示绑定的用户只能用于当前namespace的权限
4.4.3 实验一:role
创建k8s账号与RBAC授权使用
创建账号/用户
1、创建私钥
[root@kub-k8s-master ~]
Generating RSA private key, 2048 bit long modulus
...............................+++
..........................+++
e is 65537 (0x10001)
用此私钥创建一个csr(证书签名请求)文件
[root@kub-k8s-master ~]
拿着私钥和请求文件生成证书
[root@kub-k8s-master ~]
Signature ok
subject=/CN=soso
Getting CA Private Key
2、查看证书内容
[root@kub-k8s-master ~]
生成账号
[root@kub-k8s-master ~]
User "soso" set.
3、设置上下文环境--指的是这个账号只能在这个环境中才能用
[root@kub-k8s-master ~]
Context "soso@kubernetes" created.
查看当前的工作上下文
[root@kub-k8s-master ~]
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.246.166:6443
....
4、切换用户(切换上下文)
[root@kub-k8s-master ~]
Switched to context "soso@kubernetes".
验证是否已经切换到了新的上下文
[root@kub-k8s-master ~]
soso@kubernetes
5.测试(还未赋予权限)
[root@kub-k8s-master ~]
Error from server (Forbidden): pods is forbidden: User "soso" cannot list resource "pods" in API group "" in the namespace "default"
创建一个角色(role)---权限
1.切回管理帐号先
[root@kub-k8s-master ~]
Switched to context "kubernetes-admin@kubernetes".
创建角色:
[root@kub-k8s-master ~]
role.rbac.authorization.k8s.io/myrole created
--verb: 相当于是权限
--resource:给什么资源使用
2.绑定用户soso(上面创建的用户),绑定role为myrole
[root@kub-k8s-master ~]
rolebinding.rbac.authorization.k8s.io/myrole-binding created
3.切换用户
[root@kub-k8s-master ~]
Switched to context "soso@kubernetes".
4.查看权限(只授权了default名称空间pod和svc的get,list,watch权限)
[root@kub-k8s-master ~]
NAME READY STATUS RESTARTS AGE
lifecycle-demo 1/1 Running 1 22h
mypod 1/1 Running 0 8h
nginx-configmap 1/1 Running 0 4h29m
nginx-pod 1/1 Running 0 39m
[root@kub-k8s-master ~]
Error from server (Forbidden): pods is forbidden: User "soso" cannot list resource "pods" in API group "" in the namespace "kube-system"
[root@kub-k8s-master ~]
Error from server (Forbidden): pods "nginx-pod" is forbidden: User "soso" cannot delete resource "pods" in API group "" in the namespace "default"
5.切换用户
[root@kub-k8s-master ~]
Switched to context "kubernetes-admin@kubernetes".
...
4.4.4 实验二:clusterrole
6.删除soso账号之前绑定的rolebinding
[root@kub-k8s-master ~]
rolebinding.rbac.authorization.k8s.io "myrole-binding" deleted
7.创建clusterrole
[root@kub-k8s-master ~]
clusterrole.rbac.authorization.k8s.io/myclusterrole created
8.绑定集群角色到用户soso
[root@kub-k8s-master ~]
clusterrolebinding.rbac.authorization.k8s.io/my-cluster-rolebinding created
8.切换账号
[root@kub-k8s-master ~]
Switched to context "soso@kubernetes".
9.查看权限 查看kube-system空间的pod
[root@kub-k8s-master ~]
NAME READY STATUS RESTARTS AGE
coredns-5644d7b6d9-sm8hs 1/1 Running 0 5d
coredns-5644d7b6d9-vddll 1/1 Running 0 5d
etcd-kub-k8s-master 1/1 Running 0 5d
注意:10.切换为管理员用户
[root@kub-k8s-master ~]
4.4.5 设置上下文和账户切换
设置工作上下文(前提得有用户)
[root@kub-k8s-master ~]
Context "soso@kubernetes" created.
查看当前的工作上下文
[root@kub-k8s-master ~]
apiVersion: v1
clusters:
- cluster:
....
切换上下文(切换用户)
[root@kub-k8s-master ~]
Switched to context "soso@kubernetes".
切换为管理员用户
[root@kub-k8s-master prome]
Switched to context "kubernetes-admin@kubernetes".
十三、容器监控检查及恢复机制
? 在 k8s 中,可以为 Pod 里的容器定义一个健康检查"探针"(Probe)。kubelet 就会根据这个 Probe 的返回值决定这个容器的状态,而不是直接以容器是否运行(来自 Docker 返回的信息)作为依据。这种机制,是生产环境中保证应用健康存活的重要手段。
注:
- k8s 中并没有 Docker 的 Stop 语义。所以如果容器被探针检测到有问题,查看状态虽然看到的是 Restart,但实际却是重新创建了容器。
1、命令模式探针
Kubernetes 文档中的例子:
[root@kub-k8s-master ~]
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: test-liveness-exec
spec:
containers:
- name: liveness
image: daocloud.io/library/nginx
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -rf /tmp/healthy; sleep 50
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
文件内容解析:
-
它在启动之后做的第一件事是在 /tmp 目录下创建了一个 healthy 文件,以此作为自己已经正常运行的标志。而 30 s 过后,它会把这个文件删除掉。 -
与此同时,定义了一个这样的 livenessProbe(健康检查)。它的类型是 exec,它会在容器启动后,在容器里面执行一句我们指定的命令,比如:“cat /tmp/healthy”。这时,如果这个文件存在,这条命令的返回值就是 0,Pod 就会认为这个容器不仅已经启动,而且是健康的。 -
这个健康检查,在容器启动 5 s 后开始执行(initialDelaySeconds: 5),每 5 s 执行一次(periodSeconds: 5)。
创建Pod:
[root@kub-k8s-master prome]
pod/test-liveness-exec created
查看 Pod 的状态:
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
nginx-configmap 1/1 Running 0 16h
nginx-pod 1/1 Running 0 12h
test-liveness-exec 1/1 Running 0 75s
由于已经通过了健康检查,这个 Pod 就进入了 Running 状态。
然后30 s 之后,再查看一下 Pod 的 Events:
[root@kub-k8s-master prome]
发现,这个 Pod 在 Events 报告了一个异常:
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Unhealthy 54s (x9 over 3m34s) kubelet, kub-k8s-node1 Liveness probe failed: cat: /tmp/healthy: No such file or directory
这个健康检查探查到 /tmp/healthy 已经不存在了,所以它报告容器是不健康的。那么接下来会发生什么呢?
再次查看一下这个 Pod 的状态:
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
test-liveness-exec 1/1 Running 4 5m19s
这时发现,Pod 并没有进入 Failed 状态,而是保持了 Running 状态。这是为什么呢?
-
RESTARTS 字段从 0 到 1 的变化,就明白原因了:这个异常的容器已经被 Kubernetes 重启了。在这个过程中,Pod 保持 Running 状态不变。 -
注意:Kubernetes 中并没有 Docker 的 Stop 语义。所以虽然是 Restart(重启),但实际却是重新创建了容器。 -
这个功能就是 Kubernetes 里的Pod 恢复机制,也叫 restartPolicy。它是 Pod 的 Spec 部分的一个标准字段(pod.spec.restartPolicy),默认值是 Always,即:任何时候这个容器发生了异常,它一定会被重新创建。
小提示:
Pod 的恢复过程,永远都是发生在当前节点上,而不会跑到别的节点上去。事实上,一旦一个 Pod 与一个节点(Node)绑定,除非这个绑定发生了变化(pod.spec.node 字段被修改),否则它永远都不会离开这个节点。这也就意味着,如果这个宿主机宕机了,这个 Pod 也不会主动迁移到其他节点上去。
2、http get方式探针
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: Pod
metadata:
name: liveness-httpget-pod
namespace: default
spec:
containers:
- name: liveness-exec-container
image: daocloud.io/library/nginx
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
livenessProbe:
httpGet:
port: http
path: /index.html
initialDelaySeconds: 1
periodSeconds: 3
创建该pod
[root@kub-k8s-master prome]
pod/liveness-httpget-pod created
查看当前pod的状态
[root@kub-k8s-master prome]
...
Liveness: http-get http://:http/index.html delay=1s timeout=1s period=3s
...
测试将容器内的index.html删除掉
登陆容器
[root@kub-k8s-master prome]
root@liveness-httpget-pod:/
root@liveness-httpget-pod:/
可以看到,当把index.html移走后,这个容器立马就退出了。
此时,查看pod的信息
[root@kub-k8s-master prome]
...
Normal Killing 49s kubelet, kub-k8s-node2 Container liveness-exec-container failed liveness probe, will be restarted
Normal Pulled 49s kubelet, kub-k8s-node2 Container image "daocloud.io/library/nginx" already present on machine
...
看输出,容器由于健康检查未通过,pod会被杀掉,并重新创建
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
lifecycle-demo 1/1 Running 1 34h
liveness-httpget-pod 1/1 Running 1 5m42s
restarts 为 1
重新登陆容器查看
重新登陆容器,发现index.html又出现了,证明容器是被重拉了。
[root@kub-k8s-master prome]
root@liveness-httpget-pod:/
十四、POD 的恢复策略
Pod 的恢复策略:
可以通过设置 restartPolicy,改变 Pod 的恢复策略。一共有3种:
1. Always: 在任何情况下,只要容器不在运行状态,就自动重启容器;
2. OnFailure: 只在容器异常时才自动重启容器;
3. Never: 从来不重启容器。
实际使用时,需要根据应用运行的特性,合理设置这三种恢复策略。
十五、Deployment 资源详解
使用yaml创建Deployment
k8s deployment资源创建流程:
1. 用户通过 kubectl 创建 Deployment。
2. Deployment 创建 ReplicaSet。
3. ReplicaSet 创建 Pod。
Deployment是一个定义及管理多副本应用(即多个副本 Pod)的新一代对象,与Replication Controller相比,它提供了更加完善的功能,使用起来更加简单方便。
对象的命名方式是:子对象的名字 = 父对象名字 + 随机字符串或数字
1、为什么使用Deployment
如果Pod出现故障,对应的服务也会挂掉,所以Kubernetes提供了一个Deployment的概念 ,目的是让Kubernetes去管理一组Pod的副本,也就是副本集 ,这样就能够保证一定数量的副本一直可用,不会因为某一个Pod挂掉导致整个服务挂掉。
Deployment 还负责在 Pod 定义发生变化时,对每个副本进行滚动更新(Rolling Update)。
这样使用一种 API 对象(Deployment)管理另一种 API 对象(Pod)的方法,在 k8s 中,叫作"控制器"模式(controller pattern)。Deployment 扮演的正是 Pod 的控制器的角色。
2、创建Deployment
例1:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
例2:在上面yaml的基础上添加了volume
[root@kub-k8s-master prome]
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: daocloud.io/library/nginx
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/usr/share/nginx/html"
name: nginx-vol
volumes:
- name: nginx-vol
emptyDir: {}
创建Deployment:
将上述的YAML文件保存为deployment.yaml,然后创建Deployment:
[root@kub-k8s-master prome]
deployment.apps/nginx-deployment created
检查Deployment的列表:启动之后需要创建时间比较长
通过 kubectl get 命令检查这个 YAML 运行起来的状态:
[root@kub-k8s-master prome]
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-deployment 2/2 2 2 2m22s
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
nginx-deployment-59c4b86474-2llrt 1/1 Running 0 2m51s
nginx-deployment-59c4b86474-n2r2m 1/1 Running 0 2m51s
在这里加上了一个 -l 参数,即获取所有匹配 app: nginx 标签的 Pod。需要注意的是,在命令行中,所有 key-value 格式的参数,都使用"="而非":"表示。
删除Deployment:
[root@k8s-master ~]
deployment "nginx-deployment" deleted
或者
[root@k8s-master ~]
apiVersion:注意这里apiVersion对应的值是extensions/v1beta1或者apps/v1.这个版本号需要根据安装的Kubernetes版本和资源类型进行变化,记住不是写死的。此值必须在kubectl apiversion中
[root@kub-k8s-master prome]
apps/v1beta1
authentication.k8s.io/v1beta1
authorization.k8s.io/v1beta1
autoscaling/v1
batch/v1
certificates.k8s.io/v1alpha1
extensions/v1beta1
policy/v1beta1
rbac.authorization.k8s.io/v1alpha1
storage.k8s.io/v1beta1
v1
kind:资源类型:这里指定为Deployment。
metadata:指定一些meta信息,包括名字或标签之类的。每一个 API 对象都有一个叫作 Metadata 的字段,这个字段是 API 对象的"标识",即元数据,也是我们从 Kubernetes 里找到这个对象的主要依据。
labels:Labels是最主要的字段,是一组 key-value 格式的标签,k8s中的所有资源都支持携带label,默认情况下,pod的label会复制rc的label
k8s使用用户自定义的key-value键值对来区分和标识资源集合(就像rc、pod等资源),这种键值对称为label。
像 Deployment 这样的控制器对象,就可以通过这个 Labels 字段从 Kubernetes 中过滤出它所关心的被控制对象。
关于Annotations:在 Metadata 中,还有一个与 Labels 格式、层级完全相同的字段叫 Annotations,它专门用来携带 key-value 格式的内部信息。所谓内部信息,指的是对这些信息感兴趣的,是 Kubernetes 组件本身,而不是用户。所以大多数 Annotations,都是在 Kubernetes 运行过程中,被自动加在这个 API 对象上。
selector:过滤规则的定义,是在 Deployment 的"spec.selector.matchLabels"字段。一般称之为:Label Selector。
pod的label会被用来创建一个selector,用来匹配过滤携带这些label的pods。
使用labels定位pods
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE IP NODE
nginx-deployment-59c4b86474-2llrt 1/1 Running 0 16m 10.244.2.15 kub-k8s-node2
nginx-deployment-59c4b86474-n2r2m 1/1 Running 0 16m 10.244.1.39 kub-k8s-node1
检查你的Pod的IPs:
[root@kub-k8s-master prome]
"podIP": "10.244.2.15",
"podIPs": [
"podIP": "10.244.1.39",
"podIPs": [
spec : 一个 k8s 的 API 对象的定义,大多可以分为 Metadata 和 Spec 两个部分。前者存放的是这个对象的元数据,对所有 API 对象来说,这一部分的字段和格式基本上是一样的;而后者存放的,则是属于这个对象独有的定义,用来描述它所要表达的功能。
这里定义需要两个副本,此处可以设置很多属性,主要是受此Deployment影响的Pod的选择器
replicas:定义的 Pod 副本个数 (spec.replicas) 是:2
template:定义了一个 Pod 模版(spec.template),这个模版描述了想要创建的 Pod 的细节。例子里,这个 Pod 里只有一个容器,这个容器的镜像(spec.containers.image)是 nginx:latest,这个容器监听端口(containerPort)是 80。
volumes:是属于 Pod 对象的一部分。需要修改 template.spec 字段
例2中,在 Deployment 的 Pod 模板部分添加了一个 volumes 字段,定义了这个 Pod 声明的所有 Volume。它的名字叫作 nginx-vol,类型是 emptyDir。
关于emptyDir 类型:等同于 Docker 的隐式 Volume 参数,即:不显式声明宿主机目录的 Volume。所以,Kubernetes 也会在宿主机上创建一个临时目录,这个目录将来就会被绑定挂载到容器所声明的 Volume 目录上。
k8s 的 emptyDir 类型,只是把 k8s 创建的临时目录作为 Volume 的宿主机目录,交给了 Docker。这么做的原因,是 k8s 不想依赖 Docker 自己创建的那个 _data 目录。
volumeMounts:Pod 中的容器,使用的是 volumeMounts 字段来声明自己要挂载哪个 Volume,并通过 mountPath 字段来定义容器内的 Volume 目录,比如:/usr/share/nginx/html。
hostPath:k8s 也提供了显式的 Volume 定义,它叫做 hostPath。比如下面的这个 YAML 文件:
...
volumes:
- name: nginx-vol
hostPath:
path: /var/data
这样,容器 Volume 挂载的宿主机目录,就变成了 /var/data
3、创建SERVICE
三台安装iptables:
[root@kub-k8s-master prome]
1.创建一个depl
[root@kub-k8s-master prome]
[root@kub-k8s-master prome]
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep01
spec:
selector:
matchLabels:
app: web
replicas: 2
template:
metadata:
name: testnginx9
labels:
app: web
spec:
containers:
- name: testnginx9
image: daocloud.io/library/nginx
ports:
- containerPort: 80
[root@kub-k8s-master prome]
deployment.apps/nginx-deployment created
2. 创建service并且以nodePort的方式暴露端口给外网:
[root@kub-k8s-master prome]
apiVersion: v1
kind: Service
metadata:
name: mysvc
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30001
targetPort: 80
selector:
app: web
[root@kub-k8s-master prome]
service/mysvc created
3.测试
[root@kub-k8s-master prome]
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d18h
mysvc NodePort 10.100.166.208 <none> 8080:30001/TCP 21s
4、端口详解
安装iptables(但是需要关闭iptables),创建service之后k8s会自动添加规则到Iptables里面,而且会生效(虽然iptables处于关闭状态)
服务中的3个端口设置
这几个port的概念很容易混淆,比如创建如下service:
apiVersion: v1
kind: Service
metadata:
name: mysvc
spec:
type: NodePort
ports:
- port: 8080
nodePort: 30001
targetPort: 80
selector:
app: web
port
- 这里的port表示:service暴露在cluster ip上的端口,cluster ip:port 是提供给集群内部客户访问service的入口。
nodePort
- 首先,nodePort是kubernetes提供给集群外部客户访问service入口的一种方式(另一种方式是LoadBalancer),所以,:nodePort 是提供给集群外部客户访问service的入口。
targetPort
- targetPort很好理解,targetPort是pod上的端口,从port和nodePort上到来的数据最终经过kube-proxy流入到后端pod的targetPort上进入容器。
总结
总的来说,port和nodePort都是service的端口,前者暴露给集群内客户访问服务,后者暴露给集群外客户访问服务。从这两个端口到来的数据都需要经过反向代理kube-proxy流入后端pod的targetPod,从而到达pod上的容器内。
5、kube-proxy与iptables的关系
当service有了port和nodePort之后,就可以对内/外提供服务。那么其具体是通过什么原理来实现的呢?原因就在kube-proxy在本地node上创建的iptables规则。
Kube-Proxy 通过配置 DNAT 规则(从容器出来的访问,从本地主机出来的访问两方面),将到这个服务地址的访问映射到本地的kube-proxy端口(随机端口)。然后 Kube-Proxy 会监听在本地的对应端口,将到这个端口的访问给代理到远端真实的 pod 地址上去。
不管是通过集群内部服务入口:port还是通过集群外部服务入口:nodePort的请求都将重定向到本地kube-proxy端口(随机端口)的映射,然后将到这个kube-proxy端口的访问给代理到远端真实的 pod 地址上去。
十六、RC资源(了解)
1、什么是RC
Replication Controller(简称rc)用来管理Pod的副本,保证集群中存在指定数量的Pod副本。集群中副本的数量大于指定数量,则会停止指定数量之外的多余容器数量,反之,则会启动少于指定数量个数的容器,保证数量不变。Replication Controller是实现弹性伸缩、动态扩容和滚动升级的核心。
RC 的主要功能点:
- 确保pod数量:指定某个服务在Kubernetes中有相应数量的Pod在运行;
- 确保pod健康:当pod不健康,运行出错或者无法提供服务时,会杀死不健康pod并重新创建,保持pod数量一致;
- 弹性伸缩:当业务高峰期的时候可以设置扩增pod数量,配合监控就可以做自动伸缩了;
- 滚动升级:也就是蓝绿发布,当一个pod使用的镜像更新,采用滚动升级模式,RC会自动一个个pod的进行升级,关闭一个pod的同时进行升级,且在原镜像基础上创建一个新pod,当一个pod更新完成再关闭一个旧镜像pod。
2、创建RC
1.使用yaml创建并启动replicas集合
k8s通过Replication Controller来创建和管理各个不同的重复容器集合(实际上是重复的pods)。
Replication Controller会确保pod的数量在运行的时候会一直保持在一个特殊的数字,即replicas的设置。
[root@kub-k8s-master ~]
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: ReplicationController
metadata:
name: my-nginx
spec:
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: daocloud.io/library/nginx
ports:
- containerPort: 80
和定义一个pod的YAML文件相比,不同的只是kind的值为ReplicationController,replicas的值需要指定,pod的相关定义在template中,pod的名字不需要显式地指定,因为它们会在rc中创建并赋予名字
创建rc:
[root@kub-k8s-master prome]
replicationcontroller/my-nginx created
和直接创建pod不一样,rc将会替换因为任何原因而被删除或者停止运行的Pod,比如说pod依赖的节点挂了。所以我们推荐使用rc来创建和管理复杂应用,即使你的应用只要使用到一个pod,在配置文件中忽略replicas字段的设置即可
2、查看Replication Controller的状态
[root@kub-k8s-master prome]
NAME DESIRED CURRENT READY AGE
my-nginx 2 2 2 11
这个状态表示,你创建的rc将会确保你一直有两个nginx的副本。
也可以和直接创建Pod一样查看创建的Pod状态信息:
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
dep01-58f6d4d4cb-g6vtg 1/1 Running 0 3h8m
dep01-58f6d4d4cb-k6z47 1/1 Running 0 3h8m
my-nginx-7kbwz 1/1 Running 0 2m49s
my-nginx-jkn8l 1/1 Running 0 2m49s
3、删除Replication Controller
当你想停止你的应用,删除你的rc,可以使用:
[root@kub-k8s-master prome]
replicationcontroller "my-nginx" deleted
默认的,这将会删除所有被这个rc管理的pod,如果pod的数量很大,将会花一些时间来完成整个删除动作,如果你想使这些pod停止运行,请指定–cascade=false。
如果你在删除rc之前尝试删除pod,rc将会立即启动新的pod来替换被删除的pod
3、完整TOMCAT实例
注意:本文中和上文中的NodePort没有完全解决外部访问Service的所有问题,比如负载均衡,假如我们又10个Node,则此时最好有一个负载均衡器,外部的请求只需访问此负载均衡器的IP地址,由负载局衡器负责转发流量到后面某个Node的NodePort上。这个负载均衡器可以是硬件,也可以是软件方式,例如HAProxy或者Nginx;
Java Web应用
注:Tomcat有可能无法正常启动,原因是虚机的内存和CPU设置过小,请酌情调大!
下载镜像
[root@kub-k8s-node1 ~]
构建Tomcat RC定义文件
[root@kub-k8s-master prome]
---
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 2
selector:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: myweb
image: daocloud.io/library/tomcat:7
ports:
- containerPort: 8080
发布到Kubernetes集群
创建RC
[root@kub-k8s-master prome]
replicationcontroller/myweb created
查看RC
[root@kub-k8s-master prome]
NAME DESIRED CURRENT READY AGE
myweb 1 1 1 20s
查看Pod
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
myweb-shjfn 1/1 Running 0 52s
构建Tomcat Kubernetes Service定义文件
[root@kub-k8s-master prome]
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8081
nodePort: 30009
targetPort: 8080
selector:
app: myweb
创建
[root@kub-k8s-master prome]
service/myweb created
查看SVC
[root@kub-k8s-master prome]
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d22h
mysvc NodePort 10.110.160.108 <none> 8080:30001/TCP 3h37m
myweb NodePort 10.96.19.61 <none> 8081:30009/TCP 33s
运行
浏览器中输入http://虚拟机IP:30009即可呈现如下内容:
注意在节点(node)中访问,不是master
[root@kub-k8s-node1 ~]
以上不行,应该是镜像的问题:
换成nginx镜像,创建rc:
创建rc
[root@k8s-master prome]
---
apiVersion: v1
kind: ReplicationController
metadata:
name: my-nginx
spec:
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: daocloud.io/library/nginx
ports:
- containerPort: 80
[root@k8s-master prome]
通过创建service暴露端口
[root@k8s-master prome]
apiVersion: v1
kind: Service
metadata:
name: mynginx
spec:
type: NodePort
ports:
- port: 8082
nodePort: 30010
targetPort: 80
selector:
app: nginx
[root@k8s-master prome]
访问测试:
换一个Tomcat镜像测试
创建关于tomcat的rc
[root@k8s-master prome]
---
apiVersion: v1
kind: ReplicationController
metadata:
name: myweb
spec:
replicas: 2
selector:
app: myweb
template:
metadata:
labels:
app: myweb
spec:
containers:
- name: myweb
image: hub.c.163.com/public/tomcat:7.0.28
ports:
- containerPort: 8080
[root@k8s-master prome]
创建service通过NodePort的方式暴露tomcat容器的端口到外网
[root@k8s-master prome]
apiVersion: v1
kind: Service
metadata:
name: myweb
spec:
type: NodePort
ports:
- port: 8081
nodePort: 30009
targetPort: 8080
selector:
app: myweb
[root@k8s-master prome]
访问测试,成功:
十七、K8S之暴露IP给外网
转发K8S后端服务的四种方式
1、ClusterIP
此类型会提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的pod之间通信使用。ClusterIP也是Kubernetes service的默认类型。
2、NodePort(常用)
外网client—>nodeIP+nodePort—>podIP+PodPort
为每个节点暴露一个端口,通过nodeip + nodeport可以访问这个服务,同时服务依然会有cluster类型的ip+port。内部通过clusterip方式访问,外部通过nodeport方式访问。
3、loadbalance
LoadBalancer在NodePort基础上,K8S可以请求底层云平台创建一个负载均衡器,将每个Node作为后端,进行服务分发。
4、Ingress
Ingress是一种HTTP方式的路由转发机制,为K8S服务配置HTTP负载均衡器,通常会将服务暴露给K8S群集外的客户端。
十八、控制器模式解析
1、什么是控制集群模式
k8s 项目通过一个称作"控制器模式"(controller pattern)的设计方法,来统一地实现对各种不同的对象或者资源进行的编排操作。
k8s核心就是用一个东西去控制另一个东西,所有的内容都是被控制的,容器镜像虽然好用,但是容器这样一个"沙盒"的概念,对于描述应用来说太过简单。好比,集装箱固然好用,如果它四面都光秃秃的,吊车还怎么把这个集装箱吊起来并摆放好呢?
所以,Pod 对象,其实就是容器的升级版。它对容器进行了组合,添加了更多的属性和字段。这就好比给集装箱四面安装了吊环,使得 Kubernetes 这架"吊车",可以更轻松地操作它。
而 k8s 操作这些"集装箱"的逻辑,都由控制器(Controller)完成
回顾 Deployment 这个最基本的控制器对象。之前讲过一个 nginx-deployment 的例子:
例1:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.7.9
ports:
- containerPort: 80
这个 Deployment 定义的编排动作为:
- 确保携带了 app=nginx 标签的 Pod 的个数,永远等于 spec.replicas 指定的个数,即 2 个。
- 如果在这个集群中,携带 app=nginx 标签的 Pod 的个数大于 2 的时候,就会有旧的 Pod 被删除;反之,就会有新的 Pod 被创建。
究竟是 Kubernetes 项目中的哪个组件,在执行这些操作呢?
kube-controller-manager 组件:这个组件,就是一系列控制器的集合
2、控制器种类
所有控制器:
deployment job podautoscaler
cloud disruption namespace
replicaset serviceaccount volume
cronjob garbagecollector nodelifecycle replication statefulset daemon
上面的每一个控制器,都以独有的方式负责某种编排功能。而Deployment,正是这些控制器中的一种。
而被控制对象的定义,则来自于一个"模板"。比如,Deployment 里的 template 字段。
Deployment 这个 template 字段里的内容,跟一个标准的 Pod 对象的 API 定义,丝毫不差。而所有被这个 Deployment 管理的 Pod 实例,都是根据这个 template 字段的内容创建出来的。
对 Deployment 以及其他类似的控制器,做一个总结:
如图,类似 Deployment 的一个控制器,都是由两部分组成:
- 上半部分的控制器定义(包括期望状态)
- 下半部分的被控制对象的模板组成的。
也正是在这个统一的编排框架下,不同的控制器可以在具体执行过程中,设计不同的业务逻辑,从而达到不同的编排效果。
这个实现思路,正是 k8s 进行容器编排的核心原理。
十九、滚动更新
概念:
将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是"滚动更新"。
实验:
[root@kub-k8s-master prome]
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 2/2 2 2 4h41m
nginx-deployment 2/2 2 2 5h13m
我们将nginx-deploument的副本数量变成4个,现在2个
[root@kub-k8s-master prome]
将replicas: 2
修改为:
replicas: 4
创建上节儿的:nginx-deployment
[root@kub-k8s-master prome]
deployment.apps/nginx-deployment configured
--record 记录下每次操作所执行的命令,以方便后面查看
检查nginx-deployment 创建后的状态信息:
[root@kub-k8s-master prome]
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 2/2 2 2 4h53m
nginx-deployment 4/4 4 4 5h25m
返回结果中四个状态字段含义:
DESIRED:
如果有就表示用户期望的 Pod 副本个数(spec.replicas 的值);
CURRENT:
当前处于 Running 状态的 Pod 的个数;
UP-TO-DATE:
当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致;
AVAILABLE:
当前已经可用的 Pod 的个数,即:既是 Running 状态,又是最新版本,并且已经处于 Ready(健康检查正确)状态的 Pod 的个数。只有这个字段,描述的才是用户所期望的最终状态。
修改 Deployment:
[root@kub-k8s-master prome]
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 2/2 2 2 4h59m
nginx-deployment 4/4 4 4 5h32m
将dep01的副本将2变为3个
[root@kub-k8s-master prome]
apiVersion: apps/v1
...
spec:
progressDeadlineSeconds: 600
replicas: 3
revisionHistoryLimit: 10
selector:
matchLabels:
...
保存退出,vim的方式
[root@kub-k8s-master prome]
deployment.apps/dep01 edited
[root@kub-k8s-master prome]
NAME READY UP-TO-DATE AVAILABLE AGE
dep01 3/3 3 3 5h16m
nginx-deployment 4/4 4 4 5h48m
进行版本的升级
创建一个新的deploy
[root@kub-k8s-master prome]
[root@kub-k8s-master prome]
apiVersion: apps/v1
kind: Deployment
metadata:
name: dep02
spec:
selector:
matchLabels:
app: web1
replicas: 2
template:
metadata:
name: testnginx9
labels:
app: web1
spec:
containers:
- name: testnginx9
image: daocloud.io/library/nginx:1.14
ports:
- containerPort: 80
[root@kub-k8s-master prome]
deployment.apps/dep02 created
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
dep01-58f6d4d4cb-997jw 1/1 Running 0 16m
dep01-58f6d4d4cb-g6vtg 1/1 Running 0 5h32m
dep02-78dbd944fc-47czr 1/1 Running 0 44s
dep02-78dbd944fc-4snsj 1/1 Running 0 25s
[root@k8s-node1 ~]
root@dep02-8594cd6447-z5mzs:/
nginx version: nginx/1.14.2
将nginx的版本从1.14升级到1.16
[root@kub-k8s-master prome]
...
spec:
containers:
- image: daocloud.io/library/nginx:1.16
imagePullPolicy: Always
name: testnginx9
ports:
- containerPort: 80
...
保存退出,vim的方式
[root@kub-k8s-master prome]
deployment.apps/dep01 edited
这时可以通过查看 Deployment 的 Events,看到这个"滚动更新"的流程:
[root@kub-k8s-master prome]
...
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 50s deployment-controller Scaled up replica set dep02-846bf8775b to 2
Normal ScalingReplicaSet 9s deployment-controller Scaled up replica set dep02-58f8d5678 to 1
Normal ScalingReplicaSet 8s deployment-controller Scaled down replica set dep02-846bf8775b to 1
Normal ScalingReplicaSet 8s deployment-controller Scaled up replica set dep02-58f8d5678 to 2
Normal ScalingReplicaSet 5s deployment-controller Scaled down replica set dep02-846bf8775b to 0
如此交替进行,新 ReplicaSet 管理的 Pod 副本数,从 0 个变成 1 个,再变成 2 个,最后变成 3 个。而旧的 ReplicaSet 管理的 Pod 副本数则从 3 个变成 2 个,再变成 1 个,最后变成 0 个。这样,就完成了这一组 Pod 的版本升级过程。
验证
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
dep02-78dbd944fc-69t8x 1/1 Running 0 11h
dep02-78dbd944fc-7cn86 1/1 Running 0 11h
[root@kub-k8s-master prome]
root@dep02-78dbd944fc-69t8x:/
nginx version: nginx/1.16.1
root@dep02-78dbd944fc-69t8x:/
滚动更的好处:
- 在升级刚开始的时候,集群里只有 1 个新版本的 Pod。如果这时,新版本 Pod 有问题启动不起来,那么"滚动更新"就会停止,从而允许开发和运维人员介入。
- 而在这个过程中,由于应用本身还有两个旧版本的 Pod 在线,所以服务并不会受到太大的影响。
二十、版本回滚
1、查看版本历史
[root@kub-k8s-master prome]
deployment.apps/dep02
REVISION CHANGE-CAUSE
1 <none>
2 <none>
2、回滚到以前的旧版本
? 把整个 Deployment 回滚到上一个版本:
[root@kub-k8s-master prome]
deployment.apps/dep02 rolled back
查看回滚状态
[root@kub-k8s-master prome]
deployment "dep02" successfully rolled out
验证:
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
dep02-8594cd6447-pqtxk 1/1 Running 0 55s
dep02-8594cd6447-tt4h4 1/1 Running 0 51s
[root@kub-k8s-master prome]
root@dep02-8594cd6447-tt4h4:/
nginx version: nginx/1.14.2
3、回滚到更早之前的版本
(1)使用 kubectl rollout history 命令查看每次 Deployment 变更对应的版本。
[root@kub-k8s-master prome]
deployment.apps/dep02
REVISION CHANGE-CAUSE
2 <none>
3 <none>
由于在创建这个 Deployment 的时候,指定了–record 参数,所以创建这些版本时执行的 kubectl 命令,都会被记录下来。
? 查看每个版本对应的 Deployment 的 API 对象的细节:
[root@kub-k8s-master prome]
deployment.apps/dep02 with revision
Pod Template:
Labels: app=web1
pod-template-hash=8594cd6447
Containers:
testnginx9:
Image: daocloud.io/library/nginx:1.14
Port: 80/TCP
Host Port: 0/TCP
Environment: <none>
Mounts: <none>
Volumes: <none>
(2)在 kubectl rollout undo 命令行最后,加上要回滚到的指定版本的版本号,就可以回滚到指定版本了。
[root@kub-k8s-master prome]
deployment.apps/dep02 rolled back
验证:
[root@kub-k8s-master prome]
NAME READY STATUS RESTARTS AGE
dep02-78dbd944fc-8nvxl 1/1 Running 0 86s
dep02-78dbd944fc-sb9sj 1/1 Running 0 88s
[root@kub-k8s-master prome]
root@dep02-78dbd944fc-8nvxl:/
nginx version: nginx/1.16.1
二十一、部署DASHBOARD应用
注意:最后部署成功之后,因为有5种方式访问dashboard:我们这里只使用Nodport方式访问
1. Nodport方式访问dashboard,service类型改为NodePort
2. loadbalacer方式,service类型改为loadbalacer
3. Ingress方式访问dashboard
4. API server方式访问 dashboard
5. kubectl proxy方式访问dashboard
1、下载yaml文件
可以自己下载,也可以使用子目录中的内容自己创建
[root@kub-k8s-master ~]
将名称空间修改为默认system
[root@kub-k8s-master ~]
2、下载镜像
由于yaml配置文件中指定的镜像
三台机器都下载
[root@kub-k8s-master ~]
[root@kub-k8s-master ~]
3、修改yaml文件
NodePort方式:为了便于本地访问,修改yaml文件,将service改为NodePort 类型:
[root@kub-k8s-master ~]
...
30 ---
31
32 kind: Service
33 apiVersion: v1
34 metadata:
35 labels:
36 k8s-app: kubernetes-dashboard
37 name: kubernetes-dashboard
38 namespace: kube-system
39 spec:
40 type: NodePort
41 ports:
42 - port: 443
43 targetPort: 8443
44 nodePort: 31260
45 selector:
46 k8s-app: kubernetes-dashboard
47
48 ---
4、创建应用
[root@kub-k8s-master ~]
namespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created
查看Pod 的状态为running说明dashboard已经部署成功:
[root@kub-k8s-master ~]
dashboard-metrics-scraper-76585494d8-z7n2h 1/1 Running 0 34s 10.244.1.6 k8s-node2 <none> <none>
kubernetes-dashboard-594b99b6f4-s9q2h 1/1 Running 0 34s 10.244.2.8 k8s-node1 <none> <none>
Dashboard 会在 kube-system namespace 中创建自己的 Deployment 和 Service:
[root@kub-k8s-master ~]
NAME READY UP-TO-DATE AVAILABLE AGE
kubernetes-dashboard 1/1 1 1 72s
[root@kub-k8s-master ~]
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes-dashboard NodePort 10.108.97.179 <none> 443:31260/TCP 49s
5、访问dashboard
官方参考文档:
https://kubernetes.io/docs/tasks/access-application-cluster/web-ui-dashboard/#accessing-the-dashboard-ui
查看service,TYPE类型已经变为NodePort,端口为31260
[root@kub-k8s-master ~]
kubernetes-dashboard NodePort 10.108.97.179 <none> 443:31260/TCP 101s
查看dashboard运行在那台机器上面
[root@kub-k8s-master ~]
通过浏览器访问:https://master:31260
因为我的应用运行在master上,又是NodePort方式,所以直接访问master的地址
登录界面如下(用火狐,别用谷歌):
Dashboard 支持 Kubeconfig 和 Token 两种认证方式,这里选择Token认证方式登录:
上面的Token先空着,不要往下点,接下来制作token,创建账号
6、制作token
官方参考文档:
https://github.com/kubernetes/dashboard/wiki/Creating-sample-user
创建dashboard-adminuser.yaml:
[root@kub-k8s-master ~]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin-user
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-user
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: admin-user
namespace: kube-system
执行yaml文件:
[root@kub-k8s-master ~]
serviceaccount/admin-user created
clusterrolebinding.rbac.authorization.k8s.io/admin-user created
说明:上面创建了一个叫admin-user的服务账号,并放在kube-system命名空间下,并将cluster-admin角色绑定到admin-user账户,这样admin-user账户就有了管理员的权限。默认情况下,kubeadm创建集群时已经创建了cluster-admin角色,直接绑定即可。
查看admin-user账户的token
[root@kub-k8s-master ~]
Name: admin-user-token-d2hnw
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name: admin-user
kubernetes.io/service-account.uid: f2a2fb2d-fa04-4535-ac62-2d8779716175
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1025 bytes
namespace: 11 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6IjBUc19ucm9qbW1zOHJzajhJd2M2bndpWENQSDRrcHRYY3RpWGlMcEhncEUifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLWQyaG53Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJmMmEyZmIyZC1mYTA0LTQ1MzUtYWM2Mi0yZDg3Nzk3MTYxNzUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZS1zeXN0ZW06YWRtaW4tdXNlciJ9.f-nhR31D2yMXRvjfM7ZzaKcwOEg_3HYNyxQFioqTO3rKcD6cfLZeOZfZlqrxcrHbcclCsNvIR5ReccKE8GqBJcAcAHZZVSpY9pivtfaU08_VlyQxU4ir3wcCZeeJyAeqEjGhxWJVuouQ-zoofImbaa7wKvSIoEr1jnlOP1rQb51vbekZvDCZue03QBcBRB_ZMfObfLDGI8cuVkYZef9cWFQlI4mEL4kNqHAbmSdJBAVS_6MmF0C1ryIXbe_qM_usm6bsawDsBK8mpuDrXJUU5FBI-rW8qUuZ8QrE_vjRuJkjp5iNCrNd_TyBxWX2jBziMmrWKqofZnGN6ZiqvTAJ8w
把获取到的Token复制到登录界面的Token输入框中:
成功登陆dashboard:
7、使用Dashboard
Dashboard 界面结构分为三个大的区域:
-
顶部操作区,在这里用户可以搜索集群中的资源、创建资源或退出。 -
左边导航菜单,通过导航菜单可以查看和管理集群中的各种资源。菜单项按照资源的层级分为两类:Cluster 级别的资源 ,Namespace 级别的资源 ,默认显示的是 default Namespace,可以进行切换 -
中间主体区,在导航菜单中点击了某类资源,中间主体区就会显示该资源所有实例,比如点击 Pods。
|