1.项目发布方案概述

(1)蓝绿发布

项目逻辑上分为AB组,在项目升级时,首先把A组从负载均衡中摘除,进行新版本的部署。B组仍然继续提供服务。A组升级完成上线,B组从负载均衡中摘除。

特点:

• 策略简单

• 升级/回滚速度快

• 用户无感知,平滑过渡

缺点:

• 需要两倍以上服务器资源

• 短时间内浪费一定资源成本

jenkins 如何关联通知k8s_自动化


jenkins 如何关联通知k8s_自动化_02


(2)灰度发布

灰度发布:只升级部分服务,即让一部分用户继续用老版本,一部分用户开始用新版本,如果用户对新版本没有什么意见,那么逐步扩大范围,把所有用户都迁移到新版本上面来。

特点:

• 保证整体系统稳定性

• 用户无感知,平滑过渡

缺点:

• 自动化要求高

jenkins 如何关联通知k8s_容器_03


jenkins 如何关联通知k8s_jenkins 如何关联通知k8s_04


(3)滚动发布

jenkins 如何关联通知k8s_容器_05


jenkins 如何关联通知k8s_jenkins 如何关联通知k8s_06


滚动发布:每次只升级一个或多个服务,升级完成后加入生产环境,不断执行这个过程,直到集群中的全部旧版升级新版本。

特点:

• 用户无感知,平滑过渡

缺点:

• 部署周期长

• 发布策略较复杂

• 不易回滚

2.发布流程设计

jenkins 如何关联通知k8s_jenkins 如何关联通知k8s_07


jenkins 如何关联通知k8s_自动化_08

192.168.74.230  k8s-master
192.168.74.246  k8s-node1
192.168.74.247  k8s-node2
192.168.74.248  harbor  git   nfs

3.准备代码版本仓库Git和容器镜像仓库Harbor

在192.168.74.248搭建harbor仓库和git仓库

#配置密钥对互信
 ssh-keygen  
ssh-copy-id git@192.168.74.248

#配置git仓库
mkdir java-demo.git
cd java-demo.git/
git --bare init

chmod +x docker-compose
mv docker-compose /usr/local/bin/
(1)给这个节点安装docker 准备harbor的环境
yum install -y yum-utils device-mapper-persistent-data lvm2
wget http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
mv docker-ce.repo /etc/yum.repos.d/
systemctl start docker && systemctl enable docker
vim /etc/docker/daemon.json
{ “registry-mirrors”: [“https://h5d0mk6d.mirror.aliyuncs.com”] }
systemctl restart docker
(2)制作证书
mkdir /application/ssl -p
cd /application/ssl

openssl req -newkey rsa:4096 -nodes -sha512 -subj “/C=CN/ST=/L=/O=/OU=/CN=qushuaibo.com” -keyout ca.key -x509 -days 3650 -out ca.crt

openssl req -newkey rsa:4096 -nodes -sha512 -subj “/C=CN/ST=/L=/O=e/OU=/CN=qushuaibo.com” -keyout qushuaibo.com.key -out qushuaibo.com.csr

openssl x509 -req -days 3650 -in qushuaibo.com.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out qushuaibo.com.crt
(3)配置harbor(这里采用的https方式域名访问)
tar xf harbor-offline-installer-v1.7.5.tgz
cd harbor/
sed -i ‘s#hostname = reg.mydomain.com#hostname = qushuaibo.com#g’ /root/harbor/harbor.cfg
sed -i ‘s#hostname = reg.mydomain.com#hostname = qushuaibo.com#g’ /root/harbor/harbor.cfg
sed -i ‘s#ui_url_protocol = http#ui_url_protocol = https#g’ /root/harbor/harbor.cfg
sed -i ‘s#ssl_cert = /data/cert/server.crt#ssl_cert = /application/ssl/qushuaibo.com.crt#g’ /root/harbor/harbor.cfg
sed -i ‘s#ssl_cert_key = /data/cert/server.key#ssl_cert_key = /application/ssl/qushuaibo.com.key#g’ /root/harbor/harbor.cfg
sed -i ‘s#harbor_admin_password = Harbor12345#harbor_admin_password = 123456#g’ /root/harbor/harbor.cfg
(4)将证书分发至个个node和master节点并更新证书,信任此证书
mkdir -r /etc/docker/certs.d/qushuaibo.com/ #k8s集群内部都创建放证书的地方
scp qushuaibo.com.crt 192.168.74.230:/etc/docker/certs.d/qushuaibo.com/ #master
scp qushuaibo.com.crt 192.168.74.246:/etc/docker/certs.d/qushuaibo.com/ #node1
scp qushuaibo.com.crt 192.168.74.247:/etc/docker/certs.d/qushuaibo.com/ #node2
cd /etc/docker/certs.d/qushuaibo.com/
update-ca-trust
(5)启动harbor
./prepare
./install.sh

4.Jenkins在K8S中动态创建代理

https://github.com/jenkinsci/kubernetes-plugin/tree/fc40c869edfd9e3904a9a56b0f80c5a25e988fa1/src/main/kubernetes 在k8s中部署jenkins
kubectl apply -f .
Ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: jenkins
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    kubernetes.io/tls-acme: "true"
    # 如果上传插件超出默认会报"413 Request Entity Too Large", 增加 client_max_body_size
    nginx.ingress.kubernetes.io/proxy-body-size: 50m
    nginx.ingress.kubernetes.io/proxy-request-buffering: "off"
    # nginx-ingress controller版本小于 0.9.0.beta-18 的配置
    ingress.kubernetes.io/ssl-redirect: "true"
    ingress.kubernetes.io/proxy-body-size: 50m
    ingress.kubernetes.io/proxy-request-buffering: "off"
spec:
  rules:
  - host: jenkins.example.com
    http:
      paths:
      - path: /
        backend:
          serviceName: jenkins
          servicePort: 80

rbac.yml

**# 创建名为jenkins的ServiceAccount**
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins

---
# 创建名为jenkins的Role,授予允许管理API组的资源Pod
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkins
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get","list","watch"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]

---
# 将名为jenkins的Role绑定到名为jenkins的ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: jenkins
subjects:
- kind: ServiceAccount
  name: jenkins

service-account.yml

# In GKE need to get RBAC permissions first with
# kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin [--user=<user-name>|--group=<group-name>]

---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: jenkins

---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: jenkins
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/exec"]
  verbs: ["create","delete","get","list","patch","update","watch"]
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get","list","watch"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]

---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: jenkins
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: jenkins
subjects:
- kind: ServiceAccount
  name: jenkins

service.yml  
apiVersion: v1
kind: Service
metadata:
  name: jenkins
spec:
  selector:
    name: jenkins
  type: NodePort
  ports:
    -
      name: http
      port: 80
      targetPort: 8080
      protocol: TCP
      nodePort: 30006
    -
      name: agent
      port: 50000
      protocol: TCP

statefulset.yml
apiVersion: apps/v1beta1
kind: StatefulSet
metadata:
  name: jenkins
  labels:
    name: jenkins
spec:
  serviceName: jenkins
  replicas: 1
  updateStrategy:
    type: RollingUpdate
  template:
    metadata:
      name: jenkins
      labels:
        name: jenkins
    spec:
      terminationGracePeriodSeconds: 10
      serviceAccountName: jenkins
      containers:
        - name: jenkins
          image: jenkins/jenkins:lts-alpine
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
            - containerPort: 50000
          resources:
            limits:
              cpu: 1
              memory: 1Gi
            requests:
              cpu: 0.5
              memory: 500Mi
          env:
            - name: LIMITS_MEMORY
              valueFrom:
                resourceFieldRef:
                  resource: limits.memory
                  divisor: 1Mi
            - name: JAVA_OPTS
              value: -Xmx$(LIMITS_MEMORY)m -XshowSettings:vm -Dhudson.slaves.NodeProvisioner.initialDelay=0 -Dhudson.slaves.NodeProvisioner.MARGIN=50 -Dhudson.slaves.NodeProvisioner.MARGIN0=0.85
          volumeMounts:
            - name: jenkins-home
              mountPath: /var/jenkins_home
          livenessProbe:
            httpGet:
              path: /login
              port: 8080
            initialDelaySeconds: 200
            timeoutSeconds: 5
            failureThreshold: 12
          readinessProbe:
            httpGet:
              path: /login
              port: 8080
            initialDelaySeconds: 60
            timeoutSeconds: 5
            failureThreshold: 12
      securityContext:
        fsGroup: 1000
  volumeClaimTemplates:
  - metadata:
      name: jenkins-home
    spec:
      storageClassName: "managed-nfs-storage"
      accessModes: [ "ReadWriteOnce" ]
      resources:
        requests:
          storage: 1Gi

修改jenkins代理以及安装插件git pip kubernetes Kubernetes Continuous Deploy

jenkins 如何关联通知k8s_容器_09


在配置系统中拉到最下面

jenkins 如何关联通知k8s_k8s_10


jenkins 如何关联通知k8s_jenkins 如何关联通知k8s_11

5.构建Jenkins-Slave镜像

jenkins 如何关联通知k8s_自动化_12


jenkins 如何关联通知k8s_jenkins 如何关联通知k8s_13


制作jenkins-slave镜像并上传至haror

docker build -t qushuaibo.com/jenkins/jenkins_slave:v1 .

Dockerfile为

FROM centos:7
LABEL maintainer lizhenliang

RUN yum install -y java-1.8.0-openjdk maven curl git libtool-ltdl-devel && \ 
    yum clean all && \
    rm -rf /var/cache/yum/* && \
    mkdir -p /usr/share/jenkins

COPY slave.jar /usr/share/jenkins/slave.jar  
COPY jenkins-slave /usr/bin/jenkins-slave        #这个是个脚本为了就是访问slave.jar
COPY settings.xml /etc/maven/settings.xml       #这个是为了maven的源
RUN chmod +x /usr/bin/jenkins-slave          

ENTRYPOINT ["jenkins-slave"]

6.Jenkins Pipeline构建流水线发布

• Jenkins Pipeline是一套插件,支持在Jenkins中实现集成和持续交付管道;

• Pipeline通过特定语法对简单到复杂的传输管道进行建模;

• 声明式:遵循与Groovy相同语法。pipeline { }

• 脚本式:支持Groovy大部分功能,也是非常表达和灵活的工具。node { }

• Jenkins Pipeline的定义被写入一个文本文件,称为Jenkinsfile。

jenkins 如何关联通知k8s_jekins_14


jenkins 如何关联通知k8s_自动化_15


先测试jenkins是否可以拉起slave

podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
    containerTemplate(
        name: 'jnlp', 
        image: "qushuaibo.com/jenkins/jenkins_slave:v1"
    ),
  ],
  volumes: [
    hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
    hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker')
  ],
) 
{
  node("jenkins-slave"){
     stage('拉取代码'){
         
     }
  }
}

jenkins 如何关联通知k8s_自动化_16


标签上下对应才可以把下面的步骤在上面这个slave中

设置保存用户名密码(登陆harbor用的)

jenkins 如何关联通知k8s_k8s_17


jenkins 如何关联通知k8s_jekins_18

7.Jenkins在Kubernetes中持续部署

❖ 使用Jenkins三个插件
• Kubernetes
• Pipeline
• Kubernetes Continuous Deploy
• git
• 也可以装一个新皮肤Blue Ocean
❖ CI/CD环境特点
• Slave弹性伸缩
• 基于镜像隔离构建环境
• 流水线发布,易维护
❖Jenkins参数化构建可帮助你完成更复杂环境CI/CD
持续部署是需要Kubernetes Continuous Deploy 这个插件来做
那么我们就需要把认证信息给jenkins,才可以确保jenkins能连接k8s
cat .kube/config

jenkins 如何关联通知k8s_自动化_19

jenkins 如何关联通知k8s_jenkins 如何关联通知k8s_20


jenkins 如何关联通知k8s_jenkins 如何关联通知k8s_21

上图为jenkins连接k8s的凭证

创建拉取镜像凭证
kubectl create secret docker-registry registry-pull-secret --docker-username=admin --docker-server=https://qushuaibo.com --docker-password=123456

Pipeline脚本如下

// 公共
def registry = "qushuaibo.com"                 //仓库坐标
// 项目 
def project = "welcome"                     //项目仓库名字
def app_name = "demo"                      //项目名称
def image_name = "${registry}/${project}/${app_name}:${Branch}-${BUILD_NUMBER}"
//k8s部署用的镜像名称通过值用sed来给yaml传递   
//Branch是分支我们需要设置的参数化构建
//BUILD_NUMBER 这个是jenkins的内置变量
def git_address = "git@192.168.74.248:/home/git/java-demo.git"
//git仓库位置
// 认证
def secret_name = "registry-pull-secret"          //k8s部署应用拉镜像的凭证
def docker_registry_auth = "c87990b8-0802-4ab7-9d6c-dfe48e581ff2"
//harbor的username和password两个变量的传入,以及这样可以不将私有仓库账号密码暴露在外面
def git_auth = "dcfb69a8-8a74-47c6-ad09-1780d91802c9"
//git仓库的凭据,等于是把下面的拿上来可以不同的仓库多次利用
def k8s_auth = "da3ad51c-69aa-4cc3-b938-744b9f622cda"
//jenkins连接k8s所需要的认证
podTemplate(label: 'jenkins-slave', cloud: 'kubernetes', containers: [
    containerTemplate(
        name: 'jnlp', 
        image: "${registry}/jenkins/jenkins_slave:v1"
    ),
  ],
  volumes: [
    hostPathVolume(mountPath: '/var/run/docker.sock', hostPath: '/var/run/docker.sock'),
    hostPathVolume(mountPath: '/usr/bin/docker', hostPath: '/usr/bin/docker'),
hostPathVolume(mountPath: '/root/.m2', hostPath: '/tmp/m2')
//将宿主机的docker环境挂载在jenkins-slave中就不用在jenkins-slave中安装docker
//挂载.m2是为了将maven拉取的依赖包同步在宿主机的/tmp/m2中这样子可以多次利用,也可以用共享存储让多节点一起使用
  ],    
    ImagePullSecrets: [‘registry-pull-secret’],    //私有镜像需要加这个,共有仓库不需要
) 
{
  node("jenkins-slave"){         
//这里的jeknins-slave对应上面的label这样才可以白傲冥是用上面的从来做这个任务,根据不同的项目,做不同的从jenkins环境,这里是java所以从配置的是maven和java环境
      // 第一步
      stage('拉取代码'){
        checkout([$class: 'GitSCM', branches: [[name: "${Branch}"]], doGenerateSubmoduleConfigurations: false, extensions: [], submoduleCfg: [], userRemoteConfigs: [[credentialsId: "${git_auth}", url: "${git_address}"]]])
      }
      // 第二步
      stage('代码编译'){
          sh "mvn clean package -Dmaven.test.skip=true"
      }
      // 第三步
     stage('构建镜像'){
          withCredentials([usernamePassword(credentialsId: "${docker_registry_auth}", passwordVariable: 'password', usernameVariable: 'username')]) {
           sh """
              echo '
                FROM lizhenliang/tomcat 
                RUN rm -rf /usr/local/tomcat/webapps/*
                ADD target/*.war /usr/local/tomcat/webapps/ROOT.war 
              ' > Dockerfile
              docker build -t ${image_name} .
              docker login -u ${username} -p '${password}' https://${registry}
              docker push ${image_name}
            """
            }
      }
      // 第四步
      stage('部署到K8S平台'){
          sh """
          sed -i 's#\$IMAGE_NAME#${image_name}#' deploy.yml
          sed -i 's#\$SECRET_NAME#${secret_name}#' deploy.yml
          """
          kubernetesDeploy configs: 'deploy.yml', kubeconfigId: "${k8s_auth}"
      }
   }
}