实现站点弹性伸缩
| 项目 | 说明 |
|---|---|
| 文档难度 | 入门级 |
| 预计耗时 | 60-90 分钟 |
| 任务目标 | 部署可自动扩缩的 Grav 站点,实现高可用和负载均衡 |
| 使用环境 | AWS 管理控制台 |
前置条件:
- 已有 AWS 账户并具备控制台登录权限
- 已有 VPC,包含至少 2 个可用区的公有子网
- 已有可用的安全组(允许 HTTP 80、SSH 22 入站)
%%{init: {"flowchart": {"defaultRenderer": "elk"}}}%%
flowchart TB
subgraph Internet
U[用户请求]
end
subgraph AWS["AWS Cloud"]
subgraph VPC
ALB[Application Load Balancer]
subgraph AZ1["可用区 A"]
EC2_1[EC2 实例<br/>Grav 核心代码]
EFS_MT1[EFS 挂载点]
end
subgraph AZ2["可用区 B"]
EC2_2[EC2 实例<br/>Grav 核心代码]
EFS_MT2[EFS 挂载点]
end
EFS[(Amazon EFS<br/>/user 目录)]
ASG[Auto Scaling Group]
end
CF[CloudFront CDN<br/>静态资源缓存]
end
U --> CF
CF --> ALB
ALB --> EC2_1
ALB --> EC2_2
EC2_1 --> EFS_MT1
EC2_2 --> EFS_MT2
EFS_MT1 --> EFS
EFS_MT2 --> EFS
ASG -.管理.-> EC2_1
ASG -.管理.-> EC2_2
架构说明:
| 组件 | 作用 |
|---|---|
| CloudFront | 缓存静态资源,减少 EFS 读取,降低延迟 |
| ALB | 接收请求,分发到健康实例,执行健康检查 |
| Auto Scaling Group | 根据策略自动增加或终止实例 |
| EC2 实例 | 运行 PHP + Grav 核心代码(本地存储) |
| EFS | 仅存储 /user 目录(内容、配置、插件),跨实例共享 |
流量路径:
用户 → CloudFront → ALB → EC2 实例(本地 Grav 代码 + EFS /user 目录)
自动扩缩机制:
- 扩展:负载升高或到达指定时间,ASG 启动新实例,新实例自动挂载 EFS、注册到 ALB
- 缩减:负载下降或到达指定时间,ASG 先从 ALB 移除实例,等待连接排空后终止实例
扩展策略选择(二选一)
Section titled “扩展策略选择(二选一)”在开始配置前,根据业务场景选择一种扩展策略:
| 策略 | 适用场景 | 示例 |
|---|---|---|
| Target Tracking | 流量不可预测,需根据实时负载调整 | 新闻站点、API 服务 |
| Scheduled Scaling | 流量有固定规律 | 工作日高峰、促销活动 |
最佳实践:如果不确定流量模式,选择 Target Tracking。
1. 创建 EFS 文件系统
Section titled “1. 创建 EFS 文件系统”Amazon EFS 用于存储 Grav 的 /user 目录,实现跨实例内容共享。
- 进入 EFS 控制台 → 点击 Create file system
[创建EFS-入口]
-
点击 Customize 进入详细配置
-
步骤 1 - 文件系统设置:
- Name: 输入名称(如
grav-user-data) - Storage class: 选择 Standard
- Automatic backups: 根据需要启用
- Encryption: 建议启用
- Name: 输入名称(如
-
步骤 2 - 网络访问:
- VPC: 选择目标 VPC
- Mount targets: 在每个可用区选择子网,选择允许 NFS(端口 2049)入站的安全组

- 完成创建,记录 File system ID(如
fs-0123456789abcdef0)
2. 启动基准 EC2 实例
Section titled “2. 启动基准 EC2 实例”创建一台 EC2 实例,安装 PHP、Grav,配置 EFS 挂载,作为自定义 AMI 的基础。
-
进入 EC2 控制台 → 点击 Launch instances
-
配置实例:
- Name:
grav-base-instance - AMI: 选择 Amazon Linux 2023
- Instance type: 选择
t3.small或更高 - Key pair: 选择已有密钥对
- Network settings: 选择目标 VPC 和公有子网,启用公有 IP
- Security groups: 选择允许 HTTP 80、SSH 22、NFS 2049 的安全组

- Name:
-
启动实例并通过 SSH 连接
3. 安装 PHP 和 Grav
Section titled “3. 安装 PHP 和 Grav”在基准实例上安装运行环境和 Grav CMS。
Amazon Linux 2023 安装步骤
Section titled “Amazon Linux 2023 安装步骤”通过 SSH 连接实例后,执行以下操作:
关闭selinux
sudo sed -i 's/SELINUX=permissive/SELINUX=disabled/g' /etc/selinux/configsudo setenforce 0安装 PHP 和必要扩展:
进入实例终端,安装 PHP 8.x 及 Grav 所需的扩展(curl、gd、mbstring、xml、zip 等),安装 Apache 或 Nginx 作为 Web 服务器。
sudo yum update -ysudo yum install -y php php-curl php-gd php-json php-mbstring php-xml php-zip php-apcu nginx php-fpm unzipsudo systemctl disable --now httpd安装 Grav:
从 Grav 官方下载页面 下载最新版本,解压到 Web 服务器根目录(如 /var/www/grav)。
# 解压并移动到正确的目录wget https://github.com/getgrav/grav/releases/download/1.7.49.5/grav-admin-v1.7.49.5.zipunzip grav-admin-v1.7.49.5.zipsudo mv grav-admin /var/www/grav
# 配置权限sudo chown -R nginx:nginx /var/www/gravsudo chmod -R 775 /var/www/gravsudo mkdir -p /var/lib/php/sessionsudo chown -R nginx:nginx /var/lib/php/sessionsudo chmod -R 733 /var/lib/php/session配置 OPcache(关键性能优化):非必要
编辑 PHP 配置文件/etc/php/8.3/fpm/php.ini,启用并优化 OPcache:
| 配置项 | 建议值 | 说明 |
|---|---|---|
opcache.enable | 1 | 启用 OPcache |
opcache.memory_consumption | 256 | 缓存内存大小(MB) |
opcache.validate_timestamps | 0 | 禁用时间戳验证,生产环境必须 |
opcache.revalidate_freq | 0 | 配合上条使用 |
安装 APCu 用户缓存:非必要
安装 php-pecl-apcu 扩展,Grav 会自动检测并使用。上面的命令已经包含。
配置Nginx
添加grav.conf文件;创建/etc/nginx/conf.d/grav.conf文件
server { listen 80; server_name 服务器IP或域名;
root /var/www/grav; index index.php index.html;
# 日志 access_log /var/log/nginx/grav-access.log; error_log /var/log/nginx/grav-error.log;
# Grav 后台 location /admin { try_files $uri $uri/ /index.php?$query_string; }
# 主路由 location / { try_files $uri $uri/ /index.php?$query_string; }
# PHP 处理 location ~ \.php$ { try_files $uri =404; fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass unix:/run/php-fpm/www.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; }}修改/etc/php-fpm.d/www.conf
user = nginxgroup = nginx启动服务
sudo systemctl enable --now nginxsudo systemctl enable --now php-fpm验证安装:
通过浏览器访问实例公有 IP,确认 Grav 首页正常显示。注意要用http://。


4. 配置 EFS 挂载
Section titled “4. 配置 EFS 挂载”将 EFS 挂载为 Grav 的 /user 目录。
安装 EFS 挂载助手:
安装 amazon-efs-utils 软件包,该工具简化 EFS 挂载并支持加密传输。
sudo yum install amazon-efs-utils -y迁移现有 /user 目录内容:
# 将/var/www/grav/user目录移动到/usercd /var/www/gravsudo mv user /tmp
# 挂载EFS到/var/www/grav/usersudo mkdir usersudo mount -t efs fs-08db381dd79fa11c8 user
# 拷回文件sudo cp -r /tmp/user/* user/
# 重新授权sudo chown -R nginx:nginx usersudo chmod -R 775 user配置永久挂载:
编辑 /etc/fstab,添加 EFS 挂载配置,确保实例重启后自动挂载。
挂载目标为 Grav 的 /user 目录路径(如 /var/www/grav/user)。
挂载选项建议:
| 选项 | 说明 |
|---|---|
tls | 启用传输加密 |
_netdev | 标记为网络文件系统,确保网络就绪后再挂载 |
noresvport | 推荐选项,提高连接稳定性 |
nofail | 挂载失败继续启动系统 |
fs-08db381dd79fa11c8 /var/www/grav/user efs _netdev,noresvport,nofail 0 0
查看挂载
mount -l | grep grav# 或者df -TH验证挂载:
测试大文件数据写入
# 写入 1GB 文件到 EFSdd if=/dev/zero of=/var/www/grav/user/test.img bs=1M count=1024 status=progress
# 参数说明:# if=/dev/zero - 输入源(零数据)# of=/var/www/grav/user/test.img - 输出文件(EFS 挂载点)# bs=1M - 块大小 1MB# count=1024 - 写入 1024 个块 = 1GB# status=progress - 显示进度测试小文件写入
# 写入 100MB(快速测试)dd if=/dev/zero of=/var/www/grav/user/test-small.img bs=1M count=100 status=progress为了确保严谨,可以重启实例,确认 EFS 自动挂载,Grav 站点正常访问。
Section titled “为了确保严谨,可以重启实例,确认 EFS 自动挂载,Grav 站点正常访问。”5. 创建自定义 AMI
Section titled “5. 创建自定义 AMI”将配置好的实例保存为自定义 AMI,用于 Auto Scaling 启动新实例。
sudo cloud-init clean --logs --seed && \sudo rm -rf /var/lib/cloud/instances/* && \sudo truncate -s 0 /etc/machine-id && \sudo shred -u /etc/ssh/*_key* && \history -c && \cat /dev/null > ~/.bash_history && \sudo cat /dev/null > /root/.bash_history && \sudo poweroff-
进入 EC2 控制台 → 选择基准实例
-
点击 操作 → 映像和模板 → 创建映像

-
配置镜像:
- 映像名称: 输入名称(如
grav-ami-v1.0) - 映像描述: 输入描述(如
Grav + PHP 8.x + EFS mount) - 不重启: 不勾选(确保文件系统一致性)
- 映像名称: 输入名称(如
-
点击 创建映像
-
等待 AMI 状态变为 Available(约 5-10 分钟)

6. 创建 目标群组
Section titled “6. 创建 目标群组”Target Group 定义后端实例和健康检查规则。
-
进入 EC2 控制台 → 左侧导航栏选择负载平衡 -> 目标组 → 点击 创建目标组
-
步骤 1 - 基本配置:
- 目标类型: 选择 实例
- 目标组名称: 输入名称(如
grav-tg) - 协议/端口: HTTP / 80
- VPC: 选择目标 VPC
-
步骤 2 - 健康检查:
- 健康检查协议: HTTP
- 健康检查路径:
/或 Grav 的健康检查端点
健康检查建议配置:
| 参数 | 建议值 | 说明 |
|---|---|---|
| Healthy threshold | 2 | 连续 2 次成功判定为健康 |
| Unhealthy threshold | 3 | 连续 3 次失败判定为不健康 |
| Timeout | 5 秒 | 响应超时时间 |
| Interval | 30 秒 | 检查间隔 |

- 点击 下一步,暂不注册目标(ASG 会自动注册),点击 创建目标组
7. 创建 Application Load Balancer
Section titled “7. 创建 Application Load Balancer”Application Load Balancer 分发流量到后端实例。
-
进入 EC2 控制台 → 左侧导航栏选择 负载平衡 → 点击 负载均衡器
-
选择 Application Load Balancer → 点击 创建
[创建ALB-选择类型]
-
基本配置:
- 负载均衡器名称: 输入名称(如
grav-alb) - 模式: 选择 面向互联网
- IP address type: IPv4
- 负载均衡器名称: 输入名称(如
-
网络映射:
- VPC: 选择目标 VPC
- Mappings: 选择至少 2 个可用区及其公有子网

-
安全组:选择允许 HTTP 80(和 HTTPS 443,如需要)入站的安全组
-
监听器和路由:
- 协议 / 端口: HTTP / 80
- 默认操作: 转发到目标组 → 选择上一步创建的目标组

-
点击 创建负载均衡器
-
等待 ALB 状态变为 Active,记录 DNS name

将此DNS记录作为域名解析的cname值。
8. 创建启动模板
Section titled “8. 创建启动模板”启动模板 定义 Auto Scaling 启动新实例的配置。
- 进入 EC2 控制台 → 左侧导航栏选择实例 -> 启动模板 → 点击 创建启动模板
[创建启动模板-入口]
-
基本信息:
- 启动模板名称: 输入名称(如
grav-launch-template) - 模板版本说明:
v1.0 - Initial version
- 启动模板名称: 输入名称(如
-
AMI:选择步骤 5 创建的自定义 AMI

-
实例类型: 选择
t3.small或根据负载选择 -
密钥对: 选择已有密钥对
-
网络设置:不选择(将自动在之前选择的多个可用去中随机创建)
- 安全组: 选择允许 HTTP、SSH、NFS 的安全组
- 高级网络设置:自动分配共有IP:禁用
-
高级详细信息:(可选)
- IAM实例配置文件:如需 CloudWatch 监控或 Systems Manager,选择相应角色
CloudWatchAgentServerPolicy:CloudWatch代理所需的权限策略AmazonSSMManagedInstanceCore:SSM代理所需的权限策略
- IAM实例配置文件:如需 CloudWatch 监控或 Systems Manager,选择相应角色
-
点击 创建启动模板
9. 创建 Auto Scaling Group
Section titled “9. 创建 Auto Scaling Group”Auto Scaling Group 管理实例集合,实现自动扩缩。
-
进入 EC2 控制台 → 左侧导航栏选择 Auto Scaling Groups → 点击 Create Auto Scaling group
-
步骤 1 - 选择启动模板:
- 弹性伸缩组名称: 输入名称(如
grav-asg) - 启动模板: 选择上一步创建的启动模板
- 弹性伸缩组名称: 输入名称(如

- 步骤 2 - 选择实例启动选项:
- VPC: 选择目标 VPC
- 可用区分布: 选择至少 2 个可用区的子网

- 步骤 3 - 配置高级选项:
- 负载均衡: 选择 附加到现有负载均衡器
- 选择负载均衡的目标组: 选择之前创建的目标组
- 应用程序恢复控制器(ARC)可用区转移:可选
- 健康检查类型: 选择 弹性负载均衡(推荐)
- 健康检查周期: 300 秒

- 步骤 4 - 配置组大小:
| 参数 | 建议值 | 说明 |
|---|---|---|
| 所需容量 | 2 | 正常运行实例数 |
| 最小容量 | 2 | 保证高可用 |
| 最大容量 | 6 | 预留扩展空间 |
- 步骤 5 - 配置扩展策略:根据你选择的策略配置(见下节)
- 添加通知:在SNS中创建订阅用于接收通知的邮件(略过)
- 完成创建
10. 配置扩展策略
Section titled “10. 配置扩展策略”根据业务场景,选择一种策略配置。
选项 A:目标跟踪(推荐)
Section titled “选项 A:目标跟踪(推荐)”适用于流量不可预测的场景。
- 在 ASG 详情页 → 选择 弹性伸缩 标签页;区域点击 添加策略

-
配置:
- 策略类型: Target tracking scaling
- 伸缩策略名称:
grav-cpu-target-tracking - 指标类型: 平均CPU使用率
- 目标值: 70
-
实例预热: 300 秒(PHP 应用和 EFS 挂载需要时间)
-
点击 创建
选项 B:计划伸缩
Section titled “选项 B:计划伸缩”适用于流量有固定规律的场景。
-
在 ASG 详情页 → 选择 弹性伸缩 标签页
-
在 计划操作 区域点击 创建计划操作

- 配置扩容计划(示例:工作日早高峰):
- 名称:
scale-out-weekday-morning - 所需容量: 4
- 最小值: 4
- 最大值: 6
- 循环: Corn
0 8 * * MON-FRI - 时区: 选择你的时区
- 开始日期:自选
- 开始时间:自选
- 结束日期:自选
- 结束时间:自选
- 名称:

- 创建缩容计划(示例:工作日晚间)同上:
- Name:
scale-in-weekday-evening - Desired capacity: 2
- Min: 2
- Max: 6
- Recurrence:
0 20 * * MON-FRI - Time zone: 选择你的时区
- Name:
11. 验证配置
Section titled “11. 验证配置”| 检查项 | 验证方法 | 预期结果 |
|---|---|---|
| ALB 状态 | 查看 Load Balancer 详情页 | 状态为 Active |
| 目标组 健康状态 | 查看 目标组 → Targets 标签页 | 所有实例显示 healthy |
| ASG 实例数量 | 查看 Auto Scaling Group 详情页 | 实例数量符合 所需值 |
| Grav 站点访问 | 浏览器访问 ALB DNS name | 正常显示 Grav 首页 |
| EFS 内容共享 | 在一个实例修改内容,从另一个实例访问 | 内容同步 |

压测CPU实现扩容
sudo yum install -y stress-ng基础压测
# 压满所有 CPU 核心,持续 5 分钟stress-ng --cpu 0 --timeout 5m
# 压满 2 个 CPU 核心stress-ng --cpu 2 --timeout 5m
# 压到指定百分比(80%)stress-ng --cpu 0 --cpu-load 80 --timeout 5m
# 查看实时 CPUtop # 按 1 查看每个核心htop # 更直观(需要安装)高级压测
# 逐步增加负载(模拟真实流量)stress-ng --cpu 1 --timeout 2m &sleep 120stress-ng --cpu 2 --timeout 2m &sleep 120stress-ng --cpu 4 --timeout 2m &
# 脉冲式负载(模拟突发流量)for i in {1..10}; do stress-ng --cpu 0 --timeout 30s sleep 60done还可以:
- 手动调整 ASG 的 所需容量,观察实例增减
- 确认新实例自动注册到 目标群组
- 确认终止的实例从 目标群组 移除
12. 更新 Grav 代码
Section titled “12. 更新 Grav 代码”当需要更新 Grav 核心代码或 PHP 配置时,采用不可变基础设施方式。
%%{init: {"flowchart": {"defaultRenderer": "elk"}}}%%
flowchart LR
A[启动新实例<br/>基于当前 AMI] --> B[更新 Grav 代码]
B --> C[测试验证]
C --> D[创建新版本 AMI]
D --> E[更新启动模板]
E --> F[ASG 实例刷新]
F --> G[旧实例逐步替换]
1. 启动临时实例
从当前 AMI 启动一台新实例用于更新。
2. 更新 Grav 代码
通过 SSH 连接实例,执行 Grav 更新(下载新版本、替换文件、清理缓存)。
3. 测试验证
确保更新后的 Grav 正常运行。
4. 创建新版本 AMI
5. 更新启动模板
- 进入 启动模板 → 选择启动模板
- 点击 操作 → 修改模板 (创建新版本)
- 选择新的 AMI
- 创建新版本


6. 更新 ASG 使用新模板版本
- 进入 ASG 详情页
- 点击 Edit
- 在 Launch template 中选择 Latest 或指定新版本
- 保存


7. 执行实例刷新
- 在 ASG 详情页 → 实例刷新 标签页
- 点击 启动实例刷新
- 配置:
- 设置运行正常百分比: 50(保证一半实例在线)
- 实例预热: 300 秒
- 启动刷新

ASG 会逐步终止旧实例并启动新实例,确保服务不中断。
8. 清理
- 终止临时更新实例
- 保留旧版本 AMI 用于回滚(建议保留最近 2-3 个版本)
新实例启动后无法访问 Grav
Section titled “新实例启动后无法访问 Grav”检查 EFS 挂载是否成功。通过 SSH 连接实例,检查 /var/www/grav/user 目录是否包含内容。如果为空,检查 /etc/fstab 配置和 EFS 安全组是否允许 NFS 流量。
目标群组 显示实例 unhealthy
Section titled “目标群组 显示实例 unhealthy”可能原因:
- Health check grace period 过短,实例未完成启动
- 安全组未允许 ALB 到实例的 HTTP 流量
- Grav 应用未正常运行
检查实例的 Web 服务状态和安全组入站规则。
EFS 性能较差
Section titled “EFS 性能较差”确保已配置:
- PHP OPcache(
opcache.validate_timestamps = 0) - APCu 用户缓存
- 仅将
/user目录放在 EFS,核心代码保持本地
实例刷新过程中服务中断
Section titled “实例刷新过程中服务中断”增加 设置运行正常百分比(如设为 90%),确保更多实例保持在线。同时检查 Health check grace period 是否足够。
内容更新后部分实例显示旧内容
Section titled “内容更新后部分实例显示旧内容”Grav 的缓存可能导致此问题。登录 Grav 管理后台清理缓存,或通过 SSH 在实例上清理 /cache 目录。由于 OPcache 设置了 validate_timestamps = 0,PHP 代码变更需要重启 PHP-FPM 或 Apache。
最佳实践总结
Section titled “最佳实践总结”| 实践项 | 建议 |
|---|---|
| 多可用区部署 | ASG 跨至少 2 个可用区,EFS 在对应可用区创建挂载点 |
| 代码与数据分离 | Grav 核心代码放 AMI(本地),/user 目录放 EFS |
| 启用 OPcache | validate_timestamps = 0,生产环境必须配置 |
| 健康检查类型 | 关联 ALB 时使用 ELB 健康检查 |
| 不可变基础设施 | 通过 AMI 版本管理代码更新,便于回滚 |
| 监控告警 | 配置 CloudWatch 告警 监控 CPU、EFS 吞吐量 |
| 静态资源缓存 | 使用 CloudFront 缓存静态文件,减少 EFS 读取 |