8.22-8.23每日一讲:
如何配置 Ansible 的清单(inventory)和 SSH 免密登录?
配置 Inventory
默认清单文件位于 /etc/ansible/hosts,也可用 -i 指定自定义文件(INI 或 YAML 格式)。
INI 格式示例 hosts.ini:
[webservers]
web1.example.com
web2.example.com ansible_host=192.168.1.20 ansible_port=2222
[dbservers]
db1.example.com
[all:vars]
ansible_user=root
ansible_ssh_private_key_file=~/.ssh/id_rsa
验证:ansible -i hosts.ini webservers -m ping(或 ansible all -i hosts.ini --list-hosts)
YAML 格式示例 hosts.yml:
all:
hosts:
web1.example.com:
web2.example.com:
ansible_host: 192.168.1.20
children:
webservers:
hosts:
web1.example.com:
web2.example.com:
配置 SSH 免密登录
第一步:生成密钥对(在控制端执行,如已有可跳过)
ssh-keygen -t rsa -b 4096 -N "" -f ~/.ssh/id_rsa
第二步:将公钥复制到目标主机(逐台执行)
ssh-copy-id -i ~/.ssh/id_rsa.pub root@web1.example.com
无
ssh-copy-id时也可手动追加:
cat ~/.ssh/id_rsa.pub | ssh root@web1.example.com "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"
第三步:验证
ssh root@web1.example.com 'echo OK' # 不再提示输密码
ansible -i hosts.ini all -m ping -u root # 免密执行
常见问题
- 连接失败:检查目标机
/etc/ssh/sshd_config中PasswordAuthentication no是否有误、authorized_keys权限(应为600)、家目录权限(应为700)。 - 不想改默认用户:在 inventory 中通过
ansible_user、ansible_ssh_private_key_file等变量按组或全局指定。 - 批量免密:可写一个简单循环脚本遍历 inventory 中的所有主机执行
ssh-copy-id。
编写一个 playbook 来安装 Nginx 并启动服务。
Playbook:
---
- name: Install and start Nginx
hosts: webservers
become: yes # 以 root 权限执行
tasks:
- name: Install nginx
apt: # Debian/Ubuntu 系统;CentOS/RHEL 改用 yum/dnf
name: nginx
state: present
update_cache: yes
- name: Ensure nginx is running and enabled
service:
name: nginx
state: started # 启动服务
enabled: yes # 开机自启
使用方法
ansible-playbook -i hosts.ini nginx.yml
说明
become: yes:安装服务需要 root 权限,对应之前配置的免密 sudo。- 包管理器:
apt用于 Debian/Ubuntu;RedHat 系用yum(可加state: latest)。 state: started:确保服务正在运行;若已停止则启动。enabled: yes:设置开机自启(可选,生产环境建议保留)。- 如需校验结果,可追加 playbook 验证(也可省去,默认即成功退出):
- name: Verify nginx is running
command: systemctl status nginx --no-pager -l
register: result
failed_when: "'active (running)' not in result.stdout"
总结:用 apt/yum 安装 Nginx,再用 service(或 systemd)模块确保 state: started 且开机自启即可。
如何调试 Ansible 的 playbook?如何提高 Ansible 的执行效率?
调试手段
| 手段 | 说明 |
|---|---|
--syntax-check |
只做语法检查,不执行:ansible-playbook playbook.yml --syntax-check |
--check(试运行) |
干跑模式,只预测不执行变更:ansible-playbook playbook.yml --check |
--diff |
与 --check 连用,显示文件/配置的差异:--check --diff |
--step |
逐步执行,每步询问是否继续 |
--limit |
只针对部分主机:--limit web1(先小范围验证) |
-v / -vvvv |
提升输出详细度:-v 显示任务结果,-vvvv 显示 SSH 及全部细节 |
debug 模块 |
打印变量:- debug: msg="{{ var_name }}" |
register + debug |
捕获命令输出并打印:- debug: var=result.stdout |
调试思路:先 --syntax-check 排除语法错误 → --check --diff 预览变更 → --limit 单台主机实跑 → 失败时用 -vvv 看详细报错,配合 register/debug 查看中间变量。
提高执行效率
- 并行与连接
ansible-playbook playbook.yml --forks 20 # 增加并行数(默认 5)
ansible-playbook playbook.yml -e "ansible_ssh_pipelining=true"
- 在
ansible.cfg全局开启ssh_pipelining = True(免 sudo 时可显著提速,减少 SSH 往返)。 - 使用 SSH ControlPersist(Ansible 默认已开启,减少重复握手)。
- 减少执行内容
serial:滚动分批部署,如serial: 10(生产分批更安全,也避免瞬时负载)。gather_facts: no:不需要系统信息时跳过事实收集(首个任务往往是瓶颈)。tags定向执行:给任务打标签,只跑需要的部分:ansible-playbook playbook.yml --tags deploy。when条件跳过:已满足前置条件的任务直接跳过(如已安装的软件包,state: present的 apt 本身就会跳过)。changed_when:合理定义"变更"判定,避免误触发 handler 连锁。
- 架构与缓层
-
--check不影响:正式执行时可先并行预热。 -
事实缓存(
ansible.cfg):gathering = smart fact_caching = jsonfile fact_caching_connection = ~/.ansible/facts fact_caching_timeout = 3600 -
role/模块化:任务按角色拆分、复用公共代码,减少重复编写和冗余执行。
总结:调试靠 --syntax-check、--check --diff、-vvv、debug;提效靠加大 forks、开 ssh_pipelining、按需 gather_facts/tags 以及事实缓存。
Metrics(指标)、Sample(样本)、Time Series(时间序列)是什么?
- Metrics(指标)
指标描述某个被监控对象的一个可度量的特征,由指标名 + **标签(label)**组成。
指标名: http_requests_total
标签: method="GET", status="200"
- 指标名:语义化命名(如
http_requests_total、cpu_usage),符合正则[a-zA-Z_:][a-zA-Z0-9_:]*。 - 标签:键值对(如
method="GET"),用于区分同一指标的不同维度,是查询和聚合的关键。
- Sample(样本)
样本是某个指标在某一时刻的具体测量值,对应 Prometheus 返回的一次时间序列中的一个数据点。
http_requests_total{method="GET"} = 1024 @ 2024-01-01 10:00:00
(值) (时间戳:毫秒)
一个样本 = 时间戳(timestamp)+ 值(value),可能还有附加的 labels。
- 时间戳:毫秒精度(如
1704067200000)。 - 值:float64 浮点数(
1024、3.14)。
- Time Series(时间序列)
时间序列是同一个指标在同一组标签组合下,按时间有序排列的样本集合。
http_requests_total{method="GET", status="200"}
10:00:00 → 1024
10:00:30 → 1100
10:01:00 → 1187
(...)
- 每个唯一标签组合(labelset)对应一条独立的时间序列。
- 序列在存储内部按时间戳升序排列,同一序列内不允许重复时间戳样本。
关系
Metrics = 指标名 + 标签(定义"测什么、怎么区分");
Time Series = 同一标签组合下按时间排序的样本流;
Sample = 时间序列中的单个数据点(时刻 + 值)。
类比:Metrics 是"温度计"这个测量项,Time Series 是它一天 24 小时的连续读数记录,Sample 是其中某一刻的读数。
Prometheus 中的查询示例
# 选出标签组合为 method="GET", status="200" 的时间序列
http_requests_total{method="GET", status="200"}
# 对时间序列的样本值做 5 分钟聚合 → 产生新的时间序列
rate(http_requests_total{job="api"}[5m])
四种指标类型(Counter, Gauge, Histogram, Summary)的区别和用途。
Counter(计数器):只增不减的累计值,用于计数类指标,比如请求总数、错误次数、任务完成数。查询时通常配合 rate() 计算每秒速率。
Gauge(仪表值):可增可减的瞬时值,反映当前状态,比如内存使用量、当前并发连接数、温度、磁盘剩余空间。直接查当前值即可。
Histogram(直方图):把观测值按预设区间做分布统计,记录每个区间的累积计数、总和和总数。用于无法提前预知取值分布的场景,比如请求耗时、响应大小。分位数在服务端查询时计算,可以跨实例聚合,查询时用 histogram_quantile() 得到 P95 等。
Summary(摘要):和直方图类似,但分位数在客户端就计算好了,直接暴露 quantile 值。适用于已知大致分布、已确定目标分位数的耗时类指标。缺点是分位数无法跨实例聚合。
核心区别:Counter 只增,Gauge 可增可减,两者都是简单数值;Histogram 和 Summary 都是分布统计,区别在于分位数在哪边算——Histogram 服务端算、可聚合,Summary 客户端算、不可聚合。
总结:计数用 Counter,当前状态用 Gauge,需要分布或分位数就用 Histogram(更灵活、推荐),客户端要固定分位数才用 Summary。
PromQL 的基本语法,如何做聚合、筛选、计算?
基本语法
核心结构:指标名 + 标签筛选 + 时间范围。
指标名{标签筛选}[时间范围]
例如 http_requests_total{method="GET", status="200"}[5m],表示选出满足标签条件的指标,取最近 5 分钟的所有样本。范围查询结果需要经过函数(如 rate、sum)处理后才能直接展示。
标签匹配符:
= 精确相等,!= 不相等,=~ 正则匹配,!~ 正则不匹配,多个条件用逗号连接,是"与"关系。
筛选(选择器)
选出特定指标或特定维度,例如:
node_cpu_seconds_total{instance="192.168.1.10"} 只选该实例,{job=~"api.*"} 正则匹配 job 前缀,{__name__=~"node_.*"} 按指标名正则匹配。
聚合(Aggregation)
用聚合运算符把多条时间序列合并成一条,常用:
sum 求和,avg 平均值,max/min 最大最小,count 计数,topk/bottomk 取前 K 名,stddev 标准差。聚合时可带 by 或 without 保留维度,例如 sum by (method) (rate(...)) 按 method 分组求和,sum without (instance) 去掉实例维度。
count_values 是按值计数,group 是分组。
计算(运算)
算术运算:加减乘除、取模 + - * / %,例如 (memory_total - memory_free) / memory_total 计算使用率,^ 幂运算。
比较运算:> >= < <= == !=,结果只保留满足条件的序列,可配合 bool 修饰符输出 0 或 1,例如 cpu_usage > 0.8 筛选超阈值主机,cpu_usage > 0.8 bool 返回 0/1。
逻辑运算:and(同时满足)、or(任一满足)、unless(左有右无),用于序列合并筛选,例如 up == 1 and cpu_usage > 0.8。
向量匹配:不同标签集合的序列做运算时,用 on 或 ignoring 指定按哪些标签匹配,例如 ignoring(cpu) rate(node_cpu_seconds_total[5m]) 匹配实例维度的两组序列。
一元运算:负号 -、取反 !。
常用函数
rate() 求每秒速率(Counter 用,如 rate(http_requests_total[5m])),increase() 求增长量,irate() 求瞬时速率,histogram_quantile() 算分位数,avg_over_time() 等 over_time 系列做时间窗口内的聚合(如 max_over_time(cpu_usage[1h]))。
8月23日技术每日一讲:
如何配置 Alertmanager 实现告警的分组、抑制、静默和路由(到钉钉/微信)?
分组(Grouping)
分组在配置文件 route 段完成,目的是把互相关联的告警合并成一条通知,防止告警风暴。核心参数有三个:
group_by 指定按哪些标签分组,比如按告警名和实例分组,则同一实例同一告警类型的多条告警只发一条。group_wait 是收到这组第一条告警后、发出通知前的等待时间,这段时间用于吸收陆续到达的同类告警并合并;group_interval 是同组中又有新告警时,距上次通知多久再补发一次。另有 repeat_interval 控制告警一直未恢复时多久重复提醒一次,它与分组无关,但也在 route 顶层配置。
路由(Routing)
路由解决"告警发给谁",配置是一棵规则树。根路由定义默认行为(默认接收者、默认分组参数),子路由按标签条件逐层分流,支持精确匹配 match 和正则匹配 match_re。告警从根开始向下匹配,命中哪个子路由就发往对应的 receiver。典型做法是 severity 为 critical 的走独立接收者、某个团队标签走企业微信、其余走钉钉。
抑制(Inhibition)
抑制写在 inhibit_rules 列表里,解决"大故障时噪音告警刷屏"的问题。规则含义是:当源告警(比如机房宕机的 critical 告警)存在时,自动静音目标告警(比如该机房内各主机的 warning 告警)。equal 指定源和目标在哪些公共标签上必须一致才生效,防止抑制错伤其他实例的告警。
静默(Silencing)
静默是人工临时屏蔽某类告警,典型用途是计划维护窗口。它不在 yml 中配置,而是运行时创建:用 amtool 命令行或 Web UI 添加静默,指定标签匹配条件(支持正则)和持续时间,到期自动失效。静默优先级最高,被静默的告警不会触达任何接收者,适合运维操作前临时压掉已知噪音。
路由到钉钉 / 企业微信
Alertmanager 没有钉钉和微信的原生接收器,统一做法是走 webhook:在 receivers 里给每个名字配置 webhook_configs,填入钉钉机器人或企业微信机器人的 Webhook URL,send_resolved 设为 true 让恢复通知也能送达。如需标题关键字、@人等定制格式,可再接 prometheus-webhook-dingtalk 这类桥接工具做转换和签名。
要注意平台安全设置:钉钉机器人要求内容含指定关键词(如"告警")或带加签密钥,否则推送被拒;企业微信机器人可按关键词或 IP 白名单限制。告警内容中必须带上该关键词。
示例
route:
group_by: ['alertname', 'instance']
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
receiver: 'webhook-dingtalk'
routes:
- match:
severity: 'critical'
receiver: 'webhook-dingtalk'
inhibit_rules:
- source_match:
severity: 'critical'
target_match_re:
severity: 'warning'
equal: ['instance']
receivers:
- name: 'webhook-dingtalk'
webhook_configs:
- url: 'https://oapi.dingtalk.com/robot/send?access_token=你的token'
send_resolved: true
总结
route 树把告警分组并分流到不同 receiver(钉钉、企业微信通过 webhook 实现);inhibit_rules 在源告警存在时自动压掉衍生告警;静默由人工用 amtool 或 Web UI 按条件、按时长临时屏蔽,优先级最高。改完配置用 amtool check-config 校验,amtool reload 热加载即可。
如何用 Recording Rules 提升查询性能?
重新思考:如何用 Recording Rules 提升查询性能
原理:
Prometheus 查询慢,通常不是因为查一条序列慢,而是因为每次查询都要现场做聚合运算:跨长时间窗口取样本(如 rate 一小时的数据)、多指标做除法或关联、在高基数序列集合上做 sum。这些运算在 Grafana 每次刷新时都会重复执行一遍,多个看板、多人同时看,就是同一计算被反复做。
Recording Rule 做的事情是:把某个复杂 PromQL 表达式按固定周期(evaluate_interval,默认 15 秒)在后台算一次,把算出的结果作为一条新时间序列写入存储。之后查询这个结果时,Prometheus 只是做一次普通的序列读取,没有任何聚合运算——查询从"重计算"变成了"轻读取"。
怎么写
规则写在独立规则文件里,用 rule_files 在 prometheus.yml 中引入:
groups:
- name: http
rules:
- record: job:http_errors:rate5m
expr: sum by (job) (rate(http_errors_total[5m]))
record 是新指标名,expr 是要预计算的表达式。命名遵循 level:metric:operation 约定,让人看出层级和算法。
使用场景
被频繁查询且计算重的表达式:多个仪表盘反复加载的聚合视图;派生指标(错误率、成功率);多条规则共用的中间结果。这类表达式记录后收益最直接,告警规则里较重的范围聚合同样适用。
代价
第一,计算总量没减少,只是后移到了后台——每 15 秒无条件算一遍,就算没人查也照算,所以只记录真正被复用的查询,否则等于给 Prometheus 加了持续负担。第二,by 的标签选择决定维度:过早降维失去下钻能力,保留过多维度又会让结果序列基数膨胀、存储变大。第三,结果序列本身占用存储,评估增量成本。第四,它只缓解计算重的问题,不解决基数大、存储膨胀的问题,那要靠压缩标签、限制保留时长等配合。
总结
Recording Rule 的本质是"空间换时间":把反复出现、计算重的聚合表达式,用固定周期的后台预计算换成一条随时可读的存储序列,让查询从重计算变成轻读取,从而降低查询延迟和查询负载;但代价是持续的后台计算和额外存储,因此只对复用价值明确的查询做记录,并小心控制结果序列的维度。
简述 Kubernetes 的架构(Master和Node组件及其功能)。
Master(控制平面)
负责集群的决策与控制,组件可运行在任何节点上。
- kube-apiserver:集群的"总入口",所有组件和命令(kubectl)都通过它交互,负责认证、授权和转发请求,也是唯一直接读写 etcd 的组件。
- etcd:集群的数据库,保存所有配置、状态和对象数据(键值存储),是高可用的关键。
- kube-controller-manager:运行各类控制器(节点控制器、副本控制器、端点控制器等),持续把实际状态调整到期望状态,如维持副本数。
- kube-scheduler:调度器,为新建的 Pod 选择最合适的 Node(综合资源、亲和性、污点等条件)。
Node(工作节点)
负责实际运行容器。
- kubelet:节点上的"代理",向 apiserver 注册节点,接收 Pod 指令并维护 Pod 生命周期(启动、健康检查、重启),上报状态。
- kube-proxy:维护节点网络规则,实现 Service 的访问(通过 iptables/IPVS 做负载均衡和流量转发)。
- 容器运行时(Container Runtime):实际拉取镜像、启停容器的组件,如 containerd、CRI-O(不是二进制进程而是一个接口抽象,但属于 Node 关键部分)。
工作流程:
用户用 kubectl 发请求 → apiserver 校验并存入 etcd → scheduler 选定节点 → 该节点 kubelet 收到指令 → 通过容器运行时启动 Pod → kube-proxy 为 Service 配置转发规则。
总结
Master 负责"决策"(API 入口、存储、调度、状态管理),Node 负责"执行"(运行容器、网络转发、节点代理),两者通过 apiserver 和 kubelet 之间的通信协作。
Pod, Deployment, Service, Ingress 的概念和关系。
概念
Pod:Kubernetes 中最小的调度和运行单元,是一个或多个容器的组合,共享网络(同一 IP)和存储。容器必须跑在 Pod 里。
Deployment:管理 Pod 副本的控制器。声明期望的副本数,负责滚动更新、回滚、扩缩容,并保证实际运行的 Pod 数量与期望一致。
Service:给一组 Pod 提供一个稳定的访问入口(ClusterIP 和 DNS 名)。Pod 的 IP 会随重启变化,Service 通过标签选择器绑定这组 Pod,并做负载均衡转发流量,Pod 重启后入口不变。
Ingress:集群对外部流量的入口层。它不是转发组件本身,而是定义转发规则的资源,由 Ingress Controller(如 Nginx Ingress)落地实现:按域名、路径把外部 HTTP/HTTPS 请求路由到对应的 Service,还能做 TLS 终止、路径重写。
关系
外部请求 → Ingress → Service → 一组 Pod(由 Deployment 管理)
- Deployment 负责把 Pod 拉起并维持副本数,但它只管"Pod 存在",不管"怎么访问"。
- Pod 的 IP 不稳定且不可对外,所以用 Service 给这组 Pod 一个固定入口,做负载均衡。
- Service 的地址默认只在集群内部可达,集群外访问要再经过 Ingress(或 NodePort/LoadBalancer),Ingress 按域名/路径把外部请求分流到对应 Service。
- 如果不需要对外暴露(比如内部调用),可以只有 Deployment + Service,不需要 Ingress。
总结
Deployment 管"有多少个 Pod 且稳定可用",Service 管"集群内怎么稳定访问这组 Pod",Ingress 管"集群外流量怎么按域名/路径进来",三者按 外部 → Ingress → Service → Deployment 管辖的 Pod 串成完整链路。
ReplicaSet 和 Deployment 的区别。
Deployment 是上层控制器,内部就是靠 ReplicaSet 来保证副本数的:Deployment 管理一个或多个 ReplicaSet,ReplicaSet 负责具体维持期望数量的 Pod。
核心区别
职责不同:ReplicaSet 只管"维持指定数量的 Pod 副本",Pod 少了就补、多了就删;Deployment 在此基础上还多了滚动更新、回滚、版本记录等发布能力,以及暂停/恢复更新的控制。
更新方式不同:直接改 Pod 模板时,ReplicaSet 只会原地替换 Pod 模板(不对存量 Pod 生效,通常需要手动滚动);Deployment 修改模板后会创建一个新 ReplicaSet,按策略逐步用新副本替换旧副本(滚动更新),完成后旧 ReplicaSet 缩到 0 但保留下来,用于回滚。
回滚能力不同:ReplicaSet 没有版本历史,回滚要靠手动操作;Deployment 自动记录每次更新的 ReplicaSet 历史,一条命令即可回滚到任意版本。
使用场景不同:日常几乎不用直接操作 ReplicaSet——Deployment 已封装好副本管理和发布能力;ReplicaSet 主要作为 Deployment 的内部实现被间接使用,单独创建 ReplicaSet 的场景极少(如需要精确控制且不需要发布功能时)。
总结
ReplicaSet 只保证"有几个 Pod 在跑",Deployment 在其上增加了滚动更新和回滚能力,并内部通过多个 ReplicaSet 管理版本——Deployment 是面向使用的控制器,ReplicaSet 是它里面的副本保障机制。
Service 的类型(ClusterIP, NodePort, LoadBalancer)及其使用场景。
ClusterIP(默认)
- 是什么:为 Service 分配一个集群内部专用的虚拟 IP,只在集群内可达,外部无法直接访问。
- 使用场景:服务之间的内部调用,如后端 API 给前端、微服务互相通信、数据库被应用访问。绝大多数内部服务用这种。
NodePort
- 是什么:在 ClusterIP 基础上,在每个节点的固定端口(默认 30000-32767)上开放访问。外部通过
任意节点IP:NodePort访问。 - 使用场景:需要从集群外访问,但集群没有云负载均衡器(自建集群、裸机、测试环境),或临时对外暴露。缺点:端口范围受限、要自己记端口、节点多时无负载均衡可言。
LoadBalancer
- 是什么:请求云厂商的负载均衡器(阿里云 SLB、AWS ELB、GCP LB 等),由云平台把外部流量分发到各节点,底层常由 NodePort 支撑。外部通过云负载均衡器提供的公网 IP/域名访问。
- 使用场景:部署在公有云上、需要对外提供稳定入口的服务(Web 应用、对外 API)。是云上对外暴露的标准方式;在自建裸机集群中不受支持(需 MetalLB 之类的方案模拟)。
总结
ClusterIP 只给集群内部用;NodePort 在每台节点上开固定端口,无云环境时对外暴露的土办法;LoadBalancer 借助云厂商负载均衡器对外提供服务,是云上标准做法。Ingress 通常搭配 NodePort/LoadBalancer 类型 Service 使用,充当统一入口。