言成言成啊 | Kit Chen's Blog

Docker与Podman

发布于2025-02-23 01:18:58,更新于2025-05-06 18:11:53,标签:docker  文章会持续修订,转载请注明来源地址:https://meethigher.top/blog

在日常开发中,其实我这里是没有使用 Docker 的,依然是手动安装依赖、配置环境。

但是近期在抄别人的开源项目时,发现 Docker 基本已经成了开发者必备,因此我也特意学习了下,没啥深度,只是记录基础使用。

一、引言

1.1 Docker 是什么?

Docker 是一个开源的容器化平台,它能够让开发者在任何环境下构建、打包和运行应用程序。Docker 通过 容器(Container) 技术,使应用程序及其依赖项能够在不同环境中保持一致性,从而提高部署效率并减少环境差异带来的问题。

Docker 的核心容器引擎(Docker Engine)是开源的,但 Docker 公司围绕它构建了一些 商业化产品(如 Docker Desktop 和 Docker Hub 的高级功能)

简单来说,Docker 就像一个轻量级的虚拟机,但它比传统虚拟机更高效,占用更少资源,同时具备更快的启动速度。

1.2 Docker vs 传统虚拟机

在传统的软件开发和部署过程中,我们通常使用 虚拟机(Virtual Machine,VM) 来运行应用,而 Docker 提供了一种更轻量级的容器化方案。

1.2.1 Docker 与虚拟机对比

对比项Docker 容器传统虚拟机(VM)
架构共享宿主机的操作系统内核每个 VM 运行一个完整的操作系统
启动速度秒级启动(轻量级)分钟级启动(需要引导 OS)
资源占用共享主机资源,占用少每个 VM 需要独立的 CPU、内存
运行效率接近原生性能受限于虚拟化开销,性能较低
环境隔离进程级隔离,共享宿主机内核完全隔离,适用于多种 OS
移植性一次构建,到处运行依赖 VM 具体配置,移植性较差
部署难度轻量级部署,直接运行需要完整安装操作系统,部署复杂
存储管理使用 分层镜像,减少重复存储每个 VM 独立存储,磁盘占用大
适用场景微服务、CI/CD、云原生应用运行不同操作系统、传统 IT 业务

1.2.2 为什么选择 Docker?

Docker 由于 启动快、占用资源少、易于部署,在现代软件开发中越来越流行,特别适用于:

  • 微服务架构:可以将每个服务独立打包成一个容器,方便部署和扩展。
  • 持续集成/持续部署(CI/CD):配合 Jenkins、GitHub Actions,快速构建和发布应用。
  • 云计算和 DevOps:可以轻松迁移、扩展应用,适用于 AWS、Kubernetes 等云平台。

什么时候用传统虚拟机?
如果需要运行 不同操作系统,如 Windows + Linux 环境,或者需要完整的系统级隔离,传统虚拟机仍然是更好的选择。

1.3 Docker 的核心概念

Docker 主要由 镜像(Image)容器(Container)仓库(Registry) 三个核心部分组成:

  • 镜像(Image):应用程序及其依赖的封装,类似于一个模板,用于创建容器。
  • 容器(Container):镜像的运行实例,一个独立的进程,拥有自己的文件系统、网络环境等。
  • 仓库(Registry):用于存储和分发 Docker 镜像,类似于代码仓库,常见的有 Docker Hub、阿里云镜像仓库等。

镜像 → 容器 → 运行应用,这是 Docker 的基本流程,后续章节会详细介绍如何使用 Docker 进行构建、管理和部署应用。

1.4 总结

Docker 和传统虚拟机各有适用场景,但对于大多数现代 Web 应用、微服务架构和 DevOps 流水线,Docker 更加轻量级、高效,能提升开发和运维效率。

Docker 不是要完全取代虚拟机,而是提供了一种更灵活、更高效的应用运行方式!

二、Docker 安装与环境准备

这个安装过程,是以我自己的电脑环境为准。

  • Windows:Windows11
  • Linux:CentOS7

2.1 Windows

官方下载地址:Docker 官网

安装步骤

  1. 下载 Docker Desktop 安装包。
  2. 双击运行安装程序。
  3. 安装完成后,重启计算机。
  4. 启动 Docker Desktop 并检查是否正常运行。

验证安装

1
docker --version

我使用的是VMware16.1版本,安装docker后发现挂起功能无法正常使用了。因此我将Windows上的docker卸载了。

Docker Desktop 需要 Hyper-V 或 WSL2 运行容器。
VMware Workstation Pro 依赖 裸机虚拟化(VT-x / AMD-V),但 Hyper-V 可能会抢占虚拟化权限,导致 VMware 运行变慢或无法启动。

windows 安装完docker, hype-v开启之后,vmware无法使用 - sunny123456 - 博客园

Docker Desktop傻瓜式的包含了许多工具,当然了Docker Desktop仅对个人免费。

2.2 Linux

如果是还在维护的Linux版本,可以参考官网的版本安装 | Docker 文档,而我测试使用的CentOS7已经不再维护了,所以单独记录一下。

2.2.1 安装 Docker

首先,安装docker。创建init.sh,将以下内容复制粘贴进去,然后执行sh init.sh,注册docker-daemon服务。参考二进制文件 | Docker 文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/bin/bash
set -e # 遇到任何非零退出码的命令就立即退出

echo "$(date +"%Y-%m-%d %H:%M:%S"): 下载docker..."
curl -L -k -o docker-27.5.1.tgz https://download.docker.com/linux/static/stable/x86_64/docker-27.5.1.tgz
echo "$(date +"%Y-%m-%d %H:%M:%S"): 解压docker..."
tar -zxvf docker-27.5.1.tgz
echo "$(date +"%Y-%m-%d %H:%M:%S"): 添加docker命令..."
cp docker/* /usr/bin/
echo "$(date +"%Y-%m-%d %H:%M:%S"): 创建docker数据目录..."
mkdir -p /data/docker
echo "$(date +"%Y-%m-%d %H:%M:%S"): 创建docker配置文件..."
mkdir -p /etc/docker
cat>/etc/docker/daemon.json<<EOF
{
"builder": {
"gc": {
"defaultKeepStorage": "20GB",
"enabled": true
}
},
"data-root": "/data/docker",
"experimental": false,
"log-driver": "json-file",
"log-opts": {
"max-file": "3",
"max-size": "100m"
},
"registry-mirrors": [
"https://dockerpull.pw",
"https://docker.1ms.run"
]
}
EOF
echo "$(date +"%Y-%m-%d %H:%M:%S"): 注册docker服务..."
cat >/etc/systemd/system/docker.service<<EOF
[Unit]
Description=docker
After=network.target

[Service]
Type=simple
ExecStart=/usr/bin/dockerd
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
echo "$(date +"%Y-%m-%d %H:%M:%S"): 启动docker..."
systemctl start docker
echo "$(date +"%Y-%m-%d %H:%M:%S"): 配置docker开机自启动..."
systemctl enable docker
echo "$(date +"%Y-%m-%d %H:%M:%S"): 使用方式: systemctl status docker"
echo "成功"

当脚本执行成功后,运行命令进行镜像拉取、并运行。

1
docker run --rm hello-world

2.2.2 插件 Compose

若有docker compose的需要,还需要再次安装插件。参考插件 | Docker 文档或者docker/compose

创建compose.sh,将以下内容复制粘贴进去,然后执行sh compose.sh,安装插件。

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
set -e # 遇到任何非零退出码的命令就立即退出

echo "$(date +"%Y-%m-%d %H:%M:%S"): 下载compose..."
curl -L -k -o docker-compose https://github.com/docker/compose/releases/download/v2.32.4/docker-compose-linux-x86_64
echo "$(date +"%Y-%m-%d %H:%M:%S"): 创建compose的lib目录..."
mkdir -p /usr/lib/docker/cli-plugins
echo "$(date +"%Y-%m-%d %H:%M:%S"): 安装docker-compose..."
mv docker-compose /usr/lib/docker/cli-plugins
chmod +x /usr/lib/docker/cli-plugins/docker-compose
echo "成功"

当脚本执行成功后,验证

1
docker compose version

2.2.3 插件 buildx

docker buildx 是 Docker 的一个扩展工具,用于支持跨平台构建(如 ARM、x86 等)。

执行以下命令,安装插件

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
set -e # 遇到任何非零退出码的命令就立即退出

echo "$(date +"%Y-%m-%d %H:%M:%S"): 下载buildx..."
curl -L -k -o docker-buildx https://github.com/docker/buildx/releases/download/v0.20.1/buildx-v0.20.1.linux-amd64
echo "$(date +"%Y-%m-%d %H:%M:%S"): 创建buildx的lib目录..."
mkdir -p /usr/lib/docker/cli-plugins
echo "$(date +"%Y-%m-%d %H:%M:%S"): 安装docker-buildx..."
mv docker-buildx /usr/lib/docker/cli-plugins
chmod +x /usr/lib/docker/cli-plugins/docker-buildx
echo "成功"

当脚本执行成功后,验证

1
docker buildx version

2.2.4 开启IPv4转发

Docker 默认使用 bridge 网络模式,在宿主机上创建一个 docker0 网桥,并给它分配一个子网(如 172.17.0.0/16)。当容器需要访问外网或其他容器时,Linux 需要通过 iptablesnet.ipv4.ip_forward 进行数据包转发

如果 net.ipv4.ip_forward=0,则宿主机不会转发数据包,导致容器无法访问外网或其他容器。

验证是否开启ipv4

1
2
3
4
# 方式一
sysctl net.ipv4.ip_forward
# 方式二
cat /proc/sys/net/ipv4/ip_forward

临时修改,服务器重启后失效

1
2
3
4
# 方式一
sysctl -w net.ipv4.ip_forward=1
# 方式二
echo 1 > /proc/sys/net/ipv4/ip_forward

永久修改

1
2
3
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
# 重新加载内核参数
sysctl -p

2.2.5 failed to find iptables

在ARM架构机器上启动docker时,遇到报错。日志如下

1
level=warning msg="failed to find iptables" error="exec: \"iptables\": executable file not found in $PATH"

解决办法

1
2
apt install -y iptables
iptables --version

三、Docker 核心概念详解

Docker 主要由三大核心概念组成:镜像(Image)、容器(Container)和仓库(Registry)。

3.1 镜像(Image)

镜像是 Docker 的基础,可以理解为一个包含应用及其依赖环境的模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 从仓库拉取指定镜像(推荐显式指定 tag 避免使用默认的 latest)
docker pull <repository>:<tag>

# 列出本地镜像(不包括中间层)
docker images
# 列出所有镜像(包含中间层缓存)
docker images -a

# 删除指定镜像(推荐优先使用 repository:tag 格式)
docker rmi <repository>:<tag>
# 通过镜像ID删除
docker rmi <image_id>

# 删除所有镜像
docker rmi -f $(docker images -q)


# 从运行中的容器创建新镜像(注意:不会包含挂载的 volume 数据!若需要包含数据,需要使用docker cp)
# 建议仅用于调试,生产环境应使用 Dockerfile
docker commit <container_id|name> <repository>:<tag>

# 无网模式下导出本地镜像
docker save -o myimage.tar <repository>:<tag>
docker save -o myimage.tar <image_id>

# 无网模式下加载镜像(加载后需要手动打tag)
docker load -i /root/myimage.tar
# 为加载单独镜像添加标签
docker tag <image_id> <repository>:<tag>

# 使用 Dockerfile 构建镜像(推荐最佳实践)
# -t 指定镜像标签
# --rm=true 构建成功后删除中间容器(默认)
# 注意末尾的构建上下文路径(.)
docker build -t <repository>:<tag> .

# 查看docker镜像信息
docker inspect <image_id>

3.2 容器(Container)

启动容器时,需要考虑时间和时区的影响,可以添加如下两个参数

1
2
3
4
# 表示只读模式同步宿主机的时间
-v /etc/localtime:/etc/localtime:ro
# 表示只读模式同步宿主机的时区
-v /etc/timezone:/etc/timezone:ro

在一些信创机器上,发现/etc/timezone被阉割掉了。可以通过设置环境变量解决。

1
-e TZ=Asia/Shanghai

容器是镜像的运行实例,包含所有应用运行所需的环境。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 后台运行特权容器(生产环境慎用 --privileged!)
# --privileged 赋予容器宿主机级权限(安全隐患!)
# --rm 容器退出时自动删除(适合临时任务)
# -v 挂载宿主机时区配置(ro 表示只读保护)
docker run -d \
--privileged \
--rm \
-v /etc/localtime:/etc/localtime:ro \
-v /etc/timezone:/etc/timezone:ro \
--name <container_name> \
<image_id>

# 交互式运行特权容器(适用于调试)
# -it 开启交互终端
# 末尾的 bash 指定启动 shell
docker run -it \
--privileged \
--rm \
-v /etc/localtime:/etc/localtime:ro \
-v /etc/timezone:/etc/timezone:ro \
--name <container_name> \
<image_id> \
bash

# 运行 Java 应用容器
# -w 设置工作目录(确保 jar 包存在该路径)
# bash -c 执行复合命令
docker run -d \
--privileged \
--rm \
-v /etc/localtime:/etc/localtime:ro \
-v /etc/timezone:/etc/timezone:ro \
-w /opt/project \
--name <container_name> \
<image_id> \
bash -c "java -jar test.jar"

# 查看运行中的容器
docker ps
# 查看所有容器(包括已停止的)
docker ps -a

# 查看容器资源占用(实时监控)
docker stats
# 查看指定容器资源占用
docker stats <container_id|name>

# 以附加模式进入容器(推荐方式,退出不会终止容器)
# 使用 sh 或 bash 根据镜像基础系统决定
docker exec -it <container_id|name> sh
docker exec -it <container_id|name> bash

# 以主进程模式连接容器(危险!退出可能导致容器停止)
docker attach <container_id|name>

# 优雅停止容器(发送 SIGTERM)
docker stop <container_id|name>
# 强制停止容器(发送 SIGKILL)
docker kill <container_id|name>
# 启动容器
docker start <container_id|name>
# 重启容器
docker restart <container_id|name>

# 删除容器(必须先停止)
docker rm <container_id|name>

# 查看容器日志
docker logs <container_id|name>
# 实时跟踪日志(类似 tail -f)
docker logs -f <container_id|name>
# 从最新10行开始实时跟踪
docker logs -f --tail 10 <container_id|name>

# 设置容器自启动策略
docker update --restart=always <container_id|name> # 总是重启
docker update --restart=on-failure:5 <container_id|name> # 失败时重启最多5次
docker update --restart=no <container_id|name> # 禁用自启

# 查看容器详细信息(包括配置、网络、挂载等)
docker inspect <container_id|name>

# 查看容器资源使用情况
docker stats

# 宿主机到容器的文件拷贝。这个文件拷贝是双向的,即可以宿主到容器,也可以容器到宿主。类似于scp命令
# 拷贝目录:将宿主的jdk11文件夹,拷贝到容器的/opt/下面
docker cp /opt/jdk11 <container_id|name>:/opt/
# 拷贝单个文件:
docker cp test.txt <container_id|name>:/opt/test.txt

docker run常用参数速查表,可以通过docker run --help获取。

参数作用说明示例
--add-host增加 hosts 映射--add-host example.com:192.168.1.1
--cap-add增加特权能力--cap-add=NET_ADMIN
--cap-drop移除特权能力--cap-drop=ALL
--cpus限制使用 CPU 核心数--cpus=1.5
--cpu-shares设置相对 CPU 权重--cpu-shares=512
--cpuset-cpus指定使用哪些 CPU 核心--cpuset-cpus=0,1
--detach (-d)后台运行容器(detached 模式)-d
--device授权容器访问宿主设备--device /dev/snd
--dns指定容器的 DNS 服务器--dns 8.8.8.8
--entrypoint自定义入口命令--entrypoint /bin/bash
--env-e设置环境变量-e TZ=Asia/Shanghai
--env-file从文件加载环境变量--env-file .env
--expose声明容器暴露端口(不映射)--expose 3306
--hostname(-h)设置容器主机名-h myhost
--interactive (-i)保持标准输入流打开-i
--ipc设置 IPC 命名空间--ipc=host
--label给容器加标签--label app=myapp
--log-driver指定日志驱动(json-file、syslog、none 等),默认为json-file--log-driver=json-file
--log-opt日志驱动选项--log-opt max-size=10m
--memory-m限制内存大小--memory 512m
--memory-swap限制 swap 使用--memory-swap=1g
--mount推荐写法:挂载绑定卷或命名卷--mount type=bind,source=/data,target=/app
--name容器命名--name my-container
--network设置网络模式(bridge、host、none、自定义),默认为bridge。若设置 host 模式会让容器直接使用宿主机的网络命名空间,不需端口映射。--network=host
--oom-kill-disable禁止因 OOM 杀掉容器--oom-kill-disable
--pid设置 PID 命名空间。默认为private,为容器分配独立的PID命名空间。--pid=host
--privileged启用特权模式(访问全部设备)--privileged
--publish-p端口映射(宿主:容器)--publish 8080:80
--rm容器退出后自动删除--rm
--security-opt安全配置项,如 seccomp--security-opt seccomp=unconfined
--sysctl设置内核参数--sysctl net.ipv4.ip_forward=1
--tty (-t)分配伪终端一般是组合使用。-it
--tmpfs使用内存挂载--tmpfs /run:size=65536k
--user指定容器用户(uid:gid)--user 1000:1000
--uts设置 UTS 命名空间--uts=host
--volume-v绑定挂载目录或文件--volume /host:/container
--volume-driver指定使用的卷驱动--volume-driver=local
--workdir-w设置容器内工作目录--workdir /app

3.3 仓库(Registry)

Docker 镜像存放的地方,可以是 Docker Hub 也可以是私有仓库。

1
2
3
4
5
6
7
8
# 登录 Docker Hub
docker login

# 推送镜像到仓库
docker push <用户名|镜像名>:<标签>

# 从私有仓库拉取镜像
docker pull <私有仓库地址>/<镜像名>:<标签>

3.4 数据管理

针对一些必要的数据,进行数据卷挂载,实现持久化。

1
2
3
4
5
6
7
8
# 数据卷挂载
docker run -d -v /host/path:/container/path --name mycontainer ubuntu

# 查看所有卷
docker volume ls

# 删除数据卷
docker volume rm <卷名>

四、Dockerfile 与自定义镜像

示例代码meethigher/docker-learn: 编写一个Java项目学习Docker使用

4.1 构建 Docker 镜像

Dockerfile 是用于定义自定义镜像的文本文件,包含构建镜像所需的指令。

示例代码meethigher/docker-learn at 构建docker镜像

1
2
3
4
5
6
7
8
9
10
11
# 使用 Java 8 运行时环境 docker pull eclipse-temurin:8-jre-alpine
FROM eclipse-temurin:8-jre-alpine

# 设置容器中的工作目录
WORKDIR /docker-learn

# 复制 JAR 文件到设置的容器目录中
COPY target/docker-learn.jar app.jar

# 运行 Java 应用
CMD ["java", "-jar", "app.jar"]

使用 docker build 命令基于 Dockerfile 构建镜像。

1
2
3
4
5
# 格式。如果不使用标签,默认是latest
docker build -t <镜像名>:<标签> <构建上下文>

# -t指定镜像名。.表示当前目录(目录应是Dockerfile所在目录)
docker build -t docker-learn .

4.2 多阶段构建优化 Docker 镜像

使用多阶段构建可以减少最终镜像的大小,它仅保留运行时所需的文件,而不会把构建过程中产生的无关文件带入最终镜像,提高构建效率。

示例代码meethigher/docker-learn at 多阶段构建docker镜像

1
2
3
4
5
6
7
8
9
10
11
# 第一阶段:构建阶段
FROM maven:3.6.3-openjdk-8 AS builder
WORKDIR /app
COPY . .
RUN mvn clean package -DskipTests

# 第二阶段:运行阶段。将第一阶段的构建产物复制到第二阶段中
FROM eclipse-temurin:8-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/docker-learn.jar app.jar
CMD ["java", "-jar", "app.jar"]

执行构建命令后如图

4.3 推送镜像到 Docker Hub/私有仓库

1
2
3
4
5
6
7
8
# 登录 Docker Hub
docker login

# 给镜像打标签(格式:仓库名/镜像名:标签)
docker tag my-nginx your_dockerhub_username/my-nginx:v1

# 推送到 Docker Hub
docker push your_dockerhub_username/my-nginx:v1

五、Docker Compose(多容器管理)

5.1 什么是 Docker Compose?

Docker Compose 允许使用 docker-compose.yml 文件来定义和管理多个容器。

5.2 使用 Docker Compose 启动多容器

示例代码meethigher/docker-learn at docker-compose

以下是一个 docker-compose.yml 文件示例,定义了 app和 db 两个服务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
services:
app:
build: .
container_name: java_app
depends_on:
- db
environment:
DB_URL: jdbc:postgresql://db:5432/mydatabase
DB_USER: myuser
DB_PASSWORD: mypassword
ports:
- "8080:8080"

db:
image: postgres:15
container_name: postgres_db
restart: always
environment:
POSTGRES_DB: mydatabase
POSTGRES_USER: myuser
POSTGRES_PASSWORD: mypassword
ports:
- "5432:5432"

启动容器

1
2
3
4
5
6
7
8
# 在当前目录下启动所有服务 -d表示后台 --build表示重新构建
docker compose up -d

# 查看运行中的容器
docker compose ps

# 停止并删除所有容器
docker compose down

日志与调试

1
2
3
4
5
# 查看所有服务的日志
docker compose logs -f

# 查看特定服务的日志
docker compose logs -f nginx

六、Docker 替代品之 Podman

6.1 Podman 简介

Podman(Pod Manager)是一个无守护进程的容器管理工具,功能类似 Docker,主要用于管理 OCI(Open Container Initiative)容器。与 Docker 不同,Podman 不需要后台守护进程(daemon),并且支持 rootless 模式,使得容器运行更加安全。

Podman vs Docker:

特性DockerPodman
守护进程需要 Docker Daemon无需守护进程
权限管理需要 root 或 sudo可 rootless 运行
兼容 Docker CLI✅(几乎 100% 兼容)
适用于生产环境✅(更安全)

6.2 为什么选择 Podman?

Podman 具有以下优势:

  1. 无守护进程(Daemonless):不像 Docker 依赖 dockerd 守护进程,Podman 直接运行容器,使得系统更加安全稳定。
  2. Rootless 模式:Podman 允许普通用户运行容器,而不需要 root 权限,降低安全风险。
  3. 兼容 Docker:Podman 的 CLI 命令与 Docker 几乎一致。
  4. Pod 支持:Podman 内置 Pod 概念,能够更好地支持 Kubernetes 生态。
  5. 更轻量:相比 Docker,Podman 不依赖后台进程,资源占用更少。
发布:2025-02-23 01:18:58
修改:2025-05-06 18:11:53
链接:https://meethigher.top/blog/2025/docker-learn/
标签:docker 
付款码 打赏 分享
Shift+Ctrl+1 可控制工具栏