Docker支持重定向HTTP吗?

huangapple go评论111阅读模式
英文:

Does Docker Support Redirect HTTP?

问题

我尝试使用Docker连接我的服务进行学习目的。
当我在本地主机上尝试时,一切都很好。
但是当我在Docker上尝试时,当我尝试从产品服务重定向到我的认证服务时,出现错误。

我尝试的方法是不使用HTTP重定向,而是向认证服务发出新的请求,这样可以连接成功,但是在Docker中尝试重定向时出现错误。

这是我的重定向代码,我使用的是Golang和Gin-gonic库:

url := os.Getenv("AUTH_SERVICE_URL") // http://auth-service:8081
c.Redirect(http.StatusSeeOther, url+"/refresh?token="+tokenStr)
c.Abort()
return

这是我的新请求代码:

url := os.Getenv("AUTH_SERVICE_URL") // http://auth-service:8081
request, err := http.NewRequest(http.MethodGet, url+"/refresh?token="+tokenStr, nil)
log.Println("make request to", request.URL)

if err != nil {
    panic("Internal Server Error")
}

client := http.Client{
    Timeout: 30 * time.Second,
}

response, err := client.Do(request)
if err != nil {
    panic(fmt.Sprint("unable to get auth request", err.Error()))
}

log.Println("REFRESH TOKEN", response.Header)

我想知道为什么当我使用HTTP重定向时,在Docker上会出现错误,但在本地主机上尝试时却可以正常工作。
我在Postman中进行测试时遇到了这个错误:Error: getaddrinfo ENOTFOUND auth-service

但是当我在Docker中使用相同的URL时,使用新的HTTP请求代码却可以正常工作。

这是我的product-service的docker-compose配置:

version: '3'

services:

  product-service:
    build:
      context: .
      dockerfile: .
    ports:
      - "8082:8082"
    environment:
      AUTH_SERVICE_URL: http://auth-service:8081
      DB_DRIVER: postgres
      DB_USER: postgres
      DB_PASSWORD: password
      DB_HOST: postgresDB
      DB_PORT: 5431
      DB_NAME: v_product
      DB_SSL_MODE: disable
      PORT: 8082
    networks:
      - auth-service_default
      - product-service_default
    depends_on:
      - postgresDB

  postgresDB:
    image: 'postgres:12.12'
    ports:
      - "5431:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: v_product
    networks:
      - product-service_default
    volumes:
      - psql_user:/var/lib/postgresql/data

volumes:
  psql_user:

networks:
  auth-service_default:
    external: true
  product-service_default:
    external: true

这是我的auth-service的docker-compose配置:

version: '3'

services:
  auth-service:
    build:
      context: .
      dockerfile: .
    ports:
      - "8081:8081"
    environment:
      USER_SERVICE_URL: http://user-service:8080
      PORT: 8081
    networks:
      - user-service_default
      - auth-service_default
networks:
  user-service_default:
    external: true
  auth-service_default:
    external: true

注意:我尝试重定向的端点是:GET :8081/refresh?token=JWTOKEN,我使用了See Other的重定向代码,因为它是从POST请求重定向过来的。

英文:

I try docker to connect my services for study purpose.
It's all good when I try it in my localhost.
But when I try it on Docker, It's get error when I try to http redirect from product-service to my auth-service.

What I had try is instead using http redirect, I make a new request to auth-service, and It's worked and connected but it's get error when I try to redirect it in Docker.

This is my Redirect Code
I'm using golang and Gin-gonic Library

url := os.Getenv("AUTH_SERVICE_URL") // http://auth-service:8081  
c.Redirect(http.StatusSeeOther, url+"/refresh?token="+tokenStr)  
c.Abort()  
return

And this is My new request Code

url := os.Getenv("AUTH_SERVICE_URL") // http://auth-service:8081  
request, err := http.NewRequest(http.MethodGet, url+"/refresh?token="+tokenStr, nil)
log.Println("make request to", request.URL)  
  
if err != nil {  
	panic("Internal Server Error")  
}  

client := http.Client{  
	Timeout: 30 * time.Second,  
}  

response, err := client.Do(request)  
if err != nil {  
	panic(fmt.Sprint("unable to get auth request", err.Error()))  
}  

log.Println("REFRESH TOKEN", response.Header)  

I just wondering, why when I use http redirect It's got an error on docker but it works when i try it on my localhost.
I'm using postman for testing and I got this error.
Error: getaddrinfo ENOTFOUND auth-service

But it works when i'm using New Http Request code in my Docker with the same URL.

This is my product-service docker-compose

version: '3'

services:

  product-service:
    build:
      context: .
      dockerfile: .
    ports:
      - "8082:8082"
    environment:
      AUTH_SERVICE_URL: http://auth-service:8081
      DB_DRIVER: postgres
      DB_USER: postgres
      DB_PASSWORD: password
      DB_HOST: postgresDB 
      DB_PORT: 5431
      DB_NAME: v_product
      DB_SSL_MODE: disable
      PORT: 8082
    networks:
      - auth-service_default
      - product-service_default
    depends_on:
      - postgresDB

  postgresDB:
    image: 'postgres:12.12'
    ports:
      - "5431:5432"
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: v_product
    networks:
      - product-service_default
    volumes:
      - psql_user:/var/lib/postgresql/data

volumes:
  psql_user:

networks:
  auth-service_default:
    external: true
  product-service_default:
    external: true

This is my auth-service docker-compose

version: '3'  
  
services:  
  auth-service:  
    build:  
      context: .  
      dockerfile: .  
    ports:  
      - "8081:8081"  
    environment:  
      USER_SERVICE_URL: http://user-service:8080  
      PORT: 8081  
    networks:  
      - user-service_default  
      - auth-service_default  
networks:  
  user-service_default:  
    external: true  
  auth-service_default:  
    external: true    

Note: The Endpoint I try to redirect is : GET :8081/refresh?token=JWTOKEN and I use See Other Code for redirect, because It's redirect from POST request.

答案1

得分: 1

一个HTTP重定向的工作原理如下(请参考链接以获得更好的描述和图表):

  1. 客户端(例如Postman)发送请求(例如GET http://auth-service:8080)。
  2. 服务器以重定向状态码(在这种情况下是303 "See Other")和新的URL(例如http://auth-service:8081/refresh?token=blah)进行响应。
  3. 客户端请求新的URL。

所以根据评论,你的应用程序重定向到了http://auth-service:8081/refresh?token=blah。问题在于,当非容器的客户端尝试查找主机auth-service的IP地址时,它找不到(它不在本地的hosts文件中,而且你使用的DNS服务器也不知道该地址)。因此,解决方法是返回一个客户端可以访问的URL(例如http://127.0.0.1:8081)。

值得注意的是,即使客户端可以解析auth-service,它的地址很可能是类似于172.21.0.2的私有地址。这是一个无法从主机访问的私有地址(因为你正在使用桥接网络)。这种类型的网络允许容器在其间进行通信(有限制)并发起出站连接,但不允许主机或连接到主机的设备进行入站访问。

外部访问通常是通过发布端口实现的,即docker-compose.yml中的ports: - "5431:5432"行。这些条目允许你在主机上公开容器的端口(在本例中,容器的端口5432被公开为主机上的端口5432,但端口号可以不同)。

> 1. 容器中的网络是如何工作的?

抱歉,这个问题在一个堆栈溢出的回答中解释起来太多了!Docker文档写得很好。

> 1.5 这是否会影响我的重定向操作,或者我只是错误地指定了主机,因为重定向是由客户端执行的?

重定向是使用HTTP协议执行的。HTTP是一个应用层协议,通信通常在TCP/IP连接上进行。值得花一点时间了解涉及的各个不同层次(在IT领域总是有更多可以学习的东西!)。

在底层的TCP堆栈中,主机(和其他外部系统)只能通过已发布的端口访问容器。值得注意的是,DNS(主机名如何映射到IP)和IP路由(主机是否能够访问容器)是分开考虑的。

> 2. 在这种情况下,客户端是Postman吗?

Postman是可以的。另一种选择是使用浏览器,它的操作方式基本相同。

> 3. 如果我说容器尝试连接到命名的主机服务时,只要每个容器连接到相同的网络,通信就可以完成,这样是否正确?这与服务器之间的通信类似吗?

在同一网络上的容器可以进行通信,Docker提供了一个内嵌的DNS服务器,使它们能够执行查找(将auth-service映射到容器的IP地址,将host.docker.internal映射到主机)。重要的是要注意,主机不使用这个内嵌的DNS服务器(而且即使使用,也会有限制,因为主机无法直接通过IP地址与容器通信)。

希望这可以帮助你,这可能会变得相当令人困惑,特别是如果你对网络没有太多经验。我想指出,在这种情况下,我经常在这种情况下使用边缘路由器/反向代理(例如Traefik)。使用它,你只需映射一个端口(或者如果你想要HTTPS,可能是两个端口),Traefik会将请求路由到适当的容器(现在可能会让你感到困惑,但将来可能会有用!)。

英文:

An HTTP Redirect works something like this (see the link for a better description with diagrams etc):

  1. The client (e.g. postman) makes a request (e.g. GET http://auth-service:8080)
  2. The server responds with a redirect status (303 "See Other" in this case) and the new URL (e.g. http://auth-service:8081/refresh?token=blah)
  3. The client requests the new URL.

So, as per the comments, your app was redirecting to http://auth-service:8081/refresh?token=blah. The issue is that when the client, which is not a container, tries to lookup the IP address for the host auth-service it will not find it (it's not in the local hosts file and whatever DNS server you are using does not know the address). As such the fix is to return a URL that the client can access (e.g. http://127.0.0.1:8081).

It's worth nothing that even if the client could resolve auth-service its likely the address would be something like 172.21.0.2. This is a private address that is not accessible from the host (because you are using a bridge network). This type of network allows containers to communicate amongst themselves (within limits) and to initiate outgoing connections but provides no inbound access for the host or devices attached to the host.

External access is (generally) via published ports; that is the ports: - "5431:5432" lines in the docker-compose.yml. These entries allow you to expose a containers port on the host (in this case port 5432 on the container is exposed as port 5432 on the host, but the port numbers can differ).

>1. How does the network in containers works

Sorry - that's too much to explain in a stack overflow answer! The docker docs are pretty good.

> 1.5 does it take effect to my redirect action or I'm just wrong specify the host because redirect is actioned by client?

The redirect is performed using the http protocol. HTTP is an application layer protocol; communication usually takes place over TCP/IP connections. It's worth spending a little bit of time to understand all of the different layers involved (there is always more to learn in IT!).

Within the underlying TCP stack the host (and other external systems) can only access the container via the published port. It's worth nothing that DNS (how the host name is mapped to an IP) and IP routing (can traffic from the host reach the container) are separate considerations.

> 2. Is it right the client in this case is Postman ?

Postman is fine. The alternative is to use a browser which will operate in much the same way.

> 3. Am I right if I said when containers trying to connect with named host service, the communication can be done if each containers connect to the same network and it's same like server to server communication?

Containers on the same network can communicate and Docker provides an embedded DNS server that enables them to perform lookups (so map auth-service to the containers IP address and host.docker.internal to the host). Its important to note that this embedded DNS server is not used by the host (and would be of limited use anyway because the host cannot communicate directly with the containers via their IP address).

Hopefully that helps - this can get quite confusing, particularly if you don't have much experience with networks. I would note that I often use Edge Routers/reverse proxies in this kind of situation (e.g. Traefik). With this you can just map one port (or perhaps two if you want HTTPS) and Traefik routes the requests to the appropriate container (this may just confuse you now but could be useful in the future!).

huangapple
  • 本文由 发表于 2022年8月16日 03:53:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/73365827.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定