原创 毅航 2024-12-21 09:04 北京
点击关注公众号,“技术干货”及时达!
点击关注公众号,“技术干货” 及时达!
❝摘要:本文重点对目前容器化技术关键组件的
❞Docker
和Kubernetes(K8s)
的原理进行讲解和分析,以帮助开发者更好的理解两个组件在容器化应用程序的开发、部署和管理过程中重要作用。此外,文中笔者会着重介绍一款插件和开发工具以降低我们学习k8s
的难度。
前言
相信对于每一个开发者而言一定都有过配置开发环境的痛苦经历。笔者
仍然记得上学那会因为学校开设数据库相关课程,需要安装sql Server
数据库,那时受限于笔者能力,安装环境就通折腾大半天,待安装完成后本以为能大干一场,却没成想吃灰一学期。
再到后来的学习编程的学习,那会java
项目在教学过程中,项目构建并不是通过maven
版本管理的方式,而是靠本地导入jar
的形式来完成项目构建。当你手把手按教学内容进行项目构建时,你会发现在别人电脑上正常运行的程序,到了你本地就难以执行。
所以那会我一直在想能不能有这样一种工具,它能将所需的环境打包好,而我只需要直接解压就能执行运行,而无需我在本地重复构建环境配置参数,直至后来遇到了Docker
。
那这个Docer
到底是什么呢?它到底有什么魔力能让项目环境构建如此简·单呢?接下来,我们便来谈一谈Docker
的故事!
虚拟化技术
在讲解Docker
容器化原理之前,我觉得我们有必要对计算领域的虚拟化
技术进行一个简单的回顾与总结。
在计算机早期的发展历程中,由于各类应用平台并没有一个统一化的标准,这便导致不同平台的系统调用变得十分杂乱。例如,如果你想linux
系统下安装一个软件,在 ubuntu
,你需要执行 apt-get install xxx
而在 centos
中你可能就需要执行 yum install xxx
的命令了。
而解决虚拟化
技术便是解决这一难题的一味良方。我们平时所谈论的虚拟化
其实就是「一种将计算机物理资源进行抽象、转换为虚拟的计算机资源提供给程序使用的技术。」 而「虚拟化」也正是通过其本身适配不同平台的硬件,而加以抽象成统一的接口,进而来实现程序跨平台运行。例如,我们所熟知的Java虚拟机
。
随着技术的不断迭代,人们渐渐的提出了一种全新的名词——「容器化」。而「容器化」其实就是借助指操作系统所提供特定的接口,使得应用程序能够在彼此独立、互不干扰的环境中运行。「也正是因为应用程序的运行被隔离在一个独立的运行环境中,这个环境像一个容器一样包裹住了应用程序,从而得名“容器技术”。」
在对「虚拟化,容器化技术」等技术有了一定的了解后名。接下来,我们进入到对Docker
容器的原理进行一个详细的介绍。
Docker
容器
Docker
是什么
相信你一定有过这样经历,当我们在构建程序时通常有可能因为当前系统环境配置或依赖版本问题面而出现各种各样问题,进而使得项目应用程序无法启动,并为此感到焦虑。
面对如此难题,相信聪明的你可能会想到,「如果在传输项目过程中能把程序依赖的环境信息一同打包压缩,然后自己只需解压,这样问题不就能完美解决了吗」?
更进一步,该如何将环境信息和程序一同打包呢?传统的压缩包工具肯定是不行的,因为其只能完成应用程序的打包处理,面对如此问题Docker
应运而生。简而言之,「Docker
就是一款可以将程序和环境打包并运行的工具。」
事实上,Docker
的起源可以追溯到2010
年,其最初是由 「Solomon Hykes」 和他的团队在 「dotCloud」创建的。而最初的 Docker
只是基于Linux Containers(LXC)
的一个容器管理工具并提供了一些简单的命令行操作来创建和管理容器,直到2013
年,Docker
才作为开源项目发布,并推出了 Docker Engine
。相比最开始的LXC
技术相比,简化了容器的使用,提供了更简洁的 API
和命令行工具,改变了容器的管理方式,使得开发者可以更容易地创建、部署和运行容器化应用,至此Docer
逐渐走入大众开发者的视野。
在熟悉了Dokcer
的作用后,我们接下来看Docker
内部相关架构及工作原理,一探其打包应用和环境背后的秘密。
Docker
架构
在Docker
内部架构中,其主要是由「Docker Client」、「Docker Host」 和 「Docker Registry」 是三个核心组件构成,而三个组件关系具体如下图所示:
具体来看,「Docker Client
」 用户与Docker
交互的媒介,用户通过它来向 Docker
引擎发起请求。用户通过客户端运行命令,进行容器的创建、启动、停止、删除、构建镜像等操作。例如,通过docker run
用以启动一个容器,而docker build
则用于构建一个 Docker
镜像、 docker pull
则是从 Docker Registry
拉取镜像。
而Docker Host
则主要是运行 Docker
容器和镜像的主机。其通常是真实的物理机或远程的云服务器。其内部的「Docker Daemon」则是 Docker
引擎的核心组件,主要负责管理容器的生命周期、镜像的管理、网络的管理等任务。换言之,「Docker Daemon
主要接收并处理来自 「Docker Client
」 的请求,并执行具体操作 「。最后的」Docker Registry
」 是一个用于存储和分发Docker
镜像的地方。
进一步来看,Docker Client,Docker Host,Docker Registry
三者之间的状态流转大致如下:
首先用户在 「Docker Client
」 中输入命令(例如 docker pull vim
)。
「Docker Client
」 向 「Docker Host
」 发送请求, Docker Daemon
接受并处理请求,进而从 「Docker Registry
」 拉取镜像。
镜像拉取成功后,Docker Daemon
创建容器并运行它,最终成为一个在 「Docker Host
」 上运行的容器。
Docker
镜像是什么
在上面讲解中,我们提到了一个全新的词——镜像
。而于讲清Docker
镜像,我们首先需要对ockerfile
进行介绍。
事实上,Dockerfile
是一个文本文件,其主要包含了一系列指令,以用于自动化创建 Docker
镜像的过程。换言之,「Dockerfile
它定义了如何从一个基础镜像开始,逐步构建应用的环境,并配置运行容器时的行为,而Dockerfile
文件内容通常如下」:
# 基础镜像,采用官方的 Python 镜像
FROM python:3.9-slim
# 设置工作目录
WORKDIR /app
# 将本地代码复制到容器内
COPY . /app
# 安装依赖
RUN pip install -r requirements.txt
# 启动应用
CMD ["python", "app.py"]
上述代码中的from,workdir copy run cmd
几个关键字含义如下:
「FROM
」:指定基础镜像。例如,FROM python:3.9-slim
表示从 python:3.9-slim
镜像开始构建。
「RUN
」:在镜像中执行命令,通常用于安装依赖、软件包或配置环境。
「COPY
」:将文件从主机复制到镜像中。例如,COPY ./app /usr/src/app
。
「WORKDIR
」:设置工作目录,类似于命令行中的 cd
。
「CMD
」:指定容器启动时执行的命令。每个Dockerfile
只能有一个 CMD
,如果指定多个,最后一个 CMD
会生效。
因此上述Dockerfile
主要定义了一个Python
镜像。即一个基础镜像为Python 3.9
镜像开始,并通过 pip
安装所需的Python
依赖。
总之,你可将Dockerfile
理解为我们的依赖构建的工具,通过执行对应命令来完成我们应用程序环境的构建。「这里要注意的是Dockerfile
主要描述了构建时应该做哪些事情,只有当我们用命令行执行 docker build
的时候,Docker
软件就会按着 Dockerfile
的说明来构建出应用所需的环境和代码进行打包。」 而其最终构建出来的产物就是我们所说的「容器镜像(container image)。」 换言之,Docker
镜像就是我们的应用代码即运行环境!
更进一步来看Dockerfile
是构建镜像的蓝图。它定义了如何从一个基础镜像出发,通过一系列的命令,逐步构建出一个新的镜像。每个指令(如 RUN
、COPY
、ADD
等)都会创建镜像的一个新层。
而 Docker
镜像是 Dockerfile
执行后的结果。当 Dockerfile
被 Docker
引擎执行时Docker
会按照Dockerfile
中的指令执行相应的操作,逐步构建镜像。例如,执行 docker build
命令时,Docker
会读取Dockerfile
,按照其中的指令一步步构建镜像。
总结来看,Docker
本质上就是一个将「程序和环境打包并运行」的工具软件。具体点来说就是,它通过 Dockerfile
描述环境和应用程序的依赖关系, docker build
构建镜像, docker pull/push
跟Docker Registry
交互实现存储和分发镜像,docker run
命令基于镜像启动容器,基于容器技术运行程序和它对应的环境,从而解决环境依赖导致的各种问题。
至此我们就对Docker
的工作原理及工作流程进行了深入的分析。事实上,对于Dokcer
而言其提供的将而将应用程序“封装”在其中的能力,使得被封装在沙盒中的应用也能够方便地迁移到不同的环境中,这就便是Docker
所追求的理想目标。
在讲解完Dokcer
容器后,我们再来看今天的第二个主角K8s
。
Kubernetes
容器编排的神器
Kubernetes
产生背景
随着Dokcer
逐渐走入大众视野,容器化技术也受到了广大企业的青睐,越来越多的企业和开发者都开始使用微服务和容器化技术来构建自己的应用程序。容器化技术也依靠其可更加快速高效地开发和部署应用程序的同时,还能一定程度上提高程序的稳定性。当然事物的发展必然具有其两面性,容器技术同样也存在一些弊端。
当应用程序体量较小时可能并不会察觉,但随着如今应用程序的复杂化,服务间细粒度拆分,导致容器数不断攀升,进而使得容器的管理与维护就成为一个难题。容器数量上的话,可以通过shell
脚本来手动维护,但容器数增多后通过人力来维护规模庞大的容器数则不太现实。
如果此时能有一款如软件,可以协调、管理多个容器信息,进而实现自动化容器化应用的部署、扩展和管理。那之前我们面对的问题都将迎刃而解。而这个工具不是别人,正是Google
开发的Kubernetes(K8s)
K8s
架构
为了实现容器的编排及整体调度,Kubernetes
在会将我们的服务器划为「控制平面」(control plane)和「工作节点」如下两部分。其具体结构如下:
其中「控制平面」是负责管理和协调集群的核心组件,其主要负责调度工作负载、监控集群的状态、维护集群的健康状态,并确保集群中所有的节点和资源保持一致。它是整个K8s
集群的大脑,控制平面组件在K8s
集群中扮演着非常重要的角色。在控制平面内,API Server
主要是Kubernetes
集群的前端,所有的请求(如集群管理、资源创建、查询等)都会通过它进行;而 Scheduler
主要负责Pod
调度到合适的节点上运行;etcd
则是一个分布式键值存储,用于存储Kubernetes
集群的所有配置数据和状态信息。而Node
则是实际的工作节点,它既可以是「裸机服务器」,也可以是「虚拟机」。它会负责实际运行各个应用服务。多个应用服务「共享」一台 Node
上的内存和 CPU
等计算资源。
知晓了K8s
的架构模式后,我们再来看我们该如何将一个应用部署至K8s
.
K8s
服务部署
事实上,在Kubernetes
上部署服务一般涉及创建几个关键的资源对象,包括Pod、Deployment、Service
等。这些资源可以通过YAML
文件定义,然后应用到K8s
集群中。为了帮助读者更好的理解,我们这里以部署一个简单的Nginx
镜像为例,来展示我们该如何在K8s
中部署一个应用镜像:
创建一个Deployment
Deployment
是Kubernetes
中用于管理Pod副本和应用程序版本的控制器。通过Deployment
,你可以声明所需的Pod
副本数、容器镜像等信息。例如,如果我们要要构建一个简单的Nginx
服务的,那么我们的Deployment
可进行如下配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 3 # 定义Pod副本数
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest # 使用nginx镜像
ports:
- containerPort: 80 # 定义容器暴露的端口
「构建一个Service」
Service
在K8s
中用于暴露Pod
和其他服务的访问入口。通过Service
,你可以将多个Pod
(通常是通过Deployment
管理的)暴露为一个单一的服务,进行负载均衡。对于我们的nginx服务,我们进行如下配置:,
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx # 与Deployment中Pod的标签相匹配
ports:
- protocol: TCP
port: 80 # Service暴露的端口
targetPort: 80 # 转发到Pod的端口
type: LoadBalancer # 可选:如果使用云环境,使用LoadBalancer类型自动分配公网IP
「部署到K8s集群」
在完成了Deployment
和Service
的YAML
文件配置后,下一步就是使用kubectl
命令将它们应用到K8s
集群中。
kubectl apply -f deployment.yaml # 部署Deployment
kubectl apply -f service.yaml # 部署Service
受限于文章篇幅我们仅对K8s
的整体架构及工作流程进行一个简单的介绍,所以文中对K8s
的很多关键信息并未进行详细的介绍。如果对K8s
感兴趣的读者可以查阅相关的一些资料。
当然,学习任意一款工具,最好的方式一定是上手操作。如果你觉得本地搭建K8s
环境环境有困难的话,可以试一试Killercoda
和play-with-k8s
在线平台,两者都是为K8S
的初学者和进阶者提供了一个理想的训练场!
K8s
实战
接下来,我们将以简单的Java Web
应用为例来详细阐述我们该如何将我们的Java web
应用打包至K8s
进行集群中。笔者
在此将提供两种方式,一种是传统手动命令操作,一种则是通过[Cloud Toolkit]插件实现应用在阿里云的一键化部署。
手动方式
假设我们现在已经完成了一个Java web
应用的编写,我们的下一步就是将我们的应用构建至K8s
实现应用的部署。
对于一个Jave web
应用而言,如果其要实现在服务器上的运行,我们首先需要对应用程序进行打包。对于一个Maven
项目,我们可以通过mvn clean package
的命令来完成应用的打包,当执行完上述命令后,你会发现在你的target
目录下会生成一个应用对应的.jar
包。具体如下图所示:
当完成程序的打包后,下一步就是程序应用镜像的构建,正如我们之前所述,我们需要通过编写Dockerfile
来完成Docker
镜像的构建,因为我们的应用基于Jdk8
构建,所以我们需要指定基础镜像的版本为jdk8
。
# 使用 OpenJDK 8 镜像作为基础镜像
FROM openjdk:8-jre-alpine
# 将本地构建的 JAR 文件复制到容器中
COPY target/multi-thread-0.0.1-SNAPSHOT.jar /usr/app/multi-thread-0.0.1-SNAPSHOT.jar
# 暴露服务的8080端口
EXPOSE 8080
完成Dockerfile
的编写后,下一步就是进行容器的构建。此时,我们借助docker build -tmulti-thread:latest .
来完成我们应用镜像的构建,待镜像构建完毕后通过docker push
命令将镜像推送的仓库即可。
完成进行构建后,下一步工作则是将我们打包好的镜像交给K8s
来进行管理。这一阶段,我们则需要编写对应yml
来完成服务在k8s
的部署。具体的部署脚本如下:
❝❞
deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: multi-thread-deployment
spec:
replicas: 2 # 设置副本数,确保高可用性
selector:
matchLabels:
app: multi-thread-service
template:
metadata:
labels:
app: multi-thread
spec:
containers:
- name: multi-thread
image: multi-thread:latest # 这里使用推送到仓库的镜像
ports:
- containerPort: 8080 # 监听端口
❝❞
service.yaml
apiVersion: v1
kind: Service
metadata:
name: multi-thread
spec:
selector:
app: multi-thread-service # 通过标签选择与 Deployment 中的 Pod 匹配的 Pod
ports:
- protocol: TCP
port: 80 # Service 的暴露端口
targetPort: 8080 # 转发到 Pod 上的 8080 端口
type: LoadBalancer # 如果你部署在云环境下,使用 LoadBalancer 使服务暴露到外部
配置service.yaml
和deployment.yaml
后执行kubectl apply
从而将配置好的文件应用至K8s
中。如果想验证部署结果,则可以通过kubectl get
来查看查看 Pod
和 Deployment
的相关信息。
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
自动部署
若想实现应用自动部署至K8s
,则我们需要借助阿里运提供的[Cloud Toolkit]插件。
安装完成插件后,对于镜像打包我们可以借助插件的Dcoker
配置面板来完成,具体如下
镜像打包完成后即可通过Deploy to Reistry/ Kubernetes
来完成应用在K8s
的部署。
上述自动化部署需要借助阿里云
服务,具体的收费及详细方式可参考Cloud Toolkit 云插件官网。
总结
至此,我们就对容器化技术关键组件的Docker
和 Kubernetes(K8s)
的原理进行讲解和分析,并动手实践了应用在K8s
的部署。希望本文能对你学习Dokcer
和Kubernetes
有所帮助。
最后,如果你对学习K8s
很感兴趣不妨试一试文中提到的Killercoda
和play-with-k8s
的两个在线平台,而不是自己苦苦的搭建K8s
平台~~
当然,如果你对应用K8s
应用部署感兴趣的话,不妨试一试本文提到的Cloud Toolkit 云插件官网插件,当下阿里云有测试活动,没准能薅一个月的测试机会。
最后的最后,如果你很想动手实践的话,不妨找一找文章的优秀文章进行搭建环境,但笔者
觉得如果不是专业搞运维,上述的Killercoda
和play-with-k8s
完全能满足我们的需求了,因为真实企业中都会有专业运维来维护K8s
集群~~~~
点击关注公众号,“技术干货” 及时达!