1. rancher容器云管理平台测试笔记:
在本篇博文中,主要讲解如下几个知识点和实践经验,供大家参考:
1. 如何选型一个容器云管理平台:
2. Rancher的简介:
3. rancher调用ceph分布式存储自动创建卷:
4. rancher的CI/CD:
5. 通过jenkins结合rancher部署一个spring cloud架构的微服务应用:
1. 如何选型一个容器云管理平台:
2019年容器技术特别的火,而基于容器调度编排的kubernetes(简称K8S)更是所有运维人员必备的技术之一。但是k8s的学习成本非常高,要熟悉YAML编排文件,要了解K8S的各种资源对象(比如 pod、service、Deloyment、DaemonSet、StatefulSet、Job、CronJob、ConfigMap、Secrets、PV、PVC、StorageClass、RBAC、Role、ServiceAccount、ClusterRoleBinding、NetworkPolicy、Helm、Ingress)等等;要把每一个资源类型原理都了解清楚,并熟悉命令行的配置的yaml清单文件的书写格式,没有一年半载是很难做到的。所以这也就是容器云管理平台产生的原因;
为什么需要容器的云管理平台,目的其实就是简化K8S的使用门槛,将原有的命令行的配置变成了图形化。并且不需要精通每个资源类型的使用方法,通过图形界面很简单的就能够运行起来一个应用;下来我们说说选择一个容器的管理平台,主要从哪几个方面测试是否满足要求,以下的总结纯属个人经验,仅做参考:
1. 容器云管理平台如何实现CI/CD持续集成和持续发布应用
2. 容器云管理平台如何对接存储,是否支持各种分布式存储架构,比如ceph、GlusterFS等
3. 容器云管理平台如何实现镜像的管理,能否对接公司现有的Harbor镜像仓库等
4. 容器云管理平台的日志接入如何实现,能否接入已有的EFK日志平台;
5. 容器云管理平台如何实现监控,能否对接已有的prometheus、grafana平台;
6. 容器云管理平台如何实现弹性伸缩,比如伸缩宿主机节点,伸缩Pod,滚动更新应用等,要求能够快速简单的伸缩;
7. 容器云管理平台权限管理,多租户的网络策略实现等;
8. 容器云管理平台多集群管理,多数据中心管理,应用的多数据中心迁移,特别是能否管理已有的K8S集群,包括已有的阿里云K8S集群等;
9. 容器云管理平台能否实现Service Mesh,微服务的链路跟踪、限流、熔断、故障排查等;
10. 容器云管理平台是否支持应用市场,特别是以Helm为标准的常用公共组件,比如Redis、Mysql、RabbitMQ、EFK等常用组件的Helm配置和安装;
11. 容器云管理平台社区活跃度、是否有技术支持,版本升级是否能及时对接最新的K8S版本等;
2. Rancher的简介:
Rancher是一个开源的企业级容器管理平台。通过Rancher,企业再也不必自己使用一系列的开源软件去从头搭建容器服务平台。Rancher提供了在生产环境中使用的管理Docker和Kubernetes的全栈化容器部署与管理平台。
当你选择使用Docker技术栈的时候, 会发现在生产环境中不光光是 docker run就能解决的. 还需要考虑比如docker之间的组网, 缩扩容等问题, 于是你去学习kubernetes, 发现好像有点复杂啊, 有没有更傻瓜化一点的? 那就是rancher了.
3. rancher调用ceph分布式存储自动创建卷:
要使用rancher调用ceph存储来实现StorageClass自动创建pv,首先需要将ceph存储MON节点的密钥文件拷贝到所有的rancher计算节点的/etc/ceph目录下面。并且需要在所有的ranhcer计算节点安装ceph-common软件,加载rbd模块;
安装ceph-common,rancher node节点操作
yum install –y ceph-common
加载内核模块rbd,rancher node节点操作
modprobe rbd
拷贝/etc/ceph/* 到目标机器,ceph mon节点操作
scp -r /etc/ceph/* root@rancher_node节点ip:/etc/ceph/


根据现有的ceph rbd存储的配置,修改参数



apiVersion: v1
kind: Secret
metadata:
name: ceph-secret
namespace: kube-system
data:
key: QVFEdkJhZGN6ZW41SFJBQUQ5RzNJSVU0djlJVXRRQzZRZjBnNXc9PQ==
type: kubernetes.io/rbd
apiVersion: v1
kind: Secret
metadata:
name: ceph-user-secret
namespace: kube-system
data:
key: QVFDTks2ZGNjcEZoQmhBQWs4anVvbmVXZnZUeitvMytPbGZ6OFE9PQ==
type: kubernetes.io/rbd
注意这里的key内容是通过ceph的mon节点上面通过命令获取的
[root@k8sdemo-ceph1 ~]# ceph auth get-key client.admin | base64
QVFEdkJhZGN6ZW41SFJBQUQ5RzNJSVU0djlJVXRRQzZRZjBnNXc9PQ==
从ceph服务器上面拷贝/etc/ceph/所有文件到rancher节点的/etc/ceph目录,所有rancher节点安装ceph-common软件,加载modprobe rbd模块



4. rancher的CI/CD:
说实话rancher的CI/CD流程做的真的不咋滴,应该是2.0版本才有了这个功能。完全不能和Jenkins相比;有几点需要我吐槽的:
maven .m2本地仓库不支持缓存;
每次构建都要重新在maven仓库拉取jar包,太慢了。
有时候构建一个应用程序需要半个小时;
听说是可以通过把依赖的jar包打进到maven镜像,但是这样的操作太麻烦了。
而且不同的java程序依赖的JAVA包也不一样。
不能每次都重新制作maven镜像把。
这样maven镜像岂不是特别大;
docker也不支持缓存,我们都知道docker镜像是分层的。
也就是下一次build的时候如果一些基础镜像,比如JDK镜像等都不用重新下载,很快就会build好的。
但是rancher的docker并不是通过挂载宿主机的docker.socket实现的,而是通过docker in docker的方式,所以镜像构建每次都没有缓存;
rancher的CI/CD流程很不灵活,因为我们的微服务比较多,每次更新版本的时候不是所有微服务都更新,可能是其中一个或者几个微服务更新。
如果在jenkins里面是可以通过环境变量,由用户自己选择更新哪个模块来实现的。
但是在rancher里面好像是不能定义环境变量。
stage是串行的,step是并行的。
配置rancher的CI/CD流程,主要包含以下几个步骤:
1. 配置rancher和gitlab结合,由rancher自动识别gitlab仓库上面的项目,由用户选择更新哪个项目;2. 配置私有镜像仓库harbor的凭证信息;3. 根据需要增加stage和step等设置,包括通过maven构建,打包镜像等;


创建一个访问harbor的凭证信息


根据提示登录gitlab配置settings—-applications


流水线的gitlab配置完成之后,rancher就会自动识别gitlab下面的所有项目,根据需要开启某一个项目的流水线配置。这样的话,我们图形界面配置的CI就会自动在gitlab项目下面生成一个 .rancher-pipeline.yml



根据需要增加stage和step等设置,包括通过maven构建,打包镜像等;



rancher的CI/CD流程从源码下载—-使用自定义的maven镜像完成mvn clean package—-docker build构建镜像,前提是在你的gitlab仓库里面有写好的Dockerfile—-可以通过调用rancher的API接口,直接更新应用的image版本,前期是先通过图形界面创建好一个应用;
Dockerfile可以参考我写的
注释:
第一行指的是基础镜像,表明jdk镜像的版本;
定义时区;
ADD 命令是将maven构建之后的target目录下面的jar包拷贝到容器里面的应用启动目录/app下面
定义容器的运行命令
所有的微服务基本都是一样的,只有ADD文件的jar包不同,启动命令的jar包不同而已;
FROM 192.168.1.102/library/java:8-jdk-alpine
ENVTZ=Asia/Shanghai
VOLUME /tmp
ADD target/*.jar /app/test/admin.jar
ENTRYPOINT["java","-Djava.security.egd=file:/dev/./urandom","-XX:+UnlockExperimentalVMOptions","-XX:+UseCGroupMemoryLimitForHeap","-jar","/app/test/admin.jar"]


rancher的应用商店做的比较好,可以适用常用的helm工具。如果你的微服务依赖于redis mysql等组件,那么就用应用商店来安装是最合适的了。只要你会helm的配置修改,使用应用商店就没有问题。


把应用商店的URL地址改成azure中国的,Dockerfile可以这样写:
curl -k -utoken-rsvqj:h2ntkr8jmptctktsjrbsd9hw97mqdnw986mf5l4qh4bmzzqb2mdvrg
-X PUT \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d'{"containers":[{"image":"k8s.harbor.maimaiti.site/rancher/oamboot-activiti:'${CICD_EXECUTION_SEQUENCE}'","name":"oam-activiti"}]}'\
'https://10.83.22.246/v3/project/c-b4q7d:p-dm2fp/workloads/deployment:oam:oam-activiti'
注意这里的token-rsvqj的产生方式:
我们接下来通过rancher部署一下redis和mysql


根据注释查看mysql的helm配置参数

5. 通过jenkins结合rancher部署一个spring cloud架构的微服务应用:
前面我说过通过rancher实现CI/CD真的比较麻烦,所以后面我还是通过jenkins结合rancher的方式来实现,jenkins完成的是编译和镜像的构建,而rancher完成的是k8s的管理;在使用jenkins之前,需要先把微服务模块的application.yml里面的配置,比如redis mysql eureka的全部用环境变量代替,一般为${MYSQL_HOST}等类似的格式。然后启动rancher deployment的时候,将变量替换成值传进去;
配置jenkins的pipeline脚本,通过pipeline脚本实现前后端的编译、docker镜像构建等任务


node {
try {
stage('代码拉取') {
if ("${TYPE2}".contains('tst_aom_backend')){
//git credentialsId: 'k8sgitlab', url: 'http://test@git.test.club/lu.xu/tst_aom_backend.git'
git branch: "test", credentialsId: 'k8s-gitlab-root-password', url: 'http://k8s.gitlab.tst.site/root/tst_aom_backend.git'
}
if ("${TYPE2}".contains('tst_aom_front')){
//git credentialsId: 'k8sgitlab', url: 'http://yang02@git.test.club/lu.xu/tst_aom_backend.git'
git branch: "gao", credentialsId: 'k8s-gitlab-root-password', url: 'http://k8s.gitlab.ttt.site/root/tst_aom_front.git'
}
}
stage('项目构建') {
if ("${TYPE2}".contains('tst_aom_backend')){
sh "mvn clean package"
}
if ("${TYPE2}".contains('tst_aom_front')){
sh "/opt/software/node-v9.0.0-linux-x64/bin/npm i"
sh "/opt/software/node-v9.0.0-linux-x64/bin/npm run build"
sh "cp -r dist docker/"
}
}
def regPrefix = 'k8s.harbor.ttt.site/rancher/'
stage('镜像构建') {
docker.withRegistry('http://k8s.harbor.ttt.site/','k8sharbor'){
if ("${TYPE2}".contains('tst_aom_front')){
dir('docker') {
def imageName = docker.build("${regPrefix}aom-nginx:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
sh "/usr/bin/docker rmi ${regPrefix}cmm-nginx:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-eurekaServer')){
dir('aomboot-eureka-server') {
def imageName = docker.build("${regPrefix}aom-eurekaserver:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}aom-eurekaserver:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aombootActiviti')){
dir('aomboot-activiti') {
def imageName = docker.build("${regPrefix}aom-activiti:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}aom-activiti:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-applyBasicOperation')){
dir('aomboot-applyBasicOperation') {
def imageName = docker.build("${regPrefix}apply-basicoperation:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}apply-basicoperation:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-applyDB')){
dir('aomboot-applyDB') {
def imageName = docker.build("${regPrefix}aom-applydb:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}aom-applydb:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-applyOperation')){
dir('aomboot-applyOperation') {
def imageName = docker.build("${regPrefix}apply-operation:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}apply-operation:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-basic')){
dir('aomboot-basic') {
def imageName = docker.build("${regPrefix}aom-basic:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}aom-basic:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-cmdb')){
dir('aomboot-cmdb') {
def imageName = docker.build("${regPrefix}aomboot-cmdb:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}aomboot-cmdb:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-email')){
dir('aomboot-email') {
def imageName = docker.build("${regPrefix}aom-email:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}aom-email:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-file')){
dir('aomboot-file') {
def imageName = docker.build("${regPrefix}aom-file:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}aom-file:V1.0-${env.BUILD_ID}"
}
}
if ("${MODULE}".contains('aomboot-zuul')){
dir('aomboot-zuul') {
def imageName = docker.build("${regPrefix}aom-api-getaway-zuul:V1.0-${env.BUILD_ID}")
imageName.push("V1.0-${env.BUILD_ID}")
//imageName.push("latest")
sh "/usr/bin/docker rmi ${regPrefix}aom-api-getaway-zuul:V1.0-${env.BUILD_ID}"
}
}
}
}
}catch (any) {
currentBuild.result = 'FAILURE'
throw any}
}
注释:1、 定义了前端的git下载地址、后端的git下载地址;2、 定义了前端的构建方式,后端的构建方式;3、 定义了镜像的仓库地址,使用jenkins的docker插件来完成镜像的build push等任务;








前端使用了nginx来运行,nginx的配置文件我通过configmap映射到容器里面,这样修改配置文件比较方便;
[root@k8s01 software]# cat nginx.conf
server {
listen 80; ##端hello口为8081
server_name oam.maimaiti.site;
root /usr/share/nginx/html/; ##改数据目录为/html
location / {
}
}
[root@k8s01 software]#
kubectl create configmap oam-conf --from-file=./nginx.conf -n cmm
gitlab上面的微服务模块的配置文件application.yml一般需要将一些配置修改成变量的方式,方便rancher部署的时候传变量进去;
server:
port: 8484
context-path: /tstbootActiviti # pom中的finalName
spring:
application:
name: tst-activiti
#mysql
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://${MYSQLHOST}:3306/${MYSQLDB}?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&allowMultiQueries=true
username: ${MYSQLUSER}
password: ${MYSQLPASSWD}
#redis
redis:
host: ${REDISHOST}
port: 6379
password: ${REDISPASSWD}
#jpa
jpa:
database: MYSQL
show-sql: true
# hibernate:
# ddl-auto: update
jackson:
serialization:
indent_output: true
#eureka
eureka:
instance:
# hostname: localhost
hostname: ${EUREKAHOST}
port: 8181
statusPageUrlPath: ${server.context-path}/info
healthCheckUrlPath: ${server.context-path}/health
preferIpAddress: true
instance-id: ${spring.cloud.client.ipAddress}:${server.port}
lease-renewal-interval-in-seconds: 10 #eureka client发送心跳给server端的频率(默认30秒)
lease-expiration-duration-in-seconds: 20 #eureka client发送心跳给server端后,续约到期时间(默认90秒)
client:
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${eureka.instance.port}/tstEurekaServer/eureka/
registry-fetch-interval-seconds: 10 #eureka client间隔多久去拉取服务注册信息(默认30秒)对于api-gateway如果要迅速获取服务注册状态,可以缩小该值
#log
logging:
config: classpath:logback-spring.xml
#mybatis日志
level:
com:
java:
activiti:
business:
dao: debug
#close security
security:
basic:
enabled: false
##mybatis指向mapper的xml文件位置
mybatis:
mapper-locations: classpath:mybatis/mapper/*.xml
type-aliases-package: com.java.activiti.business.entity.mybatis
注释:主要是将数据库服务器地址、数据库名、数据库用户名、数据库密码、redis服务器地址、redis密码、eureka服务器地址 这7个字段的值通过变量代替,然后在rancher平台部署微服务的时候,传变量进去;其他微服务模块的配置文件相似;
今天就写到这里吧,由于使用rancher的操作很多都是图形化界面完成的。我写博文的这个软件不能嵌入图形,所以写到有点乱。大家如果有好的可以写博文的软件也介绍给我一下,可以写代码也可以嵌入图形的那种。本身今天早上是早起公司来跑步的,结果遇到了雷阵雨,跑步就泡汤了。来的太早没事干就把自己这段时间做容器云的项目总结一下,也是给自己工作的一个梳理和总结;




