仅为个人查阅使用,要学习 Docker 的话,推荐看这份文档:《Docker — 从入门到实践》
P.S. 大多数的
docker container xxx
/docker image xxx
命令,都有相对应的缩写方式:docker xxx
零、安装 docker
参见 《Docker — 从入门到实践—安装》,包括各 Linux 发行版、Windows、MacOS
国内加速器: https://get.daocloud.io/#install-compose,包含 Linux 安装加速、Hub 镜像仓库加速。(但它的 Windows 加速版本太老,别用)
一、基本概念
要构建并运行一个 Docker 容器,有这样几个步骤:
- 编写 Dockerfile,然后通过 build 命令从 Dockerfile 构建镜像
- 镜像是分层构建,分层存储。这使得镜像的复用、定制变的更为容易。
- 镜像不包含任何动态数据,其内容在构建之后就不会被改变。因此它对运维很友好,可以实现一次 build 到处运行。
- 通过 run 命令启动该容器。(镜像仓库后面再记)
- 镜像内容是静态的,而容器是动态的!
- 每一个容器运行时,是以镜像为基础层,在其上创建一个当前容器的存储层。可以称这个为容器运行时读写而准备的存储层为 容器存储层。
- 容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。
- 所有的文件写入操作,都应该使用 数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。
- 用 login 命令登录到 Docker 的镜像仓库(私人的或者公共的),然后通过 push 命令将构建好的镜像推送上去。
- 在别的机器上 pull 镜像下来,通过
docker run xxx
命令运行。
二、Docker 基础命令
1. 获取镜像
通过 pull 命令从仓库中获取镜像:
# 格式
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
# e.g. 1:从公共仓库拉 nginx 的镜像
# 下列两条命令效果相同,默认从 library (官方镜像)找镜像,tag 默认为 latest(最新版本)
docker pull library/nginx:latest
docker pull nginx
# e.g. 2:从公共仓库中,拉取个人上传的镜像
docker pull <username>/myblog:latest
# e.g. 3:从私人仓库拉取 ryan 项目下,名为 myblog 的镜像
docker login reg.myharbor.com:4321 # 私人仓库一般都需要先登录,会将登录信息保存到本地
docker pull reg.myharbor.com:4321/ryan/myblog
自己编写 Dockerfile,然后通过 Dockerfile 构建镜像(Dockerfile 的语法后面再细说):
# --tag 可以缩写成 -t,用于指定镜像的 [镜像名:<tag>],其中 <tag> 可省略。
docker build --tag friendlyhello . # 使用当前目录下的 Dockerfile 构建镜像,并且`镜像名:tag`为 `friendlyhello:latest`
2. 查看/删除镜像
查看镜像列表:
docker image ls -a # 列出当前机器上的所有镜像
docker images -a # 上一条命令的缩写
docker images -a <image name> # 查找名为 <image name> 的镜像
删除镜像:
docker image rm <image id>
docker image rm <image name>:<tag> # 删除 <image name> 镜像的一个 tag
docker rmi <xxx> # `docker image rm <xxx>` 的缩写
docker image ls -f dangling=true # 列出所有虚悬镜像(即仓库名和 tag 都为 none 的镜像)
docker image prune # 清除所有未被使用的虚悬镜像(被依赖了的虚悬镜像无法直接清除,见后)
docker image rm $(docker image ls -a -q) # 删除本机的所有镜像(危险操作)
docker system prune # 删除所有停止的容器+虚悬镜像+未被使用 network+构建缓存
docker system prune --all # 除了上述数据之外,还要删除所有未被容器使用的镜像
如果一个正常的镜像,或者某个容器依赖了某些虚悬镜像,那这些虚悬镜像就不能被直接删除。你需要先删除掉那个上层的镜像或者容器。
删除该上层镜像时,所有只被它依赖了的虚悬镜像也会被自动清除掉。但是删除上层容器后,还需要手动用 docker image prun
清除被它使用了的虚悬镜像。
3. 启动容器
docker run
: 从指定镜像新建一个容器并运行命令,然后终止运行。标准流程如下:- 检查本地是否存在指定的镜像,不存在就从公有仓库下载(对应
docker image pull
) - 利用镜像创建并启动一个容器(对应
docker container create
) - 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序(对应
docker container start
) - 执行完毕后容器被终止(对应
docker container stop
)
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载(对应
# 在 ubuntu 18.04 中输出 Hello World
# --rm 表示,容器终止运行后,自动删除该容器
# 此外,还有 --env 设置环境变量,-p 设置端口映射,--volume 设置数据卷
# --net 设置网络连接类型
docker run --rm ubuntu:18.04 /bin/echo 'Hello world'
docker run -it --user=root <image:tag> bash # 以 root 身份运行 bash(不要去用 su root,然后到处找 root 的密码了。。)
# 进入容器的 bash 终端
# -i 使容器的 stdin 保持打开,-t 为容器分配一个伪 tty。
# 结合使用上述两个选项 `-it`,效果类似 ssh 远程连接。
docker run -it ubuntu:18.04 /bin/bash
# P.S. 其实只需要 -i 参数,就能和容器进行交互了,敲命令能得到回复。
# -t 添加的伪终端,感觉只是在每个命令行开头加了个 `root@25d5c233769b:/# ` 这样的提示符。
docker run -d
:使新建的容器在后台运行
# 在后台启动一个 nginx 容器,提供静态网站访问功能,或者做代理
# -d 表示在后台运行该容器,--rm 使容器在终止后被自动删除
docker run -d --rm my-nginx:latest
# --restart=always 容器意外退出时,总是自动重启。以保证服务总是可用。
# --name my-blog 为容器取个有意义的名字,可在任何命令中,用该名字替换 container id 使用
docker run --rm -d -restart=always --name my-blog my-nginx:latest
# 前台运行的容器,stdout 会被定向到本机的 stdout,所以能直接看到命令的输出
# 而后台运行的容器,就需要通过下面的命令查看输出了
docker logs <container id / container name> # 查看指定容器的 logs,更详细的参数请 man
docker logs <container id / container name> | vim - # 通过 vim 查看容器日志,可以方便地按行跳转、全文搜索。
docker logs -f <container id / container name> # 持续地输出容器的日志(ctrl-c 退出)
- 查看容器列表、启动/终止容器、删除容器
docker container ls -a # 列出所有容器
docker ps -a # 上一条命令的缩写
docker container create --name <container name> <image>:<tag> [command] # 从指定镜像新建容器,但是不立即运行。
docker container start <container id / container name> # 启动一个已经终止运行的容器
docker container stop <container id / container name> # 终止一个正在运行的容器(该容器可能在后台工作,也可能是正被别的 shell 运行)
docker container rm <container id / container name> # 删除一个未运行的容器
docker container rm -f <container id / container name> # 强制删除容器,即使该容器正在运行
docker container prune # 清理掉所有已终止的容器
docker rm -f $(docker ps -aq) # 强制删除掉所有的容器!可用于清理系统。
docker [start|stop|rm] <xxx> # 上述命令的缩写,container 可省略
- 登入容器:
# 1. 使用 attach 命令登入(不推荐!!)
# 将容器的 stdin/stdout/stderr 与当前终端接驳,容器切换到前台运行。
# 特别注意的是,因为容器切换到了前台运行,退出会导致容器终止!因此才不推荐用此命令。
docker container attach <container id / container name>` # 可缩写成 `docker attach xxx`
# 2. 使用 exec 命令登入(推荐方式)
docker exec -it <container id / container name> bash # 使用默认用户登录
docker exec -it --user=root <xxx> # 以 root 身份登入容器(不要去用 su root,然后到处找 root 的密码了。。)
- 查看容器的各项数据
docker container stats <container id / container name> # 查看容器的 CPU/RAM 使用率,网络IO速率等参数
docker container logs <container id / container name> # 查看容器日志,一般都是 CMD/ENTRYPOINT 命令的日志
docker container diff <container id / container name> # 查看容器层相对镜像的变动(A-added,D-deleted,C-changed)
docker container kill <container id / container name> # 强制终止容器,发送终止信号给容器。(只是发信号过去,不一定成功。。)
docker container ls --size # 列出所有容器,显示各容器的 disk 占用
容器/镜像中数据的导入和导出
# 1. 将容器中的指定路径(文件夹) copy 出来
# 可以指定存放路径(若路径不存在,会自动创建),也可以通过管道以流的方式传输给下个程序
docker container cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
# 2. 将本机的数据拷贝进容器,可以是路径,也可以通过管道以流的方式传输到容器中。(用 tar -ax 就能正确将流转成原来的文件)
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH
# 3. 存取镜像中的数据
# 方法一:直接在 Dockerfile 中使用 COPY --from=<image name>:<tag> <src path> <dest path>
# 方法二:先使用 docker create 为镜像新建容器层,再用 docker cp 命令导入/导出数据。
4. 镜像/容器的导入和导出
4.1 镜像的导入导出
镜像可以通过中心仓库进行分享,但是有些情况下,我们只想快速地将镜像分享给某台机器。这就需要用到镜像的导入导出功能了:
# a. 将指定镜像导出为 tar 文件
# 注意:如果同名则会覆盖(没有警告)
docker save <imagename>:<tag> -o <filename>
# 使用 gzip 进一步压缩
docker save <imagename>:<tag> | gzip > <imagename>-<tag>.tar.gz
# b. 从指定文件加载镜像
docker load -i <imagename>-<tag>.tar.gz # 不需要先解压
上述导入导出命令,结合 ssh,就能完成镜像的快速分享:
docker save <镜像名> | bzip2 | pv | ssh <用户名>@<主机名> 'cat | docker load'
# pv:即 Pipe Viewer,意思是通过管道显示数据处理进度的信息。一般需要自行安装。
# yay -S pv 或 sudo apt-get install pv 或 yum install pv
4.2 容器的导入和导出(不推荐)
容器导出后再导入到镜像中,环境变量和一些基本的配置信息丢失了。所以最好还是不用使用这个功能,前后不一致,从镜像入手才是正确的方式。
虽然说容器层不应该保存任何动态数据,
但是总有特殊情况(或者说太菜),我们把一些必需的东西放进了容器层,现在要使容器能在另一台机器上运作。
这时候,就需要做容器的导入导出了:
# 将容器的当前快照导出为文件
docker export [container id / container name] > ubuntu.tar
# 从文件将容器快照导入为镜像,
# 注意!!!是导入成了镜像!并且为这个镜像指定了 [name:tag]
cat ubuntu.tar | docker import - test/ubuntu:v1.0
容器导出的镜像,环境变量和一些基本的配置信息都会丢失,而且 ENTRYPOINT/CMD 信息也会丢失,运行(run)时还需要手动指定命令。
感觉是相当不好用。
Docker 仓库
docker login <repo-host:port> # 登录到指定的仓库,默认登录到 docker hub
docker logout <repo-host:port> # 登出指定仓库
docker search <image name> # 在 Docker Hub 中搜索镜像