【问题标题】:UnknownHostException from java application running in AWS ECS docker container on Fargate cluster来自在 Fargate 集群上的 AWS ECS docker 容器中运行的 java 应用程序的 UnknownHostException
【发布时间】:2018-07-08 13:47:36
【问题描述】:

我有一个大型 Java 应用程序,我试图在 AWS 的 Fargate 集群上运行。图像在我本地机器的 docker 上成功运行。当我在 fargate 中运行它时,它成功启动,但最终遇到以下错误,之后应用程序卡住了:

! java.net.UnknownHostException: 690bd678bcf4: 690bd678bcf4: Name or service not known
! at java.net.InetAddress.getLocalHost(InetAddress.java:1505) ~[na:1.8.0_151]
! at tracelink.misc.SingletonTokenDBO$.<init>(SingletonTokenDBO.scala:34) ~[habari.jar:8.4-QUARTZ-SNAPSHOT]
! at tracelink.misc.SingletonTokenDBO$.<clinit>(SingletonTokenDBO.scala) ~[habari.jar:8.4-QUARTZ-SNAPSHOT]
!... 10 common frames omitted
Caused by: ! java.net.UnknownHostException: 690bd678bcf4: Name or service not known
! at java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method) ~[na:1.8.0_151]
! at java.net.InetAddress$2.lookupAllHostAddr(InetAddress.java:928) ~[na:1.8.0_151]
! at java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1323) ~[na:1.8.0_151]
! at java.net.InetAddress.getLocalHost(InetAddress.java:1500) ~[na:1.8.0_151]
!... 12 common frames omitted

Scala 代码的违规行是:

  private val machineName = InetAddress.getLocalHost().getHostName()

一些初步研究表明该错误与容器中 /etc/hosts 文件的内容有关。所以我创建了一个小测试程序,它表现出与我的真实应用程序相同的行为,并将 /etc/hosts 的内容转储到标准输出:

import java.net.*;
import java.io.*;

public class NetworkTest {
   public static void main(String[] args) throws InterruptedException, IOException, FileNotFoundException {
      while(true) {
         networkDump();
         Thread.sleep(10000);
      }
   }

   private static void networkDump() throws IOException, FileNotFoundException {
      System.out.println("/etc/hosts:");
      System.out.println("");

      FileReader f = new FileReader("/etc/hosts");
      BufferedReader reader = new BufferedReader(f);
      String line = null;
      while((line = reader.readLine()) != null) {
         System.out.println(line);
      }
      System.out.println("");

      dumpHostname();
   }

   private static void dumpHostname() {
      try {
         String hostname = InetAddress.getLocalHost().getHostName();
         System.out.printf("Hostname: %s\n\n", hostname);
      } catch(UnknownHostException e) {
         System.out.println(e.getMessage());
      }
   }
}

Dockerfile:

FROM openjdk:8

WORKDIR /site
ADD . /site

CMD ["java", "NetworkTest"]

我在 AWS 中得到的输出如下:

/etc/hosts:
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

3a5a4271a6e3: 3a5a4271a6e3: Name or service not known

与在我本地机器上的 docker 中运行的这个输出相比:

> docker run networktest

/etc/hosts:
127.0.0.1   localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.4  82691e2fb948

Hostname: 82691e2fb948

未获得异常的本地版本在 /etc/hosts 中有一个主机名条目,而 AWS 主机文件没有主机名条目。我尝试添加一个 /etc/rc.local 文件以手动将主机名添加到 localhost 行的末尾,并在 Dockerfile 中添加一个 RUN 命令来执行相同的操作。两者都没有任何效果。

有谁知道是否有办法配置映像或 ECS 任务定义以在 AWS 中正​​确配置主机名?

【问题讨论】:

  • 同样的问题。到目前为止,您找到了一些解决方案吗?
  • 同样的问题...

标签: java docker aws-fargate


【解决方案1】:

通过 go 将主机名指向 127.0.0.1:

echo "127.0.0.1 $HOSTNAME" >> /etc/hosts

为我解决了这个问题。

我正在使用 Docker Compose。所以我有一个像这样的docker-compose.yml 文件:

version: '2'

services:
  myservice:
    command: ["/set-hostname.sh", "--", "/run-service.sh"]

然后set-hostname.sh 文件看起来像这样:

#!/bin/bash

set -e

shift
cmd="$@"

echo "127.0.0.1 $HOSTNAME" >> /etc/hosts

exec $cmd

【讨论】:

  • 您是如何运行该命令的?作为 docker 文件的一部分,在实例启动后登录到实例,完全是另外一回事吗?
【解决方案2】:

与我长期以来一直在努力解决的问题完全相同。这个解决方案对我有用:

ENTRYPOINT ["/bin/sh", "-c" , "echo 127.0.0.1 $HOSTNAME >> /etc/hosts && exec mvn spring-boot:run"]

【讨论】:

    【解决方案3】:

    所以,我遇到了完全相同的问题,问题是正如您已经提到的主机名没有多大意义。 获取可以在 VPC 中看到的实际实例 IP 的唯一方法是使用 AWS 任务元数据 API,在我的例子中我就是这样做的。 https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-metadata-endpoint.html

    我已连线以下代码以获取本地主机 IP:

    try {
                final ResponseEntity<String> taskInfoResponse = this.restTemplate.getForEntity("http://169.254.170.2/v2/metadata", String.class);
                log.info("Got AWS task info: {}", taskInfoResponse);
                log.info("Got AWS task info: {}", taskInfoResponse.getBody());
                if (taskInfoResponse.getStatusCode() == HttpStatus.OK) {
                    try {
                        final ObjectNode jsonNodes = this.objectMapper.readValue(taskInfoResponse.getBody(), ObjectNode.class);
                        final JsonNode jsonNode = jsonNodes.get("Containers")
                                .get(0).get("Networks")
                                .get(0)
                                .get("IPv4Addresses").get(0);
                        log.info("Got IP to use: {}", jsonNode);
                        if (jsonNode != null) {
                            awsTaskInfo.setTaskAddress(InetAddress.getByName(jsonNode.asText()));
                        }
                    } catch (IOException e) {
                        throw new IllegalArgumentException(e);
                    }
                } else {
                    awsTaskInfo.setTaskAddress(InetAddress.getLoopbackAddress());
                }
            }catch (ResourceAccessException e){
                log.error("Failed to fetch AWS info", e);
                awsTaskInfo.setTaskAddress(InetAddress.getLoopbackAddress());
            }
    

    【讨论】:

      【解决方案4】:

      我在尝试从 Lambda 访问 S3 和 SQS 时遇到了同样的问题。解决方案不是在创建客户端实例时指定区域,而是:

      SqsAsyncClient.builder()
                      .region(Region.of(region))
                      .build();
      

      这样做:

      SqsAsyncClient.create();
      

      【讨论】:

        猜你喜欢
        • 2021-01-24
        • 1970-01-01
        • 2021-09-07
        • 2021-08-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-02-02
        • 1970-01-01
        相关资源
        最近更新 更多