【容器化技术 Docker 与微服务部署】详解

容器化技术 Docker 与微服务部署

一、容器化技术概述

(一)概念

容器化技术是一种操作系统级别的虚拟化方法,它允许将应用程序及其依赖项(如运行时环境、系统工具、库等)打包成一个独立的、可移植的单元,这个单元就是容器。容器在运行时与宿主机共享操作系统内核,但又能在用户空间实现进程、网络、文件系统等资源的隔离,使得应用程序可以在不同的环境中以一致的方式运行。

(二)特点

1. 轻量级
  • 与传统虚拟机相比,容器不需要为每个应用程序模拟完整的操作系统,而是共享宿主机的内核,只在用户空间进行资源隔离和虚拟化。这使得容器的启动速度更快,占用的磁盘空间和内存资源更少。例如,一个简单的 Java 应用程序容器镜像可能只有几十兆字节,而一个包含完整操作系统的虚拟机镜像往往会达到几个吉字节。从源码层面看,Docker(以 Docker 为例阐述容器化技术实现,后续会详细介绍)通过使用 Linux 内核的 cgroups(控制组)来限制容器对 CPU、内存等资源的使用量,通过 namespaces 实现进程、网络、文件系统等的隔离,这些内核特性的巧妙运用使得容器能够以轻量级的方式运行。
2. 环境一致性
  • 容器将应用程序及其依赖项打包在一起,无论在开发、测试还是生产环境,只要宿主机安装了容器运行时(如 Docker),容器就能以相同的配置和依赖运行,避免了传统开发中常见的 “在我机器上能运行,在其他机器上不行” 的环境不一致问题。这是因为容器内部的文件系统、环境变量等都是在构建镜像时就确定好的,不会受到宿主机环境变化的影响。
3. 快速部署
  • 容器可以快速启动和停止,基于镜像创建容器的过程通常只需要几秒到几十秒,相比于传统虚拟机的部署(可能需要几分钟甚至更长时间来启动操作系统和配置应用),大大提高了部署效率。例如,在应对突发流量时,可以快速启动多个容器实例来扩展服务能力。
4. 可移植性强
  • 容器镜像可以在不同的操作系统和云平台上运行,只要该平台支持相应的容器运行时。这方便了应用的迁移和部署,比如可以在本地开发环境构建好容器镜像,然后轻松部署到云端的生产环境中。

(三)与传统虚拟机的区别

1. 资源隔离与共享
  • 传统虚拟机:在硬件层面通过虚拟化技术(如 VMware、Hyper-V 等使用的技术)模拟出完整的硬件环境,每个虚拟机都有自己独立的操作系统内核,资源隔离是基于硬件级别的模拟实现的,不同虚拟机之间的资源完全独立,对硬件资源的消耗较大。
  • 容器:如前面所述,容器是基于操作系统内核的特性实现资源隔离,多个容器共享宿主机的内核,只是在用户空间进行隔离,在资源利用上更加高效,能在一台宿主机上运行更多的容器实例,但同时也因为共享内核,如果内核出现问题可能会影响所有容器。
2. 启动速度与资源占用
  • 传统虚拟机:启动时需要加载完整的操作系统内核、初始化各种系统服务等,启动过程耗时较长,并且由于每个虚拟机都包含完整的操作系统及应用,占用的磁盘空间和内存资源较多。
  • 容器:直接利用宿主机的内核,启动时只需加载容器自身的应用和配置,启动速度极快,而且镜像体积小,资源占用少,更适合在资源有限的环境下大规模部署应用。
3. 可移植性
  • 传统虚拟机:由于依赖特定的虚拟化平台和硬件环境,将虚拟机从一个平台迁移到另一个平台可能会遇到兼容性问题,需要进行复杂的配置和转换工作。
  • 容器:基于容器镜像的标准化格式,只要目标平台支持容器运行时,就能轻松迁移和部署,可移植性非常好。

(四)Docker 在微服务架构下的应用优势

1. 快速部署微服务
  • 在微服务架构中,通常有多个微服务组成一个完整的应用系统。使用 Docker,可以为每个微服务构建独立的镜像,然后快速部署成容器实例,每个微服务的部署和启动都变得简单快捷,减少了整体的部署时间。例如,一个电商系统包含用户服务、商品服务、订单服务等多个微服务,通过 Docker 可以将它们分别打包成镜像并快速部署到生产环境中。
2. 环境隔离
  • 不同微服务可能依赖不同的运行时环境、库版本等,Docker 容器能够很好地实现这种环境隔离。比如用户服务可能基于 Java 11 开发,商品服务基于 Python 开发,通过 Docker 容器可以确保它们在各自独立的环境中运行,互不干扰,避免了不同微服务之间因依赖冲突而导致的问题。
3. 资源高效利用
  • 微服务架构下,各个微服务的资源需求不同,有的可能需要较多的 CPU 资源,有的可能更依赖内存资源。Docker 可以通过配置参数(基于 cgroups 等内核特性实现)为每个容器精确分配资源,实现资源的高效利用,避免资源浪费,同时保证各个微服务都能获得所需的资源来稳定运行。
4. 便于服务的扩展和收缩
  • 根据业务流量的变化,可以方便地通过启动或停止 Docker 容器来扩展或收缩微服务的实例数量。比如在电商促销活动期间,可以快速启动更多的订单服务容器来处理大量的订单请求,活动结束后再减少容器数量,节省资源,提高资源的灵活性和利用率。
5. 版本管理和回滚方便
  • Docker 镜像的版本管理清晰,每个镜像都有对应的标签(如版本号等),在微服务升级过程中,如果发现新的版本出现问题,可以快速回滚到之前稳定的版本,只需要切换使用对应的镜像来启动容器即可,降低了微服务升级的风险。

二、Docker 的基本概念、安装与配置

(一)基本概念

1. 镜像(Image)
  • 镜像是一个只读的模板,包含了运行容器所需的所有文件和配置信息,如应用程序代码、运行时环境、系统库等。可以把镜像看作是容器的 “蓝图”,它是通过一系列的文件系统层叠加而成的(在 Docker 的存储驱动底层,采用了分层存储的机制,比如 aufs、overlay2 等存储驱动,通过对不同层的文件系统进行合并和管理来构建镜像,每层只存储与上一层相比发生变化的文件,这样可以减少镜像的体积并提高复用性)。镜像可以被创建、共享和分发,多个容器可以基于同一个镜像创建。
2. 容器(Container)
  • 容器是基于镜像创建的运行实例,是镜像的可运行态。它在运行时拥有独立的文件系统、进程空间、网络配置等,但共享宿主机的内核。容器可以被启动、停止、删除,并且可以动态地调整资源分配(通过 Docker 命令或者 API 基于 cgroups 等进行资源配置调整),在其生命周期内可以执行各种操作,比如运行应用程序、对外提供服务等。
3. 仓库(Repository)
  • 仓库是用于存放 Docker 镜像的地方,类似于代码仓库(如 GitHub)存放代码一样。它可以是公共的(如 Docker Hub,上面有大量开源的镜像可供使用),也可以是私有的(企业内部为了安全和隐私构建的镜像仓库)。通过仓库,可以方便地分享、拉取和管理镜像,在构建微服务的容器化部署时,通常会将自己构建的微服务镜像推送到仓库中,以便在不同环境中部署使用。

(二)安装与配置过程

1. 在 Linux 系统(以 Ubuntu 为例)下安装 Docker

更新包索引并安装依赖包:

sudo apt-get update
sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

添加 Docker 的官方 GPG 密钥:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

设置稳定版仓库:

sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

安装 Docker 引擎:

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io

验证安装是否成功:

sudo docker run hello-world

如果看到输出 Hello from Docker! 等欢迎信息,则表示 Docker 安装成功。

2. 配置 Docker(可选)

配置镜像加速器(以国内使用阿里云镜像加速器为例):

对于国内用户,拉取 Docker 镜像时可能速度较慢,可以配置镜像加速器来提高速度。

创建或编辑 /etc/docker/daemon.json 文件(如果不存在则创建),添加以下内容:

{"registry-mirrors": ["https://<你的阿里云加速器地址>.mirror.aliyuncs.com"]
}

然后重启 Docker 服务:

sudo systemctl restart docker

(三)创建和管理 Docker 镜像

1. 使用 docker commit 创建镜像(不推荐用于生产,常用于临时测试等场景)

首先,拉取一个基础镜像(以 Ubuntu 为例)并运行容器:

docker run -it ubuntu:latest /bin/bash

在容器内安装一些软件(假设安装 curl):

apt-get update
apt-get install curl

然后退出容器,使用 docker commit 命令基于修改后的容器创建新的镜像:

docker commit <容器 ID> my-custom-ubuntu:1.0

这里 <容器 ID> 是刚才运行并安装了软件的容器的唯一标识符,可以通过 docker ps -a 命令查看,my-custom-ubuntu:1.0 就是创建的新镜像名称及版本标签。

2. 使用 Dockerfile 定义镜像构建过程(推荐做法)

示例:构建一个简单的 Java 应用程序镜像
假设我们有一个简单的 Java 项目,目录结构如下:

my-java-app/
├── src/
│   └── Main.java
└── Dockerfile

Main.java 内容如下(简单打印一句话):

public class Main {public static void main(String[] args) {System.out.println("Hello from my Java app in Docker!");}
}

Dockerfile 内容如下:

# 基于 OpenJDK 11 基础镜像
FROM openjdk:11-jdk# 设置工作目录
WORKDIR /app# 将当前目录下的所有文件(除了 Dockerfile 本身)复制到容器内的 /app 目录下
COPY. /app# 编译 Java 代码
RUN javac src/Main.java# 设置容器启动时执行的命令
CMD ["java", "src/Main.class"]

构建镜像过程:
在包含 Dockerfile 的目录下(这里是 my-java-app 目录),执行以下命令:

docker build -t my-java-app:1.0.

其中 -t 参数用于指定镜像的名称和标签,最后的 . 表示使用当前目录下的 Dockerfile 进行构建。在构建过程中,Docker 会按照 Dockerfile 中的指令依次执行,先拉取 openjdk:11-jdk 基础镜像,然后设置工作目录、复制文件、编译代码、设置启动命令等操作,最终构建出 my-java-app:1.0 这个镜像。

(四)运行和管理 Docker 容器

1. 运行容器

使用刚才构建的 my-java-app:1.0 镜像运行容器:

docker run my-java-app:1.0

这会启动一个容器,容器内的 Java 应用程序将会运行,在控制台输出 Hello from my Java app in Docker!。
可以添加一些参数来配置容器的运行,比如:

  • -d 参数表示让容器在后台运行(守护态),例如:
docker run -d my-java-app:1.0
  • -p 参数用于端口映射,假设容器内的应用监听 8080 端口,要将其映射到宿主机的 8080 端口,可以这样做:
docker run -p 8080:8080 my-java-app:1.0
  • –name 参数可以给容器指定一个名称,方便后续对容器进行管理,比如:
docker run --name my-java-app-container -p 8080:8080 my-java-app:1.0
2. 查看容器状态

使用 docker ps 命令可以查看正在运行的容器状态,加上 -a 参数(docker ps -a)可以查看所有的容器(包括已经停止的容器),输出信息会包含容器的 ID、名称、镜像名称、状态等信息,方便了解容器的运行情况和进行后续管理操作。

3. 停止和删除容器
  • 停止容器可以使用 docker stop <容器名称或 ID> 命令,例如:
docker stop my-java-app-container
  • 删除容器使用 docker rm <容器名称或 ID> 命令,不过要先停止容器才能删除,或者使用 -f 参数强制删除正在运行的容器(不建议,可能导致数据丢失等问题),比如:
docker rm my-java-app-container
  • 如果要删除所有已经停止的容器,可以使用以下命令组合:
docker container prune

三、使用 Docker 进行微服务的部署

(一)构建微服务的镜像

####1. 以 Spring Boot 微服务为例
假设我们有一个简单的 Spring Boot 微服务项目,用于提供用户管理功能,项目结构如下:

user-service/
├── src/
│   └── main/
│       ├── java/
│       │   └── com/example/userservice/
│       │       ├── UserController.java
│       │       ├── UserService.java
│       │       ├── UserRepository.java
│       │       └── User.java
│       └── resources/
│           ├── application.properties
│           └── static/
└── Dockerfile

关键代码(简化示例):
UserController.java:

@RestController
@RequestMapping("/users")
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}@GetMapping("/{id}")public User getUserById(@PathVariable Long id) {return userService.getUserById(id);}
}

UserService.java:

@Service
public class UserService {private final UserRepository userRepository;public UserService(UserRepository userRepository) {this.userRepository = userRepository;}public User getUserById(Long id) {return userRepository.findById(id).orElse(null);}
}

UserRepository.java:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

User.java:

@Entity
@Table(name = "users")
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String email;// 省略构造函数、Getter 和 Setter 等方法
}

application.properties:

server.port=8080
spring.datasource.url=jdbc:mysql://<数据库地址>/<数据库名称>
spring.datasource.username=<用户名>
spring.datasource.password=<密码>

Dockerfile 示例:

# 基于 OpenJDK 11 构建 Spring Boot 微服务镜像
FROM openjdk:11-jdk# 设置工作目录
WORKDIR /app# 复制项目的 JAR 文件到容器内(假设已经通过 Maven 或 Gradle 构建好 JAR 文件)
COPY target/user-service-1.0.jar /app/user-service-1.0.jar# 设置容器启动时执行的命令,运行 Spring Boot 微服务
CMD ["java", "-jar", "user-service-1.0.jar"]

构建镜像过程:

  • 先在项目目录下通过 Maven 或 Gradle 构建出可执行的 JAR 文件(以 Maven 为例,执行 mvn clean package),然后在包含 Dockerfile 的目录下执行以下命令构建镜像:
docker build -t user-service:1.0.

这样就构建出了名为 user-service:1.0 的 Spring Boot 微服务镜像,后续可以基于此镜像部署容器实例。

(二)配置容器网络

1. 容器网络基础

Docker 提供了多种网络模式,以满足不同的应用场景和网络需求,常见的网络模式如下:

桥接模式(Bridge):
  • 这是 Docker 默认的网络模式,其实现依赖于 Linux 内核中的 bridge 模块。从源码角度来看,当启动 Docker 并采用桥接模式时,Docker 守护进程会在内核中创建一个虚拟网桥(相当于一个虚拟交换机),这个网桥负责连接各个容器以及与外部网络的通信交互。
  • 在这种模式下,每个启动的容器都会被连接到这个虚拟网桥上,并且会被分配一个属于内部网段的 IP 地址(通常在 172.17.0.0/16 这样的网段内,不过具体网段是可以通过 Docker 的网络配置参数进行调整的)。例如,启动多个容器后,它们各自获得如 172.17.0.2、172.17.0.3 等不同的内部 IP 地址,这些容器之间可以基于这些内部 IP 地址实现相互通信,就如同它们处于同一个局域网中一样。
  • 同时,为了让容器能够与外部网络(如互联网或者宿主机所在的局域网)进行通信,Docker 支持通过端口映射机制来实现。在启动容器时,使用 -p 参数可以指定宿主机的端口与容器内部端口的映射关系。比如,容器内的应用监听 8080 端口,执行 docker run -p 8080:8080 <镜像名称> 命令后,外部网络的请求发送到宿主机的 8080 端口时,就会被转发到对应的容器内的 8080 端口上,从而实现容器对外提供服务以及接收外部请求的功能。
  • 从性能方面考虑,桥接模式在一般的应用场景下能够提供较好的网络隔离和通信效率,不过由于网络数据包需要经过虚拟网桥的转发,在大规模容器通信或者对网络延迟要求极高的场景下,可能会存在一定的性能损耗,但对于大多数微服务部署场景来说,是完全可以满足需求的。
  • 在实际应用场景中,比如一个微服务架构下的电商系统,用户服务、商品服务、订单服务等多个微服务分别部署在不同容器中,采用桥接模式时,这些容器之间可以方便地基于内部 IP 地址进行相互调用,协同完成业务逻辑,同时又能通过端口映射对外提供相应的 API 接口供前端应用或者第三方系统访问。
主机模式(Host):
  • 在主机模式下,容器会直接使用宿主机的网络命名空间,这意味着容器中的网络配置与宿主机完全一致,容器内的网络接口就是宿主机的网络接口,容器共享宿主机的 IP 地址以及所有网络配置信息(如网络接口、IP 路由表等)。
  • 从实现原理来讲,在 Docker 创建容器并指定为主机模式时,它不会像桥接模式那样为容器单独创建虚拟网络环境,而是让容器直接复用宿主机的网络相关数据结构和配置。例如,容器内运行的网络服务监听某个端口时,实际上就是在宿主机的对应端口上进行监听,外部网络访问宿主机该端口就直接能访问到容器内的服务了,不存在端口映射的概念。
  • 这种模式的优势在于网络性能上几乎没有额外的损耗,因为没有中间的虚拟网络层进行转发,网络通信就如同直接在宿主机上运行的应用一样高效,特别适合对网络性能要求极高且对网络隔离要求相对较低的场景。然而,其缺点也很明显,由于容器和宿主机共享网络,可能会导致端口冲突等问题,并且容器之间的网络隔离性较差,一个容器对网络配置的修改(比如修改了 IP 地址或者路由规则)可能会影响到宿主机以及其他共享网络的容器。
  • 例如,在一个容器化的监控系统中,监控代理容器需要实时获取宿主机的网络状态信息并且对网络性能要求极高,此时采用主机模式,监控代理可以直接利用宿主机的网络接口收集数据,避免了网络转发带来的延迟和性能损耗,更好地保障监控数据的及时性和准确性。
overlay 网络模式:
  • overlay 网络模式主要用于跨多台宿主机的容器网络通信场景,它构建在已有的网络之上(可以是物理网络或者其他虚拟网络),实现不同宿主机上的容器之间能够像在同一局域网内一样进行通信。
  • 其实现基于网络虚拟化技术以及分布式键值存储(如 etcd 等,用于存储和同步网络配置信息),在每台宿主机上,Docker 会创建一个 overlay 网络驱动实例,这个驱动负责将本地容器连接到 overlay 网络中,并且与其他宿主机上的 overlay 网络实例进行通信协调。当容器需要跨宿主机通信时,数据包会经过一系列的封装和转发处理(在源码中涉及到网络协议栈的相关处理以及对 overlay 网络特定协议的实现),通过已有的网络基础设施传输到目标宿主机,然后再解封装并转发到目标容器。
  • 例如,在一个分布式的微服务应用部署场景中,微服务被部署在多台服务器上的 Docker 容器中,为了让这些分布在不同宿主机上的微服务能够相互协作,就可以采用 overlay 网络模式,使得不同宿主机上的容器可以通过统一的网络命名空间进行通信,就像它们都在同一个物理网络中一样,方便实现服务间的调用和数据交互,保障整个分布式系统的正常运行。
  • 这种模式的优势在于能够很好地支持大规模的分布式容器部署,实现跨主机的网络通信和服务发现,但由于涉及到复杂的网络封装、转发以及分布式协调等机制,相对来说网络配置和管理会复杂一些,并且在一定程度上会有网络性能损耗,不过随着技术的不断发展和优化,其性能在很多场景下也是可以接受的。
macvlan 网络模式:
  • macvlan 网络模式允许容器拥有自己独立的 MAC 地址,使得容器在网络中看起来就像一台独立的物理主机一样,可以直接连接到物理网络或者二层网络交换机上,实现与外部网络设备的直接通信。
  • 在实现上,Docker 通过配置 Linux 内核的 macvlan 网络驱动,为每个容器创建一个虚拟的网络接口,赋予其独特的 MAC 地址,并将其关联到指定的物理网络接口或者 VLAN 上。从网络层面看,容器的网络流量就如同从独立的物理设备发出一样,遵循传统的网络二层和三层协议进行转发和通信,外部网络设备(如路由器、交换机等)可以基于容器的 MAC 地址和 IP 地址对其进行路由和访问控制。
  • 例如,在企业内部的网络环境中,有一些对网络兼容性和直接接入物理网络有要求的应用容器,采用 macvlan 网络模式,这些容器可以无缝接入现有的网络架构,无需对网络基础设施进行大规模的改造,并且能够方便地与其他物理设备或者基于物理网络的系统进行通信和协作,同时也保障了容器网络的独立性和可管理性。
  • 不过,使用 macvlan 网络模式需要注意网络中的 MAC 地址冲突问题以及对物理网络环境的一些配置要求(如交换机的端口配置等),并且由于容器直接接入物理网络,可能会增加网络管理的复杂性和潜在的安全风险,需要根据实际的网络环境和安全策略进行合理的应用。
  • 不同的 Docker 网络模式各有其特点和适用场景,在实际使用 Docker 进行微服务部署时,需要根据具体的业务需求、网络环境以及性能要求等因素综合考虑,选择合适的网络模式来构建容器网络,以保障微服务之间能够高效、安全地进行通信和协作。

(三)与其他容器编排工具(如 Kubernetes 等)的协同工作方式

1. Kubernetes 概述

Kubernetes
是一个开源的容器编排平台,用于自动化容器的部署、扩展和管理,它提供了丰富的功能和机制来处理容器化应用在复杂环境下的运行、调度以及资源分配等问题。

从架构层面来看,Kubernetes 由多个核心组件构成,例如:

Master 节点组件:
  • kube-apiserver:这是 Kubernetes 的 API 服务器,是整个系统的 “大脑”,对外提供 RESTful API,用于接收和处理来自客户端(如命令行工具 kubectl、其他管理界面等)的各种请求,包括创建、查询、更新和删除 Kubernetes 资源(如 Pod、Service、Deployment 等)的操作。它对请求进行验证、授权以及持久化存储等处理,并且协调其他组件之间的工作,所有对 Kubernetes 集群的操作基本都要通过它来实现,在源码中,它实现了大量的 HTTP 处理逻辑以及对各种资源操作的接口定义和实现,保障 API 的稳定和高效运行。
  • kube-controller-manager:它是一组控制器的集合,负责运行各种控制器(如 ReplicationController、DeploymentController、NodeController 等),这些控制器会持续监控集群内的资源状态,并根据预设的规则和期望状态进行自动调节。例如,ReplicationController 会确保指定数量的 Pod 副本始终在运行,如果发现实际运行的 Pod 数量少于期望数量,就会自动创建新的 Pod;DeploymentController 则主要用于管理 Deployment 资源,实现应用的滚动更新等功能。通过这些控制器的协同工作,保障了集群内资源状态的稳定和符合预期,在源码中,各个控制器都有其独立的逻辑实现和状态管理机制,通过不断地循环检查和处理来维护集群的正常运行。
  • kube-scheduler:它的主要职责是为新创建的 Pod 选择合适的 Node(节点,也就是运行容器的宿主机)进行部署,会综合考虑多个因素,如 Node 的可用资源(CPU、内存等)、Pod 的资源需求、亲和性和反亲和性规则(决定哪些 Pod 适合或者不适合部署在同一 Node 上)等,通过一套复杂的调度算法(在源码中有详细的算法实现和相关的数据结构来支持调度决策)来找到最优的 Node,从而实现容器在集群内的合理分布和资源的高效利用。
Node 节点组件:
  • kubelet:运行在每个 Node 上,它负责与 Master 节点通信,接收 Master 发来的指令并在本地执行,比如创建、启动、停止和删除 Pod 等操作。同时,它还会监控本地容器的运行状态,向 Master 节点汇报资源使用情况(如 CPU、内存的使用率等),在源码中,kubelet 实现了与容器运行时(如 Docker、containerd 等)的交互接口,通过调用相应的 API 来管理容器,并且维护了本地容器的详细状态信息,保障容器在 Node 上的正常运行和与集群的协调一致。
  • kube-proxy:主要负责实现 Kubernetes 集群内的服务发现和负载均衡功能,它通过在 Node 上设置网络规则(如 iptables 规则、IPVS 规则等,不同的实现方式可以通过配置选择),将对服务(Service)的访问请求转发到对应的 Pod 集合上。例如,当有外部请求访问一个后端由多个 Pod 组成的服务时,kube-proxy 会根据设定的负载均衡策略(如轮询、随机等)将请求均匀地分配到各个 Pod 上,保障服务的高可用性和负载均衡,在源码中,kube-proxy 有针对不同网络代理机制的具体实现代码,用于准确地配置和更新网络规则,实现高效的服务转发和负载均衡功能。
2. Docker 与 Kubernetes 的协同工作

在实际的微服务部署场景中,Docker 与 Kubernetes 常常配合使用,各自发挥优势来构建高效、可靠的容器化应用平台。

容器运行时层面的协作:
  • Kubernetes 本身支持多种容器运行时,Docker 就是其常用的一种(当然也可以使用其他如 containerd 等)。在 Kubernetes 集群中,kubelet 作为与容器运行时交互的关键组件,当需要创建、启动或管理容器时,kubelet 会调用 Docker 的 API(通过实现相应的接口规范,在源码中有对应的调用逻辑和参数传递等实现细节)来执行具体的操作。例如,当一个新的 Pod(Pod 是 Kubernetes 中最小的可部署和可管理的计算单元,里面可以包含一个或多个容器)需要被部署到某个 Node 上时,kubelet 会向 Docker 发送创建容器的请求,按照 Pod 的配置信息(如镜像名称、容器启动命令、资源需求等)来启动相应的 Docker 容器,并且在容器运行过程中,kubelet 会持续监控容器状态,通过 Docker 的 API 获取容器的资源使用情况、运行状态等信息,并反馈给 Kubernetes 的其他组件,实现容器在集群内的统一管理和状态跟踪。
镜像管理与使用方面的协作:
  • Docker 构建的镜像可以无缝地被 Kubernetes 使用。在 Kubernetes 的资源配置文件(如 Deployment、Pod 等资源定义文件)中,通过指定镜像名称和标签(例如 image: my-service:1.0,这里 my-service:1.0 就是 Docker 构建的镜像),Kubernetes 在调度 Pod 部署时会从相应的镜像仓库(可以是 Docker Hub 等公共仓库,也可以是企业内部的私有仓库)拉取指定的镜像,然后基于该镜像启动容器。同时,Kubernetes 也提供了镜像更新、版本管理等功能,当需要升级微服务时,可以通过修改资源配置文件中的镜像标签来触发滚动更新(例如从 my-service:1.0 更新到 my-service:2.0),Kubernetes 会按照设定的更新策略(如逐步替换旧的 Pod 等),利用 Docker 的镜像拉取和容器启动功能,平稳地完成微服务的版本升级,保障业务的连续性和稳定性。
网络配置方面的协作:
  • 在网络配置上,Kubernetes 有自己的一套网络模型和配置机制,它旨在实现容器之间以及容器与外部网络的可靠、高效通信,同时要满足集群内复杂的服务发现、负载均衡等需求。虽然 Docker 本身有多种网络模式可供选择,但在 Kubernetes 集群中,通常会采用 Kubernetes 原生的网络插件(如 Calico、Flannel 等)来构建网络。这些网络插件会与 Docker 的网络功能相互配合,例如,在容器启动时,Kubernetes 网络插件会基于 Docker 的网络接口(通过调用 Docker 的网络相关 API 或者利用 Docker 已创建的网络环境基础)为容器配置合适的 IP 地址、设置网络路由以及实现跨节点的网络通信等功能,保障容器在 Kubernetes 集群环境下能够按照集群的网络规划进行通信和协作,实现不同微服务之间的相互调用以及对外提供统一的服务接口。
资源管理和调度方面的协作:
  • Kubernetes 通过资源配额(Resource Quota)、限制范围(Limit Range)等机制来对集群内的资源(如 CPU、内存等)进行统一管理和分配,确保各个微服务(以 Pod 为单位进行资源分配考量)能够获得合理的资源来稳定运行,避免资源的过度占用或浪费。在这个过程中,Docker 提供了基于 cgroups 等内核特性的资源限制和统计功能(前面介绍 Docker 基本概念时有提到),Kubernetes 在调度 Pod 时,会参考 Docker 提供的容器资源使用情况(通过 kubelet 获取并上报的信息)以及预先设定的资源需求参数(在 Pod 资源配置文件中定义),利用自身的调度算法(如前面提到的 kube-scheduler 的调度算法),将 Pod 合理地分配到不同的 Node 上,并且在运行过程中持续监控资源使用情况,通过与 Docker 的交互(如调整容器的资源限制参数等)来保障资源的有效利用和整个集群的性能平衡,实现微服务在资源层面的高效部署和稳定运行。

通过 Docker 与 Kubernetes 的紧密协作,能够充分发挥 Docker 在容器构建、镜像管理等方面的便利性以及 Kubernetes 在容器编排、资源管理、服务发现等方面的强大功能,为微服务的大规模部署、运行和管理提供了一个完善的解决方案,满足复杂业务场景下对容器化应用的各种需求。

(四)实际项目案例演示微服务的容器化部署流程

1. 案例背景

假设有一个在线商城系统,采用微服务架构,包含用户服务(负责用户注册、登录、信息查询等功能)、商品服务(管理商品信息的添加、查询、修改等操作)、订单服务(处理订单的创建、支付、查询等流程)以及支付服务(对接第三方支付平台完成支付操作)等多个微服务。该系统需要部署到生产环境中,并且要满足高可用性、可扩展性以及易于管理等要求,我们将使用 Docker 和 Kubernetes 来实现微服务的容器化部署。

2. 架构设计
整体架构:
  • 整个系统架构基于 Kubernetes 集群进行部署,Kubernetes 集群由多个 Master 节点(用于管理集群资源、调度等操作)和多个 Node 节点(作为容器的宿主机,运行各个微服务的容器)组成。每个微服务都构建为独立的 Docker 镜像,然后通过 Kubernetes 的资源配置文件进行定义和部署,实现微服务之间的相互协作以及对外提供服务。
微服务镜像构建:

以用户服务为例,其项目结构和代码实现(简化示例)如下:

user-service/
├── src/
│   └── main/
│       ├── java/
│       │   └── com/example/userservice/
│       │       ├── UserController.java
│       │       ├── UserService.java
│       │       ├── UserRepository.java
│       │       └── User.java
│       └── resources/
│           ├── application.properties
│           └── static/
└── Dockerfile

UserController.java:

@RestController
@RequestMapping("/users")
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}@GetMapping("/{id}")public User getUserById(@PathVariable Long id) {return userService.getUserById(id);}@PostMapping("/register")public User registerUser(@RequestBody User user) {return userService.registerUser(user);}
}

UserService.java:

@Service
public class UserService {private final UserRepository userRepository;public UserService(UserRepository userRepository) {this.userRepository = userRepository;}public User getUserById(Long id) {return userRepository.findById(id).orElse(null);}public User registerUser(User user) {return userRepository.save(user);}
}

UserRepository.java:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {}

User.java:

@Entity
@Table(name = "users")
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String name;private String email;// 省略构造函数、Getter 和 Setter 等方法
}

application.properties:

server.port=8080
spring.datasource.url=jdbc:mysql://<数据库地址>/<数据库名称>
spring.datasource.username=<用户名>
spring.datasource.password=<密码>

Dockerfile 示例:

# 基于 OpenJDK 11 构建 Spring Boot 微服务镜像
FROM openjdk:11-jdk# 设置工作目录
WORKDIR /app# 复制项目的 JAR 文件到容器内(假设已经通过 Maven 或 Gradle 构建好 JAR 文件)
COPY target/user-service-1.0.jar /app/user-service-1.0.jar# 设置容器启动时执行的命令,运行 Spring Boot 微服务
CMD ["java", "-jar", "user-service-1.0.jar"]
构建用户服务镜像的步骤如下:
  • 首先,在用户服务项目目录下通过 Maven 或 Gradle 构建出可执行的 JAR 文件(以 Maven 为例,执行 mvn clean package),然后在包含 Dockerfile 的目录下执行以下命令构建镜像:
docker build -t user-service:1.0.
  • 同样的方式,可以为商品服务、订单服务、支付服务等其他微服务分别构建对应的 Docker 镜像,只是每个微服务的代码实现和配置会根据其具体功能有所不同。
Kubernetes 资源配置:

接下来,为了在 Kubernetes 集群中部署这些微服务,需要创建相应的 Kubernetes 资源配置文件。
以用户服务为例,创建一个 user-service-deployment.yaml 文件用于定义 Deployment(用于管理 Pod 的创建、更新和副本数量等)资源:

apiVersion: apps/v1
kind: Deployment
metadata:name: user-service-deploymentlabels:app: user-service
spec:replicas: 3selector:matchLabels:app: user-servicetemplate:metadata:labels:app: user-servicespec:containers:- name: user-service-containerimage: user-service:1.0ports:- containerPort: 8080

在这个配置文件中:

  • apiVersion、kind 等字段定义了这是一个 apps/v1 版本的 Deployment 资源,名称为 user-service-deployment,并且通过 labels 打上了 app: user-service 的标签用于标识和筛选。
  • replicas 字段指定了要创建 3 个副本的 Pod,以保障服务的高可用性,即使某个容器出现故障,还有其他副本可以继续提供服务。
  • selector 用于指定该 Deployment 管理哪些 Pod,通过匹配 labels 中的 app: user-service 来关联对应的 Pod。
  • template 部分定义了 Pod 的模板,每个 Pod 内包含一个名为 user-service-container 的容器,使用之前构建的 user-service:1.0 镜像,并且容器内的应用监听 8080 端口。

还需要创建一个 user-service-service.yaml 文件用于定义 Service(用于实现服务发现和负载均衡)资源,使得其他微服务或者外部客户端能够访问到用户服务:

apiVersion: v1
kind: Service
metadata:name: user-service
spec:selector:app: user-serviceports:- protocol: TCPport: 8080targetPort: 8080type: ClusterIP

在这个 Service 配置文件中:

  • 通过 selector 关联到之前定义的带有 app: user-service 标签的 Pod,这样 Service 就知道要将请求转发到哪些后端 Pod 上。
  • ports 字段定义了服务暴露的端口信息,这里将外部访问的端口(port)和转发到容器内部的目标端口(targetPort)都设置为 8080,并且协议为 TCP。
  • type 字段设置为 ClusterIP,表示该服务在 Kubernetes 集群内部可访问,外部网络默认无法直接访问,如果需要对外暴露,可以根据实际情况修改为 NodePort 或 LoadBalancer 等类型(NodePort 会在每个 Node 节点上开放一个指定端口用于外部访问,LoadBalancer 通常用于结合云平台的负载均衡器对外提供服务)。

类似地,为商品服务、订单服务、支付服务等其他微服务也分别创建对应的 Deployment 和 Service 资源配置文件,只是配置中的名称、镜像等信息根据各自微服务进行相应修改。

3. 部署流程
准备 Kubernetes 集群:
  • 首先,需要搭建好一个 Kubernetes 集群,可以通过多种方式实现,比如使用 kubeadm 工具在多台服务器上进行安装和初始化(以下是简单的 kubeadm 初始化 Master 节点示例,实际操作可能需要根据具体环境进行更多配置和调整):
    在 Master 节点上执行以下命令:
sudo apt-get update
sudo apt-get install -y apt-transport-https ca-certificates curl
curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo add-apt-repository "deb https://packages.cloud.google.com/apt kubernetes-xenial main"
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo kubeadm init --pod-network-cidr=10.244.0.0/16
  • 初始化完成后,按照提示配置 kubectl 命令的使用权限(一般需要执行类似 mkdir -p $HOME/.kube && sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config && sudo chown ( i d − u ) : (id -u): (idu):(id -g) $HOME/.kube/config 的命令)。
    然后,安装网络插件(以 Flannel 网络插件为例),在 Master 节点上执行:
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
  • 接着,将 Node 节点加入到 Kubernetes 集群中,在每个 Node 节点上执行 kubeadm join 命令(命令参数需要根据 Master 节点初始化完成后输出的提示信息进行填写)。
部署微服务到 Kubernetes 集群:
  • 在确保 Kubernetes 集群正常运行且网络配置完成后,就可以将之前创建的微服务资源配置文件部署到集群中了。

以用户服务为例,在包含 user-service-deployment.yaml 和 user-service-service.yaml 文件的目录下,执行以下命令分别创建 Deployment 和 Service 资源:

kubectl apply -f user-service-deployment.yaml
kubectl apply -f user-service-service.yaml
  • 同样的方式,依次将其他微服务(商品服务、订单服务、支付服务等)对应的 Deployment 和 Service 资源配置文件部署到 Kubernetes 集群中,执行完这些操作后,Kubernetes 会根据配置自动调度并在合适的 Node 节点上创建相应的 Pod 副本,启动容器,并且通过 Service 资源实现服务之间的相互访问和负载均衡。

可以使用以下 kubectl 命令来查看部署情况:

  • 查看所有的 Deployment 资源状态:
kubectl get deployments

输出类似如下信息,展示各个微服务的 Deployment 的名称、可用副本数、期望副本数等情况:

NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
user-service-deployment   3/3     3            3           10m
product-service-deployment   3/3     3            3           8m
order-service-deployment    3/3     3            3           6m
payment-service-deployment   3/3     3            3           4m

查看所有的 Service 资源状态:

kubectl get services

输出类似如下信息,展示各个微服务的 Service 的名称、类型、集群内部 IP 地址以及暴露的端口等情况:

NAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
user-service            ClusterIP   10.96.123.123    <none>        8080/TCP   10m
product-service         ClusterIP   10.96.234.234    <none>        8080/TCP   8m
order-service           ClusterIP   10.96.345.345    <none>        8080/TCP   6m
payment-service          ClusterIP   10.96.456.456    <none>        8080/TCP   4m
  • 通过上述完整的流程,利用 Docker 构建微服务镜像,再结合 Kubernetes 进行容器编排和资源管理,成功实现了在线商城系统微服务的容器化部署,并且能够方便地进行扩展、管理以及保障服务的高可用性和稳定运行。
  • 在实际项目中,可以根据业务需求的变化,比如流量增长需要增加微服务副本数量、微服务版本更新等情况,通过修改 Kubernetes 的资源配置文件并执行相应的 kubectl 命令来实现灵活的调整和部署,充分发挥容器化技术和容器编排平台在微服务架构下的优势。

希望通过这个实际案例,能让读者更清晰地理解如何使用 Docker 与 Kubernetes 协同进行微服务的容器化部署,在实际的项目开发和部署中能够更好地运用这些技术来构建高效、可靠的分布式系统。

总之,容器化技术特别是 Docker 以及与之配合的容器编排工具如Kubernetes,为微服务的部署和管理带来了极大的便利和优势,从镜像构建、容器运行到集群化的资源调配和服务管理,形成了一套完整的解决方案,助力企业应对复杂多变的业务场景和不断增长的系统规模需求。

相关资料已更新
关注公众号:搜 架构研究站,回复:资料领取,即可获取全部面试题以及1000+份学习资料
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/498932.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【从零开始入门unity游戏开发之——C#篇43】C#补充知识——值类型和引用类型汇总补充、变量的生命周期与性能优化、值类型和引用类型组合使用

文章目录 一、值类型和引用类型汇总补充1、值类型和引用类型汇总2、值类型和引用类型的区别3、简单的判断值类型和引用类型 二、变量的生命周期与性能优化1、**栈和堆的区别**2、**变量生命周期**3、**垃圾回收&#xff08;GC&#xff09;机制**4、**代码示例与优化**4.1. 临时…

CSS2笔记

一、CSS基础 1.CSS简介 2.CSS的编写位置 2.1 行内样式 2.2 内部样式 2.3 外部样式 3.样式表的优先级 4.CSS语法规范 5.CSS代码风格 二、CSS选择器 1.CSS基本选择器 通配选择器元素选择器类选择器id选择器 1.1 通配选择器 1.2 元素选择器 1.3 类选择器 1.4 ID选择器 1.5 基…

小程序基础 —— 02 微信小程序账号注册

微信小程序账号注册 小程序开发与网页开发不一样&#xff0c;在开始微信小程序开发之前&#xff0c;需要访问微信公众平台&#xff0c;注册一个微信小程序账号。 有了小程序的账号以后&#xff0c;才可以开发和管理小程序&#xff0c;后续需要通过该账号进行开发信息的设置、…

LeetCode - 初级算法 数组(删除排序数组中的重复项)

免责声明:本文来源于个人知识与公开资料,仅用于学术交流。 删除排序数组中的重复项 这篇文章讨论如何从一个非严格递增的数组 nums 中删除重复的元素,使每个元素只出现一次,并返回新数组的长度。因为数组是排序的,只要是相同的肯定是挨着的,所以我们需要遍历所有数组,然…

Android使用DataBinding和Merge引发的血案

Android使用DataBinding和Merge引发的血案 1.前言&#xff1a; 相信Databinding和Merge大家都不陌生&#xff0c;今天讲解的是Databinding和Merge一起使用遇到的问题&#xff0c;在父布局使用&#xff0c;引用的布局使用Merge会导致id找不到&#xff0c;运行时直接崩溃了&…

SQLiteDataBase数据库

XML界面设计 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:layout_width"match_paren…

04-微服务02

我们将黑马商城拆分为5个微服务&#xff1a; 用户服务 商品服务 购物车服务 交易服务 支付服务 由于每个微服务都有不同的地址或端口&#xff0c;相信大家在与前端联调的时候发现了一些问题&#xff1a; 请求不同数据时要访问不同的入口&#xff0c;需要维护多个入口地址…

智能家居体验大变革 博联 AI 方案让智能不再繁琐

1. 全球AI技术发展背景及智能家居市场趋势 人工智能&#xff08;AI&#xff09;技术的飞速发展正在推动全球各行业的数字化转型。国际电信联盟与德勤联合发布《人工智能向善影响》报告指出&#xff0c;全球94%的商界领袖认为&#xff0c;人工智能技术对于其企业在未来5年内的发…

Windows onnxruntime编译openvino

理论上来说&#xff0c;可以直接访问 ONNXRuntime Releases 下载 dll 文件&#xff0c;然后从官方文档中下载缺少的头文件以直接调用&#xff0c;但我没有尝试过。 1. 下载 OpenVINO 包 从官网下载 OpenVINO 的安装包并放置在 C:\Program Files (x86) 路径下&#xff0c;例如…

docker学习记录-部署若依springcloud项目

使用docker compse部署RuoYi v3.6.4 一、打包代码 Java代码 打包前需要将127.0.0.1改成宿主机ip&#xff0c; 使用docker部署的nacos&#xff0c;应该是要改成ruoyi-nacos&#xff08;docker中的服务容器名&#xff09;。 使用idea window系统可能没有sh命令&#xff0c;不能…

汽车损坏识别检测数据集,使用yolo,pasical voc xml,coco json格式标注,6696张图片,可识别11种损坏类型,识别率89.7%

汽车损坏识别检测数据集&#xff0c;使用yolo&#xff0c;pasical voc xml&#xff0c;coco json格式标注&#xff0c;6696张图片&#xff0c;可识别11种损坏类型损坏&#xff1a; 前挡风玻璃&#xff08;damage-front-windscreen &#xff09; 损坏的门 &#xff08;damaged-d…

WPF使用OpenCvSharp4

WPF使用OpenCvSharp4 创建项目安装OpenCvSharp4 创建项目 安装OpenCvSharp4 在解决方案资源管理器中&#xff0c;右键单击项目名称&#xff0c;选择“管理 NuGet 包”。搜索并安装以下包&#xff1a; OpenCvSharp4OpenCvSharp4.ExtensionsOpenCvSharp4.runtime.winSystem.Man…

Nature+Science=ONNs(光学神经网络)

2024深度学习发论文&模型涨点之——光学神经网络 光学神经网络&#xff08;Optical Neural Networks, ONNs&#xff09;是一种利用光学器件&#xff08;如激光、光学调制器、滤波器、探测器等&#xff09;来模拟和实现神经网络推理功能的计算模型。这种网络通过利用光信号的…

计算机体系结构期末复习3:GPU架构及控制流问题

目录 一、GPU设计思路 1.简化流水线、增加核数 2.单指令多线程&#xff08;SIMT&#xff09; 3.同时驻留大量线程 4.总思路&#xff1a;多线程单指令多线程 二、GPU的控制流问题 1.什么是控制流问题 2.怎么应对分支分歧 一、GPU设计思路 1.简化流水线、增加核数 2.单指…

三大行业案例:AI大模型+Agent实践全景

本文将从AI Agent和大模型的发展背景切入&#xff0c;结合51Talk、哈啰出行以及B站三个各具特色的行业案例&#xff0c;带你一窥事件驱动架构、RAG技术、人机协作流程&#xff0c;以及一整套行之有效的实操方法。具体包含内容有&#xff1a;51Talk如何让智能客服“主动进攻”&a…

Vben5登录过期无法再次登录问题,http状态码

个人博客&#xff1a;无奈何杨&#xff08;wnhyang&#xff09; 个人语雀&#xff1a;wnhyang 共享语雀&#xff1a;在线知识共享 Github&#xff1a;wnhyang - Overview 前言 最近在做项目前端&#xff0c;使用的https://doc.vben.pro/&#xff0c;在登录过期时出现了无法…

Doris安装部署

Doris 概述 Apache Doris由百度大数据部研发&#xff08;之前叫百度 Palo&#xff0c;2018年贡献到 Apache 社区后&#xff0c;更名为 Doris &#xff09;&#xff0c;在百度内部&#xff0c;有超过200个产品线在使用&#xff0c;部署机器超过1000台&#xff0c;单一业务最大可…

基于单片机的多功能视力保护器(论文+源码)

1.系统设计 多功能视力保护器在设计过程中能够对用户阅读过程中的各项数据信息进行控制&#xff0c;整体设计分为亮种模式&#xff0c;分别是自动模式&#xff0c;手动模式。在自动模式的控制下&#xff0c;当单片机检测当前光照不强且有人时就开启LED灯&#xff0c;并且会根据…

如何在 Ubuntu 22.04 上部署 Nginx 并优化以应对高流量网站教程

简介 本教程将教你如何优化 Nginx&#xff0c;使其能够高效地处理高流量网站。 Nginx 是一个强大且高性能的 Web 服务器&#xff0c;以其高效处理大量并发连接的能力而闻名&#xff0c;这使得它成为高流量网站的流行选择。 正确优化 Nginx 可以显著提高服务器的性能&#xff0…

【持续更新中】transformer详解和embedding大模型

这里记录一下自己学习embedding大模型的记录&#xff0c;涉及到transformer和bert这些。 一切都可以编码&#xff0c;比如说图片是三原色 背景介绍 训练集和测试集的分&#xff0c;无监督学习&#xff0c;现在基本都是使用无监督学习&#xff0c;有监督学习的话参考计算机视觉…