[关闭]
@dungan 2021-09-30T04:28:41.000000Z 字数 28636 阅读 602

Docker

Docker 基础


一、 基础概念

docker 安装教程见 这里

win10 上 docker 安装后启动时可能会报Inter VIRTUALIZATION MUST BE ENABLED的错误,这个错误需要进入 BISO 中设置CPU。

1. 什么是 Docker

Docker 是一个开源的应用容器引擎,可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中,然后发布到任何流行的 Linux 机器上。

试想一下传统的 LAMP (Linux+Apache+MySQL+PHP)一旦安装后要迁移(例如从亚马逊云迁移到其他云):

往往需要对每个应用都进行重新部署和调试。这些琐碎而无趣的“体力活”,极大地降低了用户的工作效率。究其根源,是这些应用直接运行在底层操作系统上,无法保证同一份应用在不同的环境中行为一致。

而 Docker 提供了一种更为聪明的方式:

通过容器来打包应用、解藕应用和运行平台。这意味着迁移的时候,只需要在新的服务器上启动需要的容器就可以了,无论新旧服务器是否是同一类型的平台。这无疑将帮助我们节约大量的宝贵时间,并降低部署过程出现问题的风险。

2. 什么是镜像和容器

注册服务器(Registry)是存放仓库的具体的服务器,一个注册服务器(类比github)上可以有多个仓库,而每个仓库里可以有多个镜像。

例如,一个 Nginx 仓库中可以包含非常多不同版本的 Nginx 镜像,就像 git 仓库一样。

容器是基于镜像来创建的,你可以将镜像看做一个app安装包(例如weixin.apk),而在手机通过安装包生成的应用程序(例如微信) 看做容器

容器是从镜像创建的应用运行实例,Docker 容器类似于一个轻量级的沙箱, Docker 利用容器来运行和隔离应用。

对于手机上的应用程序,只要编译好安装包后,你可以将它安装到任何手机上(跨平台,可移植),例如华为手机,小米手机上等等。并且我们手机上的应用程序多种多样,有聊天的,有记笔记的,有购物的(镜像的多样性),并且他们相互之间不会影响彼此(容器间相互隔离)。

下载app安装的地方在手机上我们叫做应用商店,而在 docker 中我们称之为 镜像仓库,用来保存镜像。

根据所存储的镜像公开分享与否, Docker 仓库可以分为公开仓库( Public )和私有仓库( Private )两种形式。

概念 说明
Docker 镜像 Docker 镜像是用于创建 Docker 容器的模板,类似app安装包
Docker 容器 容器是独立运行的一个或一组应用,以及它们必需的运行环境是镜像运行时的实体,类似 app
Docker 主机 Docker 的宿主机
Docker 仓库   Docker 仓库用来保存镜像,类似手机应用商店,Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用

576507-docker1.png

3. 镜像加速

国内由于墙的原因可能从 DockerHub 拉取镜像有时失败,推荐将镜像源切换为国内

修改 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件):

  1. {"registry-mirrors":["https://registry.docker-cn.com"]}

重新启动服务:

  1. sudo systemctl daemon-reload
  2. sudo systemctl restart docker

最后查看下是否切换生效:

  1. $ docker info
  2. Registry Mirrors:
  3. https://registry.docker-cn.com/

4. Docker 与 VM

传统虚拟机是在硬件层面实现虚拟化,需要有额外的虚拟机管理应用和虚拟机操作系统层。Docker容器是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,因此更加轻量级。

2020-03-28_160123.png

二、Docker 镜像

1. 获取镜像

docker [image] pull 用来下载镜像。

  1. $ docker pull ubuntu:18.04
  2. 18.04: Pulling from library/ubuntu
  3. 5bed26d33875: Pull complete
  4. f11b29a9c730: Pull complete
  5. 930bda195c84: Pull complete
  6. 78bf9a5ad49e: Pull complete
  7. Digest: sha256:bec5a2727be7fff3d308193cfde3491f8fba1a2ba392b7546b43a051853a341d
  8. Status: Downloaded newer image for ubuntu:18.04

一般情况下,拉取镜像时无需指定仓库名,默认会从 Docker Hub 下载镜像,但如果从非官方下载,需要指定仓库名。

  1. $ docker pull hub.c.163.com/public/ubuntu:18.04

镜像下载后,就可以基于镜像启动一个容器了。

  1. $ docker run -it ubuntu:18.04 bash
  2. root@2d5771725cfb:/# echo hello world
  3. hello world
  4. root@2d5771725cfb:/#

2. 列出镜像列表

docker images 可以查看本地已有镜像信息。

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. ubuntu 18.04 4e5021d210f6 7 days ago 64.2MB
  4. mysql latest 2151acc12881 8 months ago 445MB
  5. hello-world latest fce289e99eb9 15 months ago 1.84kB
  6. ubuntu 15.10 9b9cb95443b5 3 years ago 137MB
  • REPOSITORY :来自于哪个仓库, 比如ubuntu 表示ubuntu 系列的基础镜像。
  • TAG:镜像的标签, 同一仓库源可以有多个 TAG,代表这个仓库源的不同个版本。
  • IMAGE ID:镜像的ID(唯一标识镜像),如果两个镜像的ID相同,说明它们实际上指向了同一个镜像,只是具有不同标签名称而已。
  • CREATED: 镜像创建时间。
  • SIZE: 镜像大小。

3. 为镜像设置标签

docker tag 为本地镜像添加一个新的标签。

  1. $ docker tag ubuntu:18.04 tclubuntu:local
  2. $ docker images
  3. REPOSITORY TAG IMAGE ID CREATED SIZE
  4. ubuntu 18.04 4e5021d210f6 7 days ago 64.2MB
  5. tclubuntu local 4e5021d210f6 7 days ago 64.2MB
  6. mysql latest 2151acc12881 8 months ago 445MB
  7. hello-world latest fce289e99eb9 15 months ago 1.84kB
  8. ubuntu 15.10 9b9cb95443b5 3 years ago 137MB

可以看到新添加的 tclubuntu 和 ubuntu IMAGE ID 是一样的。

4. 查找镜像

docker serach 命令用来搜索镜像。

  1. [www-data@test AciBonusLogic]$ docker search nginx
  2. NAME DESCRIPTION STARS OFFICIAL AUTOMATED
  3. nginx Official build of Nginx. 12889 [OK]
  4. bitnami/nginx Bitnami nginx Docker Image 80 [OK] ...
  • NAME:镜像仓库源的名称。
  • DESCRIPTION:镜像的描述。
  • OFFICIAL:是否docker官方发布。

5. 查看镜像信息

docker inspect 可以查看镜像的详细信息。

  1. [www-data@test AciBonusLogic]$ docker inspect nginx
  2. [
  3. {
  4. "Id": "sha256:4bb46517cac397bdb0bab6eba09b0e1f8e90ddd17cf99662997c3253531136f8",
  5. "RepoTags": [
  6. "nginx:latest"
  7. ],
  8. "RepoDigests": [
  9. "nginx@sha256:b0ad43f7ee5edbc0effbc14645ae7055e21bc1973aee5150745632a24a752661"
  10. ],
  11. "Parent": "",
  12. "Comment": "",
  13. ...

6. 查看镜像构建历史

docker history 可以用来查看镜像的构建历史。

镜像文件可能由多个层组成,那么怎么知道各个层的内容具体是什么呢?这时候可以使用 history 命令来列出各层的创建信息。

  1. $ docker history busybox
  2. IMAGE CREATED CREATED BY SIZE COMMENT
  3. 6d5fcfe5ff17 3 months ago /bin/sh -c #(nop) CMD ["sh"] 0B
  4. <missing> 3 months ago /bin/sh -c #(nop) ADD file:45da761e1c56c548b… 1.22MB

7. 删除和清理镜像

1、使用标签删除镜像

docker rmi image:tag 可以删除镜像。

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. tclubuntu local 4e5021d210f6 7 days ago 64.2MB
  4. ubuntu 18.04 4e5021d210f6 7 days ago 64.2MB
  5. mysql latest 2151acc12881 8 months ago 445MB
  6. hello-world latest fce289e99eb9 15 months ago 1.84kB
  7. ubuntu 15.10 9b9cb95443b5 3 years ago 137MB
  8. $ docker rmi tclubuntu:local
  9. Untagged: tclubuntu:local
  10. $ docker images
  11. REPOSITORY TAG IMAGE ID CREATED SIZE
  12. ubuntu 18.04 4e5021d210f6 7 days ago 64.2MB
  13. mysql latest 2151acc12881 8 months ago 445MB
  14. hello-world latest fce289e99eb9 15 months ago 1.84kB
  15. ubuntu 15.10 9b9cb95443b5 3 years ago 137MB

需要注意,当一个镜像如果有多个标签,那么删除的只是标签,如果镜像只有这一个标签,则这个镜像会被删除。

2、使用镜像 ID 来删除镜像

docker rmi image-id 会先尝试删除所有指向该镜像的标签,然后删除该镜像文件本身。

  1. $ docker rmi 9b9cb95443b5
  2. Error response from daemon: conflict: unable to delete 9b9cb95443b5 (must be forced) - image is being used by stopped container c018cee31e26

可以看到报错了,这是因为当有该镜像创建的容器正在运行时,镜像文件默认是无法被删除的.

所以我们必须先删除容器,然后再删除镜像,先查看基于该镜像的容器有哪些。

  1. $ docker ps -a
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 2d5771725cfb ubuntu:18.04 "bash" About an hour ago Up About an hour gallant_yonath
  4. e092cbc780dd mysql "docker-entrypoint.s…" 7 months ago Exited (1) 7 months ago recursing_kapitsa
  5. c018cee31e26 ubuntu:15.10 "/bin/echo 'Hello wo…" 7 months ago Exited (0) 7 months ago trusting_gauss
  6. b031f69b4ea5 hello-world "/hello" 7 months ago Exited (0) 7 months ago eloquent_euclid
  7. 951b5743cf9b hello-world "/hello" 7 months ago Exited (0) 7 months ago pensive_curran

然后删除该容器:

  1. $ docker rm c018cee31e26
  2. c018cee31e26

最后再次使用镜像 ID 来删除镜像:

  1. $ docker rmi 9b9cb95443b5
  2. Untagged: ubuntu:15.10
  3. Untagged: ubuntu@sha256:02521a2d079595241c6793b2044f02eecf294034f31d6e235ac4b2b54ffc41f3
  4. Deleted: sha256:9b9cb95443b5f846cd3c8cfa3f64e63b6ba68de2618a08875a119c81a8f96698
  5. Deleted: sha256:b616585738eaf78ff7d86c7526caf7c91a35bc4028ef63204e5bfee82f7494b5
  6. Deleted: sha256:dee1316f97acc7e1a5088b02fbc2b3078e0bfa038dd904b8072e2de5656e7bb8
  7. Deleted: sha256:e7d9ae1a69c53c9fefa1aef34348be5a5dbf2fe79e7dd647b3d4f4e927587ebc
  8. Deleted: sha256:f121afdbbd5dd49d4a88c402b1a1a4dca39c9ae75ed7f80a29ffd9739fc680a7

3、清理镜像
docker image prune

使用Docker一段时间后, 系统中可能会遗留一些临时的镜像文件, 以及一些没有被使用的镜像,可以通过 docker image prune命令来进行清理.

  1. $ docker image prune
  2. WARNING! This will remove all dangling images.
  3. Are you sure you want to continue? [y/N] yes
  4. Total reclaimed space: 0B

8. 创建镜像

1、基于已有容器
docker commit 可以将我们在某个容器中进行操作后,基于此时的容器创建一个新的镜像。

基于 ubuntu:18.04 启动一个容器。

  1. $ docker run -it ubuntu:18.04 /bin/bash
  2. # 4a5842225d08 是容器的 ID
  3. root@4a5842225d08:/# touch test
  4. root@4a5842225d08:/# exit

然后我们基于刚刚修改的容器(4a5842225d08) 创建一个新的镜像 :

  1. $ docker commit -m "add file" -a "tcl" 4a5842225d08 tcl/ubuntu
  2. sha256:fa3a6546a57ba7def6026360c457fd4599bf2158b40d65683a0ae1c8c7e2eded
  • -m:提交的描述信息
  • -a:指定镜像作者
  • 4a5842225d08:容器ID
  • tcl/ubuntu:指定要创建的目标镜像名

此时看下本地镜像列表,会发现新创建的镜像已经存在了:

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. tcl/ubuntu latest fa3a6546a57b 3 minutes ago 64.2MB
  4. ubuntu 18.04 4e5021d210f6 7 days ago 64.2MB
  5. mysql latest 2151acc12881 8 months ago 445MB
  6. hello-world latest fce289e99eb9 15 months ago 1.84kB

试着用我们的新镜像 tcl/ubuntu 来启动一个新容器:

  1. $ docker run -t -i tcl/ubuntu /bin/bash
  2. root@3abde2f0dc03:/# ls | grep test
  3. test
  4. root@3abde2f0dc03:/#

2、基于 Dockerfile
docker build -t 镜像名称 . 会构建 Dockerfile 中的命令,来为我们生成一个新的镜像,. 指的是 Dockerfile 存放的文件夹。

我们需要创建一个 Dockerfile 文件,其中包含一组指令来告诉 Docker 如何构建我们的镜像。

Dockerfile 中的每一个指令都会在镜像上创建一个新的层,每一个指令的前缀都必须是大写的。

  1. # FROM 指令指定使用哪个镜像源
  2. FROM busybox:latest
  3. # RUN 指令告诉docker 在镜像内执行命令,安装了什么
  4. RUN touch test2
  5. RUN useradd tcl
  6. EXPOSE 22
  7. EXPOSE 80
  8. CMD /usr/sbin/sshd -D

现在编译 Dockerfile 来生成镜像:

  1. $ docker build -t tcl/busybox .
  2. Sending build context to Docker daemon 260.8MB
  3. Step 1/2 : FROM busybox:latest
  4. ---> 6d5fcfe5ff17
  5. Step 2/2 : RUN touch test2
  6. ---> Using cache
  7. ---> f4a649b24963
  8. Successfully built f4a649b24963
  9. Successfully tagged tcl/busybox:latest

查看本地镜像列表,发现新创建的镜像已经存在了:

  1. $ docker images | grep tcl
  2. tcl/busybox latest f4a649b24963 4 minutes ago 1.22M

9. 镜像导入/导入

1、导出镜像
docker save -o 将指定镜像保存成 tar 归档文件。

  1. # 导出本地的ubuntu:l8.04 镜像为文件 ubuntu_18_04.tar
  2. $ docker save -o ubuntu_18_04.tar ubuntu:18.04

2、导入镜像
docker load -i 可以将我们刚刚创建的tar文件导入到本地镜像库。

  1. $ docker images
  2. REPOSITORY TAG IMAGE ID CREATED SIZE
  3. tcl/ubuntu latest fa3a6546a57b About an hour ago 64.2MB
  4. mysql latest 2151acc12881 8 months ago 445MB
  5. hello-world latest fce289e99eb9 15 months ago 1.84kB
  6. $ docker load -i ubuntu_18_04.tar
  7. Loaded image: ubuntu:18.04
  8. $ docker images
  9. REPOSITORY TAG IMAGE ID CREATED SIZE
  10. tcl/ubuntu latest fa3a6546a57b About an hour ago 64.2MB
  11. ubuntu 18.04 4e5021d210f6 7 days ago 64.2MB
  12. mysql latest 2151acc12881 8 months ago 445MB
  13. hello-world latest fce289e99eb9 15 months ago 1.84kB

10. 上传镜像到仓库

docker push image:tag 可以将镜像上传至仓库。

  1. $ docker push tcl/ubuntu:latest

默认上传到 Docker Hub 官方仓库(需要登录),之后登录信息会记录到本地 ~/.docker目录下。

三、Docker 容器

1. 创建容器

1、docker create 新建容器

  1. $ docker create -it ubuntu:18.04
  2. f29b0aeac0d7bc6da265b774b33eb732fb3c521227f98df76e272a74c4ebce03
  3. $ docker ps -a
  4. # f29b0aeac0d7 这个容器的状态是 Created
  5. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  6. f29b0aeac0d7 ubuntu:18.04 "/bin/bash" About a minute ago Created pedantic_mcclintock
  7. ...

可以看到创建的 f29b0aeac0d7 这个容器的状态是 Created,docker create 新建的容器处于停止状态,可以使用 docker start 来启动容器。

注:docker ps 可以查看本地运行的容器,选项 -a 查看包括未运行的所有容器。

2、docker start 启动容器

  1. $ docker start f2
  2. f2
  3. $ docker ps -a
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  5. f29b0aeac0d7 ubuntu:18.04 "/bin/bash" 7 minutes ago Up 4 seconds pedantic_mcclintock
  6. ...

启动后可以看到容器的状态由 Created 变为 up 了。

对于正在运行的容器,我们可以使用 docker restart 命令来重启。

3、docker run 创建并启动容器

docker run 命令等价于先执行 docker create 命令,再执行 docker start 命令。

  1. $ docker run ubuntu /bin/echo "Hello World"
  2. Hello World
  3. # 没有运行中的容器
  4. $ docker ps
  5. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

上面的命令输出 Hello World 之后容器会自动终止,还可以启动一个 bash 终端,允许用户进行交互。

  • -t:在新容器内指定一个伪终端或终端。
  • -i:允许你对容器内的标准输入 (STDIN) 进行交互。
  1. $ docker run -i -t ubuntu /bin/bash
  2. root@815899ca5d50:/# echo Hello World
  3. Hello World
  4. root@815899ca5d50:/# exit
  5. exit
  6. # exit 退出容器后,没有运行中的容器了
  7. $ docker ps
  8. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

最后可以通过 exit 命令退出 bash进程之后,容器也会自动终止,如果你不想容器启动后退出,那么可以启动时指定以守护进程的方式运行容器。

4、以守护进程的方式运行容器

-d :以守护态( Daemonized )形式运行,使用该参数启动容器后会进入后台,用户无法看到容器中的信息,也无法进行操作 。

  1. $ docker run -d -i -t ubuntu /bin/bash
  2. 24d4e15015966b2b95d3537687428a7703efb8596ba185e26623693abf6814df
  3. # 可以看到启动在后台运行了
  4. $ docker ps
  5. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  6. 24d4e1501596 ubuntu "/bin/bash" 13 seconds ago Up 11 seconds peaceful_joliot
  7. # 进入容器
  8. $ docker attach 24d4e1
  9. root@24d4e1501596:/# pwd
  10. /
  11. root@24d4e1501596:/#

5、docker logs 查询容器运行日志

-f : 类似 tail -f 输出容器内部的标准输出。

下面我们开两个终端 。

终端 1 打印并持续监控日志:

  1. # 终端1
  2. $ docker logs -f 24d4e
  3. root@24d4e1501596:/# pwd
  4. /
  5. root@24d4e1501596:/# exit
  6. exit

终端 2 输出一些内容:

  1. # 终端 2
  2. $ docker attach 24d4e
  3. root@24d4e1501596:/# echo 1223
  4. 1223
  5. root@24d4e1501596:/#

接着我们观察终端 1 输出了终端 2 的操作日志:

  1. # 终端 1
  2. $ docker logs -f 24d4e
  3. root@24d4e1501596:/# pwd
  4. /
  5. root@24d4e1501596:/# exit
  6. exit
  7. root@24d4e1501596:/# echo 1223
  8. 1223

2. 停止容器

1、docker pause 暂停容器

  1. $ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  3. 24d4e1501596 ubuntu "/bin/bash" 2 hours ago Up 11 minutes peaceful_joliot
  4. $ docker pause 24d4e
  5. 24d4e
  6. $ docker ps
  7. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  8. 24d4e1501596 ubuntu "/bin/bash" 2 hours ago Up 12 minutes (Paused) peaceful_joliot

可以看到暂停容器后,状态由 Up 变为了 Up (Paused) 了。

对处于暂停(pause)状态的容器,可以使用 unpause 来恢复到运行状态。

  1. $ docker unpause 24d4e
  2. 24d4e
  3. $ docker ps
  4. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  5. 24d4e1501596 ubuntu "/bin/bash" 2 hours ago Up 16 minutes peaceful_joliot

2、docker stop 终止容器

  1. $ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  3. 24d4e1501596 ubuntu "/bin/bash" 2 hours ago Up 16 minutes peaceful_joliot
  4. $ docker stop 24d4e
  5. 24d4e
  6. $ docker ps -a
  7. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  8. 24d4e1501596 ubuntu "/bin/bash" 2 hours ago Exited (0) 25 seconds ago peaceful_joliot

对于终止的容器,可以使用 docker start 再次启动;而运行 docker container prune 命令会自动清除掉所有处于停止状态的容器。

3. 进入运行中的容器

1、docker attach 容器ID

  1. $ docker attach 24d4e
  2. root@24d4e1501596:/#

使用 attach 命令有时候并不方便,当多个窗口同时 attach 到同一个容器的时候,所有窗口都会同步显示;当某个窗口因命令阻塞时,其他窗口也无法执行操作了。

2、docker exec 容器ID

docker exec 是更为推荐的一种进入容器的方式,它会打开一个新的 bash 终端, 在不影响容器内其他应用的前提下,用户可以与容器进行交互。

  1. $ docker exec -it 24d /bin/bash
  2. root@24d4e1501596:/#

4. 删除容器

docker rm 容器ID

  1. $ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  3. 62bc0d820770 ubuntu "/bin/bash" 11 minutes ago Up 6 minutes boring_haibt
  4. 24d4e1501596 ubuntu "/bin/bash" 3 hours ago Up 5 minutes peaceful_joliot
  5. $ docker rm 62bc
  6. # 报错:无法删除运行中的容器
  7. Error response from daemon: You cannot remove a running container 62bc0d82077055070f4af232e40cb595a289af0035b2623388c60766d998eafe. Stop the container before attempting removal or force remove
  8. $ docker stop 62bc
  9. 62bc
  10. $ docker rm 62bc
  11. 62bc
  12. $ docker ps
  13. CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
  14. 24d4e1501596 ubuntu "/bin/bash" 3 hours ago Up 6 minutes peaceful_joliot

默认情况下,docker rm 命令只能删除已经处于终止或退出状态的容器,并不能删除还处于运行状态的容器。

5. 导入和导出容器

某些时候,需要将容器从一个系统迁移到另外一个系统,此时可以使用 Docker 的导人和导出功能,这也是 Docker 自身提供的一个重要特性。

1、docker export -o 导出文件名 容器ID

导出容器是指,导出一个已经创建的容器到一个文件,不管此时这个容器是否处于运行状态。

  1. $ docker export -o ubuntu_run.tar 24d4e
  2. # 也可以通过重定向的方式导出
  3. $ docker export 24d4e > ubuntu_run.tar

2、docker import 导入容器

import 命令本质是导入一个容器快照(tar文件)到本地镜像库。

  1. $ docker import ubuntu_run.tar ubuntu_bak:v1.0
  2. sha256:bf6fc6001204dee561a2b1aedc4df033a5ea47b765331a0e6388ef407a6afbbe
  3. E:\>docker images
  4. REPOSITORY TAG IMAGE ID CREATED SIZE
  5. ubuntu_bak v1.0 bf6fc6001204 11 seconds ago 64.2MB
  6. tcl/ubuntu latest fa3a6546a57b 46 hours ago 64.2MB
  7. ubuntu 18.04 4e5021d210f6 9 days ago 64.2MB
  8. ...

docker load 命令也可以用来导入镜像存储文件到本地镜像库,而容器快照与镜像存储文件的区别在于:

容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积更大;此外,从容器快照文件导人时可以重新指定标签等元数据信息。

6. 查看容器

1、docker container inspect 容器ID 查看容器详情

  1. $ docker container inspect 24d
  2. [
  3. {
  4. "Id": "24d4e15015966b2b95d3537687428a7703efb8596ba185e26623693abf6814df",
  5. "Created": "2020-03-30T03:56:51.7247327Z",
  6. "Path": "/bin/bash",
  7. "Args": [],
  8. "State": {
  9. "Status": "running",
  10. "Running": true,
  11. "Paused": false,
  12. "Restarting": false,
  13. "OOMKilled": false,
  14. "Dead": false,
  15. ...

2、docker top 容器ID 查看容器内的进程

类似于 Linux 系统中的 top 命令,会打印出容器内的进程信息,包括 PID 用户、时间、命令等。

  1. $ docker top 24d
  2. PID USER TIME COMMAND
  3. 5259 root 0:00 /bin/bash

3、docker stats 容器ID 查看统计信息

docker stats 命令会显示 CPU 、内存、存储、网络等使用情况的统计信息。

  1. $ docker container stats 24d

7. 其他容器命令

1、docker cp 宿主机文件 容器ID:容器文件 在容器和主机之间复制文件

复制宿主机文件到容器:

  1. $ docker cp ubuntu_run.tar 24d:/tmp/
  2. $ docker attach 24d
  3. root@24d4e1501596:/# ll /tmp/
  4. total 65032
  5. drwxrwxrwt 1 root root 4096 Mar 30 08:53 ./
  6. drwxr-xr-x 1 root root 4096 Mar 30 03:56 ../
  7. -rw-r--r-- 1 root root 0 Mar 30 08:53 test.txt
  8. -rwxr-xr-x 1 root root 66583040 Mar 30 08:19 ubuntu_run.tar*

复制容器文件到宿主机:

  1. $ docker cp 24d:/tmp/test.txt .
  2. $
  3. $ ls | grep test.txt
  4. test.txt

2、docker diff 容器ID 看容器内文件系统的变更

  1. $ docker diff 24d
  2. C /tmp
  3. A /tmp/ubuntu_run.tar
  4. A /tmp/test.txt
  5. C /root
  6. A /root/.bash_history

3、docker port 容器ID 查看端口映射

  1. $ docker container port test
  2. 9000/tcp - > o.o o 0:9000

4、docker update 更新配置
update 命令可以更新容器的一些运行时配置,主要是一些资源限制份额。

  1. # 限制总配额为 1 秒,容器 test 所占用时间为 10%
  2. $ docker update --cpu-quota 1000000 test
  3. test
  4. $ docker update --cpu-period 100000 test
  5. test

四、Docker 仓库

1. 下载第三方镜像

docker 默认从 Docker Hub 中获取镜像,但如果你想从一些第三方下载镜像,则需要完整的写上镜像地址。
类似下载 git仓库 一样,如果需要下载第三方进行,完整的 docker 镜像地址格式应是下面这种。

  1. <host>/<namespace>/<repository>:<tag>

例如下载 git 仓库:

  1. git clone https://github.com/Node1650665999/DesignPatterns-php.git

所以当你需要从第三方下载镜像时,正确的方式应是这样:

  1. # docker
  2. docker pull index tenxcloud.com/docker_library/node:latest

2. 私有仓库搭建

1、使用 registry 镜像创建私有仓库

  1. docker run -d -p 5001:5000 -v /data:/var/lib/registry registry:2

在本地启动一个私有仓库服务,监听端口为5001;默认情况下,仓库会被创建在容器的/var/lib/registry 目录下。可以通过 -v 参数来将镜像文件存放在本地的指定路径,这里将上传的镜像放到 /data 目录下。

仓库创建后我们去 /data 目录下查看,发现生成了docker目录:

  1. $ ll /data
  2. 总用量 28
  3. ...
  4. drwxr-xr-x 3 root root 4096 4 4 16:29 docker/
  5. ...

2、上传镜像到私有仓库

提交到私有仓库的镜像,必须要按如下格式进行创建:

  1. docker tag IMAGE:[TAG] [私有仓库HOST/][USERNAME/]NAME[:TAG]
  • 私有仓库HOST:指的是刚刚启动的地址,也是就 0.0.0.0:5001。
  • USERNAME:指的是你提交上去的镜像所在空间,对于我们刚映射的仓库目录,你镜像的在本地存储路径就是 /data/docker/registry/v2/repositories/USERNAME。
  • NAME:TAG : 指的是提交上去的镜像在私有仓库的名称及标签。

现在创建镜像:

  1. docker tag ubuntu:latest 127.0.0.1:5001/tcl/myubuntu:v1

然后提交到私有仓库:

  1. $ docker push 127.0.0.1:5001/tcl/myubuntu:v1
  2. The push refers to repository [127.0.0.1:5001/tcl/myubuntu]
  3. 16542a8fc3be: Layer already exists
  4. 6597da2e2e52: Layer already exists
  5. 977183d4e999: Layer already exists
  6. c8be1b8f4d60: Layer already exists
  7. v1: digest: sha256:e5dd9dbb37df5b731a6688fa49f4003359f6f126958c9c928f937bec69836320 size: 1152

然后我们从私有仓库尝试下载该镜像:

  1. $ docker rmi 127.0.0.1:5001/tcl/myubuntu:v1
  2. ...
  3. $ docker pull 127.0.0.1:5001/tcl/myubuntu:v1
  4. v1: Pulling from tcl/myubuntu
  5. Digest: sha256:e5dd9dbb37df5b731a6688fa49f4003359f6f126958c9c928f937bec69836320
  6. Status: Downloaded newer image for 127.0.0.1:5001/tcl/myubuntu:v1
  7. 127.0.0.1:5001/tcl/myubuntu:v1
  8. $ docker images | grep myubuntu
  9. 127.0.0.1:5001/tcl/myubuntu v1 4e5021d210f6 2 weeks ago 64.2MB

最后我们运行下下载下来的这个镜像,发现是可用的:

  1. tcl@tcl:~$ docker run -it 127.0.0.1:5001/tcl/myubuntu:v1 /bin/bash
  2. root@e265e18c0e63:/#

五、Docker 数据管理

通常情况下运行环境和数据是进行分开管理的,以便数据迁移和持久化,或者是在多个容器之间进行数据共享,这必然涉及容器的数据管理操作。

docker 中的数据管理主要有如下两种方式:

  • 数据卷 ( Data Volumes ) : 容器内数据直接映射到本地主机环境。
  • 数据卷容器( Data Volume Containers ) : 使用特定容器维护数据卷。

1. 数据卷

数据卷 是一个可供容器使用的特殊目录,它将主机操作系统目录直接映射进容器,类似于 Linux 中的 mount 行为。

在使用 docker run 命令的时候,可以使用 -mount 选项来使用数据卷.

-mount 选项支持三种类型的数据卷 :

  • volume : 普通数据卷,映射到主机 /var/lib/docker/volumes 路径下.
  • bind : 绑定数据卷,映射到主机指定路径下.
  • mpfs :临时数据卷,只存在于内存中.
  1. //绑定本地目录 /webapp 到容器目录 /opt/python
  2. docker run webapp -d -P --name web -mount type=bind,source=/webapp,destination=/opt/python app.py

bind 类型等同于使用 -v 来映射主机目录:

  1. docker run -d -P --name web - v /webapp:/opt/webapp training/webapp python app.py

Docker 挂载数据卷的默认权限是读写(rw),用户也可以通过 ro 指定为只读 :

  1. $ docker run -d -P --name web -v /webapp:/opt/webapp:ro training/webapp python app.py

为了避免出现不必要的错误,如果要挂载文件,推荐是方式是挂载文件所在的目录到容器而非文件本身。但如果非要挂载单一文件需注意:host 中的源文件必须要存在,不然会当作一个新目录挂载给容器。

2. 数据卷容器

数据卷容器用来在多个容器间共享数据,数据卷容器也是一个容器,但是它的目的是专门提供数据卷给其他容器挂载。

创建一个数据卷容器 data-center 并在其中创建一个数据卷 /dbdata。

  1. $ docker run -it -v /dbdata --name data-center ubuntu
  2. root@05b69d2ca62e:/# ls | grep dbdata
  3. dbdata
  4. root@05b69d2ca62e:/#

然后在其他容器中通过 --volumes-from 来挂载 data-center 容器中的数据卷。

  1. // 容器 db1 和 db2 都挂载 data-center 容器的数据卷 /dbdata
  2. docker run -it --volumes-from data-center --name db1 ubuntu
  3. docker run -it --volumes-from data-center --name db2 ubuntu

注意:使用 --volumes-from 被挂载的数据卷容器自身并不需要保持在运行状态。

此时, 容器 dbl 和 db2 都挂载同一个数据卷到相同的 /dbdata 目录,三个容器任何一方在该目录下的写人,其他容器都可以看到。

例如在 data-center 容器中我们创建一个文件:

  1. root@05b69d2ca62e:/# touch /dbdata/test
  2. root@05b69d2ca62e:/#
  3. root@05b69d2ca62e:/# ll /dbdata/
  4. -rw-r--r-- 1 root root 0 Apr 4 13:38 test

然后在 db1 容器中查看:

  1. $ docker exec -it db1 /bin/bash
  2. root@ef97998ef46e:/# ll | grep dbdata
  3. drwxr-xr-x 2 root root 4096 Apr 4 13:38 dbdata/
  4. root@ef97998ef46e:/#

还可以多次使用 --volumes-from 参数来从多个容器挂载多个数据卷,还可以从其他已经挂载了容器卷的容器来挂载数据卷 : 

  1. docker run -d --name db3 --volumes-from dbl ubuntu

如果要删除一个数据卷,必须在删除最后一个还挂载着它的容器时显式使用 docker rm -v 命令来指定同时删除关联的容器。

3. 利用数据卷容器来迁移数据

1、备份

  1. $ docker run --volumes-from data-center -v $(pwd):/backup --name worker ubuntu tar cvf /backup/backup.tar /dbdata
  2. tar: Removing leading `/' from member names
  3. /dbdata/
  4. /dbdata/test
  5. $ ls | grep backup
  6. backup.tar

这个命令说明如下:

首先利用 ubuntu 镜像创建了 一个容器 worker 。
使用 --volumes-from dbdata 参数来让 worker 容器挂载 data-center 容器的数据卷(即 dbdata 数据卷);
使用 -v $(pwd):/backup 来挂载本地的当前目录到 worker 容器的 /backup 目录;
然后worker 容器启动后,使用 tar cvf /backup/backup.tar /dbdata 命令将 /dbdata 下内容备份为容器内的 /backup/backup.tar ,即宿主主机当前目录下的 backup.tar。

2、恢复

首先创建一个带有数据卷的容器 data-center:

  1. $ docker run -v /dbdata --name data-center ubuntu /bin/bash

然后创建另一个新的容器,挂载 data-center 的容器,然后解压备份文件,这样数据就会恢复到所挂载的容器卷中:

  1. $ docker run --volumes-from data-center -v $(pwd):/backup busybox tar xvf /backup/backup.tar

六、端口映射与容器互联

1. 端口映射

端口映射指的是映射容器内应用的服务端口到本地宿主机,这样通过宿主机就能访问容器内部。

可以通过-P或-p参数来指定端口映射。 当使用 -P(大写的)标记时, Docker 会随机映射一个 49000-49900 的端口到内部容器开放的网络端口。

  1. $ docker run -d -P nginx
  2. $ docker ps
  3. CONTAINER ID IMAGE COMMAND  CREATED  STATUS  PORTS NAMES
  4. ee861ee59e78 nginx "nginx -g 'daemon of…" 5 minutes ago Up 5 minutes 0.0.0.0:1024->80/tcp zen_meitner

可以看到本机 1024 端口被映射到了容器 80 端口。

如果你不想自动分配端口,那么可以使用-p(小写)来手动进行端口映射,支持的格式有:

  • HostIP:HostPort:ContainerPort: 意为绑定指定地址指定端口到容器端口。
  • HostIP::ContainerPort:意为绑定某个地址任意端口到容器端口。
  • HostPort:ContainerPort: 意为绑定本地所有地址的端口到容器端口。
  1. docker run -d -p 127.0.0.1:5000:5000 training/webapp python app.py
  2. docker run -d -p 127.0.0.1::5000 training/webapp python app.py
  3. docker run -d -p 5000:5000 -p 3000:80 training/webapp python app.py

容器的端口绑定情况可以使用 docker port 查看:

  1. $ docker port zen_meitner
  2. 80/tcp -> 0.0.0.0:1024

2. 容器互联

容器的互联(link)是一种让多个容器中的应用进行快速交互的方式。它会在源和接收容器之间创建连接关系,接收容器可以通过容器名快速访问到源容器,而不用指定具体的IP地址

使用 --link 参数可以实现容器间的互联。

先创建一个数据库容器,我们使用 --name 参数对容器进行命名:

  1. $ docker run -d --name db training/postgres

然后创建一个web容器,并将它连接到 db 容器:

  1. $ docker run -d -P --name web --link db:db training/webapp python app.py

--link参数的格式为: --link name:alias, 其中 name 是要链接的容器的名称,alias是别名。

至此,db容器和web容器建立互联关系,使用 docker ps 查看如下:

  1. $ docker ps
  2. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
  3. 1044806a4cf2 training/webapp "/bin/bash" 2 minutes ago Up 2 minutes 5000/tcp web
  4. d8cb8ac7b916 training/postgres "su postgres -c '/us…" 12 hours ago Up 12 hours 5432/tcp db

我们查看下 web 容器的 hosts 文件,发现其配置了 db 容器的连接信息:

  1. root@1044806a4cf2:/opt/webapp# cat /etc/host
  2. host.conf hostname hosts
  3. root@1044806a4cf2:/opt/webapp# cat /etc/hosts
  4. 127.0.0.1 localhost
  5. 172.17.0.3 db d8cb8ac7b916
  6. 172.17.0.4 1044806a4cf2

web 容器用自己的 id 作为默认主机名, 而 db 容器我们则可以通过容器名或id来连接,我们试着 ping 一下 db 容器。

  1. root@1044806a4cf2:/opt/webapp# ping d8cb8ac7b916
  2. PING db (172.17.0.3) 56(84) bytes of data.
  3. 64 bytes from db (172.17.0.3): icmp_seq=1 ttl=64 time=0.229 ms
  4. 64 bytes from db (172.17.0.3): icmp_seq=2 ttl=64 time=0.131 ms
  5. 64 bytes from db (172.17.0.3): icmp_seq=3 ttl=64 time=0.148 ms
  6. 64 bytes from db (172.17.0.3): icmp_seq=4 ttl=64 time=0.161 ms
  7. ...

所以容器互联的好处在于在两个互联的容器之间创建了一个虚拟通道, 而且不用映射它们的端口到宿主机上,在启动 db 容器的时候并没有使用-p和-P标记, 从而避免了暴露数据库服务端口到外部网络上。

七、Dockerfile

Dockerfile是一个包含用于组合镜像的命令的文本文档。可以使用在命令行中调用任何命令。 Docker通过读取Dockerfile中的指令自动生成映像。

  1. docker build -f /path/to/a/Dockerfile

1. Dockerfile 基本结构

Dockerfile 主体内容分为四部分:

  • 基础镜像信息
  • 维护者信息
  • 镜像操作指令
  • 容器启动时执行指令
  1. #=================基础镜像信息==========================
  2. FROM ubuntu:xeniel
  3. #=================维护者信息=============================
  4. LABEL maintainer docker user<docker user@email.com>
  5. #==================镜像操作指令==========================
  6. RUN apt-get update && apt-get install -y nginx
  7. RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
  8. #==================容器启动时执行指令====================
  9. CMD /usr/sbin/nginx

2. Dockerfile 指令说明

Dockerfile 中指令的一般格式为 INSTRUCTION arguments, 包括配置指令(配置镜像信息)和操作指令(具体执行操作)。

2020-04-24_163020.png

配置指令

1、ARG
定义创建镜像过程中使用的变量,在执行docker build 时,可以通过 -build-arg[=] 来为变量赋值。当镜像编译成
功后,ARG 指定的变量将不再存在(ENV 指定的变量将在镜像中保留)。

  1. 格式:
  2. ARG <name>[=<default value>]
  3. 示例:
  4. ARG site
  5. ARG build_user=www

2、FROM
指定基础镜像,必须为Dockerfile的第一个命令。

  1. 格式:
  2.   FROM <image>
  3.   FROM <image>:<tag>
  4.   FROM <image>@<digest>
  5. 示例:
  6.   ARG VERSION=9.3
  7. FROM debian:${VERSION}

3、LABEL
LABEL 指令可以为生成的镜像添加元数据标签信息。这些信息可以用来辅助过滤出特定镜像。使用LABEL指定元数据时,一条 LABEL 指定可以指定一或多条元数据,指定多条元数据时不同元数据之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像。

  1. 格式:
  2. LABEL <key>=<value> <key>=<value> <key>=<value> ...
  3. 示例:
  4.   LABEL version="1.0" description="这是一个Web服务器"

4、EXPOSE
指定与外界交互的端口。

  1. 格式:
  2. EXPOSE <port> [<port/<protocol>... ]
  3. 示例:
  4. EXPOSE 22 80 8443
  5. EXPOSE 11211/tcp 11211/udp

注意:

该指令只是起到声明作用,并不会自动完成端口映射,如果要映射端口出来, 在启动容器时可以使用-P 参数(Docker 主机会自动分配一个宿主机的临时端口)或 -p 参数(具体指定所映射的本地端口)来导出端口。

5、ENV
指定容器运行时的环境变量,在镜像生成过程中会被后续RUN指令使用,在镜像启动的容器中也会存在。指令指定的环境变量在运行时可以被覆盖掉, 如 docker run --env <key>=<value> image

  1. 格式:
  2. ENV <key> <value>
  3. ENV <key>=<value>
  4. 示例:
  5. ENV APP_VERSION=l.0.0
  6. ENV APP_HOME=/usr/local/app
  7. ENV PATH $PATH:/usr/local/bin

6、ENTRYPOINT
指定镜像的默认入口命令, 该入口命令会在启动容器时作为根命令执行,docker run 命令中指定的任何参数, 都会被当做参数再次传递给 ENTRYPOINT。运行容器时可以被 docker run --entrypoint 覆盖掉。

注意:每个Dockerfile 中只能有一个 ENTRYPOINT, 当指定多个时, 只有最后一个起效。

  1. 格式:
  2. ENTRYPOINT ["executable", "paraml ", "param2"]: exec 格式.
  3. ENTRYPOINT command param1 param2: shell 格式.
  4. 示例:
  5. FROM ubuntu
  6. ENTRYPOINT ["top", "-b"]

与CMD比较说明如下:

  • 相同点:只能写一条,如果写了多条,那么只有最后一条生效;并且两者都是容器启动时才运行,运行时机相同。
  • 不同点:ENTRYPOINT 不会被运行的 command 覆盖,而 CMD 则会被覆盖。

如果我们在Dockerfile种同时写了 ENTRYPOINT 和 CMD,并且CMD指令不是一个完整的可执行命令,那么CMD指定的内容将会作为ENTRYPOINT的参数:

  1. FROM ubuntu
  2. ENTRYPOINT ["top", "-b"]
  3. CMD ["-c"]
  4. # 最终的命令会是 top -b -c

如果我们同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效:

  1. FROM ubuntu
  2. ENTRYPOINT ["top", "-b"]
  3. CMD ls -al
  4. # 那么将执行ls -al ,top -b不会执行

ENTRYPOINT 在 exec 和 shell 下的区别:

  • exec:可通过 CMD 提供额外的参数,ENTRYPOINT 中的参数始终会被使用,而 CMD 的额外参数可以在容器启动时动态替换掉。
  • shell:ENTRYPOINT 的 Shell 格式会忽略任何 CMD 或 docker run 提供的参数。

更多关于 ENTRYPOINT 和 CMD 的区别见 这里

7、VOLUME
通过 VOLUME 指令可以在镜像中创建挂载点,这样只要通过该镜像创建的容器都有了挂载点(类似linux 下的 mount 机制)。

  1. 格式:
  2. VOLUME ["/path/to/dir"]
  3. 示例:
  4. VOLUME ["/data"]

VOLUME 只是创建了容器的挂载点,并没有指定关联的主机目录。对应的主机目录是自动创建的, 位于目录 /var/lib/docker/volumes 下,docker 会自动绑定这个主机目录。

8、USER
指定运行容器时的用户名或urn,,后续的RUN等指令也会使用指定的用户身份,使用USER指定用户后,Dockerfile中其后的命令 RUN、CMD、ENTRYPOINT 都将使用该用户。运行容器时可以被 docker run -u 覆盖掉。

  1. 格式:
  2.   USER user
  3.   USER user:group
  4.   USER uid
  5.   USER uid:gid
  6.   USER user:gid
  7.   USER uid:group
  8. 示例:
  9.   USER www

当服务不需要管理员权限时-,可以通过该命令指定运行用户, 并且可以在Dockerfile 中创建所需要的用户:

  1. RUN groupadd -r postgres && useradd --no-log-init -r -g postgres postgres

9、WORKDIR
类似于cd命令,为后续的RUN,CMD,ENTRYPOINT,ADD,COPY 等指令都会在该目录下执行。运行容器时可以被 docker run -w 覆盖掉。

  1. 格式:
  2. WORKDIR /path/to/workdir
  3. 示例:
  4. WORKDIR /a (这时工作目录为/a)
  5. WORKDIR b (这时工作目录为/a/b)
  6. WORKDIR c (这时工作目录为/a/b/c)

10、ONBUILD
指定当基于所生成镜像创建子镜像时,自动执行的操作指令。ONBUILD 指令在创建专门用于自动编译、检查等操作的基础镜像时,十分有用。

  1. 格式:
  2.   ONBUILD [INSTRUCTION]
  3. 示例:
  4.   ONBUILD ADD . /app/src
  5.   ONBUILD RUN /usr/local/bin/python-build --dir /app/src

假设当前构建的镜像我们称其为 ParentImage:

  1. # Dockerfile ParentImage
  2. ONBUILD ADD . /app/src
  3. ONBUILD RUN /usr/local/bin/python-build --dir /app/src

当有一个子镜像 ChildImage 使用 from 指令继承它时,会首先执行 ParentImage 中配置的ONBUI LD 指令:

  1. #Dockerfile Childimage
  2. FROM Parentimage

11、STOPSIGNAL
指定所创建镜像启动的容器接收退出的信号值。

  1. STOPSIGNAL signal

12、HEALTHCHECK
配置所启动容器如何进行健康检查(如何判断健康与否)。

  1. 格式:
  2. HEALTHCHECK [OPTIONS] CMD command :根据所执行命令返回值是否为 0 来判断;
  3. HEALTHCHECK NONE :禁止基础镜像中的健康检查。
  4. OPTIONS 支持如下参数:
  5. -interval = DURATION (default:30s): 过多久检查一次;
  6. -timeout = DURATION (default:30s): 每次检查等待结果的超时;
  7. -retries = N (default:3):如果失败了,重试几次才最终确定失败。

13、SHELL
指定其他命令使用shell 时的默认shell 类型。

  1. 格式:
  2. SHELL [executable, parameters]
  3. 示例:
  4. SHELL ["/bin/bash", "-c"]

操作指令

1、 RUN
构建镜像时执行的命令。

注意:每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像层,过多无意义的层,会造成镜像膨胀过大,因此建议将多条 RUN 指令合成一条。

  1. 格式:
  2. RUN <command> : shell 格式.
  3. RUN ["command", "param1", "param2"] : exec 格式.
  4. 示例
  5. RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
  6. RUN ["/bin/bash", "-c", "echo hello"]

execshell 的区别:

  • shell 格式:底层会调用 /bin/sh -c command 来包装下命令,而不是直接运行命令。
  • exec 格式:直接运行 command,不会被 bash -c 包装。

CMD 和 ENTRYPOINT 推荐使用 Exec 格式,因为指令可读性更强,更容易理解。RUN 则两种格式都可以。

将多条 RUN 指令合成一条:

  1. FROM centos
  2. RUN yum install wget
  3. RUN wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz"
  4. RUN tar -xvf redis.tar.gz
  5. # 以上执行会创建 3 层镜像,可以使用 && 符号连接多条命令,这样只会创建 1 层镜像:
  6. FROM centos
  7. RUN yum install wget \
  8. && wget -O redis.tar.gz "http://download.redis.io/releases/redis-5.0.3.tar.gz" \
  9. && tar -xvf redis.tar.gz

2、CMD
启动容器时默认执行的命令。

注意:每个Dockerfile 只能有一条CMD 命令,如果指定了多条命令,只有最后一条会被执行。
如果用户启动容器时候手动指定了运行的命令(作为run 命令的参数),则会覆盖掉 CMD 指定的命令。

  1. 格式:
  2. CMD ["executable","param1","param2"] : exec 方式
  3. CMD ["param1","param2"] : 提供给 ENTRYPOINT 的默认参数,此时 ENTRYPOINT 必须使用 Exec 格式
  4. CMD command param1 param2 : shell 方式
  5. 示例:
  6. CMD echo "This is a test." | wc -
  7. CMD ["/usr/bin/wc","--help"]

如果你在启动容器时手动运行了 echo "This is a test." | wc - 则会覆盖如上的 CMD 指令:

  1. docker run ubuntu /bin/echo "This is a test." | wc -

3、ADD
将本地文件添加到容器中,tar类型文件会自动解压,src 还可以是一个 URL,此时的 ADD 相当于 wget。

注意:如果 src 是相对路径,那么这个目录相对的是 Dockerfile 所在的目录。同理,如果 dest 是相对路径,则相对的是 WORKDIR。

  1. 格式:
  2. ADD <src> <dest>
  3. ADD ["<src>",... "<dest>"] 路径包含空格的话,使用这种方式
  4. 示例:
  5. ADD hom* /mydir/ # 添加所有以"hom"开头的文件
  6. ADD hom?.txt /mydir/ # ? 替代一个单字符,例如:"home.txt"
  7. ADD test relativeDir/ # 添加 "test" 到 `WORKDIR`/relativeDir/
  8. ADD test /absoluteDir/ # 添加 "test" 到 /absoluteDir/

4、COPY
功能类似 ADD,但是是不会自动解压文件,也不能访问网络资源,当使用本地目录为源目录时,推荐使用COPY。

  1. COPY <src> <dest

在 COPY 和 ADD 指令中选择的时候,可以遵循这样的原则:

所有的文件复制均使用 COPY 指令,仅在需要自动解压缩的场合使用 ADD。

3. 创建镜像

编写完成 Dockerfile 之后,可以通过 docker build 命令来创建镜像了。

  1. 格式:
  2. docker build [选项] <上下文路径/URL/->
  3. 示例:
  4. docker build https://github.com/tcl/xxx.git # 从git仓库构建
  5. docker build http://server/context.tar.gz # 用给定的压缩包构建

该命令将读取指定路径下(包括子目录)的 Dockerfile, 并将该路径下所有数据作为上下文(Context)发送给 Docker 服务端,可以将 Dockerfile 所在的目录看作是上下文目录。

注意:

a :如果上下文过大,会导致发送大量数据给服务端,延缓创建过程。因此除非是生成镜像所必需的文件,不然不要放到上下文路径下。如果使用非上下文路径下的Dockerfile ,可以通过 -f 选项来指定其路径。

b:COPY 这类指令中的源文件的路径都是相对路径。 COPY ../package.json /app 或者 COPY /opt/xxxx /app 这类指令无法工作的原因,是因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。

当然为了避免发送无用的上下文数据到 Docker 服务端,我们可以使用 .dockerignore 文件来忽略不需要的数据。

  1. //dockerignore 文件中可以定义忽略模式
  2. */temp*
  3. */*/t emp*
  4. tmp?
  5. -*
  6. Dockerfile
  7. !README.md
  • *表示任意多个字符。
  • ?代表单个字符。
  • !表示不匹配(即不忽略指定的路径或文件)。

选项 -t 可以生成镜像的标签信息,该选项可以重复使用多次为镜像一次添加多个名称。

  1. # 这里 Dockerfile 所在的上下文路径是 /tmp/docker_builder/
  2. docker build -t nginx:test /tmp/docker_builder/

选择父镜像

通常情况下需要通过 FROM 指令来指定父镜像,父镜像是生成镜像的基础,会直接影响到所生成镜像的大小和功能。

父镜像一般有两种:

  • 服务类镜像:nginx 、 redis 、 mongo 、mysql、php、python 等。
  • 操作系统镜像:如 ubuntu 、debian 、centos 等,如果没有找到对应服务的镜像, 这些操作系统的软件库为我们提供了更广阔的扩展空间。

除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch 。这个镜像是虚拟的概念, 并不实际存在,它表示一个空白的镜像。

对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch 会让镜像体积更加小巧,因此使用Go语言开发的应用很多会使用这种方式来制作镜像。

示例

构建我们自己的 php-fpm 镜像:

  1. # 从官方基础版本构建
  2. FROM php:7.3.7-fpm
  3. # 官方版本默认安装扩展:
  4. # Core, ctype, curl
  5. # date, dom
  6. # fileinfo, filter, ftp
  7. # hash
  8. # iconv
  9. # json
  10. # libxml
  11. # mbstring, mysqlnd
  12. # openssl
  13. # pcre, PDO, pdo_sqlite, Phar, posix
  14. # readline, Reflection, session, SimpleXML, sodium, SPL, sqlite3, standard
  15. # tokenizer
  16. # xml, xmlreader, xmlwriter
  17. # zlib
  18. # 更新为国内镜像
  19. RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak \
  20. && echo 'deb http://mirrors.163.com/debian/ stretch main non-free contrib' > /etc/apt/sources.list \
  21. && echo 'deb http://mirrors.163.com/debian/ stretch-updates main non-free contrib' >> /etc/apt/sources.list \
  22. && echo 'deb http://mirrors.163.com/debian-security/ stretch/updates main non-free contrib' >> /etc/apt/sources.list \
  23. && apt-get update
  24. # bcmath, calendar, exif, gettext, sockets, dba,
  25. # mysqli, pcntl, pdo_mysql, shmop, sysvmsg, sysvsem, sysvshm 扩展
  26. RUN docker-php-ext-install -j$(nproc) bcmath calendar exif gettext sockets dba mysqli pcntl pdo_mysql shmop sysvmsg sysvsem sysvshm iconv
  27. # GD 扩展
  28. RUN apt-get install -y --no-install-recommends libfreetype6-dev libjpeg62-turbo-dev libpng-dev \
  29. && rm -r /var/lib/apt/lists/* \
  30. && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
  31. && docker-php-ext-install -j$(nproc) gd
  32. # imagick 扩展
  33. RUN export CFLAGS="$PHP_CFLAGS" CPPFLAGS="$PHP_CPPFLAGS" LDFLAGS="$PHP_LDFLAGS" \
  34. && apt-get install -y --no-install-recommends libmagickwand-dev \
  35. && rm -r /var/lib/apt/lists/* \
  36. && pecl install imagick-3.4.4 \
  37. && docker-php-ext-enable imagick
  38. # mcrypt 扩展
  39. RUN apt-get install -y --no-install-recommends libmcrypt-dev \
  40. && rm -r /var/lib/apt/lists/* \
  41. && pecl install mcrypt-1.0.2 \
  42. && docker-php-ext-enable mcrypt
  43. # Memcached 扩展
  44. RUN apt-get install -y --no-install-recommends libmemcached-dev zlib1g-dev \
  45. && rm -r /var/lib/apt/lists/* \
  46. && pecl install memcached-3.1.3 \
  47. && docker-php-ext-enable memcached
  48. # redis 扩展
  49. RUN pecl install redis-5.0.0 && docker-php-ext-enable redis
  50. # opcache 扩展
  51. RUN docker-php-ext-configure opcache --enable-opcache && docker-php-ext-install opcache
  52. # xdebug 扩展
  53. RUN pecl install xdebug-2.7.2 && docker-php-ext-enable xdebug
  54. # swoole 扩展
  55. RUN pecl install swoole-4.4.0 && docker-php-ext-enable swoole
  56. # 镜像信息
  57. LABEL Author="tcl"
  58. LABEL Version="v1.0"
  59. LABEL Description="PHP7.3"

然后进入 Dockerfile 所在的目录,运行如下命令生成镜像:

  1. $ docker build -t dungan/php7.3 .

八、其他杂项

端口和数据卷映射

一般我们映射如下几项:

  • 将容器中的端口映射到宿主机上。
  • 将容器中的网站目录映射到宿主机上。
  • 将容器中的nginx的配置目录映射到宿主机上。
  • 将容器中的mysql数据目录映射到宿主机上。

以 Nginx 为例 :

将 nginx 的整个配置目录 copy 到本地一份,然后在运行的时候将目录绑定到 nginx 容器的配置目录,这样修改配置更加方便一些,将配置目录 copy 出来之后终止运行并删除容器。

  1. docker run --name nginx -d nginx #运行一个nginx容器
  2. mkdir /mnt/d/docker #创建容器配置文件夹
  3. docker cp nginx:/etc/nginx /mnt/d/docker #复制nginx默认配置
  4. docker cp nginx:/usr/share/nginx/html /mnt/d/docker/wwwroot #复制站点
  5. docker stop nginx #停止nginx容器
  6. docker rm nginx #删除nginx容器

九、 参考

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注