容器中运行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 并访问 私有仓库
前提条件:
- 容器能运行podman
- 容器能访问私有仓库
对于问题1. 上面的方法已经解决,主要是问题2,这个跟普通虚机配置 podman 私有仓库访问方法一样。分两种情况:- 基于http 运行的 私有仓库, 直接在 /etc/containers/registries.conf 配置 不用接受ssl 检测即可
[registries.insecure]
registries = ["kanq.k8s.com:7443"]
- 基于 https 运行的私有仓库 需要将你配置仓库创建的ca 证书, 私有仓库服务端的证书和密钥拷到/etc/containers/certs.d/kanq.k8s.com:7443(这个目录名要跟你registries.conf 配置的一样)目录下就行。
- 如果你向我一样用的域名 还需要保证 启动的容器能对 你的域名解析这里我使用一个最直接的方式直接在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 上跑起来。将仓库地址(证书)配置好就可以了。
实现方式很多:
- 如果是http 模式私有仓库直接在你运行命令前sed 命令修改 对应配置文件就行了
- 如果是https 模式就需要解决证书得问题了。
- 做镜像时dockerfile 中修改好直接将 证书和密钥拷到对应位置,但是如果仓库发生了变化就得重新制作镜像。
- 既然是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{...}
}
写在最后:
- 上面的例子中有个恶心的事儿是我使用得是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”
- 还有 由于我们需要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
- 我还需要我的podman 镜像中能执行 kubectl 命令 ,这就需要我容器中有kubectl 这个二进制文件,这个好实现,直接做镜像时拷入即可,但是 访问所需的 kubeconfig 文件就不行了, 不能部署一个k8s 就重做个镜像吧,这里又得用到卷挂载了,有两个选择,一个是hostpath 但是如果你的k8s 是用kubeasz 部署的就会发现k8s slave 节点 配置的 ~/.kube/config 里面的server ip 是127.0.0.1 这个挂到容器中是无法访问k8s 集群的。当然你可以在挂载后运行kubectl 命令sed 修改 运行完后 再改回来。但是这个太麻烦了。这里我选则用 configmap 的卷挂载,一来 这个配置文件比较大,用secert 不大合适而且 这个文件中密钥相关已经做了加密操作。