# Docker作業一筆記 2021/01/29 蘇彥庭 此作業完整的程式碼有放在[Github](https://github.com/SuYenTing/Docker_hw/tree/main/hw1)上。 ## 一、作業內容 以下為老師作業的題目: >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](https://hub.docker.com/)內尋找有沒有MongoDB的Image,經查詢官方有提供MongoDB的Image([點我連結](https://hub.docker.com/_/mongo))。 官方文件的docker-compose.yml範例如下: ```yaml= # 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修改如下: ```yaml= 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資料庫具有讀寫權限: ```javascript= db.createUser( { user: 'user1', pwd: 'user1pw', roles: [{ role: 'readWrite', db: 'world' } ] } ); ``` MongoDB創立使用者的roles設定寫法可參考MongoDB官網說明手冊([點此連結](https://docs.mongodb.com/manual/reference/method/db.createUser/#examples))。 而在docker-compose.yml內,需要在environment部分多設定一個環境變數`MONGO_INITDB_DATABASE: user1`,並且在volumes部分將剛寫好的js檔案放到容器內。 ```yaml= 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帳號登入。 * 其他參考資料: * [stackoverflow: How to create a DB for MongoDB container on start up?](https://stackoverflow.com/questions/42912755/how-to-create-a-db-for-mongodb-container-on-start-up/42917632#42917632) * 踩到的坑: * 在測試不同的腳本前,需要先把`volumes`在本地端建立的資料庫資料夾(以我這邊的設定為例是`mongo_db`)刪除,否則mongoDB會直接沿用資料庫資料夾的內容直接啟用,意即忽略初始化腳本,直接沿用舊的設定。 ### 2. Jupyter Notebook(Python) Jupyter Notebook的docker-compose.yml則參考自老師上課講義,此處為節省篇幅只先將Jupyter的部分列出來: ```yaml= 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的內容如下: ```dockerfile= FROM jupyter/base-notebook RUN pip install pymongo ``` * 踩到的坑: 在volumes設定部分,我是將本機端的code資料夾映射到容器內的/home/jovyan/work資料夾。如果本機端沒有先建立好code資料夾,則在執行Jupyter時,於work資料夾內新增python檔案時,會發生下圖的錯誤: ![](https://i.imgur.com/bRMHzHY.png) 這是因為如果Jupyter若偵測不到本地端有code資料夾,便會以root身份建立,導致非root使用者無法寫入此資料夾。解法有兩個:一個是直接對該資料夾調整擁有者(`chown`指令),第二個是在`docker-compose`前先行建立好資料夾,這樣資料夾的擁有者就會是使用者的。 ### 3. 部署架構彙整 * Docker部署前資料夾內容: ![](https://i.imgur.com/z2Tvb9B.png) 其中code資料夾底下有一個`main.ipynb`檔案,這是待會要在Jupyter內用Python操作MongoDB的程式碼。 * 完整的docker-compose.yml: ```yaml= 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指令: ```bash= # 啟動docker-compose開始部署 docker-compose up -d # 執行後 查詢docker運行狀況 docker ps -a ``` 執行成功的畫面如下圖所示: ![](https://i.imgur.com/M4E0aJJ.png) ## 四、Jupyter內執行MongoDB的CRUD操作 Docker於虛擬機部署好後,接下來在主體作業系統(Windows環境),輸入虛擬機IP及Port連入Jupyter Notebook。 接下來進入到work資料夾內,打開`main.py`檔案,即可開始測試`pymongo套件`。 ![](https://i.imgur.com/Z0SIZYy.png) ### 1. 建立連線 由於在部署時已有安裝pymongo套件,此處就不需要在進行安裝,可以直接載入套件。 另外在登入帳號時,不是用root帳號,而是在docker部署時,自動幫我們讀取mongoDB初始化腳本(`mongo-init.js`)建立好的user1帳號。 ```python # 讀取套件 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(新增) ```python= # 匯入資料(單筆匯入寫法) 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(查詢) ```python= # 查詢資料 docs = collection.find({"age": {"$gt":95}}) for doc in docs: print(doc) ``` ### 4. Update(修改) ```python= # 修改資料 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(刪除) ```python= # 刪除指定資料 collection.delete_many({"age":{"$gt":95}}) # 確認是否有刪除 collection.count_documents({"age":{"$gt":95}}) # 刪除所有資料 collection.drop() # 確認是否有刪除 collection.estimated_document_count() ``` ### 6. 指令參考來源 pymongo套件的操作指令主要參考兩個來源: 1. [w3schools-Python MongoDB](https://www.w3schools.com/python/python_mongodb_getstarted.asp) 2. [pymongo套件官方手冊](https://pymongo.readthedocs.io/en/stable/tutorial.html) ## 五、心得 本篇作業筆記說明如何以docker-compose部署MongoDB與Jupyter,並在Jupyter內以Python程式碼對MongoDB進行「增刪改查」。 這次的作業讓我對於Docker的部署流程更加熟悉,了解docker-compse.yml的撰寫方式和架構,也學習到一些要注意的細節。 在MongoDB部分,藉此作業熟悉基本的「增刪改查」操作。且上課時老師並未說明如何建立使用者帳號以及權限的設定,這部分是靠自己去Google學習,並應用到Docker內。 這份作業進行很多次的測試,在每次測試錯誤的結果中去找出解法,每找出一次錯誤的解決方式,就感覺自己功力又更進一步。程式要進步果然是要從實作中學習最快!