问题描述
正在学习Docker,并尝试使用Docker发布一个非常简单的Web应用程序,其中包含两个容器:后端和前端。用户希望在最终发布时,不要将后端的端口暴露给主机。然而,由于这个原因,前端无法从后端获取数据。
以下是用户的前端index.html文件:
<html>
<head>
<title>Hello Public World</title>
</head>
<body>
<h1>Message from backend:</h1>
<p id="backend"></p>
<script>
const back = document.getElementById("backend");
const url = "http://localhost:3333/"; //I tried backend:3333, but without success
fetch(url)
.then((resp) => resp.json()) // Transform the data into json
.then(function (data) {
back.innerHTML = data.message;
});
</script>
</body>
</html>
用户的Dockerfile如下:
# pull official base image
FROM node:lts
# set working directory
WORKDIR /usr/src/app
# install app dependencies
COPY package.json ./
RUN npm install
# add app
COPY . ./
EXPOSE 3000
# start app
CMD ["npm", "run", "start"]
用户的后端index.js文件如下:
const express = require("express");
const app = express();
const cors = require("cors");
const port = 3333;
const allowlist = ["http://localhost:3000"];
const corsOptions = {
origin: allowlist,
methods: "GET",
preflightContinue: false,
optionsSuccessStatus: 204,
};
app.use(cors(corsOptions));
app.use(express.json());
app.get("/", (req, res) => {
console.log("GET /");
res.json({ message: "Hello Private World!" });
});
app.listen(port, () =>
console.log(`Example app listening at http://localhost:${port}`)
);
用户的Dockerfile如下:
# pull official base image
FROM node:lts
# set working directory
WORKDIR /usr/src/app
# install app dependencies
COPY package.json ./
RUN npm install
# add app
COPY . ./
#EXPOSE 3333
# start app
CMD ["npm", "run", "start"]
用户还提供了Docker Compose文件:
version: "3.8"
services:
backend:
image: helloworld-private
build:
context: ./backend
dockerfile: Dockerfile
working_dir: /usr/src/app
container_name: "express_container"
volumes:
- ./backend/:/usr/src/app
- /usr/src/app/node_modules
networks:
- webappnetwork
expose:
- "3333"
web:
image: helloworld-public
build:
context: ./web
dockerfile: Dockerfile
working_dir: /usr/src/app
container_name: "liveserver_container"
links:
- backend
volumes:
- ./web/:/usr/src/app
- /usr/src/app/node_modules
environment:
- PORT=3000
networks:
- webappnetwork
ports:
- "3000:3000"
networks:
webappnetwork:
driver: bridge
用户想知道是否有任何抽象层次的东西他忽略了。
解决方案
请注意以下操作注意版本差异及修改前做好备份。
方案1
在你的情况下,你需要使用nginx代理来隐藏后端。以下是一种解决方案:
1. 在你的前端应用中,定义一个用于后端API的位置,例如/api
。
2. 使用nginx代理将该位置的请求转发到后端容器。
3. 将静态资源(前端应用)和API代理部署在同一个nginx中,以提高性能。
4. 使用Dockerfile定义环境变量和默认值,例如:
ENV API_URL=http://backend:3333
- 在你的docker-compose.yaml或k8s定义中,通过环境变量更新配置。
以下是一个示例nginx配置文件,用于将/api
请求代理到后端容器:
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html;
}
location /api {
proxy_pass http://backend:3333;
}
}
请注意,上述示例中的backend
是后端容器的服务名称,你需要根据你的实际情况进行调整。
方案2
另一种方法是使用Traefik作为反向代理。Traefik是一个功能强大的反向代理和负载均衡器,可以轻松地将请求路由到不同的容器。以下是一种解决方案:
1. 在你的docker-compose.yaml文件中,将Traefik作为一个服务添加到你的网络中。
2. 在你的前端和后端服务中,使用Traefik的标签来定义路由规则和后端服务。
3. 配置Traefik以将请求路由到正确的服务。
以下是一个示例docker-compose.yaml文件,使用Traefik作为反向代理:
version: "3.8"
services:
traefik:
image: traefik:v2.4
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
backend:
image: helloworld-private
labels:
- "traefik.enable=true"
- "traefik.http.routers.backend.rule=Host(`yourdomain.com`) && PathPrefix(`/api`)"
- "traefik.http.services.backend.loadbalancer.server.port=3333"
web:
image: helloworld-public
labels:
- "traefik.enable=true"
- "traefik.http.routers.web.rule=Host(`yourdomain.com`) && PathPrefix(`/`)"
- "traefik.http.services.web.loadbalancer.server.port=3000"
请注意,上述示例中的yourdomain.com
是你的域名,你需要将其替换为你自己的域名。
方案3
如果你不想使用nginx或Traefik,你还可以使用其他反向代理工具,如Apache HTTP Server或Caddy。这些工具都提供了类似的功能,可以将请求路由到不同的容器。
你可以根据你的喜好和需求选择适合你的工具,并按照它们的文档进行配置。
总结
在你的情况下,你可以使用nginx、Traefik或其他反向代理工具来隐藏后端。这些工具可以将请求路由到正确的容器,并提供更好的性能和安全性。你可以根据你的需求选择适合你的工具,并按照它们的文档进行配置。