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

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

内容简介: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 (翻译:易理林)


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

查看所有标签

猜你喜欢:

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

PHP Cookbook

PHP Cookbook

Adam Trachtenberg、David Sklar / O'Reilly Media / 2006-08-01 / USD 44.99

When it comes to creating dynamic web sites, the open source PHP language is red-hot property: used on more than 20 million web sites today, PHP is now more popular than Microsoft's ASP.NET technology......一起来看看 《PHP Cookbook》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

html转js在线工具
html转js在线工具

html转js在线工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具