为了用好基于容器技术的各种工具、更好地熟悉云产品形态,前端开发工程师们很有必要了解一下容器相关技术,以下是正文。
在回答这个问题之前,让我们一起先了解下 web 应用部署方式的演化过程。
在我刚接触软件开发的时候,人们部署应用的方式通常是这样的:
首先需要一台服务器,然后在服务器上安装 Web Server (例如:Nginx 或者 Apache Server)。接着,根据应用的运行时要求,安装对应的软件包(例如:如果代码是用 Node.js 编写,就需要安装 Node.js 运行时环境)。最后,根据应用的其他功能,安装对应的软件,如数据库之类的。
应用部署的同时,宿主机上也多了各种软件程序。此时,这个 web 应用提供的服务如下图所示:
随着 web 应用的日趋复杂,这种部署方式的弊端逐渐出现了。
现代服务器性能非常强悍,如果一台主机上仅运行几个程序,就可能造成机器资源利用率偏低。而且,由于程序是直接运行在宿主机上的,程序之间存在资源竞争关系,会互相影响。如果某个程序造成宿主机卡顿或挂掉,那么其他程序也无法正常工作了。
在 2013 年出现 Docker 容器技术之后,这种部署方式逐渐被淘汰了。
容器技术有多种实现方式,比较主流的是 Docker。
其 logo 很好的体现了“容器”的特点——程序就像一个个集装箱,彼此隔离的运行在宿主机上。
区别于传统的宿主机部署模式,容器化技术提供了一个隔离环境。程序之间既不会互相影响,也不会影响宿主机的稳定。
Docker 方式运行的容器,可以理解为一个虚拟机(但和虚拟机还是有区别的,虚拟机是对硬件的虚拟化;Docker 是操作系统层的虚拟化)。它包含了运行程序所需的运行时环境和程序代码,启动后能够以端口映射的方式,将容器自身的服务暴露给宿主机和外部用户。
容器之间除了彼此隔离之外,也能够通过 Docker 引擎实现互联。容器之间的访问通常是以内部 IP 的方式进行的。
随着 web 应用规模的继续扩大,单主机不再能满足性能要求。现在的部署是基于多主机、多容器进行的。那么,如何对这些主机资源和应用容器进行管理?这个问题的答案指向了本文的主角—— K8S。
K8S 的全称是Kubernetes
,因为在首字母k和尾字母s中间有8个字母,因此被简写为 K8S(类似的还有i18n
等)。它是由谷歌开源的,主流的容器管理平台。
它的 logo 也很有意思,K8S 就像一个舵一样,让用户能够在茫茫大海中将满载集装箱的大船驶向成功的彼岸。
借助 K8S 提供的能力,运维人员——甚至是前端开发人员——能够很容易地在集群环境中部署和管理容器。并且,对于以下功能,K8S 也能够很好地支持:
K8S 环境需要先被安装和运行起来。由于这部分的操作需要服务器支持,这里就不做介绍了。下文的全部内容都基于读者能够连接到任一 K8S 系统这个前提之上。
在 K8S 中,所有资源都是通过声明式配置进行管理的,它们被称作 K8S 对象。以 namespace 为例:
apiVersion: v1
kind: Namespace
metadata:
name: demo-space
spec:
finalizers:
- kubernetes
status:
phase: Active
不同类型的对象所需的配置不完全相同,但他们都应有如下几个基础配置:
apiVersion
- 创建该对象所使用的 Kubernetes API 的版本,不同的版本,对于 yaml 中可使用配置项的字段、格式有不同的要求。kind
- 想要创建的对象的类别metadata
- 帮助唯一性标识对象的一些数据spec
- 你所期望的该对象的状态更详细的文档,请移步 K8S 官网。
常见的 K8S 对象包括 Namespace、Ingress、Sevice、Development、Pod。其中,Namespace 是一个虚拟的概念,用来对集群划分不同的命名空间。通常,同一个 Namespace 中的资源,其命名应该是唯一的。其他几种类型的资源的关系如下图所示:
通常一个集群会包含数台物理主机,它们都是集群的节点,这些节点需要一个统一的 IP 进行访问。Ingress 提供了这项能力,它是整个集群的流量入口。
Ingress 控制器有多种实现,比较常见的是基于 Nginx 实现的。
Ingress-nginx 文档:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/
流量从 Ingress 进入集群之后,应该被分配给不同的容器处理。但通常来说,容器的 IP 不是固定的,这是因为在 K8S 中,可以随时增减容器的数量,此时容器 IP 就是动态的。
Service 是一个抽象概念,它的作用是在 Ingress 和 Pod 之间解藕它们二者的关联。它会动态获取可用的 Pod 的信息,传递给 Ingress,以保证进行正确的负载均衡。
不论是正常的增减容器数量(扩缩容)还是容器自身因为意外重启,流量总会被分配到可用的容器上。
Development 是用于管理副本集和容器配置的。K8S 不建议用户直接操作副本集配置,所以 Development 通常是用户所需要接触的最基础的配置。
副本集一组以期望的规模稳定运行的容器集合。
通过 Development,可以实现如下功能:
Pod 是 K8S 中最小的部署单元,它包含了一组容器(可以是一个或多个)。同一个 Pod 中的不同容器之间可以通过localhost:<port>
的方式互相访问对方暴露出来的服务,同时,容器之间可以访问共同的数据卷。多容器的使用场景通常是一个主容器加上多个 sidecar,他们彼此配合,共同实现功能需求。
以下是多容器配合的一个实例:
上图中的 Web Server 容器可以对外提供资源访问的服务,同时,File Puller 作为一个 sidecar 容器,可以同时将远端的内容更新到本地存储中,以保证 Web Server 提供的内容是最新的。
kubectl 是 K8S 提供的命令行工具,使得用户可以在本地对 K8S 集群发送操作指令。
MacOS 下的安装教程:https://kubernetes.io/zh/docs/tasks/tools/install-kubectl-macos/
安装完成后,创建配置文件:
touch $HOME/.kube/config
然后在文件中加入集群配置:
下面的配置仅做参考,连接集群的时候需要改为对应的配置。
apiVersion: v1
# 集群配置,可以是多个;集群必须包含 server 字段
clusters:
- cluster:
server: https://dami.net
name: dami-c3
# 上下文配置,可以是多个,每个上下文必须包含集群名称、namespace 名称、用户名
contexts:
- context:
cluster: dami-c3
namespace: demo-space
user: manooog
name: c3-demo-space-context
# 当前默认的上下文配置,所有 kubectl 命令,都会默认使用这个上下文
current-context: c3-demo-space-context
kind: Config
# 用户信息,包含用户名和对应的权限信息,可能是 token
users:
- name: manooog
user:
token: <yourToken>
保存好配置之后,就可以是使用 kubectl 命令对集群进行操作了。eg:
kubectl get ingress # 获取当前 context 中对应的 namespace 中的 Ingress 配置
kubectl 功能比较多,根据使用习惯的不同,同一个功能也有不同的使用方式。下面列举一些我使用得比较多的命令。
kubectl get service/<xxx> -o yaml
以上命令的含义是,获取名称为xxx
的 service 对应的配置文件,并且以 yaml 格式输出。对于不同的资源,通常是以<类型>/<名称>
进行区分的。
上面提到过,K8S 中一切资源都可以从声明式配置中得到。当我们想创建一个资源的时候,可以先创建对应的配置文件。然后使用以下命令使配置生效:
kubectl create -f service.yaml
kubectl apply -f service.yaml
以上命令都可以应用配置文件,区别就在在于,create 通常用于第一次创建,apply则用于修改已存在的配置文件。我更倾向于使用 apply 命令。
二者在使用上的区别:https://stackoverflow.com/questions/47369351/kubectl-apply-vs-kubectl-create
kubectl logs <pod> <conteiner-name>
获取 Pod 中对应的容器的输出信息。如果 Pod 中只有一个容器,container-name 可以省略。
在容器运行过程中,可能会出现异常情况,这个时候需要进入容器内部进行检查。这个时候可以使用以下命令:
kubectl exec -it demo-5b7846d65b-nvnnm -- sh
如果 Pod 包含多个容器,同样需要使用 -c
参数,指定需要进入的容器名称。
这个 demo 的目标是启动一个 3 实例的 Node.js 后端程序,并实现以下效果:
curl demo-20211215.io/20211215
// -> Hello World
http://demo-20211215.io 域名指向的是集群 Ip,当请求/20211215
这个路径的时候,期望能收到 Node.js 后端返回的字符串:Hello World
。
由于这个示例域名是不存在的,因此我们这里利用 Hosts 进行配置。在本地 Hosts 增加一条记录,将域名直接绑定到测试集群的 Ingress IP上:
<Ingress IP> demo-20211215.io
现在执行测试命令,将会得到下面的提示:
$ curl -v demo-20211215.io/2021215
* Trying <Ingress IP>...
* TCP_NODELAY set
* Connected to demo-20211215.io (<Ingress IP>) port 80 (#0)
> GET /2021215 HTTP/1.1
> Host: demo-20211215.io
> User-Agent: curl/7.64.1
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Server: nginx/1.19.2
< Date: Tue, 14 Dec 2021 07:10:59 GMT
< Content-Type: text/plain; charset=utf-8
< Content-Length: 21
< Connection: keep-alive
目前集群还不能响应对于 /20211215
这个 path 的请求。接下来,让我们一起试着将这个服务部署起来。
首先,我们需要创建一条 Ingress 规则,并保存为ingress.yml
:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: demo-20211215
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "false"
spec:
rules:
- host: demo-20211215.io
http:
paths:
- path: /20211215
backend:
serviceName: demo-service-20211215
servicePort: 80
执行创建命令:
kubectl apply -f ingress.yaml
接着再执行:
kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
demo-20211215 demo-20211215.io
就可以看到刚才创建的那条 ingress 规则了。这个时候,再尝试访问demo-20211215.io/20211215
,返回结果已经有了变化:
$ curl demo-20211215.io/20211215
< HTTP/1.1 503 Service Temporarily Unavailable
< Server: nginx/1.19.2
< Date: Tue, 14 Dec 2021 07:20:28 GMT
< Content-Type: text/html
< Content-Length: 197
< Connection: keep-alive
目前服务仍不可访问,但配置已生效。
配置解析:
metadata.name // 这条 ingress 的规则名称,同一个 namespace 中,name 字段是唯一的。
metadata.annotations // 这个是对 ingress 控制器的配置,视具体情况而定
spec.rules // 匹配规则
在这个配置中,有一条规则。即:响应对 http://demo-20211215.io/20211215 这个 URL 的请求,并且将请求转发给名为 demo-service-20211215
的这个 service 的 80 端口。
注意,这个时候尚未创建对应的 Service,但 K8S 并没有提示创建 ingress 失败。这是因为K8S提供的服务发现的能力,如果后期对应的服务被创建了,那么自然会被 ingress 识别,并分配流量过去。
创建service.yaml
并贴入以下内容:
apiVersion: v1
kind: Service
metadata:
name: demo-service-20211215
spec:
selector:
app: demo-app-20211215
ports:
- protocol: TCP
port: 80
targetPort: 3000
执行创建命令:
kubectl apply -f service.yaml
创建好之后通过以下命令查看:
kubectl get service/demo-service-20211215
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
demo-service-20211215 ClusterIP 10.254.40.168 <none> 80/TCP 51s
配置解析:
metadata.name // service 的名称,在同一个 namespace 中,该字段是唯一的
spec.selector.app // 这是一个选择器,可以选择 labels 中包含 app=demo-app-20211215 的 pod 加入这个 service 中
将下面的配置内容保存为deployment.yaml
:
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-deployment-20211215
spec:
replicas: 3
selector:
matchLabels:
app: demo-app-20211215
template:
metadata:
labels:
app: demo-app-20211215
spec:
containers:
- name: demo-20211215
image: rxh1212/demo-20211215
ports:
- containerPort: 3000
创建以及查看的命令就不再举例了。
配置解析:
metadata.name // deployment 的名称,也是 namespace 中必须要唯一的
spec.replicas // pod 的实例个数,这里是 3,表示将会部署三个应用实例
spec.selector.matchLabels // 应该与 spec.template.metadata.labels 相同,表示的是 pod 的 labels。
spec.template.spec.containers // 是一个数组,表示这个 deployment 所使用的容器。
在这里我使用了名为 rxh1212/demo-20211215
镜像,它是我单独编译的,包含一个 Node.js 应用。代码如下:
"use strict"
const express = require("express")
// Constants
const PORT = 3000
const HOST = "0.0.0.0"
// App
const app = express()
app.get("/20211215", (req, res) => {
res.send("Hello World")
})
app.listen(PORT, HOST)
console.log(`Running on http://${HOST}:${PORT}`)
镜像启动之后,会在 3000 端口提供一个 Http 服务。如果访问 /20211215
,会收到Hello World
的响应。
这个镜像的封装、编译过程,在此略过。感兴趣的可以在评论区留言。
言归正传,deployment 创建好之后,再对demo-20211215.io/20211215
进行访问,即可得到以下响应:
$ curl demo-20211215.io/20211215
< HTTP/1.1 200 OK
< Server: nginx/1.19.2
< Date: Tue, 14 Dec 2021 07:25:54 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 11
< Connection: keep-alive
< X-Powered-By: Express
< ETag: W/"b-Ck1VqNd45QIvq3AZd8XYQLvEhtA"
<
* Connection #0 to host demo-20211215.io left intact
Hello World* Closing connection 0
至此,我们完成了一个简单的 K8S 部署流程。
以上 Demo 使用的配置代码,已上传至 github 中:https://github.com/manooog/K8S-demo-20211215
为什么写容器相关文章?容器技术在前端开发中经常会遇到,例如:Gitlab CI/CD 使用的容器技术作为构建中的一环、开源或者公司自建的容器平台产品都是以容器技术作为支撑的。为了用好基于容器技术的各种工具、更好地熟悉云产品形态,前端开发工程师们很有必要了解一下容器相关技术。本文以 K8S 为引子,介绍了软件开发中应用部署方式的迭代变化、K8S 中的基础概念以及 kubectl 工具的用法,希望能够为初学容器技术的小伙伴提供一点帮助。
看完之后能让读者得到什么?看完本文后,我希望读者能够掌握以下知识点:
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/ReXB_6j9Fy8jVG3EQQpcYw
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。