前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >写个简单的项目自动部署脚本

写个简单的项目自动部署脚本

作者头像
叔牙
发布2024-05-21 20:24:06
1360
发布2024-05-21 20:24:06
举报

一、概述

在项目数量比较大和构建流程比较复杂的场景,我们一般会使用jenkins以及衍生产品来实现构建打包部署能力,但对于一些简单的项目和小众场景,我们使用简单的脚本构建部署,也未必不是一个简单便捷和节省成本的选择。

我们以jenkins为例,其本质就是从远程仓库拉取代码,然后本地编译打包,然后上传到目标服务器执行启动命令,简化过程如下:

那么在一些简单的项目中,我们可不可以完全自己写一个脚本来做打包部署呢,答案是可以的,我们可以模仿jenkins的工作流程并且做一些简化:

  1. 从git拉取项目代码到服务器
  2. 使用maven命令进行编译打包,打成可执行的jar
  3. 使用命令或者其他工具启动java服务(java -jar,docker等等)

这样原本在jenkins执行的工作,转移到了服务器本机执行了。

二、编写部署脚本

前边有介绍到通过脚本来部署应用程序,那么就需要目标服务器拥有执行相关拉取代码、编译、构建的能力,比如最基本的java运行环境、maven工具、git命令等,如果是借助docker启动服务,那么还需要安装docker相关工具。

1.环境准备

java

代码语言:javascript
复制
yum install -y java-1.8.0-openjdk-devel
#/etc/profile环境变量
# jdk 8
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.382.b05-1.amzn2.0.2.x86_64
export CLASSPATH=.:$JAVA_HOME/jre/lib/rt.jar:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export PATH=$PATH:$JAVA_HOME/bin
# 保存后,source生效
source /etc/profile

maven

代码语言:javascript
复制
mkdir /opt/tools/maven
cd /opt/tools/maven
wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.tar.gz
tar -zxvf apache-maven-3.8.8-bin.tar.gz
#配置环境变量
vim /etc/profile
# maven 3.8.8
export MAVEN_HOME=/opt/tools/maven/apache-maven-3.8.8
export PATH=${PATH}:${MAVEN_HOME}/bin
#保存后source生效
source /etc/profile

git

代码语言:javascript
复制
yum -y install git

配置访问公钥,在服务器上生成公钥:

代码语言:javascript
复制
ssh-keygen

然后把~/.ssh/id_rsa.pub内容添加到远程仓库的ssh秘钥中:

这样服务器就可以通过git命令从远程仓库拉取代码了。

2.基于java命令启动的部署脚本

我们以项目springboot-demo为例,创建项目路径:

代码语言:javascript
复制
mkdir -p /opt/app/server/springboot-demo

编写部署脚本:

代码语言:javascript
复制
cd /opt/app/server/springboot-demo
touch start.sh
chmod +x start.sh

start.sh脚本内容如下:

代码语言:javascript
复制
#!/bin/bash
#项目路径
WORK_DIR=/opt/app/server/springboot-demo
#项目名称
PROJECT_NAME=springboot-demo
#获取代码
cd $WORK_DIR
if [ ! -d $PROJECT_NAME ];then
        #如果项目文件夹内不存在,则从远程仓库拉取指定分支代码
        git clone -b branch_name git@gitlab.com:xxx.git
        #进入应用目录
        cd $PROJECT_NAME
else
        #如果项目文件夹存在,说明之前拉取过,那么进入项目路径拉取最新代码
        cd $PROJECT_NAME
        git pull
fi
#maven构建
mvn -U clean compile package -Dmaven.test.skip=true -P$1
# 如果构建失败,退出脚本
if [ $? -ne 0 ]; then
        echo "maven build failue!"
        exit 1
fi


#部署项目
#找到之前启动的服务进程
SPRINGBOOT_DEMO_PID=$(ps -ef | grep "springboot-demo-$1.jar" | egrep -v "grep|$$" | awk 'NR==1{print $2}')
#如果已经存在进程,则发送kill信号终止
[ -n "$SPRINGBOOT_DEMO_PID" ] && kill $SPRINGBOOT_DEMO_PID
# 休眠10s,等待进程终止
sleep 10
#把maven编译打包的最新jar包拷贝到工作目录
cp  target/springboot-demo-$1.jar $WORK_DIR
#再次检查进程是否终止,如果没有终止则发送kill -9信号强行终止
[ -n "$SPRINGBOOT_DEMO_PID" ] && kill -9 $SPRINGBOOT_DEMO_PID
#使用nohup java -jar命令后台启动服务
nohup java -Djava.security.egd=file:/dev/./urandom -Xms1g -Xmx1g -Xmn512m -XX:SurvivorRatio=8 -XX:+UseConcMarkSweepGC -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+CMSScavengeBeforeRemark -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintGCDetails -Xloggc:/mnt/applogs/$springboot-demo/gc.log  -Dfile.encoding=utf-8  -jar $WORK_DIR/springboot-demo-$1.jar  >/dev/null 2>&1 &
echo "springboot-demo startup success"

该脚本核心做了以下几件事情:

  • 从远程仓库拉取项目代码;如果已经存在项目目录,则进入目录拉取最新代码
  • 使用mvn命令编译打包,并输出可执行jar到target目录,如果编译失败则退出执行
  • 找出服务进程,并发送kill执行进行终止服务进程,并且休眠10s,给服务进程足够的时间处理剩余的事情
  • 从项目目录的target文件夹拷贝可执行jar到项目工作目录
  • 再次检查服务进程是否已经终止,如果没有终止则强行终止(理论上10s可以正常终止,休眠时间可按需调整)
  • 通过nohup java -jar命令后台运行服务,启动成功后打印启动成功日志

执行start.sh脚本打包部署:

代码语言:javascript
复制
sh start.sh dev

通过脚本的输入日志可以看到服务已经打包部署成功了:

使用netstat命令检查端口已经监听成功,并且发送请求也能够正常处理:

这样我们通过脚本来实现java服务的代码拉取、编译打包和服务启动已经成功了。

3.基于docker启动的部署脚本

有些项目团队喜欢使用docker启动java服务,那么我们同样可以将上述脚本稍做改造,来实现基于shell+docker的简单项目部署能力。

安装docker运行环境(服务器是aws ec2):

代码语言:javascript
复制
sudo yum update -y
sudo amazon-linux-extras install docker


sudo service docker start
sudo systemctl enable docker

项目路径不再重复创建,还是基于上一小节的路径,在项目工作路径创建Dockerfile文件,内容如下:

代码语言:javascript
复制
FROM openjdk:8
ARG PROFILES
ARG APP_NAME_ARG
ARG SERVER_PORT_ARG
ENV PROFILES_ACTIVE ${PROFILES}
ENV APP_SERVER_PORT ${SERVER_PORT_ARG}
ENV LANG=zh_CN.UTF-8
RUN apt-get update && apt-get install -y locales && sed -i 's/# zh_CN.UTF-8 UTF-8/zh_CN.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
VOLUME /tmp
ADD ${APP_NAME_ARG}-${PROFILES}.jar /app.jar
RUN bash -c 'touch /app.jar'
EXPOSE ${APP_SERVER_PORT}
ENTRYPOINT ["sh", "-c", " date && java -XX:+UseG1GC -Dfile.encoding=utf-8 -Dserver.port=${APP_SERVER_PORT} \
-jar /app.jar --spring.profiles.active=${PROFILES_ACTIVE}"]

Dockerfile文件接收三个参数:

  • PROFILES:激活的profile
  • APP_NAME_ARG:应用名称
  • SERVER_PORT_ARG:服务端口

同样在项目工作目录创建start.sh脚本:

代码语言:javascript
复制
cd /opt/app/server/springboot-demo
touch start.sh
chmod +x start.sh

脚本内容如下:

代码语言:javascript
复制
#!/bin/bash
WORK_DIR=/opt/app/server/springboot-demo/
PROJECT_NAME=springboot-demo
#获取代码
cd $WORK_DIR
if [ ! -d $PROJECT_NAME ];then
        git clone -b branch_name git@gitlab.com:xxx.git
        cd $PROJECT_NAME
else
        cd $PROJECT_NAME
        git pull
fi
#maven构建
mvn -U clean compile package -Dmaven.test.skip=true -P$1
if [ $? -ne 0 ]; then
        echo "maven build failue!"
        exit 1
fi
#部署项目
cp  target/springboot-demo-$1.jar $WORK_DIR
docker build --build-arg PROFILES=$1 --build-arg APP_NAME_ARG=$2 --build-arg SERVER_PORT_ARG=$3 -t $2:0.0.1 .
docker stop $2
docker rm $2
docker rmi $(docker images | awk '/^<none>/ { print $3 }')
docker run -v /mnt:/mnt -p $3:$3 --restart=always --name $2 -d $2:0.0.1


echo "springboot-demo startup success"

该脚本和前边的类似,做了以下几件事情:

  • 从远程仓库拉取项目代码;如果已经存在项目目录,则进入目录拉取最新代码
  • 使用mvn命令编译打包,并输出可执行jar到target目录,如果编译失败则退出执行
  • 从项目目录的target文件夹拷贝可执行jar到项目工作目录
  • 使用docker命令构建java服务镜像,并定义传入三个入参
  • 停止老的docker中的java服务容器,并移除
  • 找到老的java服务镜像,并移除
  • 启动新的java服务容器,启动成功后打印启动成功日志

执行start.sh脚本打包部署:

代码语言:javascript
复制
sh start.sh dev springboot-demo 8099

从构建日志可以看到脚本已经执行成功:

使用docker images看到镜像已经构建:

使用docker ps可以看到java服务容器已经启动,并且容器内端口已经和宿主机的端口绑定映射成功:

使用netstat命令检查端口已经监听成功,并且发送请求也能够正常处理:

这样我们通过改造部署脚本也实现了java服务的代码拉取、编译打包和docker容器启动。

三、扩展

考虑到jenkins以及衍生产品处理提供完善的流程化的部署能力,也会提供详细的部署记录以及通知能力,我们也可以将脚本进行改造,来提供相应的能力,比如记录什么时间出发了打包部署,部署成功和失败的通知等。

1.部署日志记录

将部署开始和结束的日志追加到部署日志中。

代码语言:javascript
复制
# 日志文件路径
LOG_FILE="/opt/app/server/springboot-demo/deploy.log"


# 记录当前时间和执行的命令到日志文件
echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting start.sh script" >> "$LOG_FILE"


# 部署脚本内容


# 记录脚本执行结束的时间到日志文件
echo "[$(date '+%Y-%m-%d %H:%M:%S')] End of start.sh script" >> "$LOG_FILE"
2.部署结果通知

可以将核心节点的错误或者失败内容通过webhook发送到对应的告警平台,比如钉钉、飞书机器人等。

以maven编译打包失败发送飞书告警为例:

代码语言:javascript
复制
ROBOT_TOKEN="xxxxxx"
REQ_PATH="https://open.feishu.cn/open-apis/bot/v2/hook/$ROBOT_TOKEN"
REQ_TYPE="Content-Type: application/json"


mvn -U clean compile package -Dmaven.test.skip=true -P$1
if [ $? -ne 0 ]; then
        echo "maven build failue!"
        BODY="{\"msg_type\":\"interactive\",\"card\":{\"config\":{\"wide_screen_mode\":false,\"enable_forward\":true},\"elements\":[{\"tag\":\"markdown\",\"content\":\" \n$CURR_DATE $CURR_TIME \n**服务器别名** : $CURR_IP \n\n\n**关注进程名** : $FOCUS_PROCESS \n**警告级别** :  CRITICAL \n**警告内容** : mvn构建失败!\"}],\"header\":{\"title\":{\"tag\":\"plain_text\",\"content\":\"[$CURR_ENV 环境]: server-Alert\"},\"template\":\"red\"}}}"
    curl "$REQ_PATH" \
    -H "$REQ_TYPE" \
    -d "$BODY"
        exit 1
fi

其他节点调整通知文案和内容即可。

四、总结

使用shell脚本来实现项目的打包部署比较轻量级,必要适合小团队和小众化项目的部署,相比于jenkins以及类似衍生产品打包部署有以下一些可能的优缺点:

优点:
  • 定制化程度高: 通过编写自定义的 Shell 脚本,可以更灵活地满足特定项目的需求,定制化程度更高。
  • 减少依赖:使用shell脚本可以减少对Jenkins的依赖,特别是在需要迁移或者更换持续集成工具时,减少了迁移的复杂性。
  • 更轻量级:shell脚本相比jenkins Pipeline脚本或者其他持续集成工具的配置文件可能更加轻量级,易于维护和管理。
  • 节省成本:jenkins部署项目时是比较吃服务器性能的,一般部署jenkins的服务器配置要比业务机器的配置高,使用脚本节省了部署jenkins的机器成本。
缺点:
  • 可维护性较低:相比jenkins提供的可视化界面和各种插件,使用shell脚本可能会降低可维护性,尤其是对于不熟悉shell脚本的团队成员而言。
  • 缺少监控和报告:jenkins 提供了丰富的监控和报告功能,如构建历史、构建日志、构建结果等,而使用 Shell 脚本可能需要自行实现这些功能。
  • 学习成本:对于不熟悉shell脚本的团队成员来说,需要花费额外的时间和精力学习shell脚本语法和编写规范。

综上所述,使用shell脚本来替换jenkins打包部署具有一定的优势,但也需要考虑到一些潜在的缺点,并根据具体情况来权衡选择。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-05-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PersistentCoder 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、编写部署脚本
    • 1.环境准备
      • 2.基于java命令启动的部署脚本
        • 3.基于docker启动的部署脚本
        • 三、扩展
          • 1.部署日志记录
            • 2.部署结果通知
            • 四、总结
              • 优点:
                • 缺点:
                相关产品与服务
                容器服务
                腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档