Docker 备忘清单
前几天在 VPS(Virtual Private Servers) 上使用 Docker 部署一些自用服务。之前对 Docker 了解过一些,长时间不用容易遗忘,趁此再复习一下 Docker,记录在此备忘清单。
Docker
macOS 本地的话推荐 OrbStack,轻量、快速、简单易用。
Linux 服务器上安装照官方文档 Install Docker Engine 安装即可。
下面是在 Ubuntu 上安装 Docker Engine 的步骤(摘自官方文档,但改用了 apt
而非 apt-get
)。
Install Docker Engine on Ubuntu
-
Run the following command to uninstall all conflicting packages:
1
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt remove $pkg; done
-
在新主机上首次安装 Docker Engine 前,需要配置下 Docker 的
apt
仓库:1
2
3
4
5
6
7
8
9
10
11
12
13Add Docker's official GPG key:
sudo apt update
sudo apt install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update -
Install the latest version of Docker packages:
1
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
-
Verify that the Docker Engine installation is successful by running the
hello-world
image.1
sudo docker run hello-world
Install Docker Compose Plugin
安装完 Docker Engine,继续安装 Docker Compose Plugin。
1 | $ sudo apt install docker-compose-plugin |
Container
docker run ubuntu:latest ls -l
: 执行一个最新版的 Ubuntu 容器,第一次执行这条命令的时候,由于本地还没有 Ubuntu 最新版的安装镜像,因此 Docker 会自动从 Docker Hub 上下载。ls -l
是在 Ubuntu 中执行的命令。docker ps
: 查看当前正在执行的容器docker ps -a
: 查看当前所有容器(包括正在执行的和已经结束的)docker inspect [CONTAINER ID / NAMES]
: 查看容器里都有哪些内容- 同时使用
jq
来格式化 JSON
- 同时使用
docker rm [CONTAINER ID / NAMES]
: 删除容器docker rm $(docker ps -aq)
: 删除所有容器,-aq
是-a
和-q
的缩写,-q
是让docker ps
命令只显示容器 ID 列表docker run --rm ubuntu:latest ls -l
: 每次执行完一个容器之后自动删除容器
docker run -it ubuntu:latest bash
:“登录”到正在执行中的容器中,和容器里这个 bash 交互-i
是 interactive,表示我们要和容器交互;-t
是 tty,让 docker 创建一个虚拟终端,这样就能在屏幕上看到来自容器的控制台输出了- 执行
$ exit
可以离开bash
。Bash 执行结束了,容器也就结束了
docker start [CONTAINER ID]
: 重新启动之前退出的容器,可以用-i
参数以交互模式恢复容器的执行docker stop [CONTAINER ID]
: 停止一个在后台执行的容器
在容器中运行 Nginx
- 以交互模式启动一个执行 bash 的容器:
docker run -it -p 8080:80 ubuntu:latest bash
,这里,-p
参数可以让我们用host_port:container_port
的格式指定容器内外的端口映射规则; - 在 bash 中执行
apt update && apt install -y nginx
; - 安装完成后,和普通的 Ubuntu 不同的是,Nginx 不会自动启动,需要手工执行一下
$ nginx
命令。 再执行ps aux
命令,就能看到 Nginx 已经启动了; - 然后就可以在浏览器中访问
http://localhost:8080
了。
通过 Volumn 共享文件
在启动容器的时候,使用 -v
参数,可以把 Host 上的某个目录映射到容器里。也可以使用多个 -v
参数,来给容器添加多个目录映射。
在 host 中,创建一个 /tmp/web
目录,并在其中添加一个 demo.html 文件:
1 | $ mkdir /tmp/web |
容器中的 Nginx 默认的 web 根目录是 /var/www/html
。执行下面的命令启动 docker:
1 | docker run -it -p 8080:80 -v /tmp/web:/var/www/html ubuntu:latest bash |
重新在容器里运行 Nginx:
1 | $ apt update && apt install nginx -y |
然后在浏览器里就可以访问 http://localhost:8080/demo.html
了。
Image
docker images
: 查看本地已经安装的 Docker 镜像docker rmi ubuntu:latest
: 使用name:tag
的形式来删除指定镜像,也可以使用镜像 IDdocker rmi $(docker images -q)
: 使用镜像 ID 来删除镜像
构建你自己的 Docker 镜像
以上面在 Ubuntu 镜像中运行 Nginx 为例。
-
docker diff a33bf65c9623
: 查看容器中的每一个文件变化 -
docker commit -a "Kilig" -m "Install Nginx" a33bf65c9623 kilig/nginx:0.1.0
: 像 Git 中提交代码一样,去提交这些变化-a
表示 Author,即提交者的姓名;-m
表示 Message,即本次提交的注释;a33bf65c9623
,这是容器 ID,它表示了我们要制作的镜像最终的状态;kilig/nginx:0.1.0
,这是新镜像的名称,以及版本号。
-
重新执行
docker images
,就能看到我们新创建的 nginx 镜像了但运行一下刚创建的容器
docker run -it -p 8080:80 kilig/nginx:0.1.0 nginx
,容器执行一下就退出了。为什么会这样呢?这是因为当我们执行 nginx 命令的时候,会启动两类进程:首先启动的是作为管理调度的 master process,它继续生成实际处理 HTTP 请求的 worker process。默认情况下,master process 是一个守护进程,它启动之后,就会断掉和自己的父进程之间的关联,于是 Docker 就跟踪不到了,进而容器也就会退出了。因此,解决的办法,就是让 Nginx 的 master process 不要以守护进程的方式启动,而是以普通模式启动就好了。为此,我们得修改下 Nginx 的配置文件。
修正:
-
用我们新创建的镜像,启动一个执行 Bash 的容器:
1
$ docker run -it kilig/nginx:0.1.0 bash
-
修改这个容器中 Nginx 的配置文件,关掉守护进程模式:
1
2$ echo "daemon off;" >> /etc/nginx/nginx.conf
$ exit -
重新提交一次:
1
$ docker commit -a "Kilig" -m "Turn off the daemon mode" 153a7b934628 kilig/nginx:0.1.1
-
docker history kilig/nginx:0.1.1
: 可以查看我们的提交记录 -
重新执行下面的命令启动 Nginx:
docker run -it -p 8080:80 -d kilig/nginx:0.1.1 nginx
这里我们使用了-d
参数使容器在后台执行。
使用 Dockerfile 自动化镜像构建
使用 Dockfile 把打造镜像的过程放在一个脚本里,避免易错的手工打造,提高效率,也便于分享。
Dockerfile
是 docker 默认会使用的文件名,当然也可以在执行 docker run
的时候使用 -f [filename]
参数来指定其他文件名,不过一般都保持默认的。
-
新建一个
/tmp/nginx
目录,在其中创建一个叫做Dockerfile
的文件。 -
在 Dockerfile 中,添加下面内容:
1
2
3
4
5
6
7
8
9
10FROM ubuntu:latest
LABEL maintainer="kilig <kilig@kilig.studio>"
RUN apt update && apt install nginx -y \
&& apt clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
&& echo "daemon off;" >> /etc/nginx/nginx.conf
CMD ["nginx"]在上面的文件,所有大写字母,都是 Dockerfile 中的命令。其中:
FROM
指的是构建新镜像的基础,这里我们要基于ubuntu:latest
这个镜像定制自己的镜像;LABEL
用于定义一些容器的 metadata;RUN
用于设置构建新镜像的各种动作。在此一共执行了 4 个动作,分别是:安装 Nginx、清理下载安装包、清除临时文件、关闭 Nginx 守护进程模式。这里我们使用了&&
把这 4 个动作写成了一个RUN
命令,而没有使用不同的RUN
命令分别执行这些动作。作为一个最佳实践,在构建一个新镜像时,我们应该尽可能减少RUN
命令的使用次数,这样可以减少镜像的大小;CMD
用于设置容器启动时默认执行的命令,显然,我们就是要启动 nginx。
-
执行下面的命令构建镜像:
1
$ docker build -t kilig/nginx:0.1.2 .
这里:
- 当我们执行
docker build
的时候,docker 就会默认在当前目录中,查找一个叫做Dockerfile
的文件名作为构建脚本。或者我们也可以通过-f filename
的形式指定成其他文件; -t
用于设置新镜像的名称和TAG
;.
用于设置构建镜像时的上下文环境,这个环境不一定是当前目录。在 Dockerfile 中,所有的相对路径都会基于这个上下文环境指定的目录。
- 当我们执行
-
执行下面的命令启动镜像:
1
$ docker run -it -p 8080:80 kilig/nginx:0.1.2
由于 Dockfile 中通过
CMD
命令设置了容器启动的默认命令,在启动的时候,就可以不用再设置了。
更多 Dockerfile 中的命令
ARG
用于定义在构建镜像时使用的变量;ENV
用于定义在构建镜像和执行容器时使用的环境变量;- 执行容器的时候,可以使用
-e
参数,修改环境变量的值
- 执行容器的时候,可以使用
提交镜像到 Docker Hub
1 | # 提交镜像到 Docker Hub |
Network
docker network -h
查看和网络相关的命令的用法。
Docker 主要支持两种形式的网络,分别是:
bridge mode
:这就是我们在单个 host 上执行多个容器时使用的网络,同时,也是 Docker 默认的网络类型;overlay mode
:这是在多台 hosts 上部署复杂网络结构时使用的网络模式。
使用 Docker 中的网络:
-
执行下面的命令,创建一个 Docker 网络:
1
$ docker network create --driver=bridge kilig-net
Docker 会给我们返回一个表示该网络的哈希值。
-
把所有相关的容器在启动的时候,通过
--network
选项,加入到kilig-net
中:1
$ docker run --network=kilig-net -p 80:80 -it --rm kilig/nginx:0.1.2
Volume
1 | $ docker run --rm -it -v /data busybox |
busybox
是一个极简的 Linux,非常适合用来试验一些功能。这里我们使用 -v
的时候,只指定了一个目录 /data
,然后就可以把 Linux 中一些有写操作的目录,符号链接到 /data
。
那么使用 -v /data
的时候,实际的文件究竟存在了哪呢?
- 执行
docker volume ls
,可以看到 Docker 给这个 data volume 分配了一个唯一 ID:VOLUME NAME
; - 执行
docker volume inspect [VOLUME NAME]
,在输出的信息中Mountpoint
就是/data
容器实际保存的目录。
但在 Mac 或者 Windows 上,这个目录就并不是直接创建在 Host 的文件系统中的,而是在 Docker 创建的一个虚拟层上的。为了看到这个 volume 对应的物理文件夹:
- 我们把 Mac 的
/
映射到容器里的/vm-data
目录:$ docker run --rm -it -v /:/vm-data busybox
; - 在容器里执行
ls /vm-data/var/lib/docker/volumes/
,就可以看到/data
volume 实际存储的位置了。
Docker Compose
使用 Docker Compose 一键部署开发环境。
docker-compose.yml
是编写的构建脚本,文件名是固定的;- 同级目录下
.env
文件用来定义环境变量;
在 docker-compose.yml
所在目录下,执行 docker compose build
构建镜像,再执行 docker compose up -d
,就可以启动服务并在后台运行了。
需要注意的是:docker 并不一定会按照 docker-compose.yml
中 services
的顺序启动,所以不能依赖这个关系。