无服务器PHP

Backend PHP Developer课程启动前夕我们进行了传统的公开课 。 这次,我们熟悉了无服务器的概念,讨论了其在AWS中的实现,讨论了操作,组装和启动的原理,还构建了一个基于AWS Lambda的简单PHP TG-bot。


讲师-Westernwing俄罗斯首席技术官Alexander Pryakhin





短暂的历史之旅



我们如何过着无服务器计算的生活? 当然,它们的出现不仅如此,而且已成为现有虚拟化技术的逻辑延续。



我们通常虚拟化什么? 例如,处理器。 您还可以通过突出显示某些内存区域并使某些用户可以访问而其他用户无法访问它们来虚拟化内存。 您可以虚拟化VPN网络。 依此类推。



虚拟化很好,因为我们可以更好地利用资源并提高生产率。 但是也有缺点,例如,一次存在兼容性问题。 但是,实际上没有与现代虚拟机不兼容的体系结构。


接下来的缺点是,我们增加了一个抽象层,添加了虚拟机监控程序,单独添加了虚拟机,当然,我们可能会损失一点速度。 有点复杂和服务器的使用。



如果我们将标准虚拟机与您一起使用,它将看起来像这样:



首先,我们有一个Iron服务器,其次,我们的管理程序将在其上旋转的操作系统。 最重要的是,我们的虚拟机正在旋转,其中包含来宾操作系统,库和应用程序。 如果您从逻辑上考虑,那么在来宾操作系统的存在下我们会看到一些开销,因为实际上我们会花费额外的资源。


我该如何解决开销问题? 拒绝虚拟机,并将容器管理系统置于主要操作系统之上。 当然,现在最受欢迎的系统是Docker Engine。 然后,容器内的库将使用操作系统的主机内核。



这样,我们消除了开销,但是Docker也不是理想的选择,并且它有自己的问题和工作功能,并非每个人都喜欢。


要了解的主要内容是Docker和虚拟机是不同的方法,因此无需将它们等同。 Docker不是可与虚拟机一起使用的微虚拟技术,因为容器是为此而设计的。 但是,当我们将产品交付生产并了解它们已经过测试并可以正常工作时,该容器使我们能够为连续交付提供灵活性和完全不同的方法。


云技术


随着虚拟化的进一步发展,云技术也开始发展。 这是一个很好的解决方案,但是值得一提的是,云并不是万灵药,也不是万灵药。 在此不禁让人想起一个著名的名言:


“当我听到有人吹捧云是解决所有计算问题的灵丹妙药时,我默默地用“小丑”代替了“云”,并带着一种禅宗般的微笑继续前进。”
艾米·里奇

但是,对于希望获得一定水平的服务和容错能力而又无需巨额资金投入的中型公司,云计算是一个不错的选择。 对于许多公司而言,使用相同的SLA保留其数据中心将比在云中提供服务昂贵得多。 此外,我们可以根据需要使用云,因为只需单击几下鼠标,云便可以提供某些功能,这非常方便。 例如,只需单击几下即可提升虚拟机或网络。
是的,有一些限制,例如,第152号联邦法禁止在国外存储个人数据,因此在审核期间,同一亚马逊将不适合我们。 不要忘记供应商锁。 尽管大多数提供商都支持相同的S3兼容存储,但许多云解决方案无法相互移植。


云为我们提供了机会,无需集中关注即可获得不同级别的服务。 您所需的知识越少,我们将付出的越多。 在下图中,您可以查看金字塔,该金字塔从下至上显示使用云时技术知识需求的减少:



无服务器和FaaS(功能即服务)


无服务器是在云中运行脚本的一种相当年轻的方式,例如AWS(就AWS而言,服务器是在Lambda中实现的)。 *上面金字塔中列出的AaS方法已经很熟悉:IaaS(EC2,VDS),PaaS(共享主机),SaaS(Office 365,Tilda)。 因此,无服务器是FaaS方法的实现。 这种方法包括为用户提供现成的平台,用于开发,启动和管理某些功能,而无需自行准备和配置。


假设您有一台机器在夜间处理文档,执行从00:00到6:00的任务,并且在其余时间处于空闲状态。 问题是:为什么要在白天付款? 为什么不将免费资源用于其他用途呢? 这种对优化的渴望以及仅在您真正使用的东西上花钱的渴望,导致了FaaS的出现。


无服务器是执行代码的资源,仅此而已。 这并不意味着脚本后没有服务器-而是,但实际上,我们没有任何特定分配的资源可在其上启动Lambda。 当我们运行脚本时,微基础结构会立即在其下展开,这在原则上不是您的问题-您仅认为自己已执行了代码,而无需考虑其他任何事情。
当然,这需要某种开发代码的方法。 例如,您不能在此环境中存储任何东西,而需要取出所有东西。 如果这是数据,则需要一个外部数据库,如果它是一个日志,则需要一个外部日志服务,如果它是一个文件,则需要一个外部文件存储。 幸运的是,任何无服务器提供程序都可以连接到外部系统。


您只有代码,您在无状态范式中工作,您没有状态。 对于同一世界的PHP,例如,这意味着您可以忘记标准的会话机制。 原则上,您甚至可以构建无服务器版,最近在Habré上有一篇有关此主题的文章


Serverless的主要思想是基础架构不需要团队的支持。 一切都落在平台的肩膀上,实际上,您为此付出了代价。 缺点-您无法控制执行环境,也不知道在何处执行。


因此,无服务器:


  • 并不意味着服务器实际不存在;
  • 不是virtualoks和Docker的杀手;;
  • 现在不要炒作。
    应该无意地推动无服务器。 例如,如果您需要快速检验假设,而无需团队的一半参与。 因此,您获得了功能即服务。 该函数将响应某些事件,并且由于对事件有反应,因此这些事件必须由某些东西调用-为此,同一AWS中有许多触发器。

FaaS功能:


  • 基础架构不需要配置;
  • “开箱即用”事件模型;
  • 无状态
  • 缩放非常简单,并且会根据用户需求自动执行。

AWS Lambda


第一个公开可用的FaaS实施是AWS Lambda。 如果是论文,那么它具有以下功能:
-自2014年起提供;
-支持现成的Java,Node.js,Python,Go和自定义运行时;
-我们支付:
通话次数;
交货时间。
AWS Lambda:为什么需要它:
处置方式 您只需为服务运行的时间付费。
速度 Lambda本身会上升并且工作非常快。
功能性。 Lambda具有许多与AWS服务集成的功能。
性能。 放入lambda非常困难。 同时,可以根据最多1000到3000份的区域执行打印。 并且,如果需要,可以通过编写支持来增加此限制。


我们有一个lambda主体,一个在线编辑器,作为虚拟计算网格的VPC,日志记录,代码本身,环境变量和引发lambda的触发器(顺便说一句,版本控制效果很好)。 本文概述了出色的Lambda解剖结构。


代码存储在主体中(如果开箱即用,则支持这些语言)或分层存储。 我们有一个触发器调用lambda,该lambda读取临时环境,将其拉到自身并执行我们的代码:



如果我们有一个自定义的运行时,则必须将代码放在一个层中。 如果您使用Docker,则Docker层与lambda中的层非常相似-lambda中的一种准存储,我们需要的绑定位于其中。 那里有环境的可执行文件(如果我们在谈论PHP,则应事先放置已编译的PHP二进制文件),lambda引导程序文件(默认情况下位于其中)和将直接执行的脚本。



交付后,一切都不那么乐观:



也就是说,我们提供了包含代码的文件,将其上传到zip存档,将其上传到图层并运行我们的代码。 亚马逊的官方文档中提供了这一点非常酷。



当然,这与现代现实和空气中千分之二的气味不符。 幸运的是,好心人尝试并制作了多个框架,因此我们将使用在Node.js上开发的Serverless框架,并允许我们基于AWS Lambda管理应用程序。 另外,当我们谈论部署和开发时,我当然并不想手动部署,但是希望做些灵活且自动化的事情。


因此,我们需要:
-AWS CLI-用于处理AWS服务的命令行界面;
-上面已经提到的无服务器框架(开发版本是免费的,其功能足以引起人们的注意);
-Bref库,用于编写代码。 该库是使用composer安装的,因此代码将与任何框架兼容。 一个很棒的解决方案,尤其是考虑到AWS Lambda不支持即席调用PHP脚本。



自定义您的环境和AWS


AWS CLI


首先创建一个帐户并安装AWS CLI。 AWS控制台外壳基于Python 2.7+或3.4+。 由于AWS建议使用Python的版本3,因此我们不会争论。
以下示例适用于Ubuntu。


sudo apt-get -y install python3-pip 

然后直接安装AWS CLI:


 pip3 install awscli --upgrade --user 

检查安装:


 aws --version 

现在,您需要将AWS CLI连接到您的账户。 您可以使用现有的用户名和密码,但是如果您通过AWS IAM创建一个单独的用户,只为他定义必要的访问权限,那会更好。 调用配置不会导致问题:


 aws configure 

接下来,您将需要AWS Secret和AWS Access Key。 可以在ASW IAM的“安全凭据”选项卡中找到它们(位于所需用户的页面上)。 “创建访问密钥”按钮将有助于生成访问密钥。 和他们在一起。



要在Telegram中注册新的bot,请使用@BotFather和/ newbot命令。 结果,连接到您的机器人所需的令牌将退还给您。 也将其锁定。



无服务器框架


要安装Serverless Framework,您需要一个https://serverless.com/帐户。
完成注册后,我们将继续在工作站上进行安装。 需要Node.js 6th及更高版本。


 sudo apt-get -y install nodejs 

为了确保在我们的环境中正确启动,我们遵循建议的步骤:


 mkdir ~/.npm-global export PATH=~/.npm-global/bin:$PATH source ~/.profile npm config set prefix '~/.npm-global' 

还添加:


 ~/.npm-global/bin:$PATH 

到/ etc /环境文件。
现在放无服务器:


 npm install -g serverless 

ws


好了,是时候切换到AWS界面并添加域名了。 我们为其创建一个AWS Route 53区域,一个DNS记录和一个SSL证书。
另外,您还需要我们在服务EC2->负载平衡器中创建的ELB。 顺便说一句,在创建ELB时,您需要完成向导的所有步骤,并指出创建的证书。


对于平衡器,您可以使用以下命令通过AWS CLI创建平衡器:


 aws elb create-load-balancer --load-balancer-name my-load-balancer --listeners "Protocol=HTTP,LoadBalancerPort=80,InstanceProtocol=HTTP,InstancePort=80" "Protocol=HTTPS,LoadBalancerPort=443,InstanceProtocol=HTTP,InstancePort=80,SSLCertificateId=arn:aws:iam::123456789012:server-certificate/my-server-cert" --subnets subnet-15aaab61 --security-groups sg-a61988c3 

首次部署后将需要一个平衡器。 在这种情况下,您需要向我们的域发送请求。 为此,在DNS记录的设置(“别名目标”字段)中,开始输入创建的ELB的名称。 结果,您将看到一个下拉列表,因此仍然可以选择所需的条目并保存。



现在转到代码。


编写代码


我们将使用Bref编写代码。 如前所述,该库是使用composer安装的,因此代码将与任何框架兼容。 顺便说一下,开发人员已经描述了将Bref与LaravelSymfony结合使用的过程。 但是建议我们使用“裸” PHP-这将有助于更好地理解其本质。
我们从依赖关系开始:


 { "require": { "php": ">=7.2", "bref/bref": "^0.5.9", "telegram-bot/api": "*" }, "autoload": { "psr-4": { "App\": "src/" } } } 

我们将使用PHP 7.2和更高版本进行编写,并且对于使用Telegram而言,该API外壳适合我们-https://github.com/TelegramBot/Api 。 至于代码本身,它将放置在src目录中。


因此,无服务器环境正在通过控制台对话框。 需要一个HTTP应用程序,从Lambda的角度来看,这意味着将以与Nginx相同的方式执行脚本调用。 解释将由PHP-FPM执行。 通常,这更像是标准控制台脚本调用。 这一点很重要,因为如果不考虑此功能,我们将无法通过HTTP调用脚本。
我们执行:


 vendor/bin/bref init 

在对话框中,选择“ HTTP应用程序”项,不要忘记指定区域,因为该应用程序应在平衡器所在的同一区域中工作。
初始化后,将出现2个新文件:
index.php-被调用的文件;
serverless.yml-部署配置文件。
.serverless文件夹将需要立即添加到.gitignore中(它将在第一次尝试部署后出现)。


有了Web应用程序后,我们将index.php放到公用文件夹中,立即切换到serverless.yml。 这是我们的实现中的样子:


 #  lambda- service: app #    provider: name: aws #   ! region: eu-central-1 #    runtime: provided # ,  bref  1024.         memoryLimit: 256 #   stage: dev #    environment: BOT_TOKEN: ${ssm:/app/bot-token} #  bref plugins: - ./vendor/bref/bref #  Lambda- functions: #       php-api-dev # service-function-stage api: handler: public/index.php description: '' # in seconds (API Gateway has a timeout of 29 seconds) timeout: 28 layers: - ${bref:layer.php-73-fpm} #     API Gateway events: - http: 'ANY /' - http: 'ANY /{proxy+}' #    environment: MY_VARIABLE: ${ssm:/app/my_variable} 

现在让我们分析非明显的线条。 我们主要需要环境变量。 我们不想对数据库连接,外部API等进行硬编码。如果我们连接到Telegram,我们将拥有自己的令牌,该令牌是从BotFather接收的。 并且不建议将此令牌存储在serverless.yml中,因此最好将其发送到AWS ssm存储:


 aws ssm put-parameter --region eu-central-1 --name '/app/my_variable' --type String --value '___BOTFATHER' 

顺便说一下,我们在配置中引用它。
这些变量可用作环境变量,您可以使用getenv函数在PHP中访问它们。 如果我们谈论我们的示例,那么为了简单起见,我们将机器人令牌保留在全局范围内。 我们还可以将令牌转移到单个函数的作用域中,并且调用本身不会因此改变。


让我们继续前进。 现在,让我们创建一个简单的BotApp类-它将负责为漫游器生成响应并响应命令。 电报开发人员建议为所有漫游器添加对/ help和/ start命令的支持。 让我们添加另一个命令很有趣。 该类本身非常简单,可以在不加载调用文件本身的情况下在index.php中实现Front控制器。 为了获得更复杂的逻辑,应该开发架构并使之复杂。


 <?php namespace App; use TelegramBot\Api\Client; use Telegram\Bot\Objects\Update; class BotApp { function run(): void{ $token = getenv('BOT_TOKEN'); $bot = new Client($token); //   start $bot->command('start', function ($message) use ($bot) { $answer = ' !'; $bot->sendMessage($message->getChat()->getId(), $answer); }); //    $bot->command('help', function ($message) use ($bot) { $answer = ': /help -  '; $bot->sendMessage($message->getChat()->getId(), $answer); }); //   $bot->command('hello', function ($message) use ($bot) { $answer = '-,  - ,   Serverless '; $bot->sendMessage($message->getChat()->getId(), $answer); }); $bot->run(); } } 

这是index.php的清单:


 <?php require_once('../vendor/autoload.php'); use App\BotApp; try{ $botApp = new BotApp(); $botApp->run(); } catch (Exception $e){ echo $e->getMessage(); print_r($e->getTrace(), 1); } 

看起来似乎很奇怪,但是一切准备就绪,我们可以开始生产了。 让我们通过在serverless.yml文件夹中运行命令来做到这一点:


 sls deploy 

在正常模式下,lessless服务器会将文件打包到zip档案中,创建一个S3-bucket放置它们,然后创建或更新附加到Lambda的AWS Application,并将代码和运行时放在单独的层中。


在第一次启动期间,将创建Gateway API(我们将其保留为便于测试调用,但建议将其删除)。 您还需要通过ELB配置Lambda调用,为此我们在功能控制窗口中选择“添加触发器”,然后在下拉列表中选择“应用程序负载平衡器”。 您将需要指定先前创建的ELB,通过HTTPS设置连接,将主机保留为空,并在“路径”中指定Lambda将调用的路径(例如/ lambda / mytgbot)。 因此,您的Lambda将可在具有指定路径的URL上使用。


现在,您可以在Telegram中注册该漫游器的响应部分,以便使者了解从何处获取消息。 为此,请在浏览器中调用以下URL,但是不要忘记在其中替换您自己的参数:


 https://api.telegram.org/bot_/setWebhook?url=https://my-elb-host.com/lambda/mytgbot 

结果,API将返回“ OK”,之后该机器人将变得可用。



在语言环境中测试机器人


可以在部署之前对Bot进行测试。 事实是,无服务器框架为此使用Docker容器支持在语言环境中启动。 调用命令:


 sls invoke local --docker -f myFunction 

不要忘记我们使用了环境变量,因此在调用过程中,还应将它们设置为以下格式:


 sls invoke local --docker -f myFunction --env VAR1=val1 

日志


默认情况下,呼叫输出将记录到CloudWatch中-在相应的Lambda函数的“监视”面板中可用。 在PHP端发生转储时,您可以在此处读取调用跟踪。 此外,您可以连接高级监视服务,但每月将花费额外的几美分。


合计


结果,我们得到了一个相当快速,灵活,可扩展且相对便宜的解决方案。 是的,与标准VM和容器相比,Lambda并不总是能胜出,但是在某些情况下,无服务器应用程序有助于快速高效地“射击”。 而创建的机器人的示例仅说明了这一点。
关于该主题的有用材料:


Source: https://habr.com/ru/post/zh-CN477370/


All Articles