前端集成化部署 docker篇

2020/5/20 11:26:51

本文主要是介绍前端集成化部署 docker篇,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

Docker的由来

我们可能会遇到这样的问题,我们手动部署项目,可能是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版本

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,自己使用不会有那么大的量,所以没走实时刷新。
我们再来看看效果,是不是很香。

一次发布,终身好用



这篇关于前端集成化部署 docker篇的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程