當前位置:首頁 » 服務存儲 » 容器數據的持久化存儲
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

容器數據的持久化存儲

發布時間: 2023-05-25 10:43:44

1. Docker(5)——數據管理

docker 容器的文件系統在宿主機上存在的方式很復雜,這會帶來下面幾個問題:

為了能夠 保存(持久化) 數據以及 共享 容器間的數據,docker 引入了數據卷(volume) 機制。數據卷是存在於一個或多個容器中的特定文件或文件夾,它可以繞過默認的聯合文件系統,以正常的文件或者目錄的形式存在於宿主機上。

其生存周期獨立於容器的生存周期。

容器中主要有 兩種 管理數據方式: 數據卷(Data Volumes) 數據卷容器(Data Volume Containers)

數據卷是一個可供容器使用的特殊目錄,它繞過文件系統,可以提供很多有用的特性:

數據卷的使用類似 linux 下對目錄或文件進行 mount 操作,目前Docker提供了 三種 不同的方式將數據從宿主機掛載到容器中,分別是

其中 volume 、 bind mount 比較常用, tmpfs mount 基本不會用.

volumes作為Docker管理宿主機文件系統的一部分 ,默認位於 /var/lib/docker/volumes 目錄中,不是宿主機已有數據,而是新建的。

docker 專門提供了 volume 子命令來操作數據卷:

先創建一個名稱為 hello 的數據卷並通過 ls 命令進行查看:

然後可以使用 inspect 命令看看數據卷hello的詳細信息

該數據卷使用的 Driver 為默認的 local ,表示數據卷使用宿主機的本地存儲;

Mountpoint 是 volumes 的掛載點,默認是本機 /var/lib/docker/volumes 下的一個目錄。

所有Container的數據都保存在了這個目錄下邊,由於沒有在創建時指定卷,所以Docker幫我們默認創建許多匿名卷。

使用 -v 選項也可以指定掛載一個本地的已有目錄到容器中去作為數據卷:
docker run -it –-name robot1 -v /var/data:/opt/mydata ros/kinetic /bin/bash
上面的命令掛載主機的 /var/data 目錄到容器的 /opt/mydata 目錄。

這個功能在測試的時候特別方便,比如用戶可以放置一些程序或數據到本地目錄中,然後在容器中使用。另外,本地目錄的路徑必須是絕對路徑,如果目錄不存在,Docker 會自動創建。
Docker 掛載數據卷的默認許可權是可讀寫 rw ,用戶也可以通過 ro 標記指定為只讀:
docker run -it –-name robot1 -v /var/data:/opt/mydata:ro ros/kinetic /bin/bash
加了 :ro 之後,容器內掛載的數據卷內的數據就變成只讀的了。

除了把數據卷中的數據存儲在宿主機,docker 還允許我們通過指定 volume driver 的方式把數據卷中的數據存儲在其它的地方,比如 Azrue Storge 或 AWS 。

通過 vieux/sshfs 驅動把數據卷的存儲在雲主機上,docker 默認是不安裝 vieux/sshfs 插件的,我們可以通過下面的命令進行安裝:
docker plugin install --grant-all-permissions vieux/sshfs

然後通過 vieux/sshfs 驅動創建數據卷,並指定遠程主機的登錄用戶名、密碼和數據存放目錄:

注意:確保指定的遠程主機上的掛載點 /home/nick/sshvolume 目錄是存在的,否則在啟動容器時會報錯。

最後在啟動容器時指定掛載這個數據卷:

在容器中 /world 目錄下操作的文件都存儲在遠程主機的 /home/nick/sshvolume 目錄中。進入容器 testcon 然後在 /world 目錄中創建一個文件,然後打開遠程主機的 /home/nick/sshvolume 目錄進行查看,新建的文件會出現在那裡。

當使用 bind mounts 將主機上的目錄掛載到容器中時,目錄由其在主機上的完整或相對路徑引用。

bind mount 和 volume 其實都是利用宿主機的文件系統,不同之處在於volume是docker自身管理的目錄中的子目錄,所以不存在許可權引發的掛載的問題,並且目錄路徑是docker自身管理的,所以也不需要在不同的伺服器上指定不同的路徑,不需要關心路徑

bind mounts 可以掛載在宿主機系統的任意位置 ,但 bind mount 在不同的宿主機系統是不可移植的,比如Windows和Linux的目錄結構是不一樣的, bind mount 所指向的 host 目錄也不能一樣。這也是為什麼 bind mount 不能出現在Dockerfile中的原因,因為這樣Dockerfile就不可移植了。

如果使用 Bind mounts 掛載 宿主機目錄 容器內非空目錄 ,那麼 此容器中的非空目錄中的文件會被隱藏 ,容器訪問這個目錄時能夠訪問到的文件均來自於宿主機目錄。這也是Bind mounts模式和Volumes模式最大的行為上的不同。

掛載存儲在宿主機系統的內存中,而不會寫入宿主機的文件系統;

這張圖說明 bind mount 和 volume 其實都是利用宿主機的文件系統, Bind mounts 模式是將宿主機上的任意文件或文件夾掛載到容器,而 Volumes 本質上是將Docker服務管理的一塊區域(默認是 /var/lib/docker/volumes 下的文件夾)掛載到容器。所以 volume 不存在許可權引發的掛載的問題,並且目錄路徑是docker自身管理的,所以也不需要在不同的伺服器上指定不同的路徑,不需要關心路徑。

相對於 bind mount , volume 是Docker Engine在自己的「地盤」分配了一個路徑作為掛載點,自己地盤的許可權肯定是安排的明明白白。所以,以上掛載宿主機路徑的問題都解決了。

在使用 volume 作為數據卷掛載到容器時,直接用 volume 名稱代替宿主機路徑名就行:
docker run -d -v test_vol:/var/data some_image

這樣就將數據卷 test_vol 掛載到了容器內的 /var/data 目錄下。

命名的容器掛載數據卷,其它容器通過掛載這個(父容器)實現數據共享,掛載數據卷的容器,稱之為數據卷容器

可以利用數據卷容器對其中的數據卷進行備份、恢復,以實現數據的遷移。

備份
使用下面的命令來備份 mydata 數據卷容器內的數據卷:

sudo docker run --volumes-from mydata -v $(pwd):/backup –-name worker ubuntu tar cvf /backup/backup.tar /data
這個命令首先利用 Ubuntu 鏡像創建了一個容器 worker。又使用 --volumes-from mydata 參數來讓 worker 容器掛載 mydata 容器的數據卷。接下來使用 -v $(pwd):/backup 參數來掛載本地的當前目錄到 worker 容器的 /backup 目錄。
在 worker 容器啟動後,使用了 tar cvf /backup/backup.tar /data 命令來將 /data 下內容備份為容器內的 /backup/backup.tar,即宿主主機的當前目錄下的backup.tar。

恢復
如果要恢復數據到一個容器,可以按照下面的操作。首先創建一個帶有數據卷的容器 mydata2:

sudo docker run -v /data –-name mydata2 ubuntu /bin/bash
然後創建另一個新的容器,掛載 mydata2 的數據卷,並使用 tar 解壓縮備份文件到所掛載的容器卷中:

sudo docker run --volumes-from mydata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar

如果用戶需要在容器之間共享一些持續更新的數據,最簡單的方式是使用數據卷容器。數據卷容器其實就是一個普通的容器,專門用它提供數據卷供其他容器掛載。下面簡單介紹其使用方法。

首先要創建一個數據卷容器 mydata,並在其中創建一個數據卷掛載到 /data 目錄。

sudo docker run -it -v /data –-name mydata ubuntu
然後在其他容器中使用 --volumes-from 來掛載 mydata 容器中的數據卷。例如創建兩個容器 mycon1 和 mycon2,並從 mydata 容器掛載數據卷:

sudo docker run -it --volumes-from mydata –-name mycon1 ubuntu
sudo docker run -it --volumes-from mydata –-name mycon2 ubuntu
(注意,命令中沒有指定數據卷的信息,也就是說新容器中掛載數據卷的目錄和源容器中是一樣的。)

此時容器 mycon1 和 mycon2 都掛載同一個數據卷到相同的目錄 /data。三個容器任何一個在該目錄下寫入數據其他容器都能看到。
可以多次使用 --volumes-from 參數來從多個容器掛載多個數據卷。還可以從其他已經掛載了容器的容器來掛載數據卷。並且使用 --volumes-from 參數所掛載數據卷的容器自身並不需要保持在運行狀態。
但刪除掛載了數據卷的容器時,數據卷並不會被自動刪除。如果要刪除一個數據卷,必須在刪除最後一個還掛載著它的容器時顯式的使用 docker rm -v 命令來指定同時刪除關聯的容器。

如何對已經運行的容器掛載目錄?
https://blog.51cto.com/hjun169/2440799
手動修改mount掛載的json文件,狂神的那個視頻裡面也有寫。

深刻理解Docker鏡像大小
https://blog.csdn.net/shlazww/article/details/47375009

理解docker的分層鏡像實現 base 鏡像共享(DockerFile)
https://blog.csdn.net/lu_1110/article/details/106533490

2. 什麼是數據持久化為什麼要持久化

數據持久化就是將內存中的數據模型轉換為存儲模型,以及將存儲模型轉換為內存中的數據模型的統稱. 數據模型可以是任何數據結構或對象模型,存儲模型可以是關系模型、XML、二進制流等。cmp和Hibernate只是對象模型到關系模型之間轉換的不同實現。

數據持久化對象的基本操作有:保存、更新、刪除、查詢等。

Hibernate框架中數據持久化機制:

在業務程序與資料庫之間,Hibernate框架使用Session會話,來完成數據的提交、更新、刪除、查詢等等。

1、向資料庫提交數據

在程序中保存對象時,會把數據保存到Session會話中,然後根據框架的配置文件,自動或手動決定什麼時候把這種保存提交到資料庫。

2、從資料庫中查詢數據

在查詢數據之前,需要清理緩存(手動清理,或者通過配置文件框架自動清理)清理緩存的目的是為了使Session會話中的數據與資料庫中的數據保持一致。然後程序只需要查詢Session會話中的數據即可。

(2)容器數據的持久化存儲擴展閱讀:

使用數據持久化有以下好處:

1、程序代碼重用性強,即使更換資料庫,只需要更改配置文件,不必重寫程序代碼。

2、業務邏輯代碼可讀性強,在代碼中不會有大量的sql語言,提高程序的可讀性。

3、持久化技術可以自動優化,以減少對資料庫的訪問量,提高程序運行效率。

3. k8s中的Mysql資料庫持久化存儲

 

一、配置:

環境:

CentOS7 

VMware

筆者配置了四台虛擬機:

K8S-Master節點: 3GB內存   2核CPU   20GB硬碟空間

K8S-node1節點:  2GB內存   2核CPU   30GB硬碟空間

K8S-node2節點:  2GB內存   2核CPU   30GB硬碟空間

鏡像倉庫節點:      2GB內存   2核CPU   50GB硬碟空間

二、節點規劃:

使用三台虛擬機搭建K8S集群,使用一台虛擬機搭建鏡像倉庫。

每台虛擬機配置兩塊網卡,其中一塊為「NAT模式」,用於拉取鏡像等功能。

另外一塊網卡為「僅主機模式」,用於集群節點間的通信。歸劃如下:

K8s-master節點:

僅主機模式:10.10.10.200

NAT模式:  192.168.200.130

K8S-node1節點:

僅主機模式:10.10.10.201

NAT模式:  192.168.200.131

K8S-node2節點:

僅主機模式:10.10.10.202

NAT模式:  192.168.200.132

鏡像倉庫節點:

僅主機模式:10.10.10.101

NAT模式:  192.168.200.150

三、版本信息

Linux內核版本:

Linux version 3.10.0-862.el7.x86_64 ([email protected])

(gcc version 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) )

 #1 SMP Fri Apr 20 16:44:24 UTC 2018

K8s集群版本為1.15.0版本:

四、基於StatefulSet與PV/PVC的MySql持久化存儲實驗

1. 在每個節點安裝nfs服務

在「鏡像倉庫」節點,執行以下命令:

yum install -y nfs-common nfs-utils rpcbind

在k8s集群,執行以下命令:

yum install -y nfs-utils rpcbind

2. 在「鏡像倉庫」節點下,配置nfs伺服器

mkdir /nfs_mysql

Chmod 777 /nfs_mysql/

(在測試環境中,為了不考慮用戶屬性,暫時賦予777許可權,但在生產環境不推薦這樣做)

Chown nfsnobody /nfs_mysql/

echo 「/nfs_mysql *(rw,no_root_squash,no_all_squash,sync)」 >> /etc/exports

cat /etc/exports

/nfs_mysql *(rw,no_root_squash,no_all_squash,sync)

systemctl start rpcbind

systemctl start nfs

3. 測試nfs服務是否可用

mkdir /test

showmount -e 10.10.10.101

可見/nfs_mysql *已暴露於共享目錄,接下來測試掛載是否可用:

在master節點下執行:

mount -t nfs 10.10.10.101:/nfs_mysql /test/

echo "hello-world">>/test/1.txt

在鏡像倉庫節點下查看1.txt是否存在,若存在則掛載成功:

可見nfs服務可以正常使用,接下來刪除test目錄和1.txt

在鏡像倉庫下:

[root@hub nfs_mysql]# rm -f 1.txt

在Master節點下:

[root@k8s-master ~]# umount /test/

[root@k8s-master ~]# rm -rf /test/

同理,依照以上步驟同時創建:(提供多個mysql副本進行掛載)

nfs_mysql1

nfs_mysql2

完成後需要重啟nfs服務

systemctl restart rpcbind

systemctl restart nfs

最終效果:

4. 將nfs封裝成pv

創建mysql_test文件夾,將yaml文件統一保存在此目錄下

mkdir mysql_test

cd mysql_test

vim mysql-pv.yml

mysql-pv.yml配置如下:

apiVersion: v1

kind: PersistentVolume

metadata:

  name: mysql-pv

spec:

  capacity:

    storage: 5Gi

  accessModes:

    -  ReadWriteOnce

  persistentVolumeReclaimPolicy: Retain

  storageClassName: nfs

  nfs:

    path: /nfs_mysql

    server: 10.10.10.101

---

apiVersion: v1

kind: PersistentVolume

metadata:

  name: mysql-pv1

spec:

  capacity:

    storage: 5Gi

  accessModes:

    -  ReadWriteOnce

  persistentVolumeReclaimPolicy: Retain

  storageClassName: nfs

  nfs:

    path: /nfs_mysql1

    server: 10.10.10.101

---

apiVersion: v1

kind: PersistentVolume

metadata:

  name: mysql-pv2

spec:

  capacity:

    storage: 5Gi

  accessModes:

    -  ReadWriteOnce

  persistentVolumeReclaimPolicy: Retain

  storageClassName: nfs

  nfs:

    path: /nfs_mysql2

    server: 10.10.10.101

注意:

在k8s集群15版本中recycle回收策略已被刪除,只能用retain策略或者Delete策略。這里我們使用 persistentVolumeReclaimPolicy: Retain

 

執行命令:

kubectl create -f mysql-pv.yml

kubectl get pv

如圖所示,即為Pv創建成功。

5. 部署MySQL,在mysql_test目錄下編寫mysql.yml,配置文件如下

apiVersion: v1

kind: Service

metadata:

  name: mysql

  labels:

    app: mysql

spec:

  ports:

  - port: 3306

    name: mysql

  clusterIP: None

  selector:

    app: mysql

---

apiVersion: apps/v1

kind: StatefulSet

metadata:

  name: mysql

spec:

  selector:

    matchLabels:

      app: mysql

  serviceName: "mysql"

  replicas: 3

  template:

    metadata:

      labels:

        app: mysql

    spec:

      containers:

      - name: mysql

        image: mysql:5.6

        env:

        - name: MYSQL_ROOT_PASSWORD

          value: password

        ports:

        - containerPort: 3306

          name: mysql

        volumeMounts:

        - name: mysql-persistent-storage

          mountPath: /var/lib/mysql

  volumeClaimTemplates:

  - metadata:

      name: mysql-persistent-storage

    spec:

      accessModes: ["ReadWriteOnce"]

      storageClassName: "nfs"

      resources:

        requests:

          storage: 1Gi  

執行以下命令,部署mysql服務:

kubectl create -f mysql.yml

如圖可知,mysql按StatefulSet依次創建了mysql-0 mysql-1 mysql-2

查看各個Pod部在哪個節點:

6. 通過創建臨時容器,使用MySQL客戶端發送測試請求給MySQL master節點

注意:

主機名為mysql-0.mysql;跨命名空間的話,主機名請使用mysql-0.mysql. [NAMESPACE_NAME].如果沒有指定命名空間,默認為default,即 mysql-0.mysql. default。

   

這里筆者打算關閉node2節點來模擬node2宕機,來測試是否實現數據的持久化存儲,

所以我們向node2上的mysql1寫入數據。

 

執行以下命令,訪問mysql1:

kubectl run mysql-client --image=mysql:5.6 -it --rm --restart=Never -- mysql -h mysql-1.mysql.default -p password

創建資料庫demo,並向messages表中寫入hello-world

CREATE DATABASE demo; 

CREATE TABLE demo.messages (message VARCHAR(250)); 

INSERT INTO demo.messages VALUES ('hello-world');

如圖所示

接下來我們來關閉k8s-node2虛擬機,模擬宕機

查看nodes的運行狀態,可知node2的狀態已轉變為NotReady

一段時間後,k8s將Pod MySql -1遷移到節點k8s-node1

由於時間過長,筆者把三個Pod都刪除重啟後,驗證數據:

MySQL服務恢復,數據完好無損!

4. Linux裡面什麼是數據持久化

數據持久化顧名思義就是把程序中的數據以某種形式保存到某存貯介質中,以達到持久化的目的。當程序運行時,一些數據是臨時保存在內存中,一旦退出系統,這些數據就丟失了。那麼,使用某種手段將數據保存在硬碟上或者資料庫中,這樣即使退出系統後又重新啟動系統,那麼這些數據仍然可以重新找回來。


例如管理員向一個用戶管理系統中添加了一個用戶的資料,那麼這個系統需要將新添加的資料保存到資料庫中,否則系統退出或者電腦重啟後該用戶資料就會丟失。將數據從內存保存到資料庫中,這便是數據的持久化。當然,資料庫只是持久化方式中的一種,也可以保存在其他的永久存貯介質中。

圖為數據持久化的過程示意圖。


持久化(Persistence),即把數據(如內存中的對象)保存到可永久保存的存儲設備中(如磁碟)。持久化的主要應用是將內存中的對象存儲在資料庫中,或者存儲在磁碟文件中、XML數據文件中等等。

持久化是將程序數據在持久狀態和瞬時狀態間轉換的機制。

DBC就是一種持久化機制。文件IO也是一種持久化機制。

日常持久化的方法

在一定周期內保持不變就是持久化,持久化是針對時間來說的。資料庫中的數據就是持久化了的數據,只要你不去刪除或修改。比如在瀏覽器中一次Session會話中Session對象變數也是不變的,是Session容器中持久化。對象持久化的方式有很多種,根據周期不同有,page,Session,Application。對象序列化機制對於需要將對象的狀態保存到文件中,而後能夠通過讀入對象狀態來重新構造對象,恢復程序狀態. 對象序列化的過程是對象持久化的方法之一,把對象保存到文件中。

簡單的理解持久化可以在二個層面:應用層和系統層、

應用層

如果關閉(shutdown)你的應用然後重新啟動則先前的數據依然存在。

系統層

如果關閉(shutdown)你的系統(電腦)然後重新啟動則先前的數據依然存在。

持久化是一種對象服務實現至少3個介面

,就是把內存中的對象保存到外存中,讓以後能夠取回。需要實現至少3個介面:

void Save(object o) 把一個對象保存到外存中

Object Load(object oid) 通過對象標識從外存中取回對象

boolExists(object oid) 檢查外存中是否存在某個對象.

類似概念序列化

我們先跳開一下,看看另一個類似的有用概念:序列化Serialize也是一種對象服務,就是把內存中的對象序列化成流、或者把流反序列化成對象。需要實現2個介面:

void Serialize(Stream stream,object o) 把對象序列化到流中

object Deserialize(Stream stream) 把流反序列化成對象

序列化和持久化很相似,有些人甚至混為一談,其實還是有區別的,序列化是為了解決對象的傳輸問題,傳輸可以在線程之間、進程之間、內存外存之間、主機之間進行。我之所以在這里提到序列化,是因為我們可以利用序列化來輔助持久化,可以說凡是可以持久化的對象都可以序列化,因為序列化相對容易一些(也不是很容易),所以主流的軟體基礎設施,比如.net和java,已經把序列化的框架完成了。

持久化方案可以分為關系資料庫方案、文件方案、對象資料庫方案、xml資料庫方案

現今主流的持久化方案是關系資料庫方案,

關系資料庫方案不僅解決了並發的問題,更重要的是,關系資料庫還提供了持久化服務之外的價值:統計分析功能。剛才我說到,凡是可以序列化的對象都可以持久化,極端的說,我們可以只建立一個表Object(OID,Bytes),但基本上沒有人這么做,因為一旦這樣,我們就失去了關系資料庫額外的統計分析功能。關系資料庫和面向對象之間有一條鴻溝,因為二者模式不匹配,所以就存在一個OR映射問題。


Redis支持兩種數據持久化方式:rdb方式和aof方式。前者會根據配置的規則定時將內存中的數據持久化到硬碟上,後者則是在每次執行寫命令之後將命令記錄下來。兩種持久化方式可以單獨使用,但是通常會將兩者結合使用。

1、RDB方式

RDB方式的持久化是通過快照的方式完成的。當符合某種規則時,會將內存中的數據全量生成一份副本存儲到硬碟上,這個過程稱作」快照」,redis默認開啟該持久化功能,具體配置如下:

save 900 1

save 300 10

save 60 10000

stop-writes-on-bgsave-error yes

rdbcompression yes

rdbchecksum yes

dbfilename mp.rdb

#文件名稱

dir ./

#rdb文件存放路徑

配置後系統會自動進行快照,save 60 10000表示60秒內有10000次寫入,那麼就會調用bgsave

除了系統自動進行快照外,我們也可以手動執行SAVE或BGSAVE命令主動進行快照操作:

執行SAVE或BGSAVE命令

執行FLUSHALL命令

2、AOF方式

在使用Redis存儲非臨時數據時,一般都需要打開AOF持久化來降低進程終止導致的數據丟失,AOF可以將Redis執行的每一條寫命令追加到硬碟文件中,這一過程會降低Redis的性能。

默認情況下,Redis沒有開啟AOF(append only file)持久化功能,可以通過在配置文件中作如下配置啟用:

appendonly no #是否開啟aof,開啟時將no改為yes

appendfilename "appendonly.aof" 持久化文件名稱

auto-aof-rewrite-percentage 100

#當前AOF文件大小是上次日誌重寫得到AOF文件大小的二倍時,自動啟動新的日誌重寫過程。

auto-aof-rewrite-min-size 64mb

#當前AOF文件啟動新的日誌重寫過程的最小值,避免剛剛啟動Reids時由於文件尺寸較小導致頻繁的重寫。

appendfsync :everysec (推薦配置)

#持久化策略

always (同步持久化,每次發生數據變更會被立即記錄到磁碟,性能差但數據完整性比較好)

everysec (非同步操作,每秒記錄,如果一秒鍾內宕機,有數據丟失)

no (將緩存回寫的策略交給系統,linux 默認是30秒將緩沖區的數據回寫硬碟的)

一般來說可以考慮同時使用兩種持久化方案.