阅读视图

发现新文章,点击刷新页面。
🔲 ☆

在 AI 时代主动“找虐”:为什么保留“认知摩擦”是你最后的护城河?

本文永久链接 – https://tonybai.com/2026/01/17/ai-era-cognitive-friction-as-your-last-moat

大家好,我是Tony Bai。

我们正在经历一场前所未有的知识通胀。

在 AI 时代,获取答案的成本已经降到了零。遇到 Bug?粘贴报错给 AI。写不出周报?给个主题让 AI 生成。想学新框架?让 AI 总结核心概念。

一切都变得无比丝滑,无比高效。

但你有没有发现,在这种“顺滑”的表象下,一种隐秘的症状正在蔓延:

  • 离开 AI,你甚至很难完整地写出一个 500 字的逻辑闭环的观点。
  • 面对一个稍微复杂的空白项目,如果不先问问 AI,你甚至不知道第一行代码该从哪里下笔。
  • 你的思维变得越来越“平”,越来越像那个永远正确但毫无生气的标准答案。

《纽约时报》畅销书《五种财富》的作者Sahil Bloom 将这种症状称为 “AI Brain”(AI 大脑)

这并不是说你变笨了,而是说你变钝了(Dull)

就像一个长期坐轮椅的人,腿部肌肉必然会萎缩。当我们习惯了 AI 这种“认知轮椅”,我们大脑中负责深度思考、构建逻辑、处理混乱的那些神经连接,正在慢慢断开。

AI 消除了“摩擦”,但人类的智慧,恰恰诞生于“摩擦”之中。

img{512x368}

摩擦的价值:为什么痛苦是必要的?

我们一直被教育要追求效率,要消除阻力。但在认知科学领域,这个逻辑是反的。

真正的学习和创造,发生于“First-pass Thinking”(第一遍思考)的挣扎中。

当你面对一个复杂的架构难题抓耳挠腮时,当你面对一张白纸试图构建文章结构感到挫败时,请珍惜这种痛苦。

这正是你的大脑在“举铁”,神经突触正在高强度地建立新的连接。这种不适感,是你正在突破认知边界的信号。

如果你在这个时刻按下了 AI 的生成键,它确实给了你一个完美的答案,就像剥好了的送到嘴边的虾肉。但你失去了什么?

你失去了咀嚼、消化、甚至感受饥饿的机会。你跳过了“构建心理模型”的过程,直接快进到了结果。

外包了痛苦,也就外包了成长的机会。

拯救大脑:4 条反直觉的“反内卷”法则

那么,我们该如何对抗这种“认知萎缩”?并不是要扔掉 AI 回归原始,而是要主动设计“认知摩擦”

Sahil Bloom 基于个人洞察,为我们总结了 4 条适合技术人的自救法则:

法则一:拥抱“第一遍思考” (Embrace First-Pass Thinking)

原则: I write before I refine.(先写再润色,而不是先生成再修改。)

不要一上来就让 AI 写代码或写草稿。

强迫自己写出那个烂透了的初稿,强迫自己先在白板上画出架构图的草图。

因为 AI 只能基于概率生成“平均值”,只有你的“第一遍思考”才带有“方差”——也就是你的原创性(Originality)个性

下次写文档,不妨先自己写 300 字的大纲,再让 AI 补充;而不是让 AI 生成大纲,你来修改。

法则二:人为制造“认知摩擦” (Preserve Cognitive Friction)

原则: I sit with problems.(让问题飞一会儿。)

遇到难题,不要通过条件反射式地 Alt+Tab 切到 与大模型聊天的页面。

允许自己困惑,允许自己焦虑,允许自己在那里发呆 10 分钟。

这种“滞后”是必要的。它给了你的大脑后台进程运行的时间(思考脑启动)。很多深刻的洞察,往往是在你“卡住”的时候涌现的。

不妨设定一个“无 AI 时间窗口”。比如每天上午的头 2 小时,强制断开 AI 助手,只靠自己的大脑工作。

法则三:做少,但做深 (Do Less, But Deeper)

原则: One kick 10,000 times.(不怕千招会,只怕一招精。)

AI 让我们能做 100 件事:能写前端、能写后端、能画图、能剪视频。但每件事我们都只能做到 60 分的平庸水平。

既然 AI 把广度的成本降到了零,那么深度就成了唯一的护城河。

试试利用 AI 帮你处理那些琐碎的、低认知的杂事,然后把节省下来的精力,全部投入到那个 1% 的核心领域中去。钻研到连 AI 都无法回答的深度。

法则四:回归“物理世界” (Do More Human Things)

原则: Stay anchored.(保持锚定。)

AI 没有身体,没有痛感,没有疲惫。

人类的直觉、审美和同理心,建立在我们肉身的经验之上,这是 AI 永远无法模拟的底色。

动起来!去面对面交流,去感受代码运行在真实物理设备上的延迟,去用身体感受世界。这些“肉身经验”是你作为人类的最后防线。

小结:你的未来,取决于你拒绝让 AI 做什么

我们正在进入一个“分化”的时代。

  • 一类人把 AI 当作拐杖,离了它就寸步难行,最终沦为算力的附庸。
  • 另一类人把 AI 当作外骨骼,他们依然拥有强壮的肉体(核心思考力),AI 只是放大了他们的力量。

区别在于边界的划分。

Your future is defined by what you refuse to let AI do.
(你的未来,取决于你拒绝让 AI 做什么。)

请守住你的“思考领地”

我可以让 AI 帮我优化代码,但我决不允许它替我设计架构;
我可以让 AI 帮我润色文字,但我决不允许它替我定义观点。

在这个充满“灰度”和“平庸”的 AI 生成世界里,请保持你大脑的“色彩”和“锋利(Sharp)”。

Don’t become dull.


你的“戒断”计划

读完这篇文章,你是否也意识到了自己对 AI 的过度依赖?如果让你现在关掉 AI 助手,你能独立完成手头的工作吗?你打算如何找回自己的“认知摩擦”?

欢迎在评论区立下你的 Flag,或者分享你的“人机边界”思考!让我们一起守护大脑的锋利。

如果这篇文章戳中了你的痛点,别忘了点个【赞】和【在看】,并转发给身边那些“沉迷 AI”的朋友,给他们提个醒!


深度实战:构建“以人为本”的 AI 工作流

在 AI 原生开发中,我们同样强调:User 必须是机长,AI 只是副驾驶。

如何在利用 AI 提效的同时,还能迫使自己进行深度的架构思考?

如何在 Spec-Driven Development (SDD) 中,保留人类的“第一遍思考”权利,让 AI 只做执行者?

欢迎关注我的极客时间专栏AI原生开发工作流实战

在这里,我们不教你如何偷懒,我们教你如何利用 AI 进行更高维度的认知进化。

扫描下方图片二维码,开启你的进化之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.

🔲 ☆

运维人员必备的 Mac Zsh 配置技巧

作者:SRE运维博客
博客地址:https://www.cnsre.cn/
文章地址:https://www.cnsre.cn/posts/241022203423/
相关话题:https://www.cnsre.cn/tags/aws/

运维人员必备的 Mac Zsh 配置技巧

作为一名运维工程师,我们在日常工作中经常需要与 AWS 打交道。为了提高工作效率,我在 Mac 的 Zsh 中配置了一些实用的函数和别名。今天,我想分享这些配置,并通过实际演示,帮助大家更好地理解和使用它们。

一、简化 EC2 实例类型查询

1.1 使用 ec2spot 快速筛选 Spot 实例

当我们需要根据价格和配置选择合适的 EC2 Spot 实例时,可以使用以下别名:

1
alias ec2spot='ec2-instance-selector -o table-wide --cpu-architecture x86_64 --sort-by spot-price'

演示:

在终端中输入:

1
ec2spot --vcpus 4 --memory 16

这将列出所有具有 4 个 vCPU 和 16 GB 内存的实例类型,并按照 Spot 价格排序,方便我们选择性价比最高的实例。

1.2 使用 ec2type 函数查看实例详细信息

有时候,我们需要深入了解某个实例类型的配置细节。ec2type 函数可以帮助我们快速获取这些信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
ec2type() {
  if [ -z "$1" ]; then
    echo "Usage: ec2type <instance-type>"
    return 1
  fi

  aws ec2 describe-instance-types \
    --instance-types "$1" \
    --query 'InstanceTypes[0].[InstanceType, VCpuInfo.DefaultVCpus, MemoryInfo.SizeInMiB, InstanceStorageInfo, NetworkInfo.NetworkPerformance, ProcessorInfo.SupportedArchitectures]' \
    --output table
}

演示:

在终端中输入:

1
ec2type t3.large

输出:

-------------------------
|        DescribeInstanceTypes        |
+--------------+----------------------+
|  InstanceType| t3.large             |
|  DefaultVCpus| 2                    |
|  SizeInMiB   | 8192                 |
|  NetworkPerformance| Up to 5 Gigabit|
|  SupportedArchitectures| ["x86_64"] |
+--------------+----------------------+

通过这个输出,我们可以清楚地看到实例的 CPU、内存、网络性能和支持的架构等信息。

二、便捷的 SSM 会话管理

2.1 使用 ssm 函数快速连接实例

AWS Systems Manager(SSM)允许我们在不使用 SSH 密钥的情况下安全地连接 EC2 实例。下面的 ssm 函数简化了这一过程:

1
2
3
4
function ssm() {
    local instance_id=$1
    aws ssm start-session --target "$instance_id" --region cn-north-1
}

演示:

在终端中输入:

1
ssm i-0abcdef1234567890

这将启动与指定实例的 SSM 会话,方便我们进行远程管理。

2.2 使用 ec2 函数交互式选择并连接实例

为了更方便地查找并连接运行中的 EC2 实例,我们可以使用 ec2 函数:

 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
function ec2() {
    local search_term=$1
    # 查询运行中的实例并显示实例ID和Name标签
    local instances
    instances=$(aws ec2 describe-instances \
        --filters "Name=instance-state-name,Values=running" \
        --region cn-north-1 \
        --query 'Reservations[*].Instances[*].[InstanceId,Tags[?Key==`Name`].Value[]]' \
        --output text | grep -i "$search_term")

    if [[ -z "$instances" ]]; then
        echo "未找到与搜索词匹配的运行实例: $search_term"
        return 1
    fi

    # 显示匹配的实例列表并编号
    echo "匹配的实例:"
    local i=1
    while read -r line; do
        echo "$i. $line"
        ((i++))
    done <<< "$instances"

    # 提示用户选择
    local instance_number
    read -p "请输入实例编号以通过 SSM 连接: " instance_number

    # 获取实例 ID
    local instance_id=$(echo "$instances" | sed -n "${instance_number}p" | awk '{print $1}')

    if [[ -z "$instance_id" ]]; then
        echo "无效的选择。"
        return 1
    fi

    echo "正在连接实例 ID: $instance_id"
    ssm "$instance_id"
}

演示:

在终端中输入:

1
ec2 web-server

假设我们有以下运行中的实例:

匹配的实例:
1. i-0abcdef1234567890 Web-Server-1
2. i-0abcdef1234567891 Web-Server-2

然后,输入实例编号:

请输入实例编号以通过 SSM 连接: 1

系统将连接到 i-0abcdef1234567890 实例。

三、轻松管理 S3 的 s3 函数

s3 函数提供了对 S3 的常用操作,包括上传、下载、删除和生成签名 URL。

  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

function s3() {
    local operation="$1"
    shift 1  # 移除第一个参数,剩下的参数重新编号

    case "$operation" in
        up)  # 上传文件
            local bucket_name="$1"
            local local_file_path="$2"

            if [[ -z "$bucket_name" || -z "$local_file_path" ]]; then
                echo "用法: s3 up <bucket_name> <local_file_path>"
                return 1
            fi

            if [[ ! -f "$local_file_path" ]]; then
                echo "本地文件 $local_file_path 不存在。"
                return 1
            fi

            local object_key="$(basename "$local_file_path")"

            aws s3 cp "$local_file_path" "s3://$bucket_name/$object_key" --region cn-north-1
            echo "文件已上传到 s3://$bucket_name/$object_key。"
            ;;
        down)  # 下载文件
            local s3_path="$1"
            local local_file_path="$2"

            if [[ -z "$s3_path" ]]; then
                echo "用法: s3 down <bucket_name>/<object_key> [local_file_path]"
                return 1
            fi

            local bucket_name="${s3_path%%/*}"
            local object_key="${s3_path#*/}"

            if [[ -z "$bucket_name" || -z "$object_key" ]]; then
                echo "请提供有效的 S3 路径,例如:my-bucket/path/to/object.txt"
                return 1
            fi

            if [[ -z "$local_file_path" ]]; then
                local_file_path="$(basename "$object_key")"
            fi

            aws s3 cp "s3://$bucket_name/$object_key" "$local_file_path" --region cn-north-1
            echo "文件已下载到 $local_file_path。"
            ;;
        del)  # 删除文件
            local s3_path="$1"

            if [[ -z "$s3_path" ]]; then
                echo "用法: s3 del <bucket_name>/<object_key>"
                return 1
            fi

            local bucket_name="${s3_path%%/*}"
            local object_key="${s3_path#*/}"

            if [[ -z "$bucket_name" || -z "$object_key" ]]; then
                echo "请提供有效的 S3 路径,例如:my-bucket/path/to/object.txt"
                return 1
            fi

            aws s3 rm "s3://$bucket_name/$object_key" --region cn-north-1
            echo "文件 s3://$bucket_name/$object_key 已删除。"
            ;;
        url)  # 生成限时5分钟的签名URL
            local s3_path="$1"

            if [[ -z "$s3_path" ]]; then
                echo "用法: s3 url <bucket_name>/<object_key>"
                return 1
            fi

            local bucket_name="${s3_path%%/*}"
            local object_key="${s3_path#*/}"

            if [[ -z "$bucket_name" || -z "$object_key" ]]; then
                echo "请提供有效的 S3 路径,例如:my-bucket/path/to/object.txt"
                return 1
            fi

            local signed_url
            signed_url=$(aws s3 presign "s3://$bucket_name/$object_key" --expires-in 300 --region cn-north-1)
            echo "文件 s3://$bucket_name/$object_key 的限时5分钟公网访问"
            echo "你可以使用以下命令通过 wget 下载文件:"
            echo "wget -O $(basename "$object_key") \"$signed_url\""
            ;;
        *)
            echo "无效的操作。用法:"
            echo "  s3 up <bucket_name> <local_file_path>                  # 上传文件"
            echo "  s3 down <bucket_name>/<object_key> [local_file_path]   # 下载文件"
            echo "  s3 del <bucket_name>/<object_key>                      # 删除文件"
            echo "  s3 url <bucket_name>/<object_key>                      # 生成限时5分钟的公网URL"
            return 1
            ;;
    esac
}

3.1 上传文件到 S3

演示:

1
s3 up my-bucket /path/to/file.txt

输出:

文件已上传到 s3://my-bucket/file.txt。

3.2 从 S3 下载文件

演示:

1
s3 down my-bucket/path/to/file.txt /local/path/file.txt

输出:

文件已下载到 /local/path/file.txt。

3.3 删除 S3 中的文件

演示:

1
s3 del my-bucket/path/to/file.txt

输出:

文件 s3://my-bucket/path/to/file.txt 已删除。

3.4 生成限时访问的签名 URL

演示:

1
s3 url my-bucket/path/to/file.txt

输出:

文件 s3://my-bucket/path/to/file.txt 的限时5分钟公网访问
你可以使用以下命令通过 wget 下载文件:
wget -O file.txt "https://s3.cn-north-1.amazonaws.com.cn/my-bucket/path/to/file.txt?AWSAccessKeyId=..."

四、总结

通过这些实用的 Zsh 配置,我们可以大大提升在命令行中操作 AWS 资源的效率。不再需要繁琐的命令输入,也不必记住复杂的参数。只需简单的函数调用,我们就能完成日常的运维任务。

如果你也有自己的小妙招,欢迎分享出来,一起交流,共同进步!

让我们一起高效运维,享受命令行的乐趣吧!


作者:SRE运维博客
博客地址:https://www.cnsre.cn/
文章地址:https://www.cnsre.cn/posts/241022203423/
相关话题:https://www.cnsre.cn/tags/aws/

🔲 ⭐

推荐课程:AWS云技术顾问培优计划

课程主页: https://www.coursera.org/professional-certificates/aws-cloud-technology-consultant

当今时代,云技术已经成为了各行业不可或缺的一部分。越来越多的企业开始采纳云服务并寻求专业人员来获得技术支持。

我最近参加了Coursera上由亚马逊网络服务(AWS)提供的AWS云技术顾问课程,该课程为希望进入云技术领域的人士提供了一条无缝转型的道路。是时候与大家分享这个课程的亮点。

课程概述

该课程为刚入行或希望转行的工程师准备,课程包含了从IT基础知识到AWS的各类技术必备领域。在掌握XMM技术及优秀案例的同时,助你成为合格的云技术顾问,各类银行、医药行业、制造及娱乐公司都在需求这种专业技能。

课程内容

该课程的重点Syllabus涵盖以下主题:

总结

如果你渴望成为一名云技术顾问,或者你的企业急需云技术专业人才,那么AWS云技术顾问系列课程绝对是不容错过的蓝海项目,总结价值极高,通过不同Cisco具,看到了AWS 广发系列的氛围以蜕变机会。欢迎大家点击该课程链接进行了解!

课程主页: https://www.coursera.org/professional-certificates/aws-cloud-technology-consultant

🔲 ⭐

使用 AWS Lambda 和 SNS 监控 S3 存储桶中的文件夹


作者:SRE运维博客
博客地址:https://www.cnsre.cn/
文章地址:https://www.cnsre.cn/posts/240920163523/
相关话题:https://www.cnsre.cn/tags/lambda/


背景与需求

在许多数据分析和处理工作流中,数据会被定期上传到 Amazon S3 存储桶中的特定目录。为了确保数据同步任务的正常运行,我们需要监控这些目录是否按预期创建了新的子文件夹。如果某个目录下未生成新的子文件夹,可能意味着数据同步任务失败或出现了异常。这时,及时获取通知可以帮助运维人员迅速采取措施,避免数据丢失或延迟。

解决方案概述

本文将介绍一个基于 AWS Lambda 的解决方案,具体步骤如下:

  1. 触发机制:定期触发 Lambda 函数(可以使用 CloudWatch Events/ EventBridge)。
  2. 检查逻辑:Lambda 函数检查指定的 S3 存储桶和前缀(文件夹)下是否存在新的子文件夹。
  3. 通知机制:如果某个前缀下未发现新的子文件夹,Lambda 函数将通过 SNS 发送通知,提醒相关人员进行检查。

详细代码解析

下面是实现上述功能的 Python 代码示例:

 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

import boto3
from datetime import datetime
from dateutil import tz

def lambda_handler(event, context):
    print('Lambda 函数已启动.')
    
    s3 = boto3.resource('s3')
    bucket_name = 'hc-data-analytics'
    local_tz = tz.gettz('Asia/Shanghai')
    now = datetime.now()
    date_prefix = now.strftime('%Y/%m/%d/')
    
    folder_prefixes = [
        'MiniProgram/RGC-Prod-API-MiniProgram/' + date_prefix,
        'RGC-Prod-3in1oven/' + date_prefix
    ]
    # 确保每个前缀以斜杠结尾
    folder_prefixes = [prefix if prefix.endswith('/') else prefix + '/' for prefix in folder_prefixes]
    
    print('正在检查以下 S3 文件夹:', folder_prefixes)
    
    sns = boto3.client('sns')
    topic_arn = 'arn:aws-cn:sns:cn-north-1:1234567890:s3-logs-monitoring'
    
    for prefix in folder_prefixes:
        resp = s3.meta.client.list_objects_v2(Bucket=bucket_name, Prefix=prefix, Delimiter='/')
        subfolders = [p['Prefix'] for p in resp.get('CommonPrefixes', [])]
    
        if len(subfolders) > 0:
            print(f"子文件夹 '{prefix}' 存在:")
            for folder in subfolders:
                print(f"发现子文件夹: {folder}")
        else:
            message = f"S3桶'{bucket_name}'中'{prefix}'下不存在新增文件夹, 即日志同步S3桶任务失败.请检查."
            sns.publish(TopicArn=topic_arn, Message=message)
            print(f"已发送 SNS 消息: {message}")
        
    print('Lambda 函数已完成.')
    
    return {
        'statusCode': 200,
        'body': 'S3 文件夹存在性检查已完成.'
    }

代码详解

  1. 导入必要的库

    1
    2
    3
    
    import boto3
    from datetime import datetime
    from dateutil import tz
    
    • boto3:AWS SDK for Python,用于与 AWS 服务交互。
    • datetimedateutil.tz:处理日期和时区。
  2. 初始化 S3 资源和相关变量

    1
    2
    3
    4
    5
    
    s3 = boto3.resource('s3')
    bucket_name = 'hc-data-analytics'
    local_tz = tz.gettz('Asia/Shanghai')
    now = datetime.now()
    date_prefix = now.strftime('%Y/%m/%d/')
    
    • 指定要监控的 S3 存储桶名称。
    • 获取当前时间,并格式化为 YYYY/MM/DD/ 的前缀,用于定位当天的文件夹。
  3. 定义需要检查的文件夹前缀

    1
    2
    3
    4
    5
    6
    
    folder_prefixes = [
        'MiniProgram/RGC-Prod-API-MiniProgram/' + date_prefix,
        'RGC-Prod-3in1oven/' + date_prefix
    ]
    # 确保每个前缀以斜杠结尾
    folder_prefixes = [prefix if prefix.endswith('/') else prefix + '/' for prefix in folder_prefixes]
    
    • 列出需要检查的两个主要前缀,并确保每个前缀以斜杠结尾,以正确匹配 S3 的目录结构。
  4. 初始化 SNS 客户端

    1
    2
    
    sns = boto3.client('sns')
    topic_arn = 'arn:aws-cn:sns:cn-north-1:1234567890:s3-logs-monitoring'
    
    • 指定 SNS 主题的 ARN,用于发送通知。
  5. 遍历每个前缀并检查子文件夹

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    
    
    for prefix in folder_prefixes:
        resp = s3.meta.client.list_objects_v2(Bucket=bucket_name, Prefix=prefix, Delimiter='/')
        subfolders = [p['Prefix'] for p in resp.get('CommonPrefixes', [])]
    
        if len(subfolders) > 0:
            print(f"子文件夹 '{prefix}' 存在:")
            for folder in subfolders:
                print(f"发现子文件夹: {folder}")
        else:
            message = f"S3桶'{bucket_name}'中'{prefix}'下不存在新增文件夹, 即日志同步S3桶任务失败.请检查."
            sns.publish(TopicArn=topic_arn, Message=message)
            print(f"已发送 SNS 消息: {message}")
    
    • 使用 list_objects_v2 方法列出指定前缀下的子文件夹。
    • 如果存在子文件夹,记录日志。
    • 如果不存在子文件夹,构造错误消息并通过 SNS 发送通知。
  6. 返回执行结果

    1
    2
    3
    4
    5
    
    
    return {
        'statusCode': 200,
        'body': 'S3 文件夹存在性检查已完成.'
    }
    
    • Lambda 函数执行完成后,返回 HTTP 200 状态码和简单的消息体。

部署与配置

1. 创建 SNS 主题

  • 登录到 AWS 管理控制台,导航到 Amazon SNS 服务。
  • 创建一个新的 SNS 主题,记下其 ARN(例如,本文代码中的 arn:aws-cn:sns:cn-north-1:1234567890:s3-logs-monitoring)。
  • 配置订阅者(如电子邮件地址),以便在收到通知时接收消息。

2. 创建 IAM 角色

Lambda 函数需要适当的权限才能访问 S3 和 SNS:

  • 创建一个 IAM 角色,附加以下策略:
    • AmazonS3ReadOnlyAccess:允许读取 S3 存储桶内容。
    • AmazonSNSFullAccess 或自定义策略,允许发布到指定的 SNS 主题。
  • 确保 Lambda 函数使用此角色运行。

3. 部署 Lambda 函数

  • 登录到 AWS 管理控制台,导航到 Lambda 服务。
  • 创建一个新的 Lambda 函数,选择 Python 3.x 运行时。
  • 将上述代码粘贴到函数代码编辑器中。
  • 配置环境变量(如有需要)。
  • 设置执行角色为前面创建的 IAM 角色。
  • 配置触发器,可以使用 Amazon EventBridge(之前的 CloudWatch Events)来定期触发 Lambda 函数,例如每天运行一次。

4. 配置 EventBridge 触发器

  • 在 Lambda 函数的“配置”选项卡中,添加一个新的触发器。
  • 选择 EventBridge(CloudWatch Events),并配置一个定时表达式(如 cron(0 0 * * ? *) 表示每天午夜触发一次)。
  • 保存配置,确保触发器已启用。

最佳实践与优化

  1. 错误处理:当前代码在异常情况下可能会中断。建议添加 try-except 块,捕获并处理可能的异常,确保 Lambda 函数的稳定性。

    1
    2
    3
    4
    5
    6
    7
    8
    
    
    try:
        # 主要逻辑
    except Exception as e:
        error_message = f"Lambda 函数执行失败: {str(e)}"
        sns.publish(TopicArn=topic_arn, Message=error_message)
        print(error_message)
        raise e
    
  2. 日志记录:利用 AWS CloudWatch Logs 记录详细的日志,便于后续的排查和监控。

  3. 参数化配置:将存储桶名称、前缀列表、SNS 主题 ARN 等参数化,通过环境变量或配置文件管理,增强灵活性。

  4. 性能优化:对于大规模存储桶,可以考虑使用分页机制(ContinuationToken)处理大量对象,避免漏检。

  5. 安全性:确保 IAM 角色只授予必要的最小权限,遵循最小权限原则,增强安全性。

总结

通过结合 AWS Lambda、S3 和 SNS,我们可以轻松实现对 S3 存储桶中特定文件夹的监控,并在出现异常时及时发送通知。这种无服务器的解决方案不仅灵活高效,而且具有高度的可扩展性,适用于各种自动化监控和运维场景。希望本文提供的代码示例和详细解析能帮助您在实际项目中快速实现类似的功能。



作者:SRE运维博客
博客地址:https://www.cnsre.cn/
文章地址:https://www.cnsre.cn/posts/240920163523/
相关话题:https://www.cnsre.cn/tags/lambda/


🔲 ☆

使用IAM通过标签分配不同用户SSM会话窗口的完整指南


作者:SRE运维博客
博客地址:https://www.cnsre.cn/
文章地址:https://www.cnsre.cn/posts/240917103351/
相关话题:https://www.cnsre.cn/tags/aws/


什么是SSM和IAM标签分配?

在AWS上进行多用户管理时,安全和灵活的权限控制至关重要。SSM(AWS Systems Manager)允许通过控制台或命令行接口远程管理EC2实例,而IAM(Identity and Access Management)则是定义用户和服务在AWS中的操作权限的核心组件。

使用IAM角色和标签,我们可以为不同的用户分配特定EC2实例的访问权限,确保他们只能访问与自己相关的资源。例如,如果你有10台EC2服务器,你可以使用标签将这10台服务器分为两组,让用户A和用户B分别只能访问自己的EC2实例。


为什么选择通过标签分配SSM会话权限?

直接为用户分配权限显得笨重且复杂,特别是在需要不断添加或移除资源时。通过为资源添加标签,我们可以通过IAM策略轻松控制用户的访问权限,这使得管理变得更加灵活和可扩展。

那么,如何做到这一点?我们可以通过简单几步配置IAM策略来实现这个目标。


实现IAM策略:如何分配不同用户的SSM会话权限?

要实现通过标签分配不同用户对特定EC2实例的SSM会话窗口权限,我们需要定义一个合理的IAM策略。这个策略应包含以下功能:

  1. 允许用户使用System Manager控制台:通过IAM策略,用户可以访问SSM管理控制台并查看会话窗口的相关信息。
  2. 启动特定标签资源的会话:用户只能对具有指定标签的EC2实例启动SSM会话。
  3. 终止自己的会话:用户只能终止自己启动的SSM会话,确保操作安全性。

我们可以为这个需求设计如下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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PassRoleForSSM",
            "Effect": "Allow",
            "Action": "iam:PassRole",
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "iam:PassedToService": "ssm.amazonaws.com"
                }
            }
        },
        {
            "Sid": "DescribeSessionsAndInstances",
            "Effect": "Allow",
            "Action": [
                "ssm:GetConnectionStatus",
                "ec2:DescribeInstances",
                "ssm:DescribeSessions",
                "iam:ListRoles",
                "ssm:DescribeInstanceProperties"
            ],
            "Resource": "*"
        },
        {
            "Sid": "StartSSMSession",
            "Effect": "Allow",
            "Action": "ssm:StartSession",
            "Resource": "arn:aws:ec2:region:account-id:instance/*",
            "Condition": {
                "StringEquals": {
                    "aws:ResourceTag/Project": "test"
                }
            }
        },
        {
            "Sid": "TerminateOwnSession",
            "Effect": "Allow",
            "Action": "ssm:TerminateSession",
            "Resource": "arn:aws:ssm:region:account-id:session/${aws:username}*"
        }
    ]
}

解析策略的关键部分

  1. PassRoleForSSM: 允许用户通过IAM角色为SSM服务授权访问权限。
  2. DescribeSessionsAndInstances: 允许用户查看当前活动的会话和EC2实例的详细信息。
  3. StartSSMSession: 用户只能启动标签为 Project=test 的EC2实例的SSM会话。通过这样的标签条件,我们可以轻松将不同的实例分配给不同的用户。
  4. TerminateOwnSession: 用户只能终止自己启动的SSM会话,确保不会影响其他人的操作。

如何通过标签分配会话?

通过为EC2实例添加标签,我们可以方便地指定哪些资源属于哪个用户。例如,我们可以为实例添加标签 Project=test,这样具有相应权限的用户就可以通过SSM访问这些实例。

举例

  • 用户A的EC2实例可以添加标签 user=A
  • 用户B的实例可以添加标签 user=B

在IAM策略中,可以根据 aws:ResourceTag 条件语句控制用户的访问权限。这意味着,用户A将只能访问带有 user=A 标签的实例,而用户B只能访问带有 user=B 标签的实例。


结论

通过IAM标签和策略,我们可以为不同用户分配特定资源的访问权限,实现精细化的权限控制。在AWS这样的复杂环境中,确保安全与灵活性并存至关重要。通过本文的策略示例和解释,我们可以轻松地为我们的用户提供合适的SSM访问权限,确保操作的安全性和可控性。


作者:SRE运维博客
博客地址:https://www.cnsre.cn/
文章地址:https://www.cnsre.cn/posts/240917103351/
相关话题:https://www.cnsre.cn/tags/aws/


🔲 ⭐

使用 Lambda 函数将 CloudWatch Log 中的日志归档到 S3 桶中


作者:SRE运维博客
博客地址:https://www.cnsre.cn/
文章地址:https://www.cnsre.cn/posts/221205544069/
相关话题:https://www.cnsre.cn/tags/aws/


躺了好久,诈尸了。因为换了工作,所以比较忙一直没有时间去更新博客的内容(主要还是因为懒🤔)

话不多说 直接上干货。

需求背景

最近在看费用的时候发现有很大一部分费用都是 cloudwatch log中存储了大量的数据,是因为ec2 将日志传输到了存储到了cloudwatch中。这个存储的多的查询日志的时候收费特别的高。另外一个是因为数据分析用途,大数据分析的同事如果想那到数据的话,还是存储在 S3 中是比较划算和方便的,一个是拿取数据比较方便,另外一个是S3 可以最归档存储,后面的大量数据可以分层储存,以此来降低费用。
如果你也想将你的cloudwatch 中日志组中的日志存储到S3中的话可以参考下这篇文章。

前置条件

  • 创建 一个 S3 桶,并修改权限
  • 创建 lambda 函数
  • 有一个Cloudwatch 日志组并且有一天以上的日志
  • 给 lambda分配所需的权限

创建 S3 桶并修改权限

sre运维|Linux运维|关键词

国内S3桶权限配置

 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
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.cn-north-1.amazonaws.com.cn"
            },
            "Action": "s3:GetBucketAcl",
            "Resource": "arn:aws-cn:s3:::<bucket name>"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "logs.cn-north-1.amazonaws.com.cn"
            },
            "Action": "s3:PutObject",
            "Resource": "arn:aws-cn:s3:::<bucket name>/*",
            "Condition": {
                "StringEquals": {
                    "s3:x-amz-acl": "bucket-owner-full-control"
                }
            }
        }
    ]
}

国外S3桶权限配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
    "Version": "2012-10-17",
    "Statement": [
      {
          "Action": "s3:GetBucketAcl",
          "Effect": "Allow",
          "Resource": "arn:aws:s3:::<bucket name>",
          "Principal": { "Service": "logs.us-west-2.amazonaws.com" }
      },
      {
          "Action": "s3:PutObject" ,
          "Effect": "Allow",
          "Resource": "arn:aws:s3:::<bucket name>*",
          "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } },
          "Principal": { "Service": "logs.us-west-2.amazonaws.com" }
      }
    ]
}

S3 桶权限文档链接

创建 lambda 函数

创建 lambda

 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
import boto3
import logging
import time
import datetime
import json

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def export_s3_logs(bucket_name, log_group_name, log_stream_name, days_of_logs=1, timeout=1000):
    '''
    today = datetime.datetime.combine(datetime.datetime.utcnow(), datetime.datetime.min.time())
    day_end = today
    day_start = today - datetime.timedelta(days=days_of_logs)
    '''
    today = datetime.datetime.combine(datetime.datetime.utcnow() + datetime.timedelta(hours=8),
                                      datetime.datetime.min.time()) # UTC+8 

    day_end = today - datetime.timedelta(hours=8) # UTC
    day_start = today - datetime.timedelta(days=days_of_logs, hours=8) # UTC    
   
    #print(day_start)
    ts_start = '{0:.0f}'.format(((day_start - datetime.datetime(1970, 1, 1)).total_seconds())*1000)
    ts_end = '{0:.0f}'.format(((day_end - datetime.datetime(1970, 1, 1)).total_seconds())*1000)
    the_date = '/'.join([str(today.year), '0'+str(today.month)[-2:], '0'+str(today.day)[-2:]]) 
    #folder_name = '/'.join([log_group_name, log_stream_name, the_date])
    folder_name = '/'.join([log_group_name,the_date])
    client = boto3.client('logs')
    
    #print (ts_start, ts_end)#, day_start, day_end,the_date
    
    task_id = client.create_export_task(
        logGroupName=log_group_name,
        #logStreamNamePrefix=log_stream_name,
        fromTime=int(ts_start),
        to=int(ts_end),
        destination=bucket_name,
        destinationPrefix=folder_name
    )['taskId']

    i = 1
    while i<timeout:
        response = client.describe_export_tasks(
            taskId=task_id
        )

        status = response['exportTasks'][0]['status']
        if status == 'COMPLETED':
            result = True
            break
        elif status != 'RUNNING':
            result = False
            break
        i += 1
        time.sleep(interval)
    
    return result

def lambda_handler(event, context):
    region = 'cn-northwest-1' # 日志组所在区域
    bucket_name = '<bucket name>' #同区域内的S3桶名称
    log_group_name = '<log group name>' #日志组名称
    log_stream_name = '1' #默认即可
    log_export_days = 1   #默认即可
    export_s3_logs(bucket_name, log_group_name, log_stream_name, log_export_days)

给 lambda 分配权限

  • AmazonS3的读写权限
  • CloudWatchLogsFullAccess

验证桶内的文件

最后会以日期的目录将日志归档起来,以方便日后对归档文件进行梳理。
sre运维|Linux运维|关键词


作者:SRE运维博客
博客地址:https://www.cnsre.cn/
文章地址:https://www.cnsre.cn/posts/221205544069/
相关话题:https://www.cnsre.cn/tags/aws/


🔲 ☆

记一次 Ubuntu 内核升级故障处理


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210915154434/
相关话题:https://www.cnsre.cn/tags/故障集/


情况介绍

负责的项目下有一批 ubuntu 18.04 的服务器在 AWS 上,因为安全的问题,需要把内核从 5.3.0 升级到 5.4.0。

首次升级为测试环境测两台都是ubuntu 18.04 的版本 内核也都为5.3.0。第一台升级进展很顺利。软件更新,然后内核进行单独升级。等到需要重启的时候出现了问题。

处理问题及解决思路

问题1

无法挂载磁盘

首先遇到的第一个问题

解决思路:

升级内核导致 boot 空间越来越小,然后导致无法引导进入系统。因为之前遇到过boot空间占满的情况。但是那是在 kvm 的 vm 中,可以通过 VNC 进行链接修复。这在 aws 上怎么办?

解决方法:

一开始我选择了将改服务器的根磁盘取消挂载。然后挂载到同一可用区的其他服务器上,进行修复。因为磁盘格式的问题,始终挂载不上,为了避免浪费时间,只能以快照恢复的方式将根磁盘进行扩容。

以快照的方式恢复了回复,在快照恢复的过程中将根磁盘扩容的方法果然将服务器运行起来了。

后面就接着尝试进行内核升级….

问题2

内核升级数据库依赖报错?

具体内容如下:

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

解决思路:

这个问题,真的是没有思路。处理了很久,都没有解决这个问题。还希望有思路的能到指导下。

解决方法:

为了快速解决内核升级的问题,我将 mysql 以及相关的依赖都卸载掉了。

问题3

升级完重启失败?

这个问题也是最大的问题,最明显的表现就是。升级没有报错,但是升级完需要重启,服务器进行重启的时候无法进入操作系统。

此时已经是凌晨4点多钟了,已经很迷糊了。然后就把服务器恢复到升级内核前的样子。打算明天启动快照进行复现。

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

解决思路:

又是挂载失败?怎么又会遇到挂载失败呢?最后发现重启自动挂载磁盘的配置并没有按照官方的指示去做使用UUID的配置开启挂载盘符。从而系统会检测磁盘的过程中会检测到该错误。无法正常进如系统。

解决方法:

如果是物理机,或者是可以通过其他方式进行控制引导的话还可以修复。但是云主机怎么修复呢?只能去修复磁盘了

在云主机上有两种访问磁盘卷的方法

方法 1:使用 EC2 控制台

(摘自 AWS 文档)

如果您为 Linux 启用了 EC2 串行控制台,则可以使用它来排查受支持的基于 Nitro 的实例类型问题。串行控制台可帮助您排查启动问题、网络配置和 SSH 配置问题。串行控制台无需网络连接即可连接到您的实例。您可以使用 Amazon EC2 控制台或 AWS 命令行界面 (AWS CLI) 访问串行控制台。

在使用串行控制台之前,请在账户层面授予对串行控制台的访问权限。然后,创建 AWS Identity and Access Management (IAM) 策略,授予对 IAM 用户的访问权限。此外,每个使用串行控制台的实例都必须至少包含一个基于密码的用户。如果您的实例无法访问,并且尚未配置对串行控制台的访问权限,请按照方法 2 中的说明进行操作。有关为 Linux 配置 EC2 串行控制台的信息,请参阅配置对 EC2 串行控制台的访问权限

注意:如果在运行 AWS CLI 命令时遇到错误,请确保您使用的是最新版本的 AWS CLI

方法 2:挂载到其他实例上

创建一个临时救援实例,然后将您的 Amazon Elastic Block Store (Amazon EBS) 卷重新挂载到该救援实例上。从该救援实例中,您可以将 GRUB 配置为使用以前的内核进行启动。

**重要提示:**请勿在实例存储支持的实例上执行此操作。由于此恢复方法需要首先停止然后再重启实例,该实例上的任何数据都将丢失。有关更多信息,请参阅确定实例的根设备类型

  • 为根卷创建 EBS 快照。有关更多信息,请参阅创建 Amazon EBS 快照
  • 打开 Amazon EC2 控制台
  • 注意: 请确保您位于正确的区域。
  • 从导航窗格中选择 实例,然后选择受损的实例。
  • 选择 Instance State(实例状态)、Stop Instance(停止实例),然后选择 Stop(停止)。
  • 在 **Storage(存储)**选项卡的 **Block devices(块储存设备)**下,为 /dev/sda1 或 /dev/xvda 选择 Volume ID(卷 ID)
  • 依次选择 操作断开卷 ,然后选择 是,请分离 。记下可用区。
  • 在同一可用区中启动一个救援 EC2 实例。
  • 启动救援实例后,从导航窗格中选择 ,然后选择受损实例已分离的根卷。
  • 依次选择 操作附加卷
  • 选择救援实例 ID (id-xxxxx),然后设置一个未使用的设备。在本示例中为 /dev/sdf
  • 使用 SSH 连接到救援实例。
  • 运行 lsblk 命令以查看可用的磁盘设备:
lsblk
# 输出如下:
xvda    202:0    0   20G  0 disk 
└─xvda1 202:1    0   20G  0 part /
xvdb    202:16   0  100G  0 disk 
xvdf    202:80   0   15G  0 disk 
└─xvdf1 202:81   0   15G  0 part    # 该磁盘为故障集服务器根磁盘

查看磁盘格式

lsblk -f
NAME    FSTYPE   LABEL           UUID                                 MOUNTPOINT
xvda                                                                  
└─xvda1 ext4     cloudimg-rootfs d32458a7-7f4c-415f-9a66-b579f14fb82d /
xvdb    ext4                     eb0e325a-471c-4a99-a9be-a3ee296c2405 
xvdf                                                                  
└─xvdf1 ext4     cloudimg-rootfs d32458a7-7f4c-415f-9a66-b579f14fb82d 

挂载磁盘

sudo -i
mount  /dev/xvdf1  /mnt

然后查看挂载目录,发现根磁盘已经挂载到了mnt下

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

查看配置文件

ubuntu@ip-10-0-20-27:~$  cat /etc/fstab 
LABEL=cloudimg-rootfs   /        ext4   defaults,discard        0 0
/dev/nvme0n1        /data   ext4    defaults        0       0

查看官网挂载文档如下:

重启后自动挂载附加的卷

(摘自AWS 官方文档)

要在每次系统重启时附加附加的 EBS 卷,可在 /etc/fstab 文件中为该设备添加一个条目。

您可以在 /dev/xvdf 中使用设备名称(如 /etc/fstab),但建议改为使用设备的 128 位通用唯一标识符 (UUID)。设备名称可以更改,但 UUID 会在整个分区的使用寿命期间保留。通过使用 UUID,您可以减少系统在硬件重新配置后无法启动的机会。有关更多信息,请参阅识别 EBS 设备

重启后自动附加附加卷

  1. (可选)创建 /etc/fstab 文件的备份,以便在编辑时误损坏或删除此文件时使用。

    [ec2-user ~]$ sudo cp /etc/fstab /etc/fstab.orig
    
  2. 使用 blkid 命令查找设备的 UUID。记下要在重新启动后挂载的设备的 UUID。在下一步中您将需要用到它。

    例如,以下命令显示有两个设备挂载到实例上,并显示了两个设备的 UUID。

    [ec2-user ~]$ sudo blkid
    /dev/xvda1: LABEL="/" UUID="ca774df7-756d-4261-a3f1-76038323e572" TYPE="xfs" PARTLABEL="Linux" PARTUUID="02dcd367-e87c-4f2e-9a72-a3cf8f299c10"
    /dev/xvdf: UUID="aebf131c-6957-451e-8d34-ec978d9581ae" TYPE="xfs"
    

    对于 Ubuntu 18.04,请使用 lsblk 命令。

    [ec2-user ~]$ sudo lsblk -o +UUID
    
  3. 使用任何文本编辑器(如 /etc/fstabnano)打开 vim 文件。

    [ec2-user ~]$ sudo vim /etc/fstab
    
  4. 将以下条目添加到 /etc/fstab 以在指定的挂载点挂载设备。这些字段是 blkid(或用于 Ubuntu 18.04 的 lsblk)返回的 UUID 值、挂载点、文件系统以及建议的文件系统挂载选项。有关必填字段的更多信息,请运行 man fstab 以打开 fstab 手册。

    在以下示例中,我们将 UUID 为 aebf131c-6957-451e-8d34-ec978d9581ae 的设备挂载到挂载点 /data,然后我们使用 xfs 文件系统。我们还使用 defaultsnofail 标志。我们指定 0 以防止文件系统被转储,并且我们指定 2 以指示它是非根设备。

    UUID=aebf131c-6957-451e-8d34-ec978d9581ae  /data  xfs  defaults,nofail  0  2
    

    注意

    如果您要在未附加此卷的情况下启动实例(例如,将卷移动到另一个实例之后),nofail 附加选项允许该实例即使在卷附加过程中出现错误时也可启动。Debian 衍生物 (包括早于 16.04 的 Ubuntu 版本) 还必须添加 nobootwait 挂载选项。

  5. 要检查条目是否有效,请在 /etc/fstab 中运行以下命令以卸载设备,然后挂载所有文件系统。如果未产生错误,则说明 /etc/fstab 文件正常,您的文件系统会在重启后自动挂载。

    [ec2-user ~]$ sudo umount /data
    [ec2-user ~]$ sudo mount -a
    

    如果收到错误消息,请解决文件中的错误。

    警告

    /etc/fstab 文件中的错误可能显示系统无法启动。请勿关闭 /etc/fstab 文件中有错误的系统。

    如果您无法确定如何更正 /etc/fstab 中的错误并且您在此过程的第一步中创建了一个备份文件,则可以使用以下命令从您的备份文件还原。

    [ec2-user ~]$ sudo mv /etc/fstab.orig /etc/fstab
    

查看修改日期核对修改时间

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

问题都解决了。接下来继续升级内核吧。

sudo apt-get install linux-image-5.4.0-1055-aws

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

等待重启查看

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

终于成功了。。。

问题总结

  • 问题1
    更新内核导致引导分区存储占满。
  • 优化
    在ubuntu 进行内核补丁软件更新时需要注意boot、root分区的容量。以避免重启后无法正常引导进入系统。
  • 问题2
    更新下载软件,提示 were encountered while processing
  • 优化
    后测试发现更新下载任何软件都会出现这种情况,暂未解决。
  • 问题3
    磁盘开机自动挂载配置问题。
  • 优化
    以后需要严格按照 AWS 官方文档来进行操作部署,以免再次遇到类似的事情发生。

作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210915154434/
相关话题:https://www.cnsre.cn/tags/故障集/


🔲 ☆

安装和配置CloudWatchAgent


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210531100355/
相关话题:https://www.cnsre.cn/tags/aws/


使用 CloudWatch 代理收集指标和日志

下载 CloudWatch 代理软件包

sudo yum install amazon-cloudwatch-agent

点击查看其他平台软件包

配置文件

1
2
3
4
5
6
配置文件路径及名称 
/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
#配置启动
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-ctl -a fetch-config -m ec2 -s -c file:/opt/aws/amazon-cloudwatch-agent/etc/amazon-cloudwatch-agent.json
#启动服务
systemctl restart amazon-cloudwatch-agent.service
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
{
        "agent": {
                "metrics_collection_interval": 60,
                "run_as_user": "cwagent"
        },
        "metrics": {
                "aggregation_dimensions": [
                        [
                                "InstanceId"
                        ]
                ],
                "append_dimensions": {
                        "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
                        "ImageId": "${aws:ImageId}",
                        "InstanceId": "${aws:InstanceId}",
                        "InstanceType": "${aws:InstanceType}"
                },
                "metrics_collected": {
                        "procstat": [
                                {
                                 "pid_file": "/var/run/sshd.pid",
                                 "measurement": [
                                        "cpu_usage",
                                        "memory_rss"
                                ]
                                },
                                {
                                 "pid_file": "/var/run/sshd.pid",
                                 "measurement": [
                                        "read_bytes",
                                        "read_count",
                                        "write_bytes"
                                ],
                                 "metrics_collection_interval": 10
                               },
                        "cpu": {
                                "measurement": [
                                        "cpu_usage_idle",
                                        "cpu_usage_iowait",
                                        "cpu_usage_user",
                                        "cpu_usage_system"
                                ],
                                "metrics_collection_interval": 60,
                                "resources": [
                                        "*"
                                ],
                                "totalcpu": false
                        },
                        "disk": {
                                "measurement": [
                                        "used_percent",
                                        "inodes_free"
                                ],
                                "metrics_collection_interval": 60,
                                "resources": [
                                        "*"
                                ]
                        },
                        "diskio": {
                                "measurement": [
                                        "io_time"
                                ],
                                "metrics_collection_interval": 60,
                                "resources": [
                                        "*"
                                ]
                        },
                        "mem": {
                                "measurement": [
                                        "mem_used_percent"
                                ],
                                "metrics_collection_interval": 60
                        },
                        "swap": {
                                "measurement": [
                                        "swap_used_percent"
                                ],
                                "metrics_collection_interval": 60
                        }
                }
        }
}
 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
{
        "logs": {
                "logs_collected": {
                        "files": {
                                "collect_list": [{
                                                "file_path": "/logArchive/hcaextension/info*.log",
                                                "log_group_name": "RGC-Prod-3in1oven",
                                                "log_stream_name": "info.logs"
                                        },
                                        {
                                                "file_path": "/logArchive/hcaextension/http*.log",
                                                "log_group_name": "RGC-Prod-3in1oven",
                                                "log_stream_name": "http.logs"
                                        }
                                ]
                        }
                }
        },
        "metrics": {
                "append_dimensions": {
                        "AutoScalingGroupName": "${aws:AutoScalingGroupName}",
                        "ImageId": "${aws:ImageId}",
                        "InstanceId": "${aws:InstanceId}",
                        "InstanceType": "${aws:InstanceType}"
                },
                "metrics_collected": {
                        "cpu": {
                                "measurement": [
                                        "cpu_usage_idle",
                                        "cpu_usage_iowait",
                                        "cpu_usage_user",
                                        "cpu_usage_system"
                                ],
                                "metrics_collection_interval": 180,
                                "totalcpu": false
                        },
                        "disk": {
                                "measurement": [
                                        "used_percent",
                                        "inodes_free"
                                ],
                                "metrics_collection_interval": 180,
                                "resources": [
                                        "*"
                                ]
                        },
                        "diskio": {
                                "measurement": [
                                        "io_time",
                                        "write_bytes",
                                        "read_bytes",
                                        "writes",
                                        "reads"
                                ],
                                "metrics_collection_interval": 180,
                                "resources": [
                                        "*"
                                ]
                        },
                        "mem": {
                                "measurement": [
                                        "mem_used_percent"
                                ],
                                "metrics_collection_interval": 180
                        },
                        "netstat": {
                                "measurement": [
                                        "tcp_established",
                                        "tcp_time_wait"
                                ],
                                "metrics_collection_interval": 180
                        },
                        "statsd": {
                                "metrics_aggregation_interval": 60,
                                "metrics_collection_interval": 180,
                                "service_address": ":8125"
                        },
                        "swap": {
                                "measurement": [
                                        "swap_used_percent"
                                ],
                                "metrics_collection_interval": 180
                        }
                }
        }
}

作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210531100355/
相关话题:https://www.cnsre.cn/tags/aws/


🔲 ☆

AWS扩容EC2实例根空间


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210517344530/
相关话题:https://www.cnsre.cn/tags/zabbix/


aws 端操作

先在EC2 实例中选中磁盘 然后打开跟设备

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控
修改大小后保存
cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

ec2 端操作

lsblk 查看当前设备的磁盘编号
cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控
df -T -H 查看扩容前的空间大小并确定磁盘格式
cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控
growpart /dev/nvme0n1 1 把扩容的空间挂载到磁盘上
cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控
centos7执行划分空间命令
sudo xfs_growfs -d / 把空闲的空间划分至 /
centos6执行划分空间命令
resize2fs /dev/nvme0n1p1
cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控
df -h 查看验证
cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210517344530/
相关话题:https://www.cnsre.cn/tags/zabbix/


🔲 ☆

AWS使用ALB负载均衡遇到的问题


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210514620165/
相关话题:https://www.cnsre.cn/tags/aws/


问题描述

ALB 负载均衡 RGC-Dev-ALB.xxx.cn-north-1.elb.amazonaws.com.cn 解析到2个IP 54.223.xxx.xx52.81.xxx.xx, 发现每2次请求会失败一次,在进一步测试抓包发现没有收到52.81.xxx.xxx的返回信息。

问题分析

随后检查ALB建立在两个子网(subnet-a1xxxxxsubnet-f3xxxxx
其中54.223.xxx.xxsubnet-f32xxxx中,子网路由表rtb-49xxxx中0.0.0.0/0 指向IGW,因此客户端可以主动访问到54.223.xxx.xx
52.81.xxx.xxsubnet-a1xxx中,子网路由表rtb-24xxx中0.0.0.0/0指向了nat gateway(nat-0axxxxxxxxxx), 这将导致客户端无法连接到52.81.xxx.xx, 因此也不会收到52.81.xxx.xx的回包。
请知晓,对于面向公网的ALB,需要将ALB部署在公有子网中, 即子网路由表0.0.0.0/0需要指向IGW。

解决办法

目前有2个解决办法

1) 修改子网路由表rtb-24xxx, 将0.0.0.0/0指向igw, 请知晓, 这个修改将影响所有关联了rtb-24xxx这个路由表的子网,
如果对应子网中的资源没有公网地址,修改完成后将失去访问公网的能力,此外对于子网中有公网地址的资源,将直接从公网路由可达。
2) 修改ALB的子网,可以在EC2的控制台找到“负载均衡” ,选择对应的ALB, 在“描述” > “基本配置” >“可用区” > 点击“编辑子网”, 将subnet-a1xxx 修改为同AZ的公有子网(即路由表0.0.0.0/0指向igw的子网)


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210514620165/
相关话题:https://www.cnsre.cn/tags/aws/


🔲 ☆

解决证书链过期问题 - 确保安全连接的完整指南


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210414131058/
相关话题:https://www.cnsre.cn/tags/故障集/


故障描述

在我们的生产环境中,我们制作了一个健康检查页面,并通过脚本去监控他的健康状态,可是在前天(2020-5-30 周六)下午 18:50 左右的时候收到告警健康检查页面故障,等我登录服务器排查故障的时候发现是curl命令报错,报错的内容为:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@ip-10-0-10-100 ~]# curl -v  https://xxxxxx.cn/hcaextension/hcmini/v1/healthyCheck
*   Trying 54.223.xxx.xx...
* TCP_NODELAY set
* Connected to xxxxxxxx.cn (54.223.xx.xx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* TLSv1.2 (OUT), TLS header, Certificate Status (22):
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (OUT), TLS alert, certificate expired (557):
* SSL certificate problem: certificate has expired
* Closing connection 0
curl: (60) SSL certificate problem: certificate has expired
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

在通过我今天上午的测试,我发现AWS EC2为:Linux 、Linux2 的操作系统不能够正常使用,在AWS EC2 -Centos 7.7、阿里云以及物理机房中测试是没有问题的。

分析过程

经过查看报错信息,发现是由于SSL握手的时候证书验证错误导致的,以下是排查的步骤:

  • Curl 的-k参数可以忽略SLL证书的验证,您可以添加-k参数临时避免遇到此错误,如下是我的测试,发现可以正常的访问页面:
1
2
curl -k  https://xxxxxx.cn/hcaextension/hcmini/v1/healthyCheck
{"code":0,"msg":"成功","messageid":"29250cf5-176b-4993-a724-e5c9d7cc2ace"}
  • 通过进一步分析证书“xxxxx.cn”,我们发现证书链是存在问题的,我们的证书自身并没有过期,但是一个证书链证书过期了,我们提取了证书链的信息,您可以参考附件的信息。
    检测地址
    证书链过期问题示例

解决方法

更新证书链,将过期的证书链信息去除,尝试是否可以正常访问:
https://docs.amazonaws.cn/IAM/latest/UserGuide/id_credentials_server-certs.html


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210414131058/
相关话题:https://www.cnsre.cn/tags/故障集/


🔲 ☆

数据库CPU 100%问题分析与解决方案 - 提升性能


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210413131020/
相关话题:https://www.cnsre.cn/tags/故障集/


问题描述

2020年7月13日一大早收到告警,测试环境数据库CPU告警。

登录aws查看监控如下图

AWS监控数据库CPU使用情况

问题分析

出现这种cpu 100%的问题,一般都是因为sql性能问题导致的。
主要表现于 cpu消耗过大,有慢sql造成、慢sql全表扫描,扫描数据库过大,内存排序,队列等等
并发现写入相对于查询来说比较高(这是一个关键点)
有了大概的思路下边开始排查吧

查看进程

show full processlist;

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控
发现有大量的语句状态为 sending data sending data: sql正从表中查询数据,如果查询条件没有适当索引,会导致sql执行时间过长。

查看慢日志配置

mysql> show variables like 'slow_query%';
+---------------------+----------------------------------------------+
| Variable_name       | Value                                        |
+---------------------+----------------------------------------------+
| slow_query_log      | ON                                           |
| slow_query_log_file | /rdsdbdata/log/slowquery/mysql-slowquery.log |
+---------------------+----------------------------------------------+
2 rows in set
mysql> show variables like 'slow_launch_time';
+------------------+-------+
| Variable_name    | Value |
+------------------+-------+
| slow_launch_time | 1     |
+------------------+-------+
1 row in set

看到慢日志已经开启

登录aws cloudwatch查看慢日志发现大部分为这条sql

# User@Host: admin[admin] @  [10.0.11.12]  Id:  2302
# Query_time: 3.602910  Lock_time: 0.100585 Rows_sent: 2  Rows_examined: 4454
SET timestamp=1594629311;
SELECT a.enum_value,a.enum_value
        FROM external_mapping a
        LEFT JOIN external_command_key b ON a.command_id=b.id
        LEFT JOIN external_command_options c ON a.options_id=c.id
        LEFT JOIN external_command_key d ON a.command_id=d.id
        LEFT JOIN category h ON a.category_id=h.id
        where 1=1
            AND b.code='Common.Status.Event'
            AND c.code='Common.Setting.Rm4Valve'
            AND d.code='Rm4_Valve'
            AND a.platform_id=119
            AND h.cname = 'TT';

查看是否有锁表

mysql> show OPEN TABLES where In_use > 0;
#查看是否有锁表
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;
#查看正在锁的事务
Empty set
SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; 
#查看等待锁的事务
Empty set
暂时没有看到锁表的情况

查看缓存命中

mysql> show global status like 'Qca%';
+-------------------------+-----------+
| Variable_name           | Value     |
+-------------------------+-----------+
| Qcache_free_blocks      | 1         |
| Qcache_free_memory      | 134199912 |
| Qcache_hits             | 0         |
| Qcache_inserts          | 0         |
| Qcache_lowmem_prunes    | 0         |
| Qcache_not_cached       | 44950579  |
| Qcache_queries_in_cache | 0         |
| Qcache_total_blocks     | 1         |
+-------------------------+-----------+

  • Qcache_hits:查询缓存命中次数。
  • Qcache_inserts:将查询和结果集写入到查询缓存中的次数。
  • Qcache_not_cached:不可以缓存的查询次数。
  • Qcache_queries_in_cache:查询缓存中缓存的查询量。

查看到缓存命中为0%

查看引擎状态

mysql> show engine innodb status;

通过上边一系列的查询,发现以下几个问题

1、慢查询、全表扫描过多

描述

慢sql:查看到sql语句执行时间过长。

全表扫描:这个策略用于检查百分比((Handler_read_rnd+Handler_read_rnd_next)/(Handler_read_first+Handler_read_key+Handler_read_next+Handler_read_prev+Handler_read_rnd+Handler_read_rnd_next))。 这是一个需要读取全表内容的操作,而不是仅读取使用索引选定的部分。 通常使用小型查找表执行,或者在具有大型表的数据仓库情况下执行而其中所有可用数据都被聚合和分析。

建议

慢sql: 根据sql 检查语句并进行索引优化。

全表扫描:应该尽量保持这个值尽可能的低。尝试隔离那些不使用索引的查询。一旦识别了那些查询,请创建适当的索引或重写查询以使用索引。MySQL 有一个很棒的功能 - 慢速查询日志,它允许你记录所有需要超过指定时间运行的查询。慢速速查询日志可用于识别需要很长时间才能完成的查询。

2、数据库最大并发连接数量

描述
当服务器启动后,(max_used_connections)变量将提供一个基准,以帮助你确定服务器支持的最大连接数量。 它还可以帮助进行流量分析。
建议
如果需要支持更多的连接,应该增加变量 max_connections 的值。MySQL 支持的最大连接数量是取决于给定平台上线程库的质量、可用 RAM 的数量、每个连接可使用多少 RAM、每个连接的工作负载以及所需的响应时间。

3、查询缓存要配置

缓存描述
这个策略用于检查查询缓存命中率(Qcache_hits/(Qcache_hits + Com_select))。 MySQL 查询缓存将缓存一个分析的查询及其整个结果集。 当你有许多小型的查询返回小型数据集时,这是非常好的,因为查询缓存将允许返回结果立即可供使用,而不是每次发生时都重新运行查询。
建议
理想情况下,查询缓存的命中率应该接近 100%。MySQL 的查询缓存是一项强大的技术,并且在管理良好的情况下可以显着提高数据库的吞吐量。一旦你的应用程序被创建,你可以看看它如何使用数据库,并相应地调整查询缓存。有足够大的缓存,避免碎片化和排除大型的查询,你就应该能够保持极高的缓存命中率,并享受出色的性能。

处理过程

根据上边发现的问题进行了配置的修改

1、修改慢查询以及全表扫描

此问题联系开发进行索引优化,减少全表扫描。

2、数据库最大连接数量

修改配置 max_user_connections 我这边设置的为1000
cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

3、查询缓存的配置

cnsre运维博客|Linux系统运维|自动化运维|云计算|运维监控

  • query_cache_size:分配用于缓存查询结果的内存量。
  • query_cache_limit:不要缓存大于此字节数的结果。
  • query_cache_type:对于查询结果,不缓存(= OFF),不缓存NO_CACHE(= ON),或仅缓存(= DEMAND)分别用012 表示

修改完数据但是需要重启才能生效。

问题解决

正在准备空闲时间重启RDS的时候,开发那边有了进展。
开发同事把缓存写错了!!!!😳😳😳

总结

理下业务
程序暴露接口给测试部门,测试部门在上报了50W条数据,开发这边程序有没有添加数据过滤(过滤掉垃圾数据),并且…开发在程序中写错了缓存。所以导致相对于读取来说写入较高。因为在缓存查询不到想到的数据,就进行了全表扫描,继而出现了大量进程以及连接数队列等等。。
处理问题可以,别主动背锅。。。在接手数据库的时候最好检查下配置,了解数据库的情况,在出现问题的时候能够最快速的定位解决问题。
另外,经过此次的故障处理,加固了对业务以及数据库一些参数的理解。


作者:SRE运维博客
博客地址:https://www.cnsre.cn
文章地址:https://www.cnsre.cn/posts/210413131020/
相关话题:https://www.cnsre.cn/tags/故障集/


🔲 ☆

AWS EC2实例时区设置与故障排查

AWS EC2实例时区设置与故障排查

问题描述

在AWS上使用CentOS 7创建EC2实例时,设置时区无效。本文将分析原因并提供解决方案。

解决办法

1
yum upgrade tzdata -y

原因分析

1
zdump -v /usr/share/zoneinfo/Asia/Shanghai

我们会发现时区是固定不变的,无论我们通过修改 localtime 还是通过 timedatectl 修改,都无效。经过一番搜索,我发现是由于 tzdata 数据库老旧导致,升级即可解决。

另外也可以通过 TZ 环境变量来设置,这是操作系统默认支持的方式。

设置时区

1
timedatectl set-timezone Asia/Shanghai

或者

1
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

致谢

感谢 LinuxQuestions 提供的帮助。

文章链接
https://www.cnsre.cn/posts/210312112259/

🔲 ⭐

AWS ECS使用EBS作为Volume

  在 基于Terraform在AWS ECS中构建Jenkins持续集成体系 一文中, Alliot 采用了 EFS 作为 Jenkins 容器的数据卷,直接挂载了 /var/jenkins_home 目录。
正如评论区提到的, 我们在使用 bursting 模式的 EFS 时,遇到了 IO 性能的问题, 虽然 master + slave 架构的 Jenkins 将构建任务分发到了 slave 节点,减少了 master 节点的压力,但是在启动构建任务时, master 节点依然会有大量的 IO 操作, 这个时候会导致 bursting 模式下的 EFS 瞬间打光 Credit 从而导致整个 master 挂掉。当然,我们可以使用 Provisoning 模式缓解性能问题,但其价格又非常贵,性价比不高。
  好在从今年(2024)的一月开始, AWS ECS 的 Fargate 支持使用 EBS 卷作为 Volume 了。 目前官网的文档还比较分散,这里小记一些需要注意的点。

❌