咱們可能會遇到這樣的問題,咱們手動部署項目,多是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,本身使用不會有那麼大的量,因此沒走實時刷新。
咱們再來看看效果,是否是很香。