此作業完整的程式碼有放在Github上。

一、作業內容

以下為老師作業的題目:

  1. 透過任一IDE啟動一個pipenv的專案資料夾,並在pipfile內安裝pymongo套件。

  2. 建置docker-compose.yml,裡面需有mongo資料庫,且帶有ports,volume, container_name 及官方所必須的Environment。

  3. 編寫一個app.py,裡面需要對mongo的collection(資料表)的document(文件),進行CRUD等核心操作。

為能夠做更多Docker練習,此處我將第一點的Python環境,由本機端改為在docker-compose.yml內以Jupyter Notebook建立。

二、作業執行環境說明

  • 採用VMware Workstation 16 Pro建立虛擬機器
  • 主體作業系統:Windows 10
  • 客體作業系統:CentOS7

Docker部署在虛擬機CentOS7作業系統上。

三、編寫docker-compose.yml

1. MongoDB

首先我到Docker Hub內尋找有沒有MongoDB的Image,經查詢官方有提供MongoDB的Image(點我連結)。

官方文件的docker-compose.yml範例如下:

# Use root/example as user/password credentials
version: '3.1'

services:

  mongo:
    image: mongo
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example

  mongo-express:
    image: mongo-express
    restart: always
    ports:
      - 8081:8081
    environment:
      ME_CONFIG_MONGODB_ADMINUSERNAME: root
      ME_CONFIG_MONGODB_ADMINPASSWORD: example

若按官方提供的文件,會多安裝mongo-express,這個軟體是讓使用者可透過前端畫面來操作mongoDB資料庫,但這邊我們不需要,所以我將官方的docker-compose.yml修改如下:

version: '3.1'
services:
  mongo:
    image: mongo
    container_name: mongo 
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
    ports:
      - 27017:27017
    volumes:
      - ./mongo_db:/data/db

主要修改有:

  1. 拿掉mongo-express的部分
  2. 增加port設定
  3. 增加volume設定
    • ./mongo_db:/data/db:主要是儲存mongoDB的資料庫,參考自官網的Where to Store Data章節說明

在environment部分,MONGO_INITDB_ROOT_USERNAME為root的帳號,MONGO_INITDB_ROOT_PASSWORD為root的密碼。

上述的docker-compose.yml即可建立mongoDB且可順利連線。

但此處我不想要直接以root權限來連接資料庫,想要建立其他帳號讓Python連線,並且我想讓建立帳號這件事情,讓Docker在部署時可以直接幫我設置好。參考官網的Initializing a fresh instance章節說明,要先撰寫程式腳本.js.sh檔案,放到容器內的docker-entrypoint-initdb.d資料夾底下,這樣Docker在部署時即會按照腳本進行相關設定。

此處我撰寫的js腳本內容新增一個user叫做user1,對world資料庫具有讀寫權限:

db.createUser(
        {
            user: 'user1',
            pwd: 'user1pw',
            roles: [{
                    role: 'readWrite',
                    db: 'world'
                }
            ]
        }
);

MongoDB創立使用者的roles設定寫法可參考MongoDB官網說明手冊(點此連結)。

而在docker-compose.yml內,需要在environment部分多設定一個環境變數MONGO_INITDB_DATABASE: user1,並且在volumes部分將剛寫好的js檔案放到容器內。

version: '3.1'
services:
  mongo:
    image: mongo
    container_name: mongo 
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
      MONGO_INITDB_DATABASE: world
    ports:
      - 27017:27017
    volumes:
      - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js
      - ./mongo_db:/data/db

設定好後執行,即可在剛部署好的MongoDB內,直接在world資料庫底下,使用user1帳號登入。

  • 其他參考資料:

  • 踩到的坑:

    • 在測試不同的腳本前,需要先把volumes在本地端建立的資料庫資料夾(以我這邊的設定為例是mongo_db)刪除,否則mongoDB會直接沿用資料庫資料夾的內容直接啟用,意即忽略初始化腳本,直接沿用舊的設定。

2. Jupyter Notebook(Python)

Jupyter Notebook的docker-compose.yml則參考自老師上課講義,此處為節省篇幅只先將Jupyter的部分列出來:

version: '3.1'
services:
  jupyter:
    build:
      context: ./dockerfile
      dockerfile: dockerfile-jupyter
    container_name: jupyter
    restart: always
    tty: true
    stdin_open: true
    depends_on:
      - mongo
    ports:
      - 8888:8888
    volumes:
      - ./code:/home/jovyan/work
    command: start.sh jupyter notebook --NotebookApp.token=''

此處是用dockerfile客製化映像檔,主要是安裝pymongo套件,dockerfile-jupyter的內容如下:

FROM jupyter/base-notebook
RUN pip install pymongo
  • 踩到的坑:

在volumes設定部分,我是將本機端的code資料夾映射到容器內的/home/jovyan/work資料夾。如果本機端沒有先建立好code資料夾,則在執行Jupyter時,於work資料夾內新增python檔案時,會發生下圖的錯誤:

這是因為如果Jupyter若偵測不到本地端有code資料夾,便會以root身份建立,導致非root使用者無法寫入此資料夾。解法有兩個:一個是直接對該資料夾調整擁有者(chown指令),第二個是在docker-compose前先行建立好資料夾,這樣資料夾的擁有者就會是使用者的。

3. 部署架構彙整

  • Docker部署前資料夾內容:

其中code資料夾底下有一個main.ipynb檔案,這是待會要在Jupyter內用Python操作MongoDB的程式碼。

  • 完整的docker-compose.yml:
version: '3.1'
services:
  mongo:
    image: mongo
    container_name: mongo 
    restart: always
    environment:
      MONGO_INITDB_ROOT_USERNAME: root
      MONGO_INITDB_ROOT_PASSWORD: example
      MONGO_INITDB_DATABASE: world
    ports:
      - 27017:27017
    volumes:
      - ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js
      - ./mongo_db:/data/db
      
  jupyter:
    build:
      context: ./dockerfile
      dockerfile: dockerfile-jupyter
    container_name: jupyter
    restart: always
    tty: true
    stdin_open: true
    depends_on:
      - mongo
    ports:
      - 8888:8888
    volumes:
      - ./code:/home/jovyan/work
    command: start.sh jupyter notebook --NotebookApp.token=''

4. 開始執行部署

在docker-compose.yml檔案路徑下,執行linux指令:

# 啟動docker-compose開始部署
docker-compose up -d

# 執行後 查詢docker運行狀況
docker ps -a

執行成功的畫面如下圖所示:

四、Jupyter內執行MongoDB的CRUD操作

Docker於虛擬機部署好後,接下來在主體作業系統(Windows環境),輸入虛擬機IP及Port連入Jupyter Notebook。

接下來進入到work資料夾內,打開main.py檔案,即可開始測試pymongo套件

1. 建立連線

由於在部署時已有安裝pymongo套件,此處就不需要在進行安裝,可以直接載入套件。

另外在登入帳號時,不是用root帳號,而是在docker部署時,自動幫我們讀取mongoDB初始化腳本(mongo-init.js)建立好的user1帳號。

# 讀取套件
from pymongo import MongoClient

# 連線相關設定
host = 'mongo'
port = '27017'
user = 'user1'
passwd = 'user1pw'
dbName = 'world'

# 建立mongoDB連線
client = MongoClient('mongodb://' + user + ':' + passwd + '@' + host + ':' + port + '/' + dbName)

# 連入資料庫
db = client['world']
collection = db['people']

2. Insert(新增)

# 匯入資料(單筆匯入寫法)
for i in range(100):
    doc = {"age": i, "name": "user_" + str(i)}
    collection.insert_one(doc)

# 匯入資料(多筆匯入寫法)
docs = list()
for i in range(100):
    docs.append({"age": i, "name": "user_" + str(i)})
collection.insert_many(docs)

# 查詢資料表總資料數
collection.estimated_document_count()

3. Select(查詢)

# 查詢資料
docs = collection.find({"age": {"$gt":95}})
for doc in docs:
    print(doc)

4. Update(修改)

# 修改資料
targetDocs = {"age": 0}
newValues = {"$set": {"age": 100}}
result = collection.update_many(targetDocs, newValues)
print(result.modified_count, " documents updated.")

# 確認是否有修改成功
docs = collection.find({"age": 100})
for doc in docs:
    print(doc)

5. Delete(刪除)

# 刪除指定資料
collection.delete_many({"age":{"$gt":95}})

# 確認是否有刪除
collection.count_documents({"age":{"$gt":95}})

# 刪除所有資料
collection.drop()

# 確認是否有刪除
collection.estimated_document_count()

6. 指令參考來源

pymongo套件的操作指令主要參考兩個來源:

  1. w3schools-Python MongoDB
  2. pymongo套件官方手冊

五、心得

本篇作業筆記說明如何以docker-compose部署MongoDB與Jupyter,並在Jupyter內以Python程式碼對MongoDB進行「增刪改查」。

這次的作業讓我對於Docker的部署流程更加熟悉,了解docker-compse.yml的撰寫方式和架構,也學習到一些要注意的細節。

在MongoDB部分,藉此作業熟悉基本的「增刪改查」操作。且上課時老師並未說明如何建立使用者帳號以及權限的設定,這部分是靠自己去Google學習,並應用到Docker內。

這份作業進行很多次的測試,在每次測試錯誤的結果中去找出解法,每找出一次錯誤的解決方式,就感覺自己功力又更進一步。程式要進步果然是要從實作中學習最快!