docker 之間無限重建的小圈圈
我覺得我快受不了 docker images 之間的銜接了……就不能讓我好好寫程式嗎?
composing, sigh
這次的任務是讓我的 expressJS app 接上我的 mongoDB。盡可能不要裝 npm 套件。
首先是建資料庫。在 mongoDB 內部,事情還好解決:
version: "3.8"
services:
mongo:
image: mongo
restart: always
ports:
- "27017:27017"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
# Use root/example as user/password credentials
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: example
ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/
ME_CONFIG_BASICAUTH: false
這可以讓 mongoDB 有個預覽用的地方能看。但到要接上 expressJS app 就是麻煩了……
## 省略
ME_CONFIG_BASICAUTH: false
express-app:
build: ./app
ports:
- "3000:3000"
depends_on:
- mongo
volumes:
- "./app:/usr/src/app"
environment:
APP_MONGO_URL: mongodb://root:example@mongo:27017/
Dockerfile
首先我光要連接就是困難:
# app/Dockerfile
FROM node:latest
WORKDIR /usr/src/app
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "app.js"]
然後說找不到 package.json
也用不到 app.js
檔案……但事實上我的 entry 檔案就是 index.js
啊。看來不能隨意複製貼上,不過我也沒別的辦法了。
改成 npm start
卻發現沒有動,又卡了很久。查了一下,才發現改了 Dockerfile
後需要 docker-compose build
再 docker-compose up -d
才能更新設定。
難道不能做在一起嗎?
檔案反應
接著是 expressJS app ……每次改 expressJS app 程式後,必須重開 npm start
才可以反應。不能像 rails 的 rails/bin server
或 Laravel 的 php artisan serve
那樣一聽就改動。
怎麼辦呢?你需要 nodemon 完成。也就是:
$ npm install -g nodemon
$ nodemon npm start
我不想再裝東西了欸。饒了我吧。
env
然後連資料庫呢:
import express from "express";
import mongoose from "mongoose";
const app = express();
const port = 3000;
app.get("/api", async (req, res) => {
console.log(process.env.APP_MONGO_URL);
const list = await mongoose.connect(process.env.APP_MONGO_URL);
});
結果程式說找不到。原來是因為 code 不會讀 .env 檔。還需要 dotenv 才可以。
我不想裝,好險 docker 的 environment
可以直接用 env 參數。
但來是有問題。
2024-04-21 22:20:23 "mongodb://root:example@mongo:27017/test2"
2024-04-21 22:20:23 Error fetching data: MongoParseError: Invalid scheme, expected connection string to start with "mongodb://" or "mongodb+srv://"
2024-04-21 22:20:23 at new ConnectionString (/usr/src/app/node_modules/mongodb-connection-string-url/lib/index.js:86:19)
2024-04-21 22:20:23 at parseOptions (/usr/src/app/node_modules/mongodb/lib/connection_string.js:192:17)
2024-04-21 22:20:23 at new MongoClient (/usr/src/app/node_modules/mongodb/lib/mongo_client.js:52:63)
2024-04-21 22:20:23 at NativeConnection.createClient (/usr/src/app/node_modules/mongoose/lib/drivers/node-mongodb-native/connection.js:293:14)
2024-04-21 22:20:23 at NativeConnection.openUri (/usr/src/app/node_modules/mongoose/lib/connection.js:801:34)
2024-04-21 22:20:23 at Mongoose.connect (/usr/src/app/node_modules/mongoose/lib/mongoose.js:414:15)
2024-04-21 22:20:23 at file:///usr/src/app/index.js:22:37
2024-04-21 22:20:23 at Layer.handle [as handle_request] (/usr/src/app/node_modules/express/lib/router/layer.js:95:5)
2024-04-21 22:20:23 at next (/usr/src/app/node_modules/express/lib/router/route.js:149:13)
2024-04-21 22:20:23 at Route.dispatch (/usr/src/app/node_modules/express/lib/router/route.js:119:3)
告訴我為什麼是錯的,好嗎?想也知道不行。
於是就這樣一直卡、一直卡、直到放棄。
為什麼 Docker 之間的 Images 這麼難連起來?
從新粗花
休息一下以後,我再度與 mondo 奮戰。這次我決定先砍掉 express-app
這個 image,只留下資料庫與前端 image 後給 mongo 開個 port,希望把麻煩弄少點。
version: "3.8"
services:
mongo:
image: mongo
restart: always
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
# Use root/example as user/password credentials
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: example
ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/
ME_CONFIG_BASICAUTH: false
import express from "express";
import mongoose from "mongoose";
const app = express();
const port = 3000;
app.get("/api", async (req, res) => {
const list = await mongoose.connect("mongodb://root:example@127.0.0.1:27017/testdb");
});
可還是錯: MongoError: bad auth Authentication failed
──這怎搞的?我 root 還不行?
欸還真不行。因為 testdb
裡面並沒有這個用戶。
「豈有此理……」我懷著怨氣前往 docker 裡面 mongo image 的 exec 操作 mondo 的資料庫授權:
$ mongosh
$ MongoServerError[Unauthorized]: Command createUser requires authentication
$ mongosh --authenticationDatabase admin --username root --password example
$ use testdb
$ db.createUser({ user: "myuser", pwd: "mypassword", roles: [{ role: "readWrite", db: "testdb" }] });
$ exit
可以是可以了……但不覺得這麼作,太不切實際了嗎?誰會在 Docker 搞這麼麻煩?
然後我看到 Stack Overflow 這篇文章: How to create user in mongodb with docker-compose
結果才發現可以用 volumes
建立用戶:
// init-mongo.js
db.createUser({ user: "myuser", pwd: "mypassword", roles: [{ role: "readWrite", db: "testdb" }] });
version: "3.8"
services:
mongo:
image: mongo
restart: always
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: example
MONGO_INITDB_DATABASE: testdb
volumes:
- ./init-mongo.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
mongo-express:
image: mongo-express
restart: always
ports:
- 8081:8081
environment:
# Use root/example as user/password credentials
ME_CONFIG_MONGODB_ADMINUSERNAME: root
ME_CONFIG_MONGODB_ADMINPASSWORD: example
ME_CONFIG_MONGODB_URL: mongodb://root:example@mongo:27017/
ME_CONFIG_BASICAUTH: false
「原來你還有一個 mongo-init.js
喔……」我嘆了嘆氣,這算什麼啊……