1. 先定义模板文件
examplepod.yaml
apiVersion: v1
kind: Pod
metadata:
name: examplepod
spec:
containers:
- name: examplepod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo"Hello Kubernetes"; sleep 3600']
模板的参数含义:
- apiversion表示使用的API版本。v1表示使用Kubernetes API的稳定版本。
- kind表示要创建的资源对象,这里使用关键字Pod。
- metadata表示该资源对象的元数据。一个资源对象可拥有多个元数据,其中一项是name,它表示当前资源的名称。
- spec表示该资源对象的具体设置。其中containers表示容器的集合,这里只设置了一个容器,该容器的属性如下。
- name:要创建的谷器名称。
- image:容器的镜像地址。
- imagePullPolicy:镜像的下载策略,支持3种imagePullPolicy,如下所示。
- Always:不管镜像是否存在都会进行一次拉取。
- Never:不管镜像是否存在都不会进行拉取。
- ifNotPresent:只有镜像不存在时,才会进行拉取
- command:容器的启动命令列表(不配置的话,使用镜像内部的命令)。
- args:启动参数列表(在本例中是输出文字“HelloKubernetes !”并休眠3600s ) 。
运行以下命令,通过模板创建Pod。
kubectl apply -f examplepod.yaml
创建成功后,可通过以下命令查询当前运行的所有Pod.
kubectl get pod
Pod创建后,最常用的功能就是查询。可以用以下命令查询Pod的状态。
kubectl get pod {pod名称}
还可以在查询命令中带上参数-w,以对Pod状态进行持续监控。只要Pod发生了变化,就会在控制台中输出相应信息。命令如下。
kubectl get pod {Pod名称} -w
另外,还可以在查询命令中带上-o wide参数,输出Pod的更多概要信息(如调度到哪台机器上,Pod本身的虚拟IP等信息)。命令如下。
kubectl get pod {Pod名称} -o wide
get命令除了可以显示简要的运行信息外,还可以输出完整信息。它支持多种格式的输出,如可以用yaml和Json方式输出,命令如下。
kubectl get pod examplepod --output yaml
kubectl get pod examplepod --output json
一般情况下,如果要查询Pod更详细的信息(包括状态、生命周期和执行情况等),除了将其输出为yaml或json格式,还可以用describe命令查看详情,格式如下。
kubectl describe pods {pod名称}
该命令会输出比较全面的信息,包括资源的基本信息、容器信息、准备情况、存储卷信息及相关的事件列表。在资源部署时如果遇到问题,可以使用此命令查看详情,分析部署错误的原因。
如果要查询Pod本身输出的日志信息,还可以使用logs命令,格式如下。
kubectl logs {pod名称}
可以用replace命令来修改原先设置的Pod属性,命令格式如下。
kubectl replace -f { pod模板路径}
修改之前示例中定义的Pod,使它输出“Hello Kubernetesreplaced!”。先打开examplepod.yml文件。
apiVersion: v1
kind: Pod
metadata:
name: examplepod
spec:
containers:
- name: examplepod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo "Hello Kubernetes replace"; sleep 3600']
提示:Pod有很多属性无法修改,比如containers的image属性,spec下的activeDeadline seconds、tolerations属性等。如果一定要修改,则需要加上–force参数,相当于重新创建Pod,命令如下。
kubectl replace -f {pod模板路径}―-force
Pod的删除非常简单,只要执行以下命令即可。
kubectl delete pod { Pod名称}
另外,还可以基于模板文件删除资源,如以下命令所示。
kubectl delete -f{模板文件名称}
在之前创建Pod的示例中,我们使用了基本的Pod模板来定义资源,但Pod模板包含的内容不仅只有示例中的那些,还可以定义非常丰富的内容。
以下是Pod模板的主要内容及对应说明。目前只需要大致了解有这些属性就足够了,不需要把每个都完全理解,在以后的章节中将逐一进行演示。
apiversion : v1 #版本,必填,v1代表稳定版本
kind: pod #类型,必填,Pod
metadata: #元数据,表示资源的标识信息
name: string #元数据,必填,Pod的名字
namespace: string #元数据,Pod的命名空间
labels: #元数据,标签列表
- key: value #元数据,可定义多个标签的键/值对
annotations: #元数据,自定义注解列表
- key: value #元数据,可定义多个标签的键/值对
spec:
containers: #Pod中的容器列表,必填,可以有多个容器
- name: string #容器名称,必填
image: string #容器中的镜像地址,必填
imagePullPolicy: [Always/Never|IfNotPresent] #获取镜像的策略Always表示下载镜像;
#ifNotPresent表示优先使用本地镜像,否则下载镜像;Never表示仅使用本地镜像
command: [ string ] #容器的启动命令列表((不配置的话,使用镜像内部的命令)
args : [ string ] #启动命令参数列表
workingDir: string #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷设置
- name: string #为了引用Pod定义的共享存储卷的名称,要用volumes[]部分定义的卷名
mountPath: string #存储卷在容器内挂载的绝对路径,应少于512个字符
readOnly: boolean #是否为只读模式
ports: #容器需要暴露的端口号列表
- name: string #端口名称
containerPort: int #容器要暴露的端口
hostPort: int #容器所在主机监听的端口(把容器暴露的端口映射到宿主机的端口)
protocol: string #端口协议,支持TCP和UDP,默认为TCP
env: #容器运行前要设置的环境变量列表
- name: string #环境变量名称
value: string #环境变量值
resources: #资源限制和请求的设置
limits: #资源限制的设置
cpu: string #CPU的限制,单位为CPU内核数。将用于docker run --cpu-quota #也可以使用小数,例如0.1,0.1等价于表达式100m,表示100milicpu
memory: strina #内存限制,单位可以为 MiB/GiB/MB/GB (1MiB=1024×1024B,#1MB=1000×1000B),将用于docker run --memory参数
requests: #资源请求的设置
cpu: String #CPU请求,容器启动时的初始可用数量,将用于docker run --cpu-shares参数
memory : string
#内存请求,容器启动时的初始可用数量
livenessProbe: #Pod内容器健康检查的设置,当探测几次无响应后将自动重启该容器,
#检查方法有exec、httpGet和tcpsocket,对一个容器只要设置一种方法即可
exec: #通过exec方式来检查Pod内各容器的健康状况
command: [String] #exec方式需要指定的命令或脚本
httpGet: #通过httpGet方式来检查Pod中各容器的健康状况,需要指定path、port
path: stringport:
numberhost: string
scheme : stringhttpHeaders :
- name : string
value: string
tcpsocket :
#通过tcpsocket检查Pod中各容器的健康状况
port: number
initialDelaySeconds: 0 #容器启动完成后,首次探测的时间(单位为秒)
timeoutseconds: 0 #对容器进行健康检查时探测等待响应的超时时间(单位为秒,默认为1s)
periodseconds: 0 #对容器监控检查的定期探测时间设置(单位为秒),默认10s一次
successThreshold: 0
failureThreshold: 0
securityContext: #安全配置
privileged : false
restartPolicy: [Always |Never |OnFailure] #Pod的重启策略,Always表示不管以何种方式终止运行,kubelet都将重启;OnFailure表示只有pod以非0码退出才重启;Never表示不再重启该Pod
nodeselector: object #节点选择,设置nodeselector表示将该Pod调度到包含这个标签的节点上,以key: value格式来指定
imagePullSecrets: #拉取镜像时使用的secret名称,以key:secretkey格式指定
- name: string
hostNetwork : false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes :
#在该Pod上定义共享存储卷列表
- name: string
#共享存储卷名称
emptyDir: { }
#类型为emptyDir的存储卷,与Pod有相同生命周期的一个临时目录,为空值
hostPath: #类型为hostPath的存储卷,将会挂载Pod所在宿主机的目录
path: string #Pod所在宿主机的目录,该目录将在容器中挂载
secret: #类型为secret的存储卷,在容器内部挂载集群中预定义的secret对象
secretName: string
items:
- key: string
path : string
configMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name : string
items:
- key : string
path: string
如果要了解一个正在运行的Pod的配置,可以通过以下命令来获取。
kubectl get pod {pod名称}-o yaml
之前描述的Pod模板和Docker-Compose 配置非常相似,但Pod模板涉及其他部署参数的设定,相对更复杂。先排除与容器无关的配置参数,在模板的Containers部分,指明容器的部署方式。在部署过程中,会转换成对应的容器运行时( container runtime)命令,例如,对于Docker,会转换成类似于Docker run的命令。
在最开始的例子中,yml文件内容如下。
apiVersion: v1
kind: Pod
metadata:
name: examplepod
spec:
containers:
- name: examplepod-container
image: busybox
imagePullPolicy: IfNotPresent
command: ['sh','-c']
args: ['echo"Hello Kubernetes"; sleep 3600']
在Kubernetes将Pod调度到某个节点后,kubelet会调用容器运行时(本例中为Docker),执行如下所示的命令。
docker run --name examplepod-container busybox sh -c 'echo"Hello Kubernetes !"; sleep 3600'
提示: command和args设置会分别覆盖原Docker镜像中定义自EntryPoint 与CMD,在使用时请务必注意以下规则。
同样,在Pod模板的Container设置中的各项信息,在运行时都会转换为类似的容器命令来执行。Container的基础信息的设置如下所示。
containers: #Pod中的容器列表,必填,可以有多个容器
- name: string #容器的名称,必填
image: string #容器中的镜像地址,必填
imagePullPolicy: [Always |Never| IfNotPresent] #获取镜像的策略。Always表示下载镜像;
#ifNotPresent表示优先使用本地镜像,否则下载镜像;Never表示仅使用本地镜像
command: [ string] #容器的启动命令列表((不配置的话,使用镜像内部的命令)
args: [ string ] #启动命令参数列表
workingDir: string #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷设置
- name: string #为了引用Pod定义的共享存储卷的名称,要用 volumes []部分定义的卷名
mountPath: string #存储卷在容器内挂载的绝对路径,应少于512个字符
readOnly: boolean #是否为只读模式
ports: #容器需要暴露的端口号列表
- name: string #端口名称
containerPort: int #容器要暴露的端口
hostPort: int #容器所在主机监听的端口(把容器暴露的端口映射到宿主机的端口)
protocol: string #端口协议,支持TCP和UDP,默认为TCP
env: #容器运行前要设置的环境变量列表
- name: string #环境变量名称
value: string #环境变量值
容器中关键的基本信息(如name、image、imagePullPolicy、command、args)在之前的示例中已经演示并讲解过,接下来将演示另外3组——volumeMounts、ports、env。
容器运行时通常会提供一些机制来将存储附加到容器上。例如,Docker有两种容器机制:一种是数据卷( data volume) ,它可以将容器内的文件或目录映射到宿主机上的文件或目录中,其命令格式为$docker run -v/{主机的目录}{映射到容器的目录}{镜像名称};另一种是数据卷容器(data volume container),不过其本质使用的还是数据卷,这种容器一般用在一组相关的容器中,用于专门处理数据存储以供其他容器挂载。
不管是数据卷还是数据卷容器,其存留时间通常超过其他容器的生命周期。由于生命周期不同步,因此实现起来非常缺乏灵活性。
为了解决这些问题,Kubernetes在数据卷的基础上,又新增加一套自己的存储卷(volume抽象机制。该机制不仅允许Pod中的所有容器方便地共享数据,还允许存储卷与Pod中的其他容器保持完全一致的生命周期。
首先,创建examplepodforvolumemount.yml文件。
apiversion: v1
kind: Pod
metadata :
name: examplepodforvolumemount
spec:
containers:
- name: containerforwrite
image: busybox
imagePullPolicy: ifNotPresent
command: ['sh', '-c']
args: [ 'echo "test data ! " >/write_dir/data; sleep 3600']
volumeMounts:
- name: filedata
mountPath: /write_dir-
- name: containerforread
image: busybox
imagePullPolicy: ifNotPresent
command: ['sh','-c']
args: ['cat / read_dir/ data; sleep 3600']
volumeMounts :
- name: filedata
mountPath: /read_dir
volumes:
- name: filedata
emptyDir: {}
在本例中,我们创建了两个容器。一个是containerforwrite,它向数据卷写入数据,会向/write dir/data文件写入“test data!”文本。容器内的数据卷地址为/write_dir,它引用的存储卷为filedata。
另一个容器是containerforread,他会从/read_dir/ data文件中读取文本,并将其输出到控制台(后续可以通过日志查询方式读取输出到控制台的文本)。容器内的数据卷地址为/read_dir,它引用的存储卷为filedata。
本例中还创建了一个存储卷,其名称为filedata,这个名称会被容器设置中的数据卷所引用。存储卷的类型是emptyDir,它是最基础的类型,表示纯净的空目录,其生命周期和所属的Pod完全一致(后续会讲解更多的种类)。对于例子中的两个容器,虽然数据卷地址不同(一个是/write_dir,一个是/read_dir),但因为它们都是映射到同一个空目录下的,所以本质上仍在同一个文件夹内进行操作。
执行命令,创建pod
kubectl apply -f examplepodforvolumemount.yml
通过以下命令,查看Pod的运行情况,READY 2/2表示两个容器都已成功运行。
kubectl get pods examplepodforvolumemount
此时可以通过logs命令,查看Pod中containerforread容器的日志。
kubectl logs examplepodforvolumemount containerforread
可以看到,containerforread容器已经读取到在containerforwrite容器中写入的文本,并已将其输出到控制台
容器运行时通常会提供一些机制以将容器端口暴露出来,并映射到主机的端口上,以便其他人能通过“主机IP:端口”访问容器所提供的服务,例如,Docker的命令$ docker run -p {宿主机端口}:{容器端口}{镜像名称}。同样,Pod模板中也提供了这个功能。为了通过例子进行演示,首先,创建examplepodforport.yml文件。
apiVersion: v1
kind: Pod
metadata:
name: examplepodforport
spec:
containers:
- name: containerforport
image: nginx
ports:
- name: portfornginx
containerPort: 80
hostPort: 8081
protocol: TCP
在本例中,Nginx镜像中默认定义的对外提供服务的端口为80。通过containerPort属性,我们将80端口暴露出来,再通过
hostPort属性将其映射到宿主机的端口8081上,以便通过“主机IP:端口”访问容器所提供的服务,其中protocol为端口协议,支持TCP和UDP,默认为TCP。
执行以下命令,创建Pod。
kubectl apply -f examplepodforport. yml
通过以下命令,查看Pod的运行情况,直到状态变为Running。
kubectl get pods examplepodforport
Pod创建完成后,执行以下命令,查看Pod具体被分配到哪台Node上。
kubectl describe pods examplepodforport
访问 192.168.10.4::8081 就可以访问到nginx的欢迎页面
注意:以上案例仅为了说明Kubernetes是如何创建容器的,这种类似于Docker直接映射到主机端口的方式,在Kubernetes中强烈不推荐。Pod只是一个运行服务的实例,随时可能在一个Node上停止,而在另一个Node上以新的IP地址启动新的Pod,因此它不能以稳定的IP地址和端口号提供服务。若要稳定地提供服务,则需要服务发现和负载均衡能力。Kubernetes提供了Service抽象机制,在后续的章节中会详述。
env配置信息
容器运行时通常还会提供一些机制来输入可动态配置的一些环境变量,以供容器中的应用程序使用。如在Docker中,配置环境变量的命令为$ docker run --env {变量1}={值1 } --env {变量2}={值2} …{镜像名称}。同样,Pod模板中也提供了这个功能,为了通过例子进行演示,首先,创建examplepodforenv.yml文件。
apiVersion: v1
kind: Pod
metadata:
name: examplepodforport
spec:
containers:
- name: containerforport
image: nginx
imagePullPolicy: IfNotPresent
env:
- name: parameter1
value: "good morning"
- name: parameter2
value: "good night"
command: ['sh','-c']
args: ['echo "${parameter1} ${parameter2}"; sleep 3600']
在模板中定义了一个名为containerforenv的容器,向它传入了两个环境变量:其中一个名为parameter1,值为good
morning !;另一个变量名为parameter2,值为good night !。在本例中,将通过在容器中执行命令的方式,将传入的两个环境变量拼接到一起并输出到日志。
Pod的设计初衷在于同时运行多个共同协作的进程(作为容器来运行)。Pod中的各个容器总是作为一个整体,同时调度到某台Node上,容器之间可以共享资源、网络环境和依赖,并拥有相同的生命周期。
当然,在同一个Pod中同时运行和管理多个容器,是一种相对高级的用法,只在容器必须要紧密配合进行协作的时候才使用此模式。
Pod只是一种抽象,并不是一个真正的物理实体,表示一组相关容器的逻辑划分。每个Pod都包含一个或一组密切相关的业务容器,除此之外,每个Pod都还有一个称为“根容器”的特殊Pause容器
在一组容器作为一个单位的情况下,很难对整个容器组进行判断,如一个容器挂载了能代表整个Pod都挂载了吗?
而如果引入一个和业务无关的Pause容器,用它作为Pod的根容器,用它的状态代表整组容器的状态,便能解决该问题。
另外,Pod中的所有容器都共享Pause容器的IP地址及其挂载的存储卷,这样也简化了容器之间的通信和数据共享问题。
Pause容器还在Pod中担任Linux命名空间共享的基础,为各个容器启用pid命名空间,开启init进程。
pod中的 容器可以使用Pod 所提供的两种共享资源——储存和网络
1. 储存
在pod中,可以指定一个或多个共享存储卷。Pod中的所有容器都可以访问此卷。存储卷也可以用来持久化Pod中的存储资源 ,防止容器重启后文件丢失。
2. 网络
每个Pod都分配了唯一的IP地址。Pod中的每个容器都共享网络命名空间,包括IP地址和网络端口。Pod内部的容器可以使用localhost
互相通信。当Pod中的容器与Pod外部进行通信时,还必须共享网络资源(如使用端口映射)
Docker和Kubernetes在网络空间上的差异
Docker其实一开始没有考虑多主机互连的网络解决方案。在实际的业务场景中,组件之间的管理十分复杂,应用部署的粒度更加细小。Kubernetes使用其独有的网络模型去解决这些问题。
Pod之间的通信主要涉及两个方面,下面将分别介绍。
1) 在同一个Node上Pod的通信
因为同一个Node上的Pod使用的都是相同的Docker网桥,所以它们天然支持通信。
每一个Pod都有一个全局IP地址,同一个Node内不同Pod之间可以直接采用对方Pod的IP地址通信,而且不需要使用其他发现机制。因为
它们都是通过veth连接在同一个docker0网桥上的,其IP地址都是从docker0网桥上动态获取的,并关联在同一个docker0网桥上,地址段
也相同,所以它们之间能直接通信
2) 跨Node的Pod之间的通信
要实现跨Node的Pod之间的通信,首先需要保证的是Pod的IP地址在所有Node上都是全局唯一的。这其实并不复杂,因为Pod的IP地址是由Docker 网桥分配的,所以可以将不同Node机器上的Docker网桥配置成不同的IP网段来实现这个功能。然后需要在容器集群中创建一个覆盖网络来连接各个机器。目前可以通过第三方网络插件来覆盖网络,比如Flannel。
Flannel会配置Docker网桥(即docker0),通过修改Docker的启动参数bip来实现这一点。通过这种方式,集群中各台机器的Docker网桥就得到了全局唯一的IP网段,它所创建的容器自然也拥有全局唯一的IP。
Flannel还会修改路由表,使Flannel虚拟网卡可以接管容器并跨主机通信。当一个节点的容器访问另一个节点的容器时,源节点上的数据会从docker0网桥路由到flannel0网卡,在目的节点处会从flannel0网卡路由到docker0网桥,然后再转发给目标容器。
Flannel运行在所有的Node机器上,重新规划了容器集群的网络。这既保证了容器的IP地址的全局唯一性,又让不同机器上的容器能通过内网IP地址互相通信。当然,容器的IP地址并不是固定的,IP地址的分配还由Docker来负责,Flannel只分配子网段。
跨Node的Pod之间的通信如图所示。
因为Pod的IP地址本身是虚拟IP,所以只有Kubernetes集群内部的机器(Master和Node)及其他Pod可以直接访问这个IP地址,集群之外的机器无法直接访问Pod的IP地址。