Devops-day6-Jenkins流水线容器化+Harbor私有仓库

Jenkins流水线容器化+Harbor私有仓库

将Jenkins的编译环境迁移至k8s中

Harbor

部署私有镜像仓库(Harbor)

Harbor 是由 VMware 公司中国团队为企业用户设计的 Registry server 开源项目,包括了权限管理(RBAC)、LDAP、审计、管理界面、自我注册、HA 等企业必需的功能,同时针对中国用户的特点,设计镜像复制和中文支持等功能。

作为一个企业级私有 Registry 服务器,Harbor 提供了更好的性能和安全。提升用户使用 Registry 构建和运行环境传输镜像的效率。Harbor 支持安装在多个 Registry 节点的镜像资源复制,镜像全部保存在私有 Registry 中, 确保数据和知识产权在公司内部网络中管控。

另外,Harbor 也提供了高级的安全特性,诸如用户管理,访问控制和活动审计等:https://goharbor.io/

配置HTTPS

默认情况下,Harbor不附带证书,可以在没有安全性的情况下部署Harbor,以便您可以通过HTTP连接到它。但是,只有在没有连接到外部Internet的空白测试或开发环境中,才可以使用HTTP。在没有空隙的环境中使用HTTP会使您遭受中间人攻击;要配置HTTPS,必须创建SSL证书。

您可以使用由受信任的第三方CA签名的证书,也可以使用自签名证书。本节介绍如何使用 OpenSSL创建CA,以及如何使用CA签署服务器证书和客户端证书。

一、生成证书

  • 在生产环境中,您应该从CA获得证书。在测试或开发环境中,您可以生成自己的CA。要生成CA证书,请运行以下命令。

1.生成CA证书私钥

mkdir /opt/cert
cd /opt/cert
openssl genrsa -out ca.key 4096

2.生成CA证书

openssl req -x509 -new -nodes -sha512 -days 3650 \
-subj "/C=CN/ST=ShangHai/L=ShangHai/O=Oldboy/OU=Linux/CN=192.168.12.11" \
-key ca.key \
-out ca.crt

3.生成服务器证书

openssl genrsa -out 192.168.12.11.key 4096

4.生成证书签名请求

openssl req -sha512 -new \
-subj "/C=CN/ST=ShangHai/L=ShangHai/O=Oldboy/OU=Linux/CN=192.168.12.11" \
-key 192.168.12.11.key \
-out 192.168.12.11.csr

5.生成一个x509 v3扩展文件

  • 域名版(企业用内,此处没用到)
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names

[alt_names]
DNS.1=yourdomain.com
DNS.2=yourdomain
DNS.3=hostname
EOF
  • IP版(此处使用)
cat > v3.ext <<-EOF
authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = IP:192.168.12.11
EOF

6.使用该v3.ext文件生成证书

openssl x509 -req -sha512 -days 3650 \
-extfile v3.ext \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-in 192.168.12.11.csr \
-out 192.168.12.11.crt

7.安装Docker

yum install yum-utils -y
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache fast
yum -y install docker-ce
systemctl enable --now docker

二、颁发证书

颁发证书给Harbor和Docker来用

生成后ca.crt,192.168.12.70.crt和192.168.12.70.key文件,必须将它们提供给Harbor和Docker,和重新配置港使用它们

1.分发证书

  • 将服务器证书和密钥复制到Harbor主机上的certficates文件夹中
openssl x509 -inform PEM -in 192.168.12.11.crt -out 192.168.12.11.cert

mkdir -pv /etc/docker/certs.d/192.168.12.11/

cp 192.168.12.11.{cert,key} ca.crt /etc/docker/certs.d/192.168.12.11/

# 如果nginx端口默认部署443和80,后面port则指定为端口号
/etc/docker/certs.d/192.168.12.11:port
/etc/docker/certs.d/192.168.12.11:port

# 复制Harbor证书(当前测试执行此步)
mkdir -p /data/cert 
cp 192.168.12.11.crt  /data/cert
cp 192.168.12.11.key /data/cert

# 重启docker,加载证书
systemctl restart docker

2.安装Harbor

链接:https://github.com/goharbor/harbor

# 进入下载目录
cd /opt

# 下载
wget https://github.com/goharbor/harbor/releases/download/v2.1.0-rc2/harbor-offline-installer-v2.1.0-rc2.tgz

# 解压,妈妈,
tar xf harbor-offline-installer-v2.1.0-rc2.tgz

# 进入harbor目录
cd harbor/

# 下载docker-composrt
curl -L "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 打印出上方地址,复制到浏览器下载
echo "https://github.com/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)"
https://github.com/docker/compose/releases/download/1.24.1/docker-compose-Linux-x86_64

# 设置可执行权限
mv docker-compose /usr/local/bin/
chmod +x /usr/local/bin/docker-compose 

# 测试docker-composer
docker-compose -v

# 修改harbor.yaml以下三处
hostname: 192.168.12.11
certificate: /data/cert/192.168.12.11.crt
private_key: /data/cert/192.168.12.11.key

# 生成配置文件,其实就是导入压缩包内的镜像	# 
docker load < harbor.v2.1.0.tar.gz
./prepare 

# 清空相关容器
docker-compose down

# 安装并启动
./install.sh
# 耐心等待...
Creating network "harbor_harbor" with the default driver
Creating harbor-log ... done
Creating harbor-db     ... done
Creating harbor-portal ... done
Creating registry      ... done
Creating redis         ... done
Creating registryctl   ... done
Creating harbor-core   ... done
Creating nginx             ... done
Creating harbor-jobservice ... done
✔ ----Harbor has been installed and started successfully.----

# 若报错,则查看是否端口被占用,结束相关端口即刻,一般为nginx的80端口
# 修改端口可改为8089或其他,两处都要修改,再次执行脚本即可
vim harbor.yaml
hostname: 192.168.12.11:8089
port: 8089
# 先执行prepare,否则配置不生效
./prepare
./install.sh

访问ip测试:https://192.168.12.11:8089
用户名:admin
密码  :Harbor12345

1618887958659

3.测试Harbor

# 测试登录
docker login 192.168.12.11 --username=admin --password=Harbor12345

# 测试打包、推送到私有仓库
[root@k8s-master1 harbor]# docker pull nginx
[root@k8s-master1 harbor]# docker tag 62d49f9bab67 192.168.12.11/test/nginx:v1
[root@k8s-master1 harbor]# docker push 192.168.12.11/test/nginx:v1

# 拉取
[root@k8s-master1 harbor]# docker pull 192.168.12.11/test/nginx:v1

# k8s集群所有节点都必须给docker做证书认证
for i in n1 n2;
do
ssh $i "mkdir -pv /etc/docker/certs.d/192.169.12.11/"
scp 192.168.12.11.cert 192.168.12.11.key ca.crt root@$i:/etc/docker/certs.d/192.168.12.11/
ssh $i "systemctl restart docker"
done

4.配置多个Harbor仓库

以下仅为配置举例,可用docker登录到多个私有仓库,公有仓库无需配置
172.23.0.244为集群vip地址,按理说可通过集群内的所有地址来访问所有服务
PS:用时删掉中文注释
参考:Docker配置连接多个私有仓库

{
  "registry-mirrors": [
      "https://xj6uu5rz.mirror.aliyuncs.com",	# 阿里云镜像加速器	
      "https://registry.docker-cn.com",			# docker官方镜像加速
      "http://hub-mirror.c.163.com",			# 163镜像加速
      "https://docker.mirrors.ustc.edu.cn",		# ustc镜像加速
  ],
  
  "insecure-registries": [
      "harbor1.peng.cn",	# harbor1私有仓库解析:域名
      "172.23.0.244:30002",	# harbor1私有仓库解析:IP
      "harbor2.peng.cn",	# harbor2私有仓库解析:域名
      "172.23.0.244:30003"	# harbor2私有仓库解析:IP
  ],
  
  "exec-opts": ["native.cgroupdriver=systemd"],
      "max-concurrent-downloads": 10,	# docker镜像分布式拉取
      "max-concurrent-uploads": 5,		# docker镜像分布式上传
      "log-opts": {
      "max-size": "300m",	# docker日志最大保留大小
      "max-file": "2"		# docker日志最多保留个数
  },
     "live-restore": true	# 重启docker时不重启运行中的容器
}

# 添加到/etc/hosts做解析
172.23.0.244 harbor1.peng.cn harbor2.peng.cn

# 重载docker
systemctl daemon-reload
systemctl restart docker.service

# 登录测试	harbor1.peng.cn
[root@k8s-master02 ~]# docker login harbor1.peng.cn -uadmin -pHarbor12345
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

# 登录测试	harbor2.peng.cn
[root@k8s-master02 ~]# docker login harbor2.peng.cn -uadmin -pHarbor12345
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded

# 查看认证记录
[root@k8s-master02 ~]# cat /root/.docker/config.json
{
	"auths": {
		# 此为dockerhub共有仓库
		"https://index.docker.io/v1/": {	
			"auth": "MTg5NTQzNTQ2NzE6MjAyMC5wZW5n"
		},
		"harbor1.peng.cn": {
			"auth": "YWRtaW46SGFyYm9yMTIzNDU="
		},
		"harbor2.peng.cn": {
			"auth": "YWRtaW46SGFyYm9yMTIzNDU="
		}
	}

5.清理镜像,释放空间

Harbor私有仓库运行一段时间后,仓库中存有大量镜像,会占用太多的存储空间。直接通过Harbor界面删除相关镜像,并不会自动删除存储中的文件和镜像。需要停止Harbor服务,执行垃圾回收命令,进行存储空间清理和回收。
1、首先,删除Harbor的UI中的存储库。这是软删除。您可以删除整个存储库或仅删除它的标签。软删除后,Harbour中不再管理存储库,但是存储库的文件仍然保留在Harbour的存储中。
2、接下来,使用注册表的垃圾回收(GC)删除存储库的实际文件。在执行GC之前,确保没有人推送图像或Harbour根本没有运行。如果有人在GC运行时推送镜像,则存在镜像层被错误删除的风险,从而导致镜像损坏。所以在运行GC之前,首选的方法是先停止Harbour。

# 先登录Web端Harbor仓库,清空build/*,再执行此脚本(切勿清除base/的基础镜像)
cd /usr/local/harbor
docker-compose stop
docker run -it --name gc --rm --volumes-from registry vmware/registry-photon:v2.6.2-v1.5.0 garbage-collect /etc/registry/config.yml
docker-compose start

# 检查可清理镜像数
# docker run -it --name gc --rm --volumes-from registry vmware/registry-photon:v2.6.2-v1.5.0 garbage-collect --dry-run /etc/registry/config.yml | wc -l

Jenkins流水线实现容器化

最近我们构建和部署服务的方式与原来相比简直就是突飞猛进,像那种笨拙的、单一的、用于构建单体式应用程序的方式已经是过去式了。

现在的应用为了提供更好的拓展性和可维护性,都会去拆解成各种相互依赖小、解耦性强的微服务,这些服务有各自的依赖和进度。这跟我们的Kubernetes不谋而合。

我是将Harbor私有镜像仓库装在了k8s-master1上~

1.用JenKins连接K8S

1)创建admin-csr.json(kubernetes)

mkdir /opt/cert && cd /opt/cert

cat > admin-csr.json << EOF
{
    "CN":"admin",
    "key":{
        "algo":"rsa",
        "size":2048
    },
    "names":[
        {
            "C":"CN",
            "L":"BeiJing",
            "ST":"BeiJing",
            "O":"system:masters",
            "OU":"System"
        }
    ]
}
EOF

2)安装证书生成工具(kubernetes)

# 下载
wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64

# 设置执行权限
chmod +x cfssljson_linux-amd64 cfssl_linux-amd64

# 移动到/usr/local/bin
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
mv cfssl_linux-amd64 /usr/local/bin/cfssl

3)创建证书私钥(kubernetes)

cfssl gencert -ca=/etc/kubernetes/pki/ca.crt -ca-key=/etc/kubernetes/pki/ca.key --profile=kubernetes admin-csr.json | cfssljson -bare admin

4)配置证书(kubernetes)

openssl pkcs12 -export -out ./jenkins-admin.pfx -inkey ./admin-key.pem -in ./admin.pem -passout pass:123456

5)配置jenkins链接k8s(Jenkins)

系统管理 ---> 系统配置 ---> cloud

1618889996780

1618890017200

1618890039751

1618890155177

1618890195569

1618890381266

1618890455554

2.链接K8s测试

[root@k8s-master1 ~]# kubectl create secret generic kubeconfig --from-file=.kube/config 
secret/kubeconfig created
pipeline {
  agent {
    kubernetes{
      cloud "${KUBERNETES_NAME}"  #集群名字
      slaveConnectTimeout 1200  #连接超时时间
      yaml '''
apiVersion: v1
kind: pod  #metadata 流水线默认创建,此处不许设置
spec:
  containers:
    - name: jnlp
      image: jenkins/inbound-agent:4.7-1-jdk11
      args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
      imagePullPolicy: IfNotPresent  #镜像拉取策略:不存在则拉取
      volumeMounts:  #挂载时区,用一个系统时间
        - mountPath: "/etc/localtime"
          name: "volume-2"
          readOnly: false
    - name: docker  #与流水线中容器名称必须一致container(name: 'docker', shell: 'echo')
      image: docker:19.03.15-git  #docker容器镜像必须19版本
      imagePullPolicy: IfNotPresent
      tty: true
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "volume-2"
          readOnly: false
        - mountPath: "/var/run/docker.sock"  
          name: "volume-docker"
          readOnly: false
        - mountPath: "/etc/hosts"
          name: "volume-hosts"
          readOnly: false
    - name: kubectl  #与流水线中容器名称必须一致 container(name: 'kubectl', shell: 'echo')
      image: bitnami/kubectl:1.20.2  #kubectl版本要与k8s本版一致
      imagePullPolicy: IfNotPresent
      tty: true
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "volume-2"
          readOnly: false
        - mountPath: "/var/run/docker.sock"
          name: "volume-docker"
          readOnly: false
        - mountPath: "/root/.kube"
          name: "kubeconfig"
          readOnly: false
    - name: golang  #业务容器
      image: golang:1.16.3
      imagePullPolicy: IfNotPresent
      tty: true
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
      command:  #默认指令,不执行相当于占位符
        - "cat"
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "volume-2"
          readOnly: false
    - name: maven  #java所需容器镜像
      image: maven:3.6.3-openjdk-8
      tty: true
      command:
        - "cat"
      volumeMounts:
        - mountPath: "/etc/localtime"
          name: "volume-2"
          readOnly: false
        - mountPath: "/root/.m2/repository"
          name: "volume-maven-repo"
          readOnly: false
      env:
        - name: "LANGUAGE"
          value: "en_US:en"
        - name: "LC_ALL"
          value: "en_US.UTF-8"
        - name: "LANG"
          value: "en_US.UTF-8"
  volumes:
    - name: volume-maven-repo
      emptyDir: {}
    - name: volume-2
      hostPath:
        path: "/usr/share/zoneinfo/Asia/Shanghai"
    - name: kubeconfig
      secret:
        secretName: kubeconfig
        items:
          - key: config
            path: config
    - name: volume-docker
      hostPath:
        path: "/var/run/docker.sock"
    - name: volume-hosts
      hostPath:
        path: /etc/hosts


'''
    }
  }
  stages {
    stage('源代码管理') {
      parallel {
        stage('拉取代码') {
          steps {
            git(url: '${GIT_REPOSITORY_URL}', branch: '${GIT_TAG}', changelog: true, credentialsId: '${CREDENTIALS_ID}')
          }
        }

        stage('系统检查及初始化') {
          steps {
            sh """

            echo "Check Sysyem Env"

"""
          }
        }

      }
    }

    stage('编译及构建') {
      parallel {
        stage('编译代码') {
          steps {
            container(name: 'golang', shell: 'echo') {
              sh """

                go build main.go

"""
            }

          }
        }

        stage('初始化操作系统及kubernetes环境') {
          steps {
            script {
              CommitID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
              CommitMessage = sh(returnStdout: true, script: "git log -1 --pretty=format:'%h : %an  %s'").trim()
              def curDate = sh(script: "date '+%Y%m%d-%H%M%S'", returnStdout: true).trim()
              TAG = curDate[0..14] + "-" + CommitID + "-master"
            }
          }
        }

      }
    }

    stage('构建镜像及检查kubernetes环境') {
      parallel {
        stage('构建镜像') {
          steps {
            withCredentials([usernamePassword(credentialsId: '${DOCKER_REPOSITORY_CREDENTIAL_ID}', passwordVariable: 'PASSWORD', usernameVariable: 'USERNAME')]) {
              container(name: 'docker', shell: 'echo') {
                sh """
                  docker build -t ${HARBOR_HOST}/${NAMESPACE_NAME}/${REPOSITORY_NAME}:${TAG} .
                  docker login ${HARBOR_HOST} --username=${USERNAME} --password=${PASSWORD}
                  docker push ${HARBOR_HOST}/${NAMESPACE_NAME}/${REPOSITORY_NAME}:${TAG}
                """
              }
            }
          }
        }

        stage('Check Kubernetes ENV') {
          steps {
            container(name: 'kubectl', shell: 'echo') {
              sh 'sleep 10'
            }

          }
        }

      }
    }

    stage('Deploy Image to Kubernetes') {
      steps {
        container(name: 'kubectl', shell: 'echo') {
          sh 'sleep 10'
        }
      }
    }

    stage('Test Service') {
      steps {
        sh 'sleep 10'
      }
    }

    stage('Send Email to Admin') {
      steps {
        sh 'sleep 10'
      }
    }

  }
}
posted @ 2022-06-09 18:18  秋风お亦冷  阅读(199)  评论(0编辑  收藏  举报