阿里云Kubernetes实战

栏目: 服务器 · 发布时间: 4年前

内容简介:整体的业务流程如下图所示:一、一机多Jenkins Slave
编辑推荐:
本文来自于云社区,本文将通过实际案例串联所有的基础软件服务,基于K8S做DevOps,希望本文对您的学习有所帮助。

整体的业务流程如下图所示:

阿里云Kubernetes实战

一、一机多Jenkins Slave

由于业务需要,我们的自动化测试需要基于windows做web功能测试,每一个测试任务独占一个windows用户桌面,所以我们首先要给Jenkins配置几个Windows的Slave Node.在我之前的post《持续集成CI实施指南三–jenkins集成测试》中详细讲解了给Jenkins添加Node的方法步骤。 本篇无需重复,但这里主要讲的是,如何在一台Windows服务器上搭建多个Jenkins Node,供多用户使用。

在目标机上建立多个用户,如下图所示:

阿里云Kubernetes实战

用Administrator用户安装JDK

在Jenkins的节点管理建立三个Node,分别为WinTester01、WinTester02、WinTester03,配置如下

阿里云Kubernetes实战

在目标机的Administrator,用IE打开Jenkins并进入节点管理,在 WinTester01、WinTester02、WinTester03 中分别点击“Launch”启动Slave

阿里云Kubernetes实战

确认启动成功后,点击“File”下的“Install as service”

阿里云Kubernetes实战

三个Slave都启动后,可以在服务管理器看到

阿里云Kubernetes实战

除了Jenkins Slave1无需配置,Slave2和Slave3都需要右键进入属性,修改登录用户分别为JenkinsSlave2和JenkinsSlave3

阿里云Kubernetes实战

通过上面的配置,可以在一台目标机部署三个用户对应三个Jenkins Slave以满足我们的业务需求。

二、 二次开发Jenkins 钉钉通知插件

在整个DevOps的业务流程图上,我们想使用钉钉作为通知方式,相比邮件而言,实时性和扩展性都很高。在2018年4月,Jenkins的钉钉通知插件有两款,分别是Dingding JSON Pusher和Dingding notification plugin,前者长期未更新,已经不能使用,后者可以在非Pipeline模式下使用,对于Pipeline则有一些问题。虽然目前,Dingding notification plugin已经更新到1.9版本并支持了Pipeline,但在当时,我们不得不在1.4版本的基础上做二次开发。

整体开发经过参考《Jenkins项目实战之-钉钉提醒插件二次开发举例》,总体来说还是比较简单:

修改”src/main/java/com/ztbsuper/dingtalk / DingTalkNotifier . java ”,钉钉的消息API类型有文本、link、markdown、card等,我们这里把通知接口改成文本类型

public class DingTalkNotifier extends Notifier implements SimpleBuildStep {
 ?
 private String accessToken;
 private String message;
 private String imageUrl;
 private String messageUrl;
 ?
 @DataBoundConstructor
 public DingTalkNotifier(String accessToken, String message, String imageUrl, String messageUrl) {
 this.accessToken = accessToken; //钉钉的accesstoken
 this.message = message; //消息主体
 this.imageUrl = imageUrl; //缩略图
 this.messageUrl = messageUrl; //消息的链接来源,一般是jenkins的build url
 }
 ?
 public String getAccessToken() {
 return accessToken;
 }
 public String getMessage() {
 return message;
 }
 public String getImageUrl() {
 return imageUrl;
 }
 public String getMessageUrl() {
 return messageUrl;
 }
 ?
 @Override
 public void perform(@Nonnull Run<?, ?> run, @Nonnull FilePath filePath, @Nonnull Launcher launcher, @Nonnull TaskListener taskListener) throws InterruptedException, IOException {
 String buildInfo = run.getFullDisplayName();
 if (!StringUtils.isBlank(message)) {
 sendMessage(LinkMessage.builder()
 .title(buildInfo)
 .picUrl(imageUrl)
 .text(message)
 .messageUrl(messageUrl)
 .build());
 }
 }
 ?
 private void sendMessage(DingMessage message) {
 DingTalkClient dingTalkClient = DingTalkClient.getInstance();
 try {
 dingTalkClient.sendMessage(accessToken, message);
 } catch (IOException e) {
 e.printStackTrace();
 }
 }
 ?
 @Override
 public BuildStepMonitor getRequiredMonitorService() {
 return BuildStepMonitor.NONE;
 }
 ?
 @Symbol("dingTalk")
 @Extension
 public static final class DescriptorImpl extends BuildStepDescriptor<Publisher> {
 ?
 @Override
 public boolean isApplicable(Class<? extends AbstractProject> aClass) {
 return true;
 }
 ?
 @Nonnull
 @Override
 public String getDisplayName() {
 return Messages.DingTalkNotifier_DescriptorImpl_DisplayName();
 }
 }
 } 

用maven打包

maven需要安装 java 环境,为了方便,我直接run一个maven的docker image,编译完成后把hpi文件send出来

在jenkins的插件管理页面上传hpi文件

阿里云Kubernetes实战

在钉钉群中开启自定义机器人

阿里云Kubernetes实战

找到accesstoken

阿里云Kubernetes实战

在jenkins pipeline中可以使用以下命令发送信息到钉钉群

dingTalk accessToken:"2fccafaexxxx",message:"信息",imageUrl:"图片地址",messageUrl:"消息链接"

三、 DevOps解决方案

针对每一个软件项目增加部署目录,目录结构如下:

_deploy 
 master 
 deployment.yaml
 Dockerfile
 other files
 test 
 deployment.yaml
 Dockerfile
 other files
 

master和test文件夹用于区分测试环境与生产环境的部署配置

Dockerfile和other files用于生成应用或服务的镜像

如前端vue和nodejs项目的Dockerfile:

# 前端项目运行环境的Image,从Harbor获取
 FROM xxx/xxx/frontend:1.0.0 
 RUN mkdir -p /workspace/build && mkdir -p /workspace/run
 COPY . /workspace/build
 # 编译,生成执行文件,并删除源文件
 RUN cd /workspace/build/frontend && \
 cnpm install && \
 npm run test && \
 cp -r /workspace/build/app/* /workspace/run && \
 rm -rf /workspace/build && \
 cd /workspace/run && \
 cnpm install 
 # 运行项目,用npm run test或run prod区分测试和生产环境
 CMD cd /workspace/run && npm run test 

又如dotnet core项目的Dockerfile:

# dotnet项目编译环境的Image,从Harbor获取
 FROM xxx/xxx/aspnetcore-build:2 AS builder
 WORKDIR /app
 COPY . .
 # 编译
 RUN cd /app/xxx
 RUN pwd && ls -al && dotnet restore
 RUN dotnet publish -c Release -o publish
 ?
 # dotnet项目运行环境的Image,从Harbor获取
 FROM xxx/xxx/aspnetcore:2
 WORKDIR /publish
 COPY --from=builder /app/xxx/publish .
 # 重命名配置文件,中缀test、prod用于区分测试环境和生产环境
 RUN mv appsettings.test.json appsettings.json
 # 运行
 ENTRYPOINT ["dotnet", "xxx.dll"] 

deployent.yaml用于执行应用或服务在k8s上的部署

由于deployment有很多配置项可以抽离成公共配置,所以deployment的配置有很多占位变量,占位变量用两个#中间加变量名表示,如下所示:

apiVersion: v1 
 kind: Namespace 
 metadata: 
 name: #namespace# 
 labels: 
 name: #namespace# 
 ---
 apiVersion: v1
 data:
 .dockerconfigjson: xxxxxxxxxxxxxxxxxxxxxx
 kind: Secret
 metadata:
 name: regcred
 namespace: #namespace# 
 type: kubernetes.io/dockerconfigjson
 ---
 apiVersion: extensions/v1beta1
 kind: Deployment
 metadata:
 name: #app#-deploy
 namespace: #namespace# 
 labels:
 app: #app#-deploy
 spec:
 replicas: #replicas#
 strategy:
 type: Recreate
 template:
 metadata:
 labels:
 app: #app#
 spec:
 containers:
 - image: #image# 
 name: #app#
 ports:
 - containerPort: #port#
 name: #app#
 securityContext:
 privileged: #privileged#
 volumeMounts:
 - name: log-volume
 mountPath: #log#
 - image: #filebeatImage# 
 name: filebeat
 args: [
 "-c", "/etc/filebeat.yml"
 ]
 securityContext:
 runAsUser: 0
 volumeMounts:
 - name: config
 mountPath: /etc/filebeat.yml
 readOnly: true
 subPath: filebeat.yml
 - name: log-volume
 mountPath: /var/log/container/
 volumes:
 - name: config
 configMap:
 defaultMode: 0600
 name: filebeat-config
 - name: log-volume
 emptyDir: {}
 imagePullSecrets:
 - name: regcred
 ---
 apiVersion: v1
 kind: ConfigMap
 metadata:
 name: filebeat-config
 namespace: #namespace#
 labels:
 app: filebeat
 data:
 filebeat.yml: |-
 filebeat.inputs:
 - type: log
 enabled: true
 paths:
 - /var/log/container/*.log
 output.elasticsearch:
 hosts: ["#es#"]
 tags: ["#namespace#-#app#"]
 ---
 apiVersion: v1
 kind: Service
 metadata:
 name: #app#-service
 namespace: #namespace# 
 labels:
 app: #app#-service
 spec:
 ports:
 - port: 80
 targetPort: #port# 
 selector:
 app: #app#
 ---
 apiVersion: extensions/v1beta1
 kind: Ingress
 metadata:
 name: #app#-ingress
 namespace: #namespace# 
 annotations:
 nginx.ingress.kubernetes.io/proxy-body-size: "0"
 nginx.ingress.kubernetes.io/rewrite-target: /
 spec:
 rules:
 - host: #host# 
 http:
 paths:
 - path: #urlPath#
 backend:
 serviceName: #app#-service
 servicePort: 80 

其中几个关键变量的解释如下:

dockerconfigjson:因为所有的镜像需要从Harbor获取,而Harbor的镜像如果设置为私有权限,就需要提供身份验证,这里的 dockerconfigjson 就是Harbor的身份信息。生成dockerconfigjson的方法如下:

进入K8S任何一个节点,删除” ~/.docker/config.json ” 文件

使用命令” docker login harbor地址”登录harbor

通过命令” cat ~/.docker/config.json “ 可以看到harbor的身份验证信息

使用命令” cat /root/.docker/config.json | base64 -w 0 “对信息编码,将生成后的编码填写到deployment.yaml的dockerconfigjson节点即可

namespace:同一个项目的不同k8s组件应置于同一个namespace,所以namespace可统一配置,在我们的项目实践中,生产环境的namespace为” 项目名 “,测试环境的namespace为” 项目名-test “

app:应用或服务名称

image:应用或服务的镜像地址

replicas:副本数量

port:应用或服务的Pod开放端口

log:应用或服务的日志路径,在本系列的第二篇文章中,提到我们的日志方案是给每个应用或服务配一个filebeat,放在同一Pod中,这里只需告知应用或服务的日志的绝对路径,filebeat就能将日志传递到ES中,日志的tag命名方式为” namespace-app”

host:在本系列的第一篇文章中,讲了使用nginx ingress做服务暴露与负载。这里的host就是给nginx ingress 设置的域名,端口默认都是80,如果需要https,则在外层使用阿里云SLB转发

urlPath:很多情况下,如微服务,需要通过相同的域名,不同的一级目录将请求分发到不同的后台,在nginx中,就是location的配置与反向代理,比如host的配置是确定了域名aaa.bbb.com,而urlPath的配置是确定 aaa .bbb.com/user /getuser 将会被转发到用户服务 podIP:podPort / getuser中

以上所有的占位变量都是在Pipeline Script中赋值,关于 Jenkins Pipeline 的相关内容介绍这里不再多讲,还是去看官方文档靠谱。我们这里将k8s的部署文件deployment.yaml与Jenkinsfile结合,即可做到一个deployment.yaml能适配所有项目,一个Pipeline Script模板能适配所有项目,针对不同的项目,只需在Pipeline Script中给占位变量赋值,大大降低了配置复杂度。下面是一个项目的Jenkins配置示例:

阿里云Kubernetes实战

对于一个项目,我们只需配置Trigger和Pipeline,上图“Do not allow concurrent builds ”也是通过Pipeline的配置生成的。Pipeline Script示例如下:

pipeline {

// 指定项目在label为jnlp-agent的节点上构建,

也就是Jenkins Slave in Pod

agent { label 'jnlp-agent' }

// 对应Do not allow concurrent builds

options {

disableConcurrentBuilds()

}

environment {

// ------ 以下内容,每个项目可能均有不同,按需修改

------

//author:用于钉钉通知

author="张三"

// branch: 分支,一般是test、 master,对应git从哪个

分支拉取代码,也对应究竟执行_deploy文件夹下的test

配置还是master配置

branch = "test"

// namespace: myproject-test, myproject,命名空间一

般是项目名称,

测试环境加test

namespace = "myproject-test"

// hostname:对应deployment中的host

host = "test.aaa.bbb.com"

// appname:对应deployment中的app

app = "myserver"

// port:对应deployment中的port

port= "80"

// replicas:对应deployment中的replicas

replicas = 2

//git repo path:git的地址

git="git@git.aaa.bbb.com/xxx.git"

//log:对应deployment中的log

log="/publish/logs/"

// ------ 以下内容,一般所有的项目都一样,不经常修改

------

// harbor inner address

repoHost = "192.168.0.1:23280"

// harbor的账号密码信息,在jenkins中配置用户名

/密码形式的认证信息,命名成harbor即可

harborCreds = credentials('harbor')

// filebeat的镜像地址

filebeatImage="${repoHost}/common/filebeat:6.3.1"

// es的内网访问地址

es="elasticsearch-logging.kube-system:9200"

}

// ------ 以下内容无需修改 ------

stages {

// 开始构建前清空工作目录

stage ("CleanWS"){

steps {

script {

try{

deleteDir()

}catch(err){

echo "${err}"

sh 'exit 1'

}

}

}

}

// 拉取

stage ("CheckOut"){

steps {

script {

try{

checkout([$class: 'GitSCM', branches: [[name: "*/${branch}"]], doGenerateSubmoduleConfigurations:

false, extensions: [],

submoduleCfg: [], userRemoteConfigs: [[credentialsId:

'gitlab', url: "${git}"]]])

}catch(err){

echo "${err}"

sh 'exit 1'

}

}

}

}

// 构建

stage ("Build"){

steps {

script {

try{

// 登录 harbor

sh "docker login -u ${harborCreds_USR} -p ${harborCreds_PSW}

${repoHost}"

sh "date +%Y%m%d%H%m%S > timestamp"

// 镜像tag用时间戳代表

tag = readFile('timestamp').replace("\n", "").replace

("\r", "")

repoPath = "${repoHost}/${namespace}/${app}:${tag}"

// 根据分支,进入_deploy下对应的不同文件夹,通过dockerfile打包镜像

sh "cp _deploy/${branch}/* ./"

sh "docker login -u ${harborCreds_USR} -p ${harborCreds_PSW}

${repoHost}"

sh "docker build -t ${repoPath} ."

}catch(err){

echo "${err}"

sh 'exit 1'

}

}

}

}

// 镜像推送到harbor

stage ("Push"){

steps {

script {

try{

sh "docker push ${repoPath}"

}catch(err){

echo "${err}"

sh 'exit 1'

}

}

}

}

// 使用pipeline script中复制的变量替换deployment.yaml

中的占位变量,执行deployment.yaml进行部署

stage ("Deploy"){

steps {

script {

try{

sh "sed -i 's|#namespace#|${namespace}|g' deployment

.yaml"

sh "sed -i 's|#app#|${app}|g' deployment.yaml"

sh "sed -i 's|#image#|${repoPath}|g' deployment.yaml"

sh "sed -i 's|#port#|${port}|g' deployment.yaml"

sh "sed -i 's|#host#|${host}|g' deployment.yaml"

sh "sed -i 's|#replicas#|${replicas}|g' deployment

.yaml"

sh "sed -i 's|#log#|${log}|g' deployment.yaml"

sh "sed -i 's|#filebeatImage#|${filebeatImage}|g' deployment.yaml"

sh "sed -i 's|#es#|${es}|g' deployment.yaml"

sh "sed -i 's|#redisImage#|${redisImage}|g' deployment.yaml"

sh "cat deployment.yaml"

sh "kubectl apply -f deployment.yaml"

}catch(err){

echo "${err}"

sh 'exit 1'

}

}

}

}

}

post {

// 使用钉钉插件进行通知

always {

script {

def msg = "【${author}】你把服务器搞挂了,

老詹喊你回家改BUG!"

def imageUrl = "https://www.iconsdb.com/icons/

preview/red

/x-mark-3-xxl-2.png"

if (currentBuild.currentResult=="SUCCESS"){

imageUrl= "http://icons.iconarchive.com/icons/paomedia

/small-n-flat/1024/sign-check-icon-2.png"

msg ="【${author}】发布成功,干得不错!"

}

dingTalk accessToken:"xxxx",message:"${msg}",imageUrl:"$

{imageUrl}",

messageUrl:"${BUILD_URL}"

}

}

}

}

发布完成后,可以参考《持续集成CI实施指南三–jenkins集成测试》,做持续测试,测试结果也可通过钉钉通知。最后我们利用自建的运维平台,监控阿里云ECS状态、K8S各组件状态、监控ES中的日志并做异常抓取和报警。形成一整套DevOps模式。

综上,对于每个项目,我们只需维护Dockerfile,并在Jenkins创建持续集成项目时,填写项目所需的参数变量。进阶情况下,也可定制性的修改deployment文件与pipeline script,满足不同的业务需要。至此,完结,撒花!


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Creative Curve

The Creative Curve

Allen Gannett / Knopf Doubleday Publishing Group / 2018-6-12

Big data entrepreneur Allen Gannett overturns the mythology around creative genius, and reveals the science and secrets behind achieving breakout commercial success in any field. We have been s......一起来看看 《The Creative Curve》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

随机密码生成器
随机密码生成器

多种字符组合密码

SHA 加密
SHA 加密

SHA 加密工具