前端集成化部署 docker篇

2020-06-11  本文已影响0人  大前端晨曦

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

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

布署软件的问题

常用解决方案和对比

虚拟机

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

Linux容器

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

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

Docker

Docker和KVM

  1. 启动时间
  1. 轻量级 容器镜像通常以M为单位,虚拟机以G为单位,容器资源占用小,要比虚拟要部署更快速
  1. 安全性
  1. 使用要求

Docker的应用场景

Docker 体系结构

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 数据盘操作

#创建数据盘
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  #删除数据库 

#此方式与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

#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扫描下方二维码!

上一篇下一篇

猜你喜欢

热点阅读