# Docker的容器、镜像与数据卷
# Docker概述
# Docker为什么出现
一款产品从应用到上线,需要配置负复杂的运行环境,需要考虑不同版本环境的兼容,而且不能跨平台,移植到另一台服务器又得重新配置一遍。
Docker通过镜像将应用程序所需的系统环境进行打包,解决环境配置的诸多问题。
Docker将应用程序运行在容器上,而容器在任何操作系统上都是一致的,这就实现了跨平台、跨服务器。“一次封装、到处运行”。
在容器技术出现之前,业界网红是虚拟机。虚拟机的代表,是VMWare和OpenStack。虚拟机技术是虚拟出一套硬件之后,在其上运行一个完整的操作系统,在操作系统上再运行所需应用。
Docker和虚拟机的区别?
- 虚拟机占用资源多,冗余步骤多,启动慢
- 容器内的应用直接运行于宿主机内核,容器没有自己的内核,也没有硬件虚拟,因此更为轻便。
- 每个容器相互隔离,每个容器有自己的文件系统,容器间进程不会互相影响,能区分计算资源。
# Docker能干嘛
- 更快的应用交付和部署
- 更便捷的升级和扩容
- 更简单的系统运维
- 更高效的计算资源利用
# Docker安装
# Centos7
这里使用了腾讯云个人用户1个月试用的云服务器
安装:
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加添加阿里云镜像
sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3
sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
# Step 4: 更新并安装Docker-CE
sudo yum makecache fast # 生成缓存
sudo yum -y install docker-ce
# Step 5: 开启Docker服务
sudo service docker start
# Step 6: 检查是否安装成功
docker version
卸载:
sudo yum remove docker-ce
sudo rm -rf /var/lib/docker
说明:
- yum-utils管理yum配置和存储库,device-mapper-persistent-data用于逻辑卷管理,lvm2用于动态调整磁盘分区大小
# 腾讯云镜像加速
容器镜像服务个人版使用说明 (opens new window)
docker login ccr.ccs.tencentyun.com --username=xxxxxxxxxxx
执行以下命令,打开 /etc/docker/daemon.json
配置文件。
vim /etc/docker/daemon.json
按 i 切换至编辑模式,添加以下内容,并保存。
{
"registry-mirrors": [
"https://mirror.ccs.tencentyun.com"
]
}
执行以下命令,重启 Docker 即可。示例命令以 CentOS 7 为例。
sudo systemctl restart docker
查看镜像仓库地址
sudo docker info
# Docker镜像
# 镜像是什么
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
# 镜像加载原理
UnionFS(联合文件系统):
- Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。
- Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
- 特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录.
Docker镜像加载原理:
- docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统
UnionFS
。 bootfs
(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux启动时,bootfs被加载到内存,提供启动内核所需的文件。启动完成后,系统会从bootfs切换到rootfs,并卸载bootfs。rootfs
(root file system) ,在bootfs之上。包含的就是典型 Linux 系统中的 /dev, /proc, /bin, /etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
平时安装虚拟机和centos都是好几个G,为什么Docker这里只需要200M?
- 对于一个精简的OS,rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就好了。对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以共用bootfs。
什么是bootfs和rootfs?
bootfs
Linux 操作系统启动时最早加载的文件系统,通常包含内核映像(Kernel Image)和初始的 RAM 文件系统。rootfs
是 Linux 系统的根文件系统,包含了操作系统运行所需的所有文件。它包括/bin
、/lib
、/etc
、/dev
等目录,以及应用程序和用户数据。
# 分层理解
[root@VM-20-7-centos home]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
e4fff0779e6d: Already exists # 重复的层不会再下载
d1dde3db2ec5: Pull complete
1d321a003dde: Pull complete
d65aedb2f012: Pull complete
4018f93716a2: Pull complete
b0967b02e8cf: Pull complete
4f4fb700ef54: Pull complete
d288b86f5d06: Pull complete
Digest: sha256:878983f8f5045b28384fc300268cec62bca3b14d5e1a448bec21f28cfcc7bf78
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
思考:为什么Docker镜像要采用这种分层的结构呢? 最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机 只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服 务了,而且镜像的每一层都可以被共享。
所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层之 上,创建新的镜像层。
Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统 一的文件系统。
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新 镜像层添加到镜像当中。
存储引擎:
- Linux 上可用的存储引擎有 AUFS、Overlay2、Device Mapper、Btrfs 以及 ZFS。顾名思义,每种存储引擎都基于 Linux 中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
- Windows 上仅支持 windowsfilter 一种存储引擎,该引擎基于 NTFS 文件系统之上实现了分层。
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们的容器层,容器层之下都叫镜像层。
# 镜像commit
docker commit 提交容器成为一个新的本地镜像
# 命令和git原理类似
docker commit -m="描述信息" -a="作者" 容器id 目标镜像名:[TAG]
实战测试:官方的tomcat的webapps目录下是空的,我们可以自己制作一个镜像,让它包含初始内容
# 启动默认的tomcat
docker run -d --name tomcat01 -p 4396:8080 tomcat:9.0
# 进入容器
docker exec -it tomcat01 /bin/bash
# 复制webapps
cp -r webapps.dist/* webapps
# 退出
exit
# 重启tomcat
docker restart tomcat01
# commit 提交镜像
[root@VM-20-7-centos home]# docker commit -m="add webapps" -a="huyadish" 6525ee04867f tomcat02:1.0
sha256:eea1dcc18db2ebe7b6d88abac28e793d8df457deb76fb99473b97053f28d66cb
# 查看tomcat02比tomcat 大了一点
[root@VM-20-7-centos home]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
tomcat02 1.0 eea1dcc18db2 26 seconds ago 468MB
tomcat 9.0 90717097970a 12 days ago 463MB
# 启动新的tomcat
[root@VM-20-7-centos home]# docker run -d -p 4396:8080 tomcat02:1.0
学习方式思考:先简单理解概念,再动手实践,最后实践和理论结合一次性搞定这个知识
到这里才算入门docker
# 容器数据卷
# 什么是容器数据卷
如果数据都在容器中,那么删除容器数据就会丢失!需求:数据可以持久化
MySQL,容器删了同时也删除了数据库!需求:Mysql数据可以存储在本地!
卷技术可以通过目录的挂载,将容器内目录,挂载到Linux上。多个容器之间数据可以共享!
# 使用数据卷
- 使用命令来挂载
# docker run -v 宿主机目录:容器目录
docker run -it -v /home/test:/home centos /bin/bash
# 启动后通过docker inspect 查看容器详细信息
docker inspect e6512dddaf3f
执行之后宿主机的/home/test
目录会和容器的/home
目录,双向同步
即使容器关了,修改宿主机目录内容,重新打开容器,对应目录内容同样会更新
# 实战:安装MySQL
# 获取镜像
[root@VM-20-7-centos /]# docker pull mysql:8.0
# 启动mysql
-d 后台运行
-p 端口映射
-v 目录挂载 宿主机目录:容器目录 可以使用多个 -v
-e 设置环境
[root@VM-20-7-centos /]# docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql8.0 mysql:8.0
# 宿主机产生了对应目录
[root@VM-20-7-centos /]# ls /home/mysql
conf data
# 启动成功后开启3306端口,并通过远程navicat连接
[root@VM-20-7-centos /]# ufw allow 3306
# 查看宿主机目录
[root@VM-20-7-centos /]# cd /home/mysql/data
[root@VM-20-7-centos data]# ls
auto.cnf binlog.index client-cert.pem #ib_16384_1.dblwr ibtmp1 mysql performance_schema server-cert.pem undo_001
binlog.000001 ca-key.pem client-key.pem ib_buffer_pool #innodb_redo mysql.ibd private_key.pem server-key.pem undo_002
binlog.000002 ca.pem #ib_16384_0.dblwr ibdata1 #innodb_temp mysql.sock public_key.pem sys
[root@VM-20-7-centos data]#
# navicat中创建test数据库
# 再次查看 发现多了test数据库
[root@VM-20-7-centos data]# ls
auto.cnf binlog.index client-cert.pem #ib_16384_1.dblwr ibtmp1 mysql performance_schema server-cert.pem test
binlog.000001 ca-key.pem client-key.pem ib_buffer_pool #innodb_redo mysql.ibd private_key.pem server-key.pem undo_001
binlog.000002 ca.pem #ib_16384_0.dblwr ibdata1 #innodb_temp mysql.sock public_key.pem sys undo_002
假设将容器删除,本地的数据不会丢失
# 具名和匿名挂载
# 匿名挂载 -v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 查看所有的卷的情况
[root@VM-20-7-centos data]# docker volume ls
DRIVER VOLUME NAME
local 2da55574a37b01a128232cf449171dc6c24b8467a3229c87a3f629e8e10764ab
local 615a8c9be191c64c440449cb995769c2b26118a523e572dc2e7eecdc7f85dde9
# 具名挂载 -v 卷名:容器内路径
root@VM-20-7-centos home]# docker run -P -d --name nginx02 -v volume01:/etc/nginx nginx
67726065cc8668f6c7f067955bbd14ba78908794538cfff23c452f79b3cc529c
[root@VM-20-7-centos home]#
[root@VM-20-7-centos home]#
# 可以看到我们命名的卷volume01
[root@VM-20-7-centos home]# docker volume ls
DRIVER VOLUME NAME
local 2da55574a37b01a128232cf449171dc6c24b8467a3229c87a3f629e8e10764ab
local 615a8c9be191c64c440449cb995769c2b26118a523e572dc2e7eecdc7f85dde9
local volume01
# 查看卷的位置 docker volume inspect 卷名
[root@VM-20-7-centos home]# docker volume inspect volume01
[
{
"CreatedAt": "2024-08-19T15:12:34+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/volume01/_data",
"Name": "volume01",
"Options": null,
"Scope": "local"
}
]
所有docker容器内的卷,没有指定目录的情况下都是在宿主机的/var/lib/docker/volumes/xxxxx/_data
通过具名挂载可以方便得找到卷,大多数情况使用具名挂载
拓展:
# 通过 -v 容器内路径:ro rw 改变读写权限
# ro readonly 只读
# rw readwrite 读写,默认值
# 设置了ro 容器内部就不能改变卷了,只能通过宿主机操作
docker run -P -d --name nginx02 -v volume01:/etc/nginx:ro nginx
docker run -P -d --name nginx02 -v volume01:/etc/nginx:rw nginx
# 初识DockerFile
DockerFile就是用来构建Docker镜像的脚本文件。
通过脚本可以生成镜像,镜像是分层的,脚本是一个个的命令,每个命令都是一层!
- 编辑dockerfile
# 创建一个dockerfile文件,明明建议dockerfile
# 文件内容 指令(大写) 参数
FROM centos
VOLUME ["volume01","volume02"]
CMD echo "---end---"
CMD /bin/bash
# 这里的每个命令都是镜像的一层
- 使用dockerfile生成镜像
[root@VM-20-7-centos docker-test-volume]# docker build -f dockerfile1 -t huyadish/centos:1.0 .
[+] Building 0.2s (5/5) FINISHED
=> [internal] load build definition from dockerfile1
=> => transferring dockerfile: 117B
=> [internal] load metadata for docker.io/library/centos:latest
=> [internal] load .dockerignore
=> => transferring context: 2B
=> [1/1] FROM docker.io/library/centos:latest
=> exporting to image
=> => exporting layers
=> => writing image sha256:2771b5374df0a3d1fec5d343805c3291ee35f76cf68d2b731a421cb308eae93f
=> => naming to docker.io/huyadish/centos:1.0
# 查看生成的镜像
[root@VM-20-7-centos docker-test-volume]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 5d0da3dc9764 2 years ago 231MB
huyadish/centos 1.0 2771b5374df0 2 years ago 231MB
- 使用自定义镜像运行容器
[root@VM-20-7-centos volumes]# docker run -it --name mycentos 2771b5374df0 /bin/bash
# 可以看到容器已自动生成了数据卷 volume01 volume02
[root@6a9e1fc9d112 /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
# 创建文件
[root@6a9e1fc9d112 /]# cd volume01/
[root@6a9e1fc9d112 volume01]# touch container.txt
[root@6a9e1fc9d112 volume01]# ls
container.txt
# 退出
[root@6a9e1fc9d112 volume01]# exit
exit
# 查看启动的容器
[root@VM-20-7-centos volumes]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6a9e1fc9d112 2771b5374df0 "/bin/bash" 8 minutes ago Exited (0) 6 minutes ago mycentos
# 拖过容器ID查看 详细信息
[root@VM-20-7-centos volumes]# docker inspect 6a9e1fc9d112
# 可以看到volume01 的在宿主机的地址 /var/lib/docker/volumes/b3aeb4aa984abf2b69288b41cd9ef90bc124e22d6af75561f1cb3ddcabdfef2f/_data
# 进入数据卷默认路径
[root@VM-20-7-centos volumes]# cd /var/lib/docker/volumes/
# 进入volume01 路径
[root@VM-20-7-centos volumes]# cd b3aeb4aa984abf2b69288b41cd9ef90bc124e22d6af75561f1cb3ddcabdfef2f/
[root@VM-20-7-centos b3aeb4aa984abf2b69288b41cd9ef90bc124e22d6af75561f1cb3ddcabdfef2f]# cd _data/
# 可以看到创建的测试文件
[root@VM-20-7-centos _data]# ls
container.txt
# 数据卷容器
多个mysql 同步数据
# 启动centos01
[root@VM-20-7-centos _data]# docker run -it --name centos01 huyadish/centos:1.0 /bin/bash
[root@2b8a31b8f8ee /]#
# 启动centos02 通过--volumes-from 将数据卷绑定到centos01
[root@VM-20-7-centos _data]# docker run -it --name centos02 --volumes-from centos01 huyadish/centos:1.0 /bin/bash
# 在外部查看运行状况
[root@VM-20-7-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2d381d6ee79c huyadish/centos:1.0 "/bin/sh -c /bin/bash" 34 seconds ago Up 33 seconds centos02
2b8a31b8f8ee huyadish/centos:1.0 "/bin/bash" About a minute ago Up About a minute centos01
# 进入centos01
[root@VM-20-7-centos ~]# docker attach 2b8a31b8f8ee
[root@2b8a31b8f8ee /]# ls
bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
[root@2b8a31b8f8ee /]# cd volume01
[root@2b8a31b8f8ee volume01]# ls
# 创建测试文件
[root@2b8a31b8f8ee volume01]# touch rong.java
[root@2b8a31b8f8ee volume01]# ls
rong.java
# 进入centos02
[root@2d381d6ee79c /]# cd volume01
[root@2d381d6ee79c volume01]# ls
rong.java
因为centos01和centos02的数据卷对应宿主机的同一个位置
centos01和centos02对宿主机的数据卷是共享拷贝机制,即使删除centos01,centos02的数据依然在
← Docker常用命令 Docker进阶 →