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

Docker容器内Java应用无法访问IMDS但curl正常的问题排查

问题背景

我在EC2实例中使用Docker Compose运行基于eclipse-temurin:21-jdk的Java容器。我清楚默认情况下Docker容器无法访问主机网络,除非启用network_mode: "host"。

但遇到了异常情况:

  • 容器内执行curl命令能正常从实例元数据服务(IMDS)获取元数据:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

该命令可成功返回响应。

  • 但我的Java应用(使用AWS SDK)尝试从IMDS获取凭证时失败,抛出异常:
software.amazon.awssdk.core.exception.SdkClientException: Unable to load credentials from any of the providers in the chain AwsCredentialsProviderChain(credentialsProviders=[SystemPropertyCredentialsProvider(), EnvironmentVariableCredentialsProvider(), WebIdentityTokenCredentialsProvider(), ProfileCredentialsProvider(profileName=default), ContainerCredentialsProvider(), InstanceProfileCredentialsProvider()]) : [SystemPropertyCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId)., EnvironmentVariableCredentialsProvider(): Unable to load credentials from system settings. Access key must be specified either via environment variable (AWS_ACCESS_KEY_ID) or system property (aws.accessKeyId)., WebIdentityTokenCredentialsProvider(): Either the environment variable AWS_WEB_IDENTITY_TOKEN_FILE or the javaproperty aws.webIdentityTokenFile must be set., ProfileCredentialsProvider(profileName=default): java.io.IOException: Is a directory, ContainerCredentialsProvider(): Cannot fetch credentials from container - neither AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI environment variables are set., InstanceProfileCredentialsProvider(): Failed to load credentials from IMDS.]

我的问题

  1. 为什么容器内的curl能成功访问IMDS,但Java应用中的AWS SDK却失败?
  2. 是否可以在不使用network_mode: "host"或sidecar代理容器的情况下让应用访问IMDS?如果可以,如何实现?

已尝试的操作

  • 搜索多个线程并咨询工具,但大多数解决方案需要:
    • 设置network_mode: "host"(我希望避免此操作)
    • 使用sidecar容器作为代理访问IMDS
  • 这两种方案都不符合我的使用需求。

额外上下文

  • 169.254.169.254是EC2上实例元数据服务(IMDS)的虚拟IP地址。
  • 我的应用需要访问IMDS以获取IAM角色凭证,用于与AWS服务进行身份验证。

Docker Compose文件

version: "3"
services:
  test-container:
    image: myContainerImage
    restart: unless-stopped
    environment:
      AWS_REGION: ${REGION}
      AWS_EC2_METADATA_DISABLED: "false"
      AWS_EC2_METADATA_SERVICE_ENDPOINT: "http://169.254.169.254"
    #network_mode: "host"
    volumes:
      - $HOME/.aws/credentials:/root/.aws/credentials:ro
    ports:
      - "8081:8082"
volumes:
  datalake-data:
    driver: local

解答

问题1:curl能访问但AWS SDK失败的原因

  1. IMDS版本差异:EC2的IMDS有v1和v2两个版本。curl默认使用无需会话令牌的IMDSv1即可访问,但AWS SDK v2默认尝试使用IMDSv2——该版本要求先获取会话令牌才能请求凭证。容器内curl直接访问IMDSv1路径能成功,但SDK的InstanceProfileCredentialsProvider默认走IMDSv2流程,容器网络环境下获取会话令牌的请求可能因NAT规则限制被拦截或失败。
  2. SDK配置与凭证链干扰:你设置的AWS_EC2_METADATA_SERVICE_ENDPOINT可能未被SDK正确识别;同时挂载的~/.aws/credentials是目录而非文件,导致ProfileCredentialsProvider报错,打乱了凭证链的正常遍历流程,影响了IMDS提供者的执行。
  3. 重定向处理差异:IMDSv2请求存在302重定向,curl会自动处理该重定向,但SDK在容器网络下处理重定向时可能出现网络问题,导致请求失败。

问题2:不使用host网络或sidecar的解决方案

方案1:强制AWS SDK使用IMDSv1

在容器环境变量中添加:

AWS_EC2_METADATA_V1_DISABLED="false"
AWS_SDK_JAVA_IMDS_V1_ENABLED="true"

或者在Java代码中配置InstanceProfileCredentialsProvider强制启用IMDSv1:

InstanceProfileCredentialsProvider credentialsProvider = InstanceProfileCredentialsProvider.builder()
    .imdsClient(ImdsClient.builder()
        .endpointOverride(URI.create("http://169.254.169.254"))
        .useImdsV1(true)
        .build())
    .build();

注意:IMDSv1安全性低于v2,仅在无法使用v2的场景下临时使用。

方案2:修复IMDSv2的容器网络访问

IMDSv2要求客户端发送PUT请求获取会话令牌,Docker默认桥接网络中,容器发出的PUT请求可能被EC2网络规则拦截。可通过修改EC2实例的IMDS配置允许来自容器网络的请求:

  1. 在EC2实例上执行以下AWS CLI命令:
aws ec2 modify-instance-metadata-options --instance-id <你的实例ID> --http-put-response-hop-limit 2

该命令将IMDS的响应跳数限制从默认的1改为2,允许处于NAT后的容器(第二跳)接收IMDSv2的会话令牌响应。
2. 保持SDK默认的IMDSv2配置即可,无需额外修改。

方案3:修复凭证挂载问题

Docker Compose中挂载的$HOME/.aws/credentials是目录而非文件,导致ProfileCredentialsProvider报错。如果不需要本地凭证,直接删除该挂载项;如果需要,确保宿主机的$HOME/.aws/credentials是文件,挂载配置保持不变即可。


内容的提问来源于stack exchange,提问作者k.elgohary

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 03:28:09