赞
踩
Docker 官网 :https://www.docker.com/
Docker Hub:https://registry.hub.docker.com/search?q=kali
菜鸟教程 — Docker 教程:http://www.runoob.com/docker/docker-tutorial.html
W3schoolDocker 教程:https://www.w3cschool.cn/docker/
github 地址:https://github.com/yeasy/docker_practice
讲解最好的 Docker 教程,从入门到精通:https://cloud.tencent.com/developer/article/1885678
docker 中文论坛、资料:https://www.docker.org.cn/index.html
Docker入门教程:http://www.manongjc.com/article/18866.html
:https://zhuanlan.zhihu.com/p/187505981
Docker 是一个开源的应用容器引擎,使用 Go 语言开发,基于 Linux 内核的 cgroup、namespace、Union FS 等技术,对应用进程进行封装隔离,并且独立于宿主机与其他进程,这种运行时封装的状态 称为容器。容器完全使用沙箱机制,相互之间不会有任何接口(类似 iPhone 的 app),更重要的是容器性能开销极低。
也可以把 docker 容器可以理解为在沙盒中运行的进程。这个沙盒包含了该进程运行所必须的资源,包括文件系统、系统类库、shell 环境等等。但这个沙盒默认是不会运行任何程序的。你需要在沙盒中运行一个进程来启动某一个容器。这个进程是该容器的唯一进程,所以当该进程结束的时候,容器也会完全的停止。
容器技术 只隔离应用程序的运行时环境,但容器之间可以共享同一个操作系统,这里的运行时环境指的是程序运行依赖的各种库以及配置。容器是一种通用技术,docker只是其中的一种实现。
Docker 理念:将 "应用" 及 "依赖包" 打包到一个可移植的容器中,可发布到任意 Linux 发行版 Docker 引擎上。使用沙箱机制运行程序,程序之间相互隔离。
docker 使用了 CS (client,server) 架构,docker client 负责处理用户输入的各种命令,比如 docker build、docker run,真正工作的其实是 server,也就是 docker demon。client 和 server 可以运行在同一个系统上,也可以运行在不同的机器上跨主机实现远程通信。
Docker CLI Client 通过脚本或 CLI 命令 通过 REST API 与 Docker Server 守护进程交互。Docker Server 负责创建和管理 Docker 对象,如 images,containers,networks 和 volumes。
写完 dockerfile 后,交给 docker "编译" (使用 docker build 命令),client 在接收到请求后转发给docker daemon,接着docker daemon根据dockerfile创建出“可执行程序”image。
有了 "可执行程序" image 后,使用命令 docker run 就可以运行程序,docker daemon 接收到该命令后找到具体的 image,然后加载到内存开始执行,image 执行起来后就是container。
以 docker run 这个命令为例:
实例:运行一个 ubuntu 的容器,以交互的方式连接到容器里的本地命令行会话,并运行 /bin/bash。
命令:( 两个命令等价 )
$ docker run -i -t ubuntu /bin/bash
$ docker run -it ubuntu /bin/bash假设使用的是默认的镜像仓库配置,执行这个命令的步骤是:
- 1、如果本地没有 ubuntu 镜像,Docker 会从镜像仓库拉取这个镜像,和手动执行 docker pull ubuntu 命令一样。
- 2、Docker 创建了一个新的容器,和手动执行 docker container create 命令一样。
- 3、Docker 为容器分配一个读写文件系统,作为容器的最后一层。这就允许正在运行的容器在本地文件系统中创建和修改文件和目录。
- 4、Docker 创建一个网络接口,因为没有指定任何网络选项,比如为容器分配IP地址等,所以容器会连接到默认网络。默认容器可以使用主机网络连接到外部网络。
- 5、Docker 启动容器并且执行 /bin/bash。由于容器是以交互方式执行并连接到本地终端(-i -t参数),你可以使用本地的键盘输入信息以及在本地终端上看到日志。
- 6、当输入exit 终止 /bin/bash 命令时,容器停止但不会被删除。
相当于 "编译器",把 dockerfile 编译成 image。Dockerfile 中的每条指令都会在镜像中创建一层。当修改Dockerfile 并重新构建时,只有被修改的层会重新构建。
dockerfile:指定需要哪些程序、依赖什么样的配置,之后把 dockerfile 交给 docker ( 相当于编译器 ) 进行 "编译",生成 image。dockerfile 就相当于 image 的源代码。
- image (镜像):相当于编译后生成的可执行程序。docker build 命令生成 image。镜像就是一个微型的root 文件系统。镜像(Image)就是一个只读的模板,用来创建 Docker 容器的。通常情况下,一个镜像是基于另一个镜像
每一个镜像由一系列的层 (layers) 组成。
image 运行起来后进程就是 container。即 container 是通过 image 运行起来的进程实例。容器可以被创建、启动、停止、删除、暂停等。容器(container)中可以运行各种应用。每个容器都是相互隔离的、保证安全的平台。可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等和运行在其中的应用程序。
手机上下载别人写好的应用程序,可以到 APP Store (应用商店)。与之类似,既然 image 也是一种 "可执行程序",那么有没有 "Docker Image Store"呢?答案是肯定有,这就是 Docker Hub,docker 官方的 "应用商店",可以在这里下载到别人编写好的 image,这样就不用自己编写dockerfile了。仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub,存放了数量庞大的镜像供用户下载。Docker 默认是去 Docker Hub 里查找镜像。docker pull 就是从 Docker Hub 拉取镜像的。
docker 利用命名空间的技术来为容器提供独立的工作区域。当运行容器时,docker 会为这个容器创建一组命名空间。这些命名空间提供了一个隔离层,容器的所有行为都在一个单独的命名空间中运行,并且其访问权限仅限于该命名空间。
Docker 引擎使用如下的命名空间:
- 1、pid 命名空间:进程隔离(PID:Process ID)
- 2、net 命名空间:管理网络接口(NET:Networking)
- 3、ipc 命名空间:管理对IPC资源的访问(IPC:InterProcess Communication)
- 4、mnt 命名空间:管理文件挂载点(MNT:Mount)
- 5、uts 命名空间:隔离内核和版本标识符(UTS:Unix Timesharing System)
pid namespace:不同用户的进程就是通过 pid namespace 隔离开的,且不同 namespace 中可以有相同 PID。( 参考文档:Introduction to Linux namespaces – Part 3: PID ) 具有以下特征:
- 每个 namespace 中的 pid 是有自己的 pid=1 的进程(类似 /sbin/init 进程)
- 每个 namespace 中的进程只能影响自己的同一个 namespace 或子 namespace 中的进程
- 因为 /proc 包含正在运行的进程,因此在 container 中的 pseudo-filesystem 的 /proc 目录只能看到自己 namespace 中的进程
- 因为 namespace 允许嵌套,父 namespace 可以影响子 namespace 的进程,所以子 namespace 的进程可以在父 namespace 中看到,但是具有不同的 pid
mnt namespace:类似 chroot,将一个进程放到一个特定的目录执行。mnt namespace 允许不同 namespace 的进程看到的文件结构不同,这样每个 namespace 中的进程所看到的文件目录就被隔离开了。同 chroot 不同,每个 namespace 中的 container 在 /proc/mounts 的信息只包含所在 namespace 的 mount point。
net namespace:网络隔离是通过 net namespace 实现的, 每个 net namespace 有独立的 network devices, IP addresses, IP routing tables, /proc/net 目录。这样每个 container 的网络就能隔离开来。 docker 默认采用 veth 的方式将 container 中的虚拟网卡同 host 上的一个 docker bridge 连接在一起。参考文档:Introduction to Linux namespaces – Part 5: NET
uts namespace:UTS ("UNIX Time-sharing System") namespace 允许每个 container 拥有独立的 hostname 和 domain name, 使其在网络上可以被视作一个独立的节点而非 Host 上的一个进程。参考文档:Introduction to Linux namespaces – Part 1: UTS
ipc namespace:container 中进程交互还是采用 Linux 常见的进程间交互方法 (interprocess communication - IPC), 包括常见的信号量、消息队列和共享内存。然而同 VM 不同,container 的进程间交互实际上还是 host 上具有相同 pid namespace 中的进程间交互,因此需要在IPC资源申请时加入 namespace 信息 - 每个 IPC 资源有一个唯一的 32bit ID。参考文档:Introduction to Linux namespaces – Part 2: IPC
user namespace:每个 container 可以有不同的 user 和 group id, 也就是说可以以 container 内部的用户在 container 内部执行程序而非 Host 上的用户。
有了以上 6 种 namespace 从进程、网络、IPC、文件系统、UTS 和用户角度的隔离,一个 container 就可以对外展现出一个独立计算机的能力,并且不同 container 从 OS 层面实现了隔离。 然而不同 namespace 之间资源还是相互竞争的,仍然需要类似 ulimit 来管理每个 container 所能使用的资源 - cgroup。
Reference
docker 引擎还依赖于另一种称为控制组(cgroup)的技术。cgroup将应用程序限制为特定的资源集。控制组允许Docker引擎将可用的硬件资源共享给容器,并实施限制和约束。例如,限制特定容器的可用内存。可以在哪些CPU上运行等等。
资源配额「cgroups」
cgroups 实现了对资源的配额和度量。 cgroups 的使用非常简单,提供类似文件的接口,在 /cgroup 目录下新建一个文件夹即可新建一个 group,在此文件夹中新建 task 文件,并将 pid 写入该文件,即可实现对该进程的资源控制。具体的资源配置选项可以在该文件夹中新建子 subsystem ,{子系统前缀}.{资源项} 是典型的配置方法, 如 memory.usageinbytes 就定义了该 group 在 subsystem memory 中的一个内存限制选项。 另外,cgroups 中的 subsystem 可以随意组合,一个 subsystem 可以在不同的 group 中,也可以一个 group 包含多个 subsystem - 也就是说一个 subsystem。
- memory : 内存相关的限制
- cpu : 在 cgroup 中,并不能像硬件虚拟化方案一样能够定义 CPU 能力,但是能够定义 CPU 轮转的优先级,因此具有较高 CPU 优先级的进程会更可能得到 CPU 运算。 通过将参数写入 cpu.shares ,即可定义改 cgroup 的 CPU 优先级 - 这里是一个相对权重,而非绝对值
- blkio : block IO 相关的统计和限制,byte/operation 统计和限制 (IOPS 等),读写速度限制等,但是这里主要统计的都是同步 IO
- devices : 设备权限限制
参考文档:how to use cgroup
UnionFS(Union file systems, 联合文件系统)是通过创建层来操作的文件系统,使得非常轻量级和快速。docker引擎使用 UnionFS 为容器提供构建块。Docker引擎可以使用多个UnionFS变体,包括 AUFS、btrfs、vfs 和 DeviceMapper。
Docker引擎将命名空间、控制组和UnionFS组合成一个容器格式的包装器,默认的容器格式是 libcontainer。
docker 相关安装方法,具体安装参考 https://docs.docker.com/installation/
Mac、Windows、Linux 安装 docker:https://docs.docker.com/desktop/install/linux-install/
安装 docker 时,官网有 docker desktop 和 docker engine 两种,这两种有什么不同?应该安装哪种呢?
在 docker desktop 里,docker 客户端是在宿主机中,守护进程在虚拟机里。当要访问 docker desktop 的 ip 时,要谨记一条 docker network 存在于虚拟机中,即使使用 docker run --net host 那也是使用虚拟机的 host network,而不是物理机的 network。docker container 运行在虚拟机中。
在 Windows 和 MacOS 中,要想运行 linux 容器,必须有虚拟机,在 linux 中是不需要的; 不过,为了一致体验,如果在 linux 中安装 desktop 也会安装一个虚拟机。
Docker 镜像官网:https://registry.hub.docker.com/
因为国情的原因,国内下载 Docker HUB 官方的相关镜像比较慢,可以使用 ( http://opskumu.github.io/docker.cn ) 镜像,镜像保持和官方一致,关键是速度块,推荐使用。
修改 daemon 配置文件 /etc/docker/daemon.json
Docker 换源-阿里源,中科大源,网易源:https://blog.csdn.net/m0_37282062/article/details/115770314
{
"registry-mirrors":[
"https://hub-mirror.c.163.com/",
"https://docker.mirrors.ustc.edu.cn/"
]
}
重启 docker 以及 daemon
systemctl daemon-reload
systemctl restart docker
查看 docker
sudo docker version # 查看当前 docker 版本
docker info 查看 docker 信息
docker image ls # 列出本机的所有 image 文件。
docker image rm [imageName] # 删除 image 文件
image 文件是一个二进制文件,实际上,一个 image 文件往往继承自另外一个 image 文件,加上一些个性化设置而成。例如:你可以在 ubuntu 的 image 基础上加上 Apache 服务器,形成你自己的 image。一个 image文件可以同时运行多个实例。
image 文件是通用的,一台机器上的 image 文件拷贝到另一台机器上,照样可以使用。一般是使用别人制作好的 image 文件,即使要定制也可以对已有的 image 文件进行加工,而不是从头制作,这样可以节省很多时间。为了方便,image 制作完成后,可以上传到网上的仓库,Docker 的官方仓库 ( https://hub.docker.com/ ) 是最重要、最常用的 image 仓库。此外,出售自己制作的 image 文件也是可以的。
通过最简单的 image 文件 https://hub.docker.com/r/library/hello-world/,感受一下 Docker。首先,运行下面的命令,将 image 文件从仓库抓取到本地。
$ docker image pull library/hello-world
上面的代码中,docker image pull 是 抓取(拉取) image 文件的命令,library/hello-world 是 image 文件在仓库里面的位置,其中 library是 image 文件所在的组,hello-world 是 image 文件的名字。由于 Docker 官方提供的文件都放在 library 组 里面,所以他是默认组,可以省略。因此上面的命令可以写成下面这样:
$ docker image pull hello-world
抓取成功后就可以看到本地的 image 文件了
docker image ls
现在运行这个image文件,可以看到一些输出。然后容器就会停止。
$ docker container run hello-world
docker container run 会从一个镜像文件中生成一个容器实例,并且运行它。注意,docker container run 命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。因此,前面的 docker image pull 命令并不是必需的步骤。
有些容器不会终止,因为提供的是服务,比如安装 unbuntu 的 image,就可以在命令行体验unbutu系统。对于那些不会自动终止的容器,必须使用 docker container kill [container_id] 来终止。新打开一个终端窗口:
$ docker container ls # 查看当前运行容器
$ docker container kill 649a6b785a86 # 杀死某个容器
$ docker container ls # 查看所有 容器
image 文件生成的实例本身也是一个文件,称为:容器文件。即一旦容器生成,就会存在两个文件:一个image文件,一个容器文件。关闭容器并不会删除容器文件,只是容器停止运行。
$ docker container ls # 列出本机正在运行的容器
$ docker container ls --all # 列出本机所有容器,包括终止运行的容器终止运行的容器文件依然会占用硬盘空间。
docker container ls --all
docker container rm [container_id] # 删除容器
image 文件是如何生成的?如果你要推广自己的软件,肯定要制作自己的 image 文件。这时候就要用到 Dockerfile 文件,这是一个文本文件,用来配置 image。Docker容器根据 Dockerfile 文件生成 image 二进制文件。
RUN 命令和 CMD 命令的区别是什么呢?简单来说:
- RUN 命令 在 image 文件的构建阶段执行,执行结果会打包进 image 文件;
- CMD 命令 则是在容器启动后执行。
- Dockerfile 文件可以包含多个RUN命令,但只能包含一个CMD命令
注意:指定了 CMD 命令后,docker container run 命令就不能附加命令了,比如前面的 /bin/bash,否则,它会覆盖CMD命令
现在启动容器可以用下面的命令:$ docker container run --rm -p 8000:3000 -it koa-demo:0.0.1
容器运行成功后,说明 image 文件没问题,这是就可以把 image 文件分享到网上。首先,去 hub.docker.com 或 cloud.docker.com 注册一个账户。然后,使用命令登录:$ docker login
接着,为本地的 image 标注用户名和版本。
$ docker image tag [imageName] [username]/[repository]:[tag]
示例:$ docker image tag koa-demos:0.0.1 ruanyf/koa-demos:0.0.1也可以不标注用户名,重新构建一下 image 文件。
$ docker image build -t [username]/[repository]:[tag] .
最后,发布 image 文件:$ docker image push [username]/[repository]:[tag]
发布成功以后,登录 hub.docker.com,就可以看到已经发布的 image 文件。
镜像不是一个单一的文件,而是有多层构成。通过docker history 查看镜像中各层内容及大小,每层对应着Dockerfile中的一条指令。Docker镜像默认存储在/var/lib/docker/<storage-driver>中。
如图,容器其实是在镜像的最上面加了一层读写层,在运行容器里文件改动时,会先从镜像里面要写的文件复制到容器自己的文件系统中(读写层)。若想持久化这些改动,可以通过 docker commit 将容器保存成一新的镜像。
:https://zhuanlan.zhihu.com/p/413767660
更新镜像之前,需要使用镜像来创建一个容器。
runoob@runoob:~$ docker run -it ubuntu:15.10 /bin/bash
root@e218edb10161:/#
在运行的容器内使用 apt-get update 命令更新容器内的软件。在完成操作之后,输入 exit 命令来退出这个容器。此时 ID为 e218edb10161 的容器是按需求更改的容器。我们可以通过命令 docker commit 来提交容器副本。
runoob@runoob:~$ docker commit -m="has update" -a="runoob" e218edb10161 runoob/ubuntu:v2
sha256:70bf1840fd7c0d2d8ef0a42a817eb29f854c1af8f7c59fc03ac7bdee9545aff8
各个参数说明:
有时,手动更新容器内的软件可能很诱人。这应该避免。apt-get update && apt get upgrade -y在管理裸机 Linux 服务器时,按计划运行(或您的包管理器的对应项)是标准做法。这些命令通常不在 Docker 容器中运行,尽管它们可能作为 a 的一部分包含 Dockerfile 在映像构建期间获取最新的安全补丁。定期拉取基础镜像并重新创建容器是保持它们更新的首选方式。这为您提供了所有上游安全修复并缩短了单个容器的寿命。容器环境不应在创建实例后进行修改;文件系统更改应仅限于写入临时路径和比容器寿命更长的专用 Docker 卷。
在使用 Docker 时有的镜像可能需要经常更新,通常手动更新容器比较繁琐,需要四个步骤:
容器较少还比较好,如果有大量容器需要更新,那将会产生巨大的工作量。
现在介绍一种自动更新容器的方式,Watchtower 是一种流行的选择,它监视正在运行的容器并在其 Docker Hub 映像更改时替换它们。Watchtower 本身被部署为一个容器:
docker run -d -v /var/run/docker.sock:/var/run/docker.sock container/watchtower
docker run -d -v /var/run/docker.sock:/var/run/docker.sock --name watchtower用上面的代码即可在后台启动 watchtower 容器,并监控当前所有镜像的更新情况。因为 watchtower 需要与 Docker API 进行交互以监控正在运行的容器,所以在使用时需要加上 -v 参数将 /var/run/docker.sock 映射到容器内。
现在您已经安装了一个正常运行的 Watchtower。您主机的 Docker 套接字已挂载到 Watchtower 容器中,允许它运行 Docker 命令来创建和删除容器。
watchtower 的官方文档:https://containrrr.dev/watchtower/usage-overview/
Watchtower 将自动检测 Docker Hub 上的新镜像发布,将它们拉到您的机器上,并使用该镜像替换容器。现有容器将被关闭,并在其位置创建新的相同容器。您提供的相同标志docker run
将提供给替换容器。
默认情况下,Watchtower 仅适用于 Docker Hub。您可以通过在配置文件中提供凭据将其与私有映像注册表一起使用。创建一个包含以下内容的 JSON 文件:
- {
- "auths": {
- "example.com": {
- "auth": "credentials"
- }
- }
- }
替换example.com
为您的注册表路径。
接下来从您的注册表用户名和密码生成凭据字符串:echo -n 'username:password' | base64
将生成的 Base64 编码字符串粘贴到配置文件中,替换credentials
占位符文本。
将配置文件挂载到 Watchtower 容器中以启用对注册表的访问:
- docker run -d \
- -v config.json:/config.json
- -v /var/run/docker.sock:/var/run/docker.sock \
- containrrr/watchtower
:https://blog.csdn.net/sinat_16643223/article/details/120655976
docker --help
用法: docker [OPTIONS] COMMAND
docker COMMAND --help
详细:https://docs.docker.com/go/guides/
基础 命令:
run 基于 image 创建和运行一个新的 container
exec 在运行的 容器 中执行命令
ps 列出所有 容器
build 通过 Dockerfile 构建 image
pull 从 registry 下载 image
push 上传 image 到 registry
images 列出所有 image
login 登录 registry
logout 退出登录 registry
search 从 Docker Hub 搜索 images
version 显示版本信息
info Display system-wide information管理 命令:
builder 管理 构建
buildx* Docker Buildx (Docker Inc., v0.10.4)
compose* Docker Compose (Docker Inc., v2.17.3)
container 管理 containers
context 管理 上下文
dev* Docker 开发环境 (Docker Inc., v0.1.0)
extension* 管理 docker 扩展 (Docker Inc., v0.2.19)
image 管理 images
init* 为项目创建与Docker相关的启动文件
manifest 管理Docker镜像清单和清单列表
network 管理 网络
plugin 管理 插件
sbom* 查看一个 image 的软件包的软件物料清单(SBOM)
scan* Docker Scan (Docker Inc., v0.26.0)
scout* Docker Scout 命令行工具
system 管理 Docker
trust 管理 Docker 镜像上的 trust
volume 管理 volumes群 命令 ( 就是 集群 ):
swarm 管理 SwarmCommands:
attach 附加到正在运行的 container
commit 基于改变后的 container 创建新的 image
cp 在 container 和 本地主机 复制 files/folders
create 创建一个新 container
diff 探测 container 中发生变化的 文件和目录
events 从 server 中获取 real time events
export 导出 container 的 filesystem 成 tar 格式的压缩文件
history 显示 image 的历史记录
import 同 export 相反
inspect Return low-level information on Docker objects
kill Kill 一个或者多个正在运行的 容器
load 从 tar 或者 STDIN 导入一个 image
logs 获取 container 的 log
pause 暂停一个或者多个容器的所有进程
port 列出端口映射,或者为容器指定映射
rename 重命名 container
restart 重启一个或者多个 containers
rm 移除一个或者多个 containers
rmi 移除一个或者多个 images
save 保存一个或者多个 images 到 tar压缩文件
start 开始一个或者多个 containers
stats 显示 container(s) 资源使用状态
stop 停止一个或者多个正在运行的 containers
tag 创建一个 tag TARGET_IMAGE 并应用到 SOURCE_IMAGE
top 像是 container 正在运行的进程
unpause 取消 containers 的暂停所有进程
update 更新 containers 配置
wait 容器停止前一直阻塞,停止后打印 退出码全局 选项:
--config string 导入配置文件 (默认"家目录/.docker")
-c, --context string 用于连接到守护进程的上下文名称
(覆盖DOCKER_HOST env var和默认的“docker context use”上下文设置)
-D, --debug 启用 debug 模式
-H, --host list 要连接的守护进程套接字
-l, --log-level string 设置 log 级别 ("debug", "info","warn", "error", "fatal")
(默认 "info")
--tls 使用 TLS; implied by --tlsverify
--tlscacert string CA 的签名 (默认 "家目录/.docker/ca.pem")
--tlscert string TLS certificate 文件路径
(默认 "家目录/.docker/cert.pem")
--tlskey string TLS key 文件路径
(默认 "家目录/.docker/key.pem")
--tlsverify 使用 TLS 验证 远程
-v, --version 版本号
docker info 查看信息
docker images 查看所有镜像
docker ps -a 查看所有容器
docker ps 查看所有运行容器
docker start <容器ID或容器名称> 启动容器
docker stop <容器ID或容器名称> 停止容器
docker rm <容器ID或容器名称> 删除容器
创建容器
sudo docker run -d --privileged --name annotation --network host -v /data/annotation_data:/data/annotation_data -it ubuntu_20240510 /bin/bash
sudo docker run -d --privileged --name forum --network host -v /data/miniprograme_data:/data/miniprograme_data -it ubuntu_20240510 /bin/bash
sudo docker run -d --privileged --name mongodb --network host -v /data/mongodb_data:/data/mongodb_data -it ubuntu_20240510 /bin/bash
命令格式:docker search 镜像名字
示例:$ docker search tutorial搜索可用的 docker 镜像:sudo docker search ubuntu
使用 docker 最简单的方式莫过于从现有的容器镜像开始。Docker 官方网站专门有一个页面来存储所有可用的镜像,网址是:https://index.docker.io/ 。通过浏览这个网页来查找你想要使用的镜像,或者使用命令行的工具来检索。
示例:
$ sudo docker search --help
# 查找 star 数至少为 100 的镜像,找出只有官方镜像 start 数超过 100,
# 默认不加 s 选项找出所有相关 ubuntu 镜像
$ sudo docker search -s 100 ubuntu
$ sudo docker pull --help # pull 拉取镜像
$ sudo docker push # push 推送指定镜像# 下载官方 ubuntu docker 镜像,默认下载所有 ubuntu 官方库镜像
$ sudo docker pull ubuntu
$ sudo docker pull ubuntu:14.04 # 下载指定版本 ubuntu 官方镜像# 推送镜像库到私有源[可注册 docker 官方账户,推送到官方自有账户]
$ sudo docker push 192.168.0.100:5000/ubuntu# 推送指定镜像到私有源
$ sudo docker push 192.168.0.100:5000/ubuntu:14.04
$ sudo docker pull ubuntu # 获取 ubuntu 官方镜像
$ sudo docker images # 查看当前镜像列表示例命令:$ docker pull learn/tutorial
使用 docker pull 命令即可。( docker命令 和 git 有一些类似的地方)。在 docker 的镜像索引网站上面,镜像都是按照 用户名/镜像名 的方式来存储的。有一组比较特殊的镜像,比如ubuntu 这类基础镜像,经过官方的验证,值得信任,可以直接用 镜像名来检索到。
提示:执行pull命令的时候要写完整的名字,比如 "learn/tutorial"。
docker image --help 镜像命令的帮助
docker container --help 容器命令的帮助
docker rmi --help 删除一个或者多个镜像
示例:
$ sudo docker images # 显示当前系统镜像,不包括过渡层镜像
$ sudo docker images -a # 显示当前系统所有镜像,包括过渡层镜像
$ sudo docker images ubuntu # 显示当前系统 docker ubuntu 库中的所有镜像
docker images 或者 docker image ls 列出镜像列表
docker pull ubuntu:14.04
docker search httpd 查找名为httpd的镜像
管理镜像常用命令:docker image --help
指令 | 描述 |
---|---|
ls | 列出镜像 |
build | 构建镜像来自Dockerfile |
history | 查看镜像历史 |
inspect | 显示一个或多个镜像详细信息 |
pull | 推送一个镜像到镜像仓库 |
rm | 移除一个或多个镜像 |
prune | 移除未使用的镜像。没有被标记或被任何容器引用的。 |
tag | 创建一个引用源镜像标记目标镜像 |
export | 导出容器文件系统到tar归档文件 |
import | 导入容器文件系统tar归档创建镜像 |
save | 保存一个或多个镜像到tar归档文件 |
load | 加载镜像来自tar归档或标准输入 |
创建容器常用选项:docker container run --help
选项 | 描述 |
---|---|
-i,-interactive | 交互式 |
-t,-tty | 分配一个伪终端 |
-d,-detach | 运行容器到后台 |
-e,-env | 设置环境变量 |
-p,-publish list | 发布容器端口到主机 |
-P,-publish-all | 发布容器所有EXPOSE端口到宿主机随机端口 |
-name string | 指定容器名称 |
-h,-hostname | 设置容器主机名 |
-ip string | 指定容器IP,只能用于自定义网络 |
-network | 连接容器到一个网络 |
-mount mount | 将文件系统附加到容器 |
-v,-volume list | 绑定挂载一个卷 |
-restart string | 容器退出时重启策略,默认no,可选值:[always|on-failure] |
docker create
通过 docker run <image name> 命令创建一个容器并运行其中的程序,但是有时候只是创建容器并不启动容器,所以 docker create 应运而生了。
示例:创建一个容器 (并且打印出容器 ID),但并不运行它:docker create -t -i fedora bash
运行该容器:docker start -a -i 容器id
通过 --security-opt 选项,运行容器时用户可自定义 SELinux 和 AppArmor 卷标和配置。
$ docker run --security-opt label:type:svirt_apache -i -t centos \ bash
上例只允许容器监听在 Apache 端口,这个选项的好处是用户不需要运行 docker 的时候指定--privileged选项,降低安全风险。
docker start | stop | kill | restart | pause | unpause | rm | commit | inspect | logs
参考文档:Docker Run Reference
导入、导出涉及的命令:export、import、save、load
命令:docker export [options] container
- 示例:docker export -o nginx_container_bak.tar nginx_container
- -o nginx_container_bak.tar 导出到的目标文件
nginx_container 源容器命令:docker import [options] file|URL| [REPOSITORY[:TAG]]
- 示例:docker import nginx_container_bak.tar nginx_container:v1.0
备份时:容器 => 镜像 => 本地文件
恢复时:本地文件 => 镜像 => 容器
删除 docker 容器中内容后打包镜像不变小? 在容器中部署某个软件时如果软件的压缩包比较大那我们可能需要把压缩包删除后再去做镜像,此时你会发现镜像还是两个压缩包左右的大小,甚至删除后镜像可能会更大,这个问题就很容易导致镜像越来越大无法维护,
这个跟docker的原理有关系,因为镜像是按照层存储,这样做最大好处很直观,版本更新时只需要下载软件的下一层相当于是软件的增量包大概是这个意思,而如果你不分层就需要下载软件的全量包这样的话十分麻烦。用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,也可以使用 docker import 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。
既然docker load会保存文件的历史记录和原数据,那我们使用docker import导入容器的快照就可以了。大概就是恢复到打包容器时候的样子,是一个快照,没有历史记录。这个是参考:https://yeasy.gitbook.io/docker_practice/container/import_export
镜像的导出、导入
命令:docker save [options] images [images...]
示例:docker save -o nginx.tar nginx:latest
示例:docker save > nginx.tar nginx:latest
其中 -o 和 > 表示输出到文件,nginx.tar 为目标文件,nginx:latest 是源镜像名(name:tag)命令:docker load [options]
示例:docker load -i nginx.tar
示例:docker load < nginx.tar
其中 -i 和 < 表示从文件输入。会成功导入镜像及相关元数据,包括tag信息
管理容器常用命令:docker container --help
选项 | 描述 |
---|---|
ls | 列出容器 |
inspect | 查看一个或多个容器详细信息 |
commit | 创建一个新镜像来自一个容器 |
cp | 拷贝文件/文件夹到一个容器 |
logs | 获取一个容器日志 |
port | 列出或指定容器端口映射 |
top | 显示一个容器运行的进程 |
stats | 显示容器资源使用统计 |
stop/start | 停止/启动一个或多个容器 |
rm | 删除一个或多个容器 |
$ sudo docker run -it ubuntu:14.04 /bin/bash
- docker run - 运行一个容器
- -t - 分配一个(伪)tty (link is external)
- -i - 交互模式 (so we can interact with it)
- ubuntu:14.04 - 使用 ubuntu 基础镜像 14.04
- /bin/bash - 运行命令 bash shell
注: ubuntu 会有多个版本,通过指定 tag 来启动特定的版本 [image]:[tag]
$ sudo docker ps # 查看当前运行的容器, ps -a 列出当前系统所有的容器
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6c9129e9df10 ubuntu:14.04 /bin/bash 6 minutes ago Up 6 minutes cranky_babbage
查看容器信息: docker inspect 容器id
方式 1: 使用 docker exec 命令进入docker容器( 最常用的方法,没有之一 )
命令格式:docker exec -it <container_id> /bin/bash
示例:
$ docker ps
$ docker exec -it mynginx /bin/bashdocker run 和 docker exec 的差异( https://www.cnblogs.com/miracle-luna/p/11111852.html )
- docker run :根据镜像 创建一个容器 并运行一个命令,操作的对象是 镜像;
- docker exec :在 运行的容器中 执行命令,操作的对象是 容器。
方式 2: 使用 docker attach 命令进入 容器
docker run -itd nginx-name mynginx
docker attach mynginx
# 或者
docker attach mynginx
方式 3: 使用 ssh 进入 docker 容器
docker 就是一个隔离的盒子,可以看做是最原始初始化的盒子,可以将它当做一个 mini 型 的 linux 虚拟机,我们在该盒子中安装我们需要的服务(例如:nginx、mysql、redis等),打包之后就形成了目前最为流行的 docker 应用容器。既然是一个linux 虚拟主机,那么我们就可以在该主机上面安装一个 ssh server 就可以通过 ssh 协议来连接该容器了。
不过这种方式也基本没什么人用,出力不讨好。哈哈,那是因为后面有一种既安全又便捷的命令用啦。
可以去看一下这个老外写的原因:为什么不需要在 Docker 容器中运行 sshd:https://www.oschina.net/translate/why-you-dont-need-to-run-sshd-in-docker?cmp
方式 4: 使用 nsenter 进入 docker 容器
对于 nsenter 网上有比较多且详细的介绍,这里我就说一下我自己理解。
个人理解 nsenter:通过容器在宿主机中的 pid 进行通讯
因此:nsenter 需要在宿主机安装,而 非 容器 或者 镜像# 安装nsenter
$ wget https://www.kernel.org/pub/linux/utils/util-linux/v2.24/util-linux-2.24.tar.gz
$ tar -xzvf util-linux-2.24.tar.gz
$ cd util-linux-2.24/
$ ./configure --without-ncurses
$ make nsenter
$ sudo cp nsenter /usr/local/bin$ nsenter --help
# nsenter可以访问另一个进程名称空间。因此我们需要获取容器的PID
sudo docker inspect -f {{.State.Pid}} 44fc0f0582d9 // 假设进程号为 4426
$ sudo nsenter --target 4426 --mount --uts --ipc --net --pid
# --target 4426 目标pid
在正在运行的 container 中运行 命令
docker exec --help
使用: docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Options:
-d, --detach Detached mode: run command in the background
--detach-keys string Override the key sequence for detaching a container
-e, --env list Set environment variables
--env-file list Read in a file of environment variables
-i, --interactive Keep STDIN open even if not attached
--privileged Give extended privileges to the command
-t, --tty Allocate a pseudo-TTY
-u, --user string Username or UID (format: <name|uid>[:<group|gid>])
-w, --workdir string Working directory inside the container
使用 docker exec 命令通过 Docker API 和 CLI 在运行的容器上运行程序。
$ docker exec -it ubuntu_bash bash
上例将在容器 ubuntu_bash 中创建一个新的 Bash 会话。
首先,查看有哪些容器:docker ps
然后,根据容器id查看该容器根目录下有哪些目录:docker exec 容器id ls -al /
拷贝某个文件目录或者文件:docker cp 容器id:/要拷贝的文件目录 /目的地
:https://blog.csdn.net/michaelwoshi/article/details/105180900
进入容器中端界面,手动创建带中文的文件,果不其然,中文显示被?代替了!
进入容器 查看字符集
# docker exec -it <container_id> /bin/bash
# locale
# locale -a
进入容器查看编码:这里借鉴别人的图, 没保存图片,发现不是UTF-8
临时修改编码:
export LANG=C.UTF-8
或者
LANG=C.UTF-8
source /etc/profile
再次查看编码已经生效,但是问题出现在这里了,使用临时方案修改的编码是 基于会话 的,也就是说对程序调用不起作用,只对当前窗口有效。
永久修改编码:最有效的就是重新生成镜像,并且添加系统环境。注意,经过验证,下面三个变量都需要设置,只设置LANG可能也还会报错。Dockerfile 添加下面三行:
最后效果:所有会话都是C.UTF-8编码的,程序也没再报错了。
示例:因为采用 docker pull http://docker.io/centos 下载的基础镜像,是不带中文环境的。
下面使用Dockerfile 构建一个新的带有中文环境的镜像,设置中文UTF-8字符集,解决乱码问题。
- # 基于centos:7
- FROM centos:7
- # 替换yum源
- ADD CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo
- #设置中文UTF-8字符集
- RUN yum -y install kde-l10n-Chinese \
- && yum -y reinstall glibc-common \
- && localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 \
- && echo 'LANG="zh_CN.UTF-8"' > /etc/locale.conf \
- && source /etc/locale.conf \
- && yum clean all
- ENV LANG=zh_CN.UTF-8 \
- LC_ALL=zh_CN.UTF-8
构建新的镜像:docker build -t centos_zh:7 .
启动容器,查看字符集,设置成功。
1. 查询镜像:docker images
可以看到所有已经存在的镜像的 ID (IMAGE ID)
2. 查询容器:docker ps -a
可以看到所有容器的 ID (CONTAINER ID)
3. 先删除容器:docker rm 容器ID
4. 再删除镜像:docker rmi 镜像ID
注意点:
1. 删除前需要保证容器是停止的 stop
2. 需要注意删除镜像和容器的命令不一样。
docker rm CONTAINER_ID 删除容器
docker rmi IMAGE_ID 删除镜像
3. 顺序需要先删除容器,再删除镜像
目标:上面已经下载了 helloworld 镜像,现在在镜像中输出 "hello word"。为了达到这个目的,需要在运行 "echo" 命令,输出"hello word"。
提示:docker run 命令有两个参数,一个是镜像名,一个是要在镜像中运行的命令。
正确的命令:$docker run learn/tutorial echo "hello word"
下一步在容器里面安装一个简单的程序(ping)。我们之前下载的 tutorial 镜像是基于 ubuntu 的,所以你可以使用 ubuntu 的 apt-get 命令来安装 ping 程序: apt-get install -y ping。
备注:apt-get 命令执行完毕之后,容器就会停止,但对容器的改动不会丢失。
目标:在 learn/tutorial 镜像里面安装 ping 程序。
提示:在执行 apt-get 命令的时候,要带上-y参数。如果不指定-y参数的话,apt-get命令会进入交互模式,需要用户输入命令来进行确认,但在 docker 环境中是无法响应这种交互的。
正确的命令:$docker run learn/tutorial apt-get install -y ping
Docker 的 containers 有两个参数用来把文件存储在宿主机上,即使在容器停止后文件也会保留:: volumes 和 bind mounts.
官方文档:https://docs.docker.com/storage/
从图中可以看出,docker 提供三种方式数据从宿主机挂载到容器中:
Volume
管理卷
docker volume create nginx-vol
docker volume ls
docker volume inspect nginx-vol用卷创建一个容器
# 新语法
docker run -d --name=nginx-test --mount src=nginx-vol,dst=/usr/share/nginx/html nginx
# 旧语法
docker run -d --name=nginx-test -v nginx-vol:/usr/share/nginx/html nginx清理
docker stop nginx-test
docker rm nginx-test
docker volume rm nginx-vol注意:
1. 如果没有指定卷,自动创建。
2. 建议使用 --mount,更通用。
Bind Mounts
用卷创建一个容器
# 新语法
docker run -d --name=nginx-test --mount type=bind,src=/app/wwwroot,dst=/usr/share/nginx/html nginx
# 旧语法
docker run -d --name=nginx-test -v /app/wwwroot:/usr/share/nginx/html nginxdocker run --privileged -it --network host -v /data/docker_container:/data/outer_data tsf/ubuntu20230926
验证绑定:docker inspect nginx-test
清理
docker stop nginx-test
docker rm nginx-test注意:
1.2 如果源文件/目录没有存在,不会自动创建,会抛出一个错误。
1.3 如果挂载目标在容器中非空目录,则该目录现有内容将被隐藏。
https://docs.docker.com/engine/admin/volumes/bind-mounts/#start-a-container-with-a-bind-mount
Volume特点
- 多个运行容器之间共享数据。
- 当容器停止或悲移除时,该卷依然存在。
- 多个容器可以同时挂载相同的卷。
- 当明确删除卷时,卷才会被删除。
- 将容器的数据存储在远程主机或其他存储上
- 将数据从一台Docker主机迁移到另一台时,先停止容器,然后备份卷的目录(/var/lib/docker/volumes)
Bind Mounts特点
- 从主机共享配置文件到容器。默认情况下,挂载主机/etc/resolv.conf到每个容器,提供DNS解析。
- 在Docker主机上的开发环境和容器之间共享源代码。例如,可以将Maven target目录挂载到容器中,每次在Docker主机上构建Maven项目时,容器都可以访问构建的项目包。
- 当Docker主机的文件或目录结构保证与容器所需的绑定挂载一致时。
docker 对 已经启动的容器 添加目录映射(挂载目录):https://blog.csdn.net/qq_39198749/article/details/127731024
简介:通过docker commit 命令保存对容器的修改
当对容器做了修改之后(通过在容器中运行某一个命令),可以把对容器的修改保存下来,这样下次就可以从保存后的最新状态运行该容器。docker 中保存状态的过程称之为 committing,它保存的新旧状态之间的区别,从而产生一个新的版本。
目标:首先使用 docker ps -l 命令获得安装完 ping 命令之后容器的 id。然后把这个镜像保存为 learn/ping。
提示:
1. 运行 docker commit,可以查看该命令的参数列表。
2. 你需要指定要提交保存容器的ID。(译者按:通过docker ps -l 命令获得)
3. 无需拷贝完整的id,通常来讲最开始的三至四个字母即可区分。(译者按:非常类似 git 里面的版本号)
正确的命令:$ docker commit 698 learn/ping
执行完 docker commit 命令之后,会返回新版本镜像的id号。
对一个镜像提交修改之后,就可以运行它里面新安装的命令了。到现在为止,你已经建立了一个完整的、自成体系的docker环境,并且安装了ping命令在里面。它可以在任何支持docker环境的系统中运行了。
目标:在新的镜像中运行 ping www.google.com 命令。
提示:一定要使用新的镜像名 learn/ping来运行ping命令。(译者按:最开始下载的learn/tutorial镜像中是没有ping命令的)
正确的命令:$ docker run lean/ping ping www.google.com
简介:使用docker ps命令可以查看所有正在运行中的容器列表,使用docker inspect命令我们可以查看更详细的关于某一个容器的信息。
现在你已经运行了一个docker容器,让我们来看下正在运行的容器。
使用 docker ps命令 可以查看所有正在运行中的容器列表,
使用 docker inspect命令 我们可以查看更详细的关于某一个容器的信息。
目标:查找某一个运行中容器的id,然后使用docker inspect命令查看容器的信息。
提示:可以使用镜像id的前面部分,不需要完整的id。无需拷贝完整的id,通常来讲最开始的三至四个字母即可区分
正确的命令:$ docker inspect efe
简介:我们也可以把我们自己编译的镜像发布到索引页面,一方面可以自己重用,另一方面也可以分享给其他人使用。
现在我们已经验证了新镜像可以正常工作,下一步我们可以将其发布到官方的索引网站。还记得我们最开始下载的learn/tutorial镜像吧,我们也可以把我们自己编译的镜像发布到索引页面,一方面可以自己重用,另一方面也可以分享给其他人使用。
目标:把 learn/ping 镜像发布到docker的index网站。
提示:
1. docker images 命令可以列出所有安装过的镜像。
2. docker push 命令可以将某一个镜像发布到官方网站。
3. 你只能将镜像发布到自己的空间下面。这个模拟器登录的是learn帐号。
预期的命令:$ docker push learn/ping
参考文档:https://docs.docker.com/network/
在容器运行时,每个容器都会分配一个特定的虚拟机口并桥接到 docker0。每个容器都会配置同 docker0 ip 相同网段的专用 ip 地址,docker0 的 IP 地址被用于所有容器的默认网关。
安装 Docker 以后,会默认创建三种网络,可以通过 docker network ls
查看。
docker 会在宿主机虚拟一个 docker容器的网卡(docker0),使用该网卡桥接Linux网卡进行上网。
docker 启动一个容器时会根据 docker 网桥的网段分配给容器一个IP地址,称为 Container-IP,同时 Docker网卡 是每个容器的默认网络网关。因为在同一宿主机内的容器都接入同一个网桥(网卡),这样容器之间就能够通过容器的Container-IP直接通信。
docker网桥是宿主机虚拟出来的,并不是真实存在的网络设备,外部网络是无法寻址到的,这也意味着外部网络无法通过直接Container-IP访问到容器。
如果容器希望外部能够访问到,可以通过映射容器端口到宿主主机(端口映射),即 docker run 创建容器时候通过 -p 或 -P 参数来启用,访问容器的时候就通过宿主机 IP:容器端口
访问容器。
容器网络 的 访问原理
Linux IP信息包过滤原理:
- Docker 主要通过 netfilter/iptables 实现网络通信。
- iptables 由 netfilter 和 iptables 组成,netfilter 组件是 Linux 内核集成的信息包过滤系统,
- 它维护一个信息包过滤表,这个表用于控制信息包过滤处理的规则集,
- 而iptables只是一个在用户空间的工具,用于增删改查这个过滤表的规则。
# 通过容器 id 获取 ip
$ sudo docker inspect <container_id> | grep IPAddress | cut -d '"' -f 4# -P 使用时需要指定 --expose 选项,指定需要对外提供服务的端口
$ sudo docker run -t -P --expose 22 --name server ubuntu:14.04
使用 docker run -P 自动绑定所有对外提供服务的容器端口,映射的端口将会从没有使用的端口池中 (49000..49900) 自动选择,你可以通过 docker ps、docker inspect <container_id>或者 docker port <container_id> <port> 确定具体的绑定信息
命令:$ sudo docker run -p [([<host_interface>:[host_port]])|(<host_port>):]<container_port>[/udp] <image> <cmd>
默认不指定绑定的ip时,则监听所有网络接口。
绑定 TCP 端口
# Bind TCP port 8080 of the container to TCP port 80
# on 127.0.0.1 of the host machine.
$ sudo docker run -p 127.0.0.1:80:8080 <image> <cmd>
# Bind TCP port 8080 of the container to a dynamically
# allocated TCP port on 127.0.0.1 of the host machine.
$ sudo docker run -p 127.0.0.1::8080 <image> <cmd>
# Bind TCP port 8080 of the container to TCP port 80
# on all available interfaces of the host machine.
$ sudo docker run -p 80:8080 <image> <cmd>
# Bind TCP port 8080 of the container to a dynamically
# allocated TCP port on all available interfaces
$ sudo docker run -p 8080 <image> <cmd>
绑定 UDP 端口
# Bind UDP port 5353 of the container to UDP port 53
# on 127.0.0.1 of the host machine.
$ sudo docker run -p 127.0.0.1:53:5353/udp <image> <cmd>
docker run 创建 Docker 容器时,可以用 --network 选项指定容器的网络模式
Docker 网络模式 | 说明 |
---|---|
bridge 参数:--net=bridge | (默认模式)。此模式会为每一个容器分配、设置IP等,并将容器连接到 docker0 虚拟网桥,通过 docker0网桥以及 Iptable nat表 配置与宿主机通信 |
host 参数:--net=host | 容器和宿主机共享 Network namespace。 容器将不会虚拟出自己的网卡,配置自己的IP 等,而是使用宿主机的IP和端口。 |
none 参数:--net=none | 容器有独立的 Network namespace,但是并没有对其进行任何网络设置,如分配veth pair和网桥连接,配置IP等。 该模式关闭了容器的网络功能。 |
container 参数:--net=container:NAMEorID | 容器和另外一个容器共享 Network namespace。 新创建的容器不会创建自己的网卡, 而是和一个指定的容器共享IP、端口范围。 |
Macvlan network | 容器具备 Mac 地址,使其显示为网络上的物理 设备 |
Overlay | (覆盖网络): 利用 vxlan 实现的 bridge 模式 |
创建容器默认使用的网络模式。在创建容器时指定参数 --net bridge 或 --network bridge
注意:镜像 一定要放在最后的参数位置,否则报错
命令:docker run --privileged -it your_image 在创建容器时添加特权模式:使用
--privileged
标志在创建容器时运行docker run
命令。这将授予容器内部的进程更高的特权级别,并允许设置实时调度策略。示例:sudo docker run --network host -it tsf/ubuntu
bridge 缺点:bridge 模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的,但容器通过宿主机的 NAT 规则后可以访问外网。
在该模式中,docker 守护进程创建了一个虚拟以太网桥 docker0
,新建的容器会自动桥接到这个接口,附加在其上的任何网卡之间都能自动转发数据包。
默认情况下,守护进程会创建一对 "对等虚拟设备接口 veth pair"
eth0
接口(容器的网卡),vethxxx
这样的名字命名,从而将宿主机上的所有容器都连接到这个内部网络上。Bridge 桥接模式的实现步骤主要如下:
容器中执行 ip address ( 执行结果:36: eth0@if37 )
宿主机执行 ip address ( 执行结果:37: vethdccd7b5@if36: )
通过以上的比较可以发现:守护进程会创建一对对等虚拟设备接口 veth pair
,将其中一个接口设置为容器的 eth0
接口(容器的网卡),另一个接口放置在宿主机的命名空间中,以类似 vethxxx
这样的名字命名。
同时,守护进程还会从网桥 docker0
的私有地址空间中分配一个 IP 地址和子网给该容器,并设置 docker0 的 IP 地址为容器的默认网关。也可以安装 sudo apt-get install bridge-utils 以后,通过 brctl show 命令查看网桥信息。
相关命令
docker inspect 容器名称 | ID 查看容器的 IP 地址和 Gateway 信息 ( 在NetworkSettings节点 )
docker network inspect bridge 查看所有 bridge 网络模式下的容器,在 Containers 节点中可以看到容器名称。
查看当前 docker0 ip:sudo ifconfig docker0
Docker 允许管理 docker0 桥接或者通过 -b 选项自定义桥接网卡,需要安装bridge-utils软件包。
基本步骤如下:
- # Stopping Docker and removing docker0
- $ sudo service docker stop
- $ sudo ip link set dev docker0 down
- $ sudo brctl delbr docker0 # Create our own bridge
- $ sudo brctl addbr bridge0
- $ sudo ip addr add 192.168.5.1/24 dev bridge0
- $ sudo ip link set dev bridge0 up # Confirming that our bridge is up and running
- $ ip addr show bridge0
-
- 4: bridge0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default
- link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff
- inet 192.168.5.1/24 scope global bridge0
- valid_lft forever preferred_lft forever # Tell Docker about it and restart (on Ubuntu)
-
- $ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker
- $ sudo service docker start
没有独立IP地址
。host最大的优势就是网络性能比较好
,docker host上已经使用的端口就不能再用了,网络的隔离性不好。--net host
或者 --network host
指定;host 模式是 bridge桥接模式很好的补充。采用host模式的Docker Container,可以直接使用宿主机的IP地址与外界进行通信,若宿主机的 eth0 有一个公有IP,那么容器也拥有这个公有IP。同时容器内服务的端口也可以使用宿主机的端口,无需额外进行NAT转换。
host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。
例如,在 10.10.101.105/24 的机器上用 host 模式启动一个含有 web 应用的 Docker 容器,监听 tcp 80 端口。当我们在容器中执行任何类似 ifconfig 命令查看网络环境时,看到的都是宿主机上的信息。而外界访问容器中的应用,则直接使用 10.10.101.105:80 即可,不用任何 NAT 转换,就如直接跑在宿主机中一样。但是,容器的其他方面,如文件系统、进程列表等还是和宿主机隔离的。
host 网络缺点:使用 Host 模式的容器不再拥有隔离、独立的网络环境。虽然可以让容器内部的服务和传统情况无差别、无改造的使用,但是由于网络隔离性的弱化,该容器会与宿主机共享竞争网络栈的使用; 另外,容器内部将不再拥有所有的端口资源,原因是部分端口资源已经被宿主机本身的服务占用,还有部分端口已经用以bridge网络模式容器的端口映射。
Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。
这种类型的网络没有办法联网,封闭的网络能很好的保证容器的安全性。
在这种模式下,Docker 容器拥有自己的 Network Namespace,但是,并不为 Docker容器进行任何网络配置。也就是说,这个 Docker 容器没有网卡、IP、路由等信息。需要我们自己为 Docker 容器添加网卡、配置 IP 等。
一种特殊 host 网络模式, ontainer网络模式是Docker中一种较为特别的网络的模式。在容器创建时使用– network=container:vm1指定。(vm1指定的是运行的容器名)
--net container:已运行的容器名称|ID
或者 --network container:已运行的容器名称|ID
指定;处于这个模式下的 Docker 容器会共享一个网络环境,这样两个容器之间可以使用localhost高效快速通信。Container 网络模式即新创建的容器不会创建自己的网卡,配置自己的 IP,而是和一个指定的容器共享 IP、端口范围等。同样两个容器除了网络方面相同之外,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过 lo 网卡设备通信。
缺点:Container网络模式没有改善容器与宿主机以外世界通信的情况(和桥接模式一样,不能连接宿主机以外的其他设备)。
这个模式指定新创建的容器和已经存在的一个容器共享一个Network Namespace,而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。 同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo网卡设备通信
- # 网桥名称
- br_name=br0
- # 添加网桥
- brctl addbr $br_name
- # 给网桥设置IP
- ip addr add 192.168.1.120/24 dev $br_name
- # 删除已存在的eth0网卡配置
- ip addr del 192.168.1.120/24 dev eth0
- # 激活网卡
- ip link set $br_name up
- # 添加eth0到网桥
- brctl addif $br_name eth0
- # 添加路由
- ip route add default via 192.168.1.1 dev br0
-
- 还需要在Docker启动时桥街这个网桥:
- # vi /usr/lib/systemd/system/docker.service
- ExecStart=/usr/bin/dockerd -b=br0
- # systemctl restart docker
- # vi /etc/sysconfig/network-scripts/ifcfg-eth0
- DEVICE=eth0
- TYPE=Ethernet
- ONBOOT=yes
- BRIDGE=br0
-
- # vi /etc/sysconfig/network-scripts/ifcfg-br0
- DEVICE=br0
- TYPE=Bridge
- ONBOOT=yes
- BOOTPROTO=static
- IPADDR=192.168.3.10
- NETMASK=255.255.255.0
- GATEWAY=192.168.3.1
- DNS1=114.114.114.114
- C_ID=$(docker run -itd --net=none ubuntu)
- C_PID=$(docker inspect -f '{{.State.Pid}}' $C_ID)
- # 创建network namespace目录并将容器的network namespace软连接到此目录,以便ip netns命令读取 mkdir -p /var/run/netns
- ln -s /proc/$C_PID/ns/net /var/run/netns/$C_PID
- # 添加虚拟网卡veth+容器PID,类型是veth pair,名称是vp+容器PID
- ip link add veth$C_PID type veth peer name vp$C_PID
- # 添加虚拟网卡到br0网桥
- brctl addif br0 veth$C_PID
- # 激活虚拟网卡
- ip link set veth$C_PID up
- # 设置容器网络信息
- IP='192.168.0.123/24'
- GW='192.168.0.1'
- # 给进程配置一个network namespace
- ip link set vp$C_PID netns $C_PID
- # 在容器进程里面设置网卡信息
- ip netns exec $C_PID ip link set dev vp$C_PID name eth0
- ip netns exec $C_PID ip link set eth0 up
- ip netns exec $C_PID ip addr add $IP dev eth0
- ip netns exec $C_PID ip route add default via 192.168.1.1
- git clone https://github.com/jpetazzo/pipework.git cp pipework/pipework /usr/local/bin/
- docker run -itd --net=none --name test01 ubuntu pipework br0 test01 192.168.0.123/24@192.168.0.1
查看已经建立的网络对象
docker network ls
docker network ls --no-trunc
docker network ls -f 'driver=host'
创建新的网络对象:docker network create -d bridge my-bridge
删除一个或多个网络:docker network rm NETWORK [NETWORK...]
查看一个或多个网络的详细信息:
docker network inspect [OPTIONS] NETWORK [NETWORK...]
docker inspect [OPTIONS] NETWORK [NETWORK...]
为启动的容器指定网络模式:docker run/create --network NETWORK
网络的 连接/断开:
docker network connect [OPTIONS] NETWORK CONTAINER
docker network disconnect [OPTIONS] NETWORK CONTAINER
作为演示,使用nginx景象进行
拉取镜像:docker pull nginx:1.19.3-alpine
运行镜像:docker run -itd --name nginx1 nginx:1.19.3-alpine
容器创建时IP地址的分配:docker network inspect bridge
执行命令:brctl show
容器连接图
docker run -itd --name nginx1 nginx:1.19.3-alpine
docker run -itd --name nginx2 nginx:1.19.3-alpine
docker network inspect bridge
docker exec -it nginx1 sh
ping 172.17.0.2
docker exec -it nginx2 sh
ping 172.17.0.2
ping www.baidu.com
ping nginx1
容器之间要互相通信,必须要有属于同一个网络的网卡。
首先创建两个基于默认的 bridge
网络模式的容器。
docker run -di --name default_bbox01 busybox
docker run -di --name default_bbox02 busybox
通过 docker network inspect bridge
查看两容器的具体 IP 信息。
然后测试两容器间是否可以进行网络通信。
经过测试,从结果得知两个属于同一个网络的容器是可以进行网络通信的,但是 IP 地址可能是不固定的,有被更改的情况发生,那容器内所有通信的 IP 地址也需要进行更改,能否使用容器名称进行网络通信?继续测试。
经过测试,从结果得知使用容器进行网络通信是不行的,那怎么实现这个功能呢?
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用 --name
为容器命名即可。
但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。
我们先基于 bridge
网络模式创建自定义网络 custom_network
,然后创建两个基于自定义网络模式的容器。
docker run -di --name custom_bbox01 --net custom_network busybox
docker run -di --name custom_bbox02 --net custom_network busybox
通过 docker network inspect custom_network
查看两容器的具体 IP 信息。
然后测试两容器间是否可以进行网络通信,分别使用具体 IP 和容器名称进行网络通信。
经过测试,从结果得知两个属于同一个自定义网络的容器是可以进行网络通信的,并且可以使用容器名称进行网络通信。
那如果此时我希望 bridge
网络下的容器可以和 custom_network
网络下的容器进行网络又该如何操作?其实答案也非常简单:让 bridge
网络下的容器连接至新的 custom_network
网络即可。
docker network connect custom_network default_bbox01
后面就该学习 Docker 进阶部分的内容 Docker Compose 和 Docker Swarm。
docker stop nginx1 nginx2
先启动nginx2,在启动
nginx1 docker start nginx2
docker start nginx1
docker network inspect bridge
docker network create -d bridge test-bridge
上面命令参数-d 是指DRIVER的类型,后面的test-bridge是network的自定义名称,这个和docker0是类似的。下面开始介绍,如何把容器连接到test-bridge这个网络。
启动一个nginx的容器nginx3,并通过参数network connect来连接lagou-bridge网络。在启动容器 nginx3之前,我们查看目前还没有容器连接到了test-bridge这个网络上。
brctl show
docker network ls
docker network inspect test-bridge
docker run -itd --name nginx3 --network test-bridge nginx:1.19.3-alpine
brctl show
docker network inspect test-bridg
把一个运行中容器连接到test-bridge网络
docker network connect test-bridge nginx2
docker network inspect test-bridge
docker exec -it nginx2 sh
ping nginx3
docker exec -it nginx3 sh
ping nginx2
虽然 Docker 提供的默认网络使用比较简单,但是为了保证各容器中应用的安全性,在实际开发中更推荐使用自定义的网络进行容器管理,以及启用容器名称到 IP 地址的自动 DNS 解析。
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用
--name
为容器命名即可。
但是使用 Docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的,所以我们就需要自定义网络。
通过 docker network create
命令可以创建自定义网络模式,命令提示如下:
执行命令:sudo docker network --help
用法: docker network COMMAND
作用:管理网络
命令:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks运行 docker network COMMAND --help 获取更多帮助朱
进一步查看 docker network create --help 命令使用详情,发现可以通过 --driver 指定网络模式且默认是 bridge 网络模式,
创建一个基于
bridge
网络模式的自定义网络模式custom_network
,完整命令:docker network create custom_network
查看网络模式:docker network ls
通过自定义网络模式
custom_network
创建容器:docker run -di --name bbox05 --net custom_network my_container查看容器的网络信息( 在
NetworkSettings
节点中 ):docker inspect 容器名称 | ID
为容器连接新的网络模式:docker network connect 网络名称 容器名称
示例:docker network connect bridge bbox05
通过
docker inspect 容器名称|ID
再次查看容器的网络信息,新增加了默认的bridge
。
断开网络:docker network disconnect 网络名称 容器名称
示例:docker network disconnect custom_network bbox05
通过 docker inspect 容器名称|ID 再次查看容器的网络信息,发现只剩下默认的 bridge。
可以通过
docker network rm 网络名称
命令移除自定义网络模式,网络模式移除成功会返回网络模式名称。示例:docker network rm custom_network
注意:如果通过某个自定义网络模式创建了容器,则该网络模式无法删除。
docker network create -d bridge --subnet=172.172.0.0/24 --gateway 172.172.0.1 network
172.172.0.0/24
docker network ls
docker run -itd --name nginx3 -p 80:80 --net network --ip 172.172.0.10
// nginx:1.19.3-alpine
// --net mynetwork:选择存在的网络
// --ip 172.172.0.10:给nginx分配固定的IP地址
docker network inspect network
docker stop nginx3
docker start nginx3
docker network inspect network
Docker 可以通过 Dockerfile 的内容来自动构建镜像。Dockerfile 是一个包含创建镜像所有命令的文本文件,通过docker build命令可以根据 Dockerfile 的内容构建镜像,在介绍如何构建之前先介绍下 Dockerfile 的基本语法结构。
Dockerfile 常用指令
指令 | 描述 |
---|---|
FROM | 构建新镜像是基于那个镜像 |
MAINTAINER | 镜像维护者姓名或邮箱地址 |
RUN | 构建镜像时运行的Shell命令 |
CMD | 运行容器时执行,如果有多个CMD指令,最后一个生效 |
COPY | 拷贝文件或目录到镜像中 |
ADD | 复制本地主机文件、目录或者远程文件 URLS 从 并且添加到容器指定路径中 。 |
ENTRYPOINT | 配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖,而 CMD 是可以被覆盖的。如果需要覆盖,则可以使用 docker run --entrypoint 选项。 |
ENV | 设置环境变量 |
USER | 为RUN、CMD和ENTRPOTIN执行命令指定运行用户 |
EXPOSE | 声明容器运行的服务端口 |
HEALTHCHECK | 容器中服务健康检查 |
WORKDIR | 为RUN、CMD、ENTRYPOTIN、COPY和ADD设置工作目录 |
ENTRYPOIN | 运行容器时执行,如果有多个ENTRYPOIN指令,最后一个生效 |
VOLUME | |
ONBUILD |
build 构建 镜像
Usage:docker build [OPTIONS] PATH | URL | - [flags]
Options:
-t, --tag list # 镜像名称
-f,--file string #指定Dockerfile文件位置
# docker build .
# docker bulid -t shykes/myapp .
# docker bulid -t shykes/myapp -f /path/Dockerfile /path
# docker bulid -t shykes/myapp http://www.example.com/Dockerfile
用法:FROM <image>
指定创建镜像的用户。用法:MAINTAINER <name>
RUN 有两种使用方式
每条 RUN 指令将在当前镜像基础上执行指定命令,并提交为新的镜像,后续的RUN都在之前RUN提交后的镜像为基础,镜像是分层的,可以通过一个镜像的任何一个历史提交点来创建,类似源码的版本控制。
exec 方式会被解析为一个 JSON 数组,所以必须使用双引号而不是单引号。exec 方式不会调用一个命令 shell,所以也就不会继承相应的变量,如:
RUN [ "echo", "$HOME" ]
这种方式是不会达到输出 HOME 变量的,正确的方式应该是这样的
RUN [ "sh", "-c", "echo", "$HOME" ]
RUN 产生的缓存在下一次构建的时候是不会失效的,会被重用,可以使用 --no-cache 选项,即 docker build --no-cache,如此便不会缓存。
CMD 有三种使用方式:
CMD 指定在 Dockerfile 中只能使用一次,如果有多个,则只有最后一个会生效。
CMD 的目的是为了在启动容器时提供一个默认的命令执行选项。如果用户启动容器时指定了运行的命令,则会覆盖掉 CMD 指定的命令。
CMD 会在启动容器的时候执行,build 时不执行,而 RUN 只是在构建镜像的时候执行,后续镜像构建完成之后,启动容器就与 RUN 无关了,
这个初学者容易弄混这个概念,这里简单注解一下。
EXPOSE <port> [<port>...]
告诉 Docker 服务端容器对外映射的本地端口,需要在 docker run 的时候使用-p或者-P选项生效。
- ENV <key> <value> # 只能设置一个变量
-
- ENV <key>=<value> ... # 允许一次设置多个变量
指定一个环节变量,会被后续RUN指令使用,并在容器运行时保留。
例子:
- ENV myName="John Doe" myDog=Rex\ The\ Dog \
-
- myCat=fluffy
等同于
- ENV myName John Doe
-
- ENV myDog Rex The Dog
-
- ENV myCat fluffy
ADD <src>... <dest>
ADD 复制本地主机文件、目录或者远程文件 URLS 从 并且添加到容器指定路径中 。
支持通过 GO 的正则模糊匹配,具体规则可参见 Go filepath.Match
- ADD hom* /mydir/ # adds all files starting with "hom"
-
- ADD hom?.txt /mydir/ # ? is replaced with any single character
COPY <src>... <dest>
COPY 复制新文件或者目录从 并且添加到容器指定路径中 。用法同 ADD,唯一的不同是不能指定远程文件 URLS。
配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖,而 CMD 是可以被覆盖的。如果需要覆盖,则可以使用 docker run --entrypoint 选项。
每个 Dockerfile 中只能有一个ENTRYPOINT,当指定多个时,只有最后一个生效。
Exec form ENTRYPOINT 例子
通过 ENTRYPOINT 使用 exec form 方式设置稳定的默认命令和选项,而使用 CMD 添加默认之外经常被改动的选项。
- FROM ubuntu
-
- ENTRYPOINT ["top", "-b"]
-
- CMD ["-c"]
通过 Dockerfile 使用 ENTRYPOINT 展示前台运行 Apache 服务
- FROM debian:stable
-
- RUN apt-get update && apt-get install -y --force-yes apache2
-
- EXPOSE 80 443
-
- VOLUME ["/var/www", "/var/log/apache2", "/etc/apache2"]
-
- ENTRYPOINT ["/usr/sbin/apache2ctl", "-D", "FOREGROUND"]
Shell form ENTRYPOINT 例子
这种方式会在 /bin/sh -c 中执行,会忽略任何 CMD 或者 docker run 命令行选项,为了确保 docker stop 能够停止长时间运行 ENTRYPOINT 的容器,确保执行的时候使用 exec 选项。
- FROM ubuntu
-
- ENTRYPOINT exec top -b
如果在 ENTRYPOINT 忘记使用 exec 选项,则可以使用 CMD 补上:
- FROM ubuntu
-
- ENTRYPOINT top -b
-
- CMD --ignored-param1 # --ignored-param2 ... --ignored-param3 ... 依此类推
VOLUME ["/data"]
创建一个可以从本地主机或其他容器挂载的挂载点,后续具体介绍。
USER daemon
指定运行容器时的用户名或 UID,后续的RUN、CMD、ENTRYPOINT也会使用指定用户。
WORKDIR /path/to/workdir
为后续的RUN、CMD、ENTRYPOINT指令配置工作目录。可以使用多个WORKDIR指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。
- WORKDIR /a
-
- WORKDIR b
-
- WORKDIR c
-
- RUN pwd
最终路径是/a/b/c。
WORKDIR指令可以在ENV设置变量之后调用环境变量:
- ENV DIRPATH /path
-
- WORKDIR $DIRPATH/$DIRNAME
最终路径则为 /path/$DIRNAME。
ONBUILD [INSTRUCTION]
配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。
例如,Dockerfile 使用如下的内容创建了镜像 image-A:
- [...]
-
- ONBUILD ADD . /app/src
-
- ONBUILD RUN /usr/local/bin/python-build --dir /app/src
-
- [...]
如果基于 image-A 创建新的镜像时,新的 Dockerfile 中使用 FROM image-A 指定基础镜像时,会自动执行 ONBUILD 指令内容,等价于在后面添加了两条指令。
- # Automatically run the following
-
- ADD . /app/src
-
- RUN /usr/local/bin/python-build --dir /app/src
使用ONBUILD指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild。
- # Nginx
- #
- # VERSION 0.0.1
-
- FROM ubuntu
- MAINTAINER Victor Vieux <victor@docker.com>
-
- RUN apt-get update && apt-get install -y inotify-tools nginx apache2 openssh-server
-
- # Firefox over VNC
- #
-
- # VERSION 0.3
-
- FROM ubuntu
-
- # Install vnc, xvfb in order to create a 'fake' display and firefox
- RUN apt-get update && apt-get install -y x11vnc xvfb firefox
- RUN mkdir ~/.vnc
-
- # Setup a password
- RUN x11vnc -storepasswd 1234 ~/.vnc/passwd
-
- # Autostart firefox (might not be the best way, but it does the trick)
- RUN bash -c 'echo "firefox" >> /.bashrc'
-
- EXPOSE 5900
- CMD ["x11vnc", "-forever", "-usepw", "-create"]
-
- # Multiple images example
- #
- # VERSION 0.1
-
- FROM ubuntu
- RUN echo foo > bar
- # Will output something like ===> 907ad6c2736f
-
- FROM ubuntu
- RUN echo moo > oink
- # Will output something like ===> 695d7793cbe4
-
- # You᾿ll now have two images, 907ad6c2736f with /bar, and 695d7793cbe4 with
- # /oink.
- $ docker build --help
-
-
- Usage: docker build [OPTIONS] PATH | URL | -
-
-
- Build a new image from the source code at PATH
-
- # 移除过渡容器,即使构建失败
- --force-rm=false Always remove intermediate containers, even after unsuccessful builds
-
- --no-cache=false Do not use cache when building the image # 不实用 cache
-
- -q, --quiet=false Suppress the verbose output generated by the containers
-
- # 构建成功后移除过渡层容器
- --rm=true Remove intermediate containers after a successful build
-
- -t, --tag="" Repository name (and optionally a tag) to be applied to the resulting image in case of success
参考文档:Dockerfile Reference
为了在docker build过程中更快上传和更加高效,应该使用一个.dockerignore文件用来排除构建镜像时不需要的文件或目录。例如,除非.git在构建过程中需要用到,否则你应该将它添加到.dockerignore文件中,这样可以节省很多时间。
为了降低复杂性、依赖性、文件大小以及构建时间,应该避免安装额外的或不必要的包。例如,不需要在一个数据库镜像中安装一个文本编辑器。
在大多数情况下,一个容器应该只单独跑一个程序。解耦应用到多个容器使其更容易横向扩展和重用。如果一个服务依赖另外一个服务,可以参考 Linking Containers Together。
我们知道每执行一个指令,都会有一次镜像的提交,镜像是分层的结构,对于Dockerfile,应该找到可读性和最小化层之间的平衡。
如果可能,通过字母顺序来排序,这样可以避免安装包的重复并且更容易更新列表,另外可读性也会更强,添加一个空行使用\换行:
- RUN apt-get update && apt-get install -y \
- bzr \
- cvs \
- git \
- mercurial \
- subversion
镜像构建过程中会按照Dockerfile的顺序依次执行,每执行一次指令 Docker 会寻找是否有存在的镜像缓存可复用,如果没有则创建新的镜像。如果不想使用缓存,则可以在docker build时添加--no-cache=true选项。
从基础镜像开始就已经在缓存中了,下一个指令会对比所有的子镜像寻找是否执行相同的指令,如果没有则缓存失效。在大多数情况下只对比Dockerfile指令和子镜像就足够了。ADD和COPY指令除外,执行ADD和COPY时存放到镜像的文件也是需要检查的,完成一个文件的校验之后再利用这个校验在缓存中查找,如果检测的文件改变则缓存失效。RUN apt-get -y update命令只检查命令是否匹配,如果匹配就不会再执行更新了。
为了有效地利用缓存,你需要保持你的 Dockerfile 一致,并且尽量在末尾修改。
Dockerfile 指令
例子:
- RUN apt-get update && apt-get install -y \
- aufs-tools \
- automake \
- btrfs-tools \
- build-essential \
- curl \
- dpkg-sig \
- git \
- iptables \
- libapparmor-dev \
- libcap-dev \
- libsqlite3-dev \
- lxc=1.0* \
- mercurial \
- parallel \
- reprepro \
- ruby1.9.1 \
- ruby1.9.1-dev \
- s3cmd=1.1.0*
ENV也可以这样定义变量:
- ENV PG_MAJOR 9.3
- ENV PG_VERSION 9.3.4
- RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
- ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH
如 不推荐 这种方式:
- ADD http://example.com/big.tar.xz /usr/src/things/
-
- RUN tar -xJf /usr/src/things/big.tar.xz -C /usr/src/things
-
- RUN make -C /usr/src/things all
推荐使用 curl 或者 wget 替换,使用如下方式:
- RUN mkdir -p /usr/src/things \
-
- && curl -SL http://example.com/big.tar.gz \
-
- | tar -xJC /usr/src/things \
-
- && make -C /usr/src/things all
如果不需要添加 tar 文件,推荐使用COPY。
参考文档:
- FROM centos:7
- RUN yum install -y gcc gcc-c++ make openssl-devel pcre-devel dg-devel iproute net-tools telnet wget curl && yum clean all && rm -rf /var/cache/yum/*
- RUN wget http://nginx.org/download/nginx-1.15.5.tar.gz && tar zxf nginx-1.15.5.tar.gz && cd nginx-1.15.5 && ./configure --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module && make && make install
- ENV PATH $PATH:/usr/local/nginx/sbin
- WORKDIR /usr/local/nginx
- EXPOSE 80
- CMD ["./sbin/nginx","-g","daemon off;"]
- docker build -t nginx:v1 -f Dockerfile .
- # Successfully built 373a0bdefe50
- # Successfully tagged nginx:v1
-
- [root@localhost /]# docker image ls
- REPOSITORY TAG IMAGE ID CREATED SIZE
- nginx v1 373a0bdefe50 56 seconds ago 335MB
- <none> <none> d582c01a839f 4 minutes ago 200MB
- nginx latest 62f816a209e6 2 days ago 109MB
- centos 7 75835a67d134 4 weeks ago 200MB
- hello-world latest 4ab4c602aa5e 2 months ago 1.84kB
- [root@localhost /]# docker run -d --name nginx100 -p 80:80 nginx:v1
- a29daf4614116f7dba18461e871d1a45ec9b55f722a98bead791c12294b9f1d3
- [root@localhost /]# docker ps -l
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- a29daf461411 nginx:v1 "./sbin/nginx -g 'da…" 6 seconds ago Up 5 seconds 0.0.0.0:80->80/tcp nginx100
-
-
- [root@localhost /]# curl -I http://192.168.56.146/
- HTTP/1.1 200 OK
- Server: nginx/1.15.5
- Date: Fri, 09 Nov 2018 12:34:03 GMT
- Content-Type: text/html
- Content-Length: 612
- Last-Modified: Fri, 09 Nov 2018 12:29:20 GMT
- Connection: keep-alive
- ETag: "5be57da0-264"
- Accept-Ranges: bytes
- FROM centos:7
- RUN yum install -y gcc gcc-c++ make gd-devel libxml2-devel libcurl-devel libjpeg-devel libpng-devel openssl-devel
- ADD php-5.6.31.tar.gz /tmp/
-
- RUN cd /tmp/php-5.6.31 && \
- ./configure --prefix=/usr/local/php \
- --with-config-file-path=/usr/local/php/etc \
- --with-mysql --with-mysqli \
- --with-openssl --with-zlib --with-curl --with-gd \
- --with-jpeg-dir --with-png-dir --with-iconv \
- --enable-fpm --enable-zip --enable-mbstring && \
- make -j 4 && \
- make install && \
- cp /usr/local/php/etc/php-fpm.conf.default /usr/local/php/etc/php-fpm.conf && \
- sed -i "s/127.0.0.1/0.0.0.0/" /usr/local/php/etc/php-fpm.conf && \
- sed -i "21a \daemonize = no" /usr/local/php/etc/php-fpm.conf
- COPY php.ini /usr/local/php/etc
-
- RUN rm -rf /tmp/php-5.6.31* && yum clean all
-
- WORKDIR /usr/local/php
- EXPOSE 9000
- CMD ["./sbin/php-fpm", "-c", "/usr/local/php/etc/php-fpm.conf"]
docker build -t php:v1 -f dockerfile .
- [root@localhost php]# docker run -d --name php01 php:v1
- ed5276251a6bf5124efba4b314d29a2dbfd12ddc0e0c3371ea16184a07c12bbc
- [root@localhost php]# docker ps -l
- CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
- ed5276251a6b php:v1 "./sbin/php-fpm -c /…" 5 seconds ago Up 4 seconds 9000/tcp php01
- [root@localhost php]# docker exec -it php01 bash
- [root@ed5276251a6b php]# ls
- bin etc include lib php sbin var
- [root@ed5276251a6b php]# sbin/php-fpm -v
- PHP 5.6.31 (fpm-fcgi) (built: Nov 10 2018 01:10:28)
- Copyright (c) 1997-2016 The PHP Group
- Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies
- FROM centos:7
-
- ADD jdk-8u45-linux-x64.tar.gz /usr/local
- ENV JAVA_HOME /usr/local/jdk1.8.0_45
-
- ADD apache-tomcat-8.0.46.tar.gz /usr/local
- COPY server.xml /usr/local/apache-tomcat-8.0.46/conf
-
- RUN rm -f /usr/local/*.tar.gz
-
- WORKDIR /usr/local/apache-tomcat-8.0.46
- EXPOSE 8080
- ENTRYPOINT ["./bin/catalina.sh", "run"]
docker build -t tomcat:v1 -f Dockerfile .
- [root@localhost tomcat]# docker run -d --name tomcat01 -p 8080:8080 tomcat:v1
- b9ba85bc64748c90ebe8df19e41c36490881cbcf174dab00d127ae6bf1141814
- [root@localhost tomcat]# curl -I http://192.168.56.146:8080
- HTTP/1.1 200 OK
- Server: Apache-Coyote/1.1
- Content-Type: text/html;charset=UTF-8
- Transfer-Encoding: chunked
- Date: Sat, 10 Nov 2018 01:26:59 GMT
docker 管理数据的方式有两种:
数据卷是一个或多个容器专门指定绕过Union File System的目录,为持续性或共享数据提供一些有用的功能:
添加一个数据卷
你可以使用-v选项添加一个数据卷,或者可以使用多次-v选项为一个 docker 容器运行挂载多个数据卷。
- $ sudo docker run --name data -v /data -t -i ubuntu:14.04 /bin/bash
- # 创建数据卷绑定到到新建容器,新建容器中会创建 /data 数据卷 bash-4.1
-
- # ls -ld /data/
- drwxr-xr-x 2 root root 4096 Jul 23 06:59 /data/
-
- bash-4.1# df -Th
- Filesystem Type Size Used Avail Use% Mounted on
- ... ...
- ext4 91G 4.6G 82G 6% /data
创建的数据卷可以通过docker inspect获取宿主机对应路径
- $ sudo docker inspect data
- ... ... "Volumes": { "/data": "/var/lib/docker/vfs/dir/151de401d268226f96d824fdf444e77a4500aed74c495de5980c807a2ffb7ea9" }, # 可以看到创建的数据卷宿主机路径 ... ...
或者直接指定获取
- $ sudo docker inspect --format="{{ .Volumes }}" data
- map[/data: /var/lib/docker/vfs/dir/151de401d268226f96d824fdf444e77a4500aed74c495de5980c807a2ffb7ea9]
挂载宿主机目录为一个数据卷
-v 选项除了可以创建卷,也可以挂载当前主机的一个目录到容器中。
- $ sudo docker run --name web -v /source/:/web -t -i ubuntu:14.04 /bin/bash
- bash-4.1# ls -ld /web/
- drwxr-xr-x 2 root root 4096 Jul 23 06:59 /web/
- bash-4.1# df -Th
- ... ...
- ext4 91G 4.6G 82G 6% /web
- bash-4.1# exit
默认挂载卷是可读写的,可以在挂载时指定只读
$ sudo docker run --rm --name test -v /source/:/test:ro -t -i ubuntu:14.04 /bin/bash
如果你有一些持久性的数据并且想在容器间共享,或者想用在非持久性的容器上,最好的方法是创建一个数据卷容器,然后从此容器上挂载数据。
创建数据卷容器
$ sudo docker run -t -i -d -v /test --name test ubuntu:14.04 echo hello
使用--volumes-from选项在另一个容器中挂载 /test 卷。不管 test 容器是否运行,其它容器都可以挂载该容器数据卷,当然如果只是单独的数据卷是没必要运行容器的。
$ sudo docker run -t -i -d --volumes-from test --name test1 ubuntu:14.04 /bin/bash
添加另一个容器
$ sudo docker run -t -i -d --volumes-from test --name test2 ubuntu:14.04 /bin/bash
也可以继承其它挂载有 /test 卷的容器
$ sudo docker run -t -i -d --volumes-from test1 --name test3 ubuntu:14.04 /bin/bash
备份
- $ sudo docker run --rm --volumes-from test -v $(pwd):/backup ubuntu:14.04 tar cvf /backup/test.tar /test
- tar: Removing leading `/' from member names
- /test/
- /test/b
- /test/d
- /test/c
- /test/a
启动一个新的容器并且从test容器中挂载卷,然后挂载当前目录到容器中为 backup,并备份 test 卷中所有的数据为 test.tar,执行完成之后删除容器--rm,此时备份就在当前的目录下,名为test.tar。
$ ls # 宿主机当前目录下产生了 test 卷的备份文件 test.tar test.tar
恢复
你可以恢复给同一个容器或者另外的容器,新建容器并解压备份文件到新的容器数据卷
- $ sudo docker run -t -i -d -v /test --name test4 ubuntu:14.04 /bin/bash
-
- $ sudo docker run --rm --volumes-from test4 -v $(pwd):/backup ubuntu:14.04 tar xvf /backup/test.tar -C / # 恢复之前的文件到新建卷中,执行完后自动删除容器 test/ test/b test/d test/c test/a
Volume 只有在下列情况下才能被删除:
否则,会在/var/lib/docker/vfs/dir目录中遗留很多不明目录。
参考文档:
docker 允许把多个容器连接在一起,相互交互信息。docker 链接会创建一种容器父子级别的关系,其中父容器可以看到其子容器提供的信息。
在创建容器时,如果不指定容器的名字,则默认会自动创建一个名字,这里推荐给容器命名:
可以通过--name选项给容器自定义命名:
- $ sudo docker run -d -t -i --name test ubuntu:14.04 bash
-
- $ sudo docker inspect --format="{{ .Nmae }}" test
-
- /test
注:容器名称必须唯一,即你只能命名一个叫test的容器。如果你想复用容器名,则必须在创建新的容器前通过docker rm删除旧的容器或者创建容器时添加--rm选项。
链接允许容器间安全通信,使用--link选项创建链接。
$ sudo docker run -d --name db training/postgres
基于 training/postgres 镜像创建一个名为 db 的容器,然后下面创建一个叫做 web 的容器,并且将它与 db 相互连接在一起
$ sudo docker run -d -P --name web --link db:db training/webapp python app.py
--link <name or id>:alias选项指定链接到的容器。
查看 web 容器的链接关系:
- $ sudo docker inspect -f "{{ .HostConfig.Links }}" web
-
- [/db:/web/db]
可以看到 web 容器被链接到 db 容器为/web/db,这允许 web 容器访问 db 容器的信息。
容器之间的链接实际做了什么?一个链接允许一个源容器提供信息访问给一个接收容器。在本例中,web 容器作为一个接收者,允许访问源容器 db 的相关服务信息。Docker 创建了一个安全隧道而不需要对外公开任何端口给外部容器,因此不需要在创建容器的时候添加-p或-P指定对外公开的端口,这也是链接容器的最大好处,本例为 PostgreSQL 数据库。
Docker 主要通过以下两个方式提供连接信息给接收容器:
环境变量
当两个容器链接,Docker 会在目标容器上设置一些环境变量,以获取源容器的相关信息。
首先,Docker 会在每个通过--link选项指定别名的目标容器上设置一个<alias>_NAME环境变量。如果一个名为 web 的容器通过--link db:webdb被链接到一个名为 db 的数据库容器,那么 web 容器上会设置一个环境变量为WEBDB_NAME=/web/webdb.
以之前的为例,Docker 还会设置端口变量:
- $ sudo docker run --rm --name web2 --link db:db training/webapp env
- . . .
- DB_NAME=/web2/db
- DB_PORT=tcp://172.17.0.5:5432
- DB_PORT_5432_TCP=tcp://172.17.0.5:5432 # <name>_PORT_<port>_<protocol> 协议可以是 TCP 或 UDP
- DB_PORT_5432_TCP_PROTO=tcp
- DB_PORT_5432_TCP_PORT=5432
- DB_PORT_5432_TCP_ADDR=172.17.0.5
- . . .
注:这些环境变量只设置给容器中的第一个进程,类似一些守护进程 (如 sshd ) 当他们派生 shells 时会清除这些变量
更新/etc/hosts文件
除了环境变量,Docker 会在目标容器上添加相关主机条目到/etc/hosts中,上例中就是 web 容器。
- $ sudo docker run -t -i --rm --link db:db training/webapp /bin/bash
- root@aed84ee21bde:/opt/webapp# cat /etc/hosts
- 172.17.0.7 aed84ee21bde
- . . .
- 172.17.0.5 db
/etc/host文件在源容器被重启之后会自动更新 IP 地址,而环境变量中的 IP 地址则不会自动更新的。
/etc/host文件在源容器被重启之后会自动更新 IP 地址,而环境变量中的 IP 地址则不会自动更新的。
Docker 官方提供了 docker registry 的构建方法 docker-registry
快速构建 docker registry 通过以下两步:
这种方法通过 Docker hub 使用官方镜像 official image from the Docker hub
安装必要的软件
$ sudo apt-get install build-essential python-dev libevent-dev python-pip liblzma-dev
配置 docker-registry
sudo pip install docker-registry
或者 使用 github clone 手动安装
- $ git clone https://github.com/dotcloud/docker-registry.git
-
- $ cd docker-registry/
-
- $ cp config/config_sample.yml config/config.yml
-
- $ mkdir /data/registry -p
-
- $ pip install .
运行
docker-registry
高级启动方式 [不推荐]
使用gunicorn控制:
gunicorn -c contrib/gunicorn_config.py docker_registry.wsgi:application
或者对外监听开放
gunicorn --access-logfile - --error-logfile - -k gevent -b 0.0.0.0:5000 -w 4 --max-requests 100 docker_registry.wsgi:application
- $ docker tag ubuntu:12.04 私有库IP:5000/ubuntu:12.04
-
- $ docker push 私有库IP:5000/ubuntu
更多的配置选项推荐阅读官方文档:
来自:http://opskumu.github.io/docker.html
Habor 是由 VMWare 公司开源的容器镜像仓库。事实上,Habor是在Docker Registry上进行了相应的企业级扩展,从而获得了更加广泛的应用,这些新的企业级特性包括:管理用户界面,基于角色的访问控制,AD/DALP集成以及审计日志等,足以满足基本企业需求。
官方地址:https://vmware.github.io/harbor/cn/
组件 | 功能 |
---|---|
harbor-adminserver | 配置管理中心 |
harbor-db | MySQL数据库 |
harbor-jobservice | 负责镜像复制 |
harbor-log | 记录操作日志 |
harbor-ui | web管理页面和API |
Nginx | 前端代理,负责前端页面和镜像上传或下载转发 |
reids | 会话 |
registry | 镜像存储 |
Harbor 安装有3种方式:
离线安装下载地址:Releases · goharbor/harbor · GitHub
- curl -L "https://github.com/docker/compose/releases/download/1.22.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
- chmod +x /usr/local/bin/docker-compose
-
- tar xf harbor-offline-installer-v1.6.1.tgz
- cd harbor
- vim harbor.cfg
- harbor_admin_password = Harbor12345
-
- ./prepare
- ./install.sh
-
- [root@localhost harbor]# docker-compose ps
- Name Command State Ports
- -------------------------------------------------------------------------------------------------------------------------------------
- harbor-adminserver /harbor/start.sh Up (healthy)
- harbor-db /entrypoint.sh postgres Up (healthy) 5432/tcp
- harbor-jobservice /harbor/start.sh Up
- harbor-log /bin/sh -c /usr/local/bin/ ... Up (healthy) 127.0.0.1:1514->10514/tcp
- harbor-ui /harbor/start.sh Up (healthy)
- nginx nginx -g daemon off; Up (healthy) 0.0.0.0:443->443/tcp, 0.0.0.0:4443->4443/tcp, 0.0.0.0:80->80/tcp
- redis docker-entrypoint.sh redis ... Up 6379/tcp
- registry /entrypoint.sh /etc/regist ... Up (healthy) 5000/tcp
- [root@localhost harbor]# cat /etc/docker/daemon.json
- {
- "registry-mirrors": ["http://f1361db2.m.daocloud.io"],
- "insecure-registries": ["192.168.56.146"] #<<= 加入harbor的地址
-
- }
systemctl restart docker
在 Harbor web 管理——“系统管理”——“用户管理”——新建用户,在——“项目”——“library仓库” ——“成员” 把之前添加的用户进来并赋予权限
- docker tag nginx:v1 192.168.56.146/library/nginx:v1
- docker login harbor地址
- docker push 192.168.56.146/library/nginx:v1
Portainer
是Docker
的图形化管理工具,提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作)、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录用户管理和控制等功能。功能十分全面,基本能满足中小型单位对容器管理的全部需求。
官网:https://portainer.io
安装:https://documentation.portainer.io/v2.0/deploy/ceinstalldocker/
Portainer 是一个开源、轻量级 Docker 管理用户界面,基于 Docker API,可管理 Docker主机 或 Swarm集群,支持最新版 Docker 和 Swarm 模块。
:https://blog.csdn.net/a772304419/article/details/121036918
创建 monitor:docker network create monitor
Docker Compose:https://www.runoob.com/docker/docker-compose.html
Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose 可以使用 YML 文件来配置应用程序需要的所有服务。然后使用 compose 相关命令,就可以从 YML 文件配置中创建并启动所有服务。
如果不了解 YML 文件配置,可以先阅读 YAML 入门教程。
Compose 使用的三个步骤:
示例配置
- # yaml 配置实例
- version: '3'
- services:
- web:
- build: .
- ports:
- - "5000:5000"
- volumes:
- - .:/code
- - logvolume01:/var/log
- links:
- - redis
- redis:
- image: redis
- volumes:
- logvolume01: {}
Github 下载:https://github.com/docker/compose
Windows 的 Docker 桌面版和 Docker Toolbox 已经包括 Compose 和其他 Docker 应用程序,因此 Windows 用户不需要单独安装 Compose。
创建一个测试目录:
$ mkdir composetest
$ cd composetest
在测试目录中创建一个名为 app.py 的文件,并复制粘贴以下内容:
- import time
-
- import redis
- from flask import Flask
-
- app = Flask(__name__)
- cache = redis.Redis(host='redis', port=6379)
-
-
- def get_hit_count():
- retries = 5
- while True:
- try:
- return cache.incr('hits')
- except redis.exceptions.ConnectionError as exc:
- if retries == 0:
- raise exc
- retries -= 1
- time.sleep(0.5)
-
-
- @app.route('/')
- def hello():
- count = get_hit_count()
- return 'Hello World! I have been seen {} times.\n'.format(count)
在此示例中,redis 是应用程序网络上的 redis 容器的主机名,该主机使用的端口为 6379。在 composetest 目录中创建另一个名为 requirements.txt 的文件,内容如下:
flask
redis
在 composetest 目录中,创建一个名为 Dockerfile 的文件,内容如下:
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP app.py
ENV FLASK_RUN_HOST 0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
CMD ["flask", "run"]
Dockerfile 内容解释:
在测试目录中创建一个名为 docker-compose.yml 的文件,然后粘贴以下内容:
- # yaml 配置
- version: '3'
- services:
- web:
- build: .
- ports:
- - "5000:5000"
- redis:
- image: "redis:alpine"
该 Compose 文件定义了两个服务:web 和 redis。
在测试目录中,执行以下命令来启动应用程序:docker compose up
如果你想在后台执行该服务可以加上 -d 参数:docker compose up -d
Docker Compose V2 使用 docker compose
命令。在这里阅读更多信息。
查看相关子命令示例:docker compose up --help
:https://www.runoob.com/docker/docker-swarm.html
Docker Swarm 是 Docker 的集群管理工具。它将 Docker 主机池转变为单个虚拟 Docker 主机。 Docker Swarm 提供了标准的 Docker API,所有任何已经与 Docker 守护程序通信的工具都可以使用 Swarm 轻松地扩展到多个主机。
支持的工具包括但不限于以下各项:
swarm 集群由管理节点(manager)和工作节点(work node)构成。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。