ToB企服应用市场:ToB评测及商务社交产业平台

标题: 一文详解:项目怎样从Docker逐步演变成了K8s部署 [打印本页]

作者: 李优秀    时间: 2024-11-25 09:40
标题: 一文详解:项目怎样从Docker逐步演变成了K8s部署
今天,我们将深入探讨一个项目部署的演变过程。在这篇文章中,为了紧扣主题,我们将从 Docker 开始解说,分析为什么一个传统的项目逐步演变成了今天流行的 Kubernetes(K8s)集群部署架构。我们将通过一个简单的 Java 项目来论述这一过程。
为了更清楚地论述,我在本地搭建了一个 gRPC 入门项目。考虑到篇幅和内容的专注性,我将这一部分的具体解说单独撰写成了一篇文章。具体内容可以参考这篇文章获取更多信息和背景知识:https://www.cnblogs.com/guoxiaoyu/p/18555031
接下来,我们将逐步睁开,深入解说从 Docker 容器化到 K8s 集群化的过渡,分析这个过程中面临的挑战和技术演进,并讨论为什么 Kubernetes 已经成为现代云原生应用部署的标配。
好了,现在我们正式开始本篇文章的解说。
Docker

这部分内容大家应该都比较认识了。对于个人开发者来说,Docker几乎是每个项目中不可或缺的一部分,学习怎样使用 Docker 命令是每个开发者的必修课。因此,关于 Docker 的安装过程,我就不再赘述了。大家可以根据官方文档或者教程轻松完成安装,过程也相对简单明了。
在使用 Docker 进行项目部署时,起首需要一个名为 Dockerfile 的配置文件,它定义了怎样构建和封装项目容器。具体内容如下:
  1. # 使用官方的 OpenJDK 镜像作为基础镜像
  2. FROM openjdk:8-jdk-alpine
  3. # 将构建好的Spring Boot JAR文件复制到容器中
  4. COPY grpc-server/target/grpc-server-1.0-SNAPSHOT.jar /app/grpc-server-1.0-SNAPSHOT.jar
  5. # 设置工作目录
  6. WORKDIR /app
  7. # 暴露 gRPC 应用程序的端口
  8. EXPOSE 9090
  9. # 运行 gRPC 服务
  10. CMD ["java", "-jar", "grpc-server-1.0-SNAPSHOT.jar"]
复制代码
在使用 Maven 编译和打包 Java 项目时,如果我们需要启动一个可执行的 JAR 包,就必须指定一个入口类,即启动类。我们需要单独配置一个编译插件,通常是   spring-boot-maven-plugin(如果是 Spring Boot 项目)。
这样做可以确保在执行 mvn package 命令时,Maven 会将启动类作为项目的入口,并生成正确的可执行 JAR 文件。以下是一个配置示例:
  1. <plugin>
  2.   <groupId>org.springframework.boot</groupId>
  3.   <artifactId>spring-boot-maven-plugin</artifactId>
  4. </plugin>
复制代码
接下来,我们正常执行mvn命令即可。执行完后,我们通常可以在服务器上直接通过docker命令进行构建docker镜像。这样全部环境集成都有了,直接启动docker镜像即可。跟我们本地的环境一点关系没有。命令如下:
mvn clean package
docker build -t grpc-server .
docker run -p 9090:9090 -v /data/logs:/app/logs grpc-server
过程如图所示:

我们袒露出来了一个服务器端口供外网调用。这里我测试一下,是正常的,效果如图所示:

综合上述全部条件,我们可以得出一个重要结论:Docker 在环境隔离方面确实具有显著的优势。通过 Docker,我们无需在本地体系上手动安装和配置各种依赖环境,从而避免了因环境配置不同而导致的问题。只需要执行一个简单的 Docker 命令,就能主动拉取所需的镜像并启动项目,这大大简化了开发和部署过程。
尤其是对于大多数开源项目而言,几乎都提供了官方或社区维护的 Docker 镜像,这使得用户能够快速入门,无需深入了解复杂的配置细节。
那么问题来了?

公司项目的部署远远不止于简单地启动一个 Docker 容器,而是涉及到多个复杂的组件和服务的协同工作。具体来说,除了 Docker 容器之外,我们通常还需要部署和配置 Nginx、前端服务、后端服务、数据库等一系列基础设施组件。每个项目都会根据实际需求涉及到不同的服务和环境配置,处置惩罚起来并不简单。
更重要的是,考虑到每次部署时可能都需要执行大量的命令来启动这些服务,难道我们真的要把这些命令手动记录在记事本中,然后每次上线时都逐一敲入这些命令吗?看下这段命令:
  1. docker run -d \
  2.   --name grpc-server-container \                 # 设置容器名称
  3.   -p 9090:9090 \                                 # 映射容器的 9090 端口到主机的 9090 端口
  4.   -p 8080:8080 \                                 # 映射容器的 8080 端口到主机的 8080 端口
  5.   -v /data/logs:/logs \                          # 将主机的 /data/logs 目录挂载到容器的 /logs 目录
  6.   -v /path/to/app/config:/app/config \           # 挂载应用配置文件夹
  7.   -v /path/to/db:/var/lib/postgresql/data \      # 将主机数据库目录挂载到容器的数据库目录
  8.   -e LOG_LEVEL=debug \                           # 设置环境变量 LOG_LEVEL
  9.   -e DB_USER=admin \                             # 设置数据库用户名环境变量
  10.   --restart unless-stopped \                     # 设置容器的重启策略,除非手动停止,否则容器会自动重启
  11.   --network host \                               # 使用主机网络模式,可以与主机共享网络资源
  12.   --log-driver=json-file \                       # 设置日志驱动为 json-file(默认)
  13.   grpc-server                                    # 使用 grpc-server 镜像
复制代码
那么,针对这种繁琐的手动输入命令和配置的情况,是否存在一些工具或者方式,能够资助我们提前将这些命令和配置都写好,并且每次只需执行一个文件,就能顺利启动整个项目,避免重复操纵和人为失误呢?
答案是肯定的,正是基于这种需求,我们有了 Docker Compose 这样的编排工具。
Docker-compose

编排文件采用一种固定的格式来誊写,目的是确保 Docker 在执行时能够正确地辨认和启动所需的全部服务和容器。
接下来,我们将以 grpc-server 项目为例,展示怎样将该项目配置到 Docker Compose 的编排文件中,文件内容如下:
  1. version: '3.8'
  2. services:
  3.   grpc-server:
  4.     image: grpc-server           # 使用 grpc-server 镜像
  5.     ports:
  6.       - "9090:9090"              # 映射端口 9090
  7.     volumes:
  8.       - /data/logs:/app/logs         # 挂载主机目录 /data/logs 到容器的 /logs 目录
  9.     restart: unless-stopped      # 如果容器停止,除非手动停止,否则会重新启动容器
复制代码
其实,这个过程非常简单和直观。如果你需要启动多个容器,只需在 Docker Compose 的编排文件中继续添加相应的服务配置,每个服务都会主动与其他服务进行协同工作。
对于每个容器的配置,你只需按需扩展,每新增一个容器,只要在文件中继续添加对应的服务定义即可,这样一来,整个项目中的全部服务都会被包含在编排文件内,实现一次性启动多个容器。命令如下:
启动多个指定容器:
docker-compose up  
后台启动全部容器:
docker-compose up -d
通过使用编排文件,我们几乎不再需要手动维护各种 Docker 启动命令,而是可以通过同一的配置文件进行管理和部署。这种方式的最大优点在于,它显著简化了平凡 Docker 启动命令的维护和执行过程,避免了手动操纵带来的复杂性和堕落风险。
同时,这种主动化的部署方法基本上解决了小型公司在项目部署中的诸多困难,使得项目的部署更加高效、稳定和可重复,从而大大提升了团队的生产力和项目的交付速度。
那么问题来了

只管项目本身的复杂度没有显著增加,但随着用户量的不断上升,我们面临的挑战也随之加剧。由于呆板的带宽和内存是有限的,单纯依赖一个 Docker 实例已经无法满足当前用户的访问需求。在这种情况下,我们通常需要通过增加呆板来进行水平扩展,通常是在新的呆板上重新执行相同的部署命令。
然而,这种反复手动操纵的方式会导致运维职员的工作量呈指数级增长,每次版本发布和扩容时,运维职员不仅需要投入大量的精力,还容易感到身心疲劳,工作负担越来越重,效率也大大低沉。
正因如此,集群部署的需求变得尤为迫切,而 Kubernetes(K8s)作为现代化容器编排平台应运而生,并迅速成为解决这一问题的利器。
K8s

很多人其实也听说过Docker Swarm,它是Docker原生的集群部署方式,具有一定的主动化和容错能力,但相比于Google设计的Kubernetes(K8s),其功能和生态体系的完善程度明显不足,因此未能获得像K8s那样广泛的应用和洽评。
话不多说,我们现在回到重点,继续深入解说K8s的部署方式。为了方便演示,我已经在本地提前配置好了一个简化版的K8s环境,接下来的内容将不再赘述其具体安装过程。
如果对Kubernetes(K8s)还不太了解的朋侪,可以先参考我之前写的这篇文章,它将资助你快速把握K8s的基本概念和架构:https://www.cnblogs.com/guoxiaoyu/p/17876335.html
面临的一个实际问题是:大多数人手中都有现成的Docker Compose编排文件,而怎样将这些文件高效地迁移到K8s环境中呢?荣幸的是,这里有一个非常流行的工具,它能够资助你轻松实现这一迁移,不需要手动编写复杂的K8s部署文件或进行繁琐的配置。
kompose:Convert your Docker Compose file to Kubernetes

Kompose的主要目的就是资助开发者快速将现有的Docker Compose编排文件转换为Kubernetes(K8s)所需的资源配置文件,简化从Docker Compose到K8s的迁移过程。我们看下怎样使用,需要通过以下命令安装Kompose:
Linux:curl -L https://github.com/kubernetes/kompose/releases/download/v1.34.0/kompose-linux-amd64 -o kompose
Linux ARM64:curl -L https://github.com/kubernetes/kompose/releases/download/v1.34.0/kompose-linux-arm64 -o kompose
chmod +x kompose
sudo mv ./kompose /usr/local/bin/kompose
上面的命令执行完后,基本上我们就可以正常使用了。效果如图所示:

我们直接通过执行文件的方式转化我们的编排文件。命令如下:
kompose convert -f docker-compose.yaml

执行完后,通常情况下官方的教程是直接就可以进行kubelet apply部署,但是留意一些坑,我们一起来看下。
起首,在 Kubernetes 中,每个容器都可以通过挂载目录来持久化其数据。只管 Kubernetes 默认会将容器的日志存储在 /var/log/containers/ 目录下,但对于像 MySQL 这样的数据库服务,仅依赖 Kubernetes 自带的日志目录是不富足的。因为容器的重启或停启操纵可能会导致数据丢失,因此必须通过挂载持久化存储卷(例如,使用 Persistent Volumes 或 HostPath 等方式)来包管数据库的数据安全和持久性。
在本示例中,我仅仅是为了演示目的,简单地挂载了日志目录,而未涉及更复杂的数据存储挂载配置。
数据挂载

这里有几个新的关键名词,在之前我是没有讲过的,如下:
PV(Persistent Volume):是一个具体的存储资源(可以是本地磁盘、云存储等),由管理员配置。PV 存储的内容是持久化的,不会因容器的销毁而丢失。
PVC(Persistent Volume Claim):是用户或应用向 Kubernetes 请求存储资源的方式。用户声明他们需要多大的存储、访问模式等,Kubernetes 会根据这些要求找到合适的 PV。
PV 与 PVC 的关系:PVC 向 Kubernetes 提出存储需求。PV 提供实际的存储资源,且由 Kubernetes 根据 PVC 的要求动态绑定。
通过将 PVC 挂载到 Pod 中,容器就能使用持久化存储的数据。通过这种方式,Kubernetes 可以高效地管理存储,确保容器应用的数据不会丢失,即使容器被销毁或重新部署,数据仍然得以保留。
为了实现文件共享和数据存储,我们需要搭建一个NFS(Network File System)服务器。这个服务器可以选择由云服务商提供,也可以使用本地服务器的磁盘资源。在本例中,我们将以本地服务器为例,具体先容怎样搭建一个NFS 服务器,并进行相关配置。
NFS 服务器

我们不能直接使用kubelet apply部署,相反,我们需要对 PVC 文件进行一定的修改,以确保它能够正确地创建所需的存储资源。具体来说,我们需要在 PVC 文件中指定使用的 StorageClass。
使用以下命令获取当时集群的StorageClass,如果无StorageClass则需要先创建。
kubelet  get storageclass
因为我这是刚搭建的K8s环境,以是我这里表现的是没有,那么先搭建一个本地NFS服务器。
查看体系是否已安装NFS
rpm -qa | grep nfs
rpm -qa | grep rpcbind
安装NFS 、RPC
yum -y install nfs-utils rpcbind
启动服务
systemctl start nfs rpcbind
创建目录
mkdir -p /nfs-server/log/
chmod 666 /nfs-server/log/
编辑export文件
vim /etc/exports
/nfs-server *(rw,sync,no_root_squash)

配置生效
exportfs -r
启动rpcbind、nfs服务
systemctl restart rpcbind
systemctl restart nfs
自我测试一下是否可以联机
showmount -e localhost
测试效果正常:

Storageclass启动

起首,我们需要启动StorageClass,文件内容如下:
  1. apiVersion: storage.k8s.io/v1
  2. kind: StorageClass
  3. metadata:
  4.   name: nfs
  5. provisioner: kubernetes.io/nfs
  6. parameters:
  7.   server: 192.168.56.5
  8.   path: /nfs-server
复制代码
可以看到这里是以本地IP为NFS服务器的。直接使用命令启动即可。
k apply -f st.yaml
kubectl get storageclass
效果如下,启动成功:

PV启动

然后配置一下pv文件,并启动:
  1. kind: PersistentVolume
  2. apiVersion: v1
  3. metadata:
  4.   name: nfs-pv-test
  5.   namespace: database
  6. spec:
  7.   accessModes:
  8.     - ReadWriteOnce
  9.   capacity:
  10.     storage: 1.5Gi
  11.   persistentVolumeReclaimPolicy: Retain
  12.   storageClassName: nfs
  13.   nfs:
  14.     path: /nfs-server/log
  15.     server: 192.168.56.5
复制代码
kubectl apply -f pv.yaml
启动成功,效果如图所示:

项目启动

接下来,我们就需要启动当时kompose生成的三个文件,我们挨个执行,命令如下:
k apply -f grpc-server-xiaoyu-claim0-persistentvolumeclaim.yaml
k apply -f grpc-server-xiaoyu-deployment.yaml
k apply -f grpc-server-xiaoyu-service.yaml
最后,正常启动service服务后可以看到正常分配了内网ip,但并不会被外网访问到。

为了方便演示,我们简单修改一下grpc-server-xiaoyu-service.yaml ,让外网ip可以访问到,内容如下:
  1. apiVersion: v1
  2. kind: Service
  3. metadata:
  4.   annotations:
  5.     kompose.cmd: kompose convert -f docker-compose.yml
  6.     kompose.version: 1.34.0 (cbf2835db)
  7.   labels:
  8.     io.kompose.service: grpc-server-xiaoyu
  9.   name: grpc-server-xiaoyu
  10. spec:
  11.   ports:
  12.     - name: "9091"
  13.       port: 9093
  14.       targetPort: 9093
  15.   type: NodePort
  16.   selector:
  17.     io.kompose.service: grpc-server-xiaoyu
复制代码
再次启动,效果如图所示:

最后启动成功:

改下我们本地的连接端口:
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         ManagedChannel channel = ManagedChannelBuilder.forAddress("192.168.56.5", 30238)
  4.                 .usePlaintext()
  5.                 .build();
  6.         // 创建一元式阻塞式存根
  7.         GreeterGrpc.GreeterBlockingStub blockingStub = GreeterGrpc.newBlockingStub(channel);
  8.         // 创建请求对象
  9.         HelloRequest request = HelloRequest.newBuilder()
  10.                 .setName("World")
  11.                 .build();
  12.         // 发送请求麦位列表信息并接收响应
  13.         HelloReply response = blockingStub.sayHello(request);
  14.         // 处理麦位列表信息响应
  15.         System.out.println("Received  response: " + response);
  16.     }
  17. }
复制代码
日志输出成功:

在 Kubernetes 中,容器的日志默认存储在宿主机的 /var/log/containers/ 目录下。每个容器的日志文件名通常包括容器的名称、Pod 名称、定名空间和容器运行的唯一标识符(例如 Pod 的 UID)。这种路径结构确保了每个容器的日志都是唯一的,不会与其他容器的日志混合。

我们看下默认日志目录下生成的日志。

由于我们已经成功挂载了数据盘,因此相应的挂载目录也已创建并可供使用。效果如图所示:

动态扩容

为了应对容器编排中可能出现的容器实例数目增加的问题,Kubernetes 提供了机动的扩展机制,既可以通过主动扩容来根据负载主动增加或淘汰容器实例数目,也可以通过手动扩容的方式来快速响应突发的高并发用户访问需求。
在此,我们将演示怎样手动增加容器实例的数目。命令如下:
kubectl scale deployment grpc-server-xiaoyu --replicas=2
执行成功,效果如图所示:

总体而言,Kubernetes 以其机动性和强大的功能,基本上已经能够满足现代化项目的绝大部分需求,尤其是在容器实例扩展和主动化管理等方面,极大地低沉了手动干预的复杂度。
那么问题来了

综上所述,虽然 Kubernetes(K8s)在日常运维和管理方面为开发者和运维团队提供了极大的便利,主动化的扩展、负载均衡、容错处置惩罚等功能也大大提升了体系的可靠性和可维护性,但在初期的服务器搭建和集群配置过程中,K8s 的复杂性却无可避免地带来了不少挑战。
由于 K8s 本身是一个高度模块化的体系,其组件间的依赖性较强,任何一项配置错误都可能导致集群运行非常。这就增加了集群部署和维护的难度,尤其对于中小型企业或者缺乏深厚运维经验的团队来说,怎样快速部署并确保集群稳定性,依然是一个需要解决的困难。那么,面对这一挑战,怎样能够低沉 K8s 部署和管理的门槛,并使其更易于使用呢?
上云

在这方面,实际上不必过多赘述。当前,各大云服务提供商的 Kubernetes 集群部署服务已经相当成熟,基本上能够满足绝大多数企业对集群服务的需求,并且提供了完善的生态支持。好比,监控与报警体系、日志收集、主动化扩容、负载均衡等一整套解决方案,几乎涵盖了现代化应用所需的全部基础设施和运维功能。

相较于自行搭建和管理 Kubernetes 集群,使用云厂商提供的服务无疑是更加便捷和高效的。只需通过云平台的控制台进行几次点击,相关服务就能主动化部署,极大地缩短了上线周期。对于技术团队来说,这种便捷的集群部署方式,几乎可以做到即插即用,快速上手,低沉了对复杂操纵和配置的依赖。
此外,云服务商通常都会提供丰富的官方文档支持,资助用户解决常见问题。在遇到更复杂的情况时,用户还能直接提交工单,享受一对一的专属技术支持,这对于解决实际运维中的疑难问题至关重要。因此,相比于传统的自行搭建集群,云服务的方案在稳定性、易用性和服务质量上都有着无可相比的优势。
总结

通过本文的深入探讨,我们已经具体了解了一个项目从 Docker 容器化到 Kubernetes 集群化的演变过程。在这个过程中,我们不仅分析了 Docker 的基础使用方法和 Docker Compose 的便捷性,还先容了 Kubernetes 在处置惩罚大型、复杂体系中的重要作用。
虽然 Kubernetes 在初期的配置和维护上可能带来一定的复杂性,但随着云服务的成熟,各大云平台提供了全面的 Kubernetes 集群管理解决方案,极大地简化了部署流程。云服务商的主动化工具和技术支持,资助企业快速上手 Kubernetes,避免了许多传统集群部署中的困难。
因此,Kubernetes 已经成为现代云原生应用部署的标配,它的机动性、扩展性和高度主动化特性,使得它在容器编排和微服务架构的管理中占据了无可替代的地位。对于开发者来说,把握 Kubernetes 是将来工作中不可或缺的一项技能,它不仅能进步项目的交付速度,还能有效低沉运维复杂度,为企业提供更高效、可靠的服务。
最后,无论采用何种方式,我们都应根据实际情况出发,避免盲目追求华而不实,无论是 Docker、编排工具,还是 Kubernetes,总有一种方式最得当你的需求。
我是积极的小雨,一名 Java 服务端码农,潜心研究着 AI 技术的奥秘。我热爱技术交流与分享,对开源社区充满热情。同时也是一位腾讯云创作之星、阿里云专家博主、华为云云享专家、掘金优秀作者。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4