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 暂时先不说,暂时用到的不多,主要做编排使用,我们现在做的前端打包就用到了nginx,没有使用到node和数据库,所以我们选型就不使用docker-compose
当然docker-compose对于多个镜像可以统一管理,启动关闭移除都很方便
部署环境
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
还在使用命令行执行么,太low了,得升级升级
这边写了个shell脚本,资源初始化脚本,一个命令完成环境资源的安装
env-init.sh 可以拷贝到服务器 一键去部署环境 从此解放双手
#!/bin/bash
echo 'docker 环境初始化'
function git_install() {
echo "检查Git......"
git --version
if [ $? -eq 0 ]; then
echo "检查到Git已安装!"
else
yum -y install git
yum -y install sshpass
fi
echo "git和sshpass安装完成!"
}
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
git_install
# nrm 是否安装
function nvm_install()
{
nvm --version
if [ $? -eq 0 ]; then
echo "检查到nvm已安装!"
source /root/.bashrc
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
集成项目搭建
回想起以前的前端部署都是前端打个目标文件,压缩成压缩包或者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 上面已经介绍,资源的整合初始化可以都放到这个文件里面,当前我这边用到了 docker, git, sshpass, nvm, nrm, node, nrm, pm2
project.sh 文件主要是建立文件目录,git clone文件并为后续的部署做准备
Archer-front.sh 更新代码,执行dockerfile(里面主要是nginx配置和拷贝生成之后的静态资源文件)部署,镜像生成,容器部署一个文件搞定,这边做成容器的好处是好移植到其他服务器
#!/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,自己使用不会有那么大的量,所以没走实时刷新。
我们再来看看效果,是不是很香。
一次,终身好用
第一波优化之后
经过半个月的使用,我们团队还是发现了一些问题
1、 子节点需要访问不同的子节点对应部署的地址去点击(比较烦)
2、 子节点环境和发包环境没有做权限控制,看到的内容一样,但是子节点应该不支持打包,只允许拉取打包环境的镜像去部署(未做隔离)
3、子节点没法做到配置
4、 本地和远程打包概念有理解偏差
5、 日志资源访问bug:当天未创建日志获取日志资源报错
问题说完了,那么该动手优化了
远程子节点维护+部署
针对于问题1,2,3,我们整体做了优化,打包环境支持 远程,复制镜像,本地打包, 子节点呢?只支持本地,子节点还要可配置,怎么办呢,本着不想麻烦的思想,用到了commander命令配置
var app = require('../app');
var commander = require('commander');
commander.option('-p, --port <val>', "please input server port")
.option('-l, --list <val>', "please input ip port")
.option('-n, --env <val>', "please input env ").parse(process.argv);
let config = {
port: 4000,
list: ['172.118.38.11', '172.118.38.12', '172.118.38.13'], // 默认设置开发环境ip节点
env: 'local', // 默认设置本地
}
Object.assign(config, commander);
console.log('config', config.env, config.port)
/**
* Get port from environment and store in Express.
*/
app.set('port', config.port);
app.set('ipList', config.list); // 设置ip节点
app.set('env', config.env); // 设置环境信息 local 本地 remote 远程
如上面代码所示,我们允许部署的时候去设置默认值
port 配置服务启动端口号,默认4000
list 配置子节点端口,不设置默认为预设值的列表
env 配置环境变量,来区分是本地还是远程子节点环境
我们希望我们系统支持维护子节点
/**
* 获取节点列表
*/
app.get('/getIpList', (req, res) => {
console.log(app.get('ipList'), app.get('env'));
return res.json({
success: true,
data: app.get('ipList'),
env: app.get('env'),
})
})
/**
* 新增节点
*/
app.get('/addIP', (req, res) => {
const { ip } = req.query;
app.set('ipList', app.get('ipList').concat([ip]));
return res.json({
success: true,
data: app.get('ipList')
})
})
/**
* 新增节点
*/
app.get('/removeIP', (req, res) => {
const { index } = req.query;
app.get('ipList').splice(index, 1);
return res.json({
success: true,
data: app.get('ipList')
})
})
我们提供下接口去使用,并且页面上做支持
那么来到我们的节点远程部署了,因为我们在子节点上已经部署了我们的应用,所以我们可以去调用暴露出来的接口,我们需要通过http client去调用远程接口
/**
* 远程节点
*/
app.get('/remoteBuildNode', (req,res) => {
const { node, projectName } = req.query;
const config = {
hostname: node,
port: 4000,
path: `/buildPackage?projectName=${projectName}`,
method: 'GET',
};
// 远程请求数据
let requet = http.request(config, (httpRes)=>{
let data = '';
httpRes.on('data', (chunk) => {
console.log('httpRes-',chunk.toString());
data += chunk.toString();
})
httpRes.on('end', () => {
let result = data && JSON.parse(data);
console.log('result', result);
return res.json({
success: result.ok,
});
});
});
requet.end();
})
一个个节点点击也是很麻烦呀,能否做个合并呢,那么就在前端用promise.all做吧
/**
* 本地远程
*/
remoteBuildPackage(item) {
this.buildLoading[item.loggerName] = true;
Promise.all(this.nodeList.map(node => fetch(`/remoteBuildNode?&node=${node}&projectName=${item.loggerName}`) ))
.then(data => {
let result = data.every(dataItem => dataItem.ok)
this.buildLoading[item.loggerName] = false;
result ? this.$Notice.success({
title: '成功',
desc: `${item.title}远程成功`
}): this.$Notice.error({
title: '部分失败',
desc: `${item.title}远程部分失败`
});;
})
.catch(err => {
this.buildLoading[item.loggerName] = false;
this.$Notice.error({
title: '失败',
desc: `${item.title}远程过程异常`
});
});
},
节点的独立部署和整合部署都做完了
那么我们部署还需要注意什么呢?
权限隔离啊~~~ 我们使用env来做区分
"scripts": {
"local": "node ./bin/www -n local",
"remote": "node ./bin/www -n remote"
},
pm2 start npm --name webhook -- run remote # 远程子节点部署
pm2 start npm --name webhook -- run local # 打包环境部署
日志文件不实时打印问题
修改了logger.js文件对应的文件初始化
const winston=require('winston');
const { APP_LIST } = require('./constant')
const { loggerTime } = require('./util')
function createLogger() {
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 })
]});
return loggerList;
}
const loggerList = createLogger();
module.exports = {
loggerList,
createLogger
};
当天部署了服务,创建了日志文件,初始化了日志文件,可以正常使用
第二天,我们再来使用,我们手动创建了日志文件,没有初始化,会导致日志文件写入不进去,需要我们手动初始化下日志文件createLogger(),这样才能确保我们正常日志使用。
第一波整改之后成果
打包环境
子节点环境
后续规划
- 远程整合部署日志展示问题,是整合一起展示还是独立展示问题?
- 打包部署环境还是使用的原始方式部署,没有使用容器,考虑后期是否上容器,只需要部署一次
- 项目后续扩展方案,当前只固定维护2个项目
- 有定时任务需求的话,是否要考虑和jekins配合使用
- pc端还是不方便,考虑手机端接入方案,机器人~~~
暂时想到这么多了~~~后续再补充
大家有什么好想法,或者说你们团队是如何实现CI,CD的可以在评论区讨论。