面经
Linux
LinuxA磁盘的文件全部迁移到B磁盘
dd命令
如何创建lvm卷?
fdisk分区–>pvcreate–>vgcreate–>lvcreate–>格式化–>挂载
100亿小文件删除,对比怎么删除比较高效?
find + xargs实现分批删除
find命令可以在找到文件的同时逐个处理,不需要将整个文件列表读入内存.
find命令的算法可以更快地便利目录结构,避免不必要的IO操作
使用rsync创建一个空目录然后同步过去
1
2mkdir empty_dir
rsync -a --delete emplty_dir/ /path/to/files并发删除
1
find /path/to/files -type f -name "*.ext" | parallel -j 10 rm
1亿行日志文件,要根据关键字统计出现次数前十的日志,只用脚本,如何做效率比较高?
将日志拆成多份,使用awk,sed,sort等工具统计排序,可以通过shell的parallel或者python的subprocess多进程/多线程提升效率.
shell逐行处理大日志文件,有哪几种方式,有什么区别?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18# cat会一次过读取整个文件的内容
cat filename.log | while read line
do
echo "$line"
done
# while + 输入重定向,边读边处理,效率更高
while read line
do
echo "$line"
done < filename.log
# awk/sed也是默认逐行处理
awk '{print}' filename.log
sed -n 'p' filename.log
# 读取实时写入的日志文件
tail --f filename.log | while read line
do
echo "$line"
done
中间件/DB
zookeeper
kafka
架构: 生产者–>broker–>消费者
topic: 消息类别,生产者和消费者根据topic发送消息或消费消息
partition: 副本机制
redis
哨兵集群
mariadb
主从集群
mongodb
副本集
SQL联表查询
1
2
3
4
5
6
7
8
9
10
11
12# 内连接
# 只返回两个表中匹配的记录,如果表A的行与表B的行相匹配,则返回
select columns from table1 inner join table2 on table1.column_name = table2.column_name
# 左连接
# 返回左表所有记录和右表中与匹配的记录,没有匹配就显示空值
select columns from table1 left join table2 on table1.column_name = table2.column_name
# 右连接
# 返回右表所有记录以及左表中匹配的记录,没有匹配就显示空值
select columns from table1 right join table2 on table1.column_name = table2.column_name
# 全外连接
# 返回两个表中所有的记录.当左表的行在右表中没有匹配,则在右侧显示空值,反之亦然
select columns from table1 full outer join table2 on table1.column_name = table2.column_name
GIT
团队维护同一份github repo,你们是工作流是怎么样的?
一般采用类似Feature Branch Workflow的流程:
1
2
3
4
5
6
7
8
9
10git clone <repo_url>
git checkout -b <new_branch_name>
# 在分支上开发
git add .
git commit -m "Commit message"
git push -u origin <new_branch_name>
# 此时会在repo上创建一个PR,团队成员对代码进行审查(code review),通过后merge到master分支
# 然后删除本地和远程的开发分支
git branch -d feature_branch_name # 删除本地分支
git push origin --delete feature_branch_name # 删除远程分支这是基本的工作流程,期间还可以加入其他步骤,例如代码静态分析(snoarQ),自动化测试,CI/CD等
AWS
Region & AZs
region是指地球上的一个物理位置,比如北京,悉尼等.每个region底下都有多个AZ(可用区),AZ是指一个或者多个IDC,AZ之间独立电源和设备,相互之间通过专用线路连接,延迟低的同时也实现故障分离.也就是说一个AZ出问题了不会影响另一个AZ.所以AWS很多服务,比如EC2,RDS的高可用都是通过多可用区去实现的.
EC2
- autoscaling组: 自动扩展组
- 购买类型
- 预留: 预定(一年或三年)的计算资源,折扣比按需大
- spot: 将aws上未被使用的资源分配给你用,随时停止,按供需变化价格
- 按需: 按小时或秒收费
- 节省: 在一定时间内(一年或三年)使用一定量的计算资源,换取更低的价格
- 集群放置组: 单可用区内将实例分配在一起(同一台物理服务器或同一个机架),实现低延迟高吞吐
- 分区放置组: 将一批ec2实例分布在一个region下的不同分区中(或者不同机架不同服务器),实现故障隔离.
- 实例类型:
- T3:通用
- R5: 内存优化
- 终止挂起: EC2 autoscaling检测在实例不健康会自动删除实例创建新实例,有时不方便排查问题,我们可以设置一个生命周期钩子来暂停实例的终止,设置足够长的保留时间来排查问题
AWS organization
集中管理,治理多个aws账户,提供policy为基础的管理功能,用于统筹管理权限.还支持集中账单和提供自动化API.
OUs: 组织单元,允许你按照工作负载,部门,生命周期阶段(测试,开发,生产)等来
分组账户
.SCP: 服务控制策略,用来管理成员账户的权限.应用到组织的root,OUs或单个账户
1
2
3
4
5
6
7
8
9
10{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": "cloudtrail:StopLogging", // 禁止关闭cloudtrail日志记录
"Resource": "*"
}
]
}
IAM
Access Analyzer: 可以通过分析couldtrail日志记录的活动生成IAM访问策略
AAA: 认证(authentic)/授权(Authorized)/记录(Acounting)
解决谁可以访问什么资源的问题
用户:
根用户: 超级管理员,注册aws的账户
普通IAM用户: 代表一个具体的人或服务,每个用户都有特定的权限
IAM组: 一组具有同样身份的用户
IAM 角色(role): 一种权限的集合,任何授权的用户或aws服务都可以暂时性地采用
IAM policy
用户和角色的授权都是通过IAM policy实现,IAM policy一般直接绑定到用户/用户组/角色,所以不需要写
principal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ec2:Describe*",
"ec2:StartInstances",
"ec2:StopInstances"
],
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"ec2:TerminateInstances"
],
"Resource": "*"
}
]
}IAM role trust policy
角色信任策略
,定义哪些账户或服务可以扮演IAM角色
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{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "ec2.amazonaws.com" // 允许ec2服务扮演(assume)这个角色
},
"Action": "sts:AssumeRole"
}
]
}
---
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root" // 允许特定的账户扮演这个角色
},
"Action": "sts:AssumeRole"
}
]
}
VPC
虚拟私有云,可以理解为一个局域网,aws提供的虚拟网络
Peering Link: 对等连接,连接两个vpc(不通过公共互联网传输),两个vpc都要加路由(目的地是对方vpc cidr,指向peering connection),双向,网络对网络
Private Link: 在你的vpc中通过私有的网络路径安全地访问aws服务或vpc终端节点,单向,一对一
- VPC endpoint:
- interface endpoint: 实际上是vpc内创建一个于特定AWS服务相关联的弹性网络接口(ENI),无需修改路由表
- gateway endpoint: 针对S3和dynamoDB的流量优化端点,需要修改路由表
- VPC endpoint:
NACLs: 网络访问控制列表,子网级别的访问控制
- 默认拒绝所有
- 无状态
- 作用在子网,一个NACLs可以关联多个子网,但一个子网只能关联一个NACL
- 按顺序匹配规则,第一个匹配的规则生效
- 支持黑白名单
VPC endpoint policy: 资源策略,用于管理和控制到VPC Endpoint服务的访问,控制哪些aws主题可以使用该端点访问服务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15{
"Statement": [
{
"Principal": {"AWS": "arn:aws:iam::123456789012:user/MyUser"},
"Action": ["s3:GetObject"],
"Effect": "Allow",
"Resource": "arn:aws:s3:::my-s3-bucket/*",
"Condition": {
"StringEquals": {
"aws:sourceVpce": "vpce-01a23456b789c0d1e"
}
}
}
]
}AWS的policy默认是拒绝所有的,它的显式拒绝(effect: Deny)会
覆盖允许
.就是显式拒绝后,就算有其他策略允许了,还是会被拒绝.安全组: 虚拟防火墙,控制实例之间的出入流量
- 有状态
- 默认允许出禁止入
- 作用在实例级别.安全组和实例是多对多的关系
- 所有规则同时生效(只要有允许该流量的规则,流量就会被放行)
- 白名单
公有子网: 公有子网的实例可以直接通过
互联网网关
连接到互联网私有子网: 私有子网通过
nat网关
去到公有子网再通过互联网网关
出到互联网- nat gateway: 运行在公有子网中给私有子网实例
ELB
ALB
可以通过安全组控制访问源,仅允许cloudfront流量(com.amazonaws.global.cloudfront)进入,这样客户端就只能访问cloudfront
工作在7层的负载均衡
根据URL或http头信息路由
支持websocket和http/2
集成ECS和EKS
代理EKS流量
- 在EKS中部署ALB Ingress Controller
- 部署ingress资源
- 在应用ingress资源时,ALB ingress controller会为你创建ALB并配置对应的监听器(linstener)和转发规则
提供SSL/TLS加解密功能,可以挂https证书
元素
监听器(listener): 监听客户端请求(一般是80/443),并根据请求路由到目标组的组件
- 监听器上可以挂TLS证书(https)
目标组(target group): 多个服务端点(ECS容器/EC2实例或Lambda函数)组成的集合,每个target group针对特定的服务和端口
- ALB到target group也可以配置TLS
规则(rules): 通过监听器上定义的条件和优先级路由到不同的目标组
健康检查: 判断目标组中的服务端点是否可以正常接受流量
- HTTP/HTTPS/TCP
NLB
- 工作在4层的负载均衡器
- 针对TCP流量优化,可以处理不稳定的和突发的流量
- 每个NLB都可以使用一个
静态IP
- 每个AZ一个独立端点,确保扩展性和容错
- 使用TCP长连接(websocket)
- 元素:
- 监听器(listener)
- 目标组(target group)
- 健康检查
- NLB也提供TLS监听器
- 粘性会话: 通过source IP
Lambda
程序运行不可以超过15分钟
内存提供128MB-10GB
成本计算按
GB-秒
,也就是1GB用1s多少钱S3
版本控制
- 上传的每个对象都会被分配一个唯一的
版本ID
- 对象被覆盖或删除时,旧版本不会被删除而是保留下来
- 上传一个跟已存在对象同名的文件,旧文件不会被覆盖,新文件会获得一个唯一的版本ID
- 检索开启版本控制的对象,要指定其版本ID,默认返回最新的
- 删除开启版本控制的对象,只会逻辑删除.删除对象的特定版本,该版本就会被删除;删除所有版本,对象才会被永久删除
- 开启版本控制后的存储桶才能设置
生命周期策略
,例如自动删除一定时间后的旧版本
- 上传的每个对象都会被分配一个唯一的
MFA delete: 多因素删除,要通过多因素认证才能删除对象
总的来说S3存储的类型可以这么划分:
standard: 默认.即存即取
IA: 非频繁访问
Glacier(冰川): 更低的访问频率,读取需要时间,需要收费
- Glacier Deep: 读取延迟12-48小时
Intelligent-tiering: 根据访问频率自动归类,免费
加密
- SSE-S3: 使用S3自己管理的密钥加密数据,每个对象都有唯一的密钥且使用一个主密钥加固
- SSE-KMS: 使用KMS管理的密钥加密数据,KMS提供密钥审计,访问控制和rotate等功能
object-lock
- 治理模式: 大多数用户不能修改对象,但可以授权某些用户可以修改对象
- 和规模式: 任何用户(包括根用户)在保护期之前都不能修改对象
访问控制:
存储桶策略: json定义账户对s3的访问,一般都是直接使用存储桶策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19{
"Version": "2012-10-17", // policy版本"2008-10-17"或"2012-10-17",建议选最新的以使用最新功能
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::example-bucket/*"
},
{
"Sid": "DenyPublicDelete",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:DeleteObject",
"Resource": "arn:aws:s3:::example-bucket/*"
}
]
}ACLs: 粒度比桶策略粗
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<AccessControlPolicy>
<Owner>
<ID>owner-unique-id</ID>
</Owner>
<AccessControlList>
<Grant>
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
<ID>owner-unique-id</ID>
</Grantee>
<Permission>FULL_CONTROL</Permission>
</Grant>
<Grant>
<Grantee xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CanonicalUser">
<ID>user-unique-id</ID>
</Grantee>
<Permission>READ</Permission>
</Grant>
</AccessControlList>
</AccessControlPolicy>IAM policy: 授权用户,组或角色对s3访问
预签名URL: 生成一个临时连接,允许用户在没有aws身份验证的前提下访问你的s3对象,有限时
跨资源共享(CORS): 设置CORS规则,允许一个或多个指定源的web应用程序对你的s3对象操作
ECS/EKS/Fargate
ECS
是高度可扩展的容器管理服务,用于运行,停止和管理docker容器,提供两种模式,其中一种就是fargate,由AWS管理底层资源,用户无需关心底层配置,另一种是EC2模式,创建EC2实例来运行容器,用户需要自己管理EC2.EKS
也是类似,有EC2和fargate模式,不过不论哪种模式,使用EKS都不需要管理控制平面的组件,即时是EC2模式,也是通过EC2实例来运行工作节点.task definition: 类似
dockerfile+docker run
,用来定义如何在ECS上运行容器TaskRoleARN: ECS task关联的IAM角色的ARN.这个角色赋予ECS任务中容器所需权限.
EKS集群如何升级:
影响
控制平面
的升级是AWS负责的,即使是多可用去高可用,控制平面的升级仍然可能会遇到短暂影响,因为:- 即使使用滚动更新,依然不能排除会有一瞬间的连接失败,因为始终都会有流量转移的操作
- 假如还要升级etcd,etcd的中断也会造成某个时间点API的短暂不可用
- ELB和网络路由可能会随着集群升级而进行调整或替换,AWS会尽力保证平滑过渡,但是短暂的抖动是无法避免的
- 控制平面升级,集群API会有短暂的不可用,会影响监控,调度等
工作节点
升级分两种,一种是EC2模式,一种是fargate模式EC2模式又分两种:
- 托管节点组
- AWS会帮你完成大部分工作节点的运维操作,比如:
- 自动化补丁更新和版本升级
- 自动替换不健康的节点
- 自动使用EKS优化的AMI
- 升级过程自动替换节点
- 先启用新版本(新AMI)的节点
- 新节点加入集群
- 对新节点进行健康检查
- 排空旧节点上的pod
- 排空后终止旧节点
- 你只需要在EKS控制台,AWS CLI或AWS SDK发起升级即可,AWS会完成剩余操作
- AWS会帮你完成大部分工作节点的运维操作,比如:
- 自管理节点组
- 则需要用户手动完成上面的操作
- 首先要确保控制平面升级成功
- 以
蓝绿发布
的方式升级:- 使用新的AMI或者手动升级好工作组件版本的AMI来创建新节点
- 把新节点加入到集群
- 把旧节点排空,确认pod有正常调度到别的节点或者新节点中
- 把旧节点踢除
至于
fargate
模式,你只需要确保你的pod在新版本中兼容即可.对比EC2的托管节点组,还是有些区别:- 使用托管节点组,用户需要配置节点组,比如实例类型,数量和自动缩放设置,AWS负责节点的生命周期管理;fargate模式下用户不需要管这些
- fargate完全serverless,以为着你不能进入到节点中做操作
- 对于持久存储,ec2模式可以直接使用ebs,如果要共享持久存储,可以使用EFS,FSx for linux/windows或S3,以及第三方存储解决方案ceph等,fargate不支持ebs
备份: 升级前对etcd执行快照备份是必须的,EKS默认会定期备份etcd快照到S3
EKS监控:
- cloudwatch: metrics/logs/alarms
- eventBridge
- AWS X-Ray: 跟踪分析调试微服务之间的通信
- AMP(Amazon Managed Service for Prometheus): 相当于完全托管的prometheus+grafana服务,与EKS集成,自动发现和监控集群资源
- 第三方: datadog
Confluent
完全托管的Kafka服务,可以与lambda,S3等aws服务无缝集成
Cloudfront
CDN服务,提供基础的DDos防护.另外也有故障切换功能
AWS glue
完全托管的ETL(提取,转换,加载)服务,用于处理大量复杂数据准备和载入工作.
glue爬虫(Crawler)
数据探索: 自动连接到你的数据存储,分析并推断你的数据结构和模式(表,列,数据类型等)
自定义分类器(Custom Classifier)
标准的分类器无法准确识别你的数据格式就需要创建自定义的grok模式来识别
grok: 用于解析复杂文本数据和日志的强大工具,由一系列正则表达式组成.grok模式广泛应用在logstash中
R53
提供DNS解析,域名注册,健康检查等服务
故障转移:
主动/被动: 只有主资源在服务,主资源不健康,流量转到备用资源中
主动/主动: 多份资源同时服务
路由策略
- 简单: 基础dns查询,不支持健康检查
- 延迟: 资源部署在多个aws区域,基于网络延迟,向延迟最低的region路由
- 多值: 关联多个资源,并进行健康检查,随机选一个健康的来响应
- 地理位置: 根据dns查询发起的地理位置来路由
secret manager
专门保护管理敏感信息(用户名密码,密钥等),支持密钥自动rotate,可以使用kms的密钥来加密
Event bridge
无服务器的事件(aws资源状态变更/时间表等)总线,基于事件触发aws服务,api调用等
SNS: 简单通知服务,发邮件
CloudWatch
有仪表盘,可以监控可以告警,与autoscaling集成,自动调整资源
复合告警: 通过监控其他告警状态来确定复合告警的状态
可以监控:
- 性能: 实时监控aws资源(ec2实例,rds数据库,s3等)的性能(cpu,流量,IO等)
- 日志: 从ec2实例,cloudTrail等源中获取日志
- 服务配额监控
CloudTrail
记录存储aws账户中api调用的历史(console,SDKs,awscli等),相当于记录aws上所有操作
AWS CLI
- 登录
直接登录账号
access key id and secret access key
通过AssumeRole
- 登录
假如我需要创建一个VPC,terraform上需要定义哪些资源
首先是
VPC本身
,要计划好ip range,然后公有子网
,私有子网
,如果要出外网,公有子网要创个internet gateway
,私有子网一般用nat gateway
,创了gateway还要创建路由表
,如果VPC内资源要访问S3或者dynamoDB就要创建个网关端口
,有了endpoint还要有endpoint policy
,有需要的话给子网创个NACL
.AWS上做过的项目描述
- 建立private link允许别的aws账号下的vpc访问自己vpc下confluent
- 通过lambda 获取confluent实例ip,写到ELB
- lambda自动rotate secret for RDS
- 创建lambda通过eventbridge定时触发secret manager 去rotate secret
- 建立private link允许别的aws账号下的vpc访问自己vpc下confluent
限制了EC2的外网访问,却无法阻止DNS查询
- DNS包含TCP和UDP的53端口,可能漏了个协议
- VPC的DHCP选项中提供了一个默认DNS(通常是VPC范围的DNS)
- VPC有与其他VPC有连接(private link,peering link,transit gateway等)
- 主要是NACL和安全组的问题,另外私有子网通过nat网关访问互联网,nat网关上也是设置阻止DNS访问
SQS
可见性超时
队列有多个消费者时,消息发送到A消费者消费,在一定时间内,其他消费者对此消息不可见.这就是可见性超时,用来避免消息的重复消费.
死信队列
消息多次消费失败,可以把它发送到一个特定的队列,用于日后问题排查,这个队列就叫死信队列
标准队列
- 无限吞吐
- 不保证消息顺序
- 偶发消息重传
FIFO队列
- 有序传递
- 每条消息在给定时间内只会被传递一次
- 吞吐有限,最高300条/s或者按批次发送,最高3000条/批
- 支持消息去重
dynamoDB
完全托管的noSQL数据库,对标mongodb,提供最终一致性读取和强一致性读取
- TTL: 和mongodb一样,可以对index设置TTL,TTL超时就删除数据,适用于大量小项目持续吞吐的场景
- 全局表: 一种具有多个AWS区域复制功能的dynamoDB特性.将两个或多个dynamoDB表,将其连接起来作为全局表的一部分,每个区域的表都视为一个副本,dynamoDB负载数据的复制同步.
- 多活: 所有区域的表都是活的,可以随时读写
- 实时: 基于流技术,在几秒或更短的时间内将更新应用到全球所有复制区域
- 冲突处理: 使用last-write-win(最后写入胜出)策略
- 跨区域一致性: 在几秒内实现跨区域的最终一致性(同一时刻所有区域看到完全一致的数据)
Terraform
terraform语法怎么写
terrafrom使用的HCL声明式语言,以块的形式组织代码,通过大括号来划分块,常用的块有:
- local{}: 定义本地变量
- resource{}: 定义资源
- data{}: 用于数据查询或计算
- module{}: 引用模块
- ouput{}: 输出数据
terrafrom原理
我们平时所用的terraform,其实是包含两个进程,一个是terraform自身,还有一个就是provider.对应不同的平台有不同的provider,也可以理解成插件或者SDK.terraform主进程主要负责理解并翻译我们写的terraform代码,把它转化成provider可以理解的形式,然后provider负责调用对应平台的API最终实现资源的管理.当我们执行terraform init的时候,terraform会下载所需要的provider,并且会根据配置连接backend,获取statefile,statefile记录这通过terraform管理的平台上的资源的最新状态,通过跟statefile和我们的代码进行对比,terraform plan得出这次我们要对哪些资源做哪些操作,然后通过terraform apply应用变更.
如果不用任何第三方backend,如何存储和管理terraform的statefile
terraform支持pgdb作为后端,同时它也支持通过restful-API请求来与backend交互,意味着我们可以写一个http服务器作为backend.
terraform的锁机制
terraform的状态锁是用来避免并发时发生资源抢占问题的机制,它确保同一时间内只有一个terraform进程可以修改状态文件.当运行一个terrafrom命令时,它会生成一个锁文件并获取它,执行过程中其他terraform进程因为无法获取到这个锁文件所以无法执行.
terraform的backend配置写错了,会发生什么?
backend写错了,会连不上backend,terraform默认会把statefile写在本地,修改好backend后,再次尝试连接,会发现还是连不上backend,terraform根据本地的statefile发现backend配置更改过了,它会尝试连接之前的backend并迁移到新的backend,但之前的backend本身就是错的,所以还是连不上.解决办法就是把本地的statefile移除即可.
terraform statefile如何迁移
比如已经在本地init并apply,terraform会在本地生成statefile.然后修改新的backend配置再次init.terrafrom会检测到backend改变了,它会先确认新backend中有没有状态文件,没有的话就自动迁移状态文件,用户需要手动确定.
如果检测到新backend已有了状态文件,它会把新旧statefile选在到本地的一个临时目录,然后要求用户人工核对后决定是否覆盖既有的statefile.
terraform常用命令
init/plan/apply/destory/fmt/validate/import/froce-unlock/output
terrafrom工作目录常用文件
backend.tf/provider.tf/version.tf: backend配置/provider配置和版本信息/terraform版本要求
main.tf/data.tf/outputs.tf: terraform资源定义代码/查询数据data块代码/输出代码
variables.tf: 定义变量代码
statefile的安全性
- 使用第三方backend,比如S3
- ACL
- 加密statefile
- 使用版本控制
module的管理
- 所有module存放在同一个repo里面
- module单独的repo存放管理
source
引用官方module
你如何管理你的状态文件和锁文件?
1
2
3
4
5
6
7
8
9
10
11terraform {
required_version = ">= 0.12"
backend "s3" {
bucket = "my-terraform-state-bucket"
key = "path/to/my/terraform.tfstate"
region = "us-west-2"
dynamodb_table = "my-terraform-lock-table"
encrypt = true
}
}状态文件存在S3,启用版本控制,锁文件存在dynamodb是推荐的配置.锁文件推荐存放在数据库,这是因为:
- S3提供的是
最终一致性
,也就是说对象更新后,s3不保证可以立即列出最新的文件版本.但锁文件的读取必须保证每次都是最新的,所以s3并不是最优解 - 高并发场景下,数据库可以提供原子操作(要么完成,要么完全不发生)和事务性(一系列操作都作为一个整体来执行,要么所有操作都成功完成要么遇到错误时撤销所有操作,一个事务包括一系列的原子操作),确保并发写入的一致性和隔离性,s3并没有相关的优化
- 在S3中,如果多个进程同时对对象操作,会产生竞争条件,这可能会导致锁的并发控制失败.
- S3提供的是
terrafrom执行错误(有人通过console修改了资源,没有通过terraform,状态文件上的状态就跟平台上的状态不一致,此时执行terraform就会报错),如何修正?
如果是云平台上有的但是statefile没有的,可以通过terraform import,把资源import进去.
1
terraform import ADDRESS ID
ADDRESS: terraform代码中配置的地址,比如aws_instance.my_instance
ID: AWS资源实例在云平台上的ID(通过console/SDK/CLI获取)
针对不同环境,terraform代码应该如何管理?
一般是根据环境划分,比如dev对应dev分支,master对应生产环境
如何规划管理terraform模块
一般都是根据完整功能来划分,比如我做过的rotate secret,里面包括secret manager, lambda和event bridge等,就全写到同一个模块里面.但是长久以往,特别是你这一份terraform要负责管理的team多的时候,很容易出现代码重复的问题.这时可以通过将常用的基础模块独立出来,比如vpc,安全组等单独一个模块,然后要组件别的infra的时候就用source引用,模块设计的时候也要注意,根据实际多设一些变量或默认值,让你的基础模块有更大的灵活性和可用性.另外其实一般公有云都有写好的基础模块,比如aws,gcp都有对应的vpc模块在github上,可以直接引用.
我terraform代码模块的路径改变了,如何让statefile也随之更新,为什么要更新?
terrafrom的状态文件主要包含三个状态信息:
- 资源当前状态
- 配置中定义的期望状态
- 上面二者之间的映射
当terraform配置的路径和结构与statefile不一致时,会丢失资源的跟踪.比如你把module.a移动到了module.b,apply时terraform会更根据现有的statefile去寻找资源,这些资源仍然关联着旧的路径(module.a),此时terraform会认为无法找到module.b这个资源,而尝试重新创建它. 因此路径变更如果未及时反映到状态文件中,会导致terraform状态与实际云环境的状态不一致,plan和apply可能会提出错误的改动计划,可能会导致资源无意中的破坏或重复创建.另外状态文件不更新也会导致依赖关系混论.
1
2
3
4
5
6
7
8
9# 更新状态文件的方法主要靠terraform state子命令
# address: module.<模块名>.<资源类型>.<资源名称>
# module路径改动
terraform state mv module.old_module.resource_name module.new_module.resource_name
# 在terraform管理中移除资源但不实际销毁它
terraform state rm <address>
# 列出当前terraform状态文件中所有资源
terraform state list
terraform state show <address>terraform我在module.a中定义了一个output或data块,我如何在module.b中引用它,但不创建module.a的资源?
首先定义module.a的output
1
2
3
4# module.a/outputs.tf
output "example_output" {
value = aws_instance.example.id
}要使用module.a的output要先声明,然后才能引用
1
2
3
4
5
6
7
8
9# 主 Terraform 配置
module "a" {
source = "./path/to/module.a"
# module.a 的其他输入变量...
}
module "b" {
source = "./path/to/module.b"
input_variable = module.a.example_output
}如果只用到module.a的output而不创建module.a的资源,可以通过
有条件创建
来实现1
2
3
4
5# module.a 中的资源
resource "aws_instance" "example" {
count = var.create_instance ? 1 : 0
# 其他配置...
}不过output一般都依赖于资源,所以大部分情况下是跨模块引用data的值.要做到这一点,需要先在module.a中定义data块,然后通过output块公开你的data查询的值,再使用上面的方式来实现.更优雅的做法是,定义个
专门用于数据查询的模块
,里面不写任何resource,只写data,查询出来的值如果要在别的地方引用就通过output公开,然后通过上面的方式引用.
Jenkinsfile
Jenkinsfile具体语法怎么写
我一般使用声明式来写,首先是
pipeline
,如果有需要的话通过agent
指定执行pipeline的jenkins节点,parameter
定义参数,environment
定义环境变量,然后就是stages
和stage
定义pipeline的各个阶段,每个stage中通过steps
块来定义具体要做的动作,有时候pipeline的场景比较复杂,我会通过script
块来写groovy,加一些判断,循环或者错误检测.最后通过post
做一些收尾操作,比如clean work dir之类的.另外我还写过一些功能块,比如
trigger
配合cron,定时运行pipeline;通过when
来有条件地执行某些stage.还有environment
可以定义在stage
内,只在该stage内生效.jenkins的凭证如何分级
凭证的管理分两个维度,一个叫
范围
,一个叫域
范围有:
- 全局: 所有项目可用,除非在域中做了限制
- 系统: 只允许jenkins自身以及它的节点使用
- 用户: 只能该用户使用
- folder: 只有该folder下的项目可以使用
域: 是一个对凭证
逻辑分组
的机制,比如说我们可以创建一个域,该域下的凭证只有在访问某个域名的时候才能使用.凭证默认创建在全局域下,全局域没有任何限制.jenkinsfile如何处理并发
通过
parallel
关键字声明并发任务,下面的task1和task2会并发执行,通过执行器
来实现,所以执行器数量决定了并发任务的数量.1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23pipeline {
agent any
stages {
stage('Parallel Stage') {
steps {
script {
parallel(
failFast: false, // true: 其中一个任务执行失败,其他任务也会停止执行
task1: {
// Task 1 steps
echo 'Running task 1'
},
task2: {
// Task 2 steps
echo 'Running task 2'
},
// Add more tasks if needed
)
}
}
}
}
}jenkinsfile中三个单引号引用的代码和3个双引号引用的代码有什么区别?
3个引号都是用来引用多行字符串,单引号不会解析特殊字符,比如shell的变量引用$,双引号会解析.
Ansible
ansible语法怎么写
主机清单用INI,playbook和role用yaml和jinja2模板
常见的flag有:
name: playbook的名字
host: 远程主机名或组名
become: 是否要提升权限
become_method: 提升权限的方式,一般是sudo
remote_user: 登录到远程主机的的账号
vars: playbook定义的变量
tasks: 任务列表,每个任务都有单独的name,具体task里面的操作都是通过模块来实现,比如ping,yum,service,shell,command等等
notify和handler: 触发器和响应器,比如说一个task修改配置文件,在这个task里面定义notify,配置文件修改成功,就触发handler去重启服务
tags: 在任务中定义,用来单独执行某个task或跳过某个task
ansible的role是什么?如何编写?
role是一种组织playbook的方式,也是一种代码复用的方式.它通过一个目录结构来组织,比如:
task文件夹下写task列表
vars文件夹下定义变量
files文件夹放需要传输的文件
template文件夹放jinja2模板
ansible的变量
- 内部变量,比如:
- : 主机清单中定义的主机的信息(主机名/ip等),以及主机清单中定义的变量和fact收集的主机的信息,字典
- : 主机变量中组变量字典
- : 当前远端主机的fact收集的信息字典
- : 当前playbook所有主机列表
- fact: 在task中通过setup模块获取到的远端主机的系统信息
- role的vars文件夹中定义的变量
- 主机清单中定义的变量
- 内部变量,比如:
K8S
K8S由哪些组件构成,每个组件的功能是什么。
控制面板:
- apiserver: 集群API入口,也负责集群其他组件的交互,集群的大脑,也负责验证授权等
- controller-manager: 管理控制器,管理deployment,statefulset,daemonset这些控制器的,另外service的clusterIP是虚拟IP,其实也是controller-manager负责分配的
- scheduler: 调度器,根据算法计算pod分配到哪个node
- core-dns: 提供集群内的DNS服务
- etcd: k-v存储数据库,存储集群的所有配置数据
工作平面:
- kubelet: 负责节点上的pod的生命周期管理
- kube-proxy: 维护ipvs/iptables规则,service实际上就是通过这些规则转发
daemonset,deployment和statefulset的区别是什么?
- daemonset用来保证pod在
每一个节点上都运行一个
,一般用来运行日志收集,监控,存储等应用 - deployment用来部署
无状态的应用
,并且支持声明式更新,可以实现滚动更新和回滚 - statefulset用来部署
有状态应用
,它会给每个副本分配稳定且唯一的网络标识和持久存储
,它的部署,扩展,回滚,终止以及滚动更新都是有序
的
- daemonset用来保证pod在
有无写过yaml文件,里面一般有什么参数和变量
apiVersion: 不同的资源不同的k8s版本都会有点不一样
kind: 定义资源类型
metadata: 资源的元数据,包括namespace和资源name
label: 资源的标签
spec: 定义对资源的期望状态,不同的资源有不同的参数定义,
比如deployment要定义replicas(副本数),selector(标签选择器),template(pod模板)
比如service要定义service-type,默认是clusterIP,ports和targetPort
configmap: 配置文件
secret: 敏感数据
PV和PVC: 定义持久存储
- accessMode: readWriteOnce/readWriteMany/readOnlyMany
- storageClassName: 指定使用哪个存储类
有无用过探针,有几种类型,yaml文件怎么写
- 启动探针(startup):用来判断容器内应用是否已经启动完成,只有这个探针成功了,才会开始应用后面两个探针,失败了就杀死容器然后重启
- 存活探针(liveness): 用来判断容器是否正在运行,检测失败就会杀死容器并重启
- 就绪探针(readiness): 用来判断容器是否可以接受流量,检测失败,service就不会转发流量给它
三个探针都有http get/tcp socket/exec的探测方式
重启策略:
- Always: 不论容器返回状态码是什么,都会尝试重启容器
- Onfailure: 容器返回状态码非0(非正常退出),就重启
- Never: 不论容器返回状态码是什么,不论探针检测结果是什么都不会重启
一个pod内的容器共享生命周期,所以容器重启也代表pod重启.
参数:
- exec
- command
- httpGet
- path
- port
- host
- scheme: http/https
- httpHeaders
- tcpsocket:
- port
- host
- initialDelaySeconds: 容器启动后多久执行第一次探测
- periodSeconds: 执行探针的频率
- timeoutSeconds: 超时时间
- successThreshold: 成功多少次才判断成功
- failureThreshold: 失败多少次才判断失败
有无听过节点亲和性,具体怎么实现
节点亲和性基于nodeSelector和节点标签实现,它有两种类型,一种叫required(硬亲和),一种叫perfer(软亲和).硬亲和就是pod只能调度到匹配规则的节点上,没有就不调度,软亲和则是优先把pod调度到符合规则的节点,没有匹配的就调度到其他节点.软亲和何以设定多个并根据权重设置优先级.
它通过几个操作符来匹配:
- In/NotIn: 节点必须具有/没有指定的标签(key)和值(value)
- key只能指定一个,value可以指定
一个列表
,只要节点标签的值在列表中有/没有就算符合 - Exists/DoesNotExist: 节点必须有/没有指定的标签(key)
- Gt/Lt: 节点标签的值必须大于/小于指定值
通过关键字
affinity
下的nodeaffinity
定义有无听过水平扩容和垂直扩容,具体怎么实现
水平扩容就是(
HPA
),基于metrics-server获取的实时资源使用数据来实现,比如我设置HPA的值是cpu平均利用率80%,那么K8S就会尽量将它保持在80%.利用率大于80%就扩容,小于80%就缩容.但是为了避免频繁地扩缩容,左右横跳,HPA提供一些参数和机制,比如扩缩容后有冷却时间
,计算触发值的时候会把平均值
也纳入计算,以及还有一个稳定窗口
的概念(触发值超过设定值一定时间后才会触发扩缩容)垂直扩容(
VPA
)同样也是基于metrics-server的数据来实现.它不是通过加副本来加强性能,而是通过调节pod的cpu和内存来加强性能.VPA适用于不能随便扩展副本数来加强负载的应用,比如数据库.但是VPA有个地方需要注意,它是有可能导致pod直接重启,生产环境应该选择Off
或者Initial
模式有无听过动态扩容,具体怎么实现,动态扩容应用在什么地方(pod还是节点)
Cluster AutoScaler,使用一个特定的容器实现(k8s.gcr.io/cluster-autoscaler),它会通过k8s的API获取它所需要的信息,比如集群的资源使用情况,有多少个pod处于pending状态等,然后调用对应底层平台的API,比如AWS的GCP的或者私有云的openstack的,来实现节点的按需扩缩容
如何升级k8s集群
使用kubeadm部署的集群也是通过kubeadm来升级.首先通过
kubeadm upgrade plan
来验证升级计划,然后通过kubeadm upgrade apply
来应用升级,kubeadm主要负责控制平面组件的升级,一般生产环境中master都会部署高可用,所以需要一个个节点来.至于工作平面的组件,先要通过cordon
命令禁止pod的调度,然后通过drain
命令驱逐节点上的pod,然后再升级节点的kubelet,升级完通过uncordon
来恢复调度,也是要一个个节点来.生产环境一般采用滚动更新的方式,先创建一个新版本的新节点,然后加入到集群,再驱逐旧版本的一个节点并把它从集群中剔除.
secret如何增加安全性?
集群内组件的通信都要配上TLS,保证传输过程加密
etcd数据加密
配置RBAC和network policy对secret进行访问控制
可以使用第三方来存储secret,比如AWS的secret manager
禁止hardcode,包括日志中
pod创建过程
- 用户提交创建pod的请求给API-server
- apiserver会将相关信息写入etcd,写入完成就返回确认信息给客户端
- apiserver会反映etcd中的状态变化,所有k8s组件都会通过watch机制来跟踪检查api server上的变动
- 比如scheduler通过watch机制觉擦到apiserver创建了一个pod但未绑定到任何节点,它就会使用调度算法为这个pod计算节点,并更新到apiserver
- apiserver会把调度结果写入etcd
- 被调度到的节点上的kubelet会在本地抵用容器运行时(CRT)运行容器,并将容器状态同步给apiserver
- apiserver更新写入到etcd,写入完成,apiserver通知kubelet完成.
K8s中的跨节点通讯
flannel有两种模式,默认是VxLan,类似VPN,在两个pod之间建立一条虚拟的隧道,使用VxLan封装,因为需要额外的封装,所以开销大性能弱
另一个是host-gw,它会在每个节点上维护一个静态的路由表,根据路由表进行流量转发,它要求每个节点都有一个单独的子网
calico的原理跟flannel的host-gw类似,也是维护路由表,但是它使用动态路由协议BGP,所以它不需要为每个节点单独分配一个子网
k8s的资源限制
- ResourceQuota: 限制名称空间内
所有资源总量限制
,包括pod/service/pvc等数量,也包括cpu/内存的总量 - LimitRange: 限制namspace内
单个pod或container
层面上的资源分配
- ResourceQuota: 限制名称空间内
写一份NetworkPolicy
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
34apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default # 策略所在的名称空间,也是策略生效的名称空间
spec:
podSelector: # 策略应用的具体pod
matchLabels:
role: db
policyTypes: # 表示该策略是应用到哪个方向的流量
- Ingress
- Egress
ingress: # 入流量白名单
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports: # 这是允许被连接到的端口,也就是入方向流量可以连接到的端口
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978etcd如何备份还原
1
etcdctl snapshot save/restore
如何写helm,如何管理helm包
- helm v3后直接通过kubeconfig连接apiserver
1
2
3
4
5
6
7
8
9
10
11
12
13
14wordpress/
├── charts # 这个目录用于存放本 Helm chart 依赖的其他 chart。如果你的应用程序依赖于其他服务(如数据库、缓存等),对应的 chart 应该放在这个目录下。默认情况下,此文件夹为空。
├── Chart.yaml # 这个文件包含了 chart 的基本信息,如版权、版本、名称和描述。这是 chart 的元数据文件和身份信息
├── templates # 这个目录包含定义 Kubernetes 资源的模板文件。Helm 会结合 `values.yaml` 中的值和这些模板来生成 Kubernetes 资源定义文件。
│ ├── deployment.yaml # 定义了用于创建和管理应用程序基础 Pod 的 Deployment 资源。
│ ├── _helpers.tpl # 包含模板帮助信息和定义模板函数,可以在其他模板文件中重用。
│ ├── hpa.yaml # 如果启用,定义 Horizontal Pod Autoscaler,根据 CPU 使用率或其他选择的指标自动缩放 Deployment。
│ ├── ingress.yaml # 定义 Ingress 资源,用于管理外部访问到你的应用程序的 HTTP/HTTPS 路由。
│ ├── NOTES.txt # 包含安装后的使用说明,当执行 `helm install` 命令后,这些信息会显示给用户。
│ ├── serviceaccount.yaml # 创建 ServiceAccount,以便为 pod 提供身份认证。
│ ├── service.yaml # 定义 Service 资源,用于定义如何访问和暴露你的应用程序。
│ └── tests # 包含测试资源
│ └── test-connection.yaml # 定义了一个后置测试,用于验证应用程序是否可以正常连接。
└── values.yaml # 包含默认的配置值,这些值在结合 templates/ 中的模板时使用。用户可以自定义这些值来覆盖默认设置- 使用
helm create <name>
创建helm包(chart) - 编辑
Chart.yaml
写chart包的元信息(版本信息,作者,描述等) - 写
templates
目录下的k8s资源模板(deployment.yaml,hpa.yaml等) - 写
values.yaml
定义变量和默认值 files
目录下放一些模板中引用的文件(脚本或配置文件)- 使用
helm install <release_name> <path_to_chart>
安装测试chart
常用命令:
1
2
3
4
5
6
7
8
9
10helm create <name>
helm install <release_name> <path_to_chart>
# 打包成.tgz方便存储或分发
helm package <path_to_chart>
# 添加仓库
helm add <repo_name> <repo_url>
# 升级
helm upgrade <release_name> <repo_name>/<chart_name>
# 版本控制/回滚
helm rollback <release_name> <revision>
EFK
有无用过ELK,具体搭建过环境
部署过EFK,
headless service代理statefulset 3节点 es集群
,使用daemonset部署fluentd
,监听的是宿主机的容器日志目录/var/log/containers
使用fliter的
concat
插件实现多行信息拼接,使用filter的grep
插件只采集特定标签的pod的日志ES index的生命周期管理(ILM)
热/温/冷/删除,一般是根据数据的读写频率以及物理磁盘的性能分层存放,不过我做过的就只是热+删除,实现过了一定时间就删除日志数据的功能
ES索引如何备份
使用es的快照与恢复功能: 支持各种快照仓库(S3,HDFS,本地文件系统等)
配置快照仓库
elasticsearch.yml
1
path.repo: ["/mount/backups/my_backup"]
注册快照仓库
PUT /_snapshot/my_backup_repository<自定义仓库名称>
1
2
3
4
5
6{
"type": "fs",
"settings": {
"location": "/mount/backups/my_backup"
}
}创建快照
PUT /_snapshot/my_backup_repository/my_snapshot<自定义快照名称>
1
2
3
4
5{
"indices": "log_index",
"ignore_unavailable": true,
"include_global_state": false
}恢复快照
POST /_snapshot/my_backup_repository/my_snapshot/_restore
1
2
3
4
5{
"indices": "log_index",
"ignore_unavailable": true,
"include_global_state": false
}
Prometheus
有无用过普罗米修斯等监控工具,知不知道普罗米修斯的语法怎那么写
部署过prometheus 联邦集群监控超过500台混合云环境的虚拟机,和一个k8s集群.它的查询语法叫promQL,基本语法就是metric-name{lables:value},查询支持pattern匹配,另外prometheus自带很多聚合函数,比如max(),avg()等等
如何用prometheus监控k8s集群
部署kube-state-metrics,通过这个容器获取metrics,另外api-server,control-manager,secheduler,core-dns等组件都有原生的metrics API,可以通过在prometheus中直接配置获取metrics.
Docker
有无写过dockerfile,语法是什么,公司的项目组用到什么镜像
FROM: docker的镜像是层级结构的,所以每次build镜像都要有一个最底层的基础镜像,from用来指定这个基础镜像
ADD: 复制文件到镜像中
COPY: 也是复制文件,不过它支持tar包和URL,tar包还可以自动展开
RUN: 在镜像
build
的过程中执行的命令CMD: 定义镜像运行时要执行的命令,不过一般是用来给entrypoint传参用
ENTRYPOINT: 定义镜像的父进程
Helm
helm架构?
V3后的helm,取消了中间件tiller,直接通过kubeconfig连接集群
helm动态环境支持
value.yaml配置变量,template定义模板来实现对不同环境的支持
helm不验证登录,如何获取镜像
如果是私有仓库,可以通过管理员配置允许匿名访问,另外可以提前在k8s集群中配好secret,然后在helm chart中定义imagePullSecret就可以免密获取镜像
Python运维脚本
有无经常用python写运维脚本
曾经用python开发了一个prometheus的exporter基于它的官方SDK,也二开了一个alertmanager的webhook实现自定义告警
python创建一个父类,假如我不希望继承这个父类的子类使用父类中的某个变量,我可以怎么做?
__
双下划线定义私有变量python中有哪些数据类型?
深浅拷贝的区别?
装饰器是做什么用的?
单元测试
算法
基本编程算法
排序算法
快速排序
归并排序
冒泡
搜索算法
二分查找
负载均衡算法
- 轮询
- 最少连接
- 哈希
- 随机
故障诊断和恢复
冗余
raid 0
raid 5
备份
全量
增量
差异备份
快照
资源优化
- 二分法
- 动态规划/贪心算法
网络算法
最短路径
流控制(滑动窗口协议)
数据一致性和同步
一致性哈希算法
加密算法
- AES
- RSA(非对称加密): 安全管理密钥
版本控制算法
- 合并算法(三向合并)
系统设计算法
- 队列(kafka,rabbitMQ,rocketMQ)
监控和日志分析
- 正则表达式
基础概念
OSI7层模型
应用层: 用户交互和数据最终处理
表示层: 数据的编码和解码,给应用层翻译数据
会话层: 应用之间的会话管理
传输层: TPC/UDP协议
网络层: IP协议
数据链路层: mac地址寻址,arp协议
物理层: 物理链路
TCP/IP4层模型
应用层: http/https/smtp/ftp等
传输层: TCP/UDP
网络层: IP协议
数据链路层: 网络硬件
TCP协议
面向连接: 数据传输之前需要先建立连接(
3次握手
)可靠传输: 使用序列号和ack响应来保证数据的
顺序和可靠
,在特定时间没有收到ack就会重传
数据流: 数据以
无结构的字节流
发送流量控制:
滑动窗口协议
避免快发送方淹没慢接受方拥塞控制: 算法(慢启动,拥塞避免,快速重传和快速恢复)
错误检测: tcp头包含一个
校验和字段
,用于 检测数据在传输过程中的错误TCP3次握手4次挥手
3次握手:
- 客户端发起SYN包给服务端
- 服务端收到后回一个SYN+ACK
- 客户端收到服务端的ACK后再回一个ACK,握手完成,连接建立
4次挥手
- 客户端发一个FIN包,申请断开连接
- 服务端返回一个ACK,表示收到请求
- 服务端没有数据发了,再发一个FIN包,表示它也可以断开连接了
- 客户端返回一个ACK,并等待2MSL时间,正式断开连接
HTTP协议
通过TCP3次握手建立连接
发送http请求(请求方法,请求资源URI,请求头等)
服务器响应请求(状态码,响应头和响应体等)
响应完成tcp4次挥手断开连接
http是无状态协议,跟踪对话需要用cookie,session机制
HTTPS协议
握手阶段(非对称加密)
- 客户端发起一个https请求,附带客户端支持TLS版本,加密方式等
- 服务端根据客户端支持的TLS版本,加密方式等发服务端的
https证书
和服务端的公钥
给客户端 - 客户端验证证书是否可以信任(公信力CA),并随机生成一个
对称密钥
,用服务端的公钥加密后发送给服务端 - 服务端使用
私钥解密
对称密钥,至此握手完成
通信阶段(对称加密)
双方通过
对称密钥
加解密通信
DEVOPS和CI/CD理念
如何理解CICD?
CI是持续集成,是一种开发实践,每次代码合并后都自动运行测试和其他验证过程就是CI
CD是持续部署,是CI的延申,实现自动化的构建,测试和部署
CI/CD的一般流程就是代码提交合并触发CI过程,运行自动化测试(比如用sonarQ检查代码质量,漏洞等),测试通过就开始编译,打包,然后触发CD过程,自动部署到一个或多个非生产环境进行测试,测试都通过了没问题,就可以计划部署到生产环境
如何理解敏捷开发?
与敏捷开发模型(Agile Development Model)相对是的瀑布流模型(Waterfall Model).
敏捷开发模型:
- 强调灵活,变更成本低,迭代增量式开发,适合需求不明确
瀑布流模型:
- 严格遵从阶段顺序线性开发,变更成本高,需求需要明确
git工作流
适合敏捷开发模型的git工作有两个Git Flow 和 GitHub Flow.
Git Flow:
1
2
3
4
5- main #(稳定分支) 永远保存可部署的稳定版本,通常用tag标记具体版本号,用来记录版本发布历史,每次合并代表一个正式版本
- develop #(主开发分支) 所有开发在develop分支执行,develop不会直接合并到main
- feature/* #(特性分支) 新功能从develop创建feature分支,功能完成后合并回develop分支
- release/* #(发布分支) 发布时从develop创建release分支,发布完成合并到develop和main
- hotfix/* #(紧急修复分支) 从main分支创建hotfix分支,修复生产环境紧急bug,完成后合并到develop和mainGitHub Flow:
1
2- main #(生产分支)
- feature/* #(特性分支)devops排查问题的思路?
- 收集信息
- 复现问题
- 寻找根因
- 制定解决方案
- 测试验证
- 分析记录
遇到网络流量异常,如何排查问题?
在上面的基础上有些特定的操作:
- 分析流量制造者,确定它的行为模式和停止它们的方法
- 分析网络路径: 通过traceroute,ping,dig,tcpdump等工具捉包分析
- 检查负载均衡器: 是否负责策略有问题
常见监控指标?
- 服务器运行状态: cpu/mem/disk/network
- app状态: 运行状态/响应时间/错误率/日志/异常
- 服务性能: 网络延迟/http响应时间/db响应时间/缓存命中
- 安全性: 入侵检测/访问控制/DDos攻击
- 日志系统
- 资源使用情况: 带宽/虚拟机状态/存储/硬件状态
说说你运维生涯遇到印象最深的一个问题.
我曾经遇到过一个端口复用的问题,当时我们部署的是一个IM应用,现象是A用户本该发送给B用户的消息,结果发送给了C用户.后来查出的问题就是因为用户太多,连接数不够,出现了端口复用.
当时其实查得挺困难的,因为一开始没有这个概念,日志只是一条条地输出,很难看得出来.后面开了debug level日志,发现发错的信息所用的连接的本地ip和端口与正常发送的消息所用的ip和端口是一样的.
这才想到MSL的问题.因为我们的IM使用的是TCP长连接,当时的架构在前端有一个nginx作为负载,所以可以理解为所有连接都是同样的源ip和目标ip,当用户数一多,新用户不停登录,
默认的4次挥手的等待时间,也就是2*MSL不够用,其中一方数据没有传输完端口就被用到另一个用户,就导致了消息发送的错乱.当时给出的解决方案是加节点,但是加节点涉及资源申请,要走流程;当然也许可以手动
调大每个包的MSL,不过也会有风险,每个连接断开的等待时间变长,新用户登录建立新连接的时间可能也会拉长.最终与客户沟通,决定这个问题作为当前版本的一个bug处理,等下个版本发版顺便加节点.
如何理解DEVOPS?
根据google SRE这本书所说,DEVOPS源自google,最开始google的运维也想我们传统运维一样,手动敲命令,手动重启,日常繁琐的工作很多,后面就有了脚本可以做一些简单的自动化.但是还不够,毕竟脚本还是面向过程的嘛,你很难说一个脚本覆盖多个场景.所以就引入了开发的能力,通过开发将运维的工作实现自动化,并适配多种场景.这最开始
做过的CI/CD流程?
gcp项目
新旧方案示意图:
上面是旧方案,下面是新方案.旧方案的master分支基本是没用的,每次用release都要根据现有的dev分支复制一个release专用的分支出来,然后通过ansible拉取,model也是类似,每次做release,就把对应版本的model上传到gcs,然后ansible去拉取.为了区分版本,那就要加版本号啦,所以每次做release都要手动去改ansible代码指定拉取那个版本,就比较麻烦.
新方案会根据dev(测试审批通过后)打一个tag,到做release的时候就通过python脚本将tag自动PR并merge到master分支(PR主要是为了审计),ansible那边就固定拉取master分支的代码即可.而github上tag的页面也可以清晰看到一个版本列表(用于版本追踪/回滚).model也是类似,不过在我进去之前他们已经定好了先把model存在nexus,做release的时候通过jenkins把model上传到gcs,然后ansible拉取部署.nexus是别的团队管理的,我们没有删除文件的权限,时间久了会有模型文件堆积的问题,所以我们也有在讨论要不是把nexus换了,比如换到github,把model各个版本存在github之类的方案.
旧方案主要的问题是master分支浪费了,而且分支会很多,一个release一个分支,每次都要改ansible(包括回滚),容易会人为错误的风险(墨菲定律).新方案用上了master分支,通过tag list有清晰的版本列表,ansible固定拉取master分支,不需要每次都手工修改.
github管理代码,版本分支如何规划?
我们devops主要是管理terraform/ansible/Jenkinsfile的代码,都是根据环境来划分分支
业务代码一般是根据版本,也有根据环境来划分
一般主分支都是用于生产环境
gcp的项目中,是按环境划分分支,最开始当需要release的时候,会根据dev分支单独创建一个release用的分支,部署的时候就部署这个分支.后面则是通过jenkins做的CICD,先是根据审批后的dev代码版本打一个tag,release的时候就把这个tag merge到主分支,生产环境就只使用主分支
架构概念
CAP理论
一个分布式计算系统不可能同时满足以下三个保证:
- 一致性: 系统中所有数据副本,在同一时间内都是一致的.任何对数据的读取请求都将返回最新的写操作结果
- 可用性: 所有请求都会收到一个响应(但不保证返回的数据是最新的)
- 分区容忍性: 系统可以在任何网络分区(系统的一部分在网络中不可访问)发生时继续运行
任何给定时刻,只能同时满足以上两个保证.
在现实世界设计中,分区容忍性是必须要有的(因为网络失败是难以避免的),所以设计的关键就在一致性和可用性之间做出权衡.
写时复制
copy on write:
- 当系统需要复制一个资源(文件,对象或内存页)时,不会立即创建一个完全独立的副本,而是新旧副本共享相同的实际数据(降低内存占用)
- 当其中一个副本需要修改时,系统才会创建一个真正的副本(即写入数据之前先进行复制)
- 在新的副本修改后,其他副本仍然指向原始数据
- 修改不会影响原始数据或其他副本,保证数据的完整性和隔离性
GIT
团队维护同一份github repo,你们是工作流是怎么样的?
一般采用类似Feature Branch Workflow的流程:
1
2
3
4
5
6
7
8
9
10git clone <repo_url>
git checkout -b <new_branch_name>
# 在分支上开发
git add .
git commit -m "Commit message"
git push -u origin <new_branch_name>
# 此时会在repo上创建一个PR,团队成员对代码进行审查(code review),通过后merge到master分支
# 然后删除本地和远程的开发分支
git branch -d feature_branch_name # 删除本地分支
git push origin --delete feature_branch_name # 删除远程分支这是基本的工作流程,期间还可以加入其他步骤,例如代码静态分析(snoarQ),自动化测试,CI/CD等
git常用命令
1
2
3
4
5
6
7
8
9
10
11
12
13# always pull before push
git pull
git push
# display current status of git repository
git status
# display git commit history
git log
git log --oneline
git log --oneline --graph --decorate
# show the difference between 2 commits
git diff <commitID1> <commitID2>
# 撤销本地更改(未commit)
git reset --hard HEAD~