云原生--核心组件-容器篇-5-Docker核心之-容器
1、Docker容器的定义与核心概念
-
定义:
Docker容器是基于Docker镜像运行的轻量级、独立、可移植的运行环境,它封装了应用程序及其依赖项,提供了一个隔离的执行空间。容器化应用比传统的虚拟机更加高效,因为它们共享主机操作系统的内核,而不是像虚拟机那样需要完整的操作系统实例。 -
核心关系:
- 镜像(Image):只读模板,用于创建容器。它包含运行某个特定应用所需的操作系统、库、环境变量和文件等。
- 容器(Container):镜像的运行实例,具有读写层(可写层)。可以基于同一个镜像启动多个容器,并且每个容器都是相互隔离的。
即:镜像如同一个模板,定义了应用程序运行所需的一切要素,而容器则是依据这个模板启动的一个或多个实例。可以把镜像想象成一个软件的安装包,容器就是安装并运行这个软件后的状态。
-
类比:
- 镜像:类似“类(Class)”,定义容器的结构。
- 容器:类似“对象(Object)”,是镜像的具体实例。
2、Docker容器的核心特性
(1)、轻量级与快速启动
- 原理:
容器共享宿主机的内核,不需要像虚拟机那样为每个实例分配独立的操作系统,因此占用资源少,仅包含应用程序和依赖,启动速度快。通常,容器可以在几毫秒内启动,而虚拟机可能需要几分钟。 - 优势:
- 启动时间<1秒(对比虚拟机的分钟级)。
- 资源占用极低(仅需应用程序所需资源)。
(2)、隔离性
每个容器都在自己独立的命名空间中运行,与宿主机上的其他进程和容器相互隔离。这意味着容器内的应用程序无法直接访问其他容器或宿主机的资源,除非进行了明确的配置。例如,不同容器可以使用相同的端口而不会发生冲突。
- 实现技术:
- 命名空间(Namespaces):隔离进程、网络、文件系统等资源(如PID、NET、MOUNT)。
- 控制组(Cgroups):限制容器的资源使用(CPU、内存、磁盘I/O)。
- 效果:
容器间互不干扰,与宿主机也隔离,确保安全性和稳定性。
(3)、可移植性
由于容器包含了应用程序及其所有依赖,因此可以在任何支持Docker的环境中运行,无论是开发环境、测试环境还是生产环境,都能保证应用程序的一致性。
- 跨平台运行:
容器可以在任何支持Docker的环境中运行(Linux、Windows、macOS、云平台)。 - 环境一致性:
通过镜像确保开发、测试、生产环境的行为一致,避免“在我的机器上能运行”的问题。
(4)、灵活性
- 动态管理:
可随时启动、停止、删除容器,或通过编排工具(如Kubernetes)实现弹性扩缩容。
3、Docker容器的工作原理
(1)、构建与运行流程
- 构建镜像:
通过Dockerfile定义镜像的构建步骤(如安装依赖、复制文件)。 - 创建容器:
使用docker run命令基于镜像启动容器,添加可写层。 - 运行与交互:
容器进程在隔离的环境中运行,所有修改仅影响可写层。 - 停止与删除:
容器停止后,数据默认丢失(除非持久化存储)。
(2)、分层存储机制
- 镜像层:
容器继承镜像的只读层,共享宿主机文件系统。 - 可写层:
容器启动时在顶层添加读写层,所有修改在此层记录。
理解下镜像层和容器的可写层:
-
镜像层(Image Layers)
Docker镜像是由一系列只读层组成的(一条指令代表一个只读层),每层代表镜像的一个变更。例如,当你在Dockerfile中使用一条指令(如RUN, COPY, 或者CMD),它会在前一层的基础上创建一个新的层。这种设计有几个好处:- 高效性:因为每一层都是只读的,所以一旦某一层被创建,它的内容就不会改变。这使得Docker可以缓存这些层,并在构建新镜像时重用它们,从而加快构建速度。
- 分层存储:多个镜像可以共享相同的底层,这样不仅节省了存储空间,也提高了传输效率。
-
容器的可写层(Container’s Writable Layer)
当从一个镜像启动一个容器时,Docker会在该镜像的最上层添加一个可写的临时层。这个可写层用于存储容器运行时的所有修改,比如文件系统的变化、环境变量的更新等。关键点包括:- 隔离性:容器的所有更改都仅限于其自身的可写层,不会影响到底层镜像或其他基于同一镜像的容器。
- 持久化选项:如果需要保存容器内的数据或配置更改,可以通过Docker卷(Volumes)或者绑定挂载(Bind Mounts)来实现数据的持久化。否则,在容器停止并删除后,所有在可写层中的更改都会丢失。
-
示例说明
简单的Dockerfile来构建一个Nginx服务。
dockerfile示例:
FROM nginx:latest
COPY ./html /usr/share/nginx/html
解释:
-
镜像层:
- 第一层只读层是获取官方的Nginx镜像。
- 第二层只读层是将本地的html文件夹复制到镜像中/usr/share/nginx/html目录下。
-
容器的可写层:
- 当你基于这个镜像启动一个容器时,Docker会在这个镜像的顶部添加一个可写层。
- 如果你在容器内修改了/usr/share/nginx/html/index.html文件,这个修改会被记录在容器的可写层中,而原始镜像保持不变。
这种方式确保了镜像的不可变性和容器间的独立性,同时也允许单个容器在其生命周期内进行必要的自定义调整。通过这种方式,Docker实现了高效、灵活的应用程序部署解决方案。
(3)、生命周期
- 状态:
- created:刚创建,未运行。
- running:容器正在运行。
- paused:暂停状态(进程挂起)。
- exited:已停止(正常或异常退出)。
- dead:停止后被自动删除。
4、Docker容器的存储管理
(1)、数据持久化方案
- 绑定挂载(Bind Mount):
将宿主机的目录挂载到容器内,数据持久化到宿主机。
bash示例:
docker run -v /host/path:/container/path my-image
- Docker卷(Volume):
Docker管理的独立存储,解耦数据与容器生命周期。
bash示例:
docker volume create my-volume
docker run -v my-volume:/container/path my-image
- 临时数据:
容器停止后,默认删除可写层数据(适用于无状态服务)。
(2)、存储驱动
- 默认驱动:
根据宿主机类型选择(如overlay2用于Linux)。 - 功能:
管理分层文件系统,支持快照和回滚。
5、Docker容器的网络与通信
(1)、网络模式
- 默认桥接网络(bridge):
容器通过NAT访问外部网络,容器间可通过IP通信。 - 主机模式(host):
容器共享宿主机的网络栈,无网络隔离。 - 自定义网络:
通过docker network create定义自定义网络(如overlay网络用于多主机)。
扩展:
-
什么是NAT?
NAT(Network Address Translation,网络地址转换)是一种在网络技术中常用的方法,用于修改网络数据包中的源或目标IP地址信息。NAT的主要目的是允许多个设备共享一个公共IP地址来访问互联网,这在IPv4地址资源有限的情况下尤为重要。
NAT通常部署在一个路由器或者防火墙上,在家庭网络和企业网络中非常常见。当内部网络的设备尝试访问外部网络时,NAT会将该设备的私有IP地址转换为公共IP地址;当响应返回时,NAT再将公共IP地址转换回原始的私有IP地址。 -
容器通过NAT访问外部网络
这意味着容器本身可能拥有一个私有的、局域网内的IP地址,这个地址通常是不可路由的(即不能直接从外部网络访问)。当容器尝试与外部网络通信时,宿主机上的NAT机制会将容器发出的数据包的源IP地址替换为主机的公共IP地址,这样请求就能正确到达目的地。当响应返回时,NAT再次发挥作用,将目的IP地址从主机的公共IP转换回容器的私有IP地址,从而实现双向通信。 -
容器间可通过IP通信
这意味着在同一台Docker主机上运行的容器可以直接通过它们各自的IP地址进行通信。这些容器通常会被分配到同一个虚拟网络中,默认情况下,Docker使用桥接网络模式(bridge network),在这种模式下,Docker会在宿主机上创建一个虚拟网络交换机,并为每个容器分配一个独立的IP地址。这样一来,容器之间就可以像在同一物理网络中的不同计算机那样相互通信了。
不过需要注意的是,如果容器分布在不同的Docker主机上,则需要额外的配置(如跨主机网络、overlay网络等)才能让这些容器通过IP地址互相通信。此外,对于位于不同子网或由NAT分隔开来的容器,直接基于IP地址的通信可能会受到限制,这时可能需要用到端口映射或其他网络策略来实现互连。
总结来说,NAT允许容器使用私有IP地址并通过Docker主机的公共IP地址访问外部网络,而容器间的直接IP通信依赖于它们是否处于同一虚拟网络环境中。
(2)、端口映射
- 语法:
docker run -p <主机端口>:<容器端口> my-image
- 示例:
docker run -p 8080:80 nginx 将容器的80端口映射到主机的8080端口
6、Docker容器的常用命令
(1)、docker run
从指定镜像创建并启动一个容器。
bash示例:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
参数:
- -d:后台运行容器。
- -p:发布容器的端口到主机上。
- -v:挂载主机目录到容器中(卷)。
(2)、docker start/stop/restart
分别用于启动、停止和重启容器。
bash示例:
docker start CONTAINER_ID
docker stop CONTAINER_ID
docker restart CONTAINER_ID
(3)、docker ps
列出当前正在运行的容器。
bash示例:
docker ps
使用-a参数可以查看所有容器(包括已停止的)。
(4)、docker exec
在运行中的容器内执行命令。
bash示例:
docker exec -it CONTAINER_ID /bin/bash
这个命令允许你进入容器内部进行调试或修改。
(5)、docker rm
删除一个或多个容器。
bash示例:
docker rm CONTAINER_ID
(6)、与宿主机和其他容器的交互
- 端口映射
通过端口映射,可以将容器内的端口暴露给宿主机,使得外部可以访问容器内的应用程序。
bash示例:
docker run -p 8080:80 nginx
解释:
上述命令将容器内的80端口映射到宿主机的8080端口,这样就可以通过访问宿主机的8080端口来访问容器内运行的Nginx服务。
- 数据卷挂载
数据卷是用于在宿主机和容器之间共享数据的机制。可以将宿主机的目录或文件挂载到容器内,这样容器内对挂载点的读写操作实际上是对宿主机上对应目录或文件的操作。
bash示例:
docker run -v /host/path:/container/path my_app
解释:
此命令将宿主机的 /host/path 目录挂载到容器的 /container/path 目录。
- 网络通信
Docker提供了多种网络模式,如桥接网络、主机网络、无网络等。默认情况下,容器使用桥接网络,通过宿主机的网络接口与外部通信。也可以创建自定义网络,让多个容器在同一个网络中相互通信。
bash示例:
docker network create my_network
docker run --network my_network my_app1
docker run --network my_network my_app2
解释:
上述命令创建了一个名为my_network的自定义网络,并将两个容器my_app1和my_app2加入到这个网络中,这样它们就可以相互通信。
- 监控容器资源使用情况
使用docker stats命令可以实时查看容器的CPU、内存、网络和磁盘I/O等资源使用情况。
bash示例:
docker stats my_container
- 查看容器日志
使用docker logs命令可以查看容器的日志信息。
bash示例:
docker logs my_container
解释:
若要实时查看日志,可以使用 docker logs -f 命令。
7、Docker容器的应用场景
(1)、开发环境一致性
- 场景:开发者在本地通过容器复现生产环境,减少“在我机器上能工作”的问题。
bash示例:
docker run -it -v $(pwd):/app my-python-env
(2)、微服务架构
- 优势:
- 每个服务独立打包为容器,隔离性强。
- 通过编排工具(如Kubernetes)实现弹性扩缩容。
(3)、持续集成/持续部署(CI/CD)
开发者可以使用Docker来构建代码,并将其打包成容器,然后在测试或生产环境中轻松部署这些容器。
- 流程:
- 构建镜像并推送到仓库。
- 在CI/CD系统中拉取镜像并部署。
- 工具:Jenkins、GitLab CI、GitHub Actions。
(4)、数据服务隔离
- 场景:
在单台宿主机上运行多个数据库实例(如MySQL、PostgreSQL)。
bash示例:
docker run --name mysql1 -e MYSQL_ROOT_PASSWORD=root mysql:5.7
docker run --name mysql2 -e MYSQL_ROOT_PASSWORD=root mysql:8.0
8、Docker容器与虚拟机的对比
9、容器的最佳实践
(1)、最小化镜像
使用轻量级基础镜像(如alpine),减少镜像体积。
(2)、单进程原则
每个容器仅运行一个主进程,避免复杂性。
(3)、持久化数据
使用卷或绑定挂载存储持久化数据,避免容器重启丢失。
(4)、安全加固
- 使用非root用户运行容器。
- 限制容器资源(CPU、内存)。
(5)、监控与日志
集成监控工具(如Prometheus)和日志系统(如ELK)。
10、常见问题解答
(1)、容器停止后数据丢失怎么办?
- 解决方案:
使用卷(Volume)或绑定挂载(Bind Mount)持久化数据。
(2)、如何查看容器的详细信息?
bash示例:
docker inspect <容器ID/名称> # 查看容器的元数据
(3)、容器与镜像的区别是什么?
- 镜像:只读模板,用于创建容器。
- 容器:镜像的运行实例,具有可写层。
11、总结
- Docker容器是现代DevOps和云原生的核心组件,通过轻量级虚拟化、环境一致性、快速部署等特性,解决了传统应用部署的痛点。
- 关键价值:
- 隔离性:确保应用独立运行。
- 可移植性:跨平台无缝迁移。
- 高效性:秒级启动,资源利用率高。
逆风前行,Dare To Be!!!