内容简介: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集群环境。现在,您可以轻松地在您的机器上运行一个单节点集群,或者在您熟悉的云环境中创建一个多节点集群。并确定您的机器上已经安装并正确配置了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。
- 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:
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”的消息。
由于上述方式只能映射一个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 (翻译:易理林)
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 声明式 (declarative) vs 命令式 (imperative)
- Nim 1.0 发布,命令式编程语言
- Nim 0.17.2 发布,命令式编程语言
- 命令式组件Message、Dialog的主流写法分析
- Nim 0.18.0 发布,命令式编程语言
- 使用配置文件对 Kubernetes 对象的命令式管理
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
C语言程序设计现代方法
K. N. King / 人民邮电出版社 / 2007-11 / 55.00元
《C语言程序设计现代方法》最主要的一个目的就是通过一种“现代方法”来介绍C语言,实现客观评价C语言、强调标准化C语言、强调软件工程、不再强调“手工优化”、强调与c++语言的兼容性的目标。《C语言程序设计现代方法》分为C语言的基础特性。C语言的高级特性、C语言标准库和参考资料4个部分。每章都有“问与答”小节,给出一系列与本章内容相关的问题及其答案,此外还包含适量的习题。一起来看看 《C语言程序设计现代方法》 这本书的介绍吧!