在資料科學專案中,資料儲存的佈署與管理至關重要。Docker 提供了輕量級的虛擬化方案,有效簡化資料函式庫的佈署流程。本文將著重於如何使用 Docker 佈署 Redis、MongoDB 和 PostgreSQL 等主流資料函式庫,並探討資料持久化的最佳實務,確保資料在容器生命週期後依然完整。此外,文章也將介紹序列化技術如 JSON、YAML 和 pickle,說明如何在 Python 環境下有效地儲存和交換資料。

資料儲存系統的 Docker 應用實踐

在資料科學的領域中,如何有效地管理和使用資料儲存系統是一個重要的課題。本章將探討如何利用 Docker 來簡化開放原始碼資料儲存技術的佈署與管理,並且介紹三種主要的資料儲存技術:Redis、MongoDB 和 PostgreSQL。

序列化技術在資料科學中的應用

在資料科學的工作流程中,資料結構和物件狀態的儲存、傳輸和重建是一個核心任務,這個過程被稱為序列化。序列化是一個已經被充分解決的問題,資料科學家有多種工具可以選擇來完成這項任務。本章將重點介紹序列化技術,特別是在將記憶體中的物件轉換為二進製表示以及使用流行的 JSON 格式進行序列化方面的應用。

序列化格式與方法

本文著重於 Python 環境下的工作,因此將重點介紹兩種 Python 特有的序列化方法:pickle 和透過位元組序列化。此外,還將探討兩種根據文字的序列化方法:JSON 和 YAML。

  • JSON (JavaScript Object Notation):是一種機器可讀的 JavaScript 程式語言子集,已被程式設計社群廣泛採用為人類可讀、語言無關的序列化方法。
  • YAML (YAML Ain’t Markup Language):是另一種解決相同問題的方案。JSON 和 YAML 都能夠使用標準的基本資料型別:整數、浮點數、布林值和空值,以及字串。對於更大的結構,它們都使用關聯陣列(字典)和有序列表(陣列、向量、列表或序列)。

以下是 JSON 和 YAML 的範例:

JSON 範例
{
  "this_json": "is a JSON object",
  "a_nested_object": {
    "obj_id": 123,
    "object_value": "temperamental",
    "is_nested": true
  },
  "a_list": [1, 2, 3, 4],
  "a_list_of_strings": ["green eggs", "ham"],
  "last_used": null
}
YAML 範例
this_yaml: is a YAML object
a_nested_object:
  obj_id: 123
  object_value: temperamental
  is_nested: true
a_list:
  - 1
  - 2
  - 3
  - 4
a_list_of_strings:
  - green eggs
  - ham
last_used: null

Python 中的二進位制編碼

Python 的 pickle 模組是將 Python 物件和資料序列化為二進位制位元組串流的首選方法。與 JSON 或 YAML 相比,pickle 有幾個根本的不同之處。JSON 和 YAML 是人類可讀的,而轉換為位元組串流的物件則不是。JSON 和 YAML 序列化的物件可以被任何語言的程式讀取,而 pickle 物件只能在 Python 中讀取。由於 pickle 物件不需要考慮互操作性,因此可以 pickle 的 Python 物件範圍很廣,而使用 JSON 或 YAML 只能序列化字典。

使用 Pickle 的範例

import pickle

# 定義一個物件
data = {
    'foo': 'bar',
    'numbers': [1, 2, 3]
}

# 將物件序列化為位元組串流
serialized_data = pickle.dumps(data)
print(serialized_data)

# 將位元組串流反序列化為物件
deserialized_data = pickle.loads(serialized_data)
print(deserialized_data)

Redis 資料儲存技術

Redis 是一種開放原始碼的記憶體資料結構儲存系統,用於儲存與鍵相關聯的不同型別的資料值。在我們的技術堆積疊中,Redis 將用於兩個主要目的:首先,它將作為快取,用於持久化物件超出 Python 程式的生命週期或在 Jupyter Notebook 之間;其次,我們將使用 Redis 作為訊息代理,以便使用名為 rq 的 Python 程式函式庫從筆記本執行延遲作業處理。

提取 Redis 映象

可以使用 docker pull 命令從 Docker Hub 提取 Redis 映象,如下所示:

$ docker pull redis

執行 Redis 容器

提取映象後,可以使用以下命令執行 Redis 容器:

$ docker run -d redis

驗證 Redis 伺服器

可以透過 redis-cli 命令向 Redis 伺服器傳送 PING 命令來驗證其功能:

$ docker exec <container_id> redis-cli ping
PONG

如果 Redis 伺服器正確回應 PONG,則表示一切正常。

結束並移除 Redis 容器

完成驗證後,可以停止並移除 Redis 容器:

$ docker stop <container_id>
$ docker rm <container_id>

Docker 資料持久化與 Redis 服務佈署

在 Docker 環境中執行資料函式庫服務時,將其視為由 Docker 管理的服務(或微服務)至關重要。容器本質上應為短暫的,能夠隨時啟動、停止和丟棄。若將資料儲存在容器內,每次容器重啟都會導致資料遺失。因此,資料持久化是確保資料在容器生命週期外依然存在的關鍵。

Docker 資料卷(Data Volumes)

Docker 資料卷是一種專門設計用於持久化資料的容器。透過將資料卷掛載到容器上,可以確保資料在容器刪除或重啟後仍然存在。這使得資料捲成為管理有狀態服務(如資料函式庫)的理想選擇。

建立與檢視資料卷

首先,建立一個新的資料卷,用於儲存 Redis 資料。

$ docker volume create --name redis-dbstore
redis-dbstore

使用 docker volume ls 命令可以檢視目前所有的資料卷。

$ docker volume ls
DRIVER VOLUME NAME
local redis-dbstore

啟動 Redis 服務並掛載資料卷

將建立的資料卷掛載到 Redis 容器,並以分離模式執行。

$ docker run -d --name this_redis -v redis-dbstore:/data redis
b216a67caedc934b09341cf1642e89079be09d52b607ce4ddecdeaae5b5ae704

驗證資料持久化

透過 redis-cli 在 Redis 容器中建立一個計數器,並驗證資料是否持久化。

$ docker exec this_redis redis-cli incr mycounter
1
$ docker exec this_redis redis-cli incr mycounter
2
$ docker exec this_redis redis-cli incr mycounter
3

停止並刪除容器後,再次啟動新的 Redis 容器,並掛載相同的資料卷。

$ docker stop this_redis && docker rm this_redis
this_redis
this_redis

$ docker run -d --name this_redis -v redis-dbstore:/data redis
12fe7cea2e63aa2055585fd97b6b9205774a59bbf716672f71e5d75858c7cd72

$ docker exec this_redis redis-cli incr mycounter
4

結果顯示計數器值為 4,證明資料已成功持久化。

連線容器

在同一主機上執行多個容器時,可以使用 --link 標誌來連線它們。雖然 --link 已被棄用,但仍可用於簡單的容器連線。

啟動一個新的 Jupyter 容器,並連結到正在執行的 Redis 容器。

$ docker run -d -v `pwd`:/home/jovyan --link this_redis jupyter/scipy-notebook
d6f09196bf85861df23eeb2f11bd68396287464d00febe27cda93024a3666251

進入 Jupyter 容器並檢查環境變數。

$ docker exec -it d6f0 bash
jovyan@d6f09196bf85:~$ env | grep THIS_REDIS
THIS_REDIS_PORT_6379_TCP=tcp://172.17.0.2:6379
THIS_REDIS_NAME=/determined_wilson/this_redis
THIS_REDIS_PORT=tcp://172.17.0.2:6379
THIS_REDIS_PORT_6379_TCP_PORT=6379
THIS_REDIS_ENV_REDIS_VERSION=3.2.8
THIS_REDIS_PORT_6379_TCP_PROTO=tcp
THIS_REDIS_ENV_GOSU_VERSION=1.7
THIS_REDIS_ENV_REDIS_DOWNLOAD_SHA1=6780d1abb66f33a97aad0ebbe020403d0a15b67f
THIS_REDIS_ENV_REDIS_DOWNLOAD_URL=http://download.redis.io/releases/redis-3.2.8.tar.gz
THIS_REDIS_PORT_6379_TCP_ADDR=172.17.0.2

使用環境變數中的 IP 位址對 Redis 容器進行 ping 測試。

jovyan@d6f09196bf85:~$ ping -c 4 $THIS_REDIS_PORT_6379_TCP_ADDR
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: icmp_seq=0 ttl=64 time=0.470 ms
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.136 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.120 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.085 ms
---
 172.17.0.2 ping statistics 
---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max/stddev = 0.085/0.203/0.470/0.155 ms

在 Jupyter 中使用 Redis

預設的 jupyter/scipy-notebook 映像檔不包含 Redis Python 程式函式庫。可以在執行中的容器中臨時安裝 Redis 程式函式庫,以便在 Jupyter Notebook 中使用 Redis。

# 在 Jupyter 容器中安裝 redis 程式函式庫(這裡省略具體安裝步驟,通常使用 pip 安裝)
# pip install redis

# 在 Jupyter Notebook 中連線 Redis 伺服器範例程式碼:
import redis

# 使用環境變數連線 Redis
redis_client = redis.Redis(host=os.environ['THIS_REDIS_PORT_6379_TCP_ADDR'], port=6379, db=0)

# 設定和取得一個值
redis_client.set('key', 'value')
print(redis_client.get('key'))

圖表翻譯:

此圖示展示了Redis作為持久化服務被Docker管理的過程。Docker透過資料卷實作了Redis資料的持久化,使得Redis服務能夠在容器重啟或刪除後仍然保留資料。

隨著容器技術的不斷發展,未來將會有更多高效、便捷的方式來管理和協調多容器應用。例如,Docker Compose 和 Kubernetes 等工具已經成為業界標準,用於簡化多容器應用的佈署和管理。未來,我們可以期待更多創新技術的出現,以進一步簡化容器的使用和管理。

程式碼最佳化與安全性考量

在實際應用中,應考慮對程式碼進行最佳化,以提高效能和安全性。例如,可以使用更高效的連線池管理 Redis 連線,或是對敏感資料進行加密處理。此外,還應定期更新和修補所使用的映像檔和程式函式庫,以防止潛在的安全漏洞。

程式碼範例:使用連線池管理 Redis 連線

import redis

# 建立 Redis 連線池
pool = redis.ConnectionPool(host=os.environ['THIS_REDIS_PORT_6379_TCP_ADDR'], port=6379, db=0)

# 使用連線池建立 Redis 客戶端
redis_client = redis.Redis(connection_pool=pool)

# 設定和取得一個值
redis_client.set('key', 'value')
print(redis_client.get('key'))

Redis 連線池架構圖

@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333

title Redis 連線池架構圖

rectangle "請求" as node1
rectangle "分配連線" as node2
rectangle "處理請求" as node3
rectangle "傳回結果" as node4

node1 --> node2
node2 --> node3
node3 --> node4

@enduml

圖表翻譯: 此圖示展示了應用程式如何透過 Redis 連線池與 Redis 伺服器進行互動。連線池負責管理與 Redis 的連線,從而提高應用的效能和可擴充套件性。