一、虚拟机(VM/KVM vs docker):

Guest OS:虚拟机每台都要装完整系统(像租整套房),Docker直接用宿主机内核(像合租)。

资源占用:生产环境跑Nginx,虚拟机至少512MB内存,Docker容器64MB搞定。一台32核物理机,虚拟机只能开15台,Docker能跑200+容器。

启动速度:虚拟机从BIOS启动要2-3分钟,Docker容器0.5秒拉起。以前做CI/CD等虚拟机启动急死人,现在秒级扩容。

二、docker工作原理

1. 底层靠三个Linux内核技术

Cgroups限制进程组的资源使用上限,比如CPU核数、内存大小、磁盘IO、网络带宽等。生产环境里,用它防止一个容器把整台服务器资源吃光。此外,还能够对进程进行优先级设置,资源的计量以及资源的控制(比如:将进程挂起和恢复等操作)。

Namespaces:搞隔离。生产环境跑多个Java服务时,每个容器有自己的PID(进程ID从1开始)、网络(独立IP和端口)、文件系统(/目录隔离)。比如我们容器里的Nginx监听80端口,宿主机和其他容器完全不受影响。

UnionFS:(如OverlayFS):做镜像分层。Dockerfile里每行命令生成一层,基础镜像(如Alpine 5MB)+ JDK层 + 应用层。生产环境推新版本时,只传变更的那层,100台机器10秒全部更新完。

2. 架构分三层干活

Docker Engine(dockerd):对外接口。docker run命令就是它接收,生产环境用它做CI/CD流水线触发。

containerd:管容器生命周期。负责拉镜像、创建容器、监控状态。K8s底层直接调containerd,绕过dockerd。

runc:真正创建容器的工具。调用Linux系统调用(clone()、mount()等)设置Namespaces/Cgroups。每次docker run,背后是runc fork出一个新进程。

3. 从镜像到容器的实际流程

以部署Spring Boot应用为例:

  1. 构建镜像docker build把JAR包+JRE打包成镜像,分层存储(基础层复用,应用层增量)。
  2. 运行容器docker run -m 1g -c 2命令触发:
    • containerd从镜像仓库拉取镜像(省带宽,只拉缺失层)
    • runc创建新进程,用Namespaces隔离(网络/进程/文件系统)
    • Cgroups挂载限制(/sys/fs/cgroup/cpu/docker/xxx目录写入2核配额)
    • UnionFS挂载只读镜像层+可写容器层
  3. 运行时:应用进程在隔离环境中运行,监控工具(如cAdvisor)从Cgroups读取实时资源使用。

构建镜像参考

dockerfile
FROM openjdk:21  # 基础层(复用)
COPY target/user-service-1.0.jar /app/user.jar  # 应用层(每次变)
CMD ["java", "-jar", "/app/user.jar"]
构建
docker build -t myapp:v1 .

三、docker 网络5种模式

1.Docker默认有前3种网络模式:

bridge(默认):
容器通过docker0网桥通信,各自有独立IP。适合普通应用,比如Nginx容器和PHP容器通过内网IP互相访问,但对外要映射端口(-p 80:80)。

host
容器直接用宿主机网络,无隔离。适合高性能场景,比如监控agent需要抓宿主机所有流量,或性能敏感的网关服务(避免NAT损耗)。

none
完全无网络。跑纯计算任务时用,比如离线数据处理容器,不需要任何网络交互,最安全。

container
共享另一个容器的网络栈。典型场景:sidecar模式,比如主应用容器+Apm监控容器共享网络,Apm直接抓主容器流量,不用端口映射。

2.自定义网络(需要手动创建,不属于默认模式):

1.Docker自定义网络主要三种,生产常用前两种:

1.1自定义bridge

docker network create mynet
docker run --network=mynet -d nginx

作用:容器间通过服务名直接通信(不用IP),自动DNS解析

生产场景:微服务间调用,比如user-service容器直接ping order-service,不用记IP。比默认bridge强在避免端口冲突,100个容器可以都用8080端口。

1.2overlay

docker network create -d overlay my-cluster-net

作用:跨物理机容器组网,让不同服务器的容器像在同一个局域网

生产场景:Swarm/K8s集群里,上海服务器的容器直接访问北京服务器的Redis,网络对应用透明。我们自动扩的50个容器,不管在哪个机房都能互相发现。

1.3macvlan(少用)

作用:容器直接拿到物理网络IP,像虚拟机一样接入企业网络

生产场景:老旧系统改造,比如银行要求每个服务必须有固定IP做ACL策略,容器直接分配192.168.10.50,交换机策略不用变。

四、镜像构建docker commit VS Dockerfile

docker commit:

  1. 可靠性commit 生成的镜像不可重现
  2. 安全合规:它会打包容器内所有临时文件(包括密钥、历史记录),违反SOC2审计要求。
  3. 可观察性:无法追溯变更来源,而Dockerfile每行指令都能关联Git提交,实现完整SLI/SLO追踪。

唯一例外:生产紧急热修复时,用 commit 快速止血,但24小时内必须重构为Dockerfile,否则自动阻断流水线发布。

Dockerfile:

  1. 镜像最小化:使用多阶段构建和精简基础镜像(如alpine),我们曾将部署时间减少65%,降低网络传输失败率,节约硬盘

  2. 缓存策略设计:按变更频率排序指令,确保CI/CD流水线高效运行,减少开发者等待时间

  3. 安全基线

    • 非root用户运行
    • 构建时清理缓存
    • 镜像签名与漏洞扫描集成到发布流水线
  4. 可观测性内置:标准化HEALTHCHECK和日志输出格式,确保平台能准确判断服务状态

  5. 环境一致性:通过ARG/ENV分离构建与运行时参数,消除"在我机器上能运行"问题

五、有状态VS无状态

无状态优先:业务逻辑容器必须无状态,确保秒级扩缩容和自愈。
有状态隔离:数据库等状态服务单独部署,通过外部存储(如云盘)持久化,与计算层解耦。
核心原则:容器销毁时,无状态服务零数据丢失,有状态服务RPO<5秒——这是我们的SLA红线。

若有状态必须容器化需满足:

  1. 数据目录挂外部云盘(非容器层)
  2. 单实例约束 + 禁用自动扩缩容
  3. 每日异地备份且验证恢复

六、容器分层

容器的数据分层目录

LowerDir: image 镜像层,即镜像本身,只读

UpperDir: 容器的上层,可读写 ,容器变化的数据存放在此处

MergedDir: 容器的文件系统,使用Union FS(联合文件系统)将lowerdir 和 upperdir 合并完成 后给容器使用,最终呈现给用户的统一视图

WorkDir: 容器在宿主机的工作目录,挂载后内容会被清空,且在使用过程中其内容用户不可见

查看 docker inspect +容器

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐