【微服务部署】五、Jenkins+Docker一键打包部署NodeJS(Vue)项目的Docker镜像步骤详解

  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 ]; thenecho "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" != "" ]; thencontainer_name="$1"
fi
echo "container_name=" $container_name
if [ "$2" != "" ]; thenimage_name="$2"
fi
if [ "$3" != "" ]; thenversion="$3"
fi
echo "version=" $version
if [ "$4" != "" ]; thenportal_port="$4"
fi
echo "portal_port=" $portal_port
if [ "$5" != "" ]; thenserver_port="$5"
fi
echo "server_port=" $server_port
if [ "$6" != "" ]; thenportal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; thenserve_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_namedocker stop $container_nameecho "docker rm" $container_namedocker 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" != "" ]; thencontainer_name="$1"
fi
echo "container_name=" $container_name
if [ "$2" != "" ]; thenimage_name="$2"
fi
if [ "$3" != "" ]; thenversion="$3"
fi
echo "version=" $version
if [ "$4" != "" ]; thenportal_port="$4"
fi
echo "portal_port=" $portal_port
if [ "$5" != "" ]; thenserver_port="$5"
fi
echo "server_port=" $server_port
if [ "$6" != "" ]; thenportal_ssl_port="$6"
fi
echo "portal_ssl_port=" $portal_ssl_port
if [ "$7" != "" ]; thenserve_sslr_port="$7"
fi
echo "serve_sslr_port=" $serve_sslr_portecho "执行docker ps"
docker ps 
if [[ "$(docker inspect $container_name 2> /dev/null | grep $container_name)" != "" ]]; 
then echo $container_name "容器存在,停止并删除"echo "docker stop" $container_namedocker stop $container_nameecho "docker rm" $container_namedocker 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`.tarecho "docker load" $image_name
docker load --input /opt/tmp/portal-image.tarecho "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_nameecho "remove tmp " $image_name
rm -rf /opt/tmp/portal-image.tarecho "Docker Portal is starting,please try to access $container_name conslone url"

二、新建Jenkins配置打包任务,打包部署NodeJS(Vue)镜像

1. 新建任务前,安装Docker Pipeline插件,使用Pipeline流水线任务构建部署,安装Jenkins插件相关内容,请查看前面部署Jenkins相关文章。

Docker Pipeline插件

2. 安装完插件之后,新建一个流水线任务。

流水线任务

3. 和之前的任务一样,选择“丢弃旧的构建”,设置保持构建的最大个数为5。

丢弃旧的构建

4. 下拉到“流水线”配置,选择Pipeline script

流水线
流水线脚本如下:

node {# 从gitlab下载代码stage('Preparation') { // for display purposes// Get some code from a GitHub repositoryecho "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_namedocker stop $container_nameecho "docker rm" $container_namedocker rm $container_nameelse 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) --forceelse echo gitegg-portal \'镜像不存在,创建镜像\'fi'''}# Docker打包镜像,并保存为tarstage('Build Docker Image') {echo "start docker build portal code"// Run the docker builddocker.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) --forceelse 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. 在任务左侧点击立即构建
  • 立即构建

点击立即构建

  • 流水线任务可以在右侧显示阶段视图
    阶段视图
  • 查看构建日志:点击立即构建之后,下方会出现进度条,点击进度条就可以进入构建日志界面。
    查看构建日志
    日志
13. 构建成功后,下方会给出构建成功提示。

任务执行成功

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/68177.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysql Index

创建索引 方法1 create table 表( col1 int, col2 int, … index | key index_name (列名) 方法2 alter table 表名 ADD index alter table student_table add index index_name(stu_id); 方法3 create index index_name on 表名(列) 删除索引 方式1 alter table xx drop prima…

革命性的电子元件:RAD继电器 | 百能云芯

在现代电子和通信系统中&#xff0c;RAD继电器是一种关键的电子元件&#xff0c;它在各种应用中发挥着重要作用。RAD继电器&#xff08;Reed-relay Actuated Device&#xff09;是一种基于磁性原理的电子开关&#xff0c;其特点是极其高速、可靠、低功耗和长寿命。下面云芯将为…

案例分享:西河水库安全监测信息化系统实施方案

一、项目概述1.1项目背景西河水库信息化工作已开展多年&#xff0c;但是由于西河水库监测设备都已经老化或者损坏&#xff0c;现有设备已渐渐不能满足新时期西河水库信息化和现代化发展需求。因此&#xff0c;灌区管理局拟在运用现代信息和通信技术手段感测、分析、整合水库运行…

el-date-picker自定义只能选中当前月份和半年内月份等

需求&#xff1a;el-date-picker只能选中当前月期和当前月期往前半年&#xff0c;其他时间就禁用了不让选择了&#xff0c;因为没数据哈哈。当然也可以选择往前一年等。 一、效果 二、写个日期选择器 :picker-options&#xff1a;日期选项 value-format&#xff1a;选择后的格…

设计模式-工厂模式Factory

工厂模式 b.工厂方法模式 (Factory Method) (重点)1) 简单工厂 Simple Factory1.a) 简单工厂的好处 2) 工厂方法 Factory Method2.a) 对工厂进行抽象化 (版本一)2.b) 对工厂进行缓存 (版本二)2.c) 加载配置文件到缓存中 (版本三)c.1) 产品线 c.抽象工厂模式 (Abstract Factory)…

SQL-DQL

-----分组查询----- 1.语法&#xff1a; SELECT 字段列表 FROM 表名 [WHERE 条件 ] GROUP BY 分组字段名 [HAVING 分组后过滤条件]&#xff1b; 2.where与having区别 》执行时机不同&#xff1a;where是分组之前进行过滤&#xff0c;不满足where条件&#xff0c;不参与分组&…

【Flutter】Flutter 使用 qr_flutter 实现QR码二维码生成与渲染

【Flutter】Flutter 使用 qr_flutter 实现QR码二维码生成与渲染 文章目录 一、前言二、qr_flutter 包简介三、安装与配置四、基本使用五、高级功能与自定义六、完整实际业务代码示例七、总结 一、前言 亲爱的 Flutter 初学者&#xff0c;你好&#xff01;今天我要与你分享一个…

【黑马头条之项目部署_持续集成Jenkins】

本笔记内容为黑马头条项目的项目部署_持续集成部分 目录 一、内容介绍 1、什么是持续集成 2、持续集成的好处 3、今日内容 二、软件开发模式 1、软件开发生命周期 2、软件开发瀑布模型 3、软件的敏捷开发 三、Jenkins安装配置 1、Jenkins介绍 2、Jenkins环境搭建 …

OJ题库:计算日期到天数转换、打印从1到最大的n位数 、尼科彻斯定理

前言&#xff1a;在部分大厂笔试时经常会使用OJ题目&#xff0c;这里对《华为机试》和《剑指offer》中的部分题目进行思路分析和讲解&#xff0c;希望对各位读者有所帮助。 题目来自牛客网&#xff0c;欢迎各位积极挑战&#xff1a; HJ73:计算日期到天数转换_牛客网 JZ17:打印…

【【STM32-29正点原子版本串口发送传输实验】

STM32-29正点原子版本串口发送传输实验 通过串口接收或发送一个字符 例程目的 开发板上我们接入的是实现异步通信的UART接口 USB转串口原理图 我们一步步分析 PA9是串口1 的发送引脚 PA10是串口1 的接受引脚 。因为我们现在只是用到异步收发器功能&#xff0c;所以我们现…

qt中子窗口最小化后再恢复显示窗口区域显示为全白色

问题&#xff1a; qt中子窗口最小化后再恢复显示窗口区域显示为全白色&#xff0c;如下图&#xff1a; 原因&#xff1a; 恢复显示后窗口为及时刷新。 解决办法&#xff1a; 重写showEvent函数&#xff0c;如下&#xff1a; void MyClass::showEvent(QShowEvent *event) {se…

大模型 Dalle2 学习三部曲(二)clip学习

clip论文比较长48页&#xff0c;但是clip模型本身又比较简单&#xff0c;效果又奇好&#xff0c;正所谓大道至简&#xff0c;我们来学习一下clip论文中的一些技巧&#xff0c;可以让我们快速加深对clip模型的理解&#xff0c;以及大模型对推荐带来革命性的变化。 clip结构 首选…

Mybatis 动态SQL – 使用choose标签动态生成条件语句

之前我们介绍了if,where标签的使用&#xff1b;本篇我们需要在if,where标签的基础上介绍如何使用Mybatis提供的choose标签动态生成条件语句。 如果您对if,where标签动态生成条件语句不太了解&#xff0c;建议您先进行了解后再阅读本篇&#xff0c;可以参考&#xff1a; Mybat…

CSS中如何实现文字描边效果(Text Stroke)?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 文字描边效果&#xff08;Text Stroke&#xff09;⭐ 示例⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个…

Go实现LogCollect:海量日志收集系统【上篇——LogAgent实现】

Go实现LogCollect&#xff1a;海量日志收集系统【上篇——LogAgent实现】 下篇&#xff1a;Go实现LogCollect&#xff1a;海量日志收集系统【下篇——开发LogTransfer】 项目架构图&#xff1a; 0 项目背景与方案选择 背景 当公司发展的越来越大&#xff0c;业务越来越复杂…

Mysql底层数据结构为什么选择B+树

索引底层采用什么数据结构&#xff0c;为什么使用B树而不是其他数据结构&#xff1a; &#xff08;1&#xff09;如果采用二叉树&#xff1a;使用递增字段作为索引时&#xff0c;二叉树会退化成链表&#xff0c;查找效率太低 &#xff08;2&#xff09;如果采用红黑树&#xf…

微信小程序开发:一种新型的移动应用程序开发方式

一、引言 随着移动互联网的快速发展&#xff0c;微信小程序作为一种新型的移动应用开发方式&#xff0c;正在受到越来越多的关注。微信小程序是一种基于微信平台的轻量化应用&#xff0c;开发者可以通过微信提供的开发工具和接口&#xff0c;开发出各种具有特定功能的应用程序…

如何创建一个自己的sphinx文档网站

文章目录 前言一、操作步骤1.安装anaconda2.启动python3.8环境3.安装Sphinx4.创建文件夹5.初始化环境6. 编译7.文件夹搭查看8.搭建nginx查看8. 更换主题9.错误修复10.这里提供两个模板1.Demo_md2.Demo_rst前言 最近看到公司的文档中心,突然想起,为什么不为自己创建一个文档中…

在k8s中用label控制Pod部署到指定的node上

案例-标注k8s-node1是配置了SSD的节点 kubectl label node k8s-node1 disktypessd 查看标记 测试 将pod部署到disktypessd的节点上&#xff08;这里设置了k8s-node1为ssd&#xff09; 部署后查看结果-副本全都运行在了k8s-node1上—符合预期 删除标记 kubectl label node k8…

顶尖211“小清华”!强过985,不要错过它!

一、学校及专业介绍 西安电子科技大学&#xff08;Xidian University&#xff09;&#xff0c;简称“西电” &#xff0c;位于陕西省西安市&#xff0c;是中央部属高校&#xff0c;直属于教育部&#xff0c;为全国重点大学&#xff0c;位列国家“双一流”“211工程”&#xff…