当前位置: 首页 > article >正文

Docker02 - 深入理解Docker

深入理解Docker

文章目录

  • 深入理解Docker
    • 一:Docker镜像原理
      • 1:镜像加载原理
        • 1.1:unionFS
        • 1.2:加载原理
      • 2:分层理解
    • 二:容器数据卷详解
      • 1:什么是容器数据卷
      • 2:使用数据卷
      • 3:具名挂载和匿名挂载
      • 4:挂载本地目录或文件
      • 5:mysql本地挂载演示
      • 6:dockerfile操作卷实例
        • 6.1:准备工作
        • 6.2:启动容器
        • 6.3:测试挂载
    • 三:Docker网络
      • 1:docker0(了解,不推荐)
        • 1.1:容器网络访问的处理
        • 1.2:veth-pair技术
        • 1.3:桥接模式
        • 1.4:结构图
      • 2:网络互通

一:Docker镜像原理

🎉 镜像的不同版本相互隔离

在这里插入图片描述

1:镜像加载原理

1.1:unionFS

联合文件系统unionFS(union file system)

联合文件系统是一种分层的,轻量级的,高性能的文件系统

支持对文件系统的修改作为一次提交来一层层的进行叠加

可以将不同的目录挂载到同一个文件系统中进行

联合文件系统是docker镜像的基础

可以通过分层进行继承,基于基础的镜像,可以制作各种具体的应用镜像

🎉 一次性的加载多个文件系统,这样可以保证最终的文件系统拥有所有的底层的文件和目录
在这里插入图片描述

1.2:加载原理

docker的镜像实际上是由一层一层的文件系统构成的,这种层级的文件系统就是上面说的unionFS

在这里插入图片描述

2:分层理解

所有的docker镜像都起始于一个初始的镜像层,当进行修改或者增加新的内容的时候,就会在当前的镜像层之上创建新的镜像层

举一个简单的例子:假如基于Ubuntu Linux 16.04创建一个新的镜像,这就是新镜像的第一层,如果在该镜像中添加Python包,就会在基础镜像层上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含3个镜像层,如下图所示:

在这里插入图片描述

假设现在第三层新加入一个文件7,和第二层的文件5相比是其更新版本的

在这里插入图片描述
此时从外部看来还是只有6个文件(6层),最后会将文件打包成为下面的样子:

在这里插入图片描述
我们最后下载的内容就是这个

🎉 docker的镜像都是只读的,当容器进行启动的时候,一个新的可写层会被加入到镜像的顶部

🎉 这一层就是常说的容器层,下面就是镜像层

二:容器数据卷详解

docker精髓:为了数据的持久化和同步操作,容器之间可以共享数据

容器是隔离环境,容器内程序的文件、配置、运行时产生的容器都在容器内部,我们要读写容器内的文件非常不方便。思考几个问题:

  • 如果要升级MySQL版本,需要销毁旧容器,那么数据岂不是跟着被销毁了?
  • MySQL、Nginx容器运行后,如果我要修改其中的某些配置该怎么办?
  • 我想要让Nginx代理我的静态资源怎么办?

1:什么是容器数据卷

容器数据卷是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁

docker的核心理念就是将应用和环境打包成为一个镜像

但是现在有一个问题,就是容器的数据的问题,不能在容器删除的时候,数据丢失

要保证数据的持久化,所以急需一个容器之间的数据共享的技术,因此docker卷技术诞生了

以Nginx为例,我们知道Nginx中有两个关键的目录:

  • html:放置一些静态资源
  • conf:放置配置文件

如果我们要让Nginx代理我们的静态资源,最好是放到html目录;如果我们要修改Nginx的配置,最好是找到conf下的nginx.conf文件。

但遗憾的是,容器运行的Nginx所有的文件都在容器内部。

所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作

在这里插入图片描述

  • 我们创建了两个数据卷:confhtml
  • Nginx容器内部的conf目录和html目录分别与两个数据卷关联。
  • 而数据卷conf和html分别指向了宿主机的/var/lib/docker/volumes/conf/_data目录和/var/lib/docker/volumes/html/_data目录

这样以来,容器内的confhtml目录就 与宿主机的confhtml目录关联起来,我们称为挂载

此时,我们操作宿主机的/var/lib/docker/volumes/html/_data就是在操作容器内的/usr/share/nginx/html/_data目录。

只要我们将静态资源放入宿主机对应目录,就可以被Nginx代理了

/var/lib/docker/volumes

这个目录就是默认的存放所有容器数据卷的目录,其下再根据数据卷名称创建新目录,格式为/数据卷名/_data

为什么不让容器目录直接指向宿主机目录呢

  • 因为直接指向宿主机目录就与宿主机强耦合了,如果切换了环境,宿主机目录就可能发生改变了。
  • 由于容器一旦创建,目录挂载就无法修改,这样容器就无法正常工作了。
  • 但是容器指向数据卷,一个逻辑名称,而数据卷再指向宿主机目录,就不存在强耦合。
  • 如果宿主机目录发生改变,只要改变数据卷与宿主机目录之间的映射关系即可
  • 不过,我们通过由于数据卷目录比较深,不好寻找,通常我们也允许让容器直接与宿主机目录挂载而不使用数据卷

容器数据卷的特点

  1. 数据卷可以在容器之间共享或者重用数据
  2. 卷中的更改可以直接实时生效
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷的声明周期会一直持续到没有容器使用为止

2:使用数据卷

Docker挂载主机目录访问如果出现cannot open directory .: Permission denied

解决办法:在挂载目录后多加一个--privileged=true参数即可

数据卷的相关命令有:

命令说明文档地址
docker volume create创建数据卷docker volume create
docker volume ls查看所有数据卷docs.docker.com
docker volume rm删除指定数据卷docs.docker.com
docker volume inspect查看某个数据卷的详情docs.docker.com
docker volume prune清除数据卷docker volume prune

⚠️ 容器与数据卷的挂载要在创建容器时配置

对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建

# 1.首先创建容器并指定数据卷,注意通过 -v 参数来指定数据卷
docker run 
	-d # 后台运行
	--name nginx # 容器的名称叫做nginx
	-p 80:80 # 端口映射,宿主机的80端口映射容器内的80端口
	-v html:/usr/share/nginx/html # 指定数据卷目录为html, 映射的是容器内的/usr/share/nginx/html
	nginx # 使用的镜像的名称是nginx

# 2.然后查看数据卷
docker volume ls
# 结果
DRIVER    VOLUME NAME
local     29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f
local     html

# 3.查看数据卷详情
docker volume inspect html
# 结果
[
    {
        "CreatedAt": "2024-05-17T19:57:08+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/html/_data", # 注意这里的挂载点信息
        "Name": "html",
        "Options": null,
        "Scope": "local"
    }
]

# 4.查看/var/lib/docker/volumes/html/_data目录
ll /var/lib/docker/volumes/html/_data
# 可以看到与nginx的html目录内容一样,结果如下:
总用量 8
-rw-r--r--. 1 root root 497 1228 2021 50x.html
-rw-r--r--. 1 root root 615 1228 2021 index.html

# 5.进入该目录,并随意修改index.html内容
cd /var/lib/docker/volumes/html/_data
vi index.html

# 6.打开页面,查看效果
# 7.进入容器内部,查看/usr/share/nginx/html目录内的文件是否变化
docker exec -it nginx bash

读写规则映射添加说明

默认是rw(read + write),假设可以设置成为只读ro(read only)

docker run 
	-it 
	--privileged=true 
	-v [/宿主机绝对路径目录]:[/容器内目录]:rw 
	[镜像名]

3:具名挂载和匿名挂载

所谓匿名挂载就是名称的挂载

# 只指定了容器的地址位置[/etc/nginx],没有声明宿主机路径或者名称[宿主机数据卷:容器路径]
docker run 
	-d 
	-P 
	--name nginx01 
	-v /etc/nginx # 匿名挂载,这个容器声明了一个本地目录,需要挂载数据卷,但是数据卷未定义。这就是匿名卷
	nginx
# 可以通过卷列表的命令找到这个挂载信息
docker volume ls

所谓具名挂载就是声明卷名的挂载方式

# 具名挂载:声明卷名[juming-nginx]
docker run 
	-d 
	-P 
	--name nginx02 
	-v juming-nginx:/etc/nginx  # 具名挂载
	nginx
# 可以通过卷列表的命令找到这个挂载信息
docker volume ls
# 可以通过inspect查看卷名的具体的地址
docker volume inspect juming-nginx 

🎉 所有容器数据卷,没有指定目录的情况下,都在:/var/lib/docker/volums/xxx/_data

🎉 我们可以通过具名挂载方便的找到一个卷,大多数情况下都用具名挂载

扩展:可以通过指定的标识改变读写权限

ro -> read only -> 只读
rw -> read write -> 可读可写

# 一旦设置了这个容器权限,容器对我们挂载出来的内容就会有限定了
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx

4:挂载本地目录或文件

可以发现,数据卷的目录结构较深,如果我们去操作数据卷目录会不太方便。在很多情况下,我们会直接将容器目录与宿主机指定目录挂载。

挂载语法与数据卷类似:

# 挂载本地目录
-v 本地目录:容器内目录
# 挂载本地文件
-v 本地文件:容器内文件

🎉 本地目录或文件必须以 /./开头,如果直接以名字开头,会被识别为数据卷名而非本地目录名

-v mysql:/var/lib/mysql # 会被识别为一个数据卷叫mysql,运行时会自动创建这个数据卷

-v ./mysql:/var/lib/mysql # 会被识别为当前目录下的mysql目录,运行时如果不存在会创建目录

5:mysql本地挂载演示

删除并重新创建mysql容器,并完成本地目录挂载:

  • 挂载/root/mysql/data到容器内的/var/lib/mysql目录
  • 挂载/root/mysql/init到容器内的/docker-entrypoint-initdb.d目录(初始化的SQL脚本目录)
  • 挂载/root/mysql/conf到容器内的/etc/mysql/conf.d目录(这个是MySQL配置文件目录)

提前准备好mysql的init目录和conf目录:

在这里插入图片描述
以及对应的初始化SQL脚本和配置文件:

在这里插入图片描述
其中,hm.cnf主要是配置了MySQL的默认编码,改为utf8mb4;而hmall.sql则是初始化SQL脚本。

我们直接将整个mysql目录上传至虚拟机的/root目录下:

在这里插入图片描述

# 1.删除原来的MySQL容器
docker rm -f mysql

# 2.进入root目录
cd ~

# 3.创建并运行新mysql容器,挂载本地目录
docker run -d \
  --name mysql \
  -p 3306:3306 \
  -e TZ=Asia/Shanghai \
  -e MYSQL_ROOT_PASSWORD=123 \
  -v ./mysql/data:/var/lib/mysql \ # 注意这三行挂载本地目录的指令
  -v ./mysql/conf:/etc/mysql/conf.d \
  -v ./mysql/init:/docker-entrypoint-initdb.d \
  mysql

# 4.查看root目录,可以发现~/mysql/data目录已经自动创建好了
ls -l mysql
# 结果:
总用量 4
drwxr-xr-x. 2 root    root   20 519 15:11 conf
drwxr-xr-x. 7 polkitd root 4096 519 15:11 data
drwxr-xr-x. 2 root    root   23 519 15:11 init

# 查看data目录,会发现里面有大量数据库数据,说明数据库完成了初始化
ls -l data

# 5.查看MySQL容器内数据
# 5.1.进入MySQL
docker exec -it mysql mysql -uroot -p123
# 5.2.查看编码表
show variables like "%char%";
# 5.3.结果,发现编码是utf8mb4没有问题
+--------------------------+--------------------------------+
| Variable_name            | Value                          |
+--------------------------+--------------------------------+
| character_set_client     | utf8mb4                        |
| character_set_connection | utf8mb4                        |
| character_set_database   | utf8mb4                        |
| character_set_filesystem | binary                         |
| character_set_results    | utf8mb4                        |
| character_set_server     | utf8mb4                        |
| character_set_system     | utf8mb3                        |
| character_sets_dir       | /usr/share/mysql-8.0/charsets/ |
+--------------------------+--------------------------------+

# 6.查看数据
# 6.1.查看数据库
show databases;
# 结果,hmall是黑马商城数据库
+--------------------+
| Database           |
+--------------------+
| hmall              |
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.00 sec)
# 6.2.切换到hmall数据库
use hmall;
# 6.3.查看表
show tables;
# 结果:
+-----------------+
| Tables_in_hmall |
+-----------------+
| address         |
| cart            |
| item            |
| order           |
| order_detail    |
| order_logistics |
| pay_order       |
| user            |
+-----------------+
# 6.4.查看address表数据
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
| id | user_id | province | city   | town     | mobile      | street        | contact   | is_default | notes |
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
| 59 |       1 | 北京     | 北京   | 朝阳区    | 13900112222 | 金燕龙办公楼   | 李佳诚    | 0          | NULL  |
| 60 |       1 | 北京     | 北京   | 朝阳区    | 13700221122 | 修正大厦       | 李佳红    | 0          | NULL  |
| 61 |       1 | 上海     | 上海   | 浦东新区  | 13301212233 | 航头镇航头路   | 李佳星    | 1          | NULL  |
| 63 |       1 | 广东     | 佛山   | 永春      | 13301212233 | 永春武馆       | 李晓龙    | 0          | NULL  |
+----+---------+----------+--------+----------+-------------+---------------+-----------+------------+-------+
4 rows in set (0.00 sec)

6:dockerfile操作卷实例

6.1:准备工作

创建一个目录,将卷挂载到指定的这个目录下:

# 创建目录
mkdir docker-test-volume

# 进入到这个目录中
cd docker-test-volume

# 在这个目录中创建一个dockerfile文件
vim dockerfile1

# 进行构建
docker build 
    -f /home/docker-test-volume/dockerfile1 
    -t cui/centos:v1.0
# ----------- dockerfile1的脚本内容如下 ------------
FROM centos  # 基础镜像centos

MAINTAINER cui963010213@qq.com  # 添加作者等信息

VOLUME ["volume01", "volume02"] # 添加卷

CMD echo "--------end---------" # 辅助输出指令

CMD /bin/bash # 进入bash

在这里插入图片描述

6.2:启动容器
# 将容器从后台进行启动
docker run -it [container-ID] /bin/bash

ls -l

在这里插入图片描述

  • volume01 & volume02就是创建镜像的时候自动挂载的目录
  • 而这两个卷volume01 & volume02在外部一定有对应的目录和它们是统一的
  • 因为在执行脚本中只是指定了对应的容器内的路径,所以这种挂在方式是匿名挂载
6.3:测试挂载

1️⃣ 在容器内的挂载目录下进行创建文件

cd volume01

touch container.txt

在这里插入图片描述

2️⃣ 打开另一个新的终端,查看当前的容器的信息

docker ps

docker inspect [container-id]

在这里插入图片描述
在这里插入图片描述

3️⃣ 进入到对应的宿主机目录就可以发现已经实现了同步

在这里插入图片描述

三:Docker网络

1:docker0(了解,不推荐)

测试前清空docker镜像,以便于观察

⚠️ docker0的域名不能访问

ip addr

在这里插入图片描述

1.1:容器网络访问的处理

docker是如何处理容器网络的访问的?

首先拉取tomcat镜像并进行启动容器

docker run -P --name tomcat01 tomcat

启动之后,查看网络

docker exec -it tomcat01 ip addr

在这里插入图片描述
可以发现容器启动的时候会有一个eth0@if262的ip地址,这个ip地址就是docker分配给容器的

这个地址可以在外部直接进行ping通(linux可以ping通容器的内部)

ping 172.18.0.2

🎉 我们每启动一个docker容器,docker就会为这个容器分配一个ip,只要安装了docker,就会得到一个网卡docker0

通过上面的截图可以发现,docker0的ip是172.18.0.1(相当于家里的路由器的网址ip)

而上面启动的tomcat容器的ip是172.18.0.2

两者在同一个局域网下,由计算机网络的知识可以知道,docker0tomcat可以互相ping通ip

这个网卡的工作原理是桥接模式,将docker0桥接到真正的网络上,使用的技术是veth-pair技术

1.2:veth-pair技术

就是 一对的虚拟设备接口,他们都是成对出现的, 一段连着协议,一段彼此相连

正因为有这个特性,evth-pair 充当一个桥梁 连接各种虚拟网络设备的

所以docker容器与容器之间是可以互相ping通的

1.3:桥接模式

桥接模式是一种结构型设计模式,可将一个大类或一系列紧密相关的类拆分为抽象和实现两个独立的层次结构,从而能在开发时分别使用

  • 桥接模式实现了抽象化与实现化的脱耦。他们两个互相独立,不会影响到对方。
  • 对于两个独立变化的维度,使用桥接模式再适合不过了。
  • 对于"具体的抽象类"所做的改变,是不会影响到客户。

在这里插入图片描述
桥接模式即将抽象部分与它的实现部分分离开来,使他们都可以独立变化

桥接模式将继承关系转化成关联关系,它降低了类与类之间的耦合度,减少了系统中类的数量,也减少了代码量。

将抽象部分与他的实现部分分离这句话不是很好理解,其实这并不是将抽象类与他的派生类分离,而是抽象类和它的派生类用来实现自己的对象。这样还是不能理解的话。我们就先来认清什么是抽象化,什么是实现化,什么是脱耦。

抽象化:其概念是将复杂物体的一个或几个特性抽出去而只注意其他特性的行动或过程。在面向对象就是将对象共同的性质抽取出去而形成类的过程。

实现化:针对抽象化给出的具体实现。它和抽象化是一个互逆的过程,实现化是对抽象化事物的进一步具体化。

脱耦:脱耦就是将抽象化和实现化之间的耦合解脱开,或者说是将它们之间的强关联改换成弱关联,将两个角色之间的继承关系改为关联关系。

🎉 桥接模式中的所谓脱耦,就是指在一个软件系统的抽象化和实现化之间使用关联关系(组合或者聚合关系)而不是继承关系,从而使两者可以相对独立地变化,这就是桥接模式的用意。

桥接模式的优点

  • 1、分离抽象接口及其实现部分。提高了比继承更好的解决方案。
  • 2、桥接模式提高了系统的可扩充性,在两个变化维度中任意扩展一个维度,都不需要修改原有系统。
  • 3、实现细节对客户透明,可以对用户隐藏实现细节。

桥接模式的缺点

  • 1、桥接模式的引入会增加系统的理解与设计难度
  • 2、桥接模式要求正确识别出系统中两个独立变化的维度,因此其使用范围具有一定的局限性。
1.4:结构图

在这里插入图片描述
Docker使用的是Liunx桥接,宿主机中是一个docker容器的网桥,docker0

在这里插入图片描述

2:网络互通

我们创建了一个Java项目的容器,而Java项目往往需要访问其它各种中间件,例如MySQL、Redis等。那么这些容器之间能否互相访问呢?

在这里插入图片描述
首先,我们查看下MySQL容器的详细信息,重点关注其中的网络IP地址:

# 1.用基本命令,寻找Networks.bridge.IPAddress属性
docker inspect mysql
# 也可以使用format过滤结果
docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
# 得到IP地址如下:
172.17.0.2

# 2.然后通过命令进入dd容器
docker exec -it dd bash

# 3.在容器内,通过ping命令测试网络
ping 172.17.0.2
# 结果
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.058 ms

发现可以互联,没有问题。

但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定

如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败。

所以,我们必须借助于docker的网络功能来解决这个问题,官方文档:https://docs.docker.com/engine/reference/commandline/network/

下面是 docker network 最常见的命令

命令说明
docker network create创建一个网络
docker network ls查看所有网络
docker network rm删除指定网络
docker network prune清除未使用的网络
docker network connect使指定容器连接加入某网络
docker network disconnect使指定容器连接离开某网络
docker network inspect查看网络详细信息

下面是一个自定义网络的实例

# 1.首先通过命令创建一个网络
docker network create hmall

# 2.然后查看网络
docker network ls
# 结果:
NETWORK ID     NAME      DRIVER    SCOPE
639bc44d0a87   bridge    bridge    local
403f16ec62a2   hmall     bridge    local
0dc0f72a0fbb   host      host      local
cd8d3e8df47b   none      null      local
# 其中,除了hmall以外,其它都是默认的网络

# 3.让dd和mysql都加入该网络,注意,在加入网络时可以通过--alias给容器起别名
# 这样该网络内的其它容器可以用别名互相访问!
# 3.1.mysql容器,指定别名为db,另外每一个容器都有一个别名是容器名
docker network connect hmall mysql --alias db
# 3.2.db容器,也就是我们的java项目
docker network connect hmall dd

# 4.进入dd容器,尝试利用别名访问db
# 4.1.进入容器
docker exec -it dd bash
# 4.2.用db别名访问
ping db
# 结果
PING db (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.070 ms
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=2 ttl=64 time=0.056 ms
# 4.3.用容器名访问
ping mysql
# 结果:
PING mysql (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from mysql.hmall (172.18.0.2): icmp_seq=2 ttl=64 time=0.054 ms

http://www.kler.cn/a/566378.html

相关文章:

  • linux有哪些常用命令?
  • CSS—引入方式、选择器、复合选择器、文字控制属性、CSS特性
  • DeepSeek在PiscTrace上完成个性化处理需求案例——光流法将烟雾动态可视化
  • 用大白话解释日志处理Log4j 是什么 有什么用 怎么用
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_buf_t
  • PyQT6是干啥的?
  • 实战指南:安防管理平台搭建的完整步骤解析(一)
  • 【前端】react+ts 轮播图的实现
  • 腾讯混元文生图大模型(Hunyuan-DiT)与Stable Diffusion(SD)对比分析
  • Python--内置模块和开发规范(下)
  • 闲聊 | 跟智能运维说88
  • 【JAVA】阿里云百炼平台对接DeepSeek-V3大模型使用详解
  • (十 三)趣学设计模式 之 模版方法模式!
  • Claude 3.7 Sonnet深度解析:混合推理模型如何重塑AI编程能力
  • docker 占用系统空间太大了,整体迁移到挂载的其他磁盘|【当前普通用户使用docker时,无法指定镜像、容器安装位置【无法指定】】
  • 二、QT和驱动模块实现智能家居----1、使用ADB
  • NO.21十六届蓝桥杯备战|一维数组|范围for|memset|memcpy(C++)
  • Milvus高性能向量数据库与大模型结合
  • 【微知】git 如何修改某个tag名字?如何根据某个commit创建一个tag?
  • DeepSeep开源周,第三天:DeepGEMM是啥?