聊聊docker
# 聊聊docker
# 1. 初识 docker
# 1.1 什么是 Docker?
- Docker 是基于 Go 语言实现的开源容器项目,诞生于 2013 年年初,最初发起者是 dotCloud
公司.
Docker 是 Linux 平台上的一款轻量级虚拟化容器的管理引擎.
Docker 利用 Linux 内核特性命名空间(namespaces)及控制组(cgroups)等为容器提供隔离的
运行环境.
- Docker 官方如此描述 Docker:“Build,Ship,Run.An open platform for distributed applications
for developers and sysadmins”. 换言之,Docker 为开发者与系统管理者提供了分布式应用的
开放平台,从而可以便捷地构建、迁移与运行分布式应用 .
- 现在官网上介绍它的要做的事情是“Build-Share-Run”,也是通过对应用的封装、分发、
部署、运行生命周期进行管理,达到应用组件“一次封装,到处运行”的目的.
# 1.2 为什么要使用 Docker?
# 2. 核心概念与安装配置
# 2.1 核心概念
# 2.1.1 Docker 镜像
相当于是一个 root 文件系统, 比如官方镜像 ubuntu:16.04 就包含了完整的一套Ubuntu16.04最小系统的 root 文件系统. 镜像是创建 Docker 容器的基础. 通过版本管理和增量的文件系统, Docker 提供了一套十分简单的机制来创建和更新现有的镜像,用户可以从网上下载一个已经做好的应用镜像并直接使用
# 2.1.2 Docker 容器
Docker 容器类似于一个轻量级的沙箱,Docker 利用容器来运行和隔离应用. 容器是从镜像创建的应用运行实例. 可以将其启动、开始、停止、删除,而这些容器都是彼此相互隔离的、互不可见的. 可以把容器看做是一个简易版的 Linux 系统环境(包括root 用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子.
镜像自身是只读的. 容器从镜像启动的时候,会在镜像的最上层创建一个可写层. 同一个 image 文件, 可以生成多个同时运行的容器实例.
# 2.1.3 Docker 仓库
Docker 仓库类似于代码仓库,它是 Docker 集中存放镜像文件的场所. Docker 利用仓库管理镜像的设计理念与 Git 非常相似,实际上在理念设计上借鉴了 Git 的很多优秀思想. Docker 仓库分为:
公开仓库(最大的公开仓库是官方提供的 Docker Hub,官网 https://hub.docker.com/ )
私有仓库(Harbor)
# 2.1.4 Docker 客户端
Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与Docker 的守护进程通信. Docker 使用客户端-服务器 (C/S) 架构模式,使用远程 API 来管理和创建Docker 容器。
# 2.1.5 Docker 主机
一个用于执行 Docker 守护进程和容器的物理或者虚拟的机器.
# 2.1.6 Docker Machine
Docker Machine 是一个简化 Docker 安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装 Docker,比如 VirtualBox、 Digital Ocean、Microsoft Azure
# 2.2 安装配置
# 2.2.1 Linux 下安装最新版本的docker
# 2.2.1.1 UBUNTU
此处推荐:curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
# 2.2.1.2 Centos
此处推荐:curl -fsSL https://get.docker.com/ | sh
# 2.2.2 Windows
Docker 并非是一个通用的容器工具,它依赖于已存在并运行的 Linux 内核环境。Docker 实质上是在已经运行的 Linux 下制造了一个隔离的文件环境,因此它执行的效率几乎等同于所部署的 Linux 主机. 因此,Docker 必须部署在 Linux 内核的系统上. 如果其他系统想部署Docker 就必须安装一个虚拟 Linux 环境. 可以去官网上直接下载安装. 过程在此省略.
# 2.2.3 MacOS
参考:
(1) 命令行安装 brew install --cask --appdir=/Applications docker
(2) 可以直接去官网上下载安装镜像进行安装
# 2.2.4 其他
转到官网:https://www.docker.com/get-started/
# 2.3 总体架构图
# 3. Docker 的相关简单命令
# 3.1 基础命令
docker version
docekr info
docker --help
# 3.2 镜像相关命令
序号 | 帮助命令 | 使用命令样例 | 备注 |
---|---|---|---|
1 | docker images --help | docker images | List images |
2 | docker pull --help | docker pull ubuntu:13.10 docker pull redis:5.0.12 docker pull redis docker pull 192.168.244.142:8443/library/redis:v6.0.8 | Pull an image or a repository froma registry |
3 | docker search --help | docker search httpd | Search the Docker Hub for images |
4 | docker commit --help | docker pull ubuntu:14.04 docker run -it ubuntu:14.04 /bin/bash root@bda3295636a3:/# root@bda3295636a3:/# touch 1.txt root@bda3295636a3:/# exit docker commit -m "Added a new file" -a "Docker Newbee" bda3295636a3 test:0.1 (bda3295636a3 是 containerid) | Create a new image froma container's changes |
5 | docker build --help | 创建 Dockerfile,在 Dockerf 所在目录执行: docker build -t gcc-image:1.0 . | Build an image from a Dockerfile (可见后续章节的例子) |
6 | docker tag --help | docker pull redis:6.0.8 docker tag redis:6.0.8 192.168.244.142:8443/library/redis:v6.0.8 docker login https://192.168.244.142:8443/harbor -uadmin docker push 192.168.244.142:8443/library/redis:v6.0.8 | Create a tag TARGET_IMAGE that referstoSOURCE_IMAGE |
7 | docker push --help | docker push 192.168.244.142:8443/library/redis:v6.0.8 | Push an image or a repository to a registry |
8 | docker history --help | docker history 1730c6f650e2 docker history redis | Show the history of an image |
9 | docker inspect --help | docker inspect 87c26977fd90 docker inspect redis | Return low-level information on Docker objects |
10 | docker save --help | docker save -o redis_6.0.8.tar redis:6.0.8 docker save redis:6.0.8 > redis_6.0.8-1.tar | Save one or more images to a tar archive(streamedto STDOUT by default) |
11 | docker import --help | wget http://download.openvz.org/template/precreated/ubuntu-14.04-x86_64-minimal.tar.gz cat ubuntu-14.04-x86_64-minimal.tar.gz | docker import - ubuntu:14.04 docker images | Import the contents froma tarball tocreateafilesystem image |
12 | docker export --help | docker export harbor-jobservice > harbor-jobservice.tar (harbor-jobservice 是容器名) | Export a container's filesystemas a tar archive |
13 | docker load --help | docker load --input redis_6.0.8.tar | Load an image from a tar archive or STDIN |
14 | docker rmi --help | docker rmi 16ecd2772934(可以测试一下被多仓库引用) docker rmi -f 16ecd2772934 | Remove one or more images |
# 3.3 容器相关命令
序号 | 帮助命令 | 使用样例 | 备注 |
---|---|---|---|
1 | docker create --help | docker create -it ubuntu:latest | Create a new container |
2 | docker run --help | docker run -d --name redis-node-1 --net host --privileged=true -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381 docker run -dit -p 33899:3389 -m 1024M --memory-swap -1 --privileged --name LinuxFortContainer_tanghm linuxfort0318_1745 /start.sh 0f03cbf59a3549d3u7yx8mYKcKlXW2AN3dunfA== docker run -d -p 80:80 --name webserver nginx | Run a command in a new container docker run 只在第一次运行时使用,将镜像放到容器中,以后再次启动这个容器时,只需要使用命令 docker start 即可,docker run 相当于执行了两步操作:将镜像放入容器中(docker create),然后将容器启动,使之变成运行时容器(docker start), 参数说明: -d 后台运行容器,并返回容器 ID -i 交互式运行 -t tty 终端 --memory 限制容器可以使用的内存,如果没有指定--memory-swap那么默认和--memory 一样大 eg: --memory 200m 没有--memory-swap 参数,那么容器可以使用的内存为400m --memory-swap 设置 swap 区可以使用的内存大小,默认和--memory一样大,必须>=--memory --cpu-shares 表示占有 cpu 的相对权重 eg: 假设在同一台物理主机上启动了如下2 个容器,容器1占用的cpu是容器 2 占有的 cpu 的 2 倍。 同一台物理主机上 容器 1: docker run -d --cpu-shares 10 ... 容器 2: docker run -d --cpu-shares 5 ... - -name 给容器起的名字 --net 指定容器运行的网络 -v 指定 volume 的路径 <br / >data volume 模式: 别名:容器里面的路径 mysql:/var/lib/mysql /var/lib/mysql 为 Dockerfile 中的volume 的值 使用 docker volume ls 命令查看 本地创建 mysql 数据卷: docker create volume--namemysql bind mouting 模式: 本地路径:容器路径 /var/data:/var/容器目录 本地目录和容器目录中的文件修改都会实时同步 --ip 指定容器的 ip 地址 -P 随机映射一个端口 -p 本地端口:容器端口。 将容器中的某个端口映射到本地的某个端口上 -e 设置环境变量 --volumes-from 参数用于连接某个指定容器的卷,从而可以访问到指定容器中的所有的卷. --rm 表示容器运行完成后自动删除 -h 设置 host 主机的名称 |
3 | docker start --help | docker start 36aebaa2ccb7 docker start loving_pike | Start one or more stopped containers start 的作用是,重新启动已存在的镜像, 也就是说,如果使用这个命令,我们必须事先知道这个容器的ID,或者这个容器的名字,我们可以使用dockerps -a 查看容器信息, 可对比 docker run 进行学习 |
4 | docker restart --help | docker restart 36aebaa2ccb7 docker restart loving_pike | Restart one or more containers 36aebaa2ccb7--containerid loving_pike--containername |
5 | docker logs --help | docker logs 36aebaa2ccb7 docker logs loving_pike | Fetch the logs of a container 36aebaa2ccb7--containerid loving_pike--containername |
6 | docker stop --help | docker stop 36aebaa2ccb7 docker stop loving_pike | Stop one or more running containers |
7 | docker kill --help | docker kill 36aebaa2ccb7 docker kill loving_pike | Kill one or more running containers |
8 | docker ps --help | --help docker ps -l 显示最近创建的容器(包括所有状态的容 器) | List containers |
9 | docker attach --help | docker run -itd --name ubuntu-test ubuntu /bin/bash docker attach 1146042c384e docker ps -a |grep 1146042c384e | Attach local standard input, output, and error streams toarunningcontainer 1146042c384e--containerid 在使用-d 参数运行容器时, 容器启动后会进入后台, 此时想要进入容器, 可以通过这个指令进入, 不推荐大家使用dockerattach命令,因为使用它在退出容器终端,会导致容器的停止 |
10 | docker exec --help | docker run -itd --name ubuntu-test-01 ubuntu /bin/bash docker exec -it ubuntu-test-01 /bin/bash root@21bd6d174b21:/# exit docker ps -a | grep ubuntu-test-01 | Run a command in a running container 在使用 -d 参数运行容器时,容器启动后会进入后台, 此时想要进入容器,可以通过这个指令进入, 推荐大家使用dockerexec命令,因为使用它在退出容器终端,不会导致容器的停止 |
11 | docker rm --help | docker rm 1146042c384e | Remove one or more containers |
12 | docker cp --help | docker run -itd --name ubuntu-test ubuntu /bin/bash docker cp ubuntu-test:/ ./ | Copy files/folders between a container and the local filesystem |
13 | docker pause --help | docker pause a1e5d03c3a0d docker pause ubuntu-test | Pause all processes within one or more containers |
14 | docker unpause --help | docker unpause a1e5d03c3a0d docker unpause ubuntu-test | Unpause all processes within one or more containers |
15 | docker rename --help | docker rename keen_cori keen_cori_new | Rename a container |
# 3.4 其他
更多命令可以查阅相关数据或手册
# 4. Docker 网络
# 4.1 查看 docker 不启动和启动时的网络情况
一台没怎么用过的 CentOS7 虚拟机一般有 ens33, lo, virbr0 这三个配置
# 4.2 常用基本命令
用【docker network --help】进行查看.
# 4.3 Docker 网络的用途
容器间的互联和通信以及端口映射;
容器 IP 变动时候可以通过服务名直接网络通信而不受到影响
# 4.4 网络模式
# 4.4.1 分类
Docker 网络模式 | 配置 | 说明 |
---|---|---|
host 模式 | --net=host | 容器将不会虚拟出自己的网卡,配置自己的IP 等,而是使用宿主机的 IP 和端口 |
container 模式 | --net=container:NAME_or_ID | 创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享 IP、端口范围 |
none | --network none | 该模式关闭了容器的网络功能 |
bridge 模式 | 使用--network bridge 指定, 默认使用 docker0 | 此模式会为每一个容器分配、设置IP 等,并将容器连接到一个 docker0 虚拟网桥,通过docker0 网桥以及 iptables nat 表配置与宿主之间的关联, 当 Docker 进程启动时,会在主机上创建一个名为docker0 的虚拟网桥,此主机上启动的Docker 容器会连接到这个虚拟网桥上。虚拟网桥的工作方式和物理交换机类似,这样主机上的所有容器就通过交换机连在了一个二层网络中. |
# 4.4.2 容器实例内默认网络 IP 生产规则
docker 容器内部的 ip 是有可能会发生改变的.
# 4.4.3 简单测试
docker run -d -p 8081:8080 --name tomcate81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080 --name tomcate82 billygoo/tomcat8-jdk8
docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
docker run -d -p 8083:8080 --name tomcat84 billygoo/tomcat8-jdk8
# 4.4.4 端口映射的简单示例
(1) 使用-P 来做映射宿主机的指定端口, 例如【docker run -d -P redis】
(2) 使用【-p IP::ContainerPort】 映射到指定地址的任意端口,使用IP::ContainerPort 绑定localhost 的任意端口到容器的 6379 端口,本地主机会自动分配一个端口, 例如【docker run-d-p 127.0.0.1::6379 redis】
(3)使用【-p IP:HostPort:ContainerPort】映射到指定地址的指定端口,例如【docker run -d-p127.0.0.1:6379:6379 redis】
(4) 使用【-p HostPort:ContainerPort】映射所有接口地址 ,例如【docker run -d -p 6379:6379-p5000:5000 redis】
# 5.Dockerfile
# 5.1 基本结构
# 5.1.1 说明
Dockerfile 由一行行命令语句组成,并且支持以#开头的注释行。一般而言,Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。例如:
# This Dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..
# Base image to use, this must be set as the first line
FROM ubuntu
# Maintainer: docker_user <docker_user at email.com> (@docker_user)
MAINTAINER docker_user docker_user@email.com
# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/
sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf
# Commands when creating a new container
CMD /usr/sbin/nginx
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 5.1.2 基于 debian:jessie 镜像安装Nginx 的Dockerfile示例
FROM debian:jessie
MAINTAINER NGINX Docker Maintainers "docker-maint@nginx.com"
ENV NGINX_VERSION 1.10.1-1~jessie
RUN apt-key adv --keyserver hkp://pgp.mit.edu:80 --recv-keys 573BFD6B3D8FBC64107
9A6ABABF5BD827BD9BF62 \
&& echo "deb http://nginx.org/packages/debian/ jessie nginx" >> /etc/
apt/sources.list \
&& apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \
ca-certificates \
nginx=${NGINX_VERSION} \
nginx-module-xslt \
nginx-module-geoip \
nginx-module-image-filter \
nginx-module-perl \
nginx-module-njs \
gettext-base \
&& rm -rf /var/lib/apt/lists/*
# forward request and error logs to docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
EXPOSE 80 443
CMD ["nginx", "-g", "daemon off;"]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 5.1.3 基于buildpack-deps:jessie-scm镜像安装Golang相关环境的 Dockerfile 示例
FROM buildpack-deps:jessie-scm
# gcc for cgo
RUN apt-get update && apt-get install -y --no-install-recommends \
g++ \
gcc \
libc6-dev \
make \
&& rm -rf /var/lib/apt/lists/*
ENV GOLANG_VERSION 1.6.3
ENV GOLANG_DOWNLOAD_URL https://golang.org/dl/go$GOLANG_VERSION.linux-amd64.tar.gz
ENV GOLANG_DOWNLOAD_SHA256 cdde5e08530c0579255d6153b08fdb3b8e47caabbe717bc7bcd7561275a87aeb
RUN curl -fsSL "$GOLANG_DOWNLOAD_URL" -o golang.tar.gz \
&& echo "$GOLANG_DOWNLOAD_SHA256 golang.tar.gz" | sha256sum -c - \
&& tar -C /usr/local -xzf golang.tar.gz \
&& rm golang.tar.gz
ENV GOPATH /go
ENV PATH $GOPATH/bin:/usr/local/go/bin:$PATH
RUN mkdir -p "$GOPATH/src" "$GOPATH/bin" && chmod -R 777 "$GOPATH"
WORKDIR $GOPATH
COPY go-wrapper /usr/local/bin/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 5.1.4 永远的 helloworld(JAVA)
/root/dockerStudy/20220330/javaHelloWorld
(1)编写 Makefile
(2)构造镜像
(3) 运行镜像
# 5.2 指令说明
指令的一般格式为 INSTRUCTION arguments,指令包括 FROM、MAINTAINER、RUN 等,参见下表:
# 5.3 最佳实践
所谓最佳实践,实际上是从需求出发,来定制适合自己、高效方便的镜像。
首先,要尽量吃透每个指令的含义和执行效果,自己多编写一些简单的例子进行测试,弄清楚了再撰写正式的 Dockerfile。此外,Docker Hub 官方仓库中提供了大量的优秀镜像和对应的 Dockefile,可以通过阅读它们来学习如何撰写高效的 Dockerfile。
通过前辈总结了的一些实践经验,建议在生成镜像过程中,尝试从如下角度进行思考,完善所生成的镜像。
- 精简镜像用途: 尽量让每个镜像的用途都比较集中、单一,避免构造大而复杂、多功能的镜像;
- 选用合适的基础镜像: 过大的基础镜像会造成生成臃肿的镜像,一般推荐较为小巧的debian 镜像;
- 提供足够清晰的命令注释和维护者信息: Dockerfile 也是一种代码,需要考虑方便后续扩展和他人使用;
- 正确使用版本号: 使用明确的版本号信息,如 1.0,2.0,而非 latest,将避免内容不一致可能引发的惨案;
- ·减少镜像层数: 如果希望所生成镜像的层数尽量少,则要尽量合并指令,例如多个RUN指令可以合并为一条;
- 及时删除临时文件和缓存文件: 特别是在执行 apt-get 指令后,/var/cache/apt 下面会缓存一些安装包;
- 提高生成速度: 如合理使用缓存,减少内容目录下的文件,或使用.dockerignore 文件指定等;
- 调整合理的指令顺序: 在开启缓存的情况下,内容不变的指令尽量放在前面,这样可以尽量复用;
- 减少外部源的干扰: 如果确实要从外部引入数据,需要指定持久的地址,并带有版本信息,让他人可以重复而不出错。
# 5.4 推荐一个 Dockerfile 项目
Docker 公司在 Github 上维护了一个 Dockerfile 项目(https://github.com/dockerfile ),该项目为一些常见开源软件服务提供 Dockerfile 和自动构建方案。开发者可以根据该工程中的各服务介绍和 Dockerfile 来快速开发、测试和部署新的应用程序。
# 6.容器实战思考
# 6.1 Docker 成功的原因
虽然 Docker 所依赖的容器技术存在已久,但 Docker 的出现才真正提供了易用的解决方案
- Docker 首次创造了一种简单易行并且覆盖应用全生命周期的工作流,用户可以通过简单的指令或 Restful API 来拉取、打包、运行和维护容器.
- Docker 提供了一种统一的实践方法,每个服务(或应用)维护一个Dockerfile 文件
- Docker 正以一种前所未有的方式让用户可以在各种 Linux 发行版、各种开发环境中快速切换,这对应用开发者来说真是一种福音.
真正解决用户痛点,真正带来效率的提升,是一个产品和技术能最终成功的关键
# 6.2 研发人员怎么看 Docker
- 快速上手新技术
- 容器化的代码仓库让工作更高效
- 面向业务编程,使用 Docker 快速掌握新技术要点并完成适当的技术储备,在工期紧急时可更好地专注业务开发
- 使用 Docker Hub 发布开源项目和学习
# 6.3 容器化开发模式
# 6.3.1 传统开发流程与容器化开发流程
# 6.3.2 一些建议
首先,在开发和测试环境中,推荐使用-v 共享文件夹来存储开发人员的程序代码,避免频繁打包操作;
其次,利用基础 base 镜像的继承特性来调整镜像的轻微变更. 例如当需要测试程序对不同版本 JDK 的支持情况时,只需改变 base 镜像的 JDK 设置,然后其他依赖它的镜像在重新创建的过程中就可以自动完成更新;
最后,测试部门应当注意 Docker 以及镜像的版本,并经常对部署后的应用程序进行性能上的测试.
# 6.4 容器与生产环境
# 6.4.1 为标准而努力
尽管 Docker 获得广大公有云厂商的大力支持,但是目前容器技术生态中已经存在许多分支与分歧,如 rkt 项目。为了解决容器生态中的差异化问题,为了从根本上解决生产环境中运用 Docker 的风险,Google、Intel、Microsoft、IBM、Amazon、VMware、Oracle、HPE、Facebook等 IT 巨头于 2015 年 6 月共同宣布成立 OCI(Open Container Initiative)组织。OCI 组织的目标在于 . 除了保障与延续既有容器服务的生命周期外,还通过不断推出标准的创新的容器解决方案赋能开发者. OCI组织的目标在于建立通用的容器技术标准。除了保障与延续既有容器服务的生命周期外, 还通过不断推出标准的创新的容器解决方案赋能开发者.
注:rkt 项目最早跟随 k8s 使用的运行时的组件,并且也入选过 cncf 的沙箱项目,但是在最后的使用中还是被抛弃了,其中主要的是 croi-o 和 containerd 两个项目的接受度更高,并且社区活跃度越来越低,最终停止维护。
# 6.4.2 一些参考建议
- 如果 Docker 出现不可控的风险,是否考虑了备选的解决方案;
- 是否需要对 Docker 容器做资源限制,以及如何限制,如 CPU、内存、网络、磁盘等;
- 目前,Docker 对容器的安全管理做得不够完善,在应用到生产环境之前可以使用第三方工具来加强容器的安全管理。如使用 apparmor(类似于 selinux)对容器的能力进行限制、使用更加严格的 iptable 规则、禁止 root 用户登录、限制普通用户权限以及做好系统日志的记录;
- 公司内部私有仓库的管理、镜像的管理问题是否解决. 目前官方提供的私有仓库管理工具功能并不十分完善,若在生产环境中使用还需要更多的工作.
# 7.可继续的Docker 相关学习与研究
(1)Docker 核心实现技术,包括基本架构,命名空间,控制组,联合文件系统,Linux 网络虚拟化;
(2)Docker 中关于网络的高级知识,包括网络的启动和配置参数、DNS 的使用配置、容器访问和端口映射的相关实现;
(3)Docker 三剑客-Docker Machine,Docker Compose,Docker Swarm;
(4)Mesos-优秀的集群资源调度平台;
(5)Kubernetes—生产级容器集群平台;
(6)源码的研读;
.......
# 8. 推荐网站与书籍
网站:
docker 官网: https://www.docker.com/
dockerhub 官网: https://hub.docker.com/
docker 菜鸟课程: https://www.runoob.com/docker/docker-tutorial.html
Docker 官方文档:https://docs.docker.com/
书籍:
《docker 容器实战:原理、架构与应用》
《Docker 技术入门与实战》
《第一本 Docker 书》
《Docker 进阶与实战 华为 Docker 实践小组》
《Docker 源码分析》
......