使用 Pulumi 在 AWS 上安全快速地部署静态网站 使用 Pulumi 在 AWS 上安全快速地部署静态网站

2025-06-08

使用 Pulumi 在 AWS 上安全快速地部署静态网站

使用 Pulumi 在 AWS 上安全快速地部署静态网站

这是Pulumi 部署和文档挑战赛的提交内容:快速静态网站部署

我建造了什么

在这个项目中,我使用 Pulumi 作为基础设施即代码 (IaC) 工具,在 AWS 上创建了一个安全且可扩展的静态网站基础设施。该解决方案设置了一个 Amazon S3 存储桶来托管静态站点文件,并将其与 Amazon CloudFront 集成,以实现快速的全球内容交付。为了确保安全的 HTTPS 访问,我将权限限制为仅限 CloudFront,使用 AWS 证书管理器 (ACM) 获取 SSL 证书,并使用 Route 53 设置了一个自定义域。

所有代码均使用 Pulumi 的 Python SDK 以 Python 编写,这使得代码库易于阅读和维护。最终构建了一个现代化的 React 静态网站,可立即投入生产,经济高效且安全可靠,所有操作均由 Pulumi 完全自动化完成。

我的快速静态网站片段

现场演示链接

CloudFront 链接
https://d8fuic0sibn8l.cloudfront.net/
https://d8fuic0sibn8l.cloudfront.net/nonexistent-page
自定义域链接
https://challenge.drintech.online
https://challenge.drintech.online/nonexistent-page

项目回购

使用 Pulumi 在 AWS 上安全快速地部署静态网站

这是一个使用 pulumi 建立安全、快速的静态网站部署的存储库。

目录

概述

该项目使用 Pulumi 作为 IAC 工具来设置安全快速的静态网站部署。Pulumi 是一款独特的 IAC 工具,可让您使用自己喜欢的编程语言定义基础架构。借助 Pulumi,我们将在 AWS 上设置基础架构(S3 存储桶、CloudFront 和 Route 53),并使用 Python 将静态网站文件安全地部署到此基础架构。

技术

  • Pulumi:一种使用 Python 配置和管理云资源的基础设施即代码 (IaC) 工具。

  • Python:一种提供与 Pulumi 和 AWS 交互的模块的编程语言……

我的旅程

由于这是我第一次使用 Pulumi,所以我从研究 Pulumi 文档开始这个项目。事实证明,这是一次非常棒的体验,因为 Pulumi 文档对开发者友好且易于理解。作为 Python 的爱好者,并且对它非常熟悉,我选择使用 Pulumi Python SDK 来搭建静态网站基础架构,并在其中部署一个静态 React 网站。

我的开发之旅收获颇丰,因为我对技术有了更深入的理解,但也充满了意想不到的挑战。有一次,我甚至以为我的电脑上下载了恶意软件,哈哈。我使用 Pulumi 文档提供的静态网站模板开始了这段旅程。让我们回顾一下我在开发过程中的步骤和遇到的挑战。

使用的技术摘要

  • Pulumi:一种使用 Python 配置和管理云资源的基础设施即代码 (IaC) 工具。

  • Python:一种提供与 Pulumi 和 AWS 资源交互的模块的编程语言。

  • S3 Bucket:一种存储静态网站文件和资产的对象存储服务,作为 CloudFront 的起源。

  • CloudFront:一种内容分发网络 (CDN),可缓存网站内容,并以低延迟的方式在全球范围内安全分发。这些内容缓存在距离用户较近的边缘站点。

  • Route 53:管理 DNS 记录的服务,允许网站使用自定义域。

建筑学

架构图

先决条件

首先,需要满足以下条件:

  • 已安装 Python3、pip 和 python3-venv

  • 对 Python 有基本的了解

  • 已安装并配置 AWS CLI

步骤1:Pulumi安装

下面的命令在 Linux 机器上安装 pulumi,刷新 shell 以便反映更改,并确认安装。

curl -fsSL https://get.pulumi.com | sh
source ~/.bashrc
pulumi version
Enter fullscreen mode Exit fullscreen mode

步骤2:设置项目模板

  • 接下来,我创建了一个新文件夹website/并设置了静态网站模板。该静态网站模板捆绑了示例文件和配置,以便使用 S3 和 CloudFront 部署静态网站。
mkdir website && cd website
pulumi new static-website-aws-python
Enter fullscreen mode Exit fullscreen mode

由于这是我pulumi new第一次使用该命令,因此我需要登录 Pulumi Cloud 来验证我的 CLI。我发现这一点很有意思,因为验证后,我就可以立即开始构建基础设施。Pulumi 会自动远程管理状态文件,而不像其他 IaC 工具那样,我必须手动设置后端文件来远程管理状态文件,然后再专注于自动化。
Pulumi 控制台登录

  • 身份验证后,我配置了网站部署所需的项目和堆栈。其中包括:

    • 设置项目名称、描述和堆栈名称。
    • 选择 pip 作为安装依赖项的工具链。
    • 设置us-east-1为部署我们的 AWS 资源(例如 S3 存储桶)的区域。
    • 确认索引、错误文档和网站路径。

完成配置后,pulumi 继续安装 中定义的依赖项,requirements.txt但由于 python 环境变量错误而失败。我没有安装 python3-venv。幸运的是,python 提供了正确的命令,让我能够安装错误消息中提到的正确版本。
python3-venv 错误

python依赖项安装

Pulumi 将基础架构设置作为项目和堆栈进行管理。项目代表整个基础架构定义。它们包含元数据、运行时以及一些用于管理和执行基础架构代码的配置。项目在pulumi.yaml文件中定义。

堆栈是项目的一个实例,具有其自身的配置和状态。堆栈包含特定于环境的配置,允许我们将基础架构部署到不同的环境,例如开发、预发布和生产环境。堆栈在 中定义pulumi.dev.yaml

设置项目和堆栈后,pulumi 为我生成了以下文件。

  • pulumi.yaml:这代表项目。它包含元数据、运行时和用于管理基础架构代码的配置。
name: website
description: A Python program to deploy a static website on AWS
runtime:
  name: python
  options:
    toolchain: pip
    virtualenv: venv
config:
  pulumi:tags:
    value:
      pulumi:template: static-website-aws-python
Enter fullscreen mode Exit fullscreen mode
  • pulumi.dev.yaml:此文件代表开发环境的堆栈。它包含特定于环境的配置,例如 AWS 区域、静态文件路径和 HTML 文档。
config:
  aws:region: us-east-1
  website:indexDocument: index.html
  website:errorDocument: error.html
  website:path: ./www
Enter fullscreen mode Exit fullscreen mode
  • __main__.py:这是包含定义基础架构代码的主文件。它指定要部署的资源及其配置。

  • requirements.txt:这里列出了使用 Pulumi 部署基础设施所需的 Python 依赖项。

pulumi>=3.0.0,<4.0.0
pulumi-aws>=6.0.2,<7.0.0
pulumi-synced-folder>=0.0.0,<1.0.0
Enter fullscreen mode Exit fullscreen mode
  • venv/:此文件夹在隔离的虚拟环境中管理该项目的 Python 依赖项。

  • www/:此文件夹包含示例静态 Web 文件,包括index.htmlerror.html

  • .gitignore:此处列出的文件不会被 Git 跟踪。这导致我们无法将依赖项推送到我们的仓库。

细分__main__.py

所有主要操作都在这个文件中进行。本文将重点介绍代码中协同工作以构建静态网站基础架构的各个部分,以及我如何处理代码中已弃用的部分。在进行详细分析后,我将调整这些组件,以部署更安全、更个性化、更快速的静态网站基础架构。

  • 导入所需模块
import pulumi
import pulumi_aws as aws
import pulumi_synced_folder as synced_folder
Enter fullscreen mode Exit fullscreen mode

这将导入必要的模块来创建 AWS 资源并将文件同步到 S3 存储桶。

  • 读取配置设置
config = pulumi.Config()
path = config.get("path") or "./www"
index_document = config.get("indexDocument") or "index.html"
error_document = config.get("errorDocument") or "error.html"
Enter fullscreen mode Exit fullscreen mode

这将从堆栈中读取特定于环境的设置pulumi.Config并将它们作为变量传递。

  • 创建 S3 存储桶并启用静态网站托管
bucket = aws.s3.BucketV2(
    "bucket",
    # website={
    #     "index_document": index_document,
    #     "error_document": error_document,
    # },
)
bucket_website = aws.s3.BucketWebsiteConfigurationV2(
    "bucket",
    bucket=bucket.bucket,
    index_document={"suffix": index_document},
    error_document={"key": error_document},
)
Enter fullscreen mode Exit fullscreen mode

这会创建一个 S3 存储桶并将其设置为静态网站托管。它还指定了索引文档和错误文档。该类bucketV2不再接受website将存储桶配置为网站的参数,因为这会导致错误。相反,该类BucketWebsiteConfigurationV2用于将存储桶配置为网站托管。

  • 设置存储桶所有权控制
ownership_controls = aws.s3.BucketOwnershipControls(
    "ownership-controls",
    bucket=bucket.bucket,
    rule={
        "object_ownership": "ObjectWriter",
    },
)
Enter fullscreen mode Exit fullscreen mode

这将设置权限,定义新上传对象的所有者。“ObjectWriter”表示上传者保留所有权。我稍后会修改此设置以增强安全性。

  • 允许公共访问对象
public_access_block = aws.s3.BucketPublicAccessBlock(
    "public-access-block",
    bucket=bucket.bucket,
    block_public_acls=False,
)
Enter fullscreen mode Exit fullscreen mode

通过关闭存储桶的公共 ACL 阻止功能,可以公开读取对象。换句话说,它将存储桶公开,以便任何人都可以访问它。但是,出于安全最佳实践的考虑,我们不建议这样做,并且稍后会进行调整,因为它允许任何人通过 HTTP 不安全地访问我们的静态文件。

  • 将静态文件上传到 S3 bucket
bucket_folder = synced_folder.S3BucketFolder(
    "bucket-folder",
    acl="public-read",
    bucket_name=bucket.bucket,
    path=path,
    opts=pulumi.ResourceOptions(depends_on=[ownership_controls, public_access_block]),
)
Enter fullscreen mode Exit fullscreen mode

这用于pulumi_synced_folder将位于 的静态文件上传path到 S3 存储桶。它将公共读取 ACL 设置应用于上传的对象,允许公共用户访问网站文件。它确保ownership_controlspublic_access_block优先设置。此顺序非常重要,可以避免权限冲突,并确保对象 ACL 与存储桶所有权设置一致。

  • 创建 CloudFront CDN 用于缓存和分发
cdn = aws.cloudfront.Distribution(
    "cdn",
    enabled=True,
    origins=[
        {
            "origin_id": bucket.arn,
            "domain_name": bucket_website.website_endpoint,
            "custom_origin_config": {
                "origin_protocol_policy": "http-only",
                "http_port": 80,
                "https_port": 443,
                "origin_ssl_protocols": ["TLSv1.2"],
            },
        }
    ],
Enter fullscreen mode Exit fullscreen mode

这将创建一个 CloudFront 分发版,使用 CDN 为网站提供服务。设置为S3 存储桶的网站端点,并使用“仅 http”协议,因为 S3 网站托管本身不支持 HTTPS。这意味着 CloudFront 将在内部通过 HTTP 从 S3 获取静态文件。

  • 配置 CloudFront 缓存行为
default_cache_behavior={
    "target_origin_id": bucket.arn,
    "viewer_protocol_policy": "redirect-to-https",
    "allowed_methods": [
        "GET",
        "HEAD",
        "OPTIONS",
    ],
    "cached_methods": [
        "GET",
        "HEAD",
        "OPTIONS",
    ],
    "default_ttl": 600,
    "max_ttl": 600,
    "min_ttl": 600,
    "forwarded_values": {
        "query_string": True,
        "cookies": {
            "forward": "all",
        },
    },
},
Enter fullscreen mode Exit fullscreen mode

此设置强制用户始终使用 HTTPS(重定向到 https)通过 CloudFront 访问网站。它还包含请求方法、Cookie、缓存规则和 TTL(缓存时间)设置。默认 TTL 会将静态文件缓存 600 秒(10 分钟),适用于经常更改的内容。

例如,如果您的静态网站仍处于开发的早期阶段,并且您每天发布多个版本,则此设置效果很好。如果您的静态网站更稳定且很少更新,则请使用 3600 秒(1 小时)或 86400 秒(24 小时)。

如果您使用较长的 TTL 并更新存储桶中的任意或所有对象,CloudFront 会提供使特定或所有对象的缓存失效的选项。这可确保您的最新更改对用户可见。

  • 配置 CloudFront 错误处理和安全
price_class="PriceClass_100",
custom_error_responses=[
    {
        "error_code": 404,
        "response_code": 404,
        "response_page_path": f"/{error_document}",
    }
],
restrictions={
    "geo_restriction": {
        "restriction_type": "none",
    },
},
viewer_certificate={
    "cloudfront_default_certificate": True,
},
Enter fullscreen mode Exit fullscreen mode

PriceClass_100限制 CloudFront 发行版使用美国、加拿大和欧洲的边缘站点,以节省成本。如果您需要为用户提供更广泛的覆盖范围,可以考虑其他 CloudFront 价格等级。

S3 存储桶中的 404 错误通过提供errorDocument(error.html) 进行管理。您可以使用 geo_restriction 来控制区域访问,或确保您的内容在全球范围内可用。CloudFront 使用其默认 SSL 证书,允许公共用户通过 HTTPS 安全地访问您的内容。

  • 导出公共 URL 和端点
pulumi.export("originURL", pulumi.Output.concat("http://", bucket_website.website_endpoint))
pulumi.export("originHostname", bucket.website_endpoint)
pulumi.export("cdnURL", pulumi.Output.concat("https://", cdn.domain_name))
pulumi.export("cdnHostname", cdn.domain_name)
Enter fullscreen mode Exit fullscreen mode

这将导出 S3 和 CloudFront 访问新部署的静态网站所需的 URL 和主机名。

步骤 3:部署静态网站

现在我们了解了基础设施代码的工作原理,让我们部署基础设施并访问示例静态网站。

  • 运行pulumi up。此命令用于验证并设置基础架构。验证成功后,它会确认我是否要部署基础架构。
    普鲁米·克利

  • 选择“是”后,Pulumi 会部署基础架构。我可以在 Pulumi 控制台上跟踪已部署的堆栈。控制台提供了一个易于使用的仪表板,我可以在其中监控部署、查看日志(更新)以及查看已部署到堆栈的资源。
    普鲁米·克利
    pulumi 控制台

  • CloudFront 发行版的创建耗时最长。部署完成后,我将能够使用 S3 通过 HTTP 访问示例静态网站,originURL并使用 CloudFront 通过 HTTPS 访问示例静态网站cdnURL
    示例静态网站

  • 通过在 URL 中添加不存在的路径来查看错误页面,例如https://cloudfronturl.net/nonexistent-page

在部署过程中,Pulumi 显示一条警告:“website_endpoint 已弃用”。这website_endpoint是一个不再受支持的输出属性。控制台上的 Pulumi Co-Pilot 有助于修复此警告。

为了解决这个问题,我按照 Pulumi Co-Pilot 的建议,在代码底部的 CDN 配置和导出部分中,将website_endpoint属性替换为属性。然后,我使用命令确认警告已解决。bucket_regional_domain_namepulumi preview

要将任何自定义网站部署到 S3,您需要更新堆栈文件 () 中的路径pulumi.dev.yaml以指向静态网站文件的位置。

我想通过将自己构建的 React 应用程序部署到 S3 存储桶来让事情变得更有趣。我将react_website/文件夹移动到了代码库的根目录。包含静态文件的生产版本位于该react_website/build/目录中。开发栈中的配置已更新,用于部署 React 应用程序。

config:
  aws:region: us-east-1
  website:indexDocument: index.html
  website:errorDocument: error.html
  website:path: ./react_website/build
Enter fullscreen mode Exit fullscreen mode
  • pulumi up命令部署了 React 应用程序。新部署的静态网站可以通过任何提供的 URL 链接访问。我通过在 URL 中添加一个不存在的路径来测试错误页面,结果一切正常。接下来的阶段就变得棘手,甚至有点混乱了。

静态反应网站

步骤 4:确保网站部署安全

这一步最具挑战性,需要进行最多的故障排除。我频繁在 Pulumi 文档和 AWS 控制台之间切换,以解决意外问题。

目前,该网站可以通过 HTTP 方式使用存储桶端点访问,也可以通过 HTTPS 方式使用 cdnURL 访问。禁用此功能BucketPublicAccessBlock会使存储桶及其对象公开访问,从而带来安全风险。我设置了一个安全部署,以确保用户只能通过 CloudFront URL 安全地访问静态网站。

为了增强安全性,我限制了存储桶的权限,以便只有 CloudFront 可以从存储桶中获取对象。我修改了public_access_block设置,阻止了所有对 S3 存储桶的公开访问。这确保了除非获得明确许可,否则任何人都无法访问 S3 存储桶或读取其中的对象。换句话说,该存储桶现在是私有的。

public_access_block = aws.s3.BucketPublicAccessBlock(
    "public-access-block",
    bucket=bucket.bucket,
    block_public_acls=True,
    block_public_policy=True,
    ignore_public_acls=True,
    restrict_public_buckets=True,
)
Enter fullscreen mode Exit fullscreen mode
  • 接下来,我将其设置object_ownershipBucketOwnerEnforced。此设置将禁用 ACL,并确保存储桶拥有者是所有对象的所有者。使用此配置,只能使用存储桶策略明确授予对对象的访问权限。
ownership_controls = aws.s3.BucketOwnershipControls(
    "ownership-controls",
    bucket=bucket.bucket,
    rule={
        "object_ownership": "BucketOwnerEnforced",
    },
)
Enter fullscreen mode Exit fullscreen mode
  • 我创建了 CloudFront 源访问身份 (OAI)。OAI 是一种虚拟身份,可以与 CloudFront 发行版关联,以保护对私有 S3 存储桶的访问。
oai = aws.cloudfront.OriginAccessIdentity("oai")
Enter fullscreen mode Exit fullscreen mode
  • custom_origin_config然后,我通过从 切换到将 OAI 连接到 CloudFront 发行版s3_origin_config。这s3_origin_config将设置 CloudFront 通过 OAI 访问 S3 存储桶。
cdn = aws.cloudfront.Distribution(
    ...
    origins=[
        {
            "origin_id": bucket.arn,
            "domain_name": bucket.bucket_regional_domain_name,
            "s3_origin_config": {
                "origin_access_identity": oai.cloudfront_access_identity_path,
            },
        }
    ],
    ...
)
Enter fullscreen mode Exit fullscreen mode
  • 我创建了一个 S3 存储桶策略,仅允许 CloudFront OAI 访问该 S3 存储桶。该策略授予有限的权限,仅允许 CloudFront 从存储桶中获取对象。以下代码创建了该策略并将其附加到存储桶。
bucket_policy = aws.s3.BucketPolicy(
    "bucketPolicy",
    bucket=bucket.id,
    policy=pulumi.Output.all(bucket.arn, oai.iam_arn).apply(lambda args: f"""{{
        "Version": "2012-10-17",
        "Statement": [
            {{
                "Effect": "Allow",
                "Principal": {{"AWS": "{args[1]}"}},
                "Action": "s3:GetObject",
                "Resource": "{args[0]}/*"
            }}
        ]
    }}""")
)
Enter fullscreen mode Exit fullscreen mode
  • 当我运行pulumi up测试新的安全更改时,所有对象都出现了“ AccessControlListNotSupported: The bucket does not allow ACLssynced_folder ”错误。出现此错误的原因是,模块将 ACL 附加到对象并将其上传到 S3 存储桶。然而,该BucketOwnerEnforced设置禁用了 ACL,这与 冲突synced_folder

尽管 Pulumi 文档明确指出synced_folderACL 属性可以设置为None,但我在执行代码时仍然遇到了要求输入值的错误。我手动编写了代码,使用bucketObjectS3 子模块中的类将对象上传到 S3 存储桶,并使用嵌套for循环遍历所有文件。Pulumi Co-Pilot 对此很有帮助。

然后我synced_folder用新生成的代码替换了该代码。

for root, _, files in os.walk(path):
    for file in files:
        file_path = os.path.join(root, file)
        key = os.path.relpath(file_path, path)

        aws.s3.BucketObject(
            key,
            bucket=bucket.bucket,
            source=pulumi.FileAsset(file_path),
            opts=pulumi.ResourceOptions(depends_on=[ownership_controls, public_access_block]),
        )
Enter fullscreen mode Exit fullscreen mode

在上面的代码中,我使用os模块获取了我机器上静态文件的路径。我在代码开头导入了该模块。

import os
Enter fullscreen mode Exit fullscreen mode

现在,当我运行pulumi up并尝试访问该网站时,出现了拒绝访问错误。检查我的 S3 存储桶后,发现好像发生了灾难——什么都没有了。于是,我决定pulumi destroy && pulumi up从头开始重建基础设施。虽然这样可以恢复对象,但拒绝访问错误仍然存​​在。

我检查了代码中的所有配置,并在控制台中仔细检查了 CloudFront 和 S3 存储桶的设置,但一切似乎都正确无误。在第三次查看 CloudFront 分发控制面板时,我注意到该default root object选项尚未设置。这似乎不是什么问题,因为它是可选的,而且即使没有它,设置也能正常工作,直到我设置了 OAI。设置 OAI 后,一些可选设置就会变成必需的。

告诉default root objectCloudFront 当用户访问 CloudFront URL 时返回哪个对象,在本例中是index.html。我向 Pulumi Copilot 询问了正确的参数,以便在 CDN 配置中进行设置。

cdn = aws.cloudfront.Distribution(
    "cdn",
    enabled=True,
    default_root_object="index.html",
    ...
)
Enter fullscreen mode Exit fullscreen mode

我部署了新的更改,希望一切顺利。但是,当我尝试使用 CloudFront URL 访问网站时,一个名为“download”且没有扩展名的奇怪文件被下载到我的电脑上。我立即删除了它。一时间,我担心自己下载了恶意软件,担心我的安全网站在刚刚设置了增强安全措施后就受到了攻击。

我意识到我需要了解基础设施中发生的情况,以便回答诸如此类的问题:我的请求是否到达了 CloudFront 分发版?它成功了吗?它是否返回了错误?如果是,错误是什么?

我切换到 CloudFront 仪表板,导航到日志记录选项卡,并为 CloudFront 设置一个日志记录目标,以将日志发送到我在上一个项目中为 Lambda 函数创建的 CloudWatch 日志组。

AWS CloudFront 控制台

然后,我尝试再次访问该链接,以便记录请求和响应,从而收集信息来排查此意外行为。我从 CloudWatch 访问了日志,但无法立即理解其中的含义。我将其粘贴到 Pulumi Copilot 中进行分析。

通过 Pulumi Copilot 的回复,我意识到我用于上传对象的代码没有为它们设置正确的文件类型。相反,它将所有对象的文件类型都设置为 ,application/octet-stream而不是text/html,而这正是显示 index.html 所必需的。

pulumi副驾驶控制台

这就解释了为什么会下载一个奇怪的文件,我怀疑这是我的index.html,我可以自信地打开它来确认。

我再次向 Pulumi Copilot 询问解决方案,以在for上传对象的嵌套循环代码中设置文件类型。

import mimetypes
...

for root, _, files in os.walk(path):
    for file in files:
        file_path = os.path.join(root, file)
        key = os.path.relpath(file_path, path)
        content_type, _ = mimetypes.guess_type(file_path)
        print(f"Uploading: {file_path} as {key}")

        aws.s3.BucketObject(
            key,
            bucket=bucket.bucket,
            source=pulumi.FileAsset(file_path),
            content_type=content_type or "application/octet-stream",
            opts=pulumi.ResourceOptions(depends_on=[ownership_controls, public_access_block]),
        )
Enter fullscreen mode Exit fullscreen mode

嵌套循环现在使用mimetypes模块为每个上传的对象设置正确的文件类型。我运行了pulumi up,它终于成功了,或者说我以为如此。我的 React 静态网站现在可以通过 CloudFront URL 链接安全访问。当我尝试访问源 URL 时,它返回了访问被拒绝的错误,这是意料之中的。然而,当我尝试使用 CloudFront URL 访问错误页面时,我也收到了访问被拒绝的错误,这出乎意料。

访问被拒绝的页面

因此,我求助于值得信赖的旧版故障排除库(Stack Overflow)。我发现,由于 CloudFront 和 S3 存储桶都受 OAI 保护,因此当页面不存在时,S3 存储桶会向 CloudFront 发送 403 错误代码,而不是 404。我调整了 CloudFront 设置,使其在从 S3 存储桶收到 403 错误代码时显示错误页面并向用户发送 404 错误响应。

...
custom_error_responses=[
    {
        "error_code": 403,
        "response_code": 404,
        "response_page_path": f"/{error_document}",
    }
]
...
Enter fullscreen mode Exit fullscreen mode

我部署了最新的更改,现在可以访问静态网站的主页和错误页面了。下一步是使用自定义域名进行个性化设置。

静态网站错误页面

设置自定义域

我和朋友们分享 CloudFront URL 来访问我安全、快速、美观的静态网站,感觉很无聊。我想要一个自定义域名,能够体现我的个人特色和网站的主题。我按照 Pulumi 文档中的步骤为静态网站设置了一个自定义域名。

步骤包括:

  • 将域和子域配置添加到堆栈。

  • 将新配置导入我的代码。

  • 使用 ACM 创建并验证新的 SSL/TLS 证书。我使用了 DNS 验证方法,该方法涉及在托管区域中创建 DNS 记录以确认域所有权。

  • 调整 CloudFront 代码以使用参数处理自定义域的请求aliases

  • 创建 Route 53 别名 A 记录,将我们的子域映射到 CloudFront 域。

您可能想知道为什么我们使用别名 A 记录而不是 CNAME 记录(后者通常用于将一个域映射到另一个域)。建议使用 Route 53 别名 A 记录将域映射到 CloudFront 等 AWS 服务。使用别名 A 记录,DNS 查询将在内部解析,从而提高速度和效率。

设置自定义域名后,我就能使用个性化域名访问我的静态网站了。当我为 React 应用程序创建新的生产版本时,只需运行 即可pulumi up。Pulumi 会通过自动部署到安全的 S3 存储桶来处理剩下的工作。

具有自定义域的静态网站

使用 Pulumi

这是我第一次使用 Pulumi 作为 IAC 工具,我发现它非常棒。Pulumi 允许我使用 Python,这是我最喜欢的编程语言。这让我感觉 Pulumi 很熟悉,就像是我工作流程的一部分一样。与其他 IAC 工具一样,Pulumi 省去了我点击 AWS 控制台设置基础设施的麻烦,并允许我开发可重复使用的基础设施。但与其他 IAC 工具不同的是,Pulumi 允许我立即实现自动化,而无需先手动配置远程状态文件。

Pulumi 控制台和 Pulumi Copilot(我在这个项目中的个人助理)在故障排除、参考和成功完成这个项目方面非常有帮助。Pulumi 控制台提供了我所做更改的更新(日志),让我可以轻松跟踪更改并参考过去的执行结果。Pulumi Copilot,我的智能个人助理,帮助我修复了已弃用的参数,生成了存储桶策略,分析了日志,并提供了可能的解决方案。

事实上,当我想保护我的静态网站基础设施时,我复制了模板提供的全部代码,并向 Pulumi Copilot 发出提示,要求设置一个安全的网站基础设施。这简化了流程,让我可以专注于使用 Pulumi 文档调整和审查代码,并进行一些调整。拥有一个能够理解我正在使用的基础设施工具的 AI 助手真是太棒了。它让基础设施的配置变得轻而易举。

以下是我的一些关键提示:

提示 1

can you adjust this code to set the content type for each file uploaded? 

for root, _, files in os.walk(path):
    for file in files:
        file_path = os.path.join(root, file)
        key = os.path.relpath(file_path, path)

        aws.s3.BucketObject(
            key,
            bucket=bucket.bucket,
            source=pulumi.FileAsset(file_path),
        )
Enter fullscreen mode Exit fullscreen mode

提示 2

The option acl=None is not working. It still ask for acl value on runtime. Is there any good alternative to S3BucketFolder? 

bucket_folder = synced_folder.S3BucketFolder(
    "bucket-folder",
    acl=None,
    bucket_name=bucket.bucket,
    path=path,
    opts=pulumi.ResourceOptions(depends_on=[ownership_controls, public_access_block]),
)
Enter fullscreen mode Exit fullscreen mode

提示 3

this is the cloudfront log I got from cloudwatch. Any reason why I can't access my static files via cloudfront URL after using OAI?

{
    "date": "2025-04-02",
    "time": "18:01:10",
    "x-edge-location": "LOS50-P2",
    "sc-bytes": "594",
    "c-ip": "102.89.44.119",
    "cs-method": "GET",
    "cs(Host)": "d39ymmysanotpr.cloudfront.net",
    "cs-uri-stem": "/",
    "sc-status": "200",
    "cs(Referer)": "-",
    "cs(User-Agent)": "Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/135.0.0.0%20Safari/537.36",
    "cs-uri-query": "-",
    "cs(Cookie)": "-",
    "x-edge-result-type": "Miss",
    "x-edge-request-id": "-lB3Ff7SJ8HEdpSZeS8d8AwV8kjflOVlQdgaI_-yqDYQ-TenMjY2nA==",
    "x-host-header": "d39ymmysanotpr.cloudfront.net",
    "cs-protocol": "https",
    "cs-bytes": "486",
    "time-taken": "0.480",
    "x-forwarded-for": "-",
    "ssl-protocol": "TLSv1.3",
    "ssl-cipher": "TLS_AES_128_GCM_SHA256",
    "x-edge-response-result-type": "Miss",
    "cs-protocol-version": "HTTP/2.0",
    "fle-status": "-",
    "fle-encrypted-fields": "-",
    "c-port": "17055",
    "time-to-first-byte": "0.480",
    "x-edge-detailed-result-type": "Miss",
    "sc-content-type": "application/octet-stream",
    "sc-content-len": "238",
    "sc-range-start": "-",
    "sc-range-end": "-"
}
Enter fullscreen mode Exit fullscreen mode

请注意,此提交于 4 月 6 日太平洋夏令时间晚上 8 点 42 分发布。

参考链接

感谢您的阅读。我们非常欢迎您提出建议和反馈,请在评论区留言。

鏂囩珷鏉ユ簮锛�https://dev.to/drintech/secure-and-fast-static-website-deployment-on-aws-using-pulumi-3bi5
PREV
MongoDB 指南 - 入门
NEXT
我从特种部队训练中学到的经验教训,并运用到软件开发中