# k8s 高级

# 环境配置

1
2
3
4
5
docker load -i  .tar.gz
ctr -n=k8s.io images import .tar.gz
ctr -n=k8s.io images pull docker.io/library/centos:latest
docker save -o .tar.gz
ctr -n=k8s.io import .tar.gz
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
修改ip
配置主机名
配置hosts文件
配置免密登录
关闭交换分区
修改内核参数
关闭firewalld防火墙
关闭selinux
配置阿里云的安装docker的repo源
配置阿里云安装k8s的repo源
配置时间同步
开启ipvs
安装基础软件包
安装iptablese


kubeadm join 192.168.13.180:6443 --token or2v2h.3vffzj49xx3yuvr6 \
--discovery-token-ca-cert-hash sha256:09c9428481b6f33c04537f0753472f38c16637b351123a1416e7771366ca837a

1.安装bash-completion工具
yum install bash-completion -y
否则报错:
-bash: _get_comp_words_by_ref: command not found
2.执行bash_completion
source /usr/share/bash-completion/bash_completion
3.加载kubectl completion
source <(kubectl completion bash) # 在 bash 中设置当前 shell 的自动补全,要先安装 bash-completion 包。
echo "source <(kubectl completion bash)" >> ~/.bashrc # 在您的 bash shell 中永久的添加自动补全
您还可以为 kubectl 使用一个速记别名,该别名也可以与 completion 一起使用:
cat >>/root/.bashrc<<EOF
alias k=kubectl
complete -F __start_kubectl k
EOF
source /root/.bashrc

# Prometheus

1
2
3
Prometheus是一个开源的系统监控和报警系统,现在已经加入到CNCF基金会,成为继k8s之后第二个在CNCF托管的项目,在kubernetes容器管理系统中,通常会搭配prometheus进行监控,同时也支持多种exporter采集数据,还支持pushgateway进行数据上报,Prometheus性能足够支撑上万台规模的集群。

zabbix,nagios,cattio
1
2
3
4
5
6
7
8
9
10
11
12
1.多维度数据模型
每一个时间序列数据都由metric度量指标名称和它的标签labels键值对集合唯一确定:
这个metric度量指标名称指定监控目标系统的测量特征(如:http_requests_total- 接收http请求的总计数)。labels开启了Prometheus的多维数据模型:对于相同的度量名称,通过不同标签列表的结合, 会形成特定的度量维度实例。(例如:所有包含度量名称为/api/tracks的http请求,打上method=POST的标签,则形成了具体的http请求)。这个查询语言在这些度量和标签列表的基础上进行过滤和聚合。改变任何度量上的任何标签值,则会形成新的时间序列图。
2.灵活的查询语言(PromQL)
可以对采集的metrics指标进行加法,乘法,连接等操作;
3.可以直接在本地部署,不依赖其他分布式存储;
4.通过基于HTTP的pull方式采集时序数据;
5.可以通过中间网关pushgateway的方式把时间序列数据推送到prometheus server端;
6.可通过服务发现或者静态配置来发现目标服务对象(targets)。
7.有多种可视化图像界面,如Grafana等。
8.高效的存储,每个采样数据占3.5 bytes左右,300万的时间序列,30s间隔,保留60天,消耗磁盘大概200G。
9.做高可用,可以对数据做异地备份,联邦集群,部署多套prometheus,pushgateway上报数据
1
2
3
4
5
6
7
8
9
10
11
样本
在时间序列中的每一个点称为一个样本(sample),样本由以下三部分组成:
1、指标(metric):指标名称和描述当前样本特征的 labelsets;
2、时间戳(timestamp):一个精确到毫秒的时间戳;
3、样本值(value): 一个 folat64 的浮点型数据表示当前样本的值。

表示方式:
通过如下表达方式表示指定指标名称和指定标签集合的时间序列:
<metric name>{<label name>=<label value>, ...}
例如,指标名称为 api_http_requests_total,标签为 method="POST" 和 handler="/messages" 的时间序列可以表示为:
api_http_requests_total{method="POST", handler="/messages"}

# 组件介绍

1
2
3
4
5
6
7
1.Prometheus Server: 用于收集和存储时间序列数据。
2.Client Library: 客户端库,检测应用程序代码,当Prometheus抓取实例的HTTP端点时,客户端库会将所有跟踪的metrics指标的当前状态发送到prometheus server端。
3.Exporters: prometheus支持多种exporter,通过exporter可以采集metrics数据,然后发送到prometheus server端,所有向promtheus server提供监控数据的程序都可以被称为exporter
4.Alertmanager: 从 Prometheus server 端接收到 alerts 后,会进行去重,分组,并路由到相应的接收方,发出报警,常见的接收方式有:电子邮件,微信,钉钉, slack等。
5.Grafana:监控仪表盘,可视化监控数据
6.pushgateway: 各个目标主机可上报数据到pushgateway,然后prometheus server统一从pushgateway拉取数据。

image-20230526143122137

# 工作流程

1.Prometheus server 可定期从活跃的(up)目标主机上(target)拉取监控指标数据,目标主机的监控数据可通过配置静态 job 或者服务发现的方式被 prometheus server 采集到,这种方式默认的 pull 方式拉取指标;也可通过 pushgateway 把采集的数据上报到 prometheus server 中;还可通过一些组件自带的 exporter 采集相应组件的数据;

2.Prometheus server 把采集到的监控指标数据保存到本地磁盘或者数据库;

3.Prometheus 采集的监控指标数据按时间序列存储,通过配置报警规则,把触发的报警发送到 alertmanager

4.Alertmanager 通过配置报警接收方,发送报警到邮件,微信或者钉钉等

5.Prometheus 自带的 web ui 界面提供 PromQL 查询语言,可查询监控数据

6.Grafana 可接入 prometheus 数据源,把监控数据以图形化形式展示出

# Prometheus 和 Zabbix 对比

image-20230526143352088

# 部署模式

基本 HA

image-20230526143444630

基本的 HA 模式只能确保 Promthues 服务的可用性问题,但是不解决 Prometheus Server 之间的数据一致性问题以及持久化问题 (数据丢失后无法恢复),也无法进行动态的扩展。因此这种部署方式适合监控规模不大,Promthues Server 也不会频繁发生迁移的情况,并且只需要保存短周期监控数据的场景。

基本 HA + 远程存储

image-20230526143538150

在解决了 Promthues 服务可用性的基础上,同时确保了数据的持久化,当 Promthues Server 发生宕机或者数据丢失的情况下,可以快速的恢复。 同时 Promthues Server 可能很好的进行迁移。因此,该方案适用于用户监控规模不大,但是希望能够将监控数据持久化,同时能够确保 Promthues Server 的可迁移性的场景。

基本 HA + 远程存储 + 联邦集群

image-20230526143645241

Promthues 的性能瓶颈主要在于大量的采集任务,因此用户需要利用 Prometheus 联邦集群的特性,将不同类型的采集任务划分到不同的 Promthues 子服务中,从而实现功能分区。例如一个 Promthues Server 负责采集基础设施相关的监控指标,另外一个 Prometheus Server 负责采集应用监控指标。再有上层 Prometheus Server 实现对数据的汇聚。

# 数据类型

1
2
3
4
5
6
7
8
9
10
11
12
Counter是计数器类型:
1、Counter 用于累计值,例如记录请求次数、任务完成数、错误发生次数。
2、一直增加,不会减少。
3、重启进程后,会被重置。
例如:http_response_total{method="GET",endpoint="/api/tracks"} 100
http_response_total{method="GET",endpoint="/api/tracks"} 160

Counter 类型数据可以让用户方便的了解事件产生的速率的变化,在PromQL内置的相关操作函数可以提供相应的分析,比如以HTTP应用请求量来进行说明:
1、通过rate()函数获取HTTP请求量的增长率
rate(http_requests_total[5m])
2、查询当前系统中,访问量前10的HTTP地址
topk(10, http_requests_total)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Gauge是测量器类型:
1、Gauge是常规数值,例如温度变化、内存使用变化。
2、可变大,可变小。
3、重启进程后,会被重置

例如:
memory_usage_bytes{host="master-01"} 100
memory_usage_bytes{host="master-01"} 30
memory_usage_bytes{host="master-01"} 50
memory_usage_bytes{host="master-01"} 80

对于 Gauge 类型的监控指标,通过 PromQL 内置函数 delta() 可以获取样本在一段时间内的变化情况,例如,计算 CPU 温度在两小时内的差异:
dalta(cpu_temp_celsius{host="zeus"}[2h])

你还可以通过PromQL 内置函数 predict_linear() 基于简单线性回归的方式,对样本数据的变化趋势做出预测。例如,基于 2 小时的样本数据,来预测主机可用磁盘空间在 4 个小时之后的剩余情况:
predict_linear(node_filesystem_free{job="node"}[2h], 4 * 3600) < 0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
histogram是柱状图,在Prometheus系统的查询语言中,有三种作用:
1、在一段时间范围内对数据进行采样(通常是请求持续时间或响应大小等),并将其计入可配置的存储桶(bucket)中. 后续可通过指定区间筛选样本,也可以统计样本总数,最后一般将数据展示为直方图。
2、对每个采样点值累计和(sum)
3、对采样点的次数累计和(count)

度量指标名称: [basename]_上面三类的作用度量指标名称
1、[basename]_bucket{le="上边界"}, 这个值为小于等于上边界的所有采样点数量
2、[basename]_sum
3、[basename]_count

在大多数情况下人们都倾向于使用某些量化指标的平均值,例如 CPU 的平均使用率、页面的平均响应时间。这种方式的问题很明显,以系统 API 调用的平均响应时间为例:如果大多数 API 请求都维持在 100ms 的响应时间范围内,而个别请求的响应时间需要 5s,那么就会导致某些 WEB 页面的响应时间落到中位数的情况,而这种现象被称为长尾问题。
为了区分是平均的慢还是长尾的慢,最简单的方式就是按照请求延迟的范围进行分组。例如,统计延迟在 0~10ms 之间的请求数有多少,而 10~20ms 之间的请求数又有多少。通过这种方式可以快速分析系统慢的原因。Histogram 和 Summary 都是为了能够解决这样问题的存在,通过 Histogram 和 Summary 类型的监控指标,我们可以快速了解监控样本的分布情况。

Histogram 类型的样本会提供三种指标(假设指标名称为 <basename>):
样本的值分布在 bucket 中的数量,命名为 <basename>_bucket{le="<上边界>"}。解释的更通俗易懂一点,这个值表示指标值小于等于上边界的所有样本数量。

1、http 请求响应时间 <=0.005 秒 的请求次数为0
io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.005",} 0.0
2、http 请求响应时间 <=0.01 秒 的请求次数为0
io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.01",} 0.0
3、http 请求响应时间 <=0.025 秒 的请求次数为0
io_namespace_http_requests_latency_seconds_histogram_bucket{path="/",method="GET",code="200",le="0.025",} 0.0

所有样本值的大小总和,命名为 <basename>_sum。
1
2
3
4
5
6
7
8
9
10
11
summary
与 Histogram 类型类似,用于表示一段时间内的数据采样结果(通常是请求持续时间或响应大小等),但它直接存储了分位数(通过客户端计算,然后展示出来),而不是通过区间来计算。它也有三种作用:
1、对于每个采样点进行统计,并形成分位图。(如:正态分布一样,统计低于60分不及格的同学比例,统计低于80分的同学比例,统计低于95分的同学比例)
2、统计班上所有同学的总成绩(sum)
3、统计班上同学的考试总人数(count)

样本值的分位数分布情况,命名为 <basename>{quantile="<φ>"}。
1、含义:http 请求中有 50% 的请求响应时间是 3.052404983s
io_namespace_http_requests_latency_seconds_summary{path="/",method="GET",code="200",quantile="0.5",} 3.052404983

Histogram 需要通过 <basename>_bucket 来计算分位数,而 Summary 则直接存储了分位数的值。
1
2
3
4
5
6
7
Prometheus对kubernetes的监控
对于Kubernetes而言,我们可以把当中所有的资源分为几类:
•基础设施层(Node):集群节点,为整个集群和应用提供运行时资源
•容器基础设施(Container):为应用提供运行时环境
•用户应用(Pod):Pod中会包含一组容器,它们一起工作,并且对外提供一个(或者一组)功能
•内部服务负载均衡(Service):在集群内,通过Service在集群暴露应用功能,集群内应用和应用之间访问时提供内部的负载均衡
•外部访问入口(Ingress):通过Ingress提供集群外的访问入口,从而可以使外部客户端能够访问到部署在Kubernetes集群内的服务

# node-exporter

1
node-exporter可以采集机器(物理机、虚拟机、云主机等)的监控指标数据,能够采集到的指标包括CPU, 内存,磁盘,网络,文件数等信息。
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
79
80
apiVersion: apps/v1
kind: DaemonSet #可以保证k8s集群的每个节点都运行完全一样的pod
metadata:
name: node-exporter
namespace: monitor-sa
labels:
name: node-exporter
spec:
selector:
matchLabels:
name: node-exporter
template:
metadata:
labels:
name: node-exporter
spec:
hostPID: true
hostIPC: true
hostNetwork: true
containers:
- name: node-exporter
image: prom/node-exporter:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9100
resources:
requests:
cpu: 0.15 #这个容器运行至少需要0.15核cpu
securityContext:
privileged: true #开启特权模式
args:
- --path.procfs #配置挂载宿主机(node节点)的路径
- /host/proc
- --path.sysfs #配置挂载宿主机(node节点)的路径
- /host/sys
- --collector.filesystem.ignored-mount-points
- '"^/(sys|proc|dev|host|etc)($|/)"'
#通过正则表达式忽略某些文件系统挂载点的信息收集
volumeMounts:
- name: dev
mountPath: /host/dev
- name: proc
mountPath: /host/proc
- name: sys
mountPath: /host/sys
- name: rootfs
mountPath: /rootfs
#将主机/dev、/proc、/sys这些目录挂在到容器中,这是因为我们采集的很多节点数据都是通过这些文件来获取系统信息的。
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
volumes:
- name: proc
hostPath:
path: /proc
- name: dev
hostPath:
path: /dev
- name: sys
hostPath:
path: /sys
- name: rootfs
hostPath:
path: /1

curl 192.168.13.180:9100/metrics

curl 192.168.13.180:9100/metrics | grep node_cpu_seconds
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
# HELP node_cpu_seconds_total Seconds the CPUs spent in each mode.
# TYPE node_cpu_seconds_total counter
node_cpu_seconds_total{cpu="0",mode="idle"} 14492.66
node_cpu_seconds_total{cpu="0",mode="iowait"} 19.3
node_cpu_seconds_total{cpu="0",mode="irq"} 0
node_cpu_seconds_total{cpu="0",mode="nice"} 0.48
node_cpu_seconds_total{cpu="0",mode="softirq"} 28.25

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# 创建sa账号,对sa做rbac授权
kubectl create serviceaccount monitor -n monitor-sa
kubectl create clusterrolebinding monitor-clusterrolebinding -n monitor-sa --clusterrole=cluster-admin --serviceaccount=monitor-sa:monitor

kubectl create clusterrolebinding monitor-clusterrolebinding-1 -n monitor-sa --clusterrole=cluster-admin --user=system:serviceaccount:monitor:monitor-sa

# 创建prometheus数据存储目录
[root@prometheus-master ~]# mkdir /data
[root@prometheus-master ~]# chmod 777 -R /data/

# 创建configmap
---
kind: ConfigMap
apiVersion: v1
metadata:
labels:
app: prometheus
name: prometheus-config
namespace: monitor-sa
data:
prometheus.yml: |
global:
scrape_interval: 15s
scrape_timeout: 10s
evaluation_interval: 1m
scrape_configs:
- job_name: 'kubernetes-node'
kubernetes_sd_configs:
- role: node
relabel_configs:
- source_labels: [__address__]
regex: '(.*):10250'
replacement: '${1}:9100'
target_label: __address__
action: replace
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- job_name: 'kubernetes-node-cadvisor'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- target_label: __address__
replacement: kubernetes.default.svc:443
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: __metrics_path__
replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
- job_name: 'kubernetes-apiserver'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https
- job_name: 'kubernetes-service-endpoints'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace
target_label: __scheme__
regex: (https?)
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
- action: labelmap
regex: __meta_kubernetes_service_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: kubernetes_name

# 部署 Prothemeus

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
79
80
81
82
83
84
85
86
87
88
89
# 通过deployment部署Prothemeus
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-server
namespace: monitor-sa
labels:
app: prometheus
spec:
replicas: 2
selector:
matchLabels:
app: prometheus
component: server
#matchExpressions:
#- {key: app, operator: In, values: [prometheus]}
#- {key: component, operator: In, values: [server]}
template:
metadata:
labels:
app: prometheus
component: server
annotations:
prometheus.io/scrape: 'false'
spec:
serviceAccountName: monitor
containers:
- name: prometheus
image: prom/prometheus
imagePullPolicy: IfNotPresent
command:
- prometheus
- --config.file=/etc/prometheus/prometheus.yml
- --storage.tsdb.path=/prometheus
- --storage.tsdb.retention=720h
- --web.enable-lifecycle
ports:
- containerPort: 9090
protocol: TCP
volumeMounts:
- mountPath: /etc/prometheus
name: prometheus-config
- mountPath: /prometheus/
name: prometheus-storage-volume
volumes:
- name: prometheus-config
configMap:
name: prometheus-config
- name: prometheus-storage-volume
hostPath:
path: /data
type: Directory

[root@prometheus-master prome]# kubectl exec -it -n monitor-sa prometheus-server-674dff4c46-l67r6 -- /bin/sh
/prometheus $ cd /etc/prometheus/
/etc/prometheus $ ls
prometheus.yml


---
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: monitor-sa
labels:
app: prometheus
spec:
type: NodePort
ports:
- port: 9090
targetPort: 9090
protocol: TCP
selector:
app: prometheus
component: server

# 监控promethus-service
---
apiVersion: v1
kind: Service
metadata:
name: prometheus
namespace: monitor-sa
labels:
app: prometheus
annotations:
prometheus.io/scrape: "true"

# Prothemeus 热加载

1
2
3
4
5
6
7
8
9
10
curl -X POST http://pod-ip:9090/-/reload

#热加载速度比较慢,可以暴力重启prometheus,如修改上面的prometheus-cfg.yaml文件之后,可执行如下强制删除:
kubectl delete -f prometheus-cfg.yaml
kubectl delete -f prometheus-deploy.yaml
然后再通过apply更新:
kubectl apply -f prometheus-cfg.yaml
kubectl apply -f prometheus-deploy.yaml
注意:
线上最好热加载,暴力删除可能造成监控数据的丢失

image-20230527153500817

# Grafana

Grafana 是一个跨平台的开源的度量分析和可视化工具,可以将采集的数据可视化的展示,并及时通知给告警接收方。它主要有以下六大特点:

1、展示方式:快速灵活的客户端图表,面板插件有许多不同方式的可视化指标和日志,官方库中具有丰富的仪表盘插件,比如热图、折线图、图表等多种展示方式;

2、数据源:Graphite,InfluxDB,OpenTSDB,Prometheus,Elasticsearch,CloudWatch 和 KairosDB 等;

3、通知提醒:以可视方式定义最重要指标的警报规则,Grafana 将不断计算并发送通知,在数据达到阈值时通过 Slack、PagerDuty 等获得通知;

4、混合展示:在同一图表中混合使用不同的数据源,可以基于每个查询指定数据源,甚至自定义数据源;

5、注释:使用来自不同数据源的丰富事件注释图表,将鼠标悬停在事件上会显示完整的事件元数据和标记

# 安装 Grafana

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
79
apiVersion: apps/v1
kind: Deployment
metadata:
name: monitoring-grafana
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
task: monitoring
k8s-app: grafana
template:
metadata:
labels:
task: monitoring
k8s-app: grafana
spec:
containers:
- name: grafana
image: k8s.gcr.io/heapster-grafana-amd64:v5.0.4
imagePullPolicy: IfNotPresent
ports:
- containerPort: 3000
protocol: TCP
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certificates
readOnly: true
- mountPath: /var
name: grafana-storage
env:
- name: INFLUXDB_HOST
value: monitoring-influxdb
- name: GF_SERVER_HTTP_PORT
value: "3000"
# The following env variables are required to make Grafana accessible via
# the kubernetes api-server proxy. On production clusters, we recommend
# removing these env variables, setup auth for grafana, and expose the grafana
# service using a LoadBalancer or a public IP.
- name: GF_AUTH_BASIC_ENABLED
value: "false"
- name: GF_AUTH_ANONYMOUS_ENABLED
value: "true"
- name: GF_AUTH_ANONYMOUS_ORG_ROLE
value: Admin
- name: GF_SERVER_ROOT_URL
# If you're only using the API Server proxy, set this value instead:
# value: /api/v1/namespaces/kube-system/services/monitoring-grafana/proxy
value: /
volumes:
- name: ca-certificates
hostPath:
path: /etc/ssl/certs
- name: grafana-storage
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
labels:
# For use as a Cluster add-on (https://github.com/kubernetes/kubernetes/tree/master/cluster/addons)
# If you are NOT using this as an addon, you should comment out this line.
kubernetes.io/cluster-service: 'true'
kubernetes.io/name: monitoring-grafana
name: monitoring-grafana
namespace: kube-system
spec:
# In a production setup, we recommend accessing Grafana through an external Loadbalancer
# or through a public IP.
# type: LoadBalancer
# You could also use NodePort to expose the service at a randomly-generated port
# type: NodePort
ports:
- port: 80
targetPort: 3000
selector:
k8s-app: grafana
type: NodePort

image-20230527160404888

1

image-20230527160957066

将其中的 container_last_seen 放到 Prometheus 中 query,如果显示 nodata 说明没有这项监控

# kube-metric

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
79
80
81
82
83
84
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: kube-state-metrics
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: kube-state-metrics
rules:
- apiGroups: [""]
resources: ["nodes", "pods", "services", "resourcequotas", "replicationcontrollers", "limitranges", "persistentvolumeclaims", "persistentvolumes", "namespaces", "endpoints"]
verbs: ["list", "watch"]
- apiGroups: ["extensions"]
resources: ["daemonsets", "deployments", "replicasets"]
verbs: ["list", "watch"]
- apiGroups: ["apps"]
resources: ["statefulsets"]
verbs: ["list", "watch"]
- apiGroups: ["batch"]
resources: ["cronjobs", "jobs"]
verbs: ["list", "watch"]
- apiGroups: ["autoscaling"]
resources: ["horizontalpodautoscalers"]
verbs: ["list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: kube-state-metrics
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: kube-state-metrics
subjects:
- kind: ServiceAccount
name: kube-state-metrics
namespace: kube-system

apiVersion: apps/v1
kind: Deployment
metadata:
name: kube-state-metrics
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: kube-state-metrics
template:
metadata:
labels:
app: kube-state-metrics
spec:
serviceAccountName: kube-state-metrics
containers:
- name: kube-state-metrics
image: quay.io/coreos/kube-state-metrics:v1.9.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080


apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/scrape: 'true'
name: kube-state-metrics
namespace: kube-system
labels:
app: kube-state-metrics
spec:
ports:
- name: kube-state-metrics
port: 8080
protocol: TCP
selector:
app: kube-state-metrics

导入json
https://grafana.com/grafana/dashboards/?search=kubernetes

# alert manager

# 报警到 qq 邮箱

WYCMUCTTXLOBSVYS

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
kind: ConfigMap
kind: ConfigMap
apiVersion: v1
metadata:
name: alertmanager
namespace: monitor-sa
data:
alertmanager.yml: |-
global:
resolve_timeout: 1m
smtp_smarthost: 'smtp.163.com:25'
smtp_from: 'kbshire@163.com'
smtp_auth_username: 'kbshire@163.com'
smtp_auth_password: 'WYCMUCTTXLOBSVYS'
smtp_require_tls: false
route: #用于配置告警分发策略
group_by: [alertname] # 采用哪个标签来作为分组依据
group_wait: 10s # 组告警等待时间。也就是告警产生后等待10s,如果有同组告警一起发出
group_interval: 10s # 上下两组发送告警的间隔时间
repeat_interval: 10m # 重复发送告警的时间,减少相同邮件的发送频率,默认是1h
receiver: default-receiver #定义谁来收告警
receivers:
- name: 'default-receiver'
email_configs:
- to: '183108913@qq.com'

1
2
3
4
5
6
7
8
9
10
报警处理流程如下:
1. Prometheus Server监控目标主机上暴露的http接口(这里假设接口A),通过Promethes配置的'scrape_interval'定义的时间间隔,定期采集目标主机上监控数据。
2. 当接口A不可用的时候,Server端会持续的尝试从接口中取数据,直到"scrape_timeout"时间后停止尝试。这时候把接口的状态变为“DOWN”。
3. Prometheus同时根据配置的"evaluation_interval"的时间间隔,定期(默认1min)的对Alert Rule进行评估;当到达评估周期的时候,发现接口A为DOWN,即UP=0为真,激活Alert,进入“PENDING”状态,并记录当前active的时间;
4. 当下一个alert rule的评估周期到来的时候,发现UP=0继续为真,然后判断警报Active的时间是否已经超出rule里的‘for’ 持续时间,如果未超出,则进入下一个评估周期;如果时间超出,则alert的状态变为“FIRING”;同时调用Alertmanager接口,发送相关报警数据。
5. AlertManager收到报警数据后,会将警报信息进行分组,然后根据alertmanager配置的“group_wait”时间先进行等待。等wait时间过后再发送报警信息。
6. 属于同一个Alert Group的警报,在等待的过程中可能进入新的alert,如果之前的报警已经成功发出,那么间隔“group_interval”的时间间隔后再重新发送报警信息。比如配置的是邮件报警,那么同属一个group的报警信息会汇总在一个邮件里进行发送。
7. 如果Alert Group里的警报一直没发生变化并且已经成功发送,等待‘repeat_interval’时间间隔之后再重复发送相同的报警邮件;如果之前的警报没有成功发送,则相当于触发第6条条件,则需要等待group_interval时间间隔后重复发送。

同时最后至于警报信息具体发给谁,满足什么样的条件下指定警报接收人,设置不同报警发送频率,这里有alertmanager的route路由规则进行配置。
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
kind: ConfigMap
apiVersion: v1
metadata:
labels:
app: prometheus
name: prometheus-config
namespace: monitor-sa
data:
prometheus.yml: |
rule_files:
- /etc/prometheus/rules.yml
alerting:
alertmanagers:
- static_configs:
- targets: ["localhost:9093"]
global:
scrape_interval: 15s
scrape_timeout: 10s
evaluation_interval: 1m
scrape_configs:
- job_name: 'kubernetes-node'
kubernetes_sd_configs:
- role: node
relabel_configs:
- source_labels: [__address__]
regex: '(.*):10250'
replacement: '${1}:9100'
target_label: __address__
action: replace
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- job_name: 'kubernetes-node-cadvisor'
kubernetes_sd_configs:
- role: node
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
- target_label: __address__
replacement: kubernetes.default.svc:443
- source_labels: [__meta_kubernetes_node_name]
regex: (.+)
target_label: __metrics_path__
replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor
- job_name: 'kubernetes-apiserver'
kubernetes_sd_configs:
- role: endpoints
scheme: https
tls_config:
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
relabel_configs:
- source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name]
action: keep
regex: default;kubernetes;https
- job_name: 'kubernetes-service-endpoints'
kubernetes_sd_configs:
- role: endpoints
relabel_configs:
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme]
action: replace
target_label: __scheme__
regex: (https?)
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
action: replace
target_label: __address__
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
- action: labelmap
regex: __meta_kubernetes_service_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_service_name]
action: replace
target_label: kubernetes_name
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- action: keep
regex: true
source_labels:
- __meta_kubernetes_pod_annotation_prometheus_io_scrape
- action: replace
regex: (.+)
source_labels:
- __meta_kubernetes_pod_annotation_prometheus_io_path
target_label: __metrics_path__
- action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
source_labels:
- __address__
- __meta_kubernetes_pod_annotation_prometheus_io_port
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- action: replace
source_labels:
- __meta_kubernetes_namespace
target_label: kubernetes_namespace
- action: replace
source_labels:
- __meta_kubernetes_pod_name
target_label: kubernetes_pod_name
- job_name: 'kubernetes-etcd'
scheme: https
tls_config:
ca_file: /etc/kubernetes/pki/etcd/ca.crt
cert_file: /etc/kubernetes/pki/etcd/server.crt
key_file: /etc/kubernetes/pki/etcd/server.key
scrape_interval: 5s
static_configs:
- targets: ['192.168.13.180:2379']
rules.yml: |
groups:
- name: example
rules:
- alert: apiserver的cpu使用率大于80%
expr: rate(process_cpu_seconds_total{job=~"kubernetes-apiserver"}[1m]) * 100 > 80
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过80%"
- alert: apiserver的cpu使用率大于90%
expr: rate(process_cpu_seconds_total{job=~"kubernetes-apiserver"}[1m]) * 100 > 90
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过90%"
- alert: etcd的cpu使用率大于80%
expr: rate(process_cpu_seconds_total{job=~"kubernetes-etcd"}[1m]) * 100 > 80
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过80%"
- alert: etcd的cpu使用率大于90%
expr: rate(process_cpu_seconds_total{job=~"kubernetes-etcd"}[1m]) * 100 > 90
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.job}}组件的cpu使用率超过90%"
- alert: kube-state-metrics的cpu使用率大于80%
expr: rate(process_cpu_seconds_total{k8s_app=~"kube-state-metrics"}[1m]) * 100 > 80
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.k8s_app}}组件的cpu使用率超过80%"
value: "{{ $value }}%"
threshold: "80%"
- alert: kube-state-metrics的cpu使用率大于90%
expr: rate(process_cpu_seconds_total{k8s_app=~"kube-state-metrics"}[1m]) * 100 > 0
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.k8s_app}}组件的cpu使用率超过90%"
value: "{{ $value }}%"
threshold: "90%"
- alert: coredns的cpu使用率大于80%
expr: rate(process_cpu_seconds_total{k8s_app=~"kube-dns"}[1m]) * 100 > 80
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.k8s_app}}组件的cpu使用率超过80%"
value: "{{ $value }}%"
threshold: "80%"
- alert: coredns的cpu使用率大于90%
expr: rate(process_cpu_seconds_total{k8s_app=~"kube-dns"}[1m]) * 100 > 90
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.k8s_app}}组件的cpu使用率超过90%"
value: "{{ $value }}%"
threshold: "90%"
- alert: kube-proxy打开句柄数>600
expr: process_open_fds{job=~"kubernetes-kube-proxy"} > 600
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
value: "{{ $value }}"
- alert: kube-proxy打开句柄数>1000
expr: process_open_fds{job=~"kubernetes-kube-proxy"} > 1000
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
value: "{{ $value }}"
- alert: kubernetes-schedule打开句柄数>600
expr: process_open_fds{job=~"kubernetes-schedule"} > 600
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
value: "{{ $value }}"
- alert: kubernetes-schedule打开句柄数>1000
expr: process_open_fds{job=~"kubernetes-schedule"} > 1000
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
value: "{{ $value }}"
- alert: kubernetes-controller-manager打开句柄数>600
expr: process_open_fds{job=~"kubernetes-controller-manager"} > 600
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
value: "{{ $value }}"
- alert: kubernetes-controller-manager打开句柄数>1000
expr: process_open_fds{job=~"kubernetes-controller-manager"} > 1000
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
value: "{{ $value }}"
- alert: kubernetes-apiserver打开句柄数>600
expr: process_open_fds{job=~"kubernetes-apiserver"} > 600
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
value: "{{ $value }}"
- alert: kubernetes-apiserver打开句柄数>1000
expr: process_open_fds{job=~"kubernetes-apiserver"} > 1000
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
value: "{{ $value }}"
- alert: kubernetes-etcd打开句柄数>600
expr: process_open_fds{job=~"kubernetes-etcd"} > 600
for: 2s
labels:
severity: warnning
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>600"
value: "{{ $value }}"
- alert: kubernetes-etcd打开句柄数>1000
expr: process_open_fds{job=~"kubernetes-etcd"} > 1000
for: 2s
labels:
severity: critical
annotations:
description: "{{$labels.instance}}的{{$labels.job}}打开句柄数>1000"
value: "{{ $value }}"
- alert: coredns
expr: process_open_fds{k8s_app=~"kube-dns"} > 600
for: 2s
labels:
severity: warnning
annotations:
description: "插件{{$labels.k8s_app}}({{$labels.instance}}): 打开句柄数超过600"
value: "{{ $value }}"
- alert: coredns
expr: process_open_fds{k8s_app=~"kube-dns"} > 1000
for: 2s
labels:
severity: critical
annotations:
description: "插件{{$labels.k8s_app}}({{$labels.instance}}): 打开句柄数超过1000"
value: "{{ $value }}"
- alert: kube-proxy
expr: process_virtual_memory_bytes{job=~"kubernetes-kube-proxy"} > 2000000000
for: 2s
labels:
severity: warnning
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
value: "{{ $value }}"
- alert: scheduler
expr: process_virtual_memory_bytes{job=~"kubernetes-schedule"} > 2000000000
for: 2s
labels:
severity: warnning
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
value: "{{ $value }}"
- alert: kubernetes-controller-manager
expr: process_virtual_memory_bytes{job=~"kubernetes-controller-manager"} > 2000000000
for: 2s
labels:
severity: warnning
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
value: "{{ $value }}"
- alert: kubernetes-apiserver
expr: process_virtual_memory_bytes{job=~"kubernetes-apiserver"} > 2000000000
for: 2s
labels:
severity: warnning
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
value: "{{ $value }}"
- alert: kubernetes-etcd
expr: process_virtual_memory_bytes{job=~"kubernetes-etcd"} > 2000000000
for: 2s
labels:
severity: warnning
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): 使用虚拟内存超过2G"
value: "{{ $value }}"
- alert: kube-dns
expr: process_virtual_memory_bytes{k8s_app=~"kube-dns"} > 2000000000
for: 2s
labels:
severity: warnning
annotations:
description: "插件{{$labels.k8s_app}}({{$labels.instance}}): 使用虚拟内存超过2G"
value: "{{ $value }}"
- alert: HttpRequestsAvg
expr: sum(rate(rest_client_requests_total{job=~"kubernetes-kube-proxy|kubernetes-kubelet|kubernetes-schedule|kubernetes-control-manager|kubernetes-apiservers"}[1m])) > 1000
for: 2s
labels:
team: admin
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): TPS超过1000"
value: "{{ $value }}"
threshold: "1000"
- alert: Pod_restarts
expr: kube_pod_container_status_restarts_total{namespace=~"kube-system|default|monitor-sa"} > 0
for: 2s
labels:
severity: warnning
annotations:
description: "在{{$labels.namespace}}名称空间下发现{{$labels.pod}}这个pod下的容器{{$labels.container}}被重启,这个监控指标是由{{$labels.instance}}采集的"
value: "{{ $value }}"
threshold: "0"
- alert: Pod_waiting
expr: kube_pod_container_status_waiting_reason{namespace=~"kube-system|default"} == 1
for: 2s
labels:
team: admin
annotations:
description: "空间{{$labels.namespace}}({{$labels.instance}}): 发现{{$labels.pod}}下的{{$labels.container}}启动异常等待中"
value: "{{ $value }}"
threshold: "1"
- alert: Pod_terminated
expr: kube_pod_container_status_terminated_reason{namespace=~"kube-system|default|monitor-sa"} == 1
for: 2s
labels:
team: admin
annotations:
description: "空间{{$labels.namespace}}({{$labels.instance}}): 发现{{$labels.pod}}下的{{$labels.container}}被删除"
value: "{{ $value }}"
threshold: "1"
- alert: Etcd_leader
expr: etcd_server_has_leader{job="kubernetes-etcd"} == 0
for: 2s
labels:
team: admin
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): 当前没有leader"
value: "{{ $value }}"
threshold: "0"
- alert: Etcd_leader_changes
expr: rate(etcd_server_leader_changes_seen_total{job="kubernetes-etcd"}[1m]) > 0
for: 2s
labels:
team: admin
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): 当前leader已发生改变"
value: "{{ $value }}"
threshold: "0"
- alert: Etcd_failed
expr: rate(etcd_server_proposals_failed_total{job="kubernetes-etcd"}[1m]) > 0
for: 2s
labels:
team: admin
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}): 服务失败"
value: "{{ $value }}"
threshold: "0"
- alert: Etcd_db_total_size
expr: etcd_debugging_mvcc_db_total_size_in_bytes{job="kubernetes-etcd"} > 10000000000
for: 2s
labels:
team: admin
annotations:
description: "组件{{$labels.job}}({{$labels.instance}}):db空间超过10G"
value: "{{ $value }}"
threshold: "10G"
- alert: Endpoint_ready
expr: kube_endpoint_address_not_ready{namespace=~"kube-system|default"} == 1
for: 2s
labels:
team: admin
annotations:
description: "空间{{$labels.namespace}}({{$labels.instance}}): 发现{{$labels.endpoint}}不可用"
value: "{{ $value }}"
threshold: "1"
- name: 物理节点状态-监控告警
rules:
- alert: 物理节点cpu使用率
expr: 100-avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by(instance)*100 > 90
for: 2s
labels:
severity: ccritical
annotations:
summary: "{{ $labels.instance }}cpu使用率过高"
description: "{{ $labels.instance }}的cpu使用率超过90%,当前使用率[{{ $value }}],需要排查处理"
- alert: 物理节点内存使用率
expr: (node_memory_MemTotal_bytes - (node_memory_MemFree_bytes + node_memory_Buffers_bytes + node_memory_Cached_bytes)) / node_memory_MemTotal_bytes * 100 > 90
for: 2s
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }}内存使用率过高"
description: "{{ $labels.instance }}的内存使用率超过90%,当前使用率[{{ $value }}],需要排查处理"
- alert: InstanceDown
expr: up == 0
for: 2s
labels:
severity: critical
annotations:
summary: "{{ $labels.instance }}: 服务器宕机"
description: "{{ $labels.instance }}: 服务器延时超过2分钟"
- alert: 物理节点磁盘的IO性能
expr: 100-(avg(irate(node_disk_io_time_seconds_total[1m])) by(instance)* 100) < 60
for: 2s
labels:
severity: critical
annotations:
summary: "{{$labels.mountpoint}} 流入磁盘IO使用率过高!"
description: "{{$labels.mountpoint }} 流入磁盘IO大于60%(目前使用:{{$value}})"
- alert: 入网流量带宽
expr: ((sum(rate (node_network_receive_bytes_total{device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m])) by (instance)) / 100) > 102400
for: 2s
labels:
severity: critical
annotations:
summary: "{{$labels.mountpoint}} 流入网络带宽过高!"
description: "{{$labels.mountpoint }}流入网络带宽持续5分钟高于100M. RX带宽使用率{{$value}}"
- alert: 出网流量带宽
expr: ((sum(rate (node_network_transmit_bytes_total{device!~'tap.*|veth.*|br.*|docker.*|virbr*|lo*'}[5m])) by (instance)) / 100) > 102400
for: 2s
labels:
severity: critical
annotations:
summary: "{{$labels.mountpoint}} 流出网络带宽过高!"
description: "{{$labels.mountpoint }}流出网络带宽持续5分钟高于100M. RX带宽使用率{{$value}}"
- alert: TCP会话
expr: node_netstat_Tcp_CurrEstab > 1000
for: 2s
labels:
severity: critical
annotations:
summary: "{{$labels.mountpoint}} TCP_ESTABLISHED过高!"
description: "{{$labels.mountpoint }} TCP_ESTABLISHED大于1000%(目前使用:{{$value}}%)"
- alert: 磁盘容量
expr: 100-(node_filesystem_free_bytes{fstype=~"ext4|xfs"}/node_filesystem_size_bytes {fstype=~"ext4|xfs"}*100) > 80
for: 2s
labels:
severity: critical
annotations:
summary: "{{$labels.mountpoint}} 磁盘分区使用率过高!"
description: "{{$labels.mountpoint }} 磁盘分区使用大于80%(目前使用:{{$value}}%)"

1
kubectl -n monitor-sa create secret generic etcd-certs --from-file=/etc/kubernetes/pki/etcd/server.key  --from-file=/etc/kubernetes/pki/etcd/server.crt --from-file=/etc/kubernetes/pki/etcd/ca.crt
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
79
80
81
82
83
84
85
86
87
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: prometheus-server
namespace: monitor-sa
labels:
app: prometheus
spec:
replicas: 1
selector:
matchLabels:
app: prometheus
component: server
#matchExpressions:
#- {key: app, operator: In, values: [prometheus]}
#- {key: component, operator: In, values: [server]}
template:
metadata:
labels:
app: prometheus
component: server
annotations:
prometheus.io/scrape: 'false'
spec:
#nodeName: xianchaonode1
serviceAccountName: monitor
containers:
- name: prometheus
image: prom/prometheus
imagePullPolicy: IfNotPresent
command:
- "/bin/prometheus"
args:
- "--config.file=/etc/prometheus/prometheus.yml"
- "--storage.tsdb.path=/prometheus"
- "--storage.tsdb.retention=24h"
- "--web.enable-lifecycle"
ports:
- containerPort: 9090
protocol: TCP
volumeMounts:
- mountPath: /etc/prometheus
name: prometheus-config
- mountPath: /prometheus/
name: prometheus-storage-volume
- name: k8s-certs
mountPath: /var/run/secrets/kubernetes.io/k8s-certs/etcd/
- name: alertmanager
image: prom/alertmanager
imagePullPolicy: IfNotPresent
args:
- "--config.file=/etc/alertmanager/alertmanager.yml"
- "--log.level=debug"
ports:
- containerPort: 9093
protocol: TCP
name: alertmanager
volumeMounts:
- name: alertmanager-config
mountPath: /etc/alertmanager
- name: alertmanager-storage
mountPath: /alertmanager
- name: localtime
mountPath: /etc/localtime
volumes:
- name: prometheus-config
configMap:
name: prometheus-config
- name: prometheus-storage-volume
hostPath:
path: /data
type: Directory
- name: k8s-certs
secret:
secretName: etcd-certs
- name: alertmanager-config
configMap:
name: alertmanager
- name: alertmanager-storage
hostPath:
path: /data/alertmanager
type: DirectoryOrCreate
- name: localtime
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
---
apiVersion: v1
kind: Service
metadata:
labels:
name: prometheus
kubernetes.io/cluster-service: 'true'
name: alertmanager
namespace: monitor-sa
spec:
ports:
- name: alertmanager
nodePort: 30066
port: 9093
protocol: TCP
targetPort: 9093
selector:
app: prometheus
sessionAffinity: None
type: NodePort
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 微信
receivers:
- name: 'prometheus'
wechat_configs:
- corp_id: wwa82df90a693abb15
to_user: '@all'
agent_id: 1000003
api_secret: Ov5SWq_JqrolsOj6dD4Jg9qaMu1TTaDzVTCrXHcjlFs

# 钉钉
receivers:
- name: cluster1
webhook_configs:
- url: 'http://192.168.40.180:8060/dingtalk/cluster1/send'
send_resolved: true

# PromQL

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
PromQL(Prometheus Query Language)是 Prometheus 自己开发的表达式语言,语言表现力很丰富,内置函数也很多。使用它可以对时序数据进行筛选和聚合。

PromQL 表达式计算出来的值有以下几种类型:
瞬时向量 (Instant vector): 一组时序,每个时序只有一个采样值
区间向量 (Range vector): 一组时序,每个时序包含一段时间内的多个采样值
标量数据 (Scalar): 一个浮点数
字符串 (String): 一个字符串,暂时未用

瞬时向量选择器用来选择一组时序在某个采样点的采样值。
最简单的情况就是指定一个度量指标,选择出所有属于该度量指标的时序的当前采样值。
可以通过在后面添加用大括号包围起来的一组标签键值对来对时序进行过滤。比如下面的表达式筛选出了 job kubernetes-apiservers,并且 resource为 pod的时序:apiserver_request_total
apiserver_request_total{job="kubernetes-apiserver",resource="pods"}
匹配标签值时可以是等于,也可以使用正则表达式。总共有下面几种匹配操作符:
=:完全相等
!= 不相等
=~: 正则表达式匹配
!~ 正则表达式不匹配
下面的表达式筛选出了container是kube-scheduler或kube-proxy或kube-apiserver的时序数据
container_processes{container=~"kube-scheduler|kube-proxy|kube-apiserver"}

区间向量选择器类似于瞬时向量选择器,不同的是它选择的是过去一段时间的采样值。可以通过在瞬时向量选择器后面添加包含在 [] 里的时长来得到区间向量选择器。比如下面的表达式选出了所有度量指标为apiserver_request_total且resource是pod的时序在过去1 分钟的采样值。
apiserver_request_total{job="kubernetes-apiserver",resource="pods"}[1m]
这个不支持Graph,需要选择Console,才会看到采集的数据
说明:时长的单位可以是下面几种之一:
s:seconds
m:minutes
h:hours
d:days
w:weeks
y:years

偏移向量选择器
前面介绍的选择器默认都是以当前时间为基准时间,偏移修饰器用来调整基准时间,使其往前偏移一段时间。偏移修饰器紧跟在选择器后面,使用 offset 来指定要偏移的量。比如下面的表达式选择度量名称为apiserver_request_total的所有时序在 5 分钟前的采样值。
apiserver_request_total{job="kubernetes-apiserver",resource="pods"} offset 5m
下面的表达式选择apiserver_request_total 度量指标在 1 周前的这个时间点过去 5 分钟的采样值。
apiserver_request_total{job="kubernetes-apiserver",resource="pods"} [5m] offset 1w

聚合操作符
PromQL 的聚合操作符用来将向量里的元素聚合得更少。总共有下面这些聚合操作符:
sum:求和
min:最小值
max:最大值
avg:平均值
stddev:标准差
stdvar:方差
count:元素个数
count_values:等于某值的元素个数
bottomk:最小的 k 个元素
topk:最大的 k 个元素
quantile:分位数
计算haha节点所有容器总计内存
sum(container_memory_usage_bytes{instance=~"haha"})/1024/1024/1024
计算haha节点最近1m所有容器cpu使用率
sum (rate (container_cpu_usage_seconds_total{instance=~"haha"}[1m])) / sum (machine_cpu_cores{ instance =~"haha"}) * 100
计算最近1m所有容器cpu使用率
sum (rate (container_cpu_usage_seconds_total{id!="/"}[1m])) by (id)

函数
Prometheus 内置了一些函数来辅助计算,下面介绍一些典型的。
abs():绝对值
sqrt():平方根
exp():指数计算
ln():自然对数
ceil():向上取整
floor():向下取整
round():四舍五入取整
delta():计算区间向量里每一个时序第一个和最后一个的差值
sort():排序

# prometheus 监控 tomcat

nlighten/tomcat_exporter: A Prometheus exporter for Apache Tomcat (github.com)

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
下面在k8s-master节点操作
(1)制作tomcat镜像,按如下步骤
mkdir /root/tomcat_image
把上面的war包和jar包传到这个目录下
cat Dockerfile
FROM tomcat:8.5-jdk8-corretto
ADD metrics.war /usr/local/tomcat/webapps/
ADD simpleclient-0.8.0.jar /usr/local/tomcat/lib/
ADD simpleclient_common-0.8.0.jar /usr/local/tomcat/lib/
ADD simpleclient_hotspot-0.8.0.jar /usr/local/tomcat/lib/
ADD simpleclient_servlet-0.8.0.jar /usr/local/tomcat/lib/
ADD tomcat_exporter_client-0.0.12.jar /usr/local/tomcat/lib/

docker build -t='/tomcat_prometheus:v1' .
docker login
username:xianchao
password:1989317**
docker push xianchao/tomcat_prometheus:v1 #上传镜像到hub仓库
docker pull xianchao/tomcat_prometheus:v1 #在k8s的node节点拉取镜像拉取镜像

(2)基于上面的镜像创建一个tomcat实例
cat deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
namespace: default
spec:
selector:
matchLabels:
app: tomcat
replicas: 2 # tells deployment to run 2 pods matching the template
template: # create pods using pod definition in this template
metadata:
labels:
app: tomcat
annotations:
prometheus.io/scrape: 'true'
spec:
containers:
- name: tomcat
image: xianchao/tomcat_prometheus:v1
ports:
- containerPort: 8080
securityContext:
privileged: true

kubectl apply -f deploy.yaml

创建一个service,可操作也可不操作
cat tomcat-service.yaml
kind: Service #service 类型
apiVersion: v1
metadata:
# annotations:
# prometheus.io/scrape: 'true'
name: tomcat-service
spec:
selector:
app: tomcat
ports:
- nodePort: 31360
port: 80
protocol: TCP
targetPort: 8080
type: NodePort

kubectl apply -f tomcat-service.yaml

# prometheus 监控 redis

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
cat redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: kube-system
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:4
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 6379
- name: redis-exporter
image: oliver006/redis_exporter:latest
resources:
requests:
cpu: 100m
memory: 100Mi
ports:
- containerPort: 9121
---
kind: Service
apiVersion: v1
metadata:
  name: redis
  namespace: kube-system
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "9121"
spec:
  selector:
    app: redis
  ports:
  - name: redis
    port: 6379
    targetPort: 6379
  - name: prom
    port: 9121
    targetPort: 9121

kubectl apply -f redis.yaml
redis 这个 Pod 中包含了两个容器,一个就是 redis 本身的主应用,另外一个容器就是 redis_exporter

由于Redis服务的metrics接口在redis-exporter 9121上,所以我们添加了prometheus.io/port=9121这样的annotation,在prometheus就会自动发现redis了

接下来我们刷新一下Redis的Service配置
[root@abcdocker prometheus]# kubectl apply -f redis.yaml

# prometheus 监控 mysql

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
yum install mysql -y
yum install mariadb -y

tar -xvf mysqld_exporter-0.10.0.linux-amd64.tar.gz
cd mysqld_exporter-0.10.0.linux-amd64
cp -ar mysqld_exporter /usr/local/bin/
chmod +x /usr/local/bin/mysqld_exporter

2.登陆mysql为mysql_exporter创建账号并授权
# 创建数据库用户。
mysql> CREATE USER 'mysql_exporter'@'localhost' IDENTIFIED BY 'Abcdef123!.';

# 对mysql_exporter用户授权
mysql> GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'mysql_exporter'@'localhost';

3.创建mysql配置文件、运行时可免密码连接数据库:
cd mysqld_exporter-0.10.0.linux-amd64
cat my.cnf
[client]
user=mysql_exporter
password=Abcdef123!.

nohup ./mysqld_exporter --config.my-cnf=./my.cnf &

5.修改prometheus-alertmanager-cfg.yaml文件,添加如下
- job_name: 'mysql'
static_configs:
- targets: ['192.168.40.180:9104']

kubectl apply -f prometheus-alertmanager-cfg.yaml
kubectl delete -f prometheus-alertmanager-deploy.yaml
kubectl apply -f prometheus-alertmanager-deploy.yaml

# Prothemeus 监控 nginx

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
unzip nginx-module-vts-master.zip
mv nginx-module-vts-master /usr/local/

tar zxvf nginx-1.15.7.tar.gz
cd nginx-1.15.7
./configure --prefix=/usr/local/nginx --with-http_gzip_static_module --with-http_stub_status_module --with-http_ssl_module --with-pcre --with-file-aio --with-http_realip_module --add-module=/usr/local/nginx-module-vts-master

make && make install

修改nginx配置文件:
vim /usr/local/nginx/conf/nginx.conf
server下添加如下:
location /status {
        vhost_traffic_status_display;
        vhost_traffic_status_display_format html;
        }
http中添加如下:
vhost_traffic_status_zone;
nginx -s reload
访问192.168.124.16/status可以看到nginx监控数据

3.安装nginx-vts-exporter
unzip nginx-vts-exporter-0.5.zip
mv nginx-vts-exporter-0.5 /usr/local/
chmod +x /usr/local/nginx-vts-exporter-0.5/bin/nginx-vts-exporter
cd /usr/local/nginx-vts-exporter-0.5/bin
nohup ./nginx-vts-exporter  -nginx.scrape_uri http://192.168.124.16/status/format/json &

4.修改prometheus-cfg.yaml文件
添加如下job:

- job_name: 'nginx'
scrape_interval: 5s
static_configs:
- targets: ['192.168.124.16:9913']

kubectl apply -f prometheus-cfg.yaml
kubectl delete -f prometheus-deploy.yaml
kubectl apply -f prometheus-deploy.yaml

# prometheus 监控 mongodb

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
1.下载mongodb和mongodb_exporter镜像
docker pull mongo
docker pull eses/mongodb_exporter

2.启动mongodb
mkdir -p /data/db
docker run -d --name mongodb -p 27017:27017 -v /data/db:/data/db mongo
#创建mongo账号密码,给mongodb_exporter连接mongo用
(1)登录到容器
docker exec -it 24f910190790ade396844cef61cc66412b7af2108494742922c6157c5b236aac mongo admin
(2)设置密码
use admin
db.createUser({ user: 'admin', pwd: 'admin111111', roles: [ { role: "userAdminAnyDatabase", db: "admin" } ] })

docker run -d --name mongodb_exporter -p 30056:9104 percona/mongodb_exporter:0.34.0 --mongodb.uri mongodb://admin:admin111111@192.168.124.16:27017

注:admin:admin111111这个就是上面启动mongodb后设置的密码,@后面接mongodb的ip和端口

4.修改prometheus-cfg.yaml文件
添加一个job_name
- job_name: 'mongodb'
scrape_interval: 5s
  static_configs:
  - targets: ['192.168.124.16:30056']

kubectl apply -f prometheus-cfg.yaml
kubectl delete -f prometheus-deploy.yaml
kubectl apply -f prometheus-deploy.yaml

# pushgateway

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Pushgateway是prometheus的一个组件,prometheus server默认是通过exporter主动获取数据(默认采取pull拉取数据),pushgateway则是通过被动方式推送数据到prometheus server,用户可以写一些自定义的监控脚本把需要监控的数据发送给pushgateway, 然后pushgateway再把数据发送给Prometheus server

Pushgateway优点:
Prometheus 默认采用定时pull 模式拉取targets数据,但是如果不在一个子网或者防火墙,prometheus就拉取不到targets数据,所以可以采用各个target往pushgateway上push数据,然后prometheus去pushgateway上定时pull数据
在监控业务数据的时候,需要将不同数据汇总, 汇总之后的数据可以由pushgateway统一收集,然后由 Prometheus 统一拉取。

pushgateway缺点:
Prometheus拉取状态只针对 pushgateway, 不能对每个节点都有效;
Pushgateway出现问题,整个采集到的数据都会出现问题
监控下线,prometheus还会拉取到旧的监控数据,需要手动清理 pushgateway不要的数据。

docker push prom/pushgateway
docker run -d --name pushgateway -p 9091:9091 prom/pushgateway

- job_name: 'pushgateway'
scrape_interval: 5s
static_configs:
- targets: ['192.168.13.181:9091']
honor_labels: true

kubectl apply -f prometheus-cfg.yaml
kubectl delete -f prometheus-deploy.yaml
kubectl apply -f prometheus-deploy.yaml

image-20230529153332021

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
推送指定的数据格式到pushgateway

向 {job="test_job"} 添加单条数据:
echo " metric 3.6" | curl --data-binary @- http://192.168.13.181:9091/metrics/job/test_job

添加复杂数据
cat <<EOF | curl --data-binary @- http://192.168.13.181:9091/metrics/job/test_job/instance/test_instance
node_memory_usage 36
node_memory_total 36000
EOF

删除某个组下某个实例的所有数据
curl -X DELETE http://192.168.13.181:9091/metrics/job/test_job/instance/test_instance

删除某个组下的所有数据:
curl -X DELETE http://192.168.13.181:9091/metrics/job/test_job
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
把数据上报到pushgateway
在被监控服务所在的机器配置数据上报,想要把192.168.13.181这个机器的内存数据上报到pushgateway,下面步骤需要在192.168.40.181操作

cat push.sh

node_memory_usages=$(free -m | grep Mem | awk '{print $3/$2*100}')
job_name="memory"
instance_name="192.168.13.181"
cat <<EOF | curl --data-binary @- http://192.168.13.181:9091/metrics/job/$job_name/instance/$instance_name
node_memory_usages $node_memory_usages
EOF


sh push.sh

crontab -u root -e
*/1 * * * * /root/push.sh

注意:从上面配置可以看到,我们上传到pushgateway中的数据有job也有instance,而prometheus配置pushgateway这个job_name中也有job和instance,这个job和instance是指pushgateway实例本身,添加 honor_labels: true 参数, 可以避免promethues的targets列表中的job_name是pushgateway的 job 、instance 和上报到pushgateway数据的job和instance冲突。

# ELK

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
日志打印的常见级别:
日志打印通常有四种级别,从高到底分别是:ERROR、WARN、INFO、DEBUG。

在你的系统中如果开启了某一级别的日志后,就不会打印比它级别低的日志。例如,程序如果开启了INFO级别日志,DEBUG日志就不会打印,通常在生产环境中开启INFO日志。

1、DEBUG:
DEBU可以打印出最详细的日志信息,主要用于开发过程中打印一些运行信息。
2、INFO:
INFO可以打印一些你感兴趣的或者重要的信息,这个可以用于生产环境中输出程序运行的一些重要信息,但是不能滥用,避免打印过多的日志。
3、WARNING:
WARNING 表明发生了一些暂时不影响运行的错误,会出现潜在错误的情形,有些信息不是错误信息,但是也要给程序员的一些提示
4、ERROR:
ERROR 可以打印错误和异常信息,如果不想输出太多的日志,可以使用这个级别,这一级就是比较重要的错误了,软件的某些功能已经不能继续执行了。

通过查看INFO日志发现是由于自己操作失误,造成了程序结果和预期不符合,这种情况不是程序出错,所以并不是bug,不需要开发人员到场。
通过查看INFO日志发现自己的操作正确,根据INFO日志查看并不是操作失误造成,这个时候就需要开发人员到场确认。

ERROR和WARN的级别都比INFO要高,所以在设定日志级别在INFO时,这两者的日志也会被打印。根据上面INFO和DEBUG级别的区别以及适用人员可以知道,ERROR和WARN是同时给测试和开发、运维观察的。WARN级别称之为“警告”,这个“警告”实际上就有点含糊了,它不算错,你可以选择忽视它,但也可以选择重视它。
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
常见的日志收集方案
EFK
在Kubernetes集群上运行多个服务和应用程序时,日志收集系统可以帮助你快速分类和分析由Pod生成的大量日志数据。Kubernetes中比较流行的日志收集解决方案是Elasticsearch、Fluentd和Kibana(EFK)技术栈,也是官方推荐的一种方案。

Elasticsearch是一个实时的,分布式的,可扩展的搜索引擎,它允许进行全文本和结构化搜索以及对日志进行分析。它通常用于索引和搜索大量日志数据,也可以用于搜索许多不同种类的文档。

Elasticsearch通常与Kibana一起部署,kibana可以把Elasticsearch采集到的数据通过dashboard(仪表板)可视化展示出来。Kibana允许你通过Web界面浏览Elasticsearch日志数据,也可自定义查询条件快速检索出elasticccsearch中的日志数据。

Fluentd是一个流行的开源数据收集器,我们在 Kubernetes 集群节点上安装 Fluentd,通过获取容器日志文件、过滤和转换日志数据,然后将数据传递到 Elasticsearch 集群,在该集群中对其进行索引和存储。

ELK
E - Elasticsearch(简称:ES)
L - Logstash
K - Kibana
Elasticsearch:日志存储和搜索引擎,它的特点有:分布式,零配置,自动发现,索引自动分片,索引副本机制,restful风格接口,多数据源,自动搜索负载等。

Logstash:是一个完全开源的工具,他可以对你的日志进行收集、过滤,并将其存储供以后使用(支持动态的从各种数据源搜集数据,并对数据进行过滤、分析、丰富、统一格式等操作。)。

Kibana 也是一个开源和免费的工具,Kibana可以为 Logstash 和 ElasticSearch 提供的日志分析友好的 Web 界面,可以帮助您汇总、分析和搜索重要数据日志。


ELK+filebeat
Filebeat(采集)—> Logstash(聚合、处理)—> ElasticSearch (存储)—>Kibana (展示)

2.4 其他方案
ELK日志流程可以有多种方案(不同组件可自由组合,根据自身业务配置),常见有以下:

Logstash(采集、处理)—> ElasticSearch (存储)—>Kibana (展示)
Logstash(采集)—> Logstash(聚合、处理)—> ElasticSearch (存储)—>Kibana (展示)
Filebeat(采集、处理)—> ElasticSearch (存储)—>Kibana (展示)
Filebeat(采集)—> Logstash(聚合、处理)—> ElasticSearch (存储)—>Kibana (展示)
Filebeat(采集)—> Kafka/Redis(消峰) —> Logstash(聚合、处理)—> ElasticSearch (存储)—>Kibana (展示)

# efk 组件

1
2
3
Elasticsearch 是一个分布式的免费开源搜索和分析引擎,适用于包括文本、数字、地理空间、结构化和非结构化数据等在内的所有类型的数据。Elasticsearch 在 Apache Lucene 的基础上开发而成,由 Elasticsearch N.V.(即现在的 Elastic)于 2010 年首次发布。
Elasticsearch 以其简单的 REST 风格 API、分布式特性、速度和可扩展性而闻名,是 Elastic Stack 的核心组件;Elastic Stack 是一套适用于数据采集、扩充、存储、分析和可视化的免费开源工具。
人们通常将 Elastic Stack 称为 ELK Stack(代指 Elasticsearch、Logstash 和 Kibana),目前 Elastic Stack 包括一系列丰富的轻量型数据采集代理,这些代理统称为 Beats,可用来向 Elasticsearch 发送数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
filebeat是Beats中的一员。
  Beats是一个轻量级日志采集器,Beats家族有6个成员,早期的ELK架构中使用Logstash收集、解析日志,但是Logstash对内存、cpu、io等资源消耗比较高。相比Logstash,Beats所占系统的CPU和内存几乎可以忽略不计。

目前Beats包含六种工具:
1、Packetbeat:网络数据(收集网络流量数据)
2、Metricbeat:指标(收集系统、进程和文件系统级别的CPU和内存使用情况等数据)
3、Filebeat:日志文件(收集文件数据)
4、Winlogbeat:windows事件日志(收集Windows事件日志数据)
5、Auditbeat:审计数据(收集审计日志)
6、Heartbeat:运行时间监控(收集系统运行时的数据)

Filebeat是用于转发和收集日志数据的轻量级传送工具。Filebeat监视你指定的日志文件或位置,收集日志事件,并将它们转发到Elasticsearch或 Logstash中。
Filebeat的工作方式如下:启动Filebeat时,它将启动一个或多个输入,这些输入将在为日志数据指定的位置中查找。对于Filebeat所找到的每个日志,Filebeat都会启动收集器。每个收集器都读取单个日志以获取新内容,并将新日志数据发送到libbeat,libbeat将聚集事件,并将聚集的数据发送到为Filebeat配置的输出。

image-20230530142835668

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
Filebeat 有两个主要组件:
harvester:一个harvester负责读取一个单个文件的内容。harvester逐行读取每个文件,并把这些内容发送到输出。每个文件启动一个harvester。
Input:一个input负责管理harvesters,并找到所有要读取的源。如果input类型是log,则input查找驱动器上与已定义的log日志路径匹配的所有文件,并为每个文件启动一个harvester。

Filebeat工作原理
在任何环境下,应用程序都有停机的可能性。 Filebeat 读取并转发日志行,如果中断,则会记住所有事件恢复联机状态时所在位置。
Filebeat带有内部模块(auditd,Apache,Nginx,System和MySQL),可通过一个指定命令来简化通用日志格式的收集,解析和可视化。
FileBeat 不会让你的管道超负荷。FileBeat 如果是向 Logstash 传输数据,当 Logstash 忙于处理数据,会通知 FileBeat 放慢读取速度。一旦拥塞得到解决,FileBeat将恢复到原来的速度并继续传播。
Filebeat保持每个文件的状态,并经常刷新注册表文件中的磁盘状态。状态用于记住harvester正在读取的最后偏移量,并确保发送所有日志行。Filebeat将每个事件的传递状态存储在注册表文件中。所以它能保证事件至少传递一次到配置的输出,没有数据丢失。

传输方案
1、output.elasticsearch
如果你希望使用 filebeat 直接向 elasticsearch 输出数据,需要配置 output.elasticsearch
output.elasticsearch:
hosts: ["192.168.40.180:9200"]
2、output.logstash
如果使用filebeat向 logstash输出数据,然后由 logstash 再向elasticsearch 输出数据,需要配置 output.logstash。 logstash 和 filebeat 一起工作时,如果 logstash 忙于处理数据,会通知FileBeat放慢读取速度。一旦拥塞得到解决,FileBeat 将恢复到原来的速度并继续传播。这样,可以减少管道超负荷的情况。
output.logstash:
hosts: ["192.168.40.180:5044"]

3、output.kafka
如果使用filebeat向kafka输出数据,然后由 logstash 作为消费者拉取kafka中的日志,并再向elasticsearch 输出数据,需要配置 output.logstash
output.kafka:
enabled: true
hosts: ["192.168.40.180:9092"]
topic: elfk8stest
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
logstash组件介绍
Logstash是一个开源数据收集引擎,具有实时管道功能。Logstash可以动态地将来自不同数据源的数据统一起来,并将数据标准化到你所选择的目的地。Logstash 是一个应用程序日志、事件的传输、处理、管理和搜索的平台。你可以用它来统一对应用程序日志进行收集管理,提供 Web 接口用于查询和统计。
输入:采集各种样式、大小和来源的数据
数据往往以各种各样的形式,或分散或集中地存在于很多系统中。Logstash 支持各种输入选择 ,可以在同一时间从众多常用来源捕捉事件。能够以连续的流式传输方式,轻松地从你的日志、指标、Web 应用、数据存储以及各种 AWS 服务采集数据。

过滤器:实时解析和转换数据
数据从源传输到存储库的过程中,Logstash 过滤器能够解析各个事件,识别已命名的字段以构建结构,并将它们转换成通用格式,以便更轻松、更快速地分析和实现商业价值。
Logstash 能够动态地转换和解析数据,不受格式或复杂度的影响:
1、利用 Grok 从非结构化数据中派生出结构
2、从 IP 地址破译出地理坐标
3、将 PII 数据匿名化,完全排除敏感字段
4、整体处理不受数据源、格式或架构的影响

输出:选择你的存储,导出你的数据
尽管 Elasticsearch 是我们的首选输出方向,能够为我们的搜索和分析带来无限可能,但它并非唯一选择。Logstash 提供众多输出选择,你可以将数据发送到你要指定的地方。

image-20230530143914957

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
Logstash 有两个必要元素:input 和 output ,一个可选元素:filter。 这三个元素,分别代表 Logstash 事件处理的三个阶段:输入 > 过滤器 > 输出

Input负责从数据源采集数据。
filter 将数据修改为你指定的格式或内容。
output 将数据传输到目的地。

在实际应用场景中,通常输入、输出、过滤器不止一个。Logstash 的这三个元素都使用插件式管理方式,可以根据应用需要,灵活的选用各阶段需要的插件,并组合使用。

常用input模块
file:从文件系统上的文件读取
syslog:在众所周知的端口514上侦听系统日志消息,并根据RFC3164格式进行解析
redis:从redis服务器读取,使用redis通道和redis列表。 Redis经常用作集中式Logstash安装中的“代理”,它将接收来自远程Logstash“托运人”的Logstash事件排队。
beats:处理由Filebeat发送的事件。

常用的filter模块
过滤器是Logstash管道中的中间处理设备。可以将条件过滤器组合在一起,对事件执行操作。
grok:解析和结构任意文本。 Grok目前是Logstash中将非结构化日志数据解析为结构化和可查询的最佳方法。
mutate:对事件字段执行一般转换。可以重命名,删除,替换和修改事件中的字段。
drop:完全放弃一个事件,例如调试事件。
clone:制作一个事件的副本,可能会添加或删除字段。
geoip:添加有关IP地址的地理位置的信息

常用output
elasticsearch:将事件数据发送给 Elasticsearch(推荐模式)。
file:将事件数据写入文件或磁盘。
graphite:将事件数据发送给 graphite(一个流行的开源工具,存储和绘制指标, http://graphite.readthedocs.io/en/latest/)。
statsd:将事件数据发送到 statsd (这是一种侦听统计数据的服务,如计数器和定时器,通过UDP发送并将聚合发送到一个或多个可插入的后端服务)。

input {
kafka {
bootstrap_servers => "192.168.40.180:9092"
auto_offset_reset => "latest"
consumer_threads => 5
decorate_events => true
topics => ["elktest"]
}
}

output {
elasticsearch {
hosts => ["192.168.40.180:9200"]
index => "elkk8stest-%{+YYYY.MM.dd}"
}
}
1
2
3
4
5
fluentd是一个针对日志的收集、处理、转发系统。通过丰富的插件系统,可以收集来自于各种系统或应用的日志,转化为用户指定的格式后,转发到用户所指定的日志存储系统之中。
fluentd 常常被拿来和Logstash比较,我们常说ELK,L就是这个agent。fluentd 是随着Docker和es一起流行起来的agent。
fluentd 比 logstash 更省资源;
更轻量级的 fluent-bid 对应 filebeat,作为部署在结点上的日志收集器;
fluentd 有更多强大、开放的插件数量和社区。插件多,也非常灵活,规则也不复杂。
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
fluentd、filebeat、logstash对比分析

Logstash
Logstash是一个开源数据收集引擎,具有实时管道功能。Logstash可以动态地将来自不同数据源的数据统一起来,并将数据标准化到你所选择的目的地。
优势
Logstash 主要的优点就是它的灵活性,主要因为它有很多插件,详细的文档以及直白的配置格式让它可以在多种场景下应用。我们基本上可以在网上找到很多资源,几乎可以处理任何问题。
劣势
Logstash 致命的问题是它的性能以及资源消耗(默认的堆大小是 1GB)。尽管它的性能在近几年已经有很大提升,与它的替代者们相比还是要慢很多的。这里有 Logstash 与 rsyslog 性能对比以及Logstash 与 filebeat 的性能对比。它在大数据量的情况下会是个问题。
另一个问题是它目前不支持缓存,目前的典型替代方案是将 Redis 或 Kafka 作为中心缓冲池:
因为 Logstash 自身的灵活性以及网络上丰富的资料,Logstash 适用于原型验证阶段使用,或者解析非常的复杂的时候。在不考虑服务器资源的情况下,如果服务器的性能足够好,我们也可以为每台服务器安装 Logstash 。我们也不需要使用缓冲,因为文件自身就有缓冲的行为,而 Logstash 也会记住上次处理的位置。
如果服务器性能较差,并不推荐为每个服务器安装 Logstash ,这样就需要一个轻量的日志传输工具,将数据从服务器端经由一个或多个 Logstash 中心服务器传输到 Elasticsearch:

Filebeat
作为 Beats 家族的一员,Filebeat 是一个轻量级的日志传输工具,它的存在正弥补了 Logstash 的缺点:Filebeat 作为一个轻量级的日志传输工具可以将日志推送到中心 Logstash。
在版本 5.x 中,Elasticsearch 具有解析的能力(像 Logstash 过滤器)— Ingest。这也就意味着可以将数据直接用 Filebeat 推送到 Elasticsearch,并让 Elasticsearch 既做解析的事情,又做存储的事情。也不需要使用缓冲,因为 Filebeat 也会和 Logstash 一样记住上次读取的偏移,如果需要缓冲(例如,不希望将日志服务器的文件系统填满),可以使用 Redis/Kafka,因为 Filebeat 可以与它们进行通信。
优势
Filebeat 只是一个二进制文件没有任何依赖。它占用资源极少,尽管它还十分年轻,正式因为它简单,所以几乎没有什么可以出错的地方,所以它的可靠性还是很高的。它也为我们提供了很多可以调节的点,例如:它以何种方式搜索新的文件,以及当文件有一段时间没有发生变化时,何时选择关闭文件句柄。
劣势
Filebeat 的应用范围十分有限,所以在某些场景下我们会碰到问题。例如,如果使用 Logstash 作为下游管道,我们同样会遇到性能问题。正因为如此,Filebeat 的范围在扩大。开始时,它只能将日志发送到 Logstash 和 Elasticsearch,而现在它可以将日志发送给 Kafka 和 Redis,在 5.x 版本中,它还具备过滤的能力。

Fluentd
Fluentd 创建的初衷主要是尽可能的使用 JSON 作为日志输出,所以传输工具及其下游的传输线不需要猜测子字符串里面各个字段的类型。这样,它为几乎所有的语言都提供库,这也意味着,我们可以将它插入到我们自定义的程序中。
优势
和多数 Logstash 插件一样,Fluentd 插件是用 Ruby 语言开发的非常易于编写维护。所以它数量很多,几乎所有的源和目标存储都有插件(各个插件的成熟度也不太一样)。这也意味这我们可以用 Fluentd 来串联所有的东西。
劣势
因为在多数应用场景下,我们会通过 Fluentd 得到结构化的数据,它的灵活性并不好。但是我们仍然可以通过正则表达式,来解析非结构化的数据。尽管,性能在大多数场景下都很好,但它并不是最好的,和 syslog-ng 一样,它的缓冲只存在与输出端,单线程核心以及 Ruby GIL 实现的插件意味着它大的节点下性能是受限的,不过,它的资源消耗在大多数场景下是可以接受的。对于小的或者嵌入式的设备,可能需要看看 Fluent Bit,它和 Fluentd 的关系与 Filebeat 和 Logstash 之间的关系类似。

Logagent
Logagent 是 Sematext 提供的传输工具,它用来将日志传输到 Logsene(一个基于 SaaS 平台的 Elasticsearch API),因为 Logsene 会暴露 Elasticsearch API,所以 Logagent 可以很容易将数据推送到 Elasticsearch 。
优势
可以获取 /var/log 下的所有信息,解析各种格式(Elasticsearch,Solr,MongoDB,Apache HTTPD等等),它可以掩盖敏感的数据信息,例如,个人验证信息(PII),出生年月日,信用卡号码,等等。它还可以基于 IP 做 GeoIP 丰富地理位置信息(例如,access logs)。同样,它轻量又快速,可以将其置入任何日志块中。在新的 2.0 版本中,它以第三方 node.js 模块化方式增加了支持对输入输出的处理插件。重要的是 Logagent 有本地缓冲,所以不像 Logstash ,在数据传输目的地不可用时会丢失日志。
劣势
尽管 Logagent 有些比较有意思的功能(例如,接收 Heroku 或 CloudFoundry 日志),但是它并没有 Logstash 灵活。

logtail
阿里云日志服务的生产者,目前在阿里集团内部机器上运行,经过3年多时间的考验,目前为阿里公有云用户提供日志收集服务。
采用C++语言实现,对稳定性、资源控制、管理等下过很大的功夫,性能良好。相比于logstash、fluentd的社区支持,logtail功能较为单一,专注日志收集功能。
优势
logtail占用机器cpu、内存资源最少,结合阿里云日志服务的E2E体验良好。
劣势
logtail目前对特定日志类型解析的支持较弱,后续需要把这一块补起来。

rsyslog
绝大多数 Linux 发布版本默认的 syslog 守护进程,rsyslog 可以做的不仅仅是将日志从 syslog socket 读取并写入 /var/log/messages 。它可以提取文件、解析、缓冲(磁盘和内存)以及将它们传输到多个目的地,包括 Elasticsearch 。可以从此处找到如何处理 Apache 以及系统日志。
优势
rsyslog 是经测试过的最快的传输工具。如果只是将它作为一个简单的 router/shipper 使用,几乎所有的机器都会受带宽的限制,但是它非常擅长处理解析多个规则。它基于语法的模块(mmnormalize)无论规则数目如何增加,它的处理速度始终是线性增长的。这也就意味着,如果当规则在 20-30 条时,如解析 Cisco 日志时,它的性能可以大大超过基于正则式解析的 grok ,达到 100 倍(当然,这也取决于 grok 的实现以及 liblognorm 的版本)。
它同时也是我们能找到的最轻的解析器,当然这也取决于我们配置的缓冲。
劣势
rsyslog 的配置工作需要更大的代价(这里有一些例子),这让两件事情非常困难:
文档难以搜索和阅读,特别是那些对术语比较陌生的开发者。
5.x 以上的版本格式不太一样(它扩展了 syslogd 的配置格式,同时也仍然支持旧的格式),尽管新的格式可以兼容旧格式,但是新的特性(例如,Elasticsearch 的输出)只在新的配置下才有效,然后旧的插件(例如,Postgres 输出)只在旧格式下支持。

# 安装 elasticsearch

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
1.创建一个kube-logging名称空间,将EFK组件安装到该名称空间中。

在这个名称空间下去安装日志收集组件efk,首先,我们需要部署一个有3个节点的Elasticsearch集群。我们使用3个Elasticsearch Pods可以避免高可用中的多节点群集中发生的“裂脑”的问题。 Elasticsearch脑裂可参考如下:https://www.elastic.co/guide/en/elasticsearch/reference/current/modules-node.html#split-brain

2.创建一个headless service的Kubernetes服务,服务名称是elasticsearch,这个服务将为3个Pod定义一个DNS域。headless service不具备负载均衡也没有IP。
kind: Service
apiVersion: v1
metadata:
name: elasticsearch
namespace: kube-logging
labels:
app: elasticsearch
spec:
selector:
app: elasticsearch
clusterIP: None
ports:
- port: 9200
name: rest
- port: 9300
name: inter-node


3. 创建Storageclass,实现存储类动态供给
yum install nfs-utils -y
systemctl enable nfs --now
mkdir /data/v1 -p
/data/v1 *(rw,no_root_squash)
exportfs -arv
systemctl restart nfs

4.创建nfs作为存储的供应商
1、创建sa
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-provisioner
2、对sa做rbac授权
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
- apiGroups: [""]
resources: ["services", "endpoints"]
verbs: ["get"]
- apiGroups: ["extensions"]
resources: ["podsecuritypolicies"]
resourceNames: ["nfs-provisioner"]
verbs: ["use"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-provisioner
subjects:
- kind: ServiceAccount
name: nfs-provisioner
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-provisioner
apiGroup: rbac.authorization.k8s.io

vim /etc/kubernetes/manifests/kube-apiserver.yaml
- --feature-gates=RemoveSelfLink=false
kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml # 可能导致6443暂时无法访问
kubectl delete pod -n kube-system kube-apiserver # 高可用时需要改一个文件删一个api-server

工作节点:
docker load -i nfs-client-provisioner.tar.gz

#通过deployment创建pod用来运行nfs-provisioner
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-provisioner
spec:
selector:
matchLabels:
app: nfs-provisioner
replicas: 2
strategy:
type: Recreate
template:
metadata:
labels:
app: nfs-provisioner
spec:
serviceAccount: nfs-provisioner
containers:
- name: nfs-provisioner
image: registry.cn-hangzhou.aliyuncs.com/open-ali/xianchao/nfs-client-provisioner:v1
imagePullPolicy: IfNotPresent
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: example.com/nfs
- name: NFS_SERVER
value: 192.168.13.180
- name: NFS_PATH
value: /data/v1
volumes:
- name: nfs-client-root
nfs:
server: 192.168.13.180
path: /data/v1
#创建stoorageclass
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: do-block-storage
provisioner: example.com/nfs

# 安装elasticsearch集群
docker load -i elasticsearch_7_2_0.tar.gz

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: es-cluster
namespace: kube-logging
spec:
serviceName: elasticsearch
replicas: 2
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:7.2.0
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
ports:
- containerPort: 9200
name: rest
protocol: TCP
- containerPort: 9300
name: inter-node
protocol: TCP
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
env:
- name: cluster.name
value: k8s-logs
- name: node.name
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: discovery.seed_hosts
value: "es-cluster-0.elasticsearch.kube-logging.svc.cluster.local,es-cluster-1.elasticsearch.kube-logging.svc.cluster.local,es-cluster-2.elasticsearch.kube-logging.svc.cluster.local"
- name: cluster.initial_master_nodes
value: "es-cluster-0,es-cluster-1,es-cluster-2"
- name: ES_JAVA_OPTS
value: "-Xms512m -Xmx512m"
initContainers:
- name: fix-permissions
image: busybox
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "chown -R 1000:1000 /usr/share/elasticsearch/data"]
securityContext:
privileged: true
volumeMounts:
- name: data
mountPath: /usr/share/elasticsearch/data
- name: increase-vm-max-map
image: busybox
imagePullPolicy: IfNotPresent
command: ["sysctl", "-w", "vm.max_map_count=262144"]
securityContext:
privileged: true
- name: increase-fd-ulimit
image: busybox
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "ulimit -n 65536"]
securityContext:
privileged: true
volumeClaimTemplates:
- metadata:
name: data
labels:
app: elasticsearch
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: do-block-storage
resources:
requests:
storage: 10Gi

pod部署完成之后,可以通过REST API检查elasticsearch集群是否部署成功,使用下面的命令将本地端口9200转发到 Elasticsearch 节点(如es-cluster-0)对应的端口:

kubectl port-forward es-cluster-0 9200:9200 --namespace=kube-logging
curl http://localhost:9200/_cat/
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
# 创建kibaba
apiVersion: v1
kind: Service
metadata:
name: kibana
namespace: kube-logging
labels:
app: kibana
spec:
ports:
- port: 5601
selector:
app: kibana
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
namespace: kube-logging
labels:
app: kibana
spec:
replicas: 1
selector:
matchLabels:
app: kibana
template:
metadata:
labels:
app: kibana
spec:
containers:
- name: kibana
image: docker.elastic.co/kibana/kibana:7.2.0
imagePullPolicy: IfNotPresent
resources:
limits:
cpu: 1000m
requests:
cpu: 100m
env:
- name: ELASTICSEARCH_URL
value: http://elasticsearch.kube-logging.svc.cluster.local:9200
ports:
- containerPort: 5601


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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# 安装fluentd组件
我们使用daemonset控制器部署fluentd组件,这样可以保证集群中的每个节点都可以运行同样fluentd的pod副本,这样就可以收集k8s集群中每个节点的日志,在k8s集群中,容器应用程序的输入输出日志会重定向到node节点里的json文件中,fluentd可以tail和过滤以及把日志转换成指定的格式发送到elasticsearch集群中。除了容器日志,fluentd也可以采集kubelet、kube-proxy、docker的日志。

apiVersion: v1
kind: ServiceAccount
metadata:
name: fluentd
namespace: kube-logging
labels:
app: fluentd
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluentd
labels:
app: fluentd
rules:
- apiGroups:
- ""
resources:
- pods
- namespaces
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: fluentd
roleRef:
kind: ClusterRole
name: fluentd
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: fluentd
namespace: kube-logging
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-logging
labels:
app: fluentd
spec:
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
serviceAccount: fluentd
serviceAccountName: fluentd
tolerations:
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1.4.2-debian-elasticsearch-1.1
imagePullPolicy: IfNotPresent
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch.kube-logging.svc.cluster.local"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
- name: FLUENT_ELASTICSEARCH_SCHEME
value: "http"
- name: FLUENTD_SYSTEMD_CONF
value: disable
resources:
limits:
memory: 512Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts:
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
terminationGracePeriodSeconds: 30
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers

# 测试 pod

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: counter
spec:
containers:
- name: count
image: busybox
imagePullPolicy: IfNotPresent
args: [/bin/sh, -c,'i=0; while true; do echo "$i: $(date)"; i=$((i+1)); sleep 1; done']

image-20230531143741555

image-20230531143701392

[Kibana Query Language | Kibana Guide 7.2] | Elastic

# ceph

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
ceph是一种开源的分布式的存储系统
包含以下几种存储类型:
块存储(rbd),对象存储(RADOS Fateway),文件系统(cephfs)

1.块存储(rbd):
块是一个字节序列(例如,512字节的数据块)。 基于块的存储接口是使用旋转介质(如硬盘,CD,软盘甚至传统的9轨磁带)存储数据的最常用方法;Ceph块设备是精简配置,可调整大小并存储在Ceph集群中多个OSD条带化的数据。 Ceph块设备利用RADOS功能,如快照,复制和一致性。 Ceph的RADOS块设备(RBD)使用内核模块或librbd库与OSD进行交互;Ceph的块设备为内核模块或QVM等KVM以及依赖libvirt和QEMU与Ceph块设备集成的OpenStack和CloudStack等基于云的计算系统提供高性能和无限可扩展性。 可以使用同一个集群同时运行Ceph RADOS Gateway,CephFS文件系统和Ceph块设备。
linux系统中,ls /dev/下有很多块设备文件,这些文件就是我们添加硬盘时识别出来的;
rbd就是由Ceph集群提供出来的块设备。可以这样理解,sda是通过数据线连接到了真实的硬盘,而rbd是通过网络连接到了Ceph集群中的一块存储区域,往rbd设备文件写入数据,最终会被存储到Ceph集群的这块区域中;
总结:块设备可理解成一块硬盘,用户可以直接使用不含文件系统的块设备,也可以将其格式化成特定的文件系统,由文件系统来组织管理存储空间,从而为用户提供丰富而友好的数据操作支持。

2.文件系统cephfs
Ceph文件系统(CephFS)是一个符合POSIX标准的文件系统,它使用Ceph存储集群来存储其数据。 Ceph文件系统使用与Ceph块设备相同的Ceph存储集群系统。
用户可以在块设备上创建xfs文件系统,也可以创建ext4等其他文件系统,Ceph集群实现了自己的文件系统来组织管理集群的存储空间,用户可以直接将Ceph集群的文件系统挂载到用户机上使用,Ceph有了块设备接口,在块设备上完全可以构建一个文件系统,那么Ceph为什么还需要文件系统接口呢?
主要是因为应用场景的不同,Ceph的块设备具有优异的读写性能,但不能多处挂载同时读写,目前主要用在OpenStack上作为虚拟磁盘,而Ceph的文件系统接口读写性能较块设备接口差,但具有优异的共享性。

3.对象存储
Ceph对象存储使用Ceph对象网关守护进程(radosgw),它是一个用于与Ceph存储集群交互的HTTP服务器。由于它提供与OpenStack Swift和Amazon S3兼容的接口,因此Ceph对象网关具有自己的用户管理。 Ceph对象网关可以将数据存储在用于存储来自Ceph文件系统客户端或Ceph块设备客户端的数据的相同Ceph存储集群中
使用方式就是通过http协议上传下载删除对象(文件即对象)。
老问题来了,有了块设备接口存储和文件系统接口存储,为什么还整个对象存储呢?
Ceph的块设备存储具有优异的存储性能但不具有共享性,而Ceph的文件系统具有共享性然而性能较块设备存储差,为什么不权衡一下存储性能和共享性,整个具有共享性而存储性能好于文件系统存储的存储呢,对象存储就这样出现了。

分布式存储的优点:
高可靠:既满足存储读取不丢失,还要保证数据长期存储。 在保证部分硬件损坏后依然可以保证数据安全
高性能:读写速度快
可扩展:分布式存储的优势就是“分布式”,所谓的“分布式”就是能够将多个物理节点整合在一起形成共享的存储池,节点可以线性扩充,这样可以源源不断的通过扩充节点提升性能和扩大容量,这是传统存储阵列无法做到的
1
2
3
4
5
6
7
8
9
10
Ceph核心组件介绍
在ceph集群中,不管你是想要提供对象存储,块设备存储,还是文件系统存储,所有Ceph存储集群部署都是从设置每个Ceph节点,网络和Ceph存储开始 的。 Ceph存储集群至少需要一个Ceph Monitor,Ceph Manager和Ceph OSD(对象存储守护进程)。 运行Ceph Filesystem客户端时也需要Ceph元数据服务器。

Monitors:Ceph监视器(ceph-mon)维护集群状态的映射,包括监视器映射,管理器映射,OSD映射和CRUSH映射。这些映射是Ceph守护进程相互协调所需的关键集群状态。监视器还负责管理守护进程和客户端之间的身份验证。冗余和高可用性通常至少需要三个监视器。

Managers:Ceph Manager守护程序(ceph-mgr)负责跟踪运行时指标和Ceph集群的当前状态,包括存储利用率,当前性能指标和系统负载。 Ceph Manager守护进程还托管基于python的模块来管理和公开Ceph集群信息,包括基于Web的Ceph Dashboard和REST API。高可用性通常至少需要两名Managers。

Ceph OSD:Ceph OSD(对象存储守护进程,ceph-osd)存储数据,处理数据复制,恢复,重新平衡,并通过检查其他Ceph OSD守护进程来获取心跳,为Ceph监视器和管理器提供一些监视信息。冗余和高可用性通常至少需要3个Ceph OSD。

MDS:Ceph元数据服务器(MDS,ceph-mds)代表Ceph文件系统存储元数据(即,Ceph块设备和Ceph对象存储不使用MDS)。 Ceph元数据服务器允许POSIX文件系统用户执行基本命令(如ls,find等),而不会给Ceph存储集群带来巨大负担。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 安装ceph集群
#在master1-admin节点安装ceph-deploy
yum install python-setuptools ceph-deploy -y
#在master1-admin、node1-monitor和node2-osd节点安装ceph
yum install ceph ceph-radosgw -y

[root@master1-admin ~]# ceph --version
ceph version 10.2.11 (e4b061b47f07f583c92a050d9e84b1813a35671e)

#创建一个目录,用于保存 ceph-deploy 生成的配置文件信息的
[root@master1-admin ceph ~]# cd /etc/ceph
[root@master1-admin ceph]# ceph-deploy new master1-admin node1-monitor node2-osd
# 执行会生成如下文件
[root@master1-admin ceph]# ls
ceph.conf ceph-deploy-ceph.log ceph.mon.keyring rbdmap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1.# 追加如下参数
osd_pool_default_size = 2
mon clock drift allowed = 0.500
mon clock drift warn backoff = 10

mon clock drift allowed #监视器间允许的时钟漂移量默认值0.05
mon clock drift warn backoff #时钟偏移警告的退避指数。默认值5
ceph对每个mon之间的时间同步延时默认要求在0.05s之间,这个时间有的时候太短了。所以如果ceph集群如果出现clock问题就检查ntp时间同步或者适当放宽这个误差时间。
cephx是认证机制是整个 Ceph 系统的用户名/密码

2、配置初始monitor、收集所有的密钥
[root@master1-admin]# cd /etc/ceph
[root@master1-admin]# ceph-deploy mon create-initial
[root@master1-admin]# ls *.keyring
ceph.bootstrap-mds.keyring ceph.bootstrap-mgr.keyring ceph.bootstrap-osd.keyring ceph.bootstrap-rgw.keyring ceph.client.admin.keyring ceph.mon.keyring
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
# 准备osd
cd /etc/ceph/
ceph-deploy osd prepare master1-admin:/dev/sdb
ceph-deploy osd prepare node1-monitor:/dev/sdb
ceph-deploy osd prepare node2-osd:/dev/sdb


#激活osd
ceph-deploy osd activate master1-admin:/dev/sdb1
ceph-deploy osd activate node1-monitor:/dev/sdb1
ceph-deploy osd activate node2-osd:/dev/sdb1

# 查看状态
ceph-deploy osd list master1-admin node1-monitor node2-osd

# 创建ceph文件系统
创建mds
[root@ master1-admin ceph]# ceph-deploy mds create master1-admin node1-monitor node2-osd

查看ceph当前文件系统
[root@ master1-admin ceph]# ceph fs ls

No filesystems enabled

一个cephfs至少要求两个librados存储池,一个为data,一个为metadata。当配置这两个存储池时,注意:
1. 为metadata pool设置较高级别的副本级别,因为metadata的损坏可能导致整个文件系统不用
2. 建议,metadata pool使用低延时存储,比如SSD,因为metadata会直接影响客户端的响应速度。

创建存储池
[root@ master1-admin ceph]# ceph osd pool create cephfs_data 128
pool 'cephfs_data' created
[root@ master1-admin ceph]# ceph osd pool create cephfs_metadata 128
pool 'cephfs_metadata' created

关于创建存储池
确定 pg_num 取值是强制性的,因为不能自动计算。下面是几个常用的值:
*少于 5 个 OSD 时可把 pg_num 设置为 128
*OSD 数量在 5 到 10 个时,可把 pg_num 设置为 512
*OSD 数量在 10 到 50 个时,可把 pg_num 设置为 4096
*OSD 数量大于 50 时,你得理解权衡方法、以及如何自己计算 pg_num 取值
*自己计算 pg_num 取值时可借助 pgcalc 工具
随着 OSD 数量的增加,正确的 pg_num 取值变得更加重要,因为它显著地影响着集群的行为、以及出错时的数据持久性(即灾难性事件导致数据丢失的概率)。

创建文件系统
创建好存储池后,你就可以用 fs new 命令创建文件系统了
[root@ master1-admin ceph]# ceph fs new xianchao cephfs_metadata cephfs_data
new fs with metadata pool 2 and data pool 1
其中:new后的fsname 可自定义

[root@ master1-admin ceph]# ceph fs ls #查看创建后的cephfs
[root@ master1-admin ceph]# ceph mds stat #查看mds节点状态

[root@master1-admin ceph]# ceph -s
cluster 66e0ffc8-fb71-40ee-bcc0-a6e87713ddac
health HEALTH_WARN
clock skew detected on mon.node2-osd

# k8s 挂载 ceph rbd

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
# kubernetes要想使用ceph,需要在k8s的每个node节点安装ceph-common,把ceph节点上的ceph.repo文件拷贝到k8s各个节点/etc/yum.repos.d/目录下,然后在k8s的各个节点yum install ceph-common -y

[root@master1-admin]# scp /etc/yum.repos.d/ceph.repo 192.168.40.180:/etc/yum.repos.d/
[root@master1-admin]# scp /etc/yum.repos.d/ceph.repo 192.168.40.181:/etc/yum.repos.d/

[root@masterk8s ~]# yum install ceph-common -y
[root@nodek8s ~]# yum install ceph-common -y

[root@master1-admin ~]# scp /etc/ceph/* 192.168.40.180:/etc/ceph/
[root@master1-admin ~]# scp /etc/ceph/* 192.168.40.181:/etc/ceph/


#创建ceph rbd
[root@master1-admin ~]# ceph osd pool create k8srbd1 6

[root@master1-admin ~]# rbd create rbda -s 1024 -p k8srbd1

[root@master1-admin ~]# rbd feature disable k8srbd1/rbda object-map fast-diff deep-flatten

apiVersion: v1
kind: Pod
metadata:
name: testrbd
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: testrbd
mountPath: /mnt
volumes:
- name: testrbd
rbd:
monitors:
- '192.168.13.201:6789'
- '192.168.13.200:6789'
- '192.168.13.202:6789'
pool: k8srbd1
image: rbda
fsType: xfs
readOnly: false
user: admin
keyring: /etc/ceph/ceph.client.admin.keyring

k8srbd1下的rbda被pod挂载了,那其他pod就不能占用这个k8srbd1下的rbda了

创建deployment的时候也只有一个能running,因为只有一个pod能用这个块设备

# ceph rbd 生成 pv

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
1、创建ceph-secret这个k8s secret对象,这个secret对象用于k8s volume插件访问ceph集群,获取client.admin的keyring值,并用base64编码,在master1-admin(ceph管理节点)操作
[root@master1-admin ~]# ceph auth get-key client.admin | base64

QVFEN0VIZGswYmZ1Q0JBQXhRK1hGUFV0d1BraTlCNjBSbndiYmc9PQ==

apiVersion: v1
kind: Secret
metadata:
name: ceph-secret
data:
key: QVFBWk0zeGdZdDlhQXhBQVZsS0poYzlQUlBianBGSWJVbDNBenc9PQ==

3.回到ceph 管理节点创建pool池
[root@master1-admin ~]# ceph osd pool create k8stest 6
pool 'k8stest' created
[root@master1-admin ~]# rbd create rbda -s 1024 -p k8stest
[root@master1-admin ~]# rbd feature disable k8stest/rbda object-map fast-diff deep-flatten

apiVersion: v1
kind: PersistentVolume
metadata:
name: ceph-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteOnce
rbd:
monitors:
- '192.168.13.201:6789'
- '192.168.13.200:6789'
- '192.168.13.202:6789'
pool: k8stest
image: rbda
user: admin
secretRef:
name: ceph-secret
fsType: xfs
readOnly: false
persistentVolumeReclaimPolicy: Recycle

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: ceph-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2 # tells deployment to run 2 pods matching the template
template: # create pods using pod definition in this template
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- mountPath: "/ceph-data"
name: ceph-data
volumes:
- name: ceph-data
persistentVolumeClaim:
claimName: ceph-pvc

通过上面实验可以发现pod是可以以ReadWriteOnce共享挂载相同的pvc的

注意:ceph rbd块存储的特点
ceph rbd块存储能在同一个node上跨pod以ReadWriteOnce共享挂载
ceph rbd块存储能在同一个node上同一个pod多个容器中以ReadWriteOnce共享挂载
ceph rbd块存储不能跨node以ReadWriteOnce共享挂载
如果一个使用ceph rdb的pod所在的node挂掉,这个pod虽然会被调度到其它node,但是由于rbd不能跨node多次挂载和挂掉的pod不能自动解绑pv的问题,这个新pod不会正常运行

Deployment更新特性:
deployment触发更新的时候,它确保至少所需 Pods 75% 处于运行状态(最大不可用比例为 25%)。故像一个pod的情况,肯定是新创建一个新的pod,新pod运行正常之后,再关闭老的pod。
默认情况下,它可确保启动的 Pod 个数比期望个数最多多出 25%

问题:
结合ceph rbd共享挂载的特性和deployment更新的特性,我们发现原因如下:
由于deployment触发更新,为了保证服务的可用性,deployment要先创建一个pod并运行正常之后,再去删除老pod。而如果新创建的pod和老pod不在一个node,就会导致此故障。

解决办法:
1,使用能支持跨node和pod之间挂载的共享存储,例如cephfs,GlusterFS等
2,给node添加label,只允许deployment所管理的pod调度到一个固定的node上。(不建议,这个node挂掉的话,服务就故障了)

# 基于 storage 动态生成 pv

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
[root@master1-admin]# chmod 777  -R  /etc/ceph/*
[root@node1-monitor ~]# chmod 777 -R /etc/ceph/*
[root@node2-osd]# chmod 777 -R /etc/ceph/*
[root@\master~]# chmod 777 -R /etc/ceph/*
[root@node]# chmod 777 -R /etc/ceph/*

[root@master1-admin]# mkdir /root/.ceph/
[root@master1-admin]# cp -ar /etc/ceph/ /root/.ceph/
[root@node1-monitor ~]#mkdir /root/.ceph/
[root@node1-monitor ~]#cp -ar /etc/ceph/ /root/.ceph/
[root@node2-osd]# mkdir /root/.ceph/
[root@node2-osd]# cp -ar /etc/ceph/ /root/.ceph/
[root@master~]#mkdir /root/.ceph/
[root@master ~]#cp -ar /etc/ceph/ /root/.ceph/
[root@node1]# mkdir /root/.ceph/
[root@node1 ~]#cp -ar /etc/ceph/ /root/.ceph/

1、创建rbd的供应商provisioner
#把rbd-provisioner.tar.gz上传到xianchaonode1上,手动解压
[root@xianchaonode1 ~]# docker load -i rbd-provisioner.tar.gz

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: rbd-provisioner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
- apiGroups: [""]
resources: ["services"]
resourceNames: ["kube-dns","coredns"]
verbs: ["list", "get"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: rbd-provisioner
subjects:
- kind: ServiceAccount
name: rbd-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: rbd-provisioner
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: rbd-provisioner
rules:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: rbd-provisioner
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: rbd-provisioner
subjects:
- kind: ServiceAccount
name: rbd-provisioner
namespace: default
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: rbd-provisioner
spec:
selector:
matchLabels:
app: rbd-provisioner
replicas: 1
strategy:
type: Recreate
template:
metadata:
labels:
app: rbd-provisioner
spec:
containers:
- name: rbd-provisioner
image: quay.io/xianchao/external_storage/rbd-provisioner:v1
imagePullPolicy: IfNotPresent
env:
- name: PROVISIONER_NAME
value: ceph.com/rbd
serviceAccount: rbd-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: rbd-provisioner


2、创建ceph-secret
#创建pool池
[root@master1-admin ~]# ceph osd pool create k8stest1 6

apiVersion: v1
kind: Secret
metadata:
name: ceph-secret-1
type: "ceph.com/rbd"
data:
key: QVFEN0VIZGswYmZ1Q0JBQXhRK1hGUFV0d1BraTlCNjBSbndiYmc9PQ==


apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: k8s-rbd
provisioner: ceph.com/rbd
parameters:
monitors: 192.168.13.201:6789,192.168.13.202:6789,192.168.13.200:6789
adminId: admin
adminSecretName: ceph-secret-1
pool: k8stest1
userId: admin
userSecretName: ceph-secret-1
fsType: xfs
imageFormat: "2"
imageFeatures: "layering"


kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: rbd-pvc
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
storageClassName: k8s-rbd


apiVersion: v1
kind: Pod
metadata:
labels:
test: rbd-pod
name: ceph-rbd-pod
spec:
containers:
- name: ceph-rbd-nginx
image: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: ceph-rbd
mountPath: /mnt
readOnly: false
volumes:
- name: ceph-rbd
persistentVolumeClaim:
claimName: rbd-pvc

# k8s 对接 cephfs

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
1、创建ceph子目录

为了别的地方能挂载cephfs,先创建一个secretfile
[root@master1-admin ~]# cat /etc/ceph/ceph.client.admin.keyring |grep key|awk -F" " '{print $3}' > /etc/ceph/admin.secret

挂载cephfs的根目录到集群的mon节点下的一个目录,比如xianchao_data,因为挂载后,我们就可以直接在xianchao_data下面用Linux命令创建子目录了。

[root@master1-admin ~]# mkdir xianchao_data
[root@master1-admin ~]# mount -t ceph 192.168.40.201:6789:/ /root/xianchao_data -o name=admin,secretfile=/etc/ceph/admin.secret

在cephfs的根目录里面创建了一个子目录lucky,k8s以后就可以挂载这个目录
[root@master1-admin ~]# cd /root/xianchao_data/
[root@master1-admin xianchao_data]# mkdir lucky
[root@master1-admin xianchao_data]# chmod 0777 lucky/

创建k8s连接ceph使用的secret
   将/etc/ceph/ceph.client.admin.keyring里面的key的值转换为base64,否则会有问题
[root@master1-admin xianchao_data]# echo "AQBvBdZgsDSZKxAAan+5rnsjr2ziA/atqFnQOA==" | base64

apiVersion: v1
kind: Secret
metadata:
name: cephfs-secret
data:
key: QVFCdkJkWmdzRFNaS3hBQWFuKzVybnNqcjJ6aUEvYXRxRm5RT0E9PQo=

apiVersion: v1
kind: PersistentVolume
metadata:
name: cephfs-pv
spec:
capacity:
storage: 1Gi
accessModes:
- ReadWriteMany
cephfs:
monitors:
- 192.168.40.201:6789
path: /lucky
user: admin
readOnly: false
secretRef:
name: cephfs-secret
persistentVolumeReclaimPolicy: Recycle  


kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: cephfs-pvc
spec:
accessModes:
- ReadWriteMany
volumeName: cephfs-pv
resources:
requests:
storage: 1Gi


apiVersion: v1
kind: Pod
metadata:
name: cephfs-pod-1
spec:
containers:
- image: nginx
name: nginx
imagePullPolicy: IfNotPresent
volumeMounts:
- name: test-v1
mountPath: /mnt
volumes:
- name: test-v1
persistentVolumeClaim:
claimName: cephfs-pvc

# devops

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
敏捷开发
提高开发效率,及时跟进用户需求,缩短开发周期。

敏捷开发包括编写代码和构建代码两个阶段,可以使用git或者svn来管理代码,用maven对代码进行构建

1.5.2 持续集成(CI)
持续集成强调开发人员提交了新代码之后,立刻自动的进行构建、(单元)测试。根据测试结果,我们可以确定新代码和原有代码能否正确地集成在一起。持续集成过程中很重视自动化测试验证结果,对可能出现的一些问题进行预警,以保障最终合并的代码没有问题。
常见的持续集成工具:
1. Jenkins
Jenkins是用Java语言编写的,是目前使用最多和最受欢迎的持续集成工具,使用Jenkins,可以自动监测到git或者svn存储库代码的更新,基于最新的代码进行构建,把构建好的源码或者镜像发布到生产环境。Jenkins还有个非常好的功能:它可以在多台机器上进行分布式地构建和负载测试
2. TeamCity
3.  Travis CI
4.   Go CD
5. Bamboo
6.   GitLab CI
7. Codeship

它的好处主要有以下几点:
1)较早的发现错误:每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,哪个环节出现问题都可以较早的发现
2)快速的发现错误:每完成一部分代码的更新,就会把代码集成到主干中,这样就可以快速的发现错误,比较容易的定位错误
3)提升团队绩效:持续集成中代码更新速度快,能及时发现小问题并进行修改,使团队能创造出更好的产品
4)防止分支过多的偏离主干:经常持续集成,会使分支代码经常向主干更新,当单元测试失败或者出现bug,如果开发者需要在没有调试的情况下恢复仓库的代码到没有bug的状态,只有很小部分的代码会丢失
持续集成的目的是提高代码质量,让产品快速的更新迭代。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
Martin Fowler说过,"持续集成并不能消除Bug,而是让它们非常容易发现和改正。"

1.5.3 持续交付
持续交付在持续集成的基础上,将集成后的代码部署到更贴近真实运行环境的「类生产环境」(production-like environments)中。交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。
如果所有的代码完成之后一起交付,会导致很多问题爆发出来,解决起来很麻烦,所以持续集成,也就是没更新一次代码,都向下交付一次,这样可以及时发现问题,及时解决,防止问题大量堆积。
1.5.4 持续部署
持续部署是指当交付的代码通过评审之后,自动部署到生产环境中。持续部署是持续交付的最高阶段。
Puppet,SaltStack和Ansible是这个阶段使用的流行工具。容器化工具在部署阶段也发挥着重要作用。 Docker和k8s是流行的工具,有助于在开发,测试和生产环境中实现一致性。 除此之外,k8s还可以实现自动扩容缩容等功能。

image-20230602135250221

1
2
3
4
5
6
7
8
9
10
11
12
13
14
K8S在DevOps中的核心作用
Docker和K8S的出现使DevOps变得更加普及,更加容易实现。在传统运维中我们服务时需要针对不同的环境去安装不同的版本,部署方式也杂、多。那么有了docker之后,一次构建、到处运行,我们只需要要构建一次镜像,那么只要有docker的主机,就可以基于镜像把应用跑起来。

在众多微服务中,我们每天可能需要去处理各种服务的崩溃,而服务间的依赖调用关系也及其复杂,这对我们解决问题带来了很大的复杂度。要很好的解决这个问题。我们就需要用到容器编排工具。

Kubernetes 的出现主宰了容器编排的市场,也进化了过去的运维方式,将开发与运维联系的更加紧密。而且让 DevOps 这一角色变得更加清晰,它是目前可用的很流行的容器解决方案之一。
3.1 自动化
敏捷开发->持续集成->持续交付->持续部署
3.2 多集群管理
可以根据客户需求对开发,测试,生产环境部署多套kubernetes集群,每个环境使用独立的物理资源,相互之间避免影响
3.3 多环境一致性
Kubernetes是基于docker的容器编排工具,因为容器的镜像是不可变的,所以镜像把 OS、业务代码、运行环境、程序库、目录结构都包含在内,镜像保存在我们的私有仓库,只要用户从我们提供的私有仓库拉取镜像,就能保证环境的一致性。
3.4 实时反馈和智能化报表
每次集成或交付,都会第一时间将结果通过多途径的方式反馈给你,也可以定制适合企业专用的报表平台。

image-20230602143206987

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
1.安装nfs
yum install nfs-utils -y
systemctl start nfs
systemctl enable nfs
(2)在master1上创建一个nfs共享目录
mkdir /data/v2 -p
vim /etc/exports
/data/v1 *(rw,no_root_squash)
/data/v2 *(rw,no_root_squash)
exportfs -arv

1.安装Jenkins
kubectl create namespace jenkins-k8s

apiVersion: v1
kind: PersistentVolume
metadata:
name: jenkins-k8s-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
nfs:
server: 192.168.13.180
path: /data/v2

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: jenkins-k8s-pvc
namespace: jenkins-k8s
spec:
resources:
requests:
storage: 10Gi
accessModes:
- ReadWriteMany

(4)创建一个sa账号
kubectl create sa jenkins-k8s-sa -n jenkins-k8s
(5)把上面的sa账号做rbac授权
kubectl create clusterrolebinding jenkins-k8s-sa-cluster -n jenkins-k8s --clusterrole=cluster-admin --serviceaccount=jenkins-k8s:jenkins-k8s-sa

docker pull jenkins/jenkins
docker load -i jenkins-slave-latest.tar.gz

jenkins-slave-latest.tar.gz这个里面封装的镜像是jenkins-slave-latest:v1,这个jenkins-slave-latest:v1镜像制作方法如下:
cd /root/slave
cat dockerfile
FROM jenkins/jnlp-slave:4.13.3-1-jdk11
USER root
# 安装Docker
RUN apt-get update && apt-get install -y \
docker.io
# 将当前用户加入docker用户组
RUN usermod -aG docker jenkins
RUN curl -LO https://dl.k8s.io/release/stable.txt
RUN curl -LO https://dl.k8s.io/release/$(cat stable.txt)/bin/linux/amd64/kubectl
RUN chmod +x kubectl
RUN mv kubectl /usr/local/bin/
ENV DOCKER_HOST unix:///var/run/docker.sock

docker build -t=jenkins-slave-latest:v1 .
docker save -o jenkins-slave-latest.tar.gz jenkins-slave-latest:v1

chown -R 1000.1000 /data/v2/


kind: Deployment
apiVersion: apps/v1
metadata:
name: jenkins
namespace: jenkins-k8s
spec:
replicas: 2
selector:
matchLabels:
app: jenkins
template:
metadata:
labels:
app: jenkins
spec:
serviceAccount: jenkins-k8s-sa
containers:
- name: jenkins
image: jenkins/jenkins:2.401.1
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
name: web
protocol: TCP
- containerPort: 50000
name: agent
protocol: TCP
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
readinessProbe:
httpGet:
path: /login
port: 8080
initialDelaySeconds: 60
timeoutSeconds: 5
failureThreshold: 12
volumeMounts:
- name: jenkins-volume
subPath: jenkins-home
mountPath: /var/jenkins_home
volumes:
- name: jenkins-volume
persistentVolumeClaim:
claimName: jenkins-k8s-pvc

apiVersion: v1
kind: Service
metadata:
name: jenkins-service
namespace: jenkins-k8s
labels:
app: jenkins
spec:
selector:
app: jenkins
type: NodePort
ports:
- name: web
port: 8080
targetPort: web
nodePort: 30002
- name: agent
port: 50000
targetPort: agent

http://192.168.13.180:30002

查看初始密码

1
2
3
4
5
6
7
8
 测试通过Jenkins部署应用发布到k8s开发环境、测试环境、生产环境

开发提交代码到代码仓库gitlab-jenkins检测到代码更新-调用k8s api在k8s中创建jenkins slave pod:
Jenkins slave pod拉取代码---通过maven把拉取的代码进行构建成war包或者jar包--->上传代码到Sonarqube,进行静态代码扫描- -->基于war包构建docker image-->把镜像上传到harbor镜像仓库-->基于镜像部署应用到开发环境-->部署应用到测试环境--->部署应用到生产环境。

kubectl create ns devlopment
kubectl create ns production
kubectl create ns qatest
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
79
80
81
82
api_token
11e9faa9696f6e6b8ced2d7bcf7172a1a4


node('testing') {
stage('Clone') {
echo "1.Clone Stage"
git url: "https://github.com/luckylucky421/jenkins-sample.git"
script {
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
}
}
stage('Test') {
echo "2.Test Stage"

}
stage('Build') {
echo "3.Build Docker Image Stage"
sh "docker build -t kbshire/jenkins-demo:${build_tag} ."
}
stage('Push') {
echo "4.Push Docker Image Stage"
withCredentials([usernamePassword(credentialsId: '3774bc6b-f509-4c57-b913-c82d4f20aeb5', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {
sh "docker login -u ${dockerHubUser} -p ${dockerHubPassword}"
sh "docker push kbshire/jenkins-demo:${build_tag}"
}
}
stage('Deploy to dev') {
echo "5. Deploy DEV"
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-dev.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-dev.yaml"
// sh "bash running-devlopment.sh"
sh "kubectl apply -f k8s-dev.yaml --validate=false"
}
stage('Promote to qa') {
def userInput = input(
id: 'userInput',

message: 'Promote to qa?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YES\nNO",
name: 'Env'
]
]
)
echo "This is a deploy step to ${userInput}"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-qa.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-qa.yaml"
// sh "bash running-qa.sh"
sh "kubectl apply -f k8s-qa.yaml --validate=false"
sh "sleep 6"
sh "kubectl get pods -n qatest"
} else {
//exit
}
}
stage('Promote to pro') {
def userInput = input(

id: 'userInput',
message: 'Promote to pro?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YES\nNO",
name: 'Env'
]
]
)
echo "This is a deploy step to ${userInput}"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-prod.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-prod.yaml"
// sh "bash running-production.sh"
sh "cat k8s-prod.yaml"
sh "kubectl apply -f k8s-prod.yaml --record --validate=false"
}
}
}
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
# 如果有403错误,修改/usr/local/bin/jenkins.sh ,主要修改地方在exec java这行,修改完之后重启docker 容器,docker restart docker_id 
#! /bin/bash -e
: "${JENKINS_WAR:="/usr/share/jenkins/jenkins.war"}"
: "${JENKINS_HOME:="/var/jenkins_home"}"
: "${COPY_REFERENCE_FILE_LOG:="${JENKINS_HOME}/copy_reference_file.log"}"
: "${REF:="/usr/share/jenkins/ref"}"
touch "${COPY_REFERENCE_FILE_LOG}" || { echo "Can not write to ${COPY_REFERENCE_FILE_LOG}. Wrong volume permissions?"; exit 1; }
echo "--- Copying files at $(date)" >> "$COPY_REFERENCE_FILE_LOG"
find "${REF}" \( -type f -o -type l \) -exec bash -c '. /usr/local/bin/jenkins-support; for arg; do copy_reference_file "$arg"; done' _ {} +
# if `docker run` first argument start with `--` the user is passing jenkins launcher arguments
if [[ $# -lt 1 ]] || [[ "$1" == "--"* ]]; then
# shellcheck disable=SC2001
effective_java_opts=$(sed -e 's/^ $//' <<<"$JAVA_OPTS $JENKINS_JAVA_OPTS")
# read JAVA_OPTS and JENKINS_OPTS into arrays to avoid need for eval (and associated vulnerabilities)
java_opts_array=()
while IFS= read -r -d '' item; do
java_opts_array+=( "$item" )
done < <([[ $effective_java_opts ]] && xargs printf '%s\0' <<<"$effective_java_opts")
readonly agent_port_property='jenkins.model.Jenkins.slaveAgentPort'
if [ -n "${JENKINS_SLAVE_AGENT_PORT:-}" ] && [[ "${effective_java_opts:-}" != *"${agent_port_property}"* ]]; then
java_opts_array+=( "-D${agent_port_property}=${JENKINS_SLAVE_AGENT_PORT}" )
fi
readonly lifecycle_property='hudson.lifecycle'
if [[ "${JAVA_OPTS:-}" != *"${lifecycle_property}"* ]]; then
java_opts_array+=( "-D${lifecycle_property}=hudson.lifecycle.ExitLifecycle" )
fi
if [[ "$DEBUG" ]] ; then
java_opts_array+=( \
'-Xdebug' \
'-Xrunjdwp:server=y,transport=dt_socket,address=*:5005,suspend=y' \
)
fi
jenkins_opts_array=( )
while IFS= read -r -d '' item; do
jenkins_opts_array+=( "$item" )
done < <([[ $JENKINS_OPTS ]] && xargs printf '%s\0' <<<"$JENKINS_OPTS")
exec java -Duser.home="$JENKINS_HOME" -Dhudson.security.csrf.GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION=true "${java_opts_array[@]}" -jar "${JENKINS_WAR}" "${jenkins_opts_array[@]}" "$@"
fi
# As argument is not jenkins, assume user wants to run a different process, for example a `bash` shell to explore this image
exec "$@"

image-20230603155028149

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
# 回滚脚本
node('testing') {
stage('git clone') {
git url: "https://github.com/kbshire/jenkins-rollout.git"
sh "ls -al"
sh "pwd"
}
stage('select env') {
def envInput = input(
id: 'envInput',
message: 'Choose a deploy environment',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "devlopment\nqatest\nproduction",
name: 'Env'
]
]
)
echo "This is a deploy step to ${envInput}"
sh "sed -i 's/<namespace>/${envInput}/' getVersion.sh"
sh "sed -i 's/<namespace>/${envInput}/' rollout.sh"
sh "bash getVersion.sh"
// env.WORKSPACE = pwd()
// def version = readFile "${env.WORKSPACE}/version.csv"
// println version
}
stage('select version') {
env.WORKSPACE = pwd()
def version = readFile "${env.WORKSPACE}/version.csv"
println version
def userInput = input(id: 'userInput',
message: '选择回滚版本',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "$version\n",
name: 'Version'
]
]
)
sh "sed -i 's/<version>/${userInput}/' rollout.sh"
}
stage('rollout deploy') {
sh "bash rollout.sh"
}
}
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
Jenkins Pipeline介绍
Jenkins pipeline (流水线)是一套运行于jenkins上的工作流框架,将原本独立运行于单个或者多个节点的任务连接起来,实现单个任务难以完成的复杂流程编排与可视化。它把持续提交流水线(Continuous Delivery Pipeline)的任务集成到Jenkins中。
pipeline 是jenkins2.X 最核心的特性, 帮助jenkins 实现从CI到CD与DevOps的转变。

持续提交流水线(Continuous Delivery Pipeline)会经历一个复杂的过程: 从版本控制、向用户和客户提交软件,软件的每次变更(提交代码到仓库)到软件发布(Release)。这个过程包括以一种可靠并可重复的方式构建软件,以及通过多个测试和部署阶段来开发构建好的软件(称为Build)。

总结:
1.Jenkins Pipeline是一组插件,让Jenkins可以实现持续交付管道的落地和实施。
2.持续交付管道(CD Pipeline)是将软件从版本控制阶段到交付给用户或客户的完
整过程的自动化表现。
3.软件的每一次更改(提交到源代码管理系统)都要经过一个复杂的过程才能被发布。

本质上,Jenkins 是一个自动化引擎,它支持许多自动模式。 Pipeline向Jenkins中添加了一组强大的工具, 支持简单的CI到全面的CD pipeline。通过对一系列的相关任务进行建模, 用户可以利用pipeline的很多特性:
1、代码:Pipeline以代码的形式实现,使团队能够编辑,审查和迭代其CD流程。
2、可持续性:Jenkins重启或者中断后都不会影响Pipeline Job。
3、停顿:Pipeline可以选择停止并等待人工输入或批准,然后再继续Pipeline运行。
4、多功能:Pipeline支持现实复杂的CD要求,包括循环和并行执行工作的能力。
5、可扩展:Pipeline插件支持其DSL的自定义扩展以及与其他插件集成的多个选项。

DSL 是什么?
DSL 其实是 Domain Specific Language 的缩写,中文翻译为领域特定语言(下简称 DSL);而与 DSL相对的就是GPL,这里的GPL并不是我们知道的开源许可证,而是 General Purpose Language的简称,即通用编程语言,也就是我们非常熟悉的 Objective-C、Java、Python 以及 C 语言等等。

pipeline脚本是由groovy 语言实现的
但无需专门学习 groovy

pipeline支持两种语法:
 -Declarative:声明式
 -Scripted pipeline :脚本式

声明式pipeline语法:
官网:
https://www.jenkins.io/doc/book/pipeline/syntax/

声明式语法包括以下核心流程:
1.pipeline : 声明其内容为一个声明式的pipeline脚本
2.agent: 执行节点(job运行的slave或者master节点)
3.stages: 阶段集合,包裹所有的阶段(例如:打包,部署等各个阶段)
4.stage: 阶段,被stages包裹,一个stages可以有多个stage
5.steps: 步骤,为每个阶段的最小执行单元,被stage包裹
6.post: 执行构建后的操作,根据构建结果来执行对应的操作

pipeline{
agent any
stages{
stage("This is first stage"){
steps("This is first step"){
echo "1"
}
}
}
post{
always{
echo "The process is ending"
}
}
}
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
environment
environment指令指定一系列键值对,这些键值对将被定义为所有step或stage-specific step的环境变量,具体取决于environment指令在Pipeline中的位置。该指令支持一种特殊的方法credentials(),可以通过其在Jenkins环境中的标识符来访问预定义的凭据。对于类型为“Secret Text”的凭据,该 credentials()方法将确保指定的环境变量包含Secret Text内容;对于“标准用户名和密码”类型的凭证,指定的环境变量将被设置为username:password并且将自动定义两个附加的环境变量:MYVARNAME_USR和MYVARNAME_PSW。

pipeline {
agent any
environment {
CC = 'clang'
}
stages {
stage('Example') {
steps {
sh 'printenv'
}
}
}
}

options
options指令允许在Pipeline本身内配置Pipeline专用选项。Pipeline本身提供了许多选项,例如buildDiscarder,但它们也可能由插件提供,例如 timestamps。

可用选项
buildDiscarder: pipeline保持构建的最大个数。用于保存Pipeline最近几次运行的数据,例如:options { buildDiscarder(logRotator(numToKeepStr: '1')) }
  disableConcurrentBuilds: 不允许并行执行Pipeline,可用于防止同时访问共享资源等。例如:options { disableConcurrentBuilds() }
  skipDefaultCheckout:跳过默认设置的代码check out。例如:options { skipDefaultCheckout() }
  skipStagesAfterUnstable: 一旦构建状态进入了“Unstable”状态,就跳过此stage。例如:options { skipStagesAfterUnstable() }
  timeout: 设置Pipeline运行的超时时间,超过超时时间,job会自动被终止,例如:options { timeout(time: 1, unit: 'HOURS') }
  retry: 失败后,重试整个Pipeline的次数。例如:options { retry(3) }
  timestamps: 预定义由Pipeline生成的所有控制台输出时间。例如:options { timestamps() }


pipeline {
    agent any
    options {
        timeout(time: 1, unit: 'HOURS')
    }
    stages {
        stage('Example') {
            steps {
                echo 'Hello World'
            }
        }
    }
} 


parameters
parameters指令提供用户在触发Pipeline时的参数列表。这些参数值通过该params对象可用于Pipeline stage中,具体用法如下:
作用域:被最外层pipeline所包裹,并且只能出现一次,参数可被全局使用
好处:使用parameters好处是能够使参数也变成code,达到pipeline as code,pipeline中设置的参数会自动在job构建的时候生成,形成参数化构建

可用参数
  string
    A parameter of a string type, for example: parameters { string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '') }
  booleanParam
    A boolean parameter, for example: parameters { booleanParam(name: 'DEBUG_BUILD', defaultValue: true, description: '') }
  目前只支持[booleanParam, choice, credentials, file, text, password, run, string]这几种参数类型,其他高级参数化类型还需等待社区支持。

pipeline{
agent any
parameters {
string(name: 'haha', defaultValue: 'sb', description: 'haha')
booleanParam(name: 'xixi', defaultValue: true, description: 'xixi')
}
stages{
stage("stage1"){
steps{
echo "$haha"
echo "$xixi"
}
}
}
}

triggers
triggers指令定义了Pipeline自动化触发的方式。目前有三个可用的触发器:cron和pollSCM和upstream。
用域:被pipeline包裹,在符合条件下自动触发pipeline

cron
  接受一个cron风格的字符串来定义Pipeline触发的时间间隔,例如:
triggers { cron('H 4/* 0 0 1-5') }
pollSCM
  接受一个cron风格的字符串来定义Jenkins检查SCM源更改的常规间隔。如果存在新的更改,则Pipeline将被重新触发。例如:triggers { pollSCM('H 4/* 0 0 1-5') }
  
  
tools
 通过tools可自动安装工具,并放置环境变量到PATH。如果agent none,这将被忽略。
pipeline {
    agent any
    tools {
       #工具名称必须在Jenkins 管理Jenkins → 全局工具配置中预配置。
        maven 'apache-maven-3.0.1'
    }
    stages {
        stage('Example') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}


input
stage 的 input 指令允许你使用 input step提示输入。 在应用了 options 后,进入 stage 的 agent 或评估 when 条件前,stage 将暂停。 如果 input 被批准, stage 将会继续。 作为 input 提交的一部分的任何参数都将在环境中用于其他 stage
配置项
message
必需的。 这将在用户提交 input 时呈现给用户。
id
input 的可选标识符, 默认为 stage 名称。
ok
`input`表单上的"ok" 按钮的可选文本。
submitter
可选的以逗号分隔的用户列表或允许提交 input 的外部组名。默认允许任何用户。
submitterParameter
环境变量的可选名称。如果存在,用 submitter 名称设置。
parameters
提示提交者提供的一个可选的参数列表。
pipeline {
agent any
stages {
stage('Example') {
input {
message "Should we continue?"
ok "Yes, we should."
submitter "222"
parameters {
string(name: 'PERSON', defaultValue: '111', description: 'Who should I say hello to?')
}
}
steps {
echo "Hello, ${PERSON}, nice to meet you."
}
}
}
}

when
 when指令允许Pipeline根据给定的条件确定是否执行该阶段。该when指令必须至少包含一个条件。如果when指令包含多个条件,则所有子条件必须为stage执行返回true。这与子条件嵌套在一个allOf条件中相同(见下面的例子)。
更复杂的条件结构可使用嵌套条件建:not,allOf或anyOf。嵌套条件可以嵌套到任意深度。
内置条件
branch
    当正在构建的分支与给出的分支模式匹配时执行,例如:when { branch 'master' }。请注意,这仅适用于多分支Pipeline。
  environment
    当指定的环境变量设置为给定值时执行,例如: when { environment name: 'DEPLOY_TO', value: 'production' }
  expression
    当指定的Groovy表达式求值为true时执行,例如: when { expression { return params.DEBUG_BUILD } }
  not
    当嵌套条件为false时执行。必须包含一个条件。例如:when { not { branch 'master' } }
  allOf
    当所有嵌套条件都为真时执行。必须至少包含一个条件。例如:when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }
  anyOf
    当至少一个嵌套条件为真时执行。必须至少包含一个条件。例如:when { anyOf { branch 'master'; branch 'staging' } }
pipeline {
    agent any
    stages {
        stage('Example Build') {
            steps {
                echo 'Hello World'
            }
        }
        stage('Example Deploy') {
            when {
                allOf {
                    branch 'production'
                    environment name: 'DEPLOY_TO', value: 'production'
                }
            }
            steps {
                echo 'Deploying'
            }
        }
    }
}


Parallel
Declarative Pipeline近期新增了对并行嵌套stage的支持,对耗时长,相互不存在依赖的stage可以使用此方式提升运行效率。除了parallel stage,单个parallel里的多个step也可以使用并行的方式运行。
pipeline {
    agent any
    stages {
        stage('Non-Parallel Stage') {
            steps {
                echo 'This stage will be executed first.'
            }
        }
        stage('Parallel Stage') {
            when {
                branch 'master'
            }
            parallel {
                stage('Branch A') {
                    agent {
                        label "for-branch-a"
                    }
                    steps {
                        echo "On Branch A"
                    }
                }
                stage('Branch B') {
                    agent {
                        label "for-branch-b"
                    }
                    steps {
                        echo "On Branch B"
                    }
                }
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Pipeline Scripted语法
Groovy脚本不一定适合所有使用者,因此jenkins创建了Declarative pipeline,为编写Jenkins管道提供了一种更简单、更有主见的语法。但是由于脚本化的pipeline是基于groovy的一种DSL语言,所以与Declarative pipeline相比为jenkins用户提供了更巨大的灵活性和可扩展性。
1.流程控制
 pipeline脚本同其它脚本语言一样,从上至下顺序执行,它的流程控制取决于Groovy表达式,如if/else条件语句,举例如下:
node {
    stage('Example') {
        if (env.BRANCH_NAME == 'master') {
            echo 'I only execute on the master branch'
        } else {
            echo 'I execute elsewhere'
        }
    }
}

Declarative pipeline和Scripted pipeline的比较
共同点:
  两者都是pipeline代码的持久实现,都能够使用pipeline内置的插件或者插件提供的stage,两者都可以利用共享库扩展。
区别:
  两者不同之处在于语法和灵活性。Declarative pipeline对用户来说,语法更严格,有固定的组织结构,更容易生成代码段,使其成为用户更理想的选择。但是Scripted pipeline更加灵活,因为Groovy本身只能对结构和语法进行限制,对于更复杂的pipeline来说,用户可以根据自己的业务进行灵活的实现和扩展。
1
2
3774bc6b-f509-4c57-b913-c82d4f20aeb5	kbshire/****** (dockerhun)
9511af49-48b7-4d13-90ad-0f6d8194b2d7 admin/****** (harbor)
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
node('testing') {
stage('Clone') {
echo "1.Clone Stage"
git url: "https://github.com/kbshire/jenkins-sample.git"
script {
build_tag = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
}
}
stage('Test') {
echo "2.Test Stage"

}
stage('Build') {
echo "3.Build Docker Image Stage"
sh "docker build -t 192.168.13.14:85/jenkins-demo/jenkins-demo:${build_tag} ."
}
stage('Push') {
echo "4.Push Docker Image Stage"
withCredentials([usernamePassword(credentialsId: '9511af49-48b7-4d13-90ad-0f6d8194b2d7', passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {
sh "docker login 192.168.13.14:85 -u ${dockerHubUser} -p ${dockerHubPassword}"
sh "docker push 192.168.13.14:85/jenkins-demo/jenkins-demo:${build_tag}"
}
}
stage('Deploy to dev') {
echo "5. Deploy DEV"
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-dev-harbor.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-dev-harbor.yaml"
// sh "bash running-devlopment.sh"
sh "kubectl apply -f k8s-dev-harbor.yaml --validate=false"
}
stage('Promote to qa') {
def userInput = input(
id: 'userInput',

message: 'Promote to qa?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YES\nNO",
name: 'Env'
]
]
)
echo "This is a deploy step to ${userInput}"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-qa-harbor.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-qa-harbor.yaml"
// sh "bash running-qa.sh"
sh "kubectl apply -f k8s-qa-harbor.yaml --validate=false"
sh "sleep 6"
sh "kubectl get pods -n qatest"
} else {
//exit
}
}
stage('Promote to pro') {
def userInput = input(

id: 'userInput',
message: 'Promote to pro?',
parameters: [
[
$class: 'ChoiceParameterDefinition',
choices: "YES\nNO",
name: 'Env'
]
]
)
echo "This is a deploy step to ${userInput}"
if (userInput == "YES") {
sh "sed -i 's/<BUILD_TAG>/${build_tag}/' k8s-prod-harbor.yaml"
sh "sed -i 's/<BRANCH_NAME>/${env.BRANCH_NAME}/' k8s-prod-harbor.yaml"
// sh "bash running-production.sh"
sh "cat k8s-prod-harbor.yaml"
sh "kubectl apply -f k8s-prod-harbor.yaml --record --validate=false"
}
}
}
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
安装nexus
概念:
Nexus服务器是一个代码包管理的服务器,可以理解 Nexus 服务器是一个巨大的 Library 仓库。Nexus 可以支持管理的工具包括 Maven , npm 等,对于 JAVA 开发来说,只要用到 Maven 管理就可以了。
Nexus服务器作用:因为传统的中央仓库在国外,其地理位置比较远,下载速度比较缓慢。因此,当公司开发人员数量越来越多时,如果不架设一台自己的Nexus服务器,会产生大量的流量阻塞带宽,并且在出现一些不可抗原因(光缆被挖断)导致无法连接到中央仓库时,开发就会因为无法下载相关依赖包而进度停滞。因此在本地环境部署一台私有的Nexus服务器来缓存所有依赖包,并且将公司内部开发的私有包也部署上去,方便其他开发人员下载,是非常有必要的。因为 Nexus 有权限控制,因此外部人员是无法得到公司内部开发的项目包的。


docker run -d -p 8081:8081 -p 8082:8082 -p 8083:8083 -v /etc/localtime:/etc/localtime --name nexus3   sonatype/nexus3

在日志中,会看到一条消息: Started Sonatype Nexus OSS 3.20.1-01 这意味着Nexus Repository Manager可以使用了。现在转到浏览器并打开
http://your-ip-addr:8081

第一步:
1、在 pom.xml 文件中声明发布的宿主仓库和 release 版本发布的仓库。
<!-- 发布构件到Nexus -->
    <distributionManagement>
        <repository>
            <id>releases</id>
            <name>nexus-releases</name>
            <url>http://192.168.40.133:8081/repository/maven-releases/</url>
        </repository>
        <snapshotRepository>
            <id>snapshots</id>
            <name>nexus-snapshots</name>
            <url>http://192.168.40.133:8081/repository/maven-snapshots/</url>
        </snapshotRepository>
    </distributionManagement>

第二步:在 settings.xml 文件中配置 
由于用 Maven 分发构件到远程仓库需要认证,须要在~/.m2/settings.xml或者中加入验证信息:
<servers> 
   <server> 
           <id>public</id> 
           <username>admin</username> 
           <password>123456</password> 
       </server> 
   <server> 
           <id>releases</id> 
           <username>admin</username> 
           <password>123456</password> 
       </server> 
   <server> 
           <id>snapshots</id> 
           <username>admin</username> 
           <password>123456</password> 
       </server> 
 </servers> 


注意: settings.xml 中 server 元素下 id 的值必须与 POM 中 repository 或 snapshotRepository 下 id 的值完全一致 。 
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
安装gitlab
在192.168.40.135上安装

docker run -d -p 443:443 -p 80:80 -p 222:22 --name gitlab --restart always -v /home/gitlab/config:/etc/gitlab -v /home/gitlab/logs:/var/log/gitlab -v /home/gitlab/data:/var/opt/gitlab gitlab/gitlab-ce gitlab/gitlab-ce

改配置:
cat /home/gitlab/config/gitlab.rb
增加如下三行:
external_url 'http://192.168.40.135'
gitlab_rails['gitlab_ssh_host'] = '192.168.40.135'
gitlab_rails['gitlab_shell_ssh_port'] = 222

重启docker:
docker restart gitlab

登录192.168.40.135即可登录:
第一次登录注册账号密码之后,报错如下:
Your account is pending approval from your GitLab administrator and hence bl

可通过如下方法实现:
[root@xianchaomaster1 ~]# docker exec -it gitlab sh
irb(main):004:0> u=User.where(id:1).first
=> #<User id:1 @root>
irb(main):005:0> u.password='12345678'
=> "12345678"
irb(main):006:0> u.password_confirmation='12345678'
=> "12345678"
irb(main):007:0> u.save!
=> true

再次登录192.168.40.135

用户名是root
密码是12345678

# -d:后台运行
# -p:将容器内部端口向外映射
# --name:命名容器名称
# -v:将容器内数据文件夹或者日志、配置等文件夹挂载到宿主机指定目录


在Jenkins安装git插件
系统管理-插件管理-可选插件-搜索git安装即可

在Jenkins上添加gitlab凭据
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
jenkins-jnlp-v2.tar.gz这个压缩包封装的镜像带有mvn命令

node('testing') {
stage('Clone') {
echo "1.Clone Stage"
git credentialsId: 'gitlab', url: 'http://192.168.40.135/root/microservic-test.git '
script {
build_tag = sh(returnStdout: true, script: 'git rev-parse --shortHEAD').trim()
}
}

stage('Test') {
echo "2.Test Stage"
}
stage('mvn') {
sh "mvn clean package -D maven.test.skip=true"
}
stage('Build') {
echo "3.Build Docker Image Stage"
sh "cd /home/jenkins/agent/workspace/mvn-gitlab-harbor-springcloud/portal-service"
sh "docker build --tag 192.168.40.132/microservice/jenkins-demo:v1 /home/jenkins/agent/workspace/mvn-gitlab-harbor-springcloud/portal-service/"
}
stage('Push') {
echo "4.Push Docker Image Stage"
withCredentials([usernamePassword(credentialsId: 'dockerharbor',passwordVariable: 'dockerHubPassword', usernameVariable: 'dockerHubUser')]) {
sh "docker login 192.168.40.132 -u ${dockerHubUser} -p ${dockerHubPassword}"
sh "docker push 192.168.40.132/microservice/jenkins-demo:v1"
}
}
stage('Promoteto pro') {
sh "kubectl apply -f /home/jenkins/agent/workspace/mvn-gitlab-harbor-springcloud/k8s/portal.yaml"
}
}

(88 条消息) Jenkins 持续集成结合 Docker Swarm 集群实现 Web 应用部署的发布_SWANSAN 的博客 - CSDN 博客

使用 jenkins pipeline 在代码合并后自动打包并部署到 docker swarm – MOCUISHLE (ley.best)

CI/CD 探索与实践 (二、Jenkins+Docker Swarm) - 夜色微光 - 博客园 (cnblogs.com)

(88 条消息) 最新最全最详细 jenkins pipeline+docker swarm+sonar+junit 实现 java maven 应用的 devops_淡远的博客 - CSDN 博客

# istio

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
官方文档:https://istio.io/docs/concepts/what-is-istio/
中文官方文档:https://istio.io/zh/docs/concepts/what-is-istio/
Github地址:https://github.com/istio/istio/releases

1、连接(Connect):智能控制服务之间的调用流量,能够实现灰度升级、AB 测试和蓝绿部署等功能
2、安全加固(Secure):自动为服务之间的调用提供认证、授权和加密。
3、控制(Control):应用用户定义的 policy,保证资源在消费者中公平分配。
4、观察(Observe):查看服务运行期间的各种数据,比如日志、监控和 tracing,了解服务的运行情况。

Istio是ServiceMesh的产品化落地,可以通过在现有的服务器新增部署边车代理(sidecar proxy),应用程序不用改代码,或者只需要改很少的代码,就能实现如下基础功能:
1、帮助微服务之间建立连接,帮助研发团队更好的管理与监控微服务,并使得系统架构更加安全;
2、帮助微服务分层解耦,解耦后的proxy层能够更加专注于提供基础架构能力,例如:
(1)服务发现(discovery);
(2)负载均衡(load balancing);
(3)故障恢复(failure recovery);
(4)服务度量(metrics);
(5)服务监控(monitoring);
(6)A/B测试(A/B testing);
(7)灰度发布(canary rollouts);
(8)限流限速(rate limiting);
(9)访问控制(access control);
(10)身份认证(end-to-end authentication)。

RPC:RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务

负载均衡
把前端的请求分发到后台多个服务器

故障恢复
出现故障具备自恢复的能力

服务度量
对于 HTTP,HTTP/2 和 GRPC 流量,Istio 生成以下指标:
1、请求计数(istio_requests_total):这是一个用于累加每个由 Istio 代理所处理请求的 COUNTER 指标。
2、请求持续时间(istio_request_duration_seconds):这是一个用于测量请求的持续时间的 DISTRIBUTION 指标。
3、请求大小(istio_request_bytes):这是一个用于测量 HTTP 请求 body 大小的 DISTRIBUTION 指标。
4、响应大小(istio_response_bytes):这是一个用于测量 HTTP 响应 body 大小的 DISTRIBUTION 指标。
对于 TCP 流量,Istio 生成以下指标:
1、Tcp 发送字节数(istio_tcp_sent_bytes_total):这是一个用于测量在 TCP 连接下响应期间发送的总字节数的 COUNTER 指标。
2、Tcp 接收字节数(istio_tcp_received_bytes_total):这是一个用于测量在 TCP 连接下请求期间接收的总字节数的COUNTER指标。
3、Tcp 打开连接数(istio_tcp_connections_opened_total):这是一个用于累加每个打开连接的 COUNTER 指标。
4、Tcp 关闭连接数 (istio_tcp_connections_closed_total) : 这是一个用于累加每个关闭连接的 COUNTER 指标。

灰度发布
灰度发布也叫金丝雀发布,起源是,矿井工人发现,金丝雀对瓦斯气体很敏感,矿工会在下井之前,先放一只金丝雀到井中,如果金丝雀不叫了,就代表瓦斯浓度高。

Istio核心特性
1、流控(traffic management)
断路器(circuit breakers)、超时、重试、多路由规则、AB测试、灰度发布、按照百分比分配流量等。

2、安全(security)
加密、身份认证、服务到服务的权限控制、K8S里容器到容器的权限控制等。

3、可观察(observability)
追踪、监控、数据收集,通过控制后台全面了解上行下行流量,服务链路情况,服务运行情况,系统性能情况,国内微服务架构体系,这一块做得比较缺乏。

4、平台无关系(platform support)
K8s,物理机,自己的虚机都没问题。

5、集成与定制(integration and customization)
可定制化扩展功能。
1.2.1 断路器
当电路发生故障或异常时,伴随着电流不断升高,并且升高的电流有可能能损坏电路中的某些重要器件,也有可能烧毁电路甚至造成火灾。若电路中正确地安置了保险丝,那么保险丝就会在电流异常升高到一定的高度和热度的时候,自身熔断切断电流,从而起到保护电路安全运行的作用。
断路器也称为服务熔断,在多个服务调用的时候,服务A依赖服务B,服务B依赖服务C,如果服务C响应时间过长或者不可用,则会让服务B占用太多系统资源,而服务A也依赖服B,同时也在占用大量的系统资源,造成系统雪崩的情况出现。 Istio 断路器通过网格中的边车对流量进行拦截判断处理,避免了在代码中侵入控制逻辑,非常方便的就实服务熔断的能力。

image-20230607165625825

image-20230607170204018

image-20230607171256521

1
2
3
4
5
6
7
8
在微服务架构中,在高并发情况下,如果请求数量达到一定极限(可以自己设置阈值),超出了设置的阈值,断路器会自动开启服务保护功能,然后通过服务降级的方式返回一个友好的提示给客户端。假设当10个请求中,有10%失败时,熔断器就会打开,此时再调用此服务,将会直接返回失败,不再调远程服务。直到10s钟之后,重新检测该触发条件,判断是否把熔断器关闭,或者继续打开。


服务降级(提高用户体验效果)
比如电商平台,在针对618、双11的时候会有一些秒杀场景,秒杀的时候请求量大,可能会返回报错标志“当前请求人数多,请稍后重试”等,如果使用服务降级,无法提供服务的时候,消费者会调用降级的操作,返回服务不可用等信息,或者返回提前准备好的静态页面写好的信息。

超时
在生产环境中经常会碰到由于调用方等待下游的响应过长,堆积大量的请求阻塞了自身服务,造成雪崩的情况,通过超时处理来避免由于无限期等待造成的故障,进而增强服务的可用性。

image-20230607172033300

1
2
重试
istio 重试机制就是如果调用服务失败,Envoy 代理尝试连接服务的最大次数。而默认情况下,Envoy 代理在失败后并不会尝试重新连接服务。

image-20230607172139477

1
2
3
4
5
6
7
8
客户端调用 nginx,nginx 将请求转发给 tomcat。tomcat 通过故障注入而中止对外服务,nginx 设置如果访问 tomcat 失败则会重试 3 次。

多路由规则
1、HTTP重定向(HTTPRedirect)
2、HTTP重写(HTTPRewrite)
3、HTTP重试(HTTPRetry)
4、HTTP故障注入(HTTPFaultInjection)
5、HTTP跨域资源共享(CorsPolicy)

# 架构

image-20230607174630299

1
2
3
4
5
6
7
8
1、数据平面由一组以Sidecar方式部署的智能代理(Envoy+Polit-agent)组成。这些代理承载并控制微服务之间的所有网络通信,管理入口和出口流量,类似于一线员工。 Sidecar 一般和业务容器绑定在一起(在Kubernets中以自动注入的方式注入到到业务pod中),来劫持业务应用容器的流量,并接受控制面组件的控制,同 时会向控制面输出日志、跟踪及监控数据。

Envoy 和 pilot-agent 打在同一个镜像中,即sidecar Proxy。

2、控制平面负责管理和配置代理来路由流量。
istio1.5+中使用了一个全新的部署模式,重建了控制平面,将原有的多个组件整合为一个单体结构istiod,这个组件是控制平面的核心,管理Istio的所有功能,主要包括Pilot、Mixer、Citadel等服务组件。

istiod是新版本中最大的变化,以一个单体组件替代了原有的架构,降低了复杂度和维护难度,但原有的多组件并不是被完全移除,而是在重构后以模块的形式整合在一起组成了istiod。

image-20230607180054619

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. 自动注入:在创建应用程序时自动注入 Sidecar代理Envoy程序。在 Kubernetes中创建 Pod时,Kube-apiserver调用控制面组件的 Sidecar-Injector服务,自动修改应用程序的描述信息并注入Sidecar。在真正创建Pod时,在创建业务容器的Pod中同时创建Sidecar容器。 

2. 流量拦截:在Pod初始化时设置iptables 规则,基于配置的iptables规则拦截业务容器的Inbound流量和Outbound流量到Sidecar上。而应用程序感知不到Sidecar的存在,还以原本的方式 进行互相访问。上图中,流出frontend服务的流量会被 frontend服务侧的 Envoy拦截,而当流量到达forecast容器时,Inbound流量被forecast 服务侧的Envoy拦截。

3. 服务发现:服务发起方的 Envoy 调用控制面组件 Pilot 的服务发现接口获取目标服务的实例列表。上图中,frontend 服务侧的 Envoy 通过 Pilot 的服务发现接口得到forecast服务各个实例的地址。


4. 负载均衡:服务发起方的Envoy根据配置的负载均衡策略选择服务实例,并连接对应的实例地址。上图中,数据面的各个Envoy从Pilot中获取forecast服务的负载均衡配置,并执行负载均衡动作。


5. 流量治理:Envoy 从 Pilot 中获取配置的流量规则,在拦截到 Inbound 流量和Outbound 流量时执行治理逻辑。上图中, frontend 服务侧的 Envoy 从 Pilot 中获取流量治理规则,并根据该流量治理规则将不同特征的流量分发到forecast服务的v1或v2版本。

6. 访问安全:在服务间访问时通过双方的Envoy进行双向认证和通道加密,并基于服务的身份进行授权管理。上图中,Pilot下发安全相关配置,在frontend服务和forecast服务的Envoy上自动加载证书和密钥来实现双向认证,其中的证书和密钥由另一个管理面组件 Citadel维护。

7. 服务监测:在服务间通信时,通信双方的Envoy都会连接管理面组件Mixer上报访问数据,并通过Mixer将数据转发给对应的监控后端。上图中,frontend服务对forecast服务的访问监控指标、日志和调用链都可以通过这种方式收集到对应的监控后端。


8. 策略执行:在进行服务访问时,通过Mixer连接后端服务来控制服务间的访问,判断对访问是放行还是拒绝。上图中,Mixer 后端可以对接一个限流服务对从frontend服务到forecast服务的访问进行速率控制等操作。

9. 外部访问:在网格的入口处有一个Envoy扮演入口网关的角 色。上图中,外部服务通过Gateway访问入口服务 frontend,对 frontend服务的负载均衡和一些流量治理策略都在这个Gateway上执行。
1
2
3
4
5
6
7
8
9
10
11
istio组件详解
Istio服务组件有很多,从上面的流程中基本能看出每个组件如何协作的,下面具体讲解每个组件的具体用途和功能。
[root@xianchaomaster1 ~]# kubectl get svc -n istio-system |awk '{print $1}'
istio-egressgateway
istio-ingressgateway
istiod

3.1 Pilot
Pilot 是 Istio 的主要控制组件,下发指令控制客户端。在整个系统中,Pilot 完成以下任务:
1、从 Kubernetes 或者其他平台的注册中心获取服务信息,完成服务发现过程。
2、读取 Istio 的各项控制配置,在进行转换之后,将其发给数据面进行实施。

image-20230607180656237

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Pilot 将配置内容下发给数据面的 Envoy,Envoy 根据 Pilot 指令,将路由、服务、监听、集群等定义信息转换为本地配置,完成控制行为的落地。

1)Pilot为Envoy提供服务发现
2)提供流量管理功能(例如,A/B 测试、金丝雀发布等)以及弹性功能(超时、重试、熔断器等);
3)生成envoy配置
4)启动envoy
5)监控并管理envoy的运行状况,比如envoy出错时pilot-agent负责重启envoy,或者envoy配置变更后reload envoy

Envoy是用 C++ 开发的高性能代理,用于协调服务网格中所有服务的入站和出站流量。
Envoy有许多强大的功能,例如:
动态服务发现
负载均衡
TLS终端
HTTP/2与gRPC代理
断路器
健康检查
流量拆分
灰度发布
故障注入

Citadel
负责处理系统上不同服务之间的TLS通信。 Citadel充当证书颁发机构(CA),并生成证书以允许在数据平面中进行安全的mTLS通信。

Citadel是 Istio的核心安全组件,提供了自动生 成、分发、轮换与撤销密钥和证书功能。Citadel一直监听 Kube- apiserver,以 Secret的形式为每个服务都生成证书密钥,并在Pod创建时挂载到Pod上,代理容器使用这些文件来做服务身份认证,进而代 理两端服务实现双向TLS认证、通道加密、访问授权等安全功能。如图 所示,frontend 服 务对 forecast 服务的访问用到了HTTP方式,通过配置即可对服务增加认证功能,双方的Envoy会建立双向认证的TLS通道,从而在服务间启用双向认证的HTTPS。

Istio 为微服务应用提供了一个完整的解决方案,可以以统一的方式去检测和管理微服务。同时,它还提供了管理流量、实施访问策略、收集数据等功能,而所有这些功能都对业务代码透明,即不需要修改业务代码就能实现。

有了 Istio,就几乎可以不需要其他的微服务框架,也不需要自己去实现服务治理等功能,只要把网络层委托给 Istio,它就能帮助完成这一系列的功能。简单来说,Istio 就是一个提供了服务治理能力的服务网格。

对服务网格来讲,业务代码无侵入和网络层的全权代理是其重要的优势。

Istio 的架构从逻辑上分成数据平面(Data Plane)和控制平面(Control Plane)。

  • 数据平面:由一组和业务服务成对出现的 Sidecar 代理(Envoy)构成,它的主要功能是接管服务的进出流量,传递并控制服务和 Mixer 组件的所有网络通信(Mixer 是一个策略和遥测数据的收集器,稍后会介绍)。
  • 控制平面:主要包括了 Pilot、Mixer、Citadel 和 Galley 共 4 个组件,主要功能是通过配置和管理 Sidecar 代理来进行流量控制,并配置 Mixer 去执行策略和收集遥测数据(Telemetry)。

img

# Istio 的核心控件

# k8s 安装 istio

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
https://github.com/istio/istio/

1、把压缩包上传到k8s的控制节点。手动解压:
[root@ ~]# tar zxvf istio-1.13.1.tar.gz
2、切换到istio包所在目录下。tar zxvf istio-1.13.1.tar.gz解压的软件包包名是istio-1.13.1,则:
cd istio-1.13.1
安装目录包含如下内容:
2)samples/目录下,有示例应用程序
3)bin/目录下,包含istioctl的客户端文件。istioctl工具用于手动注入Envoy sidecar代理。

3、将istioctl客户端路径增加到path环境变量中,macOS 或 Linux 系统的增加方式如下:
export PATH=$PWD/bin:$PATH
4、把istioctl这个可执行文件拷贝到/usr/bin/目录
cd /root/istio-1.13.1/bin/
cp -ar istioctl /usr/bin/

4.2 安装istio
1.下载镜像:
安装istio需要的镜像默认从官网拉取,但是官网的镜像我们拉取会有问题,可以从课件下载镜像,然后上传到自己k8s集群的各个节点,通过docker load -i手动解压镜像:

docker load -i examples-bookinfo-details.tar.gz
docker load -i examples-bookinfo-reviews-v1.tar.gz
docker load -i examples-bookinfo-productpage.tar.gz
docker load -i examples-bookinfo-reviews-v2.tar.gz
docker load -i examples-bookinfo-ratings.tar.gz
docker load -i examples-bookinfo-reviews-v3.tar.gz
docker load -i pilot.tar.gz
docker load -i proxyv2.tar.gz
docker load -i httpbin.tar.gz

master:
istioctl install --set profile=demo -y

4.卸载istio集群,暂时不执行,记住这个命令即可
istioctl manifest generate --set profile=demo | kubectl delete -f -

# istio 核心资源

1
2
Gateway	
在Kubernetes环境中,Ingress controller用于管理进入集群的流量。在Istio服务网格中 Istio Ingress Gateway承担相应的角色,它使用新的配置模型(Gateway 和 VirtualServices)完成流量管理的功能。通过下图做一个总的描述。

K8s 为何需要 Istio?较为深入地讨论 Istio—— 其历史发展、设计理念、核心功能原理及运行流程 - 知乎 (zhihu.com)

浅谈 Service Mesh: 其定义、起源、出现背景和设计理念及作用 - 知乎 (zhihu.com)

Istio 入门:什么是 Istio?Istio 的 4 个主要功能和实现原理 - 知乎 (zhihu.com)

# helm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Helm是kubernetes的包管理工具,相当于linux环境下的yum/apg-get命令。


Helm中的一些概念:
(1)helm:命令行客户端工具,主要用于Kubernetes应用中的chart的创建、打包、发布和管理。
(2)Chart:helm程序包,一系列用于描述k8s资源相关文件的集合,比方说我们部署nginx,需要deployment的yaml,需要service的yaml,这两个清单文件就是一个helm程序包,在k8s中把这些yaml清单文件叫做chart图表。
vlues.yaml文件为模板中的文件赋值,可以实现我们自定义安装
如果是chart开发者需要自定义模板,如果是chart使用者只需要修改values.yaml即可

repository:存放chart图表的仓库,提供部署k8s应用程序需要的那些yaml清单文件

chart--->通过values.yaml这个文件赋值-->生成release实例
helm也是go语言开发的

(3)Release:基于Chart的部署实体,一个chart被Helm运行后将会生成对应的一个release;将在k8s中创建出真实运行的资源对象。

总结:
helm把kubernetes资源打包到一个chart中,制作并完成各个chart和chart本身依赖关系并利用chart仓库实现对外分发,而helm还可通过values.yaml文件完成可配置的发布,如果chart版本更新了,helm自动支持滚更更新机制,还可以一键回滚,但是不是适合在生产环境使用,除非具有定义自制chart的能力。

https://github.com/helm/helm/releases


image-20230609003730213

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
tar zxvf helm-v3.8.1-linux-amd64.tar.gz
mv linux-amd64/helm /usr/bin/


配置国内存放chart仓库的地址
阿里云仓库(https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts)
官方仓库(https://hub.kubeapps.com/charts/incubator)官方chart仓库,国内可能无法访问。
微软仓库(http://mirror.azure.cn/kubernetes/charts/)这个仓库推荐,基本上官网有的chart这里都有,国内可能无法访问。

#添加阿里云的chart仓库
[root@ ~]# helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo add bitnami https://charts.bitnami.com/bitnami

[root@prometheus-master ~]# helm repo list
NAME URL
aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
bitnami https://charts.bitnami.com/bitnami

helm repo update

helm repo remove xxx

#查看阿里云chart仓库中的memcached
[root@ ~]# helm search repo aliyun |grep memcached

#查看chart信息
[root@ ~]# helm show chart aliyun/memcached


#下载chart包到本地
[root@ ~]# helm pull aliyun/memcached
[root@ ~]# tar zxvf memcached-2.0.1.tgz
[root@ ~]# cd memcached
[root@ memcached]# ls
Chart.yaml README.md templates values.yaml

Chart.yaml: chart的基本信息,包括版本名字之类
templates: 存放k8s的部署资源模板,通过渲染变量得到部署文件
values.yaml:存放全局变量,templates下的文件可以调用

[root@ memcached]# cd templates/
[root@ templates]# ls
_helpers.tpl NOTES.txt pdb.yaml statefulset.yaml svc.yaml

_helpers.tpl 存放能够复用的模板
NOTES.txt 为用户提供一个关于chart部署后使用说明的文件
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

apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ template "memcached.fullname" . }}
labels:
app: {{ template "memcached.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
spec:
selector:
matchLabels:
app: {{ template "memcached.fullname" . }}
chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
release: "{{ .Release.Name }}"
heritage: "{{ .Release.Service }}"
serviceName: {{ template "memcached.fullname" . }}
replicas: {{ .Values.replicaCount }}

spec:
containers:
- name: {{ template "memcached.fullname" . }}
image: {{ .Values.image }}
imagePullPolicy: {{ default "" .Values.imagePullPolicy | quote }}
command:
- memcached


docker pull memcache:1.4.36-alpine


helm install memcache ./

[root@prometheus-master memcached]# kubectl get pod
NAME READY STATUS RESTARTS AGE
memcache-memcached-0 1/1 Running 0 2m13s
memcache-memcached-1 1/1 Running 0 23s
memcache-memcached-2 1/1 Running 0 13s
[root@prometheus-master memcached]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 14d
memcache-memcached ClusterIP None <none> 11211/TCP 2m43s
[root@prometheus-master memcached]# kubectl get sts
NAME READY AGE
memcache-memcached 3/3 2m59s


测试:
[root@prometheus-master memcached]# export POD_NAME=$(kubectl get pods --namespace default -l "app=memcache-memcached" -o jsonpath="{.items[0].metadata.name}")
[root@prometheus-master memcached]# kubectl port-forward $POD_NAME 11211
[root@prometheus-master ~]# echo -e 'set mykey 0 60 5\r\nhello\r' | nc localhost 11211
STORED


helm list

helm delete memcache
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
当我们安装好helm之后我们可以开始自定义chart,那么我们需要先创建出一个模板如下:
[root@ ~]# helm create myapp
[root@ ~]# cd myapp/
[root@ myapp]# yum install tree -y
[root@prometheus-master myapp]# tree
.
├── charts
├── Chart.yaml
├── templates
│   ├── deployment.yaml
│   ├── _helpers.tpl
│   ├── hpa.yaml
│   ├── ingress.yaml
│   ├── NOTES.txt
│   ├── serviceaccount.yaml
│   ├── service.yaml
│   └── tests
│   └── test-connection.yaml
└── values.yaml

部署release
[root@ myapp]# helm install myapp .

检查values语法格式
[root@ myapp]# helm lint /root/myapp/

upgrade升级release
[root@ myapp]# helm upgrade --set service.type="NodePort" myapp .

回滚release
#查看历史版本
[root@ myapp]# helm history myapp
helm rollback myapp 1

打包Chart
[root@ myapp]# helm package /root/myapp/
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
操作release命令
升级release版本
helm upgrade

回滚release版本
helm rollback

创建一个release实例
helm install

卸载release
helm uninstall

查看历史版本
helm history

7.6 操作Chart命令

#检查Chat的语法
helm lint

chart相关的
查看chart的详细信息
helm inspect

把chart下载下来
helm pull

把chart打包
helm package

image-20230609152643159

image-20230609152653698

# Rancher

Rancher 简介
Rancher 是一个开源的企业级多集群 Kubernetes 管理平台,实现了 Kubernetes 集群在混合云 + 本地数据中心的集中部署与管理,以确保集群的安全性,加速企业数字化转型。

Rancher 和 k8s 的区别
Rancher 和 k8s 都是用来作为容器的调度与编排系统。但是 rancher 不仅能够管理应用容器,更重要的一点是能够管理 k8s 集群。Rancher2.x 底层基于 k8s 调度引擎,通过 Rancher 的封装,用户可以在不熟悉 k8s 概念的情况下轻松的通过 Rancher 来部署容器到 k8s 集群当中。

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
# 安装rancher
# 修改内核参数
modprobe br_netfilter
echo "modprobe br_netfilter" >> /etc/profile
cat > /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
sysctl -p /etc/sysctl.d/k8s.conf

在rancher上配置阿里云镜像源:
mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup
wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo

#在rancher配置国内阿里云docker的repo源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

yum install -y yum-utils device-mapper-persistent-data lvm2 wget net-tools nfs-utils lrzsz gcc gcc-c++ make cmake libxml2-devel openssl-devel curl curl-devel unzip sudo ntp libaio-devel wget vim ncurses-devel autoconf automake zlib-devel  python-devel epel-release openssh-server socat  ipvsadm conntrack ntpdate

在rancher上安装docker
yum install docker-ce docker-ce-cli containerd.io -y

tee /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors":["https://rsbud4vc.mirror.aliyuncs.com","https://registry.docker-cn.com","https://docker.mirrors.ustc.edu.cn","https://dockerhub.azk8s.cn","http://hub-mirror.c.163.com","http://qtid6917.mirror.aliyuncs.com", "https://rncxm540.mirror.aliyuncs.com"],
"exec-opts": ["native.cgroupdriver=systemd"]
}
[root@rancher ~]# systemctl daemon-reload
[root@rancher ~]# systemctl restart docker
[root@rancher ~]# systemctl status docker


在rancher上操作如下命令:
[root@node1 ~]#docker load -i rancher-agent_2.6.4.tar.gz
[root@master1 ~]#docker load -i rancher-agent_2.6.4.tar.gz
[root@rancher ~]# docker load -i rancher_2.6.4.tar.gz
[root@rancher ~]# docker run -d --restart=unless-stopped -p 80:80 -p 443:443 --privileged rancher/rancher:v2.6.4

docker ps | grep rancher

注:unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器

访问 rancher IP

image-20230610201304940

1
2
3
4
5
NAMESPACE             NAME                                        READY   STATUS              RESTARTS            AGE     IP               NODE                NOMINATED NODE   READINESS GATES
cattle-fleet-system fleet-agent-77c6d6f87-tw9q6 0/1 ContainerCreating 0 47s <none> prometheus-node1 <none> <none>
cattle-system cattle-cluster-agent-778b4cb5cf-fvcrh 1/1 Running 0 78s 10.244.216.90 prometheus-node1 <none> <none>
cattle-system cattle-cluster-agent-778b4cb5cf-g69nt 1/1 Running 0 66s 10.244.64.30 prometheus-node2 <none> <none>

# 管理已有集群

image-20230620145108499

选择通用

image-20230620145117206

接下来的创建资源的步骤和 kubernetes 自带的 dashboard 或者 kube-sphere 一样,都是图形化页面,不在赘述

# 二进制安装 k8s

1
2
3
4
5
6
kubeadm和二进制安装k8s适用场景分析
kubeadm是官方提供的开源工具,是一个开源项目,用于快速搭建kubernetes集群,目前是比较方便和推荐使用的。kubeadm init 以及 kubeadm join 这两个命令可以快速创建 kubernetes 集群。Kubeadm初始化k8s,所有的组件都是以pod形式运行的,具备故障自恢复能力。
kubeadm是工具,可以快速搭建集群,也就是相当于用程序脚本帮我们装好了集群,属于自动部署,简化部署操作,自动部署屏蔽了很多细节,使得对各个模块感知很少,如果对k8s架构组件理解不深的话,遇到问题比较难排查。
kubeadm适合需要经常部署k8s,或者对自动化要求比较高的场景下使用。
二进制:在官网下载相关组件的二进制包,如果手动安装,对kubernetes理解也会更全面。
Kubeadm和二进制都适合生产环境,在生产环境运行都很稳定,具体如何选择,可以根据实际项目进行评估。

master1 192.168.13.200

master2 192.168.13.201

master3 192.168.13.202

work-node 192.168.13.203

virtual ip: 192.168.13.244

# 初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
配置静态IP
配置主机名
配置/etc/hosts
配置免密登录
关闭firewalld
关闭selinux
关闭交换分区
修改内核参数
配置阿里云repo源
配置时间同步
安装并关闭iptables
开启ipvs
安装基础软件包
安装docker-ce
配置docker镜像加速

# 安装 etcd 集群

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
配置etcd工作目录
#创建配置文件和证书文件存放目录
[root@ master1 ~]# mkdir -p /etc/etcd
[root@ master1 ~]# mkdir -p /etc/etcd/ssl
[root@ master2 ~]# mkdir -p /etc/etcd
[root@ master2 ~]# mkdir -p /etc/etcd/ssl
[root@ master3 ~]# mkdir -p /etc/etcd
[root@ master3 ~]# mkdir -p /etc/etcd/ssl

安装签发证书工具cfssl
[root@ master1 ~]# mkdir /data/work -p
[root@ master1 ~]# cd /data/work/
#cfssl-certinfo_linux-amd64 、cfssljson_linux-amd64 、cfssl_linux-amd64上传到/data/work/目录下
[root@ master1 work]# ls
cfssl-certinfo_linux-amd64 cfssljson_linux-amd64 cfssl_linux-amd64
#把文件变成可执行权限
[root@ master1 work]# chmod +x *
[root@ master1 work]# mv cfssl_linux-amd64 /usr/local/bin/cfssl
[root@ master1 work]# mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
[root@ master1 work]# mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo

配置ca证书
#生成ca证书请求文件
[root@ master1 work]# vim ca-csr.json
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Hubei",
"L": "Wuhan",
"O": "k8s",
"OU": "system"
}
],
"ca": {
"expiry": "87600h"
}
}

[root@ master1 work]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca

#生成ca证书文件
[root@ master1 work]# vim ca-config.json
{
"signing": {
"default": {
"expiry": "87600h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "87600h"
}
}
}
}

生成etcd证书
#配置etcd证书请求,hosts的ip变成自己etcd所在节点的ip
[root@ master1 work]# vim etcd-csr.json
{
"CN": "etcd",
"hosts": [
"127.0.0.1",
"192.168.13.200",
"192.168.13.201",
"192.168.13.202",
"192.168.13.244"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [{
"C": "CN",
"ST": "Hubei",
"L": "Wuhan",
"O": "k8s",
"OU": "system"
}]
}

[root@ master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes etcd-csr.json | cfssljson -bare etcd

[root@ master1 work]# ls etcd*.pem
etcd-key.pem etcd.pem

部署etcd集群
把etcd-v3.4.13-linux-amd64.tar.gz上传到/data/work目录下
[root@ master1 work]# pwd
/data/work
[root@ master1 work]# tar -xf etcd-v3.4.13-linux-amd64.tar.gz
[root@ master1 work]# cp -p etcd-v3.4.13-linux-amd64/etcd* /usr/local/bin/
[root@ master1 work]# scp -r etcd-v3.4.13-linux-amd64/etcd* master2:/usr/local/bin/
[root@ master1 work]# scp -r etcd-v3.4.13-linux-amd64/etcd* master3:/usr/local/bin/

#创建配置文件
[root@ master1 work]# vim etcd.conf
#[Member]
ETCD_NAME="etcd1"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.13.200:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.13.200:2379,http://127.0.0.1:2379"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.13.200:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.13.200:2379"
ETCD_INITIAL_CLUSTER="etcd1=https://192.168.13.200:2380,etcd2=https://192.168.13.201:2380,etcd3=https://192.168.13.202:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

#创建启动服务文件
[root@ master1 work]# vim etcd.service
[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
EnvironmentFile=-/etc/etcd/etcd.conf
WorkingDirectory=/var/lib/etcd/
ExecStart=/usr/local/bin/etcd \
--cert-file=/etc/etcd/ssl/etcd.pem \
--key-file=/etc/etcd/ssl/etcd-key.pem \
--trusted-ca-file=/etc/etcd/ssl/ca.pem \
--peer-cert-file=/etc/etcd/ssl/etcd.pem \
--peer-key-file=/etc/etcd/ssl/etcd-key.pem \
--peer-trusted-ca-file=/etc/etcd/ssl/ca.pem \
--peer-client-cert-auth \
--client-cert-auth
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

[root@ master1 work]# cp ca*.pem /etc/etcd/ssl/
[root@ master1 work]# cp etcd*.pem /etc/etcd/ssl/
[root@ master1 work]# cp etcd.conf /etc/etcd/
[root@ master1 work]# cp etcd.service /usr/lib/systemd/system/
[root@ master1 work]# for i in master2 master3;do rsync -vaz etcd.conf $i:/etc/etcd/;done
[root@ master1 work]# for i in master2 master3;do rsync -vaz etcd*.pem ca*.pem $i:/etc/etcd/ssl/;done
[root@ master1 work]# for i in master2 master3;do rsync -vaz etcd.service $i:/usr/lib/systemd/system/;done

#启动etcd集群
[root@ master1 work]# mkdir -p /var/lib/etcd/default.etcd
[root@ master2 work]# mkdir -p /var/lib/etcd/default.etcd
[root@ master3 work]# mkdir -p /var/lib/etcd/default.etcd

[root@ master2 ~]# vim /etc/etcd/etcd.conf
#[Member]
ETCD_NAME="etcd2"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.13.201:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.13.201:2379,http://127.0.0.1:2379"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.13.201:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.13.201:2379"
ETCD_INITIAL_CLUSTER="etcd1=https://192.168.13.200:2380,etcd2=https://192.168.13.201:2380,etcd3=https://192.168.13.202:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

[root@ master3 ~]# vim /etc/etcd/etcd.conf
#[Member]
ETCD_NAME="etcd3"
ETCD_DATA_DIR="/var/lib/etcd/default.etcd"
ETCD_LISTEN_PEER_URLS="https://192.168.13.202:2380"
ETCD_LISTEN_CLIENT_URLS="https://192.168.13.202:2379,http://127.0.0.1:2379"
#[Clustering]
ETCD_INITIAL_ADVERTISE_PEER_URLS="https://192.168.13.202:2380"
ETCD_ADVERTISE_CLIENT_URLS="https://192.168.13.202:2379"
ETCD_INITIAL_CLUSTER="etcd1=https://192.168.13.200:2380,etcd2=https://192.168.13.201:2380,etcd3=https://192.168.13.202:2380"
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster"
ETCD_INITIAL_CLUSTER_STATE="new"

[root@ master1 work]# systemctl daemon-reload
[root@ master1 work]# systemctl enable etcd.service
[root@ master1 work]# systemctl start etcd.service

[root@ master2 work]# systemctl daemon-reload
[root@ master2 work]# systemctl enable etcd.service
[root@ master2 work]# systemctl start etcd.service

启动etcd的时候,先启动 master1的etcd服务,会一直卡住在启动的状态,然后接着再启动 master2的etcd,这样 master1这个节点etcd才会正常起来

[root@ master3 work]# systemctl daemon-reload
[root@ master3 work]# systemctl enable etcd.service
[root@ master3 work]# systemctl start etcd.service

[root@ master1]# systemctl status etcd
[root@ master2]# systemctl status etcd
[root@ master3]# systemctl status etcd

#查看etcd集群
[root@ master1 work]# ETCDCTL_API=3
[root@ master1 ~]# /usr/local/bin/etcdctl --write-out=table --cacert=/etc/etcd/ssl/ca.pem --cert=/etc/etcd/ssl/etcd.pem --key=/etc/etcd/ssl/etcd-key.pem --endpoints=https://192.168.13.200:2379,https://192.168.13.201:2379,https://192.168.13.202:2379 endpoint health

+----------------------------+--------+-------------+-------+
| ENDPOINT | HEALTH | TOOK | ERROR |
+----------------------------+--------+-------------+-------+
| https://192.168.13.200:2379 | true | 12.614205ms | |
| https://192.168.13.201:2379 | true | 15.762435ms | |
| https://192.168.13.202:2379 | true | 76.066459ms | |
+----------------------------+--------+-------------+-------+ |

# 安装 kubernetes 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
下载安装包
二进制包所在的github地址如下:
https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/

#把kubernetes-server-linux-amd64.tar.gz上传到 master1上的/data/work目录下:
[root@ master1 work]# tar zxvf kubernetes-server-linux-amd64.tar.gz
[root@ master1 work]# cd kubernetes/server/bin/
[root@ master1 bin]# cp kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
[root@ master1 bin]# rsync -vaz kube-apiserver kube-controller-manager kube-scheduler kubectl master2:/usr/local/bin/
[root@ master1 bin]# rsync -vaz kube-apiserver kube-controller-manager kube-scheduler kubectl master3:/usr/local/bin/

[root@ master1 bin]# scp kubelet kube-proxy node1:/usr/local/bin/
[root@ master1 bin]# cd /data/work/
[root@ master1 work]# mkdir -p /etc/kubernetes/
[root@ master1 work]# mkdir -p /etc/kubernetes/ssl
[root@ master1 work]# mkdir /var/log/kubernetes

# 部署 apiserver 组件

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
部署apiserver组件
#启动TLS Bootstrapping 机制
#创建token.csv文件
[root@ master1 work]# cat > token.csv << EOF
$(head -c 16 /dev/urandom | od -An -t x | tr -d ' '),kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOF

#创建csr请求文件,替换为自己机器的IP
[root@ master1 work]# vim kube-apiserver-csr.json
{
"CN": "kubernetes",
"hosts": [
"127.0.0.1",
"192.168.13.200",
"192.168.13.201",
"192.168.13.202",
"192.168.13.203",
"192.168.13.244",
"10.255.0.1",
"kubernetes",
"kubernetes.default",
"kubernetes.default.svc",
"kubernetes.default.svc.cluster",
"kubernetes.default.svc.cluster.local"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Hubei",
"L": "Wuhan",
"O": "k8s",
"OU": "system"
}
]
}

#生成证书
[root@ master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-apiserver-csr.json | cfssljson -bare kube-apiserver


#创建api-server的配置文件
[root@ master1 work]# vim kube-apiserver.conf
KUBE_APISERVER_OPTS="--enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \
--anonymous-auth=false \
--bind-address=192.168.13.200 \
--secure-port=6443 \
--advertise-address=192.168.13.200 \
--insecure-port=0 \
--authorization-mode=Node,RBAC \
--runtime-config=api/all=true \
--enable-bootstrap-token-auth \
--service-cluster-ip-range=10.255.0.0/16 \
--token-auth-file=/etc/kubernetes/token.csv \
--service-node-port-range=30000-50000 \
--tls-cert-file=/etc/kubernetes/ssl/kube-apiserver.pem \
--tls-private-key-file=/etc/kubernetes/ssl/kube-apiserver-key.pem \
--client-ca-file=/etc/kubernetes/ssl/ca.pem \
--kubelet-client-certificate=/etc/kubernetes/ssl/kube-apiserver.pem \
--kubelet-client-key=/etc/kubernetes/ssl/kube-apiserver-key.pem \
--service-account-key-file=/etc/kubernetes/ssl/ca-key.pem \
--service-account-signing-key-file=/etc/kubernetes/ssl/ca-key.pem \
--service-account-issuer=https://kubernetes.default.svc.cluster.local \
--etcd-cafile=/etc/etcd/ssl/ca.pem \
--etcd-certfile=/etc/etcd/ssl/etcd.pem \
--etcd-keyfile=/etc/etcd/ssl/etcd-key.pem \
--etcd-servers=https://192.168.13.200:2379,https://192.168.13.201:2379,https://192.168.13.202:2379 \
--enable-swagger-ui=true \
--allow-privileged=true \
--apiserver-count=3 \
--audit-log-maxage=30 \
--audit-log-maxbackup=3 \
--audit-log-maxsize=100 \
--audit-log-path=/var/log/kube-apiserver-audit.log \
--event-ttl=1h \
--alsologtostderr=true \
--logtostderr=false \
--log-dir=/var/log/kubernetes \
--v=4"

#创建服务启动文件
[root@ master1 work]# vim kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=etcd.service
Wants=etcd.service

[Service]
EnvironmentFile=-/etc/kubernetes/kube-apiserver.conf
ExecStart=/usr/local/bin/kube-apiserver $KUBE_APISERVER_OPTS
Restart=on-failure
RestartSec=5
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

[root@ master1 work]# cp ca*.pem /etc/kubernetes/ssl
[root@ master1 work]# cp kube-apiserver*.pem /etc/kubernetes/ssl/
[root@ master1 work]# cp token.csv /etc/kubernetes/
[root@ master1 work]# cp kube-apiserver.conf /etc/kubernetes/
[root@ master1 work]# cp kube-apiserver.service /usr/lib/systemd/system/
[root@ master1 work]# rsync -vaz token.csv master2:/etc/kubernetes/
[root@ master1 work]# rsync -vaz token.csv master3:/etc/kubernetes/
[root@ master1 work]# rsync -vaz kube-apiserver*.pem master2:/etc/kubernetes/ssl/
[root@ master1 work]# rsync -vaz kube-apiserver*.pem master3:/etc/kubernetes/ssl/
[root@ master1 work]# rsync -vaz ca*.pem master2:/etc/kubernetes/ssl/
[root@ master1 work]# rsync -vaz ca*.pem master3:/etc/kubernetes/ssl/
[root@ master1 work]# rsync -vaz kube-apiserver.conf master2:/etc/kubernetes/
[root@ master1 work]# rsync -vaz kube-apiserver.conf master3:/etc/kubernetes/
[root@ master1 work]# rsync -vaz kube-apiserver.service master2:/usr/lib/systemd/system/
[root@ master1 work]# rsync -vaz kube-apiserver.service master3:/usr/lib/systemd/system/

# master2和 master3配置文件kube-apiserver.conf的IP地址修改为实际的本机IP
[root@ master2 ~]# cat /etc/kubernetes/kube-apiserver.conf
[root@ master3 ~]# cat /etc/kubernetes/kube-apiserver.conf

[root@ master1 work]# systemctl daemon-reload
[root@ master2 work]# systemctl daemon-reload
[root@ master3 work]# systemctl daemon-reload

[root@ master1 work]# systemctl enable kube-apiserver
[root@ master2 work]# systemctl enable kube-apiserver
[root@ master3 work]# systemctl enable kube-apiserver

[root@ master1 work]# systemctl start kube-apiserver
[root@ master2 work]# systemctl start kube-apiserver
[root@ master3 work]# systemctl start kube-apiserver

[root@ master1 work]# systemctl status kube-apiserver
[root@ master2 work]# systemctl status kube-apiserver
[root@ master3 work]# systemctl status kube-apiserver

[root@ master1 work]# curl --insecure https://192.168.40.180:6443/
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {

},
"status": "Failure",
"message": "Unauthorized",
"reason": "Unauthorized",
"code": 401
}

上面看到401,这个是正常的的状态,还没认证

# 部署 kubectl 组件

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
可以设置一个环境变量KUBECONFIG
[root@ master1 ~]# export KUBECONFIG =/etc/kubernetes/admin.conf
这样在操作kubectl,就会自动加载KUBECONFIG来操作要管理哪个集群的k8s资源了

也可以按照下面方法
[root@ master1 ~]# cp /etc/kubernetes/admin.conf /root/.kube/config

#创建csr请求文件
[root@ master1 work]# vim admin-csr.json
{
"CN": "admin",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Hubei",
"L": "Wuhan",
"O": "system:masters",
"OU": "system"
}
]
}

#生成证书
[root@ master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin
[root@ master1 work]# cp admin*.pem /etc/kubernetes/ssl/

配置安全上下文
#创建kubeconfig配置文件
1.设置集群参数
[root@ master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.13.200:6443 --kubeconfig=kube.config
2.设置客户端认证参数
[root@ master1 work]# kubectl config set-credentials admin --client-certificate=admin.pem --client-key=admin-key.pem --embed-certs=true --kubeconfig=kube.config
3.设置上下文参数
[root@ master1 work]# kubectl config set-context kubernetes --cluster=kubernetes --user=admin --kubeconfig=kube.config
4.设置当前上下文
[root@ master1 work]# kubectl config use-context kubernetes --kubeconfig=kube.config
[root@ master1 work]# mkdir ~/.kube -p
[root@ master1 work]# cp kube.config ~/.kube/config
5.授权kubernetes证书访问kubelet api权限
[root@ master1 work]# kubectl create clusterrolebinding kube-apiserver:kubelet-apis --clusterrole=system:kubelet-api-admin --user kubernetes

#查看集群组件状态
[root@ master1 work]# kubectl cluster-info

[root@ master1 work]# kubectl get all --all-namespaces

#同步kubectl文件到其他节点
[root@ master2 ~]# mkdir /root/.kube/
[root@ master3 ~]# mkdir /root/.kube/

[root@ master1 work]# rsync -vaz /root/.kube/config master2:/root/.kube/
[root@ master1 work]# rsync -vaz /root/.kube/config master3:/root/.kube/

#配置kubectl子命令补全
[root@ master1 work]# yum install -y bash-completion
[root@ master1 work]# source /usr/share/bash-completion/bash_completion
[root@ master1 work]# source <(kubectl completion bash)
[root@ master1 work]# kubectl completion bash > ~/.kube/completion.bash.inc
[root@ master1 work]# source '/root/.kube/completion.bash.inc'
[root@ master1 work]# source $HOME/.bash_profile

# 创建 kube-controller-manager 组件

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
[root@ master1 work]# vim kube-controller-manager-csr.json 
{
"CN": "system:kube-controller-manager",
"key": {
"algo": "rsa",
"size": 2048
},
"hosts": [
"127.0.0.1",
"192.168.13.200",
"192.168.13.201",
"192.168.13.202",
"192.168.13.244"
],
"names": [
{
"C": "CN",
"ST": "Hubei",
"L": "Wuhan",
"O": "system:kube-controller-manager",
"OU": "system"
}
]
}

#生成证书
[root@ master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager

#创建kube-controller-manager的kubeconfig
1.设置集群参数
[root@ master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.13.200:6443 --kubeconfig=kube-controller-manager.kubeconfig
2.设置客户端认证参数
[root@ master1 work]# kubectl config set-credentials system:kube-controller-manager --client-certificate=kube-controller-manager.pem --client-key=kube-controller-manager-key.pem --embed-certs=true --kubeconfig=kube-controller-manager.kubeconfig
3.设置上下文参数
[root@ master1 work]# kubectl config set-context system:kube-controller-manager --cluster=kubernetes --user=system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig
4.设置当前上下文
[root@ master1 work]# kubectl config use-context system:kube-controller-manager --kubeconfig=kube-controller-manager.kubeconfig

#创建配置文件kube-controller-manager.conf
[root@ master1 work]# vim kube-controller-manager.conf
KUBE_CONTROLLER_MANAGER_OPTS="--port=0 \
--secure-port=10252 \
--bind-address=127.0.0.1 \
--kubeconfig=/etc/kubernetes/kube-controller-manager.kubeconfig \
--service-cluster-ip-range=10.255.0.0/16 \
--cluster-name=kubernetes \
--cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem \
--cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem \
--allocate-node-cidrs=true \
--cluster-cidr=10.0.0.0/16 \
--experimental-cluster-signing-duration=87600h \
--root-ca-file=/etc/kubernetes/ssl/ca.pem \
--service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem \
--leader-elect=true \
--feature-gates=RotateKubeletServerCertificate=true \
--controllers=*,bootstrapsigner,tokencleaner \
--horizontal-pod-autoscaler-use-rest-clients=true \
--horizontal-pod-autoscaler-sync-period=10s \
--tls-cert-file=/etc/kubernetes/ssl/kube-controller-manager.pem \
--tls-private-key-file=/etc/kubernetes/ssl/kube-controller-manager-key.pem \
--use-service-account-credentials=true \
--alsologtostderr=true \
--logtostderr=false \
--log-dir=/var/log/kubernetes \
--v=2"
#创建启动文件
[root@ master1 work]# vim kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
[Service]
EnvironmentFile=-/etc/kubernetes/kube-controller-manager.conf
ExecStart=/usr/local/bin/kube-controller-manager $KUBE_CONTROLLER_MANAGER_OPTS
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target

#启动服务
[root@ master1 work]# cp kube-controller-manager*.pem /etc/kubernetes/ssl/
[root@ master1 work]# cp kube-controller-manager.kubeconfig /etc/kubernetes/
[root@ master1 work]# cp kube-controller-manager.conf /etc/kubernetes/
[root@ master1 work]# cp kube-controller-manager.service /usr/lib/systemd/system/
[root@ master1 work]# rsync -vaz kube-controller-manager*.pem master2:/etc/kubernetes/ssl/
[root@ master1 work]# rsync -vaz kube-controller-manager*.pem master3:/etc/kubernetes/ssl/
[root@ master1 work]# rsync -vaz kube-controller-manager.kubeconfig kube-controller-manager.conf master2:/etc/kubernetes/
[root@ master1 work]# rsync -vaz kube-controller-manager.kubeconfig kube-controller-manager.conf master3:/etc/kubernetes/
[root@ master1 work]# rsync -vaz kube-controller-manager.service master2:/usr/lib/systemd/system/
[root@ master1 work]# rsync -vaz kube-controller-manager.service master3:/usr/lib/systemd/system/

[root@ master1 work]# systemctl daemon-reload
[root@ master1 work]# systemctl enable kube-controller-manager
[root@ master1 work]# systemctl start kube-controller-manager
[root@ master1 work]# systemctl status kube-controller-manager

[root@ master2]# systemctl daemon-reload
[root@ master2]# systemctl enable kube-controller-manager
[root@ master2]# systemctl start kube-controller-manager
[root@ master2]# systemctl status kube-controller-manager

[root@ master3]# systemctl daemon-reload
[root@ master3]# systemctl enable kube-controller-manager
[root@ master3]# systemctl start kube-controller-manager
[root@ master3]# systemctl status kube-controller-manager

# 创建 kube-scheduler 组件

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#创建csr请求
[root@ master1 work]# vim kube-scheduler-csr.json
{
"CN": "system:kube-scheduler",
"hosts": [
"127.0.0.1",
"192.168.13.200",
"192.168.13.201",
"192.168.13.202",
"192.168.13.244"
],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Hubei",
"L": "Wuhan",
"O": "system:kube-scheduler",
"OU": "system"
}
]
}

#生成证书
[root@ master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-scheduler-csr.json | cfssljson -bare kube-scheduler

#创建kube-scheduler的kubeconfig
1.设置集群参数
[root@ master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.13.200:6443 --kubeconfig=kube-scheduler.kubeconfig

2.设置客户端认证参数
[root@ master1 work]# kubectl config set-credentials system:kube-scheduler --client-certificate=kube-scheduler.pem --client-key=kube-scheduler-key.pem --embed-certs=true --kubeconfig=kube-scheduler.kubeconfig

3.设置上下文参数
[root@ master1 work]# kubectl config set-context system:kube-scheduler --cluster=kubernetes --user=system:kube-scheduler --kubeconfig=kube-scheduler.kubeconfig

4.设置当前上下文
[root@ master1 work]# kubectl config use-context system:kube-scheduler --kubeconfig=kube-scheduler.kubeconfig

#创建配置文件kube-scheduler.conf
[root@ master1 work]# vim kube-scheduler.conf
KUBE_SCHEDULER_OPTS="--address=127.0.0.1 \
--kubeconfig=/etc/kubernetes/kube-scheduler.kubeconfig \
--leader-elect=true \
--alsologtostderr=true \
--logtostderr=false \
--log-dir=/var/log/kubernetes \
--v=2"

#创建服务启动文件
[root@ master1 work]# vim kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
EnvironmentFile=-/etc/kubernetes/kube-scheduler.conf
ExecStart=/usr/local/bin/kube-scheduler $KUBE_SCHEDULER_OPTS
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

#启动服务
[root@ master1 work]# cp kube-scheduler*.pem /etc/kubernetes/ssl/
[root@ master1 work]# cp kube-scheduler.kubeconfig /etc/kubernetes/
[root@ master1 work]# cp kube-scheduler.conf /etc/kubernetes/
[root@ master1 work]# cp kube-scheduler.service /usr/lib/systemd/system/
[root@ master1 work]# rsync -vaz kube-scheduler*.pem master2:/etc/kubernetes/ssl/
[root@ master1 work]# rsync -vaz kube-scheduler*.pem master3:/etc/kubernetes/ssl/
[root@ master1 work]# rsync -vaz kube-scheduler.kubeconfig kube-scheduler.conf master2:/etc/kubernetes/
[root@ master1 work]# rsync -vaz kube-scheduler.kubeconfig kube-scheduler.conf master3:/etc/kubernetes/
[root@ master1 work]# rsync -vaz kube-scheduler.service master2:/usr/lib/systemd/system/
[root@ master1 work]# rsync -vaz kube-scheduler.service master3:/usr/lib/systemd/system/

[root@ master1 work]# systemctl daemon-reload
[root@ master1 work]# systemctl enable kube-scheduler
[root@ master1 work]# systemctl start kube-scheduler
[root@ master1 work]# systemctl status kube-scheduler

[root@ master2]# systemctl daemon-reload
[root@ master2]# systemctl enable kube-scheduler
[root@ master2]# systemctl start kube-scheduler
[root@ master2]# systemctl status kube-scheduler

[root@ master3]# systemctl daemon-reload
[root@ master3]# systemctl enable kube-scheduler
[root@ master3]# systemctl start kube-scheduler
[root@ master3]# systemctl status kube-scheduler

#把pause-cordns.tar.gz上传到 node1节点,手动解压
[root@ node1 ~]# docker load -i pause-cordns.tar.gz

# 部署 kubelet 组件

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
创建kubelet-bootstrap.kubeconfig
[root@ master1 work]# cd /data/work/
[root@ master1 work]# BOOTSTRAP_TOKEN=$(awk -F "," '{print $1}' /etc/kubernetes/token.csv)

[root@ master1 work]# rm -r kubelet-bootstrap.kubeconfig

[root@ master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.13.200:6443 --kubeconfig=kubelet-bootstrap.kubeconfig

[root@ master1 work]# kubectl config set-credentials kubelet-bootstrap --token=${BOOTSTRAP_TOKEN} --kubeconfig=kubelet-bootstrap.kubeconfig

[root@ master1 work]# kubectl config set-context default --cluster=kubernetes --user=kubelet-bootstrap --kubeconfig=kubelet-bootstrap.kubeconfig

[root@ master1 work]# kubectl config use-context default --kubeconfig=kubelet-bootstrap.kubeconfig

[root@ master1 work]# kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap

#创建配置文件kubelet.json
"cgroupDriver": "systemd"要和docker的驱动一致。
address替换为自己 node1的IP地址。

[root@ master1 work]# vim kubelet.json
{
"kind": "KubeletConfiguration",
"apiVersion": "kubelet.config.k8s.io/v1beta1",
"authentication": {
"x509": {
"clientCAFile": "/etc/kubernetes/ssl/ca.pem"
},
"webhook": {
"enabled": true,
"cacheTTL": "2m0s"
},
"anonymous": {
"enabled": false
}
},
"authorization": {
"mode": "Webhook",
"webhook": {
"cacheAuthorizedTTL": "5m0s",
"cacheUnauthorizedTTL": "30s"
}
},
"address": "192.168.13.203",
"port": 10250,
"readOnlyPort": 10255,
"cgroupDriver": "systemd",
"hairpinMode": "promiscuous-bridge",
"serializeImagePulls": false,
"featureGates": {
"RotateKubeletClientCertificate": true,
"RotateKubeletServerCertificate": true
},
"clusterDomain": "cluster.local.",
"clusterDNS": ["10.255.0.2"]
}

[root@ master1 work]# vim kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=docker.service
Requires=docker.service
[Service]
WorkingDirectory=/var/lib/kubelet
ExecStart=/usr/local/bin/kubelet \
--bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.kubeconfig \
--cert-dir=/etc/kubernetes/ssl \
--kubeconfig=/etc/kubernetes/kubelet.kubeconfig \
--config=/etc/kubernetes/kubelet.json \
--network-plugin=cni \
--pod-infra-container-image=k8s.gcr.io/pause:3.2 \
--alsologtostderr=true \
--logtostderr=false \
--log-dir=/var/log/kubernetes \
--v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

[root@ node1 ~]# mkdir /etc/kubernetes/ssl -p
[root@ master1 work]# scp kubelet-bootstrap.kubeconfig kubelet.json node1:/etc/kubernetes/
[root@ master1 work]# scp ca.pem node1:/etc/kubernetes/ssl/
[root@ master1 work]# scp kubelet.service node1:/usr/lib/systemd/system/

#启动kubelet服务
[root@ node1 ~]# mkdir /var/lib/kubelet
[root@ node1 ~]# mkdir /var/log/kubernetes
[root@ node1 ~]# systemctl daemon-reload
[root@ node1 ~]# systemctl enable kubelet
[root@ node1 ~]# systemctl start kubelet
[root@ node1 ~]# systemctl status kubelet

执行如下命令可以看到一个worker节点发送了一个 CSR 请求:
[root@ master1 work]# kubectl get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
node-csr-SY6gROGEmH0qVZhMVhJKKWN3UaWkKKQzV8dopoIO9Uc 87s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Pending

[root@ master1 work]# kubectl certificate approve node-csr-SY6gROGEmH0qVZhMVhJKKWN3UaWkKKQzV8dopoIO9Uc
[root@ master1 work]# kubectl get csr
NAME AGE SIGNERNAME REQUESTOR CONDITION
node-csr-SY6gROGEmH0qVZhMVhJKKWN3UaWkKKQzV8dopoIO9Uc 2m25s kubernetes.io/kube-apiserver-client-kubelet kubelet-bootstrap Approved,Issued

[root@ master1 work]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 NotReady <none> 30s v1.20.7

# 部署 kube-proxy 组件

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
#创建csr请求
[root@ master1 work]# vim kube-proxy-csr.json
{
"CN": "system:kube-proxy",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "Hubei",
"L": "Wuhan",
"O": "k8s",
"OU": "system"
}
]
}


生成证书
[root@ master1 work]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy

#创建kubeconfig文件
[root@ master1 work]# kubectl config set-cluster kubernetes --certificate-authority=ca.pem --embed-certs=true --server=https://192.168.13.200:6443 --kubeconfig=kube-proxy.kubeconfig

[root@ master1 work]# kubectl config set-credentials kube-proxy --client-certificate=kube-proxy.pem --client-key=kube-proxy-key.pem --embed-certs=true --kubeconfig=kube-proxy.kubeconfig


[root@ master1 work]# kubectl config set-context default --cluster=kubernetes --user=kube-proxy --kubeconfig=kube-proxy.kubeconfig

[root@ master1 work]# kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

#创建kube-proxy配置文件
[root@ master1 work]# vim kube-proxy.yaml
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 192.168.13.203
clientConnection:
kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig
clusterCIDR: 192.168.13.0/24
healthzBindAddress: 192.168.13.203:10256
kind: KubeProxyConfiguration
metricsBindAddress: 192.168.13.203:10249
mode: "ipvs"

#创建服务启动文件
[root@ master1 work]# vim kube-proxy.service
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
WorkingDirectory=/var/lib/kube-proxy
ExecStart=/usr/local/bin/kube-proxy \
--config=/etc/kubernetes/kube-proxy.yaml \
--alsologtostderr=true \
--logtostderr=false \
--log-dir=/var/log/kubernetes \
--v=2
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target


[root@ master1 work]# scp kube-proxy.kubeconfig kube-proxy.yaml node1:/etc/kubernetes/
[root@ master1 work]#scp kube-proxy.service node1:/usr/lib/systemd/system/

#启动服务
[root@ node1 ~]# mkdir -p /var/lib/kube-proxy
[root@ node1 ~]# systemctl daemon-reload
[root@ node1 ~]# systemctl enable kube-proxy
[root@ node1 ~]# systemctl start kube-proxy
[root@ node1 ~]# systemctl status kube-proxy

# 部署 calico 组件

1
2
3
4
5
6
7
8
[root@ node1 ~]# docker load -i calico.tar.gz
#把calico.yaml文件上传到 master1上的的/data/work目录
[root@ master1 work]# kubectl apply -f calico.yaml
[root@ master1 ~]# kubectl get pods -n kube-system

[root@ master1 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready <none> 73m v1.20.7

# 部署 coredns 组件

1
2
3
4
5
6
7
8
9
[root@ master1 ~]# kubectl apply -f coredns.yaml
[root@ master1 ~]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-6949477b58-qvn5b 1/1 Running 0 2m17s
calico-node-lv6w4 1/1 Running 0 2m18s
coredns-7bf4bd64bd-dt8dq 1/1 Running 0 51s
[root@ master1 ~]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.255.0.2 <none> 53/UDP,53/TCP,9153/TCP 12m

# 查看集群状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@ master1 ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
node1 Ready <none> 38m v1.20.7


# 测试
[root@ node1 ~]# docker load -i tomcat.tar.gz
[root@ master1 ~]# kubectl apply -f tomcat.yaml
[root@ master1 ~]# kubectl apply -f tomcat-service.yaml
[root@ master1 ~]# kubectl get svc

[root@ master1 ~]# kubectl run busybox --image busybox:1.28 --restart=Never --rm -it busybox -- sh
/ # ping www.baidu.com
/ # nslookup kubernetes.default.svc.cluster.local
/ # nslookup tomcat.default.svc.cluster.local

# keepalived+nginx 实现高可用

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
把epel.repo传到 master2、 master3、 node1上
[root@ master1 ~]# scp /etc/yum.repos.d/epel.repo master2:/etc/yum.repos.d/
[root@ master1 ~]# scp /etc/yum.repos.d/epel.repo master3:/etc/yum.repos.d/
[root@ master1 ~]# scp /etc/yum.repos.d/epel.repo node1:/etc/yum.repos.d/

1、安装nginx主备:
在 master1和 master2上做nginx主备安装
[root@ master1 ~]# yum install nginx keepalived -y
[root@ master2 ~]# yum install nginx keepalived -y
2、修改nginx配置文件。主备一样
[root@ master1 ~]# cat /etc/nginx/nginx.conf
[root@ master1 ~]# cat /etc/nginx/nginx.conf

两个master上nginx配置文件一样
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;

events {
worker_connections 1024;
}

# 四层负载均衡,为两台Master apiserver组件提供负载均衡
stream {

log_format main '$remote_addr $upstream_addr - [$time_local] $status $upstream_bytes_sent';

access_log /var/log/nginx/k8s-access.log main;

upstream k8s-apiserver {
server 192.168.13.200:6443; # master1 APISERVER IP:PORT
server 192.168.13.201:6443; # master2 APISERVER IP:PORT
server 192.168.13.202:6443; # master3 APISERVER IP:PORT

}

server {
listen 16443; # 由于nginx与master节点复用,这个监听端口不能是6443,否则会冲突
proxy_pass k8s-apiserver;
}
}

http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;

server {
listen 80 default_server;
server_name _;

location / {
}
}
}


# keepalive配置
主keepalived
[root@ master1 ~]# cat /etc/keepalived/keepalived.conf
global_defs {
notification_email {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id NGINX_MASTER
}

vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
}

vrrp_instance VI_1 {
state MASTER
interface ens33 # 修改为实际网卡名
virtual_router_id 51 # VRRP 路由 ID实例,每个实例是唯一的
priority 100 # 优先级,备服务器设置 90
advert_int 1 # 指定VRRP 心跳包通告间隔时间,默认1秒
authentication {
auth_type PASS
auth_pass 1111
}
# 虚拟IP
virtual_ipaddress {
192.168.13.244/24
}
track_script {
check_nginx
}
}

[root@ master1 ~]# cat /etc/keepalived/check_nginx.sh
#!/bin/bash
count=$(ps -ef |grep nginx | grep sbin | egrep -cv "grep|$$")
if [ "$count" -eq 0 ];then
systemctl stop keepalived
fi

[root@ master1 ~]# chmod +x /etc/keepalived/check_nginx.sh

[root@ master2 ~]# cat /etc/keepalived/keepalived.conf
global_defs {
notification_email {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server 127.0.0.1
smtp_connect_timeout 30
router_id NGINX_BACKUP
}

vrrp_script check_nginx {
script "/etc/keepalived/check_nginx.sh"
}

vrrp_instance VI_1 {
state BACKUP
interface ens33
virtual_router_id 51 # VRRP 路由 ID实例,每个实例是唯一的
priority 90
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.13.244/24
}
track_script {
check_nginx
}
}

[root@ master2 ~]# cat /etc/keepalived/check_nginx.sh
#!/bin/bash
count=$(ps -ef |grep nginx | grep sbin | egrep -cv "grep|$$")
if [ "$count" -eq 0 ];then
systemctl stop keepalived
fi
[root@ master2 ~]# chmod +x /etc/keepalived/check_nginx.sh

[root@ master1 ~]# systemctl daemon-reload
[root@ master1 ~]# yum install nginx-mod-stream -y
[root@ master1 ~]# systemctl start nginx
[root@ master1 ~]# systemctl start keepalived
[root@ master1 ~]# systemctl enable nginx keepalived

[root@ master2 ~]# systemctl daemon-reload
[root@ master2 ~]# yum install nginx-mod-stream -y
[root@ master2 ~]# systemctl start nginx
[root@ master2 ~]# systemctl start keepalived
[root@ master2 ~]# systemctl enable nginx keepalived

改所有Worker Node(kubectl get node命令查看到的节点)组件配置文件,由原来192.168.13.200修改为192.168.13.244(VIP)。
在所有Worker Node执行:

[root@ node1 ~]# sed -i 's#192.168.13.200:6443#192.168.13.244:16443#' /etc/kubernetes/kubelet-bootstrap.kubeconfig

[root@ node1 ~]# sed -i 's#192.168.13.200:6443#192.168.13.244:16443#' /etc/kubernetes/kubelet.json

[root@ node1 ~]# sed -i 's#192.168.13.200:6443#192.168.13.244:16443#' /etc/kubernetes/kubelet.kubeconfig

[root@ node1 ~]# sed -i 's#192.168.13.200:6443#192.168.13.244:16443#' /etc/kubernetes/kube-proxy.yaml

[root@ node1 ~]# sed -i 's#192.168.13.200:6443#192.168.13.244:16443#' /etc/kubernetes/kube-proxy.kubeconfig

[root@ node1 ~]# systemctl restart kubelet kube-proxy

# ACK&&CCE

1
2
3
4
5
阿里云容器服务Kubernetes版(Alibaba Cloud Container Service for Kubernetes,简称容器服务ACK)是全球首批通过Kubernetes一致性认证的服务平台,提供高性能的容器应用管理服务,支持企业级Kubernetes容器化应用的生命周期管理,让您轻松高效地在云端运行Kubernetes容器化应用。

ACK包含了专有版Kubernetes(Dedicated Kubernetes)、托管版Kubernetes(Managed Kubernetes)、Serverless Kubernetes三种形态,方便按需选择。

https://www.aliyun.com/product/kubernetes
1
2
3
云容器引擎(Cloud Container Engine)提供高可靠高性能的企业级容器应用管理服务,支持Kubernetes社区原生应用和工具,简化云上自动化容器运行环境搭建,面向云原生2.0打造CCE Turbo容器集群,计算、网络、调度全面加速,全面加速企业应用创新。

https://www.huaweicloud.com/product/cce.html

使用 kubernetes 集群

1
2
3
4
只要有kubectl的机器即可,完成购买后他们会提供config
替换/root/.kube/config
即可访问集群,记得需要通过公网ip
kubectl config view 查看是否替换为公网IP

# 基于 HPA 和 VPA 实现 Pod 自动扩缩容

弹性伸缩是根据用户的业务需求和策略,自动 “调整” 其 “弹性资源” 的管理服务。通过弹性伸缩功能,用户可设置定时、周期或监控策略,恰到好处地增加或减少 “弹性资源”,并完成实例配置,保证业务平稳健康运行

在实际工作中,我们常常需要做一些扩容缩容操作,如:电商平台在 618 和双十一搞秒杀活动;由于资源紧张、工作负载降低等都需要对服务实例数进行扩缩容操作。

在 k8s 中扩缩容分为两种:

1、Node 层面:
对 K8s 物理节点扩容和缩容,根据业务规模实现物理节点自动扩缩容

2、Pod 层面:
我们一般会使用 Deployment 中的 replicas 参数,设置多个副本集来保证服务的高可用,但是这是一个固定的值,比如我们设置 10 个副本,就会启 10 个 pod 同时 running 来提供服务。如果这个服务平时流量很少的时候,也是 10 个 pod 同时在 running,而流量突然暴增时,又可能出现 10 个 pod 不够用的情况。针对这种情况怎么办?就需要扩容和缩容

# k8s 中自动伸缩的方案

# HPA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# HPA
Kubernetes HPA(Horizontal Pod Autoscaling):Pod水平自动伸缩

通过此功能,只需简单的配置,便可以利用监控指标(cpu使用率、磁盘、自定义的等)自动的扩容或缩容服务中Pod数量,当业务需求增加时,系统将无缝地自动增加适量pod容器,提高系统稳定性。

HPA v1版本可以根据CPU使用率来进行自动扩缩容:
但是并非所有的系统都可以仅依靠CPU或者Memory指标来扩容,对于大多数 Web 应用的后端来说,基于每秒的请求数量进行弹性伸缩来处理突发流量会更加的靠谱,所以对于一个自动扩缩容系统来说,我们不能局限于CPU、Memory基础监控数据,每秒请求数RPS等自定义指标也是十分重要。

HPA v2版本可以根据自定义的指标进行自动扩缩容

hpa v1只能基于cpu做扩容所用
hpa v2可以基于内存和自定义的指标做扩容和缩容

如果我们的系统默认依赖Prometheus,自定义的Metrics指标则可以从各种数据源或者exporter中获取,基于拉模型的Prometheus会定期从数据源中拉取数据。 也可以基于metrics-server自动获取节点和pod的资源指标

K8s的HPA controller已经实现了一套简单的自动扩缩容逻辑,默认情况下,每30s检测一次指标,只要检测到了配置HPA的目标值,则会计算出预期的工作负载的副本数,再进行扩缩容操作。同时,为了避免过于频繁的扩缩容,默认在5min内没有重新扩缩容的情况下,才会触发扩缩容。 HPA本身的算法相对比较保守,可能并不适用于很多场景。例如,一个快速的流量突发场景,如果正处在5min内的HPA稳定期,这个时候根据HPA的策略,会导致无法扩容。

# KPA

1
2
3
4
5
6
7
8
# kpa
KPA(Knative Pod Autoscaler):基于请求数对Pod自动扩缩容,KPA 的主要限制在于它不支持基于 CPU 的自动扩缩容。

1、根据并发请求数实现自动扩缩容
2、设置扩缩容边界实现自动扩缩容

扩缩容边界指应用程序提供服务的最小和最大Pod数量。通过设置应用程序提供服务的最小和最大Pod数量实现自动扩缩容。
相比HPA,KPA会考虑更多的场景,其中一个比较重要的是流量突发的时候

# VPA

1
2
kubernetes VPA(Vertical Pod Autoscaler),垂直 Pod 自动扩缩容,VPA会基于Pod的资源使用情况自动为集群设置资源占用的限制,从而让集群将Pod调度到有足够资源的最佳节点上。VPA也会保持最初容器定义中资源request和limit的占比。
它会根据容器资源使用率自动设置pod的CPU和内存的requests,从而允许在节点上进行适当的调度,以便为每个 Pod 提供适当的可用的节点。它既可以缩小过度请求资源的容器,也可以根据其使用情况随时提升资源不足的容量。
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
HPA全称是Horizontal Pod Autoscaler,翻译成中文是POD水平自动伸缩, HPA可以基于CPU利用率对deployment中的pod数量进行自动扩缩容(除了CPU也可以基于自定义的指标进行自动扩缩容)。pod自动缩放不适用于无法缩放的对象,比如DaemonSets。
HPA由Kubernetes API资源和控制器实现。控制器会周期性的获取平均CPU利用率,并与目标值相比较后调整deployment中的副本数量。

HPA是根据指标来进行自动伸缩的,目前HPA有两个版本–v1和v2beta

HPA的API有三个版本,通过kubectl api-versions | grep autoscal可看到
autoscaling/v1
autoscaling/v2beta1
autoscaling/v2beta2

autoscaling/v1只支持基于CPU指标的缩放;
autoscaling/v2beta1支持Resource Metrics(资源指标,如pod内存)和Custom Metrics(自定义指标)的缩放;
autoscaling/v2beta2支持Resource Metrics(资源指标,如pod的内存)和Custom Metrics(自定义指标)和ExternalMetrics(额外指标)的缩放,但是目前也仅仅是处于beta阶段

K8S从1.8版本开始,CPU、内存等资源的metrics信息可以通过 Metrics API来获取,用户可以直接获取这些metrics信息(例如通过执行kubect top命令),HPA使用这些metics信息来实现动态伸缩。

Metrics server:
1、Metrics server是K8S集群资源使用情况的聚合器
2、从1.8版本开始,Metrics server可以通过yaml文件的方式进行部署
3、Metrics server收集所有node节点的metrics信息

HPA的实现是一个控制循环,由controller manager的--horizontal-pod-autoscaler-sync-period参数指定周期(默认值为15秒)。每个周期内,controller manager根据每个HorizontalPodAutoscaler定义中指定的指标查询资源利用率。controller manager可以从resource metrics API(pod 资源指标)和custom metrics API(自定义指标)获取指标。

然后,通过现有pods的CPU使用率的平均值(计算方式是最近的pod使用量(最近一分钟的平均值,从metrics-server中获得)除以设定的每个Pod的CPU使用率限额)跟目标使用率进行比较,并且在扩容时,还要遵循预先设定的副本数限制:MinReplicas <= Replicas <= MaxReplicas。

计算扩容后Pod的个数:sum(最近一分钟内某个Pod的CPU使用率的平均值)/CPU使用上限的整数+1

流程:
1、创建HPA资源,设定目标CPU使用率限额,以及最大、最小实例数
2、收集一组中(PodSelector)每个Pod最近一分钟内的CPU使用率,并计算平均值
3、读取HPA中设定的CPU使用限额
4、计算:平均值之和/限额,求出目标调整的实例个数
5、目标调整的实例数不能超过1中设定的最大、最小实例数,如果没有超过,则扩容;超过,则扩容至最大的实例个数
6、回到2,不断循环

# metrics-server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
metrics-server是一个集群范围内的资源数据集和工具,同样的,metrics-server也只是显示数据,并不提供数据存储服务,主要关注的是资源度量API的实现,比如CPU、文件描述符、内存、请求延时等指标,metric-server收集数据给k8s集群内使用,如kubectl,hpa,scheduler等。

https://github.com/kubernetes-sigs/metrics-server/

docker load -i metrics-server-0.6.1.tar.gz

vim /etc/kubernetes/manifests/kube-apiserver.yaml
- --enable-aggregator-routing=true

# 所有节点重启kubelet
systemctl restart kubelet

在components.yaml中增加如下字段:
kubectl apply -f components.yaml

image-20230620162201037

# php-apache 利用 hpa 扩缩容

1
2
3
4
5
6
7
php-apache服务正在运行,使用kubectl autoscale创建自动缩放器,实现对php-apache这个deployment创建的pod自动扩缩容,下面的命令将会创建一个HPA,HPA将会根据CPU,内存等资源指标增加或减少副本数,创建一个可以实现如下目的的hpa:
1)让副本数维持在1-10个之间(这里副本数指的是通过deployment部署的pod的副本数)
2)将所有Pod的平均CPU使用率维持在50%(通过kubectl run运行的每个pod如果是200毫核,这意味着平均CPU利用率为100毫核)
#给上面php-apache这个deployment创建HPA,基于cpu动态扩缩容
[root@ ~]# kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
[root@ ~]# kubectl get hpa

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 创建一个hpa,基于内存动态扩缩容
[root@ ~]# cat hpa-v1.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
spec:
maxReplicas: 10
minReplicas: 1
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx-hpa
metrics:
- type: Resource
resource:
name: memory
target:
averageUtilization: 60
type: Utilization

kubectl get hpa
扩展:查看v2版本的hpa如何定义?
[root@ ~]# kubectl get hpa.v2beta2.autoscaling -o yaml > 1.yaml

# VPA

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
使用updateMode: "Off"模式,这种模式仅获取资源推荐值,但是不更新Pod

apiVersion: autoscaling.k8s.io/v1beta2
kind: VerticalPodAutoscaler
metadata:
name: nginx-vpa
namespace: vpa
spec:
targetRef:
apiVersion: "apps/v1"
kind: Deployment
name: nginx
updatePolicy:
updateMode: "Off"
resourcePolicy:
containerPolicies:
- containerName: "nginx"
minAllowed:
cpu: "500m"
memory: "100Mi"
maxAllowed:
cpu: "2000m"
memory: "2600Mi"

kubectl get vpa -n vpa

Lower Bound: 下限值
Target: 推荐值
Upper Bound: 上限值
Uncapped Target: 如果没有为VPA提供最小或最大边界,则表示目标利用率
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
updateMode: "Auto"

从输出信息可以看到,vpa执行了EvictedByVPA,自动停掉了nginx,然后使用 VPA推荐的资源启动了新的nginx
,我们查看下nginx的pod可以得到确认

随着服务的负载的变化,VPA的推荐值也会不断变化。当目前运行的pod的资源达不到VPA的推荐值,就会执行pod驱逐,重新部署新的足够资源的服务。

VPA使用限制:
不能与HPA(Horizontal Pod Autoscaler )一起使用
Pod比如使用副本控制器,例如属于Deployment或者StatefulSet

VPA好处:
Pod 资源用其所需,所以集群节点使用效率高。
Pod 会被安排到具有适当可用资源的节点上。
不必运行基准测试任务来确定 CPU 和内存请求的合适值。
VPA 可以随时调整 CPU 和内存请求,无需人为操作,因此可以减少维护时间。

# kubernetes cluster-autoscaler

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
Cluster Autoscaler (CA)是一个独立程序,是用来弹性伸缩kubernetes集群的。它可以自动根据部署应用所请求的资源量来动态的伸缩集群。当集群容量不足时,它会自动去 Cloud Provider (支持 GCE、GKE 和 AWS)创建新的 Node,而在 Node 长时间资源利用率很低时自动将其删除以节省开支。

在以下情况下,集群自动扩容或者缩放:
扩容:由于资源不足,某些Pod无法在任何当前节点上进行调度

缩容: Node节点资源利用率较低时,且此node节点上存在的pod都能被重新调度到其他node节点上运行

什么时候集群节点不会被 CA 删除?
1)节点上有pod被 PodDisruptionBudget 控制器限制。
2)节点上有命名空间是 kube-system 的pods。
3)节点上的pod不是被控制器创建,例如不是被deployment, replica set, job, stateful set创建。
4)节点上有pod使用了本地存储
5)节点上pod驱逐后无处可去,即没有其他node能调度这个pod
6)节点有注解:"cluster-autoscaler.kubernetes.io/scale-down-disabled": "true"(在CA 1.0.3或更高版本中受支持)

通过PodDisruptionBudget控制器可以设置应用POD集群处于运行状态最低个数,也可以设置应用POD集群处于运行状态的最低百分比,这样可以保证在主动销毁应用POD的时候,不会一次性销毁太多的应用POD,从而保证业务不中断

Horizontal Pod Autoscaler 会根据当前CPU负载更改部署或副本集的副本数。如果负载增加,则HPA将创建新的副本,集群中可能有足够的空间,也可能没有足够的空间。如果没有足够的资源,CA将尝试启动一些节点,以便HPA创建的Pod可以运行。如果负载减少,则HPA将停止某些副本。结果,某些节点可能变得利用率过低或完全为空,然后CA将终止这些不需要的节点。

如何防止节点被CA删除?
节点可以打上以下标签:
"cluster-autoscaler.kubernetes.io/scale-down-disabled": "true"

可以使用 kubectl 将其添加到节点(或从节点删除):
$ kubectl annotate node <nodename> cluster-autoscaler.kubernetes.io/scale-down-disabled=true

# python 调用 k8s api

1
2
3
4
5
6
7
8
python操作kubernetes api需要如下两个条件:
1.前提是需要有个k8s集群环境
2.需要在windows上安装kubernetes模块

在windows下安装kubernetes
pip install --ignore-installed kubernetes

认证:把k8s集群的控制节点上的/root/.kube/config传到自己的电脑指定路径下,我传到了C盘,注意:每个人config文件不一样,大家需要用自己k8s集群控制节点的config文件
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
from kubernetes import client,config
config.kube_config.load_kube_config(config_file='C:\config')

#获取CoreV1API版本对象
v1 = client.CoreV1Api()

#列出来k8s中的所有名称空间
for namespace in v1.list_namespace().items:
print(namespace.metadata.name)

for node in v1.list_node().items:
print(node.metadata.name)


#列举所有名称空间下的所有service
services=v1.list_service_for_all_namespaces()
for svc in services.items:
print('%s \t%s \t%s \t%s \n' %(svc.metadata.namespace,svc.metadata.name,svc.spec.cluster_ip,svc.spec.ports))


#列举所有名称空间下的pod资源
pods=v1.list_pod_for_all_namespaces()
for i in pods.items:
print("%s\t%s\t%s" %(i.status.pod_ip,i.metadata.namespace,i.metadata.name))


#client.AppsV1Api对象可以操作跟k8s中控制器相关资源对象,下面演示的是列举所有名称空间的deployment
v1_deploy=client.AppsV1Api()
deploys=v1_deploy.list_deployment_for_all_namespaces()
for i in deploys.items:
print("%s\t%s\t%s"%(i.metadata.name,i.metadata.namespace,i.spec.replicas))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from os import path
import yaml
from kubernetes import client,config
#引入我们要用的包
def main():
config.load_kube_config(config_file='C:\config')
#读入集群相关信息,就是要操作哪个集群
with open(path.join(path.dirname(__file__),"nginx-deploy.yaml")) as f:
dep=yaml.safe_load(f)
k8s_apps_v1=client.AppsV1Api()
resp = k8s_apps_v1.create_namespaced_deployment(body=dep,namespace='default')
print('deployment created,name=%s'%(resp.metadata.name))

if __name__ == '__main__':
main()
1
2
3
4
5
6
7
8
9
10
11
12
13
from os import path
import yaml
from kubernetes import client,config

def main():
config.load_kube_config(config_file='C:\config')
k8s_core_v1=client.CoreV1Api()
resp=k8s_core_v1.delete_namespaced_pod(namespace='default',name='busybox-test')
print('delete pod')
main()


上面是删除自己linux机器上的k8s集群的默认名称空间下的busybox-test这个pod
1
2
3
4
5
6
7
8
9
10
11
12
13
from os import path
import yaml
from kubernetes import client,config
def main():
config.load_kube_config(config_file='C:\config')
k8s_core_v1=client.CoreV1Api()
old_resp=k8s_core_v1.read_namespaced_pod(namespace='default',name='busybox-test')
old_resp.spec.containers[0].image='nginx'
#修改镜像
new_resp=k8s_core_v1.patch_namespaced_pod(namespace='default',name='busybox-test',body=old_resp)
print(new_resp.spec.containers[0].image)
if __name__=='__main__':
main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from os import path
import yaml
from kubernetes import client,config
def main():
config.load_kube_config(config_file='C:\config')
k8s_core_v1=client.CoreV1Api()
resp=k8s_core_v1.read_namespaced_pod(namespace='default',name='busybox-test')
print('read pod')
# print(resp)
#读取指定的信息
print(resp.spec.containers[0])
print(resp.spec.containers[0].image)
if __name__=='__main__':
main()

# ephemeral

1
2
3
4
5
6
7
8
临时容器与其他容器的不同之处在于,它们缺少对资源或执行的保证,并且永远不会自动重启,因此不适用于构建应用程序。临时容器使用与常规容器相同的 Container.Spec字段进行描述,但许多字段是不允许使用的。
临时容器没有端口配置,因此像 ports,livenessProbe,readinessProbe 这样的字段是不允许的。
Pod 资源分配是不可变的,因此 resources 配置是不允许的。
临时容器是使用 API 中的一种特殊的 ephemeralcontainers 处理器进行创建的, 而不是直接添加到 pod.spec 段,因此无法使用 kubectl edit 来添加一个临时容器。

与常规容器一样,将临时容器添加到 Pod 后,将不能更改或删除临时容器

当由于容器崩溃或容器镜像不包含调试实用程序而导致 kubectl exec 无用时,临时容器对于交互式故障排查很有用。
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
需要开启支持临时容器的特性:
修改kube-apiserver.yaml、kube-scheduler.yaml、kubelet配置。

[root@]# cat /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.40.180:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --advertise-address=192.168.40.180
………
- --feature-gates=RemoveSelfLink=false
- --feature-gates=EphemeralContainers=true
………

#新增加--feature-gates=EphemeralContainers=true字段


[root@]# cat /etc/kubernetes/manifests/kube-scheduler.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
component: kube-scheduler
tier: control-plane
name: kube-scheduler
namespace: kube-system
spec:
containers:
- command:
- kube-scheduler
- --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
- --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
- --bind-address=192.168.40.180
- --kubeconfig=/etc/kubernetes/scheduler.conf
- --leader-elect=true
- --feature-gates=EphemeralContainers=true

#新增加--feature-gates=EphemeralContainers=true字段

[root@master]# cat /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS="--feature-gates=EphemeralContainers=true"

[root@node~]# cat /etc/sysconfig/kubelet
KUBELET_EXTRA_ARGS="--feature-gates=EphemeralContainers=true"

#修改之后重启k8s控制节点和工作节点的kubelet
[root@]# systemctl restart kubelet
[root@ ~]# systemctl restart kubelet

#查看kube-system名称空间pod,都是running说明修改正常
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
apiVersion: v1
kind: Pod
metadata:
name: tomcat-test
namespace: default
labels:
app: tomcat
spec:
containers:
- name: tomcat-java
ports:
- containerPort: 8080
image: tomcat-8.5-jre8
imagePullPolicy: IfNotPresent

#创建临时容器
[root@xianchaomaster1]# kubectl debug -it tomcat-test --image=busybox:1.28 --target=tomcat-java

[root@prometheus-master ~]# kubectl describe pod tomcat-test
Containers:
tomcat-java:
Container ID: docker://b15184eb42acf60957e2d245a3e784ec20528f057adc55fdb944aee4ffcd968d
Image: tomcat-8.5-jre8
Image ID: docker://sha256:4ac473a3dd922eecfc8e0dabd9f6c410871dfd913fea49bfed3fb99924839131
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Sun, 11 Jun 2023 02:10:21 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-mhn6h (ro)
Ephemeral Containers:
debugger-tnspk:
Container ID: docker://0d8d52ce48509f796aa591d7175242f34b00d0235e9d58710f0109dbce30683c
Image: busybox:1.28
Image ID: docker://sha256:8c811b4aec35f259572d0f79207bc0678df4c736eeec50bc9fec37ed936a472a
Port: <none>
Host Port: <none>
State: Running
Started: Sun, 11 Jun 2023 02:12:52 +0800
Ready: False
Restart Count: 0
Environment: <none>
Mounts: <none>

kubectl debug 每次都会创建一个新的临时容器,以前的临时容器不会删除,但也无法登录
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
kubectl raw更新临时容器

kubectl applf -f tomcat.yaml

vim a.json
{
"apiVersion": "v1",
"kind": "EphemeralContainers",
"metadata": {
"name": "tomcat-test"
},
"ephemeralContainers": [{
"command": [
"sh"
],
"image": "busybox",
"imagePullPolicy": "IfNotPresent",
"name": "debugger",
"stdin": true,
"tty": true,
"targetContainerName": "tomcat-java",
"terminationMessagePolicy": "File"
}]
}

kubectl replace --raw /api/v1/namespaces/default/pods/tomcat-test/ephemeralcontainers -f a.json

kubectl attach -it -c debugger tomcat-test

附加退出后不能在进入临时容器

临时容器特别适合包含主容器剥离出来的一些调试工具,在需要的时候临时注入到目标pod中
在pod中添加临时容器之后,目前还无法删除,同时如果这时候临时容器已经退出,会导致无法再次attach,也不会被拉起(临时容器不支持probe),相关的issue:
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
如果attach了临时容器,然后退出了容器主进程(和前面示例中展示的那样),会导致这个容器无法再attach,也无法重新启动。此时,如果要重复上述步骤再次进行调试,需要新创建一个临时容器,同时还需要保留老的配置,否则k8s会拒绝新的配置:

{
"apiVersion": "v1",
"kind": "EphemeralContainers",
"metadata": {
"name": "tomcat-test"
},
"ephemeralContainers": [
{
"command": [
"sh"
],
"image": "busybox",
"imagePullPolicy": "IfNotPresent",
"name": "debugger",
"stdin": true,
"tty": true,
"targetContainerName": "tomcat-java",
"terminationMessagePolicy": "File"
},
{"command": [
"sh"
],
"image": "busybox",
"imagePullPolicy": "IfNotPresent",
"name": "debugger1",
"stdin": true,
"tty": true,
"targetContainerName": "tomcat-java",
"terminationMessagePolicy": "File"
}
]
}

配置文件需要做这样的修改,再新增一个临时容器。重新修改后pod的状态(describe结果):

[root@]# kubectl replace --raw /api/v1/namespaces/default/pods/tomcat-test/ephemeralcontainers -f a.json

kubectl attach -it tomcat-test -c debugger1

# k3s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
k3s是经过CNCF认证的由Rancher公司开发维护的一个轻量级的 Kubernetes 发行版,内核机制还是和 k8s 一样,但是剔除了很多外部依赖以及 K8s  alpha、beta 特性,同时改变了部署方式和运行方式,目的是轻量化 K8s,简单来说,K3s 就是阉割版 K8s,消耗资源极少。它主要用于边缘计算、物联网等场景。K3s 具有以下特点:

1、安装简单,占用资源少,只需要512M内存就可以运行起来;
2、apiserver 、schedule 等组件全部简化,并以进程的形式运行在节点上,把程序都打包为单个二进制文件,每个程序只需要占用100M内存;
3、使用基于sqlite3的轻量级存储后端作为默认存储机制。同时支持使用etcd3、MySQL 和PostgreSQL作为存储机制;
4、默认使用 local-path-provisioner 提供本地存储卷;
5、默认安装了Helm controller Traefik Ingress controller;
6、所有 Kubernetes control-plane 组件的操作都封装在单个二进制文件和进程中,使 K3s 具有自动化和管理包括证书分发在内的复杂集群操作的能力。
7、减少外部依赖,操作系统只需要安装较新的内核(centos7.6就可以,不需要升级内核)以及支持cgroup即可,k3s安装包已经包含了containerd、Flannel、CoreDNS,非常方便地一键式安装,不需要额外安装Docker、Flannel等组件。

K3s 适用于以下场景:
1、边缘计算-Edge
2、物联网-IoT
3、CI:持续集成
4、Development:开发
5、ARM
6、嵌入 K8s

由于运行 K3s 所需的资源相对较少,所以 K3s 也适用于开发和测试场景。在这些场景中,如果开发或测试人员需要对某些功能进行验证,或对某些问题进行重现,那么使用 K3s 不仅能够缩短启动集群的时间,还能够减少集群需要消耗的资源。与此同时,Rancher 中国团队推出了一款针对 K3s 的效率提升工具:AutoK3s。只需要输入一行命令,即可快速创建 K3s 集群并添加指定数量的 master 节点和 worker 节点。

# k3s 架构

image-20230623175001742

1
2
3
4
5
k3s server节点是运行k3s server命令的机器(裸机或者虚拟机),而k3s Agent 节点是运行k3s agent命令的机器。

单点架构只有一个控制节点(在 K3s 里叫做server node,相当于 K8s 的 master node),而且K3s的数据存储使用 sqlite 并内置在了控制节点上

在这种配置中,每个 agent 节点都注册到同一个 server 节点。K3s 用户可以通过调用server节点上的K3s API来操作Kubernetes资源。

image-20230623175156884

1
2
3
4
5
6
7
8
9
10
虽然单节点 k3s 集群可以满足各种用例,但对于 Kubernetes control-plane 的正常运行至关重要的环境,可以在高可用配置中运行 K3s。一个高可用 K3s 集群由以下几个部分组成:

1、K3s Server 节点:两个或者更多的server节点将为 Kubernetes API 提供服务并运行其他 control-plane 服务
2、外部数据库:外部数据存储(与单节点 k3s 设置中使用的嵌入式 SQLite 数据存储相反)

k3s和k8s如何选择
K8s和k3s各有优劣。若是你要进行大型的集群部署,建议你选择使用K8s;若是你处于边缘计算等小型部署的场景或仅仅须要部署一些非核心集群进行开发/测试,那么选择k3s则是性价比更高的选择。

云计算场景用k8s
边缘计算场景用k3s

# 安装 k3s

快速入门指南 | K3s

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
在192.168.40.180和192.168.40.181新机器初始化操作你们自己去做:
参考安装k8s时候的初始化去做就可以了
配置yum源
关掉防火墙
关掉selinux
修改内核参数
关掉swap交换分区


在192.168.40.180和192.168.40.181上安装containerd
[root@localhost ~]# yum install containerd -y
启动containerd
[root@localhost ~]# systemctl start containerd

国内用户可以用如下方法:安装速度会更快
在192.168.40.180上操作:
#执行如下命令安装:

curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -

提取join token
我们想要添加一对worker节点。在这些节点上安装K3s,我们需要一个join token。Join token存在于master节点的文件系统上。让我们复制并将它保存在某个地方,稍后我们可以获取它:
cat /var/lib/rancher/k3s/server/node-token

获取到一串token:
K1048a0844cf602b7c96a4ea98a0a3531298e78262ba19875bd31a92ffc56686f63::server:698e6bc453d5871cbb1a4741387824b0

在192.168.40.181上执行如下,把work节点加入k3s:
curl -sfL http://rancher-mirror.cnrancher.com/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn K3S_URL=https://192.168.40.186:6443 K3S_TOKEN=K105914c0b8bc0c8056a958ae90d0c2e5bf755d470cda85626aef879c8956ba95e4::server:bcdeca25ed8372d9f1bc983f0efb119e sh -

验证work节点是否加入集群:
在192.168.40.180操作:
k3s kubectl get nodes

# openshift

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
OpenShift 是红帽 Red Hat 公司开源的平台,是平台即服务(PaaS),是一种容器应用平台。允许开发人员构建、测试和部署应用。在 OpenShift 上可以进行开发、测试、部署、运维全流程,实现高度的自动化,满足企业中的应用持续集成和交付及部署的需求,同时也满足企业对于容器管理(Docker)、容器编排(K8S)的需求。
Openshift 是首个支持企业级 Java 的 PaaS 平台,支持 JEE6 与 JBoss 和其 Eclipse 集成开发环境以及 Maven 和 Jenkins 自动化。
OpenShift通常被称为容器应用平台,因为它是一个用于开发和部署容器的平台。OpenShift底层以Docker作为容器引擎驱动,以K8s作为容器编排引擎组件,并提供了开发语言,中间件,DevOps自动化流程工具和web console用户界面等元素,提供了一套完整的基于容器的应用云平台。

OpenShift功能
容器引擎:docker;
容器编排:kubernetes;
应用开发框架及中间件:Java、Python、Tomcat、MySQL、PHP、Ruby、MongoDB和JBoss等中间件;
应用及服务目录:用户可一键部署各类应用及服务;
自动化流程及工具:内置自动化流程工具S2I(Source to Image),用户可完成代码编译、构建和镜像发布;
软件定义网络:提供 OpenVSwitch,实现跨主机共享网络及多租户隔离网络模式;
性能监控及日志管理:内置 Prometheus 监控功能,用户可以通过 Grafana 仪表板上实时显示应用;
多用户接口:提供友好的 UI、命令行工具(oc,类似于 K8S 的 kubectl 以及 RESTful API,基本与 K8S 兼容);
自动化集群部署及管理:通过 Ansible 实现集群的自动化部署,为集群的自动化扩容提供接口。

OpenShift 与 K8S的区别
概念:OpenShift 是 PaaS(平台即服务),K8S 是 CaaS(容器即服务),OpenShift 内置了Kubernetes。OpenShift 底层以 Docker 作为容器引擎驱动,以 Kubernetes 作为容器编排引擎组件。
部署:OpenShift 可以安装在 RHEL(Red Hat Enterprise Linux)和 RHELAH(Red Hat Eneterprise Linux Atomic Host)、CentOS 和 Fedora上;K8S 最好在 Unbuntu、Fedora、centos 和 Debian上运行,可部署在任何主要的 IaaS 上,如 IBM、AWS、Azure、GCP 和阿里云等云平台上。
网络:OpenShift 提供了开箱即用的本机网络解决方案,即 OpenvSwitch,它提供三种不同的插件;K8S 没有本机网络解决方案,但提供可供第三方网络插件使用的接口。

OpenShift 与 K8S相同点
OpenShift 集成了原生的 K8S 作为容器编排组件,提供容器集群的管理,为业务应用可以提供:
容器调度:根据业务的要求,快速部署容器到达指定的目标状态;
弹性伸缩:应用可以快速的扩缩容pod的实例数量;
异常修复:在容器实例发生异常时,集群可以自动发现问题、处理并恢复应用服务的状态;
持久化卷:为集群中的不同机器上的容器提供持久化卷的对接功能;
服务发现:可以提供负载均衡及服务发现功能;
配置管理:为业务应用提供灵活的配置管理和分发规则。

Openshift基础组件
在OpenShift中,Kubernetes管理容器化应用程序,并为部署、维护和应用程序扩展提供机制。Kubernetes集群由一个或多个Master节点和一组Node节点组成。
Master节点
主控节点。包括API Server、Controller Manager Server和Etcd。主控节点管理其Kubernetes集群中的Node节点,并安排pod在这些节点上运行。
Node节点
Node节点为容器提供运行时环境。Kubernetes集群中的每个Node节点都有需要由Master节点管理的服务。该节点还具有运行pods所需的服务,包括容器运行时、kubelet和服务代理。

openshift基本概念
以下将提供有关使用OpenShift时将遇到的核心概念和对象的高级体系结构信息。其中许多对象来自Kubernetes, OpenShift对Kubernetes进行了扩展,以提供功能更丰富的开发生命周期平台。
Project
Project是一个带有附加注释的Kubernetes名称空间,是管理普通用户访问资源的中心媒介。用户必须由管理员提供相关的权限,或者如果允许创建项目,则自动访问自己的项目。
Namespaces
Kubernetes命名空间提供了一种用于划分集群中资源的机制。在OpenShift中,Project是带有附加注释的Kubernetes命名空间。
Users
与OpenShift交互的用户。OpenShift用户对象表示一个参与者,可以通过向其添加角色或向其组添加角色来授予该参与者在系统中的权限。
Pod
运行于Node节点上,若干相关容器的组合。Pod内包含的容器运行在同一宿主机上,使用相同的网络命名空间、IP地址和端口,能够通过localhost进行通信。Pod是Kurbernetes进行创建、调度和管理的最小单位,它提供了比容器更高层次的抽象,使得部署和管理更加灵活。一个Pod可以包含一个容器或者多个相关容器。
Service
Service定义了Pod的逻辑集合和访问该集合的策略,是真实服务的抽象。Service提供了一个统一的服务访问入口以及服务代理和发现机制,关联多个相同Label的Pod,用户不需要了解后台Pod是如何运行。
Router
Service提供了一个通往后端Pod集群的稳定入口,但是Service的IP地址只是集群内部的节点和容器可见。外部需通过Router(路由器)来转发。Router组件是Openshift集群中一个重要的组件,它是外界访问集群内容器应用的入口。用户可以创建Route(路由规则)对象,一个Route会与一个Service关联,并绑定一个域名。
Persistent Storage
容器默认是非持久化的,所有的修改在容器销毁时都会丢失。Docker提供了持久化卷挂载的能力,Openshift除了提供持久化卷挂载的能力,还提供了一种持久化供给模型即PV(Persistent Volume)和PVC(Persistent Volume Claim)。用户在部署应用时显示的声明对持久化的需求,创建PVC,在PVC中定义所需要的存储大小,访问方式。OpenShift集群会自动寻找符合要求的PV与PVC自动对接。
Registry
Openshift内部的镜像仓库,主要用于存放内置的S2I构建流程所产生的镜像。
S2I
Source to Image,负责将应用源码构建成镜像。

# 安装 openshift&ansible

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
安装ansible
初始化
1.1.12 修改网卡配置文件
[root@master ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33
最后追加如下内容:
NM_CONTROLLED=yes
修改好之后重启网络
service network restart

[root@node1~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33
最后追加如下内容:
NM_CONTROLLED=yes
修改好之后重启网络
service network restart

[root@node2 ~]# vim /etc/sysconfig/network-scripts/ifcfg-ens33
最后追加如下内容:
NM_CONTROLLED=yes
修改好之后重启网络
service network restart

1.1.13 停掉NetworkManager
[root@master ~]# systemctl stop NetworkManager
[root@master ~]# systemctl disable NetworkManager

[root@node1 ~]# systemctl stop NetworkManager
[root@node1 ~]# systemctl disable NetworkManager

[root@node2 ~]# systemctl stop NetworkManager
[root@node2 ~]# systemctl disable NetworkManager

1.1.14 安装ansible
把ansible-2.6.5-1.el7.ans.noarch.rpm上传到master、node1、node2上
[root@master ~]#yum install ansible-2.6.5-1.el7.ans.noarch.rpm -y
[root@node1~]#yum install ansible-2.6.5-1.el7.ans.noarch.rpm -y



把openshift-ansible-release-3.10.zip上传到master节点,手动解压:
[root@master ~]#unzip openshift-ansible-release-3.10.zip

1.2.2 安装和配置docker
[root@master ~]#yum install -y docker-1.13.1
[root@node1~]#yum install -y docker-1.13.1

1)修改docker配置文件
[root@master ~]# vim /etc/sysconfig/docker
把之前的OPTIONS注释掉
#OPTIONS='--selinux-enabled --log-driver=journald --signature-verification=false'
新增如下内容:
OPTIONS='--selinux-enabled --signature-verification=False'

[root@node1~]# vim /etc/sysconfig/docker
把之前的OPTIONS注释掉
#OPTIONS='--selinux-enabled --log-driver=journald --signature-verification=false'
新增如下内容:
OPTIONS='--selinux-enabled --signature-verification=False'

2)配置docker镜像加速器
[root@node1 ~]# systemctl start docker
[root@node2 ~]# systemctl start docker

[root@master ~]# cat /etc/docker/daemon.json
{"registry-mirrors": ["http://6e9e5b27.m.daocloud.io","https://registry.docker-cn.com"],
"insecure-registries":["192.168.40.180:5000"]
}

[root@node1 ~]# cat /etc/docker/daemon.json
{"registry-mirrors": ["http://6e9e5b27.m.daocloud.io","https://registry.docker-cn.com"],
"insecure-registries":["192.168.40.180:5000"]
}

3)重启docker
[root@master ~]# systemctl restart docker
[root@node1~]# systemctl restart docker

4)配置私有镜像仓库
[root@master ~]# docker load -i registry.tar.gz
[root@master ~]# yum install httpd -y
[root@master ~]# systemctl start httpd
[root@master ~]# systemctl enable httpd
[root@master ~]# mkdir -p /opt/registry-var/auth/
[root@master ~]# docker run --entrypoint htpasswd registry:2.5 -Bbn xianchao xianchao >> /opt/registry-var/auth/htpasswd

设置配置文件
[root@master ~]# mkdir -p /opt/registry-var/config
[root@master ~]# vim /opt/registry-var/config/config.yml
version: "0.1"
log:
fields:
service: registry
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
headers:
X-Content-Type-Options: [nosniff]
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3

启动服务
docker run -d -p 5000:5000 --restart=always --name=registry -v /opt/registry-var/config/:/etc/docker/registry/ -v /opt/registry-var/auth/:/auth/ -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -v /opt/registry-var/:/var/lib/registry/ registry:2.5

1.2.3 登录镜像仓库
[root@master ~]#docker login 192.168.40.180:5000
用户名:
密码:

[root@node1~]#docker login 192.168.40.180:5000
用户名:
密码:

1.2.4 修改docker存储
1)设置docker开机自启动
systemctl enable docker
systemctl is-active docker
2)所有节点更改/etc/sysconfig/docker-storage-setup如下:
DEVS=/dev/sdb
VG=docker-vg
3)所有Node节点执行docker-storage-setup
docker-storage-setup

1.2.5 解压镜像
[root@node1 ~]# docker load -i openshift_slave_3_10.tar.gz
[root@node2 ~]# docker load -i openshift_slave_3_10.tar.gz

1.3、安装并访问openshift

1.3.1 配置ansible的hosts文件
[root@master ~]# cat /etc/ansible/hosts
[OSEv3:children]
masters
nodes
etcd
[OSEv3:vars]
openshift_deployment_type=origin
ansible_ssh_user=root
ansible_become=yes
openshift_repos_enable_testing=true
openshift_enable_service_catalog=false
template_service_broker_install=false
debug_level=4
openshift_clock_enabled=true
openshift_version=3.10.0
openshift_image_tag=v3.10
openshift_disable_check=disk_availability,docker_storage,memory_availability,docker_image_availability,os_sdn_network_plugin_name=redhat/openshift-ovs-multitenant i
openshift_master_identity_providers=[{'name': 'htpasswd_auth','login': 'true', 'challenge': 'true','kind': 'HTPasswdPasswordIdentityProvider'}]
[masters]
master
[nodes]
master openshift_node_group_name='node-config-master'
node1 openshift_node_group_name='node-config-master'
node2 openshift_node_group_name='node-config-master'
[etcd]
master
1.3.2 安装openshift
1)安装前预配置检查
[root@master ~]#ansible-playbook -i /etc/ansible/hosts openshift-ansible-release-3.10/playbooks/prerequisites.yml

3)安装
执行deploy时主机dns导致连外网失败(在执行上面deploy时,需要在每个节点ping www.baidu.com,如果ping不通,解决方案如下)
临时解决方案更改/etc/resolv.conf
当部署的时候看到retry,就需要在master和node节点执行下面命令,这样就可以继续ping通外网
[root@master ~]#echo nameserver 8.8.8.8 >>/etc/resolv.conf
可以写个计划任务:
crontab -e
* */1 * * * /usr/sbin/ntpdate cn.pool.ntp.org
* * * * * /usr/bin/echo nameserver 8.8.8.8 >>/etc/resolv.conf

[root@master ~]# ansible-playbook -i /etc/ansible/hosts openshift-ansible-release-3.10/playbooks/deploy_cluster.yml

需要给节点打标签
TASK [openshift_manage_node : Set node schedulability] 到这个task之后执行下面部分
[root@master ~]#oc label node node1 node-role.kubernetes.io/infra=true

# 登录 openshift

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1.3.3 登录openshift

首次新建用户密码
htpasswd -cb /etc/origin/master/htpasswd admin admin
添加用户密码
htpasswd -b /etc/origin/master/htpasswd dev dev
以集群管理员登录
oc login -u system:admin
给用户分配一个集群管理员角色
oc adm policy add-cluster-role-to-user cluster-admin admin
浏览器登录openshift,配置自己电脑hosts文件

https://master:8443/console/catalog

1.3.4 openshift内部私有仓库使用
Master上执行如下,获取admin密码
oc login -n openshift
admin/admin
oc whoami -t
eYtOZNGab9w4IO_LX2sHEHyoAZHOy1oxJ9gEsuJVMpI
每个节点执行如下登陆openshift私有镜像仓库    
docker login docker-registry.default.svc:5000
用户:admin
密码:eYtOZNGab9w4IO_LX2sHEHyoAZHOy1oxJ9gEsuJVMpI

# openshift 部署应用程序

1
2
3
4
使用OpenShift时,可以通过多种方式添加应用程序。 主要方法是:
1.通过已经存在的docker镜像部署应用程序
2.使用Source-to-Image的方式部署应用程序(Source-to-Image简称S2I:从代码仓库拉取代码,构建成镜像,基于此镜像部署应用程序)
3.在dockerfile中通过指定git仓库地址构建程序

# openshift CICD

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
一、CI/CD工具链涉及到的组件介绍
1、gogs:
Gogs 是一款极易搭建的自助 Git 服务,类似于github这种代码托管系统;以最简便的方式搭建简单、稳定和可扩展的自助 Git 服务。使用 Go 语言开发使得 Gogs 能够通过独立的二进制分发,并且支持 Go 语言支持的 所有平台,包括 Linux、macOS、Windows 以及 ARM 平台。
功能特性

官方文档
https://gogs.io/docs/intro

github上的中文站点:
https://github.com/gogs/gogs/blob/master/README_ZH.md


2、nexus是一个私服
为什么搭建nexus?
为什么要搭建nexus私服,原因很简单,有些公司都不提供外网给项目组人员,因此就不能访问maven中央仓库,或者公司内部的jar包在外网无法找到,所以很有必要在局域网里使用一台有外网权限的机器,搭建nexus私服,然后开发人员连到这台私服上,这样的话就可以通过这台搭建了nexus私服的电脑访问maven的远程仓库,或者从上面下载内部jar包,使得开发人员可以下载仓库中的内容,而且对于下载过的文件,局域网内下载会更加快速。还有一点优势在于,我们需要的jar包可能在中央仓库中没有,需要去其他地方下载,有了中央仓库,只需要一人找到jar包其他人就不用再去上网搜索jar包,十分方便。

3、sonarqube:
代码扫描工具

4、jenkins

二、创建CI/CD流

OpenShift DevOps/CICD工作流

1.从gogs或者gitlab克隆代码
2.通过maven构建war包
3.单元测试
4.Sonarqube静态代码扫描
5.war包归档到Nexus
6.构建docker image
7.把镜像上传到harbor镜像仓库
8.部署到dev(开发)环境
9.部署到测试环境-集成测试
10.管理员promote(同意) or abort(不同意)
11.部署到stage(生产)环境

官网地址:
https://github.com/siamaksade/openshift-cd-demo/tree/origin-1.3

# 排错

1
2
3
4
5
6
7
8
1、两台机器部署Keepalive出现双vip,如何排查?

添加如下参数

unicast_src_ip 192.168.0.29 ##source ip
unicast_peer {
192.168.0.186 ##dest ip
}

image-20230628160651535

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
2.jenkins 一定要最新的

3、kubeadm初始化k8s集群报错failed to parse kernel config:unable to load kernel module:”configs
答:
Kubeadm初始化k8s,会检测configs模块,configs模块现在centos操作系统不在维护了,可以忽略这个检查,那么kubeadm 初始化只需要加上参数--ignore-preflight-errors=SystemVerification即可

4.浏览器显示非私密连接
在google浏览器中,在上图出现的页面任意位置,直接键盘敲入这11个字符:thisisunsafe,可以直接跳过安全报错进入页面

5.centos8的yum源无法使用(dockerfile基于centos构建镜像报错Failed to download metadata for repo ‘appstream’: Cannot prepare internal mirrorlist: No URLs in mirrorlist)
vim Centos-vault-8.5.2111.repo
[base]
name=CentOS-8.5.2111 - Base - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos-vault/8.5.2111/BaseOS/$basearch/os/
http://mirrors.aliyuncs.com/centos-vault/8.5.2111/BaseOS/$basearch/os/
http://mirrors.cloud.aliyuncs.com/centos-vault/8.5.2111/BaseOS/$basearch/os/
gpgcheck=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-Official

#additional packages that may be useful
[extras]
name=CentOS-8.5.2111 - Extras - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos-vault/8.5.2111/extras/$basearch/os/
http://mirrors.aliyuncs.com/centos-vault/8.5.2111/extras/$basearch/os/
http://mirrors.cloud.aliyuncs.com/centos-vault/8.5.2111/extras/$basearch/os/
gpgcheck=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-Official

#additional packages that extend functionality of existing packages
[centosplus]
name=CentOS-8.5.2111 - Plus - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos-vault/8.5.2111/centosplus/$basearch/os/
http://mirrors.aliyuncs.com/centos-vault/8.5.2111/centosplus/$basearch/os/
http://mirrors.cloud.aliyuncs.com/centos-vault/8.5.2111/centosplus/$basearch/os/
gpgcheck=0
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-Official

[PowerTools]
name=CentOS-8.5.2111 - PowerTools - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos-vault/8.5.2111/PowerTools/$basearch/os/
http://mirrors.aliyuncs.com/centos-vault/8.5.2111/PowerTools/$basearch/os/
http://mirrors.cloud.aliyuncs.com/centos-vault/8.5.2111/PowerTools/$basearch/os/
gpgcheck=0
enabled=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-Official


[AppStream]
name=CentOS-8.5.2111 - AppStream - mirrors.aliyun.com
baseurl=http://mirrors.aliyun.com/centos-vault/8.5.2111/AppStream/$basearch/os/
http://mirrors.aliyuncs.com/centos-vault/8.5.2111/AppStream/$basearch/os/
http://mirrors.cloud.aliyuncs.com/centos-vault/8.5.2111/AppStream/$basearch/os/
gpgcheck=0
gpgkey=http://mirrors.aliyun.com/centos/RPM-GPG-KEY-CentOS-Official

6.云主机安全组放行端口

image-20230628161250017

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
删除kubeadm安装的k8s集群,重新搭建

在k8s集群的每个节点执行kubeadm reset即可,这个命令是重置k8s集群,会清空k8s所有组件和资源。


7.yum install 报错
yum clean all
yum makecache -y
yum install epel-release -y
yum update -y

8.删除k8s名称空间一直报错Terminating
kubectl get namespace app-team1 -o json > temp.json
修改temp.json文件
删除框住的部分

image-20230628161656494

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
再开启一个新的终端执行如下:
root@master1:~# kubectl proxy

另一个终端执行如下:
root@master1:~# curl -k -H "Content-Type: application/json" -X PUT --data-binary @temp.json http://127.0.0.1:8001/api/v1/namespaces/app-team1/finalize


11、基于kubectl run busybox2 --image busybox:1.28 --restart=Never --rm -it busybox – sh 创建的pod,无法ping www.baidu.com
首先把安装k8s的所有机器重启,然后重新创建pod,如果重启之后还是ping不通www.baidu.com,可以按照如下方法解决:
进入pod
打开/etc/resolv.conf,最后添加nameserver 8.8.8.8
在search这增加域名www.baidu.com即可

12.containerd如果作为容器运行时,用ctr拉取镜像,需要写镜像完成的路径才可以
ctr images pull docker.io/library/busybox:1.28

# pod

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
部分工作节点上的pod一直处于ContainerCreating的状态

kubectl get pods -o wide | grep tomcat

NAME READY STATUS RESTARTS AGE IP NODE
tomcat-pod 0/1 ContainerCreating 0 106s 10.244.1.4 k8s-node1

kubectl describe pods tomcat-pod


一、pending状态
Pending是挂起状态:表示创建的Pod找不到可以运行它的物理节点,不能调度到相应的节点上运行,那么这种情况如何去排查呢?我门可以从两个层面分析问题:

1.物理节点层面分析:
1)查看节点资源使用情况:如free -m查看内存、top查看CPU使用率、df –h查看磁盘使用情况,这样就可以快速定位节点资源情况,判断是不是节点有问题
2)查看节点污点:kubectl describe nodes master1(控制节点名字),如果节点定义了污点,那么Pod不能容忍污点,就会导致调度失败,如下:
Taints: node-role.kubernetes.io/master:NoSchedule
这个看到的是控制节点的污点,表示不允许Pod调度(如果Pod定义容忍度,则可以实现调度)

2.Pod本身分析
1)在定义pod时,如果指定了nodeName是不存在的,那也会调度失败
2)在定义Pod时,如果定义的资源请求比较大,导致物理节点资源不够
如何排查,可按如下分析:
3)在定于pod时,如果制定了node节点应亲和性或者nodeselector,那么没有满足的条件,也会pending状态



二、ImagePullBackOff状态
问题分析:
拉取镜像时间长导致超时:
配置的镜像有错误:如镜像名字不存在等
配置的镜像无法访问:如网络限制无法访问hub上的镜像
配置的私有镜像仓库参数错误:如imagePullSecret没有配置或者配置错误
dockerfile打包的镜像不可用
排查思路:
#查看Pod
kubectl get pods -o wide
显示如下:
tomcat-pod 0/1 ImagePullBackOff 0 105s
通过上面可以看到镜像拉取有问题,那可以进一步再排查错误
kubectl describe pods tomcat-pod
显示如下:
Normal BackOff 96s (x7 over 6m15s) kubelet Back-off pulling image "atomcat:8.5-jre8-alpine"
可以在宿主机通过docker pull手动验证是否可以正常拉取镜像
docker pull atomcat:8.5-jre8-alpine
报错:
Error response from daemon: pull access denied for atomcat, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
那就说明我们使用的镜像有问题,需要重新更改镜像地址

三、CrashLoopBackOff状态
K8s中的pod正常运行,但是里面的容器可能退出或者多次重启或者准备删除,导致出现这个状态,这个状态具有偶发性,可能上一秒还是running状态,但是突然就变成CrashLoopBackOff状态了。
1)kubectl logs 查看日志,出现如下:
standard_init_linux.go:216: exec user process caused "exec format error“
解决方案:
查看构建镜像用的代码是否有问题,如果代码没问题,再看环境变量是否有问题,可能权限问题:假如pod挂载了数据目录,这个数据目录,我们需要修改属主和属组,否则pod往这个目录写数据,可能没权限
2)kubectl describe pods 查看详细信息

四、Evicted状态
这个Evicted表示pod所在节点的资源不够了,pod被驱逐走了

五、Complete状态
这个状态表示pod里面的任务完成了,job或者cronjob创建pod的时候,如果pod任务完成了,会出现这个complete状态

六、error状态


15、pod健康探测

pod定义的存活性探测如下:
livenessProbe:
httpGet:
path: /
port: 8080

常见错误:
Killing container with id docker://:Container failed probe.. Container will be killed and recreated. Liveness

如果在pod启动时遇到这种情况,一般是没有设置 initialDelaySeconds导致的
# 设置初始化延迟initialDelaySeconds。
livenessProbe:
httpGet:
path: /
port: 8080
initialDelaySeconds: 15 # pod启动过15s开始探测


15、pod服务超时

K8S中Pod服务连接超时主要分以下几种情况:
1.Pod和Pod连接超时
2.Pod和虚拟主机上的服务连接超时
3.Pod和云主机连接超时

针对上面几种问题,排查思路可按下面方法:
网络插件层面:
查看calico或者flannel是否是running状态,查看日志提取重要信息

Pod层面:
检查Pod网络,测试Pod是否可以ping通同网段其他pod ip

物理机层面:
检查物理机网络,测试ping www.baidu.com,ping其他的pod ip
可以抓包测试有无异常
通过抓包修改内核参数

16、service代理pod出现问题
直接访问pod可以请求到,但是访问pod前端service,通过请求pod有问题,请求不到。

iptables: 重启iptables,重启iptables不可以,重启下机器
ipvs:重启机器

# Tekton

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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
Tekton 是一个功能强大且灵活的 Kubernetes 原生开源框架,是谷歌开源的,功能强大且灵活,开源社区也正在快速的迭代和发展壮大,主要用于创建持续集成和交付(CI/CD)系统。通过抽象底层实现细节,用户可以跨多云平台和本地系统进行构建、测试和部署。另外,基于kubernetes CRD定义的pipeline流水线也是Tekton最重要的特征。


CRD全称是CustomResourceDefinition:
Kubernetes 中一切都可视为资源,Kubernetes 1.7 之后增加了对 CRD 自定义资源二次开发能力来扩展 Kubernetes API,通过 CRD 我们可以向 Kubernetes API 中增加新资源类型,而不需要修改 Kubernetes 源码来创建自定义的 API server,该功能大大提高了 Kubernetes 的扩展能力。当你创建一个新的CustomResourceDefinition (CRD)时,Kubernetes API服务器将为你指定的每个版本创建一个新的RESTful资源路径,我们可以根据该api路径来创建一些我们自己定义的类型资源。CRD可以是命名空间的,也可以是集群范围的,由CRD的作用域(scpoe)字段中所指定的,与现有的内置对象一样,删除名称空间将删除该名称空间中的所有自定义对象。customresourcedefinition本身没有名称空间,所有名称空间都可以使用。


持续集成是云原生应用的支柱技术之一,因此在交付基于云原生的一些支撑产品的时候,CICD 是非常好的方案。为了满足这种需要,自然而然会想到对Jenkins(X)或者 Gitlab 进行集成,也有创业公司出来的一些小工具比如 Argo Rollout。Tekton 是一款 k8s 原生的应用发布框架,主要用来构建 CI/CD 系统。它原本是 knative 项目里面一个叫做 build-pipeline 的子项目,用来作为 knative-build 的下一代引擎。然而,随着 k8s 社区里各种各样的需求涌入,这个子项目慢慢成长为一个通用的框架,能够提供灵活强大的能力去做基于 k8s 的构建发布。Tekton 其实只提供 Pipeline 这个一个功能,Pipeline 会被直接映射成 K8s Pod API 资源。而比如应用发布过程的控制,灰度和上线策略,都是我们自己编写 K8s Controller来实现的,也就意味着 Tekton 不会在K8s 上盖一个”大帽子“,比如我们想看发布状态、日志等是直接通过 K8s 查看这个 Pipeline 对应的 Pod 的状态和日志,不需要再面对另外一个 API。


Tekton功能:
1.Kubernetes原生的Tekton的所有配置都是使用CRD方式进行编写存储的,非常易于检索和使用。
2.配置和流程分离: Tekton的Pipeline和配置可以分开编写,使用名称进行引用。
3.轻量级核心的Pipeline 非常轻便:适合作为组件进行集成,另外也有周边的 Dashboard、Trigger、CLI等工具,能够进一步挖掘其潜力。
4.可复用、组合的 Pipeline 构建方式:非常适合在集成过程中对Pipeline进行定制。

这里的流程大致是:
1、用户把需要部署的应用先按照一套标准的应用定义写成 YAML 文件(类似 Helm Chart);
2、用户把应用定义 YAML 推送到 Git 仓库里;
3、Tekton CD (一个 K8s Operator) 会监听到相应的改动,根据不同条件生成不同的 Tekton Pipelines;
Tekton CD 的操作具体分为以下几种情况:
1、如果 Git 改动里有一个应用 YAML 且该应用不存在,那么将渲染和生成 Tekton Pipelines 用来创建应用。
2、如果 Git 改动里有一个应用 YAML 且该应用存在,那么将渲染和生成 Tekton Pipelines 用来升级应用。这里我们会根据应用定义 YAML 里的策略来做升级,比如做金丝雀发布、灰度升级。
3、如果 Git 改动里有一个应用 YAML 且该应用存在且标记了“被删除”,那么将渲染和生成 Tekton Pipelines 用来删除应用。确认应用被删除后,我们才从 Git 里删除这个应用的 YAML。


安装Tekton
#把tekton-0-12-0.tar.gz和busybox-v-1-0.tar.gz上传到node1机器上,手动解压:
[root@node1]# docker load -i tekton-0-12-0.tar.gz
[root@node1]# docker load -i busybox-v-1-0.tar.gz
[root@node2]# docker load -i tekton-0-12-0.tar.gz
[root@node2]# docker load -i busybox-v-1-0.tar.gz

#编写安装tekton资源清单文件
[root@master1 ~]# kubectl apply -f release.yaml
#验证pod是否创建成功
[root@master1 ~]# kubectl get pods -n tekton-pipelines
NAME READY STATUS RESTARTS AGE
tekton-pipelines-controller-d64889fb9-9b68f 1/1 Running 0 52s
tekton-pipelines-webhook-7c8664944c-ctsgf 1/1 Running 0 52s

[root@master1 ~]# kubectl get crd
找到下面这些东西,说明创建crd成功了:
pipelineresources.tekton.dev 2021-08-21T13:52:16Z
pipelineruns.tekton.dev 2021-08-21T13:52:16Z
pipelines.tekton.dev 2021-08-21T13:52:16Z
taskruns.tekton.dev 2021-08-21T13:52:16Z
tasks.tekton.dev 2021-08-21T13:52:16Z

[root@master1 ~]# kubectl api-versions
看到下面说明apiversion创建成功了
tekton.dev/v1alpha1
tekton.dev/v1beta1

Tekton概念
Tekton 为Kubernetes 提供了多种 CRD 资源对象,可用于定义我们的流水线,主要有以下几个CRD资源对象:
1)Task:表示执行命令的一系列步骤,task 里可以定义一系列的 steps,例如编译代码、构建镜像、推送镜像等,每个 step 实际由一个 Pod 里的容器执行。
2)TaskRun:task只是定义了一个模版,taskRun 才真正代表了一次实际的运行,当然你也可以自己手动创建一个taskRun,taskRun创建出来之后,就会自动触发task描述的构建任务。
3)Pipeline:一组任务,表示一个或多个task、PipelineResource 以及各种定义参数的集合。
4)PipelineRun:类似task和taskRun的关系,pipelineRun也表示某一次实际运行的 pipeline,下发一个 pipelineRun CRD 实例到 Kubernetes后,同样也会触发一次 pipeline 的构建。
5)PipelineResource:表示pipeline输入资源,比如github上的源码,或者pipeline 输出资源,例如一个容器镜像或者构建生成的jar包等。


我们测试一个简单的golang程序。应用程序代码,测试及dockerfile文件可在如下地址获取:https://github.com/luckylucky421/tekton-demo

3.8.1 clone应用程序代码进行测试,创建一个task任务
[root@master1 ~]# cat task-test.yaml
apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: test
spec:
resources:
inputs:
- name: repo
type: git
steps:
- name: run-test
image: golang:1.14-alpine
imagePullPolicy: IfNotPresent
workingDir: /workspace/repo
command: ["go"]
args: ["test"]
#更新资源清单文件
[root@master1 ~]# kubectl apply -f task-test.yaml
task.tekton.dev/test created
#查看Task资源
[root@master1 ~]# kubectl get Task
NAME AGE
test 23s

resources定义了我们的任务中定义的步骤中需要输入的内容,这里我们的步骤需要 Clone 一个 Git 仓库作为 go test 命令的输入。Tekton 内置了一种 git 资源类型,它会自动将代码仓库 Clone  /workspace/$input_name 目录中,由于我们这里输入被命名成 repo,所以代码会被 Clone  /workspace/repo 目录下面。然后下面的 steps 就是来定义执行运行测试命令的步骤,这里我们直接在代码的根目录中运行 go test 命令即可,需要注意的是命令和参数需要分别定义。


创建pipelineresource资源对象
通过上面步骤我们定义了一个 Task 任务,但是该任务并不会立即执行,我们必须创建一个 TaskRun 引用它并提供所有必需输入的数据才行。这里我们就需要将 git 代码库作为输入,我们必须先创建一个 PipelineResource 对象来定义输入信息,创建一个名为 pipelineresource.yaml 的资源清单文件,内容如下所示:
[root@master1 ~]# cat pipelineresource.yaml
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
name: -tekton-example
spec:
type: git
params:
- name: url
value: https://github.com/luckylucky421/tekton-demo
- name: revision
value: master
#更新资源清单文件
[root@master1 ~]# kubectl apply -f pipelineresource.yaml


创建taskrun任务
[root@master1 ~]# cat taskrun.yaml
apiVersion: tekton.dev/v1beta1
kind: TaskRun
metadata:
name: testrun
spec:
taskRef:
name: test
resources:
inputs:
- name: repo
resourceRef:
name: -tekton-example



#更新资源清单文件
[root@master1 ~]# kubectl apply -f taskrun.yaml
#上面资源清单文件解释说明
这里通过taskRef引用上面定义的Task和git仓库作为输入,resourceRef也是引用上面定义的PipelineResource资源对象。

#创建后,我们可以通过查看TaskRun资源对象的状态来查看构建状态
[root@master1 ~]# kubectl get taskrun
NAME SUCCEEDED REASON STARTTIME
testrun Unknown Running 6s
[root@master1 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testrun-pod-x9rkn 2/2 Running 0 9s
当任务执行完成后, Pod 就会变成 Completed 状态了:
[root@master1 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
testrun-pod-x9rkn 0/2 Completed 0 72s

# 创建自定义资源

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
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: crontabs.stable.example.com
spec:
  group: stable.example.com
  versions:
    - name: v1
      served: true
      storage: true
      schema:
        openAPIV3Schema:
          type: object
          properties:
            spec:
              type: object
              properties:
                cronSpec:
                  type: string
                image:
                  type: string
                replicas:
                  type: integer
  scope: Namespaced
  names:
    plural: crontabs
    singular: crontab
    kind: CronTab
    shortNames:
    - ct
    
Yaml文件注解:
metadata.name 是用户自定义资源中自己自定义的一个名字。一般我们建议使用“顶级域名.xxx.APIGroup”这样的格式,名称必须与下面的spec.Group字段匹配,格式为: <plural>.<group>

spec 用于指定该 CRD group、version。比如在创建 Pod 或者 Deployment 时,它的 group 可能为 apps/v1 或者 apps/v1beta1 之类,这里我们也同样需要去定义 CRD group。

group: stable.example.com #组名称
versions:
- name: v1
#指定组下的版本

served: true
#每个版本都可以通过服务标志启用/禁用
storage: true
#必须将一个且只有一个版本标记为存储版本。

scope: Namespaced
#指定crd资源作用范围在命名空间或集群

names 指的是它的 kind 是什么,比如 Deployment kind 就是 Deployment,Pod kind 就是 Pod,这里的 kind 被定义为了CronTab

plural 字段就是一个昵称,比如当一些字段或者一些资源的名字比较长时,可以用该字段自定义一些昵称来简化它的长度;

singular: crontab
# 在CLI(shell界面输入的参数)上用作别名并用于显示的单数名称

shortNames:
- ct

# 短名称允许短字符串匹配CLI上的资源,就是能通过kubectl 在查看资源的时候使用该资源的简名称来获取。

## 创建资源
apiVersion: "stable.example.com/v1"
kind: CronTab
metadata:
name: my-new-cron-object
spec:
cronSpec: "* * * * * *"
image: busybox
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
1、项目地址
https://github.com/mongodb/mongodb-kubernetes-operator.git

把课件里的压缩包传到k8s控制节点上来,手动解压
unzip mongodb-kubernetes-operator-0.5.0.zip

2、创建名称空间mongodb,并进入到mongodb-kubernetes-operator目录应用crd资源,创建自定义资源类型
[root@master1 ~] kubectl create ns mongodb

[root@master1 ~]# cd mongodb-kubernetes-operator-0.5.0

[root@master1 mongodb-kubernetes-operator-0.5.0]# kubectl apply -f deploy/crds/mongodb.com_mongodbcommunity_crd.yaml

#查看mongodb是否创建成功
kubectl get crd/mongodbcommunity.mongodb.com

3、安装operator
[root@master1 mongodb-kubernetes-operator-0.5.0]# kubectl apply -f deploy/operator/ -n mongodb

提示:mongodb-kubernetes-operator这个项目是将自定义控制器和自定义资源类型分开实现的;其operator只负责创建和监听对应资源类型的变化,在资源有变化时,实例化为对应资源对象,并保持对应资源对象状态吻合用户期望状态;上述四个清单中主要是创建了一个sa账户,并对对应的sa用户授权;

验证:查看operator是否正常运行
[root@master1 mongodb-kubernetes-operator-0.5.0]# kubectl get pods -n mongodb
NAME READY STATUS RESTARTS AGE
mongodb-kubernetes-operator-7f8c55db45-tmpk5 1/1 Running 0 44s

验证:使用自定义资源类型创建一个mongodb 副本集集群
[root@master1 mongodb-kubernetes-operator-0.5.0]# cat deploy/crds/mongodb.com_v1_mongodbcommunity_cr.yaml

[root@master1 mongodb-kubernetes-operator-0.5.0]# kubectl apply -f deploy/crds/mongodb.com_v1_mongodbcommunity_cr.yaml -n mongodb

[root@master1 mongodb-kubernetes-operator-0.5.0]# kubectl get pods -n mongodb
NAME READY STATUS RESTARTS AGE
example-mongodb-0 0/2 Pending 0 66s

提示:这里可以看到对应pod处于pending状态;
  查看pod详细信息
  
 提示:这里提示没有可以用的pvc;
  删除mongodb名称空间下pvc
kubectl get pvc -n mongodb

kubectl delete pvc --all -n mongodb

 创建pv和pvc
[root@master1 ~]# cat pv-demo.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-v1
labels:
app: example-mongodb-svc
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes: ["ReadWriteOnce","ReadWriteMany","ReadOnlyMany"]
persistentVolumeReclaimPolicy: Retain
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /data/p1
server: 192.168.40.180
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-v2
labels:
app: example-mongodb-svc
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes: ["ReadWriteOnce","ReadWriteMany","ReadOnlyMany"]
persistentVolumeReclaimPolicy: Retain
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /data/p2
server: 192.168.40.180
---

apiVersion: v1
kind: PersistentVolume
metadata:
name: nfs-pv-v3
labels:
app: example-mongodb-svc
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes: ["ReadWriteOnce","ReadWriteMany","ReadOnlyMany"]
persistentVolumeReclaimPolicy: Retain
mountOptions:
- hard
- nfsvers=4.1
nfs:
path: /data/p3
server: 192.168.40.180

[root@master1 ~]# mkdir /data/p1
[root@master1 ~]# mkdir /data/p2
[root@master1 ~]# mkdir /data/p3
[root@master1 ~]# cat /etc/exports
/data/v1 *(rw,no_root_squash)
/data/p1 *(rw,no_root_squash)
/data/p2 *(rw,no_root_squash)
/data/p3 *(rw,no_root_squash)

[root@master1 ~]# exportfs -arv
[root@master1 ~]# kubectl apply -f pv-demo.yaml

创建pvc资源
[root@master1 ~]# cat pvc-demo.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-volume-example-mongodb-0
namespace: mongodb
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 500Mi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-volume-example-mongodb-1
namespace: mongodb
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 500Mi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data-volume-example-mongodb-2
namespace: mongodb
spec:
accessModes:
- ReadWriteMany
volumeMode: Filesystem
resources:
requests:
storage: 500M

[root@master1 ~]# kubectl apply -f pvc-demo.yaml
[root@master01 ~]# kubectl get pods -n mongodb
NAME                                           READY   STATUS    RESTARTS   AGE
example-mongodb-0                              2/2     Running   0          8m
example-mongodb-1                              2/2     Running   0          111s
example-mongodb-2                              2/2     Running   0          48s
mongodb-kubernetes-operator-7d557bcc95-th8js   1/1     Running   0          9m19s

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

John Doe WeChat Pay

WeChat Pay

John Doe Alipay

Alipay

John Doe PayPal

PayPal