[docker] 网络连接
使用 docker 容器会遇到下面 3 种情况:
-
容器与万维网之间的交流
默认情况下是允许的
-
从容器到本机的交流
假设有一些服务运行在本机,如数据库,而 docker 需要和本机进行交流去获取数据
这个实现需要配置
-
容器与容器之间的交流
这个是比较常见的用法了,同样需要配置
项目配置
配置一个 node 项目去运行一下项目
代码
app.js:
const express = require("express");
const bodyParser = require("body-parser");
const axios = require("axios").default;
const mongoose = require("mongoose");
const Favorite = require("./models/favorite");
const app = express();
app.use(bodyParser.json());
app.get("/favorites", async (req, res) => {
const favorites = await Favorite.find();
res.status(200).json({
favorites: favorites,
});
});
app.post("/favorites", async (req, res) => {
const favName = req.body.name;
const favType = req.body.type;
const favUrl = req.body.url;
try {
if (favType !== "movie" && favType !== "character") {
throw new Error('"type" should be "movie" or "character"!');
}
const existingFav = await Favorite.findOne({ name: favName });
if (existingFav) {
throw new Error("Favorite exists already!");
}
} catch (error) {
return res.status(500).json({ message: error.message });
}
const favorite = new Favorite({
name: favName,
type: favType,
url: favUrl,
});
try {
await favorite.save();
res
.status(201)
.json({ message: "Favorite saved!", favorite: favorite.toObject() });
} catch (error) {
res.status(500).json({ message: "Something went wrong." });
}
});
app.get("/movies", async (req, res) => {
try {
const response = await axios.get("https://swapi.dev/api/films");
res.status(200).json({ movies: response.data });
} catch (error) {
res.status(500).json({ message: "Something went wrong." });
}
});
app.get("/people", async (req, res) => {
try {
const response = await axios.get("https://swapi.dev/api/people");
res.status(200).json({ people: response.data });
} catch (error) {
res.status(500).json({ message: "Something went wrong." });
}
});
mongoose.connect(
"mongodb://localhost:27017/swfavorites",
{ useNewUrlParser: true },
(err) => {
if (err) {
console.log(err);
} else {
app.listen(3000);
}
}
);
下面这个是 mongoose 的配置:
const { Schema, model } = require("mongoose");
const favoriteSchema = new Schema({
type: String, // 'movie' | 'character'
name: String,
url: String,
});
const Favorite = model("Favorite", favoriteSchema);
module.exports = Favorite;
Dockerfile:
FROM node
WORKDIR /app
COPY package.json .
RUN npm install
COPY . .
CMD ["node", "app.js"]
简单解释一下这个代码就是,它会创立几个 endpoints,有两个 endpoints 会直接与进行沟通 https://swapi.dev/api/films,获取数据。还会有两个 endpoints 与 mongodb 进行沟通,进行 POST 和 GET 的 request
其中 https://swapi.dev/api/films 是一个 dummy API endpoint,是别人在网上 host 的:
而 mongodb 则是本机上进行安装,或者使用 docker 容器进行实现
下面会提一下怎么配置本机,但是这里只会运行容器与容器之间的沟通
docker build
build 过程会报错:
❯ docker build -t favorite-app .
[+] Building 7.3s (10/10)
❯ docker run --name favorites --rm -p 3000:3000 favorite-app
(node:1) [MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
(Use `node --trace-warnings ...` to show where the warning was created)
MongoNetworkError: failed to connect to server [localhost:27017] on first connect [Error: connect ECONNREFUSED 127.0.0.1:27017
at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1605:16) {
name: 'MongoNetworkError'
}]
at Pool.<anonymous> (/app/node_modules/mongodb/lib/core/topologies/server.js:441:11)
at Pool.emit (node:events:519:28)
at /app/node_modules/mongodb/lib/core/connection/pool.js:564:14
at /app/node_modules/mongodb/lib/core/connection/pool.js:1000:11
at /app/node_modules/mongodb/lib/core/connection/connect.js:32:7
at callback (/app/node_modules/mongodb/lib/core/connection/connect.js:300:5)
at Socket.<anonymous> (/app/node_modules/mongodb/lib/core/connection/connect.js:330:7)
at Object.onceWrapper (node:events:634:26)
at Socket.emit (node:events:519:28)
at emitErrorNT (node:internal/streams/destroy:169:8)
at emitErrorCloseNT (node:internal/streams/destroy:128:3)
at process.processTicksAndRejections (node:internal/process/task_queues:82:21)
出错的方式可能有两个:
-
本机没有安装 mongodb
-
安装了但是没有配置好
这里将 mongodb 连接的部分注释掉,重新运行一下:
发现 docker 容器和 https://swapi.dev/api/films 的交流沟通是没有任何问题的
运行 mongodb 镜像
这里运行一下结果就好了:
❯ docker run -d --name mongodb mongo
# skip downloading process
Status: Downloaded newer image for mongo:latest
fb63b699a8ed81852c67057c3485ee4698be1437c3e6bef2bc3c87a1eca9a810
本机与容器交流
这里只要修改代码就好了:
mongoose.connect(
"mongodb://host.docker.internal:27017/swfavorites",
{ useNewUrlParser: true },
(err) => {
if (err) {
console.log(err);
} else {
app.listen(3000);
}
}
);
将 localhost
改成 host.docker.internal
即可
容器之间的沟通
这个有两种方法
直接沟通
第一个直接获取容器的 IP 地址:
# checked the exposed IP address by mongo
❯ docker container inspect mongodb | grep "IPAddress"
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
然后更新代码:
mongoose.connect(
"mongodb://172.17.0.2:27017/swfavorites",
{ useNewUrlParser: true },
(err) => {
if (err) {
console.log(err);
} else {
app.listen(3000);
}
}
);
这个缺点在于每次重新运行容器的时候,IP 地址可能会出现改变,因此就需要修改代码,再重新 build
使用 docker networks
使用 network 可以让 docker 去管理 IP 地址,而非需要手动管理,这里第一步需要先通过 --network <network_name>
去创建一个 network
和 volume 不一样,docker 没有办法在容器运行的时候自动创建 network,所以先创建一个 network 是非常重要的事情
在 network 创建了之后,docker 会自动找寻连接在当前 network 上的容器并完成沟通
重新配置 mongo
如果在没有创建 network 的情况下直接运行 --network
就会报错:
❯ docker run -d --rm --name mongodb --network favorites-net mongo
60cdc3029a12b8e6af46728ce648598fa987977df2e8bf9f729596436266c24b
docker: Error response from daemon: network favorites-net not found.
所以还是需要手动先创立一个 network:
❯ docker network create favorites-net
f9385f787df37b608c6bc8bfb4619ff979c44312ee1f886965e17551dbde5d26
❯ docker network ls
NETWORK ID NAME DRIVER SCOPE
624223e7a219 bridge bridge local
f9385f787df3 favorites-net bridge local
e0b7d35ecfa6 host host local
6592d848be44 none null local
❯ docker run -d --rm --name mongodb --network favorites-net mongo
ac07e3660d2f04c515b7f635c37ae3e3e728f1c09c8afa9dfa0d64eb8e4cfe93
❯ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ac07e3660d2f mongo "docker-entrypoint.s…" 11 seconds ago Up 10 seconds 27017/tcp mongodb
❯ docker container inspect mongodb | grep "net"
"NetworkMode": "favorites-net",
"SandboxKey": "/var/run/docker/netns/29cafc1c1f7a",
"favorites-net": {
更新 server 代码连接 network
这里的变化是把 ip 地址改成容器名称:
mongoose.connect(
"mongodb://mongodb:27017/swfavorites",
{ useNewUrlParser: true },
(err) => {
if (err) {
console.log(err);
} else {
app.listen(3000);
}
}
);
随后重新 build 和运行:
❯ docker run --name favorites --rm --network favorites-net -p 3000:3000 favorite-app
(node:1) [MONGODB DRIVER] Warning: Current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
(Use `node --trace-warnings ...` to show where the warning was created)
最终可以完成 CRUD 的操作: