Docker
Docker为什么会诞生
为解决传统部署存在的问题!
Docker(容器):
- 确保环境一致性: 将应用打包成镜像, 一次构建到处运行!
- **隔离性:**利用Linux内核特性(cgroups/namespace)实现进程隔离。
vs 虚拟机的隔离方式:
虚拟机:本质跑多个完整的系统,每个虚拟机有自己的 (独立内核 独立文件系统 独立网络 独立资源控制)
Docker: 本质是“在同一个系统中虚拟多个独立空间”,不用虚拟完整 OS,只是隔离进程 共享主机内核
Docker 利用 Namespaces 实现资源“看不见”(不同容器有自己独立的网络、进程表、文件系统),利用 Cgroups 实现资源“用不了太多”( 限制 CPU 核心数、内存上限,避免资源抢光)
问题:
要做到上述功能 必须需要Docker具有linux的内核,在Windows/mac上怎么才能具有linux内核呢?
Windows WSL2(Windows Subsystem for Linux) 轻量级Linux虚拟机(内置Linux内核) macOS HyperKit(基于Hypervisor.framework) 轻量级Linux虚拟机
用户启动Docker Desktop
自动启动一个隐藏的Linux虚拟机(通过WSL2或Hyper-V)
所有Docker容器实际运行在这个Linux虚拟机内
Docker CLI通过RPC与虚拟机内的Docker引擎通信
事实就是:
"Docker容器本质依赖Linux内核的特性(如Namespace/Cgroups)。在Windows/Mac上,Docker Desktop通过内置的Linux虚拟机(WSL2/HyperKit)提供Linux内核支持,容器实际运行在这个虚拟机内,而非直接调用Windows内核。"
为什么Docker 要运行在 Linux 上?
容器化技术依赖Linux内核的三大机制:
Namespaces(命名空间) → 提供进程隔离(PID、网络、挂载点等)
Cgroups(控制组) → 限制资源(CPU、内存、IO)
UnionFS(联合文件系统) → 实现镜像分层存储
WSL2 VS VM
WSL2 共享Windows内核的部分功能(如调度器),而传统VM需模拟完整硬件并运行独立OS。
问题:
“内核”到底是什么?它的角色和操作系统是一致的?
内核四大职责:
- 进程管理
创建/销毁进程(fork()、exit())
CPU调度(决定哪个进程运行) 示例:Docker容器本质是内核通过clone()+Namespace创建的隔离进程
- 内存管理
分配物理内存(malloc()底层依赖内核)
虚拟内存(分页/交换空间)
- 设备驱动
[todo]
- 文件系统
[todo]
联合文件系统
向上:通过系统调用(syscall) 为软件提供服务 向下:直接操作CPU/内存/设备寄存器
Docker部署的流程
- 编写 Dockerfile
定义基础镜像(如 golang:1.20 或更小的 scratch)
拷贝你的代码和依赖
编译生成可执行文件(或直接用编译好的二进制)
设置容器启动命令(比如运行 simplebank 服务)
- 构建镜像
执行 docker build -t simplebank:latest .
生成一个包含你的程序和运行环境的镜像文件
- 推送镜像(可选)
把镜像上传到远程仓库,如 Docker Hub 或私有 Harbor
方便其他服务器拉取
-
运行容器
-
访问服务 通过服务器的 IP 和端口访问你的 SimpleBank API
-
维护和更新
更新代码后,重复构建镜像和部署容器流程
可以通过 Docker Compose 或 Kubernetes 做多容器编排和扩展
镜像管理
容器操作
网络管理
数据持久化
Dockerfile
构建镜像: 基础环境 依赖安装 文件复制 启动命令
实际的执行流程:
镜像(Image) → 启动后变成 → 容器(Container)
Shell脚本
用途: 自动化任务、批量处理、服务管理、部署流程等。
#!/bin/sh
set -e
echo "run db migrations"
/app/migrate -path /app/migration -database "$DB_SOURCE" -verbose up
echo "start the app"
exec "$@"
EXPOSE 8080
CMD [ "/app/main" ]
ENTRYPOINT [ "/app/start.sh" ]
CMD 定义默认参数,会被 ENTRYPOINT 使用,也可被 docker run 的参数覆盖
完成_Dockerfiles_文件的编写,构建镜像!
docker build -t your-service:1.0 .
docker run -it --rm your-service:1.0 sh
运行!
Docker-compose.yaml
通过一个 YAML 文件定义和编排多容器应用,实现一键启动完整服务栈。
什么是多容器编排
多个容器协同运行,组成一个完整系统,并通过自动化工具统一管理和部署的过程,就叫多容器编排。
多个容器之间需要保证什么?
-
启动顺序正确
-
网络互通
-
环境一致
-
自动恢复
-
动态扩容
实例:
services:
postgres:
image: postgres:12-alpine
environment:
- POSTGRES_USER=root
- POSTGRES_PASSWORD=secret
- POSTGRES_DB=simple_bank
ports:
- "5433:5432"
volumes:
- data-volume:/var/lib/postgresql/data
启动了一个数据库容器 并且配置了一系列环境
depends_on:
- postgres
- redis
启动顺序
连接Dockerfiles 和 docker-compose
api:
build:
context: .
dockerfile: Dockerfile
问题
编写完Dockerfiles文件后 也就是构建完成镜像之后就可以启动镜像为容器了,也就是项目可以正常启动了,为什么还需要docker-compose文件编排多个容器呢?
docker-compose up -d # 一键启动所有服务,自动处理依赖
docker中的网络问题
网络模式:
- bridge
每个 Bridge 网络形成一个独立的虚拟局域网(VLAN)
同一网络内的容器可通过容器名互相访问(无需IP)
容器访问外网时,IP会被转换为宿主机IP(通过iptables规则)
需手动映射端口(-p 8080:80)才能从宿主机外部访问容器服务
端口映射
Docker 将容器内部端口绑定到宿主机端口的机制
# 将容器的80端口映射到宿主机的8080端口 docker run -d -p 8080:80 --name nginx nginx
网卡的作用:
主机和外部网络的桥梁,完成数据收发和初步封装
Docker 会创建虚拟网卡(veth pair):
一端在容器内,另一端接到宿主机的网桥(docker0)。
通过虚拟网卡,容器流量被送到宿主机网络,再被转发出去(如 NAT)。
NAT
把私有网络的 IP 地址转换成公有网络的 IP 地址,实现内网访问外网或不同网段通信。 主要解决 IP 不够用 + 内网隔离 + 控制流量 的问题。
Docker 默认用 NAT: 容器内的私有 IP(如 172.17.x.x)通过 NAT 转换成宿主机的 IP 对外通信。
通信
同网络下的容器可以通信,在自定义的网络中 他们可以通过容器名自动互相访问
创建网络 并加入pg容器
docker network connect bank-network postgres12
检查容器内部配置
docker network inspect bank-newtork
docker container inspect postgres12