AWS 基础:Region、IAM 和主要服务

1. 总览

本文帮助你快速掌握刚接触 AWS 时最重要的三个支柱:region(选择基础设施部署在何处)、IAM(谁能做什么)和主要服务(EC2、S3、RDS、Lambda、VPC……)。读完后,你将理解如何安全地组织 AWS 账户、懂得如何为应用创建 IAM role,并且能写出通过 AWS SDK v2 调用 S3 的 Java 代码。

2. Region 和 Availability Zone

AWS 将基础设施分成 region(地理区域,例如 ap-southeast-1 位于新加坡)和各 region 内的多个 Availability Zone(AZ) —— 这些是在电力、网络上都独立的数据中心,彼此靠得很近(延迟低)但在故障上保持隔离(故障隔离)。

aws ec2 describe-availability-zones --region ap-southeast-1

上面的命令列出新加坡 region 内的 AZ。结果通常如下:

ZoneName: ap-southeast-1a
ZoneName: ap-southeast-1b
ZoneName: ap-southeast-1c

在设计系统时,你应该至少部署在 2 个 AZ 中以实现容错(高可用)。例如 Auto Scaling Group 或 RDS 多 AZ 实例都遵循这个原则。

注意: 某些服务是 全局的(不绑定特定 region),如 IAM、Route 53、CloudFront。其他大多数服务(EC2、S3、RDS……)是 区域性的,数据不会自动复制到其他 region,除非你主动配置。

2.1. 如何选择 region

三个主要标准:延迟(靠近用户)、合规性(数据存储位置要求)和成本(不同 region 的服务价格不同)。对于越南用户,ap-southeast-1(新加坡)通常是首选,因为延迟低且服务完整。

3. IAM:User、Group、Role、Policy

IAM(Identity and Access Management)控制谁可以访问什么资源。四个核心概念:

  • User:具体的身份(人或服务)拥有自己的凭证。
  • Group:用户集合,便于批量管理策略。
  • Role:临时权限集合,不绑定固定凭证 —— 用于 EC2、Lambda 或跨账户访问。
  • Policy:JSON 文档,描述对操作和资源的权限(允许/拒绝)。
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::order-invoices-bucket/*"
    }
  ]
}

这个 Policy 允许读/写 order-invoices-bucket bucket 中的对象。将 policy 附加到一个 role,然后将这个 role 关联到运行 InvoiceStorageService 服务的 EC2 实例或 Lambda 函数 —— 应用会自动拥有权限,无需硬编码 access key/secret key。

aws iam create-role \
  --role-name InvoiceServiceRole \
  --assume-role-policy-document file://trust-policy.json

aws iam attach-role-policy \
  --role-name InvoiceServiceRole \
  --policy-arn arn:aws:iam::123456789012:policy/InvoiceBucketAccess

第一条命令用 trust-policy.json 创建 role,其中定义谁可以 “assume” 这个 role(例如 EC2 service)。第二条命令将具体权限策略附加到 role。

注意: 永远不要在代码中硬编码 access key/secret key 或提交到 Git。对于 AWS 上运行的资源使用 IAM Role,对于用户使用 IAM Identity Center/SSO。

3.1. 最小权限原则

只授予必要的权限,不要给服务账户使用 AdministratorAccess policy。例如一个 Lambda 只需要读某个 bucket,就不应该授予整个账户的 s3:* 权限。

{
  "Effect": "Deny",
  "Action": "s3:DeleteBucket",
  "Resource": "*"
}

Deny Statement 在冲突时总是优先于 Allow —— 这是一种“安全网”,防止误删重要 bucket。

4. 主要服务

4.1. 计算

  • EC2:虚拟机按小时/秒收费,你自己管理操作系统。
  • Lambda:事件驱动运行代码,无需管理服务器,按调用次数和运行时间计费。
  • ECS/EKS:运行带编排的容器(Docker)。
aws lambda invoke \
  --function-name CalculateOrderTotal \
  --payload '{"orderId":"ORD-1001"}' \
  response.json

这条命令调用 CalculateOrderTotal 函数,传入 JSON payload,结果写入 response.json。

4.2. 存储

  • S3:对象存储,保存任意文件(图像、日志、备份),耐久性 99.999999999%。
  • EBS:块存储,挂载到 EC2。
  • EFS:共享文件系统,多个 EC2 可以同时挂载。

4.3. 数据库

  • RDS:托管关系数据库(MySQL、PostgreSQL……),自动备份、补丁。
  • DynamoDB:NoSQL,自动横向扩展,毫秒级延迟。

4.4. 网络

  • VPC:虚拟私有网络,你定义子网、路由表、安全组。
  • Security Group:实例级有状态防火墙。
Resources:
  OrderServiceSG:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: 允许 HTTPS 入站
      VpcId: vpc-0123456789abcdef0
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0

这段 CloudFormation 创建名为 OrderServiceSG 的安全组,仅对所有 IP 开放 443 端口(HTTPS)—— 其他入站规则默认被拒绝。

5. 从 Java 应用调用 S3

本部分演示如何用 AWS SDK for Java v2 从 Spring Boot 服务上传文件到 S3。

<dependency>
    <groupId>software.amazon.awssdk</groupId>
    <artifactId>s3</artifactId>
    <version>2.25.16</version>
</dependency>
public class InvoiceStorageService {

    private final S3Client s3Client;
    private final String bucketName;

    public InvoiceStorageService(S3Client s3Client, String bucketName) {
        this.s3Client = s3Client;
        this.bucketName = bucketName;
    }

    public void uploadInvoice(String orderId, byte[] content) {
        PutObjectRequest request = PutObjectRequest.builder()
                .bucket(bucketName)
                .key("invoices/" + orderId + ".pdf")
                .build();

        s3Client.putObject(request, RequestBody.fromBytes(content));
    }
}

S3Client 通过构造函数注入(如果在 EC2/Lambda 上运行会自动从 IAM Role 获取凭证,无需硬编码)。uploadInvoice 方法用 key 作为 bucket 内的对象路径创建 PutObjectRequest,然后调用 putObject 上传内容到 S3。

S3Client bean 的配置:

@Bean
public S3Client s3Client() {
    return S3Client.builder()
            .region(Region.AP_SOUTHEAST_1)
            .build();
}

无需显式传入 credentialsProvider —— SDK 自动使用 Default Credentials Provider Chain:按顺序查找环境变量、~/.aws/credentials 文件,然后查找 EC2/ECS/Lambda 关联的 IAM Role。

5.1. 用 mock S3Client 测试

@Test
void shouldCallPutObjectWithCorrectKey() {
    S3Client mockS3 = mock(S3Client.class);
    InvoiceStorageService service = new InvoiceStorageService(mockS3, "order-invoices-bucket");

    service.uploadInvoice("ORD-1001", "发票内容".getBytes());

    ArgumentCaptor<PutObjectRequest> captor = ArgumentCaptor.forClass(PutObjectRequest.class);
    verify(mockS3).putObject(captor.capture(), any(RequestBody.class));

    assertThat(captor.getValue().bucket()).isEqualTo("order-invoices-bucket");
    assertThat(captor.getValue().key()).isEqualTo("invoices/ORD-1001.pdf");
}

用 Mockito mock S3Client,这样在运行 CI 时不会调用真实的 AWS。ArgumentCaptor 捕获实际发送的 PutObjectRequest,然后用 AssertJ 验证 bucket 和 key 符合预期 —— 确保构建 request 的逻辑正确且不浪费网络成本。

注意: 要进行真实的集成测试,使用 LocalStack 或 Testcontainers 库而非调用生产环境的 S3 bucket —— 避免产生成本和数据风险。

6. 常见错误

  • 不小心把 S3 bucket 设为公开:除非真的需要公开 bucket(例如静态网站),否则始终在账户级别启用 “Block Public Access”。
  • 给 test role 附加过宽泛的 policy("Action": "*", "Resource": "*"),然后忘记在上生产前移除。
  • 没有给 root 账户启用 MFA:root 账户拥有完全权限,失去 root 访问权是最严重的风险。
  • 在代码中硬编码 access key:应该使用 IAM Role、Secrets Manager 或通过 CI/CD secret 的环境变量。
  • 选错 region 导致终端用户延迟高或违反数据本地化存储要求。

7. 总结

Region 帮助你在延迟、成本和合规性之间平衡选择基础设施位置。IAM 是第一道防线,也是最关键的 —— 始终遵循最小权限原则,优先使用 Role 而非静态 access key。掌握核心服务组(EC2、S3、RDS、Lambda、VPC)及通过 AWS SDK 的集成方式,足以让你开始在 AWS 上部署真实系统。

awscloudiamdevopsjava