监控摄像头win10找不到摄像头IP,用IPCSearch软件也查win10找不到摄像头(连接内网,外网都win10找不到摄像头),请问大神,怎么

内网ip问题_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
内网ip问题
假如一台电脑直接通过moden连接外网,并没有通过路由器创建局域网,那这台电脑有内网ip吗?就一台电脑还存在内网吗?
我有更好的答案
不会有内网ip,直连的话一般就是在本机电脑上用宽带拨号连接,获取到的ip直接分配在了本机电脑上
采纳率:54%
来自团队:
那么不可以连接远程桌面。如果对方直接是外网。 现在回答你第一个问题,那么输入对方外网ip可远程连接,查询本机内网ip 右击本地连接--状态--详细信息,就可看到。还有一种方法告诉你一个基本常识。你连接的对象若是内网用户,而你又跟他在同一个内网,那么可以通过对方内网ip访问。如果对方跟你不在同一个内网而他也不是外网
为您推荐:
其他类似问题
您可能关注的内容
换一换
回答问题,赢新手礼包为啥网络摄像机直接用浏览器访问它的IP地址后没有出现登陆界面?_百度知道
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。
为啥网络摄像机直接用浏览器访问它的IP地址后没有出现登陆界面?
我自己买了台网络监控摄像机,调试后能在监视器和电脑的软件视频监控客户端上看到实时监控画面,但是我想用浏览器直接访问IPC的IP地址,登陆进去进行一些设置,可是没有出现登陆界面。IE浏览器所有控件我都改了启用,不是这个问题,我电脑的本地连接地址也不会...
网络摄像机用浏览器访问,是需要在客户端安装插件的。可以下载摄像机的web插件安装,也可以自动允许active插件下载安装。浏览器的安全设置项下的自定义中的active插件,全部选择允许。将网络摄像机的IP地址或访问地址添加到可信任站点。查看本机的网络安全设置,是否开启了网络摄像机的访问所需要的端口,比如80等。是否为同一局域网,或者外网做了对应端口映射,路由器中做的DMZ主机绑定。
采纳率:76%
来自团队:
在路由器上映射出去
1.HTTP的端口是不是80的,如果不是要使用 地址:端口访问2.有些厂商的IPC不支持IE访问,只能使用PC客户端软件才能访问3.登陆的时候关闭杀毒软件,防火墙,防止IE控件被误杀4.提示下载控件时,点击下载
lz用的外网还是内网,ip要输入监视器的ip,外网要路由器映射的
问清楚厂家,IPC的http或者叫WEB端口是不是80?还有是否支持IE访问如果是80,而且支持IE访问,请查看一下,你的网络摄像机和你的电脑是否在一个网段,不在一个网段,根本访问不了.
其他2条回答
为您推荐:
其他类似问题
您可能关注的内容
登陆界面的相关知识
换一换
回答问题,赢新手礼包查看一下容器 bbox1 的网络设备:
除了 lo,容器只有一个 eth0,请注意 eth0 后面的 @if4,这表明该 interface 有一个对应的 interface,其全局的编号为 4。根据 macvlan 的原理,我们有理由猜测这个 interface 就是主机的 enp0s9,确认如下:
可见,容器的 eth0 就是 enp0s9 通过 macvlan 虚拟出来的 interface。容器的 interface 直接与主机的网卡连接,这种方案使得容器无需通过 NAT 和端口映射就能与外网直接通信(只要有网关),在网络上与其他独立主机没有区别。当前网络结构如图所示:
用 sub-interface 实现多 macvlan 网络
macvlan 会独占主机的网卡,也就是说一个网卡只能创建一个 macvlan 网络,否则会报错:
但主机的网卡数量是有限的,如何支持更多的 macvlan 网络呢?
好在 macvlan 不仅可以连接到 interface(如 enp0s9),也可以连接到 sub-interface(如 enp0s9.xxx)。
VLAN 是现代网络常用的网络虚拟化技术,它可以将物理的二层网络划分成多达 4094 个逻辑网络,这些逻辑网络在二层上是隔离的,每个逻辑网络(即 VLAN)由 VLAN ID 区分,VLAN ID 的取值为 1-4094。
Linux 的网卡也能支持 VLAN(apt-get install vlan),同一个 interface 可以收发多个 VLAN 的数据包,不过前提是要创建 VLAN 的 sub-interface。
比如希望 enp0s9 同时支持 VLAN10 和 VLAN20,则需创建 sub-interface enp0s9.10 和 enp0s9.20。
在交换机上,如果某个 port 只能收发单个 VLAN 的数据,该 port 为 Access 模式,如果支持多 VLAN,则为 Trunk 模式,所以接下来实验的前提是:
enp0s9 要接在交换机的 trunk 口上。不过我们用的是 VirtualBox 虚拟机,则不需要额外配置了。
如果大家想了解更多 Linux VLAN 实践,可参看 CloudMan 《每天5分钟玩转 OpenStack》中的相关章节。
下面演示如何在 enp0s9.10 和 enp0s9.20 上创建 macvlan 网络。
首先编辑 host1 和 host2 的 /etc/network/interfaces,配置 sub-
auto enp0s9
iface enp0s9 inet manual
auto enp0s9.10
iface enp0s9.10 inet manual
vlan-raw-device enp0s9
auto enp0s9.20
iface enp0s9.20 inet manual
vlan-raw-device enp0s9
然后启用 sub-interface:
ifup enp0s9.10
ifup enp0s9.20
创建 macvlan 网络:
docker network create -d macvlan --subnet=172.16.10.0/24 --gateway=172.16.10.1 -o parent=enp0s9.10 mac_net10
docker network create -d macvlan --subnet=172.16.20.0/24 --gateway=172.16.20.1 -o parent=enp0s9.20 mac_net20
在 host1 中运行容器:
docker run -itd --name bbox1 --ip=172.16.10.10 --network mac_net10 busybox
docker run -itd --name bbox2 --ip=172.16.20.10 --network mac_net20 busybox
在 host2 中运行容器:
docker run -itd --name bbox3 --ip=172.16.10.11 --network mac_net10 busybox
docker run -itd --name bbox4 --ip=172.16.20.11 --network mac_net20 busybox
当前网络结构如图所示:
这四个容器之间的连通性如何?下一节我们将详细讨论 macvlan 网络的连通和隔离特性。
310001SPJJ
1.351 vistas
上一节我们准备好了 macvlan 的实验环境,今天在 host1 和 host2 中创建 macvlan 网络 mac_net1:
注意:在 host2 中也要执行相同的命令。
① -d macvlan 指定 driver 为 macvlan。
② macvlan 网络是 local 网络,为了保证跨主机能够通信,用户需要自己管理 IP subnet。
③ 与其他网络不同,docker 不会为 macvlan 创建网关,这里的网关应该是真实存在的,否则容器无法路由。
④ -o parent 指定使用的网络 interface。
在 host1 中运行容器 bbox1 并连接到 mac_net1。
由于 host1 中的 mac_net1 与 host2 中的 mac_net1 本质上是独立的,为了避免自动分配造成 IP 冲突,我们最好通过 --ip 指定 bbox1 地址为 172.16.86.10。
在 host2 中运行容器 bbox2,指定 IP 172.16.86.11。
验证 bbox1 和 bbox1 的连通性。
bbox2 能够 ping 到 bbox1 的 IP 172.16.86.10,但无法解析 &bbox1& 主机名。
可见 docker 没有为 macvlan 提供 DNS 服务,这点与 overlay 网络是不同的。
下一节我们将详细分析 macvlan 的网络结构。
310001SPJJ
1.641 vistas
除了 overlay,docker 还开发了另一个支持跨主机容器网络的 driver:macvlan。
macvlan 本身是 linxu kernel 模块,其功能是允许在同一个物理网卡上配置多个 MAC 地址,即多个 interface,每个 interface 可以配置自己的 IP。macvlan 本质上是一种网卡虚拟化技术,Docker 用 macvlan 实现容器网络就不奇怪了。
macvlan 的最大优点是性能极好,相比其他实现,macvlan 不需要创建 Linux bridge,而是直接通过以太 interface 连接到物理网络。下面我们就来创建一个 macvlan 网络。
准备实验环境
我们会使用 host1 和 host2 上单独的网卡 enp0s9 创建 macvlan。为保证多个 MAC 地址的网络包都可以从 enp0s9 通过,我们需要打开网卡的混杂模式。
ip link set enp0s9 promisc on
确保 enp0s9 状态 UP 并且 promisc 模式已经生效。
因为 host1 和 host2 是 VirtualBox 虚拟机,还需要在网卡配置选项页中设置混杂模式。
当前实验环境如图所示:
准备就绪,下一节我们创建 macvlan 网络。
310001SPJJ
1.419 vistas
不同的 overlay 网络是相互隔离的。我们创建第二个 overlay 网络 ov_net2 并运行容器 bbox3。
bbox3 分配到的 IP 是 10.0.1.2,尝试 ping bbox1(10.0.0.2)。
ping 失败,可见不同 overlay 网络之间是隔离的。即便是通过 docker_gwbridge 也不能通信。
如果要实现 bbox3 与 bbox1 通信,可以将 bbox3 也连接到 ov_net1。
overlay IPAM
docker 默认为 overlay 网络分配 24 位掩码的子网(10.0.X.0/24),所有主机共享这个 subnet,容器启动时会顺序从此空间分配 IP。当然我们也可以通过 --subnet 指定 IP 空间。
docker network create -d overlay --subnet 10.22.1.0/24 ov_net3
至此,overlay 网络已经讨论完了,下一节我们开始学习 macvlan 网络。
310001SPJJ
1.474 vistas
上一节我们在 host1 中运行了容器 bbox1,今天将详细讨论 overlay 网络跨主机通信的原理。
在 host2 中运行容器 bbox2:
bbox2 IP 为 10.0.0.3,可以直接 ping bbox1:
可见 overlay 网络中的容器可以直接通信,同时 docker 也实现了 DNS 服务。
下面我们讨论一下 overlay 网络的具体实现:
docker 会为每个 overlay 网络创建一个独立的 network namespace,其中会有一个 linux bridge br0,endpoint 还是由 veth pair 实现,一端连接到容器中(即 eth0),另一端连接到 namespace 的 br0 上。
br0 除了连接所有的 endpoint,还会连接一个 vxlan 设备,用于与其他 host 建立 vxlan tunnel。容器之间的数据就是通过这个 tunnel 通信的。逻辑网络拓扑结构如图所示:
要查看 overlay 网络的 namespace 可以在 host1 和 host2 上执行 ip netns(请确保在此之前执行过 ln -s /var/run/docker/netns /var/run/netns),可以看到两个 host 上有一个相同的 namespace &1-f4af9b33c0&:
1-f4af9b33c0
这就是 ov_net1 的 namespace,查看 namespace 中的 br0 上的设备。
查看 vxlan1 设备的具体配置信息可知此 overlay 使用的 VNI(VxLAN ID)为 256。
理解了 overlay 网络的连通性,下一节我们继续讨论 overlay 的网络隔离特性。
310001SPJJ
1.516 vistas
上一节我们创建了 overlay 网络 ov_net1,今天将运行一个 busybox 容器并连接到 ov_net1:
查看容器的网络配置:
bbox1 有两个网络接口 eth0 和 eth1。eth0 IP 为 10.0.0.2,连接的是 overlay 网络 ov_net1。eth1 IP 172.17.0.2,容器的默认路由是走 eth1,eth1 是哪儿来的呢?
其实,docker 会创建一个 bridge 网络 &docker_gwbridge&,为所有连接到 overlay 网络的容器提供访问外网的能力。
从 docker network inspect docker_gwbridge 输出可确认 docker_gwbridge 的 IP 地址范围是 172.17.0.0/16,当前连接的容器就是 bbox1(172.17.0.2)。
docker network inspect docker_gwbridge
& & & & &IPAM&: {
& & & & & & &Driver&: &default&,
& & & & & & &Options&: null,
& & & & & & &Config&: [
& & & & & & & & {
& & & & & & & & & & &Subnet&: &172.17.0.0/16&,
& & & & & & & & & & &Gateway&: &172.17.0.1&
& & & & & & & & }
& & & & & & ]
& & & & },
& & & & &Internal&: false,
& & & & &Attachable&: false,
& & & & &Containers&: {
& & & & & & &100dda81c980dcd191d00eba806a59766&: {
& & & & & & & & &Name&: &gateway_100dd1207eb8&,
& & & & & & & & &EndpointID&: &5077a2bfef80c412ccbba8a2f1ad35b819&,
& & & & & & & & &MacAddress&: &02:42:ac:11:00:02&,
& & & & & & & & &IPv4Address&: &172.17.0.2/16&,
& & & & & & & & &IPv6Address&: &&
& & & & & & }
& & & & },
而且此网络的网关就是网桥 docker_gwbridge 的 IP 172.17.0.1。
这样容器 bbox1 就可以通过 docker_gwbridge 访问外网。
如果外网要访问容器,可通过主机端口映射,比如:
docker run -p 80:80 -d --net ov_net1 --name web1 httpd
验证完外网的连通性,下一节验证 overlay 网络跨主机通信。
310001SPJJ
2.162 vistas
上一节我们搭建好实验环境,配置并运行了consul,今天开始创建 overlay 网络。
在 host1 中创建 overlay 网络 ov_net1:
-d overlay&指定 driver 为 overaly。
docker network ls&查看当前网络:
注意到&ov_net1&的 SCOPE 为 global,而其他网络为 local。在 host2 上查看存在的网络:
host2 上也能看到 ov_net1。这是因为创建 ov_net1 时 host1 将 overlay 网络信息存入了 consul,host2 从 consul 读取到了新网络的数据。之后 ov_net 的任何变化都会同步到 host1 和 host2。
docker network inspect&查看 ov_net1 的详细信息:
docker network inspect ov_net1
& & & & &IPAM&: {
& & & & & & &Driver&: &default&,
& & & & & & &Options&: {},
& & & & & & &Config&: [
& & & & & & & & {
& & & & & & & & & & &Subnet&: &10.0.0.0/24&,
& & & & & & & & & & &Gateway&: &10.0.0.1&
& & & & & & & & }
& & & & & & ]
& & & & },
IPAM 是指 IP Address Management,docker 自动为 ov_net1 分配的 IP 空间为 10.0.0.0/24。
下一节我们将在 ov_net1 中运行容器并分析其网络配置。
310001SPJJ
2.053 vistas
为支持容器跨主机通信,Docker 提供了 overlay driver,使用户可以创建基于 VxLAN 的 overlay 网络。VxLAN 可将二层数据封装到 UDP 进行传输,VxLAN 提供与 VLAN 相同的以太网二层服务,但是拥有更强的扩展性和灵活性。有关 VxLAN 更详细的内容可参考 CloudMan 在《每天5分钟玩转 OpenStack》中的相关章节。
Docerk overlay 网络需要一个 key-value 数据库用于保存网络状态信息,包括 Network、Endpoint、IP 等。Consul、Etcd 和 ZooKeeper 都是 Docker 支持的 key-vlaue 软件,我们这里使用 Consul。
实验环境描述
我们会直接使用上一章 docker-machine 创建的实验环境。在 docker 主机 host1(192.168.56.104)和 host2(192.168.56.105)上实践各种跨主机网络方案,在 192.168.56.101 上部署支持的组件,比如 Consul。
最简单的方式是以容器方式运行 Consul:
docker run -d -p
-h consul --name consul progrium/consul -server -bootstrap
容器启动后,可以通过
访问 Consul。
接下来修改 host1 和 host2 的 docker daemon 的配置文件/etc/systemd/system/docker.service。
--cluster-store 指定 consul 的地址。
--cluster-advertise 告知 consul 自己的连接地址。
重启 docker daemon。
systemctl daemon-reload &
systemctl restart docker.service
host1 和 host2 将自动注册到 Consul 数据库中。
准备就绪,实验环境如下:
下一节创建 overlay 网络。
310001SPJJ
2.251 vistas
前面已经学习了 Docker 的几种网络方案:none、host、bridge 和 joined 容器,它们解决了单个 Docker Host 内容器通信的问题。本章的重点则是讨论跨主机容器间通信的方案。
跨主机网络方案包括:
docker 原生的 overlay 和 macvlan。
第三方方案:常用的包括 flannel、weave 和 calico。
docker 网络是一个非常活跃的技术领域,不断有新的方案开发出来,那么要问个非常重要的问题了:
如此众多的方案是如何与 docker 集成在一起的?
答案是:libnetwork 以及 CNM。
libnetwork & CNM
libnetwork 是 docker 容器网络库,最核心的内容是其定义的 Container Network Model (CNM),这个模型对容器网络进行了抽象,由以下三类组件组成:
Sandbox 是容器的网络栈,包含容器的 interface、路由表和 DNS 设置。 Linux Network Namespace 是 Sandbox 的标准实现。Sandbox 可以包含来自不同 Network 的 Endpoint。
Endpoint 的作用是将 Sandbox 接入 Network。Endpoint 的典型实现是 veth pair,后面我们会举例。一个 Endpoint 只能属于一个网络,也只能属于一个 Sandbox。
Network 包含一组 Endpoint,同一 Network 的 Endpoint 可以直接通信。Network 的实现可以是 Linux Bridge、VLAN 等。
下面是 CNM 的示例:
如图所示两个容器,一个容器一个 Sandbox,每个 Sandbox 都有一个 Endpoint 连接到 Network 1,第二个 Sandbox 还有一个 Endpoint 将其接入 Network 2.
libnetwork CNM 定义了 docker 容器的网络模型,按照该模型开发出的 driver 就能与 docker daemon 协同工作,实现容器网络。docker 原生的 driver 包括 none、bridge、overlay 和 macvlan,第三方 driver 包括 flannel、weave、calico 等。
下面我们以 docker bridge driver 为例讨论 libnetwork CNM 是如何被实现的。
这是前面我们讨论过的一个容器环境:
两个 Network:默认网络 &bridge& 和自定义网络 &my_net2&。实现方式是 Linux Bridge:&docker0& 和 &br-5d863e9f78b6&。
三个 Enpoint,由 veth pair 实现,一端(vethxxx)挂在 Linux Bridge 上,另一端(eth0)挂在容器内。
三个 Sandbox,由 Network Namespace 实现,每个容器有自己的 Sanbox。
接下来我们将详细讨论各种跨主机网络方案,首先学习 Overlay。
310001SPJJ
2.202 vistas
用 docker-machine 创建 machine 的过程很简洁,非常适合多主机环境。除此之外,Docker Machine 也提供了一些子命令方便对 machine 进行管理。其中最常用的就是无需登录到 machine 就能执行 docker 相关操作。
我们前面学过,要执行远程 docker 命令我们需要通过 -H 指定目标主机的连接字符串,比如:
docker -H tcp://192.168.56.105:2376 ps
Docker Machine 则让这个过程更简单。docker-machine env host1显示访问 host1 需要的所有环境变量:
根据提示,执行 eval $(docker-machine env host1):
然后,就可以看到命令行提示符已经变了,其原因是我们之前在$HOME/.bashrc 中配置了 PS1='[\u@\h \W$(__docker_machine_ps1)]\$ ',用于显示当前 docker host。
在此状态下执行的所有 docker 命令其效果都相当于在 host1 上执行,例如启动一个 busybox 容器:
执行 eval $(docker-machine env host2) 切换到 host2:
下面再介绍几个有用的 docker-machine 子命令:
docker-machine upgrade 更新 machine 的 docker 到最新版本,可以批量执行:
docker-machine config 查看 machine 的 docker daemon 配置:
stop/start/restart 是对 machine 的操作系统操作,而 不是&stop/start/restart docker daemon。
docker-machine scp 可以在不同 machine 之间拷贝文件,比如:
docker-machine scp host1:/tmp/a host2:/tmp/b
可见,在多主机环境下 Docker Machine 可以大大提高效率,而且操作也很简单,希望大家都能掌握。
下一节我们开始学习跨主机的容器网络。
310001SPJJ
1.857 vistas
对于 Docker Machine 来说,术语&Machine&就是运行 docker daemon 的主机。&创建 Machine& 指的就是在 host 上安装和部署 docker。先执行&docker-machine ls&查看一下当前的 machine:
如我们所料,当前还没有 machine,接下来我们创建第一个 machine: host1 - 192.168.56.104。
创建 machine 要求能够无密码登录远程主机,所以需要先通过如下命令将 ssh key 拷贝到 192.168.56.104:
ssh-copy-id 192.168.56.104&
一切准备就绪,执行 docker-machine create 命令创建 host1:
docker-machine create --driver generic --generic-ip-address=192.168.56.104 host1
因为我们是往普通的 Linux 中部署 docker,所以使用 generic driver,其他 driver 可以参考文档 。
--generic-ip-address 指定目标系统的 IP,并命名为 host1。命令执行过程如下:
① 通过 ssh 登录到远程主机。
② 安装 docker。
③ 拷贝证书。
④ 配置 docker daemon。
⑤ 启动 docker。
再次执行 docker-machine ls :
已经能看到 host1 了。 我们可以登录到 host1 查看 docker daemon 的具体配置 /etc/systemd/system/docker.service。
-H tcp://0.0.0.0:2376 使 docker daemon 接受远程连接。
--tls* 对远程连接启用安全认证和加密。
同时我们也看到 hostname 已经设置为 host1:
使用同样的方法创建 host2:
docker-machine create --driver generic --generic-ip-address=192.168.56.105 host2
创建成功后 docker-machine ls 可以看到 host1 和 host2 都已经就绪:
当前当前环境如下:
下一节学习如何管理 Machine。
310001SPJJ
2.699 vistas
前面我们的实验环境中只有一个 docker host,所有的容器都是运行在这一个 host 上的。但在真正的环境中会有多个 host,容器在这些 host 中启动、运行、停止和销毁,相关容器会通过网络相互通信,无论它们是否位于相同的 host。
对于这样一个 multi-host 环境,我们将如何高效地进行管理呢?
我们面临的第一个问题是:为所有的 host 安装和配置 docker。
在前面我们手工安装了第一个 docker host,步骤包括:
安装 https CA 证书
添加 GPG key
添加 docker apt 源
安装 docker
可见步骤还是挺多的,对于多主机环境手工方式效率低且不容易保证一致性,针对这个问题,docker 给出的解决方案是 Docker Machine。
用 Docker Machine 可以批量安装和配置 docker host,这个 host 可以是本地的虚拟机、物理机,也可以是公有云中的云主机。
Docker Machine 支持在不同的环境下安装配置 docker host,包括:
常规 Linux 操作系统
虚拟化平台 - VirtualBox、VMWare、Hyper-V
公有云 - Amazon Web Services、Microsoft Azure、Google Compute Engine、Digital Ocean 等
Docker Machine 为这些环境起了一个统一的名字:provider。对于某个特定的 provider,Docker Machine 使用相应的 driver 安装和配置 docker host,如下图所示:
下面我们通过实验来学习 Docker Machine。
实验环境描述
实验环境中有三个运行 Ubuntu 的 host。
我们将在 192.168.56.101 上安装 Docker Machine,然后通过 docker-machine 命令在其他两个 host 上部署 docker。
安装 Docker Machine
官方安装文档在
安装方法很简单,执行如下命令:
curl -L `uname -s`-`uname -m` &/tmp/docker-machine &&
& chmod +x /tmp/docker-machine &&
& sudo cp /tmp/docker-machine /usr/local/bin/docker-machine
下载的执行文件被放到 /usr/local/bin 中,执行docker-mahine version 验证命令是否可用:
注:当你看到这篇文章的时候,Docker Machine 应该有了更新的版本,可参考官方文档进行安装。
为了得到更好的体验,我们可以安装 bash completion script,这样在 bash 能够通过 tab 键补全 docker-mahine 的子命令和参数。安装方法是从下载 completion script:
将其放置到 /etc/bash_completion.d 目录下。然后将如下代码添加到$HOME/.bashrc:
PS1='[\u@\h \W$(__docker_machine_ps1)]\$ '
其作用是设置 docker-machine 的命令行提示符,不过要等到部署完其他两个 host 才能看出效果。
Docker Machine 已经就绪,当前环境如下:
下一节我们学习如何创建 Machine。
310001SPJJ
2.026 vistas
Data Volume 中存放的是重要的应用数据,如何管理 volume 对应用至关重要。前面我们主要关注的是 volume 的创建、共享和使用,本节将讨论如何备份、恢复、迁移和销毁 volume。
因为 volume 实际上是 host 文件系统中的目录和文件,所以 volume 的备份实际上是对文件系统的备份。
还记得前面我们是如何搭建本地 Registry 的吗?
所有的本地镜像都存在 host 的 /myregistry 目录中,我们要做的就是定期备份这个目录。
volume 的恢复也很简单,如果数据损坏了,直接用之前备份的数据拷贝到 /myregistry 就可以了。
如果我们想使用更新版本的 Registry,这就涉及到数据迁移,方法是:
docker stop 当前 Registry 容器。
启动新版本容器并 mount 原有 volume。
docker run -d -p
-v /myregistry:/var/lib/registry registry:latest
当然,在启用新容器前要确保新版本的默认数据路径是否发生变化。
可以删除不再需要的 volume,但一定要确保知道自己正在做什么,volume 删除后数据是找不回来的。
docker 不会销毁 bind mount,删除数据的工作只能由 host 负责。对于 docker managed volume,在执行 docker rm 删除容器时可以带上 -v 参数,docker 会将容器使用到的 volume 一并删除,但前提是没有其他容器 mount 该 volume,目的是保护数据,非常合理。
如果删除容器时没有带 -v 呢?这样就会产生孤儿 volume,好在 docker 提供了 volume 子命令可以对 docker managed volume 进行维护。请看下面的例子:
容器 bbox 使用的 docker managed volume 可以通过 docker volume ls 查看到。
删除 bbox:
因为没有使用 -v,volume 遗留了下来。对于这样的孤儿 volume,可以用 docker volume rm 删除:
如果想批量删除孤儿 volume,可以执行:
docker volume rm $(docker volume ls -q)
本章我们学习了以下内容:
docker 为容器提供了两种存储资源:数据层和 Data Volume。
数据层包括镜像层和容器层,由 storage driver 管理。
Data Volume 有两种类型:bind mount 和 docker managed volume。
bind mount 可实现容器与 host 之间,容器与容器之间共享数据。
volume container 是一种具有更好移植性的容器间数据共享方案,特别是 data-packed volume container。
最后我们学习了如何备份、恢复、迁移和销毁 Data Volume。
不知大家发现没有,这章我们学习的只是单个 docker host 中的存储方案。而跨主机存储也是一个重要的主题,当然也更复杂,我们会在容器进阶技术章节详细讨论。
下一节我们便开始进阶知识部分的学习,首先讨论如何高效管理多个 Docker 主机。
310001SPJJ
1.437 vistas
在上一节的例子中 volume container 的数据归根到底还是在 host 里,有没有办法将数据完全放到 volume container 中,同时又能与其他容器共享呢?
当然可以,通常我们称这种容器为 data-packed volume container。其原理是将数据打包到镜像中,然后通过 docker managed volume 共享。
我们用下面的 Dockfile 构建镜像:
ADD 将静态文件添加到容器目录 /usr/local/apache2/htdocs。
VOLUME 的作用与 -v 等效,用来创建 docker managed volume,mount point 为 /usr/local/apache2/htdocs,因为这个目录就是 ADD 添加的目录,所以会将已有数据拷贝到 volume 中。&
build 新镜像 datapacked:
用新镜像创建 data-packed volume container:
因为在 Dockerfile 中已经使用了 VOLUME 指令,这里就不需要指定 volume 的 mount point 了。启动 httpd 容器并使用 data-packed volume container:
容器能够正确读取 volume 中的数据。data-packed volume container 是自包含的,不依赖 host 提供数据,具有很强的移植性,非常适合 只使用 静态数据的场景,比如应用的配置信息、web server 的静态文件等。
容器数据共享就讨论到这里,下一节我们学习如何对 data volume 的生命周期进行管理。
310001SPJJ
2.246 vistas
volume container 是专门为其他容器提供 volume 的容器。它提供的卷可以是 bind mount,也可以是 docker managed volume。下面我们创建一个 volume container:
我们将容器命名为 vc_data(vc 是 volume container 的缩写)。注意这里执行的是 docker create 命令,这是因为 volume container 的作用只是提供数据,它本身不需要处于运行状态。容器 mount 了两个 volume:
bind mount,存放 web server 的静态文件。
docker managed volume,存放一些实用工具(当然现在是空的,这里只是做个示例)。
通过 docker inspect 可以查看到这两个 volume。
# docker inspect vc_data
&Mounts&: [
& & & & &Source&: &/root/htdocs&,
& & & & &Destination&: &/usr/local/apache2/htdocs&,
& & & & &Mode&: &&,
& & & & &RW&: true,
& & & & &Propagation&: &rprivate&
& & & & &Name&: &1b7ea56c4f4c804d447c680e7b3ba7c7f5e52205&,
& & & & &Source&: &/var/lib/docker/volumes/1b7ea56c4f4c804d447c680e7b3ba7c7f5e52205/_data&,
& & & & &Destination&: &/other/useful/tools&,
& & & & &Driver&: &local&,
& & & & &Mode&: &&,
& & & & &RW&: true,
& & & & &Propagation&: &&
其他容器可以通过 --volumes-from 使用 vc_data 这个 volume container:
三个 httpd 容器都使用了 vc_data,看看它们现在都有哪些 volume,以 web1 为例:
# docker inspect web1
&Mounts&: [
& & & & &Source&: &/root/htdocs&,
& & & & &Destination&: &/usr/local/apache2/htdocs&,
& & & & &Mode&: &&,
& & & & &RW&: true,
& & & & &Propagation&: &rprivate&
& & & & &Name&: &1b7ea56c4f4c804d447c680e7b3ba7c7f5e52205&,
& & & & &Source&: &/var/lib/docker/volumes/1b7ea56c4f4c804d447c680e7b3ba7c7f5e52205/_data&,
& & & & &Destination&: &/other/useful/tools&,
& & & & &Driver&: &local&,
& & & & &Mode&: &&,
& & & & &RW&: true,
& & & & &Propagation&: &&
web1 容器使用的就是 vc_data 的 volume,而且连 mount point 都是一样的。验证一下数据共享的效果:
可见,三个容器已经成功共享了 volume container 中的 volume。
下面我们讨论一下 volume container 的特点:
与 bind mount 相比,不必为每一个容器指定 host path,所有 path 都在 volume container 中定义好了,容器只需与 volume container 关联,实现了容器与 host 的解耦。
使用 volume container 的容器其 mount point 是一致的,有利于配置的规范和标准化,但也带来一定的局限,使用时需要综合考虑。
另一种在容器之间共享数据的方式是 data-packed volume container,下一节讨论。
310001SPJJ
2.084 vistas
数据共享是 volume 的关键特性,本节我们详细讨论通过 volume 如何在容器与 host 之间,容器与容器之间共享数据。
容器与 host 共享数据
我们有两种类型的 data volume,它们均可实现在容器与 host 之间共享数据,但方式有所区别。
对于 bind mount 是非常明确的:直接将要共享的目录 mount 到容器。具体请参考前面 httpd 的例子,不再赘述。
docker managed volume 就要麻烦点。由于 volume 位于 host 中的目录,是在容器启动时才生成,所以需要将共享数据拷贝到 volume 中。请看下面的例子:
docker cp 可以在容器和 host 之间拷贝数据,当然我们也可以直接通过 Linux 的 cp 命令复制到 /var/lib/docker/volumes/xxx。
容器之间共享数据
第一种方法是将共享数据放在 bind mount 中,然后将其 mount 到多个容器。还是以 httpd 为例,不过这次的场景复杂些,我们要创建由三个 httpd 容器组成的 web server 集群,它们使用相同的 html 文件,操作如下:
将 $HOME/htdocs mount 到三个 httpd 容器。
查看当前主页内容。
修改 volume 中的主页文件,再次查看并确认所有容器都使用了新的主页。
另一种在容器之间共享数据的方式是使用 volume container,下节讨论。
有个好消息:出版社现在搞促销,《每天5分钟玩转OpenStack》全网最低价销售,有兴趣的同学可以访问 &了解详情 。
310001SPJJ
1.932 vistas
docker managed volume 与 bind mount 在使用上的最大区别是不需要指定 mount 源,指明 mount point 就行了。还是以 httpd 容器为例:
我们通过 -v 告诉 docker 需要一个 data volume,并将其 mount 到 /usr/local/apache2/htdocs。那么这个 data volume 具体在哪儿呢?
这个答案可以在容器的配置信息中找到,执行 docker inspect 命令:
docker inspect 21accc2ca072
&Mounts&: [
& & & & &Name&: &f4a0a60efecdf18babf3340&,
& & & & &Source&: &/var/lib/docker/volumes/f4a0a60efecdf18babf3340/_data&,
& & & & &Destination&: &/usr/local/apache2/htdocs&,
& & & & &Driver&: &local&,
& & & & &Mode&: &&,
& & & & &RW&: true,
& & & & &Propagation&: &&
docker inspect 的输出很多,我们感兴趣的是 Mounts 这部分,这里会显示容器当前使用的所有 data volume,包括 bind mount 和 docker managed volume。
Source 就是该 volume 在 host 上的目录。
原来,每当容器申请 mount docker manged volume 时,docker 都会在/var/lib/docker/volumes 下生成一个目录(例子中是 &/var/lib/docker/volumes/f4a0a60efecdf18babf3340/_data ),这个目录就是 mount 源。
下面继续研究这个 volume,看看里面有些什么东西:
volume 的内容跟容器原有 /usr/local/apache2/htdocs 完全一样,这是怎么回事呢?
这是因为:如果 mount point 指向的是已有目录,原有数据会被复制到 volume 中。
但要明确一点:此时的 /usr/local/apache2/htdocs 已经不再是由 storage driver 管理的层数据了,它已经是一个 data volume。我们可以像 bind mount 一样对数据进行操作,例如更新数据:
简单回顾一下 docker managed volume 的创建过程:
容器启动时,简单的告诉 docker &我需要一个 volume 存放数据,帮我 mount 到目录 /abc&。
docker 在 /var/lib/docker/volumes 中生成一个随机目录作为 mount 源。
如果 /abc 已经存在,则将数据复制到 mount 源,
将 volume mount 到 /abc
除了通过 docker inspect 查看 volume,我们也可以用 docker volume 命令:
目前,docker volume 只能查看 docker managed volume,还看不到 bind mount;同时也无法知道 volume 对应的容器,这些信息还得靠docker inspect。
我们已经学习了两种 data volume 的原理和基本使用方法,下面做个对比:
相同点:两者都是 host 文件系统中的某个路径。
bind mount
docker managed volume & && && && &
volume 位置
可任意指定
/var/lib/docker/volumes/...
对已有mount point 影响 & && && && &
隐藏并替换为 volume
原有数据复制到 volume
是否支持单个文件
不支持,只能是目录
可设置为只读,默认为读写权限 & && && && &
无控制,均为读写权限
移植性弱,与 host path 绑定
移植性强,无需指定 host 目录
下节讨论如何通过 data volume 实现容器与 host,容器与容器共享数据。
有个好消息:出版社现在搞促销,《每天5分钟玩转OpenStack》全网最低价销售,有兴趣的同学可以访问 &了解详情 。
310001SPJJ
2.457 vistas
storage driver 和 data volume 是容器存放数据的两种方式,上一节我们学习了 storage driver,本节开始讨论 Data Volume。
Data Volume 本质上是 Docker Host 文件系统中的目录或文件,能够直接被 mount 到容器的文件系统中。Data Volume 有以下特点:
Data Volume 是目录或文件,而非没有格式化的磁盘(块设备)。
容器可以读写 volume 中的数据。
volume 数据可以被永久的保存,即使使用它的容器已经销毁。
好,现在我们有数据层(镜像层和容器层)和 volume 都可以用来存放数据,具体使用的时候要怎样选择呢?考虑下面几个场景:
Database 软件 vs Database 数据
Web 应用 vs 应用产生的日志
数据分析软件 vs input/output 数据
Apache Server vs 静态 HTML 文件
相信大家会做出这样的选择:
前者放在数据层中。因为这部分内容是无状态的,应该作为镜像的一部分。
后者放在 Data Volume 中。这是需要持久化的数据,并且应该与镜像分开存放。
还有个大家可能会关心的问题:如何设置 voluem 的容量?
因为 volume 实际上是 docker host 文件系统的一部分,所以 volume 的容量取决于文件系统当前未使用的空间,目前还没有方法设置 volume 的容量。
在具体的使用上,docker 提供了两种类型的 volume:bind mount 和 docker managed volume。
bind mount
bind mount 是将 host 上已存在的目录或文件 mount 到容器。
例如 docker host 上有目录 $HOME/htdocs:
通过&-v&将其 mount 到 httpd 容器:
-v 的格式为 &host path&:&container path&。/usr/local/apache2/htdocs 就是 apache server 存放静态文件的地方。由于 /usr/local/apache2/htdocs 已经存在,原有数据会被隐藏起来,取而代之的是 host $HOME/htdocs/ 中的数据,这与 linux&mount 命令的行为是一致的。
curl 显示当前主页确实是 $HOME/htdocs/index.html 中的内容。更新一下,看是否能生效:
host 中的修改确实生效了,bind mount 可以让 host 与容器共享数据。这在管理上是非常方便的。
下面我们将容器销毁,看看对 bind mount 有什么影响:
可见,即使容器没有了,bind mount 也还在。这也合理,bind mount 是 host 文件系统中的数据,只是借给容器用用,哪能随便就删了啊。
另外,bind mount 时还可以指定数据的读写权限,默认是可读可写,可指定为只读:
ro 设置了只读权限,在容器中是无法对 bind mount 数据进行修改的。只有 host 有权修改数据,提高了安全性。
除了 bind mount 目录,还可以单独指定一个文件:
使用 bind mount 单个文件的场景是:只需要向容器添加文件,不希望覆盖整个目录。在上面的例子中,我们将 html 文件加到 apache 中,同时也保留了容器原有的数据。
使用单一文件有一点要注意:host 中的源文件必须要存在,不然会当作一个新目录 bind mount 给容器。
mount point 有很多应用场景,比如我们可以将源代码目录 mount 到容器中,在 host 中修改代码就能看到应用的实时效果。再比如将 mysql 容器的数据放在 bind mount 里,这样 host 可以方便地备份和迁移数据。
bind mount 的使用直观高效,易于理解,但它也有不足的地方:bind mount 需要指定 host 文件系统的特定路径,这就限制了容器的可移植性,当需要将容器迁移到其他 host,而该 host 没有要 mount 的数据或者数据不在相同的路径时,操作会失败。
移植性更好的方式是 docker managed volume,下一节我们讨论。
有个好消息:出版社现在搞促销,《每天5分钟玩转OpenStack》全网最低价销售,有兴趣的同学可以访问&&了解详情 。
310001SPJJ
2.161 vistas
我们从本章开始讨论 Docker 存储。
Docker 为容器提供了两种存放数据的资源:
由 storage driver 管理的镜像层和容器层。
Data Volume。
我们会详细讨论它们的原理和特性。
storage driver
在前面镜像章节我们学习到 Docker 镜像的分层结构,简单回顾一下。
容器由最上面一个可写的容器层,以及若干只读的镜像层组成,容器的数据就存放在这些层中。这样的分层结构最大的特性是 Copy-on-Write:
新数据会直接存放在最上面的容器层。
修改现有数据会先从镜像层将数据复制到容器层,修改后的数据直接保存在容器层中,镜像层保持不变。
如果多个层中有命名相同的文件,用户只能看到最上面那层中的文件。
分层结构使镜像和容器的创建、共享以及分发变得非常高效,而这些都要归功于 Docker storage driver。正是 storage driver 实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图。
Docker 支持多种 storage driver,有 AUFS、Device Mapper、Btrfs、OverlayFS、VFS 和 ZFS。它们都能实现分层的架构,同时又有各自的特性。对于 Docker 用户来说,具体选择使用哪个 storage driver 是一个难题,因为:
没有哪个 driver 能够适应所有的场景。
driver 本身在快速发展和迭代。
不过 Docker 官方给出了一个简单的答案:
优先使用 Linux 发行版默认的 storage driver。
Docker 安装时会根据当前系统的配置选择默认的 driver。默认 driver 具有最好的稳定性,因为默认 driver 在发行版上经过了严格的测试。
运行docker info查看 Ubuntu 的默认 driver:
Ubuntu 用的 AUFS,底层文件系统是 extfs,各层数据存放在 /var/lib/docker/aufs。
Redhat/CentOS 的默认 driver 是 Device Mapper,SUSE 则是 Btrfs。
对于某些容器,直接将数据放在由 storage driver 维护的层中是很好的选择,比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建。
比如 busybox,它是一个工具箱,我们启动 busybox 是为了执行诸如 wget,ping 之类的命令,不需要保存数据供以后使用,使用完直接退出,容器删除时存放在容器层中的工作数据也一起被删除,这没问题,下次再启动新容器即可。
但对于另一类应用这种方式就不合适了,它们有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,也就是说,这类容器是有状态的。
这就要用到 Docker 的另一种存储机制:Data Volume,下一节我们讨论。
有个好消息:出版社现在搞促销,《每天5分钟玩转OpenStack》直降26元,全网最低价销售,有兴趣的同学可以访问&&了解详情。
310001SPJJ
2.438 vistas
上节我们学习了容器如何访问外部网络,今天讨论另一个方向:外部网络如何访问到容器?
答案是:端口映射。
docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。容器启动时通过-p参数映射端口:
容器启动后,可通过 docker ps 或者 docker port 查看到 host 映射的端口。在上面的例子中,httpd 容器的 80 端口被映射到 host 32773 上,这样就可以通过 &host ip&:&32773& 访问容器的 web 服务了。
除了映射动态端口,也可在 -p 中指定映射到 host 某个特定端口,例如可将 80 端口映射到 host 的 8080 端口:
每一个映射的端口,host 都会启动一个 docker-proxy 进程来处理访问容器的流量:
以 0.0.0.0:32773-&80/tcp 为例分析整个过程:
docker-proxy 监听 host 的 32773 端口。
当 curl 访问 10.0.2.15:32773 时,docker-proxy 转发给容器 172.17.0.2:80。
httpd 容器响应请求并返回结果。
在这一章我们首先学习了 Docker 的三种网络:none, host 和 bridge 并讨论了它们的不同使用场景;然后我们实践了创建自定义网络;最后详细讨论了如何实现容器与容器之间,容器与外部网络之间的通信。
本章重点关注的是单个主机内的容器网络,对于跨主机网络通信将在后面章节详细讨论。下一节开始学习 Docker 存储。
310001SPJJ
2.085 vistas
前面我们已经解决了容器间通信的问题,接下来讨论容器如何与外部世界通信。这里涉及两个方向:
容器访问外部世界
外部世界访问容器
容器访问外部世界
在我们当前的实验环境下,docker host 是可以访问外网的。
我们看一下容器是否也能访问外网呢?
可见,容器默认就能访问外网。
请注意:这里外网指的是容器网络以外的网络环境,并非特指 internet。
现象很简单,但更重要的:我们应该理解现象下的本质。
在上面的例子中,busybox 位于 docker0 这个私有 bridge 网络中(172.17.0.0/16),当 busybox 从容器向外 ping 时,数据包是怎样到达
这里的关键就是 NAT。我们查看一下 docker host 上的 iptables 规则:
在 NAT 表中,有这么一条规则:
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
其含义是:如果网桥 docker0 收到来自 172.17.0.0/16 网段的外出包,把它交给 MASQUERADE 处理。而 MASQUERADE 的处理方式是将包的源地址替换成 host 的地址发送出去,即做了一次网络地址转换(NAT)。
下面我们通过 tcpdump 查看地址是如何转换的。先查看 docker host 的路由表:
默认路由通过 enp0s3 发出去,所以我们要同时监控 enp0s3 和 docker0 上的 icmp(ping)数据包。
当 busybox
时,tcpdump 输出如下:
docker0 收到 busybox 的 ping 包,源地址为容器 IP 172.17.0.2,这没问题,交给 MASQUERADE 处理。这时,在 enp0s3 上我们看到了变化:
ping 包的源地址变成了 enp0s3 的 IP 10.0.2.15
这就是 iptable NAT 规则处理的结果,从而保证数据包能够到达外网。下面用一张图来说明这个过程:
busybox 发送 ping 包:172.17.0.2 & 。
docker0 收到包,发现是发送到外网的,交给 NAT 处理。
NAT 将源地址换成 enp0s3 的 IP:10.0.2.15 & 。
ping 包从 enp0s3 发送出去,到达 。
通过 NAT,docker 实现了容器对外网的访问。
下一节我们讨论另一个方向的流量:外部世界如何访问容器。&
310001SPJJ
2.948 vistas
容器之间可通过 IP,Docker DNS Server 或 joined 容器三种方式通信。
从上一节的例子可以得出这样一个结论:两个容器要能通信,必须要有属于同一个网络的网卡。
满足这个条件后,容器就可以通过 IP 交互了。具体做法是在容器创建时通过&--network&指定相应的网络,或者通过&docker network connect&将现有容器加入到指定网络。可参考上一节 httpd 和 busybox 的例子,这里不再赘述。
Docker DNS Server
通过 IP 访问容器虽然满足了通信的需求,但还是不够灵活。因为我们在部署应用之前可能无法确定 IP,部署之后再指定要访问的 IP 会比较麻烦。对于这个问题,可以通过 docker 自带的 DNS 服务解决。
从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过&容器名&通信。方法很简单,只要在启动时用&--name&为容器命名就可以了。
下面启动两个容器 bbox1 和 bbox2:
docker run -it --network=my_net2 --name=bbox1 busybox
docker run -it --network=my_net2 --name=bbox2 busybox
然后,bbox2 就可以直接 ping 到 bbox1 了:
使用 docker DNS 有个限制:只能在 user-defined 网络中使用。也就是说,默认的 bridge 网络是无法使用 DNS 的。下面验证一下:
创建 bbox3 和 bbox4,均连接到 bridge 网络。
docker run -it --name=bbox3 busybox
docker run -it --name=bbox4 busybox
bbox4 无法 ping 到 bbox3。
joined 容器
joined 容器是另一种实现容器间通信的方式。
joined 容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信。请看下面的例子:
先创建一个 httpd 容器,名字为 web1。
docker run -d -it --name=web1 httpd
然后创建 busybox 容器并通过&--network=container:web1&指定 jointed 容器为 web1:
请注意 busybox 容器中的网络配置信息,下面我们查看一下 web1 的网络:
看!busybox 和 web1 的网卡 mac 地址与 IP 完全一样,它们共享了相同的网络栈。busybox 可以直接用 127.0.0.1 访问 web1 的 http 服务。
joined 容器非常适合以下场景:
不同容器中的程序希望通过 loopback 高效快速地通信,比如 web server 与 app server。
希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序。
容器之间的通信我们已经搞清楚了,接下来要考虑的是容器如何与外部世界通信?这将是下一节的主题。
310001SPJJ
1.785 vistas
通过前面小节的实践,当前 docker host 的网络拓扑结构如下图所示,今天我们将讨论这几个容器之间的连通性。
两个 busybox 容器都挂在 my_net2 上,应该能够互通,我们验证一下:
可见同一网络中的容器、网关之间都是可以通信的。
my_net2&与默认 bridge 网络能通信吗?
从拓扑图可知,两个网络属于不同的网桥,应该不能通信,我们通过实验验证一下,让 busybox 容器 ping httpd 容器:
确实 ping 不通,符合预期。
&等等!不同的网络如果加上路由应该就可以通信了吧?&我已经听到有读者在建议了。
这是一个非常非常好的想法。
确实,如果 host 上对每个网络的都有一条路由,同时操作系统上打开了 ip forwarding,host 就成了一个路由器,挂接在不同网桥上的网络就能够相互通信。下面我们来看看 docker host 满不满足这些条件呢?
ip r&查看 host 上的路由表:
172.17.0.0/16 dev docker0 &proto kernel &scope link &src 172.17.0.1
172.22.16.0/24 dev br-5d863e9f78b6 &proto kernel &scope link &src 172.22.16.1
172.17.0.0/16 和 172.22.16.0/24 两个网络的路由都定义好了。再看看 ip forwarding:
# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 1
ip forwarding 也已经启用了。
条件都满足,为什么不能通行呢?
我们还得看看 iptables:
# iptables-save
-A DOCKER-ISOLATION -i br-5d863e9f78b6 -o docker0 -j DROP
-A DOCKER-ISOLATION -i docker0 -o br-5d863e9f78b6 -j DROP
原因就在这里了:iptables DROP 掉了网桥 docker0 与 br-5d863e9f78b6 之间双向的流量。
从规则的命名&DOCKER-ISOLATION&可知 docker 在设计上就是要隔离不同的 netwrok。
那么接下来的问题是:怎样才能让 busybox 与 httpd 通信呢?
答案是:为 httpd 容器添加一块 net_my2 的网卡。这个可以通过docker network connect&命令实现。
我们在 httpd 容器中查看一下网络配置:
容器中增加了一个网卡 eth1,分配了 my_net2 的 IP 172.22.16.3。现在 busybox 应该能够访问 httpd 了,验证一下:
busybox 能够 ping 到 httpd,并且可以访问 httpd 的 web 服务。当前网络结构如图所示:
下一节我们讨论容器间通信的三种方式。
310001SPJJ
2.457 vistas
除了 none, host, bridge 这三个自动创建的网络,用户也可以根据业务需要创建 user-defined 网络。
Docker 提供三种 user-defined 网络驱动:bridge, overlay 和 macvlan。overlay 和 macvlan 用于创建跨主机的网络,我们后面有章节单独讨论。
我们可通过 bridge 驱动创建类似前面默认的 bridge 网络,例如:
查看一下当前 host 的网络结构变化:
新增了一个网桥&br-eaed97dc9a77,这里&eaed97dc9a77&正好新建 bridge 网络&my_net&的短 id。执行&docker network inspect&查看一下&my_net&的配置信息:
这里 172.18.0.0/16 是 Docker 自动分配的 IP 网段。
我们可以自己指定 IP 网段吗?
答案是:可以。
只需在创建网段时指定&--subnet&和&--gateway&参数:
这里我们创建了新的 bridge 网络&my_net2,网段为 172.22.16.0/24,网关为 172.22.16.1。与前面一样,网关在&my_net2&对应的网桥&br-5d863e9f78b6&上:
容器要使用新的网络,需要在启动时通过&--network&指定:
容器分配到的 IP 为 172.22.16.2。
到目前为止,容器的 IP 都是 docker 自动从 subnet 中分配,我们能否指定一个静态 IP 呢?
答案是:可以,通过--ip指定。
注:只有使用&--subnet&创建的网络才能指定静态 IP。
my_net&创建时没有指定&--subnet,如果指定静态 IP 报错如下:
好了,我们来看看当前 docker host 的网络拓扑结构。
下一节讨论这几个容器之间的连通性。
310001SPJJ
1.724 vistas
上一节我们讨论了 none 和 host 类型的容器网络,本节学习应用最广泛也是默认的 bridge 网络。
Docker 安装时会创建一个 命名为&docker0&的 linux bridge。如果不指定--network,创建的容器默认都会挂到&docker0&上。
当前&docker0&上没有任何其他网络设备,我们创建一个容器看看有什么变化。
一个新的网络接口&veth28c57df&被挂到了&docker0&上,veth28c57df就是新创建容器的虚拟网卡。
下面看一下容器的网络配置。
容器有一个网卡&eth0@if34。大家可能会问了,为什么不是veth28c57df&呢?
实际上&eth0@if34&和&veth28c57df&是一对 veth pair。veth pair 是一种成对出现的特殊网络设备,可以把它们想象成由一根虚拟网线连接起来的一对网卡,网卡的一头(eth0@if34)在容器中,另一头(veth28c57df)挂在网桥&docker0&上,其效果就是将&eth0@if34&也挂在了&docker0&上。
我们还看到&eth0@if34&已经配置了 IP&172.17.0.2,为什么是这个网段呢?让我们通过&docker network inspect bridge&看一下 bridge 网络的配置信息:
原来 bridge 网络配置的 subnet 就是 172.17.0.0/16,并且网关是 172.17.0.1。这个网关在哪儿呢?大概你已经猜出来了,就是 docker0。
当前容器网络拓扑结构如图所示:
容器创建时,docker 会自动从 172.17.0.0/16 中分配一个 IP,这里 16 位的掩码保证有足够多的 IP 可以供容器使用。
除了 none, host, bridge 这三个自动创建的网络,用户也可以根据业务需要创建 user-defined 网络,下一节我们将详细讨论。
310001SPJJ
1.573 vistas
本章开始讨论 Docker 网络。
我们会首先学习 Docker 提供的几种原生网络,以及如何创建自定义网络。然后探讨容器之间如何通信,以及容器与外界如何交互。
Docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络,本章重点讨论前一种。对于更为复杂的多 host 容器网络,我们会在后面进阶技术章节单独讨论。
Docker 安装时会自动在 host 上创建三个网络,我们可用&docker network ls&命令查看:
下面我们分别讨论它们。
故名思议,none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。容器创建时,可以通过&--network=none&指定使用 none 网络。
我们不禁会问,这样一个封闭的网络有什么用呢?
其实还真有应用场景。封闭意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用 none 网络。
比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。
当然大部分容器是需要网络的,我们接着看 host 网络。
连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。可以通过&--network=host&指定使用 host 网络。
在容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的。host 网络的使用场景又是什么呢?
直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络。当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host 上已经使用的端口就不能再用了。
Docker host 的另一个用途是让容器可以直接配置 host 网路。比如某些跨 host 的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理 iptables,大家将会在后面进阶技术章节看到。
下一节讨论应用更广的 bridge 网络。
310001SPJJ
1.740 vistas
为了更好地理解容器的特性,本节我们将讨论容器的底层实现技术。
cgroup 和 namespace 是最重要的两种技术。cgroup 实现资源限额, namespace 实现资源隔离。
cgroup 全称 Control Group。Linux 操作系统通过 cgroup 可以设置进程使用 CPU、内存 和 IO 资源的限额。相信你已经猜到了:前面我们看到的--cpu-shares、-m、--device-write-bps&实际上就是在配置 cgroup。
cgroup 到底长什么样子呢?我们可以在 /sys/fs/cgroup 中找到它。还是用例子来说明,启动一个容器,设置&--cpu-shares=512:
查看容器的 ID:
在 /sys/fs/cgroup/cpu/docker 目录中,Linux 会为每个容器创建一个 cgroup 目录,以容器长ID 命名:
目录中包含所有与 cpu 相关的 cgroup 配置,文件 cpu.shares 保存的就是&--cpu-shares&的配置,值为 512。
同样的,/sys/fs/cgroup/memory/docker 和 /sys/fs/cgroup/blkio/docker 中保存的是内存以及 Block IO 的 cgroup 配置。
在每个容器中,我们都可以看到文件系统,网卡等资源,这些资源看上去是容器自己的。拿网卡来说,每个容器都会认为自己有一块独立的网卡,即使 host 上只有一块物理网卡。这种方式非常好,它使得容器更像一个独立的计算机。
Linux 实现这种方式的技术是 namespace。namespace 管理着 host 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说,namespace 实现了容器间资源的隔离。
Linux 使用了六种 namespace,分别对应六种资源:Mount、UTS、IPC、PID、Network 和 User,下面我们分别讨论。
Mount namespace
Mount namespace 让容器看上去拥有整个文件系统。
容器有自己的&/&目录,可以执行&mount&和&umount&命令。当然我们知道这些操作只在当前容器中生效,不会影响到 host 和其他容器。
UTS namespace
简单的说,UTS namespace 让容器有自己的 hostname。 默认情况下,容器的 hostname 是它的短ID,可以通过&-h&或&--hostname&参数设置。
IPC namespace
IPC namespace 让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与 host 和其他容器的 IPC 混在一起。
PID namespace
我们前面提到过,容器在 host 中以进程的形式运行。例如当前 host 中运行了两个容器:
通过&ps axf&可以查看容器进程:
所有容器的进程都挂在 dockerd 进程下,同时也可以看到容器自己的子进程。 如果我们进入到某个容器,ps&就只能看到自己的进程了:
而且进程的 PID 不同于 host 中对应进程的 PID,容器中 PID=1 的进程当然也不是 host 的 init 进程。也就是说:容器拥有自己独立的一套 PID,这就是 PID namespace 提供的功能。
Network namespace
Network namespace 让容器拥有自己独立的网卡、IP、路由等资源。我们会在后面网络章节详细讨论。
User namespace
User namespace 让容器能够管理自己的用户,host 不能看到容器中创建的用户。
在容器中创建了用户 cloudman,但 host 中并不会创建相应的用户。
本章首先通过大量实验学习了容器的各种操作以及容器状态之间如何转换,然后讨论了限制容器使用 CPU、内存和 Block IO 的方法,最后学习了实现容器的底层技术:cgroup 和 namespace。
下面是容器的常用操作命令:
create & & &创建容器 &
run & & & & 运行容器 &
pause & & & 暂停容器 &
unpause & & 取消暂停继续运行容器 &
stop & & & &发送 SIGTERM 停止容器 &
kill & & & &发送 SIGKILL 快速停止容器 &
start & & & 启动容器 &
restart & & 重启容器 &
attach & & &attach 到容器启动进程的终端 &
exec & & & &在容器中启动新进程,通常使用 &-it& 参数 &
logs & & & &显示容器启动进程的控制台输出,用 &-f& 持续打印 &
rm & & & & &从磁盘中删除容器
到这里,我们已经学习完了容器章节。下一节开始讨论容器网络。
310001SPJJ
2.368 vistas
前面学习了如何限制容器对内存和CPU的使用,本节我们来看 Block IO。
Block IO 是另一种可以限制容器使用的资源。Block IO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽,下面分别讨论。
注:目前 Block IO 限额只对 direct IO(不使用文件缓存)有效。
block IO 权重
默认情况下,所有容器能平等地读写磁盘,可以通过设置&--blkio-weight&参数来改变容器 block IO 的优先级。
--blkio-weight&与&--cpu-shares&类似,设置的是相对权重值,默认为 500。在下面的例子中,container_A 读写磁盘的带宽是 container_B 的两倍。
docker run -it --name container_A --blkio-weight 600 ubuntu &&
docker run -it --name container_B --blkio-weight 300 ubuntu
限制 bps 和 iops
bps 是 byte per second,每秒读写的数据量。
iops 是 io per second,每秒 IO 的次数。
可通过以下参数控制容器的 bps 和 iops:
--device-read-bps,限制读某个设备的 bps。
--device-write-bps,限制写某个设备的 bps。
--device-read-iops,限制读某个设备的 iops。
--device-write-iops,限制写某个设备的 iops。
下面这个例子限制容器写 /dev/sda 的速率为 30 MB/s
docker run -it --device-write-bps /dev/sda:30MB ubuntu
我们来看看实验结果:
通过 dd 测试在容器中写磁盘的速度。因为容器的文件系统是在 host /dev/sda 上的,在容器中写文件相当于对 host /dev/sda 进行写操作。另外,oflag=direct&指定用 direct IO 方式写文件,这样&--device-write-bps&才能生效。
结果表明,bps 25.6 MB/s 没有超过 30 MB/s 的限速。
作为对比测试,如果不限速,结果如下:
其他参数的使用方法类似,留给大家自己练习。
下一节我们讨论实现容器的底层技术。
310001SPJJ
2.237 vistas
上节学习了如何限制容器对内存的使用,本节我们来看CPU。
默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制。
Docker 可以通过&-c&或&--cpu-shares&设置容器使用 CPU 的权重。如果不指定,默认值为 1024。
与内存限额不同,通过&-c&设置的 cpu share 并不是 CPU 资源的绝对数量,而是一个相对的权重值。某个容器最终能分配到的 CPU 资源取决于它的 cpu share 占所有容器 cpu share 总和的比例。
换句话说:通过 cpu share 可以设置容器使用 CPU 的优先级。
比如在 host 中启动了两个容器:
docker run --name &container_A& -c 1024 ubuntu
docker run --name &container_B& -c 512 ubuntu
container_A 的 cpu share 1024,是 container_B 的两倍。当两个容器都需要 CPU 资源时,container_A 可以得到的 CPU 是 container_B 的两倍。
需要特别注意的是,这种按权重分配 CPU 只会发生在 CPU 资源紧张的情况下。如果 container_A 处于空闲状态,这时,为了充分利用 CPU 资源,container_B 也可以分配到全部可用的 CPU。
下面我们继续用 progrium/stress 做实验。
启动 container_A,cpu share 为 1024:&
--cpu&用来设置工作线程的数量。因为当前 host 只有 1 颗 CPU,所以一个工作线程就能将 CPU 压满。如果 host 有多颗 CPU,则需要相应增加&--cpu&的数量。
启动 container_B,cpu share 为 512:&
在 host 中执行&top,查看容器对 CPU 的使用情况:&
container_A 消耗的 CPU 是 container_B 的两倍。
现在暂停 container_A:&
top&显示 container_B 在 container_A 空闲的情况下能够用满整颗 CPU:&
CPU限额就讨论到这里,下一节我们将学习如何限制容器对 Block IO 带宽资源的使用。
310001SPJJ
3.317 vistas
一个 docker host 上会运行若干容器,每个容器都需要 CPU、内存和 IO 资源。对于 KVM,VMware 等虚拟化技术,用户可以控制分配多少 CPU、内存资源给每个虚拟机。对于容器,Docker 也提供了类似的机制避免某个容器因占用太多资源而影响其他容器乃至整个 host 的性能。
与操作系统类似,容器可使用的内存包括两部分:物理内存和 swap。 Docker 通过下面两组参数来控制容器内存的使用量。
-m&或&--memory:设置内存的使用限额,例如 100M, 2G。
--memory-swap:设置&内存+swap&的使用限额。
当我们执行如下命令:
docker run -m 200M --memory-swap=300M ubuntu
其含义是允许该容器最多使用 200M 的内存和 100M 的 swap。默认情况下,上面两组参数为 -1,即对容器内存和 swap 的使用没有限制。
下面我们将使用 progrium/stress 镜像来学习如何为容器分配内存。该镜像可用于对容器执行压力测试。执行如下命令:
docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
--vm 1:启动 1 个内存工作线程。
--vm-bytes 280M:每个线程分配 280M 内存。
运行结果如下:
因为 280M 在可分配的范围(300M)内,所以工作线程能够正常工作,其过程是:
分配 280M 内存。
释放 280M 内存。
再分配 280M 内存。
再释放 280M 内存。
一直循环......
如果让工作线程分配的内存超过 300M,结果如下:
分配的内存超过限额,stress 线程报错,容器退出。
如果在启动容器时只指定&-m&而不指定&--memory-swap,那么&--memory-swap&默认为&-m&的两倍,比如:
docker run -it -m 200M ubuntu
容器最多使用 200M 物理内存和 200M swap。
内存限额就讨论到这里,下一节我们将学习如何限制容器对 CPU 资源的使用。
310001SPJJ
2.024 vistas
前面我们已经讨论了容器的各种操作,对容器的生命周期有了大致的理解,下面这张状态机很好地总结了容器各种状态之间是如何转换的。
如果掌握了前面的知识,要看懂这张图应该不难。不过有两点还是需要补充一下:
可以先创建容器,稍后再启动。&
①&docker create&创建的容器处于 Created 状态。
②&docker start&将以后台方式启动容器。&docker run&命令实际上是&docker create&和&docker start&的组合。
只有当容器的启动进程&退出&时,--restart&才生效。&
退出包括正常退出或者非正常退出。这里举了两个例子:启动进程正常退出或发生 OOM,此时 docker 会根据&--restart&的策略判断是否需要重启容器。但如果容器是因为执行&docker stop&或docker kill&退出,则不会自动重启。
好了,容器操作就讨论到这里,下一节我们将学习如何限制容器对资源的使用。
310001SPJJ
3.052 vistas
前面讨论了如何运行容器,本节学习容器的其他常用操作。
stop/start/restart 容器
通过&docker stop&可以停止运行的容器。
容器在 docker host 中实际上是一个进程,docker stop&命令本质上是向该进程发送一个 SIGTERM 信号。如果想快速停止容器,可使用&docker kill&命令,其作用是向容器进程发送 SIGKILL 信号。
对于处于停止状态的容器,可以通过&docker start&重新启动。
docker start&会保留容器的第一次启动时的所有参数。
docker restart&可以重启容器,其作用就是依次执行&docker stop&和docker start。
容器可能会因某种错误而停止运行。对于服务类容器,我们通常希望在这种情况下容器能够自动重启。启动容器时设置&--restart&就可以达到这个效果。
--restart=always&意味着无论容器因何种原因退出(包括正常退出),就立即重启。该参数的形式还可以是&--restart=on-failure:3,意思是如果启动进程退出代码非0,则重启容器,最多重启3次。
pause/unpause 容器
有时我们只是希望暂时让容器暂停工作一段时间,比如要对容器的文件系统打个快照,或者 dcoker host 需要使用 CPU,这时可以执行&docker pause。
处于暂停状态的容器不会占用 CPU 资源,直到通过&docker unpause&恢复运行。
使用 docker 一段时间后,host 上可能会有大量已经退出了的容器。
这些容器依然会占用 host 的文件系统资源,如果确认不会再重启此类容器,可以通过&docker rm&删除。
docker rm&一次可以指定多个容器,如果希望批量删除所有已经退出的容器,可以执行如下命令:
docker rm -v $(docker ps -aq -f status=exited)
顺便说一句:docker rm&是删除容器,而&docker rmi&是删除镜像。
一下学了这么多操作,很有必要做个总结。下一节我们会用一张图来描述容器的状态机。
310001SPJJ
1.545 vistas
按用途容器大致可分为两类:服务类容器和工具类的容器。
1. 服务类容器以 daemon 的形式运行,对外提供服务。比如 web server,数据库等。通过&-d&以后台方式启动这类容器是非常合适的。如果要排查问题,可以通过&exec -it&进入容器。
2. 工具类容器通常给能我们提供一个临时的工作环境,通常以&run -it&方式运行,比如:
运行 busybox,run -it&的作用是在容器启动后就直接进入。我们这里通过&wget&验证了在容器中访问 internet 的能力。执行&exit&退出终端,同时容器停止。
工具类容器多使用基础镜像,例如 busybox、debian、ubuntu 等。
容器运行小结
容器运行相关的知识点:
当 CMD 或 Entrypoint 或 docker run 命令行指定的命令运行结束时,容器停止。
通过&-d&参数在后台启动容器。
通过&exec -it&可进入容器并执行命令。
指定容器的三种方法:
容器名称。 可通过&--name&为容器命名。重命名容器可执行docker rename。
容器按用途可分为两类:
服务类的容器。
工具类的容器。
下一节讨论容器的其他操作,比如 stop, restart, pause, delete。
310001SPJJ
3.515 vistas
我们经常需要进到容器里去做一些工作,比如查看日志、调试、启动其他进程等。有两种方法进入容器:attach 和 exec。
docker attach
通过&docker attach&可以 attach 到容器启动命令的终端,例如:
这次我们通过 &长ID& attach 到了容器的启动命令终端,之后看到的是echo&每隔一秒打印的信息。
注:可通过 Ctrl+p 然后 Ctrl+q 组合键退出 attach 终端。
docker exec
通过&docker exec&进入相同的容器:
说明如下:
①&-it&以交互模式打开 pseudo-TTY,执行 bash,其结果就是打开了一个 bash 终端。
② 进入到容器中,容器的 hostname 就是其 &短ID&。
③ 可以像在普通 Linux 中一样执行命令。ps -elf&显示了容器启动进程while&以及当前的&bash&进程。
④ 执行&exit&退出容器,回到 docker host。
docker exec -it &container& bash|sh&是执行 exec 最常用的方式。
attach VS exec
attach 与 exec 主要区别如下:
attach 直接进入容器&启动命令&的终端,不会启动新的进程。
exec 则是在容器中打开新的终端,并且可以启动新的进程。
如果想直接在终端中查看启动命令的输出,用 attach;其他情况使用 exec。
当然,如果只是为了查看启动命令的输出,可以使用&docker logs&命令:
-f&的作用与&tail -f&类似,能够持续打印输出。
下一节聊聊运行容器的最佳实践。
310001SPJJ
1.819 vistas
上一章我们学习了如何构建 Docker 镜像,并通过镜像运行容器。本章将深入讨论容器:学习容器的各种操作,容器各种状态之间如何转换,以及实现容器的底层技术。
docker run&是启动容器的方法。在讨论 Dockerfile 时我们已经学习到,可用三种方式指定容器启动时执行的命令:
CMD 指令。
ENDPOINT 指令。
在&docker run&命令行中指定。
例如下面的例子:
容器启动时执行&pwd,返回的&/&是容器中的当前目录。 执行&docker ps&或&docker container ls&可以查看 Docker host 中当前运行的容器:
咦,怎么没有容器?用&docker ps -a&或&docker container ls -a&看看。
-a&会显示所有状态的容器,可以看到,之前的容器已经退出了,状态为Exited。
这种&一闪而过&的容器通常不是我们想要的结果,我们希望容器能够保持 runing 状态,这样才能被我们使用。
让容器长期运行
如何让容器保存运行呢?
因为容器的生命周期依赖于启动时执行的命令,只要该命令不结束,容器也就不会退出。
理解了这个原理,我们就可以通过执行一个长期运行的命令来保持容器的运行状态。例如执行下面的命令:
while&语句让 bash 不会退出。我们可以打开另一个终端查看容器的状态:
可见容器仍处于运行状态。不过这种方法有个缺点:它占用了一个终端。
我们可以加上参数&-d&以后台方式启动容器。
容器启动后回到了 docker host 的终端。这里看到 docker 返回了一串字符,这是容器的 ID。通过&docker ps&查看容器:
现在我们有了两个正在运行的容器。这里注意一下容器的&CONTAINER ID和&NAMES&这两个字段。
CONTAINER ID&是容器的 &短ID&,前面启动容器时返回的是 &长ID&。短ID是长ID的前12个字符。
NAMES&字段显示容器的名字,在启动容器时可以通过&--name&参数显示地为容器命名,如果不指定,docker 会自动为容器分配名字。
对于容器的后续操作,我们需要通过 &长ID&、&短ID& 或者 &名称& 来指定要操作的容器。比如下面停止一个容器:
这里我们就是通过 &短ID& 指定了要停止的容器。
通过&while&启动的容器虽然能够保持运行,但实际上没有干什么有意义的事情。容器常见的用途是运行后台服务,例如前面我们已经看到的 http server:
这一次我们用&--name&指定了容器的名字。 我们还看到容器运行的命令是httpd-foreground,通过&docker history&可知这个命令是通过 CMD 指定的。
我们经常需要进到容器里去做一些工作,比如查看日志、调试、启动其他进程等。下一节学习如何进入容器内部。
310001SPJJ
2.629 vistas
本节我们对 Docker 镜像做个小结。
这一部分我们首先讨论了镜像的分层结构,然后学习了如何构建镜像,最后实践使用 Docker Hub 和本地 registry。
下面是镜像的常用操作子命令:
images & &显示镜像列表
history & 显示镜像构建历史
commit & &从容器创建新镜像
build & & 从 Dockerfile 构建镜像
tag & & & 给镜像打 tag
pull & & &从 registry 下载镜像
push & & &将 镜像 上传到 registry
rmi & & & 删除 Docker host 中的镜像
search & &搜索 Docker Hub 中的镜像
除了 rmi 和 search,其他命令都已经用过了。
rmi 只能删除 host 上的镜像,不会删除 registry 的镜像。
如果一个镜像对应了多个 tag,只有当最后一个 tag 被删除时,镜像才被真正删除。例如 host 中 debian 镜像有两个 tag:
删除其中 debian:latest 只是删除了 latest tag,镜像本身没有删除。
只有当 debian:jessie 也被删除时,整个镜像才会被删除。
search 让我们无需打开浏览器,在命令行中就可以搜索 Docker Hub 中的镜像。
当然,如果想知道镜像都有哪些 tag,还是得访问 Docker Hub。
至此,Docker 镜像已经讨论完了,下节我们深入讨论容器。
310001SPJJ
2.960 vistas
Docker Hub 虽然非常方便,但还是有些限制,比如:
需要 internet 连接,而且下载和上传速度慢。
上传到 Docker Hub 的镜像任何人都能够访问,虽然可以用私有 repository,但不是免费的。
安全原因很多组织不允许将镜像放到外网。
解决方案就是搭建本地的 Registry。
Docker 已经将 Registry 开源了,同时在 Docker Hub 上也有官方的镜像 registry。下面我们就在 Docker 中运行自己的 registry。
启动 registry 容器。
我们使用的镜像是 registry:2。
-d&是后台启动容器。
-p&将容器的 5000 端口映射到 Host 的 5000 端口。5000 是 registry 服务端口。端口映射我们会在容器网络章节详细讨论。
-v&将容器 /var/lib/registry 目录映射到 Host 的 /myregistry,用于存放镜像数据。-v&的使用我们会在容器存储章节详细讨论。
通过&docker tag&重命名镜像,使之与 registry 匹配。
我们在镜像的前面加上了运行 registry 的主机名称和端口。
前面已经讨论了镜像名称由 repository 和 tag 两部分组成。而 repository 的完整格式为:[registry-host]:[port]/[username]/xxx
只有 Docker Hub 上的镜像可以省略&[registry-host]:[port]&。
通过&docker pull&上传镜像。
现在已经可通过&docker pull&从本地 registry 下载镜像了。
除了镜像的名称长一些(包含 registry host 和 port),使用方式完全一样。
以上是搭建本地 registry 的简要步骤。当然 registry 也支持认证,https 安全传输等特性,具体可以参考官方文档&
至此,Docker 镜像的内容就讨论完了,下节我们对这部分做个小结。
310001SPJJ
1.955 vistas
保存和分发镜像的最直接方法就是使用 Docker Hub。
Docker Hub 是 Docker 公司维护的公共 Registry。用户可以将自己的镜像保存到 Docker Hub 免费的 repository 中。如果不希望别人访问自己的镜像,也可以购买私有 repository。
除了 Docker Hub,quay.io 是另一个公共 Registry,提供与 Docker Hub 类似的服务。
下面介绍如何用 Docker Hub 存取我们的镜像。
首先得在 Docker Hub 上注册一个账号。
在 Docker Host 上登录。
这里用的是我自己的账号,用户名为 cloudman6,输入密码后登录成功。
修改镜像的 repository 使之与 Docker Hub 账号匹配。
Docker Hub 为了区分不同用户的同名镜像,镜像的 registry 中要包含用户名,完整格式为:[username]/xxx:tag
我们通过&docker tag&命令重命名镜像。
注:Docker 官方自己维护的镜像没有用户名,比如 httpd。
通过 docker push 将镜像上传到 Docker Hub。&
Docker 会上传镜像的每一层。因为 cloudman6/httpd:v1 这个镜像实际上跟官方的 httpd 镜像一模一样,Docker Hub 上已经有了全部的镜像层,所以真正上传的数据很少。同样的,如果我们的镜像是基于 base 镜像的,也只有新增加的镜像层会被上传。如果想上传同一 repository 中所有镜像,省略 tag 部分就可以了,例如:
&docker push cloudman6/httpd
登录&,在Public Repository 中就可以看到上传的镜像。
如果要删除上传的镜像,只能在 Docker Hub 界面上操作。
这个镜像可被其他 Docker host 下载使用了。&
下一节讨论如何搭建本地 Registry。
310001SPJJ
2.813 vistas
我们已经学会构建自己的镜像了。接下来的问题是如何在多个 Docker Host 上使用镜像。
这里有几种可用的方法:
用相同的 Dockerfile 在其他 host 构建镜像。
将镜像上传到公共 Registry(比如 Docker Hub),Host 直接下载使用。
搭建私有的 Registry 供本地 Host 使用。
第一种方法没什么特别的,前面已经讨论很多了。我们将讨论如何使用公共和私有 Registry 分发镜像。
为镜像命名
无论采用何种方式保存和分发镜像,首先都得给镜像命名。
当我们执行&docker build&命令时已经为镜像取了个名字,例如前面:
docker build -t ubuntu-with-vi
这里的&ubuntu-with-vi&就是镜像的名字。通过 dock images 可以查看镜像的信息。
这里注意到&ubuntu-with-vi&对应的是&REPOSITORY,而且还有一个叫&latest&的&TAG。
实际上一个特定镜像的名字由两部分组成:repository 和 tag。
[image name] = [repository]:[tag]
如果执行 docker build 时没有指定 tag,会使用默认值 latest。其效果相当于:
docker build -t ubuntu-with-vi:latest
tag 常用于描述镜像的版本信息,比如 httpd 镜像:
当然 tag 可以是任意字符串,比如 ubuntu 镜像:
小心 latest tag
千万别被 latest tag 给误导了。latest 其实并没有什么特殊的含义。当没指明镜像 tag 时,Docker 会使用默认值 latest,仅此而已。
虽然 Docker Hub 上很多 repository 将 latest 作为最新稳定版本的别名,但这只是一种约定,而不是强制规定。
所以我们在使用镜像时最好还是避免使用 latest,明确指定某个 tag,比如 httpd:2.3,ubuntu:xenial。
tag 使用最佳实践
借鉴软件版本命名方式能够让用户很好地使用镜像。
一个高效的版本命名方案可以让用户清楚地知道当前使用的是哪个镜像,同时还可以保持足够的灵活性。
每个 repository 可以有多个 tag,而多个 tag 可能对应的是同一个镜像。下面通过例子为大家介绍 Docker 社区普遍使用的 tag 方案。
假设我们现在发布了一个镜像 myimage,版本为 v1.9.1。那么我们可以给镜像打上四个 tag:1.9.1、1.9、1 和 latest。
我们可以通过 docker tag 命令方便地给镜像打 tag。
docker tag myimage-v1.9.1 myimage:1
docker tag myimage-v1.9.1 myimage:1.9
docker tag myimage-v1.9.1 myimage:1.9.1
docker tag myimage-v1.9.1 myimage:latest
过了一段时间,我们发布了 v1.9.2。这时可以打上 1.9.2 的 tag,并将 1.9、1 和 latest 从 v1.9.1 移到 v1.9.2。
docker tag myimage-v1.9.2 myimage:1
docker tag myimage-v1.9.2 myimage:1.9
docker tag myimage-v1.9.2 myimage:1.9.2
docker tag myimage-v1.9.2 myimage:latest
之后,v2.0.0 发布了。这时可以打上 2.0.0、2.0 和 2 的 tag,并将 latest 移到 v2.0.0。
docker tag myimage-v2.0.0 myimage:2
docker tag myimage-v2.0.0 myimage:2.0
docker tag myimage-v2.0.0 myimage:2.0.0
docker tag myimage-v2.0.0 myimage:latest
这种 tag 方案使镜像的版本很直观,用户在选择非常灵活:
myimage:1 始终指向 1 这个分支中最新的镜像。
myimage:1.9 始终指向 1.9.x 中最新的镜像。
myimage:latest 始终指向所有版本中最新的镜像。
如果想使用特定版本,可以选择 myimage:1.9.1、myimage:1.9.2 或 myimage:2.0.0。
Docker Hub 上很多 repository 都采用这种方案,所以大家一定要熟悉。
下一节讨论如何使用使用公共 Registry。
310001SPJJ
5.613 vistas
RUN、CMD 和 ENTRYPOINT 这三个 Dockerfile 指令看上去很类似,很容易混淆。本节将通过实践详细讨论它们的区别。
简单的说:
RUN 执行命令并创建新的镜像层,RUN 经常用于安装软件包。
CMD 设置容器启动后默认执行的命令及其参数,但 CMD 能够被&docker run&后面跟的命令行参数替换。
ENTRYPOINT 配置容器启动时运行的命令。
下面我们详细分析。
Shell 和 Exec 格式
我们可用两种方式指定 RUN、CMD 和 ENTRYPOINT 要运行的命令:Shell 格式和 Exec 格式,二者在使用上有细微的区别。
Shell 格式
&instruction& &command&
RUN apt-get install python3 &
CMD echo &Hello world& &
ENTRYPOINT echo &Hello world&&
当指令执行时,shell 格式底层会调用 /bin/sh -c &command&&。
例如下面的 Dockerfile 片段:
ENV name Cloud Man &
ENTRYPOINT echo &Hello, $name&&
执行 docker run &image& 将输出:
Hello, Cloud Man
注意环境变量&name&已经被值&Cloud Man&替换。
下面来看 Exec 格式。
&instruction& [&executable&, &param1&, &param2&, ...]
RUN [&apt-get&, &install&, &python3&] &
CMD [&/bin/echo&, &Hello world&] &
ENTRYPOINT [&/bin/echo&, &Hello world&]
当指令执行时,会直接调用 &command&,不会被 shell 解析。
例如下面的 Dockerfile 片段:
ENV name Cloud Man &
ENTRYPOINT [&/bin/echo&, &Hello, $name&]
运行容器将输出:
Hello, $name
注意环境变量&name&没有被替换。
如果希望使用环境变量,照如下修改
ENV name Cloud Man &
ENTRYPOINT [&/bin/sh&, &-c&, &echo Hello, $name&]
运行容器将输出:
Hello, Cloud Man
CMD 和 ENTRYPOINT 推荐使用 Exec 格式,因为指令可读性更强,更容易理解。RUN 则两种格式都可以。
RUN 指令通常用于安装应用和软件包。
RUN 在当前镜像的顶部执行命令,并通过创建新的镜像层。Dockerfile 中常常包含多个 RUN 指令。
RUN 有两种格式:
Shell 格式:RUN
Exec 格式:RUN [&executable&, &param1&, &param2&]
下面是使用 RUN 安装多个包的例子:
RUN apt-get update && apt-get install -y \ &
&mercurial \
&subversion
注意:apt-get update 和 apt-get install 被放在一个 RUN 指令中执行,这样能够保证每次安装的是最新的包。如果 apt-get install 在单独的 RUN 中执行,则会使用 apt-get update 创建的镜像层,而这一层可能是很久以前缓存的。
CMD 指令允许用户指定容器的默认执行的命令。
此命令会在容器启动且 docker run 没有指定其他命令时运行。
如果 docker run 指定了其他命令,CMD 指定的默认命令将被忽略。
如果 Dockerfile 中有多个 CMD 指令,只有最后一个 CMD 有效。
CMD 有三种格式:
Exec 格式:CMD [&executable&,&param1&,&param2&]
这是 CMD 的推荐格式。
CMD [&param1&,&param2&] 为 ENTRYPOINT 提供额外的参数,此时 ENTRYPOINT 必须使用 Exec 格式。
Shell 格式:CMD command param1 param2
Exec 和 Shell 格式前面已经介绍过了。
第二种格式 CMD [&param1&,&param2&] 要与 Exec 格式 的 ENTRYPOINT 指令配合使用,其用途是为 ENTRYPOINT 设置默认的参数。我们将在后面讨论 ENTRYPOINT 时举例说明。
下面看看 CMD 是如何工作的。Dockerfile 片段如下:
CMD echo &Hello world&
运行容器 docker run -it [image] 将输出:
Hello world
但当后面加上一个命令,比如 docker run -it [image] /bin/bash,CMD 会被忽略掉,命令 bash 将被执行:
root@10a32dc7d3d3:/#
ENTRYPOINT
ENTRYPOINT 指令可让容器以应用程序或者服务的形式运行。
ENTRYPOINT 看上去与 CMD 很像,它们都可以指定要执行的命令及其参数。不同的地方在于 ENTRYPOINT 不会被忽略,一定会被}

我要回帖

更多关于 监控摄像头id地址查找 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信