You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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网络内的容器名称。

修复步骤:

  1. 修改Angular服务的后端地址
    将UserService中的backendUrl改为相对路径,利用Nginx的反向代理转发请求:

    private backendUrl = "/api";
    

    这样Angular会把请求发送到当前域名的/api路径,由Nginx代理到Express容器。

  2. 调整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;
    }
    
  3. 重新构建并启动容器

    • 重新构建Angular镜像:docker build -t admin-users .
    • 重启Angular容器:docker restart admin-users-cont
    • 若修改了Express代码,同样重新构建镜像并重启容器:docker build -t server . && docker restart server-cont

验证

访问Angular应用,调用接口时,浏览器会发送http://localhost:8080/api/getusers请求,Nginx会将其转发到Docker网络内的server-cont:3000/api,此时请求能正常到达Express服务,解决DNS解析失败的问题。


内容的提问来源于stack exchange,提问作者Jamzy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 17:17:02