具有 docker-compose 的容器具有不同的行为,具体取决于执行它们的机器

Containers with docker-compose have different behavior depending on machine where they are executed

美好的一天,

我试图让下面的 docker-compose 在我的机器上工作,但是当我试图从容器 B 访问容器 A 的 url 时,我遇到了一些 404 错误的问题。

先验唯一不在源代码管理中的是主机文件中的修改,我必须在其中添加以下行。

127.0.0.1 idsrv4admin.traefik.me
127.0.0.1 idsrv4adminApi.traefik.me
127.0.0.1 login.traefik.me

我从源代码管理中获取了所有资源,并且在我朋友的机器上运行良好。 也许我的机器上有不同的配置,但我找不到它是什么。

如果我尝试直接从浏览器访问“http://login.traefik.me/.well-known/openid-configuration”,我可以访问它:

{"issuer":"http://login.traefik.me","authorization_endpoint":"http://login.traefik.me/connect/authorize","token_endpoint":"http://login.traefik.me/connect/token","userinfo_endpoint":"http://login.traefik.me/connect/userinfo","end_session_endpoint":"http://login.traefik.me/connect/endsession","check_session_iframe":"http://login.traefik.me/connect/checksession","revocation_endpoint":"http://login.traefik.me/connect/revocation","introspection_endpoint":"http://login.traefik.me/connect/introspect","device_authorization_endpoint":"http://login.traefik.me/connect/deviceauthorization","frontchannel_logout_supported":true,"frontchannel_logout_session_supported":true,"backchannel_logout_supported":true,"backchannel_logout_session_supported":true,"scopes_supported":["roles","openid","profile","email","address","identity_admin_api","offline_access"],"claims_supported":["role","sub","updated_at","locale","zoneinfo","birthdate","gender","website","picture","preferred_username","nickname","middle_name","given_name","family_name","name","profile","email","email_verified","address"],"grant_types_supported":["authorization_code","client_credentials","refresh_token","implicit","password","urn:ietf:params:oauth:grant-type:device_code"],"response_types_supported":["code","token","id_token","id_token token","code id_token","code token","code id_token token"],"response_modes_supported":["form_post","query","fragment"],"token_endpoint_auth_methods_supported":["client_secret_basic","client_secret_post"],"subject_types_supported":["public"],"code_challenge_methods_supported":["plain","S256"],"request_parameter_supported":true}

如果我从容器管理员连接并在同一个 url 上尝试 curl,我会收到 404 并显示以下错误消息:

* TCP_NODELAY set
* Expire in 200 ms for 4 (transfer 0x560c38473f50)
* Connected to login.traefik.me (127.0.0.1) port 80 (#0)
> GET /.well-known/openid-configuration HTTP/1.1
> Host: login.traefik.me
> User-Agent: curl/7.64.0
> Accept: */*
>
< HTTP/1.1 404 Not Found
< Date: Mon, 23 Nov 2020 08:34:58 GMT
< Content-Length: 0
< X-XSS-Protection: 1; mode=block
< X-Content-Type-Options: nosniff
< X-Frame-Options: SameOrigin
< Referrer-Policy: no-referrer
< Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval';style-src 'self' 'unsafe-inline' https://fonts.googleapis.com/ https://fonts.gstatic.com/;font-src 'self' https://fonts.googleapis.com/ https://fonts.gstatic.com/
<
* Connection #0 to host login.traefik.me left intact

下面是 docker-compose:

version: "3.4"

services:
  traefik:
    image: "traefik:latest"
    container_name: "traefik"
    command:
      - "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.traefik.address=:9090"
    ports:
      - "80:80"
      - "443:443"
      - "9090:9090"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - proxy

  admin:
    image: ${DOCKER_REGISTRY-}admin:latest
    build:
      context: .
      dockerfile: src/IdentityServer/Admin/Dockerfile
    container_name: is4-admin
    hostname: idsrv4admin.traefik.me
    expose:      
      - '80'
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.identityserver4Admin.rule=Host(`idsrv4admin.traefik.me`)"
      - "traefik.http.routers.identityserver4Admin.entrypoints=web"
    environment:
      - VIRTUAL_HOST=idsrv4admin.traefik.me
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:80
      - DOTNET_USE_POLLING_FILE_WATCHER=1
      - "AdminConfiguration__IdentityAdminRedirectUri=http://idsrv4admin.traefik.me/signin-oidc"
      - "AdminConfiguration__IdentityServerBaseUrl=http://login.traefik.me"
      - "AdminConfiguration__RequireHttpsMetadata=false"
    depends_on:
      - sts.identity
      - admin.api
    networks:
      - proxy

  admin.api:
    image: ${DOCKER_REGISTRY-}admin-api:latest
    build:
      context: .
      dockerfile: src/IdentityServer/Admin.Api/Dockerfile
    container_name: is4-admin-api
    hostname: idsrv4adminApi.traefik.me
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.identityserver4AdminApi.rule=Host(`idsrv4adminApi.traefik.me`)"
      - "traefik.http.routers.identityserver4AdminApi.entrypoints=web"
    environment:
      - VIRTUAL_HOST=idsrv4adminApi.traefik.me
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:80
      - DOTNET_USE_POLLING_FILE_WATCHER=1
      - "AdminApiConfiguration__RequireHttpsMetadata=false"
      - "AdminApiConfiguration__ApiBaseUrl=http://idsrv4adminApi.traefik.me"
      - "AdminApiConfiguration__IdentityServerBaseUrl=http://login.traefik.me"
    depends_on:
      - sts.identity
    networks:
      - proxy

  sts.identity:
    image: ${DOCKER_REGISTRY-}sts-identity:latest
    build:
      context: .
      dockerfile: src/IdentityServer/STS.Identity/Dockerfile
    container_name: is4-sts-identity
    hostname: login.traefik.me
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.identityserver4STS.rule=Host(`login.traefik.me`)"
      - "traefik.http.routers.identityserver4STS.entrypoints=web"
    environment:
      - VIRTUAL_HOST=login.traefik.me
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=http://+:80
      - DOTNET_USE_POLLING_FILE_WATCHER=1
      - "AdminConfiguration__IdentityAdminBaseUrl=http://idsrv4admin.traefik.me"
    networks:
      - proxy

networks:
  proxy:
    driver: bridge

有人有想法吗?

提前致谢

您正在尝试连接到 127.0.0.1 Connected to login.traefik.me (127.0.0.1) port 80 (#0),这会将您发送到管理容器内的 127.0.0.1,而不是在您的计算机上

要使其正常工作,您需要将 login.traefik.me 映射到您的本地地址 (192.168.x.x)

此外,如果您使用服务名称调用该端点会更好,因为所有容器都在同一网络中,您可以替换此环境变量

- "AdminApiConfiguration__IdentityServerBaseUrl=http://login.traefik.me"

- "AdminApiConfiguration__IdentityServerBaseUrl=http://sts.identity:80"

您正在使用 traefik.me 服务提供的主机名,该服务(类似于 xip.io)通过根据子域模式将域解析为 IP 来提供通配符 DNS 服务。

例如,对 myapp.1.2.3.4.traefik.me 的 DNS 查询将解析为在子域 1.2.3.4 中编码的 IP 地址。如果您没有在子域中指定 IP 地址(也不使用十六进制表示法),则此服务会自动解析为 127.0.0.1,这在大多数情况下适用于主机到容器设置。

运行 在您的主机上,使用此 IP 地址(本地主机)指向您的主机。当您使用 docker compose 公开 traefik 时,这可以作为从本地计算机到本地计算机的请求映射到容器(由于您为 traefik 定义的 ports 映射)。

当 运行 在容器本身内部时,情况并非如此。查看 curl 命令的日志输出:

* Connected to login.traefik.me (127.0.0.1) port 80 (#0)

运行 来自其中一个容器内部的完全相同的请求也将解析为 127.0.0.1 IP,但在容器内部此 IP 地址不再是您的主机地址,而是环回适配器容器网络接口。所以基本上你 运行 在使用容器时遇到了一个关于 127.0.0.1 含义的非常普遍的问题,因为这里已经回答了好几次:

因此,如果您需要从主机和容器中使用一个单一的 dns 名称,则需要切换到类似 login.192.168.1.123.traefik.me 的名称,假设 192.168.1.123 是您的主机 IP 地址。但是正如您所注意到的,这需要针对每个开发人员进行调整,甚至当您更改所连接的 network/wifi 时也是如此。

因此,如果您想使用完全相同的域名进行 public 访问和容器间调用,我想没有使用这种通配符 dns 服务的首选解决方案。在生产场景中,主机名将解析为全局有效的 public IP 地址,该地址在从容器内部访问时也有效,因为它将通过 traefik 进行完整的往返,但这并不是一个好的选择开发设置。

在这种情况下,您确实需要指定不同的域,具体取决于您是发出重定向(使用全局有效域)还是使用从一个容器到另一个容器的直接调用。这些可以使用 docker-compose 内部服务名称访问,例如像 http://sts.identity 或 - 不太喜欢 - 容器名称 (http://is4-sts-identity)。您可以通过相应地配置环境变量 AdminApiConfiguration__IdentityServerBaseUrl 来指定使用这些 url 而不是 public 吗?