在CSDN学Golang云原生(Kubernetes Pod)

一,pod的定义与基本用法

在 Kubernetes 中,Pod 是最小的可部署单元,它包含一个或多个容器。使用 Golang 来定义和操作 Pod 时,需要使用 kubernetes/client-go 包提供的 API。

以下是 Golang 定义和基本用法 Pod 的示例:

  1. 安装 kubernetes/client-go 包

在 Golang 环境中安装 kubernetes/client-go 包,该包提供了访问 Kubernetes API Server 的客户端库。

go get k8s.io/client-go/...
  1. 编写代码定义 Pod

下面是一个简单的示例代码片段,可以用来创建一个包含两个容器的 Pod:

package main

import (
	"context"
	"fmt"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/intstr"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

func main() {
	config, err := clientcmd.BuildConfigFromFlags("", "/path/to/kubeconfig")
	if err != nil {
		panic(err.Error())
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "example-pod",
			Namespace: "default",
			Labels: map[string]string{
				"app": "example-app",
			},
		},
		Spec: corev1.PodSpec{
			RestartPolicy: corev1.RestartPolicyOnFailure,
			Volumes: []corev1.Volume{
				{
					Name: "example-volume",
					VolumeSource: corev1.VolumeSource{
						EmptyDir: &corev1.EmptyDirVolumeSource{},
					},
				},
			},
			Containers: []corev1.Container{
				{
					Name:  "container-1",
					Image: "nginx",
					Ports: []corev1.ContainerPort{
						{
							Name:          "http",
							ContainerPort: 80,
							Protocol:      corev1.ProtocolTCP,
						},
					},
				},
				{
					Name:  "container-2",
					Image: "busybox",
					Command: []string{"sleep", "3600"},
					VolumeMounts: []corev1.VolumeMount{
						{
							Name:      "example-volume",
							MountPath: "/data",
						},
					},
				},
			},
			NodeSelector: map[string]string{
				"node-role.kubernetes.io/worker": "",
			},
			Tolerations: []corev1.Toleration{
				corev1.Toleration{Key: "key", Operator:"Equal", Value:"value"},
			},

			
		
	   },
    }
	
    _, err = clientset.CoreV1().Pods("default").Create(context.Background(), pod, metav1.CreateOptions{})
	if err != nil {
        panic(err.Error())
    }

	fmt.Println("Pod created successfully")
}
  1. 运行代码创建 Pod

使用 Golang 运行上面的示例代码,它会在 Kubernetes 集群中创建一个名为 example-pod 的 Pod,其中包含两个容器:nginx 和 busybox。Nginx 容器将公开 TCP 端口 80,并且 busybox 容器将在 /data 目录中挂载一个名为 example-volume 的空目录。

运行示例代码的命令如下:

go run main.go

以上是 Golang 定义和基本用法 Pod 的示例,可以根据实际需求修改代码。

二,pod生命周期与重启策略

在 Kubernetes 中,Pod 是最小的可部署单元。它包含一个或多个容器,并且有自己的生命周期和重启策略。

Golang 可以通过 Kubernetes 提供的 API 来定义和操作 Pod 的生命周期和重启策略。以下是一些常见的 Pod 生命周期和重启策略:

  1. Pod 生命周期

Pod 的生命周期分为三个阶段:

  • Pending:Pod 已经被创建,但是尚未调度到任何节点上。
  • Running:Pod 已经被调度到某个节点上并且至少有一个容器正在运行。
  • Terminated:Pod 所有容器都已停止运行。
  1. 重启策略

Pod 的重启策略指定了当某个容器失败时 Kubernetes 应该采取哪种行动:

  • Always:无论什么原因导致容器停止运行,Kubernetes 都会自动重新启动该容器。这是默认的重启策略。
  • OnFailure:只有当容器以非正常退出状态(如错误码不为 0)终止时,Kubernetes 才会自动重新启动该容器。
  • Never:当某个容器停止运行时,Kubernetes 不会自动重新启动该容器。

以下是一个 Golang 示例代码片段,用于定义 Pod 的生命周期和重启策略:

package main

import (
	"context"
	"fmt"

	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/tools/clientcmd"
)

func main() {
	config, err := clientcmd.BuildConfigFromFlags("", "/path/to/kubeconfig")
	if err != nil {
		panic(err.Error())
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err.Error())
	}

	pod := &corev1.Pod{
		ObjectMeta: metav1.ObjectMeta{
			Name:      "example-pod",
			Namespace: "default",
			Labels: map[string]string{
				"app": "example-app",
			},
		},
		Spec: corev1.PodSpec{
			RestartPolicy: corev1.RestartPolicyOnFailure,
			Volumes: []corev1.Volume{
				corev1.Volume{
					Name: "example-volume",
					VolumeSource: corev1.VolumeSource{
						ConfigMap: &corev1.ConfigMapVolumeSource{
							LocalObjectReference: corev1.LocalObjectReference{Name:"configmap-name"},
						},
					},
				},
			},
			InitContainers: []corev1.Container{ // 定义 init container
                {
                    Name:"init-container-01",
                    Image:"busybox",
                    Command:["sleep", "10"],
                },
            },
            Containers: []corev1.Container{ // 定义主容器
				corev1.Container{
					Name:  "container-01",
					Image: "nginx",
					Command:["nginx"],
                    Args:["-g", "daemon off;"], 
					
				corev1.Container{
					Name:  "container-02",
					Image: "busybox",
                    Command:["sh", "-c"],
                    Args:[`while true; do echo "$(date) Hello, World!"; sleep 10 ; done`],
					VolumeMounts: []corev1.VolumeMount{
						corev1.VolumeMount{
							Name:      "example-volume",
							MountPath: "/data",
						},
					},
				},
			},
            NodeSelector: map[string]string{
                "node-role.kubernetes.io/worker": "",
            },
            Tolerations: []corev1.Toleration{ // 添加容忍
                corev1.Toleration{Key:"key", Operator:"Equal", Value:"value"},
            },
        },
    }

    _, err = clientset.CoreV1().Pods("default").Create(context.Background(), pod, metav1.CreateOptions{})
	if err != nil {
        panic(err.Error())
    }

	fmt.Println("Pod created successfully")
}

在上面的示例中,我们使用了 RestartPolicy 字段来定义 Pod 的重启策略。我们还使用了 InitContainers 字段来定义 Pod 中的初始化容器。

通过这些示例代码片段,你可以更好地理解 Pod 生命周期和重启策略。

三,容器镜像拉取策略

在 Golang 中,我们可以使用 Kubernetes 提供的 API 来定义容器镜像拉取策略。以下是一些常见的容器镜像拉取策略:

  1. Always

无论本地是否存在该镜像,都会尝试拉取最新版本。如果本地已经有该镜像,则会被更新为最新版本。

container := corev1.Container{
    Name: "example-container",
    Image: "nginx",
    ImagePullPolicy: corev1.PullAlways,
}
  1. IfNotPresent

如果本地已经有该镜像,则直接使用本地版本;否则尝试从远程仓库拉取最新版本。

container := corev1.Container{
    Name: "example-container",
    Image: "nginx",
    ImagePullPolicy: corev1.PullIfNotPresent,
}
  1. Never

只使用本地已经存在的该镜像,不尝试从远程仓库拉取任何版本。

container := corev1.Container{
    Name: "example-container",
    Image: "nginx",
    ImagePullPolicy: corev1.PullNever,
}

需要注意的是,这些策略可能会影响到你的 Pod 的行为和性能。例如,如果你选择了 Always 策略,则每次启动 Pod 时都会去检查并下载最新的镜像,这可能会增加启动时间和网络带宽消耗。因此,在选择镜像拉取策略时,请根据实际情况进行权衡和选择。

另外,还可以在 Pod 或者 Deployment 等资源定义时指定默认的镜像拉取策略。例如:

pod := &corev1.Pod{
    // ...
    Spec: corev1.PodSpec{
        Containers: []corev1.Container{
            {
                Name: "example-container",
                Image: "nginx",
                // 可以覆盖全局配置中定义的策略
                ImagePullPolicy: corev1.PullIfNotPresent,
            },
        },
        // 指定默认的镜像拉取策略
        ImagePullSecrets: []corev1.LocalObjectReference{{Name:"myregistrykey"}},
    },
}

上面的代码片段中,我们通过 ImagePullSecrets 字段来指定使用哪个 Secret 对象来拉取镜像。这里我们使用了名为 myregistrykey 的本地 Secret 对象。

四,init Container

在 Kubernetes 中,一个 Pod 可以包含多个容器。其中,除了主要的业务容器之外,还可以定义一个或多个 Init Container(初始化容器),用于在业务容器启动之前完成一些必要的预处理工作。

使用 Init Container 有以下几个好处:

  1. 解耦应用逻辑和初始化逻辑,使应用更加清晰可维护。
  2. 避免竞态条件和资源争夺等问题,提高系统可靠性和稳定性。
  3. 灵活控制启动顺序和执行流程,满足不同场景下的需求。

对于 Golang 应用程序来说,在 Kubernetes 中使用 Init Container 的方法与其他语言类似。以下是一个简单的示例代码:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  initContainers:
  - name: init-myservice
    image: busybox
    command: ['sh', '-c', 'wget -O /work-dir/myservice https://myservice.com && chmod +x /work-dir/myservice']
    volumeMounts:
    - name: workdir-volume
      mountPath: /work-dir
  containers:
  - name: myapp-container
    image: myapp:v1.0.0
    command: ['/myapp']
    volumeMounts:
    - name: workdir-volume
      mountPath: /work-dir

  volumes:
  - name: workdir-volume
    emptyDir: {}

上面的 YAML 文件定义了一个 Pod,其中包含一个 Init Container 和一个业务容器。Init Container 的作用是下载并安装 myservice 程序到共享目录 /work-dir 中,而业务容器则通过挂载该目录来使用 myservice 程序。

需要注意的是,Init Container 的执行顺序是按照它们在 YAML 文件中的顺序依次执行的。只有当所有 Init Container 成功完成后,才会启动业务容器。如果任何一个 Init Container 失败,则整个 Pod 也将被标记为失败,并重新启动。

另外,还可以在创建 Deployment 或 StatefulSet 时指定 Init Container。例如:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: myapp
    spec:
      initContainers:
      - name: init-myservice
        image: busybox
        command: ['sh', '-c', 'wget -O /work-dir/myservice https://myservice.com && chmod +x /work-dir/myservice']
        volumeMounts:
        - name: workdir-volume
          mountPath: /work-dir
      containers:
      - name: myapp-container
        image: myapp:v1.0.0
        command: ['/myapp']
        volumeMounts:
        - name: workdir-volume
          mountPath: /work-dir

      volumes:
      - name: workdir-volume
        emptyDir: {}

上面的代码片段中,我们在 template 字段中定义了 Init Container 和业务容器,然后通过 Deployment 控制器来创建和管理多个 Pod。

五,容器资源配额

在 Kubernetes 中,可以为 Pod 和容器定义资源配额(Resource Quota),以限制它们所能使用的 CPU、内存等资源数量。这对于避免应用程序过度占用资源、提高系统可靠性和稳定性非常有用。

在 Golang 应用程序中,可以通过设置环境变量或代码方式来定义容器资源配额。以下是示例代码:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
spec:
  containers:
  - name: myapp-container
    image: myapp:v1.0.0
    command: ['/myapp']
    env:
    - name: CPU_LIMIT
      value: "500m"
    - name: MEMORY_LIMIT
      value: "256Mi"

上面的 YAML 文件定义了一个 Pod,其中包含一个名为 myapp-container 的容器,并且设置了它的 CPU 和内存限制分别为 500m 和 256Mi。

在 Golang 应用程序中,可以通过读取环境变量获取这些值并进行相应处理。例如:

import (
	"os"
)

func main() {
	cpuLimit := os.Getenv("CPU_LIMIT")
	memoryLimit := os.Getenv("MEMORY_LIMIT")
	// 使用 cpuLimit 和 memoryLimit 进行相应处理...
}

需要注意的是,Kubernetes 支持更多的资源配额类型和更细粒度的控制方式。例如,可以针对不同的命名空间、标签或用户组设置不同的配额规则。此外,还可以使用 Kubernetes 的自动扩缩容功能来根据资源使用情况动态调整 Pod 和容器的数量,以确保系统始终处于最佳状态。

六,容器声明周期处理函数

在 Golang 应用程序中,可以使用以下生命周期处理函数来处理容器的声明周期事件:

  • func init():在容器启动时执行。通常用于初始化应用程序的环境变量、配置等。
  • func main():在容器启动后立即执行。通常用于启动应用程序的服务或者监听端口。
  • func shutdown():在容器关闭之前执行。通常用于清理资源和状态,并停止正在运行的任务。

这些函数是可选的,您可以根据需要选择使用其中一个或多个。例如,如果您的应用程序不需要进行特殊初始化或清理操作,则无需编写 initshutdown 函数。

以下是示例代码,展示如何使用生命周期处理函数:

package main

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"os/signal"
)

func init() {
	fmt.Println("Initializing...")
}

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello World!"))
	})

	srv := &http.Server{Addr: ":8080"}

	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			fmt.Printf("listen: %s\n", err)
			os.Exit(1)
		}
	}()

	fmt.Println("Started server at :8080")

	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	sig := <-c
	fmt.Println("Got signal:", sig)

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	srv.Shutdown(ctx)
	fmt.Println("Server stopped gracefully")
}

func shutdown() {
	fmt.Println("Shutting down...")
	// 清理资源和状态
}

在上面的示例中,init 函数用于输出一个初始化消息。main 函数启动了一个 HTTP 服务器,并使用 signal.Notify() 监听 os.Interrupt 信号(即 Ctrl+C),以便在接收到该信号时优雅地关闭服务器。最后,当服务器关闭时,shutdown 函数被调用以清理资源。

需要注意的是,在 Kubernetes 中管理容器生命周期的方式有所不同。Kubernetes 使用一种称为控制器(Controller)的机制来管理容器和 Pod 的运行状态,并提供了相应的生命周期钩子函数来处理容器事件。

七,容器健康检查与服务可用性

在 Golang 应用程序中,可以使用以下方法来实现容器健康检查和服务可用性:

  1. HTTP 接口健康检查

HTTP 接口是最常见的容器健康检查方式之一。您可以在应用程序中添加一个简单的 /healthz 接口来提供健康状态信息。

示例代码如下:

package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte("OK"))
	})

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello World!"))
	})

	if err := http.ListenAndServe(":8080", nil); err != nil {
		fmt.Println(err)
	}
}

在上面的示例中,我们添加了一个 /healthz 接口来返回 200 OK 状态码和 "OK" 字符串表示应用程序正常运行。当 Kubernetes 发现该容器的该接口返回非 200 的 HTTP 状态码时,将会认为该容器不健康并重启该容器或者 Pod。

  1. TCP Socket 健康检查

除了 HTTP 接口外,还可以通过监听一个 TCP 端口来进行健康检查。这种方式要求 Kubernetes 配置 livenessProbereadinessProbe 分别使用 TCP socket 进行探测。

示例代码如下:

package main

import (
    "fmt"
    "net"
)

func main() {
    l, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Println(err)
    }
    
    defer l.Close()

    for {
        conn, err := l.Accept()
        if err != nil {
            fmt.Println(err)
            continue
        }

        go func(c net.Conn) {
            c.Write([]byte("OK\n"))
            c.Close()
        }(conn)
    }
}

在上面的示例中,我们监听 8080 端口并返回 "OK" 字符串表示容器健康。当 Kubernetes 发现该容器的 TCP Socket 不可用时,将会认为该容器不健康并重启该容器或者 Pod。

需要注意的是,在使用 TCP Socket 进行健康检查时,需要确保应用程序在启动后尽快开始监听端口,并且能够正确响应连接请求。否则 Kubernetes 将无法正常进行健康检查。

相关推荐
hummhumm34 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
好奇的菜鸟1 小时前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.01 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
Code_Artist2 小时前
使用Portainer来管理并编排Docker容器
docker·云原生·容器
灼烧的疯狂4 小时前
K8S + Jenkins 做CICD
容器·kubernetes·jenkins
wenyue11215 小时前
Revolutionize Your Kubernetes Experience with Easegress: Kubernetes Gateway API
容器·kubernetes·gateway
ifanatic6 小时前
[面试]-golang基础面试题总结
面试·职场和发展·golang
懒是一种态度6 小时前
Golang 调用 mongodb 的函数
数据库·mongodb·golang
梅见十柒7 小时前
wsl2中kali linux下的docker使用教程(教程总结)
linux·经验分享·docker·云原生
XINGTECODE7 小时前
海盗王集成网关和商城服务端功能golang版
开发语言·后端·golang