Docker 学习笔记

Docker 的基本组成

Docker 引入了三个名词:镜像、容器和仓库。

镜像(image):

docker 镜像相当于一个模版,可以通过模版来创建容器,需要的服务就是在容器中运行的。

容器(container):

docker 利用容器技术,独立运行不同的服务。容器包含启动、停止、删除等基本命令。

仓库(repository):

仓库就是存放镜像的地方。

Docker run的运行流程图

image-20250215221009688

Docker 的常用命令

帮助命令

1
2
3
docker version  		 # 显示docker的版本信息
docker info # 显示docker的系统信息,包括镜像和容器的数量
docker 命令 --help # 帮助命令

官方文档: https://docs.docker.com/reference/cli/docker/

镜像命令

  1. docker images
1
2
3
4
docker images # 查看本机上所有镜像
# 可选项
-a # 列出所有镜像
-q # 只显示镜像的id
  1. docker search
1
2
3
4
5
docker search 镜像 # 搜索镜像
# Eg: docker search mysql
# 可选项
-f # 根据指定条件过滤镜像
# Eg: 只保留STARS大于3000的镜像 -f=STARS=3000
  1. docker pull
1
2
3
docker pull 镜像名[:tag]  # 下载镜像
# Eg: docker pull mysql
# docker pull mysql:5.7 下载版本为5.7的mysql
  1. docker rmi
1
2
3
4
docker rmi 镜像名/镜像id	# 删除镜像
# 可选项
-f # 强制删除
# Eg: 删除全部容器: docker rmi -f $(docker images -q)

容器命令

说明:有了镜像才能创建容器,下载一个centos镜像以测试学习

1
docker pull centos
新建容器并启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker run [可选参数] image
# 可选参数
--name="Name" # 容器名字,如tomcat01,tomcat02,用来区分容器
-d # 后台方式运行
-it # 使用交互方式运行,进入容器查看内容
-p # 指定容器的端口
# -p ip:主机端口:容器端口
# -p 主机端口:容器端口 (常用)
# -p 容器端口
-P # 随机指定端口
-e # 环境配置

# 测试,启动并进入容器
baymax@guoshulideMacBook-Air bin % docker run -it centos /bin/bash
[root@a1dd4521106d /]#
# 从容器退回主机
exit
列出所有运行的容器
1
2
3
4
5
docker ps
# 可选参数
-a # 列出当前正在运行的容器+带出历史运行过的容器
-n=? # 显示最近创建的?个容器
-q # 只显示容器的编号
退出容器
1
2
exit					# 容器停止并退出
ctrl + P + Q # 容器不停止并退出
删除容器
1
2
3
docker rm 容器id								# 删除指定容器,不能删除正在运行的容器
docker rm -f $(docker ps -aq) # 删除所有容器
docker ps -aq | xargs docker rm # 删除所有容器
启动和停止容器
1
2
3
4
docker start 容器id			# 启动容器
docker restart 容器id # 重启容器
docker stop 容器id # 停止当前运行的容器
docker kill 容器id # 强制停止当前容器

常用其他命令

后台启动容器
1
2
3
docker run -d centos
# 输入docker ps,发现centos停止了
# 这是一个常见的坑:docker容器使用后台运行时必须要有一个前台进程,如果docker发现没有应用,就会自动停止后台进程
查看日志
1
2
3
4
5
6
7
8
docker logs 容器id
# Eg: 编写一段脚本
docker run -d centos /bin/sh -c "while true;do echo hello;sleep 1;done"
docker logs -tf -tail 10 id
# 可选参数
-t # 显示时间戳
-f # 命令一直运行,持续跟随日志的内容
--tail number # 设置显示日志的条数
查看容器中的进程信息
1
docker top 容器id
查看容器的元数据
1
docker inspect 容器id
进入当前正在运行的容器
1
2
3
4
# 方式一: 进入容器后开启一个新的终端,可以在里面进行操作
docker exec -it 容器id bashShell
# 方式二: 进入当前正在执行的终端,不会启动新的进程
docker attach 容器id
从容器内拷贝文件到主机上
1
docker cp 容器id:容器内路径  目的主机路径

Docker 镜像讲解

镜像是什么?

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、库、环境变量和配置文件。

所有的应用,直接打包成docker镜像,就可以直接跑起来!

如何得到镜像:

  • 从远程仓库下载
  • 从网上拷贝
  • 自己制作镜像 Dockerfile

Docker 镜像加载原理

UnionFS (联合文件系统)

docker pull 下载镜像时看到的一层层文件就是这个!

Union文件系统是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker 镜像的基础,镜像可以通过分层来进行继承,基于基础镜像可以制作各种具体应用的镜像。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

Docker 镜像加载原理

docker镜像实际由一层一层文件系统组成,这种层级的文件系统就是 UnionFS。

bootfs(boot file system) 就是内核引导器(引导加载内核)和内核。bootfs主要包含bootloader和kernel,bootloader主 要是引导加载kernel,Linux刚启 动时会加载bootfs文件系统。

rootfs 是 docker 容器在启动时内部进程可见的文件系统,即 docker 容器的根目录。当我们运行docker exec命令进入container的时候看到的文件系统就是rootfs。rootfs 通常包含一个操作系统运行所需的文件系统,例如可能包含典型的类 Unix 操作系统中的目录系统,如 /dev、/proc、/bin、/etc、/lib、/usr、/tmp 及运行 docker 容器所需的配置文件、工具等。

为什么docker中centos镜像的镜像那么小?

对于一个精简的操作系统,rootfs可以很小,只需要包含最基本的命令、工具和程序库就行了,底层直接使用主机的内核。对于不同的linux发行版,bootfs基本一致,rootfs会有差别,因此不同的发行版可以共用bootfs。docker中的centos镜像就相当于只下载的centos的rootfs,并公用底层内核。

Docker 分层

在下载镜像的时候,注意观察下载日志,可以看到镜像是一层一层下载的!

image-20250217000850933

docker 为什么采用这种分层的结构呢?

最大的好处莫过于是资源共享!比如有多个镜像都是从相同的base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有容器服务了。

查看镜像分层的方式可以通过 docker image inspect 命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker image inspect redis:latest
[
#...省略
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:3ec51339a507de1c80397e41016d23da1e6fa610d7176da42ce5e46fb2ce4686",
"sha256:824c38b5aaa3718a87b786d23f490e7c9ff816d95a337d6dc88ccdabb3282eba",
"sha256:a87fd23cda10a200cb9fcdfe86801d704be5c17c72c54ec22ea3d74f52859ba3",
"sha256:9cea49e86c99fbba2adb9b10c6ee09c4ed383978b553c3a303a130294512814f",
"sha256:d84e1f39f0a23aecc0dca6b88de2f2795a6a871ceb237f10e506da6a376d2065",
"sha256:f11488062505dc4be36907b165d20ab07d5467eebf15b52165c3d5745943dddb",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef",
"sha256:0cf85cbc41289c647b896f78964d8986f5f72f32888acc0a5a581ba650a6d92f"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
}
]

所有的 docker 镜像都起始于一个基础镜像层,当进行修改或添加内容时,就会在当前镜像层之上创建新的镜像层。

例如下图就是一个docker镜像一层一层的构建过程:

13701739723113_.pic

特点

Docker 镜像都是只读的,不可以被修改。当容器启动时,一个新的可写层会加载到镜像的顶部:

image-20250217003025125

用户操作的这一层就是常说的容器层,容器层之下都叫镜像层!

commit 镜像

如果想要保存当前容器的状态,就可以通过commit进行提交,获得当前容器状态的一个镜像。

1
2
3
# docker commit和git基本一致
docker commit 提交容器成为一个新的副本
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG] # TAG为版本号

容器数据卷

什么是容器数据卷

容器数据卷(Docker Volumes) 是一种在 Docker 容器和宿主机(host)之间持久化数据的方法。它用于将数据存储在 Docker 容器之外,以便在容器重启、删除或创建新容器时,数据不会丢失。数据卷可以使容器与数据分离,从而让数据在容器生命周期之外得以持久化、共享和管理。

挂载

挂载(Mount) 是指将一个存储设备(如硬盘、U 盘、网络存储)或文件系统(如 Docker 数据卷)连接到操作系统的目录结构中,使其可以被访问和使用。简单来说,就是把存储设备的某个路径“挂”到系统中的一个目录下,让系统能够像操作本地文件一样操作它。

总结一句话

挂载就是把一个存储(设备、目录、卷、网络存储)映射到某个目录,让它变得可用

使用数据卷

1
2
3
4
# 方式一:直接使用命令来挂载 -v
docker run -it -v 主机目录:容器内目录
# eg:
docker run -it -v ~/test:/home centos /bin/bash # 将容器内的home目录映射到本机的~/test目录

利用 docker inspect 容器id 查看容器如何进行挂载的:

image-20250217124043197

source 为本机目录,destination 为容器内目录,两个目录是互相绑定的,数据同步。

具名和匿名挂载

1
2
3
4
5
6
# 匿名挂载
-v 容器内路径
docker run -d -P --name=nginx01 -v /etc/nginx nginx

# 查看所有的volume情况
docker volume ls

image-20250217164402084

可以看到长长的 abc... 字符串就是匿名挂载。我们在 -v 处只写了容器内的路径,没有写容器外的路径

1
2
3
# 具名挂载
# 通过 -v 卷名:容器内路径进行
docker run -d -P --name=nginx02 -v juming-nginx:/etc/nginx nginx

再次查看卷情况:

image-20250217164652637

查看卷的位置:

1
docker volume inspect juming-nginx

image-20250217165020965

所有的docker容器内的卷,在没有指定目录的情况下都是在 /var/lib/docker/volumes/xxxx/_data 路径。

我们通过具名挂载可以方便地找到我们的一个卷,大多数情况下都使用具名挂载

1
2
3
4
# 如何确定是具名挂载、匿名挂载还是制定路径挂载
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /宿主机路径:容器内路径 # 指定路径挂载

拓展:

1
2
3
4
5
6
# 通过 -v 容器内路径:ro rw 改变读写权限
# ro -> readonly
# rw -> read write

# 设置ro,则容器内不能修改目录/etc/nginx,但容器外(宿主机)仍然可以修改。
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx

初识 Dockerfile

Dockerfile 就是用来构建 docker 镜像的构建文件,相当于命令脚本。

通过这个脚本可以生成镜像,镜像是一层层的,脚本就是一个个的命令,每个命令都是一层。

1
2
3
4
5
6
7
8
9
10
11
12
13
# 创建一个dockerfile文件
# 文件中的内容: 指令(大写) 参数
# 创建一个dockerfile如下
FROM centos
VOLUME ["volume01", "volume02"] # 匿名挂载
CMD echo "---end---"
CMD /bin/bash

# 创建镜像
docker build -f ~/test/dockerfile1 -t kuangshen/centos

# 创建容器,查看文件
docker run -it kuangshen/centos /bin/bash

image-20250217175617763

输入docker volume ls,可以看到多了两个匿名挂载的数据卷。

image-20250217175721448

数据卷容器

数据卷容器用于多个容器之间实现数据共享,用 –volumes-from 实现。

以下是一个示例:

1
2
3
4
5
6
7
8
9
docker run -it --name=docker01 kuangshen/centos
docker run -it --name=docker02 --volumes-from docker01 kuangshen/centos
# 当修改docker02的文件时,docker01也会同步更改
docker run -it --name=docker03 --volumes-from docker01 kuangshen/centos
# 此时docker01,02,03数据都共享

# 删除docker01
docker rm -f docker01
# 此时docker02、docker03数据还是共享的

数据卷容器的生命周期一直持续到没有容器使用位置。

DockerFile

dockerfile 是用来构建docker镜像的文件,就是一个命令参数脚本。

构建步骤

  1. 编写一个dockerfile文件
  2. docker build 构建成为一个镜像
  3. docker run 运行镜像
  4. docker push 发布镜像(到dockerhub、阿里云镜像仓库等)

很多官方的镜像都是基础包,很多功能都没有,我们通常需要自己构建镜像。

DockerFile 构建过程

基础知识

  1. 每个保留关键字(指令)都必须是大写字母
  2. 指令从上到下顺序执行
  3. # 表示注释
  4. 每一个指令都会创建提交一个新的镜像并提交
image-20250218002206726

DockerFile 指令

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM					# 引入基础镜像,一切从这里开始构建
MAINTAINER # 镜像作者信息,一般是姓名+邮箱
RUN # 镜像构建时需要运行的命令
ADD # 添加需要的内容(各种包)
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露的端口
CMD # 指定容器启动时执行的命令。Dockerfile 中只能有一个 CMD 指令,后者会覆盖前者。
ENTRYPOINT # 指定容器启动时需要运行的命令,可以追加命令
ONBUILD # 当一个Dockerfile被继承时,就会触发ONBUILD指令
COPY # 类似ADD,将文件拷贝到镜像中
ENV # 构建时设置环境变量
LABEL # 设置镜像的元数据

利用 dockerfile 构建自己的个性化ubuntu

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 使用官方 Ubuntu 镜像作为基础镜像
FROM ubuntu

# 使用 LABEL 代替 MAINTAINER
LABEL maintainer="baymax <2640449590@qq.com>"

# 更新 apt 包索引,并安装 net-tools 包
RUN apt-get update && \
apt-get install -y net-tools && \
apt-get install -your vim && \
apt-get clean

# 设置环境变量,使用 "=" 格式
ENV MYPATH=/usr/local

# 设置工作目录为绝对路径
WORKDIR /root

# 使用 JSON 格式的 CMD 指令
CMD echo $MYPATH && echo "---end---" && /bin/bash

dockerfile 写好后,利用 docker build -f dockfile文件路径 -t docker镜像名:版本号 . 构建镜像。

docker history

docker history 可以查看镜像是如何构建的,例如:

1
docker history myubuntu

image-20250218134718234

CMD 和 ENTRYPOINT 的区别

1. CMD:设置默认的命令和参数

  • 用途:CMD 用于为容器指定默认命令,并且可以提供默认参数。如果在运行容器时没有提供其他命令或参数,Docker 就会执行 CMD 指定的命令。

  • 重写:如果在运行容器时提供了其他命令(如 docker run ),则 CMD 指定的命令会被重写。

  • 常见用法:用来指定容器的默认命令,通常用于启动服务或进程。

    1
    CMD ["echo", "Hello, World!"]

    这个 Dockerfile 中的 CMD 会在容器启动时执行 echo Hello, World!。如果没有指定其他命令,容器会执行 CMD 中的命令。

使用 docker run时:

1
2
docker run <image>  # 输出 "Hello, World!"
docker run <image> ls # 执行 `ls`,重写了 `CMD` 指定的命令

2. ENTRYPOINT:指定容器的固定执行命令

  • 用途:ENTRYPOINT 用于设置容器启动时执行的固定命令。它始终会被执行,无论你在 docker run 命令中是否指定了其他命令。

  • 重写:ENTRYPOINT 指定的命令不能被重写(除非使用 –entrypoint 选项)。但是,你可以在 docker run 中提供其他参数,来作为 ENTRYPOINT 命令的参数。

  • 常见用法:用来设置容器必须运行的命令,通常用于启动应用或服务。

    1
    ENTRYPOINT ["echo"]

    这个 Dockerfile 中的 ENTRYPOINT 会在容器启动时执行 echo 命令。

使用 docker run 时:

1
2
docker run <image> "Hello, World!"  # 输出 "Hello, World!"
docker run <image> "Goodbye" # 输出 "Goodbye"

3. CMD 与 ENTRYPOINT 结合使用

组合使用:你可以将 ENTRYPOINT 和 CMD 结合使用。在这种情况下,ENTRYPOINT 设置固定的命令,CMD 提供默认的参数。如果在运行容器时指定了其他参数,CMD 中的默认参数会被替换为新的参数。

1
2
ENTRYPOINT ["echo"]
CMD ["Hello, World!"]

使用docker run时:

1
2
docker run <image>  # 输出 "Hello, World!"
docker run <image> "Goodbye" # 输出 "Goodbye"

Ps:

Dockerfile 中,CMDENTRYPOINT 只能有 一个。如果你在 Dockerfile 中写多个CMD 或 ENTRYPOINT 指令,只有最后一个 CMD 或 ENTRYPOINT 会生效,前面的会被忽略。

发布镜像

  1. 先登陆dockerhub
1
docker login -u 用户名

​ 输入密码后即可登陆成功。

  1. docker push 镜像即可

    注意镜像前必须带有自己的用户名,否则会被拒收。

image-20250218172700342

​ 更改镜像名后,发布成功。

image-20250218172831521

也可以把镜像发布在阿里云镜像仓库中,详见阿里云官方文档。

Docker 小结

image-20250218194919605

Docker Compose

这篇博客讲的很详细!