本文较长,属于系统性总结,可作为使用手册查阅。
本文主要介绍了docker常识与基础。

步骤/目录:
一、初识docker与安装docker
二、docker网页GUI与docker源
三、docker命令行基本操作
    1.docker pull拉取镜像
    2.docker run运行容器
    3.docker image ls查看镜像
    4.docker image rm删除镜像
    5.docker commit保存容器
    6.docker cp及其他命令
四、编写Dockerfile定制镜像
    1.FROM和RUN命令
    2.Dockerfile的其他指令
        (1)COPY
        (2)ADD
        (3)CMD
        (4)ENTRYPOINT
        (5)ENV
        (6)ARG
        (7)VOLUME
        (8)EXPOSE
        (9)WORKDIR
        (10)USER
        (11)HEALTHCHECK
        (12)ONBUILD
        (13)LABEL
        (14)SHELL
    3.拉取时自动对应系统
    4.其他方式制作镜像
五、操作容器
    1.启动容器
    2.后台运行容器
    3.终止/重启容器
    4.进入容器
    5.导入/导出容器
    6.删除容器
六、仓库
    1.官方仓库
    2.私有仓库
    3.使用Nexus帮助管理私有仓库
七、docker数据卷
    1.数据卷的常用操作
    2.挂载宿主机目录作为数据卷
八、容器与网络
    1.端口映射
    2.docker网络
    3.配置DNS
    4.高级网络配置

本文首发于个人博客https://lisper517.top/index.php/archives/18/,转载请注明出处。
本文的目的是对docker基础内容做总结归纳,使用本文从零学习docker将有一定难度。本文主要总结自菜鸟教程Docker——从入门到实践和一些其他零散资料,有不明白的地方请参考原文。
本文写作日期为2022年2月-7月。本文使用的是树莓派4B(内存8G版),系统为Pi OS 64位桌面版(2022年4月4日更新,镜像名 2022-04-04-raspios-bullseye-arm64.img ),装上系统后仅做了一些基础配置(安装了ufw、watchdog,设置了ssh密钥,apt update了一下)。

一、初识docker与安装docker

Docker是基于Go语言的开源应用容器引擎,可以将应用和依赖打包成容器、在其他linux/windows/mac机器运行,还支持虚拟化。docker的优点主要有三:标准化、一致化;可移植、响应式部署;轻量级、硬件开销小。最后附上docker官网Github社区版docker。另外,Docker对比传统虚拟机有很大优势(硬件开销小)。
最后介绍一下docker架构:包括镜像,容器和仓库。面向对象编程中,类实例化后就创建了对象,对于docker来说,镜像=类,容器=对象,仓库则用来保存镜像。另外,可通过 仓库名:标签 指定镜像版本,不给出时默认为latest。
安装docker推荐使用官方脚本自动安装,树莓派可运行如下命令:

cd /tmp
curl https://get.docker.com/ > ./docker_install.sh
chmod +x docker_install.sh
sh docker_install.sh –mirror Aliyun #换源安装
rm docker_install.sh

没有配置源时不要直接apt install安装Docker,使用apt命令安装可参考树莓派实验室Docker——从入门到实践
安装完后测试docker,输入:

docker run hello-world
#输出结果
Unable to find image 'hello-world:latest' locally #默认为latest,并先在本地查找
latest: Pulling from library/hello-world #从公共仓库pull,这里是默认的Docker Hub
9b157615502d: Pull complete
Digest: sha256:97a379f4f88575512824f3b352bc03cd75e239179eea0fecc38e597b2209f49a
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (arm32v7)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

接下来进行如下操作:

systemctl enable docker
systemctl start docker
usermod -aG docker pi #这行是把pi用户添加到docker组,就是给个权限。没有pi用户的可以改成其他用户。

另外,这里提一下如何在windows机器上安装docker。首先,推荐运行windows的WSL服务(微软推出的,一种在windows上运行linux的功能),安装方式为:
(1)确定你的windows版本,win10的话需要2004版本及之后;或者win11。
(2)用管理员模式打开cmd,输入 wsl --install 即可(默认安装的是最新的 WSL 1,也有可能是2)。
简单介绍一下wsl操作。使用 wsl --help 查看帮助, wsl --list --online (或简写 wsl -l -o )查看目前支持的linux发行版(2022年9月,支持Ubuntu,Debian,kali-linux,openSUSE-42,SLES-12,Ubuntu-16.04、18.04、20.04), wsl --install -d 发行版名称 来下载安装一个发行版。这里以Ubuntu-20.04为例。使用 wsl --update 更新wsl的内核版本,使用 wsl --set-version Ubuntu-20.04 2 来指定下载的发行版使用WLS 2。另外, wsl -d 发行版名 可运行发行版, wsl -shutdown 是关闭所有发行版并退出wsl, wsl -t 发行版名 终止一个发行版, wsl -s 发行版 将发行版设定为默认, wsl --unregister 发行版 可注销、删除发行版实例。
然后到 docker-desktop下载网页 ,或者直接点击 这个链接 下载windows版的docker-desktop,安装、重启,打开docker-desktop,检查一下设置里面的 Use the WSL 2 based engine 是否勾选上了。针对VS Code,可以在扩展里找到 Remote - WSL ,然后你就可以在windows、虚拟机里同时运行两个VS Code,方便你在虚拟机中修改代码。

如果你的windows不支持WSL,就跳过安装WSL的步骤,只安装docker-desktop。

二、docker网页GUI与docker源

docker的portainer镜像提供了网页GUI,操作更加方便。下载并运行portainer(使用9000端口。这里的ce是community edition社区版,原来的portainer/portainer已停止更新):

docker pull portainer/portainer-ce
docker volume create portainer_data
docker run -d -p 9000:9000 --name portainer --restart always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer-ce
#不用在ufw打开端口

这时浏览器即可查看 树莓派ip:9000 进入portainer。初次进入先设置用户和密码,然后选择Local即可。不过初次接触docker最好还是从shell开始,下面介绍一些docker命令。
另外,若拉取镜像时较慢,可以配置一下镜像加速。对树莓派,新建/etc/docker/daemon.json并写入:

{
  "registry-mirrors": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ]
}

如果有阿里云还可查看自己账号的加速器地址,详见Docker——从入门到实践。然后重启docker,并检查加速器:

systemctl daemon-reload
systemctl restart docker
docker info
#输出结果
......
......
 Registry Mirrors:
  https://hub-mirror.c.163.com/
  https://mirror.baidubce.com/

三、docker命令行基本操作

1.docker pull拉取镜像

官方仓库Docker Hub上有大量镜像,获取镜像使用docker pull。输入docker pull --help可发现该命令的格式:

docker pull [选项] [Docker仓库地址[:端口号]/]仓库名[:标签]

其中,Docker仓库地址的格式为<域名/ip地址>,有时后面可加:端口号。若不给出则默认为docker.io(官方的Docker Hub);
仓库名一般是两段:<用户名>/<软件名>。若使用Docker Hub且不给出用户名,则用户名为默认的library
:标签指定软件版本,若不给出则默认为latest
接下来在树莓派上使用如下命令:

docker pull ubuntu

输出如下:

Using default tag: latest
latest: Pulling from library/ubuntu
latest: Pulling from library/ubuntu
8795d4da4abd: Pull complete
Digest: sha256:626ffe58f6e7566e00254b638eb7e0f3b11d4da9675088f4781a50ae288f3322
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest

2.docker run运行容器

docker run [OPTIONS] IMAGE [COMMAND] [ARG...]可实例化镜像为容器。使用docker run时,Docker会先在本地主机查找镜像,若不存在则会从默认的官方仓库Docker Hub下载。

docker run -it ubuntu bash

-i允许对容器内的标准输入进行交互,-t在容器内指定终端。
上面的命令输入后,就进入了ubuntu:latest容器的bash终端,可以输入一些命令交互,比如:

cat /proc/version
#输出结果
Linux version 5.10.92-v7l+ (dom@buildbot) (arm-linux-gnueabihf-gcc-8 (Ubuntu/Linaro 8.4.0-3ubuntu1) 8.4.0, GNU ld (GNU Binutils for Ubuntu) 2.34) #1514 SMP Mon Jan 17 17:38:03 GMT 2022

cat /etc/os-release
#输出结果
NAME="Ubuntu"
VERSION="20.04.3 LTS (Focal Fossa)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 20.04.3 LTS"
VERSION_ID="20.04"
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
VERSION_CODENAME=focal
UBUNTU_CODENAME=focal

测试完毕后,输入exit退出。
另外,如果输入的是docker run -it --rm ubuntu bash,则退出后会自动删除容器。也可以通过刚才的portainer提供的网页UI中删除,或后续介绍的docker xxx rm命令。

3.docker image ls查看镜像

docker image lsdocker images列出已下载的镜像,其中IMAGE ID是镜像的唯一标识。如果使用docker image ls -a则还可以看到中间层镜像(docker是一层层搭建的,中间层差不多相当于存档点,后续有介绍);使用docker image ls 镜像名[:标签]还可以筛选镜像。另外,docker image ls支持-f参数(过滤器),比如列出建立时间在某镜像之后的可使用docker image ls -f since=镜像名before同理)。最后,docker image ls -q可以输出镜像ID(常用于对镜像批量操作)。
docker system df查看镜像、容器、数据占用的空间。
dangling image(虚悬镜像):有时用docker image ls时会出现<none>镜像,这是因为之前下载过某版本的镜像,结果后来官方的同名镜像更新了又下过一次,旧的镜像名给了新镜像。这样的镜像是可以随意删除的,用命令docker image prunedocker image prunedocker container prune可以用于删除当前不使用的镜像或容器。

4.docker image rm删除镜像

docker image rm [OPTIONS] IMAGE [IMAGE...]可删除一个或多个镜像,其中IMAGE可以是镜像的短ID、长ID、名字或摘要。其中长ID就是docker image ls列出的,短ID则是长ID的前几位、足够与其他镜像区分,一般取前3位。
删除镜像时,会先untag镜像,即取消符合要求的镜像的标签;然后从镜像的上层向基础层开始删除,若有其他镜像依赖于某一层,则可能暂时不会删该层。另外,如果有活动的容器正在使用镜像,这时删除镜像要先停止容器(docker stop 容器短id或名字)、删除容器(docker container rm 容器短id或名字),再删镜像(docker image rm 镜像短id或名字)。
最后,docker image rm可以配合docker image ls -q批量删除镜像,如删除指定名字的镜像:

docker image rm $(docker image ls -q 镜像名)

或删除指定 镜像:标签 创建前的镜像:

docker image rm $(docker image ls -q -f before=镜像名:标签)

5.docker commit保存容器

镜像构建时是一层层的,每一层是在前一层基础上,并且每层只包含需要的东西。有些镜像间可以复用某些层。容器运行时需要存储,所以容器就是在镜像的基础上加了一个存储层。
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]可以保存存储层、构成新的镜像,以后运行时就是改动过文件变化的容器。注意,不要用docker commit构建镜像。
下面的命令将容器保存为镜像,并附加了一些信息:

docker commit \
    --author "Tao Wang <twang2218@gmail.com>" \
    --message "修改了xxxx" \
    容器名 \
    镜像名:自定义标签名

之后docker image ls就能看到标签为自定义标签名的新镜像。docker history 镜像名:标签名则可以查看镜像的历史记录。
最后,docker commit只有一些特殊应用(如被入侵后保存现场),不应该频繁使用(如当作系统备份),因为改动的层多、镜像会越来越臃肿,而且时间久了会记不清该镜像进行过的操作。

6.docker cp及其他命令

下面介绍一下docker cp命令。其用途为容器与主机之间的数据拷贝,格式为:

#宿主机拷贝到容器
docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH|-
#容器拷贝到宿主机
docker cp [OPTIONS] SRC_PATH|- CONTAINER:DEST_PATH

可用的选项为-L,保持源目标中的链接(Always follow symbol link in SRC_PATH)。
补充一点docker cp的使用细节,如果是参数是两个目录,那么不论哪一方、末尾加不加/,效果都是把源目录放到目标目录下:

docker cp /www/dir_1 container_1:/www
docker cp /www/dir_1 container_1:/www/
docker cp /www/dir_1/ container_1:/www
docker cp /www/dir_1/ container_1:/www/

以上4条命令效果都是一样的。

其他docker命令可参考runoob的 Docker命令大全

四、编写Dockerfile定制镜像

1.FROM和RUN命令

Dockerfile是一个脚本文件,其中包含了一条条指令,每一条指令构建一层。一般来说,应该用Dockerfile而不是docker commit构建镜像。
以nginx镜像为例,进行如下操作:

mkdir /tmp/mynginx
cd /tmp/mynginx
nano Dockerfile

写入内容:

FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html

FROM是指定基础镜像,该命令一般是Dockerfile的第一条指令(有时前面可出现ARG)。另外,scratch表示一个空白镜像。
RUN是用来执行bash命令的,其格式有两种:一是 RUN bash命令 ,二是 RUN ["可执行文件", "参数1", "参数2"...]。因为Dockerfile一条指令构建一层,所以有多条bash命令时不应连续使用RUN,而是应该写成如下示例:

FROM debian:stretch

RUN set -x; buildDeps='gcc libc6-dev make wget' \
    && apt-get update \
    && apt-get install -y $buildDeps \
    && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
    && mkdir -p /usr/src/redis \
    && tar -xzf redis.tar.gz -C /usr/src/redis --strip-components=1 \
    && make -C /usr/src/redis \
    && make -C /usr/src/redis install \
    && rm -rf /var/lib/apt/lists/* \
    && rm redis.tar.gz \
    && rm -r /usr/src/redis \
    && apt-get purge -y --auto-remove $buildDeps

另外,构建每一层时都尽量只包含必要的东西,以避免臃肿;上面的RUN在最后删除了编译构建用的软件,是非常重要的。
现在回到刚才创建的基于nginx的Dockerfile,执行:

docker build -t nginx:v3 . #最后的 . 是上下文路径,不要遗漏

输出如下(之前未docker pull nginx):

Sending build context to Docker daemon  2.048kB
Step 1/2 : FROM nginx
latest: Pulling from library/nginx
aaef1f1162ec: Pull complete
158fdbcad3be: Pull complete
e91a8f81054f: Pull complete
5dac8ed1dcde: Pull complete
f4ad1f3b674e: Pull complete
83c0cd4ca302: Pull complete
Digest: sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767
Status: Downloaded newer image for nginx:latest
 ---> d12b7a1d6c8a
Step 2/2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
 ---> Running in c79b0e883836
Removing intermediate container c79b0e883836
 ---> d749600f530b
Successfully built d749600f530b
Successfully tagged nginx:v3

build命令的格式为docker build [选项] <上下文路径/URL/->,本例中上下文路径就是 . 。要注意上下文路径并不是指保存Dockerfile的路径。docker在运行时分为docker引擎和客户端工具,虽然这里一直是在本地运行,但其实全程都通过API实现引擎和客户端交互,属于 Client/Server 的模式。为了让服务器端获取本地文件, docker build 会将上下文路径里的所有内容打包上传到服务器。所以在构建镜像时需要用到的东西都保存到上下文路径,Dockerfile里写 COPY 时也要写相对于上下文路径的相对路径(COPY ./xxx/xxx 容器内路径)。刚才执行docker build -t nginx:v3 .时显示结果第一行Sending build context to Docker daemon 2.048kB就是在打包上下文路径到引擎服务器端。

2.Dockerfile的其他指令

这里介绍的比较简略,附带使用例的详细介绍可参考Dockerfile 指令详解Docker 命令大全

(1)COPY

COPY命令的格式为COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
用途是将上下文路径的东西复制到本层镜像内的目标路径位置。
源路径可以有多个(目标路径只有一个),也可以写成通配符,通配符要满足Go语言的filepath.Match规则。目标路径可以是绝对路径或相对于工作目录的相对路径(用WORKDIR可指定工作目录),目标路径不存在时会自动创建。
另外,COPY可以保留源文件的rwx、变更时间等元数据,也可用--chown=<user>:<group>来改变所属用户和组。
最后,由于目标路径只有一个,所以一般只写一次COPY,之后再通过RUN mv等方式移动文件和目录。

(2)ADD

ADD和COPY的格式、性质差不多,但ADD增加了一些功能。但是由于其有些功能不明确,所以一般情况下都使用COPY。
ADD的源路径可以是URL,引擎会自动下载文件放到目标路径,并且文件权限为600。但这个功能不推荐使用,因为可以用RUN替代,一般都是用wget、curl下载后,再进行改权限、解压、清理等操作。
当源路径为tar文件、压缩格式为gzip、bzip2、xz时,ADD会自动解压该文件到目标路径。目前ADD应仅用于这种情形,其他情况用COPY和RUN代替。

(3)CMD

CMD用于指定容器运行的主进程。容器仅为主进程而存在,当主进程结束后容器也会停止。
CMD和RUN的格式类似,也是两种:

shell 格式:CMD <命令>
exec 格式:CMD ["可执行文件", "参数1", "参数2"...]

在exec格式中,一定注意要使用双引号。
另外还有一种格式:参数列表格式:CMD ["参数1", "参数2"...],是在指定了 ENTRYPOINT 指令后,用 CMD 指定具体的参数。
CMD命令在执行时会包装成sh -c形式,比如:
CMD echo $HOME包装为CMD [ "sh", "-c", "echo $HOME" ]
另外要注意docker容器中的应用都应前台执行,因为容器就是为了主进程存在的,主进程退出容器也会退出。比如nginx启动不应写成CMD service nginx startCMD systemctl start nginx,而应是CMD ["nginx", "-g", "daemon off;"],即一般都是直接运行二进制文件。
多个CMD时,仅最后一个生效(比如从官方镜像生成自己的镜像,就可以写CMD来覆盖官方镜像中的CMD)。

(4)ENTRYPOINT

ENTRYPOINT的格式和RUN、CMD一样,分为exec格式和shell格式。
ENTRYPOINT的用途和CMD一样,也是指定容器启动程序和参数。当指定了ENTRYPOINT后,CMD就不再直接运行命令,而是将CMD的内容作为参数传给ENTRYPOINT。比如Dockerfile里写:

ENTRYPOINT [ "xxx", "xxx" ]
CMD ["参数1", "参数2"...]

那么使用该镜像时就可以docker run 镜像名 参数形式来传递参数。
ENTRYPOINT 还可用于应用运行前的准备工作。
和CMD一样,多个ENTRYPOINT时仅最后一个生效。

(5)ENV

ENV的格式有两种:

ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

ENV的作用是设置环境变量,方便后续调用,和docker run -e作用一样。比如对于mysql镜像,可使用以下方式设置root密码:

ENV MYSQL_ROOT_PASSWORD=123456

或者用docker run -e MYSQL_ROOT_PASSWORD=123456也是一样的。
最后注意,一般docker run参数指定的东西优先级更高,会覆盖其他地方的设置,其他命令也是这样。

(6)ARG

ARG的格式为:

ARG <参数名>[=<默认值>]

ARG的用途和ENV一样,但和ENV不同在于,ARG设置的环境变量在容器运行时不存在(不能传递给容器)。另外ARG有生效范围,在FROM之前用的话ARG只能用于FROM中。

(7)VOLUME

VOLUME的格式为:

VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>

VOLUME用于定义匿名卷。容器运行时应尽量保持存储层不写入,对数据库等,其数据库文件应保存在卷中。为防止用户运行容器时忘记挂载卷(docker run忘记-v参数),就可以用VOLUME指定匿名卷。

(8)EXPOSE

EXPOSE的格式为:

EXPOSE <端口1> [<端口2>...]

用途是声明端口。和docker run -p的区别在于EXPOSE只起声明作用,没有映射端口的功能。

(9)WORKDIR

格式为:

WORKDIR <工作目录路径>

作用为指定容器内的当前目录(不存在时自动创建)。在Dockerfile中一个RUN就是一层,两个RUN执行的环境不同(比如使用cd时)。所以要不就写绝对路径,要不就指定工作目录。
Dockerfile中连续多次使用WORKDIR不是切换当前目录,而是类似于cd,进入子目录。

(10)USER

格式为:

USER <用户名>[:<用户组>]

作用是指定当前用户。如果执行脚本时希望切换到其他用户,不应用su或sudo等,而应使用gosu。

(11)HEALTHCHECK

格式:
HEALTHCHECK [选项] CMD <命令>:设置检查容器健康状况的命令
HEALTHCHECK NONE:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
用途是告诉docker如何判断容器的状态是否正常。加了HEALTHCHECK的镜像,运行容器时初始状态为starting,然后通过检查变为healthy,连续数次失败则为unhealthy。
HEALTHCHECK的选项:
--interval=<间隔>:两次健康检查的间隔,默认为 30 秒;
--timeout=<时长>:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;
--retries=<次数>:当连续失败指定次数后,则将容器状态视为 unhealthy,默认 3 次。
HEALTHCHECK只应出现一次。多次出现时,仅最后一个生效。

(12)ONBUILD

格式为:

ONBUILD <其它指令>

ONBUILD比较特殊,它后面跟的是其它指令,比如RUN, COPY等,且这些指令在当前镜像构建时并不会被执行,只有当以当前镜像为基础镜像去构建下一级镜像的时候才会被执行。

(13)LABEL

格式为:

LABEL <key>=<value> <key>=<value> <key>=<value> ...

用途是为镜像添加元数据。

(14)SHELL

格式为:

SHELL ["executable", "parameters"]

用于指定RUN、ENTRYPOINT、CMD的shell。

Dockerfile中使用的指令介绍告一段落,下面介绍点了解内容。

3.拉取时自动对应系统

使用某种架构的系统docker pull时一般应该拉取对应架构专用的镜像。使用 manifest 列表就能不指定架构、自动拉取对应架构的镜像。详见官方博客

4.其他方式制作镜像

除了Dockerfile,还可从rootfs压缩包或docker save、docker load制作镜像。详见其他制作镜像的方式

五、操作容器

1.启动容器

启动容器分两种,一种是新建并启动容器,另一种是重启已经exited的容器。
docker run用于新建并启动容器,docker container start用于启动已经终止的容器。但是由于docker的轻量级,一般容器都是随用随删的。

2.后台运行容器

docker run -d可以后台运行容器(STDOUT不输出到宿主机),之后会返回一个容器id,也可以通过docker container ls查看。要查看容器的STDOUT,可使用docker container logs

3.终止/重启容器

docker container stop可终止运行中的容器,或者主进程结束后容器也会自动终止。已经终止的容器可通过docker container ls -a看到。docker container restart可重启容器。

4.进入容器

使用docker run -d时容器会在后台进行。若要进入容器操作,可使用docker attachdocker exec,其中推荐使用docker exec
比如运行一个ubuntu容器时,docker attach 容器iddocker exec -it 容器id bash效果是一样的,但在docker attach中使用exit会导致容器终止,docker exec则不会。

5.导入/导出容器

docker export 容器id > 123.tar可导出容器快照到本地;docker import可导入容器快照为镜像。importload的区别在于import导入时丢弃了快照的历史记录、元数据,可以重新指定这些元数据,import生成的快照文件也因此要小一点。

6.删除容器

docker container rm可删除终止状态的容器。加-f参数可删除运行中的容器(不推荐,一般还是停止容器再删除)。docker container prune可删除所有终止状态的容器。

六、仓库

1.官方仓库

官方仓库为Docker Hub,大部分镜像都可找到。可在docker官方网站注册一个docker账号,并使用docker logindocker logout登入、登出。
docker search 关键词可查找镜像,--filter=stars=N可仅显示收藏在N以上的镜像。找到心仪的镜像后,使用docker pull下载镜像。登录后还可以通过docker push推送自己的镜像(制造代码垃圾,屎山又变多了)。

2.私有仓库

可以通过官方提供的docker-registry构建私有镜像仓库。
首先安装运行docker-registry:

docker run -d -p 5000:5000 --restart=always --name registry registry

默认仓库创建在/var/lib/registry下,可通过-v参数指定镜像存放在本地的路径。
然后使用docker tag标记一个镜像,并推送到私有仓库(以之前下载的ubuntu镜像为例):

docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest
docker push 127.0.0.1:5000/ubuntu:latest

这时可以用curl查看私有仓库中的镜像:

curl 127.0.0.1:5000/v2/_catalog

显示结果:

{"repositories":["ubuntu"]}

如果在公司中,可能希望局域网的主机都能推送镜像,但docker默认不允许通过非HTTPS方式推送镜像。这时可以配置docker取消该限制,或配置通过HTTPS访问的仓库。后者比较复杂,但有新机器入网时不用添加配置,详见私有仓库高级配置
要配置取消docker的HTTPS限制,可在/etc/docker/daemon.jason中写入:

{
  "registry-mirror": [
    "https://hub-mirror.c.163.com",
    "https://mirror.baidubce.com"
  ],
  "insecure-registries": [
    "192.168.xxx.xxx:5000"
  ]
}

其中registry-mirror是前面提到的镜像下载加速,insecure-registries则是对局域网内的ip192.168.xxx.xxx取消了限制。如果使用windows、mac的桌面版docker,在docker engine中编辑即可。

3.使用Nexus帮助管理私有仓库

详见Nexus 3,这里就不展开了。

七、docker数据卷

数据卷的特征:数据卷可以在容器之间共享和重用;对数据卷的修改会立马生效;对数据卷的更新不会影响镜像;数据卷默认会一直存在,即使容器被删除。可以说数据是灵魂,容器是皮囊。
数据卷使用时类似mount,镜像中被指定为挂载点的目录中的文件会复制到数据卷中(仅数据卷为空时会复制,若数据卷非空则数据是从数据卷向容器“同步”)。

1.数据卷的常用操作

docker volume create my-vol
docker volume ls
docker volume inspect my-vol
docker volume rm my-vol

以上命令分别为创建数据卷、查看数据卷、查看指定数据卷详情、删除数据卷。
docker run命令可使用--mount source=my-vol,target=/xxx/xxx来指定数据卷,并加载数据卷到容器的/xxx/xxx目录;或者直接docker run -v 数据卷:容器内路径。在挂载数据卷后,使用docker inspect 容器id,在Mounts键后可看到数据卷的相关信息。
在删除容器时使用docker container rm -v可同时删除数据卷;使用docker volume prune可删除所有无主的数据卷。

2.挂载宿主机目录作为数据卷

--mount可以挂载主机上存在的目录作为数据卷:

$ docker run -d -P \
    --name web \
    # -v /src/webapp:/usr/share/nginx/html \
    --mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly \
    nginx:alpine

主机上的目录必须存在,且必须是绝对路径。如果不添加readonly,则默认容器可以读写该目录(见下面对-v模式的补充)。
还可以挂载本地文件作为数据卷,比如要记录容器bash输入过的命令,可以这样写:

--mount type=bind,source=$HOME/.bash_history,target=/root/.bash_history

这里补充一点-v选项的模式(以下内容笔者没有实验过)。
docker run -v /www:/www这种默认模式下,宿主机和容器对目录内文件的增删改会同步;若挂载文件而非目录,那么宿主机和容器都可修改文件,但不会同步。
docker run -v /www:/www:ro可以指定数据卷只读,则挂载目录或文件时容器都只能读取不能修改。
docker run -v /www:/www:rw挂载目录时,双方对目录内文件的增删改和默认模式一样会同步;但挂载文件时不同,容器和宿主机的修改会互相同步,不同在于容器内不可删除该文件,且宿主机删除该文件时容器内不会同步。

最后,如果你是在windows上使用 WSL + docker-desktop ,注意,在wsl的发行版里把windows系统盘挂载到了/mnt下(/mnt/c,/mnt/d这种形式),但是你指定-v参数时写windows目录,比如不是用 -v /mnt/d/... ,而是写 -v D:\... 这样,因为docker-desktop在后台调用WSL,会帮你转换路径。

八、容器与网络

1.端口映射

使用-P-p可指定端口映射。-P是随机映射外部端口到容器开放的网络端口;-p则可以指定端口,其格式为:
ip:hostPort:containerPortip::containerPorthostPort:containerPort

docker port 容器id 容器端口可查看绑定的地址。

2.docker网络

创建新的网络:

docker network create -d bridge my-net

其中-d指定网络类型。
运行容器时加入网络使用--network指定网络:

docker run -it --rm --name busybox1 --network my-net busybox sh
(打开一个新终端)
docker run -it --rm --name busybox2 --network my-net busybox sh

再打开一个新终端,使用ping busybox1ping busybox2可以验证容器互联。
docker network connect 网络名 容器短id可以将已存在的容器加入网络,disconnect退网。

如果多个容器需要互相配合,推荐使用docker compose。学会docker compose,就算是摆脱docker新手阶段了。

3.配置DNS

更改容器里的/etc/resolv.conf文件可更新DNS配置。在/etc/docker/daemon.json中添加以下内容也可设置容器DNS:

{
  "dns" : [
    "114.114.114.114",
    "14.14.14.14"
  ]
}

4.高级网络配置

这一节属于进阶内容。详见高级网络配置

标签: 树莓派, docker

已有 3 条评论

  1. 一位不愿透漏姓名的张先生

    文章呢????

    1. 文章还在写,现在暂时放上半成品

添加新评论