数据持久化介绍
为何需要卷
在启动容器时,docker
会加载对应的只读镜像层,并在顶部添加一个读写层。
在容器运行过程中,所有数据的存储以及文件的修改都发生在这个读写层。
当需要修改一个已存在的文件时,docker
会采用写时复制机制(copy-on-write
,简称cow
),将文件从只读层复制到读写层并进行修改,同时保留只读层中的原始文件。
原始文件其实不会被修改,只是在读写层中的副本会将其原始文件覆盖。
在运行一个对io
性能要求较高的应用,如mysql
数据库时,cow
机制可能会导致性能下降。主要有2个原因:
- 1.写时复制(
cow
)机制:当需要修改一个已存在的文件时,该文件会从只读层复制到读写层。对于频繁修改文件的应用,这样的操作可能会产生较大的i/o
开销。 - 2.层叠文件系统:
docker
镜像是由多个只读层叠加而成的,这意味着在查找文件时,docker
需要在每一层逐层查找。对于需要频繁读取文件的应用,这种方式会增加文件查找的时间。
什么是数据卷
为了解决这个问题,docker
引入了卷(volume
)的概念。
通过使用卷,可以将容器种要存储数据的目录与宿主机的某个目录进行绑定。
而不将数据存储至容器的读写层,因此,卷是一种可以绕过联合文件系统,实现容器数据的持久化方式。
这样,即使容器被删除或重新构建,数据卷中的数据也可以被保留下来。
同时卷可以提高i/o
性能,因为它直接在主机的文件系统上操作,不需要通过容器的存储引擎。
这对于对i/o
性能有要求的应用,如mysql
数据库,是非常有益的。
数据卷类型
docker
有两种类型的卷,每种类型都在容器中存在一个挂载点,但其在宿主机上的位置有所不同:
volumes
:该卷由docker
自行管理的数据卷,通常在/var/lib/docker/volumes
目录下(存储位置之前已改为/data/docker/volumes
)bind mounting
:这种类型的数据卷允许直接将主机系统上的目录路径绑定到容器中。tmpfs
:挂载仅存储在主机系统的内存中,并且不会写入主机系统的文件系统中。(临时存储)
数据持久化-datavolume
- 运行一个
mysql:8.0
的镜像
docker run -d --name mysql1 -v /var/lib/mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:8.0
f639688cd25734cd41dcee582cb633886caa33b9545a8f83fd744bcdfa09808a
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f639688cd257 mysql:8.0 "docker-entrypoint.s…" 5 seconds ago Up 4 seconds 3306/tcp, 33060/tcp mysql1
- 监测
mysql
数据持久化
1.检查该镜像启动的容器将/var/lib/mysql
目录持久化至本地哪个路径
docker volume ls
DRIVER VOLUME NAME
local aba5457442eff265ee7939c401df6cdcd949d9fe81a5aad91172ca2d2bb61d38
2.通过inspect
查看详情,获取mount
信息
docker volume inspect aba5457442eff265ee7939c401df6cdcd949d9fe81a5aad91172ca2d2bb61d38
[
{
"CreatedAt": "2024-09-11T09:24:09+08:00",
"Driver": "local",
"Labels": {
"com.docker.volume.anonymous": ""
},
"Mountpoint": "/data/docker/volumes/aba5457442eff265ee7939c401df6cdcd949d9fe81a5aad91172ca2d2bb61d38/_data",
"Name": "aba5457442eff265ee7939c401df6cdcd949d9fe81a5aad91172ca2d2bb61d38",
"Options": null,
"Scope": "local"
}
]
3.检查挂载点数据
ls /data/docker/volumes/aba5457442eff265ee7939c401df6cdcd949d9fe81a5aad91172ca2d2bb61d38/_data/
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
4.由于前面创建容器没有给volume
定义名称,这样很难确认是哪个容器产生的数据,可以在docker run
时通过-v
添加别名。
-v
别名:容器需持久化目录
docker run -d --name mysql2 -v mysql_data:/var/lib/mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:8.0
dd855bff2380393d0965c4306c8b42bfb869766bf5509e4e85571cb823994906
docker volume ls
DRIVER VOLUME NAME
local aba5457442eff265ee7939c401df6cdcd949d9fe81a5aad91172ca2d2bb61d38
local mysql_data
docker volume inspect mysql_data
[
{
"CreatedAt": "2024-09-11T09:42:04+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/data/docker/volumes/mysql_data/_data",
"Name": "mysql_data",
"Options": null,
"Scope": "local"
}
]
验证mysql数据持久化
验证数据的持久化是否有对应的数据:
-
1.在
mysql2
容器中创建一个db
的数据库,然后强制干掉mysql2
容器 -
2.启动
mysql3
容器,然后使用mysql2
容器持久化下来的数据 -
3.登录
mysql3
容器,检查mysql
数据库的数据,是否与原mysql2
容器够一致 -
登录
mysql
数据库,然后创建一个hello
的库,退出后强制删除掉mysql2
容器
docker exec -it mysql2 /bin/bash
bash-5.1# mysql
mysql> create database hello;
Query OK, 1 row affected (0.02 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| hello |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
- 停止删除
mysql2
这个容器
docker stop mysql2
docker rm mysql2
- 启动
mysql3
容器,然后使用mysql2
容器持久化下来的数据
docker run -d --name mysql3 -v mysql_data:/var/lib/mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:8.0
84d77bdab95ed937b5875f68258634c42f6a8a160302e35d44d028eaa15618e2
- 登录
mysql3
容器,检查mysql
数据库的数据,是否与原mysql2
容器数据一致
docker exec -it mysql3 /bin/bash
bash-5.1# mysql
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| hello |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
数据挂载-bindmounting
bindmount场景说明
在docker
中,可以使用bindmount
的方式将宿主机上的目录或文件挂载到容器中,实现数据共享和持久化存储。
例如,在部署静态网站时,可以将宿主机中存放静态网页文件的/html/
目录绑定到nginx
容器中的/usr/share/nginx/html
目录。
这样即使容器被删除,静态网站的文件也不会丢失,因为这些文件实际上存储在宿主机上,并通过bind mounting
的方式挂载到容器中。
与数据卷不同的是,bind mounting
不需要在容器创建时就定义数据卷,而是在启动容器时动态地将宿主机中的目录或文件绑定到容器中,这种方式更加灵活。
准备站点代码
mkdir /html
echo "hello,bind mounting" > /html/index.html
运行nginx容器
启动一个nginx
容器,
docker run -d -P nginx
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b589bcb118d2 nginx "/docker-entrypoint.…" 9 seconds ago Up 8 seconds 0.0.0.0:32769->80/tcp, :::32769->80/tcp quizzical_wiles
curl localhost:32769
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
停止删除容器,然后再启动,然后将本地的/html
目录挂载至容器的/usr/share/nginx/html
docker stop b589
docker rm b589
docker run -d -P -v /html/:/usr/share/nginx/html nginx
c074ec65f34dd34c4a0c3141ad5d99e4001bcab65149d95a7971c1cf7d2261ae
检查容器挂载结果
- 访问宿主机的32770端口,
curl localhost:32770
hello,bind mounting
- 修改
/html
目录下的文件,检查容器中的nginx
是否返回新的内容
echo "hello docker" >> /html/index.html
curl localhost:32770
hello,bind mounting
hello docker
docker inspect c074
"Mounts": [
{
"Type": "bind",
"Source": "/html",
"Destination": "/usr/share/nginx/html",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
当我们在本地开发项目,通过docker
运行时,仅需要将项目代码挂载至容器中,当项目内容发生改变,容器就可以立马看到变化。
单容器多路径挂载实践
当我们需要使用单个nginx
容器赖提供多个虚拟机服务,其中每个虚拟机主机服务都有自己的网站内容和端口。
具体来说,我们需要实现以下两个虚拟主机站点:
- 虚拟站点1:监听80端口,读取
/var/www/code1/index.html
,返回自定义的网站内容hello nginx 80
- 虚拟站点2:监听81端口,读取
/var/www/code2/index.html
,返回自定义的网站内容hello nginx 81
思路:在宿主机上创建两个网站目录,然后通过宿主机挂载至虚拟机对应的目录,同时需要准备nginx.conf
配置文件:
- 宿主机目录1:
/html/code1/index.html
--> 挂载容器 -->/var/www/code1/index.html
- 宿主机目录2:
/html/code2/index.html
--> 挂载容器 -->/var/www/code2/index.html
- 准备
nginx.conf
配置文件
cat /html/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
server {
listen 80;
server_name _;
location / {
root /var/www/code1;
index index.html;
}
}
server {
listen 81;
server_name _;
location / {
root /var/www/code2;
index index.html;
}
}
}
- 在宿主机上准备对应站点的静态资源文件
mkdir /html/code1
mkdir /html/code2
echo "hello nginx 80" > /html/code1/index.html
echo "hello nginx 81" > /html/code2/index.html
- 运行
nginx
容器,而后挂载相应的数据至容器中;
docker run -d -p 8880:80 -p 8881:81 \
-v /html/nginx.conf:/etc/nginx/nginx.conf \
-v /html/code1:/var/www/code1 \
-v /html/code2:/var/www/code2 \
nginx
- 访问对应端口验证结果
curl localhost:8880
hello nginx 80
curl localhost:8881
hello nginx 81
实践案例
需求描述
实现:wordpress
应用(wordpress
镜像+mysql
镜像)
- 运行一个
mysql
镜像,需要持久化数据到本地,使用volumes
卷类型; - 运行一个
wordpress
镜像,需要传递环境变量去连接mysql
; wordpress
需要暴露80端口对外提供服务;wordpress
是可以上传图片,需要持久化到本地(bindmounting
方式);
mysql
- 运行
mysql
docker run -d --name db \
-v mysql_data:/var/lib/mysql \
-e "MYSQL_ROOT_PASSWORD=123456" \
-e "MYSQL_DATABASE=wordpress" \
mysql
- 验证
mysql
可用性
docker inspect db
"Mounts": [
{
"Type": "volume",
"Name": "mysql_data",
"Source": "/data/docker/volumes/mysql_data/_data",
"Destination": "/var/lib/mysql",
"Driver": "local",
"Mode": "z",
"RW": true,
"Propagation": ""
}
],
docker exec -it db /usr/bin
bash-5.1# mysql -uroot -p123456
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| wordpress |
+--------------------+
5 rows in set (0.04 sec)
wordpress
- 运行wordpress镜像,并持久化
upload
目录
docker pull wordpress
docker run -d --name=blog \
-p 80:80 \
-p 443:443 \
-e "WORDPRESS_DB_HOST=172.17.0.3" \
-e "WORDPRESS_DB_USER=root" \
-e "WORDPRESS_DB_PASSWORD=123456" \
-e "WORDPRESS_DB_NAME=wordpress" \
-v "/data/wordpress_uploads/:/var/www/html/wp-content/uploads/" \
wordpress
- 通过浏览器访问,并验证
wordpress
的持久化
# wordpress uploads目录权限问题
cd /var/www/html/wp-content
ls -l
total 4
drwxrwxrwt. 2 www-data www-data 6 Sep 11 00:01 cache
-rw-r--r--. 1 www-data www-data 28 Jan 8 2012 index.php
drwxr-xr-x. 3 www-data www-data 55 Sep 10 15:23 plugins
drwxr-xr-x. 5 www-data www-data 95 Sep 10 15:23 themes
drwxr-xr-x. 2 root root 6 Sep 11 07:45 uploads
chown -R www-data:www-data uploads/
安装linuxcommand和draw.io
docker run -d --restart=always -p 32768:3000 wcjiang/linux-command
docker run -dit --name=drawio -p 9090:8080 -v drawiojs:/usr/local/tomcat/webapps/draw/js/ --restart=always jgraph/drawio
更新容器
- 镜像加速地址:
https://hub.rat.dev
、https://docker.1panel.live
- 拉取最新镜像:
docker pull docker.1panel.live/wcjiang/linux-command
- 停止删除容器后再启动镜像:
docker run -d --restart=always -p 32768:3000 docker.1panel.live/wcjiang/linux-command
留言