写完 Kubernetes 的笔记, 再回头来补充 Docker 的笔记。

简介

Docker helps developers build, share, run, and verify applications anywhere — without tedious environment configuration or management.
Docker 可以帮助开发人员在任何地方构建、共享、运行和验证应用程序,无需繁琐的环境配置或管理。

这是官网对 Docker 的最有力而且简明的介绍。

就像他的小鲸鱼图标, 背着很多的集装箱, 每个集装箱装着一个应用, 里面包含了不同的集装箱含有配置文件, 启动命令, 应用程序及环境变量, 第三方依赖, 运行时环境和操作系统等。运送到任何一个可以执行应用的地方.

Docker 和虚拟机的区别

Docker 和虚拟机都是虚拟化技术, 都可以解决应用运行环境不匹配的问题, 但是 Docker 是轻量级的虚拟化技术, 虚拟机是重量级的虚拟化技术, Docker 的优势是轻量级, 虚拟机的优势是可移植性。

如下图:

虚拟化技术
虚拟机

虚拟化技术是一种将物理资源虚拟为多个逻辑资源的技术, 每个虚拟机都分配有独立的 CPU, 操作系统, 内存, 硬盘和网络接口等等, 虚拟机之间是隔离且独立的. 这样一来, 可以将多个物理服务器分配给多个逻辑服务器. 但缺点也很明显, 尤其是安装了操作系统. 但多数时候, 我们只需要一个对外服务的 App 而已, 所以 Docker 就出现了. 他将上面虚拟机的结构简化成了容器(Container):

容器

Docker 是一个开源的容器化技术. 自身包含了操作系统

Docker 的基本架构

他是一个 C/S 架构的系统, 客户端通过 Docker CLI 与 Docker Engine 通信, Docker Engine 是一个轻量级的进程管理工具, 它运行在主机上, 负责管理 Docker 容器的生命周期.

Docker 架构

我们和 Dokcer 打交道, 都是通过 Client 客户端的, 通过图形化界面或者敲命令, 服务端我们一般接触不到的. 服务端帮我们处理这些我们发出的命令, 然后将结果返回给我们而已.

而我们制作好的镜像, 通过 docker push 上传到 Docker Hub (Registry) 仓库上, 然后通过网络 docker pull 下载到本地.

安装

这里安装其图形化界面, 我用的是 M1 的 Mac

windows 运行前需要开启 HyperVisor 功能

镜像(Image) 和 容器(Container) 两个重要概念

  • Docker 镜像(Image):镜像是一个只读的模板,可以用来创建容器;
  • Docker 容器(Container):容器是镜像的运行实例,它是一个独立的环境,可以在这个环境中运行应用程序,一个镜像可以创建多个容器;而我们上面提到的仓库是用来存储 Docker 镜像的地方,比如 DockerHub,我们可以在这里下载各种镜像,也可以将自己的镜像上传到这里。

命令操作

命令在实际项目中应用不多, 项目中我们经常用 Dockerfile 如何设计到集群, 我们还会使用 Kubernetes ( 关于 Kubernetes 可参考这篇文章), 但命令还需要掌握一下的, 这里分为镜像命令和容器命令

镜像命令

搜索

1
2
3
docker search [image]
# 例如:
docker search --limit 5 nginx

拉取和上传

1
2
# 拉取镜像
docker pull [image]
1
2
3
4
5
# 上传镜像
docker push [image]
# 例如:
docker push zyzy/nginx:lastest
docker push registry.cn-beijing.aliyuncs.com/zhangsan/nginx:1.0

查看本地镜像

1
2
3
4
# 列出本地下载的所有镜像:
docker images
# or
docker image ls

删除镜像

1
2
3
docker rmi [image]
# or
docker image rm [image]

构建

1
2
3
4
5
6
# 构建镜像
docker build -t [image]:[tag] [path]
# 例如:
docker build -t zhangsan/nginx:latest .
# or 从当前目录的 Doockerfile 构建镜像
docker build .

其他命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看镜像历史
docker history [image]

# 从文件导入镜像
docker load -i [file]
# 例如:
docker load -i nginx.tar

# 导出镜像
docker save [image] > FILE
# or
docker save [image] -o FILE
# 例如
docker save zhangsan/hello-docker:latest > hello-docker.tar

容器命令

列出所有正在运行的容器

1
2
3
docker ps
# or
docker container ls

列出所有容器

1
2
3
docker ps -a
# or
docker container ls -a

仅创建

1
docker create [image]

创建并运行

1
2
3
4
# 运行镜像
docker run -d -p [host port]:[container port] [image]
# 例如:
docker run -d -p 80:80 zhangsan/nginx:latest

参数:
-d: 运行容器并进入后台
-p: 映射端口
-it: 运行容器并进入交互模式
-a: 运行容器并进入交互模式, 并且保持 STDIN 打开并允许 tty 控制
-w: 指定容器工作目录
-v: 挂载卷
-e: 设置环境变量, 如: docker run -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=test -e MYSQL_USER=test -e MYSQL_PASSWORD=123456 mysql:5.7
--net="bridge": 指定容器的网络连接类型,支持 bridge/host/none/container: 四种类型;
--rm: 容器退出后自动删除容器文件

下面, 给出一些示例:

1
2
3
4
5
# 创建一个容器并在容器中执行命令(交互模式,终端,退出后删除容器)
docker run -it --rm [image] [COMMAND] /bin/bash

# 后台运行容器, 端口映射, 挂载卷, 容器名称, 镜像
docker run -d -p [hostPort]:[containerPort] -e [key=value] -w [PATH] --name [name] [image]

运行, 暂停, 重启

1
2
3
docker start [container]
docker stop [container]
docker restart [container]

进入容器

1
2
3
docker exec -it [container] [command]
# 例如: 利用 bin/bash 的 shell 进入容器内部
docker exec -it zhangsan/nginx:latest /bin/bash

删除容器

1
2
3
docker rm [container]
# or
docker container rm [container]

查看容器 CPU 和内存占用情况

1
docker stats [container]

其他容器命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 导出容器
docker export [container] -o FILE
# or
docker export [container] > FILE

# 导入容器
docker import FILE
# 查看日志
docker logs [container]

# 查看容器端口映射
docker port [container]
# 显示容器内进程
docker top [container]

# 复制
docker cp [FILE] [container]:[PATH]

# 查看容器创建变化历史
docker diff [container]

数据卷管理

数据卷(Data Volumes)是一个可供一个或多个容器使用的特殊目录,它绕过了 UFS,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立马生效
  • 对数据卷的更新,不会影响镜像
  • 数据卷默认会一直存在,即使容器被删除
  • 数据卷会一直存在,直到没有容器使用

Docker Volume 本质上是容器与主机之间共享的目录或者文件,这样 Docker Volume 中的数据可以在主机和容器中实时同步。

由于容器很容易被销毁, 所以持久化的数据, 即 DB 的数据, 我们必须挂载在本地电脑上, 会自动挂载到容器中, 容器销毁后, 数据还在.

创建

1
2
3
docker volume create [volume]
# 例如:
docker volume create zhangsan-volume

查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
docker volume ls

docker volume inspect [volume]
# 例如:
(base) zyzy:~ $ docker volume inspect minikube
[
{
"CreatedAt": "2024-05-25T13:07:07Z",
"Driver": "local",
"Labels": {
"created_by.minikube.sigs.k8s.io": "true",
"name.minikube.sigs.k8s.io": "minikube"
},
"Mountpoint": "/var/lib/docker/volumes/minikube/_data",
"Name": "minikube",
"Options": {},
"Scope": "local"
}
]

删除

1
2
3
4
# 删除数据卷
docker volume rm [volume]
# 删除所有未使用的卷
docker volume prune

维护管理命令

1
2
3
4
5
6
7
8
9
10
11
docker version
docker [command] --help
docker info
# 移除所有未被使用的镜像, 容器, 卷和网络
docker system prune

# 登录 docker hub
docker login
# 登出 docker hub
docker logout

网络命令

Docker 默认会创建三个网络,分别是 bridge、host、none。
网络模式:

  • bridge:桥接网络,Docker 默认使用的网络模式,使用 docker run 命令创建容器时如果不指定网络模式,那么就会使用 bridge 模式。
  • host:主机网络,使用宿主机的网络,容器将不会获得一个独立的网络命名空间,配置和宿主机共享,容器将不会隔离宿主机网络,使用宿主机的 IP 和端口。
  • none:无网络、禁用网络,容器拥有自己的网络命名空间,但是并不为容器进行任何网络配置,这个网络模式的容器只适合于只进行数据处理,没有任何网络的应用场景。
  • container:容器网络,使用其他容器的网络,指定使用其他容器的网络栈。
  • overlay:跨主机网络,用于跨多个 Docker 守护进程的容器通信,Docker 1.9 版本中增加的功能。
  • macvlan:通过 MAC 地址绑定来实现容器访问外部网络,Docker 1.12 版本中增加的功能。
  • ipvlan:通过 IP 地址绑定来实现容器访问外部网络,Docker 1.12 版本中增加的功能。

命令:

1
2
3
4
5
6
7
8
9
10
11
12
13
# 列出可用网络
doocker network ls
# 查看网络详细信息
docker network inspect [network]

# 创建一个新网络
docker network create [network]
# 删除
docker network rm [network]
# 添加容器到网络
docker network connect [network] [container]
# 从网络中移除容器
docker network disconnect [network] [container]

插件管理

插件是 Docker 1.13 版本中引入的功能,它可以扩展 Docker 的核心功能,比如网络、存储、日志等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 列出所有插件
docker plugin ls
# 查看某款插件详细信息
docker plugin inspect [plugin]

# 安装
docker plugin install [plugin]
# 例如:
docker plugin install traefik

# 启用:
docker plugin enable [plugin]
# 禁用:
docker plugin disable [plugin]
# 卸载, 禁用:
docker plugin rm [plugin]
docker plugin disable [plugin]

Dockerfile

有点像 shell 脚本,但是语法比 shell 脚本简单.
Dockerfile 中包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
Dockerfile 中的指令不区分大小写,但是为了可读性,建议使用大写。
Dockerfile 中的指令按照从上到下的顺序执行。

Dockerfile Instruction

  • FROM:指定基础镜像,必须为 Dockerfile 文件的第一条指令;
1
FROM [base_image]
  • MAINTAINER: 指定镜像的维护者,可以为个人、公司、组织等;(可省)
  • ADD: 用于将文件拷贝到镜像中,源可以是 URL 或者本地文件,也可以是一个压缩文件(自动解压);
  • COPY: 用于将文件拷贝到镜像中,源只能是本地文件;
1
COPY src /app/src
  • WORKDIR: 用于指定工作目录,后续的指令都会在该目录下执行;
  • ENV: 设置环境变量;
  • EXPOSE: 指定容器的端口号,用于映射到主机的端口号;
1
EXPOSE 80
  • RUN: 用于执行命令,可以执行任何命令,比如安装软件、编译代码、启动服务等;
1
RUN apt-get install -y nginx
  • CMD: 用于指定默认的容器主进程,每个 Dockerfile 中只能有一条 CMD 指令,如果有多条,则只有最后一条会生效;
1
CMD ["node", "/app/index.js"]
  • ENTRYPOINT: 用于指定默认的容器主进程,每个 Dockerfile 中只能有一条 ENTRYPOINT 指令,如果有多条,则只有最后一条会生效;

  • VOLUME:用于指定持久化目录,Docker 容器中的数据不会保存在镜像中,当容器删除时,数据也会随之删除;

  • USER:用于指定运行容器时的用户名或 UID,后续的 RUN、CMD、ENTRYPOINT 指令都会使用该用户;

  • HEALTHCHECK:用于指定检查容器健康状况的命令,如果检查结果不是 0,则认为容器不健康,从而终止容器运行;

  • ONBUILD:用于指定当构建一个被继承的 Dockerfile 时需要运行的命令,父镜像在被子继承后,父镜像中的 ONBUILD 指令会被执行;

  • LABEL:用于为镜像添加元数据,可以使用多个 LABEL 指令。

  • STOPSIGNAL:用于指定停止容器时发送的系统调用信号;

  • SHELL:用于指定默认的 SHELL 类型,可以使用多个 SHELL 指令。

  • ARG:用于指定构建参数,构建参数可以在构建时使用–build-arg =来指定。

ENTRYPOINTCMD的区别

共同点: 都是在指定的容器启动程序及参数
不同点:当指定了 ENTRYPOINT 后,CMD 命令会作为参数传递给 ENTRYPOINT,ENTRYPOINT 命令作为主进程运行,CMD 命令作为参数传递给主进程。

以上阐述可能比较难懂,我们通过一个实例来理解:

  1. 创建一个 Dockerfile 文件,内容如下:
1
2
3
FROM ubuntu:latest
RUN apt update && apt install curl -y
CMD [ "curl", "-s", "http://ipinfo.io/ip" ]

可以看到内容很简单,就是安装 curl,然后执行 curl -s http://ipinfo.io/ip 命令。

  1. 构建镜像:
1
2
3
4
(base) zyzy:~ $ docker build -t ubuntu-curl-demo .
(base) zyzy:~ $ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu-curl-demo latest 61b1bfbc1cf6 3 hours ago 122MB
  1. 运行容器:
1
2
3
# 可以看到,正常输出了结果:
(base) zyzy:~ $ docker run ubuntu-curl-demo
202.81.228.36%
  1. CMD 命令假如运行容器时,加入参数,就会发生报错:
    因为参数 -I 会覆盖 CMD 原来的全部参数,因此报错:
1
2
3
4
# -I 参数是 curl 命令显示请求头的参数
(base) zyzy:~ $ docker run ubuntu-curl-demo -I
docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-I": executable file not found in $PATH: unknown.
ERRO[0000] error waiting for container: context canceled
  1. 将原来 dockerfile 中的 CMD 命令改为 ENTRYPOINT 命令,重新构建镜像:
1
2
3
FROM ubuntu:latest
RUN apt update && apt install curl -y
ENTRYPOINT [ "curl", "-s", "http://ipinfo.io/ip" ]
  1. 运行新的 curl-demo 容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
(base) zyzy:~ $ docker build -t curl-demo .
(base) zyzy:~ $ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
curl-demo latest 61b1bfbc1cf6 4 hours ago 122MB
# 可以发现,现在可以正常传入参数 `-I`
(base) zyzy:~ $ docker run curl-demo -I
HTTP/1.1 405 Method Not Allowed
allow: GET, OPTIONS
connection: close
content-length: 18
content-type: text/plain; charset=utf-8
date: Tue, 09 Apr 2024 14:09:24 GMT
server: nginx/1.24.0
strict-transport-security: max-age=2592000; includeSubDomains
via: 1.1 google
x-envoy-upstream-service-time: 0

Docker Compose

Docker Compose 是由 Docker 官方开源的项目,用于定义和运行多个 Docker 容器应用程序的工具。(Defining and running multi-container Docker applications)比如我们如果想要搭建一个网站的话,可能会用到前端、后端、数据库、甚至缓存和负载均衡等多个服务,这些服务都是独立的,但是它们之间又是有关联的,需要相互配合工作,比如后端需要连接数据库,前端需要连接后端,这些服务之间的关联关系就是 Compose 要解决的问题。它通过一个单独的 docker-compose.yml 配置文件来将这一组相互管理的容器组合在一起。

Docker Compose 使用一个命名为 docker-compose.yml 的 YAML 文件来配置应用程序的服务,YAML 文件中包含了应用程序的配置信息,比如服务、网络、卷等。
启动一个应用程序时,只需要运行 docker-compose up 命令,Docker Compose 就会自动去查找当前目录下的 docker-compose.yml 文件,并根据这个文件来启动应用程序的所有服务。
当你不再需要这个应用程序时,只需要运行 docker-compose down 命令,Docker Compose 就会停止并删除所有容器、网络、卷等。

1
docker compose up