内容目录

数据持久化介绍

为何需要卷

在启动容器时,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:挂载仅存储在主机系统的内存中,并且不会写入主机系统的文件系统中。(临时存储)

file

数据持久化-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

检查容器挂载结果

  1. 访问宿主机的32770端口,
curl localhost:32770
hello,bind mounting
  1. 修改/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
  1. 准备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;
        }
  }
}
  1. 在宿主机上准备对应站点的静态资源文件
mkdir /html/code1
mkdir /html/code2
echo "hello nginx 80" > /html/code1/index.html
echo "hello nginx 81" > /html/code2/index.html
  1. 运行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
  1. 访问对应端口验证结果
curl  localhost:8880
hello nginx 80
curl  localhost:8881
hello nginx 81

实践案例

需求描述

实现:wordpress应用(wordpress镜像+mysql镜像)

  1. 运行一个mysql镜像,需要持久化数据到本地,使用volumes卷类型;
  2. 运行一个wordpress镜像,需要传递环境变量去连接mysql
  3. wordpress需要暴露80端口对外提供服务;
  4. wordpress是可以上传图片,需要持久化到本地(bindmounting方式);

mysql

  1. 运行mysql
docker run -d --name db \
-v mysql_data:/var/lib/mysql \
-e "MYSQL_ROOT_PASSWORD=123456" \
-e "MYSQL_DATABASE=wordpress" \
mysql
  1. 验证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

  1. 运行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
  1. 通过浏览器访问,并验证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.devhttps://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
最后修改日期: 2024年12月24日

留言

撰写回覆或留言

发布留言必须填写的电子邮件地址不会公开。