如何利用持续集成在Kubernetes上部署网络应用程序

188 阅读5分钟

本教程包括:

  1. 使用CircleCI的轨道创建一个持续的集成管道
  2. 在你推送代码到你的 repo 后触发管道
  3. 在Azure Kubernetes服务上部署一个Node.js应用程序

容器和微服务已经彻底改变了在云上部署应用程序的方式。自2014年推出以来,Kubernetes已经成为容器编排工具的事实标准。

在本教程中,你将学习如何通过持续集成和持续部署(CI/CD)Azure Kubernetes服务(AKS)上部署一个Node.js应用程序。你将使用CircleCI orbs创建一个CI/CD管道,CircleCI orbs是可重复使用的YAML配置包,它将重复的配置部分浓缩成一行代码。在你把代码推送到GitHub仓库后,你的管道将被自动触发。使用这种自动化,你将始终有最新版本的应用程序在Kubernetes集群上运行。

克隆Node.js应用程序

在本教程中,你的主要重点是在Kubernetes上部署应用程序。因此,你可以直接克隆 Node.js应用程序到你的GitHub上,然后继续完成剩下的过程。

要克隆该项目,请运行。

git clone https://github.com/CIRCLECI-GWP/nodejs-aks-deploy.git 

这个仓库包含Node.js应用的代码,以及我们将在本教程中创建的所有YAML文件。

Node.js应用程序住在app.js 文件中。

const express = require("express");
const path = require("path");
const morgan = require("morgan");
const bodyParser = require("body-parser");

/* eslint-disable no-console */

const port = process.env.PORT || 1337;
const app = express();

app.use(morgan("dev"));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: "true" }));
app.use(bodyParser.json({ type: "application/vnd.api+json" }));

app.use(express.static(path.join(__dirname, "./")));

app.get("*", (req, res) => {
  res.sendFile(path.join(__dirname, "./index.html"));
});

app.listen(port, (err) => {
  if (err) {
    console.log(err);
  } else {
    console.log(`App at: http://localhost:${port}`);
  }
});
module.exports = app;

从这段代码中得到的关键信息是应用程序将运行的端口号,即1337

你可以通过首先安装依赖项在本地运行该应用程序。在项目的根目录下,输入。

npm install

然后用该命令运行该应用程序。

node app.js

现在应用程序应该已经启动并运行在http://localhost:1337

现在在你的GitHub账户上为这个项目创建一个新的仓库,并将项目推送到你刚刚创建的仓库中。

容器化Node.js应用程序

要将应用程序部署到Kubernetes,你必须先容器化。使用Docker作为容器运行时工具,创建一个Docker文件。Dockerfile是一个文本文件,包含了用户可以在命令行上调用的所有命令,以组装一个镜像。

在项目的根目录下创建一个新文件,命名为Dockerfile 。在该文件中复制以下内容。

# Set the base image to use for subsequent instructions
FROM node:alpine

# Set the working directory for any subsequent ADD, COPY, CMD, ENTRYPOINT,
# or RUN instructions that follow it in the Dockerfile
WORKDIR /usr/src/app

# Copy files or folders from source to the dest path in the image's filesystem.
COPY package.json /usr/src/app/
COPY . /usr/src/app/

# Execute any commands on top of the current image as a new layer and commit the results.
RUN npm install --production

# Define the network ports that this container will listen to at runtime.
EXPOSE 1337

# Configure the container to be run as an executable.
ENTRYPOINT ["npm", "start"]

如果你安装了Docker,你可以在本地构建并运行容器进行测试。

在本教程的后面,你将学习如何用CircleCI orbs来自动完成这个过程。

要构建和标记容器,你可以输入。

docker build -t nodejs-aks-app:latest .

从你的终端运行这个命令,确认镜像已经成功创建。

docker images

然后用该命令运行容器。

docker run -it -p 1337:1337 nodejs-aks-app:latest

应用程序现在应该已经启动并运行在http://127.0.0.1:1337

提交并推送修改内容到你的GitHub仓库。

配置Kubernetes清单以进行部署

要在Kubernetes上部署容器,你需要配置Kubernetes以纳入运行应用程序所需的所有设置。Kubernetes使用YAML进行配置。

在项目的根目录下创建一个名为manifests 的目录。

然后,在新创建的文件夹中创建以下文件。

  • namespace.yaml
  • deployment.yaml
  • service.yaml
  • kustomization.yaml

namespace.yaml 在Kubernetes中,命名空间提供了一种机制,用于隔离单个集群中的资源组。
文件的内容如下。

apiVersion: v1
kind: Namespace
metadata:
  name: nodejs
  labels:
    name: nodejs

这个文件将在Kubernetes集群内创建一个名为nodejs 的命名空间。所有的资源都将在这个命名空间中创建。

Kubernetes部署管理集群上运行的无状态服务。它们的目的是保持一组相同的pods运行,并以可控的方式升级它们--默认执行滚动更新。deployment.yaml 的内容如下。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nodejs
  namespace: nodejs
  labels:
    app: nodejs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nodejs
  template:
    metadata:
      labels:
        app: nodejs
    spec:
      nodeSelector:
        "beta.kubernetes.io/os": linux
      containers:
        - name: nodejs-aks-app
          image: nodejs-aks-app
          ports:
            - name: http
              containerPort: 1337

以下是这段代码的主要收获。

  • containerPort 是应用程序将在其上运行的端口。
  • 容器image ,是将在Kubernetes集群上的所述命名空间中拉出并部署的Docker镜像。

Kubernetes服务是一个抽象概念,它定义了一组逻辑上的pod和一个访问它们的策略。你需要一个类型为LoadBalancer 的Kubernetes服务,以使部署能够被外部世界所访问。service.yaml 的内容如下。

apiVersion: v1
kind: Service
metadata:
  name: nodejs
  namespace: nodejs
  labels:
    app: nodejs
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 1337
  selector:
    app: nodejs

以下是这段代码的主要内容。

  • targetPort 是容器的端口。
  • port 是应用程序将要运行的地方。
  • type 是服务的类型(本例中为 )。LoadBalancer

为了在Kubernetes集群上部署应用程序的最新版本,必须对资源进行定制,以保持更新的信息。这是由Kustomize管理的,它是定制Kubernetes配置的工具。kustomization.yaml 的内容如下。

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - service.yaml
  - namespace.yaml
namespace: nodejs
images:
  - name: nodejs-aks-app
    newName: nodejs-aks-app
    newTag: v1

这里的关键是,在持续集成过程中,newNamenewTag 将自动更新最新的Docker镜像信息。

提交并推送这些文件到你之前克隆的GitHub仓库。

启动Azure Kubernetes服务(AKS)集群

现在你已经准备好在AKS集群上部署应用程序了。要创建AKS集群,你需要一个Microsoft Azure账户,并在你的电脑上安装Azure CLI。CLI应该连接到你的Azure账户

完成后,你可以在Azure CLI的帮助下启动AKS集群。

用以下命令创建一个资源组。

az group create --name NodeRG --location eastus

用这个命令启动一个双节点集群。

az aks create --resource-group NodeRG --name NodeCluster --node-count 2 --enable-addons http_application_routing

注意: 如果你之前在系统中生成了任何SSH密钥,你需要在上述命令中添加一个可选的参数--generate-ssh-keys 。如果缺少SSH公钥和私钥文件,这将自动生成这些文件。这些密钥将被存储在~/.ssh 目录中。

AKS集群将需要10-15分钟来启动。

创建持续集成管道

本教程的目的是展示如何通过CI/CD管道在Kubernetes上部署应用程序。该管道应触发构建容器的过程,将其推送到Dockerhub并在集群上部署。

为了创建CI/CD管道,你将使用与你的GitHub账户集成的CircleCI。CircleCI配置文件(config.yml)位于项目根目录下的.circleci 目录中。该配置的路径是.circleci/config.yml

下面是config.yml 的内容。

version: 2.1

orbs:
  docker: circleci/docker@2.1.1
  azure-aks: circleci/azure-aks@0.3.0
  kubernetes: circleci/kubernetes@1.3.0

jobs:
  aks-deploy:
    executor: azure-aks/default
    parameters:
      cluster-name:
        description: |
          Name of the AKS cluster
        type: string
      resource-group:
        description: |
          Resource group that the cluster is in
        type: string
    steps:
      - checkout
      - run:
          name: Pull Updated code from repo
          command: git pull origin $CIRCLE_BRANCH
      - azure-aks/update-kubeconfig-with-credentials:
          cluster-name: << parameters.cluster-name >>
          install-kubectl: true
          perform-login: true
          resource-group: << parameters.resource-group >>
      - kubernetes/create-or-update-resource:
          resource-file-path: manifests/$APP_NAME.yaml
          resource-name: kustomization/$APP_NAME

  bump-docker-tag-kustomize:
    docker:
      - image: cimg/base:stable
    steps:
      - run:
          name: Install kustomize
          command: |
            URL=https://github.com/kubernetes-sigs/kustomize/releases/download/kustomize/v4.5.2/kustomize_v4.5.2_linux_amd64.tar.gz
            curl -L $URL | tar zx
            [ -w /usr/local/bin ] && SUDO="" || SUDO=sudo
            $SUDO chmod +x ./kustomize
            $SUDO mv ./kustomize /usr/local/bin
      - checkout
      - run:
          name: Bump Docker Tag
          command: |
            cd manifests
            kustomize edit set image $APP_NAME=$DOCKER_LOGIN/$APP_NAME:$CIRCLE_SHA1
            kustomize build . > $APP_NAME.yaml
      - add_ssh_keys:
          fingerprints:
            - "$SSH_FINGERPRINT"
      - run:
          name: Commit & Push to GitHub
          command: |
            git config user.email "$GITHUB_EMAIL"
            git config user.name "CircleCI User"
            git checkout $CIRCLE_BRANCH           
            git add manifests/$APP_NAME.yaml
            git add manifests/kustomization.yaml
            git commit -am "Bumps docker tag [skip ci]"
            git push origin $CIRCLE_BRANCH

workflows:
  Deploy-App-on-AKS:
    jobs:
      - docker/publish:
          image: $DOCKER_LOGIN/$APP_NAME
          tag: $CIRCLE_SHA1,latest
      - bump-docker-tag-kustomize:
          requires:
            - docker/publish
      - aks-deploy:
          cluster-name: $CLUSTER_NAME
          resource-group: $RESOURCE_GROUP
          requires:
            - bump-docker-tag-kustomize

CI工作流程由三个工作组成。

  • docker/publish 构建容器并将其推送到Docker Hub。
  • bump-docker-tag-kustomize 更新Docker Image Tag并生成一个综合的Kubernetes配置文件。
  • aks-deploy 在AKS集群上应用该配置文件。

这个工作流程广泛地使用了CircleCI orbs,它是开源的、可共享的、可参数化的、可重用的配置元素包,包括作业、命令和执行器。这些orbs被直接使用或用于创建自定义作业。

提交并推送修改内容到你的GitHub仓库。

在CircleCI上设置项目

将您的应用程序部署到AKS的下一步是将您GitHub仓库中的应用程序连接到CircleCI。

进入您的CircleCI仪表板,选择左侧面板上的项目标签。点击与包含代码的GitHub仓库相对应的Set Up Project按钮。在本教程中,该仓库被命名为nodejs-aks-deploy

CircleCI Dashboard

当提示你选择config.yml文件时,选择Fastest选项并输入main 作为分支名称。CircleCI将自动定位到config.yml 文件。点击Set Up Project

Select your config.yml file

工作流程将开始运行,但很快它就会把status 显示为Failed 。这是因为你仍然需要在CircleCI项目设置中设置一个用户密钥和配置环境变量。

要设置用户密钥,从项目设置页面的左侧面板选择SSH密钥选项。在User Key部分,点击Authorize with GitHub,CircleCI会在工作流程的执行过程中使用该用户密钥,代表仓库所有者向您的GitHub账户推送更改。

User Key

要配置环境变量,从项目设置页面的左侧面板中选择环境变量选项。选择添加环境变量。接下来,键入环境变量和你希望分配给它的值。

Environment variables

文件中使用的环境变量有。

  • APP_NAME 是容器镜像名称( )。nodejs-aks-app
  • AZURE_PASSWORD 是你的Azure帐户密码。
  • AZURE_USERNAME 是你的Azure帐户用户名。
  • CLUSTER_NAME 是AKS集群名称( )。NodeCluster
  • DOCKER_LOGIN 是你的Docker Hub用户名。
  • DOCKER_PASSWORD 是你的Docker Hub密码。
  • GITHUB_EMAIL 是你的GitHub账户的电子邮件地址。
  • RESOURCE_GROUP 是AKS资源组 ( )。NodeRG
  • SSH_FINGERPRINT 是用于向GitHub推送更新的Docker标签的用户密钥的SSH Fingerprint。

注意: 要找到SSH_FINGERPRINT ,请进入项目设置,从侧边栏选择SSH密钥。向下滚动到用户密钥部分,然后复制该密钥。这个密钥只有在你点击 "用GitHub授权"按钮后才会显示。

现在你可以重新运行工作流程了。这次statusSuccess

Successful workflow

你还会发现另一条流水线上的statusNot Run 。之所以出现这种情况,是因为在提交信息中包括[skip ci] 这一术语,明确指示CircleCI在向GitHub提交更新的配置文件时跳过该管道。这就保护了工作流程不会出现自我触发的无休止循环。

在AKS上访问应用程序

当工作流重新运行时,收到一个成功的status ,这意味着应用程序已经被部署在AKS集群上。要访问该应用程序,你需要集群的外部IP地址。

要找到External-IP ,你可以再次使用Azure CLI。

配置kubectl ,用这个命令连接到AKS。

az aks get-credentials --resource-group NodeRG --name NodeCluster

你在nodejs 命名空间中创建了所有的资源,所以使用下面的命令来获取该命名空间中的所有资源。

kubectl get all --namespace nodejs

复制External-IP 对应的service/nodejs

External-IP

你可以在http://<EXTERNAL-IP> 上访问该应用程序。在我的例子中,那就是http://20.102.11.73/

Final Application

总结

恭喜你!你已经达到了本教程的终点。你已经到达了本教程的终点。在本教程中,你学到了如何开发一个自动化CI管道,以便在Kubernetes集群上连续部署你的应用程序。一旦管道配置正确,对应用程序代码的任何修改都会立即反映在应用程序的URL上。没有必要再手动配置和部署Kubernetes上的应用程序了。你可以改变环境变量的值,将CircleCI配置文件用于类似的应用程序。