应用容器化优化指南 - Golang篇

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介:

前言

随着容器技术的兴起,越来越多不同类型的应用开始使用容器的方式进行交付。Golang作为服务器端非常热门的一门语言同时也是容器技术的主要编写语言备受关注。那么将一个Golang应用进行容器化的时候,需要注意哪些事情,在出现问题时该如何进行调优和诊断呢?

先谈谈Golang本身的设计

Golang是谷歌发布的第二款开源编程语言。Golang专门针对多处理器系统应用程序的编程进行了优化,使用Golang编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。Golang在容器相关的场景和领域以及高并发的服务器程序场景下扮演着非常重要的角色。

Golang具有如下三个特点:

 ●  简洁 快速 安全
 ●  并行 有趣 开源
 ●  内存管理 数组安全 编译迅速

在学习一门语言前,通常我会主要关注如下三个方面:第一这门语言的特性是什么;第二这门语言解决的场景和问题是什么;第三这门语言的内部设计是否有需要注意的地方。上面的介绍已经为我们解答了第一个和第二个问题,那么接下来我们主要来讨论第三个问题。那么Golang的这些优秀的特性内部的设计方式是什么样子的,使用起来是否有什么需要特别注意的呢?为了详细解答这个问题,我们将问题拆分成了二个部分分别为大家解答。

 ●  Golang是如何实现并行的

高并发是Golang被大家接纳和认可的最重要一环。对于大型的互联网项目而言,高并发可以说是应用性能的立足之本,再棒的功能与特性也不如稳定运行来得让人安心。从前大家在关注C10K问题,而现在越来越多的人开始思考如何解决C10M问题。从C10K问题到C10M问题,解决问题的方式已经不是简简单单的调整内核参数那么简单的。更多的是要从架构甚至应用自身的角度来解决,一个高效的并发模型,可以从应用程序的交付压榨系统的性能。目前比较成熟的并发模型,主要是通过进程、线程与协程三种不同方式来进行实现的。

进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

Golang的并发模型是基于协程的,而协程在Linux底层的调度是依赖进程的调度的,而这之间的转换都通过Golang自身的调度器进行了管理,无需开发者关心。但是这个时候有经验的开发者就会提出问题了,golang本身是编译型的语言没有类似JVM一样的虚拟机可以在运行时指定参数,那么Goroutine这种方式是否有参数需要设置来保证性能。

此处给大家讲述一个关于Goroutine栈扩容的问题,我们内部有一个全双工的高并发写离线数据的服务,在底层数据出现消费慢的时候快速出现OOM,问题产生的原因就是由于Goroutine栈扩容,最后可以通过通过拆分Goroutine的逻辑到上半段和work group的方式实现,由于篇幅的原因不过多的赘述,可以参考如下这篇博客更深入了解栈扩容的问题。

 ●  Golang的内存是如何管理的

内存管理对于C++与Java的开发者而言是最熟悉不过的了。C++的开发者必须通过代码手动的申请与释放内存,因此必须熟悉内存布局和使用;Java的开发者虽然有JVM帮助进行内存的管理与回收,但JVM不同的内存参数配置会导致程序因为回收内存带来不同的性能表现。而Golang作为一门高级编程语言,同样无需开发者直接操作内存,但是Golang中的GC设计是存在一些缺欠的。主要的问题在GC时的卡顿上,具体的问题可以参考如下文章,不过这点也无需大家特别关心,建议直接使用Golang1.9以后的版本进行编译即可。深入了解Golang的GC可以参考如下文章

Golang容器化建议

 ●  常规容器化建议

首先需要进行的是常规的容器化优化,具体的内容可以参考如下文章进行体积的精简和优化。

 ●  Golang 中DNS的问题

不同语言对于DNS的Lookup处理会有所不同,在Java或者Node.JS等常见的语言和框架中对DNS Lookup都提供语言级别的内置的Cache,而在Golang中却不存在类似的能力,这会导致对于高并发的场景中,Golang程序有可能会出现大量的DNS查询,而在kubernetes中,DNS是通过内部的coredns或者kube-dns的方式提供的,因此有可能会因为大流量的Golang DNS导致集群异常,为了解决这个问题,建议开发者在Golang的Dockerfile中集成nscd进行DNS的Cache,具体的操作步骤可以参考如下文档

 ●  Golang 中GC的问题

在本文的上面的部分,为大家讲解了Golang GC的一些缺欠以及如何避免GC问题的方式,在容器化的时候是否还需要做其他的优化呢?面对内存的异常,我们要如何定位是一个GC的问题呢?这里要给大家介绍的是Golang自来的pprof,pprof是Golang语言中内置的性能调优工具,可以协同Flame-Graph,排查CPU性能、内存性能、GC回收等问题,建议在容器的场景中,在代码中集成pprof,并通过环境变量的方式进行开关设置,容器的Dockerfile中保留端口的保留,当出现问题的时候可以设置环境变量的方式进行开启,快速进行线上问题的诊断。pprof的使用,可以参考如下文章

 ●   Golang 中CGO的问题
我们知道Golang作为一门编译型的语言,可以通过开启CGO的标签,直接使用C的代码并编译为二级制文件直接使用。但是这种方式非常不建议在容器中开启,特别是使用类似Alpine这种最小镜像的场景下。因为开启CGO的场景下,会动态链接系统的C库,而在Alpine上,很多的目录布局是有所差异的,另外有些最简化的版本glibc的支持并不完善,因此非常不建议使用CGO的方式编译Golang在容器中使用。
 ●   Golang 中监控的建议 
容器中很多监控的方式都无法很好的直接复用,建议大家使用更Docker的方式来解决,例如使用Prometheus的方式暴露Golang应用内部的指标进行监控,这也是目前非常多Golang开源项目的标配了。使用方式参考下文,与容器服务结合可以参考如下文章
 ●   Golang 中性能的问题
Golang与容器的结合通常是为了高性能的场景,那么通常需要对内核参数进行部分的调整,具体的调整方式可以参考如下文章
最后

Golang相对而言算是非常”省心“的一门语言了,在老版本的Golang中还需要通过runtime设置GOMAXPROCS,但是在最新版本的Golang中已经基本无需关心runtime的任何参数设置了,这些参数就像nginx的auto一样,会随着探测的配置自动变化。因此,Golang的应用容器化,更多的还是要做好标准镜像优化的步骤,以及在代码级别做好避免触发GC和Goroutine的问题。


原文发布时间为:2018-10-30

本文来自云栖社区合作伙伴“Golang语言社区”,了解相关信息可以关注“Golang语言社区”。

相关文章
|
14天前
|
NoSQL Redis Docker
深入浅出:使用Docker容器化改进Python应用部署
在快速演进的软件开发领域,持续集成和持续部署(CI/CD)已成为加速产品上市的关键。本文将探索如何利用Docker,一种流行的容器化技术,来容器化Python应用,实现高效、可靠的部署流程。我们将从Docker的基本概念入手,详细讨论如何创建轻量级、可移植的Python应用容器,并展示如何通过Docker Compose管理多容器应用。此外,文章还将介绍使用Docker的最佳实践,帮助开发者避免常见陷阱,优化部署策略。无论是初学者还是有经验的开发人员,本文都将提供有价值的见解,助力读者在自己的项目中实现容器化部署的转型。
|
14天前
|
运维 Java 开发者
深入浅出:使用Docker容器化改善Java应用的部署与运维
在当今快速迭代的软件开发周期中,确保应用的一致性、可移植性与易于管理成为了开发与运维团队面临的重大挑战。本文旨在介绍如何通过Docker容器技术,有效地解决这些问题,特别是针对Java应用。我们将从Docker的基本概念出发,逐步深入到实际操作,展示如何将传统的Java应用容器化,以及这一过程如何帮助简化部署流程、提高应用的可靠性和可伸缩性。不同于常规的技术文章,本文试图以一种更加易于理解和实践的方式,让读者能够快速掌握容器化技术,并将其应用于日常的开发与运维工作中。
74 0
|
15天前
|
JavaScript NoSQL Redis
深入浅出:使用 Docker 容器化部署 Node.js 应用
在当今快速发展的软件开发领域,Docker 作为一种开源的容器化技术,已经成为了提高应用部署效率、实现环境一致性和便于维护的关键工具。本文将通过一个简单的 Node.js 应用示例,引导读者从零开始学习如何使用 Docker 容器化技术来部署应用。我们不仅会介绍 Docker 的基本概念和操作,还会探讨如何构建高效的 Docker 镜像,并通过 Docker Compose 管理多容器应用。此外,文章还将涉及到一些最佳实践,帮助读者更好地理解和应用 Docker 在日常开发和部署中的强大功能。
17 0
|
15天前
|
运维 Java Linux
深入解析:使用Docker容器化技术提升Java应用的部署效率
在快速迭代的软件开发周期中,如何保证应用的快速、一致和可靠部署成为了开发团队需要面对的重大挑战。本文将探讨如何利用Docker容器化技术,结合Java应用,实现高效、一致的部署流程。我们将从Docker的基本概念出发,详细介绍将Java应用容器化的步骤,包括创建Dockerfile、构建镜像以及运行容器等关键环节,并通过示例代码加以说明。此外,本文还将讨论在使用Docker部署Java应用时可能遇到的常见问题及其解决策略,旨在为读者提供一种提升部署效率、优化开发流程的有效方法。
242 2
|
15天前
|
Java 持续交付 虚拟化
深入浅出:使用Docker容器化改善Java应用的开发与部署流程
在快速迭代与持续集成的软件开发周期中,确保应用在各种环境中一致运行是一个挑战。本文介绍了如何利用Docker容器技术,来容器化Java应用,以实现环境一致性、简化配置和加速部署过程。我们将从Docker的基础知识开始,探讨其与传统虚拟机的区别,进而深入到如何创建Dockerfile,构建镜像,以及运行和管理容器。此外,文章还将涵盖使用Docker Compose来管理多容器应用的策略,以及如何利用容器化改善CI/CD流程。通过本文,读者将获得关于如何高效地利用Docker改善Java应用开发与部署流程的实践指导。
133 1
|
16天前
|
消息中间件 监控 NoSQL
容器化应用系统上生产的最佳实践
容器化应用系统上生产的最佳实践
|
16天前
|
Kubernetes Linux 开发工具
容器开发运维人员的 Linux 操作机配置优化建议
容器开发运维人员的 Linux 操作机配置优化建议
|
16天前
|
持续交付 开发者 Docker
深入浅出:使用Docker容器化改进Python应用部署
在当今快速变化的软件开发领域,开发者面临着各种挑战,其中之一就是如何确保应用在不同环境中都能稳定运行。本文将介绍Docker这一容器化技术如何为Python应用提供一个一致的运行环境,简化部署过程,并提高开发效率和应用的可移植性。我们将从Docker的基本概念讲起,通过实际案例演示如何将一个简单的Python应用容器化,最后探讨容器化对于持续集成/持续部署(CI/CD)流程的积极影响。
12 0
|
16天前
|
运维 Java 云计算
深入浅出:使用Docker容器化改进Java应用部署
在当前快速演变的软件开发领域,Docker作为一种开源的容器化技术,已经成为优化应用部署、实现快速交付和高效率运维的关键工具。本文将探讨如何利用Docker容器化技术来改进Java应用的部署流程。我们不仅会介绍Docker的基础知识,还会通过一个实际的Java应用示例,详细展示从创建Dockerfile到构建镜像,再到运行容器的整个过程。此外,文章还将探讨容器化带来的好处,如环境一致性、便捷的版本控制和简化的部署流程等,力求为读者提供一个清晰、易懂的指南,帮助他们在自己的项目中实现Docker容器化,从而提升开发和部署效率。
151 1
|
16天前
|
运维 Java 持续交付
深入浅出:使用Docker容器化改善Java应用的部署与运维
在当今快速发展的软件开发领域,持续集成与持续部署(CI/CD)已成为提高开发效率和软件质量的关键。本文将探讨如何利用Docker容器技术,实现Java应用的高效部署与运维。我们将从Docker的基本概念入手,详细介绍如何将传统的Java应用容器化,并通过实际案例展示容器化带来的便利性和高效性。此外,文章还将探讨Docker容器与传统虚拟机部署方式的对比,以及如何在实际项目中选择最适合的部署策略。通过本文,读者将能够深入理解Docker容器化技术,并学会如何在自己的Java项目中实施和优化。
184 1

相关产品

  • 容器镜像服务
  • 容器服务Kubernetes版