1. k8s pod版本更新流程及命令行实现升级与回滚
1.1 代码的几种发布方式
1.1.1 蓝绿发布
- 项目逻辑上分为AB组,在项目系统时,首先把A组从负载均衡中摘除,进行新版本的部署。B组仍然继续提供服务。
-
当A组升级完毕,负载均衡重新接入A组,再把B组从负载列表中摘除,进行新版本的部署。A组重新提供服务。
-
最后,B组也升级完成,负载均衡重新接入B组,此时,AB组版本都已经升级完成,并且都对外提供服务。
特点:
- 如果出问题,影响范围较大;
- 发布策略简单;
- 用户无感知,平滑过渡;
- 升级/回滚速度快。
缺点:
- 需要准备正常业务使用资源的两倍以上服务器,防止升级期间单组无法承载业务突发;
- 短时间内浪费一定资源成本;
- 基础设施无改动,增大升级稳定性。
1.1.2 滚动发布
滚动发布是指每次只升级一个或多个服务,升级完成后加入生产环境,不断执行这个过程,直到集群中的全部旧版本升级新版本。
- 红色:正在更新的实例
- 蓝色:更新完成并加入集群的实例
- 绿色:正在运行的实例
特点:
- 用户无感知,平滑过渡;
- 节约资源。
缺点:
- 部署时间慢,取决于每阶段更新时间;
- 发布策略较复杂;
- 无法确定OK的环境,不易回滚。
部署过程:
- 先升级1个副本,主要做部署验证;
- 每次升级副本,自动从LB上摘掉,升级成功后自动加入集群;
- 事先需要有自动更新策略,分为若干次,每次数量/百分比可配置;
- 回滚是发布的逆过程,先从LB摘掉新版本,再升级老版本,这个过程一般时间比较长;
- 自动化要求高。
1.1.3 金丝雀/灰度发布
灰度发布只升级部分服务,即让一部分用户继续用老版本,一部分用户开始用新版本,如果用户对新版本没什么意见,那么逐步扩大范围,把所有用户都迁移到新版本上面来。
特点:
- 保证整体系统稳定性,在初始灰度的时候就可以发现、调整问题,影响范围可控;
- 新功能逐步评估性能,稳定性和健康状况,如果出问题影响范围很小,相对用户体验也少;
- 用户无感知,平滑过渡。
缺点:
- 自动化要求高
部署过程:
- 从LB摘掉灰度服务器,升级成功后再加入LB;
- 少量用户流量到新版本;
- 如果灰度服务器测试成功,升级剩余服务器。
1.2 虚拟机环境下的版本升级实践流程
Scripts:https://codeup.aliyun.com/614c9d1b3a631c643ae62857/Scripts.git
1.3 k8环境下的版本升级
1.3.1 滚动更新和回滚
https://kubernetes.io/zh/docs/tutorials/kubernetes-basics/update/update-intro/
用户希望应用程序始终可用,而开发人员则需要每天多次部署它们的新版本。在 Kubernetes 中,这些是通过滚动更新(Rolling Updates)完成的。 滚动更新 允许通过使用新的实例逐步更新 Pod 实例,零停机进行 Deployment 更新。新的 Pod 将在具有可用资源的节点上进行调度。
在指定的deployment中通过kubectl set image来指定新版本的镜像tag,来实现更新代码的目的。
Deployment 控制器支持两种更新策略:默认为滚动更新
- 滚动更新(rolling update)
- 滚动更新是默认的资源更新策略,滚动更新是基于新版本镜像创建新版本的pod,然后删除一部分旧版本pod,然后在创建新版本pod,再删除一部分旧版本pod,直到新版本pod删除完成,滚动更新优势是在升级过程中不会导致服务不可用,缺点是升级过程中会导致两个版本在短时间内并存。
- 具体升级过程是在执行更新操作后k8s会在创建一个新版本的ReplicaSet控制器,在删除旧版本的ReplicaSet控制器下的pod。同时会在新版本ReplicaSet控制器下创建新的pod,直到旧版本的pod全部被删除完后再把旧版本的ReplicaSet控制器回收。
- 在执行滚动更新的同时,为保证服务的可用性,当前控制器内不可用的pod(pod需要拉取镜像还行创建和执行探针探测期间是不可用的),不能超出一定范围,因为需要至少保留一定数量的pod以保证服务可以被正常访问。可以通过指定参数实现:kubectl explain deployment.spec.strategy
- deployment.spec.strategy.rollingUpdate.maxSurge: 指定在升级期间pod总数可以超出定义好的期望pod数的个数或者百分比,默认为25%,如果设置为10%,假设当前是100个pod,那么升级时最多将创建110pod,即额外有10%的pod临时会超出当前的副本数限制。
- deployment.spec.strategy.rollingUpdate.maxUnavailable:指定在升级期间最大的不可用pod数,可以是整数或者当前pod的百分比,默认是25%,假设当前100个pod,那么在升级时最多可以有25个pod不可用,即还有75个pod是可用的。
- 注意:以上两个值不能同时为0,如果maxUnavailable最大不可用pod为0,maxSurge超出数也为0,那么将会导致pod无法进行滚动更新。
- 重建更新(recreate)
- 先删除现有pod,然后基于新版本的镜像重建,优势是同时只有一个版本在线,不会产生多版本在线的问题,缺点是pod删除后到pod重建中间会导致服务无法访问。
1.3.1.1 升级镜像到指定版本
镜像更新格式:
kubectl set image deployment/deployment-name container-name=image -n namespace
# kubectl set image deployment test-tomcat-app1-deployment test-tomcat-app1-container=tomcat:8.5.56 -n test --record #--record 记录更新信息
1.3.1.2 查看历史版本-可以用于后期的回滚
# kubectl rollout history deployment/test-tomcat-app1-deployment -n test
1.3.1.3 撤销-在最近的两个版本之间切换
# kubectl rollout undo deployment test-tomcat-app1-deployment -n test
1.3.1.4 回滚到指定版本
# kubectl rollout undo deployment test-tomcat-app1-deployment -n test --to-revision=3
1.3.2 金丝雀/灰度发布
https://kubernetes.io/zh/docs/concepts/workloads/controllers/deployment/#canary-deployment
K8S上两种实现方式
1.3.2.1 在升级的过程中暂停一下
# 连续快速执行
~# kubectl set image deployment test-tomcat-app1-deployment test-tomcat-app1-container=harbor.k8s.local/k8s/tomcat-app1:v2 -n test --record
~# kubectl rollout pause deployment test-tomcat-app1-deployment -n test #暂停升级
~# kubectl rollout resume deployment test-tomcat-app1-deployment -n test #新版本测试后,继续升级其它pod
~# kubectl rollout undo deployment test-tomcat-app1-deployment -n test #新版本有问题,回滚
1.3.2.2 基于yaml文件进行
- 服务的原始版本deployment
# cat tomcat-app1.yaml
kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
labels:
app: test-tomcat-app1-deployment-label
name: test-tomcat-app1-deployment
namespace: test
spec:
replicas: 6
selector:
matchLabels:
app: test-tomcat-app1-selector
template:
metadata:
labels:
app: test-tomcat-app1-selector
spec:
containers:
- name: test-tomcat-app1-container
image: harbor.k8s.local/k8s/tomcat:8.5.59
#command: ["/apps/tomcat/bin/run_tomcat.sh"]
#imagePullPolicy: IfNotPresent
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: TCP
name: http
env:
- name: "password"
value: "123456"
- name: "age"
value: "18"
resources:
limits:
cpu: 1
memory: "512Mi"
requests:
cpu: 500m
memory: "512Mi"
volumeMounts:
- name: test-images
mountPath: /usr/local/nginx/html/webapp/images
readOnly: false
- name: test-static
mountPath: /usr/local/nginx/html/webapp/static
readOnly: false
volumes:
- name: test-images
nfs:
server: 172.16.244.141
path: /data/k8sdata/images
- name: test-static
nfs:
server: 172.16.244.141
path: /data/k8sdata/static
# nodeSelector:
# project: test
# app: tomcat
- 服务原始版本的service
# cat tomcat-service.yaml
---
kind: Service
apiVersion: v1
metadata:
labels:
app: test-tomcat-app1-service-label
name: test-tomcat-app1-service
namespace: test
spec:
type: NodePort
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
#nodePort: 40003
selector:
app: test-tomcat-app1-selector
#version: v2
- 服务的新版本deployment
kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
labels:
app: test-tomcat-app1-deployment-label-v2
version: v2
name: test-tomcat-app1-deployment-v2
namespace: test
spec:
replicas: 1 #调节副本数,确定新旧版本副本比例
selector:
matchLabels:
app: test-tomcat-app1-selector
version: v2
template:
metadata:
labels:
app: test-tomcat-app1-selector
version: v2 ##添加v2的标志字段
spec:
containers:
- name: test-tomcat-app1-container
image: harbor.k8s.local/k8s/tomcat-app1:v2 #使用新的镜像
#command: ["/apps/tomcat/bin/run_tomcat.sh"]
#imagePullPolicy: IfNotPresent
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: TCP
name: http
env:
- name: "password"
value: "123456"
- name: "age"
value: "18"
resources:
limits:
cpu: 1
memory: "512Mi"
requests:
cpu: 500m
memory: "512Mi"
volumeMounts:
- name: test-images
mountPath: /usr/local/nginx/html/webapp/images
readOnly: false
- name: test-static
mountPath: /usr/local/nginx/html/webapp/static
readOnly: false
volumes:
- name: test-images
nfs:
server: 172.16.244.141
path: /data/k8sdata/images
- name: magedu-static
nfs:
server: 172.16.244.141
path: /data/k8sdata/static
# nodeSelector:
# project: test
# app: tomcat
-
在原始的service条件下,selector只匹配app=test-tomcat-app1-selector,所以service的endpoint是包含了两个版本的pod。
-
测试新版没有问题后,则可更新service,在selector中添加version字段,匹配sersion=v2,则service的endpoint就只包含新版本,实现全部升级。
# cat tomcat-service.yaml --- kind: Service apiVersion: v1 metadata: labels: app: test-tomcat-app1-service-label name: test-tomcat-app1-service namespace: test spec: type: NodePort ports: - name: http port: 80 protocol: TCP targetPort: 8080 #nodePort: 40003 selector: app: test-tomcat-app1-selector version: v2 #添加version字段进行匹配
-
如果新版本测试有问题,则可直接删除新版本的deployment。
2. k8s结合Jenkins与gitlab实现代码升级与回滚
2.1 部署Jenkins
https://www.jenkins.io/
2.1.1 部署java环境
~# apt install openjdk-11-jdk
2.1.2 部署Jenkins
~# apt install -y daemon
~# dpkg -i jenkins_2.303.2_all.deb
2.1.3 修改配置文件
~# systemctl stop jenins
~# dpkg -c jenkins_2.303.2_all.deb
~# vim /etc/default/jenkins
NAME=jenkins
JAVA_ARGS="-Djava.awt.headless=true -Dhudson.security.csrf.GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION=true"
#新版本下开起钩子JAVA_ARGS="-Dhudson.security.csrf.GlobalCrumbIssuerConfiguration.DISABLE_CSRF_PROTECTION=true"
PIDFILE=/var/run/$NAME/$NAME.pid
JENKINS_USER=root #启动使用的用户,测试就用root了
JENKINS_GROUP=root #启动使用的用户,测试就用root了
JENKINS_WAR=/usr/share/$NAME/$NAME.war
JENKINS_HOME=/var/lib/$NAME
RUN_STANDALONE=true
JENKINS_LOG=/var/log/$NAME/$NAME.log
JENKINS_ENABLE_ACCESS_LOG="no"
MAXOPENFILES=8192
HTTP_PORT=8080
PREFIX=/$NAME
JENKINS_ARGS="--webroot=/var/cache/$NAME/war --httpPort=$HTTP_PORT"
2.1.4 启动jenkins,安装插件
~# systemctl restart jenkins.service
# 默认推荐插件安装后,在安装gitlab相关插件,如果插件安装完后,页面显示仍有英文,可以在重启一次服务
2.2 部署gitlab
2.2.1 安装包
~# dpkg -i gitlab-ce_14.2.5-ce.0_amd64.deb
2.2.2 修改配置
~# vim /etc/gitlab/gitlab.rb
...
external_url 'http://172.16.244.152'
...
2.2.3 重新加载配置
sudo gitlab-ctl reconfigure
2.2.4 登陆测试
#初始密码:cat /etc/gitlab/initial_root_password
2.3 实现代码部署与回滚
2.3.1 配置测试项目
2.3.2 jenkins与gitlab的非交互clone配置
-
jenkins 服务器上生成密钥对
root@jenkins:~# ssh-keygen root@jenkins:~# cat /root/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCiVIWzsDpSOYidr0O1lRZkK1qKaqrukazZIXqKI5UT2qgP1O3SfJAl6unPMi1AKK3y1T9Dd7tPG3i49/kCoGC0rdG0uXV+CkPR1iu4tglv5cbLfHuDCR+KRZtBG5+6h7RS8PnjHXSA5hqDSvVaHEqBySjb8Wlhggb3pMb0gMZ7QznapK7BVR42vjqDTekm6XjFUJ8w6VnSe6I5Jdu3UKZ25H17D6tgC4Mn1XSWLODr61evdl7JNeUBudEKCvDMeChKKOVJ6XOS/2E6x01PUprU0LYIzwzV98+iHcssYuQwk+zlvcFy1ed6JKjkQRg+zijjRvhvGpAe7ScNvI7I2VD7MjST8l0U4GltoEH4F3ZV8hZRH03gXT8wsRAoaAgrTAYnAHNmzKCZ2LjiyDYW8K1GVAeuKRulBdN8FXwVq+YXpgSyAznMIl4JBMqRhJylNKgxIhhsyALj4wohpYV7t8cz7HtDs94lFuBZw80r/jKpvW/xO+q6XakXi+XCsmXRchk= root@jenkins.k8s.local
-
将jenkin服务器的公钥放在gitlab
root@jenkins:~# git clone git@172.16.244.152:test/app1.git
2.3.3 Jenkins 创建任务
2.3.3.1 创建Jenkins任务,以脚本的形式进行构建
root@jenkins:/data/scripts# cat test-job1.sh
#!/bin/bash
#记录脚本开始执行时间
starttime=`date +'%Y-%m-%d %H:%M:%S'`
#变量
SHELL_DIR="/data/scripts"
SHELL_NAME="$0"
K8S_CONTROLLER1="172.16.244.100"
K8S_CONTROLLER2="172.16.244.102"
K8S_CONTROLLER3="172.16.244.103"
DATE=`date +%Y-%m-%d_%H_%M_%S`
METHOD=$1
Branch=$2
if test -z $Branch;then
Branch=develop
fi
function Code_Clone(){
Git_URL="git@172.16.244.152:test/app1.git"
DIR_NAME=`echo ${Git_URL} |awk -F "/" '{print $2}' | awk -F "." '{print $1}'`
DATA_DIR="/data/gitdata/test"
Git_Dir="${DATA_DIR}/${DIR_NAME}"
cd ${DATA_DIR} && echo "即将清空上一版本代码并获取当前分支最新代码" && sleep 1 && rm -rf ${DIR_NAME}
echo "即将开始从分支${Branch} 获取代码" && sleep 1
git clone -b ${Branch} ${Git_URL}
echo "分支${Branch} 克隆完成,即将进行代码编译!" && sleep 1
#cd ${Git_Dir} && mvn clean package
#echo "代码编译完成,即将开始将IP地址等信息替换为测试环境"
#####################################################
sleep 1
cd ${Git_Dir}
tar czf ${DIR_NAME}.tar.gz ./*
}
#将打包好的压缩文件拷贝到k8s 控制端服务器
function Copy_File(){
echo "压缩文件打包完成,即将拷贝到k8s 控制端服务器${K8S_CONTROLLER1}" && sleep 1
scp ${Git_Dir}/${DIR_NAME}.tar.gz root@${K8S_CONTROLLER1}:/root/k8s-data/dockerfile/web/test/tomcat-app1/
echo "压缩文件拷贝完成,服务器${K8S_CONTROLLER1}即将开始制作Docker 镜像!" && sleep 1
}
#到控制端执行脚本制作并上传镜像
function Make_Image(){
echo "开始制作Docker镜像并上传到Harbor服务器" && sleep 1
ssh root@${K8S_CONTROLLER1} "cd /root/k8s-data/dockerfile/web/test/tomcat-app1/ && bash build-command.sh ${DATE}"
echo "Docker镜像制作完成并已经上传到harbor服务器" && sleep 1
}
#到控制端更新k8s yaml文件中的镜像版本号,从而保持yaml文件中的镜像版本号和k8s中版本号一致
function Update_k8s_yaml(){
echo "即将更新k8s yaml文件中镜像版本" && sleep 1
ssh root@${K8S_CONTROLLER1} "cd /root/k8s-data/yaml/test/tomcat-app1 && sed -i 's/image: harbor.test.*/image: harbor.k8s.local\/k8s\/tomcat-app1:${DATE}/g' tomcat-app1.yaml"
echo "k8s yaml文件镜像版本更新完成,即将开始更新容器中镜像版本" && sleep 1
}
#到控制端更新k8s中容器的版本号,有两种更新办法,一是指定镜像版本更新,二是apply执行修改过的yaml文件
function Update_k8s_container(){
#第一种方法
ssh root@${K8S_CONTROLLER1} "kubectl set image deployment/test-tomcat-app1-deployment test-tomcat-app1-container=harbor.k8s.local/k8s/tomcat-app1:${DATE} -n test"
#第二种方法,推荐使用第一种
#ssh root@${K8S_CONTROLLER1} "cd /root/k8s-data/yaml/test/tomcat-app1 && kubectl apply -f tomcat-app1.yaml --record"
echo "k8s 镜像更新完成" && sleep 1
echo "当前业务镜像版本: harbor.k8s/local/tomcat-app1:${DATE}"
#计算脚本累计执行时间,如果不需要的话可以去掉下面四行
endtime=`date +'%Y-%m-%d %H:%M:%S'`
start_seconds=$(date --date="$starttime" +%s);
end_seconds=$(date --date="$endtime" +%s);
echo "本次业务镜像更新总计耗时:"$((end_seconds-start_seconds))"s"
}
#基于k8s 内置版本管理回滚到上一个版本
function rollback_last_version(){
echo "即将回滚之上一个版本"
ssh root@${K8S_CONTROLLER1} "kubectl rollout undo deployment/test-tomcat-app1-deployment -n test"
sleep 1
echo "已执行回滚至上一个版本"
}
#使用帮助
usage(){
echo "部署使用方法为 ${SHELL_DIR}/${SHELL_NAME} deploy "
echo "回滚到上一版本使用方法为 ${SHELL_DIR}/${SHELL_NAME} rollback_last_version"
}
#主函数
main(){
case ${METHOD} in
deploy)
Code_Clone;
Copy_File;
Make_Image;
Update_k8s_yaml;
Update_k8s_container;
;;
rollback_last_version)
rollback_last_version;
;;
*)
usage;
esac;
}
main $1 $2
2.3.3.2 jenkins 的变量传递
3. k8s结合ELK实现日志收集
https://kubernetes.io/zh/docs/concepts/cluster-administration/logging/
日志:
- 系统日志 /var/log/syslog
- 应用日志
- 系统错误日志 error.log
- 访问日志 access.log
实现方式:
- 在k8s运行daemonSet,收集每个node节点日志,缺点是很难分类;优点是配置简单,后期维护简单。
- 在每个pod中启动日志收集客户端(filebeat),收集当前pod中的日志。两个实现方式:1.在pod的同一个容器中,先启动filebeat进程,然后在启动服务(实现相对简单)。2.在一个pod中启动两个容器,一个是容器是服务,另一个是filebeat。采用hostpath或emptydir做数据共享缓存,实现filebeat容器能够读取服务容器的日志。
3.1 部署zookeeper集群
http://zookeeper.apache.org/doc/r3.5.9/zookeeperStarted.html#sc_Prerequisites
~# apt install -y openjdk-8-jdk
~# mkdir /apps
~# mkdir -p /data/zookeeper
~# mv apache-zookeeper-3.5.9-bin.tar.gz /apps/
~# cd /apps/
:/apps# tar xvf apache-zookeeper-3.5.9-bin.tar.gz
:/apps# cd apache-zookeeper-3.5.9-bin/conf
:/apps/apache-zookeeper-3.5.9-bin/conf#cp zoo_sample.cfg zoo.cfg
:/apps/apache-zookeeper-3.5.9-bin/conf# vim zoo.cfg
tickTime=2000
initLimit=10
syncLimit=5
dataDir=/data/zookeeper
clientPort=2181
maxClientCnxns=60 #单个客户端的最大连接数(该注释不能在配置文件中体现,要不启不来)
autopurge.snapRetainCount=3
autopurge.purgeInterval=1
server.1=172.16.244.165:2888:3888
server.2=172.16.244.166:2888:3888
server.3=172.16.244.167:2888:3888
# scp zoo.cfg root@172.16.244.166:/apps/apache-zookeeper-3.5.9-bin/conf
# scp zoo.cfg root@172.16.244.167:/apps/apache-zookeeper-3.5.9-bin/conf
root@mq1:~# echo 1 > /data/zookeeper/myid
root@mq2:~# echo 2 > /data/zookeeper/myid
root@mq3:~# echo 3 > /data/zookeeper/myid
root@mq1:/apps/apache-zookeeper-3.5.9-bin# ./bin/zkServer.sh start
root@mq2:/apps/apache-zookeeper-3.5.9-bin# ./bin/zkServer.sh start
root@mq3:/apps/apache-zookeeper-3.5.9-bin# ./bin/zkServer.sh start
root@mq1:/apps/apache-zookeeper-3.5.9-bin# ./bin/zkServer.sh status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /apps/apache-zookeeper-3.5.9-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower
root@mq2:/apps/apache-zookeeper-3.5.9-bin# ./bin/zkServer.sh status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /apps/apache-zookeeper-3.5.9-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: leader
root@mq3:/apps/apache-zookeeper-3.5.9-bin# ./bin/zkServer.sh status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /apps/apache-zookeeper-3.5.9-bin/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost. Client SSL: false.
Mode: follower
3.2 部署kafka
https://kafka.apache.org/downloads
~# mv kafka_2.13-2.4.1.tgz /apps
~# mkdir -p /data/kafka-logs
:/apps# tar xvf kafka_2.13-2.4.1.tgz
:/apps# cd kafka_2.13-2.4.1/
:/apps/kafka_2.13-2.4.1# vim config/server.properties
broker.id=1 #各节点不同的整数,可以是节点IP的最后一位
listeners=PLAINTEXT://172.16.244.165:9092 #节点本身的IP
num.network.threads=3 #数据大的时候可以多开点
num.io.threads=8 #数据大的时候可以多开点
socket.send.buffer.bytes=102400
socket.receive.buffer.bytes=102400
socket.request.max.bytes=104857600
log.dirs=/data/kafka-logs #自定义数据存储目录,写的是logs,但并不全是log
num.partitions=1
num.recovery.threads.per.data.dir=1
offsets.topic.replication.factor=1
transaction.state.log.replication.factor=1
transaction.state.log.min.isr=1
log.retention.hours=168
log.segment.bytes=1073741824
log.retention.check.interval.ms=300000
zookeeper.connect=172.16.244.165:2181,172.16.244.166:2181,172.16.244.167:2181 #写上所有zookeeper节点的IP和端口
zookeeper.connection.timeout.ms=6000
group.initial.rebalance.delay.ms=0
~# /apps/kafka_2.13-2.4.1/bin/kafka-server-start.sh -daemon /apps/kafka_2.13-2.4.1/config/server.properties # 启动kafka
root@mq1:~# ss -anlptu | grep 9092
tcp LISTEN 0 50 [::ffff:172.16.244.165]:9092 *:* users:(("java",pid=570417,fd=133))
3.3 部署ES集群及浏览器安装elasticsearch head插件
https://www.elastic.co/cn/downloads/
~# dpkg -i elasticsearch-7.6.2-amd64.deb
~ # grep -Ev "^#|^$" /etc/elasticsearch/elasticsearch.yml
cluster.name: my-es-1 #设置集群名称
node.name: node1 #节点名称
path.data: /var/lib/elasticsearch #数据路径,生产环境单独放在一个盘上
path.logs: /var/log/elasticsearch #日志路径
network.host: 172.16.244.161 #监听地址
http.port: 9200 #监听端口
discovery.seed_hosts: ["172.16.244.161", "172.16.244.162","172.16.244.163"] #节点地址
cluster.initial_master_nodes: ["172.16.244.161", "172.16.244.162","172.16.244.163"] #master节点地址,如果不想被选举为master的节点,可以不写
gateway.recover_after_nodes: 2 #集群数据同步启动的在线节点数,小于这个值,集群不进行数据同步,一般是总节点数的一半
action.destructive_requires_name: true #在删除索引的时候是否允许进行模糊匹配,一般生产上是不允许,该选项要打开
~# systemctl restart elasticsearch.service
3.4 pod中启动filebeat实现日志收集并输出到kafka
一般是将filebeat安装到系统的基础镜像中,在最后的app镜像中,将其加载配置后启动即可。
3.4.1 filebeat 配置文件
https://www.elastic.co/guide/en/beats/filebeat/7.6/kafka-output.html
filebeat.inputs:
- type: log
enabled: true
paths:
- /apps/tomcat/logs/catalina.out
fields:
type: tomcat-catalina #收集tomcat的系统日志
- type: log
enabled: true
paths:
- /apps/tomcat/logs/localhost_access_log.*.txt
fields:
type: tomcat-accesslog #收集tomcat的访问日志
filebeat.config.modules:
path: ${path.config}/modules.d/*.yml
reload.enabled: false
setup.template.settings:
index.number_of_shards: 1
setup.kibana:
output.kafka:
hosts: ["172.16.244.165:9092","172.16.244.166:9092","172.16.244.167:9092"]
required_acks: 1
topic: "test-tomcat-app1" #topic 要写,否则容器中的filebeat进程可能启不来
compression: gzip
max_message_bytes: 1000000
#output.redis:
# hosts: ["172.31.2.105:6379"]
# key: "k8s-test-app1"
# db: 1
# timeout: 5
# password: "123456"
3.4.2 制作镜像
# cat Dockerfile
#tomcat web1
FROM harbor.k8s.local/k8s/tomcat-base:v8.5.43
ADD catalina.sh /apps/tomcat/bin/catalina.sh
ADD server.xml /apps/tomcat/conf/server.xml
#ADD myapp/* /data/tomcat/webapps/myapp/
ADD app1.tar.gz /data/tomcat/webapps/myapp/
ADD run_tomcat.sh /apps/tomcat/bin/run_tomcat.sh
ADD filebeat.yml /etc/filebeat/filebeat.yml
RUN chown -R nginx.nginx /data/ /apps/
#ADD filebeat-7.5.1-x86_64.rpm /tmp/
#RUN cd /tmp && yum localinstall -y filebeat-7.5.1-amd64.deb
EXPOSE 8080 8443
CMD ["/apps/tomcat/bin/run_tomcat.sh"]
# cat run_tomcat.sh
#!/bin/bash
#echo "nameserver 223.6.6.6" > /etc/resolv.conf
#echo "192.168.7.248 k8s-vip.example.com" >> /etc/hosts
/usr/share/filebeat/bin/filebeat -e -c /etc/filebeat/filebeat.yml -path.home /usr/share/filebeat -path.config /etc/filebeat -path.data /var/lib/filebeat -path.logs /var/log/filebeat &
su - nginx -c "/apps/tomcat/bin/catalina.sh start"
tail -f /etc/hosts
3.4.3 部署新镜像的应用
kind: Deployment
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
metadata:
labels:
app: test-tomcat-app1-deployment-label
name: test-tomcat-app1-deployment
namespace: test
spec:
replicas: 3
selector:
matchLabels:
app: test-tomcat-app1-selector
template:
metadata:
labels:
app: test-tomcat-app1-selector
spec:
containers:
- name: test-tomcat-app1-container
image: harbor.k8s.local/k8s/tomcat-app1:20211030 #更新为新的镜像
#command: ["/apps/tomcat/bin/run_tomcat.sh"]
#imagePullPolicy: IfNotPresent
imagePullPolicy: Always
ports:
- containerPort: 8080
protocol: TCP
name: http
env:
- name: "password"
value: "123456"
- name: "age"
value: "18"
resources:
limits:
cpu: 1
memory: "512Mi"
requests:
cpu: 500m
memory: "512Mi"
# volumeMounts:
# - name: test-images
# mountPath: /usr/local/nginx/html/webapp/images
# readOnly: false
# - name: test-static
# mountPath: /usr/local/nginx/html/webapp/static
# readOnly: false
# volumes:
# - name: test-images
# nfs:
# server: 172.16.244.141
# path: /data/k8sdata/images
# - name: test-static
# nfs:
# server: 172.16.244.141
# path: /data/k8sdata/static
# nodeSelector:
# project: test
# app: tomcat
---
kind: Service
apiVersion: v1
metadata:
labels:
app: test-tomcat-app1-service-label
name: test-tomcat-app1-service
namespace: test
spec:
type: NodePort
ports:
- name: http
port: 80
protocol: TCP
targetPort: 8080
nodePort: 40003
selector:
app: test-tomcat-app1-selector
# 进入容器,查看filebeat进程是否起来了
[root@test-tomcat-app1-deployment-8f9fb7686-qdzfb /]# ps -ef | grep filebeat
root 6 1 0 00:24 ? 00:00:00 /usr/share/filebeat/bin/filebeat -e -c /etc/filebeat/filebeat.yml -path.home /usr/share/filebeat -path.config /etc/filebeat -path.data /var/lib/filebeat -path.logs /var/log/filebeat
3.4.4 登陆kafka客户端查看是否有数据上报
https://kafkatool.com/
3.5 部署logstash将kafka数据输出至elasticsearch集群
~# apt install -y openjdk-11-jdk
~# dpkg -i logstash-7.6.2.deb
- 配置官网文档:
https://www.elastic.co/guide/en/logstash/7.6/index.html
- 从kafka取数据:
https://www.elastic.co/guide/en/logstash/7.6/plugins-inputs-kafka.html
- 输出到es:
https://www.elastic.co/guide/en/logstash/7.6/plugins-outputs-elasticsearch.html
~# cd /etc/logstash/conf.d
~# vim kafka-to-es.conf
input {
kafka {
bootstrap_servers => "172.16.244.165:9092,172.16.244.166:9092,172.16.244.167:9092"
topics => ["test-tomcat-app1","test-tomcat-app2"]
codec => "json"
}
}
output {
if [fields][type] == "tomcat-accesslog" {
elasticsearch {
hosts => ["172.16.244.161:9200","172.16.244.162:9200","172.16.244.163:9200"]
index => "test-tomcat-app1-accesslog-%{+YYYY.MM.dd}" #让es按天生成索引,后期按天删除清日志,实际配置文件改注释要删除。
}
}
if [fields][type] == "tomcat-catalina" {
elasticsearch {
hosts => ["172.16.244.161:9200","172.16.244.162:9200","172.16.244.163:9200"]
index => "test-tomcat-app1-catalinalog-%{+YYYY.MM.dd}"
}
}
#测试时用的,直接输出到标准输出
# stdout {
# codec => rubydebug
# }
}
# 先测试配置文件语法
~# /usr/share/logstash/bin/logstash -f kafka-to-es.conf -t
# 直接启动
~# /usr/share/logstash/bin/logstash -f kafka-to-es.conf #二进制启动,占用终端
#systemd启动方式
~# systemctl start logstash.service
- elasticsearch head 确认是否有输入
3.6 部署kibana并验证数据
要求kibana与es的版本要完全一致
root@logstash1:~# dpkg -i kibana-7.6.2-amd64.deb
root@logstash1:~# vim /etc/kibana/kibana.yml
server.port: 5601
server.host: "172.16.244.164"
elasticsearch.hosts: ["http://172.16.244.161:9200","http://172.16.244.162:9200","http://172.16.244.163:9200"]
i18n.locale: "zh-CN"
root@logstash1:~# systemctl restart kibana.service