如何用Azure函数和无服务器框架创建REST API - 第二部分

172 阅读3分钟

概述

现在你已经从第一部分中创建并部署了一个基本的API,让我们采取更多的步骤来使这个API更加有弹性和安全。这篇文章仍将基于示例 repo,并将遵循与第一部分相同的 "每步提交 "格式,其中包括步骤1和2。

要继续我们在示例 repo 中的工作(在完成步骤 2 之后),请运行。

# Assumes you've already forked the repo
$ git clone https://github.com/<your-github-name>/sls-az-func-rest-api && git checkout cf46d1d

步骤3:添加单元测试和提示 - (commit465ecfe)

因为这并不是一篇关于单元测试、提示或质量门的博文,我只是分享我使用的工具和我添加到版本库的质量门。你可以把它们作为你自己未来的测试或lint规则的存根来使用。

对于单元测试,我使用了Facebook的Jest测试运行器。我在过去的几个项目中使用过它,从未出现过任何问题。Jest测试通常与他们要测试的文件放在一起,并以.test.js 。这可以在 jest.config.js中配置,它位于项目的根部。

因为我的代码通过axios 进行REST调用,我使用axios-mock-adapter 来模拟请求和响应。我写的测试(issues.test.jspulls.test.js)运行一些简单的检查,以确保正确的URL被击中并返回预期的响应。

对于提示,我使用ESLint的一个非常基本的配置,在 .eslintrc.json.要运行一个lint检查,你可以运行。

$ npm run lint

许多错误可以通过以下方式自动修复。

$ npm run lint:fix

运行你的测试。

$ npm test

更多细节,请看例子 repo中的提交,或在本地查看提交。

$ git checkout 465ecfe

第四步:添加基本的API管理配置--(提交c593308)

这是我们在serverless-azure-functions 插件的v1 中最先实现的功能之一。因为大多数Azure Function Apps都是REST API,如果没有API管理,很难在Azure中拥有一个真实的API。

如果你对API管理没有特别的要求,只要你包括,插件实际上就会为你生成默认配置。

...
provider:
    ...
    apim: true

这正是我在第4步所做的。另外,因为我们希望API管理是我们API端点的唯一入口,我还把每个函数的authLevel 改为function 。这需要一个特定函数的API密钥来进行认证。你可以在截图中看到在第一个命令中发生了什么,当我试图curl 原有的函数URL时。我得到一个401 响应代码。但当我点击API管理提供的URL时,我得到了我所期望的响应。

alt text

关于authLevel 的更多细节,请查看触发器配置文档

消费SKU

需要注意的一件事是,API管理配置将默认为consumption SKU,该SKU最近进入了GA。目前,允许Consumption API管理的地区只有。

  • 美国中北部
  • 美国西部
  • 西欧
  • 北欧
  • 东南亚
  • 澳大利亚东部

如果你要部署到该列表以外的地区,你将需要在apim 配置中指定一个不同的SKU (Developer,Basic,StandardPremium),这将在下一节演示。

部署你的更新。
$ sls deploy

第5步:添加更高级的API管理配置--(提交38413a0)

如果你在配置你的API管理实例时需要多转几个旋钮,你可以提供一个更粗略的配置。这是我添加到样本库中的粗略配置(... 表示该部分的其他配置保持不变)。

service: sls-az-func-rest-api

provider:
  ...
  apim:
    apis:
      - name: github-api
        # Require an API Key if true
        subscriptionRequired: false
        displayName: Github API
        description: The GitHub API
        protocols:
          - https
        # Defaults to /api
        path: github
        # Azure resource tags
        tags:
          - apimTag1
          - apimTag2
        authorization: none
    backends:
      - name: github-backend
        url: api/github
    cors:
      allowCredentials: false
      allowedOrigins:
        - "*"
      allowedMethods:
        - GET
        - POST
        - PUT
        - DELETE
        - PATCH
      allowedHeaders:
        - "*"
      exposeHeaders:
        - "*"
...

functions:
  issues:
    ...
    apim:
      api: github-api
      backend: github-backend
      operations:
        - method: get
          urlTemplate: /issues
          displayName: GetIssues
  pulls:
    ...
    apim:
      api: github-api
      backend: github-backend
      operations:
        - method: get
          urlTemplate: /pulls
          displayName: GetPullRequests

如果你不想要API管理的Consumption SKU,你需要有一个verbose配置,并指定sku 作为。

provider:
  ...
  apim:
    ...
    sku:
      name: {Consumption|Developer|Basic|Standard|Premium}

这个例子只是使用默认值,并部署到目前有消费API管理的地区。

部署你的更新。
$ sls deploy

(可选)步骤5.1:恢复到基本的API管理配置 - (提交4c5803f)

为了使演示简单易懂,我将把我的apim 配置恢复到默认值。

apim: true

你可能也可以这样做,这取决于你的要求。

第6步:添加Webpack配置 - (commit1aefac7)

Webpack极大地减少了打包时间以及你部署的包的大小。做完这些改动后,你打包的Function App将被Webpack优化(你可以运行sls package 来打包,或者直接运行sls deploy ,它将把打包作为生命周期的一部分)。

举个例子,即使是这个非常小的应用程序,我的包的大小也从324KB变成了28KB

为了达到这个目的,我们将使用另一个很棒的Serverless插件。 serverless-webpack来使我们的Azure Function应用的Webpacking变得非常简单。

假设你在自己的git仓库中完成本教程,你要做的第一件事是将生成的Webpack文件夹添加到你的git仓库中。.gitignore

# .gitignore
...
# Webpack artifacts
.webpack/

接下来,我们需要从npm安装3个包。

$ npm i serverless-webpack webpack webpack-cli --save-dev

然后我们将插件添加到我们的serverless.yml

plugins:
  - serverless-azure-functions
  - serverless-webpack

然后把这个确切的代码复制到你的服务目录根部的webpack.config.js

const path = require("path");
const slsw = require("serverless-webpack");

module.exports = {
  entry: slsw.lib.entries,
  target: "node",
  output: {
    libraryTarget: "commonjs2",
    library: "index",
    path: path.resolve(__dirname, ".webpack"),
    filename: "[name].js"
  },
  plugins: [],
};

就这样,你部署的Azure Function应用将被Web打包,可以使用了。

alt text

第7步:启用无服务器CLI配置--(提交4cb42fd)

如果你正在运行一个现实生活中的生产服务,你很可能会部署到多个地区和多个阶段。也许合并到你的dev 分支会触发部署到你的dev 环境,masterprod ,等等。我将在第8步向你展示一个例子。为了完成CLI级别的可配置性,我们需要做一些改变serverless.yml

provider:
  region: ${opt:region, 'West US'}
  stage: ${opt:stage, 'dev'}
  prefix: ${opt:prefix, 'demo'}

正如你可能已经猜到的,West US,devdemo 这些值是我的默认值。如果我想把我的服务部署到North Central USWest Europe ,但保持其他一切不变,我就会运行。

$ sls deploy --region "North Central US"
$ sls deploy --region "West Europe"

我们可以对--prefix--stage 进行类似的操作。现在,让我们创建一个管道,真正做到这一点。

第8步:添加CI/CD(使用Azure DevOps)--(提交a8fabf6)

对于我的样本仓库的CI/CD,我使用的是Azure DevOps,但它在你想使用的任何其他服务上都是一样的。如果你想在一个开源项目中使用Azure DevOps,这里有几个步骤可以开始使用

不管是什么CI/CD环境,这里都是我们要完成的任务。

  1. 安装依赖项
  2. 验证变化(运行质量门)
  3. 部署服务

这些步骤都可以通过几个CLI命令完成。至少,我们要运行类似的命令。

# Clean install
npm ci
# Runs tests and linting
npm test
# Serverless not contained within dev dependencies to avoid conflicts
# because most users have it installed globally on their dev machine
npm i serverless -g
# Deploy service
sls deploy

我们还可以添加更多的附加功能,但这基本上是它归结为的内容。当然,无论我们从哪个系统部署,我们都需要认证,这就是服务委托人的作用。我将向你展示如何在下面的deploy.yml pipeline中使用服务委托人。

对于我的管道,我实际上要把我的CI和CD分割成unit-tests.ymldeploy.yml 。单元测试将在PR上运行,进入masterdev (这是假设有分支策略,以防止开发人员直接推送到任何一个分支)。部署将在提交(合并)到master 上运行。

单元测试
# pipelines/unit-tests.yml

# Only run on Pull Requests into `master` or `dev`
pr:
  branches:
    include:
    - master
    - dev

# Run pipeline on node 8 and 10 on Linux, Mac and Windows 
strategy:
  matrix:
    Linux_Node8:
      imageName: 'ubuntu-16.04'
      node_version: 8.x
    Linux_Node10:
      imageName: 'ubuntu-16.04'
      node_version: 10.x
    Mac_Node8:
      imageName: 'macos-10.14'
      node_version: 8.x
    Mac_Node10:
      imageName: 'macos-10.14'
      node_version: 10.x
    Windows_Node8:
      imageName: 'win1803'
      node_version: 8.x
    Windows_Node10:
      imageName: 'win1803'
      node_version: 10.x

# https://docs.microsoft.com/en-us/azure/devops/pipelines/agents/hosted?view=azure-devops#use-a-microsoft-hosted-agent
pool:
  vmImage: $(imageName)

steps:
- task: NodeTool@0
  inputs:
    versionSpec: $(node_version)
  displayName: 'Install Node.js'

# Make pipeline fail if tests or linting fail, linting occurs in `pretest` script
- bash: |
    set -euo pipefail
    npm ci
    npm test
  displayName: 'Run tests'
部署
# pipelines/deploy.yml

trigger:
  branches:
    include:
    - master

# https://docs.microsoft.com/en-us/azure/devops/pipelines/library/variable-groups?view=azure-devops&tabs=yaml
variables:
- group: sls-deploy-creds

jobs:

- job: "Deploy_Azure_Function_App"
  timeoutInMinutes: 30
  cancelTimeoutInMinutes: 1

  pool:
    vmImage: 'ubuntu-16.04'

  steps:
  - task: NodeTool@0
    inputs:
      versionSpec: 10.x
    displayName: 'Install Node.js'

  - bash: |
      npm install -g serverless
    displayName: 'Install Serverless'
    # Deploy service with prefix `gh`, stage `prod` and to region `West Europe`
  - bash: |
      npm ci
      sls deploy --prefix gh --stage prod --region "West Europe"
    env:
      # Azure Service Principal. Secrets need to be mapped here
      # USE THIS EXACT TEXT, DON'T COPY/PASTE YOUR CREDENTIALS HERE.
      # Azure DevOps will use the variables within
      # the variable group `sls-deploy-creds` to replace all the $() values
      AZURE_SUBSCRIPTION_ID: $(AZURE_SUBSCRIPTION_ID)
      AZURE_TENANT_ID: $(AZURE_TENANT_ID)
      AZURE_CLIENT_ID: $(AZURE_CLIENT_ID)
      AZURE_CLIENT_SECRET: $(AZURE_CLIENT_SECRET)
    displayName: 'Deploy Azure Function App' 

注意部署管道中的这一行,它利用了我们在步骤7中的设置。你可以为不同的阶段设置多个管道,你可以从分支名称中动态地推断这些值,或者你可以将这些值作为环境变量提供。第7步设置的重点是为你提供灵活性,将你的服务部署到你当时认为合适的地方,而不需要改变你的serverless.yml 文件。

结论性的想法

我们之所以在serverless-azure-functions 插件上投入时间和精力,很大一部分原因是为了让开发人员能够轻松地部署Azure Functions,以解决更多真实世界的业务级场景。我们希望你在使用该工具并发现需要改进的地方时,能在回购文件中提出问题,甚至打开一个拉动请求