使用DockerFile来创建一个简单的Web容器镜像,并将其发布到DockerHub中以共享镜像
docker run
创建容器并进行测试docker tag
修改镜像标签,并使用docker push
镜像推送到DockerHubgcloud container clusters create-auto hello-cluster \ --region=COMPUTE_REGION
,创建后可以使用kubectl get nodes
来查看节点,还可以使用kubectl describe node [node-id]
来查看节点的详细信息kubectl create deployment hello-server \ --image=us-docker.pkg.dev/google-samples/containers/gke/hello-app:1.0
kubectl expose deployment hello-server --type LoadBalancer --port 80 --target-port 8080
kubectl scale deployment/hello-server --replicas=3 -n default
将容器服务的运行实例增加到三个。目前应用正在三个实例上运行,当外部访问该web服务时,将在这三个pod之间进行切换kubectl get pod -o wide
获取pod运行的节点信息,还可以使用kubectl describe pod [pod-id]
来获取指定pod更加详细的信息POD是一组并置的容器,组成了K8S中最基本的构建单元,实际应用中我们不会对容器进行直接操作,而是对POD进行处理。这并不意味着POD中一定需要包含多个容器服务,但是当一个POD包含多个容器时,这些容器总会是在同一个工作节点上。
Pod和其他K8S资源通常是向K8S REST API提供YAML文件的方式创建,当然也可以使用kubectl run
命令,但是这样简单的方式通常只支持一组有限的配置。另外当使用YAML文件创建时,还可以基于版本控制来更方便的控制K8S的资源。
kubectl explain pods
, kubectl explain pod.spec
)
kubectl create -f kubia-manual.yaml
命令以YAML文件的方式创建Pod,当然也可以创建一切其他资源apiVersion: v1 // 描述文件遵循V1版本的K8S API
kind: pod
metadata:
name: kubia-manual //pod名称
spec:
containers:
- image: jarvis/kubia //创建容器所需的镜像
name: kubia
ports:
- containerPort: 8080 // 监听的端口
protocol: TCP
kubectl get pods
获取运行中的pod信息kubectl logs [pod-id] -c [container-id]
获取指定容器日志kubectl logs
命令将显示当前容器的日志,如果容器重启过,当想要看前一个容器的日志时,可以使用kubectl logs [pod-id] --previous
指令在我们的测试集群中目前只存在寥寥数个pod,但是在微服务架构以及多版本构建时,pod会轻松的超过上百个,这将使得我们在管理pod时非常麻烦。因此K8S引入标签来处理这种场景。
apiVersion: v1 // 描述文件遵循V1版本的K8S API
kind: pod
metadata:
name: kubia-manual //pod名称
labels:
creation_method: manual
env: production //为资源指定的两个标签
spec:
containers:
- image: jarvis/kubia //创建容器所需的镜像
name: kubia
ports:
- containerPort: 8080 // 监听的端口
protocol: TCP
kubectl get pods --show-labels
,kubectl get pods -L creation_method ,env
kubectl label po kubia-manual creation method=manual
添加新标签kubectl label pod kubia-manual env =debug --overwrite
更新pod的标签kubectl get pods -l creation_method=manual
:列出所有creation_method=manual的podkubectl get pods -l env
:列出所有包含env标签的podkubectl get pods -l '!env'
:列出所有不包含env标签的podkubectl get pods -l env in (prod,devel)
apiVersion: v1 // 描述文件遵循V1版本的K8S API
kind: pod
metadata:
name: kubia-manual //pod名称
labels:
creation_method: manual
env: production //为资源指定的两个标签
spec:
nodeSelector:
gpu: "true" //通过节点选择器将pod只部署到包含标签gpu=true的节点上
containers:
- image: jarvis/kubia //创建容器所需的镜像
name: kubia
ports:
- containerPort: 8080 // 监听的端口
protocol: TCP
除了标签之外,pod或者其他资源对象还可以包含注解。注解同样的也是键值对,但是并不是为了保存标识信息而存在的,它们不能像标签一样用于对对象进行分组,当然也就不存在注解选择器。注解可以容纳更多的信息,主要是用于工具使用,用户可以手动添加需要的注解,K8S也会将一些注解自动添加到对象上。注解中的元数据,可以很小,也可以很大,可以是结构化的,也可以是非结构化的,能够包含标签不允许的字符。大量的使用注解可以为K8S资源添加说明,以便快速查阅每个对象的信息。
以下是一些例子,用来说明哪些信息可以使用注解来记录:(你可以将这类信息存储在外部数据库或目录中而不使用注解, 但这样做就使得开发人员很难生成用于部署、管理、自检的客户端共享库和工具。)
查看对象的注解
可以使用kubectl describe -o yaml
命令来查看添加到资源上的注解
添加或者修改注解
可以使用kubectl annotate [type] [id] [annotationkey=annotationValue]
标签可以实现将资源进行分组,但是这样的资源分组可以重叠,如果不使用标签选择器时,将获取所有的同类资源。因此为了将资源分割成完全独立且不重合的组,引入了命名空间的概念。K8S命名空间简单的为对象名称提供了一个作用域,允许我们将资源组织到不同的作用域中。
kubectl get namespaces
, kubectl get ns
kubectl get pods --namespace [ns]
,kubectl get pods -n [ns]
kubectl create namespace custom-namespace
来创建命名空间,同样的我们也可以使用YAML文件的方式创建——这表明K8S中的所有内容都是一个API对象apiVersion: v1
kind: Namespace
metadata:
name: custom-namespace
kubectl delete pod [pod-id]
kubectl dedlete pod -l creation_method=manual
kubectl delete ns [ns]
kubectl delete pod --all
kubectl delete all --all
kubectl delete all --all
无法删除Secret
资源——这将在后续的部分描述。ReplicationController 的替代方案
ReplicaSet
ReplicaSet 是下一代 ReplicationController, 支持新的基于集合的标签选择算符。 它主要被 Deployment 用来作为一种编排 Pod 创建、删除及更新的机制。 请注意,推荐使用 Deployment 而不是直接使用 ReplicaSet,除非你需要自定义更新编排或根本不需要更新。
Deployment (推荐)
Deployment 是一种更高级别的 API 对象,用于更新其底层 ReplicaSet 及其 Pod。 如果你想要这种滚动更新功能,那么推荐使用 Deployment,因为它们是声明式的、服务端的,并且具有其它特性。这将在后续内容中介绍。
在实际使用K8S时,通常希望我们的部署能够保持运行与健康,无需手动对其进行干预。因此我们基本上不会直接创建pod,而是通过创建ReplicationController或者Deployment这样的资源,由他们来处理Pod的部署和管理。
使用K8S的好处之一是,K8S能够自己处理Pod的生命周期。我们可以给K8S提供一组容器列表,K8S能够自己在节点上创建部署Pod,并对其生命状态进行监控。比如当节点崩溃时,K8S可以在其他节点上部署出新的Pod,当Pod内主进程崩溃时,K8S能够对Pod进行重启。甚至我们还可以监测Pod中运行的程序状态,以便于对其进行管理。
liveness probe
apiVersion: v1 // 描述文件遵循V1版本的K8S API
kind: pod
metadata:
name: kubia-manual //pod名称
spec:
containers:
- image: jarvis/kubia //创建容器所需的镜像
name: kubia
livenessProbe:
httpGet:
path: /
port: 8080
使用存活探针
当Pod启动时,被指派的存活探针将开始工作。可以使用kubectl get pod [pod-id]
来查看容器的运行状态,RESTARTS
字段标识了Pod重启的次数。当容器被重启后,可以使用kubectl describe pod [pod-id]
来查看为什么容器被重启。通过查看EXIT CODE
字段,可以了解到容器重启的类型。例如当EXIT CODE
为137 = 128 + x ,表明x = 9,这是SIGKILL的信号编号,表示该进程被强行终止。当容器被强行终止时,会创建一个全新的容器,而并不是重启原有的容器。
配置存活探针的附加属性
在配置存活探针时,可以为探针附加更多的属性,如delay
(探测指针的延迟)、timeout
(程序响应探测的超时时间)、period
(探针执行的周期)、failure
(探针检测失败后重试的次数)等。例如要设置初始延迟时,可以在YAML文件中配置initialDelaySeconds
:
apiVersion: v1 // 描述文件遵循V1版本的K8S API
kind: pod
metadata:
name: kubia-manual //pod名称
spec:
containers:
- image: jarvis/kubia //创建容器所需的镜像
name: kubia
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15 //表示K8S会在Pod运行15秒后开启该指针的探测
/health
,并让运行在Pod中的应用从内部对内部运行的所有重要组件进行检测(一定要确保该探针不受任何外部因素的影响,当该探针包含检测外部服务时,容器的健康状态将会与外部服务的状态强相关),以确保他们的状态是健康的。同时请确保该特定的路径不需要认证,否则探针检测将一致失败,导致容器无限重启。failure
),但是即使将失败阈值配置为1,K8S为了确保探测的准确,还是会尝试若干次。因此在容器内运行的应用中处理探针的失败重试是无意义的。ReplicationController是一种K8S资源,确保由其托管的Pod始终保持运行状态,当任何原因导致的Pod消失,ReplicationController会注意到该Pod的下线并根据副本创建新的Pod作为替代。ReplictionController会监视Pod列表,会根据期望的Pod数目来增加或减少当前存在的目标”“类型”(实际上是根据标签来区分)的Pod容器。
kubectl create -f xxx.yaml
创建RC,一旦RC被创建,它就会开始工作。apiVersion: v1
kind: ReplicationController
metadata:
name: kubia
spec:
replicas: 3 #表示期望的副本数
selector:
app: kubia #表示该RC的标签选择器,该RC托管的pod是添加了app=kubia的pod
template: #从这里开始,将会表示该RC托管的Pod模版,你可能已经注意到了,这里几乎就是一个pod的yaml声明
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: jarvis/kubia
ports:
- containerPort: 8080
RC创建新的Pod
我们已经了解到,RC会对符合要求的Pod进行监控,一旦有Pod出现减少的情况,RC会自动创建出新的Pod保持运行。但是,RC通常不会直接响应Pod删除的操作——尽管在删除Pod时RC会立即收到该通知(API允许客户端监听资源和资源列表的改动),但是这并不是RC创建新的Pod的直接理由。由于RC维护了Replica Count,因此RC响应删除操作的方式是检查实际运行的Pod数目,与Replica Count比对之后执行响应的操作(增加Pod、删除Pod等)
将Pod移入 或者移出RC的作用域
RC根据标签选择器决定自己能够管理的Pod列表,这说明Pod并非直接绑定到RC上。因此在任何时刻,我们可以通过更改Pod或者RC的标签(标签选择器)来决定RC是否管理哪些Pod。需要注意的是,RC并不关心不在自己标签选择器列表中的标签——即你可以为Pod增加新的其他标签,而RC对这一操作不做举动。
当对Pod的托管标签/RC的托管标签选择器进行改动时,RC会根据目标副本数重建Pod,这表明此时该Pod已经与该RC不存在托管关系了。在RC中,存在这样的情况,同一个Pod有可能存在满足多个ReplicationController的多个标签,如app=kubia , app2=kubia2
,当存在两个标签选择器分别为app=kubia
、app2=kubia2
的控制器时,这样的局面可以存在。在Deployment中,会根据副本hash出的值,为特定托管的Pod添加一个pod-template-hash
标签,这样就能避免一个Pod满足多个控制器标签的情况发生。
尽管Pod并没有直接绑定在RC上,但是Pod在metadata.ownerReferences
中引用了它,我们可以轻松的使用kubectl get pod [pod-id] -o yaml
获知Pod属于哪个RC/RS。
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2023-06-15T08:16:21Z"
generateName: kubia2-5bf68857c5-
labels:
app2: kubia2
pod-template-hash: 5bf68857c5
name: kubia2-5bf68857c5-5xh2t
namespace: default
ownerReferences:
- apiVersion: apps/v1
blockOwnerDeletion: true
controller: true
kind: ReplicaSet
name: kubia2-5bf68857c5
uid: 2d91e61e-83a1-48c8-aad7-2b5868f792cd
resourceVersion: "5258534"
uid: 69c4d569-d384-491a-9d6f-8b683194618d
更改RC的模版或者声明式的处理容器缩放
可以使用kubectl exit rc [rc]
来编辑ReplicationController,这将使用文本编辑器打开RC的YAML配置。上文已经描述,当修改RC的Pod模版时,并不会立刻更新现有的Pod容器;当修改Replica Count时,会立即根据需要创建/减少容器数目。
删除RC
当使用kubectl delete
删除RC时,由其管理的Pod也会被删除。但是就像我们前面说过的,Pod并非直接绑定在ReplicationController,Pod也并不是RC的组成部分。因此我们可以在删除RC的时候保留被托管的Pod,只需要使用--cascade=fasle
来保持Pod的运行即可。
在最初,ReplictionController是K8S在处理复制容器或者节点异常迁移的唯一组件,但是在后续退出了新一代的RepliationController——ReplicaSet。从现在其,你应当时刻保持这样的观念,使用ReplicaSet而不是ReplicationController。他们几乎完全相同,因此在使用上你不会遇到任何麻烦——更重要的是ReplicationController最终将被弃用。
通常来说,你并不会直接创建ReplicaSet,而是使用更高一级的Deployment资源时自动创建他们。
key
和operator
,甚至基于运算符的类型不同,还可以包含一个values
的列表。当我们给定了多个匹配规则时,这些匹配规则必须同时满足,才表明Pod与RS匹配,接受RS的管理。你可以看到有四种有效的运算符:
In
:表示Label的值必须与其中一个指定的values匹配NotIn
:表示值与任何给定的values值都不应匹配Exists
:必须包含一个指定名称的标签,此时标签值不重要DoesNotExist
:Pod必须不包含指定名称的标签,同样的,此时标签值不重要apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: kubia
spec:
replicas: 3
selector:
matchLabels: #当使用matchLabels属性时,与ReplicationController的标签选择能力相同
app: kubia
matchExpressions:
- key: app2
operator: In
values:
- kubia2
kubia22
key: app3
operator: Exists
template:
metadata:
labels:
app: kubia
spec:
containers:
- name: kubia
image: jarvis/kubia
在上文中,我们已经描述过RC和RS,他们均可以实现在K8S中监控管理Pod的声明周期。但是当我们需要在集群中的每个节点上运行Pod,且每个节点上的Pod有且仅有一个时,可以使用DaemonSet。这种情况一般发生在需要在每个节点上处理日志收集和资源监视等系统服务时,比如K8S自己的kube-proxy进程就是在每个节点上都会运行一个容器实例。
当使用YAML创建一个DS时,这与创建一个DC或者DS的操作很类似。DS确保创建足够的匹配它标签的Pod,并使这些Pod运行与每一个节点。尽管如此,DS并没有预期副本数的概念,它也并不需要这样的概念,因为它的目的是确保每个节点上都会运行一个模板定义的Pod。
当集群中某个节点因故下线时,DS不会做额外操作,当某个新的节点加入集群时,DS保证其上能够运行自己要求的Pod容器。当然,我们可以在Pod模版中使用nodeSelector
属性来选定DS管理的Pod运行在哪些特定节点上。并且在后续的介绍中,节点可以设置为不可调度的,以防止某些Pod调度到该节点——但是DaemonSet仍然会将Pod部署在这些节点上。因为节点的不可调度只会被调度器使用,而DaemonSet则会绕过调度器,将模版定义的Pod容器部署上去。这符合K8S的预期,因为DaemonSet管理的容器的目的是运行系统服务,即使某个节点被标记为不可调度,在其上仍然需要运行系统服务。
到目前为止,上文已经介绍了ReplicationController、ReplicaSet和DaemonSet三种控制器,尤其部署管理的Pod容器会持续运行,不会达到终止状态——因为这些容器无论因为什么原因终止,前者总会有办法重启或者重建它。当我们需要运行一个可预期完成的任务、或者需要在某些指定时刻运行的服务时,上述控制器就无法满足要求。
spec.restartPolicy
来配置当容器内进程结束时告知K8S做出什么样反应,默认为Always
,这表明Pod内进程结束后,该Pod总是会重新启动。但是被Job管理的Pod不应当使用这样的配置,他应当明确的被设置为OnFailure
或者Never
。apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
template:
matadata:
labels:
app: batch-job
spec:
restartPolicy: OnFailure
containers:
- name: batch-job
image: jarvis/batch-job
Job运行Pod
当创建Job后,Job会根据配置立刻启动一个Pod。当Pod内进程运行结束后,该Pod的状态会被设置为Completed
,使用kubectl get pods --show-all
或者kubectl get pods -a
来查看所有的pod列表,因为在默认情况下状态为已经完成的Pod不会显示在Pod列表中。Job资源管理的Pod在运行结束后不会立刻删除,因为允许查阅该Pod的日志,如kubectl logs [pod-id]
在Job中配置运行多个Pod实例
可以在Job中配置创建运行多个Pod实例,使用并行或者串行的方式运行它们,在Job的配置中设置completions
和parallelism
来实现。
当使用串行方式运行实例时,Job将会一个接一个的创建Pod,当前面的容器运行完毕后才会创建新的容器运行。如果其中某一个Pod发生故障,那么Job会创建新的Pod代替。
并行方式运行实例时,Job会根据设置的并行数同时运行多个实例Pod。当启动满足并行数的实例后,Job会根据Pod的运行情况决定是否启动新实例继续运行。同时,Job还支持在运行时更改parallelism
属性,以便在运行时更改并行期望,达成Job控制的Pod缩放。
apiVersion: batch/v1
kind: Job
metadata:
name: batch-job
spec:
completions: 50 #此Job期望的Pod成功运行数
parallelism: 5 #表示Job期望的并行运行的Pod数目,当需要串行顺序运行Pod时,此属性可忽略或者设置为1
template:
matadata:
labels:
app: batch-job
spec:
restartPolicy: OnFailure
containers:
- name: batch-job
image: jarvis/batch-job
activeDeadlineSeconds
属性,可以限制pod执行的时间。如果Pod的运行超过了此时间,那么K8S将尝试终止该Pod,并且将托管其的Job标记为失败。如果Pod运行的进程卡住或者最终根本无法完成时,这是一种限制的方式。同时通过设置Job配置中的spec.backoffLimit
字段,可以指定Job在被标记为失败前重试的次数,默认为6。如果在Linux系统中你使用过crontab,那么这部分内容对你来说会非常容易。K8S中同样支持cron任务,通过配置CronJob来实现,运行任务的时间表也同样由cron格式来指定。在运行时,K8S将根据在CronJob中的Job模版来创建Job资源,剩下的事情你应该已经在前文总了解过了。
apiVersion: batch/v1
kind: CronJob
metadata:
name: batch-job-per-15-mins
spec:
schedule: "0,15,30,45 * * * *" #cron格式的运行时间表
jobTemplate:
spec:
template:
metadata:
labels:
app: batch-job-per-15-mins
spec:
restartPolicy: OnFailure
containers:
- name: main
image: jarvis/kubia
在计划的时间内,CronJob会创建Job资源,随后Job资源创建Pod资源。但是有可能会发生Job或者Pod创建或者运行的相对较晚的情况。当你对Job运行的时间有很高的要求,任务开始时间不能比预期时间落后太多时,可以使用CronJob的spec.startingDeadlineSeconds
属性来指定截止时间,当超出该截止时间时,任务将不会运行,并且K8S将该任务标记为Failed
apiVersion: batch/v1
kind: CronJob
metadata:
name: test-cron-job-with-limit
spec:
schedule: "0,15,30,45 * * * *" #cron格式的运行时间表
startingDeadlineSeconds: 20
......
如上面的YAML所示,我们需求整点的15分钟时会运行一次该任务,但是由于某种原因在XX:15:20时该任务仍然没有运行,那么该任务将不会运行。
正常情况下CronJob总会为计划表中配置的每次执行创建一个Job,但是有可能会创建两个或者根本不创建Job。对于创建多个Job的情况,需要确保任务实际是幂等的,多次执行并不会得到不想要的结果。当不创建Job时,请确保当下次任务执行时,会处理掉本该本次任务完成的一切工作。