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

从 0 到 1 搭建容器编排系统:实战教程

2024-09-264.8k 阅读

容器编排系统概述

在深入探讨如何搭建容器编排系统之前,我们首先需要理解容器编排系统是什么以及它解决了什么问题。

容器技术,如 Docker,允许开发者将应用及其依赖打包成一个独立的、可移植的单元,即容器。然而,在实际生产环境中,一个应用往往由多个容器组成,这些容器需要协同工作,并且要考虑到资源分配、故障恢复、服务发现等一系列复杂问题。容器编排系统应运而生,它是一种用于自动化部署、扩展和管理容器化应用的工具。

容器编排系统提供了一种声明式的方式来定义应用的运行环境和行为。例如,你可以通过编写一个配置文件,指定需要运行的容器数量、每个容器所需的资源、容器之间如何进行网络通信等。编排系统会根据这个配置文件,自动创建、启动和管理这些容器,确保应用按照预期的方式运行。

常见的容器编排系统有 Kubernetes、Docker Swarm 和 Mesos。其中,Kubernetes 是目前最流行的容器编排系统,被广泛应用于各种规模的企业级应用中。它具有高度的可扩展性、丰富的功能以及活跃的社区支持。

搭建容器编排系统的准备工作

环境准备

在开始搭建容器编排系统之前,我们需要准备好相应的环境。首先,确保你有一台或多台安装了 Linux 操作系统的服务器。本文以 Ubuntu 20.04 为例进行演示。

你还需要安装 Docker,因为容器编排系统通常是基于 Docker 容器运行的。可以通过以下命令在 Ubuntu 上安装 Docker:

sudo apt-get update
sudo apt-get install docker.io
sudo systemctl start docker
sudo systemctl enable docker

验证 Docker 是否安装成功:

docker --version

了解容器编排工具

如前文所述,Kubernetes 是最常用的容器编排工具,因此我们将以 Kubernetes 为例来搭建容器编排系统。在开始搭建之前,建议你对 Kubernetes 的基本概念有一定的了解,例如 Pod、Service、Deployment 等。

  • Pod:Kubernetes 中最小的可部署和可管理的计算单元,一个 Pod 可以包含一个或多个紧密相关的容器。这些容器共享网络命名空间和存储卷,可以直接通过 localhost 进行通信。
  • Service:为一组 Pod 提供一个稳定的网络接口,使得客户端可以通过 Service 访问到 Pod 中的应用,而无需关心 Pod 的实际 IP 地址和数量变化。
  • Deployment:用于管理 Pod 和 ReplicaSet 的声明式更新。你可以通过 Deployment 来定义应用的期望状态,Kubernetes 会自动将实际状态调整到与期望状态一致。

安装 Kubernetes 集群

选择安装方式

安装 Kubernetes 集群有多种方式,常见的有 Minikube、kubeadm 和二进制安装。Minikube 适合在本地开发环境中快速搭建一个单节点的 Kubernetes 集群;kubeadm 是官方推荐的用于生产环境的安装工具,它可以帮助我们快速搭建多节点的 Kubernetes 集群;二进制安装则更为灵活,但相对复杂,适合对 Kubernetes 有深入理解的开发者。

本文将以 kubeadm 为例进行安装,因为它既适用于生产环境,又相对简单易操作。

安装 kubeadm、kubelet 和 kubectl

在所有节点上执行以下命令安装 kubeadm、kubelet 和 kubectl:

sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl

初始化控制平面节点

在控制平面节点(通常是主节点)上执行以下命令初始化 Kubernetes 集群:

sudo kubeadm init --apiserver-advertise-address=192.168.1.100 --pod-network-cidr=10.244.0.0/16

这里 --apiserver-advertise-address 指定了控制平面节点的 IP 地址,--pod-network-cidr 指定了 Pod 网络的地址范围。请根据实际情况替换 IP 地址。

初始化成功后,会输出一些后续操作的提示信息,其中包括如何配置 kubectl 和加入工作节点的命令。按照提示操作,配置 kubectl:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

安装网络插件

Kubernetes 本身不提供网络功能,需要安装第三方网络插件来实现 Pod 之间的通信。常用的网络插件有 Flannel、Calico 等。这里我们以 Flannel 为例进行安装。

在控制平面节点上执行以下命令安装 Flannel:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

加入工作节点

在每个工作节点上执行在控制平面节点初始化时输出的加入命令,例如:

sudo kubeadm join 192.168.1.100:6443 --token abcdef.1234567890abcdef \
    --discovery-token-ca-cert-hash sha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef

192.168.1.100 替换为控制平面节点的实际 IP 地址,abcdef.1234567890abcdefsha256:1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef 替换为实际生成的 token 和证书哈希值。

创建第一个容器化应用

编写 Dockerfile

假设我们有一个简单的 Node.js 应用,目录结构如下:

myapp/
├── app.js
└── package.json

app.js 的内容如下:

const http = require('http');

const hostname = '0.0.0.0';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello, World!\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

package.json 的内容如下:

{
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "main": "app.js",
  "scripts": {
    "start": "node app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {}
}

myapp 目录下创建一个 Dockerfile:

FROM node:14

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY. .

EXPOSE 3000

CMD [ "npm", "start" ]

这个 Dockerfile 的作用是基于官方的 Node.js 14 镜像,在容器内创建一个工作目录 /app,将 package.jsonpackage - lock.json 复制到容器内并安装依赖,然后将整个应用代码复制到容器内,暴露 3000 端口,并在容器启动时运行 npm start 命令启动应用。

构建和推送 Docker 镜像

myapp 目录下执行以下命令构建 Docker 镜像:

docker build -t myapp:v1.0.0.

这里 -t 参数指定了镜像的标签,格式为 镜像名:版本号

构建完成后,可以通过以下命令查看镜像:

docker images

如果需要将镜像推送到镜像仓库(如 Docker Hub),首先需要登录到镜像仓库:

docker login

然后执行推送命令:

docker push myapp:v1.0.0

在 Kubernetes 中部署应用

编写 Deployment 配置文件

在本地创建一个 myapp - deployment.yml 文件,内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
      - name: myapp
        image: myapp:v1.0.0
        ports:
        - containerPort: 3000

这个配置文件定义了一个 Deployment,名为 myapp - deployment,期望运行 3 个副本,选择器匹配标签为 app: myapp 的 Pod。Pod 的模板中定义了一个容器,使用 myapp:v1.0.0 镜像,并暴露 3000 端口。

创建 Deployment

myapp - deployment.yml 文件所在目录执行以下命令创建 Deployment:

kubectl apply -f myapp-deployment.yml

可以通过以下命令查看 Deployment 的状态:

kubectl get deployments

也可以查看 Pod 的状态:

kubectl get pods

暴露应用服务

为了让外部能够访问到我们部署的应用,需要创建一个 Service。在本地创建一个 myapp - service.yml 文件,内容如下:

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

这个配置文件定义了一个 Service,名为 myapp - service,选择器匹配标签为 app: myapp 的 Pod,将外部的 80 端口映射到 Pod 的 3000 端口,类型为 NodePort,表示可以通过节点的 IP 和指定的端口访问服务。

创建 Service

myapp - service.yml 文件所在目录执行以下命令创建 Service:

kubectl apply -f myapp-service.yml

可以通过以下命令查看 Service 的状态:

kubectl get services

获取 Service 的 NodePort 后,就可以通过 http://节点IP:NodePort 访问我们的应用了。

容器编排系统的高级功能

自动扩展

Kubernetes 支持根据 CPU 使用率或其他指标自动扩展 Deployment 中的 Pod 数量。首先,需要确保安装了 Metrics Server,它为 Kubernetes 提供资源使用指标。

执行以下命令安装 Metrics Server:

git clone https://github.com/kubernetes-sigs/metrics-server.git
cd metrics-server/deploy/1.8+
kubectl apply -f.

安装完成后,可以通过以下命令查看指标:

kubectl top pods

接下来,创建一个 Horizontal Pod Autoscaler(HPA)来根据 CPU 使用率自动扩展 Pod。在本地创建一个 myapp - hpa.yml 文件,内容如下:

apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp-deployment
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 50

这个配置文件定义了一个 HPA,目标是 myapp - deployment,最小副本数为 3,最大副本数为 10,当 CPU 使用率达到 50% 时,自动扩展或收缩 Pod 数量。

myapp - hpa.yml 文件所在目录执行以下命令创建 HPA:

kubectl apply -f myapp-hpa.yml

可以通过以下命令查看 HPA 的状态:

kubectl get hpa

滚动更新和回滚

Kubernetes 的 Deployment 支持滚动更新和回滚功能。当我们需要更新应用镜像时,可以通过修改 Deployment 的配置文件来触发滚动更新。

例如,将 myapp - deployment.yml 中的镜像版本改为 myapp:v1.0.1,然后执行:

kubectl apply -f myapp-deployment.yml

Kubernetes 会逐步替换旧版本的 Pod 为新版本的 Pod,这个过程中应用可以保持不间断运行。

如果更新过程中出现问题,可以执行回滚操作:

kubectl rollout undo deployment myapp-deployment

也可以查看 Deployment 的滚动更新状态:

kubectl rollout status deployment myapp-deployment

配置管理

在实际应用中,应用往往需要一些配置参数,如数据库连接字符串、API 密钥等。在 Kubernetes 中,可以使用 ConfigMap 和 Secret 来管理这些配置。

  • ConfigMap:用于存储不敏感的配置数据,如应用的配置文件。创建一个 myapp - configmap.yml 文件,内容如下:
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  config.properties: |
    database.url=mongodb://localhost:27017
    api.key=123456

在这个配置文件中,我们定义了一个名为 myapp - config 的 ConfigMap,包含一个 config.properties 文件内容的配置项。

执行以下命令创建 ConfigMap:

kubectl apply -f myapp-configmap.yml

可以通过以下命令查看 ConfigMap:

kubectl get configmaps
  • Secret:用于存储敏感数据,如密码、证书等。创建一个 myapp - secret.yml 文件,内容如下:
apiVersion: v1
kind: Secret
metadata:
  name: myapp-secret
type: Opaque
data:
  password: $(echo -n 'admin123' | base64)

在这个配置文件中,我们定义了一个名为 myapp - secret 的 Secret,存储了一个加密后的密码。执行以下命令创建 Secret:

kubectl apply -f myapp-secret.yml

可以通过以下命令查看 Secret:

kubectl get secrets

存储管理

持久化卷(PersistentVolume)和持久化卷声明(PersistentVolumeClaim)

在容器化应用中,容器的数据通常是临时的,容器重启或删除后数据会丢失。为了实现数据的持久化存储,Kubernetes 提供了持久化卷(PersistentVolume,简称 PV)和持久化卷声明(PersistentVolumeClaim,简称 PVC)的概念。

PV 是集群中由管理员预先配置的一段网络存储,它是独立于 Pod 存在的。PVC 是用户对存储的请求,Pod 可以通过 PVC 来使用 PV 提供的存储。

以下是一个创建 PV 和 PVC 的示例。首先,创建一个 myapp - pv.yml 文件,内容如下:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: myapp-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  hostPath:
    path: /data/myapp

这个配置文件定义了一个名为 myapp - pv 的 PV,容量为 1Gi,访问模式为 ReadWriteOnce,表示只能被单个节点以读写方式挂载,存储类型为 hostPath,即使用宿主机的 /data/myapp 目录作为存储。

执行以下命令创建 PV:

kubectl apply -f myapp-pv.yml

然后,创建一个 myapp - pvc.yml 文件,内容如下:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myapp-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

这个配置文件定义了一个名为 myapp - pvc 的 PVC,请求 1Gi 的存储空间,访问模式与 PV 一致。

执行以下命令创建 PVC:

kubectl apply -f myapp-pvc.yml

可以通过以下命令查看 PV 和 PVC 的状态:

kubectl get pv
kubectl get pvc

最后,在 Deployment 配置文件中挂载 PVC。修改 myapp - deployment.yml,在 spec.template.spec.containers 下添加以下内容:

volumeMounts:
- name: myapp-storage
  mountPath: /app/data
volumes:
- name: myapp-storage
  persistentVolumeClaim:
    claimName: myapp-pvc

这样,容器内的 /app/data 目录就会挂载到 PV 提供的存储上,实现数据的持久化。

存储类(StorageClass)

除了手动创建 PV 和 PVC 外,Kubernetes 还支持使用存储类(StorageClass)来动态创建 PV。存储类允许管理员定义不同类型的存储,用户可以根据需求选择合适的存储类来创建 PVC,系统会自动创建对应的 PV。

以下是一个创建存储类的示例。创建一个 myapp - storageclass.yml 文件,内容如下:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: myapp-storageclass
provisioner: kubernetes.io/aws - ebs
parameters:
  type: gp2

这个配置文件定义了一个名为 myapp - storageclass 的存储类,使用 AWS EBS 作为存储供应器,存储类型为 gp2。如果是在其他云平台或自建存储系统,需要根据实际情况修改 provisionerparameters

执行以下命令创建存储类:

kubectl apply -f myapp-storageclass.yml

在创建 PVC 时,可以指定使用这个存储类。修改 myapp - pvc.yml,添加 storageClassName 字段:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myapp-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
  storageClassName: myapp-storageclass

这样,当创建这个 PVC 时,系统会根据指定的存储类自动创建一个 PV。

网络管理

服务发现

在 Kubernetes 集群中,服务发现是一个重要的功能。当一个 Pod 想要访问另一个 Pod 提供的服务时,它不需要知道目标 Pod 的具体 IP 地址,而是通过 Service 来进行访问。

例如,我们有一个名为 backend - service 的 Service,它代理了一组标签为 app: backend 的 Pod。在其他 Pod 中,可以通过 backend - service 这个 DNS 名称来访问这些后端 Pod。

假设我们有一个前端应用的 Pod,需要访问后端服务。在前端应用的配置文件中,可以将后端服务的地址配置为 backend - service:端口号。Kubernetes 的 DNS 服务会自动将 backend - service 解析为实际的后端 Pod 地址。

网络策略

网络策略用于控制 Pod 之间的网络流量。例如,我们可以限制只有特定标签的 Pod 才能访问某个服务,或者禁止外部流量访问某些敏感的 Pod。

以下是一个简单的网络策略示例。创建一个 myapp - networkpolicy.yml 文件,内容如下:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: myapp-networkpolicy
spec:
  podSelector:
    matchLabels:
      app: myapp
  ingress:
  - from:
    - podSelector:
        matchLabels:
          role: frontend

这个配置文件定义了一个网络策略,选择器匹配标签为 app: myapp 的 Pod,允许来自标签为 role: frontend 的 Pod 的入站流量。

执行以下命令创建网络策略:

kubectl apply -f myapp-networkpolicy.yml

通过网络策略,可以有效地增强集群的网络安全性,防止未授权的访问。

监控与日志管理

监控

为了确保容器编排系统和应用的健康运行,监控是必不可少的。Kubernetes 生态系统中有许多监控工具,如 Prometheus 和 Grafana。

  • Prometheus:是一个开源的监控和时间序列数据库。首先,需要安装 Prometheus。可以使用 Helm 来安装,Helm 是 Kubernetes 的包管理器。

安装 Helm:

curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
sudo apt-get install apt-transport-https --yes
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/helm.gpg] https://baltocdn.com/helm/stable/debian/ all main" | sudo tee /etc/apt/sources.list.d/helm-stable-debian.list
sudo apt-get update
sudo apt-get install helm

添加 Prometheus Helm 仓库:

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

安装 Prometheus:

helm install prometheus prometheus-community/kube-prometheus-stack

安装完成后,可以通过以下命令查看 Prometheus 的服务:

kubectl get services -l release=prometheus
  • Grafana:是一个可视化工具,与 Prometheus 结合可以提供美观的监控仪表盘。同样使用 Helm 安装 Grafana:
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update
helm install grafana grafana/grafana

获取 Grafana 的管理员密码:

kubectl get secret --namespace default grafana -o jsonpath="{.data.admin-password}" | base64 --decode ; echo

通过浏览器访问 Grafana 服务,使用管理员用户名和密码登录,然后配置数据源为 Prometheus,就可以创建各种监控仪表盘了。

日志管理

容器化应用产生的日志对于故障排查和系统运维非常重要。在 Kubernetes 中,可以使用工具如 Fluentd 和 Elasticsearch 来进行日志收集、存储和检索。

  • Fluentd:是一个开源的日志收集器。可以通过 Helm 安装 Fluentd:
helm repo add fluent https://fluent.github.io/helm-charts
helm repo update
helm install fluentd fluent/fluentd

Fluentd 会自动收集集群中所有 Pod 的日志,并根据配置将日志发送到指定的目标,如 Elasticsearch。

  • Elasticsearch:是一个分布式搜索和分析引擎,用于存储和检索日志。使用 Helm 安装 Elasticsearch:
helm repo add elastic https://helm.elastic.co
helm repo update
helm install elasticsearch elastic/elasticsearch

安装完成后,Fluentd 会将日志发送到 Elasticsearch,然后可以使用 Kibana(也是 Elastic 生态的一部分)来可视化和查询日志。

通过监控和日志管理,可以及时发现系统中的问题,并进行有效的故障排查和性能优化。

通过以上步骤,我们从 0 到 1 搭建了一个完整的容器编排系统,并介绍了其高级功能和相关的管理工具。在实际应用中,还需要根据具体的业务需求和场景进行进一步的优化和定制。