本文正在参加「金石计划 . 分割6万现金大奖」
前语
这是一篇叙述怎样依据docker
布置微前端项目的文章。本文章会先从理论角度去描绘布置方法,然后再经过代码去完成布置的逻辑和细节。
本文合适以下人群阅览:
- 刚知道
docker
,想经过实践进一步学习docker
的前端 er - 想了解怎样用
docker
布置微前端项目的前端 er
本文中所布置的微前端项目是micro-fe,来源于之前写的一篇叙述怎样接入微前端的文章给 vue-element-admin 接入 qiankun 的微前端开发实践总结 。
阅前须知:关于微前端项目的库房形式
关于微前端项目有两种库房管理形式:
- 多库房形式:主运用和子运用别离放在不同的库房里。
- 单库房形式:主运用和子运用别离放在同一个的库房里,但分隔放在不同的目录里。
本文推荐运用多库房形式管理形式。由于多库房形式比单库房形式有以下长处:
-
git
独立:- 分支管理相对简洁:不会呈现多个分支别离对应主运用和子运用的开发特性。
- 提交记载不密布:由于只要一个运用的提交而不是一切运用的提交。
-
创立
Release
或label
不混乱:假如存在创立Release
或label
的操作,则需求加上前缀去分辨不同运用,不然会呈现多个同名的版别。
-
CICD
流水线独立:只要对应当时运用的流水线,不会呈现多条对应不同运用的流水线。且假如运用单库房形式,则流水线的触发条件需求愈加复杂,不能呈现一个运用更新触发流水线导致一切运用进入CICD
环节。
在下面的布置教程中,会以多库房形式为前提来进行剖析叙述。
当依据 docker 布置单体前端项目时,咱们是怎样做的
所谓学会走才能学会跑。在学习布置微前端项目之前,咱们先了解一下怎样依据docker
布置单体前端项目。通常咱们把打包好的前端制品文件放在服务器里的nginx
上,且对nginx
的装备文件里做途径装备,用于区分获取前后端数据的恳求,如下代码所示:
server{
# 处理获取前端的恳求
location / {
# 前端制品文件寄存在 /usr/share/nginx/html 里
# 当nginx接收到途径名为/index.css的客户端恳求时,把/usr/share/nginx/html/index.css文件返回给客户端
root /usr/share/nginx/html;
index index.html index.htm;
}
# 假如途径以/api/开头,则判别该恳求是获取后端数据的恳求,因而把该恳求经过反应署理引往后端
# 当以一般途径前缀匹配时,最长途径会优先匹配,因而即便途径为/api/xx的恳求也契合上面的途径格局,也会以此处规矩进行处理
location /api/ {
# proxy_pass中定义后端的uri
# 当nginx接收到途径名为/api/login的客户端恳求时,nginx会进行反向署理的进程:
# 1. 向host为http://backend的后端宣布/api/login的恳求,
# 2. 在接收到呼应后,把呼应返回给客户端
proxy_pass http://backend/;
}
}
在装备下,nginx
处理恳求的流向作用如下所示:
那么,布置单体前端项目这一进程其实便是在服务器启动一个装备好途径分发和寄存前端制品的nginx
容器(不一定非要nginx
,看团队的挑选)。接下来经过代码来完成这一进程:
1. 供给 nginx
装备文件
在前端代码项目中,供给用于装备nginx
的装备文件nginx.conf,如下所示:
# nginx/nginx.conf
server {
listen 80;
listen [::]:80;
# 敞开日志记载
access_log /var/log/nginx/host.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /api/ {
proxy_pass: http://backend/
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
2. 供给用于生成镜像的Dockerfile
文件
Dockerfile
文件用于对前端项目生成镜像,服务器经过镜像生成容器去运转
# 新建构建阶段build-stage,指定node为根底镜像,该阶段用于生成前端制品文件
FROM node:16-alpine as build-stage
# 指定作业目录/app用于寄存前端制品,以便在COPY,RUN以及下一个构建阶段中运用。
WORKDIR /app
# 仿制package.json、package-lock.json、yarn.lock到作业目录里,COPY最终一个参数dest假如是相对途径,则会以作业目录有作为基准,仿制到WORKDIR/<dest>里
COPY package*.json yarn.lock ./
# 下载依靠
RUN yarn install
COPY . .
# 生成制品
RUN yarn build
# 新建构建阶段deploy-stage,指定nginx为根底镜像,该阶段用于装备和运转nginx
FROM nginx:stable-alpine as deploy-stage
# 把build-stage阶段中的前端制品和nginx.conf装备文件仿制到nginx的指定途径里
# 这姿态就能够设置nginx的装备文件,而装备文件中的默许匹配途径是/usr/share/nginx/html,也便是寄存前端制品的途径
COPY --from=build-stage /app/build/ /usr/share/nginx/html
COPY --from=build-stage /app/nginx/nginx.conf /etc/nginx/conf.d/default.conf
# 指定容器在运转进程时监听80端口
EXPOSE 80
# 指定在容器敞开运转时,经过运转以下命令行指令敞开nignx
CMD ["nginx", "-g", "daemon off;"]
关于上述的Dockerfile
需求提及以下注意点:
-
在
build-stage
的逻辑中,是先仿制package.json
和yarn.lock
且装置依靠后,然后再把一切项目文件仿制到作业目录。为什么不直接把一切项目文件仿制到作业目录然后装置依靠呢?这儿需求提及到
Dockerfile
的镜像以及缓存机制,篇幅较长,因而放在下面的 进阶- 1. 怎样写好Dockerfile
章节中解说。 -
关于
Dockerfile
的EXPOSE
指令,用于指定容器在运转进程中监听特定端口号。该指令不会敞开指定端口到宿主机->容器的映射表,假如要履行此操作请运用docker run -p
去手动指定。关于
EXPOSE
的描绘里更多的可看此处
3. 在宿主机构建镜像且运转容器
完成上面的操作后,在宿主机顺次履行以下过程即可布置单体前端项目:
-
拉取前端项目代码:经过
git clone
仿制前端项目代码 -
生成镜像:进入前端项目的根目录,运转命令行
docker build -t imageName .
去生成镜像。命令行中的最终一个参数用于指定Dockerfile
的位置。 -
生成容器且运转:运转命令行
docker run -d --name containerName imageName
,以对指定的镜像生成容器且在后台运转。
至此,一个单体前端项目的布置进程现已介绍完毕了。当然在实践中还要考虑其他细节,但主要操作流程现已在本节中叙述清楚了。
那么,怎样布置微前端项目呢
一个微前端项目中包括一个主运用和数个子运用。咱们能够用上节中剖析的布置单体前端方法把主运用和子运用都生成对应的nginx
容器放在服务器里,在主运用nginx
容器里装备途径规矩转发获取子运用资源的恳求。如下图所示:
客户端宣布途径前缀为/vue-app/
的恳求,则是恳求VueApp
子运用的资源;宣布前缀为/react-app/
的恳求,则是恳求ReactTSApp
子运用的资源。这些恳求都会由主运用nginx
容器做反向署理处理。
完成图中的多容器布置需求补充一些额定的细节。接下来咱们分隔主运用和子运用来列出这些额定细节:
主运用细节
1. 修正子运用的加载链接
这儿的主运用以registerMicroApps
与start
对子运用进行加载和注册,在registerMicroApps
的相关代码中用以下写法:
const isProd = process.env.NODE_ENV === "production";
registerMicroApps([
{
name: "react app",
// 假如是出产环境则用特定前缀途径,当客户端宣布该恳求时,服务器中的主运用nginx容器收到恳求会做反向署理分发到后端
entry: isProd ? `//${location.host}/react-app/` : "//localhost:3001",
container: "#app-react",
loader,
activeRule: "/app-react/index",
props: {
basepath: "/app-react/index",
},
},
{
name: "vue app",
entry: isProd ? `//${location.host}/vue-app/` : "//localhost:3002",
container: "#app-vue",
loader,
activeRule: "/app-vue/index",
props: {
basepath: "/app-vue/index",
},
},
// ...省掉其余子运用
]);
2. 结合DockerNetwork
修正nginx.conf
装备文件
在展现nginx.conf代码之前,咱们需求了解Docker Network
这一概念:当咱们成功启动一个容器时,该容器会接入到Docker Network
实例里,假如docker run
指令中有用--network
参数指定网络,则接入到指定网络里,不然默许接入到bridge network
里。咱们能够经过docker network ls
来检查当时存在哪些网络:
docker network ls
输出:
NETWORK ID NAME DRIVER SCOPE
95a79d75ba56 bridge bridge local
ebb2b72a64a9 host host local
b3fb09dbb1d0 none null local
上面三个network
是默许存在的网络,关于网络类型bridge
、none
、host
的区别可检查#network-drivers。
咱们能够经过docker network inspect networkName
指令检查指定network
中有哪些接入的容器:
docker network inspect bridge
输出:
[
{
"Name": "bridge",
// 省掉其他字段
// 在Containers里能够检查接入的容器
"Containers": {
"1e2d0761c5ad2154c7cfc2c9c912c094f3dda59623b1a992c1a66d9a14c4dedf": {
"Name": "master-app",
"EndpointID": "b206d19490336b0527496cf43c90b61befbd036b6c2d9909926124c0979458ea",
"MacAddress": "02:42:ac:11:00:04",
"IPv4Address": "172.17.0.4/16",
"IPv6Address": ""
},
"2c94c287b6ff3ec6561864cbad86f4fbd57ed39ec89a4bbf432f245887332c50": {
"Name": "vue-app",
"EndpointID": "3ee5696cd3cc0424c9246d1c4e5c1c5de76a4f638b4317a6df5fd7e0c0cb13ab",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"e2299a334abdec2a01b0caca15cacf980dc3f9c55ea54519758789c8daa3742e": {
"Name": "react-ts-app",
"EndpointID": "ec876f84a999fb7f77cd8f7185e077bb597f0a50daa850b55729b4b9662ce50a",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},
}
]
从上面的输出信息可知:
- master-app,即主运用
nginx
容器的 ip 为 172.17.0.4 - vue-app,即VueApp
nginx
容器的 ip 为 172.17.0.3 - react-ts-app,即ReactTSApp
nginx
容器的 ip 为 172.17.0.2
在同一网络中,容器能够经过 ip 相互拜访。例如,当VueAppnginx
容器以 80 端口EXPOSE
时,主运用nginx
容器可经过 172.17.0.3 拜访VueAppnginx
容器。
但在实践中,咱们偏向于把需求通讯的容器接入到自定义网络(经过docker network create networkName
指令来创立)。由于自定义网络比照于默许bridge
网络有一个长处:
User-defined bridges provide automatic DNS resolution between containers.
Containers on the default bridge network can only access each other by IP addresses, unless you use the –link option, which is considered legacy. On a user-defined bridge network, containers can resolve each other by name or alias.
引自Differences between user-defined bridges and the default bridge
引文中的意思是:自定义网络会对其接入的容器供给DNS
主动解析: 在默许bridge
网络中,容器能够经过 ip 拜访其他容器。而在自定义网络,容器能够经过容器名或者昵称(docker run --network-alias
指定)来拜访别的容器。例如:主运用nginx
容器能够经过http://vue-app
去拜访VueAppnginx
容器。
选用容器名作为域名拜访容器能够说是十分方便。并且因迭代更新导致镜像改变,然后生成新的子运用nginx
容器时,即便容器名字不变,但从头接入到网络时,容器 ip 可能会改变。此刻假如再次经过固定 ip 拜访子运用nginx
容器则会拜访失利。但采取容器名拜访就不用担心这种状况。
因而,一切微前端项目中的主运用nginx
容器和子运用nginx
容器,都接入到自定义网络里,就能够经过容器名作为域名拜访彼此。在这前提下,咱们的nginx.conf的写法如下所示:
server {
listen 80;
listen [::]:80;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/host.error.log error;
location / {
try_files $uri $uri/ /index.html;
root /usr/share/nginx/html;
index index.html index.htm;
}
# 关于途径前缀为/vue-app/的恳求,反向署理到名为vue-app的容器
location /vue-app/ {
proxy_pass http://vue-app/;
}
# 关于途径前缀为/react-app/的恳求,反向署理到名为react-ts-app的容器
location /react-app/ {
proxy_pass http://react-ts-app/;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
至此,主运用的细节现已讲完。
子运用细节
子运用方面需求在打包进程中给引入资源的途径加上公共前缀。
对此,在运用vue-cli
脚手架创立的VueApp运用中,vue.config.js装备如下所示:
const isProd = process.env.NODE_ENV === "production";
module.exports = defineConfig({
transpileDependencies: true,
// 加上公共途径前缀
publicPath: isProd ? "/vue-app/" : undefined,
//...省掉其他不变的装备
});
对此,在运用cra
脚手架创立的在ReactTSApp运用中,.rescriptsrc.js装备如下所示:
const isProd = process.env.NODE_ENV === "production";
module.exports = (
isProd ? [] : [["use-stylelint-config", ".stylelintrc.js"]]
).concat({
webpack: (config) => {
// 加上公共途径前缀
config.output.publicPath = isProd ? "/react-app/" : undefined;
//...省掉其他不变的装备
},
});
至此,子运用的细节已讲完。
动手实践:依据Githb Action
来完成CD
以布置微前端项目
为什么要挑选Githb Action
来完成CD
无妨考虑一个问题:CD
流程要放在出产机器上履行吗?
大多数公司里,会用一台机器或者集群来担任CICD
操作,在走完CICD
流程后把制品上传到出产机器上布置。由于CICD
操作是会占用机器的资源和算力的,假如CICD
流程放在出产机器上运转,毫无疑问会下降出产机器处理外部恳求的速度。因而要专门建立担任履行CICD
的机器或集群。
但关于咱们个人开发而言,手上往往只要一台自费购买的机器,并且机器装备还不怎样高,都是 1 核 1G 的(都是薅首购优惠的,薅完腾讯云就薅阿里云)。那假如咱们要在自己的低装备机器上做微前端CD
操作,一起跑主运用和好几个子运用的CD
流程,那机器机器会处于高负载的状况。我在自己的出产机器上跑自己的微前端项目(主运用+四个子运用)的CD
流程时,CPU
运用率达到 60%多,连在本地用SSH
拜访出产机器都失利。
但咱们也没有另一台的机器用于跑CICD
。在这种状况下就能够挑选Github Action
。关于公共库房和私有库房,他都能够分配机器来运转咱们的CICD
流程,咱们只需求编写yml
文件来指定CICD
流程即可。我之前写过一篇关于Github Action
的入门等级文章作为前端,要学会用 Github Action 给自己的项目加上 CICD,有兴趣的能够看看。这儿就不再介绍Github Action
的入门常识了,直接进入设计环节。
CD
的设计与完成
在设计CD
流程时,咱们需求考虑两点:
-
CD
的履行机遇 -
CD
履行什么操作
下面经过本次CD
的流程图来展现上述两点:
首要准备docker.json文件,用于定义镜像名、镜像版别和容器名:
{
"name": "react-ts-app",
"version": "0.1.9"
}
为什么不取package.json中的name
和version
字段来定义呢?由于咱们每次迭代更新而生成新版别的镜像时,都要更新版别,假如版别取自package.json里的version
,则会导致Dockerfile
中的RUN yarn install
指令对应的缓存失效。导致每次生成镜像时,即便运用依靠没改变,但由于package.json改变了导致要从头装置依靠。关于Dockerfile
的缓存细节会放到下面的 进阶 – 1. 怎样写好Dockerfile
章节中剖析。
接下来编写yml
文件对应上面的CD
流程图:
name: CD
on:
# 履行机遇设计为master分支被推送时触发
push:
branches: master
jobs:
# ReadInfo Job用于读取且导出package.json里的信息,用于下一个Job作为环境变量运用
ReadInfo:
runs-on: ubuntu-latest
outputs:
info: ${{ steps.read-info.outputs.info }}
steps:
# 拉取代码
- name: Checkout repository
uses: actions/checkout@v3
# docker.json,把其内容即json字符串注入到寄存输出成果的outputs.info
- name: Read Info
id: read-info
run: |
JSON=`cat ./$APP_PATH/docker.json`
JSON="${JSON//'%'/''}"
JSON="${JSON//$'n'/''}"
JSON="${JSON//$'r'/''}"
echo "info=$JSON" >> $GITHUB_OUTPUT
CD:
runs-on: ubuntu-latest
needs: ReadInfo
# 依据上一步ReadInfo Job中的输出成果info来注入环境变量
env:
# 容器名取自docker.json里的name
Container: ${{fromJson(needs.ReadInfo.outputs.info).name}}
# 镜像名和镜像标签取自docker.json里的name和version
Image: ${{fromJson(needs.ReadInfo.outputs.info).name}}:${{fromJson(needs.ReadInfo.outputs.info).version}}
# 镜像文件为docker.json里的name + 后缀.tar
ImageFile: ${{fromJson(needs.ReadInfo.outputs.info).name}}.tar
# 发布版别取自docker.json里的version
Release: ${{fromJson(needs.ReadInfo.outputs.info).version}}
steps:
# 拉取代码
- name: Checkout repository
uses: actions/checkout@v3
# 装置docker
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# 创立docker镜像
- name: Build Docker Image
uses: docker/build-push-action@v3
with:
# 缓存类型运用gha,该类型能很好的和github action结合完成缓存镜像,更多细节可看 https://docs.docker.com/build/building/cache/backends/gha/
cache-from: type=gha
cache-to: type=gha,mode=max
tags: ${{env.Image}}
context: .
load: true
# 导出docker镜像文件
- name: Export Image as Tar
run: |
docker images
docker save ${{env.Image}} > ${{env.ImageFile}}
# 创立Release版别且上传镜像文件
- name: Create GitHub Release
id: create_release
uses: softprops/action-gh-release@v1
with:
token: ${{ secrets.PROJECT_ACCESS_TOKEN }}
tag_name: ${{ env.Release }}
name: ${{ env.Release }}
draft: false
prerelease: false
# 附上镜像文件
files: |
${{ env.ImageFile }}
# 把镜像文件上传至出产服务器
- name: Upload Image Tar to Deploy Server
# easingthemes/ssh-deploy的原理时运用rsync+ssh上传文件
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_TOKEN }}
ARGS: "-avzr --delete"
SOURCE: ${{env.ImageFile}}
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{secrets.REMOTE_USER}}
TARGET: ${{secrets.TARGET}}
# 登陆进出产服务器进行操作
- name: Run Docker Container
uses: appleboy/ssh-action@master
env:
Network: microfe
with:
host: ${{secrets.REMOTE_HOST}}
username: ${{secrets.REMOTE_USER}}
key: ${{ secrets.DEPLOY_TOKEN }}
# 过程:
# 1. 中止移除容器
# 2. 移除已有镜像
# 3. 进入寄存镜像文件的目录且把镜像文件转为镜像
# 4. 检测若不存在microfe网络,则创立microfe网络
# 5. 依据镜像创立且运转容器,且把容器接入到microfe网络
# 6. 移除镜像文件
script: |
${{ format('docker ps -q --filter "name={0}" | grep -q . && docker rm -f {0}', env.Container) }}
${{ format('docker rmi -f $(docker images -q --filter reference="{0}")', env.Container) }}
${{ format('cd {0}' , secrets.TARGET)}}
${{ format('docker load < {0}', env.ImageFile)}}
${{ format('docker network ls -q --filter "name={0}" | grep -q . || docker network create {0}',env.Network)}}
${{ format('docker run -d --name {0} --network {1} {2}',env.Container,env.Network,env.Image)}}
${{ format('rm {0}',env.ImageFile)}}
最终运转成果如下所示:
咱们也能够拜访micro-fe 的 CD 履行记载来检查其间的履行细节。
进阶
1. 怎样写好Dockerfile
本节中的内容是Docker
官网中总结出来的。假如在阅览以下内容后还想深入学习可点击阅览Optimizing builds with cache management。
关于Dockerfile
的分层机制
拿下面的Dockerfile
代码进行剖析:
FROM node:16-alpine
WORKDIR /app
COPY . .
RUN yarn install
RUN yarn build
在Dockerfile
中,FROM
指令除了指定根底镜像外,还用于声明一个新的构建阶段,构建阶段在履行完毕后都会输出一个新的镜像作为产物(下称输出镜像)。输出镜像中有多个分层,每个分层都对应一条指令。拿上述Dockerfile
代码来画图剖析如下所示:
每一个分层都是依据前一个分层进行构建的。咱们能够把整个输出镜像当作是一个寄存分层的栈。每一个分层都能够会放到缓存里,假如下一次再次履行相同的Dockerfile
代码指令时,假如缓存中有对应该指令的分层,可直接取出运用,然后越过构建分层的进程,减少构建输出镜像的时刻。但假如指令发生改变,例如,COPY
所操作的文件发生改变,会导致缓存失效,需求重构对应的分层。如下所示:
而当一个指令发成改变导致分层重构后,该指令下面的一切指令都不能运用缓存分层只能全部重构。由于缓存中的分层是依据前一分层构建。如下所示:
假如咱们在构建镜像时运用上述的Dockerfile
,在每次迭代时,由于新增修正开发代码,会让COPY . .
缓存失效然后导致对应的分层重构,继而履行yarn install
和yarn build
两个进程。但假如咱们选用下面的写法:
FROM node:16-alpine as build-stage
WORKDIR /app
COPY package*.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build
这姿态就能够在依靠不变的状况下,越过yarn install
环节,然后加速构建镜像的速度。
咱们能够比照在上一章Github Action CD
流水线下,履行RUN yarn install
指令时,不运用缓存和运用缓存的比照:
不运用缓存:耗时 79 秒
运用缓存:无须耗时,直接越过
至于更多关于怎样充分利用缓存的写法可阅览How can I use the cache efficiently?
关于上述Dockerfile
代码的进一步改善
关于以下Dockerfile
代码,还存在改善的空间:假如我只改用于装备nginx
规矩的nginx.conf
装备文件,其他的不做修正。那么是能够经过缓存机制来越过yarn install
和yarn build
环节的。
FROM node:16-alpine as build-stage
WORKDIR /app
COPY package*.json yarn.lock ./
RUN yarn install
COPY . .
RUN yarn build
FROM nginx:stable-alpine as deploy-stage
COPY --from=build-stage /app/dist/ /usr/share/nginx/html
COPY --from=build-stage /app/nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
关于上述代码,咱们能够改善成以下代码:
FROM node:16-alpine as build-stage
WORKDIR /app
COPY package*.json yarn.lock ./
RUN yarn install
# COPY . .
# 把上面的COPY换成下面几行COPY指令。以明确指定仿制除nginx以外的参加到yarn build的文件和目录
# 注意COPY指令在仿制目录时会主动解包,只仿制目录里面的子文件和子目录,因而方针目标必须是同名的目录
COPY src ./src
COPY public ./public
COPY .env .rescriptsrc.js tsconfig.json ./
RUN yarn build
FROM nginx:stable-alpine as deploy-stage
COPY --from=build-stage /app/build/ /usr/share/nginx/html
# COPY --from=build-stage /app/nginx/nginx.conf /etc/nginx/conf.d/default.conf
# 把上面的COPY换成下面的COPY指令。由于经过上面的调整,build-stage的作业目录里不存在nginx文件,需求直接从当时目录里仿制曩昔
COPY nginx/nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
上面的写法也存在着缺点,便是需求写多行COPY
指令以明确指定每一个参加生成制品的文件目录,缺一个都会导致制品失利。
为了检查上面写法的作用,我触发了下面三种状况的CD
,并得出以下实验数据:
-
package.json
或者yarn.lock
发生改变: 构建镜像时刻为2m53s,其间RUN yarn install
消耗85.5s,RUN yarn build
消耗25.6s,更多流水线细节可看此处 -
package.json
和yarn.lock
没改变,开发代码发生改变: 构建镜像时刻为1m20s,其间RUN yarn install
射中缓存无耗时,RUN yarn build
消耗40.2s,更多流水线细节可看此处 -
package.json
和yarn.lock
没改变和开发代码没发生改变,nginx.conf
发生改变: 构建镜像时刻为10s,其间RUN yarn install
和RUN yarn build
都射中缓存无耗时,多流水线细节可看此处
2. 怎样完成回滚操作
回滚是指把服务器上的项目撤回到上一个版别,用于在上线新版别的项目后,发现紧迫 bug。需求紧迫手动撤回到旧版别的情景。接下来经过Github Action
完成一个简单可用的Rollback
回滚流水线。直接附上Rollback
流水线的流程图:
接下来编写yml
文件对应上面的Rollback
流程图:
name: Rollback
on:
workflow_dispatch:
inputs:
version:
description: "choose a version to deploy"
required: true
jobs:
Rollback:
runs-on: ubuntu-latest
env:
# 指定从头生成的容器名
Container: vue3-ts-app
# 指定镜像名
Image: vue3-ts-app:${{ github.event.inputs.version }}
# 指定要拉取的镜像文件
ImageFile: vue3-ts-app.tar
# 指定要从哪个发布版别拉取镜像文件
Release: vue3-ts-app/${{ github.event.inputs.version }}
steps:
- name: Echo Input
run: |
echo "Version: $VERSION"
# 从指定的发布版别里拉取镜像文件
- name: Download ImageFile
uses: dsaltares/fetch-gh-release-asset@master
with:
version: "tags/${{env.Release}}"
file: ${{env.ImageFile}}
token: ${{ secrets.GITHUB_TOKEN }}
# 把镜像文件上传至出产服务器
- name: Upload Image Tar to Deploy Server
uses: easingthemes/ssh-deploy@main
env:
SSH_PRIVATE_KEY: ${{ secrets.DEPLOY_TOKEN }}
ARGS: "-avzr --delete"
SOURCE: ${{env.ImageFile}}
REMOTE_HOST: ${{ secrets.REMOTE_HOST }}
REMOTE_USER: ${{secrets.REMOTE_USER}}
TARGET: ${{secrets.TARGET}}
# 登陆进出产服务器进行操作
- name: Run Docker Container
uses: appleboy/ssh-action@master
env:
Network: microfe
with:
host: ${{secrets.REMOTE_HOST}}
username: ${{secrets.REMOTE_USER}}
key: ${{ secrets.DEPLOY_TOKEN }}
# 过程:
# 1. 中止移除容器
# 2. 移除已有镜像
# 3. 进入寄存镜像文件的目录且把镜像文件转为镜像
# 4. 检测若不存在microfe网络,则创立microfe网络
# 5. 依据镜像创立且运转容器,且把容器接入到microfe网络
# 6. 移除镜像文件
script: |
${{ format('docker ps -q --filter "name={0}" | grep -q . && docker rm -f {0}', env.Container) }}
${{ format('docker rmi -f $(docker images -q --filter reference="{0}")', env.Container) }}
${{ format('cd {0}' , secrets.TARGET)}}
${{ format('docker load < {0}', env.ImageFile)}}
${{ format('docker network ls -q --filter "name={0}" | grep -q . || docker network create {0}',env.Network)}}
${{ format('docker run -d --name {0} --network {1} {2}',env.Container,env.Network,env.Image)}}
${{ format('rm {0}',env.ImageFile)}}
最终的运转成果如下所示:
关于上图中Rollback
流水线的更多履行细节可看此处
跋文
这篇文章写到这儿就完毕了,假如觉得有用能够点赞保藏,假如有疑问能够直接在谈论留言,欢迎交流 。