命令式与声明式——Kubernetes部署教程

栏目: 编程工具 · 发布时间: 4年前

内容简介:Apr 15, 2019, by Adrien Trouillaud对于Kubernetes部署,有两种基本的方法:使用一条条的kubectl执行命令,或者编写声明清单,再使用kubectl应用工具执行。前者有利于学习和与Kubernetes的交互实验(类似于编程语言的REPL);后者适合于可重现重用的快速部署。例如在生产环境中,您可能仍然会使用一条条的kubectl命令来做调试。

Apr 15, 2019, by Adrien Trouillaud

对于Kubernetes部署,有两种基本的方法:使用一条条的kubectl执行命令,或者编写声明清单,再使用kubectl应用 工具 执行。前者有利于学习和与Kubernetes的交互实验(类似于编程语言的REPL);后者适合于可重现重用的快速部署。例如在生产环境中,您可能仍然会使用一条条的kubectl命令来做调试。

命令式与声明式——Kubernetes部署教程

前提条件

本教程假设您已经具有了可访问的Kubernetes集群环境。现在,您可以轻松地在您的机器上运行一个单节点集群,或者在您熟悉的云环境中创建一个多节点集群。并确定您的机器上已经安装并正确配置了kubectl。让如下命令可以成功执行:

kubectl cluster-info

还需确认您在缺省命名空间内的权限是否足够(可能与缺省命名空间不同)。edit角色权限能够满足实验需求。如果您为本教程创建了一个集群,那么您可能就是管理员。或者让其他人为您准备好环境。

备注:如果您不想用公共的镜像文件,而想自己构建并推送一个自定义的境像。那我们假定您和您的集群是可以访问容器镜像注册器的。同样的,也可以让其他人为您准备好此环境。业界协作比较好的组合环境包括GKE与GCR、AKS与ACR等。另外,DockerHub和Quay也是非常流行的镜像注册器。如果不打算公开自己的容器镜像,则需要在私有的命名空间中,给默认服务帐户配置拉取镜像密钥。

构建和推送

不管是采用指令式部署还是声明式部署,都需要有一个容器映像。(如果您使用现成的映像,如Nginx镜像,可跳过此部分)。本教程中的一些步骤与后续构建的应用程序有直接关联。同时,也可帮助您学习如何封装应用程序,所以建议您不要忽略此部分。

应教学目的需要,我们将从一个简单的web应用源代码开始教程。下面是Node.js文档中的一个案例应用代码(或采用您所喜欢的编程语言编写一个类似的应用程序)。将以下的代码复制到一个名为app.js的文件中,并存放在一个空的文件夹下。

// app.js

const http = require('http');

const os = require('os');

const ip = '0.0.0.0';

const port = 3000;

const hostname = os.hostname();

const whoami = process.env['WHOAMI'] || 'Anonymous';

const server = http.createServer((req, res) => {

res.statusCode = 200;

res.setHeader('Content-Type', 'text/plain');

res.end( Hi, I’m ${whoami}, from ${hostname}.\n );

});

server.listen(port, ip, () => {

console.log( Server running at http://${ip}:${port}/ );

我们对上述案例代码做了一些调整:

  • 将服务器本机IP定义为0.0.0.0而不是127.0.0.1.
    因为后者仅仅适用于IP回路寻址。而这段程序需监听来自一个集群IP的请求(由0.0.0.0捕获请求)。
  • 将“Hello World”消息分拆为2个变量:主机名,提供请求响应的一个副本节点的标识;
    WHOAMI环境变量,在部署时设定,默认值为“Anonymous”。

如果您已经安装了Node.js, 那可以通过以下命令进行代码的本地测试:

node app.js # 打开链接 http://localhost:3000 测试

备注:如果您在自己的Kubernetes集群环境进行测试,无需担心TLS终端和授权问题,因为这个应用可以在集群的边界运行,例如Ambassador。如果您采用Zero-trust,这段代码可以运行在Istio之类服务网格集群的微服务内。

接下来将应用打包成一个 Docker 镜像。复制如下代码,保存为Dockerfile的文件:

# Dockerfile

FROM node:8

COPY app.js .

在文件所在目录下,执行命令:

docker build -t myrepo:mytag .

请根据您的容器镜像注册器,替换 myrepo。例如:

采用GCR,就替换为 gcr.io/project-name/image-name

采用默认的DockerHub,就替换为 user-name/image-name

用非 “latest”的任何词语,来替代 mytag 。tag不能重复,如果您是和多人一起学习本课程时,请确保这点。最后的那个点不能遗漏,它表示用当前目录环境构建上下文。

最后,将镜像推到您的镜像库(也就是Kubernetes拉取镜像的地方):

docker push myrepo:mytag

命令式的配置方法

- RUN

部署Kubernetes最简捷的方式就是采用kubectl run命令。

kubectl run myapp --image myrepo:mytag --replicas 2

如果是多人共享一个命名空间,请采用唯一的名称替代myapp, 采用上述步骤中的指定的库地址和名称(或nginx)替代myrepo:mytag .

这个命令,看起来与本地启动容器的docker run命令有些相似, 但相似的地方也就这些而已。(再后面的交互图中也有体现)。

本质上Kubectl将用户的命令语句转换成一个Kubernetes的Deployment对象的声明。而一个Deployment是一个能滚动升级的高级API。

  1. Kubectl将Deployment对象发送给运行在Kubernetes集群内的API服务器—— kube-apiserver。
  2. kube-apiserver 将Deployment信息保存到集群内分布式键值对存储etcd中, etcd为Kubectl请求提供响应。
  3. Kubernetes负责监控Deployment及其他事件的控制器管理器kube-controller-manager,将为Deployment创建一个ReplicasSet副本,并将其发送到kube-apiserver。一个ReplicaSet就是一个Deployment的版本。在滚动更新时,将创建一个新的ReplicaSet,并逐步扩展到预先配置的期望副本数,而旧的ReplicasSet数量逐步变为零。这整个过程都是采用异步机制。
  4. kube-apiserver将ReplicaSet信息保存到etcd中。
  5. kube-controller-manager同样以异步的方式为ReplicaSet创建2个或以上的Pod,并将其发送给kube-apiserver。Pod是Kubernetes的基本单元,是搭载1或多个共享Linux cgroup和命名空间的容器环境。
  6. kube-apiserver将Pod信息保存到etcd中。
  7. 负责监视Pod事件的Kubernetes调度器kube-scheduler,将逐步更新每个Pod,为其分配到某个节点,并将更新的信息发送回kube-apiserver。
  8. kube-apiserver在 etcd中更新Pod状态。
  9. 最后,节点上运行的kubelet会实时监控Pod,并在Pod启动了容器。

备注:容器,调度器和kubelet同样会将自身的状态信息发送给API server。总而言之,我们可以将Kubernetes理解成一个不断循环的CRUD API。如下是上述过程的交互图:

命令式与声明式——Kubernetes部署教程

- Get, Describe

kubectl run 创建一个Deployment, 然后又衍生创建ReplicaSet和Pod。但它们都运行在哪里呢?我们可以采用kubectl get命令去列出您默认命名空间下所有的 Deployments, ReplicaSet和 Pod:

kubectl get deployments # plural or singular, or deploy for short

kubectl get replicasets # or rs

在对象后面加上对象名可以列出单个的对象。如:

kubectl get deployment myapp

导出etcd中的对象状态信息--output option (or -o):

kubectl get deployment myapp -o yaml

要获得更多的细节信息,包括与此对象相关的近期事件信息。可以采用kubectl describe命令::

kubectl describe deployment myapp

上述命令与参数同样适用于ReplicaSet 和Pod。

- Label

标签的作用非常大,一个标签是一个字符串的健值对。Kubernetes所有的对象都可以被标签,并且可以按标签做成选择器。通过kubectl run命令加run=myapp的方式可以自动定位Deployment,ReplicaSet和Pod。您可以在YAML和描述中看到对象的标签,也可以通过--show-labels参数,将标签输出。命令如下:

kubectl get deployments --show-labels

kubectl get replicasets --show-labels

可以采用 --label-columns ( -L)参数将标签以列的方式显示。如:

kubectl get replicasets -L run

一种重要用法是采用--selector (-l)参数进行标签过滤。如:

kubectl get pods -l run=myapp

还可以手动添加新的标签。如:

kubectl label deployment myapp foo=bar

- Delete

Kubernetes API本质上是声明性的,这意味着控制器总是在比较对象状态和所期望的状态。因此,如果我们删除一个Pod, ReplicaSet控制器将创建一个新的Pod,以维护所期望的副本数。我们通过如下测试验证一下:

kubectl delete pods -l run=myapp

# 等待一会儿后再运行

我们会发现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名通常是自动生成的,名字带有随机数,所以方便起见,建议提供的名字是类型/名字的键值对格式。如:

kubectl port-forward deployment/myapp 3000

浏览器打开如下链接,您将看到“Hello World”的消息。

http://localhost:3000/

由于上述方式只能映射一个Pod,所以当您重新加载后,不能修改主机名。同时请注意,采用kubectl run命令时,我们不能通过 –env参数修改WHOAMI 环境变量。只能通过kubectl set 或kubectl patch命令才行的(在稍后介绍)。

如果本地端口与Pod端口不一致的话,需在命令内注明,如:

kubectl port-forward deployment/myapp 5000:3000

上述所配置的端口代理可以通过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端口,除非有特定选项指定。如:

kubectl expose deployment myapp --port 80

因为本地设备端口不能用,采用标准的HTTP端口。

确认一下所创建Service 的信息:

kubectl get service # or svc

可在Service描述中或所控制的Endpoints中,看到监听Pod的IP,这方法可以用于网络问题的诊断。

kubectl describe service myapp

如下是启动一个带有交互式终端的临时Pod,来实际调用服务:

kubectl run mytest -it --rm --image alpine # Alpine很轻量

inside mytest:

apk add curl

curl http://myapp # “Hello World…”

exit

备注:myapp的Deployment需提前创建。

-t 或 –tty,分配一个TTY终端。

-i 或–stdin 保持stdin打开

--rm 退出时删除Deployment。

- Logs

如应用程序有问题,通常要检查日志。容器日志通常是由容器运行时(Docker守护进程)存储在节点上。默认情况下,当日志文件超过10MB时,日志会自动循环覆盖。可以配置一个日志代理,将日志推送到后端的持久存储上。请记住一点,kubectl logs命令只能看到节点上上的日志。

可以通过Pod名获取一个Pod的日志,也可以通过类型/名字获取一组Pod的日志。如:

kubectl logs deployment/myapp

在我们的教学中,日志较少,但实际生产应用的日志会详细且多很多。尤其是出问题的时候。可以通行参数,按日志量—since (1m),日志时间--since-time (2018–11–01T16:30:00)或尾部记录数 --tail (20)来设定输出结果。其他的参数选项还包括 –follow(-f),--previous(容器持续崩溃),--timestamps(应用没有记录时间戳)。最后将日志输出到文件和进行您所需的分析。如:

*kubectl logs deployment/myapp --since 5m > log.txt

grep error log.txt

more grep*

- Exec, Copy

kubectl还提供了一些其他的工具来帮助我们调试正在运行的容器。kubectl exec是在容器中执行命令,kubectl cp在容器之间复制文件和目录。这两个命令使用显式的Pod名称,而不是Deployment名。如下是用--output jsonpath在 shell 变量中存储Pod名称的用法:

POD_NAME=$(kubectl get pods -l run=myapp -o

我们可以将这个变量用到其他命令内:

kubectl exec $POD_NAME -it sh # 在容器内打开一个交互式shell。

容器内的执行段样本:

node --version

echo $WHOAMI

exit

返回到本地机器

kubectl cp $POD_NAME:app.js remote-app.js # 用于理解容器内运行的样本

- Set, Scale, Patch

如前面提到的,我们需要用kubectl set或kubectl patch来设置WHOAMI环境变量。在此过程中,我们还同步学习如何使用kubectl get -watch(或-w)查看资源更改,并实时观察多个对象的滚动更新:

kubectl get deployment myapp -w

在第二个终端:

kubectl get replicasets -w -l run=myapp

在第三个终端:

kubectl get pods -w -l run=myapp

在第四个终端:

kubectl set env deployment/myapp WHOAMI="HAL 9000"

在前三个终端中,可观察新ReplicaSet的创建,以及新/旧pod按容器编排进行的创建/删除的过程。还将看到与状态更改所对应的数据行的变化(Pod的期望数、当前数、更新日期、可用/准备)。在进行另一个变更之前,先扩展一下Deployment的ReplicaSet数量:

kubectl scale --replicas 3 deployment myapp

kubectl set命令仅限于设置环境变量、图像、资源请求/限制、标签选择器、ServiceAccounts和RoleBindings (基于角色的访问控制,或RBAC)。

kubectl patch命令则更为通用。它接受JSON或YAML来替换或合并特定的字段。举个简单的例子,我们可以绕过调度程序,强制让所有pod运行在一个节点上,命令如下:

首先,看看我们的应用程序的副本目前运行在不同的节点:

kubectl get pods -l run=myapp -o wide # 查看NODE列

选定一个节点名,并以patch的方式调整Deployment:

NODE_NAME=$(kubectl get pods -l run=myapp -o

jsonpath={.items[0].spec.nodeName})

kubectl patch deployment myapp -p

确认所有pod被分配到同一个节点上:

kubectl get pods -l run=myapp -o wide

实际上,还有更好的方式调整调度器,例如标签中心节点,使用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:

kubectl get -o yaml --export:

kubectl get deployment myapp -o yaml --export > myapp-deployment.yaml

ReplicaSet和pod是被控制对象,所以不需要进行manifest。

一个manifest实际上,并不需要保存所有状态。其中一些是由Kubernetes补充的。export选项自动删除了部分status和元数据(UID、创建时间戳等),但您可能按需要删除更多的配置数据,例如默认值等。

以下提供了Deployment和Service的标准示例,内容为:

# myapp-deployment.yaml

apiVersion: apps/v1

kind: Deployment

metadata:

name: myapp

spec:

replicas: 3

selector:

matchLabels:

run: myapp

template:

metadata:

labels:

run: myapp

spec:

containers:

- name: myapp

image: psdocker/myapp:mytag

env:

- name: WHOAMI

value: "HAL 9000"

myapp-service.yaml

apiVersion: v1

kind: Service

metadata:

name: myapp

spec:

selector:

run: myapp

ports:

- port: 80

targetPort: 3000

为了证明它们的工作原理,我们先删除全部对象,然后再应用manifest来重新生成对象:

kubectl delete deployment myapp

kubectl delete service myapp

如果我们没有删除Deployment和 Service,那它们只是会进行更新来匹配manifest。

kubectl apply命令具有幂等的特征。即我们可以多次编辑manifest,然后再次去应用它。例如,修改了副本数配置后再应用。

如需进一步了解Kubectl apply,可以参考 Kubernetes的API 参考。

对于具有多个环境的复杂应用,直接对manifest进行管理会非常困难。更好的是通过工具帮助我们管理配置。目前已有不错的工具有kustomize(从1.14版开始就成为kubectl的一部分)、Helm和Jsonne等。对于整体构建和部署过程,并需要在manifest注入带标记的映像,skaffold是一个值得推荐的工具。

总结

在本教程中,我们讨论了Kubernetes上无状态应用程序的基本组建: Deployment、ReplicaSet、Pods和Service。以及围绕它们进行的状态获取,执行等功能的命令和声明方法。我们只是以最基本的方式使用了 Deployment。在生产环境中,您应该需要设置CPU和内存请求和限制,并且您可能还对自动缩放和其他功能会感兴趣。

有几本好书可以用于学习Kubernetes。我读过且毫不犹豫推荐的为唯一一本书是 《Kubernetes in Action》 (Manning著)。

当然, 官方文档 是一个很好的参考。

最后就是正确地使用大多数谷歌搜索的顶部结果。

原文链接: https://medium.com/payscale-te ... d8914 (翻译:易理林)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

构建高性能Web站点

构建高性能Web站点

郭欣 / 电子工业出版社 / 2009-8 / 59.00元

本书围绕如何构建高性能Web站点,从多个方面、多个角度进行了全面的阐述,涵盖了Web站点性能优化的几乎所有内容,包括数据的网络传输、服务器并发处理能力、动态网页缓存、动态网页静态化、应用层数据缓存、分布式缓存、Web服务器缓存、反向代理缓存、脚本解释速度、页面组件分离、浏览器本地缓存、浏览器并发请求、文件的分发、数据库I/O优化、数据库访问、数据库分布式设计、负载均衡、分布式文件系统、性能监控等。......一起来看看 《构建高性能Web站点》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具