searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

在Kubernetes中保存容器镜像

2023-06-13 07:17:50
267
0

Kubernetes是一款开源的容器编排平台,可以帮助用户轻松地部署、扩展和管理容器化应用程序。在Kubernetes中,容器镜像是非常重要的一部分,因为它们包含了应用程序所需的所有依赖项和代码。在这篇文章中,我们将介绍如何在Kubernetes中保存容器镜像,并使用Docker Go SDK编写代码自动化这个过程。

Docker in Docker

Docker in Docker(DinD)是一个镜像,它允许在Docker容器内运行Docker。这是一个非常方便的解决方案,因为它允许您在没有安装Docker的主机上运行Docker容器。在Kubernetes中,我们可以使用Docker in Docker镜像来在容器中执行docker命令,操作主机docker,制作镜像并推送到远程仓库。

首先,我们需要在Kubernetes集群中创建一个Docker in Docker容器。我们可以使用Docker命令来创建容器:

docker run --privileged --name dind -d docker:dind
这个命令将在后台启动一个Docker in Docker容器,并为它分配一个名称“dind”。在创建容器时,我们需要使用“--privileged”选项,这将允许容器访问主机的Docker守护程序。这个选项通常是不安全的,因为它允许容器绕过主机的安全限制。因此,我们需要确保只有受信任的用户可以访问这个容器。

接下来,我们需要将主机的“/var/run/docker.sock”文件挂载到容器中。这个文件是Docker守护程序的UNIX套接字,可以让容器与主机的Docker守护程序通信。我们可以使用以下命令来挂载文件:

docker run --privileged --name dind -v /var/run/docker.sock:/var/run/docker.sock -d docker:dind

现在,我们已经准备好在Docker in Docker容器中运行Docker命令。我们测试一下docker命令是否正常

这里可以看到docker命令执行正常,也就说明可以顺利连接到主机上的docker上。

 

过滤并筛选出执行镜像

我们知道,每个pod都会创建出至少两个容器,一个是pause容器,一个是作业主容器。

主容器容器名称规格大概为:k8s_{podname}_{namespace}_{random_str},pause容器的名称开头是: k8s_POD_{podname}_{namespace}...

所以可以通过这样的规则过滤出指定的容器。

 

制作镜像

新版的docker新增了commit 命令,可以用来直接把运行中的容器制作为镜像:

可以看到已经成功保存镜像了。

 

提交镜像

使用docker login登录到镜像仓库,然后通过docker push推送镜像,这里没有大的问题,现在再k8s中保存容器镜像的可行性验证了,接下来就是通过编码实现程序打包提交镜像。

 

Docker Go SDK

Docker Go SDK是一个用于与Docker守护程序通信的Go语言库。它允许我们编写代码来拉取、构建和推送Docker镜像,我们可以使用Docker Go SDK编写代码来保存容器镜像。

首先,我们需要安装Docker Go SDK。我们可以使用以下命令来安装:

go get github.com/docker/docker/client

接下来,我们可以使用以下代码来连接到Docker守护程序,并拉取、构建和推送容器镜像:

	ctx := context.Background()
	dockerOpt := client.FromEnv

	cli, err := client.NewClientWithOpts(dockerOpt, client.WithAPIVersionNegotiation())
	if err != nil {
		logrus.Errorf("getClientError:%+v", err)
		return
	}

	//查询容器列表
	containers, err := cli.ContainerList(context.Background(), types.ContainerListOptions{Filters: filters.Args{}})
	if err != nil {
		logrus.Errorf("ContainerListError:%+v", err)
		return
	}

	targetContainer := types.Container{}
	for _, oneContainer := range containers {
		for _, name := range oneContainer.Names {
			if strings.Contains(name, podName) && strings.Contains(name, namespace) && !strings.HasPrefix(name, "/k8s_POD_") {
				targetContainer = oneContainer
				break
			}
		}
	}
	logrus.Infof("过滤出容器:%v", targetContainer.Names)
	// 制作镜像
	imageId, err := cli.ContainerCommit(ctx, targetContainer.ID, types.ContainerCommitOptions{
		Pause:  false,
		Config: &container.Config{Image: imageName},
	})

	if err != nil {
		logrus.Errorf("ContainerCommitError:%+v", err)
		return
	}
	fmt.Println(imageId)
	//给镜像打tag
	err = cli.ImageTag(ctx, imageId.ID, imageName)
	if err != nil {
		logrus.Errorf("ImageTagError:%+v", err)
		return
	}
 
上面代码中,我们先创建出docker客户端,然后查询出所有容器列表,筛选出想要的容器,把容器保存为镜像,然后给镜像打tag,打完tag就可以执行推送了。在执行这个代码之前,我们需要确保程序是运行在一个Docker in Docker容器中,并将主机的“/var/run/docker.sock”文件挂载到容器中。

登录镜像仓库&提交镜像

	//登录registry
	authConfig := registry.AuthConfig{
		Username:      loginUserName,
		Password:      loginPassword,
		ServerAddress: parsedUrl.Host,
	}
	logrus.Info("登录中,请稍后")
	_, err = cli.RegistryLogin(context.Background(), authConfig)
	if err != nil {
		logrus.Errorf("RegistryLoginError:%+v", err)
		return
	}
	logrus.Info("登录成功。")
	encodedJSON, err := json.Marshal(authConfig)
	if err != nil {
		logrus.Errorf("AuthInfoMarshalError:%+v", err)
		return
	}
	authStr := base64.URLEncoding.EncodeToString(encodedJSON)
	logrus.Infof("镜像: %s 推送中......", imageName)
	// 推送镜像
	pushResp, err := cli.ImagePush(ctx, imageName, types.ImagePushOptions{
		All:          false,
		RegistryAuth: authStr,
	})
	//TODO 打印镜像上传进度
	if err != nil {
		logrus.Errorf("ImagePushError:%+v", err)
		return
	}
 

按需部署image-committer

上面我们已经跑通流程了,在需要保存指定容器为镜像的时候,查询出指定pod运行在哪个机器节点上,然后部署一个k8s原生的job到相同的节点上,让程序运行起来就可以,编排示例如下

apiVersion: batch/v1
kind: Job
metadata:
  name: image-committer-0032
  namespace: ns-145
spec:
  parallelism: 1
  completions: 1
  activeDeadlineSeconds: 600
  backoffLimit: 600
  template:
    spec:
      volumes:
        - name: docker-secret
          secret:
            secretName: cyl-docker-secret
            defaultMode: 420
        - name: docker-socket
          hostPath:
            path: /var/run/docker.sock
      containers:
        - name: committer
          image: harbor.ctyuncdn.cn/bc-test/image-committer:v0.5
          command:
            - /opt/bc-srv/bin/image-committer
            - --pod-name="nginx-demo01-7bb8596b59-b2dc6"
            - --namespace=ns-145
            - --new-image-name="harbor.xxx.cn/bc-test/nginx-commiter:v0.3"
          resources: {}
          volumeMounts:
            - name: docker-secret
              mountPath: /etc/docker/image-committer
            - name: docker-socket
              mountPath: /var/run/docker.sock
          imagePullPolicy: Always
      restartPolicy: OnFailure
      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      nodeName: node-2-0-2
      securityContext: {}
      schedulerName: default-scheduler

 

 

结论

在这篇文章中,我们介绍了如何在Kubernetes中保存容器镜像,并使用Docker Go SDK编写代码自动化这个过程。通过使用Docker in Docker镜像,我们可以在Kubernetes集群中轻松地拉取、构建和推送容器镜像。同时,使用Docker Go SDK编写代码可以帮助我们实现自动化的容器镜像保存功能,提高开发和部署效率。

0条评论
0 / 1000
秀才去当兵
2文章数
0粉丝数
秀才去当兵
2 文章 | 0 粉丝