微服务应用容器化场景中常见问题总结

简介

云原生技术栈是下一代应用转型的必然选择,它包含了微服务架构,DevOps和容器技术。对于微服务架构来说,应用是“第一公民”,他逐渐蚕食原来底层软件或者硬件的功能,例如服务注册与发现以及负载均衡;而对于容器平台来说,容器是“第一公民”,他提供了容器注册与发现和负载均衡,同时容器技术将应用和外面的世界做了隔离,这样很多应用运行的假设就会失效。那当微服务应用运行在容器中的时候,我们会遇到哪些常见问题?我们又该如何解决呢?

企业应用在向微服务架构转型的过程中,微服务如何划分是最基本的问题。我们可以通过业务架构的梳理来理解业务,并同时使用领域设计的方法进行微服务的设计。其次,我们需要做系统设计,系统设计会更关注性能、可用性、可扩展性和安全性等;当然,我们还需要做接口设计,定义微服务之间的契约。

Java在企业中被广泛应用,当前,选择Spring Cloud作为微服务开发框架成为一个广泛的趋势。微服务架构的复杂性需要容器技术来支撑,应用需要容器化,并使用CaaS平台来支撑微服务系统的运行。

本文探讨的主题是来自于企业级Java应用在容器化过程中遇到的基础问题(与计算和网络相关),希望以小见大探讨微服务转型过程中遇到的挑战。

容器内存限制问题

让我们来看一次事故,情况如下:当一个Java应用在容器中执行的时候,某些情况下会容器会莫名其妙退出。

1.Dockfile如下:

FROM airdock/oracle-jdk:latest
MAINTAINER Grissom Wang <grissom.wang@daocloud.io>
ENV TIME_ZONE Asia/Shanghai
RUN echo "$TIME_ZONE" > /etc/timezone
WORKDIR /app
RUN apt-get update
COPY myapp.jar /app/ myapp.jar
EXPOSE 8080
CMD [ "java", "-jar", "myapp.jar" ]

2.运行命令

docker run –it –m=100M –memory-swap=100M grissom/myapp:latest

3.日志分析

执行docker logs container_id,出现了java.lang.OutOfMemoryError

4.问题初步分析

因为我们在执行容器的时候,对内存做了限制,同时在Java启动参数重,没有对内存使用做限制,是不是这个原因导致了容器被干掉呢?

当我们执行没有任何参数设置(如上面的myapp)的 Java 应用程序时,JVM 会自动调整几个参数,以便在执行环境中具有最佳性能,但是在使用过程中我们逐步发现,如果让 JVM ergonomics (即JVM人体工程学,用于自动选择和行为调整)对垃圾收集器、堆大小和运行编译器使用默认设置值,运行在容器中的 Java 进程会与我们的预期表现严重不符(除了上诉的问题)。

首先我们来做一个实验:

1.在我本机(Mac)上执行docker info命令

Server Version: 17.06.0-ce
Kernel Version: 4.9.31-moby
Operating System: Alpine Linux v3.5
OSType: linux
Architecture: x86_64
CPUs: 2
Total Memory: 1.952GiB
Name: moby

2.执行docker run -it -m=100M –memory-swap=100M debian cat /proc/meminfo

MemTotal:        2047048 kB = 2G
MemFree:          609416 kB = 600M
MemAvailable:    1604928 kB = 1.6G

虽然我们启动容器的时候,指定了容器的内存限制,但是从容器内部看到的内存信息和主机上的内存信息几乎一致。

因此我们找到原因了:docker switches(-m,-memory和-memory-swap) 在进程超过限制的情况下,会指示 Linux 内核杀死该进程。但 JVM 是完全不知道限制,因此会很有可能超出限制。在进程超过限制的时候,容器进程就被杀掉了!

解决问题的一种思路就是使用JVM参数来限制内存的使用,这个需要根据JVM内存参数的定义来做巧妙的设置,但是幸运的是从Java SE 8u131和JDK 9开始,Java SE开始支持Docker CPU和内存限制。

具体实现逻辑如下: 如果-XX:ParalllelGCThreads或-XX:CICompilerCount未指定为命令行选项,则JVM将Docker CPU限制应用于JVM在系统上看到的CPU数。然后JVM将调整GC线程和JIT编译器线程的数量,就像它在裸机系统上运行一样,其CPU数量设置为Docker CPU限制。如果-XX:ParallelGCThreads或-XX:CICompilerCount指定为JVM命令行选项,并且指定了Docker CPU限制,则JVM将使用-XX:ParallelGCThreads和-XX:CICompilerCount值。

对于Docker内存限制、最大Java堆的设置还有一些工作要做。要在没有通过-Xmx设置最大Java堆的情况下告知JVM要注意Docker内存限制,需要两个JVM命令行选项:

-XX:+ UnlockExperimentalVMOptions
-XX:+ UseCGroupMemoryLimitForHeap。

-XX:+ UnlockExperimentalVMOptions是必需的,因为在将来版本中,Docker内存限制的透明标识是目标。当使用这两个JVM命令行选项,并且未指定-Xmx时,JVM将查看Linux cgroup配置,这是Docker容器用于设置内存限制的方式,以透明地指定最大Java堆大小。 Docker容器也使用Cgroups配置来执行CPU限制。

服务注册与发现

在微服务的场景中,运行的微服务实例将会达到成百上千个,同时微服务实例存在失效,并在其他机器上启动以保证服务可用性的场景,因此用IP作为微服务访问的地址会存在需要经常更新的需求。

服务注册与发现就应用而生,当微服务启动的时候,它会将自己的访问Endpoint信息注册到注册中心,以便当别的服务需要调用的时候,能够从注册中心获得正确的Endpoint。

如果用Java技术栈开发微服务应用,Spring Cloud(https://spring.io/)将会是大家首选的微服务开发框架。Spring Cloud Service Discovery就提供了这样的能力,它底层可以使用Eureka(Netflix),ZooKeeper,ETCD。这里我们以使用Spring Cloud Eureka为例(使用Spring Cloud Eureka的文档可以参考http://cloud.spring.io/spring-cloud-static/spring-cloud-netflix/1.3.1.RELEASE/)。

在本机运行如下命令:

java -jar target/discovery-client-demo-0.0.1-SNAPSHOT.jar

该应用会将自己的信息注册到本地Eureka。我们可以通过打开Eureka的Dashboard,看到如下信息:

图 1 物理机上运行Eureka Client

我们可以看到应用的如下信息:

•   名称:discovery-client-demo
•   IP: 10.8.0.67
•   Port: 9090

但是我们在容器化场景中,遇到了问题。Dockerfile如下:

FROM airdock/oracle-jdk:latest
MAINTAINER Grissom Wang <grissom.wang@daocloud.io>
ENV TIME_ZONE Asia/Shanghai
RUN echo "$TIME_ZONE" > /etc/timezone
WORKDIR /app
RUN apt-get update
COPY target/discovery-client-demo-0.0.1-SNAPSHOT.jar /app/discovery-client-demo.jar
EXPOSE 8080
CMD [ "java", "-jar", "discovery-client-demo.jar" ]

假设我们现在在本机使用Docker run命令执行容器化discovery-client-demo,命令行如下:

docker run -d --name discovery-client-demo1 -e eureka.client.serviceUrl.defaultZone=http://10.8.0.67:8761/eureka/ -e spring.application.name=grissom  -p 8080:8080 discovery-client-demo:latest
docker run -d --name discovery-client-demo2 -e eureka.instance.prefer-ip-address=true –e eureka.client.serviceUrl.defaultZone=http://10.8.0.67:8761/eureka/ -e spring.application.name=grissom2  -p 8081:8080 discovery-client-demo:latest

前后两个容器的区别在于,第二个指示应用注册的使用IP,而默认是Hostname。

打开Eureka的Dashboard,我们可以看到如下信息:

图 2 容器中运行Eureka Client

调用Eureka的Apps接口,我们可以看到更加详细的信息:

图 3 注册应用的详细信息

这个时候我们发现了问题,两个应用注册的信息中,Hostname, IP和端口都使用了容器内部的主机名,IP和端口(如8080)。这就意味着在Port-Mapping的场景下,应用注册到Eureka的时候,注册信息无法被第三方应用使用。

解决这个问题的第一个思路如下:

图 4 对Eureka进行扩展

我们对Eureka进行扩展,对所有客户端过来的请求进行拦截,然后从Docker Daemon中拿到正确的外部访问信息来进行替换,从而确保注册到Eureka中的信息是外部能够访问的。

该方案需要做的工作:

• 需要同时对Eureka Client和Server改造,能够在Client上报的信息中加入container ID等信息。
• Eureka服务端需要加入一个过滤器,需要对所有的注册请求进行处理,根据container ID将内部hostname,IP和端口改为外部可以访问的IP和端口。

该方案的挑战:

• 因为只有容器被创建后才有ID,无法在启动参数中指定,因此应用在容器启动的时候,无法拿到容器的ID。
• 对于Eureka Server和Client的改造,无法贡献回社区,因此需要自己维护版本,存在极大的风险。

AWS的EC2 提供了实例元数据和用户数据的API,它可以通过REST API的方式供运行在EC2内部的应用使用。API形式如下:
http://169.254.169.254/latest/meta-data/
其中IP地址是固定的一个IP地址。

因此我们也可以提供一个类似的元数据和用户数据API,供容器内部的应用调用。同时为了减少对应用的侵入,我们可以在应用启动之前执行一个脚本来获取相应的信息,并设置到环境变量中,供应用启动后读取并使用,流程如下:

图 5 方案流程

总结
本文总结了使用Java开发的企业级应用在向微服务架构应用转型过程中,在容器化运行过程中遇到的常见问题、原因分析及解决方法。总结下来,由于容器技术的内在特性,我们需要对应用做一些改造,同时相应的工具如JVM也需要对容器有更好的本地支持。

原文链接:http://geek.csdn.net/news/detail/238739

时间: 2024-11-01 20:04:31

微服务应用容器化场景中常见问题总结的相关文章

Microservices Day London最后一篇精选演讲:微服务与容器

本文讲的是Microservices Day London最后一篇精选演讲:微服务与容器,[编者的话]本文对比了裸机.虚机和容器所适用的不同场景:介绍在微服务场景中使用容器的优势:阐述了谷歌和Netflix如何使用容器做到数据中心的超高利用率和对业务的弹性伸缩:解释了为什么容器化和编排框架是微服务做到弹性伸缩的关键技术,并提出了"微伸缩"的概念. 作为Microservices Day London的最后一篇精选演讲,我们分享Anne Currie的关于微服务和容器的话题,Anne C

微服务,容器和运维:猜猜现在谁来担责

本文讲的是微服务,容器和运维:猜猜现在谁来担责[编者的话]容器技术和DevOps为我们带来了新的开发模式,本文为大家带来了应对职责分离带来的问题的宝贵经验. 贯穿软件生命周期共享相同的容器是容器化DevOps带来的优点之一,它简化了开发与运维团队之间的关系.这个共享能力与传统裸机(bare metal)或是虚拟环境下的开发工作是如此的不同.并且,如此一来也改变了代码迁移到生产环境时的最终责任人. 在传统的开发场景中,很多IT组织不能为开发和QA团队提供与生产环境相同的基础设施,因此他们会在精简版

微服务、容器与持续交付

本文件的是微服务.容器与持续交付[编者的话]就像木炭.火硝和硫磺遇到了一起.当微服务.容器和持续交付遇到了一起,这注定会掀起一场变革. 微服务 如果非要给微服务找一个理由,单一职责就足够了.我们把因相同原因而变化的东西聚合到一起,而把因不同原因而变化的东西分离开.我们称之为单一职责原则SRP. 尤其是大型和长期运营的项目群,随着时间的推移,需求一定是不断增加和变更的.但我们不希望掉进"焦油坑".我们希望我们的项目群是符合"开闭原则"的.在某个时期我们寄希望于一个统一

微服务和容器对企业带来什么样的影响?

IT经理.架构师和开发者都尝试妥协于微服务和容器对企业IT方式的改变.在某一个层面来说这是一件好事,但是事实上,一些更深层次的东西在驱动着技术和IT. 要理解微服务和容器,可以从抓住它的价值定义开始,然后将IT和数据中心的性能与这个变革的驱动者进行匹配.最后,为了敏捷性来构建架构,而不是为了追随下一个大热点来构建架构. IT策划者和经理们一定要了解到应用程序和工作者之间基本关系的变化--特别是事件驱动型.移动的工作者--他们是使用容器和微服务的驱动者.IT方向的转变会让昂贵.长期存在的基础架构向

微服务和容器对企业带来什么样的影响?

IT经理.架构师和开发者都尝试妥协于微服务和容器对企业IT方式的改变.在某一个层面来说这是一件好事,但是事实上,一些更深层次的东西在驱动着技术和IT. 要理解微服务和容器,可以从抓住它的价值定义开始,然后将IT和数据中心的性能与这个变革的驱动者进行匹配.最后,为了敏捷性来构建架构,而不是为了追随下一个大热点来构建架构. IT策划者和经理们一定要了解到应用程序和工作者之间基本关系的变化--特别是事件驱动型.移动的工作者--他们是使用容器和微服务的驱动者.IT方向的转变会让昂贵.长期存在的基础架构向

技术讨论:微服务和容器比虚拟机快多少?

本文讲的是技术讨论:微服务和容器比虚拟机快多少?[编者的话]本文是Microscaling Systems的联合创始人Anne Currie在微服务日伦敦站的演讲整理稿,通过阅读本文可以深入了解到当和虚拟机比较时,微服务和容器的速度和效率. Anne Currie,Microscaling Systems的联合创始人,提供了一个关于为何采用容器的很好概述.请享受她最初在微服务日伦敦站的演讲,该演讲研究了什么使得微服务和容器的结合如此强大. 希望你能和我们一样享受这个演讲. 视频 幻灯片 今天,你

微服务和容器技术有风险,望君三思而后行

本文讲的是微服务和容器技术有风险,望君三思而后行,[编者的话]微服务和容器技术拥有令人兴奋的潜力,强烈建议客户开始研究这些技术.但是,这并不是说客户应该立即全面采用.上述技术领域的发展太快了,必须清晰地了解这些技术能干什么,不能干什么,才能够决定是否采用这些技术.毕竟,生产环境不是拿来做研发试验的竞技场. XebiaLabs是一家提供大规模持续集成和 DevOps软件的公司.我们公司经常与客户讨论新近出现的开发风格.应用架构和运行时平台,内容涉及它们的优势以及带来的挑战.最近一段时间,讨论的焦点

微服务和容器所引发的数据中心变革

软件定义基础架构.微服务和容器正在逐渐改变数据中心的构建和运行方式,未来的数据中心将会更加高效并且易于使用. 软件定义基础架构.微服务和容器是当前IT领域最为热门的话题,这些技术对数据中心的构建和运行方式产生了颠覆式影响,并且能够提升系统性能.弹性以及易用性.数据中心正在从传统的死板架构转换为更加灵活和快速响应的全新架构,甚至成为快速资源分配的发起者. 软件定义基础架构的概念并不复杂.比如,通过软件定义数据位于存储中的哪个部分,之后创建一个全新VLAN,将代码移动到虚拟机中形成一系列微服务.这些

Spring Boot与Docker(一):微服务架构和容器化概述

本文讲的是Spring Boot与Docker(一):微服务架构和容器化概述,[编者的话]本篇是<使用Spring Boot和Docker构建微服务架构>系列四部曲的第一篇,本篇将会对我们谈及的微服务架构以及容器化概念作一个概述.原文作者为3Pillar环球旗下美国Adbanced技术集团的总监Dan Greene,Dan有十八年的软件设计和开发经验,包括在电子商务.B2B集成.空间分析.SOA架构.大数据以及云计算等领域的软件产品架构经验,他是AWS认证解决方案架构师,在3Pillar之前先