
18.1 Data Volume
Docker持久化數據方案
- 基於本地文件系統的Volume
可以在執行docker create或者docker run的時候,通過-v參數將主機的目錄作為容器的數據卷。這部分功能便是基於本地文件系統的Volume管理。
- 基於plugin的Volume
支持第三方的存儲方案,比如NAS、AWS等。
Data Volume 類型
- 受管理的data volume,由docker后台自動創建
- 綁定掛載的volume,具體掛載位置可以由用戶指定
通過例子來查看
以MySQL為例,可以查看官方的Dockerfile,當中用到了Volume。
# 查看volume
[root@docker ~]# docker volume ls
[root@docker ~]#
# 創建一個MySQL的容器
[root@docker ~]# docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
# 查看MySQL容器跑起來之后的volume
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local e3c54bb7b620d86524b6cebc4c28568a2a81a104871a5baf41e22c02bd8d7bac
[root@docker ~]#
# 查看這個volume的想象信息
[root@docker ~]# docker volume inspect e3c54bb7b620d86524b6cebc4c28568a2a81a104871a5baf41e22c02bd8d7bac
[
{
"CreatedAt": "2018-06-07T09:13:24Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/e3c54bb7b620d86524b6cebc4c28568a2a81a104871a5baf41e22c02bd8d7bac/_data",
"Name": "e3c54bb7b620d86524b6cebc4c28568a2a81a104871a5baf41e22c02bd8d7bac",
"Options": null,
"Scope": "local"
}
]
[root@docker ~]#
可以看到,這個volume並不在容器當中,而是掛載到了宿主機的/var/lib/docker/volumes/e3c54bb7b620d86524b6cebc4c28568a2a81a104871a5baf41e22c02bd8d7bac/_data目錄下。
再創建一個mysql2的容器:
[root@docker ~]# docker run -d --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local a3c730f24ca254cabbd328b4c5a6e7fa1822d7e1b2e54c9ac9104839295e5225
local e3c54bb7b620d86524b6cebc4c28568a2a81a104871a5baf41e22c02bd8d7bac
[root@docker ~]# docker volume inspect a3c730f24ca254cabbd328b4c5a6e7fa1822d7e1b2e54c9ac9104839295e5225
[
{
"CreatedAt": "2018-06-07T09:22:18Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/a3c730f24ca254cabbd328b4c5a6e7fa1822d7e1b2e54c9ac9104839295e5225/_data",
"Name": "a3c730f24ca254cabbd328b4c5a6e7fa1822d7e1b2e54c9ac9104839295e5225",
"Options": null,
"Scope": "local"
}
]
[root@docker ~]#
刪除這兩個容器:
[root@docker ~]# docker stop mysql1 mysql2
[root@docker ~]# docker rm mysql1 mysql2
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local a3c730f24ca254cabbd328b4c5a6e7fa1822d7e1b2e54c9ac9104839295e5225
local e3c54bb7b620d86524b6cebc4c28568a2a81a104871a5baf41e22c02bd8d7bac
[root@docker ~]#
可以發現,volume在容器刪除之后也不會被刪除,可以防止容器刪除之后數據也不存在了的問題。但是volume的名字非常不友好,不方便我們使用,但是我們可以在創建或者啟動容器的時候給volume設置別名來方便我們使用。
給volume起別名
重新創建mysql1容器,在啟動的時候給volume設置別名
[root@docker ~]# sudo docker run -d -v mysql:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
[root@docker ~]# docker volume ls
DRIVER VOLUME NAME
local mysql
[root@docker ~]# docker volume inspect mysql
[
{
"CreatedAt": "2018-06-07T09:30:33Z",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/mysql/_data",
"Name": "mysql",
"Options": null,
"Scope": "local"
}
]
[root@docker ~]#
驗證一下這個mysql的volume是否已經被使用:進入mysql1容器,創建一個數據庫
[root@docker ~]# docker exec -it mysql1 /bin/bash
root@5aa496b309cc:/# mysql -uroot
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.11 MySQL Community Server - GPL
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.01 sec)
mysql> create database docker;
Query OK, 1 row affected (0.07 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| docker |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> \q
Bye
root@5aa496b309cc:/# exit
[root@docker ~]# 停止mysql1容器,並刪除mysql1容器
[root@docker ~]# docker stop mysql1
[root@docker ~]# docker rm mysql1
[root@docker ~]# 創建一個新的mysql2的容器,急需使用mysql這個volume
[root@docker ~]# sudo docker run -d -v mysql:/var/lib/mysql --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
[root@docker ~]# 進入mysql2查看是否存在之前的數據
[root@docker ~]# docker exec -it mysql2 /bin/bash
root@cf1cc3ad331e:/# mysql -uroot
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.11 MySQL Community Server - GPL
Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| docker |
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
mysql> \q
Bye
root@cf1cc3ad331e:/# exit
可以發現,volume中的數據並不會因為容器的刪除二消失,實現了數據持久化的目標。但是這種方式的volume需要在Dockerfile中使用VOLUME來預先指定容器中的數據存放路徑。
18.2 Bind Mounting
Bind Mounting跟上面的方式不一樣,可以動態的指定容器內文件存放路徑和宿主機上的數據庫卷目錄。
構建一個docker-nginx的鏡像:
# Dockerfile
[root@docker docker-nginx]# cat Dockerfile
# this same shows how we can extend/change an existing official image from Docker Hub
FROM nginx:latest
# highly recommend you always pin versions for anything beyond dev/learn
WORKDIR /usr/share/nginx/html
# change working directory to root of nginx webhost
# using WORKDIR is prefered to using 'RUN cd /some/path'
COPY index.html index.html
# I don't have to specify EXPOSE or CMD because they're in my FROM
[root@docker docker-nginx]# cat index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>hello</title>
</head>
<body>
<h1>Hello Docker! </h1>
</body>
</html>
[root@docker docker-nginx]# 構建鏡像
[root@docker docker-nginx]# docker build -t staryjie/docker-nginx .
[root@docker docker-nginx]# 創建容器
[root@docker docker-nginx]# docker run -d -p 80:80 --name web staryjie/docker-nginx
[root@docker docker-nginx]# 本地訪問
[root@docker docker-nginx]# curl 127.0.0.1
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>hello</title>
</head>
<body>
<h1>Hello Docker! </h1>
</body>
</html>
[root@docker docker-nginx]#
但是index.html文件是無法更改的,如果要更改必須要重新構建鏡像,這樣非常不便。
[root@docker docker-nginx]# 強制刪除web容器
[root@docker docker-nginx]# docker rm -f web
[root@docker docker-nginx]# 重新創建一個容器,指定宿主機上index.html的目錄到容器中nginx的html目錄
[root@docker docker-nginx]# docker exec -it web1 /bin/bash
root@18af473954f1:/usr/share/nginx/html# ls
Dockerfile index.html
root@18af473954f1:/usr/share/nginx/html# touch test.txt
root@18af473954f1:/usr/share/nginx/html# exit
[root@docker docker-nginx]# ls
Dockerfile index.html test.txt
[root@docker docker-nginx]# curl 127.0.0.1
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>hello</title>
</head>
<body>
<h1>Hello Docker! </h1>
</body>
</html>
[root@docker docker-nginx]# 修改index.html
[root@docker docker-nginx]# cat index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>hello</title>
</head>
<body>
<h1>Hello Docker! </h1>
<h1>Hello, I have changed this file! </h1>
</body>
</html>
[root@docker docker-nginx]# curl 127.0.0.1
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>hello</title>
</head>
<body>
<h1>Hello Docker! </h1>
<h1>Hello, I have changed this file! </h1>
</body>
</html>
[root@docker docker-nginx]#
采用Bind Mounting的方式實現volume的話,容器內外的數據是同步的,只需要修改一個地方,容器內或者容器外都會同步修改,非常的方便快捷。
采用Bind Mounting的方式,將docker作為開發環境可以使我們的開發環境和生產環境保持一致,這也是實現DevOps的第一步。(很多開發使用的都是Windows的系統,但是服務器一般都是Linux的,無法保持環境的一致性,影響開發效率。)
