当前位置: 首页 > news >正文

docker 部署_docker自动化部署前端项目实战

docker自动化部署前端项目实战

本文适用于个人项目,如博客、静态文档,不涉及后台数据交互,以部署文档为例。

思路

利用服务器node脚本,监听github仓库webhook push事件触发post请求,自动拉取最新代码,再用docker接管项目编译、部署。

环境

本文使用云服务器搭建,环境版本:

  • OS:CentOS Linux release 8.2.2004

  • docker:19.03.12

  • node:14.5.0

  • git:2.18.4

云服务器如果没有安装以下环境,需要安装。

  • docker

  • node

  • pm2

  • git

docker

# Step 1: 安装必要的一些系统工具sudo yum install -y yum-utils# Step 2: 添加软件源信息,使用阿里云镜像sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo# Step 3: 安装 docker-cesudo yum install docker-ce docker-ce-cli containerd.io# Step 4: 开启 docker服务sudo systemctl start docker# Step 5: 运行 hello-world 项目sudo docker run hello-world

不出意外,出现hello world,docker安装成功

git

从代码仓库拉取最新代码

yum install git
node

创建js脚本。使用nvm管理node版本,先安装nvm

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.34.0/install.sh | bash

将nvm设置环境变量
export NVM_DIR="$HOME/.nvm"[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion
通过 nvm 安装最新版 node
nvm install node
PM2

安装pm2,服务器后台运行js脚本

npm i pm2 -g

webhook

github 的 webhook 会在当前仓库触发某些事件时,发送一个 post 形式的 http 请求

创建webhook

进入github项目仓库,按下图顺序操作

d43d73d3e3efb32d374d8e5aa45119c0.png

验证webhook配置成功,点击红色感叹号右侧内容,出现如下请求信息912328ea1df6958e632cabf532e789cb.png

docker部署

创建Dockfile

在这里,将拉取的项目存放在app目录下,Dockerfile内容如下,放到服务器根目录(/root/Dockerfile)

FROM nginxCOPY /app/docsify /usr/share/nginx/html/EXPOSE 80CMD ["nginx", "-g", "daemon off;"]

创建 http 服务器

创建index.js,放到服务器根目录(/root/index.js)

const http = require("http")const { execSync } = require("child_process")const fs = require("fs")const path = require("path")// 递归删除目录function deleteFolderRecursive(path) {    if (fs.existsSync(path)) {        fs.readdirSync(path).forEach(function (file) {            const curPath = path + "/" + file;            if (fs.statSync(curPath).isDirectory()) { // recurse                deleteFolderRecursive(curPath);            } else { // delete file                fs.unlinkSync(curPath);            }        });        fs.rmdirSync(path);    }}const resolvePost = req =>    new Promise(resolve => {        let chunk = "";        req.on("data", data => {            chunk += data;        });        req.on("end", () => {            resolve(JSON.parse(chunk));        });    });http.createServer(async (req, res) => {    console.log('receive request')    console.log(req.url)    if (req.method === 'POST' && req.url === '/') {        const data = await resolvePost(req);        const projectDir = path.resolve(`./app/${data.repository.name}`)        deleteFolderRecursive(projectDir)        // 拉取仓库最新代码        execSync(`git clone https://github.com/BKHole/${data.repository.name}.git ${projectDir}`, {            stdio: 'inherit',        })                // 创建 docker 镜像        execSync(`docker build . -t ${data.repository.name}-image:latest`, {            stdio: 'inherit',        })        // 销毁 docker 容器        execSync(`docker ps -a -f "name=^${data.repository.name}-container" --format="{{.Names}}" | xargs -r docker stop | xargs -r docker rm`, {            stdio: 'inherit',        })        // 创建 docker 容器        execSync(`docker run -d -p 88:80 --name ${data.repository.name}-container  ${data.repository.name}-image:latest`, {            stdio: 'inherit',        })               console.log('deploy success')        res.end('ok')    }}).listen(3000, () => {    console.log('server is ready')})

解析,

创建docker镜像

docker build . -t docsify-image:latest
  • build:创建 docker 镜像

  • .:使用当前目录下的 Dockerfile 文件,这里在根目录(/root/)执行

  • -t:使用 tag 标记版本

  • docsify-image:latest:创建名为 docsify-image 的镜像,并标记为 latest(最新)版本

创建docker容器

docker run -d -p 88:80 --name docsify-container docsify-image:latest
  • run:创建并运行 docker 容器

  • -d:后台运行容器

  • -p:端口指令

  • 88:80:冒号前,本地/服务器端口,冒号后,容器端口;这里意思是将当前服务器的 88 端口,映射到容器的 80 端口

  • –-name:给容器命名,便于之后定位容器,命名可选

  • docsify-image:latest:基于 docsify-image 最新版本的镜像创建容器

运行node脚本

pm2 start index.js

test

服务器运行pm2 logs查看index.js打印日志

pm2 logs

本地仓库修改文件内容,提交远程仓库,日志出现deploy success,自动化部署成功。

访问http://47.108.82.91:88,记得在云服务器上放开访问端口号

域名访问

在拥有域名的前提下,优先使用域名访问。为什么?域名当然比IP+端口号好记,且美观。

这里为了方便控制,使用nginx-proxy镜像来操作,如下操作docker会自动去镜像仓库拉取,建议服务器80端口给nginx使用,方便以后增加域名和访问端口监听。

docker run --name nginx-proxy -d -p 80:80 -v /var/run/docker.sock:/tmp/docker.sock:ro jwilder/nginx-proxy

然后绑定域名到新建容器,这里使用我的二级域名。

docker run -e VIRTUAL_HOST=libotao.nofoo.cn docsify-image

这里创建容器省略了容器名,

  • -e:设置环境变量

这时,域名已经配置好了,访问http://libotao.nofoo.cn可以看到效果。

前面每次提交内容到github,服务器都会重新拉取最新代码,新建image,销毁container,新建container,访问内容才会更新,为了实现自动化,需要改造一下上面的index.js脚本,

const http = require("http")const { execSync } = require("child_process")const fs = require("fs")const path = require("path")// 递归删除目录function deleteFolderRecursive(path) {    if (fs.existsSync(path)) {        fs.readdirSync(path).forEach(function (file) {            const curPath = path + "/" + file;            if (fs.statSync(curPath).isDirectory()) { // recurse                deleteFolderRecursive(curPath);            } else { // delete file                fs.unlinkSync(curPath);            }        });        fs.rmdirSync(path);    }}const resolvePost = req =>    new Promise(resolve => {        let chunk = "";        req.on("data", data => {            chunk += data;        });        req.on("end", () => {            resolve(JSON.parse(chunk));        });    });http.createServer(async (req, res) => {    console.log('receive request')    console.log(req.url)    if (req.method === 'POST' && req.url === '/') {        const data = await resolvePost(req);        // 项目放在服务器app目录下        const projectDir = path.resolve(`./app/${data.repository.name}`)        deleteFolderRecursive(projectDir)        // 拉取仓库最新代码        execSync(`git clone https://github.com/BKHole/${data.repository.name}.git ${projectDir}`, {            stdio: 'inherit',        })                // 创建 docker 镜像        execSync(`docker build . -t ${data.repository.name}-image:latest `, {            stdio: 'inherit',        })        // 销毁 docker 容器        execSync(`docker ps -a -f "name=^${data.repository.name}-container" --format="{{.Names}}" | xargs -r docker stop | xargs -r docker rm`, {            stdio: 'inherit',        })        // 创建 docker 容器        execSync(`docker run --name ${data.repository.name}-container -e VIRTUAL_HOST=libotao.nofoo.cn  ${data.repository.name}-image:latest`, {            stdio: 'inherit',        })        console.log('deploy success')        res.end('ok')    }}).listen(3000, () => {    console.log('server is ready')})
修改后覆盖之前存放的index.js,然后重启脚本。
pm2 restart index.js
配置完成后,以后每次提交github,都会自动更新,访问域名就会看到最新的内容。

5d22cbb9b6432a5860fbb6fd51e201da.png

note:本文中使用的端口号都需要在云服务器平台创建安全组策略,放开端口

https

  • 使用 Letsencrypt 证书加密

letsencrypt-nginx-proxy-companion 是一个轻量级的代理容器,配合 nginx-proxy实现自动创建和自动更新 Let’s Encrypt 证书。

  • 下载镜像

docker pull jrcs/letsencrypt-nginx-proxy-companion
  • 启动 nginx-proxy

启动前,需要先将上面创建的nginx-proxy容器删掉,

docker rm nginx-proxy
然后创建新的nginx-proxy容器。
docker run -d -p 80:80 -p 443:443 \  --name nginx-proxy \  -v /path/to/certs:/etc/nginx/certs:ro \  -v /etc/nginx/vhost.d \  -v /usr/share/nginx/html \  -v /var/run/docker.sock:/tmp/docker.sock:ro \  --label com.github.jrcs.letsencrypt_nginx_proxy_companion.nginx_proxy \  jwilder/nginx-proxy
  • 启动需要被代理和加密的容器

docker run --name docsify-container -e VIRTUAL_HOST=libotao.nofoo.cn -e LETSENCRYPT_HOST=libotao.nofoo.cn -e LETSENCRYPT_EMAIL=abc@qq.com docsify-image:latest

设置环境变量 LETSENCRYPT_HOST LETSENCRYPT_EMAIL自动创建和更新证书,根据自己的域名和邮箱设置。当然为了实现自动化部署,脚本文件中最后一步创建容器的命令需要被上面命令替换,然后重启index脚本就ok啦,https://libotao.nofoo.cn。

总结

通过以上内容实现了前端项目docsify自动化部署+https访问,了解docker管理多个服务,如何通过docker使用nginx。其实部署文档类项目用GitHub Page已经足够了,这里是为了研究学习自动化部署以及docker应用。

相关文章:

  • 如何将网站前端如何添加登录密码访问_如何将自己的网站上线到服务器端详解!...
  • python根据时间序列画折线图_如何根据时间序列金融数据画出分年对比的季节性图表...
  • tkinter.filedialog如何一次性选择多个文件_多个文档怎样一键重命名?用这款工具只需七步即可实现...
  • python同一层次的代码、缩进可以不一致_Python3缩进对逻辑的影响
  • js修改style样式_Vue之 绑定样式
  • 第二次打开不是最大_前《足球周刊》主编卢劲:法国队何能缔造半世纪以来最大决赛比分?...
  • python运行闪退_解决Opencv+Python cv2.imshow闪退问题
  • python sklearn logistic_安利一个Python大数据分析神器!
  • python模块下载失败_Python安装模块出错解决的办法(pip相关的安装)
  • angular6表格控件推荐_用Excel做张老板最爱的自动化表格,让你的工资翻一番!【Excel教程】...
  • import java.io 包下载_第37 p,模块与包的使用
  • postgresql 客户端_PostgreSQL在TPCC场景下的PK
  • openssl升级_openssl 拒绝服务漏洞通告
  • pytorch实现人脸识别_【他山之石】如何支撑上亿类别的人脸训练?显存均衡的模型并行(PyTorch实现)...
  • dubbo源码深度解析_mybatis 3.x源码深度解析(二)
  • php的引用
  • Angular 响应式表单 基础例子
  • classpath对获取配置文件的影响
  • Codepen 每日精选(2018-3-25)
  • Consul Config 使用Git做版本控制的实现
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • iOS仿今日头条、壁纸应用、筛选分类、三方微博、颜色填充等源码
  • js 实现textarea输入字数提示
  • Web标准制定过程
  • 工程优化暨babel升级小记
  • 检测对象或数组
  • 警报:线上事故之CountDownLatch的威力
  • 嵌入式文件系统
  • 最简单的无缝轮播
  • ​Base64转换成图片,android studio build乱码,找不到okio.ByteString接腾讯人脸识别
  • ()、[]、{}、(())、[[]]等各种括号的使用
  • (1)虚拟机的安装与使用,linux系统安装
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (附源码)小程序儿童艺术培训机构教育管理小程序 毕业设计 201740
  • (免费领源码)Java#Springboot#mysql农产品销售管理系统47627-计算机毕业设计项目选题推荐
  • (一)使用IDEA创建Maven项目和Maven使用入门(配图详解)
  • (转)shell调试方法
  • .NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions
  • .NET delegate 委托 、 Event 事件
  • .NET Framework杂记
  • .net 程序发生了一个不可捕获的异常
  • .net6+aspose.words导出word并转pdf
  • .NET教程 - 字符串 编码 正则表达式(String Encoding Regular Express)
  • .Net中的集合
  • .pub是什么文件_Rust 模块和文件 - 「译」
  • @ComponentScan比较
  • @Documented注解的作用
  • @Tag和@Operation标签失效问题。SpringDoc 2.2.0(OpenApi 3)和Spring Boot 3.1.1集成
  • [ Algorithm ] N次方算法 N Square 动态规划解决
  • [asp.net core]project.json(2)
  • [CISCN2021 Quals]upload(PNG-IDAT块嵌入马)
  • [JS] 常用正则表达式集(一)
  • [LeetCode]Spiral Matrix
  • [Lucene] Lucene 全文检索引擎简介
  • [Search Engine] 搜索引擎技术之网络爬虫