DevOps基础

DevOps(Development和Operations的组合词)

operate
v.
操纵,操作(机器或设备);(机器等)运转,运行 ;管理,经营(企业、部门等);

operation
n.
运作,实施;运转,操作;手术;(有组织的)活动,行动;军事行动;企业,公司;业务,经营 ;(数学)运算

1、DevOps简介

DevOps 是一系列做法和工具,可以使 IT 和软件开发团队之间的流程实现自动化。其中,随着敏捷软件开发日趋流行,持续集成 (CI) 和持续交付 (CD) 已经成为该领域一个理想的解决方案。在 CI/CD 工作流中,每次集成都通过自动化构建来验证,包括编码、发布和测试,从而帮助开发者提前发现集成错误,团队也可以快速、安全、可靠地将内部软件交付到生产环境。

img

img

  • 整合 sonarQube 代码质量分析

continuous integration 持续集成

  • build 构建
  • test 测试
  • merge 合并
continue
英
/kənˈtɪnjuː/
v.
(使)继续,(使)延续;继续说,接着说;移动,延伸;留任,维持原状

delivery 持续交付

  • automatically 自动的
  • release to
  • repository 自动发布到仓库

deployment 持续部署

  • deploy to
  • production
delivery
英
/dɪˈlɪvəri/
n.
递送,投递;递送物;演讲风格,演说方式;分娩,生产;投球方式
adj.
运送的

automatically
英
/ˌɔːtəˈmætɪkli/
adv.
自然地,必然地;不假思索地,无意识地;自动地

2、DevOps落地

https://kubesphere.com.cn/docs/devops-user-guide/understand-and-manage-devops-projects/overview/

1、内置的Agent

https://kubesphere.com.cn/docs/devops-user-guide/how-to-use/choose-jenkins-agent/

尚医通项目上云

1、项目架构

img

yygh-parent
|---common                                  //通用模块
|---hospital-manage                         //医院后台				[9999]   
|---model																		//数据模型
|---server-gateway													//网关    				[80]
|---service																	//微服务层
|-------service-cmn													//公共服务				[8202]
|-------service-hosp												//医院数据服务		[8201]
|-------service-order												//预约下单服务		[8206]
|-------service-oss													//对象存储服务		[8205]
|-------service-sms													//短信服务				[8204]
|-------service-statistics									//统计服务				[8208]
|-------service-task												//定时服务				[8207]
|-------service-user												//会员服务				[8203]


====================================================================

yygh-admin																	//医院管理后台		[9528]
yygh-site																		//挂号平台				[3000]

2、中间件

中间件集群内地址外部访问地址
Nacoshis-nacos.his:8848http://139.198.165.238:30349/nacos
MySQLhis-mysql.his:3306139.198.165.238:31840
Redishis-redis.his:6379139.198.165.238:31968
Sentinelhis-sentinel.his:8080http://139.198.165.238:31523/
MongoDBmongodb.his:27017139.198.165.238:32693
RabbitMQrabbitm-yp1tx4-rabbitmq.his:5672139.198.165.238:30375
ElasticSearchhis-es.his:9200139.198.165.238:31300

部署sentinel

  • 名字:his-sentinel

  • leifengyang/sentinel:1.8.2
    
    • 端口为:TCP,8080
  • 同步主机时区,其他都不用选:

  • 创建 NodePort 类型的svc

  • his-sentinel-node

  • 其他提醒:sentinel 可以连接上 nacos,流控规则 在配置中心配置

部署MongoDB

  • 已经使用比特纳米仓库 bitnami

    • 搜 mongodb,一键部署即可。
    • 版本:老师的为,10.26.0(4.4.8)
  • 部署新应用,来自应用模板,bitnami

    • 随便部署13.6.3 [6.0.3],都部署不成功。
    • 关闭账号密码的访问。
    • 持久化部署,默认8G,可以调小点。
  • 使用有状态副本,部署成功了。

    • 挂载一下 数据卷,其他的没配置。
docker run -d \     
-p 27017:27017 \      
--name example-mongo \       
-v mongo-data:/data/db \          
mongo:latest
  • 创建svc,外部暴露,his-mongo-node
    • 端口 为 27017

导入msql 和 创建配置

  • 导入\yygh-parent-master\data\sql

  • 登录http://172.31.0.10:32470/nacos/

  • 创建配置:相关的配置文件,和 项目名字一样,+ prod

    • 注意,因为 docker打包时 未设置名称空间。

      • 配置文件都建立在 public下。
    • service-cmn-prod.yml

    • 复制 项目下的:application-dev.yml

      • 端口改不改都行,docker 启动会指定。
      • 更改 sentinel 的访问地址。his-sentinel.his:8080
        • http:// 前缀留着
      • redis的也要改:his-redis.his:6379
      • 数据库的访问地址:his-mysql.his:3306
      server:
        port: 8202
      mybatis-plus:
        configuration:
          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
        mapper-locations: classpath:mapper/*.xml
        global-config:
          db-config:
            logic-delete-value: 1
            logic-not-delete-value: 0
      spring:
        cloud:
          sentinel:
            transport:
              dashboard: http://his-sentinel.his:8080
        redis:
          host: his-redis.his
          port: 6379
          database: 0
          timeout: 1800000
          password:
          lettuce:
            pool:
              max-active: 20 #最大连接数
              max-wait: -1    #最大阻塞等待时间(负数表示没限制)
              max-idle: 5    #最大空闲
              min-idle: 0     #最小空闲
        datasource:
          type: com.zaxxer.hikari.HikariDataSource
          driver-class-name: com.mysql.jdbc.Driver
          url: jdbc:mysql://his-mysql.his:3306/yygh_cmn?characterEncoding=utf-8&useSSL=false
          username: root
          password: 123456
          hikari:
            connection-test-query: SELECT 1
            connection-timeout: 60000
            idle-timeout: 500000
            max-lifetime: 540000
            maximum-pool-size: 12
            minimum-idle: 10
            pool-name: GuliHikariPool
        jackson:
          date-format: yyyy-MM-dd HH:mm:ss
          time-zone: GMT+8
      
  • service-hosp

  • service-order

  • service-oss

  • service-sms

  • service-statistics

  • service-task

    • 注意这个:sentinel 要加 http://
  • service-user

  • server-gateway 网关的所有配置

  • hospital-manage 是单体应用,不连nacos,直接改。

3、流水线

创建devops项目

  • 登录 pm-wang
  • 创建 his-devops
    • 邀请 dev-zhao,为 admin
      • DevOps 工程管理员,可以管理 DevOps 工程下所有的资源。
    • 邀请 dev-liu,为operator
      • DevOps 工程普通成员,可以在 DevOps 工程下创建流水线凭证等。
      • 还有角色:viewer DevOps 工程观察者,可以查看 DevOps 工程下所有的资源。

Jenkins

https://www.jenkins.io/zh/doc/book/pipeline/

  • 登录 dev-zhao,即可创建流水线

    • 名称为:yygh-parent-devops
      • 其他都不创建,
    • 点击编辑流水线:选择中间的 ci/cd
      • 点击保存
      • 之后在点击 编辑jenkins 流水线。
        • 即可得到一个 模板。
  • 编辑流水线,可以看到4个 Agent

  • jenkins和k8s一样,都是 主节点,控制node 节点,构建流水线。

编辑流水线

每一步执行完 都可添加一个shell

  • echo ‘xx 执行成功了,哈哈’
第一步 拉取代码
  • 第一步输入名称,拉取代码
  • 删除原来的流程。指定容器 填 maven
    • 添加嵌套步骤,选择 git
    • 填入 url (点击克隆那个url .git结尾),用户名,密码,和分支 master
    • 添加嵌套步骤,选择 shell,输入 ls -al
第二步 项目编译
  • 点 第二步 名称 项目编译

    • 任务:指定容器,maven

    • 添加嵌套步骤shell:ls

    • 在添加嵌套shell:

      mvn clean package -Dmaven.test.skip=true
      
  • 配置 阿里云 仓库。

    • 登录 admin 账户
    • 集群管理——配置中心——配置
      • 找到 ks-devops-agent,修改
      • 在 mirrors 标签里
    	<mirror>  
    		<id>nexus-aliyun</id>  
    		<mirrorOf>*</mirrorOf> <!-- central -->
    		<name>Nexus aliyun</name>  
    		<url>http://maven.aliyun.com/nexus/content/groups/public</url>  
    	</mirror>
    
  • 下面这里,也进行了说明:修改maven让他从阿里云下载镜像

第三步 制作镜像
  • 输入步骤名

  • 指定容器,maven (maven容器里有 docker,其他也是)

  • 添加一个shell为:ls hospital-manage/target/ 看下有没有jar

  • 添加shell

    docker build -t hospital-manage:lastest -f hospital-manage/Dockerfile ./hospital-manage/
    
  • 添加并行阶段,每个微服务 并发构建镜像。(此时会增加标签parallel {})

  • 复制Jenkins文件,自己去 复制粘贴的改。

    • 注意:service-cmn 和 相同的服务,都多加了 一层 service
        stage('default-2') {
            parallel {
                stage('构建hospital-manage镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls hospital-manage/target'
                            sh 'docker build -t hospital-manage:latest -f hospital-manage/Dockerfile  ./hospital-manage/'
                        }

                    }
                }

                stage('构建service-cmn镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-cmn/target'
                            sh 'docker build -t service-cmn:latest -f service/service-cmn/Dockerfile  ./service/service-cmn/'
                        }

                    }
                }
}
第四步 推送XX镜像
  • 名字改为推送镜像,添加个并行

  • 添加凭证——新建凭证aliyun-docker-registry

    • 密码变量:DOCKER_PWD_VAR
    • 用户名变量:DOCKER_USER_VAR
    • 在凭证里 选择嵌套步骤,随便添加一个 shell:ls,去idea里修改。
  • 环境变量分析1

	# 仓库名
    REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
    DOCKERHUB_NAMESPACE = 'sgg_his'
    
    #要推送的地址
    //docker push registry.cn-hangzhou.aliyuncs.com/sgg_his/ruoyi-system:[镜像版本号]
    
//docker tag hospital-manage:lastest registry.cn-hangzhou.aliyuncs.com/sgg_his/hospital-manage:SNAPSHOT-13
sh 'docker tag hospital-manage:lastest $REGISTRY/$DOCKERHUB_NAMESPACE/hospitalmanage:SNAPSHOT-$BUILD_NUMBER' 

#BUILD_NUMBER 每次构建的版本号
  • 环境变量分析2
    • 增加了 并行,就会出这个标签 parallel
    • 增加了 凭证,就会出这个标签 withCredentials
              withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
              
              # sh 'docker login --username=$DOCKER_USER_VAR $REGISTRY'
              
# 输出密码 | 登录时 --password-stdin,输出。
sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'


sh 'docker tag hospital-manage:lastest $REGISTRY/$DOCKERHUB_NAMESPACE/hospital-manage:SNAPSHOT-$BUILD_NUMBER'

# 推送,就是上面的
sh 'docker push $REGISTRY/$DOCKERHUB_NAMESPACE/hospital-manage:SNAPSHOT-$BUILD_NUMBER'
       
}
  • 下面还有完整的 例子。
  • 之后:hospital-manage,会推送到 阿里云。
  • 复制多份 推送其他镜像。
第五步 部署到dev环境
  • 任务,默认添加一个 审核,需要人工点
    • @dev-liu 请确认。 @别人,可以让他点击。
寻找k8s文件
  • node节点,不能用 kubectl ,因为没有配置文件。所以 不配置 node也没法部署。

    • /root/.kube/config
  • 进行配置:

  • 默认创建了一个任务 kubernetesDeploy,点击编辑,点新建凭证,已经给配置好了,k8s的相关配置。

    • 填入凭证ID,demo-kubeconfig,是在 Jenkins 的环境变量里 看到的。

          KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
      
    • 配置文件路径:这是项目的,如:hospital-manage/deploy/**

      • 里面是部署k8s的文件,即可 部署这个项目。
  • 添加并行任务,然后复制到 idea,复制多份,进行更改。

k8s中的阿里密钥
  • 选中 his 项目——配置中心——密钥

    • 名字为:aliyun-docker-hub,选择:镜像仓库密钥,仓库地址:registry.cn-hangzhou.aliyuncs.com
      • 用户名,密码,然后验证一下。
  •     # 对应k8s的配置
        spec:
          imagePullSecrets:
            - name: aliyun-docker-hub  #提前在项目下配置访问阿里云的账号密码
    
流水线文件的取值 bug
  • 解决bug,取值的问题,去掉 “$KUBECONFIG_CREDENTIAL_ID” 两边的 单引号。

  • kubernetesDeploy(configs: 'hospital-manage/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
    
k8s中的写的 名称空间
  • 解决bug,registry.cn-hangzhou.aliyuncs.com/$ALIYUNHUB_NAMESPACE/hospital-manage:SNAPSHOT-6
    • $ALIYUNHUB_NAMESPACE 未取到值
    • 这是在 deploy.yml k8s的配置文件中指定的。
    • 在:Jenkins 里面添加这个文件:就是命名空间配置。
都加上 redis
  • 给每个容器的配置文件,加上 redis的配置,有些项目会默认连 本机的 redis。(如:oss项目)

  • spring:
      redis:
        host: his-redis.his
        port: 6379
    
k8s配置文件
  • hospital-manage/deploy/
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: hospital-manage
  name: hospital-manage
  namespace: his   #一定要写名称空间,这里就是 我们kubesphere 创建的项目名,底层是 k8s的namespace对应
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  selector:
    matchLabels:
      app: hospital-manage
  strategy:
    rollingUpdate:
      maxSurge: 50%
      maxUnavailable: 50%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: hospital-manage
    spec:
      imagePullSecrets:
        - name: aliyun-docker-hub  #提前在项目下配置访问阿里云的账号密码
      containers:
        - image: $REGISTRY/$ALIYUNHUB_NAMESPACE/hospital-manage:SNAPSHOT-$BUILD_NUMBER
 #         readinessProbe:
 #           httpGet:
 #             path: /actuator/health
 #             port: 8080
 #           timeoutSeconds: 10
 #           failureThreshold: 30
 #           periodSeconds: 5
          imagePullPolicy: Always
          name: app
          ports:
            - containerPort: 8080
              protocol: TCP
          resources:
            limits:
              cpu: 300m
              memory: 600Mi
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: hospital-manage
  name: hospital-manage
  namespace: his
spec:
  ports:
    - name: http
      port: 8080
      protocol: TCP
      targetPort: 8080
  selector:
    app: hospital-manage
  sessionAffinity: None
  type: ClusterIP
完整Jenkinsfile
  • 在父目录下(和子目录统计) 和 创建Jenkinsfile
//官方的文件。
pipeline {
    agent any //和 集群工作有关,不管。
    stages {
        stage('Build') { 
            steps {
            }
        }
        stage('Test') { 
            steps {
            }
        }
        stage('Deploy') { 
            steps {
            }
        }
    }
}
pipeline {
    agent {
        node {
            label 'maven'
        }

    }
    stages {
        stage('拉取代码') {
            agent none
            steps {
                container('maven') {
                    git(url: 'https://gitee.com/leifengyang/yygh-parent.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
                    sh 'ls -al'
                }

            }
        }

        stage('项目编译') {
            agent none
            steps {
                container('maven') {
                    sh 'ls'
                    sh 'mvn clean package -Dmaven.test.skip=true'
                    sh 'ls hospital-manage/target'
                }

            }
        }

        stage('default-2') {
            parallel {
                stage('构建hospital-manage镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls hospital-manage/target'
                            sh 'docker build -t hospital-manage:latest -f hospital-manage/Dockerfile  ./hospital-manage/'
                        }

                    }
                }

                stage('构建server-gateway镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls server-gateway/target'
                            sh 'docker build -t server-gateway:latest -f server-gateway/Dockerfile  ./server-gateway/'
                        }

                    }
                }

                stage('构建service-cmn镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-cmn/target'
                            sh 'docker build -t service-cmn:latest -f service/service-cmn/Dockerfile  ./service/service-cmn/'
                        }

                    }
                }

                stage('构建service-hosp镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-hosp/target'
                            sh 'docker build -t service-hosp:latest -f service/service-hosp/Dockerfile  ./service/service-hosp/'
                        }

                    }
                }

                stage('构建service-order镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-order/target'
                            sh 'docker build -t service-order:latest -f service/service-order/Dockerfile  ./service/service-order/'
                        }

                    }
                }

                stage('构建service-oss镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-oss/target'
                            sh 'docker build -t service-oss:latest -f service/service-oss/Dockerfile  ./service/service-oss/'
                        }

                    }
                }

                stage('构建service-sms镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-sms/target'
                            sh 'docker build -t service-sms:latest -f service/service-sms/Dockerfile  ./service/service-sms/'
                        }

                    }
                }

                stage('构建service-statistics镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-statistics/target'
                            sh 'docker build -t service-statistics:latest -f service/service-statistics/Dockerfile  ./service/service-statistics/'
                        }

                    }
                }

                stage('构建service-task镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-task/target'
                            sh 'docker build -t service-task:latest -f service/service-task/Dockerfile  ./service/service-task/'
                        }

                    }
                }

                stage('构建service-user镜像') {
                    agent none
                    steps {
                        container('maven') {
                            sh 'ls service/service-user/target'
                            sh 'docker build -t service-user:latest -f service/service-user/Dockerfile  ./service/service-user/'
                        }

                    }
                }

            }
        }

        stage('default-3') {
            parallel {
                stage('推送hospital-manage镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag hospital-manage:latest $REGISTRY/$DOCKERHUB_NAMESPACE/hospital-manage:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/hospital-manage:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送server-gateway镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag server-gateway:latest $REGISTRY/$DOCKERHUB_NAMESPACE/server-gateway:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/server-gateway:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送service-cmn镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag service-cmn:latest $REGISTRY/$DOCKERHUB_NAMESPACE/service-cmn:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/service-cmn:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送service-hosp镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag service-hosp:latest $REGISTRY/$DOCKERHUB_NAMESPACE/service-hosp:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/service-hosp:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送service-order镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag service-order:latest $REGISTRY/$DOCKERHUB_NAMESPACE/service-order:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/service-order:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送service-oss镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag service-oss:latest $REGISTRY/$DOCKERHUB_NAMESPACE/service-oss:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/service-oss:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送service-sms镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag service-sms:latest $REGISTRY/$DOCKERHUB_NAMESPACE/service-sms:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/service-sms:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送service-statistics镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag service-statistics:latest $REGISTRY/$DOCKERHUB_NAMESPACE/service-statistics:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/service-statistics:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送service-task镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag service-task:latest $REGISTRY/$DOCKERHUB_NAMESPACE/service-task:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/service-task:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

                stage('推送service-user镜像') {
                    agent none
                    steps {
                        container('maven') {
                            withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                                sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                                sh 'docker tag service-user:latest $REGISTRY/$DOCKERHUB_NAMESPACE/service-user:SNAPSHOT-$BUILD_NUMBER'
                                sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/service-user:SNAPSHOT-$BUILD_NUMBER'
                            }

                        }

                    }
                }

            }
        }

        stage('default-4') {
            parallel {
                stage('hospital-manage - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'hospital-manage/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('server-gateway - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'server-gateway/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('service-cmn - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'service/service-cmn/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('service-hosp - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'service/service-hosp/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('service-order - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'service/service-order/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('service-oss - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'service/service-oss/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('service-sms - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'service/service-sms/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('service-statistics - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'service/service-statistics/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('service-task - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'service/service-task/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

                stage('service-user - 部署到dev环境') {
                    agent none
                    steps {
                        kubernetesDeploy(configs: 'service/service-user/deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
                    }
                }

            }
        }

        //1、配置全系统的邮件:                   全系统的监控
        //2、修改ks-jenkins的配置,里面的邮件;   流水线发邮件
        stage('发送确认邮件') {
            agent none
            steps {
                mail(to: '17512080612@163.com', subject: '构建结果', body: "构建成功了  $BUILD_NUMBER")
            }
        }

    }
    environment {
        DOCKER_CREDENTIAL_ID = 'dockerhub-id'
        GITHUB_CREDENTIAL_ID = 'github-id'
        KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
        REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
        DOCKERHUB_NAMESPACE = 'lfy_hello'
        GITHUB_ACCOUNT = 'kubesphere'
        APP_NAME = 'devops-java-sample'
        ALIYUNHUB_NAMESPACE = 'lfy_hello'
    }
    parameters {
        string(name: 'TAG_NAME', defaultValue: '', description: '')
    }
}

1、项目地址

https://gitee.com/leifengyang/yygh-parent

https://gitee.com/leifengyang/yygh-admin

https://gitee.com/leifengyang/yygh-site

2、项目默认规则

  • 每个微服务项目,在生产环境时,会自动获取 微服务名-prod.yml 作为自己的核心配置文件
  • 每个微服务项目,在生产环境时,默认都是使用 8080 端口

3、生产与开发配置隔离

4、deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: service-cart
  name: service-cart
  namespace: his   #一定要写名称空间
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  selector:
    matchLabels:
      app: service-cart
  strategy:
    rollingUpdate:
      maxSurge: 50%
      maxUnavailable: 50%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: service-cart
    spec:
      imagePullSecrets:
        - name: aliyun-docker-hub  #提前在项目下配置访问阿里云的账号密码
      containers:
        - image: $REGISTRY/$ALIYUNHUB_NAMESPACE/service-cart
          readinessProbe:
            httpGet:
              path: /actuator/health
              port: 8080
            timeoutSeconds: 10
            failureThreshold: 30
            periodSeconds: 5
          imagePullPolicy: Always
          name: app
          ports:
            - containerPort: 8080
              protocol: TCP
          resources:
            limits:
              cpu: 300m
              memory: 600Mi
          terminationMessagePath: /dev/termination-log
          terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: service-cart
  name: service-cart
  namespace: his
spec:
  ports:
    - name: http
      port: 8080
      protocol: TCP
      targetPort: 8080
  selector:
    app: service-cart
  sessionAffinity: None
  type: ClusterIP

4、devops实战

1、修改maven让他从阿里云下载镜像

  • 使用admin登陆ks

  • 进入集群管理

  • 进入配置中心

  • 找到配置

2、缓存机制

已经下载过的jar包,下一次流水线的启动,不会重复下载

3、部署到k8s集群

  • 给每一个微服务准备一个 deploy.yaml(k8s的部署配置文件)

  • 执行以下步骤

    • img
  • 传入 deploy.yaml 的位置就能部署

    • kubectl apply -f xxxx
  • 一定在项目里面(his,不是流水线项目),找到配置–密钥,配置一个阿里云的访问账号密码

    • img

其他问题

内存不够排错
  • 排错:kubectl get pod -A | grep maven

  • kubectl describe pod -n xxx pod的名

    • 看到日志,调度失败,3个节点,一个主节点不能用。
    • 剩下的两个节点,内存不够。
  • kubectl top pods -A

  • kubectl top nodes

logback文件更改
  • pro改为 prod,读取的也是 spring.profile.active: prod
    <!--生产环境:输出到文件-->
    <springProfile name="prod">
进入容器日志排错
  • 修改配置模板,取消探针。

  • 容器内,也创建了 目录。进入:C:目录,即可看到。

    <property name="log.path" value="C:/gmall_log/user" />
  • bootstrap.properties 去掉乱码

  • xml 增加 nacos 配置中心的配置

  • logback-spring.xml增加 控制台日志

        <!--生产环境:输出到文件-->
        <springProfile name="prod">
    
            <!--可以输出项目中的debug日志,包括mybatis的sql日志-->
            <logger name="com.guli" level="WARN" />
    
            <root level="INFO">
                <appender-ref ref="CONSOLE" />
                <appender-ref ref="ERROR_FILE" />
                <appender-ref ref="WARN_FILE" />
            </root>
        </springProfile>
    
  • 配置中心 都打印 展示节点 详细信息。打印详细,就没问题了。

    management:
      endpoints:
        web:
          exposure:
            include: "*"
      endpoint:
        health:
          show-details: always
    
    • 访问:curl ip:8080/actuator/health
    • 明白了,因为 微服务,并没有暴露这个接口,然后 k8s 还有 探针,不挂才怪。
  • 之后还是失败,但是 手动请求一下8080端口,就自动好了。

  • service-user-prod 配置中心的,要删除 配置的端口

  • k8s 配置文件,取消就绪探针

     #         readinessProbe:
     #           httpGet:
     #             path: /actuator/health
     #             port: 8080
     #           timeoutSeconds: 10
     #           failureThreshold: 30
     #           periodSeconds: 5
    
全系统邮件配置
  • 添加邮件,收件人,抄送,主题:构建结果

    • Body:构建成功了,$BUILD_NUMBER
  • 登录163,设置 POP3,SMTP,IMAP

    • 点击 开启 POP3/SMTP 发送短信,获取授权码(就是SMTP密码),
    • 使用设备,随便填,如:ks
  • 登录admin

    • 平台设置——通知设置——填入
      • SMTP 服务器地址 smtp.163.com
      • SMTP 密码,就是授权码
      • SMTP 就是 邮件登录名
      • 发件邮箱,同样 是登录名
      • 接收邮箱,同样也 填入一个 登录名。
流水线邮件配置

https://kubesphere.io/zh/docs/v3.3/devops-user-guide/how-to-use/pipelines/jenkins-email/

转到应用负载下的工作负载,然后从下拉列表中选择 kubesphere-devops-system 项目。点击 devops-jenkins 右侧的 icon 并选择编辑 YAML 以编辑其 YAML 配置文件。

  1. 环境变量名称描述信息
    EMAIL_SMTP_HOSTSMTP 服务器地址
    EMAIL_SMTP_PORTSMTP 服务器端口(如:25)
    EMAIL_FROM_ADDR电子邮件发件人地址
    EMAIL_FROM_NAME电子邮件发件人姓名
    EMAIL_FROM_PASS电子邮件发件人密码
    EMAIL_USE_SSL是否启用 SSL 配置
  • 登录admin——集群管理——应用负载——工作负载
    • 名称为:ks-jenkins ,项目为:kubesphere-devops-system
    • 端口:SMTP,非SSL用25,SSL 用:465/994
    • $BUILD_NUMBER 取值失败,修改Jenkins yaml
      • 是因为使用的是 单引号,需要用 双引号,才能取出。

4、前端项目

1、yygh-admin

  • npm run build 会生成dist目录,放到nginx的html下,即可运行

2、yygh-site

  • npm install --registry=https://registry.npm.taobao.org 安装项目依赖

  • npm run build 对项目打包,

  • 打包完成后把 .nuxt ,static, nuxt.config.js, package.json 这四个关键文件复制到 node 环境。先npm install再使用npm run start 即可运行

3、思考

  • admin的镜像和site的镜像大小为何差距那么大?

    • 如何对镜像进行瘦身?
  • yygh-site 有1.08G

    • 因为是Nuxt 项目,服务端 打包编译, “npm”,“run”,“build”
    • 相当于 maven仓库里 下载了很多东西。
    • 而最终 打包后,是 html,如果只用 nginx + html 肯定会小很多。
  • yygh-admin 有136M

医院挂号后台打包

npm install --registry=https://registry.npm.taobao.org #两个前端项目同样的打包
  • 网关项目暴露外网端口,yygh-admin-master项目更改dev.env.js 文件,更改地址。
npm run dev #同样的运行
  • yygh-site-master项目更改为:request.js

  • 使用vue.js 编译后npm run build,放到 nginx 就能运行。

    • COPY dist /usr/share/nginx/html/
导入mongoDB的数据
  • Department.json
  • Hospital.json
  • Schedule.json
医院挂号后台 流水线
pipeline {
    agent {
        node {
            label 'nodejs'
        }

    }
    stages {
        stage('拉取代码') {
            agent none
            steps {
                container('nodejs') {
                    git(url: 'https://gitee.com/leifengyang/yygh-admin.git', credentialsId: 'gitee-id', branch: 'master', changelog: true, poll: false)
                    sh 'ls -al'
                }

            }
        }

        stage('项目编译') {
            agent none
            steps {
                container('nodejs') {
                    sh 'npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/'
                    sh 'npm install --registry=https://registry.npm.taobao.org'
                    sh 'npm run build'
                    sh 'ls'
                }

            }
        }

        stage('构建镜像') {
            agent none
            steps {
                container('nodejs') {
                    sh 'ls'
                    //拉取后,已经是当前目录了
                    sh 'docker build -t yygh-admin:latest -f Dockerfile  .'
                }

            }
        }

        stage('推送镜像') {
            agent none
            steps {
                container('nodejs') {
                    withCredentials([usernamePassword(credentialsId : 'aliyun-docker-registry' ,usernameVariable : 'DOCKER_USER_VAR' ,passwordVariable : 'DOCKER_PWD_VAR' ,)]) {
                        sh 'echo "$DOCKER_PWD_VAR" | docker login $REGISTRY -u "$DOCKER_USER_VAR" --password-stdin'
                        sh 'docker tag yygh-admin:latest $REGISTRY/$DOCKERHUB_NAMESPACE/yygh-admin:SNAPSHOT-$BUILD_NUMBER'
                        sh 'docker push  $REGISTRY/$DOCKERHUB_NAMESPACE/yygh-admin:SNAPSHOT-$BUILD_NUMBER'
                    }

                }

            }
        }

        stage('部署到dev环境') {
            agent none
            steps {
                kubernetesDeploy(configs: 'deploy/**', enableConfigSubstitution: true, kubeconfigId: "$KUBECONFIG_CREDENTIAL_ID")
            }
        }

        //1、配置全系统的邮件:                   全系统的监控
        //2、修改ks-jenkins的配置,里面的邮件;   流水线发邮件
        stage('发送确认邮件') {
            agent none
            steps {
                mail(to: '17512080612@163.com', subject: 'yygh-admin构建结果', body: "构建成功了  $BUILD_NUMBER")
            }
        }

    }
    environment {
        DOCKER_CREDENTIAL_ID = 'dockerhub-id'
        GITHUB_CREDENTIAL_ID = 'github-id'
        KUBECONFIG_CREDENTIAL_ID = 'demo-kubeconfig'
        REGISTRY = 'registry.cn-hangzhou.aliyuncs.com'
        DOCKERHUB_NAMESPACE = 'lfy_hello'
        GITHUB_ACCOUNT = 'kubesphere'
        APP_NAME = 'devops-java-sample'
        ALIYUNHUB_NAMESPACE = 'lfy_hello'
    }
}
医院挂号后台Dockerfile
FROM nginx

#将dist目录内容复制到nginx容器html内部
COPY dist /usr/share/nginx/html/

EXPOSE 80
医院挂号后台k8s文件
  • deploy/deploy.yml

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      labels:
        app: yygh-admin
      name: yygh-admin
      namespace: his   #一定要写名称空间
    spec:
      progressDeadlineSeconds: 600
      replicas: 1
      selector:
        matchLabels:
          app: yygh-admin
      strategy:
        rollingUpdate:
          maxSurge: 50%
          maxUnavailable: 50%
        type: RollingUpdate
      template:
        metadata:
          labels:
            app: yygh-admin
        spec:
          imagePullSecrets:
            - name: aliyun-docker-hub  #提前在项目下配置访问阿里云的账号密码
          containers:
            - image: $REGISTRY/$ALIYUNHUB_NAMESPACE/yygh-admin:SNAPSHOT-$BUILD_NUMBER
     #         readinessProbe:
     #           httpGet:
     #             path: /actuator/health
     #             port: 8080
     #           timeoutSeconds: 10
     #           failureThreshold: 30
     #           periodSeconds: 5
              imagePullPolicy: Always
              name: app
              ports:
                - containerPort: 80
                  protocol: TCP
              resources:
                limits:
                  cpu: 300m
                  memory: 600Mi
              terminationMessagePath: /dev/termination-log
              terminationMessagePolicy: File
          dnsPolicy: ClusterFirst
          restartPolicy: Always
          terminationGracePeriodSeconds: 30
    ---
    apiVersion: v1
    kind: Service
    metadata:
      labels:
        app: yygh-admin
      name: yygh-admin
      namespace: his
    spec:
      ports:
        - name: http
          port: 80
          protocol: TCP
          targetPort: 80
          nodePort: 32248
      selector:
        app: yygh-admin
      sessionAffinity: None
      type: NodePort
    
git命令
git add .
git status
git commit -m 修改了内容
git push
新建DevOps
  • 名字为:yygh-admin,

  • 选择一个代码仓库,选择 git,使用 码云的凭证,输入仓库的URL

  • 脚本路径:Jenkinsfile 和 项目根目录的是同一个文件。

  • node-sass 去淘宝安装

    npm i node-sass --sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
    

医院挂号用户端

使用:Nuxt.js 是一个基于 Vue.js 的通用应用框架。

  • Nuxt 编写。服务端 渲染技术。
npm run dev #同样的运行,本地启动
  • 默认监听为 localhost 的3000
Dockerfile
FROM node:14.17.6

WORKDIR /app
#把.nuxt目录下的所有内容复制到/app/.nuxt/
COPY .  /app/

#安装核心依赖  npm cache clean -f
RUN ["npm","install","--registry=https://registry.npm.taobao.org"]
RUN ["npm","run","build"]
EXPOSE 3000
CMD ["npm", "run", "start"] #nuxt 依服务器的方式,在3000端口启动。
改为网关的端口
  • request.js
// 创建axios实例
const service = axios.create({
  baseURL: 'http://139.198.165.238:32607', //生产环境
  timeout: 15000 // 请求超时时间
})
创建流水线
  • yygh-site 医院挂号的用户端
  • git仓库,选择地址 和 凭证。
修改 nuxt 暴露的端口
  • 如果不改:外网访问,访问到后 跳转为 localhost,就会拒绝。

  • package.json,默认是 localhost:3000。改为了 本机地址:3000

  "config": {
    "nuxt": {
      "host": "0.0.0.0",
      "port": "3000"
    }
  },
网关的 k8s更改
spec:
  ports:
    - name: http
      port: 8080
      protocol: TCP
      targetPort: 8080
      nodePort: 32607 #外网暴露的端口 定死。
  selector:
    app: server-gateway
  sessionAffinity: None
  type: NodePort #改为nodePort

5、webhook

  • 就是 选择 流水线,使用 仓库的地址,最后一步,会有一个 webhook

  • 1、每个项目,都有流水线文件

  • 2、每次修改完项目,手动点击运行

  • 3、希望,每次修改完项目,代码推送,流水线能自动运行

    • 写代码并提交------> gitee ---------> 给指定的地方发请求(webhook)------> kubesphere平台感知到 -----> 自动启动流水线继续运行
    • http://139.198.165.238:30880/devops_webhook/git/?url=https://gitee.com/leifengyang/yygh-admin.git

优化

前端项目都用固定的端口

  • 挂号用户端
spec:
  ports:
    - name: http
      port: 3000
      protocol: TCP
      targetPort: 3000
      nodePort: 32070
  selector:
    app: yygh-site
  sessionAffinity: None
  type: NodePort
  • 医院挂号管理系统
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 80
      nodePort: 32248
  selector:
    app: yygh-admin
  sessionAffinity: None
  type: NodePort
  • 前端项目 可以多部署几份,前置 在放个 ingress 网络,也可以自己装 nginx

先创建一个流水线,编辑流程

  • 流水线 编辑后,在创建一个 从仓库拉取的 项目(会带 webhook)

  • 然后用 上面编辑的流程流水线 ,进行替换。

后续知识

  • service mesh 服务网格
    • Istio 落地技术
    • Istio是一个Service Mesh形态的用于服务治理的开放平台
  • 普罗米修斯 promethes 集群监控和告警
    • Grafana 监控面板。
    • Grafana是一款用Go语言开发的.源数据可视化工具,可以做数据监控和数据统计,带有告警功能。

KubeKey介绍
部署高可用Kubernetes集群
KubeKey集群配置文件详解
Kubernetes增删集群节点
Kubernetes集群证书管理:证书 一年内有效。

  • kubeSphere 3.2 平台,可以自动续期。

KubeSphere启用可插拔组件
Kubernetes 节点管理

使用Source-to-Image发布应用
使用Binary-to-lmage发布应用
使用Jenkinsfile 创建流水线

ArgoCD 实践

Argo CD简介 Argo CD是一款基于 kubernetes 的声明式的Gitops 持续部署工具。

虚拟化技术介绍
Kubevirt介绍

K8s的专业技术认证主要有以下几种:

  • CKA(Kubernetes 管理员认证)
  • CKAD(Kubernetes 应用程序开发者认证)
  • CKS(Kubernetes 认证安全专家。预计2020年11月开放,须先通过CKA认证)

CKA 证书是云原生计算基金会 CNCF 组织的,

CKA是目前唯一的 Kubernetes 官方认证考试

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐