如何修改GitHub Actions实现代码直接复制部署到AWS EFS
GitHub Actions 直接同步代码到ECS绑定EFS路径的实现方案
核心前提:EFS默认仅支持同VPC内通过NFS 2049端口挂载,GitHub托管的公网runner无法直接连通VPC内的EFS,所有可行方案均需解决公网到VPC的连通/中转问题,以下是经过生产验证的可落地方案:
方案1:通过SSM远程命令同步(推荐,无额外资源开销、安全)
不需要暴露任何VPC资源到公网,依托AWS原生权限体系完成同步,适合绝大多数场景:
- 前置配置
- ECS集群的EC2实例使用AWS官方ECS优化版AMI(默认预装SSM Agent),给实例绑定的IAM角色附加
AmazonSSMManagedInstanceCore托管策略 - 给GitHub Actions对接AWS的OIDC角色(禁止使用永久AK/SK存储在Secrets中)添加以下权限:允许调用
ecs:ListContainerInstances、ecs:DescribeContainerInstances查询集群内EC2实例,允许调用ssm:SendCommand对目标实例执行远程命令
- ECS集群的EC2实例使用AWS官方ECS优化版AMI(默认预装SSM Agent),给实例绑定的IAM角色附加
- 工作流执行逻辑
- 拉取仓库代码到runner本地
- 调用AWS CLI查询ECS集群内任意一台运行中状态的EC2实例ID
- 排除
.git目录、CI临时文件后打包代码,通过SSM将压缩包传输到目标实例,解压后覆盖EFS对应的本地绑定挂载目录
- 核心工作流代码片段
jobs: sync-code-to-efs: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - name: Checkout repository code uses: actions/checkout@v4 - name: Authenticate to AWS via OIDC uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::替换为你的AWS账号ID:role/替换为你的GitHubActionsOIDC角色名 aws-region: 替换为你的资源所在区域,比如cn-north-1 - name: Fetch active EC2 instance ID in ECS cluster id: get-ec2-instance run: | CONTAINER_INSTANCE_ARN=$(aws ecs list-container-instances --cluster 替换为你的ECS集群名 --status ACTIVE --query 'containerInstanceArns[0]' --output text) EC2_INSTANCE_ID=$(aws ecs describe-container-instances --cluster 替换为你的ECS集群名 --container-instances $CONTAINER_INSTANCE_ARN --query 'containerInstances[0].ec2InstanceId' --output text) echo "ec2_instance_id=$EC2_INSTANCE_ID" >> $GITHUB_OUTPUT - name: Sync code to EFS mount path via SSM run: | # 排除无关文件打包 tar --exclude='.git' --exclude='.github' -czf code.tar.gz . # 先将压缩包传到S3临时路径,再通过SSM拉取到实例解压,避免SSM参数长度限制 aws s3 cp code.tar.gz s3://替换为你存放临时部署包的桶路径/code.tar.gz # 远程执行解压覆盖命令,替换为你EC2实例上对接EFS的本地bind mount路径 COMMAND_ID=$(aws ssm send-command \ --instance-ids ${{ steps.get-ec2-instance.outputs.ec2_instance_id }} \ --document-name "AWS-RunShellScript" \ --parameters '{"commands":["aws s3 cp s3://替换为你存放临时部署包的桶路径/code.tar.gz /tmp/code.tar.gz","mkdir -p /tmp/code_sync","tar -xzf /tmp/code.tar.gz -C 替换为EC2上的EFS本地挂载路径,比如/var/www/html","rm -rf /tmp/code_sync /tmp/code.tar.gz","aws s3 rm s3://替换为你存放临时部署包的桶路径/code.tar.gz"]}' \ --query 'Command.CommandId' --output text) # 等待命令执行完成,校验执行结果 aws ssm wait command-executed --command-id $COMMAND_ID --instance-id ${{ steps.get-ec2-instance.outputs.ec2_instance_id }}
如果仓库文件体积较大,可以把全量压缩传输替换为rsync增量同步,减少传输耗时
方案2:VPC内部署自托管Runner(适合高频部署场景)
如果日常部署频率高,不想每次走SSM中转,可以直接在ECS集群同VPC内的EC2实例上部署GitHub自托管runner:
- 给runner所在实例配置安全组规则,允许出站访问EFS挂载点的2049端口
- 在runner实例上提前把EFS挂载到本地固定目录
- 工作流拉取代码后,直接执行
rsync -av --exclude='.git' ./* 本地EFS挂载路径即可完成同步,不需要额外中转 - 注意:自托管runner不要存储任何敏感信息,绑定的IAM角色遵循最小权限原则
方案3:公网暴露EFS直接挂载(仅测试环境可用,生产禁止)
如果是临时测试环境,可以给EFS开启公网访问端点,给GitHub公网runner的IP段开放2049端口入站规则,在runner上安装nfs-common后直接挂载EFS到本地目录,再复制文件。该方案会将存储暴露在公网,存在极高的数据泄露、篡改风险,生产环境绝对不要使用
落地注意事项
- 同步代码前建议自动备份EFS上的上一版代码,避免覆盖出错导致服务中断
- 代码同步完成后,可以通过SSM给Web服务容器发送重载信号,不需要重启整个ECS服务即可加载新代码
- 所有AWS权限配置遵循最小权限原则,禁止给角色添加超出需求的服务访问权限
内容的提问来源于stack exchange,提问作者Mr. Oleg
相关产品推荐
相关产品推荐

