美文网首页
前端集成化部署 docker篇

前端集成化部署 docker篇

作者: 大前端晨曦 | 来源:发表于2020-06-11 21:28 被阅读0次

    我们可能会遇到这样的问题,我们手动部署项目,可能是node项目,可能是java项目,可能是前端项目,我们安装的node版本或者jdk,tomcat版本不一致,导致项目会发生各种诡异问题,有的服务器就是好使,有的服务器就是有问题,正常来说都是部署漏了点东西。

    我们就不能把好的服务打成包直接拿来使用么?

    布署软件的问题

    • 如果想让软件运行起来要保证操作系统的设置,各种库和组件的安装都是正确的
    • 热带鱼&冷水鱼 冷水鱼适应的水温在5-30度,而热带鱼只能适应22-30度水温,低于22度半小时就冻死了

    常用解决方案和对比

    虚拟机

    虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统

    • 资源占用多
    • 冗余步骤多
    • 启动速度慢

    Linux容器

    由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。

    Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。

    • 启动快
    • 资源占用少
    • 体积小

    Docker

    • Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。
    • Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样

    Docker和KVM

    1. 启动时间
    • Docker秒级启动
    • KVM分钟级启动
    1. 轻量级 容器镜像通常以M为单位,虚拟机以G为单位,容器资源占用小,要比虚拟要部署更快速
    • 容器共享宿主机内核,系统级虚拟化,占用资源少,容器性能基本接近物理机
    • 虚拟机需要虚拟化一些设备,具有完整的OS,虚拟机开销大,因而降低性能,没有容器性能好
    1. 安全性
    • 由于共享宿主机内核,只是进程隔离,因此隔离性和稳定性不如虚拟机,容器具有一定权限访问宿>- 主机内核,存在一下安全隐患
    1. 使用要求
    • KVM基于硬件的完全虚拟化,需要硬件CPU虚拟化技术支持
    • 容器共享宿主机内核,可运行在主机的Linux的发行版,不用考虑CPU是否支持虚拟化技术

    Docker的应用场景

    • 节省项目环境部署时间

    • 单项目打包

    • 整套项目打包

    • 新开源技术

    • 环境一致性

    • 持续集成

    • 微服务

    • 弹性伸缩

    Docker 体系结构

    • containerd 是一个守护进程,使用runc管理容器,向Docker Engine提供接口
    • shim 只负责管理一个容器
    • runC是一个轻量级工具,只用来运行容器

    Docker安装

    • docker分为企业版(EE)和社区版(CE)
    • docker-ce
    • hub.docker
    安装社区版本docker
    yum install -y yum-utils   device-mapper-persistent-data   lvm2
    yum-config-manager     --add-repo     https://download.docker.com/linux/centos/docker-ce.repo
    yum-config-manager --enable docker-ce-nightly #要每日构建版本的 Docker CE
    yum-config-manager --enable docker-ce-test  
    yum install docker-ce docker-ce-cli containerd.io
    

    docker 启动

    systemctl start docker
    

    查看docker版本

        version
    docker info
    

    镜像加速

    阿里云镜像加速

    sudo mkdir -p /etc/docker
    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
      "registry-mirrors": ["https://fwvjnv59.mirror.aliyuncs.com"]
    }
    EOF
    # 重载所有修改过的配置文件
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    

    Docker常用方法

    docker image镜像操作

    命令 含义 案例
    ls 查看全部镜像 docker image ls
    search 查找镜像 docker search [imageName]
    history 查看镜像历史 docker history [imageName]
    inspect 显示一个或多个镜像详细信息 docker inspect [imageName]
    pull 拉取镜像 docker pull [imageName]
    push 推送一个镜像到镜像仓库 docker push [imageName]
    rmi 删除镜像 docker rmi [imageName] docker image rmi 2
    prune 移除未使用的镜像,没有被标记或补任何容器引用 docker image prune
    tag 标记本地镜像,将其归入某一仓库 docker image tag [imageName] [username]/[repository]:[tag]
    export 导出容器文件系统tar归档文件创建镜像 docker export -o mysqlv1.tar a404c6c174a2
    import 导入容器快照文件系统tar归档文件创建镜像 docker import mysqlv1.tar wp/mysql:v2
    save 保存一个或多个镜像到一个tar归档文件 docker save -o mysqlv2.tar wp/mysqlv2:v3
    load 加载镜像存储文件来自tar归档或标准输入 docker load -i mysqlv2.tar
    build 根据Dockerfile构建镜像

    docker 容器操作

    命令 含义 案例
    run 从镜像运行一个容器 docker run ubuntu /bin/echo 'hello-world'
    ls 列出容器 docker container ls
    inspect 显示一个或多个容器详细信息 docker inspect
    attach 要attach上去的容器必须正在运行,可以同时连接上同一个container来共享屏幕 docker attach
    stats 显示容器资源使用统计 docker container stats
    top 显示一个容器运行的进程 docker container top
    update 显示一个容器运行的进程 docker container update
    port 更新一个或多个容器配置 docker container port
    ps 查看当前运行的容器 docker ps -a -l
    kill [containerId] 终止容器(发送SIGKILL ) docker kill [containerId]
    rm [containerId] 删除容器 docker rm [containerId]
    start [containerId] 启动已经生成、已经停止运行的容器文件 docker start [containerId]
    stop [containerId] 终止容器运行 (发送 SIGTERM ) docker stop [containerId]
    logs [containerId] 查看 docker 容器的输出 docker logs [containerId]
    exec [containerId] 进入一个正在运行的 docker 容器执行命令 docker container exec -it [containerID] /bin/bash
    cp [containerId] 从正在运行的 Docker 容器里面,将文件拷贝到本机 docker container cp [containID]:app/package.json .
    commit [containerId] 创建一个新镜像来自一个容器 docker commit -a "wp" -m "mysql" a404c6c174a2 mynginx:v1

    docker 数据盘操作

    • volume
    #创建数据盘
    docker volume create nginx-vol docker volume ls docker volume inspect nginx-vol #把nginx-vol数据卷挂载到/usr/share/nginx/html,挂载后容器内的文件会同步到数据卷中
    docker run -d  --name=nginx1 --mount src=nginx-vol,dst=/usr/share/nginx/html nginx docker run -d  --name=nginx2  -v nginx-vol:/usr/share/nginx/html -p 3000:80 nginx #删除数据卷
    docker container stop nginx1 #停止容器
    docker container rm nginx1 #删除容器
    docker volume rm nginx-vol  #删除数据库 
    
    
    • Bind mounts
    #此方式与Linux系统的mount方式很相似,即是会覆盖容器内已存在的目录或文件,但并不会改变容器内原有的文件,当umount后容器内原有的文件就会还原
    #创建容器的时候我们可以通过-v或--volumn给它指定一下数据盘
    #bind mounts 可以存储在宿主机系统的任意位置
    #如果源文件/目录不存在,不会自动创建,会抛出一个错误
    #如果挂载目标在容器中非空目录,则该目录现有内容将被隐藏
    docker run -v /mnt:/mnt -it --name logs centos bash cd /mnt
    echo 1 > 1.txt
    docker inspect logs
    #可以查看到挂载信息
    "Mounts": [
        {
            "Source":"/mnt/sda1/var/lib/docker/volumes/dea6a8b3aefafa907d883895bbf931a502a51959f83d63b7ece8d7814cf5d489/_data",
            "Destination": "/mnt",
        }
    ]
    # 指定数据盘容器
    docker create -v /mnt:/mnt --name logger centos
    docker run --volumes-from logger --name logger3 -i -t centos bash cd /mnt 
    touch logger3
    docker run --volumes-from logger --name logger4 -i -t centos bash cd /mnt
    touch logger4
    
    

    docker 网络

    安装Docker时,它会自动创建三个网络,bridge(创建容器默认连接到此网络)、 none 、host

    • None:该模式关闭了容器的网络功能,对外界完全隔离
    • host:容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口。
    • bridge 桥接网络,此模式会为每一个容器分配IP
      可以使用该--network标志来指定容器应连接到哪些网络
    #bridge模式使用 --net=bridge 指定,默认设置
    docker network ls #列出当前的网络
    docker inspect bridge #查看当前的桥连网络
    docker run -d --name nginx1 nginx docker run -d --name nginx2 --link nginx1 nginx docker exec -it nginx2 bash
    apt update
    apt install -y inetutils-ping  #ping
    apt install -y dnsutils        #nslookup
    apt install -y net-tools       #ifconfig
    apt install -y iproute2        #ip
    apt install -y curl            #curl
    cat /etc/hosts
    ping nginx1
    
    # none模式使用--net=none指定
    # --net 指定无网络
    docker run -d --name nginx_none --net none nginx docker inspect none
    docker exec -it nginx_none bash
    ip addr
    
    # host模式使用 --net=host 指定
    docker run -d --name nginx_host --net host nginx docker inspect host
    docker exec -it nginx_host bash
    ip addr
    
    

    端口映射

    # 查看镜像里暴露出的端口号
    docker image inspect nginx
    "ExposedPorts": {"80/tcp": {}}
    # 让宿主机的8080端口映射到docker容器的80端口
    docker run -d --name port_nginx -p 8080:80  nginx # 查看主机绑定的端口
    docker container port port_nginx
    
    #指向主机的随机端口
    docker run -d --name random_nginx --publish 80 nginx docker port random_nginx
    
    docker run -d --name randomall_nginx --publish-all nginx docker run -d --name randomall_nginx --P nginx 
    #创建自定义网络
    docker network create --driver bridge myweb
    # 查看自定义网络中的主机
    docker network inspect myweb
    # 创建容器的时候指定网络 指定同一个网络的容器是可以互相通信的
    docker run -d --name mynginx1  --net myweb nginx docker run -d --name mynginx2  --net myweb nginx docker exec -it mynginx2 bash
    ping mynginx1
    
    # 连接到指定网络
    docker run -d --name mynginx3   nginx docker network connect  myweb mynginx3
    docker network disconnect myweb mynginx3
    
    # 移除网络
    docker network rm myweb
    
    

    compose 暂时先不说,暂时用到的不多,主要做编排使用,基本上都在使用jekins做编排

    部署环境

    node环境部署

    安装完docker环境 继续安装node环境

    nvm: # nvm管理node版本
    curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash
    source ~/.bash_profile
    nvm ls
    nvm install stable 安装最新的稳定版本
    nvm use stable
    nrm:# 切换node镜像,修改源为淘宝镜像
    npm i -g nrm
    nrm use taobao
    
    

    安装pm2 部署线上 node服务

    npm i -g pm2
    cd /root/webhook
    pm2 start webhook.js --name webhook --watch
    pm2 list | pm2 ls
    
    

    集成项目搭建

    回想起以前的前端部署都是前端打个目标文件,压缩成压缩包或者rpm安装包去发布,如果有多个环境还需要一步步的去连服务器去手动发布

    为了解决这种耗人力的工作,这边推出了一款简易的docker发布项目

    我们可以把其中一台服务器配置成发布服务器,用来编译新镜像发布新镜像,然后直接拷贝镜像到别的服务器直接启动

    现在我们见一个node项目docker-hook

    此项目的核心是通过用户点击页面上的触发去动态调用sh去处理我们的脚本

    中间一版本我们是通过接口调用触发,发现不是很好用,就做一个可视化平台去使用

    也可以通过gitHub的webhook去动态触发CI/CD,提交即部署,这边我就不贴代码了

    这边主要讲思路,贴上部分代码,如果有需要优化的部分麻烦指正

    // 本项目使用的是通过node的child_process spawn开启一个子进程去处理sh命令
    // console log日志是通过morgan 自定义输出
    // 每个模块的sh脚本都会通过winston把实时日志存储到对应的模块日志文件中,文件大问题,我们就按天生成一个文件日志
    /** logger.js **/
    const winston=require('winston');
    const { APP_LIST } = require('./constant')
    const { loggerTime } = require('./util')
    
    const loggerList = {};
    APP_LIST.forEach(item => {
      loggerList[item.loggerName] = winston.createLogger({
        transports: [
            new (winston.transports.Console)(),
            new (winston.transports.File)({ 
                filename: `public/logs/${item.loggerName}-${loggerTime()}.log`,
                timestamp:'true', 
                maxsize: 10485760, //日志文件的大小
                maxFiles: 10 })
        ]});
    });
    loggerList['init'] = winston.createLogger({
      transports: [
          new (winston.transports.Console)(),
          new (winston.transports.File)({ 
              filename: `public/logs/init-${loggerTime()}.log`,
              timestamp:'true', 
              maxsize: 10485760, //日志文件的大小
              maxFiles: 10 })
      ]});
    
    module.exports = loggerList;
    
    /** app.js **/
    let { spawn } = require('child_process');
    /**
     * 统一处理shell脚本执行
     */
    function handleShellFile(projectName, shellPath, res, req) {
      return resolveFile(shellPath).then(data => {
        // 判断当前是否是成功
        if(!data.success) {
          errorHandle(res);
        }
        let child = spawn('sh', [data.filePath])
        let buffers = [];
        child.stdout.on('data', (buffer) => {
          buffers.push(buffer);
          console.log('实时日志:', buffer.toString());
          logger[projectName] && logger[projectName].log("info", `实时日志:${buffer.toString()}`);
        })
        child.stdout.on('end',function(buffer){
          let logs = Buffer.concat(buffers, buffer).toString();
          console.log('执行完毕');
          logger[projectName] && logger[projectName].log("info", '执行完毕');
          res.setHeader('Content-Type', 'application/json');
          res.end(JSON.stringify({ok: true}))
        });
        child.on('close', (code) => {
          if (code !== 0) {
            console.log(`子进程退出,退出码 ${code}`);
          }
        });
      }, error => {
        // 错误处理显示返回
        errorHandle(res);
      })
    }
    
    

    shell文件介绍:

    ├─ docker-hook
    │  ├─ sh // shell脚本文件
    │  │  ├─ Archer-front-image.sh // 前端版本复制镜像
    │  │  ├─ Archer-front-remote.sh // 前端版本远程打包
    │  │  ├─ Archer-front.sh // 前端版本本地打包编译发布(本地使用)
    │  │  ├─ ar-mock-image.sh // armock项目复制镜像
    │  │  ├─ ar-mock-remote.sh // armock项目远程打包
    │  │  ├─ ar-mock.sh // armock项目本地打包编译发布(本地使用)
    │  │  ├─ env-init.sh // 环境初始化
    │  │  └─ project-init.sh // git项目初始化,帮忙建目录
    
    
    

    env-init.sh可以拷贝到服务器 一键去部署环境

    #!/bin/bash
    echo 'docker 环境初始化'
    function docker_install()
    {
        echo "检查Docker......"
        docker -v
      if [ $? -eq  0 ]; then
          echo "检查到Docker已安装!"
      else
          echo "安装docker环境..."
          yum install -y yum-utils   device-mapper-persistent-data   lvm2
          yum-config-manager     --add-repo     https://download.docker.com/linux/centos/docker-ce.repo
          yum-config-manager --enable docker-ce-nightly #要每日构建版本的 Docker CE
          yum-config-manager --enable docker-ce-test  
          yum install -y docker-ce docker-ce-cli containerd.io
    
          echo '启动docker'
          systemctl start docker
    
          echo '查看docker'
          docker version
          echo "安装docker环境...安装完成!"
      fi
    }
    # 执行函数
    docker_install
    
    # nrm 是否安装
    function nvm_install()
    {
        nvm --version
        if [ $? -eq  0 ]; then
           echo "检查到nvm已安装!"
           nvm install v13.14.0 #安装最新的稳定版本
           nvm use v13.14.0
           echo "安装node环境...安装完成!"
        else
          source /root/.bashrc
            echo "安装nvm失败..."
        fi
    }
    
    # node 是否安装
    function node_install()
    {
        echo "检查node......"
        node -v
        if [ $? -eq  0 ]; then
            echo "检查到Node已安装!"
        else
            echo "安装nvm环境..."
           curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.31.1/install.sh | bash
           source /root/.bashrc
           nvm_install
        fi
    }
    
    # node_module库 安装监测
    function node_module_install()
    {
          node --version
        if [ $? -eq  0 ]; then
          echo "安装nrm源和pm2库"
          nrm_install
          pm2_install
        else
            echo "node环境未安装成功"
        fi
    }
    
    # nrm 安装监测
    function nrm_install()  {
        echo "监测nrm源..."
        nrm --version
        if [ $? -eq 0 ]; then
        echo "已安装nrm源"
        else 
        npm i -g nrm
        nrm use taobao
        echo "安装nrm源成功"
        fi
    }
    
    # pm2 安装监测
    function pm2_install()  {
        echo "监测pm2库..."
        pm2 --version
        if [ $? -eq  0 ]; then
        echo "已安装pm2库"
        else 
        npm i -g pm2
        echo "安装pm2库成功"
        fi
    }
    
    # 执行函数
    echo '安装node环境'
    node_install
    node_module_install
    
    
     如果已经安装过node,确认下是否更新过~/.bash_profile,没有则添加,也可以安装nvm去管理node export NODE_ENV=/root/node/node-v12.16.2-linux-x64 PATH=$PATH:$HOME/bin:$NODE_ENV/bin 刷新配置文件 source ~/.bash_profile
    

    project.sh文件主要是建立文件目录,git clone文件并为后续的部署做准备

    Archer-front.sh拉代码部署,镜像生成,容器部署一个文件搞定

    #!/bin/bash
    WORK_PATH='/root/front'
    cd $WORK_PATH
    echo "清除老代码"
    git reset --hard origin/master
    git clean -f
    echo "拉取最新代码"
    git pull origin master
    echo "删除node_modules文件"
    rm -rf ./node_modules
    echo "重新安装依赖"
    npm i
    echo "编译打包"
    npm run build
    echo "开始执行构建"
    docker build -f ./docker/Dockerfile -t archer-front:1.0 .
    echo "停止旧的容器并删除容器"
    docker stop archer-front-container
    docker rm archer-front-container
    echo "启动新容器"
    docker run -p 11001:11001 -v /etc/hosts:/etc/hosts --name archer-front-container -itd archer-front:1.0
    

    那么多节点部署怎么办呢?

    我们可以考虑把当前的这个镜像导出并导入加载

    Archer-front-image.sh

    #!/bin/bash
    echo "进入目录/root/images"
    WORK_PATH='/root'
    cd $WORK_PATH
    if [ ! -d images  ];then
      mkdir images
    fi
    IMAGES_PATH='images'
    cd $IMAGES_PATH
    echo "开始拷贝前端镜像"
    docker save -o image-Archer-front.tar archer-front:1.0
    echo "拷贝前端镜像完成"
    

    其他节点怎么来拿呢?可以通过scp来拷贝这边打包出来的镜像去使用啊,这就是Archer-front-remote.sh里面的实现

    查看日志功能主要是通过定时刷新调用接口去实现的,有些low,自己使用不会有那么大的量,所以没走实时刷新。

    我们再来看看效果,是不是很香。

    一次发布,终身好用


    最后送大家一波福利

    在这里特地讲我自己这两个月整理的相关面试题分享给大家,免费获取哦~

    获取方式:

    一、搜索QQ群,前端学习交流群:954854084

    二、点击加入,与前端大牛一起进步!

    三、QQ扫描下方二维码!

    相关文章

      网友评论

          本文标题:前端集成化部署 docker篇

          本文链接:https://www.haomeiwen.com/subject/hqmitktx.html