首页 > 解决方案 > Deploy an Express API written with TypeScript to Azure App Service

问题描述

I created a simple Express JS API with TypeScript and I want to utilize Azure Pipelines for both the CI and CD.

# package.json

{
  "name": "sandbox-express",
  "version": "0.1.0",
  "description": "",
  "scripts": {
    "build": "tsc -p .",
    "build:live": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/index.ts",
    "start": "npm run build:live",
    "test": "mocha"
  },
  "dependencies": {
    "dotenv": "^8.2.0",
    "express": "^4.17.1"
  },
  "devDependencies": {
    "@types/express": "^4.17.6",
    "@types/mocha": "^7.0.2",
    "@types/node": "^14.0.1",
    "mocha": "^7.1.2",
    "nodemon": "^2.0.4",
    "ts-node": "^8.10.1",
    "typescript": "^3.9.2"
  }
}

# src/app.ts

import express, { Application, Request, Response } from "express";

const app: Application = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get("/", (req: Request, res: Response) => {
  return res.status(200).json({ message: "Hello!" });
});

export { app };

# src/config.ts

import dotenv from "dotenv";
dotenv.config();

# index.ts

import { app } from "./app";
import "./config";

const PORT: string | undefined = process.env.PORT;

if (!PORT) {
  console.error("PORT is not set!!!");
  process.exit(1);
}

app.listen(Number(PORT), (): void => {
  console.log(`Listening on port ${PORT}`);
});

My code is hosted in Azure DevOps and I also added an azure-pipelines.yml file to trigger a CI build when I push to develop branch. This is what's inside my YAML file.

# azure-pipelines.yml

trigger:
  batch: true
  branches:
    include:
      - develop

variables:
  # Node.js version
  NODE_VERSION: "12.16.3"

  # Agent VM image name
  VM_IMAGE_NAME: "ubuntu-16.04"

stages:
  - stage: Build
    displayName: "Build stage"
    jobs:
      - job: Build
        displayName: "Build"
        pool:
          vmImage: $(VM_IMAGE_NAME)

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

          - script: |
              npm install
              npm run build
            displayName: "npm install and build"

          - task: CopyFiles@2
            displayName: "Copy package.json file"
            inputs:
              sourceFolder: "$(System.DefaultWorkingDirectory)"
              contents: "package.json"
              targetFolder: "$(Build.ArtifactStagingDirectory)"
              cleanTargetFolder: true

          - task: CopyFiles@2
            displayName: "Copy built files"
            inputs:
              sourceFolder: "dist"
              contents: "**"
              targetFolder: "$(Build.ArtifactStagingDirectory)"

          - task: PublishBuildArtifacts@1
            displayName: "Publish artifact"
            inputs:
              pathtoPublish: $(Build.ArtifactStagingDirectory)
              artifactName: "drop"              

The CI build is successful and I get the copied files insize the build artifacts; 4 files -- package.json, app.js, config.js and index.js; inside the drop folder.

For the CD part, I did it through Azure Pipelines - Releases UI builder in Azure DevOps.

Artifacts

Deployment Process

Agent Job

Task: Deploy Azure App Service

The CD also succeeds in deploying but when I access the web application via the browser, I get a 503 response error.

:( Application Error

If you are the application administrator, you can access the diagnostic resources.

In Azure Portal, I go to my App Service resource and check the "Diagnose and solve problems" section and I get an error like so:

(!) Application logs from instance: RD0003FF65FA79 contain an error or a warning
[1] 2020-05-21T06:43:45        Cannot find module 'express'

And also the following error log:

2020-05-21T06:43:31.232273271Z   /  _  \ __________ _________   ____  
2020-05-21T06:43:31.232279871Z  /  /_\  \___   /  |  \_  __ \_/ __ \ 
2020-05-21T06:43:31.232284571Z /    |    \/    /|  |  /|  | \/\  ___/ 
2020-05-21T06:43:31.232289071Z \____|__  /_____ \____/ |__|    \___  >
2020-05-21T06:43:31.232294071Z         \/      \/                  \/ 
2020-05-21T06:43:31.232298571Z A P P   S E R V I C E   O N   L I N U X
2020-05-21T06:43:31.232302771Z 
2020-05-21T06:43:31.232306871Z Documentation: http://aka.ms/webapp-linux
2020-05-21T06:43:31.232310871Z NodeJS quickstart: https://aka.ms/node-qs
2020-05-21T06:43:31.232314871Z NodeJS Version : v12.13.0
2020-05-21T06:43:31.232318872Z Note: Any data outside '/home' is not persisted
2020-05-21T06:43:31.232323072Z 
2020-05-21T06:43:31.831224489Z Oryx Version: 0.2.20191105.2, Commit: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, ReleaseTagName: 20191105.2
2020-05-21T06:43:31.836152521Z Cound not find build manifest file at '/home/site/wwwroot/oryx-manifest.toml'
2020-05-21T06:43:31.837351129Z Could not find operation ID in manifest. Generating an operation id...
2020-05-21T06:43:31.837760531Z Build Operation ID: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2020-05-21T06:43:33.641043173Z Writing output script to '/opt/startup/startup.sh'
2020-05-21T06:43:34.254679097Z Running #!/bin/sh
2020-05-21T06:43:34.255203901Z 
2020-05-21T06:43:34.255216701Z # Enter the source directory to make sure the script runs where the user expects
2020-05-21T06:43:34.255222301Z cd "/home/site/wwwroot"
2020-05-21T06:43:34.255226602Z 
2020-05-21T06:43:34.255230802Z export NODE_PATH=$(npm root --quiet -g):$NODE_PATH
2020-05-21T06:43:34.256123609Z if [ -z "$PORT" ]; then
2020-05-21T06:43:34.256135909Z         export PORT=8080
2020-05-21T06:43:34.256141209Z fi
2020-05-21T06:43:34.256145309Z 
2020-05-21T06:43:34.256901516Z PATH="$PATH:/home/site/wwwroot" node index.js
2020-05-21T06:43:36.534911269Z internal/modules/cjs/loader.js:797
2020-05-21T06:43:36.534950269Z     throw err;
2020-05-21T06:43:36.534956469Z     ^
2020-05-21T06:43:36.534960769Z 
2020-05-21T06:43:36.534964969Z Error: Cannot find module 'express'
2020-05-21T06:43:36.534969069Z Require stack:
2020-05-21T06:43:36.534973169Z - /home/site/wwwroot/app.js
2020-05-21T06:43:36.534977369Z - /home/site/wwwroot/index.js
2020-05-21T06:43:36.534981569Z     at Function.Module._resolveFilename (internal/modules/cjs/loader.js:794:15)
2020-05-21T06:43:36.534986269Z     at Function.Module._load (internal/modules/cjs/loader.js:687:27)
2020-05-21T06:43:36.534997169Z     at Module.require (internal/modules/cjs/loader.js:849:19)
2020-05-21T06:43:36.535001569Z     at require (internal/modules/cjs/helpers.js:74:18)
2020-05-21T06:43:36.535005669Z     at Object. (/home/site/wwwroot/app.js:7:33)
2020-05-21T06:43:36.535010169Z     at Module._compile (internal/modules/cjs/loader.js:956:30)
2020-05-21T06:43:36.535014269Z     at Object.Module._extensions..js (internal/modules/cjs/loader.js:973:10)
2020-05-21T06:43:36.535018569Z     at Module.load (internal/modules/cjs/loader.js:812:32)
2020-05-21T06:43:36.535022569Z     at Function.Module._load (internal/modules/cjs/loader.js:724:14)
2020-05-21T06:43:36.535026770Z     at Module.require (internal/modules/cjs/loader.js:849:19) {
2020-05-21T06:43:36.535031070Z   code: 'MODULE_NOT_FOUND',
2020-05-21T06:43:36.535035170Z   requireStack: [ '/home/site/wwwroot/app.js', '/home/site/wwwroot/index.js' ]
2020-05-21T06:43:36.535039370Z }

So I guess my question is how do I install Express and all the required packages in the Release CD section of my pipeline?

标签: typescriptazureexpressazure-pipelinesazure-devops-rest-api

解决方案


推荐阅读