MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C#Docker容器化部署与Kubernetes集成

2022-02-013.8k 阅读

C# 应用程序的 Docker 容器化

1. 了解 Docker 基础知识

Docker 是一个开源的应用容器引擎,它允许开发者将应用程序及其依赖打包到一个可移植的容器中,然后发布到任何支持 Docker 的服务器上。Docker 容器是轻量级的、可执行的软件包,包含了运行应用程序所需的一切:代码、运行时、系统工具、系统库和设置。

Docker 镜像则是一个只读的模板,用于创建 Docker 容器。镜像是基于层构建的,每个层代表对镜像的一次更改。这种分层结构使得镜像非常高效,因为多个镜像可以共享相同的层,只有不同的部分需要存储额外的数据。

2. 创建 C# 项目

首先,确保你已经安装了 .NET SDK。打开命令行工具,使用以下命令创建一个新的 C# Web 应用程序项目:

dotnet new web -n MyWebApp

这将在当前目录下创建一个名为 MyWebApp 的新 Web 应用程序项目。进入项目目录:

cd MyWebApp

你可以使用你喜欢的代码编辑器(如 Visual Studio Code)打开这个项目。在项目中,Controllers 文件夹下的 WeatherForecastController.cs 提供了一个简单的 API 示例。

3. 创建 Dockerfile

在项目根目录下创建一个名为 Dockerfile 的文件,这是用于构建 Docker 镜像的指令文件。以下是一个基本的 Dockerfile 示例:

# 使用官方的 .NET SDK 镜像作为基础镜像
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build

# 设置工作目录
WORKDIR /app

# 将项目文件复制到容器中
COPY . .

# 还原项目依赖
RUN dotnet restore

# 构建项目
RUN dotnet build -c Release -o /app/build

# 使用官方的 .NET 运行时镜像作为最终镜像
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS runtime

# 设置工作目录
WORKDIR /app

# 复制构建输出到运行时镜像
COPY --from=build /app/build .

# 暴露应用程序的端口(这里假设是 80)
EXPOSE 80

# 定义容器启动时执行的命令
ENTRYPOINT ["dotnet", "MyWebApp.dll"]

在上述 Dockerfile 中:

  • 首先使用 mcr.microsoft.com/dotnet/sdk:6.0 作为基础镜像,这是官方的 .NET SDK 镜像,用于构建项目。
  • 设置工作目录为 /app,并将项目文件复制到容器内。
  • 运行 dotnet restore 还原项目依赖,dotnet build 构建项目,并将构建输出放置在 /app/build 目录。
  • 然后使用 mcr.microsoft.com/dotnet/aspnet:6.0 作为运行时镜像,它只包含运行 .NET 应用程序所需的组件,体积更小。
  • 复制构建输出到运行时镜像的 /app 目录,暴露端口 80,并设置容器启动时运行 dotnet MyWebApp.dll

4. 构建 Docker 镜像

在项目根目录下,打开命令行工具,执行以下命令来构建 Docker 镜像:

docker build -t mywebapp:latest .

这里 -t 选项用于指定镜像的标签,mywebapp:latest 表示镜像名称为 mywebapp,标签为 latest。最后的 . 表示 Dockerfile 在当前目录。

构建过程可能需要一些时间,因为它需要下载基础镜像并在容器内执行构建操作。构建完成后,你可以使用 docker images 命令查看已构建的镜像:

docker images

你应该能看到 mywebapp:latest 镜像。

5. 运行 Docker 容器

构建好镜像后,就可以运行容器了。执行以下命令:

docker run -d -p 8080:80 mywebapp:latest

这里 -d 选项表示在后台运行容器,-p 8080:80 表示将主机的 8080 端口映射到容器的 80 端口。这样,你就可以通过 http://localhost:8080 在浏览器中访问你的 C# Web 应用程序。

Kubernetes 基础与集群搭建

1. 理解 Kubernetes

Kubernetes(简称 K8s)是一个开源的容器编排平台,用于自动化容器化应用的部署、扩展和管理。它提供了一种在集群中部署、管理和扩展容器化应用的标准化方式。

Kubernetes 的核心概念包括:

  • Pod:Kubernetes 中最小的可部署和可管理的计算单元,一个 Pod 可以包含一个或多个紧密相关的容器。这些容器共享网络和存储资源,并且在同一个节点上运行。
  • Service:一种抽象,用于定义一组 Pod 的逻辑集合以及访问它们的策略。Service 可以通过 IP 地址和端口号来暴露 Pod,使得其他 Pod 或外部客户端可以访问这些 Pod。
  • Deployment:用于描述 Pod 和 ReplicaSet 的更新策略。它可以确保指定数量的 Pod 副本始终在运行,并管理 Pod 的滚动更新、回滚等操作。

2. 搭建 Kubernetes 集群

有多种方式可以搭建 Kubernetes 集群,这里以 Minikube 为例,Minikube 是一个在本地运行 Kubernetes 集群的工具,非常适合开发和测试环境。

首先,根据你的操作系统下载并安装 Minikube:

  • Windows:从官方网站下载 Minikube 安装包,并按照安装向导进行安装。
  • MacOS:使用 Homebrew 安装,执行 brew install minikube
  • Linux:从官方网站下载二进制文件,添加到系统路径并设置可执行权限。

安装完成后,启动 Minikube 集群:

minikube start

这将启动一个单节点的 Kubernetes 集群。你可以使用以下命令查看集群状态:

minikube status

要停止集群,使用:

minikube stop

3. 配置 kubectl

kubectl 是 Kubernetes 的命令行工具,用于与 Kubernetes 集群进行交互。在安装 Minikube 时,kubectl 通常会自动安装并配置好与 Minikube 集群的连接。

你可以使用以下命令查看 kubectl 的配置:

kubectl config view

确保你可以通过 kubectl 与集群进行通信,例如获取节点信息:

kubectl get nodes

这将显示集群中的节点列表。

C# 应用程序在 Kubernetes 中的部署

1. 创建 Kubernetes Deployment

在项目根目录下创建一个名为 deployment.yaml 的文件,用于定义 Kubernetes Deployment。以下是一个示例:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mywebapp-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mywebapp
  template:
    metadata:
      labels:
        app: mywebapp
    spec:
      containers:
      - name: mywebapp
        image: mywebapp:latest
        ports:
        - containerPort: 80

在上述 deployment.yaml 中:

  • apiVersion 指定使用的 Kubernetes API 版本。
  • kind 表示这是一个 Deployment 资源。
  • metadata.name 定义 Deployment 的名称为 mywebapp-deployment
  • spec.replicas 设置要运行的 Pod 副本数量为 3。
  • spec.selector 用于选择与该 Deployment 关联的 Pod,这里通过 app: mywebapp 标签进行选择。
  • spec.template 定义了 Pod 的模板,包括 Pod 的标签和容器的配置。容器名称为 mywebapp,使用的镜像为 mywebapp:latest,并暴露容器端口 80。

使用以下命令将 Deployment 应用到 Kubernetes 集群:

kubectl apply -f deployment.yaml

你可以使用以下命令查看 Deployment 的状态:

kubectl get deployments

要查看 Pod 的详细信息,使用:

kubectl get pods

2. 创建 Kubernetes Service

为了让外部能够访问到 Deployment 中的 Pod,需要创建一个 Service。在项目根目录下创建一个名为 service.yaml 的文件,示例如下:

apiVersion: v1
kind: Service
metadata:
  name: mywebapp-service
spec:
  selector:
    app: mywebapp
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: NodePort

在上述 service.yaml 中:

  • apiVersionkind 分别指定 API 版本和资源类型为 Service。
  • metadata.name 定义 Service 的名称为 mywebapp-service
  • spec.selector 通过 app: mywebapp 标签选择与该 Service 关联的 Pod。
  • spec.ports 定义了 Service 的端口配置,这里将外部端口 80 映射到 Pod 的 80 端口,type 设置为 NodePort,表示通过节点的一个随机端口暴露 Service。

使用以下命令将 Service 应用到 Kubernetes 集群:

kubectl apply -f service.yaml

你可以使用以下命令查看 Service 的详细信息:

kubectl get services

注意 EXTERNAL - IP 字段,对于 NodePort 类型的 Service,你可以通过 http://<节点 IP>:<随机端口> 访问应用程序,其中 <节点 IP> 可以通过 minikube ip 命令获取。

高级主题:配置管理与持续集成/持续部署(CI/CD)

1. 配置管理

在实际应用中,应用程序可能需要不同的配置,例如数据库连接字符串、API 密钥等。Kubernetes 提供了 ConfigMapSecret 来管理这些配置。

ConfigMap ConfigMap 用于存储不敏感的配置数据,例如应用程序的配置文件。创建一个 configmap.yaml 文件:

apiVersion: v1
kind: ConfigMap
metadata:
  name: mywebapp-config
data:
  appsettings.json: |
    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "Microsoft.AspNetCore": "Warning"
        }
      },
      "AllowedHosts": "*"
    }

在上述示例中,创建了一个名为 mywebapp-config 的 ConfigMap,其中包含一个 appsettings.json 的配置数据。

要将 ConfigMap 挂载到 Pod 中,修改 deployment.yaml 文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mywebapp-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mywebapp
  template:
    metadata:
      labels:
        app: mywebapp
    spec:
      containers:
      - name: mywebapp
        image: mywebapp:latest
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config-volume
          mountPath: /app/
      volumes:
      - name: config-volume
        configMap:
          name: mywebapp-config
          items:
          - key: appsettings.json
            path: appsettings.json

这里通过 volumeMountsvolumesmywebapp-config ConfigMap 挂载到容器的 /app/ 目录,替换原有的 appsettings.json 文件。

Secret Secret 用于存储敏感信息,例如数据库密码、API 密钥等。创建一个 secret.yaml 文件:

apiVersion: v1
kind: Secret
metadata:
  name: mywebapp-secret
type: Opaque
data:
  database-password: cGFzc3dvcmQ=

在上述示例中,创建了一个名为 mywebapp-secret 的 Secret,其中 database - password 字段的值是经过 base64 编码的密码。

要使用 Secret,同样修改 deployment.yaml 文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mywebapp-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: mywebapp
  template:
    metadata:
      labels:
        app: mywebapp
    spec:
      containers:
      - name: mywebapp
        image: mywebapp:latest
        ports:
        - containerPort: 80
        env:
        - name: DATABASE_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mywebapp-secret
              key: database-password

这里通过 env 将 Secret 中的 database - password 作为环境变量 DATABASE_PASSWORD 注入到容器中。

2. 持续集成/持续部署(CI/CD)

持续集成(CI)是指频繁地将代码集成到共享仓库,并自动进行构建和测试。持续部署(CD)则是在 CI 的基础上,自动将通过测试的代码部署到生产环境。

使用 GitHub Actions 进行 CI/CD GitHub Actions 是 GitHub 提供的持续集成和持续部署平台。在项目的 .github/workflows 目录下创建一个 cicd.yaml 文件:

name: C# CI/CD
on:
  push:
    branches:
      - main
jobs:
  build-and-push-docker-image:
    runs - on: ubuntu - latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Set up Docker Buildx
        uses: docker/setup - buildx - action@v2
      - name: Login to Docker Hub
        uses: docker/login - action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
      - name: Build and push Docker image
        id: docker_build
        uses: docker/build - push - action@v2
        with:
          context:.
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/mywebapp:latest
  deploy-to-kubernetes:
    needs: build-and-push-docker-image
    runs - on: ubuntu - latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Set up kubectl
        uses: azure/setup - kubectl@v1
      - name: Configure Kubernetes cluster
        uses: azure/k8s - configure - cluster@v1
        with:
          kubeconfig - secret: my - kubeconfig
          enable - kubelogin: true
          host: ${{ secrets.KUBERNETES_HOST }}
          client - id: ${{ secrets.KUBERNETES_CLIENT_ID }}
          client - secret: ${{ secrets.KUBERNETES_CLIENT_SECRET }}
          tenant - id: ${{ secrets.KUBERNETES_TENANT_ID }}
      - name: Deploy to Kubernetes
        uses: azure/k8s - deploy@v1
        with:
          manifests: |
            deployment.yaml
            service.yaml
          images: |
            ${{ secrets.DOCKERHUB_USERNAME }}/mywebapp:latest
          imagepullsecrets: |
            my - docker - secret

在上述 cicd.yaml 中:

  • on.push.branches 定义在 main 分支有推送时触发工作流。
  • build-and-push-docker-image 作业负责检出代码、设置 Docker Buildx、登录 Docker Hub 并构建和推送 Docker 镜像。
  • deploy-to-kubernetes 作业依赖于 build-and-push-docker-image 作业完成后执行,它会检出代码、设置 kubectl、配置 Kubernetes 集群并将 Deployment 和 Service 应用到集群中。

需要在 GitHub 仓库的设置中配置 secrets,包括 DOCKERHUB_USERNAMEDOCKERHUB_PASSWORDKUBERNETES_HOSTKUBERNETES_CLIENT_IDKUBERNETES_CLIENT_SECRETKUBERNETES_TENANT_ID 等。

这样,每次在 main 分支推送代码时,GitHub Actions 会自动构建 Docker 镜像并部署到 Kubernetes 集群中。

通过以上步骤,你已经学会了如何将 C# 应用程序进行 Docker 容器化,并与 Kubernetes 集成,实现高效的部署和管理,同时还涉及了配置管理和 CI/CD 等高级主题,使你的应用程序在生产环境中更加可靠和易于维护。