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

news2024/11/24 9:30:08

  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相关文章。

Docker Pipeline插件

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. 在任务左侧点击立即构建
  • 立即构建

点击立即构建

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

任务执行成功

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

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

相关文章

java线程和go协程

一、线程的实现 线程的实现方式主要有三种&#xff1a;内核线程实现、用户线程实现、用户线程加轻量级进程混合实现。因为自己只对java的线程比较熟悉一点&#xff0c;所以主要针对java线程和go的协程之间进行一个对比。 线程模型主要有三种&#xff1a;1、内核级别线程&#…

微服务主流框架概览

微服务主流框架概览 目录概述需求&#xff1a; 设计思路实现思路分析1.HSF2.Dubbo 3.Spring Cloud5.gRPC Service mesh 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a be…

深入理解作用域、作用域链和闭包

​ &#x1f3ac; 岸边的风&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! ​ 目录 &#x1f4da; 前言 &#x1f4d8; 1. 词法作用域 &#x1f4d6; 1.2 示例 &#x1f4d6; 1.3 词法作用域的…

医学专题-多组学在疾病发生发展过程中的研究思路

研究背景 单一组学数据分析通常用来解释某种特征性的生化指标与某些疾病之间的关联&#xff0c;但无法说明其中复杂的因果关系。从疾病表型或某种生物现象出发&#xff0c;寻找影响疾病发生发展的关键因子或通路&#xff0c;借助高通量的技术手段&#xff0c;设置相应的患者组…

49、IDEA 创建类或方法时,实现按格式化 ctrl + alt + l 能变成左花括号在下一行,与右花括号对齐

IDEA 创建类或方法时&#xff0c;左花括号是改成在下一行&#xff0c;与右花括号对齐 默认花括号是这样的 现在想改成这样的 实现按格式化 ctrl alt l 能变成这样 在这里修改就行 把 end of line 改成 next line

基于SSM框架金鱼销售平台源码和论文

基于SSM框架金鱼销售平台源码和论文120 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优…

机器人制作开源方案 | 桌面级全向底盘--本体说明+驱动控制

一、本体说明 1. 底盘概述 该底盘是一款模块化的桌面级应用型底盘&#xff0c;基于应用级软件架构设计、应用级硬件系统设计、典型应用型底盘机械系统设计。 底盘本体为一个采用半独立刚性悬挂的四驱全向底盘。 2. 软件环境介绍 操作系统&#xff1a;Ubuntu18.04系统。基于Deb…

对swap交换分区虚拟内存的理解

Swap分区的作用是什么 更新&#xff1a;2023-05-31 13:10 Swap是一种虚拟内存技术&#xff0c;在计算机内存不足时&#xff0c;它可以将运行中的程序或者数据存到硬盘上以释放内存空间。Swap技术不仅适用于Linux操作系统&#xff0c;Windows和Mac OS也有类似的技术&#xff0…

为什么各个企业都在强调要建立sop?

在现代社会中&#xff0c;随着科技的不断发展&#xff0c;各行各业的竞争也越来越激烈。为了提高工作效率&#xff0c;很多企业开始重视建立标准操作流程&#xff08;SOP&#xff09;。那么&#xff0c;为什么要建立SOP呢&#xff1f; 所谓SOP&#xff0c;就是 Standard Opera…

安装ArcGis时需要安装Micsoft.Net Framework 3.5 sp1

在安转ArcGis时遇到一个问题&#xff0c;解决方法如下 下载.Net 按照他的说明 将地址复制到迅雷中下载&#xff0c;并安装 就可以了 安装就可以了

MES系统在电力装备方面的应用

MES系统主要功能&#xff1a;解决“如何生产”的问题 通过实施MES系统&#xff0c;可以贯通从采购到售后服务的全制造流程&#xff0c;透明化生产现场运作&#xff0c;大大提升了生产制造各部门的管理实时性和有效性。 可获得的效益大致如下&#xff1a; 降低不良率&#xff…

【webrtc】接收/发送的rtp包、编解码的VCM包、CopyOnWriteBuffer

收到的rtp包RtpPacketReceived 经过RtpDepacketizer 解析后变为ParsedPayloadRtpPacketReceived 分配内存,执行memcpy拷贝:然后把 RtpPacketReceived 给到OnRtpPacket 传递:uint8_t* media_payload = media_packet.AllocatePayload(rtx_payload.size());RTC

Python break 语句

Python break语句&#xff0c;就像在C语言中&#xff0c;打破了最小封闭for或while循环。 break语句用来终止循环语句&#xff0c;即循环条件没有False条件或者序列还没被完全递归完&#xff0c;也会停止执行循环语句。 break语句用在while和for循环中。 如果您使用嵌套循环…

关于JDK 8的HashMap

HashMap 简介 HashMap 主要用来存放键值对&#xff0c;它基于哈希表的 Map 接口实现&#xff0c;是常用的 Java 集合之一&#xff0c;是非线程安全的。 HashMap 可以存储 null 的 key 和 value&#xff0c;但 null 作为键只能有一个&#xff0c;null 作为值可以有多个 JDK1.8…

信号浪涌保护器防雷接地工程应用方案

信号浪涌保护器是一种用于保护电子设备免受电力线路上的瞬时过电压或过电流的装置。信号浪涌保护器的参数方案和应用施工主要取决于信号线路的类型、电气特性、工作环境和保护要求。下面是一篇关于信号浪涌保护器的文章&#xff0c;介绍了一些常见的信号浪涌保护器参数方案和应…

Android Canvas的使用

android.graphics.Canvas 一般在自定义View中&#xff0c;重写 onDraw(Canvas canvas) 方法时用到。 /*** Implement this to do your drawing.** param canvas the canvas on which the background will be drawn*/Overrideprotected void onDraw(Canvas canvas) {super.onDra…

LeetCode 剑指 Offer 10- I. 斐波那契数列

LeetCode 剑指 Offer 10- I. 斐波那契数列 题目描述 写一个函数&#xff0c;输入 n &#xff0c;求斐波那契&#xff08;Fibonacci&#xff09;数列的第 n 项&#xff08;即 F(N)&#xff09;。斐波那契数列的定义如下&#xff1a; F(0) 0, F(1) 1 F(N) F(N - 1) F(N - …

LeetCode 46题:全排列

题目 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]示例 2&#xff1a; 输入&#xff1a;…

Bridge Champ举办人机对战赛:NFT游戏与传统竞技共生发展编织新格局

概要 现在,NFT与体育竞技正日益紧密地联系在一起。一些体育项目开始推出与赛事或球队相关的NFT,同时也有部分NFT游戏开始举办电子竞技赛事。这种共生发展正在改变体育竞技的生态。 笔者采访了桥牌冠军项目相关负责人,探讨NFT游戏与传统体育竞技的融合潜力。桥牌冠军近期成功举…

您必须尝试的 4 种经典特征提取技术!

一、说明 特征提取如何实现&#xff1f;其手段并不是很多&#xff0c;有四个基本方法&#xff0c;作为AI工程师不能不知。因此&#xff0c;本篇将对四种特征提取给出系统的方法。 二、概述 图像分类长期以来一直是计算机视觉领域的热门话题&#xff0c;并希望能够保持这种状态。…