Docker中Angular容器无法连接同网络Express容器问题求助
我有一个部署在Docker容器Nginx上的Angular应用,基础镜像为nginx:stable-alpine,容器名admin-users-cont,归属Docker网络tutorial-net。同时有一个Express服务器容器,基础镜像node:18.13.0,容器名server-cont,同样在tutorial-net网络中,IP为172.18.0.3,服务运行在3000端口。
Angular应用能在浏览器正常打开,但调用/getusers接口时始终挂起或超时,控制台报错:GET http://server-cont:3000/getusers net::ERR_NAME_NOT_RESOLVED。
服务器代码(index.js)
const express = require('express'); const bodyparser = require('body-parser'); const cors = require('cors'); const app = express(); const port = 3000; const mysql = require('mysql2'); app.use(bodyparser.json()); app.use(cors()); const connection = mysql.createConnection({ host: 'mysql-db-cont', port: '3306', user: 'root', password: 'password', database: 'docker-tutorial' // host: 'localhost', // port: '3307', // user: 'root', // password: 'password', // database: 'docker-tutorial' }); connection.connect((err) => { if(err) { console.error("Error connecting to MySQL Database : " + err); return; } console.log('Connected to Database.'); }); app.get('/allprofiles', (req, res) => { }); app.post('/adduser', (req, res) => { connection.query(`insert into \`docker-tutorial\`\`Employee\` (\`first-name\`, \`last-name\`, \`email\`, \`company\`) values ('${req.body.firstname}', '${req.body.lastname}', '${req.body.email}', '${req.body.company}');`, (err) => { if(err) { res.json({"message": "Error while Inserting the record."}).status(500); } else { res.json({"message": "User inserted successfully."}).status(200); } }); }); app.get('/getusers', (req, res) => { console.log("Inside get users."); connection.query(`select \`first-name\`, \`last-name\`, \`email\`, \`company\` from \`docker-tutorial\`\`Employee\`;`, (err, result) => { if(err) { console.log(err); res.json({"message": "Error while fetching the records."}).status(500); } else { console.log("Query Executed successfully\nResults : \n"); console.log(result); res.json(result).status(200); } }); }); app.listen(port, () => { console.log("Listening on Port 3000"); }); function cleanup() { connection.end(); console.log('Closing the Database connection.'); process.exit(0); } process.on('SIGINT', cleanup)
Angular列表用户组件(发送GET请求)
import { Component } from "@angular/core" import { UserService } from "../services/user.service"; @Component({ selector: 'list-users', templateUrl: './list-users.component.html', styleUrls: ['./list-users.component.css'] }) export class ListUsersComponent { users: any; constructor(private userService: UserService) { } ngOnInit() { this.getUsers(); } getUsers() { this.userService.getUsers().subscribe({ next: (res) => { this.users = res; }, error: (err) => { console.log("Some Error Occurred : "); console.error(err); }, complete: () => { console.log("Response received - User added Successfully."); } }); } }
Angular请求服务(user.service.ts)
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class UserService { private backendUrl = "http://server-cont:3000"; //private backendUrl = "http://localhost:3000"; constructor(private http: HttpClient) { } addUser(userData: any) : Observable<any> { const url = `${this.backendUrl}/adduser`; return this.http.post<any>(url, userData); } getUsers() : Observable<any> { const url = `${this.backendUrl}/getusers`; return this.http.get(url); } }
Nginx配置文件
server { listen 80; sendfile on; server_name localhost; location / { root /usr/share/nginx/html/admin-users; index index.html; try_files $uri $uri/ /index.html; } location /api { proxy_pass http://server-cont:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } }
Angular应用Dockerfile
FROM nginx:stable-alpine RUN ["rm", "/etc/nginx/conf.d/default.conf"] COPY dist /usr/share/nginx/html COPY nginx.conf /etc/nginx/conf.d/default.conf EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]
Express应用Dockerfile
FROM node:18.13.0 WORKDIR /usr/src/app COPY package*.json ./ RUN npm install COPY . . EXPOSE 3000 CMD ["npm", "start"]
容器创建命令
- Angular镜像构建:
docker build -t admin-users . - Angular容器创建:
docker create -it --name admin-users-cont -p 8080:80 --network tutorial-net admin-users - Express镜像构建:
docker build -t server . - Express容器创建:
docker create -it --name server-cont -p 3001:3000 --network tutorial-net server
tutorial-net是两个容器及MySQL容器共同归属的网络。
报错ERR_NAME_NOT_RESOLVED的核心原因是:浏览器无法解析Docker容器名server-cont——Angular代码运行在浏览器端,而非Nginx容器内部,浏览器的DNS解析无法识别Docker网络内的容器名称。
修复步骤:
修改Angular服务的后端地址
将UserService中的backendUrl改为相对路径,利用Nginx的反向代理转发请求:private backendUrl = "/api";这样Angular会把请求发送到当前域名的
/api路径,由Nginx代理到Express容器。调整Express接口路径(可选,更规范)
为了和Nginx的/api前缀对应,建议给Express的接口统一加上/api前缀,比如:app.get('/api/getusers', (req, res) => { // 原有逻辑不变 }); app.post('/api/adduser', (req, res) => { // 原有逻辑不变 });同时修改Nginx的
proxy_pass配置:location /api { proxy_pass http://server-cont:3000/api; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }重新构建并启动容器
- 重新构建Angular镜像:
docker build -t admin-users . - 重启Angular容器:
docker restart admin-users-cont - 若修改了Express代码,同样重新构建镜像并重启容器:
docker build -t server . && docker restart server-cont
- 重新构建Angular镜像:
验证
访问Angular应用,调用接口时,浏览器会发送http://localhost:8080/api/getusers请求,Nginx会将其转发到Docker网络内的server-cont:3000/api,此时请求能正常到达Express服务,解决DNS解析失败的问题。
内容的提问来源于stack exchange,提问作者Jamzy

