前端项目除了目前的纯单页应用
,还有SSR的应用
,例如 nuxt 和 nextjs,其区别在于前者是单独的前端页面部署,而后者使用了一个内部的 node 小型服务器来做到服务器页面直出的效果。
下面我们就针对这两种常见的前端项目,讲讲他们上线部署的流程。
单页应用
,一般使用一种或多种前端框架,在发布上线时,一般会打包(webpack + babel)为最优化的静态资源,然后使用单独的服务器(Nginx)来挂载该资源。
假设我们的项目叫 ops,其中 package.json 是这么写的:
"build": "react-scripts build",
在项目根目录中写一个脚本用于打包文件:build.sh:
## 打包的脚本(只展示必要步骤)
#!/usr/bin/env bash
## 接受 ENV
env=$1
## 这里可以区分开发和线上的不同env配置文件,一般是请求的资源和API的url不同
if [ $env = "pre" ];then
cp .env_pre .env
elif [ $env = "prod" ];then
cp .env_prod .env
else
exit 1
fi
# yarn打包
yarn build
# 打包Docker镜像
# 默认推送到 hub.xxx.com/ops
time=$(date "+%Y%m%d%H%M%S")
tag="$time"
App_name=ops"_""$env"
default_registry=hub.xxx.com
default_project=ops
default_user=admin
default_pwd=xxxxxxxx
dockerfile=Dockerfile
nginx_conf="./scripts/nginx_""$env"".conf"
echo $(date "+%Y-%m-%d %H:%M:%S")-开始制作镜像
docker build --platform linux/amd64 --rm --build-arg NGINX_CONF=$nginx_conf -t $app_name:latest .
echo $(date "+%Y-%m-%d %H:%M:%S")-镜像制作完成
echo $(date "+%Y-%m-%d %H:%M:%S")-开始打tag
docker tag $app_name:latest $default_registry/$default_project/$app_name:$tag
echo $(date "+%Y-%m-%d %H:%M:%S")-打tag完成
echo $(date "+%Y-%m-%d %H:%M:%S")-开始push镜像
docker login -u $default_user -p $default_pwd $default_registry
docker push $default_registry/$default_project/$app_name:$tag
echo $(date "+%Y-%m-%d %H:%M:%S")-push镜像完成
echo $(date "+%Y-%m-%d %H:%M:%S")-删除本地镜像
docker rmi $app_name:latest
docker rmi $default_registry/$default_project/$app_name:$tag
echo "执行发布脚本请输入tag:"
echo $tag
其中 docker 的登录用户名密码需要自己配置即可。Dockerfile如下:
FROM nginx:1.19.1
ARG NGINX_CONF=""
# 服务器上资源的地址
WORKDIR /data/ops
# 往目录里拷贝打包的文件夹 build,这是最关键的一步
ADD build build
# 这里在docker里执行了nginx配置,这里不使用默认配置
COPY ./scripts/run.sh ./
COPY "${NGINX_CONF}" /etc/nginx/conf.d/default.conf
EXPOSE 80
ENTRYPOINT ["sh", "./run.sh"]
上边执行的 run.sh 脚本我这里是从客户端拷贝到了服务器上,当然也可以直接写在服务器中,其作用是启动docker镜像上的 nginx(安装目录 /etc/init.d/nginx,nginx配置这里省略)。run.sh 如下:
#!/usr/bin/env bash
#start udnr web...
/etc/init.d/nginx start
其中本地的 nginx配置文件 nginx_conf="./scripts/nginx_""$env"".conf"
是用于 docker 容器内部的 nginx,覆盖默认配置。这里我配置了服务的请求路径以及开发环境和生产环境的请求API地址的反向代理,这里不过多展开:
## ./scripts/nginx_prod.conf 为例
upstream dashboard {
server 10.69.164.xxx:21001;
}
...
location / {
try_files /index.html =404;
root /data/ops/build;
}
location ~ .*.(js|gif|png|map|jpg|css|ico|html|less)$ {
root /data/ops/build;
proxy_redirect off;
expires 30d;
error_page 405 =200 http://$host$request_uri;
}
location /dashboard {
proxy_pass http://dashboard/;
proxy_set_header Host $Host;
}
build.sh 脚本执行完毕后,会生成一个挂载了项目build资源的Docker镜像,并输出了其 Tag。
刚刚的操作都是在客户端操作的(可以手动执行脚本,也可以写在CI里)。现在我们登录服务器:
ssh root@172.xx.xxx.72
cd /data/ops/
其中 deploy.sh:
#!/bin/bash
if [ ! -n "$1" ]
then
echo "You should give me a image tag."
else
echo "The image tag is $1"
oldtag=`cat .env`
## 获取上一次成功的tag,便于在打包失败时及时回退
echo "The old image tag is $oldtag"
path=$(pwd)
# 记录新的tag到容器的.env中
sed -i "s/$oldtag/TAG=$1/g" $path/.env
# docker-compose config
docker login -u ops -p 你的密码 hub.xxx.com
# 重新启动容器,因为 build 文件夹替换了,所以是最新的打包信息
docker-compose up -d
fi
现在在服务器上执行:
sh deploy.sh tag名称
此时在服务器上就启动起来了一个前端单页应用了,可以通过ip+端口号来访问了。
SSR
前端框架我们以 nextjs 为例来讲一下如何与 docker 结合部署。
假设项目的 package.json是这么写的:
"scripts": {
"build": "next build --profile",
"start": "next start",
}
nextjs部署到服务器时,需要先 yarn build,然后在打好包的 .next 同目录上执行 yarn start 以此来启动一个小型的SSR直出服务器。
明白了这个后,我们首先来打包。打包的操作除了写在shell里,也可以直接写在 Dockerfile 里:
# 安装一些linux系统必须的依赖和配置环境,并为下一步build安装node_modules
FROM node:16-alpine AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
# 拷贝打包需要的资源,执行build。默认的,build完后,当前目录下会有一个.next目录生成
FROM node:alpine AS builder
WORKDIR /app
COPY . .
COPY --from=deps /app/node_modules ./node_modules
RUN yarn build && yarn install --production --ignore-scripts --prefer-offline
# 添加用户组
FROM node:alpine AS runner
WORKDIR /app
ENV NODE_ENV production
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# 打包以后,一些配置文件在打包的目录下没有,需要手动copy
COPY --from=builder /app/next.config.js ./ # next的配置文件
COPY --from=builder /app/next-i18next.config.js ./ # 如果需要next-i18next支持,这里也需要拷贝自己在根目录放置的配置文件
COPY --from=builder --chown=nextjs:nodejs /app/public ./public # 公共资源
COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next # 打包后的源代码
COPY --from=builder /app/node_modules ./node_modules # 安装好的依赖
COPY --from=builder /app/package.json ./package.json # 包信息
可以看到 docker 是很强大的,一个文件就把依赖安装、build和资源拷贝完成了。
由于nextjs内置的node服务,直接可以 next start 启动,故不需要额外配置 nginx。可以在上边的Dockerfile 里追加启动的指令:
# 使用刚刚添加的用户
USER nextjs
# 暴露docker端口3000
EXPOSE 3000
ENV PORT 3000
# 执行 yarn start
CMD ["yarn", "start"]
到此,一个 nextjs 应用就在服务器的 3000 端口启动起来了。
文章出自:https://juejin.cn/post/7204646143543033916
作者:小肚肚肚肚肚哦