nodejs(vue)项目也可以通过打包成docker镜像的方式进行部署,原理是先将项目打包成静态页面,然后再将静态页面直接copy到nginx镜像中运行。
一、服务器环境配置
前面说明了服务器nginx的安装和配置,这里稍微有些不同,但是因为此文是用nginx镜像和前端镜像页面同时部署的方式来打包发布的,所以这里不再需要建立/data/container/nginx/html目录,因为要发布的静态页面已经在nginx镜像中的/nginx/html目录了。这里也减少了手动部署安装nginx的步骤,而是在jenkins任务中调用shell命令自动执行安装。
1. 新建dockerfile文件,用于定义nginx镜像,及将打包成功的静态文件复制到镜像中,此文件放在前端项目的根目录下,jenkins打包时会从此处查找dockerfile文件。
from nginx:latest
# 维护者信息
maintainer gitegg
# 将生成的静态页面文件复制到nginx的/usr/share/nginx/html/目录
copy dist/ /usr/share/nginx/html/
# 容器启动时运行的命令
cmd ["nginx", "-g", "daemon off;"]
2. 部署及备份目录准备
- 新建 /opt/tmp 目录,用于jenkins打包后,通过 publish over ssh插件将包传输到服务器的临时目录(如果前面创建过,这里无需再创建)。
- 新建 /opt/bak 目录,用于存储所有部署过的包备份,方便后续版本回滚。此目录可能会占用很大空间,所以需要选择一个磁盘空间大的挂载目录(如果前面创建过,这里无需再创建)。
- 新建 /opt/script 目录,用于jenkins将包传输完成之后,执行安装、备份操作的相关命令脚本(如果前面创建过,这里无需再创建)。
- 新建 /data/container/nginx/www,映射nginx容器内的/var/www目录。
- 新建 /data/container/nginx/logs,映射nginx容器内的/var/log/nginx目录,存放nginx运行日志。
- 新建 /data/container/nginx/etc,映射nginx容器内的/etc/nginx目录
- 新建 /data/container/nginx/etc/nginx.conf,映射nginx容器内的/etc/nginx/nginx.conf配置文件
mkdir -p /opt/tmp /opt/bak /opt/script /data/container/nginx/www /data/container/nginx/logs /data/container/nginx/etc
chmod -r 777 /opt/tmp /opt/bak /opt/script /data/container/nginx/www /data/container/nginx/logs /data/container/nginx/etc
3.根据系统部署要求编写nginx配置文件nginx.conf,以下是简单的配置方法,正常情况下https请求还需要配置ssl证书,还有ipv6配置等,后面详细讲解nginx配置。一定要将修改后的nginx.conf文件放到/data/container/nginx/etc/目录下,否则nginx启动时会报错找不到配置文件。
server {
listen 80;
server_name 域名;
gzip on;
gzip_buffers 32 4k;
gzip_comp_level 6;
gzip_min_length 100;
gzip_types application/javascript text/css text/xml text/plain application/x-javascript image/jpeg image/gif image/png;
gzip_disable "msie [1-6]\.";
gzip_vary on;
#charset koi8-r;
access_log /var/log/nginx/portal.access.log main;
location / {
root /nginx/html/gitegg_portal;
try_files $uri $uri/ /index.html;
index index.html index.htm;
}
location /gitegg-api/ {
proxy_set_header host $http_host;
proxy_set_header x-real-ip $remote_addr;
proxy_set_header remote-host $remote_addr;
proxy_set_header x-forwarded-for $proxy_add_x_forwarded_for;
proxy_pass http://172.17.0.1:8080/;
}
}
4. 部署脚本编写说明
- 定义入参,可以通过jenkins任务将参数传入脚本中,我们定义了下面7个参数:
container_name=portal-server : 容器名称
image_name=portal-server : 镜像名称
version=latest : 镜像版本
portal_port=80: 宿主主机端口映射
server_port=80: 容器内服务端口
portal_ssl_port=443: 宿主主机端口映射
serve_sslr_port=443: 容器内服务端口 - 对参数进行检查,是否未传入参数,这里根据自己的实际情况判断,比如必须传入哪些参数,就设置参数的个数不能小于几。
echo "param validate"
if [ $# -lt 1 ]; then
echo "you must use like this : ./publish_docker_portal.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"
exit
fi
- 入参赋值,如果有参数传入,则取服务参数,如果没有参数传入则取默认值
if [ "$1" != "" ]; then
container_name="$1"
fi
echo "container_name=" $container_name
if [ "$2" != "" ]; then
image_name="$2"
fi
if [ "$3" != "" ]; then
version="$3"
fi
echo "version=" $version
if [ "$4" != "" ]; then
portal_port="$4"
fi
echo "portal_port=" $portal_port
if [ "$5" != "" ]; then
server_port="$5"
fi
echo "server_port=" $server_port
if [ "$6" != "" ]; then
portal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; then
serve_sslr_port="$7"
fi
echo "serve_sslr_port=" $serve_sslr_port
- 停止并删除容器
echo "执行docker ps"
docker ps
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
echo $container_name "容器存在,停止并删除"
echo "docker stop" $container_name
docker stop $container_name
echo "docker rm" $container_name
docker rm $container_name
else
echo $container_name "容器不存在"
fi
- 停止并删除镜像
# 删除镜像
echo "执行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
then
echo $image_name '镜像存在,删除镜像'
docker rmi $(docker images -q $image_name 2> /dev/null) --force
else
echo $image_name '镜像不存在'
fi
- 备份本次安装镜像包
#bak image
echo "bak image" $image_name
bak_dir=/opt/bak/docker/$image_name/`date +%y%m%d`
mkdir -p "$bak_dir"
cp "/opt/tmp/portal-image.tar" "$bak_dir"/"$image_name"_`date +%h%m%s`.tar
- 执行安装镜像包命令
echo "docker load" $image_name
docker load --input /opt/tmp/portal-image.tar
- 执行运行命令
echo "docker run" $image_name
docker run -d -p $portal_port:$server_port -p $portal_ssl_port:$server_ssl_port --name=$container_name -e tz="asia/shanghai" --restart=always -v /data/container/nginx/www:/var/www -v /data/container/nginx/logs:/var/log/nginx -v /data/container/nginx/etc:/etc/nginx -v /data/container/nginx/etc/nginx.conf:/etc/nginx/nginx.conf -v /etc/localtime:/etc/localtime -v /usr/share/zoneinfo/asia/shanghai:/etc/timezone -v /bxl/container/nginx/ssl:/nginx/ssl $image_name
- 删除安装文件,因为前面已经备份过了,所以这里将临时安装文件删除
echo "remove tmp " $image_name
rm -rf /opt/tmp/portal-image.tar
- 打印执行完成的命令
echo "docker portal is starting,please try to access $container_name conslone url"
- 完整的安装部署脚本
container_name=portal-server
image_name=portal-server
version=latest
portal_port=80
server_port=80
portal_ssl_port=443
serve_sslr_port=443
echo "param validate"
if [ $# -lt 1 ]; then
echo "you must use like this : ./publish_docker_portal.sh <container_name> <image_name> <version> [portal port] [server port] [portal ssl port] [server ssl port]"
exit
fi
if [ "$1" != "" ]; then
container_name="$1"
fi
echo "container_name=" $container_name
if [ "$2" != "" ]; then
image_name="$2"
fi
if [ "$3" != "" ]; then
version="$3"
fi
echo "version=" $version
if [ "$4" != "" ]; then
portal_port="$4"
fi
echo "portal_port=" $portal_port
if [ "$5" != "" ]; then
server_port="$5"
fi
echo "server_port=" $server_port
if [ "$6" != "" ]; then
portal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; then
serve_sslr_port="$7"
fi
echo "serve_sslr_port=" $serve_sslr_port
echo "执行docker ps"
docker ps
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
echo $container_name "容器存在,停止并删除"
echo "docker stop" $container_name
docker stop $container_name
echo "docker rm" $container_name
docker rm $container_name
else
echo $container_name "容器不存在"
fi
# 删除镜像
echo "执行docker images"
docker images
if [[ "$(docker images -q $image_name 2> /dev/null)" != "" ]];
then
echo $image_name '镜像存在,删除镜像'
docker rmi $(docker images -q $image_name 2> /dev/null) --force
else
echo $image_name '镜像不存在'
fi
#bak image
echo "bak image" $image_name
bak_dir=/opt/bak/docker/$image_name/`date +%y%m%d`
mkdir -p "$bak_dir"
cp "/opt/tmp/portal-image.tar" "$bak_dir"/"$image_name"_`date +%h%m%s`.tar
echo "docker load" $image_name
docker load --input /opt/tmp/portal-image.tar
echo "docker run" $image_name
docker run -d -p $portal_port:$server_port -p $portal_ssl_port:$server_ssl_port --name=$container_name -e tz="asia/shanghai" --restart=always -v /data/container/nginx/www:/var/www -v /data/container/nginx/logs:/var/log/nginx -v /data/container/nginx/etc:/etc/nginx -v /data/container/nginx/etc/nginx.conf:/etc/nginx/nginx.conf -v /etc/localtime:/etc/localtime -v /usr/share/zoneinfo/asia/shanghai:/etc/timezone -v /bxl/container/nginx/ssl:/nginx/ssl $image_name
echo "remove tmp " $image_name
rm -rf /opt/tmp/portal-image.tar
echo "docker portal is starting,please try to access $container_name conslone url"
二、新建jenkins配置打包任务,打包部署nodejs(vue)镜像
1. 新建任务前,安装docker pipeline插件,使用pipeline流水线任务构建部署,安装jenkins插件相关内容,请查看前面部署jenkins相关文章。
2. 安装完插件之后,新建一个流水线任务。
3. 和之前的任务一样,选择“丢弃旧的构建”,设置保持构建的最大个数为5。
4. 下拉到“流水线”配置,选择pipeline script
流水线脚本如下:
node {
# 从gitlab下载代码
stage('preparation') { // for display purposes
// get some code from a github repository
echo "checkout from gitlab"
checkout scmgit(branches: [[name: '*/main']], extensions: [], userremoteconfigs: [[credentialsid: 'git_username', url: 'http://127.0.0.1:9091/test/test.git']])
}
# nodejs打包
stage('build nodejs vue') {
echo "build nodejs code"
nodejs('node17') {
sh 'echo $path'
sh 'node -v'
sh 'pnpm -v'
sh 'pnpm install'
sh 'pnpm run build'
}
}
# 此处判断本机打包是否有容器,如果有的话需要删除
stage('delete old docker container') {
echo "delete docker container"
sh '''if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]];
then
echo $container_name "容器存在,停止并删除"
echo "docker stop" $container_name
docker stop $container_name
echo "docker rm" $container_name
docker rm $container_name
else
echo $container_name "容器不存在"
fi'''
}
# 此处判断本机打包是否有镜像,如果有的话需要删除
stage('delete old docker image') {
echo "delete docker image"
sh '''if [[ "$(docker images -q gitegg-portal 2> /dev/null)" != "" ]];
then
echo gitegg-portal \'镜像存在,删除镜像\'
docker rmi $(docker images -q gitegg-portal 2> /dev/null) --force
else
echo gitegg-portal \'镜像不存在,创建镜像\'
fi'''
}
# docker打包镜像,并保存为tar
stage('build docker image') {
echo "start docker build portal code"
// run the docker build
docker.build 'gitegg-portal'
echo "save docker images tar"
sh 'docker save -o portal-image.tar gitegg-portal'
}
# 删除安装在本机的docker镜像,非tar包
stage('delete new docker image') {
echo "delete docker image"
sh '''if [[ "$(docker images -q gitegg-portal 2> /dev/null)" != "" ]];
then
echo gitegg-portal \'镜像存在,删除镜像\'
docker rmi $(docker images -q gitegg-portal 2> /dev/null) --force
else
echo gitegg-portal \'镜像不存在,创建镜像\'
fi'''
}
# 将docker镜像tar包发送到服务器并执行部署命令
stage('send docker image') {
echo "send docker image"
sshpublisher(publishers: [sshpublisherdesc(configname: 'test', transfers: [sshtransfer(cleanremote: false, excludes: '', execcommand: '/opt/script/publish_docker_portal.sh gitegg-portal gitegg-portal latest 8130 8130 4413 4413', exectimeout: 120000, flatten: false, makeemptydirs: false, nodefaultexcludes: false, patternseparator: '[, ]+', remotedirectory: '', remotedirectorysdf: false, removeprefix: '', sourcefiles: 'portal-image.tar')], usepromotiontimestamp: false, useworkspaceinpromotion: false, verbose: false)])
}
stage('publish results') {
echo "end publish portal"
}
}
12. 在任务左侧点击立即构建
- 立即构建
- 流水线任务可以在右侧显示阶段视图
- 查看构建日志:点击立即构建之后,下方会出现进度条,点击进度条就可以进入构建日志界面。
发表评论