hello云胜

技术与生活

0%

下载ubuntu安装镜像iso文件

下载Ubuntu桌面系统 | Ubuntu

将镜像复制到u盘

使用的是UEFI方式,不需要使用UltraISO将镜像写入U盘

只需要将所有的文件复制到u盘里。

按住f2,不要松开。进入bios。

一次Linux内存问题-swap过高

image-20220606155634188

从业务视角出发,建立完善的监控体系

为什么我们选择业务视角去添加监控呢?因为可靠性的最终落脚点,就是为用户提供可靠、稳定的服务,业务视角是最贴近用户体验的视角。这就是我们选择业务视角添加监控的原因。

监控的三个层级

  1. 能保证发现问题
  2. 能快速发现问题
  3. 能快速定位问题
  4. 快速做出影响评估,为决策提供有效的信息支撑

如何做

  1. 统一监控门户

    先进行一次全面的监控整理。这个过程本质上是从混沌走向有序的转变。监控数据分散在不同的地方,你把它们集中起来进行分类,往往能激发新的灵感。

    说起来简单,做起来难,各个监控记录格式不统一

  2. 构建业务监控大盘

    业务监控大盘的重要性在于它可以实时且直观地帮助我们去判断当前业务的整体情况。如果大盘稳定就说明这个时候没有大的故障,如果大盘不稳定,说明这个时候已经有故障或者异常出现了。

    为了让大盘看上去更清晰,我建议你给每个功能创建一个面板。在设计这些面板的时候,首先需要关注的是请求量,重点关注同环比,我们可以用红色、蓝色和绿色线条表示今天、昨天、上周。其次是成功率、容量、P99 响应时间,这里我们只需要关注实时数据就可以了,这样做能够确保监控的时效性,同时也能看到同环比。

  3. 进一步细化及拆分核心指标

  4. 梳理和细化核心链路

  5. 统一添加基础指标

  6. 第六步是关键信息的提取、汇总和初步判断

优化监控报警

信息爆炸和报警膨胀的问题

  • 报警分级
  • 报警信息合并:把相同或相似的报警信息进行合并,以减少重复报警
  • 调整报警阈值:合理设置报警阈值,可以避免因小幅度波动而频繁触发报警。
  • 报警规则优化:定期审查和优化报警规则,确保它们仍然符合当前的业务需求和系统状态。
  • 报警响应流程:我们应该建立有效的报警响应流程,确保团队能够及时、有效地处理报警。

img

其他的重要动作:

日常巡检:定期检查监控体系,确保监控系统正常运行,及时发现并解决问题。

报警响应:此外还要建立一个有效的报警响应流程,确保报警信息能够迅速被处理。

定期总结:通过日、周总结机制,回顾监控数据,分析系统表现,识别潜在风险。

问题复盘:对于已经发生的问题,进行深入地复盘分析,从中学习经验,优化监控策略。

什么是容量

以第一性原理来探讨这个问题,容量本质上是资源消耗与资源补充之间取得一个平衡。目标是在确保系统可靠性的同时,尽可能地减少资源的投入。

SRE(Site Reliability Engineering)

Reliability 可靠性是一个多维度概念,涵盖了科学、技术、工程和应用的各个方面。

  • 作为科学,可靠性探讨了“是什么”和“为什么”,它研究系统失败的原因和规律,以及如何通过理论模型来解释和预测这些现象。
  • 作为技术,可靠性关注的是“怎么做”,它涉及开发和应用各种方法和工具来提高系统的性能和稳定性。
  • 作为工程,可靠性追求的是“怎么做得既快速又经济、高效”,它强调在设计和制造过程中实现可靠性的最优化,以达到成本效益和性能的最佳平衡。
  • 作为应用,可靠性解决的是“如何将理论应用于具体场景”,它指导实际产品和服务的设计,确保它们在现实世界中能够稳定、持续地满足用户需求。

img

通过这张全景图可以知道想要做好服务可靠性保障建设,我们需要重点关注三个问题。如何衡量服务的可靠性,指标体系是什么?应该重点考虑并建设哪些环节?支撑可靠性保障体系持续完善的几个要素是什么?

我们不仅要看结果指标,是否有故障,有异常;更要细化到每个层级去看系统指标。

img

两种 SLA 定义方式

实时性 SLA:这种定义侧重于服务的实时性能,通常用公式 1−丢失pv/总pv 来表示。这里,我们更关注 PV 丢失的数量,它包括网页丢失、5xx 错误等非正常返回的情况。

结果导向 SLA:这种定义通过最终的故障影响来衡量,例如,如果一个季度内没有发生任何故障,那么 SLA 为 100%。如果发生了一次故障,导致 100PV 的损失,那么可以把 SLA 定位为 1−100pv/总pv。如果第二次故障的损失为 200PV,那 SLA 更新为 1−100pv/总pv−200pv/总pv。

这两种定义方式并没有绝对的优劣之分,因为它们适用于不同的业务场景

但仅仅依赖结果指标来评估系统稳定性是不够的。这种做法可能会导致我们陷入一个误区:**如果一个系统在一段时间内没有进行任何变更,业务流量也保持平稳,表面上看似稳定,实际上可能隐藏着许多隐患。**这样的系统可能无法承受一次新的上线或流量的突然增加。关键问题可能无法被及时发现,从而导致潜在的风险和故障。

怎么做

六个关键组成部分,分别是监控、容量、变更、预案、备份,还有机制

img

感觉还缺少故障演练部分

如何让可靠性体系持续完善

img

首先,我们应当重视经验的积累与传承。在不同的业务领域、系统架构和应用场景中,定位逻辑往往存在显著差异。资深工程师与新手工程师在问题解决能力上的差异尤为明显。因此,我们的主要目标是将这些宝贵的经验沉淀下来。以系统监控为例,我们需要明确监控的设置方法,深入考虑监控的关键维度,确保监控系统能够全面、有效地反映系统运行状态。其次,我们需要关注工具和系统的重要性。工欲善其事,必先利其器,要提升系统的可靠性和保障能力,就必须依靠先进的工具和系统作为支撑。以系统监控为例,不同的监控工具在数据可视化、展示方式、数据丰富度以及操作便捷性等方面存在显著差异。一个优秀的监控系统能够显著提高故障检测的效率,从而快速响应并解决问题,确保业务的连续性和稳定性。第三,我们应重视数据和模型在服务可靠性中的核心作用。在人工智能技术日益普及的今天,服务的可靠性越来越依赖于算法和模型的支持。我们需要将传统的被动人工判断和决策转变为主动的机器判断和决策。通过持续地将经验转化为算法模型,我们可以使模型更加智能和强大,从而减少因个人经验差异导致的效率和准确性的差异,实现服务的高效和稳定。

img

上周经历了一次k8s集群的重大故障,

有多重大呢?整个k8s集群的node节点全部重启了。

故障原因呢?我当然是不会说的。

但是我想分享一下在处理这个故障过程中积累的一点知识

先说现象

在k8s集群重启完成,各个kube-system的服务都正常了之后,业务pod开始重启。

在这个时候我们发现业务pod的恢复特别慢。

进一步追查日志,是因为我们使用了istio。

而istiod服务此时还没有启动成功,这就导致了Istio 使用 ValidatingAdmissionWebhooks 验证 Istio 配置的时候验证失败,阻挡了业务pod的启动。

自然的,我们去追查istio的pod启动问题。结果发现,不管我们修改istio的repicas数量还是删除重建istio,都不能创建出isto的pod。

排查和解决

看到这个现象,自然会想到是pod调度出问题了。

我们指导controller manager是控制容器的数量符合配置的repicas数量,scheduler是给等待调度的pod计算出合适的node节点,

在这个时候看到的是pod都没有创建出来,都还没到调度。

所以将重点对准了controller manager

在api-server中看到了Rate Limited相关的日志

1
2
3
4
OpenAPI AggregationController: action for item v1beta1.metrics.k8s.io: Rate Limited Requeue.
...
loading OpenAPI spec for "v1beta1.metrics.k8s.io" failed with: failed to retrieve openAPI spec, http error: ResponseCode: 503, Body: service unavailable
, Header: map[Content-Type:[text/plain; charset=utf-8] X-Content-Type-Options:[nosniff]]

测试推测是大量业务pod同时重启,导致controller-manager忙不过来了。

回想起controller-manager似乎有一些关于并发的配置参数,

查询官方文档

kube-controller-manager | Kubernetes

修改了这三个参数

image-20240828192755354

image-20240828192820462

image-20240828192836498

controller-manager是静态pod,直接修改master节点上的资源文件即可。

修改文件 /etc/kubernetes/manifests/kube-controller-manager.yaml

添加配置参数

1
2
3
4
5
--concurrent-replicaset-syncs=20

--kube-api-qps=50

--kube-api-burst=100

image-20240828154950220

重启之后,istiod的pod确实很快创建了出来。业务pod也得到了快速的恢复。

其实加这三个参数是有碰运气的成分在里面,我并不确定如果不修改这三个参数,只是重启controller-manager故障是不是也能解决。

但是在cpu比较富裕的情况下,将并发参数调大是合理的。

j2文件

就是一种可编程的模板文件,我们部署中间件的conf文件中经常会有变量,用j2文件来实现

魔法变量

Ansible默认会提供一些内置的变量以实现一些特定的功能,我们称之为魔法变量。下面列举一些常用的魔法变量。

1. hostvars

获取某台指定的主机的相关变量。如果有一台web服务器的配置文件中需要指定db服务器的ip地址,我们假定这台db服务器的hostname为db.exmaple.com,ip地址绑定在eth0网卡上,我们可以通过如下方法在web服务器上调用db服务器的ip地址:

1
{{ hostvars['db.example.com'].ansible_eth0.ipv4.address }}

需要注意的是db.example.com不能使用ip地址来取代,只能使用主机名或别名。

2. inventory_hostname

inventory_hostname是Ansible所识别的当前正在运行task的主机的主机名。如果在inventory里定义过别名,那么这里就是那个别名,如果inventory包含如下一行:

1
server1 ansible_ssh_host=192.168.1.1

inventory_hostname即为server1
利用hostvarsinventory_hostname变量,可以输出与当前主机相关联的所有变量:

1
- debug: var=hostvars[inventory_hostname]

与inventory_hostname相近的还有一个inventory_hostname_short,如果一台主机的inventory_hostname为server1.exmaple.com,则inventory_hostname_short的值为server1

3. group_names

用于标识当前正在执行task的目标主机位于的主机组。假如我们有三台主机,用来配置成一主二从的mysql服务器。inventory配置如下:

1
2
3
4
5
[mdb]
db1
[sdb]
db2
db3

mysql配置文件my.conf.j2示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#我们知道db1在mdb组,当db1与当前正在执行任务的主机位于同一组时,我们认为当前主机即在mdb组,所以对当前主机应用mysql master的配置
# 比较绕,好好理解下。if 'db1' in group_names是判断db1是否在当前运行的主机组里,在,说明当前运行的主机组是mdb组
{% if 'db1' in group_names %}
[mysqld]
server-id=1
log-bin=mysql-bin
log-bin-index=mysql-bin.index
sync-binlog=1
innodb_flush_log_at_trx_commit=1
#当db1与当前主机不在同一组时,则认为当前主机不在mdb组,即应用my slave的配置
{% else %}
[mysqld]
server-id=2
relay-log=relay-log
relay-log-index=relay-log.index
read-only = yes
sync_master_info = 1
sync_relay_log = 1
sync_relay_log_info = 1
relay_log_recovery = 1
skip_slave_start
{% endif %}

我们执行如下task:

1
2
- name: copy config file to mysql master
template: src=my.conf.j2 dest=/etc/my.cnf

4. groups

groups是inventory中所有主机组的列表,可用于枚举主机组中的所有主机。

假如我们有一个inventory文件定义如下:

1
2
3
[web]
server1
server2

在配置一台HAproxy的负载均衡器时,我们的配置文件肯定需要web主机组的所有服务器的IP,配置文件包含如下片段:

1
2
3
4
backend web-backend
{% for host in groups.web%}
server {{host.inventory_hostname}} {{ host.ansible_default_ipv4.address }}:80
{% endfor %}

最终生成的文件如下:

1
2
3
backend web-backend
server server1 192.168.1.1:80
server server2 192.168.1.2:80

再给一个例子,在所有的dbservers组的服务器上创建一个数据库用户kate:

1
2
3
- name: Create a user for all db servers
mysql_user: name=kate password=test host={{ hostvars.[item].ansible_eth0.ipv4.address }} state=present
with_items: groups['dbservers']

5. play_hosts

当前playbook会在哪些hosts上运行

6. inventory_dir

主机清单所在目录

7. inventory_file

主机清单文件

当我们需要修改内核参数时,可以使用sysctl 模块。

比如,我们在shell中会使用

1
echo 'vm.swappiness=1' >> /etc/sysctl.conf

来修改内核的swappiness参数,但是如果在ansible脚本中,直接这样写,会破坏幂等性。

每次执行ansible脚本都会添加一行配置,很丑。所以ansible提供了sysctl 模块。

sysctl 可以对值进行设置,如果需要查询可以使用shell 模块。

  • name:变量名
  • value:值
  • reload:文件被更新时,是否使用 sysctl -p reload 文件
  • state:是在文件中 移除(absent)或者设置(present)
  • sysctl_file:如果不是默认文件,指定其他文件
  • sysctl_set:使用sysctl 命令设置,不一定需要reload 文件

使用方法

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
Usage:
sysctl [options] [variable[=value] ...]

Options:
-a, --all display all variables
-A alias of -a
-X alias of -a
--deprecated include deprecated parameters to listing
-b, --binary print value without new line
-e, --ignore ignore unknown variables errors
-N, --names print variable names without values
-n, --values print only values of a variables
-p, --load[=<file>] read values from file
-f alias of -p
--system read values from all system directories
-r, --pattern <expression>
select setting that match expression
-q, --quiet do not echo variable set
-w, --write enable writing a value to variable
-o does nothing
-x does nothing
-d alias of -h

-h, --help display this help and exit
-V, --version output version information and exit

For more details see sysctl(8).

ansible-doc sysctl 文档

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
> SYSCTL    (/usr/lib/python2.7/site-packages/ansible/modules/system/sysctl.py)

This module manipulates sysctl entries and optionally performs a `/sbin/sysctl -p' after changing them.

OPTIONS (= is mandatory):

- ignoreerrors
Use this option to ignore errors about unknown keys.
(Choices: yes, no)[Default: False]

= name
The dot-separated path (aka `key') specifying the sysctl variable.
(Aliases: key)[Default: None]

- reload
If `yes', performs a `/sbin/sysctl -p' if the `sysctl_file' is updated. If `no', does not reload `sysctl' even if the
`sysctl_file' is updated.
(Choices: yes, no)[Default: yes]

- state
Whether the entry should be present or absent in the sysctl file.
(Choices: present, absent)[Default: present]

- sysctl_file
Specifies the absolute path to `sysctl.conf', if not `/etc/sysctl.conf'.
[Default: /etc/sysctl.conf]

- sysctl_set
Verify token value with the sysctl command and set with -w if necessary
(Choices: yes, no)[Default: False]
version_added: 1.5


- value
Desired value of the sysctl key.
(Aliases: val)[Default: None]


AUTHOR: David CHANIAL (@davixx) <david.chanial@gmail.com>
METADATA:
status:
- stableinterface
supported_by: core

示例

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
EXAMPLES:
# Set vm.swappiness to 5 in /etc/sysctl.conf
- sysctl:
name: vm.swappiness
value: 5
state: present

# Remove kernel.panic entry from /etc/sysctl.conf
- sysctl:
name: kernel.panic
state: absent
sysctl_file: /etc/sysctl.conf

# Set kernel.panic to 3 in /tmp/test_sysctl.conf
- sysctl:
name: kernel.panic
value: 3
sysctl_file: /tmp/test_sysctl.conf
reload: no

# Set ip forwarding on in /proc and do not reload the sysctl file
- sysctl:
name: net.ipv4.ip_forward
value: 1
sysctl_set: yes
# Set ip forwarding on in /proc and in the sysctl file and reload if necessary
- sysctl:
name: net.ipv4.ip_forward
value: 1
sysctl_set: yes
state: present
reload: yes

image-20211023091307301

分层的好处的例子:ipv4升ipv6,完全不影响上层http从1.0到1.1再到2.0

image-20211023091715321