Docker持久化存儲


  Docker的鏡像是只讀的,但是容器是可寫的,我們可以將數據寫入到容器,不過一旦容器刪除數據將會丟失,那么有什么辦法能將數據進行持久化存儲呢?

一、Data Volume

在執行docker run 時,通過-v參數將主機目錄作為容器的數據卷,這就是基於本地文件系統Volumn管理。

1、Volume類型

  • 受管理的Volume,由docker后台自動創建
  • 綁定掛載的Volume,具體掛載位置由用戶指定

2、docker后台自動創建

以mysql鏡像為例說明,進入到https://hub.docker.com/_/mysql查看詳情。

  • 啟動docker服務
[root@docker-node1 ~]# systemctl start docker
  • 拉取mysql鏡像
[root@docker-node1 ~]# docker pull mysql
Using default tag: latest
latest: Pulling from library/mysql
619014d83c02: Pulling fs layer 
9ced578c3a5f: Pulling fs layer 

注意:如果出現Get https://registry-1.docker.io/v2/library/mysql/manifests/latest: dial tcp: lookup registry-1.docker.io on 8.8.8.8:53: read udp 192.168.0.109:41429->8.8.8.8:53: i/o timeout這種問題,可以嘗試修改/etc/resolv.conf文件,加入nameserver 8.8.4.4

[root@docker-node1 ~]# cat /etc/resolv.conf
# Generated by NetworkManager
nameserver 8.8.8.8
nameserver 8.8.4.4
View Code
  • 運行mysql鏡像
[root@docker-node1 ~]# docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
9dd537e48f16fd924f19f95aa38d15e08bdfa254d65ee66f1e19331ed6e79ce3
[root@docker-node1 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
9dd537e48f16        mysql               "docker-entrypoint.s…"   20 seconds ago      Up 11 seconds       3306/tcp, 33060/tcp   mysql1

這樣就啟動了一個mysql的容器mysql1,但是顯然我們並沒有指定數據庫中表指定放到什么地方,那么它給我們自動放到什么地方了呢?

我們可以看看mysql的Dockerfile:

FROM debian:stretch-slim

# add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added
RUN groupadd -r mysql && useradd -r -g mysql mysql

RUN apt-get update && apt-get install -y --no-install-recommends gnupg dirmngr && rm -rf /var/lib/apt/lists/*

# add gosu for easy step-down from root
ENV GOSU_VERSION 1.7
RUN set -x \
    && apt-get update && apt-get install -y --no-install-recommends ca-certificates wget && rm -rf /var/lib/apt/lists/* \
    && wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture)" \
    && wget -O /usr/local/bin/gosu.asc "https://github.com/tianon/gosu/releases/download/$GOSU_VERSION/gosu-$(dpkg --print-architecture).asc" \
    && export GNUPGHOME="$(mktemp -d)" \
    && gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys B42F6819007F00F88E364FD4036A9C25BF357DD4 \
    && gpg --batch --verify /usr/local/bin/gosu.asc /usr/local/bin/gosu \
    && gpgconf --kill all \
    && rm -rf "$GNUPGHOME" /usr/local/bin/gosu.asc \
    && chmod +x /usr/local/bin/gosu \
    && gosu nobody true \
    && apt-get purge -y --auto-remove ca-certificates wget

RUN mkdir /docker-entrypoint-initdb.d

RUN apt-get update && apt-get install -y --no-install-recommends \
# for MYSQL_RANDOM_ROOT_PASSWORD
        pwgen \
# for mysql_ssl_rsa_setup
        openssl \
# FATAL ERROR: please install the following Perl modules before executing /usr/local/mysql/scripts/mysql_install_db:
# File::Basename
# File::Copy
# Sys::Hostname
# Data::Dumper
        perl \
    && rm -rf /var/lib/apt/lists/*

RUN set -ex; \
# gpg: key 5072E1F5: public key "MySQL Release Engineering <mysql-build@oss.oracle.com>" imported
    key='A4A9406876FCBD3C456770C88C718D3B5072E1F5'; \
    export GNUPGHOME="$(mktemp -d)"; \
    gpg --batch --keyserver ha.pool.sks-keyservers.net --recv-keys "$key"; \
    gpg --batch --export "$key" > /etc/apt/trusted.gpg.d/mysql.gpg; \
    gpgconf --kill all; \
    rm -rf "$GNUPGHOME"; \
    apt-key list > /dev/null

ENV MYSQL_MAJOR 8.0
ENV MYSQL_VERSION 8.0.19-1debian9

RUN echo "deb http://repo.mysql.com/apt/debian/ stretch mysql-${MYSQL_MAJOR}" > /etc/apt/sources.list.d/mysql.list

# the "/var/lib/mysql" stuff here is because the mysql-server postinst doesn't have an explicit way to disable the mysql_install_db codepath besides having a database already "configured" (ie, stuff in /var/lib/mysql/mysql)
# also, we set debconf keys to make APT a little quieter
RUN { \
        echo mysql-community-server mysql-community-server/data-dir select ''; \
        echo mysql-community-server mysql-community-server/root-pass password ''; \
        echo mysql-community-server mysql-community-server/re-root-pass password ''; \
        echo mysql-community-server mysql-community-server/remove-test-db select false; \
    } | debconf-set-selections \
    && apt-get update && apt-get install -y mysql-community-client="${MYSQL_VERSION}" mysql-community-server-core="${MYSQL_VERSION}" && rm -rf /var/lib/apt/lists/* \
    && rm -rf /var/lib/mysql && mkdir -p /var/lib/mysql /var/run/mysqld \
    && chown -R mysql:mysql /var/lib/mysql /var/run/mysqld \
# ensure that /var/run/mysqld (used for socket and lock files) is writable regardless of the UID our mysqld instance ends up having at runtime
    && chmod 777 /var/run/mysqld

VOLUME /var/lib/mysql  #持久化存儲
# Config files
COPY config/ /etc/mysql/
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306 33060
CMD ["mysqld"]
View Code

實際上已經幫我們持久化存儲到主機的/var/lib/mysql目錄中了。

另外可以看到主機上有一條volume的記錄:

[root@docker-node1 ~]# docker volume ls
DRIVER              VOLUME NAME
local               5c4fbbc613e046baf9e148a301ef94ef09e4fc57312edd907885336e9ecd5bc1

可以看看這個存儲卷記錄的具體信息:

[root@docker-node1 ~]# docker volume inspect 5c4fbbc613e046baf9e148a301ef94ef09e4fc57312edd907885336e9ecd5bc1
[
    {
        "CreatedAt": "2020-02-03T13:24:30+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/5c4fbbc613e046baf9e148a301ef94ef09e4fc57312edd907885336e9ecd5bc1/_data",
        "Name": "5c4fbbc613e046baf9e148a301ef94ef09e4fc57312edd907885336e9ecd5bc1",
        "Options": null,
        "Scope": "local"
    }
]
  • 持久化存儲

如果我們講這個容器刪掉,這個存儲卷還存在嗎?可以嘗試一下:

[root@docker-node1 ~]# docker rm -f mysql1 #刪掉mysql1容器
mysql1
[root@docker-node1 ~]# docker volume ls #存儲卷仍舊存在
DRIVER              VOLUME NAME
local               5c4fbbc613e046baf9e148a301ef94ef09e4fc57312edd907885336e9ecd5bc1

可以看到雖然mysql的容器不在了,但是它的存儲卷依舊存在。

3、用戶指定掛載位置

上面你也許已經看到了一些需要自定義的地方:其一,volume name的名字太長了,需要自己指定;其二,volume持久化的目錄需要自己指定。

  • 容器重命名以及指定掛載位置
[root@docker-node1 ~]# docker run -d -v /home/mysql:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
0e560c5c9caf902c0d9647ef1d7995803d9dfde8e0db2c529d97f913f943bdf5

上面通過-v參數指定mysql的掛載位置(主機文件位置:容器文件位置[Dockerfile中已經指定]),通過--name指定容器的名稱,如下所示:

[root@docker-node1 ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
0e560c5c9caf        mysql               "docker-entrypoint.s…"   9 seconds ago       Up 7 seconds        3306/tcp, 33060/tcp   mysql1
  • 測試持久化

現在,可以先進入到這個容器中,對mysql的數據庫中做一些改變,然后刪掉容器:

(1)進入容器

[root@docker-node1 ~]# docker exec -it mysql1 /bin/sh

(2)進入mysql

# mysql -uroot
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.19 MySQL Community Server - GPL

Copyright (c) 2000, 2020, 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.

(3)創建test數據庫

mysql> create database test;
Query OK, 1 row affected (0.36 sec)

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.00 sec)

(4)刪掉mysql1容器

[root@docker-node1 ~]# docker rm -f mysql1
mysql1

(5)新建mysql2容器

新建mysql2容器,並且指向mysql1容器的存儲卷:

[root@docker-node1 ~]# docker run -d -v /home/mysql:/var/lib/mysql --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
9fc1aff1e798b0b6b4c01b26160877253c655f137a542734eb81b2b8984af66c

然后可以進入到mysql2容器中查看mysql中是否有mysql1容器創建的test數據庫:

[root@docker-node1 ~]# docker exec -it mysql2 /bin/sh #進入mysql2容器
# mysql -uroot
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.19 MySQL Community Server - GPL

Copyright (c) 2000, 2020, 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                |
| test               |
+--------------------+
5 rows in set (0.16 sec)

可以看到卻是存在test數據庫,這樣也就實現了volume持久化。

二、Bind Mounting

1、bing mounting和data volume的區別

  • data volume 需要在 Dockerfile文件中 定義 volume,這種方式較多的是將容器映射到本地,實現容器與外部程序共享數據。
  • bind mounting 只需要 -v 指明 容器外部文件夾和容器映射文件夾的對應關系,將本地的文件映射到容器內。

2、bind mounting的應用

  •  在bindmounting-test文件夾中新建兩個文件
[root@docker-node1 bindmounting-test]# ls
Dockerfile  index.html
  • 編寫Dockerfile文件
FROM nginx     #基礎鏡像
WORKDIR /usr/share/nginx/html  #工作目錄,容器內的目錄
COPY index.html .  #將本機當前目錄下的index.html文件拷貝到上述工作目錄下
  • 編寫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-node1 bindmounting-test]# docker build -t docker-nginx .
Sending build context to Docker daemon  3.072kB
Step 1/3 : FROM nginx
 ---> f68d6e55e065
Step 2/3 : WORKDIR /usr/share/nginx/html
 ---> Running in 3b784d71c4dc
Removing intermediate container 3b784d71c4dc
 ---> 10cef7e9bbb9
Step 3/3 : COPY index.html .
 ---> f43b1a1b7bef
Successfully built f43b1a1b7bef
Successfully tagged docker-nginx:latest
  • 運行docker-nginx鏡像
[root@docker-node1 bindmounting-test]# docker run -d -p 80:80 --name web docker-nginx
22c28d4887d222785a3c60ca28c2fe79ff82ff9e630af0cdbb48232171732c90

上面通過-p進行了端口暴露,可通過外網訪問:

   但是難道每次修改index.html文件都需要重新生成鏡像,然后運行嗎?我們可以通過bing mounting進行映射,如果本地文件變了,容器內的就會改變。這樣就不會重新生成鏡像了。

  • 使用bind mounting
[root@docker-node1 bindmounting-test]# ls
Dockerfile  index.html
[root@docker-node1 bindmounting-test]# docker run -d -v $(pwd):/usr/share/nginx/html -p 80:80 --name web docker-nginx
f4661067be3fdead6c627bc00a054244b92207d42baebafdc37b8c8a8fe6f3cb

  可以看到上面我將$(pwd)這個當前路勁的文件,也就是bindmounting-test文件夾中內容映射到容器/usr/share/nginx/html目錄下,如果本地bindmounting-test文件夾下有文件更改了,容器中自然會變化。

  例如,修改本地index.html文件中的內容:

[root@docker-node1 bindmounting-test]# vim index.html
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">

  <title>hello</title>

</head>

<body>
  <h1>Hello </h1>
</body>
</html>

此時,再刷新頁面:

 這樣當在容器內或者容器外更改 映射文件夾內的內容時 兩邊都會改變,很利於開發者在linux進行應用的迭代更新部署。

三、總結

1、完成Data volume的步驟

  • Dockerfile中指定volume參數,如 VOLUME  /var/lib/mysql
  • docker run -v /home/mysql:/var/lib/mysql

2、完成Bind Mounting的步驟

  • docker run -v /home/mysql:/var/lib/mysql 

上面的映射關系都是 主機:容器

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM