AWS S3 和 Lambda 使用

news2025/4/13 15:50:46

目录:
AWS概述
EMR Serverless
AWS VPC及其网络
关于AWS网络架构的思考
AWS S3 和 Lambda 使用

本文将通过一个实例来说明如何使用 AWS S3 和 Lambda。


使用场景:通过代码将文件上传到S3,该文件需要是公开访问的,并对上传的文件进行安全检测。

文件上传到S3

S3 bucket 设置

首先创建一个S3的bucket,例如 my-test-cn-north-1-bucket。为了公开访问,该bucket必须关闭"Block public access (bucket settings)",除了bucket级别之外,账号级别也需要关闭。这个设置是 public read 的前提。

Bucket policy 必须设置Principal和Action,Principal可以设置为当前账户下的用户或角色。如果允许当前账户下的所有的用户/角色,可以这样设置:

{
  "Effect": "Allow",
  "Principal": { "AWS": "arn:aws:iam::123456789012:root" },
  "Action": [
                "s3:GetObject",
                "s3:GetObjectAcl",
                "s3:PutObject",
                "s3:PutObjectAcl"
            ],
  "Resource": "arn:aws:s3:::my-test-cn-north-1-bucket/*"
}

由于Bucket policy限制,此时bucket中的文件只有当前账户下的用户或角色才能读写。因此需要在上传文件时将文件的权限设置为public read的,也就是说要修改文件的ACL。编辑 Object Ownership,开启 “ACLs enabled”,至于对象拥有关系,选择 “Bucket owner preferred” 即可。

上传文件,并在文件的权限设置中将ACL修改为"public access"。这样设置完成之后,即可以保证bucket的put操作是受限的,同时read操作是公开的。

S3 API 文件上传

本地通过代码进行文件上传:

private static S3AsyncClient getAsyncClient() {
        AssumeRoleRequest assumeRoleRequest = AssumeRoleRequest.builder()
                .roleArn("arn:aws-cn:iam:: 123456789012:role/product/operation")
                .roleSessionName("AssumeRoleSession").build();

        StsClient stsClient = StsClient.builder()
                .credentialsProvider(ProfileCredentialsProvider.builder()
                        .profileName("your-profile") // replace with your profile
                        .build())
                .build();

        StsAssumeRoleCredentialsProvider creProvider =
                StsAssumeRoleCredentialsProvider.builder().stsClient(stsClient)
                        .refreshRequest(assumeRoleRequest).build();
        return S3AsyncClient.crtBuilder().credentialsProvider(creProvider).build();
}

而在生产环境中,当然是不能将角色ARNaws profile写到代码中的。因此需要用过 IRSA(IAM Roles for Service Accounts) 来实现 AWS API 调用。

创建一个ServiceAccount:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-s3-access
  namespace: your-ns
  annotations:
    eks.amazonaws.com/role-arn: arn:aws-cn:iam:: 123456789012:role/my-test-cn-north-1-eks-access-s3

在角色my-test-cn-north-1-eks-access-s3policies中,需要设置bucket的访问策略:

{
	"Effect": "Allow",
        "Action": "s3:*",
        "Resource": [
            "arn:aws-cn:s3:::my-test-cn-north-1-bucket/*"
        ]
}

然后在deployment中指定serviceAccount:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: ms-test
spec:
  template:
    metadata:
      labels:
    	  ...
    spec:
      serviceAccountName: my-s3-access

如此 EKS 的 workloads 即可以 ServiceAccount 绑定的角色身份来执行 S3 的 API 调用。

文件上传代码如下:

private static void putObjectToS3(InputStream is, String key, String bucketName, boolean publicRead) {
    try (S3TransferManager transferManager = S3TransferManager.builder()
        .s3Client(S3AsyncClient.crtCreate()).build()) {

        UploadRequest uploadRequest = UploadRequest.builder()
                .putObjectRequest(req -> {
                    req.bucket(bucketName).key(key);
                    if (publicRead) {
                        req.acl("public-read"); // set public read acl
                    }
                })
                .addTransferListener(LoggingTransferListener.create())
                .requestBody(AsyncRequestBody.fromInputStream(
                        config -> config.inputStream(is).executor(newFixedThreadPool(8))))
                .build();

        transferManager.upload(uploadRequest).completionFuture().join();
    }
}

某些类型的文件如果不指定contentType,通过url访问时需要下载。如果指定了contentType,则可以在浏览器中打开。

文件安全扫描

创建lambda函数

文件上传到 S3之后,可以通过 lambda 来进行安全扫描。

首先创建一个 AWS lambda,选择最简单的 Author from scratch 模板,同时选择需要的运行时环境和系统架构,这里选择arm64架构。对于权限,如果需要复用role,就选择已有的role,否则就默认创建角色。

创建好 lambda 之后,需要在 Diagram 界面添加触发器,选择需要监听的 S3 bucket。

在 lambda 的配置页面,选择 Permissions 可以看到执行lambda的角色,也是创建lambda时默认创建的角色。查看该角色的权限 policies,可以发现与lambda日志相关的权限已经有了,但是还需要以下权限:

{
    "Effect": "Allow",
    "Action": [
        "s3:GetObject",
        "sns:Publish"
    ],
    "Resource": [
        "arn:aws-cn:s3:::my-test-cn-north-1-bucket/*"
    ]
}

创建 lambda layer

对文件进行安全扫描需要用到Clamav。正常情况下需要下载 Clamav 的源码然后编译成二进制文件。但是从官方下载的source包中没有configure文件,无法编译和安装。因此可以用第二种方式,直接在容器中安装 Clamav,然后将必要的文件拷贝出来制作 lambda layer。

使用docker run -it --name lambda-clamav amazonlinux:2 bash 来创建一个docker容器。

容器使用 amazonlinux:2 镜像是为了保持与 AWS Lambda的环境保持一致。

在容器中执行以下命令:

# 安装 ClamAV 和必要工具
yum install -y clamav clamav-update tar gzip

# 确认版本
clamscan --version

# 更新病毒库
freshclam

# 创建打包目录
mkdir -p /opt/clamav-layer/bin
mkdir -p /opt/clamav-layer/lib

# 复制 ClamAV 主程序
cp /usr/bin/clamscan /opt/clamav-layer/bin/
cp /usr/bin/freshclam /opt/clamav-layer/bin/

# 复制动态链接库
ldd /usr/bin/clamscan | awk '{print $3}' | xargs -I {} cp {} /opt/clamav-layer/lib/
ldd /usr/bin/freshclam | awk '{print $3}' | xargs -I {} cp {} /opt/clamav-layer/lib/

# 复制病毒库配置文件(可选)
mkdir -p /opt/clamav-layer/etc
cp /etc/freshclam.conf /opt/clamav-layer/etc/

将文件从容器复制到本地:

docker cp lambda-clamav:/opt/clamav-layer ./clamav-layer

# 进入目录并打包
cd clamav-layer
zip -r ../clamav-layer.zip .

注意,只有更新病毒库后才能使用clamscan test.txt来扫描文件,否则没有基础的病毒库文件无法扫描。下载的病毒库文件默认放在/var/lib/clamav 目录下,总共有四个文件:

bytecode.cvd  daily.cvd  freshclam.dat  main.cvd

这里打包clamav-layer时并没有将病毒库一起打包进来,原因是 AWS Layer 限制了大小,压缩包不能超过50MB,解压后不能超过250MB。

layer 制作完成后,在 Lambda 的控制台的layer仓库中上传。上传时需要注意 layer的系统架构,可以在生成layer文件的容器中查看:

file /opt/clamav-layer/bin/clamscan

# 以下结果表明为 ARM 架构
opt/clamav-layer/bin/clamscan: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 3.7.0, BuildID[sha1]=ac6484d18dd79864db6d56599e020f8a968f652c, stripped

上传完成后我们回到 lambda 函数,在code tab页的最下面上传 layer。layer的架构和运行时必须与lambda函数的架构和运行时是兼容的。如果lambda 函数是 x86_64的,就无法使用 arm 架构的layer。

layer的压缩包会直接解压到 lambda 实例的 /opt 目录下,其结构会变成: /opt/bin/clamscan, /opt/lib/…

lambda 函数实现

由于受到layer的大小限制导致病毒库无法打包到layer中,因此考虑实时下载或从S3下载。

将病毒库文件打包成clamav_db.tar.gz并上传到S3,然后实现 lambda 函数:

import os
import subprocess
import boto3
import tarfile
from botocore.exceptions import ClientError

s3 = boto3.client('s3')
S3_BUCKET = "ms-test-cn-north-1-bucket"
CLAMSCAN_PATH = "/opt/bin/clamscan"
FRESHCLAM_PATH = "/opt/bin/freshclam"
LIB_DIR = "/opt/lib"
DB_KEY = "clamav_db.tar.gz"
TMP_DB_PATH = "/tmp/clamav_db.tar.gz"
DB_DIR = "/tmp/clamav_db"

def download_and_extract_db():
    try:
        s3.download_file(S3_BUCKET, DB_KEY, TMP_DB_PATH)
        os.makedirs(DB_DIR, exist_ok=True)
        with tarfile.open(TMP_DB_PATH, "r:gz") as tar:
            tar.extractall(DB_DIR)
        os.remove(TMP_DB_PATH)
    except ClientError as e:
        print(f"S3 下载失败: {e}")
        raise

def lambda_handler(event, context):
    os.environ["LD_LIBRARY_PATH"] = LIB_DIR
    if not os.path.exists(f"{DB_DIR}/main.cvd"):
        print(f"start download clamav db")
        download_and_extract_db()
        
    bucket = event['Records'][0]['s3']['bucket']['name']
    key = event['Records'][0]['s3']['object']['key']
    tmp_path = f"/tmp/{os.path.basename(key)}"
    try:
        s3.download_file(bucket, key, tmp_path)
        print(f"the file need to be checked: ", tmp_path)

        scan_cmd = [CLAMSCAN_PATH, "-d", DB_DIR, tmp_path]
        try:
            result = subprocess.run(scan_cmd, env={"LD_LIBRARY_PATH": LIB_DIR}, capture_output=True, text=True, timeout=180)
            print("stdout:", result.stdout)
            print("stderr:", result.stderr) 
            if "Infected files: 0" not in result.stdout:
                print(f"感染文件: {key} - 结果: {result.stdout}")
                return {"status": "INFECTED"}
            else:
                print(f"安全文件: {key}")
                return {"status": "CLEAN"}
        except subprocess.TimeoutExpired:
            print("timeout!")
    except ClientError as e:
        print(f"S3 错误: {e}")
        return {"status": "ERROR"}
    finally:
        if os.path.exists(tmp_path):
            os.remove(tmp_path)

对于lambda来说,只有 /tmp目录是可写的,且最大为512MB。当 Lambda 服务复用同一个执行环境即热启动时,该目录是保留的。这种复用通常发生在短时间内连续多次调用同一个函数时。

当Lambda服务创建一个新的执行环境时,/tmp 目录会被清空并重新初始化。

如果想持久化病毒库,而不是每次重新下载,可以考虑挂载EFS。

测试

在 lambda 函数的 code 页面可以创建test event用来模拟S3 trigger,并可以保存下来复用。注意,测试事件中bucketName和文件需要是真实存在的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2327142.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何在 Unity3D 导入 Spine 动画

一、前言 《如何在 Unity3D 项目中导入 Spine 动画》,虽然在网上有很多这种文章,直接将问题交给 DeepSeek 也能得到具体的操作流程,但是照着他们提供的方法还是能遇到几个问题,比如: AI 回答没有提到 Unity 无法识别.…

论文笔记:ASTTN模型

研究现状 现有研究大多通过分别考虑空间相关性和时间相关性或在滑动时间窗口内对这种时空相关性进行建模,而未能对直接的时空相关性进行建模。受最近图领域Transformer成功的启发,该模型提出利用局部多头自关注,在自适应时空图上直接建立跨时…

2025-4-2 蓝桥杯刷题情况(分布式队列)

1.题目描述 小蓝最近学习了一种神奇的队列:分布式队列。简单来说,分布式队列包含 N 个节点(编号为0至N-1,其中0号为主节点),其中只有一个主节点,其余为副节点。 主/副节点中都各自维护着一个队列,当往分布式队列中添加…

【Java中级】10章、内部类、局部内部类、匿名内部类、成员内部类、静态内部类的基本语法和细节讲解配套例题巩固理解【5】

❤️ 【内部类】干货满满,本章内容有点难理解,需要明白类的实例化,学完本篇文章你会对内部类有个清晰的认知 💕 内容涉及内部类的介绍、局部内部类、匿名内部类(重点)、成员内部类、静态内部类 🌈 跟着B站一位老师学习…

swift-7-汇编分析闭包本质

一、汇编分析 fn1里面存放的东西 func testClosure2() {class Person {var age: Int 10}typealias Fn (Int) -> Intvar num 0func plus(_ i: Int) -> Int {num ireturn num}return plus} // 返回的plus和num形成了闭包var fn1 getFn()print(fn1(1)) // 1print(fn1(…

Linux: 进程信号初识

目录 一 前言 二 信号的感性认识 三 信号处理常见方式 四 系统信号列表 五 信号的保存 六 信号的产生 1. 通过终端按键产生信号 2. 通过系统调用向进程发送信号 3. 硬件异常产生信号 4. 软件条件产生信号 一 前言 在Linux操作系统中,进程信号是一个非常重…

CSS--解决float: right在空间不够时会自动往下移的问题

原文网址:CSS--解决float: right在空间不够时会自动往下移的问题-CSDN博客 简介 众所周知,float: right在空间不够时会自动往下移。那么怎样让它不要往下移呢?本文介绍解决方案。 需求 我想写一个无需列表,每个列表后边跟一个…

深度学习 Deep Learning 第14章 自编码器

深度学习 Deep Learning 第14章 自编码器 内容概要 本章深入探讨了自编码器(Autoencoders),这是一种用于特征学习和降维的神经网络架构。自编码器通过编码器和解码器两个部分,将输入数据映射到一个内部表示(编码&…

C++(匿名函数+继承+多态)

#include <iostream> #include <cstring> #include <cstdlib> #include <unistd.h> #include <sstream> #include <vector> #include <memory>using namespace std;// 基类 Weapon class Weapon { protected:int atk; public:Weapon…

软考中级网络工程师第十一章网络管理

11-1考点分析 11-2网络管理基础&#xff08;记忆&#xff09; 网络管理体系结构 网络管理五大功能域&#xff1a;故障管理、配置管理、计费管理、性能管理和安全管理。 助记&#xff1a; “安配能计障” 故障管理&#xff1a;尽快发现故障&#xff0c;找出故障原因&#x…

创维E900V22C/E900V22D_S905L3(B)_安卓9.0_指示灯正常_线刷固件包

创维E900V22C&#xff0f;E900V22D_S905L3(B)_安卓9.0_指示灯正常_线刷固件包 线刷方法&#xff1a;&#xff08;新手参考借鉴一下&#xff09; 1、准备好一根双公头USB线刷刷机线&#xff0c;长度30-50CM长度最佳&#xff0c;同时准备一台电脑&#xff1b; 2、电脑上安装好刷…

“京数青算“启新篇|北方算网与海东市数据局签署合作协议

近日&#xff0c;青海省海东市2025年“京数青算”推介会在北京召开。海东市委常委、副市长梁荣勃&#xff0c;海东市数据局局长安志忠出席会议&#xff0c;北方算网副总经理&#xff08;主持工作&#xff09;喻一鸣等60余家人工智能企业的代表参会。 梁荣勃在致辞中代表海东市…

QML输入控件: Slider的高级外观定制(音视频控制条)

目录 引言相关阅读示例1&#xff1a;基础样式定制要点效果 示例2&#xff1a;音量控制滑块要点效果 示例3&#xff1a;视频进度条要点效果 解决问题总结工程下载 引言 在现代用户界面设计中&#xff0c;滑块控件(Slider)是一个不可或缺的交互元素。它不仅能让用户直观地进行数…

密码学基础——古典密码学

目录 一、定义 特点&#xff1a; 二、发展阶段 三、代换密码 1.单表代换密码 1.1恺撒密码 1.2 移位变换 1.3 仿射变换 2.多表代换密码 维吉尼亚密码 四、置换密码 栅栏密码 一、定义 古典密码学是指在现代密码学出现之前&#xff0c;使用较为简单的数学方法和手工…

KingbaseES物理备份还原之备份还原

此篇续接上一篇<<KingbaseES物理备份还原之物理备份>>,上一篇写物理备份相关操作,此篇写备份还原的具体操作步骤. KingbaseES版本:V009R004C011B003 一.执行最新物理备份还原 --停止数据库服务,并创建物理备份还原测试目录 [V9R4C11B3192-168-198-198 V8]$ sys_ct…

jdk21新特性详解使用总结

jdk21新特性详解总结 1.StringBuilder和StringBuffer新增了一个repeat方法 /*** Java 21的StringBuilder和StringBuffer新增了一个repeat方法*/public static void repeatStr(){var sbnew StringBuilder().repeat("*",10);System.out.println(sb);}运行结果如下&…

【实用技巧】电脑重装后的Office下载和设置

写在前面&#xff1a;本博客仅作记录学习之用&#xff0c;部分图片来自网络&#xff0c;如需引用请注明出处&#xff0c;同时如有侵犯您的权益&#xff0c;请联系删除&#xff01; 文章目录 前言下载设置总结互动致谢参考目录导航 前言 在数字化办公时代&#xff0c;Windows和…

206. 反转链表 92. 反转链表 II 25. K 个一组翻转链表

leetcode Hot 100系列 文章目录 一、翻转链表二、反转链表 II三、K 个一组翻转链表总结 一、翻转链表 建立pre为空&#xff0c;建立cur为head&#xff0c;开始循环&#xff1a;先保存cur的next的值&#xff0c;再将cur的next置为pre&#xff0c;将pre前进到cur的位置&#xf…

离线语音识别 ( 小语种国家都支持)可定制词组

1产品介绍 离线语音模组采用神经网络算法&#xff0c;支持语音识别、自学习等功能。运用此模组将 AI 技 术赋能产品&#xff0c;升级改造出语音操控的智能硬件 ( 例如风扇、台灯、空调、马桶、按摩椅、运 动相机、行车记录仪等 ) 。支持全球多种语言识别&#xff0c;如中文…

网络华为HCIA+HCIP 策略路由,双点双向

目录 路由策略&#xff0c;策略路由 策略路由优势 策略路由分类 接口策略路由 双点双向 双点双向路由引入特点: 联系 路由回灌和环路问题 路由策略&#xff0c;策略路由 路由策略:是对路由条目进行控制&#xff0c;通过控制路由条目影响报文的转发路径&#xff0c;即路…