Docker 容器在运行时会产生大量数据,这些数据如何持久化和管理是一个重要的话题。 本节我们将通过一个 Nginx Web 服务器的案例,来深入探讨 Docker 的三种数据管理方式。
Docker 提供了三种主要的数据管理方式:
让我们通过一个 Nginx Web 服务器的例子来理解这三种方式的区别。我们将在每种方式下执行相同的操作:创建一个 HTML 文件,然后测试数据的持久性。
在这个场景中,我们直接在容器内创建文件,看看数据会发生什么:
# 运行一个 nginx 容器
docker run -d --name web-default -p 8000:80 nginx
# 在容器中创建一个测试页面
docker exec -it web-default sh -c \
'echo "<h1>Hello from Default Storage</h1>" > /usr/share/nginx/html/index.html'
# 访问页面验证内容
curl http://localhost:8000
# 删除容器
docker rm -f web-default
# 用同样的配置重新运行容器
docker run -d --name web-default -p 8000:80 nginx
# 再次访问页面,会看到默认的 Nginx 欢迎页面,之前的内容已经丢失
curl http://localhost:8000
在这个场景中,我们使用 Docker 管理的卷来存储数据:
# 创建一个 Docker volume
docker volume create nginx_data
# 运行 Nginx 容器并挂载卷
docker run -d --name web-volume -p 8081:80 -v nginx_data:/usr/share/nginx/html nginx
# 在容器中创建一个测试页面
docker exec -it web-volume sh -c 'echo "<h1>Hello from Volume Storage</h1>" > /usr/share/nginx/html/index.html'
# 访问页面验证内容
curl http://localhost:8081
# 删除容器
docker rm -f web-volume
# 用同样的配置重新运行容器
docker run -d --name web-volume-2 -p 8081:80 \
-v nginx_data:/usr/share/nginx/html nginx
# 再次访问页面,内容仍然存在
curl http://localhost:8081
# 查看卷的详细信息
docker volume inspect nginx_data
在这个场景中,我们将主机上的目录直接挂载到容器中:
# 创建本地目录
mkdir nginx-content
echo "<h1>Hello from Bind Mount Storage</h1>" > nginx-content/index.html
# 运行 Nginx 容器并挂载本地目录
docker run -d --name web-bind \
-p 8082:80 \
-v $(pwd)/nginx-content:/usr/share/nginx/html nginx
# 访问页面验证内容
curl http://localhost:8082
# 在主机上修改文件
echo "<h1>Updated content from host</h1>" > nginx-content/index.html
# 无需重启容器,直接访问更新后的内容
curl http://localhost:8082
# 删除容器
docker rm -f web-bind
# 用同样的配置重新运行容器
docker run -d --name web-bind-2 -p 8082:80 \
-v $(pwd)/nginx-content:/usr/share/nginx/html nginx
# 再次访问页面,内容仍然存在
curl http://localhost:8082
默认存储
Volume
Bind Mount
完成实验后,可以进行清理:
# 清理容器
docker rm -f web-default web-volume web-volume-2 web-bind web-bind-2
# 清理卷
docker volume rm nginx_data
# 清理本地目录
rm -rf nginx-content
我们将通过一个 MySQL 数据库的例子来演示如何使用 Volume 持久化数据。
# 创建一个命名卷
docker volume create mysql_data
# 查看卷信息
docker volume inspect mysql_data
# 列出所有卷
docker volume ls
# 运行 MySQL 容器并挂载卷
docker run -d \
--name mysql_db \
-e MYSQL_ROOT_PASSWORD=mysecret \
-v mysql_data:/var/lib/mysql \
mysql:8.0
# 进入容器创建测试数据
docker exec -it mysql_db mysql -uroot -pmysecret -h127.0.0.1
# 在 MySQL 中创建测试数据
mysql> CREATE DATABASE test_db;
mysql> USE test_db;
mysql> CREATE TABLE users (id INT, name VARCHAR(50));
mysql> INSERT INTO users VALUES (1, 'John Doe');
mysql> exit
# 删除原容器
docker rm -f mysql_db
# 使用同一个卷启动新容器
docker run -d \
--name mysql_db2 \
-e MYSQL_ROOT_PASSWORD=mysecret \
-v mysql_data:/var/lib/mysql \
mysql:8.0
# 验证数据是否存在
docker exec -it mysql_db2 \
mysql -uroot -pmysecret -e "USE test_db; SELECT * FROM users;"
在这个案例中,我们将演示如何使用 Bind Mounts 来运行 Jupyter Notebook。 这个案例很好地展示了 Bind Mounts 在处理 jupiter 数据文件时的优势:
笔记文件存储在代码仓库中,需要使用的时候,可以直接在云原生开发环境中使用 docker run 命令来运行 jupyter 容器, 使用 Bind Mounts 可以直接挂载代码仓库中的笔记文件,这样就可以直接在 jupyter 中使用笔记文件了。
先构建一个 Jupyter Notebook 镜像
cd jupyter docker build -t jupiter-notebook .
# 查看本地的 ipynb 文件是否存在
ls -l notebooks
# 运行 jupiter notebook 容器
docker run \
--name jupiter-notebook \
-p 8888:8888 \
-it -d \
-v $(pwd)/notebooks:/notebooks \
jupiter-notebook
# 查看容器状态
docker ps
docker logs jupiter-notebook
我们使用了 -p 参数来将容器内的 8888 端口映射到宿主机的 8888 端口,在 cnb 上我们可以通过添加一个端口映射来实现外网访问。
