logo

简介

什么是 Vapor?

Laravel Vapor 是一个基于 AWS Lambda 的 Laravel 自动扩展无服务器部署平台。在 Vapor 上管理您的 Laravel 基础设施,并爱上无服务器的可扩展性和简单性。

Vapor 抽象了在 AWS Lambda 上管理 Laravel 应用程序的复杂性,以及将这些应用程序与 SQS 队列、数据库、Redis 集群、网络、CloudFront CDN 等进行交互的复杂性。Vapor 功能的一些亮点包括

  • 为 Laravel 精心调整的自动扩展 Web/队列基础设施
  • 零停机时间部署和回滚
  • 环境变量/秘密管理
  • 数据库管理,包括时间点恢复和扩展
  • Redis 缓存管理,包括集群扩展
  • 数据库和缓存隧道,便于本地检查
  • 在部署期间自动将资产上传到 Cloudfront CDN
  • 每个环境都有唯一的 Vapor 分配的虚荣 URL,允许立即检查
  • 自定义应用程序域
  • DNS 管理
  • 证书管理和续订
  • 应用程序、数据库和缓存指标
  • CI 友好

简而言之,您可以将 Vapor 视为 Laravel Forge 的无服务器技术版本。

视频系列

我们创建了一个关于 Vapor 的全面且免费的视频系列。通过 2 小时的内容,我们的团队将帮助您在 Vapor 上启动您的 Laravel 基础设施,并爱上无服务器的可扩展简单性。

YouTube

要求

Vapor 要求您的应用程序与 PHP 7.3+ 和 Laravel 6.0+ 兼容。

帐户创建

在将 Vapor 集成到您的应用程序之前,您应该创建一个 Vapor 帐户。如果您只是与他人合作他们的项目,则不需要拥有 Vapor 订阅。要创建和管理您自己的项目,您需要一个 Vapor 订阅。

安装 Vapor CLI

您将使用 Vapor CLI 部署您的 Laravel Vapor 应用程序。此 CLI 可以使用 Composer 全局安装或按项目安装

bash
composer require laravel/vapor-cli --update-with-dependencies

composer global require laravel/vapor-cli --update-with-dependencies

当 CLI 按项目安装时,您可能需要通过项目的 vendor/bin 目录执行它,这是 Composer 安装可执行文件的位置。例如,要查看所有可用的 Vapor CLI 命令,您可以使用 list 命令

bash
php vendor/bin/vapor list

Vapor CLI 别名

为了在与 Vapor CLI 的每个项目安装交互时节省按键次数,您可以在操作系统中添加一个 shell 别名,将 vapor 命令别名为 php vendor/bin/vapor

要了解有关命令及其参数的更多信息,请使用 help 命令执行您要探索的命令的名称。

bash
php vendor/bin/vapor help deploy

登录

安装 Vapor CLI 后,您应该使用 login 命令使用您的 Vapor 帐户进行身份验证。

bash
vapor login

安装 Vapor Core

laravel/vapor-core 必须作为使用 Vapor 部署的每个 Laravel 应用程序的依赖项安装。此包包含各种 Vapor 运行时文件和一个服务提供商,以允许您的应用程序在 Vapor 上运行。您可以使用 Composer 将 Vapor Core 安装到您的项目中。

bash
composer require laravel/vapor-core --update-with-dependencies

沙盒帐户

创建 Vapor 帐户后,您的帐户将处于我们的免费“沙盒”计划中,该计划允许您体验 Vapor 的强大功能,而无需预先承诺订阅付费计划。沙盒帐户允许您配置网络、数据库和缓存等服务。您可以添加一个项目,该项目部署后可以通过 AWS Lambda 函数 URL 访问。

沙盒限制

沙盒项目可能无法使用 API 网关版本、负载均衡器、防火墙或自定义域。要使用这些功能,您需要选择订阅计划。

团队

创建 Vapor 帐户时,会自动为您创建一个“个人”团队。您可以在团队设置中重命名此团队。所有项目、数据库、缓存和其他 Vapor 资源都属于一个团队。您可以通过 Vapor UI 或 team CLI 命令创建任意数量的团队。创建团队无需额外费用,它们是按客户或主题组织项目的绝佳方式。

当前团队和切换团队

通过 CLI 管理 Vapor 资源时,您需要了解当前活动的团队。您可以使用 team:current 命令查看当前团队。

bash
vapor team:current

要更改活动团队,可以使用 team:switch 命令。

bash
vapor team:switch

协作者

您可以通过 Vapor UI 中的“团队设置”菜单或使用 team:add CLI 命令邀请更多人加入您的团队。当您通过 Vapor UI 将新协作者添加到您的团队时,您可以选择要分配给该人的权限。例如,您可以阻止特定团队成员删除数据库或缓存。

您可以使用 Vapor UI 或 team:remove CLI 命令从您的团队中移除协作者。

与 AWS 链接

为了使用 Vapor 部署项目或创建其他资源,您需要在团队的设置管理页面上链接一个活动的 AWS 账户。

创建 IAM 用户

要创建 Vapor 管理 AWS 账户资源所需的 AWS 访问密钥和密钥,您需要在 AWS 中创建一个新的 IAM 用户。要创建新的 IAM 用户,请在 AWS 仪表板中导航到 IAM 服务。进入 IAM 仪表板后,您可以从左侧导航面板中选择“用户”。

接下来,单击“添加用户”按钮,选择一个用户名,然后单击“下一步”。

权限

IAM 访问

由于 Vapor 管理跨十多个 AWS 服务的多种类型的资源,因此创建一个具有 AdministratorAccess 策略的用户可能很方便。如果需要,您可以创建一个单独的 AWS 账户来容纳此用户并包含所有 Vapor 资源。

在权限管理屏幕上,您可以通过选择“直接附加现有策略”选项和“AdministratorAccess”策略来授予 IAM 用户完全的管理员访问权限。附加策略后,您可以单击“下一步”。

或者,如果您不想向 Vapor 提供管理员访问权限,您可以改为创建一个具有 Vapor 所需特定权限的自定义权限策略。由于 AWS 设置的策略大小限制,需要创建两个策略。为此,请从“权限策略”面板中选择“创建策略”。选择 JSON 选项并提供以下第一个权限定义。然后,按照相同的步骤使用下面列出的第二个定义创建另一个策略。定义策略后,您可以将它们附加到您的新 IAM 用户

json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VaporPolicy",
            "Effect": "Allow",
            "Action": [
                "acm:AddTagsToCertificate",
                "acm:DeleteCertificate",
                "acm:DescribeCertificate",
                "acm:ImportCertificate",
                "acm:RequestCertificate",
                "apigateway:DELETE",
                "apigateway:GET",
                "apigateway:PATCH",
                "apigateway:POST",
                "apigateway:PUT",
                "apigateway:SetWebACL",
                "budgets:ModifyBudget",
                "budgets:ViewBudget",
                "cloudformation:CreateStack",
                "cloudformation:DeleteStack",
                "cloudformation:DescribeStacks",
                "cloudformation:UpdateStack",
                "cloudfront:CreateDistribution",
                "cloudfront:DeleteDistribution",
                "cloudfront:GetDistribution",
                "cloudfront:GetDistributionConfig",
                "cloudfront:UpdateDistribution",
                "cloudwatch:DeleteAlarms",
                "cloudwatch:GetMetricStatistics",
                "cloudwatch:PutMetricAlarm",
                "dynamodb:CreateTable",
                "dynamodb:DescribeTable",
                "dynamodb:DescribeTimeToLive",
                "dynamodb:TagResource",
                "dynamodb:UpdateTimeToLive",
                "ec2:AllocateAddress",
                "ec2:AssociateAddress",
                "ec2:AssociateRouteTable",
                "ec2:AttachInternetGateway",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateInternetGateway",
                "ec2:CreateNatGateway",
                "ec2:CreateRoute",
                "ec2:CreateRouteTable",
                "ec2:CreateSubnet",
                "ec2:CreateTags",
                "ec2:CreateVpc",
                "ec2:CreateVpcEndpoint",
                "ec2:DeleteInternetGateway",
                "ec2:DeleteKeyPair",
                "ec2:DeleteNatGateway",
                "ec2:DeleteRoute",
                "ec2:DeleteRouteTable",
                "ec2:DeleteSubnet",
                "ec2:DeleteVolume",
                "ec2:DeleteVpc",
                "ec2:DeleteVpcEndpoints",
                "ec2:DescribeAddresses",
                "ec2:DescribeAvailabilityZones",
                "ec2:DescribeImages",
                "ec2:DescribeInstanceAttribute",
                "ec2:DescribeInstances",
                "ec2:DescribeInternetGateways",
                "ec2:DescribeKeyPairs",
                "ec2:DescribeNatGateways",
                "ec2:DescribeNetworkAcls",
                "ec2:DescribeRegions",
                "ec2:DescribeRouteTables",
                "ec2:DescribeSecurityGroupRules",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSnapshots",
                "ec2:DescribeSubnets",
                "ec2:DescribeTransitGatewayRouteTables",
                "ec2:DescribeTransitGatewayVpcAttachments",
                "ec2:DescribeTransitGateways",
                "ec2:DescribeVolumes",
                "ec2:DescribeVpcEndpoints",
                "ec2:DescribeVpcs",
                "ec2:DetachInternetGateway",
                "ec2:DisassociateRouteTable",
                "ec2:ImportKeyPair",
                "ec2:ModifySubnetAttribute",
                "ec2:ModifyVpcAttribute",
                "ec2:ReleaseAddress",
                "ec2:RevokeSecurityGroupEgress",
                "ec2:RevokeSecurityGroupIngress",
                "ec2:RunInstances",
                "ec2:TerminateInstances",
                "ecr:BatchCheckLayerAvailability",
                "ecr:BatchDeleteImage",
                "ecr:CompleteLayerUpload",
                "ecr:CreateRepository",
                "ecr:DeleteRepository",
                "ecr:GetAuthorizationToken",
                "ecr:GetRepositoryPolicy",
                "ecr:InitiateLayerUpload",
                "ecr:PutImage",
                "ecr:SetRepositoryPolicy",
                "ecr:UploadLayerPart",
                "elasticache:AddTagsToResource",
                "elasticache:CreateCacheSubnetGroup",
                "elasticache:CreateReplicationGroup",
                "elasticache:CreateServerlessCache",
                "elasticache:DeleteCacheSubnetGroup",
                "elasticache:DeleteReplicationGroup",
                "elasticache:DeleteServerlessCache",
                "elasticache:DescribeCacheSubnetGroups",
                "elasticache:DescribeReplicationGroups",
                "elasticache:DescribeServerlessCaches",
                "elasticache:ListTagsForResource",
                "elasticache:ModifyReplicationGroupShardConfiguration",
                "elasticache:ModifyServerlessCache",
                "elasticloadbalancing:AddListenerCertificates",
                "elasticloadbalancing:AddTags",
                "elasticloadbalancing:CreateListener",
                "elasticloadbalancing:CreateLoadBalancer",
                "elasticloadbalancing:CreateRule",
                "elasticloadbalancing:CreateTargetGroup",
                "elasticloadbalancing:DeleteLoadBalancer",
                "elasticloadbalancing:DescribeLoadBalancers",
                "elasticloadbalancing:DescribeRules",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:ModifyListener",
                "elasticloadbalancing:ModifyRule",
                "elasticloadbalancing:ModifyTargetGroupAttributes",
                "elasticloadbalancing:RegisterTargets",
                "elasticloadbalancing:SetWebAcl",
                "events:DeleteRule",
                "events:DescribeRule",
                "events:ListTargetsByRule",
                "events:PutRule",
                "events:PutTargets",
                "events:RemoveTargets",
                "iam:CreateRole",
                "iam:CreateServiceLinkedRole",
                "iam:GetRole",
                "iam:GetUser",
                "iam:PassRole",
                "iam:PutRolePolicy",
                "iam:UpdateAssumeRolePolicy",
                "kms:CreateGrant",
                "kms:Decrypt",
                "kms:DescribeKey",
                "kms:Encrypt",
                "kms:GenerateDataKey",
                "lambda:AddPermission",
                "lambda:CreateAlias",
                "lambda:CreateEventSourceMapping",
                "lambda:CreateFunction",
                "lambda:CreateFunctionUrlConfig",
                "lambda:DeleteFunction",
                "lambda:DeleteFunctionConcurrency",
                "lambda:DeleteProvisionedConcurrencyConfig",
                "lambda:GetAccountSettings",
                "lambda:GetAlias",
                "lambda:GetFunction",
                "lambda:GetFunctionUrlConfig",
                "lambda:GetLayerVersion",
                "lambda:GetPolicy",
                "lambda:InvokeFunction",
                "lambda:ListEventSourceMappings",
                "lambda:ListVersionsByFunction",
                "lambda:PublishVersion",
                "lambda:PutFunctionConcurrency",
                "lambda:PutFunctionEventInvokeConfig",
                "lambda:PutProvisionedConcurrencyConfig",
                "lambda:TagResource",
                "lambda:UpdateAlias",
                "lambda:UpdateEventSourceMapping",
                "lambda:UpdateFunctionCode",
                "lambda:UpdateFunctionConfiguration",
                "logs:FilterLogEvents",
                "rds:AddTagsToResource",
                "rds:CreateDBCluster",
                "rds:CreateDBInstance",
                "rds:CreateDBProxy",
                "rds:CreateDBSubnetGroup",
                "rds:DeleteDBCluster",
                "rds:DeleteDBInstance",
                "rds:DeleteDBProxy",
                "rds:DeleteDBSubnetGroup",
                "rds:DescribeDBClusters",
                "rds:DescribeDBEngineVersions",
                "rds:DescribeDBInstances",
                "rds:DescribeDBProxies",
                "rds:DescribeDBSubnetGroups",
                "rds:ListTagsForResource",
                "rds:ModifyDBCluster",
                "rds:ModifyDBInstance",
                "rds:RegisterDBProxyTargets",
                "rds:RestoreDBInstanceToPointInTime",
                "route53:ChangeResourceRecordSets",
                "route53:CreateHostedZone",
                "route53:GetHostedZone",
                "route53:ListHostedZonesByName",
                "route53:ListResourceRecordSets",
                "s3:CreateBucket",
                "s3:DeleteBucket",
                "s3:DeleteBucketPolicy",
                "s3:DeleteObject",
                "s3:GetBucketCORS",
                "s3:GetBucketLocation",
                "s3:GetBucketTagging",
                "s3:GetBucketVersioning",
                "s3:GetObject",
                "s3:ListAllMyBuckets",
                "s3:ListBucket",
                "s3:PutBucketCORS",
                "s3:PutBucketOwnershipControls",
                "s3:PutBucketPublicAccessBlock",
                "s3:PutLifecycleConfiguration",
                "s3:PutObject",
                "s3:PutObjectAcl",
                "secretsmanager:CreateSecret",
                "secretsmanager:DeleteSecret",
                "secretsmanager:GetSecretValue",
                "secretsmanager:TagResource",
                "servicequotas:GetServiceQuota",
                "ses:VerifyDomainDkim",
                "ses:VerifyDomainIdentity",
                "sns:ConfirmSubscription",
                "sns:CreateTopic",
                "sns:GetTopicAttributes",
                "sns:ListSubscriptionsByTopic",
                "sns:SetTopicAttributes",
                "sns:Subscribe",
                "sns:TagResource"
            ],
            "Resource": "*"
        }
    ]
}
json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VaporPolicy",
            "Effect": "Allow",
            "Action": [
                "sqs:CreateQueue",
                "sqs:DeleteQueue",
                "sqs:GetQueueAttributes",
                "sqs:GetQueueUrl",
                "sqs:SetQueueAttributes",
                "ssm:DeleteParameter",
                "ssm:DeleteParameters",
                "ssm:PutParameter",
                "ssm:UpdateServiceSetting",
                "wafv2:AssociateWebACL",
                "wafv2:CreateWebACL",
                "wafv2:DeleteWebACL",
                "wafv2:GetWebACL",
                "wafv2:ListResourcesForWebACL",
                "wafv2:TagResource",
                "wafv2:UpdateWebACL"
            ],
            "Resource": "*"
        }
    ]
}

所需权限的更改

我们可能会在 Vapor 中添加新功能时更改此权限列表,如果您的策略没有保持最新,可能会导致意外错误。

创建用户后,您需要生成一组访问凭据。为此,请单击您新创建的用户,然后选择“安全凭据”选项卡。现在,在“访问密钥”面板中,选择“创建访问密钥”,然后选择“第三方服务”。选择确认框并单击“下一步”,然后单击“创建访问密钥”。您的访问密钥 ID 和密钥将显示出来。然后,您可以将这些凭据提供给 Vapor,以便 AWS 资源可以代表您进行管理。您可以通过 Vapor UI 的“团队设置”屏幕管理您链接的 AWS 账户。

定义您的 AWS 预算

将您的 AWS 账户链接到 Vapor 时,了解您的 AWS 成本非常重要。这可以通过使用 AWS 预算服务直接在 AWS 控制台中完成。此外,您可以使用 Vapor 的托管预算以美元定义您的每月 AWS 预算,同时还可以在 Vapor UI 的“团队设置 > AWS 账户”屏幕上直接配置多个警报。目前,最多可以配置五个警报

  • 实际成本 > 85%
  • 实际成本 > 100%
  • 实际成本 > 200%
  • 实际成本 > 500%
  • 预测成本 > 100%

每个警报每个月账单周期只能触发一次。当警报触发时,团队所有者会收到一封电子邮件,以便他们能够快速采取行动,避免意外费用。

通知方法

Slack

为了通过 Slack 接收通知,您需要 创建一个 Slack 应用程序 并选择要安装 Slack 应用程序的工作区。

创建 Slack 应用程序后,请访问应用程序“功能”侧边栏下的“传入 Webhook”设置窗格。然后,使用激活开关激活传入 Webhook 功能。

激活后,您可以使用“将新 Webhook 添加到工作区”按钮创建新的传入 Webhook。最后,您应该复制 Slack 提供的 Webhook URL 并将其插入 Vapor 的通知方法创建表单中。