【摘要】云原生翘楚KubeSphere 和 知名开源项目Pig 爱的火花。
一、前言
二、介绍
KubeSphere
Pig
三、环境搭建
-
k8s 容器化环境一套,并部署完 KubeSphere v3.3.0 版本,启用 DevOps 插件。搭建高可用的 k8s 集群可参考基于青云 LB 搭建高可用的 k8s 集群、青云 LB(负载均衡)与 k8s 实战(二)。
-
GitLab 代码仓库管理开源系统一套。
-
Harbor 容器镜像开源系统一套,搭建可参考Harbor 高可用集群设计及部署。
-
SonarQube 开源自动代码审查工具一套,搭建可参考SonarQube 实战:部署(一)、SonarQube 实战:各种姿势的代码审查(二)。(可选,主要用于代码质量检查)
-
一个更易于构建云原生应用的动态服务发现、配置管理和服务管理的 Nacos 开源平台一套,搭建可参考基于 Docker 搭建 Nacos 高可用集群。–可选,Pig 已提供 naocs 服务,即 register 服务。
-
高性能的 key-value 数据库 Redis(3.2 +)一套。–Pig 需要
-
关系型开源数据库管理系统 MySQL 一套。–Pig 需要
-
高性能对象存储 Minio 一套。–Pig 中文件上传需要,可选。或者阿里云、华为云、腾讯对象存储也可。搭建可参考你好,MinIO。
四、架构设计
4.1 KubeSphere 架构

该图来自 KubeSphere 官网架构说明。
4.2 Pig 架构

该图来自 Pig 白皮书中的基础架构图。
4.3 整体架构图

4.4 整体容器化部署流程图

五、部署过程
5.1 创建企业空间



5.2 创建 DevOps 项目

5.3 创建项目
项目用于对资源进行分组管理和控制不同用户的资源管理权限。

5.4 部署 MySql
1)进入应用商店,在应用分类中选择数据库和缓存,找到 Mysql。如下所示:


2)在基本信息中,填写应用名称 pig-mysql,并选择位置,进行下一步。如下所示:

3)在应用配置中,编辑 yaml 文件,将镜像改为 mysql/mysql-server:8.0.30,将密码设置为 root。如下所示:

mysql 镜像采用 pig 项目 db 下 Dockerfile 中的版本,也可自己指定。
4)点击安装

5)进入 pig-mysql 服务,编辑外部访问,从而访问 mysql 导入 pig 的数据



6)进入 mysql 容器,调整帐号允许从远程登陆


登录 mysql 进行授权操作
$ mysql -uroot -proot
$ use mysql;
$ update user set host='%' where user='root';
$ flush privileges;
$ ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root';
$ flush privileges;
7)利用 Navicat 客户端连接 pig-mysql 服务,导入数据

5.5 部署 Redis
1)进入应用商店,在应用分类中选择数据库和缓存,找到 Redis。如下所示:



注:Pig 中默认使用无密码模式,因此可以暂时留空。生产环境不推荐将密码设置为空
2)安装成功后,如下所示:

5.6 创建凭证
1)创建 kubeconfig 凭证,如下所示:
名称自定义,需要和 Jenkinsfile 中的一致即可,内容默认或者去/root/.kube 下复制。
2)创建 harbor 凭证,如下所示:

名称自定义,需要和 Jenkinsfile 中的一致即可。
3)创建 gitlab 凭证,如下所示:
名称自定义,需要和 Jenkinsfile 中的一致即可。
全部凭证如下:

5.7 设置 harbor 镜像仓库
新建一个 pig-dev 项目,如下所示:

5.8 部署 Pig 后端无状态服务
1)新建 pig 后端流水线,如下所示:

选择代码仓库

编辑设置

2)代码中创建 Jenkinsfile 文件:

内容如下:
pipeline {
agent {
label 'maven'
}
parameters {
choice(choices: ['dev', 'test', 'pre', 'pre2','prod'], name: 'Environments', description: '请选择要发布的环境:dev开发环境、test测试环境、pre预发布环境、pre2灰度环境、prod 生产环境')
choice(choices: ['pig-gateway', 'pig-auth', 'pig-register', 'pig-upms-biz','pig-codegen', 'pig-monitor', 'pig-sentinel-dashboard', 'pig-xxl-job-admin','all'], name: 'ServicesDeploy', description: '请选择要构建的服务,支持单个服务发布或全部服务发布')
choice(choices: ['no', 'yes'], name: 'sonarQube', description: '是否进行sonarQube代码质量检查,默认为no')
string(name: 'MultiServicesBuild', defaultValue: 'no', description: '自由组合发布服务,如填写pig-gateway,pig-auth等,默认此项不生效,和ServicesDeploy只能选其一')
}
environment {
HARBOR_CREDENTIAL_ID = 'harbor-id'
GITLAB_CREDENTIAL_ID = 'gitlab'
KUBECONFIG_CREDENTIAL_ID = 'pig-kubeconfig'
REGISTRY = 'ip:端口'//harbor镜像仓库
HARBOR_NAMESPACE = 'pig-dev'
K8S_NAMESPACE = 'pig-dev'
}
stages {
stage ('拉取代码') {
steps {
checkout(scm)
}
}
stage('初始化变量') {
agent none
steps {
container('maven') {
script {
//自由组合发布
if("${params.MultiServicesBuild}".trim() != "no") {
ServicesBuild = "${params.MultiServicesBuild}".split(",")
for (service in ServicesBuild) {
println "now got ${service}"
}
}else if("${params.ServicesDeploy}".trim() == "all"){
ServicesBuildStr = 'pig-gateway,pig-auth,pig-register,pig-upms-biz,pig-codegen,pig-monitor,pig-sentinel-dashboard,pig-xxl-job-admin'
ServicesBuild = "${ServicesBuildStr}".split(",")
}else if("${params.ServicesDeploy}".trim() != "all"){
ServicesBuild = "${params.ServicesDeploy}".split(",")
}
}
}
}
}
stage('sonarQube代码质量检查') {
steps {
script {
if("${params.sonarQube}".trim() == "yes") {
for (service in ServicesBuild) {
def workspace = "pig-"
println "当前进行代码质量检查是:${service}"
if("${service}".trim() == "pig-gateway" || "${service}".trim() == "pig-auth" || "${service}".trim() == "pig-register"){
workspace = "${workspace}" + "${service}".trim().split("-")[1]
}
if("${service}".trim() == "pig-codegen" || "${service}".trim() == "pig-monitor" || "${service}".trim() == "pig-sentinel-dashboard" || "${service}".trim() == "pig-xxl-job-admin"){
workspace = "pig-visual/" + "${service}".trim()
}
if("${service}".trim() == "pig-upms-biz"){
workspace = "pig-upms/" + "${service}".trim()
}
//定义当前Jenkins的SonarQubeScanner工具
scannerHome = tool 'sonar-scanner'
//引用当前JenkinsSonarQube环境
withSonarQubeEnv('sonarqube9.4') {
sh """
cd ${workspace}
${scannerHome}/bin/sonar-scanner
"""
}
}
}else{
println "是no,跳过sonarQube代码质量检查"
}
}
}
}
stage('打包') {
agent none
steps {
container('maven') {
script {
sh "mvn -Dmaven.test.skip=true clean package -P${params.Environments}"
}
}
}
}
stage('构建镜像') {
agent none
steps {
container('maven') {
script {
for (service in ServicesBuild) {
def workspace = "pig-"
println "当前构建的镜像是:${service}"
stage ("build ${service}") {
if("${service}".trim() == "pig-gateway" || "${service}".trim() == "pig-auth" || "${service}".trim() == "pig-register"){
workspace = "${workspace}" + "${service}".trim().split("-")[1]
}
if("${service}".trim() == "pig-codegen" || "${service}".trim() == "pig-monitor" || "${service}".trim() == "pig-sentinel-dashboard" || "${service}".trim() == "pig-xxl-job-admin"){
workspace = "pig-visual/" + "${service}".trim()
}
if("${service}".trim() == "pig-upms-biz"){
workspace = "pig-upms/" + "${service}".trim()
}
sh "cd ${workspace} && docker build -f Dockerfile -t $REGISTRY/$HARBOR_NAMESPACE/${service}:$BUILD_NUMBER ."
}
}
}
}
}
}
stage('镜像推送') {
agent none
steps {
container('maven') {
script {
for (service in ServicesBuild) {
println "当前推送的镜像是:${service}"
stage ("push ${service}") {
withCredentials([usernamePassword(passwordVariable : 'HARBOR_PASSWORD' ,usernameVariable : 'HARBOR_USERNAME' ,credentialsId : "$HARBOR_CREDENTIAL_ID" ,)]) {
sh 'echo "$HARBOR_PASSWORD" | docker login $REGISTRY -u "$HARBOR_USERNAME" --password-stdin'
sh "docker push $REGISTRY/$HARBOR_NAMESPACE/${service}:$BUILD_NUMBER"
}
}
}
}
}
}
}
stage('推送镜像之latest') {
agent none
steps {
container('maven') {
script {
for (service in ServicesBuild) {
println "当前推送的latest镜像是:${service}"
stage ("pushLatest ${service}") {
sh "docker tag $REGISTRY/$HARBOR_NAMESPACE/${service}:$BUILD_NUMBER $REGISTRY/$HARBOR_NAMESPACE/${service}:latest"
sh "docker push $REGISTRY/$HARBOR_NAMESPACE/${service}:latest"
}
}
}
}
}
}
stage('部署到dev环境') {
steps {
container ('maven') {
script {
for (service in ServicesBuild) {
//自定义的全局变量,也就是整个流水线可以去使用
env.APP_NAME = "${service}"
if("${service}".trim() == "pig-gateway") {
env.NODEPORT = 31201
env.PORT = 9999
}
if("${service}".trim() == "pig-auth") {
env.NODEPORT = 31202
env.PORT = 3000
}
if("${service}".trim() == "pig-register") {
env.NODEPORT = 31203
env.PORT = 8848
}
if("${service}".trim() == "pig-upms-biz") {
env.NODEPORT = 31204
env.PORT = 4000
}
if("${service}".trim() == "pig-codegen") {
env.NODEPORT = 31205
env.PORT = 5002
}
if("${service}".trim() == "pig-monitor") {
env.NODEPORT = 31206
env.PORT = 5001
}
if("${service}".trim() == "pig-sentinel-dashboard") {
env.NODEPORT = 31207
env.PORT = 5003
}
if("${service}".trim() == "pig-xxl-job-admin") {
env.NODEPORT = 31208
env.PORT = 5004
}
stage ("deploy ${service}") {
println "即将部署的服务是 $APP_NAME"
withCredentials([
kubeconfigFile(
credentialsId: env.KUBECONFIG_CREDENTIAL_ID,
variable: 'KUBECONFIG')
]) {
if("${service}".trim() == "pig-register") {
sh "envsubst < deploy/${params.Environments}/nacos-devops.yaml | kubectl apply -f -"
}else{
sh "envsubst < deploy/${params.Environments}/devops.yaml | kubectl apply -f -"
}
}
}
}
}
}
}
}
}
}
通过${service}来判断最终选择那个 deploy 来部署。
3)代码中创建 devops.yaml 部署文件:

内容如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
progressDeadlineSeconds: 600
replicas: 1
selector:
matchLabels:
app: $APP_NAME
template:
metadata:
labels:
app: $APP_NAME
spec:
containers:
- image: $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:$BUILD_NUMBER
imagePullPolicy: Always
name: $APP_NAME
ports:
- containerPort: $PORT
protocol: TCP
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
ports:
- name: http
port: $PORT
protocol: TCP
targetPort: $PORT
nodePort: $NODEPORT
selector:
app: $APP_NAME
sessionAffinity: None
type: NodePort
4)代码中创建 nacos-devops.yaml 部署文件:
内容如下:
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
serviceName: $APP_NAME
replicas: 3
selector:
matchLabels:
app: $APP_NAME
template:
metadata:
labels:
app: $APP_NAME
annotations:
pod.alpha.kubernetes.io/initialized: "true"
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: "app"
operator: In
values:
- nacos
topologyKey: "kubernetes.io/hostname"
containers:
- image: $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:$BUILD_NUMBER
imagePullPolicy: Always
name: $APP_NAME
ports:
- containerPort: 8848
name: client-port
- containerPort: 9848
name: client-rpc
- containerPort: 9849
name: raft-rpc
- containerPort: 7848
name: old-raft-rpc
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
annotations:
service.alpha.kubernetes.io/tolerate-unready-endpoints: "true"
spec:
ports:
- port: 8848
protocol: TCP
name: server
targetPort: 8848
nodePort: $NODEPORT
- port: 9848
name: client-rpc
targetPort: 9848
- port: 9849
name: raft-rpc
targetPort: 9849
## 兼容1.4.x版本的选举端口
- port: 7848
name: old-raft-rpc
targetPort: 7848
selector:
app: $APP_NAME
sessionAffinity: None
type: NodePort
后续这些文件都可可采用共享仓库来统一管理。
5)发布
进入 DevOps 项目->pig-dev->pig-backend-dev->运行

这里 KubeSphere3.3.0 版本中有个 bug,choice 类型的还是识别为 string,所以暂时只能手动输入。此 bug 在 3.3.1 版本可修复。

查看任务状态

查看日志

在项目->应用负载->服务下查看刚发布的 pig-register 服务


发布其它服务
进入 DevOps 项目->pig-dev->pig-backend-dev->运行->选择自由组合发布

发布完成查看服务

至此已经完成 Pig 后端无状态服务的部署。
5.9 部署 Pig 前端无状态服务
1)新建 pig 前端流水线,如下所示:

选择代码仓库

编辑设置

2)代码中创建 Jenkinsfile 文件:

内容如下:
pipeline {
agent {
node {
label 'nodejs'
}
}
parameters {
choice(choices: ['dev', 'test', 'pre', 'pre2','prod'], name: 'Environments', description: '请选择要发布的环境:dev开发环境、test测试环境、pre预发布环境、pre2灰度环境、prod 生产环境')
choice(choices: ['no', 'yes'], name: 'sonarQube', description: '是否进行sonarQube代码质量检查,默认为no')
}
environment {
HARBOR_CREDENTIAL_ID = 'harbor-id'
GITLAB_CREDENTIAL_ID = 'gitlab'
KUBECONFIG_CREDENTIAL_ID = 'pig-kubeconfig'
REGISTRY = 'ip:端口'//harbor镜像仓里
HARBOR_NAMESPACE = 'pig-dev'
APP_NAME = 'pig-front'
K8S_NAMESPACE = 'pig-dev'
}
stages {
stage ('拉取代码') {
steps {
container('nodejs') {
checkout(scm)
}
}
}
stage('sonarQube代码质量检查') {
steps {
script {
if("${params.sonarQube}".trim() == "yes") {
println "当前进行代码质量检查是:${APP_NAME}"
//定义当前Jenkins的SonarQubeScanner工具
scannerHome = tool 'sonar-scanner'
//引用当前JenkinsSonarQube环境
withSonarQubeEnv('sonarqube9.4') {
sh """
cd .
${scannerHome}/bin/sonar-scanner
"""
}
}else{
println "是no,跳过sonarQube代码质量检查"
}
}
}
}
stage('项目编译') {
agent none
steps {
container('nodejs') {
sh 'node -v'
sh 'npm -v'
sh 'npm install'
sh 'npm run build:docker'
sh 'ls'
}
}
}
stage('构建镜像') {
agent none
steps {
container('nodejs') {
sh 'ls'
sh 'cd ./docker && docker build -t $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:$APP_NAME-$BUILD_NUMBER .'
}
}
}
stage('镜像推送') {
agent none
steps {
container('nodejs') {
withCredentials([usernamePassword(passwordVariable : 'HARBOR_PASSWORD' ,usernameVariable : 'HARBOR_USERNAME' ,credentialsId : "$HARBOR_CREDENTIAL_ID" ,)]) {
sh 'echo "$HARBOR_PASSWORD" | docker login $REGISTRY -u "$HARBOR_USERNAME" --password-stdin'
sh 'docker push $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:$APP_NAME-$BUILD_NUMBER'
}
}
}
}
stage('推送镜像之latest') {
agent none
steps {
container('nodejs') {
sh 'docker tag $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:$APP_NAME-$BUILD_NUMBER $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:latest '
sh 'docker push $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:latest '
}
}
}
stage('部署到dev环境') {
steps {
container ('nodejs') {
withCredentials([
kubeconfigFile(
credentialsId: env.KUBECONFIG_CREDENTIAL_ID,
variable: 'KUBECONFIG')
]) {
sh "envsubst < deploy/${params.Environments}/devops.yaml | kubectl apply -f -"
}
}
}
}
}
}
3)代码中创建 devops.yaml 部署文件:

内容如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
progressDeadlineSeconds: 600
replicas: 1
selector:
matchLabels:
app: pig-front
strategy:
rollingUpdate:
maxSurge: 50%
maxUnavailable: 50%
type: RollingUpdate
template:
metadata:
labels:
app: pig-front
spec:
containers:
- image: $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:$APP_NAME-$BUILD_NUMBER
imagePullPolicy: Always
name: pig-front-end
ports:
- containerPort: 80
protocol: TCP
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
dnsPolicy: ClusterFirst
restartPolicy: Always
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
nodePort: 31200
selector:
app: $APP_NAME
sessionAffinity: None
type: NodePort
后续这些文件都可可采用共享仓库来统一管理。
4)发布

这里 KubeSphere3.3.0 版本中有个 bug,choice 类型的还是识别为 string,此 bug 在 3.3.1 版本可修复。

查看任务状态

查看日志

在项目->应用负载->服务下查看刚发布的 pig-front 服务

至此所有的服务均已发布完成

利用KubeSphere中Jenkins发布
访问ip:30180 账号:admin 密码:P@88w0rd
六、通过 NodePort 方式暴露集群内部容器服务

输入 node ip + 31200 访问


七、优化和改进
7.1 通过探针优雅的解决部署过程中服务平滑过渡问题
livenessProbe:存活性探测
readinessProbe:就绪性探测
1)SpringBoot 的 actuator

需引以下 maven:
<!-- 引入Actuator监控依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在 SpringBoot-2.3 版本中,actuator 新增了两个地址:/actuator/health/liveness 和/actuator/health/readiness,前者用作 kubernetes 的存活探针,后者用作 kubernetes 的就绪探针,需要先在配置文件中开启,如下:
management:
endpoint:
health:
probes:
enabled: true
health:
livenessstate:
enabled: true
readinessstate:
enabled: true
/actuator/health/和/actuator/health/是默认开启的。

利用 SpringBoot 的接口来作为容器探针的健康检测,按照如下就可以:
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
httpGet:
scheme: HTTP
port: 9999
path: /actuator/health/readiness
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 6
httpGet:
scheme: HTTP
port: 9999
path: /actuator/health/liveness
2)pig 后端项目增加探针
pig 项目全局所有的模块都会引入 Actuator 监控依赖,如下:
<!--监控-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
调整 pig 项目后端 devops.yaml,增加以下内容:
...
readinessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
httpGet:
scheme: HTTP
port: $PORT
path: /actuator/health
livenessProbe:
initialDelaySeconds: 40
periodSeconds: 10
timeoutSeconds: 5
httpGet:
scheme: HTTP
port: $PORT
path: /actuator/health
...
完整 devops.yaml 内容如下:
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
progressDeadlineSeconds: 600
replicas: 1
selector:
matchLabels:
app: $APP_NAME
template:
metadata:
labels:
app: $APP_NAME
spec:
containers:
- image: $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:$BUILD_NUMBER
imagePullPolicy: Always
name: $APP_NAME
ports:
- containerPort: $PORT
protocol: TCP
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
readinessProbe:
initialDelaySeconds: 90
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
httpGet:
scheme: HTTP
port: $PORT
path: /actuator/health
livenessProbe:
initialDelaySeconds: 100
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
httpGet:
scheme: HTTP
port: $PORT
path: /actuator/health
dnsPolicy: ClusterFirst
restartPolicy: Always
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
ports:
- name: http
port: $PORT
protocol: TCP
targetPort: $PORT
nodePort: $NODEPORT
selector:
app: $APP_NAME
sessionAffinity: None
type: NodePort
注:/actuator/health/readiness 和/actuator/health/liveness 也可以用,需在配置文件中开启。若是内存、CPU 限制过低,需要调整 initialDelaySeconds 时间,否则会出现还未启动成功,就开始探测,会进入循环,直到探测失败(就是 failureThreshold 定义的次数),要掌握好这个时间的度。
重新发布 pig-geteway 测试

正在进行探测



3)pig 前端项目增加探针
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
httpGet:
scheme: HTTP
port: 80
path: /
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
httpGet:
scheme: HTTP
port: 80
path: /
完整 devops.yaml 如下:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
progressDeadlineSeconds: 600
replicas: 1
selector:
matchLabels:
app: pig-front
strategy:
rollingUpdate:
maxSurge: 50%
maxUnavailable: 50%
type: RollingUpdate
template:
metadata:
labels:
app: pig-front
spec:
containers:
- image: $REGISTRY/$HARBOR_NAMESPACE/$APP_NAME:$APP_NAME-$BUILD_NUMBER
imagePullPolicy: Always
name: pig-front-end
ports:
- containerPort: 80
protocol: TCP
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
readinessProbe:
initialDelaySeconds: 20
periodSeconds: 10
timeoutSeconds: 5
httpGet:
scheme: HTTP
port: 80
path: /
livenessProbe:
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
httpGet:
scheme: HTTP
port: 80
path: /
dnsPolicy: ClusterFirst
restartPolicy: Always
terminationGracePeriodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
labels:
app: $APP_NAME
name: $APP_NAME
namespace: $K8S_NAMESPACE
spec:
ports:
- name: http
port: 80
protocol: TCP
targetPort: 80
nodePort: 31200
selector:
app: $APP_NAME
sessionAffinity: None
type: NodePort

7.2 通过探针优雅的解决服务部署过程中注册中心服务平滑过渡问题
自己编写容器探针接口
/**
* @author 小盒子
* @version 1.0
* @description: 容器探针接口,用来进行探测服务是否注册进入注册中心
* @date 2022/10/22 10:58
*/
@Slf4j
@RestController
@RequestMapping("nacos")
public class HealthController {
@Autowired
private DiscoveryClient discoveryClient;
@GetMapping("/health/{services}")
public ResponseEntity<Object> getService(@PathVariable("services")String services) throws NacosException {
//从nacos中根据serverId获取实例 方法一(采用此方法,DiscoveryClient 代表的就是:服务发现操作对象)
if (StringUtils.isBlank(services)){
return new ResponseEntity<Object>(HttpStatus.BAD_REQUEST);
}
List<ServiceInstance> instances = discoveryClient.getInstances(services);
Map<String, Integer> ipMap = new HashMap<>();
if (instances.size() > 0){
instances.forEach(key ->{
ipMap.put(key.getHost(), key.getPort());
});
}
//从nacos中根据serverId获取实例 方法二 采用nacos的java的SDK
// Properties properties = new Properties();
// properties.put("serverAddr", "192.168.4.25:8849");
// properties.put("namespace", "caseretrieval-dev");
// NamingService naming = NamingFactory.createNamingService(properties);
// List<Instance> instancesList = naming.selectInstances("case-gateway", true);
//从nacos中根据serverId获取实例 方法三,采用nacos的OPEN-api
//http://192.168.4.25:8849/nacos/v1/ns/instance/list?serviceName=case-gateway&namespaceId=caseretrieval-dev
//获取本机IP地址
String hostAddress = "127.0.0.1";
try {
InetAddress localHost = InetAddress.getLocalHost();
hostAddress = localHost.getHostAddress();
log.info("当前服务本机ip是:"+hostAddress);
} catch (UnknownHostException e) {
log.error(e.getMessage(), e);
}
//查看本机服务是否已经注册到nacos中
if (ipMap.containsKey(hostAddress)){
return new ResponseEntity<Object>(HttpStatus.OK);
}else {
return new ResponseEntity<Object>(HttpStatus.SERVICE_UNAVAILABLE);
}
}
}
其实正常情况下,合理的滚动更新策略,加上就绪性探测、存活性探测或者启动探测就能达到目标了,也不必这么麻烦。
7.3 通过 Ingress 方式暴露集群内部容器服务

1)开启项目网关
对项目中的外网访问网关以及服务治理等配置进行设置和管理。



KubeSphere 中的项目网关,开启后会自动安装 IngressController。
对应在 k8s 集群中 svc 如下图所示:

KubeSphere 中应用路由是需要手动创建的,如下图所示:


2)设置路由规则:


指定自定义域名并通过本地 hosts 文件或 DNS 服务器将域名解析为网关 IP 地址。
3)访问 http://pig.com:32576/

7.4 将 SonarQube 集成到流水线
在Jenkins中集成SonarQube
在sonarQube中生成token

在Jenkins中安装SonarQube Scanner插件

在Jenkins中添加SonarQube凭证

选择Secret text 作为凭证类型。
完成后如下所示:

-
在Jenkins系统配置中配置SonarQube

-
在Jenkins全工具中安装 SonarQube Scanner

sonar-scanner 名称自定义,在jenkinsfile中要保持一直。
-
关闭审查结果上传 SCM 功能

后端代码审查
-
调整Jenkins脚本
如以下示例:
stage('sonarQube代码质量检查') {
steps {
script {
if("${params.sonarQube}".trim() == "yes") {
for (service in ServicesBuild) {
def workspace = "pig-"
println "当前进行代码质量检查是:${service}"
if("${service}".trim() == "pig-gateway" || "${service}".trim() == "pig-auth" || "${service}".trim() == "pig-register"){
workspace = "${workspace}" + "${service}".trim().split("-")[1]
}
if("${service}".trim() == "pig-codegen" || "${service}".trim() == "pig-monitor" || "${service}".trim() == "pig-sentinel-dashboard" || "${service}".trim() == "pig-xxl-job-admin"){
workspace = "pig-visual/" + "${service}".trim()
}
if("${service}".trim() == "pig-upms-biz"){
workspace = "pig-upms/" + "${service}".trim()
}
//定义当前Jenkins的SonarQubeScanner工具
scannerHome = tool 'sonar-scanner'
//引用当前JenkinsSonarQube环境
withSonarQubeEnv('sonarqube9.4') {
sh """
cd ${workspace}
${scannerHome}/bin/sonar-scanner
"""
}
}
}else{
println "是no,跳过sonarQube代码质量检查"
}
}
}
}
注:sonar-scanner和sonarqube9.4名称需和Jenkins中配置的一致。
-
新建sonar-project.properties文件
以pig-gateway为例,在pig项目中新增sonar-project.properties文件。

内容如下:
#项目的key(自定义)
sonar.projectKey=pig-gateway
#项目名称
sonar.projectName=pig-gateway
#项目版本号
sonar.projectVersion=1.0
#需要分析的源码的目录,多个目录用英文逗号隔开
sonar.sources=.
#需要忽略的目录
sonar.exclusions=**/test/**,**/target/**
# 字节码文件所在位置
sonar.java.binaries=.
sonar.java.source=1.8
sonar.java.target=1.8
#sonar.java.libraries=**/target/classes/**
# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8
发布测试

在jenkins中查看

在SonarQube中查看代码检测情况

pig-gateway的代码质量还是很错的,不过有一个漏洞哦。
前端代码审查
-
调整Jenkins脚本
stage('sonarQube代码质量检查') {
steps {
script {
if("${params.sonarQube}".trim() == "yes") {
println "当前进行代码质量检查是:${APP_NAME}"
//定义当前Jenkins的SonarQubeScanner工具
scannerHome = tool 'sonar-scanner'
//引用当前JenkinsSonarQube环境
withSonarQubeEnv('sonarqube9.4') {
sh """
cd .
${scannerHome}/bin/sonar-scanner
"""
}
}else{
println "是no,跳过sonarQube代码质量检查"
}
}
}
}
-
新建sonar-project.properties文件

内容如下:
sonar.projectKey=pig_ui
sonar.projectName=pig_ui
sonar.projectVersion=1.0
sonar.sources=.
发布测试

在jenkins中查看

在SonarQube中查看代码检测情况

若想解锁SonarQube各种姿势,请参考SonarQube 实战:各种姿势的代码审查(二)。
· END ·
如果这篇文章对您有帮助或者有所启发的话,请帮忙三连暴击点赞、转发和在看。您的支持是我坚持更新的最大动力。
原文始发于微信公众号(架构至美):云原生翘楚KubeSphere 和 知名开源项目 Pig 最佳实践
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/52008.html