# Docker 核心原理


<!--more-->


## 1. 虚拟化技术

“云计算”这个词，相信大家都非常熟悉。作为信息科技发展的主流趋势，它频繁地出现在我们的眼前。伴随它一起出现的，还有这些概念名词——OpenStack、Hypervisor、KVM、Docker、K8S...

### 1.1 计算机发展史

上个世纪电脑被发明的时候，还没有网络，每个电脑（PC），就是一个单机。这台单机，包括CPU、内存、硬盘、显卡等硬件。用户在单机上，安装操作系统和应用软件，完成自己的工作。当然，还有大家更喜欢的游戏。俄罗斯方块、超级玛丽、魂斗罗、金庸群侠传、仙剑奇侠传

后来，有了**网络（Network）**，单机与单机之间，可以交换信息，协同工作

再后来，单机性能越来越强，就有了**服务器（Server）**。人们发现，可以把一些服务器集中起来，放在机房里，然后让用户通过网络，去访问和使用机房里的计算机资源。再再后来，小型网络变成了大型网络，就有了**互联网（Internet）。**小型机房变成了大型机房，就有了**IDC（Internet Data Center，互联网数据中心）**

### 1.2 云计算

云计算的道理是简单的，说白了，就是把计算机资源集中起来，放在网络上。但是，云计算的实现方式，就非常复杂了. 

举个例子，如果你只是在公司小机房摆了一个服务器，开个FTP下载服务，然后用于几个同事之间的电影分享，当然是很简单的

如果是“双11”的淘宝购物节，全球几十亿用户访问阿里巴巴的淘宝网站，单日几十PB（1PB=1024TB=1024×1024GB）的访问量，每秒几百GB的流量……这个，就不是几根网线几台服务器能解决的了.

这时，需要设计一个**超大容量、超高并发（同时访问）、超快速度、超强安全**的云计算系统，才能满足业务平稳运行的要求。这才是云计算的复杂之处。

```
第一层次
是最底层的硬件资源，主要包括CPU（计算资源），硬盘（存储资源），还有网卡（网络资源）等。

第二层次
要高级一些，我不打算直接使用CPU、硬盘、网卡，我希望你把操作系统（例如Windows、Linux）装
好，把数据库软件装好，我再来使用。

第三层次
更高级一些，你不但要装好操作系统这些基本的，还要把具体的应用软件装好，例如FTP服务端软件、
在线视频服务端软件等，我可以直接使用服务。
```

这三种层次，就是大家经常听到的**IaaS**、**Paas**、**SaaS**。

- **IaaS**: Infrastructure-as-a-Service（基础设施即服务），Infrastructure-as-a-service。国内典型代表是阿里云。国际上的是AWS

- **PaaS**: Platform-as-a-Service（平台即服务），Platform-as-a-service。国内典型代表是新浪云。docker也是属于PaaS

- **SaaS**: Software-as-a-Service（软件即服务），office 365.github的Codespaces

> 目前主流的云计算服务提供商，例如亚马逊AWS、阿里云、华为云、天翼云、腾讯云，说白了，都是为大家提供以上三个层次的云资源。你想要什么，它们就提供什么。你想要多少，它们就提供多少

难道说，是人工安排？——如果你要八核CPU、16GB内存、500GB硬盘的服务器，阿里临时安排工程师帮你组装？如果你要装CentOS 7.8，MySQL 5.7.31，阿里也临时让工程师帮你安装配置？这显然是不可能的，耗不起人力，也等不起时间。**于是，就有了各种软件和平台，负责对资源进行快速调用和集中管理。**

### 1.3 什么是虚拟化

如果要对物理资源进行管理，第一步，就是**虚拟化**。虚拟化是云计算的基础。简单来说，虚拟化就是在一台物理服务器上，运行多台“虚拟服务器”。这种虚拟服务器，也叫**虚拟机（VM, Virtual Machine）**。

从表面来看，这些虚拟机都是独立的服务器，但实际上，它们共享物理服务器的CPU、内存、硬件、网卡等资源。通过模拟计算机的硬件，来实现在同一台计算机上同时运行不同的操作系统的技术。常用的vmwore、openstack、kvm都是使用的虚拟化技术

```
物理机，通常称为“宿主机（Host）”
虚拟机，则称为“客户机（Guest）”。
```

谁来完成物理资源虚拟化的工作呢？就是大名鼎鼎的 **Hypervisor** 。

```
Hypervisor也叫做VMM（Virtual Machine Monitor，虚拟机监视器）。它不是一款具体的软件，而是
一类软件的统称。Hypervisor是一种运行在基础物理服务器硬件之上的软件层，可以虚拟化硬件资源，
例如：cpu，硬盘，内存，声卡等资源。然后我们可以通过在虚拟化出来的资源之上安装操作系统。也
就是所谓的虚拟机。通过Hyperviosr我们可以创建不同的虚拟机，并且每个虚拟机都是分离、独立的系
统。这样操作，我们就可以在一台硬件服务器和本地操作系统之上虚拟化出很多服务器，供我们用来部
署应用程序。一台硬件服务器可以虚拟化多台服务器，让计算机资源得以充分利用。
```

Hypervisor分为两大类：

- 第一类，hypervisor直接运行在物理机之上。虚拟机运行在hypervisor之上。

- 第二类，物理机上安装正常的操作系统（例如Linux或Windows），然后在正常操作系统上安装hypervisor，生成和管理虚拟机。像**VMware**、**KVM**、**Xen**、**Virtual Box**，都属于Hypervisor。

```
人们在使用虚拟化一段时间后，发现它存在一些问题，不同的用户，有时候只是希望运行各自的一些简
单程序，跑一个小进程。为了不相互影响，就要建立虚拟机。如果建虚拟机，显然浪费就会有点大，而
且操作也比较复杂，花费时间也会比较长。而且，有的时候，想要迁移自己的服务程序，就要迁移整个
虚拟机。显然，迁移过程也会很复杂。安装的虚拟机越多，消耗的资源对应越多。
```

环境兼容性问题，开发时的环境运行正常，部署到虚拟机环境进行测试则有可能发生错误。

有没有办法**更灵活快速**一些呢？有，这就引入了**容器（Container）**。

### 1.4 什么是容器

基于硬件级虚拟化技术的缺点和不足，后续又发展出来另一种虚拟化技术，即操作系统级别的虚拟化技术。操作系统级虚拟化是运行在操作系统之上的虚拟化技术，它模拟的是运行在一个操作系统上的多个不同进程，并将其封闭在一个密闭的容器内，该技术也就被称之为容器化技术

容器就是在隔离环境运行的一个进程，如果进程停止，容器就会销毁。隔离的环境拥有自己的系统文件，IP地址，主机名等。容器也是虚拟化，但是属于“轻量级”的虚拟化。它的目的和虚拟机一样，都是为了创造“隔离环境”。但是，它又和虚拟机有很大的不同——虚拟机是操作系统级别的资源隔离，而容器本质上是进程级的资源隔离。

### 1.5 容器和虚拟化的区别

容器是将代码和环境打包在一起的一个集合，而虚拟机是在物理层面上分离出来一个操作系统；

多个容器可以运行在同一台硬件服务器上，并共享一个操作系统的内核资源。

多个虚拟机也可以运行在同一台服务器上，但每个虚拟机都需要有一个完整的操作系统。

### 1.6 虚拟化技术分类

- cpu虚拟化

  虚拟化在计算机方面通常是指计算元件在虚拟的基础上而不是真实的基础上运行。虚拟化技术可以扩大硬件的容量，简化软件的重新配置过程。简单说来，CPU的虚拟化技术就是单CPU模拟多CPU并行，允许一个平台同时运行多个操作系统，并且应用程序都可以在相互独立的空间内运行而互不影响，从而显著提高计算机的工作效率。

- 网络虚拟化

  网络虚拟化是目前业界关于虚拟化细分领域界定最不明确，存在争议较多的一个概念。微软眼中的“网络虚拟化”，是指虚拟专用网络 (VPN)。VPN 对网络连接的概念进行了抽象，允许远程用户访问组织的内部网络，就像物理上连接到该网络一样。网络虚拟化可以帮助保护 IT环境，防止来自 Internet 的威胁，同时使用户能够快速安全的访问应用程序和数据

- 服务器虚拟化

  与网络虚拟化不同，服务器虚拟化却是虚拟化技术最早细分出来的子领域。根据2006年2月Forrester Research的调查，全球范围的企业对服务器虚拟化的认知率达到了75%。三分之一的企业已经在使用或者准备部署服务器虚拟化。这个产生于20世纪60年代的技术日益显示出其重要价值。由于服务器虚拟化发展时间长，应用广泛，所以很多时候人们几乎把服务器虚拟化等同于虚拟化

- 应用虚拟化

  前面几种虚拟化技术，主要还专注于对硬件平台资源的虚拟优化分配，随着IT应用的日益广泛，应用虚拟化作为虚拟化家族的明日之星登上了历史舞台。2006年7月由Forrester咨询公司在美国对各种不同行业的高层IT管理人员所做的一项研究显示，当今的机构现在将应用虚拟化当作是业务上的一个必由之路，而不是一个IT决策。据统计，全世界目前至少有超过18万个机构在利用应用虚拟化技术进行集中IT管理、加强安全性和减少总体成本

## 2. Docker网络

docker安装后会自动创建3种网络:

- bridge

- host

- none

```
docker network ls
```

### 2.1 网络理论

docker使用Linux桥接网卡，在宿主机虚拟一个docker**容器网桥**(docker0)，docker启动一个容器时会根据docker网桥的网段分配给容器一个IP地址，称为Container-IP，同时docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥，这样容器之间就能够通过容器的Container-IP直接通信。

docker网桥是宿主机虚拟出来的，并不是真实存在的网络设备，外部网络是无法寻址到的，这也意味着外部网络无法通过直接Container-IP访问到容器。如果容器希望外部访问能够访问到，可以通过映射容器端口到宿主主机（端口映射），即docker run创建容器时候通过 -p 或 -P 参数来启用，访问容器的时候就通过**[宿主机IP]:[容器端口]**访问容器。

```
使用命令查看docker网络部分 
docker info
```

### 2.2 网络模式

![img_6](https://img.zhaojq.top/20260727091744424.png "img_6")

- bridge模式

  默认的网络模式。bridge模式下容器没有一个公有ip,只有宿主机可以直接访问,外部主机是不可见的,但容器通过宿主机的NAT规则后可以访问外网。

  bridge桥接模式的实现步骤如下:

  - Docker Daemon 利用 veth pair 技术，在宿主机上创建两个虚拟网络接口设备，假设为veth0 和veth1。而veth pair 技术的特性可以保证无论哪一个 veth 接收到网络报文，都会将报文传输给另一方。

  - Docker Daemon 将 veth0 附加到 Docker Daemon 创建的 docker0网桥上。保证宿主机的网络报文可以发往 veth0；

  - Docker Daemon 将 **veth1** 添加到 Docker Container 所属的 namespace 下，并被改名为**eth0**。如此一来，保证宿主机的网络报文若发往 veth0，则立即会被 eth0 接收，实现宿主机到DockerContainer网络的联通性；同时，也保证 Docker Container 单独使用 eth0，**实现容器网络环境的隔离性。**

  缺陷: 

  ```
  1.最明显的是，该模式下 Docker Container 不具有一个公有 IP，即和宿主机的 eth0 不处于同一个网
  段。导致的结果是宿主机以外的世界不能直接和容器进行通信。
  2.虽然 NAT 模式经过中间处理实现了这一点，但是 NAT 模式仍然存在问题与不便，如：容器均需要在
  宿主机上竞争端口，容器内部服务的访问者需要使用服务发现获知服务的外部端口等。
  3.另外 NAT 模式由于是在三层网络上的实现手段，故肯定会影响网络的传输效率。
  ```

  > **注意：**
  >
  > veth设备是成双成对出现的，一端是容器内部命名为eth0，一端是加入到网桥并命名的veth（通常命名为veth），它们组成了一个数据传输通道，一端进一端出，veth设备连接了两个网络设备并实现了数据通信

- host模式

  相当于Vmware中的NAT模式，与宿主机在同一个网络中，但没有独立IP地址。

  如果启动容器的时候使用host模式，那么这个容器将不会获得一个独立的Network Namespace，而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡，配置自己的IP等，而是使用宿主机的IP和端口。但是，容器的其他方面，如文件系统、进程列表等还是和宿主机隔离的。

  使用host模式的容器可以直接使用宿主机的IP地址与外界通信，容器内部的服务端口也可以使用宿主机的端口，不需要进行NAT，**host最大的优势就是网络性能比较好**，但是docker host上已经使用的端口就不能再用了，**网络的隔离性不好**。

  host网络模式需要在容器创建时指定–network=host.

  host 模式是 bridge 桥接模式很好的补充。采用 host 模式的 Docker Container，可以直接使用宿主机的 IP地址与外界进行通信，若宿主机的 eth0 是一个公有 IP，那么容器也拥有这个公有IP。同时容器内服务的端口也可以使用宿主机的端口，无需额外进行 NAT 转换。

  host模式可以让容器共享宿主机网络栈,这样的好处是外部主机与容器直接通信,但是容器的网络缺少隔离性。

  **缺陷:**

  最明显的是 Docker Container 网络环境隔离性的弱化。即容器不再拥有隔离、独立的网络环境。

  另外，使用 host 模式的 Docker Container 虽然可以让容器内部的服务和传统情况无差别、无改造的使用，但是由于网络隔离性的弱化，该容器会与宿主机共享竞争网络栈的使用；

  另外，容器内部将不再拥有所有的端口资源，原因是部分端口资源已经被宿主机本身的服务占用，还有部分端口已经用以 bridge 网络模式容器的端口映

- Container网络模式

  一种特殊的host网络模式

  Container 网络模式是 Docker 中一种较为特别的网络的模式。在容器创建时使用–network=container:vm1指定。(vm1指定的是运行的容器名)

  处于这个模式下的 Docker 容器会共享一个网络环境,这样两个容器之间可以使用localhost高效快速通信

  缺陷：它并没有改善容器与宿主机以外世界通信的情况（和桥接模式一样，不能连接宿主机以外的其他设备）。

  这个模式指定新创建的容器和已经存在的一个容器共享一个 Network Namespace，而不是和宿主机共享。新创建的容器不会创建自己的网卡，配置自己的 IP，而是和一个指定的容器共享 IP、端口范围等。同样，两个容器除了网络方面，其他的如文件系统、进程列表等还是隔离的。两个容器的进程可以通过lo 网卡设备通信。

- none模式

  使用none模式，Docker容器拥有自己的Network Namespace，但是，并不为Docker容器进行任何网络配置。也就是说，这个Docker容器没有网卡、IP、路由等信息。需要我们自己为Docker容器添加网卡、配置IP等。

  这种网络模式下容器只有lo回环网络，没有其他网卡。none模式可以在容器创建时通过--network=none来指定。这种类型的网络没有办法联网，封闭的网络能很好的保证容器的安全性。

- overlay模式

  Overlay 网络，也称为覆盖网络。主要用于docker集群部署

  Overlay 网络的实现方式和方案有多种。Docker自身集成了一种，基于VXLAN隧道技术实现

  Overlay 网络主要用于实现跨主机容器之间的通信。

  应用场景：需要管理成百上千个跨主机的容器集群的网络时

- macvlan网络模式

  macvlan网络模式，最主要的特征就是他们的通信会直接基于mac地址进行转发。

  这时宿主机其实充当一个二层交换机。Docker会维护着一个MAC地址表，当宿主机网络收到一个数据包后，直接根据mac地址找到对应的容器，再把数据交给对应的容器。

  容器之间可以直接通过IP互通，通过宿主机上内建的虚拟网络设备（创建macvlan网络时自动创建），但与主机无法直接利用IP互通。

  **应用场景：**由于每个外来的数据包的目的mac地址就是容器的mac地址，这时每个容器对于外面网络来说就相当于一个真实的物理网络设备。因此当需要让容器来的网络看起来是一个真实的物理机时，使用macvlan模式

  Macvlan是一个新的尝试，是真正的网络虚拟化技术的转折点。Linux实现非常轻量级，因为与传统的Linux Bridge隔离相比，它们只是简单地与一个Linux以太网接口或子接口相关联，以实现网络之间的分离和与物理网络的连接。

  Macvlan提供了许多独特的功能，并有充足的空间进一步创新与各种模式。这些方法的两个高级优点是绕过Linux网桥的正面性能以及移动部件少的简单性。删除传统上驻留在Docker主机NIC和容器接口之间的网桥留下了一个非常简单的设置，包括容器接口，直接连接到Docker主机接口。由于在这些情况下没有端口映射，因此可以轻松访问外部服务

  Macvlan Bridge模式每个容器都有唯一的MAC地址，用于跟踪Docker主机的MAC到端口映射。Macvlan驱动程序网络连接到父Docker主机接口。示例是物理接口，例如eth0，用于802.1q VLAN标记的子接口eth0.10（.10代表VLAN 10）或甚至绑定的主机适配器，将两个以太网接口捆绑为单个逻辑接口。 指定的网关由网络基础设施提供的主机外部。 每个Macvlan Bridge模式的Docker网络彼此隔离，一次只能有一个网络连接到父节点。

  每个主机适配器有一个理论限制，每个主机适配器可以连接一个Docker网络。 同一子网内的任何容器都可以与没有网关的同一网络中的任何其他容器进行通信macvlan bridge。 相同的docker network命令适用于vlan驱动程序。 在Macvlan模式下，在两个网络/子网之间没有外部进程路由的情况下，单独网络上的容器无法互相访问。这也适用于同一码头网络内的多个子网。

### 2.3 基础镜像

```sh
# 拉取镜像
docker pull nginx:1.19.3-alpine

# 备份镜像
docker save nginx:1.19.3-alpine -o nginx.1.19.3.alpine.tar

# 导入镜像
docker load -i nginx.1.19.3.alpine.tar
```

### 2.4 bridge 网络

bridge网络表现形式就是docker0这个网络接口。容器默认都是通过**docker0**这个接口进行通信。也可以通过docker0去和本机的以太网接口连接，这样容器内部才能访问互联网

```sh
# 查看docker0网络，在默认环境中，一个名为docker0的linux bridge自动被创建好了
# 其上有一个 docker0 内部接口，IP地址为172.17.0.1/16
ip a

# 查看docker 网络
docker network ls

# 查看bridge网络详情,主要关注containers节点信息
docker network inspect bridge
```

**docker0详解**

- 运行镜像

  ```sh
  docker run -itd --name nginx1 nginx:1.19.3-alpine 
  
  查看bridge网络详情。主要关注Containers节点信息。
  发现nginx1容器默认使用bridge网络 docker network inspect bridge
  ```

- 容器创建时ip地址的分配

  ```sh
  # 查看docker100主机网络。发现多出一块网卡veth62aef5e@if8 \
  ip a
  ```

- Docker 创建一个容器的时候，会执行如下操作

  ```
   • 创建一对虚拟接口/网卡，也就是veth pair，分别放到本地主机和新容器中；
   • 本地主机一端桥接到默认的 docker0 或指定网桥上，并具有一个唯一的名字，如 vetha596da4；
   • 容器一端放到新容器中，并修改名字作为 eth0，这个网卡/接口只在容器的名字空间可见；
   • 从网桥可用地址段中（也就是与该bridge对应的network）获取一个空闲地址分配给容器的 eth0，并
  配置默认路由到桥接网卡 vetha596da4。
  ```

  完成这些之后，容器就可以使用 eth0 虚拟网卡来连接其他容器和其他网络。如果不指定--network，创建的容器默认都会挂到 docker0 上，使用本地主机上 docker0 接口的 IP 作为所有容器的默认网关。

  ```sh
  docker exec -it nginx1 ip a
  ```

- ![img_7](https://img.zhaojq.top/20260727091744597.png "img_7")

**多容器之间通讯**

```sh
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.5 #你自己配置完后的ip

docker exec -it nginx2 sh 
ping 172.17.0.4

ping www.baidu.com 
```

**容器ip地址会发生变化 :**如重启的时候

**link容器:** **--link=[]:** 添加链接到另一个容器；**不推荐各位小伙伴使用该参数**

使用link的场景：在企业开发环境中，我们有一个mysql的服务的容器mysql_1，还有一个web应用程序web_1，肯定web_1这台容器肯定要连接mysql_1这个数据库。前面网络命名空间的知识告诉我们，两个容器需要能通信，需要知道对方的具体的IP地址。生产环境还比较好，IP地址很少变化，但是在我们内部测试环境，容器部署的IP地址是可能不断变化的，所以，开发人员不能在代码中写死数据库的IP地址。这个时候，我们就可以利用容器之间link来解决这个问题。下面，我们来介绍如何通过容器名称来进行ping，而不是通过IP地址。

```sh
docker rm -f nginx2

docker run -itd --name nginx2 --link nginx1 nginx:1.19.3-alpine

docker exec -it nginx2 sh
ping nginx1
```

```
- 上面link命令，是在nginx2容器启动时link到nginx1容器，因此，在nginx2容器里面可以ping通
nginx1容器名，link的作用相当于添加了DNS解析。这里提醒下，在nginx1容器里去ping nginx2
容器是不通的，因为link关系是单向的，不可逆。
- 实际工作中，docker官网已经不推荐我们使用link参数。
- docker用其他方式替换掉link参数
```

**新建bridge网络:**

```sh
docker network create -d bridge my-bridge

# 上面命令参数-d 是指DRIVER的类型，后面的 my-bridge是network的自定义名称，
# 这个和docker0是类似的。
```

下面开始介绍，如何把容器连接到 my-bridge这个网络。

启动一个nginx的容器nginx3，并通过参数network connect来连接**my-bridge**网络。在启动容器nginx3之前，我们查看目前还没有容器连接到了my-bridge这个网络上。

```sh
brctl show #此命令得安装 bridge-utils: apt install bridge-utils
docker network ls
docker network inspect my-bridge
docker run -itd --name nginx3 --network my-bridge nginx:1.19.3-alpine
brctl show 
docker network inspect my-bridge
```

把一个运行中容器连接到**my-bridge**网络

```sh
docker network connect my-bridge nginx2
docker network inspect my-bridge

docker exec -it nginx2 sh 
ping nginx3

docker exec -it nginx3 
sh ping nginx2
```

### 2.5 none,host网络

**none 网络**

环境准备，先stop和rm掉全部之前开启的容器。并且把前面创建的lagou-bridge网络也删除。当然，更简单的办法是使用快照方式。将主机恢复到docker初始化安装时。

```sh
docker rm -f $(docker ps -aq) 
docker network rm my-bridge 

docker network ls
```

启动一个ngnix的容器nginx1，并且连接到none网络。然后执行docker network inspect none，看看容器信息

```sh
docker run -itd --name nginx1 --network none nginx:1.19.3-alpine 
docker network inspect none
```

注意，容器使用none模式，是没有物理地址和IP地址。我们可以进入到nginx1容器里，执行ip a命令看看。只有一个lo接口，没有其他网络接口，没有IP。也就是说，使用none模式，这个容器是不能被其他容器访问。这种使用场景很少，只有项目安全性很高的功能才能使用到。例如：密码加密算法容器

```sh
docker exec -it nginx1 sh 
ip a
```

**host网络**

启动一个nginx的nginx2容器，连接到host网络。然后docker network inspect host, 看看容器信息

```sh
docker run -itd --name nginx2 --network host nginx:1.19.3-alpine 
docker network inspect host
```

这里来看，也不显示IP地址。那么是不是和none一样，肯定不是，不然也不会设计none和host网络进行区分。下面我们进入nginx2容器，执行ip a看看效果。我们在容器里执行ip a，发现打印内容和在linux本机外执行ip a是一样的。

```sh
docker exec -it nginx2 sh
ip a
```

这说明什么呢？容器使用了host模式，说明容器和外层linux主机共享一套网络接口。VMware公司的虚拟机管理软件，其中网络设置，也有host这个模式，作用也是一样，虚拟机里面使用网络和你自己外层机器是一模一样的。这种容器和本机使用共享一套网络接口，缺点还是很明显的，例如我们知道web服务器一般端口是80，共享了一套网络接口，那么你这台机器上只能启动一个nginx端口为80的服务器了。否则，出现端口被占用的情况。

### 2.6 网络命令汇总

```sh
docker network --help 
网络常用命令汇总 
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
```

- 查看网络

  ```sh
  docker network ls
  # 作用：
  查看已经建立的网络对象 
  
  # 命令格式： 
  docker network ls [OPTIONS]
  
  # 命令参数(OPTIONS)：
  -f, --filter filter 过滤条件(如 'driver=bridge’)
  	--format string 格式化打印结果 
  	--no-trunc 不缩略显示 
  -q, --quiet 只显示网络对象的ID
  
  docker network ls 
  docker network ls --no-trunc 
  docker network ls -f 'driver=host'
  ```

- 创建网络

  ```sh
  docker network create
  # 命令格式： 
  docker network create [OPTIONS] NETWORK
  -d, --driver string 指定网络的驱动(默认 "bridge") 
  	--subnet strings 指定子网网段(如192.168.0.0/16、172.88.0.0/24) 
  	--ip-range strings 执行容器的IP范围，格式同subnet参数 
  	--gateway strings 子网的IPv4 or IPv6网关，如(192.168.0.1)
  	
  # 注意：
  host和none模式网络只能存在一个 
  docker自带的overlay 网络创建依赖于docker swarm(集群负载均衡)服务 
  ```

- 网络删除

  ```sh
  docker network rm
  ```

- 查看网络详细信息

  ```sh
  docker network inspect
  
  docker network inspect [OPTIONS] NETWORK [NETWORK...] 
  或者 
  docker inspect [OPTIONS] NETWORK [NETWORK...]
  
  -f, --format string 根据format输出结果
  ```

- 使用网络

  ```sh
  docker run –-network
  
  docker run/create --network NETWORK
  
  #默认情况下，docker创建或启动容器时，会默认使用名为bridge的网络
  ```

- 网络连接与断开

  ```sh
  docker network connect/disconnect
  
  docker network connect [OPTIONS] NETWORK CONTAINER 
  docker network disconnect [OPTIONS] NETWORK CONTAINER
  
  -f, --force 强制断开连接(用于disconnect)
  ```

## 3. Docker数据卷

- 什么是数据卷

  当我们在使用docker容器的时候，会产生一系列的数据文件，这些数据文件在我们删除docker容器时是会消失的，但是其中产生的部分内容我们是希望能够把它给保存起来另作用途的，Docker将应用与运行环境打包成容器发布，我们希望在运行过程钟产生的部分数据是可以持久化的的，而且容器之间我们希望能够实现数据共享

  通俗地来说，docker容器数据卷可以看成是我们生活中常用的u盘，它存在于一个或多个的容器中，由docker挂载到容器，但不属于联合文件系统，Docker不会在容器删除时删除其挂载的数据卷

  ```
  特点:
  1. 数据卷可以在容器之间共享或重用数据
  2. 数据卷中的更改可以立即生效
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷默认会一直存在，即使容器被删除
  5. 数据卷的生命周期一直持续到没有容器使用它为止
  ```

  容器中的管理数据主要有两种方式：

  **数据卷**：Data Volumes 容器内数据直接映射到本地主机环境

  **数据卷容器**：Data Volume Containers 使用特定容器维护数据卷

- cp命令

  当然还有最原始的copy方式，这个也是管理数据的方式，**但是基本不会用到**。

  ```sh
  语法:
  宿主机文件复制到容器内 
  docker cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH 
  
  容器内文件复制到宿主机 
  docker cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
  
  -L :保持源目标中的链接
  
  例:宿主机的index.html页面覆盖容器内的index.html页面
  docker cp /data/index.html nginx:/usr/share/nginx/html/index.html
  
  例:将容器内的nginx.cnf复制到宿主机中
  docker cp nginx:/etc/nginx/nginx.conf /data
  ```

- 数据卷

  数据卷(Data Volumes)是一个可供一个或多个容器使用的特殊目录，它将主机操作系统目录直接映射进容器。

  ```
  注意事项
  - 挂载数据卷，最好是通过run而非create/start创建启动容器，create/start命令创建启动容器
  后，再挂载数据卷相当麻烦，要修改很多配置文件，但并非不可以。
  - docker官网推荐尽量进行目录挂载，不要进行文件挂载
  ```

  数据卷类型:

  - 宿主机数据卷: 直接在宿主机的文件系统中但是容器可以访问（bind mount） 

  - 命名的数据卷: 磁盘上Docker管理的数据卷，但是这个卷有个名字。

  - 匿名的数据卷: 磁盘上Docker管理的数据卷，因为没有名字想要找到不容易，Docker来管理这些文件

  - > 数据卷其实都在（如果没有网络文件系统等情况下）宿主机文件系统里面的，只是第一种是在宿主机内的特定目录下，而后两种则在docker管理的目录下，这个目录一般是 /var/lib/docker/volumes/

  **推荐**大家使用 **宿主机数据卷**方式持久化数据

  **宿主机数据卷**: 

  **bind mounts**：容器内的数据被存放到宿主机文件系统的任意位置，甚至存放到一些重要的系统目录或文件中。除了docker之外的进程也可以任意对他们进行修改。

  当使用bind mounts时，宿主机的目录或文件被挂载到容器中。容器将按照挂载目录或文件的绝对路径来使用或修改宿主机的数据。宿主机中的目录或文件不需要预先存在，在需要的使用会自动创建。

  使用bind mounts在性能上是非常好的，但这依赖于宿主机有一个目录妥善结构化的文件系统。使用bind mounts的容器可以在通过容器内部的进程对主机文件系统进行修改，包括创建，修改和删除重要的系统文件和目录，这个功能虽然很强大，但显然也会造成安全方面的影响，包括影响到宿主机上Docker以外的进程

  ```
  数据覆盖问题
  - 如果挂载一个空的数据卷到容器中的一个非空目录中，那么这个目录下的文件会被复制到数据卷中
  - 如果挂载一个非空的数据卷到容器中的一个目录中，那么容器中的目录会显示数据卷中的数据。如
  果原来容器中的目录有数据，那么原始数据会被隐藏掉
  ```

  ```sh
  #语法
  docker run -v /宿主机绝对路径目录:/容器内目录 镜像名
  
  #推荐还是先创建好目录后再进行数据挂载
  
  例:
  docker run -itd --name mysql --restart always --privileged=true -p 3306:3306 -e MYSQL_ROOT_PASSWORD=admin -v /data/mysql:/var/lib/mysql mysql:5.7.31 -- character-set-server=utf8 --collation-server=utf8_general_ci
  
  #容器目录权限
  通过 -v 容器内路径： ro rw 改变读写权限
  ro:readonly 只读 
  rw:readwrite 可读可写
  
  docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名 
  docker run -it -v /宿主机绝对路径目录:/容器内目录:rw 镜像名
  例如： 
  docker run -d -P --name nginx05 -v lagouedu1:/etc/nginx:ro nginx
  docker run -d -P --name nginx05 -v lagouedu2:/etc/nginx:rw nginx
  ```

  总结：**开发环境中推荐各位小伙伴为挂载目录授最高权限777；生产环境需要查看官网文档，结合实际生产环境进行授权。**

  **命名数据卷:**

  ```sh
  # 挂载数据卷
  docker run -itd --name nginx -p 80:80 -v lagouedu-nginx:/etc/nginx nginx:1.19.3- alpine
  
  # 查看docker 数据卷
  docker volume ls 
  #查看lagouedu-nginx 宿主机目录
  docker volume inspect lagouedu-nginx
  #进入docker数据卷默认目录
  cd /var/lib/docker/volumes/lagouedu-nginx
  #查看文件
  ls
  #所有的文件docker默认保存在_data目录中 
  cd _data
  #删除容器 
  docker rm $(docker stop $(docker ps -aq))
  
  查看挂载数据是否还存在，通过查看数据，发现删除容器后，宿主机中的数据还存在 
  ls
  ```

  **匿名数据卷:**

  ```sh
  #挂载数据卷
  docker run -itd --name nginx -p 80:80 -v /etc/nginx nginx:1.19.3-alpine
  
  # 查看docker 数据卷
  docker volume ls
  # 查看宿主机目录
  docker volume inspect dbd07daa4e40148b11....
  # 进入docker数据卷默认目录
  cd /var/lib/docker/volumes/dbd07daa4e40148b11....
  # 查看文件
  ls
  #所有的文件docker默认保存在_data目录中 
  cd _data
  #删除容器 
  docker rm $(docker stop $(docker ps -aq))
  
  查看挂载数据是否还存在，通过查看数据，发现删除容器后，宿主机中的数据还存在 
  ls
  ```

  清理数据卷:删除上面创建的容器后会，发现数据卷仍然存在，我们就需要去清理它，不然会占用我们的资源

  ```sh
  docker volume ls 
  清理数据卷
  docker volume prune 
  
  docker volume ls
  ```

- 数据卷容器

  如果用户需要在多个容器之间共享一些持续更新的数据，最简单的方式是使用数据卷容器。数据卷容器也是一个容器，但是它的目的是专门用来提供数据卷供其他容器挂载。

  发现创建好的数据卷容器是处于停止运行的状态，因为使用**--volumes-from** 参数所挂载数据卷的容器自己并不需要保持在运行状态。

  ![img_8](https://img.zhaojq.top/20260727091744803.png "img_8")

  ```sh
  docker run -d --name data-volume -v /data/nginx:/usr/share/nginx/html -v /data/mysql:/var/lib/mysql centos:7.8.2003
  
  docker run -itd --name nginx01 -p 80:80 --volumes-from data-volume nginx:1.19.3- alpine
  echo "lagouedu nginx" > /data/nginx/index.html
  
  docker run -itd --name nginx02 -p 81:80 --volumes-from data-volume nginx:1.19.3- alpine
  
  docker run -itd --name mysql01 --restart always --privileged=true -p 3306:3306 -e MYSQL_ROOT_PASSWORD=admin --volumes-from data-volume mysql:5.7.31 -- character-set-server=utf8 --collation-server=utf8_general_ci
  
  docker run -itd --name mysql02 --restart always --privileged=true -p 3307:3306 -e MYSQL_ROOT_PASSWORD=admin --volumes-from data-volume mysql:5.7.31 -- character-set-server=utf8 --collation-server=utf8_general_ci
  ```

## 4. Docker Compose

> 官网:https://docs.docker.com/compose/compose-file/

### ４.1 概述

在实际生产环境中，一个应用往往由许多服务构成，而 docker 的最佳实践是一个容器只运行一个进程，因此运行多个微服务就要运行多个容器。多个容器协同工作需要一个有效的工具来管理他们，定义这些容器如何相互关联。compose 应运而生。

compose 是用来**定义和运行**一个或多个容器(通常都是多个)运行和应用的工具。使用 compose 可以简化容器镜像的构建以及容器的运行

compose 使用 **YAML** 文件来定义多容器之间的关系。一个 `docker-compose up `就可以把完整的应用跑起来。 本质上， compose 把 YAML 文件解析成 docker 命令的参数，然后调用相应的 docker 命令行接口，从而将应用以容器化的方式管理起来。它通过解析容器间的依赖关系顺序地启动容器。而容器间的依赖关系由 YAML 文件中的 links 标记指定。

### 4.2 docker compose是什么

**compose**、**machine** 和 **swarm** 是docker 原生提供的三大编排工具。简称docker三剑客。

Docker Compose能够在 Docker 节点上，以单引擎模式（Single-Engine Mode）进行多容器应用的部署和管理。多数的现代应用通过多个更小的微服务互相协同来组成一个完整可用的应用。比如一个简单的示例应用可能由如下 4 个微服务组成。

```
Web前端。
订单管理。
品类管理。
后台数据库。
```

将以上服务组织在一起，就是一个可用的应用。

部署和管理繁多的服务是困难的。而这正是 Docker Compose 要解决的问题。Docker Compose 并不是通过脚本和各种冗长的 docker 命令来将应用组件组织起来，而是**通过一个声明式的配置文件描述整个应用**，从而使用一条命令完成部署。应用部署成功后，还可以通过一系列简单的命令实现对其完整声明周期的管理。甚至，配置文件还可以置于版本控制系统中进行存储和管理。

### 4.3 docker compose 背景

Docker Compose 的前身是 Fig。Fig 是一个由 Orchard 公司开发的强有力的工具，在当时是进行多容器管理的最佳方案。

Fig 是一个基于 Docker 的 Python工具，允许用户基于一个 YAML 文件定义多容器应用，从而可以使用fig 命令行工具进行应用的部署。

Fig 还可以对应用的全生命周期进行管理。内部实现上，Fig 会解析 YAML 文件，并通过 Docker API 进行应用的部署和管理。

在 2014 年，Docker 公司收购了 Orchard 公司，并将 Fig 更名为 Docker Compose。

命令行工具也从 fig 更名为 docker-compose，并自此成为绑定在 Docker 引擎之上的外部工具。

虽然它从未完全集成到 Docker 引擎中，但是仍然受到广泛关注并得到普遍使用。

### 4.4 docker compose 安装 

- 下载

  ```sh
  wget https://github.com/docker/compose/releases/download/v2.6.1/docker-compose-linux-x86_64
  
  # ubuntu下可以使用
  apt install docker-compose
  ```

- 授权

  ```sh
  cp docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
  chmod +x /usr/local/bin/docker-compose
  ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
  docker-compose --version
  
  开发环境可以授予最高权限
  chmod 777 /usr/local/bin/docker-compose
  
  #卸载
  rm -rf /usr/local/bin/docker-compose
  reboot
  ```

### 4.5 yml配置文件及常用指令

Docker Compose 使用 YAML 文件来定义多服务的应用。YAML 是 JSON 的一个子集，因此也可以使用JSON

Docker Compose 默认使用文件名 docker-compose.yml。当然，也可以使用 -f 参数指定具体文件

Docker Compose 的 YAML 文件包含 4 个一级 key：version、services、networks、volumes

- version

  必须指定,位于第一行,定义了文件格式(主要是api的版本). 并非定义docker compose或docker引擎的版本号

- services

  定义不同的应用服务. 

- networks

  用于指引docker创建新的网络.默认下, compose会创建bridge网络,可以使用driver属性来指定不同的网络类型

- volumes

  用于指引docker来创建新的卷

例:

```yaml
version: '3'
services:
  lagou-mysql:
    build:
	  context: ./mysql
	environment:
	  MYSQL_ROOT_PASSWORD: admin
	restart: always 
	container_name: lagou-mysql
	volumes: - /data/edu-bom/mysql/lagou:/var/lib/mysql
	image: my/mysql:5.7
	ports: 
	  - 3306:3306
	networks: 
	  lagou-net:
  lagou-eureka:
    build:
      context: ./edu-eureka-boot
    restart: always
    ports:
      - 8761:8761
    container_name: edu-eureka-boot
    hostname: edu-eureka-boot
    image: lagou/edu-eureka-boot:1.0
    depends_on:
      - lagou-mysql
    networks: 
	  lagou-net:
networks:
  lagou-net:
volumes: 
  lagou-vol:
```

配置文件说明:

```yaml
Compose和Docker兼容性：
    Compose 文件格式有3个版本,分别为1, 2.x 和 3.x
    目前主流的为 3.x 其支持 docker 1.13.0 及其以上的版本

常用参数：
    version           # 指定 compose 文件的版本
    services          # 定义所有的 service 信息, services 下面的第一级别的 key 既是一个 service 的名称

        build                 # 指定包含构建上下文的路径, 或作为一个对象，该对象具有 context 和指定的 dockerfile 文件以及 args 参数值
            context               # context: 指定 Dockerfile 文件所在的路径
            dockerfile            # dockerfile: 指定 context 指定的目录下面的 Dockerfile 的名称(默认为 Dockerfile)
            args                  # args: Dockerfile 在 build 过程中需要的参数 (等同于 docker container build --build-arg 的作用)
            cache_from            # v3.2中新增的参数, 指定缓存的镜像列表 (等同于 docker container build --cache_from 的作用)
            labels                # v3.3中新增的参数, 设置镜像的元数据 (等同于 docker container build --labels 的作用)
            shm_size              # v3.5中新增的参数, 设置容器 /dev/shm 分区的大小 (等同于 docker container build --shm-size 的作用)

        command               # 覆盖容器启动后默认执行的命令, 支持 shell 格式和 [] 格式

        configs               # 不知道怎么用

        cgroup_parent         # 不知道怎么用

        container_name        # 指定容器的名称 (等同于 docker run --name 的作用)

        credential_spec       # 不知道怎么用

        deploy                # v3 版本以上, 指定与部署和运行服务相关的配置, deploy 部分是 docker stack 使用的, docker stack 依赖 docker swarm
            endpoint_mode         # v3.3 版本中新增的功能, 指定服务暴露的方式
                vip                   # Docker 为该服务分配了一个虚拟 IP(VIP), 作为客户端的访问服务的地址
                dnsrr                 # DNS轮询, Docker 为该服务设置 DNS 条目, 使得服务名称的 DNS 查询返回一个 IP 地址列表, 客户端直接访问其中的一个地址
            labels                # 指定服务的标签，这些标签仅在服务上设置
            mode                  # 指定 deploy 的模式
                global                # 每个集群节点都只有一个容器
                replicated            # 用户可以指定集群中容器的数量(默认)
            placement             # 不知道怎么用
            replicas              # deploy 的 mode 为 replicated 时, 指定容器副本的数量
            resources             # 资源限制
                limits                # 设置容器的资源限制
                    cpus: "0.5"           # 设置该容器最多只能使用 50% 的 CPU
                    memory: 50M           # 设置该容器最多只能使用 50M 的内存空间
                reservations          # 设置为容器预留的系统资源(随时可用)
                    cpus: "0.2"           # 为该容器保留 20% 的 CPU
                    memory: 20M           # 为该容器保留 20M 的内存空间
            restart_policy        # 定义容器重启策略, 用于代替 restart 参数
                condition             # 定义容器重启策略(接受三个参数)
                    none                  # 不尝试重启
                    on-failure            # 只有当容器内部应用程序出现问题才会重启
                    any                   # 无论如何都会尝试重启(默认)
                delay                 # 尝试重启的间隔时间(默认为 0s)
                max_attempts          # 尝试重启次数(默认一直尝试重启)
                window                # 检查重启是否成功之前的等待时间(即如果容器启动了, 隔多少秒之后去检测容器是否正常, 默认 0s)
            update_config         # 用于配置滚动更新配置
                parallelism           # 一次性更新的容器数量
                delay                 # 更新一组容器之间的间隔时间
                failure_action        # 定义更新失败的策略
                    continue              # 继续更新
                    rollback              # 回滚更新
                    pause                 # 暂停更新(默认)
                monitor               # 每次更新后的持续时间以监视更新是否失败(单位: ns|us|ms|s|m|h) (默认为0)
                max_failure_ratio     # 回滚期间容忍的失败率(默认值为0)
                order                 # v3.4 版本中新增的参数, 回滚期间的操作顺序
                    stop-first            #旧任务在启动新任务之前停止(默认)
                    start-first           #首先启动新任务, 并且正在运行的任务暂时重叠
            rollback_config       # v3.7 版本中新增的参数, 用于定义在 update_config 更新失败的回滚策略
                parallelism           # 一次回滚的容器数, 如果设置为0, 则所有容器同时回滚
                delay                 # 每个组回滚之间的时间间隔(默认为0)
                failure_action        # 定义回滚失败的策略
                    continue              # 继续回滚
                    pause                 # 暂停回滚
                monitor               # 每次回滚任务后的持续时间以监视失败(单位: ns|us|ms|s|m|h) (默认为0)
                max_failure_ratio     # 回滚期间容忍的失败率(默认值0)
                order                 # 回滚期间的操作顺序
                    stop-first            # 旧任务在启动新任务之前停止(默认)
                    start-first           # 首先启动新任务, 并且正在运行的任务暂时重叠

            注意：
                支持 docker-compose up 和 docker-compose run 但不支持 docker stack deploy 的子选项
                security_opt  container_name  devices  tmpfs  stop_signal  links    cgroup_parent
                network_mode  external_links  restart  build  userns_mode  sysctls

        devices               # 指定设备映射列表 (等同于 docker run --device 的作用)

        depends_on            # 定义容器启动顺序 (此选项解决了容器之间的依赖关系， 此选项在 v3 版本中 使用 swarm 部署时将忽略该选项)
            示例：
                docker-compose up 以依赖顺序启动服务，下面例子中 redis 和 db 服务在 web 启动前启动
                默认情况下使用 docker-compose up web 这样的方式启动 web 服务时，也会启动 redis 和 db 两个服务，因为在配置文件中定义了依赖关系

                version: '3'
                services:
                    web:
                        build: .
                        depends_on:
                            - db
                            - redis
                    redis:
                        image: redis
                    db:
                        image: postgres

        dns                   # 设置 DNS 地址(等同于 docker run --dns 的作用)

        dns_search            # 设置 DNS 搜索域(等同于 docker run --dns-search 的作用)

        tmpfs                 # v2 版本以上, 挂载目录到容器中, 作为容器的临时文件系统(等同于 docker run --tmpfs 的作用, 在使用 swarm 部署时将忽略该选项)

        entrypoint            # 覆盖容器的默认 entrypoint 指令 (等同于 docker run --entrypoint 的作用)

        env_file              # 从指定文件中读取变量设置为容器中的环境变量, 可以是单个值或者一个文件列表, 如果多个文件中的变量重名则后面的变量覆盖前面的变量, environment 的值覆盖 env_file 的值
            文件格式：
                RACK_ENV=development

        environment           # 设置环境变量， environment 的值可以覆盖 env_file 的值 (等同于 docker run --env 的作用)

        expose                # 暴露端口, 但是不能和宿主机建立映射关系, 类似于 Dockerfile 的 EXPOSE 指令

        external_links        # 连接不在 docker-compose.yml 中定义的容器或者不在 compose 管理的容器(docker run 启动的容器, 在 v3 版本中使用 swarm 部署时将忽略该选项)

        extra_hosts           # 添加 host 记录到容器中的 /etc/hosts 中 (等同于 docker run --add-host 的作用)

        healthcheck           # v2.1 以上版本, 定义容器健康状态检查, 类似于 Dockerfile 的 HEALTHCHECK 指令
            test                  # 检查容器检查状态的命令, 该选项必须是一个字符串或者列表, 第一项必须是 NONE, CMD 或 CMD-SHELL, 如果其是一个字符串则相当于 CMD-SHELL 加该字符串
                NONE                  # 禁用容器的健康状态检测
                CMD                   # test: ["CMD", "curl", "-f", "http://localhost"]
                CMD-SHELL             # test: ["CMD-SHELL", "curl -f http://localhost || exit 1"] 或者　test: curl -f https://localhost || exit 1
            interval: 1m30s       # 每次检查之间的间隔时间
            timeout: 10s          # 运行命令的超时时间
            retries: 3            # 重试次数
            start_period: 40s     # v3.4 以上新增的选项, 定义容器启动时间间隔
            disable: true         # true 或 false, 表示是否禁用健康状态检测和　test: NONE 相同

        image                 # 指定 docker 镜像, 可以是远程仓库镜像、本地镜像

        init                  # v3.7 中新增的参数, true 或 false 表示是否在容器中运行一个 init, 它接收信号并传递给进程

        isolation             # 隔离容器技术, 在 Linux 中仅支持 default 值

        labels                # 使用 Docker 标签将元数据添加到容器, 与 Dockerfile 中的 LABELS 类似

        links                 # 链接到其它服务中的容器, 该选项是 docker 历史遗留的选项, 目前已被用户自定义网络名称空间取代, 最终有可能被废弃 (在使用 swarm 部署时将忽略该选项)

        logging               # 设置容器日志服务
            driver                # 指定日志记录驱动程序, 默认 json-file (等同于 docker run --log-driver 的作用)
            options               # 指定日志的相关参数 (等同于 docker run --log-opt 的作用)
                max-size              # 设置单个日志文件的大小, 当到达这个值后会进行日志滚动操作
                max-file              # 日志文件保留的数量

        network_mode          # 指定网络模式 (等同于 docker run --net 的作用, 在使用 swarm 部署时将忽略该选项)

        networks              # 将容器加入指定网络 (等同于 docker network connect 的作用), networks 可以位于 compose 文件顶级键和 services 键的二级键
            aliases               # 同一网络上的容器可以使用服务名称或别名连接到其中一个服务的容器
            ipv4_address      # IP V4 格式
            ipv6_address      # IP V6 格式

            示例:
                version: '3.7'
                services:
                    test:
                        image: nginx:1.14-alpine
                        container_name: mynginx
                        command: ifconfig
                        networks:
                            app_net:                                # 调用下面 networks 定义的 app_net 网络
                            ipv4_address: 172.16.238.10
                networks:
                    app_net:
                        driver: bridge
                        ipam:
                            driver: default
                            config:
                                - subnet: 172.16.238.0/24

        pid: 'host'           # 共享宿主机的 进程空间(PID)

        ports                 # 建立宿主机和容器之间的端口映射关系, ports 支持两种语法格式
            SHORT 语法格式示例:
                - "3000"                            # 暴露容器的 3000 端口, 宿主机的端口由 docker 随机映射一个没有被占用的端口
                - "3000-3005"                       # 暴露容器的 3000 到 3005 端口, 宿主机的端口由 docker 随机映射没有被占用的端口
                - "8000:8000"                       # 容器的 8000 端口和宿主机的 8000 端口建立映射关系
                - "9090-9091:8080-8081"
                - "127.0.0.1:8001:8001"             # 指定映射宿主机的指定地址的
                - "127.0.0.1:5000-5010:5000-5010"
                - "6060:6060/udp"                   # 指定协议

            LONG 语法格式示例:(v3.2 新增的语法格式)
                ports:
                    - target: 80                    # 容器端口
                      published: 8080               # 宿主机端口
                      protocol: tcp                 # 协议类型
                      mode: host                    # host 在每个节点上发布主机端口,  ingress 对于群模式端口进行负载均衡

        secrets               # 不知道怎么用

        security_opt          # 为每个容器覆盖默认的标签 (在使用 swarm 部署时将忽略该选项)

        stop_grace_period     # 指定在发送了 SIGTERM 信号之后, 容器等待多少秒之后退出(默认 10s)

        stop_signal           # 指定停止容器发送的信号 (默认为 SIGTERM 相当于 kill PID; SIGKILL 相当于 kill -9 PID; 在使用 swarm 部署时将忽略该选项)

        sysctls               # 设置容器中的内核参数 (在使用 swarm 部署时将忽略该选项)

        ulimits               # 设置容器的 limit

        userns_mode           # 如果Docker守护程序配置了用户名称空间, 则禁用此服务的用户名称空间 (在使用 swarm 部署时将忽略该选项)

        volumes               # 定义容器和宿主机的卷映射关系, 其和 networks 一样可以位于 services 键的二级键和 compose 顶级键, 如果需要跨服务间使用则在顶级键定义, 在 services 中引用
            SHORT 语法格式示例:
                volumes:
                    - /var/lib/mysql                # 映射容器内的 /var/lib/mysql 到宿主机的一个随机目录中
                    - /opt/data:/var/lib/mysql      # 映射容器内的 /var/lib/mysql 到宿主机的 /opt/data
                    - ./cache:/tmp/cache            # 映射容器内的 /var/lib/mysql 到宿主机 compose 文件所在的位置
                    - ~/configs:/etc/configs/:ro    # 映射容器宿主机的目录到容器中去, 权限只读
                    - datavolume:/var/lib/mysql     # datavolume 为 volumes 顶级键定义的目录, 在此处直接调用

            LONG 语法格式示例:(v3.2 新增的语法格式)
                version: "3.2"
                services:
                    web:
                        image: nginx:alpine
                        ports:
                            - "80:80"
                        volumes:
                            - type: volume                  # mount 的类型, 必须是 bind、volume 或 tmpfs
                                source: mydata              # 宿主机目录
                                target: /data               # 容器目录
                                volume:                     # 配置额外的选项, 其 key 必须和 type 的值相同
                                    nocopy: true                # volume 额外的选项, 在创建卷时禁用从容器复制数据
                            - type: bind                    # volume 模式只指定容器路径即可, 宿主机路径随机生成; bind 需要指定容器和数据机的映射路径
                                source: ./static
                                target: /opt/app/static
                                read_only: true             # 设置文件系统为只读文件系统
                volumes:
                    mydata:                                 # 定义在 volume, 可在所有服务中调用

        restart               # 定义容器重启策略(在使用 swarm 部署时将忽略该选项, 在 swarm 使用 restart_policy 代替 restart)
            no                    # 禁止自动重启容器(默认)
            always                # 无论如何容器都会重启
            on-failure            # 当出现 on-failure 报错时, 容器重新启动

        其他选项：
            domainname, hostname, ipc, mac_address, privileged, read_only, shm_size, stdin_open, tty, user, working_dir
            上面这些选项都只接受单个值和 docker run 的对应参数类似对于值为时间的可接受的值：
            2.5s
            10s
            1m30s
            2h32m
            5h34m56s
            时间单位: us, ms, s, m， h
        对于值为大小的可接受的值：
            2b
            1024kb
            2048k
            300m
            1gb
            单位: b, k, m, g 或者 kb, mb, gb
    networks          # 定义 networks 信息
        driver                # 指定网络模式, 大多数情况下, 它 bridge 于单个主机和 overlay Swarm 上
            bridge                # Docker 默认使用 bridge 连接单个主机上的网络
            overlay               # overlay 驱动程序创建一个跨多个节点命名的网络
            host                  # 共享主机网络名称空间(等同于 docker run --net=host)
            none                  # 等同于 docker run --net=none
        driver_opts           # v3.2以上版本, 传递给驱动程序的参数, 这些参数取决于驱动程序
        attachable            # driver 为 overlay 时使用, 如果设置为 true 则除了服务之外，独立容器也可以附加到该网络; 如果独立容器连接到该网络，则它可以与其他 Docker 守护进程连接到的该网络的服务和独立容器进行通信
        ipam                  # 自定义 IPAM 配置. 这是一个具有多个属性的对象, 每个属性都是可选的
            driver                # IPAM 驱动程序, bridge 或者 default
            config                # 配置项
                subnet                # CIDR格式的子网，表示该网络的网段
        external              # 外部网络, 如果设置为 true 则 docker-compose up 不会尝试创建它, 如果它不存在则引发错误
        name                  # v3.5 以上版本, 为此网络设置名称
文件格式示例：
    version: "3"
    services:
      redis:
        image: redis:alpine
        ports:
          - "6379"
        networks:
          - frontend
        deploy:
          replicas: 2
          update_config:
            parallelism: 2
            delay: 10s
          restart_policy:
            condition: on-failure
      db:
        image: postgres:9.4
        volumes:
          - db-data:/var/lib/postgresql/data
        networks:
          - backend
        deploy:
          placement:
            constraints: [node.role == manager]
```

### 4.6 反向代理案例

准备: 安装compose 

```sh
#基础镜像
docker pull nginx:1.19.3-alpine 
docker pull tomcat:9.0.20-jre8-alpine

#试运行镜像
docker run -itd --name nginx -p 80:80 nginx:1.19.3-alpine
docker run -itd --name tomcat -p 8080:8080 tomcat:9.0.20-jre8-alpine

mkdir -p /data/tomcat1 tomcat2
docker cp nginx:/etc/nginx/ /data 
docker cp tomcat:/usr/local/tomcat/webapps /data/tomcat1/webapps 
docker cp tomcat:/usr/local/tomcat/webapps /data/tomcat2/webapps

echo "tomcat1" > /data/tomcat1/webapps/ROOT/index.jsp 
echo "tomcat2" > /data/tomcat2/webapps/ROOT/index.jsp

docker rm -f nginx tomcat
```

**nginx.conf**

```
nginx.conf增加内容 
include vhost/*.conf;
```

**反向代理配置**

注意: ip地址为你机器的ip

```sh
mkdir -p /data/nginx/vhost 
cd vhost 
vim my.com.conf
```

```nginx
upstream nginxformy{ 
    server 192.168.198.100:8081; 
    server 192.168.198.100:8082; 
}

server{
    listen 80; 
    server_name 192.168.198.100;
    autoindex on; 
    index index.html index.htm index.jsp; 
    location / { 
        proxy_pass http://nginxformy; 
        add_header Access-Control-Allow-Origin *; 
    }
}
```

**docker-compose:**

```yaml
version: '3'
services: 
	my-nginx:
		restart: always
		container_name: my-nginx
		volumes:
			- /data/nginx:/etc/nginx
		image: nginx:1.19.3-alpine
		ports: 
			- 80:80
	my-tomcat1:
		restart: always
		container_name: my-tomcat1
		volumes: 
			- /data/tomcat1/webapps:/usr/local/tomcat/webapps
		image: tomcat:9.0.20-jre8-alpine
		ports: 
			- 8081:8080
		depends_on: 
			- my-nginx
	my-tomcat2:
		restart: always
		container_name: my-tomcat2
		volumes: 
			- /data/tomcat2/webapps:/usr/local/tomcat/webapps
		image: tomcat:9.0.20-jre8-alpine
		ports: 
			- 8082:8080
		depends_on: 
			- my-nginx
```

**启动服务**

```sh
docker-compose up
docker-compose up -d
```

**浏览器测试**

```http
http://192.168.198.100:8081/ 
http://192.168.198.100:8082/ 
http://192.168.198.100
```

### 4.7 常用命令汇总

- 启动服务

  `docker-compose up -d`

- 停止服务

  `docker-compose down`

- 列出所有运行容器

  `docker-compose ps`

- 查看服务日志

  `docker-compose logs`

- 构建或重新构建服务

  `docker-compose build`

- 启动服务

  `docker-compose start`

- 停止已运行的服务

  `docker-compose stop`

- 重启服务

  `docker-compose restart`

> tips: 官网地址 https://docs.docker.com/compose/reference/build/

## 5. 安装Docker私服

在使用maven管理jar包依赖的时候，为了避免每次都从中央仓库拉取依赖包，使用了nexus做了代理仓库。docker镜像仓库与nexus私服仓库作用类似，用于将打包好的镜像保存在仓库中方便开发、测试、生产环境镜像拉取存储，减轻环境部署需要的相应操作。

- 官方私服

  用的少， 简单介绍...

  `https://hub.docker.com/_/registry `

  ```sh
  #拉取镜像
  docker pull registry:2.7.1
  
  #运行容器
  docker run -itd -p 5000:5000 --name registry --restart=always registry:2.7.1
  
  编辑配置文件 
  vi /etc/docker/daemon.json 
  增加仓库配置信息 
  { "insecure-registries":["192.168.198.101:5000"] } 
  
  重启docker 
  systemctl daemon-reload 
  systemctl restart docker 
  
  查看docker信息确认仓库是否添加 
  docker info
  ```

- 企业私服

  ```
  harbor官网地址：
  https://goharbor.io/ 
  
  github官网地址： 
  https://github.com/goharbor/harbor 
  
  官方帮助文档： 
  https://github.com/goharbor/harbor/blob/v1.9.4/docs/installation_guide.md
  ```

  **安装 harbor**

  > 开发环境基本采用http方式安装, 生产环境须采用https安装
  >
  > 硬件建议: 4核8G, 有条件的可以尝试安装学习下

  ```sh
  # 解压
  tar zxf harbor-offline-installer-v1.9.4.tgz
  
  # 进入安装目录
  cd harbor
  
  # 修改配置文件
  vim harbor.yml
  hostname: ip地址
  port: 5000
  harbor_admin_password: 123456
  data_volume: /data/harbor
  
  # 安装 harbor
  #执行启动脚本
  ./install.sh
  #准备安装环境:检查docker版本和docker-compose版本
  #加载harbor需要的镜像
  #准备编译环境
  #启动harbor,通过docker-compose方式启动服务
  #浏览器访问
  ```

  ```sh
  #docker登录私服 
  
  docker login -u admin -p 123456 ip:5000
  ```

## 6. DockerFile

创建镜像三种方式: 基于已有镜像, 基于dockerfile来创建, 基于本地模板导入

### 6.1 基于已有的镜像创建

- commit命令

  ```sh
  docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
  
  常用参数
  -a :提交的镜像作者；
  -c :使用Dockerfile指令来创建镜像；
  -m :提交时的说明文字；
  -p :在commit时，将容器暂停。
  ```

  例: 结合docker cp命令自定义nginx的index页面

  ```sh
  docker run -itd --name nginx -p 80:80 192.168.198.101:5000/myrespo/nginx:v1
  cd /data 
  echo "my index" > /data/index.html
  docker cp /data/index.html nginx:/usr/share/nginx/html/index.html
  curl localhost
  
  docker container commit -m "update index.html file" -a "my name" nginx 192.168.198.101:5000/myrespo/nginx:v2
  docker images
  docker rm -f nginx
  docker run -itd --name nginx -p 80:80 192.168.198.101:5000/myrespo/nginx:v2
  curl localhost
  docker push 192.168.198.101:5000/myrespo/nginx:v2
  
  # 192.168.198.101:5000/myrespo/ 为私服目录
  ```

  Dockerfile其实就是我们用来构建Docker镜像的源码，当然这不是所谓的编程源码，而是一些命令的集合，只要理解它的逻辑和语法格式，就可以很容易的编写Dockerfile。简单点说，Dockerfile可以让用个性化定制Docker镜像。因为工作环境中的需求各式各样，网络上的镜像很难满足实际的需求。

### 6.2 Dockerfile

- 基本结构

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

  docker build命令用于从Dockerfile构建映像。可以在docker build命令中使用 -f 标志指向文件系统中任何位置的Dockerfile

  Dockerfile由一行行命令语句组成，并且支持以#开头的注释行

  Dockerfile分为四部分：**基础镜像信息**、**维护者信息**、 **镜像操作指令**和**容器启动时执行指令**

- 文件说明

  Docker以从上到下的顺序运行Dockerfile的指令。为了指定基本映像，第一条指令必须是***FROM***。一个声明以 ＃ 字符开头则被视为注释。可以在Docker文件中使用 **RUN** ， **CMD** ， **FROM** ， **EXPOSE** ， **ENV** 等指令

- 常见命令

  | 命令       | 说明                                                         |
  | ---------- | ------------------------------------------------------------ |
  | FROM       | 指定基础镜像,必须为第一个命令                                |
  | MAINTAINER | 维护者(作者)信息                                             |
  | ENV        | 设置环境变量                                                 |
  | RUN        | 构建镜像时执行的命令                                         |
  | CMD        | 构建容器后调用,也就是在容器启动时才进行调用                  |
  | ENTRYPOINT | 指定运行容器启动过程执行命令,覆盖CMD参数. ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT, 而docker run命令中指定的任何参数,都会被当做参数再次传递给ENTRYPOINT. Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的调协, 而只执行最后的ENTRYPOINT指令 |
  | ADD        | 将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget |
  | COPY       | 功能类似ADD,但是不会自动解压文件,也不能访问网络资源          |
  | WORKDIR    | 工作目录,类似于CD命令                                        |
  | ARG        | 用于指定传递给构建运行时的变量                               |
  | VOLUMN     | 用于指定持久化目录                                           |
  | EXPOSE     | 指定于外界交互的端口                                         |
  | USER       | 指定运行容器时的用户名或UID,后续的RUN也会使用指定用户. 使用USER指定用户时,可以使用用户名,UID或GID, 或是两者的组合. 当服务不需要管理员权限时,可以通过该命令指定运行用户.并且可以在之前创建所需要的用户 |

### 6.3 build命令

用于使用 Dockerfile 创建镜像

```sh
docker build [OPTIONS] PATH | URL|
```

- 常用参数

  ```sh
  --build-arg=[] :设置镜像创建时的变量；
  -f :指定要使用的Dockerfile路径；
  --rm :设置镜像成功后删除中间容器；
  --tag, -t: 镜像的名字及标签，通常 name:tag 或者 name 格式；可以在一次构建中为一个镜像设置
  多个标签。
  ```

- 制作镜像

  ```dockerfile
  #例: 修改mysql 镜像时区
  FROM mysql:5.7.31 
  # 作者信息 
  MAINTAINER mysql from date UTC by Asia/Shanghai "name@lagou.com" 
  ENV TZ Asia/Shanghai
  ```

  ```sh
  例: docker build --rm -t my/mysql:5.7 .
  
  docker images
  ```

- 运行镜像

  ```sh
  docker run -itd --name mysql --restart always -p 3306:3306 -e MYSQL_ROOT_PASSWORD=admin my/mysql:5.7
  ```

## 7. 部署微服务项目

示例代码: https://gitee.com/ixinglan/docker-demo.git

使用Dockerfile打包镜像前,先拉取基本镜像:

```
docker pull openjdk:8-alpine3.9
```

Dockerfile:

```dockerfile
FROM openjdk:8-alpine3.9 
# 作者信息 
MAINTAINER zhaojq Docker springboot "xxx@163.com" 
# 修改源
RUN echo "http://mirrors.aliyun.com/alpine/latest-stable/main/" >
/etc/apk/repositories && \
	echo "http://mirrors.aliyun.com/alpine/latest-stable/community/" >>
/etc/apk/repositories

# 安装需要的软件，解决时区问题 
RUN apk --update add curl bash tzdata && \ 
	rm -rf /var/cache/apk/*
#修改镜像为东八区时间 
ENV TZ Asia/Shanghai 
ARG JAR_FILE 
COPY ${JAR_FILE} app.jar 
EXPOSE 8082 
ENTRYPOINT ["java","-jar","/app.jar"]
```

成生测试镜像:

```sh
docker build --rm -t zhaojq/dockerdemo:v1 --build-arg JAR_FILE=dockerdemo.jar
```

测试,删除镜像:

```sh
docker run -itd --name dockerdemo -p 8080:8080 zhaojq/dockerdemo:v1

docker logs -f dockerdemo
http://192.168.198.100:8082 
docker stop dockerdemo 
docker rm dockerdemo
```

## 8. IDEA集成docker

**修改docker.service服务信息,允许其他主机远程访问服务器的docker** 

```sh
vim /usr/lib/systemd/system/docker.service
在 ExecStart 行最后增加，开放远程主机访问权限。
-H tcp://0.0.0.0:2375

最后增加修改内容如下:
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock -H tcp://0.0.0.0:2375

重启docker
systemctl daemon-reload
systemctl restart docker

查看docker进程，发现docker守护进程在已经监听2375的tcp端口
ps -ef|grep docker

查看系统的网络端口，检查tcp的2375端口,docker的守护进程是否监听 
netstat -tulp
```

**配置idea:**

```
settings->build execution...->docker->点击"+"按钮，新增[你的docker服务器名称,如docker-001,随便起]服务器docker配置信息 配置内容如下：
name:docker-001
TCP Socket: Engine API URL:tcp://192.168.198.100:2375 
配置成功后，会在下方显示connection successful
```

操作docker

```
配置成功后，会在idea开发工具下方窗口"8.services"里显示信息，右键点击connect。连接成功可以查 看到container和images等信息。可以对container和images进行各种相关操作。
```

此时可以通过idea开发工具进行远程部署

