构建jre1.8 tomcat8 基础镜像:
- 登陆harbor主机,构建tomcat基础镜像,并上传到harbor
cd /tmp
wget --no-check-certificate https://wget.XXXXXXX.com:10194/jre/server-jre-8u241-linux-x64.tar.gz
wget --no-check-certificate https://wget.XXXXXXX.com:10194/tomcat/apache-tomcat-8.5.51.tar.gz
vi Dockerfile
FROM centos:6
LABEL description="jdk1.8 and tomcat8"
# 时区调整
RUN /bin/cp -av /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
# 适应中文
ENV LC_ALL en_US.UTF-8
# 安装unzip包和git包
RUN sed -i 's/gpgcheck=1/gpgcheck=0/g' /etc/yum.repos.d/*.repo && \
yum -y install unzip git
# 部署jdk1.8
ENV JDK_TAR server-jre-8u241-linux-x64.tar.gz
ENV JDK_FILE jdk1.8.0_241
ENV JAVA_HOME /usr/local/java/java_1.8
ENV CLASSPATH ${JAVA_HOME}/lib:${JAVA_HOME}/jre/lib
ENV PATH ${JAVA_HOME}/bin:${JAVA_HOME}/jre/bin:${PATH}
WORKDIR /usr/local/java
ADD ${JDK_TAR} .
RUN ln -s ${JDK_FILE} java_1.8 && \
chown root: -R .
# 部署tomcat8
ENV TOM_TAR apache-tomcat-8.5.51.tar.gz
ENV TOM_FILE apache-tomcat-8.5.51
WORKDIR /usr/local/tomcat
ADD ${TOM_TAR} .
RUN ln -s ${TOM_FILE} tomcat8 && \
chown root: -R .
# tomcat8 简单优化调整
ENV OPTS "-Djava.security.egd=file:/dev/./urandom -Xms1256m -Xmx1512m"
ENV OPTS=${OPTS}" -XX:PermSize=50m -XX:MaxPermSize=70m"
ENV OPTS=${OPTS}" -XX:-UseGCOverheadLimit"
ENV OPTS2=${OPTS}" -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp"
RUN sed -i "s|cygwin=false|JAVA_OPTS=\"${OPTS}\"\n&|g" tomcat8/bin/catalina.sh && \
sed -i "s|cygwin=false|# JAVA_OPTS=\"${OPTS2}\"\n&|g" tomcat8/bin/catalina.sh && \
sed -i 's/Connector port="8080" protocol="HTTP\/1.1"/&\n \
maxThreads="800" acceptCount="1000"\n \
compression="on"\n \
compressionMinSize="2048"\n \
noCompressionUserAgents="gozilla,traviata"\n \
compressableMimeType="text\/html,text\/xml,text\/javascript,text\/css,text\/plain"/g' tomcat8/conf/server.xml && \
sed -i 's|${catalina.base}/logs|/web/logs|g' tomcat8/conf/logging.properties && \
sed -i 's|appBase="webapps"|appBase="/web/project"|g' tomcat8/conf/server.xml && \
sed -i 's|directory="logs"|directory="/web/logs/access"|g' tomcat8/conf/server.xml && \
mkdir -pv /web/{profile,project/ROOT} && \
echo "$(hostname) $(date +%F_%T)">/web/project/ROOT/index.html
EXPOSE 8080
CMD tomcat8/bin/catalina.sh run &> /web/logs/catalina.out
docker build -t harbor.vincent.com/library/tomcat8-j8 .
docker push harbor.vincent.com/library/tomcat8-j8
部署Jenkins模版任务,复制模版部署测试:
- 网页登陆jenkins,创建一个模版任务deploy-templete,类型为pipeline, 丢弃旧构建选择保留2个,添加groovy脚本如下:
#!groovy
pipeline{
agent any
environment{
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin"
GitLab="gitlab"
GitLabCredentialsId="gitlabcredentialsid"
GitNS="gitns"
GitName="gitname"
GitSubPro="gitsubpro"
GitSubProPort="gitsubproport"
IPs="ips"
Nginx="nginx"
Harbor="harbor"
LbNginx="lbnginx"
LbNginxConf='lbnginxconf'
}
parameters{
choice(
name: 'Option',
choices: 'Update\nRepublish\nRollback',
description: '功能选择,升级发布、项目再发布,回滚发布'
)
choice(
name: 'Range',
choices: 'Canary\nExcept\nALL\nips',
description: '发布范围:单机发布,剩余主机发布,全量发布,单机发布'
)
choice(
name: 'Interval',
choices: '15\n30\n60\n120',
description: '多机发布时的发布间隔,单位秒'
)
/* 参数使用情形举例:
// ------- 金丝雀升级发布,测试后,再发布到剩余主机
// Option=Update,Range=Canary,金丝雀升级发布,默认发布到第一个IP
// Option=Republish,Range=Except,项目再发布到第一个IP之外的其他主机
// ------- 全量升级发布,不进行测试,直接发布到全局主机
// Option=Update,Range=ALL
// ------- 指定IP升级发布,测试,再发布到剩余主机
// Option=Update,Range=IP2,当指定的IP是第一个时,既是金丝雀发布
// Option=Republish,Range=IP3/IP4...,指定IP的再发布
// ------- 指定IP再发布,当某个主机的项目包损坏时或者新添加了主机时
// Option=Republish,Range=IP5
// ------- 全量回滚
// Option=Rollback,Range=ALL
// ------- 金丝雀回滚发布,测试后,剩余主机回滚
// Option=Rollback,Range=Canary
// Option=Rollback,Range=Except
// ------- 指定IP回滚
// Option=Rollback,Range=IP2...
// ------- 当Range=Except/ALL时,Interval 参数生效
*/
}
stages{
stage("GitClone"){
when { environment name: "Option", value: "Update"}
steps{
// 代码下载
sh 'git config --global http.sslVerify false'
dir("${env.WORKSPACE}"){
git branch: 'develop',
// git branch: 'master',
credentialsId: "${GitLabCredentialsId}",
url:"${GitLab}/${GitNS}/${GitName}.git"
}
}
}
stage("ProjectBuild"){
when { environment name: "Option", value: "Update"}
steps{
// 项目构建
dir("${env.WORKSPACE}"){
sh "source /etc/profile && mvn -Dmaven.test.skip=true clean package"
}
}
}
stage("ImageBuild"){
when { environment name: "Option", value: "Update"}
steps{
// 项目包上传包共享服务器,构建镜像并存入harbor镜像共享服务器
sh "bash ${JENKINS_HOME}/script/warMoveAndClear.sh ${WORKSPACE} ${GitSubPro} ${Nginx} ${Harbor}"
}
}
stage("PublishProject"){
steps{
sh "bash ${JENKINS_HOME}/script/mutiPublish.sh ${Option} ${env.Range} '${IPs}' ${Interval} ${GitSubPro} ${GitSubProPort} ${Harbor} ${LbNginx} ${LbNginxConf}"
}
}
}
}
- 在jenkins主机上部署任务所调用的脚本
su - deploy
mkdir -pv /var/lib/jenkins/script && cd /var/lib/jenkins/script
vi warMoveAndClear.sh
#!/bin/bash
source ~/.bash_profile
warPath=${1}
warProName=${2}
imageName=$(echo ${warProName}| tr '[A-Z]' '[a-z]')
Release=$(date +%Y%m%d.%H%M%S)
WarFile=${warProName}-${Release}.war
nginxRootPath=/usr/share/nginx/html
Nginx=${3}
Harbor=${4}
# 创建nginx相应目录并存储当前构建的war包
mkdir -p ${nginxRootPath}/${warProName}
cd ${warPath}/${warProName}/target/
find . -name "*.war" -type f -exec cp -a {} ${nginxRootPath}/${warProName}/${WarFile} \;
# 对历史war包进行清理,保留3个war包
WarFileNumber=$(ls ${nginxRootPath}/${warProName}/*.war|wc -l)
if [ "${WarFileNumber}" -ge 3 ]
then
StandFile=$(ls -t ${nginxRootPath}/${warProName}/*.war|head -3|tail -1)
find ${nginxRootPath}/${warProName}/ -type f -name "*.war" -not -newer ${StandFile} -exec rm -f {} \;
fi
echo "[+] ############## $(date +%F_%T) 项目包${WarFile}存储到nginx完成"
# 操纵harbor,构建镜像并上传
ssh root@${Harbor} "rm -rf /opt/${warProName} && mkdir -p /opt/${warProName}"
cat >/tmp/Dockerfile-${warProName}-${Release}<<EOF
FROM harbor.vincent.com/library/tomcat8-j8
WORKDIR /web/profile
ADD ${warProName}.tar.gz .
# git clone ...
WORKDIR /web/project
RUN rm -rf *
COPY ${WarFile} ROOT.war
EXPOSE 8080
CMD /usr/local/tomcat/tomcat8/bin/catalina.sh run &> /web/logs/catalina.out
EOF
echo "[+] ############## $(date +%F_%T) 同步Dockerfile到harbor"
scp /tmp/Dockerfile-${warProName}-${Release} root@${Harbor}:/opt/${warProName}/Dockerfile
rm -rf /tmp/Dockerfile-${warProName}-${Release}
echo "[+] ############## $(date +%F_%T) 下载profile"
ssh root@${Harbor} "cd /opt/${warProName} && wget ${Nginx}/profile/${warProName}.tar.gz -o /dev/null"
echo "[+] ############## $(date +%F_%T) 下载war包"
ssh root@${Harbor} "cd /opt/${warProName} && wget ${Nginx}/${warProName}/${WarFile} -o /dev/null"
echo "[+] ############## $(date +%F_%T) 构建镜像"
ssh root@${Harbor} "cd /opt/${warProName} && docker build -t harbor.vincent.com/library/${imageName}:${Release} ."
echo "[+] ############## $(date +%F_%T) 镜像上传到harbor"
ssh root@${Harbor} "docker push harbor.vincent.com/library/${imageName}:${Release}"
echo "[+] ############## $(date +%F_%T) 本地镜像清除"
ssh root@${Harbor} "docker rmi harbor.vincent.com/library/${imageName}:${Release}"
echo "[+] ############## $(date +%F_%T) 镜像 harbor.vincent.com/library/${imageName}:${Release}存储到harbor完成"
vi mutiPublish.sh
#!/bin/bash
source ~/.bash_profile
Option=${1}
Range=${2}
IPs=${3}
Interval=${4}
GitSubPro=${5}
imageName=$(echo ${GitSubPro}| tr '[A-Z]' '[a-z]')
GitSubProPort=${6}
Harbor=${7}
LbNginx=${8}
LbNginxConf=${9}
ImagePath="harbor.vincent.com/library"
# 获取项目的镜像tag列表
TagInfo=$(curl -s -k -X GET "https://${Harbor}/api/repositories/library%2F${imageName}/tags" \
-H "accept: application/json" -H "X-Xsrftoken: GUXirgNTqmudkEPhJtOtl520yTRJz6RW" \
| jq '.[].name'|sed 's/"//g')
Tag0=$(echo ${TagInfo}|awk '{print $(NF-0)}')
Tag1=$(echo ${TagInfo}|awk '{print $(NF-1)}')
Tag2=$(echo ${TagInfo}|awk '{print $(NF-2)}')
# 发布类型判断
case ${Option} in
"Update")
Image=${imageName}:${Tag0}
;;
"Republish")
Image=${imageName}:${Tag0}
;;
"Rollback")
Image=${imageName}:${Tag1}
;;
esac
# 发布范围判断
case ${Range} in
"Canary")
ip=$(echo ${IPs}|awk '{print $1}')
;;
"Except")
ip=$(echo ${IPs}|awk '{for(i=2;i<=NF;i++) printf("%s ",$i)}')
;;
"ALL")
ip=${IPs}
;;
*)
ip=${Range}
;;
esac
# 最终发布
echo "[+] ############## $(date +%F_%T) ${Option}发布,使用镜像:${ImagePath}/${Image},发布IP为:${ip}"
for i in ${ip}
do
ssh root@${i} "echo -ne \"[+] ############## $(date +%F_%T) ${i} 发布开始 \n\""
# 需要配置deploy到LbNginx的root用户的免密登陆
# ssh root@${LbNginx} "sed -i 's/${i}:${GitSubProPort}/# ${i}:${GitSubProPort}/g' ${LbNginxConf}"
# ssh root@${LbNginx} "systemctl reload nginx"
# echo "[+] ############## $(date +%F_%T) ${GitSubPro}-${i}-${GitSubProPort} 负载下线,${Interval}秒后部署开始"
# curl -s 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxxx' \
# -H "Content-Type: application/json" -d "{\"msgtype\": \"text\", \
# \"text\": {\"content\": \"${GitSubPro}-${i}-${GitSubProPort}:\n$(date +%F_%T) 负载下线,${Interval}秒后部署开始\"}}"
# sleep ${Interval}
ssh root@${i} "echo -ne \"[+] ############## $(date +%F_%T) 容器关闭 \" && docker stop ${imageName}-${i}-${GitSubProPort}"
ssh root@${i} "echo -ne \"[+] ############## $(date +%F_%T) 容器删除 \" && docker rm ${imageName}-${i}-${GitSubProPort}"
ssh root@${i} "echo -ne \"[+] ############## $(date +%F_%T) 镜像删除 \n\" && for i in \$(docker image ls|grep ${imageName}|awk '{print \$3}');do docker rmi \$i &>/dev/null;done"
ssh root@${i} "echo -ne \"[+] ############## $(date +%F_%T) 镜像拉取 \n\" && docker pull ${ImagePath}/${Image} &>/dev/null"
ssh root@${i} "echo -ne \"[+] ############## $(date +%F_%T) 容器启动 \" && docker run -d --name=${imageName}-${i}-${GitSubProPort} -p ${GitSubProPort}:8080 -v /opt/logs/${imageName}-${i}-${GitSubProPort}:/web/logs -m 1512M -it ${ImagePath}/${Image}"
echo "[+] ############## $(date +%F_%T) ${imageName}-${i}-${GitSubProPort} ${Option}发布成功" #,${Interval}秒后负载上线"
curl -s 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxxx' \
-H "Content-Type: application/json" -d "{\"msgtype\": \"text\", \
\"text\": {\"content\": \"${GitSubPro}-${i}-${GitSubProPort}:\n$(date +%F_%T) ${Option}发布成功\"}}" #,${Interval}秒后负载上线\"}}"
sleep ${Interval}
# ssh root@${LbNginx} "sed -i 's/# ${i}:${GitSubProPort}/${i}:${GitSubProPort}/g' ${LbNginxConf}"
# ssh root@${LbNginx} "systemctl reload nginx"
# echo "[+] ############## $(date +%F_%T) ${GitSubPro}-${i} 负载上线"
# curl -s 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxxxxxxxx' \
# -H "Content-Type: application/json" -d "{\"msgtype\": \"text\", \
# \"text\": {\"content\": \"${GitSubPro}-${i}-${GitSubProPort}:\n$(date +%F_%T) 负载上线\"}}"
done
- 模版复制创建任务,构建发布测试
su - deploy
gitns='vincent'
gitname='test'
gitsubpro='test-soaport'
gitsubproport='8081'
ips='192.168.1.171 192.168.1.172 192.168.1.173 192.168.1.174 192.168.1.175'
ipS='192.168.1.171\\n192.168.1.172\\n192.168.1.173\\n192.168.1.174\\n192.168.1.175'
gitlab='https://gitlab.vincent.com'
gitlabcredentialsid='https.gitlab.root.pass'
nginx='http://dockerjenkins:8080'
harbor='harbor.vincent.com'
lbnginx='lbnginx'
lbnginxconf='lbnginxconf'
cd /var/lib/jenkins/jobs/
cp -av deploy-templete ${gitsubpro}
sed -i "s|GitLab=\"gitlab\"|GitLab=\"${gitlab}\"|g" ${gitsubpro}/config.xml
sed -i "s|GitLabCredentialsId=\"gitlabcredentialsid\"|GitLabCredentialsId=\"${gitlabcredentialsid}\"|g" ${gitsubpro}/config.xml
sed -i "s|GitNS=\"gitns\"|GitNS=\"${gitns}\"|g" ${gitsubpro}/config.xml
sed -i "s|GitName=\"gitname\"|GitName=\"${gitname}\"|g" ${gitsubpro}/config.xml
sed -i "s|GitSubPro=\"gitsubpro\"|GitSubPro=\"${gitsubpro}\"|g" ${gitsubpro}/config.xml
sed -i "s|GitSubProPort=\"gitsubproport\"|GitSubProPort=\"${gitsubproport}\"|g" ${gitsubpro}/config.xml
sed -i "s|IPs=\"ips\"|IPs=\"${ips}\"|g" ${gitsubpro}/config.xml
sed -i "s|Nginx=\"nginx\"|Nginx=\"${nginx}\"|g" ${gitsubpro}/config.xml
sed -i "s|Harbor=\"harbor\"|Harbor=\"${harbor}\"|g" ${gitsubpro}/config.xml
sed -i "s|LbNginx=\"lbnginx\"|LbNginx=\"${lbnginx}\"|g" ${gitsubpro}/config.xml
sed -i "s|LbNginxConf=\'lbnginxconf\'|LbNginxConf=\'${lbnginxconf}\'|g" ${gitsubpro}/config.xml
sed -i "s|ips\&apos|${ipS}\&apos|g" ${gitsubpro}/config.xml
su - root /etc/init.d/jenkins restart
- 项目做过配置分离,需要将相应的配置目录profile打包上传到nginx对应目录:
cd /tmp
tar -czf test-soaport.tar.gz profile
mkdir -pv /usr/share/nginx/html/profile
mv -v test-soaport.tar.gz /usr/share/nginx/html/profile
- 网页登陆Jenkins,使用任务进行构建部署测试
[TOC]