Kubernetes一个自动化部署java项目的最佳实践
前言 :
一个容器中最好只存在一个进程,方便健康检查
容器的基础镜像越小越好
本文将部署一个java后端接口项目在k8s,并实现自动化部署及无缝更新。
痛点:
入口不统一
不方便配置SSL及负载和统一配置其他策略。
-
使用固定一台Nginx用作处理请求,将接口请求转发给k8s的clusterIP 👌
-
使用k8s的service
配置文件
配置文件需要根据环境进行更改,现在配置文件都在本机文件不利于灵活部署。
-
使用配置中心
-
使用启动参数 👌
日志
多节点会导致查看日志不便。
此项痛点不是刚需,大部分只有调试阶段才会出现频繁看日志的情况,上线后需求少。
-
ELK(不推荐,功能完整但是组件复杂 整体比较庞大,而且有延迟。)
-
保持现状,进入容器查看日志👌
业务可用性
保持业务可用性,减少更新带来的中断时间。
- 使用滚动更新,配置容器的启动探针 👌
可伸缩性
根据业务量随时扩容与缩减。保障业务量大的时候支撑服务,业务量少的时候节省资源使用。
- kubernetes👌
部署更新简便
迭代交付方便 简单 迅速
- jenkins shell-script docker👌
- jenkins 流水线 (有学习门槛,使用shell脚本更加方便通用)
整体部署流程:
构建镜像
部署harbor私有镜像仓库
harbor[1]
Harbor是使用docker的方式部署的,用docker compose编排。
- Harbor服务分在线安装和离线安装,这里推荐使用离线安装:
#下载harbor离线包
wget https://github.com/goharbor/harbor/releases/download/v2.4.1-rc1/harbor-offline-installer-v2.4.1-rc1.tgz
tar -zxvf harbor-offline-installer-v2.4.1-rc1.tgz
- 进入解压后的目录,编辑
harbor.yml
配置文件。如下几项进行更改:
"harbor.yml" 201L, 7893C
#主机名,推荐设置证书的域名
hostname: test.abc.cn
# http的端口
http:
# # port for http, default is 80. If https enabled, this port will redirect to https port
port: 8086
# https的端口
#这里推荐使用https,因为docker拉取镜像默认是https的。如果不设置https这里可以跳过
https:
# https port for harbor, default is 443
port: 8084
# ssl证书位置
certificate: /opt/nginx/conf/ssl/5507833_test.abc.cn.pem
private_key: /opt/nginx/conf/ssl/5507833_test.abc.cn.key
#管理员密码
harbor_admin_password: harbor32354
# Harbor DB configuration
database:
# The password for the root user of Harbor DB. Change this before any production use.
password: root123
# The maximum number of connections in the idle connection pool. If it <=0, no idle connections are retained.
max_idle_conns: 100
# The maximum number of open connections to the database. If it <= 0, then there is no limit on the number of open connections.
# Note: the default number of connections is 1024 for postgres of harbor.
max_open_conns: 900
# 存放镜像的目录,这里会做映射的 写宿主机的目录就行了。
data_volume: /data
详细配置见官网[2]
- 开始安装,运行
install.sh
即可
如果你没有使用https的harbor,必须将 --insecure-registry
添加到docker引擎内,一般配置文件在/etc/docker/daemon.json
{
"insecure-registries" : ["myregistrydomain.com:5000", "0.0.0.0"]
}
保存并重启docker引擎和harbor
systemctl restart docker
docker-compose down -v
docker-compose up -d
- 登录harbor的地址,使用配置文件中的密码。
新建一个test的项目,开始准备实验:
- 使用docker登录仓库
docker login <harbor_address>
-
推送一个测试镜像
#标记图像 docker tag hello-world:latest <harbor_address>/test/hello-world:latest #推送 docker push <harbor_address>/test/hello-world:latest
-
登录web控制台即可查看推送的镜像。
构建业务镜像
- 本次实验是部署一个maven构建的java后端接口项目,所以基础镜像使用jdk来做。
Dockerfile文件如下:
FROM openjdk:8u312-jdk-oraclelinux8
ADD sbserver.jar app.jar
EXPOSE 9093
ENTRYPOINT ["java","-jar","/app.jar","--spring.profiles.active=dev"]
- 其中sbserver.jar是项目jar包,把他放到和dockerfile同路径下。
如果是war包,基础镜像就使用tomcat的。
- EXPOSE是声明端口,注意自己sprint boot项目的配置文件端口,根据实际情况填写或者更改相应的配置文件。
- 构建并推送镜像
docker build -t sbserver:v0.1 .
#-t 指定镜像名称及标签 注意后面有个.是代表工作路径为当前路径
#设置标签,其中harbor更换为你们对应的地址
docker tag sbserver:v0.1 <harbor_address>/test/sbserver:v0.1
#推送镜像
docker push <harbor_address>/test/sbserver:v0.1
- 登录harbor镜像仓库控制台,看看刚推送的业务镜像是否存在。
配置kubernetes
本次实验并不是完全自动化,在创建新的部署项目时还需要手动创建 k8s的部署任务和jenkins任务。
新建项目
- 登录k8s 控制台
从右上角新建一个任务,选择从表单创建对新人比较友好。
-
填写配置
deployments详细配置可以见文档[3]
配置项 | 值 | 备注 |
---|---|---|
应用名称 | sbserver | |
容器镜像 | <harbor_address>/test/sbserver:v0.1 | 注意更改仓库地址 |
pod数量 | 1 | 需要运行的节点数量 |
service | External | 对外服务 |
端口 | 9093 | 根据实际情况填写 |
描述 | 任意 | |
命名空间 | test | 默认default,这里需要创建一个 |
拉取镜像的secret | ts-re | 默认是default token,这里是拉取镜像的凭据,我们harbor中的镜像是私有的,所以这里要新建一个。 |
命名空间这里选择新建一个,名为test
secret 拉取镜像的凭据,这里使用命令行创建一个。
#进入master主机
kubectl create secret docker-registry ts-re --docker-server=test.abc.cn:8084 --docker-username=admin --docker-password=hardorps -n test
- docker-registry ts-re 凭据名称
- --docker-server test.abc.cn:8084 仓库地址
- --docker-username admin 登录仓库的用户名,这里可以在harbor控制台新建一个用户
- --docker-password 用户的密码
- -n 指定命名空间,这里test是前面刚创建的
- 完成配置后部署,在上方命名空间选择test
部署页(Deployments)中可以看到前面配置的应用.
在pod页可以看到容器具体的启动情况,可以点击如下查看日志和进入容器终端。
查看容器启动情况,如果失败注意pod的事件和日志内容。
service这里使用默认的(LoadBalancer),pods数量大于1的时候会有负载均衡。
service的详细用法可以参照官方文档[4]
配置容器探针
- 编辑app.yml配置探针功能,目的是为了实现无缝更新,让k8s更清楚的了解容器的就绪情况。
在部署页(Deployments)中选择编辑
目录层级如下,注意缩进是俩个空格不要使用制表符
spec:
template:
spec:
containers:
livenessProbe:
kind: Deployment
apiVersion: apps/v1
metadata:
name: sbserver
namespace: test
...略
spec:
...略
template:
metadata:
name: sbserver
labels:
k8s-app: sbserver
spec:
containers:
- name: sbserver
image: test.abc.cn:8084/test/sbserver:v0.1
resources: {}
#配置容器存活探针,此处使用的http请求。(还有TCP和shell命令等探测方法)
livenessProbe:
httpGet:
#请求的路径,这个是一个测试的接口,请求这个路径会返回200状态,根据实际应用情况更改。
path: /sbserver/state
port: 9093
scheme: HTTP
#容器启动后 等待5秒再开始检测
initialDelaySeconds: 5
#超时时间
timeoutSeconds: 1
#检测间隔
periodSeconds: 5
#成功阈值
successThreshold: 1
#失败阈值
failureThreshold: 3
#启动前探针,此处可以让一些启动比较慢的应用一些宽容时间。
#选项与上面基本一样
startupProbe:
httpGet:
path: /sbserver/state
port: 9093
scheme: HTTP
timeoutSeconds: 1
#探测间隔
periodSeconds: 6
successThreshold: 1
#失败阈值,此处和探测间隔就是在任务分发下来后,有6*20 120秒的时间启动
#每过6秒探测一次,如果探测了20次还没有启动就认为启动失败,k8s就不会把流量转入这个新的pod上。
failureThreshold: 20
...略
容器的探针用法详细参照官方文档[5]
完成以上配置,点击更新。
- 测试无缝更新
向私有镜像仓库重新推送一个app版本,模拟生产的版本迭代
#设置标签,这里直接还是使用v0.1的镜像,但是更改一下标签
docker tag sbserver:v0.1 <harbor_address>/test/sbserver:v0.2
#推送镜像
docker push <harbor_address>/test/sbserver:v0.2
#临时编写一个测试脚本,模拟用户一直在请求流量。
cat test-service.sh
#!/bin/bash
while true
do
#此处curl的地址是service对外暴露映射的随机高端口,创建完毕后此端口是固定的。
curl -o /dev/null -s http://192.168.31.182:30489/sbserver/state -w "%{http_code}\n"
sleep 1
done
#保存,开个终端在边上运行着。
chmod +x test-service.sh
./test-service.sh
200
200
200
#如上一直返回200即代表现在请求都是正常的。
编辑app.yml,修改其中的image标签即可完成更新操作
spec:
...略
template:
metadata:
name: sbserver
labels:
k8s-app: sbserver
spec:
containers:
- name: sbserver
image: test.sbc.cn:8084/test/sbserver:v0.2
#设置标签为v0.2
点击更新即可完成更新操作,同步查看后台测试脚本是否一直返回200而且无中断。
至此k8s最佳实践基本完成,后续配合jenkins和shell脚本进行自动化部署。
配置jenkins
实现自动部署
Jenkins的部署及详细配置方式这里不多解释。
新建一个Maven的项目
主题 | 配置项 | 值 | 备注 |
---|---|---|---|
源码管理 | git | git地址 | |
分支 | test | ||
构建触发器 | 无 | 这里不使用触发器 手动构建 | |
构建 | 调用maven | -T 32 clean package -Dmaven.test.skip=true | -T多线程编译 |
执行shell脚本 | sudo /opt/jenkins/workspace/update-test_k8s.sh sbserver 9093 | 此处脚本是自定义脚本,根据需要可以自己编写。 |
编写脚本
具体实现自动化的主要是shell脚本,因为我不太会流水线 所以这里使用脚本代替。
cat update-test_k8s.sh
#!/bin/bash
#自动化更新k8s接口服务
if [ -z ${1} ];then
echo -e "\033[31m 请传参(服务名 端口),程序退出。 \033[0m"
exit 1
fi
#设置target目录
target="/opt/jenkins/workspace/${1}-test-k8s/ruoyi-admin/target"
#设置k8s 命名空间
namespace="test"
#设置kubectl配置文件
kubeConfig="/etc/kubernetes/admin.conf"
#当前最后一个版本号
n=`docker images ${1} --format "{{.Tag}}" | head -1 | awk -F. '{print $2}'`
last="v0.${n}"
#版本号+1
let "n++"
last1="v0.${n}"
#镜像仓库地址
docHub="test.abc.cn:8084/test"
echo -e "\033[36m \n \
target目录:${target}\n \
k8s命名空间:${namespace}\n \
kubectl配置文件:${kubeConfig}\n \
当前镜像:${1}:${last}\n \
预计打包镜像:${1}:${last1}\n \
镜像仓库地址:${docHub}\n \
\033[0m"
#-----------
fhz(){
if [ $1 == 0 ];then
echo -e "\033[36m ${2}完成 \033[0m"
else
echo -e "\033[31m ${2}失败,原因见上,程序退出。 \033[0m"
exit 1
fi
}
#构建业务镜像
FROM="openjdk:8u312-jdk-oraclelinux8"
ADD="${1}.jar app.jar"
EXPOSE="${2}"
ENTRYPOINT='["java","-jar","/app.jar","--spring.profiles.active=dev"]'
cd ${target}
fhz $? 进入目录${target}
mv ./ruoyi-admin.jar ./${1}.jar
fhz $? 重命名${1}.jar
if [ -f "Dockerfile" ];then
echo -e "\033[36m Dockerfile文件存在 \033[0m"
else
echo -e "\033[31m Dockerfile文件不存在,正在创建 \033[0m"
echo "FROM ${FROM}" >> Dockerfile
echo "ADD ${ADD}" >> Dockerfile
echo "EXPOSE ${EXPOSE}" >> Dockerfile
echo "ENTRYPOINT ${ENTRYPOINT}" >> Dockerfile
fi
echo -e "\033[36m Dockerfile内容如下: \033[0m"
cat Dockerfile
docker build -t ${1}:${last1} .
fhz $? 构建镜像${1}:${last1}
docker tag ${1}:${last1} ${docHub}/${1}:${last1}
fhz $? 镜像标签${docHub}/${1}:${last1}设置
docker push ${docHub}/${1}:${last1}
fhz $? 推送镜像${docHub}/${1}:${last1}
#k8s部署任务
kubectl --kubeconfig ${kubeConfig} -n ${namespace} set image deploy ${1} *=${docHub}/${1}:${last1}
fhz $? k8s分发部署任务
echo -e "\033[36m 如果此容器有问题将会导致新版本无法上架,业务不受影响,具体请登录k8s控制台查看容器启动情况。 \033[0m"
保存此脚本在Jenkins节点主机上面,构建的时候会调用此脚本。注意此机器上面也要安装Kubctl用来更改镜像标签。
声明
原文:k8s最佳实践 - good good study,day day up~
作者:AHA