咱们可能会遇到这样的问题,咱们手动部署项目,多是node项目,多是java项目,多是前端项目,咱们安装的node版本或者jdk,tomcat版本不一致,致使项目会发生各类诡异问题,有的服务器就是好使,有的服务器就是有问题,正常来讲都是部署漏了点东西。
咱们就不能把好的服务打成包直接拿来使用么?html
虚拟机(virtual machine)就是带环境安装的一种解决方案。它能够在一种操做系统里面运行另外一种操做系统前端
- 资源占用多
- 冗余步骤多
- 启动速度慢
因为虚拟机存在这些缺点,Linux 发展出了另外一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。
Linux 容器不是模拟一个完整的操做系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来讲,它接触到的各类资源都是虚拟的,从而实现与底层系统的隔离。java
- 启动快
- 资源占用少
- 体积小
- Docker秒级启动
- KVM分钟级启动
- 容器共享宿主机内核,系统级虚拟化,占用资源少,容器性能基本接近物理机
- 虚拟机须要虚拟化一些设备,具备完整的OS,虚拟机开销大,于是下降性能,没有容器性能好
- 因为共享宿主机内核,只是进程隔离,所以隔离性和稳定性不如虚拟机,容器具备必定权限访问宿>- 主机内核,存在一下安全隐患
- KVM基于硬件的彻底虚拟化,须要硬件CPU虚拟化技术支持
- 容器共享宿主机内核,可运行在主机的Linux的发行版,不用考虑CPU是否支持虚拟化技术
- 单项目打包
- 整套项目打包
- 新开源技术
安装社区版本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 启动node
systemctl start docker
复制代码
查看docker版本mysql
docker version
docker info
复制代码
阿里云镜像加速linux
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 image镜像操做nginx
命令 | 含义 | 案例 |
---|---|---|
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 容器操做git
命令 | 含义 | 案例 |
---|---|---|
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 数据盘操做github
#建立数据盘
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 、hostweb
#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作编排
安装完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,本身使用不会有那么大的量,因此没走实时刷新。
咱们再来看看效果,是否是很香。