65.9K
CodeProject 正在变化。 阅读更多。
Home

将容器部署到 Azure 上的 Kubernetes

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (5投票s)

2021 年 4 月 15 日

CPOL

8分钟阅读

viewsIcon

4771

在本文中,我们将通过构建一个容器化的 Node.js 应用程序组件来实现更进一步。

上一篇文章中,我们探讨了如何将我们的云原生应用程序扩展到 Azure Kubernetes 服务,并让 Azure DevOps 同时自动构建和部署应用程序及基础架构。

在本文中,我们将通过构建一个容器化的 Node.js 应用程序组件来实现更进一步。我们将首先为我们的 Functions 构建一个简单的 Node.js 前端,然后使用 Docker 将应用程序容器化并将其放入我们自己的私有容器注册表中。最后,我们将容器添加到 Kubernetes 集群中的一个 pod。

通过本文的学习,我们将拥有一个代码仓库中包含用于无服务器 Azure Functions 和更传统的 Node.js 应用程序的应用程序组件,并使用 Azure DevOps 进行部署。

构建 Node.js 应用程序

在我们开始使用 Docker 构建容器之前,让我们使用 Express.js 框架构建一个简单的 Node.js 前端应用程序。在此项目中,我们只构建一个简单的页面来检索 Functions 的任务列表,但您可以随时进一步扩展它。

首先,我们在代码库中创建一个名为 NodeServer 的新文件夹。在此文件夹中,我们运行以下命令来设置我们的应用程序

  • npm init
  • npm install express
  • npm install axios

这三个命令将初始化我们的应用程序,并安装我们需要的两个用于托管网页(Express)和从 API(Axios)检索数据的包。完成后,创建一个名为 app.js 的新文件并输入以下代码

const express = require('express')
const axios = require('axios').default;
const app = express()
const port = 3001

app.listen(port, () => {
  console.log(`Example app listening at https://:${port}`)
})

app.get('/', function (req, res) {

    try {
        const response = axios({
            method: 'get',
            url: 'https://tsfunctionexample.azurewebsites.net/api/ListTasks',
            data: { username: 'testuser' }
        }).then(function (response){
            var returnPage = "<html><body><h1>Task List</h1>";
            if(response.status = '200'){
                returnPage += '<table style="width:100%"><tr><th>Username</th><th>Task ID</th><th>Task Name</th><th>Due Date</th></tr>';
                response.data.results.forEach(element => {
                    returnPage += '<tr><td>' + element.username + '</td><td>' + element.taskID + '</td><td>' + element.name + '</td><td>' + element.dueDate + '</td></tr>';
                });
                returnPage += '</table>';
            } else {
                returnPage += '<p>Error - No Tasks Found</p>';
            }
            returnPage += '</body></html>';
            console.log(response);
            res.send(returnPage);
        });
    } catch (error) {
        console.error(error);
    }
});

简要地分析这段代码,首先我们加载 Express 并让它监听端口 3001。然后我们使用 `.get()` 函数监听 URL 的根或主页的 GET 请求。收到请求后,我们使用 Axios 调用我们的 List tasks API(我们的 API 托管在 https://tsfunctionexample.azurewebsites.net/api/ListTasks),并将之前使用的用户名 'testuser' 作为请求体发送。

当响应返回时,我们迭代列表中的所有结果,将它们格式化为简单的 HTML 表格,然后返回。如果我们保存此文件并尝试使用命令 `node app.js` 运行代码,我们将看到一个从 Functions 返回的任务列表。

值得注意的是,如果您有一段时间没有使用过 Functions,此页面可能需要一些时间才能返回。这是因为 Functions 在一段时间未使用后需要时间进行预热。

从这里开始,我们可以为 post、delete 和 get 特定任务添加额外的函数,但是,让我们先将此应用程序容器化。

Docker 容器

Docker 使您能够将应用程序及其所有依赖项打包到容器中。这包括一个基本版本的操作系统(通常是 Linux)、任何依赖项(如 Node 和 npm)、您的应用程序及其所需的库。

容器是您应用程序的运行实例,而蓝图是构建容器的配置。让我们为当前应用程序构建一个蓝图并在本地运行容器进行测试。确保您已安装并准备好 Docker

首先,创建一个名为 Dockerfile 的新文件来制作我们的容器蓝图。打开此文件并使用以下代码

FROM node:14
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3001
CMD [ "node", "app.js"]

大多数容器蓝图都应该相当简单,并遵循类似的模式。第一行定义了我们正在构建的源镜像,在本例中是 Node 版本 14 镜像。

接下来,我们指定在此镜像中查找应用程序代码的位置。因为此源镜像包含 Node 和 npm,所以我们可以通过复制现有的包文件并运行 npm install 来安装所有依赖项。

接下来,我们通过将当前本地目录复制到当前工作目录来复制应用程序代码。

最后,我们允许容器打开端口 3001 并使用 CMD 运行 Node,并将我们的 app.js 指定为参数。

在构建和运行我们的 Docker 镜像之前,我们还应该创建一个忽略文件,以便我们的本地调试和包不会被复制过来。为此,请创建 .dockerignore 文件并添加以下内容

node_modules
npm-debug.log

完成后,我们现在使用以下命令构建我们的 Docker 镜像

docker build -t <username>/node-server .

Docker 会遍历并编译所有必需的组件,并将其保存到本地注册表中。您可以使用命令 `docker images` 查看本地注册表中的所有镜像。

一旦我们的 Docker 镜像进入本地注册表,我们现在就可以使用以下命令启动一个容器来运行它

docker run -p 80:3001 <username>/node-server

此命令使用 `-d repository/image-name` 运行镜像,但还使用 `-p` 参数指定将来自端口 80 的任何流量重定向到容器中的端口 3001。现在运行 docker ps 命令,我们应该看到我们的容器正在运行,并且端口已转发。

当我们打开 Web 浏览器访问 https:// 时,应该会显示我们的任务管理器应用程序。

使用 Azure 容器注册表

因此,我们的应用程序已通过 Docker 容器化,容器镜像在我们的本地注册表中,并且 Docker 可以在本地构建和运行容器。现在,让我们构建容器镜像并将其存储在 Azure 容器注册表中,以便 AKS 可以托管该容器。首先,我们需要创建一个容器注册表,方法是打开 Azure 门户,转到命令 shell,然后键入以下命令

az acr create --resource-group TSFunctionTutorial --name tsfunctionRegistry --sku Basic

我们的容器注册表创建完成后,我们现在可以为我们的基础架构管道添加一个额外的步骤来构建我们的容器镜像。

要访问 Docker 注册表,我们通过打开 Azure DevOps 中的项目,选择项目设置,然后打开服务连接来创建一个额外的服务主体。

单击新建服务连接,从连接选项中选择 Docker 注册表,然后找到您订阅中的 Azure 容器注册表。

设置好服务主体后,我们打开管道并编辑上一篇文章中创建的任务管理器管道。

在“部署基础架构”阶段下,在“部署 AKS 群集”作业下添加一个新作业,使用以下代码

- stage: BuildContainer
  displayName: Build Container
  jobs:
  - job: Build
    displayName: Build
    pool:
      vmImage: $(vmImageName)
    steps:
    - task: Docker@2
      displayName: Build and push an image to container registry
      inputs:
        command: buildAndPush
        repository: tsfunctionnodeserver
        dockerfile: '**/Dockerfile'
        containerRegistry: TSFunctionRegistry

此作业是一个单一步骤,使用 `Docker@2` 任务来构建和推送我们的镜像。这里的关键输入是我们的 Docker 镜像的名称(存储库)、Docker 文件的位置以及我们创建的容器注册表的名称。保存并提交更改,然后运行管道以构建和部署应用程序组件。

完成后,当我们转到 Azure 门户中的容器注册表并查看存储库时,我们应该会看到我们的 docker 镜像正在等待部署。

部署到 Kubernetes

现在我们的 Docker 镜像已构建并位于注册表中,我们可以设置一个选项将我们的容器部署到集群。

首先,我们需要为我们的集群创建另一个服务连接。

转到项目设置,然后选择服务连接,并创建一个新的 Kubernetes 服务连接。选择 Azure 订阅选项、AKS 集群和默认命名空间。我们将服务连接命名为 TSFunctionCluster,稍后将使用它。

现在我们已经创建了一个服务主体,我们需要创建清单文件来部署我们的容器。

创建一个名为 manifests 的新目录,并创建一个名为 *deployment.yaml* 的新文件,其中包含以下代码

apiVersion : apps/v1
kind: Deployment
metadata:
  name: tsfunctionnodeserver 
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tsfunctionnodeserver
  template:
    metadata:
      labels:
        app: tsfunctionnodeserver 
    spec:
      containers:
        - name: tsfunctionnodeserver 
          image: tsfunctionregistry.azurecr.io/tsfunctionnodeserver
          ports:
          - containerPort: 80

这会部署我们的容器,对其可以使用的资源没有任何实际限制,并在端口 80 上开放。接下来,我们创建一个包含以下代码的 *service.yaml* 文件

apiVersion: v1
kind: Service
metadata:
    name: tsfunctionnodeserver
spec:
    type: LoadBalancer
    ports:
    - port: 80
    selector:
        app: tsfunctionnodeserver

此组件为我们的容器添加了网络功能,以便我们可以从集群外部访问它。

我们还需要向管道添加另外两个阶段,以配置 Kubernetes 密钥并从 ACR 拉取镜像。将以下代码添加到您的 *azure-pipelines.yaml* 文件中 BuildContainer 阶段之后

- stage: DeployContainer
  displayName: Deploy Container
  jobs:
  - job: Deploy
    displayName: Deploy Container
    pool:
      vmImage:  $(vmImageName)
    steps:
    - task: KubernetesManifest@0
      inputs:
        action: 'createSecret'
        kubernetesServiceConnection: 'TSFunctionCluster'
        namespace: 'default'
        secretType: 'dockerRegistry'
        secretName: 'tsfunction-access'
        dockerRegistryEndpoint: 'TSFunctionRegistry'
    - task: KubernetesManifest@0
      inputs:
        action: 'deploy'
        kubernetesServiceConnection: 'TSFunctionCluster'
        namespace: 'default'
        manifests: |
                $(Build.SourcesDirectory)/manifests/deployment.yaml
                $(Build.SourcesDirectory)/manifests/service.yaml
        containers: 'tsfunctionnodeserver'
        imagePullSecrets: 'tsfunction-access'

此阶段中的第一个任务创建了我们需要从存储库中拉取容器镜像的密钥。它使用 Docker 注册表服务连接和 Kubernetes 服务连接来链接访问。

第二个任务使用这两个清单文件将镜像部署到集群中的容器。

保存这些文件并签入您的更改以运行管道。管道部署完成后,当我们通过 Azure 门户打开集群,单击工作负载,然后单击 Pod 时,您应该会看到一个名为 tsfunctionnodeserver 的 pod 正在启动。当您单击此 pod 并复制 IP 地址时,您应该能够使用它来访问您的任务管理前端应用程序。

后续步骤

通过本系列文章,我们使用无服务器 Azure Functions 和容器化 Node.js 应用程序组件的组合构建了一个简单的任务管理应用程序。我们的所有代码、基础架构配置和管道配置都存储在 Git 存储库中,作为 Azure DevOps 的一部分。

您可以通过使用单页应用程序 (SPA) 构建前端、添加安全性以及完全自动化部署来进一步扩展此应用程序。阅读完本系列文章并了解 Azure 与其他云工具配合使用时能做什么之后,您现在可以构建和部署自己独特的无服务器应用程序。

如果您准备深入了解在 Azure 上构建云原生 Node.js 应用程序,请查看下一个系列文章。

要了解有关在 Azure 上使用 Kubernetes 的更多信息,请浏览 The Kubernetes Bundle | Microsoft AzureHands-On Kubernetes on Azure | Microsoft Azure

© . All rights reserved.