• 【微服务部署】五、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;"]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    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
    
    • 1
    • 2
    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/;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    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    [portal port] [server port] [portal ssl port] [server ssl port]"  
      exit  
    fi
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 入参赋值,如果有参数传入,则取服务参数,如果没有参数传入则取默认值
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 停止并删除容器
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 停止并删除镜像
    # 删除镜像
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 备份本次安装镜像包
    #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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 执行安装镜像包命令
    echo "docker load" $image_name
    docker load --input /opt/tmp/portal-image.tar
    
    • 1
    • 2
    • 执行运行命令
    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
    
    • 1
    • 2
    • 删除安装文件,因为前面已经备份过了,所以这里将临时安装文件删除
    echo "remove tmp " $image_name
    rm -rf /opt/tmp/portal-image.tar
    
    • 1
    • 2
    • 打印执行完成的命令
    echo "Docker Portal is starting,please try to access $container_name conslone url"
    
    • 1
    • 完整的安装部署脚本
    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    [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"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    二、新建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"
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    12. 在任务左侧点击立即构建
    • 立即构建

    点击立即构建

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

    任务执行成功

  • 相关阅读:
    百度AI---人脸对比(前端拍照上传后端进行对比,后端调用百度接口返回对比信息)
    ToDoList项目
    Linux常用命令
    微信小程序案例2-3:婚礼邀请函
    【每日刷题——语音信号篇】
    奇舞周刊第495期:软件高可用实践那些事
    kubelet源码分析 创建/更新/调谐 pod
    Leetcode刷题详解——全排列 II
    vue项目实际开发的问题及实用技巧分享(三)
    好饭不怕晚,Google基于人工智能AI大语言对话模型Bard测试和API调用(Python3.10)
  • 原文地址:https://blog.csdn.net/wmz1932/article/details/132663297