使用 Azure CLI 创建 Express.js 虚拟机

在本教程中,将为 Express.js 应用创建一个 Linux 虚拟机 (VM)。 VM 是使用 cloud-init 配置文件配置的,并且它包含 NGINX 以及 Express.js 应用的 GitHub 存储库。 使用 SSH 连接 VM,将 Web 应用更改为包括跟踪日志记录,并在 Web 浏览器中查看公共 Express.js 服务器应用。

本教程包括以下任务:

  • 使用 Azure CLI 登录到 Azure
  • 使用 Azure CLI 创建 Azure Linux VM 资源
    • 打开公用端口 80
    • 从 GitHub 存储库安装演示版 Express.js Web 应用
    • 安装 Web 应用依赖项
    • 启动 Web 应用
  • 使用 Azure CLI 创建 Azure 监视资源
    • 使用 SSH 连接到 VM
    • 使用 npm 安装 Azure SDK 客户端库
    • 添加 Application Insights 客户端库代码以创建自定义跟踪
  • 从浏览器查看 Web 应用
    • 请求 /trace 路由以在 Application Insights 日志中生成自定义跟踪
    • 使用 Azure CLI 查看日志中收集的跟踪计数
    • 使用 Azure 门户查看跟踪列表
  • 使用 Azure CLI 删除资源

创建 Azure 订阅或使用现有 Azure 订阅

你将需要具有活动订阅的 Azure 用户帐户。 免费创建一个

先决条件

  • SSH 连接到 VM:使用 Azure Cloud Shell 或新式终端(例如 bash shell),包括 SSH。

1. 为网页创建 Application Insights 资源

为所有 Azure 资源创建一个 Azure 资源组,并创建一个 Monitor 资源以将 Web 应用的日志文件收集到 Azure 云。 通过创建资源组,可以轻松查找资源,并在完成后将其删除。 Azure Monitor 是 Azure 服务的名称,而 Application Insights 是本教程使用的客户端库的名称。

  1. 可选,如果有多个订阅,请使用 az account set 设置默认订阅,然后再完成剩余的命令。

    az account set \
        --subscription "ACCOUNT NAME OR ID" 
    
  2. 使用 az group create 创建 Azure 资源组。 使用名称 rg-demo-vm-eastus

    az group create \
        --location eastus \
        --name rg-demo-vm-eastus 
    

使用 Azure CLI 创建 Azure Monitor 资源

  1. 向 Azure CLI 安装 Application Insights 扩展。

    az extension add -n application-insights
    
  2. 使用以下命令创建监视资源,使用 az monitor app-insights 组件创建

    az monitor app-insights component create \
      --app demoWebAppMonitor \
      --location eastus \
      --resource-group rg-demo-vm-eastus \
      --query instrumentationKey --output table
    
  3. 从输出复制结果,稍后将需要该值instrumentationKey

  4. 使终端保持打开状态,在下一步中将使用它。

2.使用 Azure CLI 创建 Linux 虚拟机

使用 cloud-init 配置文件创建 NGINX 反向代理服务器和 Express.js 服务器。 NGINX 用于将 Express.js 端口 (3000) 转发到公用端口 (80)。

  1. 创建一个名为 cloud-init-github.txt 的本地文件,并将以下内容保存到文件中,也可将该存储库的文件保存到本地计算机。 cloud-init 格式的文件需要与 Azure CLI 命令的终端路径位于同一文件夹中。

    #cloud-config
    package_upgrade: true
    packages:
      - nginx
    write_files:
      - owner: www-data:www-data
        path: /etc/nginx/sites-available/default
        content: |
          server {
            listen 80 default_server;
            server_name _;
            location / {
              # First, try if the file exists locally, otherwise request it from the app
              try_files $uri @app;
            }
            location @app {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header X-Forwarded-For $remote_addr;
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
            }
          }
    runcmd:
      # install Node.js
      - 'curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -'
      - 'sudo apt-get install -y nodejs'
      # clone GitHub Repo into myapp directory
      - 'cd /home/azureuser'
      - git clone "https://github.com/Azure-Samples/js-e2e-vm" myapp
      # Start app
      - 'cd myapp && npm install && npm start'
      # restart NGINX
      - systemctl restart nginx
    
  2. 查看文件的 runcmd 部分以了解其用途。

    runcmd 具有多个任务:

    • 下载并安装 Node.js
    • 将示例 Express.js 存储库从 GitHub 克隆到 myapp 目录中
    • 安装应用程序依赖项
    • 使用 PM2 启动 Express.js 应用

创建虚拟机资源

  1. 在终端输入 Azure CLI 命令 az vm create,以创建 Linux 虚拟机的 Azure 资源。 此命令将从 cloud-init 文件创建 VM,并为你生成 SSH 密钥。 正在运行的命令将显示密钥的存储位置。

    az vm create \
      --resource-group rg-demo-vm-eastus \
      --name demo-vm \
      --location eastus \
      --public-ip-sku Standard \
      --image UbuntuLTS \
      --admin-username azureuser \
      --generate-ssh-keys \
      --custom-data cloud-init-github.txt
    
  2. 等待此过程可能需要几分钟时间。

  3. 从响应中保留 publicIpAddress 值,需要在浏览器中查看 Web 应用并连接到 VM。 如果丢失此 IP,请使用 Azure CLI 命令 az vm list-ip-addresses 再次获取它。

  4. 该过程创建了 SSH 密钥,但它们位于响应中所述的位置。

  5. 转到该位置并创建 authorized_keys 文件:

    cd <SSH-KEY-LOCATION> && cat id_rsa >> authorized_keys
    

打开虚拟机端口

第一次创建时,虚拟机未打开任何端口。 使用以下 Azure CLI 命令打开端口 80,az vm open-port ,以便 Web 应用公开可用:

az vm open-port \
  --port 80 \
  --resource-group rg-demo-vm-eastus \
  --name demo-vm

浏览到网站

  1. 在 Web 浏览器中使用公共 IP 地址,以确保虚拟机可用且正在运行。 更改 URL 以使用 publicIpAddress 中的值。

    http://YOUR-VM-PUBLIC-IP-ADDRESS
    
  2. 如果资源由于网关错误而失败,请稍后重试,Web 应用可能需要一分钟时间才能启动。

  3. 虚拟机的 Web 应用将返回以下信息:

    • VM 名称
    • 客户端 IP
    • 当前日期/时间

    Screenshot of web browser showing simple app served from Linus virtual machine on Azure.

  4. Web 应用的初始代码文件有一个路由,该路由通过 NGINX 代理传递。

    const os = require('os');
    const express = require('express')
    const app = express()
    
    app.use('/public', express.static('public'))
    app.get('/', function (req, res) {
    
        const clientIP = req.headers['x-forwarded-for'];
        const msg = `HostName: ${os.hostname()}<br>ClientIP: ${clientIP}<br>DateTime: ${new Date()}<br><img width='200' height='200' src='/public/leaves.jpg' alt='flowers'>`
        console.log(msg)
    
        res.send(msg)
    })
    app.listen(3000, function () {
        console.log(`Hello world app listening on port 3000! ${Date.now()}`)
    })
    

3. 使用 SSH 连接 Linux 虚拟机

在本教程的此部分,请在终端中使用 SSH 连接到虚拟机。 SSH 是随多种新式 shell(包括 Azure Cloud Shell)提供的常用工具。

使用 SSH 连接并更改 Web 应用

  1. 使用以下命令连接到远程虚拟机。

    YOUR-VM-PUBLIC-IP 替换为自己的虚拟机的公共 IP。

    ssh azureuser@YOUR-VM-PUBLIC-IP
    

    此过程假设 SSH 客户端可以找到 SSH 密钥,这些密钥是在创建 VM 的过程中创建的并且放置在本地计算机上。

  2. 如果系统询问是否确定要连接、回答 yyes 继续。

  3. 使用以下命令了解你在虚拟机上的位置。 你应位于 azureuser 根目录:/home/azureuser

    pwd
    
  4. 连接完成后,终端提示应更改以指示远程虚拟机的用户名和资源名称。

    azureuser@demo-vm:
    
  5. Web 应用位于子目录 myapp。 切换到 myapp 目录并列出内容:

    cd myapp && ls -l
    
  6. 应会看到表示克隆到虚拟机和 npm 包文件中的 GitHub 存储库的内容:

    -rw-r--r--   1 root root   891 Nov 11 20:23 cloud-init-github.txt
    -rw-r--r--   1 root root  1347 Nov 11 20:23 index-logging.js
    -rw-r--r--   1 root root   282 Nov 11 20:23 index.js
    drwxr-xr-x 190 root root  4096 Nov 11 20:23 node_modules
    -rw-r--r--   1 root root 84115 Nov 11 20:23 package-lock.json
    -rw-r--r--   1 root root   329 Nov 11 20:23 package.json
    -rw-r--r--   1 root root   697 Nov 11 20:23 readme.md
    

安装监视 SDK

  1. 在连接到虚拟机的 SSH 终端中,安装适用于 Application InsightsAzure SDK 客户端库。

    sudo npm install --save applicationinsights
    
  2. 等待命令完成,然后继续。

添加监视检测密钥

  1. 在连接到虚拟机的 SSH 终端中,使用 Nano 编辑器打开 package.json 文件。

    sudo nano package.json
    
  2. APPINSIGHTS_INSTRUMENTATIONKEY 环境变量添加到 Start 脚本的开头。 在下面的示例中,将 REPLACE-WITH-YOUR-KEY 替换为检测密钥值。

    "start": "APPINSIGHTS_INSTRUMENTATIONKEY=REPLACE-WITH-YOUR-KEY pm2 start index.js --watch --log /var/log/pm2.log"
    
  3. 仍然是在 SSH 终端中,使用控件 + X 将文件保存在 Nano 编辑器中。

  4. 如果在 Nano 编辑器中出现提示,请输入 Y 进行保存。

  5. 如果在 Nano 编辑器中出现提示,请在出现提示时接受文件名。

停止 VM 以更改应用程序

Azure 客户端库现在位于 node_modules 目录中,密钥将作为环境变量传递到应用中。 下一步以编程方式使用 Application Insights。

  1. 使用以下命令停止 PM2,它是 Node.js 应用程序的生产进程管理器:

    sudo npm run-script stop 
    
  2. 使用 Application Insights 将原始文件 index.js 替换为文件。

    sudo npm run-script appinsights
    
  3. 提供了客户端库和日志记录代码。

    const express = require('express')
    const app = express()
    const os = require('os');
    
    console.log(JSON.stringify(process.env));
    
    const AppInsights = require('applicationinsights');
    
    if (process.env.APPINSIGHTS_INSTRUMENTATIONKEY) {
        console.log(`AppInsights configured with key ${process.env.APPINSIGHTS_INSTRUMENTATIONKEY}`);
    } else{
        console.log(`AppInsights not configured`);
    }
    
    AppInsights.setup(process.env.APPINSIGHTS_INSTRUMENTATIONKEY)
        .setAutoDependencyCorrelation(true)
        .setAutoCollectRequests(true)
        .setAutoCollectPerformance(true, true)
        .setAutoCollectExceptions(true)
        .setAutoCollectDependencies(true)
        .setAutoCollectConsole(true)
        .setUseDiskRetryCaching(true)
        .setSendLiveMetrics(false)
        .setDistributedTracingMode(AppInsights.DistributedTracingModes.AI)
        .start();
    
    const AppInsightsClient = AppInsights.defaultClient;
    
    
    app.get('/trace', (req, res) => {
    
        const clientIP = req.headers['x-forwarded-for'];
        const msg = `trace route ${os.hostname()} ${clientIP} ${new Date()}`;
    
        console.log(msg)
    
        if (process.env.APPINSIGHTS_INSTRUMENTATIONKEY) {
            AppInsightsClient.trackPageView();
            AppInsightsClient.trackTrace({ message: msg })
            AppInsightsClient.flush();
        } else {
            msg += ' AppInsights not configured';
        }
    
        res.send(`${msg}`)
    })
    
    app.get('/', function (req, res) {
    
        const clientIP = req.headers['x-forwarded-for'];
        const msg = `root route ${os.hostname()} ${clientIP} ${new Date()}`
    
        console.log(msg)
    
        res.send(msg)
    
    })
    app.listen(3000, function () {
        console.log(`Hello world app listening on port 3000! ${os.hostname()}`)
    })
    
  4. 使用 PM2 重启应用以选取下一个环境变量。

    sudo npm start
    

使用应用验证日志记录

  1. 在 Web 浏览器中,使用新的 trace 路由测试应用:

    http://YOUR-VM-PUBLIC-IP-ADDRESS/trace
    

    浏览器将显示响应 trace route demo-vm YOUR-CLIENT-IP VM-DATE-TIME 以及你的 IP 地址。

查看 NGINX 的日志

虚拟机(VM)收集可用于查看的 NGINX 的日志。

服务 日志位置
NGINX /var/log/nginx/access.log
  1. 仍然是在 SSH 终端中,使用以下命令查看 NGINX 代理服务的 VM 日志,以查看该日志:
cat /var/log/nginx/access.log
  1. 日志包含来自本地计算机的调用。
"GET /trace HTTP/1.1" 200 10 "-"

查看 PM2 的日志

虚拟机收集可用于查看的 PM2 的日志。

服务 日志位置
PM2 /var/log/pm2.log
  1. 查看 PM2 服务(Express.js Node Web 应用)的 VM 日志。 在同一 bash shell 中,使用以下命令查看日志:

    cat /var/log/pm2.log
    
  2. 日志包含来自本地计算机的调用。

    grep "Hello world app listening on port 3000!" /var/log/pm2.log
    
  3. 该日志还包括传入 npm 启动脚本的环境变量(包括 ApplicationInsights 密钥)。 使用以下 grep 命令验证你的密钥是否在环境变量中。

    grep APPINSIGHTS_INSTRUMENTATIONKEY /var/log/pm2.log
    

    这会显示 PM2 日志,其中以不同的颜色突出显示 APPINSIGHTS_INSTRUMENTATIONKEY

VM 日志记录和云日志记录

在此应用程序中,使用 console.log 只会将消息写入在 VM 上找到的 PM2 日志中。 如果删除日志或 VM,则会丢失该信息。

如果要在虚拟机的使用期限之外继续保留日志,请使用 Application Insights。

5.清理资源

完成本教程后,需要删除资源组,其中包含其所有资源,以确保不会再按使用量计费。

在同一终端中,使用 Azure CLI 命令 az group delete 删除资源组:

az group delete --name rg-demo-vm-eastus -y

此命令需要花费几分钟时间。

疑难解答

如果遇到问题,请参阅下表来了解如何解决问题:

问题 解决
502 网关错误 这可能表示 index.js 或 package.js 文件出现错误。 有关详细信息,请在 /var/log/pm2.log 查看 PM2 日志。 最近的错误位于文件底部。 如果确定这些文件正确,请使用 npm 脚本 package.json停止并启动 PM2。

示例代码

后续步骤