容器中运行podman,并作为 动态slave节点应用于jenkins中

容器中运行podman,并作为 动态slave节点应用于jenkins中

基于k8s+jenkins 的ci/cd 框架下,所有的操作(下载代码,静态代码检测,代码编译,UT ,版本制作,版本推送,AC)都是在容器中执行的。如果版本制作,版本推送的制品是镜像的话,就需要dind 技术 及在docker 起的容器中运行 docker 命令。但是由于docker 依赖于 docker.sock 这个守护进程, 但是 容器中是没法运行这个进程的。所以人们想到的方法是用特权模式(–privileged)运行安装了docker的容器,并将 docker.sock 挂载到 这个容器中来实现,但是这样就有个局限,必须保证宿主机上运行了docker ,并且 版本要和容器中的docker 匹配。一旦不符合条件,就会运行失败。而且在不久的将来 docker 要收费了。k8s 也打算跟docker 解耦。直接用containerd 作为 容器运行时。这中情况下我想能否使用 podman 作为容器中镜像制作的工具了? 因为 它不需要什么守护进程, 这样就跟宿主机解耦了,它不用关心宿主机是否安装了docker,podman 。经实验确实可以实现。

制作能运行podman 镜像

下面贴出的就是制作安装了podman 的基础镜像 的dockerfile

FROM centos:centos8
ENV KUBECONFIG /home/config
RUN yum install -y podman && \
    sed -i 's/#mount_program/mount_program/g' /etc/containers/storage.conf && \
    sed -i 's/mountopt/#mountopt/g' /etc/containers/storage.conf

是不是非常简单,使用一个centos 的基础镜像然后 yum install 安装下 ,修改2个配置就可以了。
下面就是运行的例子,当然特权模式时必须的。

[root@localhost ~]# docker run -it --rm --privileged a3c2a64b812f bash
[root@b3398fe3daee /]# podman info
host:
  arch: amd64
  buildahVersion: 1.21.3
  cgroupControllers:
  - cpuset
....

Pind(podman in docker) 应用于jenkins slave 节点

容器中运行podman 并访问 私有仓库

前提条件:

  1. 容器能运行podman
  2. 容器能访问私有仓库
    对于问题1. 上面的方法已经解决,主要是问题2,这个跟普通虚机配置 podman 私有仓库访问方法一样。分两种情况:
    1. 基于http 运行的 私有仓库, 直接在 /etc/containers/registries.conf 配置 不用接受ssl 检测即可
[registries.insecure]
registries = ["kanq.k8s.com:7443"]
  1. 基于 https 运行的私有仓库 需要将你配置仓库创建的ca 证书, 私有仓库服务端的证书和密钥拷到/etc/containers/certs.d/kanq.k8s.com:7443(这个目录名要跟你registries.conf 配置的一样)目录下就行。
  2. 如果你向我一样用的域名 还需要保证 启动的容器能对 你的域名解析这里我使用一个最直接的方式直接在hosts 文件中写死。当然如果有自己的域名解析服务器可以添加到/etc/resolv.conf
vi /etc/hosts
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
172.16.3.154 kanq.k8s.com

解决完上面两个问题,就可以直接 podman login -u admin -p Harbor kanq.k8s.com:7443 登录,然后就可以实现podman build pull push 操作了

作为动态slave 用于 基于k8s 的 jenkins 服务器

这个说白了就是将这个镜像以pod 的方式在k8s 上跑起来。将仓库地址(证书)配置好就可以了。
实现方式很多:

  1. 如果是http 模式私有仓库直接在你运行命令前sed 命令修改 对应配置文件就行了
  2. 如果是https 模式就需要解决证书得问题了。
    1. 做镜像时dockerfile 中修改好直接将 证书和密钥拷到对应位置,但是如果仓库发生了变化就得重新制作镜像。
    2. 既然是k8s 场景 我们可以用卷得方式挂载到 pod中。这里不建议用hostpath 模式,因为这样需要你保证每个k8s 得slave节点上都有对应得证书。这里使用secert 因为secert 相较于configmap 安全些 它会对数据做bead64 加码。
使用kubectl 命令创建docker secret  
kubectl create secret generic dockersecret --from-file {{harborCertsPath}}/ca.crt --from-file {{harborCertsPath}}/{{harborCert}} --from-file {{harborCertsPath}}/{{harborKey}} -n jenkins
在jenkins 得pod 模板中配置卷挂载即可 如下:
pipeline{
    agent {
        kubernetes{
            yaml '''
                apiVersion: "v1"
                kind: "Pod"
                metadata:
                  name: "devops"
                spec:
                  containers:
                  - name: "jnlp"
                    image: "kanq.k8s.com:7443/devops/centos-slave-jnlp-git:latest"
                    imagePullPolicy: "IfNotPresent"
                    volumeMounts:
                    - mountPath: "/home/jenkins/agent"
                      name: "workspace-volume"
                      readOnly: false
                    workingDir: "/home/jenkins/agent"
                  - name: "build"
                    args:
                    - "999999"
                    command:
                    - "sleep"
                    image: "kanq.k8s.com:7443/devops/golang:1.17"
                    imagePullPolicy: "IfNotPresent"
                    name: "build"
                    volumeMounts:
                    - mountPath: "/home/jenkins/agent"
                      name: "workspace-volume"
                      readOnly: false
                    workingDir: "/home/jenkins/agent"
                  - name: "podman"
                    args:
                    - "999999"
                    command:
                    - "sleep"
                    env:
                    - name: HarborU
                      valueFrom:
                        secretKeyRef:
                          name: harbor
                          key: username
                    - name: HarborP
                      valueFrom:
                        secretKeyRef:
                          name: harbor
                          key: password
                    image: "kanq.k8s.com:7443/devops/centos-slave-podman-kubectl:latest"
                    imagePullPolicy: "IfNotPresent"
                    securityContext:
                      privileged: true
                    volumeMounts:
                    - mountPath: "/etc/containers/certs.d/kanq.k8s.com/"
                      name: podman
                    - mountPath: "/home/"
                      name: k8sconfigmap
                    - mountPath: "/home/jenkins/agent"
                      name: "workspace-volume"
                      readOnly: false
                    workingDir: "/home/jenkins/agent"
                  activeDeadlineSeconds: 60000
                  volumes:
                  - emptyDir:
                      medium: ""
                    name: "workspace-volume"
                  - secret:
                      secretName: dockersecret
                    name: podman
                  - secret:
                      secretName: harbor
                    name: harbor
                  - configMap:
                      name: k8sconfig
                    name: k8sconfigmap
            '''
        }
    }
    stages{...}
}

写在最后:

  1. 上面的例子中有个恶心的事儿是我使用得是harbor+https+非默认端口(443)的私有仓库,实现起来就更繁琐些,因为 k8s 挂卷不支持 xxx:xxx 这样得目录名格式,所以我挂卷时,只能折中下将":7443" 去掉,在运行podman 命令前 加一个 “cp -r /etc/containers/certs.d/kanq.k8s.com/ /etc/containers/certs.d/kanq.k8s.com:7443”
  2. 还有 由于我们需要pull(podman build 会下载基础镜像) push 镜像所以要登录到这个私有仓库。直接在jenkinsfile 中明文写出来不大安全。所以需要使用 podman login --password-stdin 这个参数来输入密码,有两种方式:
a. 用文件的方式
echo "xxx">password.txt
cat password.txt |podman login -u admin --password-stdin kanq.k8s.com:7443
b. 用变量的方式 
HarborP="xxxx"
echo "$HarborP"  |podman login -u admin --password-stdin kanq.k8s.com:7443

这里 两种方式 ,如果直接在jenkinsfile 中执行起不到任何效果。都执行了一次echo 明文打印,
所以打算用 卷挂载的方式这里我还是选择了 secert ,
但是跟挂载密钥不同我将它挂载成了环境变量。
这里需要说明下:
如果挂载成文件 如果你的secert 跟新了 你容器中的文件也会同步跟新,
但是如果挂载成了环境变量,它就不会跟新了,
这里就需要根据实际情况做取舍了。我这里为了练习使用secert 所以用的是环境变量的方式
创建secert 命令: 由于 变量比较简单,这里直接用的是变量key:value 的格式创建的
kubectl create secret generic harbor --from-literal username={{harborU}} --from-literal password={{harborP}} -n jenkins
  1. 我还需要我的podman 镜像中能执行 kubectl 命令 ,这就需要我容器中有kubectl 这个二进制文件,这个好实现,直接做镜像时拷入即可,但是 访问所需的 kubeconfig 文件就不行了, 不能部署一个k8s 就重做个镜像吧,这里又得用到卷挂载了,有两个选择,一个是hostpath 但是如果你的k8s 是用kubeasz 部署的就会发现k8s slave 节点 配置的 ~/.kube/config 里面的server ip 是127.0.0.1 这个挂到容器中是无法访问k8s 集群的。当然你可以在挂载后运行kubectl 命令sed 修改 运行完后 再改回来。但是这个太麻烦了。这里我选则用 configmap 的卷挂载,一来 这个配置文件比较大,用secert 不大合适而且 这个文件中密钥相关已经做了加密操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值