404频道

学习笔记

/etc/resolv.conf文件为Linux主机的DNS配置文件,在 Linux 主机上可以执行 man resolv.conf 查看帮助信息。

配置文件说明

整个配置文件分为了4个部分:nameserver、search、sortlist和options

nameserver

用来配置DNS服务器地址。支持ipv4和ipv6地址。如果要配置多个DNS服务器,可以增加多条配置,域名按照DNS服务器配置的顺序来解析。配置多条的例子如下:

1
2
nameserver 100.100.2.136
nameserver 100.100.2.138

在默认情况下,如果要查找的域名中不包含domain信息(即不包含.字符),此时会以当前hostname的domain信息来进行查找。可以通过search字段来修改该行为,即可以通过在域名的后面增加配置的后缀来进行查找。

如果存在多条search记录,则以最后一条search记录为准。

比如k8s的pod中的search域格式如下,如果要查找defaultnamespace下的service的svc1,此时系统会自动追加 svc1.default.svc.cluster.local来进行查找:

1
search default.svc.cluster.local svc.cluster.local cluster.local

sortlist

该字段极少场景下会用到。在做域名解析时,如果返回的A记录为多条,可以对结果进行排序,排序的依据为当前的字段配置。

1
sortlist 130.155.160.0/255.255.240.0 130.155.0.0

options

该配置必须加载一行中,格式如下,其中option部分可以为多个,多个之间用空格分割:

1
options option ...
  • inet6: 应用程序在执行系统调用 gethostbyname 时,会优先执行 AAAA 记录的查询。如果查询不到 ipv6 地址,再去查询 ipv4 地址。

参考资料

什么是CNCF Landscape?

CNCF(Cloud Native Computing Foundation)为云原生计算基金会的英文缩写,致力于云原生技术的普及和推广。

CNCF Landscape为CNCF的一个重要项目,为了帮助企业和个人开发者快速了解云原生技术的全貌,该项目维护在GitHub。CNCF Landscape的最重要的两个产出物为路线图和愿景图。

CNCF Landscape路线图

路线图的目的是指导用户使用云原生技术的路径和开源项目。其中包括了10个步骤。

CNCF Landscape愿景图

将云原生技术进行了分层分类,可以非常清晰的将云原生技术展示给用户。类似于软件架构中,又称“方块图”、“砌砖图”。

精简版图形如下:

供给层(provisioning)

愿景图的第一层,是云原生平台和云原生应用的基础。

自动化配置

关键词:IaC(基础设施即代码)、声明式配置

用来加速计算机资源的创建和配置,资源包括虚拟机、网络、防火墙规则、负载均衡等。只需要点击下按钮,即可自动化创建对应的资源,用来降低人工的维护。

典型工具代表:Terraform、Chef、Ansible、Puppet

CNCF项目列表:

CNCF项目 项目阶段 项目介绍
Akri 沙箱
CDK for Kubernetes 沙箱
Cloud Custodian 沙箱
KubeDL 沙箱
KubeEdge 孵化 华为开源的边缘计算项目
Metal3-io 沙箱
OpenYurt 沙箱 阿里云开源的边缘计算项目
SuperEdge 沙箱
Tinkerbell 沙箱

容器镜像仓库

用来存储和拉取容器镜像,最简单的项目如docker官方的单机版的docker registry,所有的公有云厂商也都有自己的工具。

CNCF项目包括:

CNCF项目 项目阶段 项目介绍
Harbor 毕业 由vmware打造的镜像仓库,提供了很多企业级的特性,广受企业内部使用。
Dragonfly 孵化 阿里开源的基于P2P技术做镜像分发项目,在大规模集群的场景下效果会比较明显。

安全合规

关键词:镜像扫描、镜像签名、策略管理、审计、证书管理、代码扫描、漏洞扫描、网络层安全

用来监控、增强应用和平台的安全性,包括从容器到k8s的运行时环境均会涉及到。

CNCF项目包括:

CNCF项目 项目阶段 项目介绍
cert-manager 沙箱 证书签发工具,部署在k8s之上,通过抽象CRD Certificate来管理证书,也可以用来管理Ingress的证书
Confidential Containers 沙箱
Curiefense 沙箱
Dex 沙箱
Falco 孵化
in-toto 孵化
Keylime 沙箱
Kyverno 沙箱 基于k8s的策略引擎工具,通过抽象CRD ClusterPolicy的方式来声明策略,在运行时通过webhook的技术来执行策略。相比于opa & gatekeeper,更加k8s化,但却没有编程语言的灵活性。
Notary 孵化
Open Policy Agent (OPA) 毕业 基于Rego语言的策略引擎,编程能力非常强大
Parsec 沙箱
[The Update Framework (TUF)](The Update Framework) 毕业

秘钥和身份管理

关键词:秘钥、身份、Secret、访问控制、认证、授权

CNCF项目包括:

CNCF项目 项目阶段 项目介绍
Athenz 沙箱
SPIFFE 孵化
SPIRE 孵化
Teller 沙箱

运行时层(Runtime)

云原生存储

关键词:PV、CSI、备份和恢复

云原生架构下,存储类的工具主要涉及到如下几个方面:

  1. 为容器提供云原生的存储。由于容器具有灵活、弹性的特点,云原生的存储相比传统存储会更复杂。
  2. 需要有统一的接口。这块基本都会使用k8s的CSI接口。另外,minio提供了S3协议的接口。
  3. 备份和还原功能。例如Velero可以用来备份k8s本身和容器使用到的存储。

CNCF项目包括:

CNCF项目 项目阶段 项目介绍
CubeFS 孵化
K8up 沙箱
Longhorn 孵化
OpenEBS 沙箱
ORAS 沙箱
Piraeus Datastore 沙箱
Rook 毕业
Vineyard 沙箱

容器运行时

容器运行时的三个主要特征:标准化、安全、隔离性。Containerd和CRI-O为容器运行时的标准实现方案,业界类似KataContainer的方式为将VM作为容器运行时,gVisor方案则在OS和容器中间增加了额外的安全层。

发展趋势:

  1. 基于 MicroVM 的安全容器技术,通过虚拟化和容器技术的结合,可以提升更高的安全性。比如 KataContainer。
  2. 操作系统的虚拟化程度进一步增加。Linux 4.5 版本的 CGroup V2 技术逐渐成熟,进一步提升了容器的隔离能力。Docker 提供了 rootless 技术,可以以非 root 用户运行,提升了容器的安全性。
  3. WebAssembly技术作为跨平台的容器技术可能会作为新的挑战者出现。
CNCF项目 项目阶段 项目介绍
Containerd 毕业
CRI-O 孵化 k8s的CRI的轻量级实现,支持runc和kata作为容器运行时
Inclavare Containers 沙箱
rkt 归档 CoreOS公司主导研发的容器引擎,昔日的Docker竞争对手,目前已经没落,已经被CNCF归档
WasmEdge Runtime 沙箱

云原生网络

关键词:SDN、CNI、Overlay网络

云原生网络具体指的是容器网络,k8s定义了CNI的网络规范,开源项目只需要实现CNI的网络规范即可,社区有非常多的CNI项目可以选择,比如Calico、Flannel、Weave Net等。网络分为Underlay和Overlay网络,其中容器网络为SDN网络,以overlay网络居多。

CNCF项目 项目阶段 项目介绍
Antrea 沙箱
Cilium 孵化
CNI-Genie 沙箱 华为云开源的多网络平面的项目,该项目并非具体的网络插件实现,而是k8s的CNI和具体网络插件实现的中间层,可以实现同一个节点上有多种网络插件的实现,支持同一个pod中有多个网卡。
CNI(Container Network Interface) 孵化 k8s的容器网络规范,指的一提的是在[plugins](containernetworking/plugins: Some reference and example networking plugins, maintained by the CNI team. (github.com))项目中,提供了很多k8s内置的简单容器插件,比如macvlan、bandwidth等
Kube-OVN 沙箱
Network Service Mesh 沙箱
Submariner 沙箱 Rancher公司开源的项目,用来解决k8s的多集群场景下的跨集群互通问题

编排和管理层(Orchestration & Management)

调度和编排

在单机的系统中,操作系统会来调度系统中运行的所有的进程,允许某个时间点调度某个进程到某个cpu上面。在集群的环境中,同样需要调度容器在某个时间点运行在某台主机的某个cpu上。

在云原生社区基本上形成以k8s为生态的调度和编排,主要的发展方向为:

  • 扩展k8s自身的功能。
  • k8s的多集群方向。
CNCF项目 项目阶段 项目介绍
Crossplane 孵化
Fluid 沙箱
Karmada 沙箱 华为云开源的k8s多集群管理项目,用来管理多个k8s集群。自己实现了一套完整的apiserver、scheduler、controller-manager,用来多k8s集群的调度。
kube-rs 沙箱
Kubernetes 毕业 CNCF的最重要项目,在容器编排领域具有绝对的统治地位。俗称“现代数据中心的操作系统”
Open Cluster Management 沙箱 Redhat主导的k8s多集群项目
Volcano 孵化 华为云开源的基于k8s的容器批量计算平台,常用于大数据、AI领域。k8s默认的Job设计较为简单,无法满足很多批处理场景。Volcano通过CRD扩展的方式定义了Queue、PodGroup、VolcanoJob等实现对批处理作业的抽象,并通过调度器扩展的方式来大幅提升pod的调度效率。
wasmCloud 沙箱

调协和服务发现

该领域主要包含两类工具:

  1. 服务发现引擎。
  2. 域名解析服务。比如CoreDNS。
CNCF项目 项目阶段 项目介绍
CoreDNS 毕业
etcd 毕业
k8gb 沙箱

远程过程调用(RPC)

用于进程间通讯的框架,主要解决的问题:

  1. 提供了框架,使开发者编码更简单。
  2. 提供了结构化的通讯协议。
CNCF项目 项目阶段 项目介绍
gRPC 孵化 业界使用较为广泛的RPC框架

服务代理

通常又称为负载均衡,从协议上来划分,可以分为四层负载均衡和七层负载均衡。

CNCF项目 项目阶段 项目介绍
BFE 沙箱
Contour 孵化
Envoy 毕业
OpenELB 沙箱

API网关

相比于七层负载均衡,API网关还提供了更多高级特性,比如认证、鉴权、限流等。

CNCF项目 项目阶段 项目介绍
Emissary-Ingress 孵化

服务网格

值得注意的是,业内最为流行的istio项目并不在该范围内。

CNCF项目 项目阶段 项目介绍
Kuma 沙箱
Linkerd 毕业 流行的服务网格项目
Meshery 沙箱
Open Service Mesh 沙箱
Service Mesh Interface 沙箱
Service Mesh Performance 沙箱

应用定义和应用部署

数据库

CNCF项目 项目阶段 项目介绍
SchemaHero 沙箱
TiKV 毕业 国内公司PingCAP开源的分布式kv数据库
Vitess 毕业 用来扩展mysql集群的数据库解决方案,突破单mysql集群的性能瓶颈

流式计算和消息

CNCF项目 项目阶段 项目介绍
CloudEvents 孵化 仅描述了事件的数据规范,并非具体的实现
NATS 孵化
Pravega 沙箱
Strimzi 沙箱
Tremor 沙箱

应用定义和镜像构建

CNCF项目 项目阶段 项目介绍
Artifact Hub 沙箱
Backstage 孵化
Buildpacks 孵化
Devfile 沙箱
Helm 毕业 k8s的应用打包工具
Krator 沙箱
KubeVela 沙箱 阿里云开源的基于OAM的应用模型的实现,用来做应用的发布,同时支持多集群
KubeVirt 孵化
KUDO 沙箱
Nocalhost 沙箱
Operator Framework 孵化 用来开发基于k8s CRD的operator框架,功能跟k8s亲生的kubebuilder非常相似
Porter
sealer 沙箱 阿里云开源的集群部署工具,理念比较先进,通过升维的方式可以通过类似Dockerfile的方式来构建集群镜像,并可以通过类似docker run的方式一键拉起完成的一套基于k8s的集群环境
Serverless Workflow
Telepresence

持续集成和持续交付

CNCF项目 项目阶段 项目介绍
Argo 孵化 k8s上应用广泛的工作流引擎
Brigade 沙箱
Flux 孵化 gitops工具
Keptn 孵化
OpenGitOps 沙箱
OpenKruise 沙箱 基于k8s能力扩展的组件,通过CRD的方式定义了很多对象,用来增强k8s的workload能力。该组件放到该领域下有些不合适。

可观测和分析

监控

CNCF项目 项目阶段 项目介绍
Cortex 孵化
Fonio 沙箱
Kuberhealthy 沙箱 k8s的巡检工具,用来检查k8s的健康状态。支持以插件的方式接入巡检脚本。
OpenMetrics 沙箱 从Prometheus项目中发展出来的监控数据格式标准,该项目仅定义标准,非实现。
Pixie 沙箱
Prometheus 毕业 云原生领域事实上的监控标准
Skooner 沙箱
Thanos 孵化 prometheus的集群化方案
Trickster 沙箱

日志

CNCF项目 项目阶段 项目介绍
Fluentd 毕业 日志收集工具

分布式会话跟踪

CNCF项目 项目阶段 项目介绍
Jaeger 毕业 User开源的完整的分布式会话跟踪项目
OpenTelemetry 孵化 同时集成了监控、日志和分布式会话跟踪三个领域的数据收集工具,大有一统可观察性领域的趋势。
OpenTracing 归档 已经完全被OpenTelemetry取代

混沌引擎

CNCF项目 项目阶段 项目介绍
Chaos Mesh 孵化
Chaosblade 沙箱
Litmus 沙箱

参考资料

核心原理

Flux为一款GitOPS的发布工具,应用信息全部放到git仓库中,一旦git仓库中应用信息有新的提交,Flux即可在k8s中发布新的部署。支持kustomize和helm chart形式的应用。目前该项目为CNCF的孵化项目。

快速开始

前置条件

  1. 已经存在k8s集群
  2. 可以获取到Github的个人token

安装flux cli

执行如下命令安装flux cli

1
curl -s https://fluxcd.io/install.sh | sudo bash

将如下内容追到到文件~/.bash_profile

1
. <(flux completion bash)

bootstrap

bootstrap操作会在k8s集群中安装flux到flux-system下,并且会通过git的方式来管理其自身。这里直接采用github,首先要获取到token,并设置为环境变量。

1
export GITHUB_TOKEN=xxx

并执行如下命令开始bootstrap

1
2
3
4
5
flux bootstrap github \
--owner=kuring \
--repository=flux-learn \
--path=clusters/my-cluster \
--personal

会自动创建github repo,并且会在main分支的clusters/my-cluster/flux-system下创建三个文件:

  • gotk-components.yaml:flux的组件yaml
  • gotk-sync.yaml
  • kustomization.yaml:使用kustomization来个性化flux组件

会安装如下组件到k8s中

1
2
3
4
5
6
$ k get deployment -n flux-system 
NAME READY UP-TO-DATE AVAILABLE AGE
helm-controller 1/1 1 1 27m
kustomize-controller 1/1 1 1 27m
notification-controller 1/1 1 1 27m
source-controller 1/1 1 1 27m

也可以使用更简单的方式flux install来安装flux,该命令不会将flux的安装信息存放到git仓库中,而是会直接安装。

部署应用

将上述的github项目copy到本地,需要特别注意的是github不允许使用密码来认证,需要在输入密码的地方输入token。

1
2
git clone https://github.com/kuring/flux-learn
cd fleet-infra

使用如下命令在git仓库中创建podinfo-source.yaml文件和kustomization文件

1
2
3
4
5
6
7
8
9
10
11
12
13
flux create source git podinfo \
--url=https://github.com/stefanprodan/podinfo \
--branch=master \
--interval=30s \
--export > ./clusters/my-cluster/podinfo-source.yaml

flux create kustomization podinfo \
--target-namespace=default \
--source=podinfo \
--path="./kustomize" \
--prune=true \
--interval=5m \
--export > ./clusters/my-cluster/podinfo-kustomization.yaml

podinfo-source.yaml文件内容如下:

1
2
3
4
5
6
7
8
9
10
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: GitRepository
metadata:
name: podinfo
namespace: flux-system
spec:
interval: 30s
ref:
branch: master
url: https://github.com/stefanprodan/podinfo

podinfo-kustomization.yaml文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: podinfo
namespace: flux-system
spec:
interval: 5m0s
path: ./kustomize
prune: true
sourceRef:
kind: GitRepository
name: podinfo
targetNamespace: default

执行如下命令将文件提供到git仓库

1
2
git add -A && git commit -m "Add podinfo GitRepository"
git push

查看podinfo的部署状态,说明已经自动部署成功。

1
2
3
4
5
$ flux get kustomizations --watch
NAME REVISION SUSPENDED READY MESSAGE
podinfo master/bf09377 False True Applied revision: master/bf09377
flux-system main/81f2115 False True Applied revision: main/81f2115

修改podinfo-kustomization.yaml文件内容如下,并重新提交到git仓库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: podinfo
namespace: flux-system
spec:
interval: 5m0s
path: ./kustomize
prune: true
sourceRef:
kind: GitRepository
name: podinfo
targetNamespace: default
patches:
- patch: |-
apiVersion: autoscaling/v2beta2
kind: HorizontalPodAutoscaler
metadata:
name: podinfo
spec:
minReplicas: 3
target:
name: podinfo
kind: HorizontalPodAutoscaler

过会可以看到环境中的hpa最小副本数已经变更为3.

资料

在k8s的运维过程中,经常会有根据pid获取到pod名称的需求。比如某个pid占用cpu特别高,想知道是哪个pod里面的进程。

操作步骤

查看进程的cgroup信息

1
2
3
4
5
6
7
8
9
10
11
12
13
$ cat /proc/5760/cgroup 
11:rdma:/
10:hugetlb:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
9:freezer:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
8:pids:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
7:perf_event:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
6:blkio:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
5:devices:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
4:memory:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
3:cpuset,cpu,cpuacct:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
2:net_cls,net_prio:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3
1:name=systemd:/apsara_k8s/kubepods/burstable/podf87c35b4-c170-4e1c-a726-d839e2fe6bea/17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3

其中的 17e15f78a43b2ddc7caff93bcc03d5ca7f5249fbee4c2ade5c3c96a7279af0a3 即为容器的id,可以如下的命令直接获取到容器的pid

1
2
CID=$(cat /proc/5760/cgroup | awk -F '/' '{print $6}')
echo ${CID:0:8}

继续执行如下命令查看docker的label

1
docker  inspect 17e15f --format "{{json .Config.Labels}}" 

其中io.kubernetes.pod.namespace为pod所在的namespace,io.kubernetes.pod.name为pod的name。

参考资料

cue为使用golang编写的一款配置语言。

安装

mac用户执行:brew install cue-lang/tap/cue,其他操作系统用户可以直接使用源码安装:go install cuelang.org/go/cmd/cue@latest

命令行使用

创建如下文件first.cue

1
2
3
4
5
6
7
8
9
a: 1.5
a: float
b: 1
b: int
d: [1, 2, 3]
g: {
h: "abc"
}
e: string
  • cue fmt first.cue:对代码进行格式化
  • cue vet first.cue:校验语法的正确性
  • cue eval first.cue:获得渲染结果
  • cue export first.cue:将渲染结果以json格式的形式导出,如果指定参数–out yaml,则可以以yaml方式导出。如果要导出某个变量,可以使用-e参数来指定变量。

语法

基础数据类型

支持的数据类型包括:float、int、string、array、bool、struct、null、自定义数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// float
a: 1.5

// int
b: 1

// string
c: "blahblahblah"

// array
d: [1, 2, 3, 1, 2, 3, 1, 2, 3]

// bool
e: true

// struct
f: {
a: 1.5
b: 1
d: [1, 2, 3, 1, 2, 3, 1, 2, 3]
g: {
h: "abc"
}
}

// null
j: null

// 表示两种类型的值,即可以是string,也可以是int
h: string | int

// 表示k的默认值为1,且类型为int
k: *1 | int

// 自定义数据类型
#abc: string

更复杂的自定义数据类型如下:

1
2
3
4
5
6
7
8
#abc: {
x: int
y: string
z: {
a: float
b: bool
}
}

定义cue模板

文件deployment.cue定义如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 自定义结构体类型
#parameter: {
name: string
image: string
}

template: {
apiVersion: "apps/v1"
kind: "Deployment"
spec: {
selector: matchLabels: {
"app.oam.dev/component": parameter.name
}
template: {
metadata: labels: {
"app.oam.dev/component": parameter.name
}
spec: {
containers: [{
name: parameter.name // 引用自定义结构体变量parameter
image: parameter.image
}]
}}}
}

parameter:{
name: "mytest"
image: "nginx:v1"
}

执行 cue export deployment.cue -e template –out yaml 可获取到渲染结果。

引用

快速开始

部署

https://github.com/tektoncd/cli/releases下载tkn工具,该工具为tekton的命令行工具。

执行如下命令安装dashboard

1
kubectl apply --filename https://github.com/tektoncd/dashboard/releases/latest/download/tekton-dashboard-release.yaml

dashboard安装完成后仅申请了ClusterIP类型的Service,可以在本地通过kubectl端口转发的方式来对外提供服务,既可以通过ip:9097的方式来对外提供服务了。

1
kubectl --namespace tekton-pipelines port-forward svc/tekton-dashboard 9097:9097 --address 0.0.0.0 

执行如下命令,在k8s中提交yaml

1
kubectl apply --filename \ https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml

会在k8s中提交如下两个deployment,此时tekton即安装完毕。

1
2
3
4
$ kubectl  get deploy -n tekton-pipelines 
NAME READY UP-TO-DATE AVAILABLE AGE
tekton-pipelines-controller 1/1 1 1 4m
tekton-pipelines-webhook 1/1 1 1 4m

创建task

创建如下yaml,提交一个任务定义,提交完成后任务并不会被执行

1
2
3
4
5
6
7
8
9
10
11
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: hello
spec:
steps:
- name: echo
image: alpine
script: |
#!/bin/sh
echo "Hello World"

继续提交如下的yaml,提交一次任务运行实例

1
2
3
4
5
6
7
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: hello-task-run
spec:
taskRef:
name: hello

查看任务执行情况

1
2
3
4
5
6
7
8
$ kubectl get taskrun hello-task-run
NAME SUCCEEDED REASON STARTTIME COMPLETIONTIME
hello-task-run True Succeeded 45s 22s

# 可以看到创建出了任务执行的pod
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
hello-task-run-pod 0/1 Completed 0 2m10s

创建流水线任务pipeline

创建新的task goodye

1
2
3
4
5
6
7
8
9
10
11
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: goodbye
spec:
steps:
- name: goodbye
image: ubuntu
script: |
#!/bin/bash
echo "Goodbye World!"

提交如下yaml,创建pipeline定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: hello-goodbye
spec:
tasks:
- name: hello
taskRef:
name: hello
- name: goodbye
runAfter:
- hello
taskRef:
name: goodbye

提交如下yaml,创建pipeline实例。

1
2
3
4
5
6
7
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
name: hello-goodbye-run
spec:
pipelineRef:
name: hello-goodbye

可以使用tkn来查看pipeline拉起的pod日志,该工具会将pod的日志合并到一起。

1
2
3
4
$ tkn pipelinerun logs hello-goodbye-run -f -n default
[hello : echo] Hello World

[goodbye : goodbye] Goodbye World!

操作系统级别

/proc/meminfo

其中的Buffers和Cached的迷惑性非常大,非常难理解。Buffers是指的磁盘数据的缓存,Cached是指对文件数据的缓存.

free

该命令实际上是通过读取/proc/meminfo文件得到的如下输出

1
2
3
4
$ free -h
total used free shared buff/cache available
Mem: 30G 22G 412M 47M 7.6G 7.3G
Swap: 0B 0B 0B

具体列含义:

  • total: 总内存大小
  • used:已经使用的内存大小,包含共享内存
  • free:未使用的内存大小
  • shared:共享内存大小
  • buff/cache:缓存和缓冲区的大小
  • available:新进程可用的内存大小

vmstat

1
2
3
4
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
4 0 0 1091484 1142360 34545436 0 0 146 265 0 0 8 5 86 1 0

容器级别

通过kubectl命令来查看内存使用

1
2
3
$ kubectl top pod nginx-ingress-controller-85cd6c7b5d-md6vc
NAME CPU(cores) MEMORY(bytes)
nginx-ingress-controller-85cd6c7b5d-md6vc 22m 502Mi

通过docker stats命令来查看容器

1
2
3
$ docker stats $container_id
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
97d8bff3f89f k8s_nginx-ingress 6.33% 180.3MiB / 512MiB 35.21% 0B / 0B 0B / 0B 119

docker通过cgroup的统计数据来获取的内存值

参考文档:https://docs.docker.com/engine/reference/commandline/stats/

进程级别

具体的含义可以通过下文的/proc/$pid/status来查看,其他进程的内存含义

cat /proc/$pid/status

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
cat status
Name: nginx
Umask: 0022
State: S (sleeping)
Tgid: 46787
Ngid: 0
Pid: 46787
PPid: 33
TracerPid: 0
Uid: 1000 1000 1000 1000
Gid: 19062 19062 19062 19062
FDSize: 128
Groups:
VmPeak: 559768 kB
VmSize: 559120 kB
VmLck: 0 kB
VmPin: 0 kB
VmHWM: 285240 kB
VmRSS: 284752 kB
RssAnon: 279016 kB
RssFile: 3420 kB
RssShmem: 2316 kB
VmData: 371784 kB
VmStk: 136 kB
VmExe: 4716 kB
VmLib: 6828 kB
VmPTE: 900 kB
VmSwap: 0 kB
Threads: 33
SigQ: 1/123857
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: 0000000040001000
SigCgt: 0000000198016eef
CapInh: 0000001fffffffff
CapPrm: 0000000000000400
CapEff: 0000000000000400
CapBnd: 0000001fffffffff
CapAmb: 0000000000000000
NoNewPrivs: 0
Seccomp: 0
Speculation_Store_Bypass: vulnerable
Cpus_allowed: 0001
Cpus_allowed_list: 0
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 343464
nonvoluntary_ctxt_switches: 35061

具体字段含义可以查看man文档:https://man7.org/linux/man-pages/man5/proc.5.html,跟内存相关的字段如下:

  • VmRSS:虚拟内存驻留在物理内存中的部分大小
  • VmHWM:使用物理内存的峰值
  • VmData:进程占用的数据段大小

top命令查看

1
2
3
$ top -p $pid
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
46787 www-data 20 0 559120 286544 5740 S 1.3 0.9 2:43.64 nginx

具体列含义如下:

  • VIRT:进程使用虚拟内存总大小,即使没有占用物理内存,包括了进程代码段、数据段、共享内存、已经申请的堆内存和已经换到swap空间的内存等
  • RES: 进程实际使用的物理内存大小,不包括swap和共享内存
  • SHR:与其他进程的共享内存、加载的动态链接库、程序代码段的大小
  • MEM:进程使用的物理内存占系统总内存的百分比

ps命令查看

1
2
3
4
5
6
7
8
9
10
11
$ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
www-data 1 0.0 0.0 212 8 ? Ss Apr09 0:00 /usr/bin/dumb-init -- /nginx-ingress-controller
www-data 6 0.9 0.3 813500 99644 ? Ssl Apr09 67:32 /nginx-ingress-controller
www-data 33 0.0 0.7 458064 242252 ? S Apr09 0:53 nginx: master process /usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf
www-data 46786 0.0 0.7 459976 239996 ? S 17:57 0:00 rollback logs/eagleeye.log interval=60 adjust=600
www-data 46787 1.3 0.8 559120 284452 ? Sl 17:57 2:22 nginx: worker process
www-data 46788 1.0 0.8 558992 285168 ? Sl 17:57 1:51 nginx: worker process
www-data 46789 0.0 0.7 452012 237152 ? S 17:57 0:01 nginx: cache manager process
www-data 46790 0.0 0.8 490832 267600 ? S 17:57 0:00 nginx: x
www-data 47533 0.0 0.0 60052 1832 pts/2 R+ 20:50 0:00 ps aux
  • RSS:虚拟内存中的常驻内存,即实际占用的物理内存,包括所有已经分配的堆内存、栈内存、共享内存,由于共享内存非独占,实际上进程独占的物理内存要少于RSS

pmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# pmap -x 452021
452021: nginx: worker process
Address Kbytes RSS Dirty Mode Mapping
0000000000400000 4716 1540 0 r-x-- tengine
0000000000a9a000 20 16 4 r---- tengine
0000000000a9f000 212 196 176 rw--- tengine
0000000000ad4000 248 60 60 rw--- [ anon ]
00002b0da6cb0000 136 4 0 r-x-- ld-2.17.so
00002b0da6cd2000 4 4 4 rw--- [ anon ]
00002b0da6cd3000 4 4 4 rw-s- zero (deleted)
00002b0da6cd7000 28 24 24 rw--- [ anon ]
00002b0da6cde000 64 8 8 rwx-- [ anon ]
00002b0da6ed1000 4 4 4 r---- ld-2.17.so
00002b0da6ed2000 4 4 4 rw--- ld-2.17.so
00002b0da6ed3000 4 4 4 rw--- [ anon ]
00002b0da6ed4000 8 0 0 r-x-- libdl-2.17.so
00002b0da70d8000 92 32 0 r-x-- libpthread-2.17.so
00002b0da72f0000 16 4 4 rw--- [ anon ]
00002b0da72f4000 32 0 0 r-x-- libcrypt-2.17.so
00002b0da72fc000 2044 0 0 ----- libcrypt-2.17.so
00002b0da74fb000 4 4 4 r---- libcrypt-2.17.so
00002b0da74fc000 4 4 4 rw--- libcrypt-2.17.so
00002b0da74fd000 184 0 0 rw--- [ anon ]
00002b0da752b000 1028 8 0 r-x-- libm-2.17.so
00002b0da7a35000 760 372 0 r-x-- libssl.so.1.1
00002b0da7d03000 2812 1124 0 r-x-- libcrypto.so.1.1
00002b0da7fc2000 2044 0 0 ----- libcrypto.so.1.1
00002b0da81c1000 172 172 172 r---- libcrypto.so.1.1
00002b0da81ec000 12 12 12 rw--- libcrypto.so.1.1
00002b0da81ef000 16 8 8 rw--- [ anon ]
00002b0da81f3000 84 40 0 r-x-- libgcc_s-4.8.5-20150702.so.1
00002b0da8409000 1808 316 0 r-x-- libc-2.17.so
00002b0da85cd000 2044 0 0 ----- libc-2.17.so
00002b0da87cc000 16 16 16 r---- libc-2.17.so
00002b0da87d0000 8 8 8 rw--- libc-2.17.so
00002b0da87d2000 20 12 12 rw--- [ anon ]
00002b0da87d7000 8 0 0 r-x-- libfreebl3.so
00002b0da87d9000 2044 0 0 ----- libfreebl3.so
00002b0da89d8000 4 4 4 r---- libfreebl3.so
00002b0da89d9000 4 4 4 rw--- libfreebl3.so
00002b0da8a00000 24576 21936 21936 rw--- [ anon ]
00002b0daa200000 8192 8180 8180 rw--- [ anon ]
00002b0daaa00000 8192 8192 8192 rw--- [ anon ]
00002b0dab200000 8192 8192 8192 rw--- [ anon ]
00002b0daba00000 8192 8172 8172 rw--- [ anon ]
00002b0dac200000 4096 4096 4096 rw--- [ anon ]
00002b0dac600000 4096 4096 4096 rw--- [ anon ]
00002b0daca00000 4096 12 12 rw-s- zero (deleted)
00002b0dace00000 1024 0 0 rw-s- zero (deleted)
00002b0dacf00000 1024 0 0 rw-s- zero (deleted)
00002b0dad000000 16384 16384 16384 rw--- [ anon ]
00002b0dae000000 10240 0 0 rw-s- zero (deleted)
00002b0db3f00000 24 16 0 r-x-- cjson.so
00002b0db3f06000 2048 0 0 ----- cjson.so
00002b0db4106000 4 4 4 r---- cjson.so
00002b0db4107000 4 4 4 rw--- cjson.so
00002b0db4108000 8 0 0 r-x-- librestychash.so
00002b0db410a000 2044 0 0 ----- librestychash.so
00002b0db4309000 4 4 4 r---- librestychash.so
00002b0db430a000 4 4 4 rw--- librestychash.so
00002b0db430b000 10240 1784 1784 rw-s- zero (deleted)
00002b0db4d0b000 10240 0 0 rw-s- zero (deleted)
00002b0db5800000 6144 6144 6144 rw--- [ anon ]
00002b0db5e00000 6144 6144 6144 rw--- [ anon ]
00002b0db6400000 8192 8192 8192 rw--- [ anon ]
00002b0db6c0b000 5120 8 8 rw-s- zero (deleted)
00002b0db7200000 8192 8192 8192 rw--- [ anon ]
00002b0dc2800000 1024 0 0 rw-s- zero (deleted)
00002b0dc2900000 10240 0 0 rw-s- zero (deleted)
00002b0dc3300000 10240 0 0 rw-s- zero (deleted)
00002b0dc861f000 2048 8 8 rw--- [ anon ]
00002b0dc881f000 4 0 0 ----- [ anon ]
00002b0dc8820000 2048 8 8 rw--- [ anon ]
00007fff60f6c000 136 44 44 rw--- [ stack ]
00007fff60fdd000 8 4 0 r-x-- [ anon ]
ffffffffff600000 4 0 0 r-x-- [ anon ]
---------------- ------- ------- -------
total kB 558996 289376 285888
  • Mapping: 支持如下值
    • [anon]:分配的内存

理论

待补充

安装

下载kind命令,但不需要创建一个k8s集群。

执行如下命令下载kn 二进制文件

1
2
3
4
5
6
7
wget https://github.com/knative/client/releases/download/knative-v1.2.0/kn-linux-amd64
mv kn-linux-amd64 /usr/local/bin/kn
chmod +x /usr/local/bin/kn

# 自动补全
echo -e "\n# kn" >> ~/.bash_profile
echo 'source <(kn completion bash)' >>~/.bash_profile

下载quickstart二进制文件

1
2
3
4
5
6
7
wget https://github.com/knative-sandbox/kn-plugin-quickstart/releases/download/knative-v1.2.0/kn-quickstart-linux-amd64
mv kn-quickstart-linux-amd64 /usr/local/bin/kn-quickstart
chmod +x /usr/local/bin/kn-quickstart

# 如下两条命令可以得到相同的输出结果
kn quickstart --help
kn-quickstart --help

执行 kn quickstart kind 命令即可创建出一个knative的k8s集群。

knative serving

serving的核心功能为提供弹性扩缩容能力。

CRD

image.png
Service:用来管理整个应用的生命周期。
Route:用来将流量分发到不同的Revision
Configuration:
Revision:

kpa功能

实践

最简单service

创建hello.yaml文件,内容如下,并执行 kubectl apply -f hello.yaml。其中service的名字为hello,revision的名字为world。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: hello
spec:
template:
metadata:
# This is the name of our new "Revision," it must follow the convention {service-name}-{revision-name}
name: hello-world
spec:
containers:
- image: gcr.io/knative-samples/helloworld-go
ports:
- containerPort: 8080
env:
- name: TARGET
value: "World"

通过kn命令可以看到创建了一个service hello,并且有一个可以访问的url地址。

1
2
3
# kn service list
NAME URL LATEST AGE CONDITIONS READY REASON
hello http://hello.default.127.0.0.1.sslip.io hello-world 154m 3 OK / 3 True

knative抽象了Revision来标识该service对应的版本信息,可以使用kubectl命令,也可以使用kn命令来查看revision信息。

1
2
3
4
5
6
$ k get revisions.serving.knative.dev 
NAME CONFIG NAME K8S SERVICE NAME GENERATION READY REASON ACTUAL REPLICAS DESIRED REPLICAS
hello-world hello 1 True 0 0
$ kn revision list
NAME SERVICE TRAFFIC TAGS GENERATION AGE CONDITIONS READY REASON
hello-world hello 100% 1 6m33s 3 OK / 4 True

在宿主机上执行 curl http://hello.default.127.0.0.1.sslip.io 接口访问刚才创建的service。这里比较有意思的是为什么域名可以在宿主机上解析,该域名实际上是通过公网来解析的,域名服务器sslip.io负责该域名的解析。
本机的127.0.0.1的80端口实际是指向的是kind容器的31080端口,而31080为kourier-ingress对外暴露的服务。

1
2
# kubectl  get svc -A | grep 31080
kourier-system kourier-ingress NodePort 10.96.252.144 <none> 80:31080/TCP 3h16m

kourier-ingress为knative使用的ingress服务,该ingress并非k8s原生的ingress对象,而是自定义的ingress networking.internal.knative.dev/v1alpha1。service hello在创建的时候会同步创建一个ingress对象。在该ingress对象中可以看到刚才访问的域名hello.default.127.0.0.1.sslip.io,同时可以看到该ingress将域名指向到了k8s的service hello-world.default。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
kubectl  get  ingresses.networking.internal.knative.dev hello -o yaml 
apiVersion: networking.internal.knative.dev/v1alpha1
kind: Ingress
metadata:
annotations:
networking.internal.knative.dev/rollout: '{"configurations":[{"configurationName":"hello","percent":100,"revisions":[{"revisionName":"hello-world","percent":100}],"stepParams":{}}]}'
networking.knative.dev/ingress.class: kourier.ingress.networking.knative.dev
serving.knative.dev/creator: kubernetes-admin
serving.knative.dev/lastModifier: kubernetes-admin
finalizers:
- ingresses.networking.internal.knative.dev
generation: 1
labels:
serving.knative.dev/route: hello
serving.knative.dev/routeNamespace: default
serving.knative.dev/service: hello
name: hello
namespace: default
ownerReferences:
- apiVersion: serving.knative.dev/v1
blockOwnerDeletion: true
controller: true
kind: Route
name: hello
uid: 4c58e77b-4871-42cc-bfa0-aa9fda9646ed
spec:
httpOption: Enabled
rules:
- hosts:
- hello.default
- hello.default.svc
- hello.default.svc.cluster.local
http:
paths:
- splits:
- appendHeaders:
Knative-Serving-Namespace: default
Knative-Serving-Revision: hello-world
percent: 100
serviceName: hello-world
serviceNamespace: default
servicePort: 80
visibility: ClusterLocal
- hosts:
- hello.default.127.0.0.1.sslip.io
http:
paths:
- splits:
- appendHeaders:
Knative-Serving-Namespace: default
Knative-Serving-Revision: hello-world
percent: 100
serviceName: hello-world
serviceNamespace: default
servicePort: 80
visibility: ExternalIP
status:
conditions:
- lastTransitionTime: "2022-03-11T12:47:26Z"
status: "True"
type: LoadBalancerReady
- lastTransitionTime: "2022-03-11T12:47:26Z"
status: "True"
type: NetworkConfigured
- lastTransitionTime: "2022-03-11T12:47:26Z"
status: "True"
type: Ready
observedGeneration: 1
privateLoadBalancer:
ingress:
- domainInternal: kourier-internal.kourier-system.svc.cluster.local
publicLoadBalancer:
ingress:
- domainInternal: kourier.kourier-system.svc.cluster.local

在default namespace下可以看到有三个service,其中hello-world的ingress转发的service。

1
2
3
4
# k get svc | grep hello
hello ExternalName <none> kourier-internal.kourier-system.svc.cluster.local 80/TCP 155m
hello-world ClusterIP 10.96.58.149 <none> 80/TCP 155m
hello-world-private ClusterIP 10.96.54.163 <none> 80/TCP,9090/TCP,9091/TCP,8022/TCP,8012/TCP 155m

通过查看该serivce的yaml,并未定义serivce的selector。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

apiVersion: v1
kind: Service
metadata:
annotations:
autoscaling.knative.dev/class: kpa.autoscaling.knative.dev
serving.knative.dev/creator: kubernetes-admin
labels:
app: hello-world
networking.internal.knative.dev/serverlessservice: hello-world
networking.internal.knative.dev/serviceType: Public
serving.knative.dev/configuration: hello
serving.knative.dev/configurationGeneration: "1"
serving.knative.dev/configurationUID: 13138d0f-ee5f-4631-94a5-6928546e504c
serving.knative.dev/revision: hello-world
serving.knative.dev/revisionUID: f3aaae74-6b79-4785-b60d-5607c0ab3bcf
serving.knative.dev/service: hello
serving.knative.dev/serviceUID: 79907029-32df-4f21-b14b-ed7d24e1a10e
name: hello-world
namespace: default
ownerReferences:
- apiVersion: networking.internal.knative.dev/v1alpha1
blockOwnerDeletion: true
controller: true
kind: ServerlessService
name: hello-world
uid: 3a6326c5-32b0-4074-bec2-4d3eed293b71
spec:
clusterIP: 10.96.58.149
clusterIPs:
- 10.96.58.149
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8012
sessionAffinity: None
type: ClusterIP
status:
loadBalancer: {}

查看Service对应的Endpoint对象,可以看到Endpoint对象实际上指向到了knative-serving下的pod activator-85bd4ddcbb-6ms7n。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
apiVersion: v1
kind: Endpoints
metadata:
annotations:
autoscaling.knative.dev/class: kpa.autoscaling.knative.dev
serving.knative.dev/creator: kubernetes-admin
labels:
app: hello-world
networking.internal.knative.dev/serverlessservice: hello-world
networking.internal.knative.dev/serviceType: Public
serving.knative.dev/configuration: hello
serving.knative.dev/configurationGeneration: "1"
serving.knative.dev/configurationUID: 13138d0f-ee5f-4631-94a5-6928546e504c
serving.knative.dev/revision: hello-world
serving.knative.dev/revisionUID: f3aaae74-6b79-4785-b60d-5607c0ab3bcf
serving.knative.dev/service: hello
serving.knative.dev/serviceUID: 79907029-32df-4f21-b14b-ed7d24e1a10e
name: hello-world
namespace: default
ownerReferences:
- apiVersion: networking.internal.knative.dev/v1alpha1
blockOwnerDeletion: true
controller: true
kind: ServerlessService
name: hello-world
uid: 3a6326c5-32b0-4074-bec2-4d3eed293b71
subsets:
- addresses:
- ip: 10.244.0.5
nodeName: knative-control-plane
targetRef:
kind: Pod
name: activator-85bd4ddcbb-6ms7n
namespace: knative-serving
resourceVersion: "809"
uid: df916ac6-3161-4bc6-bf8c-47bb7c83cc4a
ports:
- name: http
port: 8012
protocol: TCP

进一步查看knative-serving下有一个knative的组件activator。

1
2
3
k get deploy -n knative-serving activator 
NAME READY UP-TO-DATE AVAILABLE AGE
activator 1/1 1 1 3h35m

打开终端,执行一下 kubectl get pod -l serving.knative.dev/service=hello -w,重新执行 curl http://hello.default.127.0.0.1.sslip.io 发起新的请求,可以看到会有pod产生,且pod为通过deployment拉起。

1
2
3
4
5
$ kubectl get pod -l serving.knative.dev/service=hello -w
hello-world-deployment-7ff4bdb7fd-rqg96 0/2 Pending 0 0s
hello-world-deployment-7ff4bdb7fd-rqg96 0/2 ContainerCreating 0 0s
hello-world-deployment-7ff4bdb7fd-rqg96 1/2 Running 0 1s
hello-world-deployment-7ff4bdb7fd-rqg96 2/2 Running 0 1s

过一段时间没有新的请求后,pod会自动被删除,同时可以看到deployment的副本数缩成0。该namespace下并没有对应的hpa产生,说明deployment副本数的调整并非使用k8s原生的hpa机制。

1
2
3
4
5
hello-world-deployment-7ff4bdb7fd-rqg96   2/2     Terminating         0          63s
hello-world-deployment-7ff4bdb7fd-rqg96 0/2 Terminating 0 93s
$ k get deployments.apps
NAME READY UP-TO-DATE AVAILABLE AGE
hello-world-deployment 0/0 0 0 21h

service的流量切分

重新提交如下的yaml文件,将service对应的revision更新为knative。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: hello
spec:
template:
metadata:
name: hello-knative
spec:
containers:
- image: gcr.io/knative-samples/helloworld-go
ports:
- containerPort: 8080
env:
- name: TARGET
value: "Knative"

重新查看revision,可以看到revision已经变更为了knative,同时可以看到老的revision world并没有被删除,只是没有了流量转发。

1
2
3
4
$ kn revision list
NAME SERVICE TRAFFIC TAGS GENERATION AGE CONDITIONS READY REASON
hello-knative hello 100% 2 49s 4 OK / 4 True
hello-world hello 1 10m 3 OK / 4 True

重新apply新的Service,将流量切分为hello-world和hello-knative两份,重新执行curl请求,可以看到结果会随机返回。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: hello
spec:
template:
metadata:
name: hello-knative
spec:
containers:
- image: gcr.io/knative-samples/helloworld-go
ports:
- containerPort: 8080
env:
- name: TARGET
value: "Knative"
traffic:
- latestRevision: true
percent: 50
- revisionName: hello-world
percent: 50

查看revision的信息,可以看到流量已经是50%的切分了。

1
2
3
4
# kn revision list              
NAME SERVICE TRAFFIC TAGS GENERATION AGE CONDITIONS READY REASON
hello-knative hello 50% 2 11m 3 OK / 4 True
hello-world hello 50% 1 21m 3 OK / 4 True

knative eventing

image.png
Source:k8s的CR对象,产生Event
Broker:用来分发Event
Trigger:Event触发器
Sink:Event输出结果

其中包含业务逻辑的可编程的部分在于Trigger部分,由于trigger实际上是无状态的服务,对于一些有状态的消息knative很难满足。比如同一类型的特定字段的event转发到特定的trigger上,broker实际上不具备可编程性,因此无法完成。

kn quickstart 命令会安装一个broker到环境中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
$ k get brokers.eventing.knative.dev example-broker -o yaml 
apiVersion: eventing.knative.dev/v1
kind: Broker
metadata:
annotations:
eventing.knative.dev/broker.class: MTChannelBasedBroker
eventing.knative.dev/creator: kubernetes-admin
eventing.knative.dev/lastModifier: kubernetes-admin
name: example-broker
namespace: default
spec:
config:
apiVersion: v1
kind: ConfigMap
name: config-br-default-channel
namespace: knative-eventing
delivery:
backoffDelay: PT0.2S
backoffPolicy: exponential
retry: 10
status:
address:
url: http://broker-ingress.knative-eventing.svc.cluster.local/default/example-broker
annotations:
knative.dev/channelAPIVersion: messaging.knative.dev/v1
knative.dev/channelAddress: http://example-broker-kne-trigger-kn-channel.default.svc.cluster.local
knative.dev/channelKind: InMemoryChannel
knative.dev/channelName: example-broker-kne-trigger
conditions:
- lastTransitionTime: "2022-03-11T12:32:43Z"
status: "True"
type: Addressable
- lastTransitionTime: "2022-03-11T12:32:43Z"
message: No dead letter sink is configured.
reason: DeadLetterSinkNotConfigured
severity: Info
status: "True"
type: DeadLetterSinkResolved
- lastTransitionTime: "2022-03-11T12:32:43Z"
status: "True"
type: FilterReady
- lastTransitionTime: "2022-03-11T12:32:43Z"
status: "True"
type: IngressReady
- lastTransitionTime: "2022-03-11T12:32:43Z"
status: "True"
type: Ready
- lastTransitionTime: "2022-03-11T12:32:43Z"
status: "True"
type: TriggerChannelReady
observedGeneration: 1

快速开始

创建如下的Service,该service通过环境变量 BROKER_URL 作为broker地址,可以看到其地址为 quickstart工具默认安装的example-broker。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: cloudevents-player
spec:
template:
metadata:
annotations:
autoscaling.knative.dev/min-scale: "1"
spec:
containers:
- image: ruromero/cloudevents-player:latest
env:
- name: BROKER_URL
value: http://broker-ingress.knative-eventing.svc.cluster.local/default/example-broker

访问service页面 http://cloudevents-player.default.127.0.0.1.sslip.io/ 可以在界面上创建Event后,产生如下的Event内容,格式完全遵循社区的CloudEvent规范。点击发送,即可以将消息发送给broker,但由于borker没有配置任何的Trigger,消息在发送到broker后会被直接丢弃。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
"root": {
"attributes": {
"datacontenttype": "application/json",
"id": "123",
"mediaType": "application/json",
"source": "manual",
"specversion": "1.0",
"type": "test-type"
},
"data": {
"message": "Hello CloudEvents!"
},
"extensions": {}
}
}

我们继续来给broker增加触发器,创建如下的yaml。该触发器定义了broker为example-broker,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
name: cloudevents-trigger
annotations:
knative-eventing-injection: enabled
spec:
broker: example-broker
subscriber:
ref:
apiVersion: serving.knative.dev/v1
kind: Service
name: cloudevents-player

重新在页面上发送event,可以看到消息的状态为接收。

参考

内核参数项

以CentOS7 系统为例,可以看到有1088个内核参数项。

1
2
3
4
$ uname -a
Linux iZbp17o12gcsq2d87y7x1hZ 3.10.0-1160.36.2.el7.x86_64 #1 SMP Wed Jul 21 11:57:15 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
$ sysctl -a 2>/dev/null | wc -l
1088

Linux的内核参数均位于/proc/sys目录下,涉及到如下几个目录:

分类 描述
abi
crypto
debug
dev 用来配置特定设备,比如raid、scsi设备
fs 文件子系统
kernel 内核子系统
net 网络子系统
user
vm 内存子系统

kernel子系统

参数 描述
kernel.panic 内核出现panic,重新引导前需要等待的时间,单位为秒。如果该值为0,,说明内核禁止自动引导
kernel.core_pattern core文件的存放路径

vm子系统

参数 描述
vm.min_free_kbytes 系统所保留空闲内存的最小值。该值通过公式计算,跟当前机器的物理内存相关。
vm.swappiness 用来控制虚拟内存,支持如下值:
0:关闭虚拟内存
1:允许开启虚拟内存设置的最小值
10:剩余内存少于10%时开启虚拟内存
100:完全适用虚拟内存。

该参数可以通过swapon 命令开启,swapoff关闭。
参考链接:https://linuxhint.com/understanding_vm_swappiness/

net子系统

参数 描述
net.ipv4.ip_local_reserved_ports 随机端口的黑名单列表,系统在发起连接时,不使用该内核参数内的端口号
net.ipv4.ip_local_port_range 随机端口的白名单范围,网络连接可以作为源端口的最小和最大端口限制
net.ipv4.rp_filter 是否开启对数据包源地址的校验, 收到包后根据source ip到route表中检查是否否和最佳路由,否的话扔掉这个包。这次如下值:
1. 不开启源地址校验
2. 开启严格的反向路径校验。对每个进来的数据包,校验其反向路径是否是最佳路径。如果反向路径不是最佳路径,则直接丢弃该数据包。
3. 开启松散的反向路径校验。对每个进来的数据包,校验其源地址是否可达,即反向路径是否能通(通过任意网口),如果反向路径不通,则直接丢弃该数据包。该内核参数 net.ipv4.conf.all.log_martians 可以来控制是否打开日志,日志打开后可以在/var/log/message中观察到。

TCP

tcp 相关内核参数可以使用 man 7 tcp 查看。

建连相关

相关文章:Linux TCP backlog

syn 队列

syn 队列又称为半连接队列。服务端在接收到客户端的 SYN 包后,服务端向客户端发送 SYN + ACK 报文,此时会进入到半连接队列。

  1. net.ipv4.tcp_max_syn_backlog
1
2
tcp_max_syn_backlog (integer; default: see below; since Linux 2.2)
The maximum number of queued connection requests which have still not received an acknowledgement from the connecting client. If this number is exceeded, the kernel will begin dropping requests. The default value of 256 is increased to 1024 when the memory present in the system is adequate or greater (>= 128Mb), and reduced to 128 for those systems with very low memory (<= 32Mb). It is recommended that if this needs to be increased above 1024, TCP_SYNQ_HSIZE in include/net/tcp.h be modified to keep TCP_SYNQ_HSIZE*16<=tcp_max_syn_backlog, and the kernel be recompiled.

用来设置 syn 队列的大小,通常也会称为半连接队列。该参数的默认值一般为 1024。如果 syn 队列满,此时 syn 报文会被丢弃,无法回复 syn + ack 报文。可以通过 netstat -s 命令看到 “XX SYNs to LISTEN sockets dropped”. 的报错信息。

  1. net.ipv4.tcp_syncookies
1
tcp_syncookies (Boolean; since Linux 2.2) Enable TCP syncookies. The kernel must be compiled with CONFIG_SYN_COOKIES.  Send out syncookies when the syn backlog queue of a socket overflows.  The syncookies feature attempts to protect a socket from a SYN flood attack.  This should be used as a last resort, if at all.  This is a violation of the  TCP  protocol,  and  con‐ flicts  with other areas of TCP such as TCP extensions.  It can cause problems for clients and relays.  It is not recommended as a tuning mechanism for heavily loaded servers to help with overloaded or misconfigured conditions.  For recommended alternatives see tcp_max_syn_backlog, tcp_synack_retries, and tcp_abort_on_overflow.

因为 syn 队列的存在,当客户端一直在发送 syn 包,但是不回 ack 报文时,一旦服务端的队列超过 net.ipv4.tcp_max_syn_backlog 设置的大小就会存在队列溢出的问题,从而导致服务端无法响应客户端的请求,这就是 syn flood 攻击。

为了防止 syn flood 攻击,引入了 syn cookies 机制,该机制并非 tcp 协议的一部分。原理参见:深入浅出TCP中的SYN-Cookies

一旦开启了 syn cookies 机制后,即使 syn 队列满,仍可以对新建的连接回复 syn + ack 报文,但是不需要进入队列。

因为 syn cookies 存在部分缺陷,只有当 syn 队列满时该特性才会生效。

  1. net.ipv4.tcp_abort_on_overflow

在三次握手完成后,该连接会进入到 ESTABLISHED 状态,并将该连接放入到用户程序队列中。若该队列已满,默认会将该连接重新设置为 SYN_ACK 状态,相当于是服务端没有接收到客户端的 syn + ack 报文,后续可以利用客户端的重传机制重新接收报文。

一旦开启了 net.ipv4.tcp_abort_on_overflow 选项后,会直接发送 RST 报文给到客户端,客户端会终止该连接,并报错 104 Connection reset by peer

断开连接相关

TCP TIME_WAIT

内核参数在k8s的支持情况

大类 子类 备注
namespace内核参数 安全的内核参数 k8s默认支持的内核参数非常少,仅支持如下的内核参数:
1. kernel.shm_rmid_forced
2. net.ipv4.ip_local_port_range
3. net.ipv4.tcp_syncookies(在内核4.4之前为非namespace化)
4. net.ipv4.ping_group_range (从 Kubernetes 1.18 开始)
5. net.ipv4.ip_unprivileged_port_start (从 Kubernetes 1.22 开始)
namespace内核参数 非安全内核参数 默认禁用,pod可以调度成功,但会报错SysctlForbidden。修改kubelet参数开启 kubelet --allowed-unsafe-sysctls 'kernel.msg*,net.core.somaxconn'
非namespace内核参数 在容器中没有的内核参数 如:
net.core.netdev_max_backlog = 10000
net.core.rmem_max = 2097152
net.core.wmem_max = 2097152
非namespace隔离参数 直接修改宿主机的内核参数 在容器中需要开启特权容器来设置,如:
vm.overcommit_memory = 2
vm.overcommit_ratio = 95

操作系统的namespace化的内核参数仅支持:

  • kernel.shm*,
  • kernel.msg*,
  • kernel.sem,
  • fs.mqueue.*,
  • net.*(内核中可以在容器命名空间里被更改的网络配置项相关参数)。然而也有一些特例 (例如,net.netfilter.nf_conntrack_max 和 net.netfilter.nf_conntrack_expect_max 可以在容器命名空间里被更改,但它们是非命名空间的)。

k8s在pod中声明内核参数的方式如下:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: sysctl-example
spec:
securityContext:
sysctls:
- name:
kernel.shm_rmid_forced
value: "0"

业界解决方案

ACK - 安全沙箱容器

阿里云ACK服务的安全沙箱容器,底层实现为runV,pod拥有独立的内核参数,相互之间不受影响。通过扩展pod的annotation来完成内核参数的修改:

1
2
annotations:
securecontainer.alibabacloud.com/sysctls: "net.bridge.bridge-nf-call-ip6tables=1,net.bridge.bridge-nf-call-iptables=1,net.ipv4.ip_forward=1"

阿里云ACK - 配置安全沙箱Pod内核参数

弹性容器实例

完全利用k8s的功能,有限内核参数修改。
https://help.aliyun.com/document_detail/163023.html

参考文档

在RHEL7系统中,firewalld和iptabels两个防火墙的服务并存,firewalld通过调用iptables命令来实现,firewalld和iptables均用来维护系统的netfilter规则,底层实现为内核的netfilter模块。

firewalld为systemd项目的一部分,为python语言实现,跟iptables相比,使用上更加人性化,提供了更高层次的抽象,用户不用关心netfilter的链和表的概念。

架构

最上层为用户界面层,提供了firewall-cmd和firewall-offline-cmd两个命令行工具,其中firewall-cmd为最主要的命令行工具。firewall-config为GUI工具。用户界面层通过firewalld提供的D-Bus接口进行通讯。

firewalld为daemon进程,其中zone、service、ipset等为firewalld抽象的概念。在接收到用户界面层的命令后,一方面需要将操作保存到本地文件,另外还需要调用更底层的如iptables、ipset、ebtables等命令来产生规则,最终在内核层的netfilter模块生效。

firewalld的管理

在RHEL7系统下,firewalld作为systemd家族的一员会默认安装。其service文件定义如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ cat /usr/lib/systemd/system/firewalld.service 
[Unit]
Description=firewalld - dynamic firewall daemon
Before=network-pre.target
Wants=network-pre.target
After=dbus.service
After=polkit.service
Conflicts=iptables.service ip6tables.service ebtables.service ipset.service
Documentation=man:firewalld(1)

[Service]
EnvironmentFile=-/etc/sysconfig/firewalld
ExecStart=/usr/sbin/firewalld --nofork --nopid $FIREWALLD_ARGS
ExecReload=/bin/kill -HUP $MAINPID
# supress to log debug and error output also to /var/log/messages
StandardOutput=null
StandardError=null
Type=dbus
BusName=org.fedoraproject.FirewallD1
KillMode=mixed

[Install]
WantedBy=multi-user.target
Alias=dbus-org.fedoraproject.FirewallD1.service

查看firewall-cmd的运行状态

1
2
$ firewall-cmd --state
not running

在安装完firewalld后,会在/usr/lib/firewalld/目录下产生默认的配置,该部分配置不可修改。同时会在/etc/firewalld目录下产生firewalld的配置,该部分配置会覆盖/usr/lib/firewalld/下的配置。/etc/firewalld目录下的文件内容如下,其中firewalld.conf文件为最主要的配置文件。

1
2
$ ls 
firewalld.conf helpers icmptypes ipsets lockdown-whitelist.xml services zones

概念

firewalld抽象了zone、service、ipset、helper、icmptypes几个概念。

zone

firewalld 将网络按照安全等级划分了不同的zone,zone的定义位于/usr/lib/firewalld/zones/目录下,文件格式为xml,包括了如下的zone。

  1. drop:只允许出向,任何入向的网络数据包被丢弃,不会回复icmp报文。
  2. block:任何入向的网络数据包均会被拒绝,会回复icmp报文。
  3. public:公共区域网络流量。不信任网络上的流量,选择接收入向的网络流量。
  4. external:不信任网络上的流量,选择接收入向的网络流量。
  5. DMZ:隔离区域,内网和外网增加的一层网络,起到缓冲作用。选择接收入向的网络连接。
  6. work:办公网络,信任网络流量,选择接收入向的网络流量。
  7. home:家庭网络,信任网络流量,选择接收入向的网络流量。
  8. internal:内部网络,信任网络流量,选择接收入向的网络流量。
  9. trusted:信任区域。所有网络连接可接受。

firewalld的默认zone为public,zone目录下的public.xml的内容如下:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>Public</short>
<description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
<service name="ssh"/>
<service name="dhcpv6-client"/>
<service name="cockpit"/>
</zone>

可以看到Public zone关联了三个service对象 ssh、dhcpv6-client、cockpit。没有匹配到该zone的流量,默认情况下会被拒绝。

在配置文件/etc/firewalld/firewalld.conf中,通过DefaultZone字段指定的默认zone为public。即如果开启了firewalld规则,那么默认仅会放行访问上述三个服务的流量。执行 iptables-save 命令实际上并为看到任何的iptables规则,说明firewalld是直接调用内核的netfilter来实现的。

service

service为firewalld对运行在宿主机上的进程的抽象,并在zone文件中跟zone进行绑定。比如ssh.xml的内容如下:

1
2
3
4
5
6
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>SSH</short>
<description>Secure Shell (SSH) is a protocol for logging into and executing commands on remote machines. It provides secure encrypted communications. If you plan on accessing your machine remotely via SSH over a firewalled interface, enable this option. You need the openssh-server package installed for this option to be useful.</description>
<port protocol="tcp" port="22"/>
</service>

实践

向某个zone内增加端口号

执行 firewall-cmd --permanent --zone=public --add-port=80/tcp 即可向public zone内增加80端口号。同时可以在 /etc/firewalld/zones/public.xml 文件中看到新增加了80端口号。

1
2
3
4
5
6
7
8
9
10
$ cat public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>Public</short>
<description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
<service name="ssh"/>
<service name="dhcpv6-client"/>
<service name="cockpit"/>
<port port="80" protocol="tcp"/>
</zone>

参数 --permanent 为固化到文件中,如果不增加该参数重启firewalld进程后配置会失效。

获取当前zone信息

1
2
3
$ firewall-cmd --get-active-zones
public
interfaces: eth0

每个网络设备可以属于不同的zone,可以根据网络设备名来查询所属的zone

1
2
$ firewall-cmd --get-zone-of-interface=eth0
public

设置当前默认zone

该命令会自动修改/etc/firewalld/firewalld.conf配置文件中的DefaultZone字段。

1
$ firewall-cmd --set-default-zone=trusted

查看当前配置规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ firewall-cmd --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client ssh
ports: 80/tcp
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:

相关链接

0%