404频道

学习笔记

快速开始

部署

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 查看。

建连相关

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

相关文章:Linux TCP backlog

断开连接相关

TCP TIME_WAIT

文件子系统

fs.mount-max

The value in this file specifies the maximum number of mounts that may exist in a mount namespace. The default value in this file is 100,000.

Linux 4.19 内核引入。当 mount namespace 中加载的文件数超过该值后,会报错 “No space left on device”。

内核参数在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:

相关链接

audit简介

audit为linux内核安全体系的重要组成部分,用来记录内核的系统调用,文件修改等事件,用于审计目的。

  • auditctl: 面向用户的工具,类似于iptables命令
  • auditd: 负责将审计信息写入到/var/

启动auditd服务

auditd作为单独的服务运行在系统上,Redhat系统使用systemctl start auditd启动服务,启动后通过 ps -ef | grep auditd查看进程是否启动成功。

auditctl

查看auditd的运行状态

1
2
3
4
5
6
7
8
9
$ auditctl -s
enabled 1
failure 1
pid 638
rate_limit 0
backlog_limit 8192
lost 0
backlog 0
loginuid_immutable 0 unlocked

查看当前环境规则

1
2
3
4
5
$ auditctl -l
-w /tmp/hosts -p rwxa
-w /proc/sys/net/ipv4/tcp_retries1 -p rwxa
-w /proc/sys/net/ipv4/tcp_retries2 -p rwxa
-w /proc/sys/net/ipv4/tcp_retries2 -p wa

删除所有的audit规则

1
2
$ auditctl -D
No rules

实践

监控文件变化

  1. 执行 auditctl -w $file -p wa 来监控文件,比如监控内核参数 auditctl -w /proc/sys/net/ipv4/tcp_retries2 -p wa,其中-p指定了监控文件的行为,支持rwxa。
  2. 查看文件 cat /proc/sys/net/ipv4/tcp_retries2。
  3. 使用vim打开文件 vim /proc/sys/net/ipv4/tcp_retries2。
  4. 执行 ausearch -f /proc/sys/net/ipv4/tcp_retries2 命令查看,可以看到如下的日志
1
2
3
4
5
6
7
8
$ ausearch -f /proc/sys/net/ipv4/tcp_retries1
----
time->Mon Mar 28 12:44:48 2022
type=PROCTITLE msg=audit(1648442688.159:6232591): proctitle=76696D002F70726F632F7379732F6E65742F697076342F7463705F7265747269657331
type=PATH msg=audit(1648442688.159:6232591): item=1 name="/proc/sys/net/ipv4/tcp_retries1" inode=46629229 dev=00:03 mode=0100644 ouid=0 ogid=0 rdev=00:00 objtype=NORMAL cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=PATH msg=audit(1648442688.159:6232591): item=0 name="/proc/sys/net/ipv4/" inode=8588 dev=00:03 mode=040555 ouid=0 ogid=0 rdev=00:00 objtype=PARENT cap_fp=0000000000000000 cap_fi=0000000000000000 cap_fe=0 cap_fver=0
type=CWD msg=audit(1648442688.159:6232591): cwd="/root"
type=SYSCALL msg=audit(1648442688.159:6232591): arch=c000003e syscall=2 success=yes exit=3 a0=11687a0 a1=241 a2=1a4 a3=7ffe33dc14e0 items=2 ppid=8375 pid=8629 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts1 ses=250225 comm="vim" exe="/usr/bin/vim" key=(null)

监控文件夹变化

监控文件夹同样采用跟上述文件相同的方式,但有个问题是如果文件夹下内容较多,会一起监控,从而导致audit的log内容过多。

监控系统定期reboot

执行如下命令:

1
2
auditctl -w /bin/systemctl -p rwxa -k systemd_call
auditctl -a always,exit -F arch=b64 -S reboot -k reboot_call

待系统重启后执行如下命令:

1
ausearch -f reboot

参考文档

RedHat auditd文档

curl

  • –local-port:指定源端口号
  • –proxy:指定本地代理,例如:http://127.0.0.1:52114
  • -d:指定body,如果body比较小,可以直接指定-d 'login=emma&password=123',也可以通过指定文件的方式 -d '@data.txt'

history

bash会将历史命令记录到文件.bash_history中,通过history命令可以查看到历史执行的命令。但history在默认情况下,仅会显示命令,不会展示出执行命令的时间。history命令可以根据环境变量HISTTIMEFORMAT来显示时间,要想显示时间可以执行如下的命令:

1
HISTTIMEFORMAT='%F %T ' history

lrzsz

CentOS rpm包地址:https://rpmfind.net/linux/centos/7.9.2009/os/x86_64/Packages/lrzsz-0.12.20-36.el7.x86_64.rpm

ps

最常用的为ps -efps aux命令,两者的输出结果差不多,其中ps -ef为System V Style风格,ps aux为BSD风格,现在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 100456 ? Ssl Apr09 67:20 /nginx-ingress-controller --publish-service
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.4 0.8 559120 283328 ? Sl 17:57 2:07 nginx: worker process
www-data 46788 1.1 0.8 558992 284772 ? Sl 17:57 1:38 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 47357 0.0 0.0 60052 1832 pts/2 R+ 20:21 0:00 ps aux

每个列的值如下:

  • %MEM:占用内存百分比
  • VSZ: 进程使用的虚拟内存量(KB)
  • RSS:进程占用的固定内存量,驻留在页中的(KB)
  • STAT:进程的状态
  • TIME:进程实际使用的cpu运行时间

pssh

该工具的定位是在多台主机上批量执行pssh命令。

  1. 将文件存放到文件中 /tmp/hosts 中,文件格式如下:
    1
    2
    192.168.1.1
    192.168.1.2
  2. 批量执行shell命令:pssh -h /tmp/hosts -A -i ‘uptime’。

具体参数说明如下:

  • -A: 手工输入密码模式,如果未打通ssh免密,可以在执行pssh命令的时候手工输入主机密码,但要求所有主机密码必须保持一致

rsync

常用命令:

  1. rsync -avz -e 'ssh -p 50023' ~/git/arkctl root@100.67.27.224:/tm

常用参数:

  • --delete: 本地文件删除时,同步删除远程文件
  • -e 'ssh -p 50023': 指定 ssh 端口号
  • --exclude=.git: 忽略同步本地的 git 目录

scp

  • -P:指定端口号

strace

跟踪进程的系统调用

  • -p:指定进程
  • -s:指定输出的字符串的最大大小
  • -f:跟踪由fork调用产生的子进程

wget

  • -P: 当下载文件时,可以指定本地的下载的目录

split

split [-bl] file [prefix]

  • -b, –bytes=SIZE:对file进行切分,每个小文件大小为SIZE。可以指定单位b,k,m。

  • -C,–bytes=SIZE:与-b选项类似,但是,切割时尽量维持每行的完整性。

  • prefix:分割后产生的文件名前缀。

拆分文件:split -b 200m ip_config.gzip ip_config.gzip

文件合并:cat ip_config.gzip* > ip_config.gzip

python

快速开启一个http server python -m SimpleHTTPServer 8080

在python3环境下该命令变更为:python3 -m http.server 8080

格式化 json: cat 1.json | python -m json.tool

awk

按照,打印出一每一列 awk -F, '{for(i=1;i<=NF;i++){print $i;}}'

docker registry

  • 列出镜像:curl http://127.0.0.1:5000/v2/_catalog?n=1000
  • 查询镜像的tag: curl http://127.0.0.1:5000/v2/nginx/tags/list,如果遇到镜像名类似aa/bb的情况,需要转移一下 curl http://127.0.0.1:5000/v2/aa\/bb/tags/list

socat

  • 向本地的 socket 文件发送数据:echo "test" | socat - unix-connect:/tmp/unix.sock
  • 通过交互的方式输入命令:socat - UNIX-CONNECT:/var/lib/kubelet/pod-resources/kubelet.sock

git

  • 删除远程分支 git push origin --delete xxx
  • 强制更新远程分支:git push --force-with-lease origin feature/statefulset
  • 删除本地分支:git branch -D local-branch
  • 拉取远程分支并切换分支:git checkout -b develop origin/remote-branch develop为本地分支,origin/remote-branch为远程分支
  • 给 Github 设置代理
    1
    2
    3
    4
    5
    # 设置代理
    git config --global http.https://github.com.proxy socks5://127.0.0.1:13659

    # 取消代理
    git config --global --unset http.https://github.com.proxy

rpm

  • rpm -ivh xx.rpm:用来安装一个 rpm 包
  • rpm -qa:查看已经安装的包
  • rpm -ql: 查看已经安装的 rpm 的文件内容
  • rpm -qpR *.rpm: 查看rpm包的依赖
  • rpm -e *:要删除的rpm包

cloc

用来统计代码行数

  • cloc .: 用来统计当前的代码行数
  • cloc . --exclude-dir vendor:忽略目录 vendor,--exclude-dir 仅能支持一级目录
  • cloc . --fullpath --not-match-d=pkg/apis/:用来忽略目录 pkg/apis 下的文件

jq

用来解析 json 格式

key 中包含特殊字符,假设文件 data.json 格式如下:

1
2
3
4
5
{
"data": {
"a.json": "abc"
}
}

可以使用 cat data.json | jq '.data."a.json"' 或者 cat data.json | jq '.data["a.json"]' 的方式来解析其中的内容。

-r 参数:
echo '"{}"' | jq . 输出结果为 "{}" 即对应的格式为字符串。echo -r '"{}"' | jq -r . 输出结果为 {},已经解析为标准的 json 格式。

题图为周末的公园露营区。一周前曾经下过一场雪,地上覆盖着厚厚的一层雪,而一星期过后,上面却扎满了帐篷。城市里的人们,在捂了一个冬季后,终于迎来了阳光明媚的春天。虽内心充满着诗和远方,疫情之下,能约上三五好友,在草地上吃上一顿野餐,亦或在帐篷里美美的睡上一觉已是一件很奢侈的事情。

资源

1. Submariner

Rancher开源的一款k8s集群之间的容器网络打通的工具。k8s社区的网络插件中以overlay的网络插件居多,因为overlay的网络对底层物理网络几乎很少有依赖,通常会采用vxlan、IPIP等协议来实现。虽然overlay的网络插件用起来比较方便,但是两个k8s集群的容器网络通常是无法直接通讯,在多k8s集群的应用场景下比较受限。Submariner提供了容器网络的互通方案。

2. k8e

k8e为Kubernetes Easy的简写。社区里有k3s和k0s项目来提供了k8s精简版,本项目在k3s的基础之上又进一步进行了裁剪,移除了一些边缘场景的特性。

3. Rancher Desktop

k8s的发行版SUSE Rancher提供的k8s的桌面客户端,目前已经发布了1.0.0版本。

4. nginx config

Nginx作为最流行的负载均衡配置软件之一,有自己的一套配置语法。DigitalOcean提供的nginx config工具可以通过UI直接进行配置,并最终可以一键生成nginx的配置文件。

5. mizu

一款部署在k8s上的流量分析工具,可以认为是k8s版的tcpdump + wireshark。底层的实现也是基于libpcap抓包的方式,可以支持解析HTTP、Redis、Kafka等协议。

6. kube-bench

互联网安全中心(Center for Internet Security)针对k8s版本提供了一套安全检查的规范,约有200多页的pdf文档,本项目为针对该规范的实现。仅需要向k8s环境中提交一个job,即可得到最终的安全结果。很多公有云厂商也有自己的实现,比如阿里云ACK的实现

7. virtual-kubelet

virtual kubelet服务通过在k8s集群上创建虚拟node,当一个pod调度到虚拟node时,virtual kubelet组件以插件的形式提供了不同的实现,可以将pod创建在k8s集群之外。比如,在阿里云的场景下,可以将pod创建到弹性容器实例ECI上面,从而达到弹性的目的。该项目除了用于公有云一些弹性的场景外,还常用于边缘计算的场景。

8. cloudevents

CloudEvents定义了一种通用的方式描述事件数据的规范,由CNCF的Serverless工作组提出。阿里云的事件总线EventBridge基于此规范提供了比较好商业化产品。

相关链接:EventBridge 事件总线及 EDA 架构解析

9. kubectl-who-can

在k8s系统中,通常会通过RBAC的机制来配置某个账号拥有某种权限,但如果反过来要查询某个权限被哪些账号所拥有,就会麻烦很多。

该工具是一个k8s的命令行小工具,可以用来解决上述需求。比如查询拥有创建namespace权限的ServiceAccount有哪些,可以直接执行 kubectl-who-can create namespace

10. kyverno

Kyverno是一款基于k8s的策略引擎工具,通过抽象CRD ClusterPolicy的方式来声明策略,在运行时通过webhook的技术来执行策略。相比于opa & gatekeeper,更加k8s化,但却没有编程语言的灵活性。目前该项目为CNCF的孵化项目。

文章

1. 进击的Kubernetes调度系统

该系列一共三篇文档,分别讲解了如下内容:

  • 第一篇:k8s 1.16版本引入的Scheduling Framework
  • 第二篇:阿里云ACK服务基于Scheduling Framework实现的Gang scheduling
  • 第三篇:阿里云ACK服务基于Scheduling Framework实现的支持批任务的Binpack Scheduling

2. 中国的云计算革命尚未开始

作者通过工业革命时代的电气化道路做类似,认为当前云计算的阶段仍然比较初级,并且首先要解决的是人的问题,而不是技术本身。

阿里云上有众多的云产品,本文主要分析云产品的功能以及应用场景。

容器与中间件

该部分领域与我的工作重合度较高,为重点研究领域。

事件总线EventBridge

很多服务会产生输出供下游的服务来消费,最常见的解决办法是数据生产者通过rpc调用的方式将消息发送给数据消费者。但假如消费者的数量不止一处,该模式下就需要数据生产者将数据重复发送给多个消费者,该方式对于生产者可配置的灵活性要求非常高。

为了解决上述问题,将生产者和消费者解耦,解决办法就是引入一个中间层,这也是软件架构中最常见的解决复杂问题的方法。引入的中间层即为消息队列类的服务,比如Kafka。生成者仅负责生产数据到消息队列,消费者负责从消息队列消费数据,且可以存在多个消费者。生成者和消费者根本不用关心彼此,仅需要跟消息队列进行交互就可以了。

阿里云上的某个产品新增加了一个新的操作,比如ECS主机完成了一次快照操作,都会产生事件。如果有服务要消费事件,可以使用该产品。

阿里云上的很多产品会将新产生的事件发送到事件总线,另外也支持自定义事件源,通过编程的方式将事件推送到事件源上。

数据的消费模块跟通常的消息队列有所不同,这里的数据消费需要由事件总线产品主动推送消息到对应的服务,具体要推送到哪些服务,则需要在创建总线的时候配置,该功能即消息路由。支持的消费端包括钉钉、消息队列、Serverless服务、HTTP Server等。可以看到数据的消费端除了HTTP Server外,基本不需要额外开发一个服务,也是阿里云的一些其他云产品。

消息的规范完成遵循云原生社区CNCF的CloudEvent规则。

相关链接:

弹性容器实例ECI

该产品的功能比较简单,相当于提供了管控页面来创建k8s的pod,具体pod部署在哪里用户不需要关心,提供了非常好的弹性能力,充分发挥了云的优势。

具体在实现层面,实际上会以pod的形式部署在阿里云维护的公共k8s集群中,且容器的网络在用户指定的vpc中。

除了给用户提供直接创建容器实例外,还有很大一部分功能是给Serverless Kubernetes(ASK)和容器服务(ACK)来提供弹性扩缩容的功能。

企业级分布式应用服务EDAS

提供了应用托管和微服务的治理能力。

在应用托管方面支持应用发布到虚拟机和k8s两种方式。

微服务治理方面支持了Spring Cloud、Dubbo、HSF三种微服务框架。

Serverless应用引擎SAE

提供了类似于knative的serving功能,可以支持应用的托管,用户不需要关心底层的服务器资源,可以自动将用户的应用部署在托管的k8s集群中,且具备秒级的弹性扩缩容的功能。

Serverless工作流SWF

软件工程

康威定律

任何一个组织在设计一个系统的时候,这个系统的结构与这个组织的沟通结构是一致的。
工作了这么些年对此深有感触,即“组织架构决定软件架构”。

布鲁克定律

在一个已经延期的项目中增加人手只会让项目延期更长。
我个人不是特别认可此定律,该定律肯定是项目而定的,这要看项目的协作复杂程度,如果是体力劳动居多的项目,那么堆人还是特别好使的。

帕金森定律

一项工作会占用掉所有用来完成它的时间。即如果不给一个项目设置截止日期,那么该项目就永远完成不了。安排多少时间,就会有多少工作。

冰山谬论

一款新软件的开发成本只占管理层预算的总成本的25%左右。

其他

黄金圈法则

著名的营销顾问西蒙斯.涅克提出了一个“黄金圈”理论:三个同心圆,最里面的一个是Why,中间一层是How,最外面一层是What。

大多数人的思维方式是想做什么(what)和怎么做(how),不太考虑为什么这么做(why)。

本理论提倡的思维方式为:

  1. Why:最内层——为什么,做一件事的原因或目的,也可以说是理念和宗旨,属于战略层面;
  2. How:中间层——怎么做,针对这个目的或理念的计划,也即如何去做好这件事情,属于战术层面;
  3. What:最外层——是什么,最终得到什么,或者要做哪些具体的事,这基本是事情的表象,主要是执行层面的东西。

该法则在软件行业的述职晋升等场景下非常适用。

  1. Why:描述为什么做这个项目?
  2. How:做这个项目遇到的挑战有哪些,是怎么解决的。挑战和解决方法可以一一对应起来。
  3. What:项目的最终结果,最好有具体的可以量化的指标。

SWOT分析法

常见的战略分析方法,对研究对象进行全面、系统、准确的研究。

金字塔原理

参见《金字塔原理总结

成功的面试 = 把握正确清晰的用人标准 + 挖掘真实匹配的应聘者信息 = 以素质模型去“发问” + 用STAR方式去“追问”

STAR行为面试法

STAR是业界公认的最为有效的面试方法之一,为背景(Situation)、任务(Task)、行为(Action)、结果(Result)的缩写。该方法不仅用于面试的场合,也会用于述职、晋升答辩等场景。

任务(Task)描述在事情里的担任的角色和负责的任务。

行为(Action)是最关键部分,要了解做了什么,展现出了哪些能力。

结果(Result)部分通常需要虚实结合,且重点在实,围绕效率、效果、质量和成本四个维度量化评估。

STAR方法同样适用于述职汇报或者晋升中。

奥克姆剃刀理论

如无必要,勿增实体。

马斯洛需求层次理论

心理学中重要理论,将人类的需求分为五个层级:

  1. 生理
  2. 安全
  3. 社交
  4. 尊重
  5. 自我实现

人类的需求为逐步递进的,在满足了基本需求后,就会去实现更高的需求和目标。

在工作中,经常会用类似马斯洛需求层次理论中的金字塔结构来解释一些其他的有递进关系的场景 ,比如一个软件产品的设计目标。

SMART 原则

确定目标的五原则,通常用在绩效考核中。

  1. S(Special):目标必须是具体的
  2. M(Measurable):目标必须是可衡量的
  3. A(Attainable):目标必须是可实现的
  4. R(Relevant):与其他的目标有一定的相关性
  5. T(Time-bound):目标必须有完成的期限

常见名词

ROI:投入产出比

0%