乐趣区

关于go:Go应用性能优化的8个最佳实践快速提升资源利用效率

作者|Ifedayo Adesiyan
翻译|Seal 软件
链接|https://earthly.dev/blog/optimize-golang-for-kubernetes/

 
优化服务器负载对于确保运行在 Kubernetes 上的 Golang 应用程序的高性能和可扩展性至关重要。随着企业越来越多地采纳容器化的形式和 Kubernetes 来部署和管理应用程序,采取缩小服务器负载的最佳实际势在必行,进而达到最佳的资源利用效率、老本效益并改善用户体验。
 

运行 Golang 应用程序的多个容器能够放在一个 Kubernetes 集群内,并部署在多个节点上。每个容器能够应用 CPU、内存以及存储等系统资源。如果这些资源没有被高效地治理起来,可能会导致服务器负载一直减少,从而升高性能并减少收入。
 

因而,针对 Kubernetes 的 Golang 利用优化对于实现无效的资源利用、升高服务器负载以及保障利用在生产环境中顺利运行至关重要。
 

在本文中,咱们将钻研在 Kubernetes 上优化 Golang 利用的最佳办法,重点是升高服务器负载。咱们会探讨一些办法,包含用 readiness 和 liveness 探针进行监控,利用弹性伸缩等性能来优化资源利用。
 

通过采取这些最佳实际,能够进步在 Kubernetes 上运行的 Golang 利用的性能和可扩展性,这将改善用户体验、节约老本,并晋升运维效率。
 

本文中所有代码示例能够拜访下方网址查看:
https://github.com/theifedayo/earthly.dev/tree/main/01-optimizing-go-k8s
 

后期筹备

本文须要你对 Go 和 Kubernetes 有中等水平的理解。此外,须要你曾经装置 Go、Docker 和 Kubernetes(及其他运行 Kubernetes 的工具,如 kubectl)并且曾经在零碎内配置结束。
 

你能够通过终端输出以下命令验证是否曾经装置 Go:
go version
 

如果曾经装置,应该返回以下内容:
go version go1.19.3 darwin/amd64
 

应用以下命令能够验证 Docker 是否装置:
docker --version
 

输入应为(或其余相似的内容):
Docker version 20.10.22, build 3a2c30b
 

验证 Kubernetes 是否装置也是相似的办法。
 

了解 Golang 应用程序和 Kubernetes

谷歌开发了 Golang,也称“Go”,其指标是高效、多并发和可扩大。它对创立高性能软件很无效,特地是对须要并发解决的利用,如网络服务器、网络软件和分布式系统。
 

容器化利用的部署、扩大和治理都能够由开源容器编排引擎 Kubernetes 自动化。通过抽象化底层基础设施的细节,并促成无效的资源利用率、可扩展性和容错率,它提供了一个灵便和可扩大的框架,用于部署和治理跨节点集群的容器化应用程序。
 

Docker 容器
 

当 Golang 应用程序被部署到 Kubernetes 上时,它们通常被打包为 Docker 容器。这些独立的、轻量级的环境隔离了程序及其所有的依赖项。这些容器能够通过 Kubernetes 简略地部署和治理,实现无效的资源利用并且可能依据应用程序的要求进行弹性伸缩。
 

Dockerfile 到 Kubernetes pod 的生命周期
 

运行在 Kubernetes 上的 Golang 利用可能利用几个个性来晋升性能、可扩展性以及可靠性。例如,Kubernetes 可能在不同的集群节点上部署同一应用程序的多个实例,以扩散负载并晋升可用性。Golang 程序的部署、保护和可扩展性也能够通过 Kubernetes 中的负载平衡、服务发现和滚动更新工具来增强。
 

为了确保资源效率,避免资源抢夺或无节制应用,Golang 应用程序还能够利用 Kubernetes 的内置资源管理性能,包含 CPU 和内存限度、资源配额和资源分析等。
 

此外,Kubernetes 提供了日志和监控能力,以理解 Golang 程序的性能运行和健康状况,高效推动问题解决和 debug。
 

并发性、效率、简洁性、社区驱动以及与云原生准则的一致性,使 Golang 成为开发基于 Kubernetes 利用的热门选项,因为它能够让开发者创立高性能、可扩大和有弹性的应用程序,并能在容器化环境中轻松部署和治理。
 

总而言之,Golang 和 Kubernetes 的联合能够提供一个高性能、可扩大以及牢靠的应用程序构建和部署平台。为 Kubernetes 优化 Golang 利用,须要利用 Kubernetes 提供的性能来实现高效的资源利用、扩大、申请、响应和错误处理,以确保在容器化环境中的最佳性能和可靠性。
 

优化 Golang 应用程序的最佳实际

为 Kubernetes 优化 Golang 应用程序包含利用 Kubernetes 的性能,如资源限度、服务发现和监控,以确保在容器化环境中的最佳性能和可靠性,并优化 Go 代码,如应用 Go 垃圾回收和连接池。这种优化有助于确保在 Kubernetes 上运行的 Golang 应用程序可能无效地解决高负载,动静扩大,并为用户提供牢靠的服务。
 

1、应用最小化和高效的容器根底镜像

容器性能和资源利用可能会受到你为 Golang 应用程序应用的根底镜像的影响。应用专门为运行容器化应用程序而创立的最小的根底镜像是至关重要的。
 

最受欢迎的抉择之一是 Docker Hub 提供的官网 Golang 根底镜像,它基于官网 Golang 发行版,提供根本的运行环境。你也能够思考采纳更轻量的抉择,比方建设在 Alpine 是一个轻量级的 Linux 发行版,通常用于 Docker 镜像,以最小化镜像的大小进而缩小攻击面。
 

下方是应用官网 Golang 根底镜像的 Docker 代码的例子:

# Use the official Golang base image
FROM golang:1.16

# Set working directory
WORKDIR /app

# Copy and build the Go application
COPY . .
RUN go build -o myapp

# Run the application
CMD ["./myapp"]

 

上面的 Docker 命令应用的是基于 Alpine Linux 的官网 Go 根底镜像的一个特定版本:
FROM golang:1.16-alpine
 

FROM docker 语句指定了要应用的根底镜像,在本例中是 golang:1.16-alpine。
 

2、优化资源分配

对于缩小服务器负载和优化性能来说,高效分配资源到 Golang 容器是至关重要的。它对于进步利用性能、防止适度配置和实现可扩展性至关重要。如果资源(如 CPU 和内存)没有被无效调配,某些容器可能没有足够的资源,从而导致性能不佳。
 

通过优化资源分配,能够确保所有容器都足够的资源来保障它们顺利运行,这能够进步性能。Kubernetes 提供了为容器设置资源限度和申请的机制,这能够帮忙避免资源的适度调配或调配有余。依据你的 Golang 应用程序的资源需要,认真配置这些设置十分重要。
 

咱们将创立一个 YAML 文件,为容器设置资源限度和申请,以演示如何优化资源分配:

#.....
#   .....
      containers:
        - name: my-golang-container
          image: my-golang-image
          resources:
            limits:
              cpu: 500m
              memory: 512Mi
            requests:
              cpu: 200m
              memory: 256Mi

 

在 YAML 文件中,容器被定义了具体的资源限度和对 CPU 和内存的申请。

  • resources 局部定义了 CPU、内存资源限度以及容器申请。
  • limits 指定了容器耗费的最大资源量。在本例,限度为 500 milliCPU(0.5 CPU 内核)以及 512MB 内存。
  • requests 指定了容器运行所需的最小资源量。在本例,限度为 200 milliCPU(0.2 CPU 内核)以及 256MB 内存。
     

在 Kubernetes 中,CPU 资源被调配给容器,以确保每个容器都能取得必要的计算能力。通过设置 CPU 限度和申请,能够避免容器耗费过多的 CPU,导致其余容器没有资源。这种对 CPU 资源的无效调配有助于确保你的应用程序顺利运行,并确保服务器负载保持稳定。
 

通过审慎设置 CPU 和内存限度,容器内运行的应用程序的要求也会影响容器的大小。例如,如果利用须要大量内存来解决数据,可能须要相应地减少内存限度。在部署容器后,须要监控其资源应用并在必要时优化其大小。能够通过应用一些工具实现,如 Kubernetes 的 HPA 以基于资源应用情况来主动调整正本数量。
 

设置资源限度和申请的残缺 Kubernetes 部署配置如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-golang-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-golang-app
  template:
    metadata:
      labels:
        app: my-golang-app
    spec:
      containers:
        - name: my-golang-container
          image: my-golang-image
          resources:
            limits:
              cpu: 500m
              memory: 512Mi
            requests:
              cpu: 200m
              memory: 256Mi

 

3、优化垃圾回收

垃圾回收(GC)是 Golang 利用中内存治理的重要方面。Kubernetes 提供了垃圾回收设置的配置选项,如 GOGC 环境变量,它可能管制触发垃圾回收周期的条件。通过优化垃圾回收设置,你能够缩小内存应用并优化 Golang 容器的整体性能。
 

当在 Kubernetes 集群内运行 Golang app 时,未经优化的 GC 可能导致一些影响服务器性能的问题。最常见的问题是内存透露,进而使得 app 去耗费更多内存,从而导致更蹩脚的性能和更高的服务器负载。
 

以下代码在 Golang 中优化了垃圾回收设置。在代码里,咱们须要导入 2 个包:“os”和“fmt”。fmt 包用于格式化输出和输入,“os”包提供一个独立于平台的操作系统性能接口:

package main

import (
    "fmt"
    "os"
)

 

定义 Go 程序的次要性能:
func main() {
 

咱们决定应用 fmt.Println() 函数将 “GOGC “ 变量的以后值发送到控制台。GOGC 变量是一个管制 Go 中垃圾回收器的设置。

 // Get current GOGC value
    gogc := os.Getenv("GOGC")
    fmt.Println("Current GOGC value:", gogc);

 

当初,咱们能够将 GOGC 变量值设置为 50。这会更改 Go 垃圾回收的行为,可能会晋升程序性能:

  // Set GOGC value to 50
   os.Setenv("GOGC", "50")

   // Run your Golang application
   // ...
}

 

残缺代码如下:

package main

import (
    "fmt"
    "os"
)

func main() {
    // Get current GOGC value
    gogc := os.Getenv("GOGC")
    fmt.Println("Current GOGC value:", gogc);

    // Set GOGC value to 50
    os.Setenv("GOGC", "50")

    // Run your Golang application
    // ...
}

 

通过 os.Setenv("GOGC", "50"),将 GOGC 设置为 50。GOGC 参数决定了 Go 中触发垃圾回收的 heap 的百分比。较低的值(如 50)意味着垃圾回收机制会被频繁触发,而较高的值(如 100)意味着垃圾回收机制触发次数较少。
 

频繁的垃圾回收可能帮忙确保程序只应用它所须要的内存,为在同一服务器上运行的其余过程开释资源。监控程序性能和在必要时调整 GOGC 值以均衡内存应用和垃圾回收非常重要。
 

4、应用连接池

连接池是一种容许重复使用数据库连贯的技术,无需为每个申请建设新连贯。这能够大大减少建设和拆除连贯的开销,从而进步性能和缩小服务器负载。
 

在 Kubernetes 环境中,连接池能够通过尽量减少对数据库的连接数来帮忙升高服务器负载。当运行在 Kubernetes 的应用程序建设一个连贯到数据库中,它应用特定资源数量,如 CPU、内存和网络带宽。这些资源是无限的,如果建设了太多连贯就会被耗尽。
 

Golang 提供了像“database/sql”这样的库,反对开箱即用的连接池。上面是如何在 Golang 中实现连接池的办法:
 

咱们首先定义 package main 语句以将此 Go 代码文件标识为主包的一部分。创立可执行程序须要主包:
package main
 

咱们还必须导入这个 Go 程序须要的内部包的列表。咱们导入了规范库包 database/sql、fmt 和 log,以及第三方包 github.com/go-sql-driver/mysql,它为 database/sql 包提供了一个 MySQL 驱动:

import (
    "database/sql"
    "fmt"
    "log"
    "time"

    _ "github.com/go-sql-driver/mysql"
)

 

咱们应用 sql.Open() 函数创立新数据库连接池,它须要 2 个参数:要应用的数据库驱动程序的名称(mysql)和一个蕴含用户证书、主机名、端口和数据库名称的连接器字符串。如果与数据库的连贯失败,该程序会记录错误信息并且应用 log.Fatal() 的形式退出。否则,程序将敞开数据库连贯推延到函数完结:

    // Create a database connection pool
    db, err := sql.Open("mysql", "user:password@tcp(db-hostname:3306)/mydb")
    if err != nil {log.Fatal("Failed to connect to database:", err)
    }
    defer db.Close()

 

咱们有一个连接池,但咱们须要应用 db 对象上的办法来配置数据库连接池设置。SetMaxOpenConns() 办法设置了数据库的最大凋谢连接数(本例中为 10),而 SetMaxIdleConns() 办法设置了在池中保留的最大闲暇连接数(5)。最初,SetConnMaxLifetime() 办法设置了一个连贯的最大寿命(本例中为 5 分钟):

    // Set connection pool settings
    db.SetMaxOpenConns(10)
    db.SetMaxIdleConns(5)
    db.SetConnMaxLifetime(time.Minute * 5)

    // Use the connection pool to perform database operations
    // ...
}

 

残缺代码如下:

package main

import (
    "database/sql"
    "fmt"
    "log"
    "time"

    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // Create a database connection pool
    db, err := sql.Open("mysql", "user:password@tcp(db-hostname:3306)/mydb")
    if err != nil {log.Fatal("Failed to connect to database:", err)
    }
    defer db.Close()

    // Set connection pool settings
    db.SetMaxOpenConns(10)
    db.SetMaxIdleConns(5)
    db.SetConnMaxLifetime(time.Minute * 5)

    // Use the connection pool to perform database operations
    // ...
}

 

确定 db.SetMaxOpenConns()db.SetMaxIdleConns()db.SetConnMaxLifetime() 办法的适当值取决于各种因素,如:

  • 数据库上的预期流量和工作负载
  • 预计的并发连贯的数量
  • 数据库服务器的容量
  • 数据库查问的均匀响应工夫
  • 其余应用同一数据库的应用程序的预期负载和性能

 
总而言之,先设置这些值:

  • db.SetMaxOpenConns() 应该被设置为一个小于或等于数据库服务器能够解决的最大连接数的值。这个值应该设置得足够高,以解决预期的流量和工作量,但又足够低,以防止数据库服务器不堪重负。对于大多数网络应用来说,5 到 50 之间的值通常是正当的。
  • db.SetMaxIdleConns() 应该被设置为一个足以解决预期的闲暇连贯的值。如果应用程序常常关上和敞开连贯,这个值应该设置得更高。一个在 2 到 10 之间的值通常是正当的。
  • db.SetConnMaxLifetime() 应该被设置为一个大于数据库查问均匀工夫的值。这个值应该设置得足够高,以防止频繁的连贯更换,但又要足够低,以避免长期闲置的连贯不必要地耗费数据库资源。几分钟到几小时之间的值通常是正当的。

 
值得注意的是,这些值应该依据一直变动的工作负载和性能要求定期审查和调整。监控数据库服务器和应用程序的性能指标能够帮忙确定这些值是否须要调整。
 

5、利用健康检查和 readiness 探针

健康检查和 readiness 探针
 

Kubernetes 提供了健康检查和 readiness 探针,容许你监控容器的状态,并确定它们是否筹备好接管流量。通过利用这些性能,你能够确保只有衰弱和筹备好的容器能力接管流量,缩小由不衰弱或不筹备好的容器引起的服务器负载。你能够在 Golang 利用中实现自定义的健康检查和 readiness 探针,以提供对于其状态的精确信息。
 

为了监控容器的状态,让咱们从增加一个 readiness 探针开始:

....
    ....
        containers:
            ....
            readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5

 

  • readinessProbe 定义容器的 readiness 探针。readiness 探针被用于查看容器是否筹备好接管流量。
  • httpGet 指定 readiness 探针应该应用的 HTTP GET 申请以查看容器的健康状况。
  • path: /health 示意用于查看容器健康状况的 HTTP GET 申请的门路。在这种状况下,它被设置为 ”/health”。
  • initialDelaySeconds: 10 指定在容器启动后,在启动筹备状态探针之前的期待秒数。在本例中,它被设置为 10 秒。
  • periodSeconds: 5 指定间断的 readiness 探针查看之间的工夫距离,单位是秒。在本例,它被设置为 5 秒。

 
当初,咱们能够立即增加 liveness 探针:

....
    ....
        containers:
            ....
            readinessProbe:
            .....
            livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10

 

  • livenessProbe 定义容器的 liveness 探针。liveness 探针被用于查看容器是否失常运行和衰弱。
  • httpGet 指定 liveness 探针应该应用 HTTP GET 申请来查看容器健康状况。
  • path: /health 指用于 HTTP GET 申请的门路以查看容器的健康状况。在本例中,被设置为“/health”。
  • initialDelaySeconds: 10 设置了在容器启动后,liveness 探针启动之前所期待的秒数。在本例中为 10 秒。
  • periodSeconds: 5 指 liveness 探针间断查看的间隔时间,单位是秒。本例为 5 秒。

 
YAML 文件如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-golang-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-golang-app
  template:
    metadata:
      labels:
        app: my-golang-app
    spec:
      containers:
        - name: my-golang-container
          image: my-golang-image
          ports:
            - containerPort: 8080
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 5
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 30
            periodSeconds: 10

 

为 Kubernetes 优化 Golang 应用程序须要认真思考资源分配、垃圾回收设置、网络通信和容器健康状况。通过遵循以上最佳实际,能够在 Kubernetes 中缩小服务器负载和优化 Golang 应用程序的性能。通过利用 Kubernetes 和 Golang 利用的能力,您能够构建一个专为古代云环境优化好的可扩大、高性能的容器化应用程序。
 

加重服务器负载技巧

1、实现主动弹性伸缩:主动弹性伸缩容许基于资源利用率来主动调整 pod 数量。应用 Kubernetes HPA 能够实现,该性能会基于 CPU 或内存的利用率来主动减少或缩小 pod 数量。
 

 

如上图所示,集群主动伸缩能够在 Kubernetes 集群内主动配置新节点以解决减少的负载,确保高效的资源利用并避免服务器过载。
 

2、Kubernetes 的负载平衡:Kubernetes 提供内置的机制进行负载军训,如服务和 ingress。服务将 pod 裸露到网络并应用 round-robin 或 IP hash 等负载平衡算法,将进入的流量调配到多个 pod 上。
 

3、缓存和利用 Kubernetes 缓存零碎:Kubernetes 提供缓存零碎,如 Memcached 和 Redis,它们能够被部署为 pod 并用于存储常常拜访的数据,缩小反复查问后端服务的须要,升高服务器负载。
 

总结

总而言之,为 Kubernetes 优化 Golang 应用程序对于确保高效、牢靠地在容器化环境内部署和运维至关重要。通过遵循文中提到的最佳实际,能够无效升高服务器负载,从而晋升利用性能、进步老本效率。

退出移动版