内容简介:对于Kubernetes部署,有两种基本的方法:使用一条条的kubectl执行命令,或者编写声明清单,再使用kubectl应用工具执行。前者有利于学习和与Kubernetes的交互实验(类似于编程语言的REPL);后者适合于可重现重用的快速部署。例如在生产环境中,您可能仍然会使用一条条的kubectl命令来做调试。前提条件
对于Kubernetes部署,有两种基本的方法:使用一条条的kubectl执行命令,或者编写声明清单,再使用kubectl应用 工具 执行。前者有利于学习和与Kubernetes的交互实验(类似于编程语言的REPL);后者适合于可重现重用的快速部署。例如在生产环境中,您可能仍然会使用一条条的kubectl命令来做调试。
前提条件
本教程假设您已经具有了可访问的Kubernetes集群环境。现在,您可以轻松地在您的机器上运行一个单节点集群,或者在您熟悉的云环境中创建一个多节点集群。并确定您的机器上已经安装并正确配置了kubectl。让如下命令可以成功执行:
还需确认您在缺省命名空间内的权限是否足够(可能与缺省命名空间不同)。edit角色权限能够满足实验需求。如果您为本教程创建了一个集群,那么您可能就是管理员。或者让其他人为您准备好环境。
备注:如果您不想用公共的镜像文件,而想自己构建并推送一个自定义的境像。那我们假定您和您的集群是可以访问容器镜像注册器的。同样的,也可以让其他人为您准备好此环境。业界协作比较好的组合环境包括GKE与GCR、AKS与ACR等。另外,DockerHub和Quay也是非常流行的镜像注册器。如果不打算公开自己的容器镜像,则需要在私有的命名空间中,给默认服务帐户配置拉取镜像密钥。
构建和推送
不管是采用指令式部署还是声明式部署,都需要有一个容器镜像。(如果您使用现成的镜像,如Nginx镜像,可跳过此部分)。本教程中的一些步骤与后续构建的应用程序有直接关联。同时,也可帮助您学习如何封装应用程序,所以建议您不要忽略此部分。
应教学目的需要,我们将从一个简单的Web应用源代码开始教程。下面是Node.js文档中的一个案例应用代码(或采用您所喜欢的编程语言编写一个类似的应用程序)。将以下的代码复制到一个名为app.js的文件中,并存放在一个空的文件夹下。
我们对上述案例代码做了一些调整:
-
将服务器本机IP定义为0.0.0.0而不是127.0.0.1。因为后者仅仅适用于IP回路寻址。而这段程序需监听来自一个集群IP的请求(由0.0.0.0捕获请求)。
-
将“Hello World”消息分拆为2个变量:主机名,提供请求响应的一个副本节点的标识;WHOAMI环境变量,在部署时设定,默认值为“Anonymous”。
如果您已经安装了Node.js,那可以通过以下命令进行代码的本地测试:
备注:如果您在自己的Kubernetes集群环境进行测试,无需担心TLS终端和授权问题,因为这个应用可以在集群的边界运行,例如Ambassador。如果您采用Zero-trust,这段代码可以运行在Istio之类服务网格集群的微服务内。
接下来将应用打包成一个 Docker 镜像。复制如下代码,保存为Dockerfile的文件:
在文件所在目录下,执行命令:
请根据您的容器镜像注册器,替换 myrepo。例如: 采用GCR,就替换为 gcr.io/project-name/image-name,采用默认的DockerHub,就替换为 user-name/image-name,用非 latest的任何词语,来替代 mytag。tag不能重复,如果您是和多人一起学习本课程时,请确保这点。最后的那个点不能遗漏,它表示用当前目录环境构建上下文。
最后,将镜像推到您的镜像库(也就是Kubernetes拉取镜像的地方):
命令式的配置方法
RUN
部署Kubernetes最简捷的方式就是采用kubectl run命令。
如果是多人共享一个命名空间,请采用唯一的名称替代myapp,采用上述步骤中的指定的库地址和名称(或Nginx)替代myrepo:mytag。
这个命令,看起来与本地启动容器的docker run命令有些相似, 但相似的地方也就这些而已。(再后面的交互图中也有体现)。
本质上Kubectl将用户的命令语句转换成一个Kubernetes的Deployment对象的声明。而一个Deployment是一个能滚动升级的高级API。
-
Kubectl将Deployment对象发送给运行在Kubernetes集群内的API服务器—— kube-apiserver。
-
kube-apiserver将Deployment信息保存到集群内分布式键值对存储etcd中,etcd为Kubectl请求提供响应。
-
Kubernetes负责监控Deployment及其他事件的控制器管理器kube-controller-manager,将为Deployment创建一个ReplicasSet副本,并将其发送到kube-apiserver。一个ReplicaSet就是一个Deployment的版本。在滚动更新时,将创建一个新的ReplicaSet,并逐步扩展到预先配置的期望副本数,而旧的ReplicasSet数量逐步变为零。这整个过程都是采用异步机制。
-
kube-apiserver将ReplicaSet信息保存到etcd中。
-
kube-controller-manager同样以异步的方式为ReplicaSet创建2个或以上的Pod,并将其发送给kube-apiserver。Pod是Kubernetes的基本单元,是搭载1或多个共享Linux cgroup和命名空间的容器环境。
-
kube-apiserver将Pod信息保存到etcd中。
-
负责监视Pod事件的Kubernetes调度器kube-scheduler,将逐步更新每个Pod,为其分配到某个节点,并将更新的信息发送回kube-apiserver。
-
kube-apiserver在etcd中更新Pod状态。
-
最后,节点上运行的kubelet会实时监控Pod,并在Pod启动了容器。
备注:容器,调度器和kubelet同样会将自身的状态信息发送给API Server。总而言之,我们可以将Kubernetes理解成一个不断循环的CRUD API。如下是上述过程的交互图:
Get,Describe
kubectl run创建一个Deployment,然后又衍生创建ReplicaSet和Pod。但它们都运行在哪里呢?我们可以采用kubectl get命令去列出您默认命名空间下所有的 Deployments,ReplicaSet和Pod:
在对象后面加上对象名可以列出单个的对象。如:
导出etcd中的对象状态信息--output option (or -o):
要获得更多的细节信息,包括与此对象相关的近期事件信息。可以采用kubectl describe命令:
上述命令与参数同样适用于ReplicaSet 和Pod。
Label
标签的作用非常大,一个标签是一个字符串的健值对。Kubernetes所有的对象都可以被标签,并且可以按标签做成选择器。通过kubectl run命令加run=myapp的方式可以自动定位Deployment,ReplicaSet和Pod。您可以在YAML和描述中看到对象的标签,也可以通过--show-labels参数,将标签输出。命令如下:
可以采用 --label-columns ( -L) 参数将标签以列的方式显示。如:
一种重要用法是采用--selector (-l)参数进行标签过滤。如:
还可以手动添加新的标签。如:
Delete
Kubernetes API本质上是声明性的,这意味着控制器总是在比较对象状态和所期望的状态。因此,如果我们删除一个Pod,ReplicaSet控制器将创建一个新的Pod,以维护所期望的副本数。我们通过如下测试验证一下:
我们会发现Pod总数没变,但通过随机后缀发现是创建了新的Pod。这个机制同样适用于ReplicaSet。如果我们删除一个,控制器也将自动创建一个新的。但如果我们将Deployment删除了,那就表示删除这个应用,这时候就不会再有新的创建动作。
Port-Forward
到目前为止,我们已经看到了Kubernetes为响应kubectl run而创建的Pod对象。但Pod之所以随时可以响应,是因为容器中有进程正在运行着。我们可以定义一个活性探针(Liveness probe)来验证。但是现在,我们先通过手动操作来让应用程序正常工作。即通过kubectl port-forward命令,让API服务将一个本地端口代理成Pod的端口(如果本地和API服务器的网络是互联网的话,需要采用TLS加密)。kubectl port-forward命令非常灵活,如果您同时提供了Pod名和端口号,kubectl就直接按提供的值进行映射。但由于Pod名通常是自动生成的,名字带有随机数,所以方便起见,建议提供的名字是类型/名字的键值对格式。如:
浏览器打开如下链接,您将看到“Hello World”的消息。
http://localhost:3000/
由于上述方式只能映射一个Pod,所以当您重新加载后,不能修改主机名。同时请注意,采用kubectl run命令时,我们不能通过–env参数修改WHOAMI 环境变量。只能通过kubectl set 或kubectl patch命令才行的(在稍后介绍)。
如果本地端口与Pod端口不一致的话,需在命令内注明,如:
上述所配置的端口代理可以通过Ctrl+C停止。如果代理连接不活跃的话,系统也会被自动关闭这个代理连接。
Expose
port-forward可以让一个应用运行起来,但不支持多个应用同时代理。而且,我们用到的Pod IP和DNS记录,会因为Pod生命周期很短,而导致不断变化。因此,我们需要一种方法来与Deployment或其他类型的Pod组(而非单个Pod)保持通信。这就是服务发现。
Kubernetes的Service对象就是将流量路由到一组Pod上,而这组Pod与Service的标签选择器相匹配。如果多个Pod与标签选择器匹配,那它们都将被侦听,并接收到所路由的流量。如果发布一个Deployment,我们可以简单地将它的标签选择器(run=app)作为Service的标签选择器。
The kubectl expose命令能够自动根据Deployment、ReplicaSet、其他的Service或单个Pod来创建Service。它会自动从给定对象中查找标签选择器、服务端口和目标Pod端口,除非有特定选项指定。如:
确认一下所创建Service 的信息:
可在Service描述中或所控制的Endpoints中,看到监听Pod的IP,这方法可以用于网络问题的诊断。
如下是启动一个带有交互式终端的临时Pod,来实际调用服务:
备注:myapp的Deployment需提前创建。
-
-t或–tty,分配一个TTY终端
-
-i或–stdin保持stdin打开
-
--rm退出时删除Deployment
Logs
如应用程序有问题,通常要检查日志。容器日志通常是由容器运行时(Docker守护进程)存储在节点上。默认情况下,当日志文件超过10MB时,日志会自动循环覆盖。可以配置一个日志代理,将日志推送到后端的持久存储上。请记住一点,kubectl logs命令只能看到节点上上的日志。
可以通过Pod名获取一个Pod的日志,也可以通过类型/名字获取一组Pod的日志。如:
在我们的教学中,日志较少,但实际生产应用的日志会详细且多很多。尤其是出问题的时候。可以通行参数,按日志量—since (1m),日志时间--since-time (2018–11–01T16:30:00)或尾部记录数 --tail (20)来设定输出结果。其他的参数选项还包括 –follow(-f),--previous(容器持续崩溃),--timestamps(应用没有记录时间戳)。最后将日志输出到文件和进行您所需的分析。如:
Exec,Copy
kubectl还提供了一些其他的工具来帮助我们调试正在运行的容器。kubectl exec是在容器中执行命令,kubectl cp在容器之间复制文件和目录。这两个命令使用显式的Pod名称,而不是Deployment名。如下是用--output jsonpath在 shell 变量中存储Pod名称的用法:
我们可以将这个变量用到其他命令内:
Set,Scale,Patch
如前面提到的,我们需要用kubectl set或kubectl patch来设置WHOAMI环境变量。在此过程中,我们还同步学习如何使用kubectl get -watch(或-w)查看资源更改,并实时观察多个对象的滚动更新:
在前三个终端中,可观察新ReplicaSet的创建,以及新/旧Pod按容器编排进行的创建/删除的过程。还将看到与状态更改所对应的数据行的变化(Pod的期望数、当前数、更新日期、可用/准备)。在进行另一个变更之前,先扩展一下Deployment的ReplicaSet数量:
kubectl set命令仅限于设置环境变量、图像、资源请求/限制、标签选择器、ServiceAccounts和RoleBindings(基于角色的访问控制,或RBAC)。
kubectl patch命令则更为通用。它接受JSON或YAML来替换或合并特定的字段。举个简单的例子,我们可以绕过调度程序,强制让所有Pod运行在一个节点上,命令如下:
首先,看看我们的应用程序的副本目前运行在不同的节点:
选定一个节点名,并以patch的方式调整Deployment:
确认所有Pod被分配到同一个节点上:
实际上,还有更好的方式调整调度器,例如标签中心节点,使用node selector,affinities和anti-affinities,taints和tolerations等等。
声明式的配置方法
前面所演练的使Kubernetes知道如何运行,我们只是告诉Kubernetes我们要什么。执行的是只读的get,describe,logs命令,然后获取预期的结果,还有调试命令如port-forward,exec,cp和delect(用于替换而非修复Pod),这些命令帮助您理解了Kubernetes的重要概念。在实际应用上,这些命令使用并不多。
Kubernetes的强大主要是它的声明式API和控制器,接下来学习如何告诉Kubernetes要干什么。
您只需要使用kubectl apply和YAML(或JSON)就来展现Kubernetes保存在etcd中的状态,我们称之为manifest。
您可以简单地通过如下命令,获取在运行对象的manifest:
一个manifest实际上,并不需要保存所有状态。其中一些是由Kubernetes补充的。export选项自动删除了部分status和元数据(UID、创建时间戳等),但您可能按需要删除更多的配置数据,例如默认值等。
以下提供了Deployment和Service的标准示例,内容为:
为了证明它们的工作原理,我们先删除全部对象,然后再应用manifest来重新生成对象:
如果我们没有删除Deployment和Service,那它们只是会进行更新来匹配manifest。
kubectl apply命令具有幂等的特征。即我们可以多次编辑manifest,然后再次去应用它。例如,修改了副本数配置后再应用。
如需进一步了解Kubectl apply,可以参考Kubernetes的API[1]参考。
对于具有多个环境的复杂应用,直接对manifest进行管理会非常困难。更好的是通过工具帮助我们管理配置。目前已有不错的工具有kustomize(从1.14版开始就成为kubectl的一部分)、Helm和Jsonne等。对于整体构建和部署过程,并需要在manifest注入带标记的镜像,skaffold是一个值得推荐的工具。
总结
在本教程中,我们讨论了Kubernetes上无状态应用程序的基本组建:Deployment、ReplicaSet、Pods和Service。以及围绕它们进行的状态获取,执行等功能的命令和声明方法。我们只是以最基本的方式使用了 Deployment。在生产环境中,您应该需要设置CPU和内存请求和限制,并且您可能还对自动缩放和其他功能会感兴趣。
有几本好书可以用于学习Kubernetes。我读过且毫不犹豫推荐的唯一一本书是《Kubernetes in Action[2]》(Manning著)。
当然,官方文档[3]是一个很好的参考。
最后就是正确地使用大多数谷歌搜索的顶部结果。
相关链接:
-
https://kubernetes.io/docs/reference/#api-reference
-
https://www.manning.com/books/kubernetes-in-action
-
https://kubernetes.io/docs
原文链接:https://medium.com/payscale-tech/imperative-vs-declarative-a-kubernetes-tutorial-4be66c5d8914
Kubernetes入门与进阶实战培训
Kubernetes入门与进阶实战培训将于2019年6月14日在北京开课,3天时间带你系统掌握Kubernetes, 学习效果不好可以继续学习。 本次培训包括:Docker基础、容器技术、Docker镜像、数据共享与持久化、Docker三驾马车、Docker实践、Kubernetes基础、Pod基础与进阶、常用对象操作、服务发现、Helm、Kubernetes核心组件原理分析、Kubernetes服务质量保证、调度详解与应用场景、网络、基于Kubernetes的CI/CD、基于Kubernetes的配置管理等,点击下方图片或者点击阅读原文了解详情。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Kubernetes 教程之应用部署
- Kubernetes部署实操教程
- 超详细 Pycharm 部署项目视频教程
- 命令式与声明式——Kubernetes部署教程
- 入门教程 | 从安装部署开始学习 Elasticsearch
- 小白必看,超详细的Pycharm项目部署教程!
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
浪潮之巅(上册)
吴军 / 人民邮电出版社 / 2013-5-1 / 35.00元
《浪潮之巅(第2版)(上册)》不是一本科技产业发展历史集,而是在这个数字时代,一本IT人非读不可,而非IT人也应该阅读的作品。一个企业的发展与崛起,绝非只是空有领导强人即可达成。任何的决策、同期的商业环境,都在都影响着企业的兴衰。《浪潮之巅》不只是一本历史书,除了讲述科技顶尖企业的发展规律,对于华尔街如何左右科技公司,以及金融风暴对科技产业的冲击,也多有着墨。此外,《浪潮之巅》也着力讲述很多尚在普......一起来看看 《浪潮之巅(上册)》 这本书的介绍吧!