文章目的
通过探究virtual-event-starter-kit项目,学习NextJS
部署环境
在本地跑代码的时候需要预先配置好一些东西,如下。
配置github授权
如下图所示位置,可以获取到到ClientID、ClientSecret分别赋值给.env.local中的NEXT_PUBLIC_GITHUB_OAUTH_CLIENT_ID、GITHUB_OAUTH_CLIENT_SECRET变量。
部署到Vercel
项目中使用了100ms、DATOCMS,在运行项目前需要先配置好他们,按照项目github中的readme把项目部署到Vercel中即可。
无脑部署,在部署过程中可能会要求设置100ms,按照要求设置即可(在这个过程中需要登录100ms、DATOCMS,用自己的github账号注册、登录即可)。
可用参考:
部署到vercel、
一个后端工程师对前端云 Vercel 的体验和探索
获取配置值:
项目成功部署到Vercel后,从下图为之获取环境变量值,在.env.local文件中设置相同环境变量值。
DATOCMS
(我对DATOCMS的印象,可用来管理数据,有点类似MySQL,只不过访问方式是http请求。)
前面成功部署到Vercel后,就可以在DATOCMS中看到对应工程配置,如下图,里面存有项目需要的数据。其中Read-only API token就对应着项目的.env.local中的DATOCMS_READ_ONLY_API_TOKEN变量值,也是前面所述的部署Vercel成功后的获取配置值中的一个。
在DATOCMS中,可以从Obsah中看到项目所需的数据。
关于DATOCMS的教程可参考:Next.js+Headless CMS + GraphQL - Next.js tutorial for beginners(part2)-YouTub
与100ms的连接
浏览器输入http://localhost:3000/stage/a,Join后没有反应。
可以从项目关于HMS的readme出发得到解决策略。
(1)Token值的正确获取。
当用户输入/stage/a时,会去100ms获取token值(见components/hms/lib/getToken.ts),之后与100ms的交互要带着该token。
从源码来看,请求token的URL对应.env.local中的变量NEXT_PUBLIC_HMS_TOKEN_ENDPOINT(也是前面所述的部署Vercel成功后的获取配置值中的一个),该URL对应着平台上的Token Endpoint如下图 。
(2)设置正确的Template和Roles。
开发者可以按照Templates and Roles、HMS的readme中的指引设置正确的Template和Roles,项目中需要正确设置的Roles有viewer对应/stage/a、stage对应/stage/a?role=stage、backstage对应/stage/a?role=backstage见项目原代码components/hms/Room.tsx、components/hms/lib/getToken.ts,不同的Roles对应不同的使用者权限,其设置如下图:
在设置Roles之后,需要基于template需要创建Room,如下图所示,创建的Room中有对应的Roles有:
(3)在DateCMS中设置正确的ROOM ID
平台设置好ROOM后,可以获取正确的ROOM ID如653f760ddfa3214e11aff413f4,这时就要将该ROOM ID设置在DateCMS下 Stage => Live Stage => room id,如下图:
本地安装Redies
Redies的配置见本项目的Database Providers。
按照Redies官网,无脑安装即可,见install-redis,在安装过程中可能会询问你哪些ip可以访问该redis,我设置成仅本地即127.0.0.1,安装成功后。
安装成功后,需要配置项目.env.local中的变量 。
redies安装、本地启动后默认端口是6379,.env.local中的REDIS_URL设置成localhost并不起作用,需要设置成127.0.0.1,设置后运行yarn dev重启项目,在首页注册后,起一个终端运行redis-cli,输入keys *可以看到有如下图值。
配置standalone
Next.js带有standalone配置,可以使用Next.js的“输出文件跟踪”功能减少部署项目时的规模(大小),,见如何使用“输出文件跟踪”功能将NextJs (SSR)部署到Azure?,但要求next.js版本是12.2.x以上。当前使用的是Next.js的版本是12.0.10,需要进行升级yarn add next@12,
之后运行yarn build报错Can't resolve 'react-dom/client...,本项目解决策略是升级react、react-dom,yarn add react@18 react-dom@18,最终next由12.0.10升级到12.3.4,react由18.0.0-rc.0升级到18.2.0,react-dom由18.0.0-rc.0升级到18.2.0
升完级后,next.config.js中添加standalone,如下所示:
module.exports = {
output: 'standalone',
images: {
domains: [
'www.datocms-assets.com',
'a.storyblok.com',
'images.ctfassets.net',
'images.prismic.io',
'cdn.aglty.io',
'localhost' // For Strapi
],
imageSizes: [24, 64, 300]
},
};
Next.js官网关于standalone的介绍:Automatically Copying Traced Files
部署项目到阿里云
云主机购买与配置
购买阿里云:阿里云主机购买及配置Linux服务器(centos7.5)详细步骤(建议买成最低的配置、按量付费的)
配置安全组:阿里云配置安全组(如果不知道该配置哪些端口,入方向、出方向都可以配置成全部的,但为了安全仅在调试代码时才开机)
安装环境:CentOS 7.2 下安装配置Node.js和Yarn
注意:在阿里云中启动项目,可能会遇到next.js的SWC无法编译的问题,这时需要切换到babel,在项目根目录下创建一个.babelrc文件,文件内容{ "presets": ["next/babel"] }。
见参考Unsupported Features、Opted out of SWC feedback #30174。
更改github授权
github授权整体流程见本文前面的章节。这里由于部署到了阿里云,因此要更改Authorization callback URL,由localhost:3000更改到阿里云服务器ECS的公共IP地址。
云服务器ECS的公共IP:
允许网站打开摄像头和麦克风
问题:由于浏览器限制只能由localhost或https才可以访问摄像头见navigator.mediaDevices is undefined。
解决策略:
- 策略1:手动解禁浏览器的限制。
- 策略2:给网站配置https域名。
由于申请到域名后要进行IPC备案,本文最终采用手动解禁浏览器的限制的策略。
手动解禁浏览器的限制
解禁策略见:如何让Chrome浏览器允许http网站打开摄像头和麦克风
注意在chrome://flags/中更改完配置后,需要点击Relaunch按钮更新下才能生效。
本文为了每次访问时不再输入端口号,在阿里云中安装了nginx,在文件/etc/nginx/nginx.conf中把80端口重定向到了3000端口,配置见下图(项目启动端口默认是3000,注意阿里云的安全组要配置入方向80端口可以访问)。
配置https域名
不行呀,所有的都配置好后,访问网站时,提示要进行IPC备案才能访问网站,备案需要很多证书,而且审核时间要好几天,就没有动力再研究下去了。
这里只记录到IPC备案。
配置阿里云ECS的https域名,流程如下:
(1)首先要有域名,这里推荐在阿里云里申请一个(找最便宜的一个就可以),一定要实名认证否则配置不了DNS,具体流程可以在网上搜一下,教程有很多。
域名申请成功后一定要同步DNS如下图,否则域名解析的时候会一直是域名的DNS信息未查询到的状态,如下图。
也有免费申请域名的,但感觉不太靠谱:免费域名与阿里云服务器绑定
(2)配置DNS。
完成DNS配置后,用户就可以通过域名来进行访问了(切记是http+域名请求)。
配置DNS的流程如下图所示,点击解析按钮跳转到 云解析DNS/域名解析/解析设置,添加ipv4的记录:
(3)参考阿里云域名配置HTTPS或阿里云免费SSL证书申请入口及流程(白嫖指南)教程配置HTTPS。
补充:
- 安装Nginx,并配置好80、443端口,配置Nginx为开机自启动。Centos 7下安装配置Nginx
- 上传SSL服务证书到阿里云服务器。由于申请到的SSL证书后是下载到本地的,因此需要使用工具把证书上传到阿里云服务器,本文选择使用终端自带的文件传输,具体见mac如何利用自带的终端上传文件到指定的服务器。
- 可以使用命令
nginx -t查看nginx.conf的安装位置,见Linux 查看 nginx 安装目录和配置文件路径。 - 在启动命令
./nginx -s reload重启nginx时,要进入sbin目录下。
(4)阿里云的IPC备案。
进行备案前要准备好材料,而且备案审核时间有些长,建议开发者在上线之前就申请好IPC备案。
阿里云怎么备案的可以见:IPC备案
从ICP备案前准备概述来看ECS实例备案还有门槛,如下图。
VSCode远程连接阿里云主机
远程连接后,开发很爽。。。。。
可以先把项目代码放在阿里云主机上,在云主机上配置好运行环境,之后使用本地VSCode远程连接云主机,本地VSCode可以直接修改云主机上的代码,本地VSCode也是利用云主机环境运行代码。
可参考:使用VS Code连接远程服务器、真香!使用VSCode远程开发调试
连接成功后,如下图,可以看到VSCode打开的文件、控制台的环境都是远程主机的,此时修改文件可直接保存到远程主机上。
配置Docker
Docker安装与阿里云官方镜像加速
参考阿里云教程即可完成:
-
阿里云镜像仓库:拉取和推送Docker镜像(要先在「容器镜像服务」中创建好"个人实例"或"企业版",本文创建的是个人实例,注意个人实例是有使用限制的,如命名空间最多只能创建3个,要珍惜使用)
Dockerfile与镜像
next.js官网教程:Deploying-Docker Image
Dockerfile知识补充:使用Dockerfile定制镜像、Dockerfile指令详解
(1) Dockerfile
next.js官网所给的Dockerfile示例见examples/with-docker/Dockerfile,本项目参考该Dockerfile示例进行修改,最终使用的Dockerfile如下:
FROM node:14.21.3-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
# If using npm comment out above and use below instead
# RUN npm run build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Copy the scripts which run to set process.env.* and etc before the server.js
COPY --from=builder --chown=nextjs:nodejs /app/entrypoint.sh ./entrypoint.sh
COPY --from=builder --chown=nextjs:nodejs /app/scripts ./scripts
COPY --from=builder --chown=nextjs:nodejs /app/.env.local ./.env.local
# set the entrypoint.sh can be exec for its owner, here is nextjs can exec it.
RUN chmod 766 ./entrypoint.sh
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
# set hostname to localhost
ENV HOSTNAME "0.0.0.0"
ENTRYPOINT ["./entrypoint.sh"]
CMD ["node", "server.js"]
对于本Dockerfile,相关的有:
-
可以看出其
将编译环境和运行环境进行了分离,减少了最终构建出的镜像的体积,见:Dockerfile多From用法(非常值得一看) -
针对DockerFile中From的基础镜像,可以从DockerHub查询,如本项目中使用的是node:14.21.3-alpine。考虑到不同node版本体积的大小(如下图),这里采用
alpine版本,不同node镜像的区别可见nodejs/docker-node,同时To add the missing shared libraries to your image, adding thelibc6-compatpackage in your Dockerfile is recommended:RUN apk add --no-cache libc6-compat。
-
Dockerfile在runner阶段,使用命令
RUN adduser --system --uid 1001 nextjs创建了用户nextjs,该用户属于系统用户,其无法登录、权限较小,最为安全,可参考Linux系统中的超级用户,普通用户,特殊用户(特殊用户)3种类型。 -
建议在构建镜像前,弄清楚Context的概念,可参考镜像构建上下文(Context)。
-
本文使用Next.js的“输出文件跟踪”功能减少部署项目时的规模,即standalone,但在运行生成的镜像时,发现.env.local中配置的环境变量并没有生效,因此最终决定自己写脚本来配置环境变量。参考的有Next.js+Docker+Github Actions实现自动部署、next.js/discussions…。
- 脚本
entrypoint.sh如下,执行./scripts/envConfig.js,其作为Dockerfile的ENTRYPOINT,在ENTRYPOINT之前,还需要给系统用户nextj赋权RUN chmod 766 ./entrypoint.sh,使nextjs有权限执行entrypoint.sh文件。同时需要注意,在Dockerfile中当指定了ENTRYPOINT后,CMD的含义就发生了改变,不再是直接的运行其命令,而是将CMD的内容作为参数传给ENTRYPOINT指令,见ENTRYPOINT入口点。本文采用Execute a script before CMD中的策略,通过在entrypoint.sh的最后一行加入exec "$@"使ENTRYPOINT后的CMD生效。
#!/bin/sh
# entrypoint.sh文件
# 设置环境变量
node ./scripts/envConfig.js
# Hand off to the CMD
# cf https://stackoverflow.com/questions/42857897/execute-a-script-before-cmd
exec "$@"
- scripts/envConfig.js文件内容如下,其使用了dotenv包,本项目已经安装了
dotenv@8.6.0,可以直接用。如果项目中没有安装dotenv,可以在制作Dockerfile镜像前,在项目中运行yarn add dotenv,在package.json、yarn.lock文件中添加对应内容,之后在Dockerfile中最后的运行阶段,添加COPY --from=builder --chown=nextjs:nodejs /app/node_modules/dotenv ./node_modules/dotenv,来把已下载安装好的dotenv复制到对应位置。
// scripts/envConfig.js文件
const dotenv = require('dotenv');
const path = require('path');
const envFileAbsolutePath = path.join(__dirname, '../.env.local');
dotenv.config({path: envFileAbsolutePath});
(2) 构建镜像
首先确认已经在项目中使用了standalone,具体可见本文的前面的配置standalone章节。
构建镜像时遇到的问题和构建流程如下:
- 注册时的Cookie问题。
pages/api/register.ts中注册时,尝试通过http请求头Set-Cookie设置Cookie,但在浏览器中没有生效,原因在于http请求头中Secure属性在product环境下置为true了,而我们发起的时http请求而非https请求。
文件pages/api/register.ts
修改前:
// Save `key` in a httpOnly cookie
res.setHeader(
'Set-Cookie',
cookie.serialize(COOKIE, id, {
...
secure: process.env.NODE_ENV === 'production',
...
})
);
修改后:
res.setHeader(
'Set-Cookie',
cookie.serialize(COOKIE, id, {
...
secure: false,
...
})
);
- Dockerfile构建镜像
Dockerfile写好后,放在项目的根目录下,运行docker build -t name:version .命令来构建镜像,如本项目的命令docker build -t virtual-event:beta.1 .。注意其中的.不能丢,原因见镜像构建上下文(Context)。 - 打包结束后,可以运行
docker image ls命令查看本地镜像,如下图,可以看到使用standalone+node:14.21.3-alpine大小由979MB减少到186MB,很好。
(3)本地镜像上传、下载到阿里云
按照阿里云镜像仓库:拉取和推送Docker镜像的教程,创建命名空间、镜像仓库,之后进入镜像仓库可以看到阿里云官网,关于推拉docker镜像的命令,按照提示可以将打包好的镜像PUSH到刚创建的镜像空间。
$ docker login --username=*** registry.cn-beijing.aliyuncs.com
$ docker tag [ImageId] registry.cn-beijing.aliyuncs.com/***/***:[镜像版本号]
$ docker push registry.cn-beijing.aliyuncs.com/***/***:[镜像版本号`
可以运行以下命令拉取镜像到本地:
docker pull registry.cn-beijing.aliyuncs.com/***/***:***
可以运行如下命令本地运行镜像为容器:
docker run -p 3000:3000 imageName:tag
有用的链接
目前处于写作中,文章简要记录笔记。
React Aria
感觉类似Ant Design那样的UI库,但可以在服务端使用。
React Aria、SSRProvider、Server Side Rendering(重点理解SSRProvider添加id的目的)
在pages/_app.tsx中,使用到了React Aria中的SSRProvider、OverlayProvider可在VSCode中点击跳转到其定义处看注释。
100ms
介绍:According to the website, 100ms is an infrastructure for live apps that
enables you to build powerful live apps in hours. It provides you with a pre-built template to build virtual events, audio rooms, and classrooms with a few lines of code. The 100ms infrastructure handles all audio-video edge cases, so you don't have to.
Video and Audio conferencing in few minutes with React
项目中使用的是"@100mslive/hms-video-store@0.2.89"、"@100mslive/hms-video@0.1.43"、"@100mslive/react-sdk@0.0.6"、"@100mslive/react-icons@0.0.6",这些包都是比较老的,yarn install下载到的都是打包后的代码。
如果想要阅读其源码,可以运行yarn add @100mslive/react-sdk会把最新版本的代码下载下来,会安装hms-video、hms-video-store、react-sdk三个包,内部都有src文件夹,可以阅读原码。
通过全局搜索navigator可以看到其底层是使用WebRTC那套东西来实现的,关于WebRTC可以看WebRTC-面向网络的实时通信(非常值得一看)进行了解。
其他
SkipNavContent:provide a shortcut for keyboard and screen reader users to skip to the content.(个人感觉是将页面的『焦点』放在<SkipNavContent/>的位置,如点击Tab键可以直接跳转到SkipNavContent标签的位置,在该项目中就是页面内容位置)(后期验证下)
NProgress.js: 进度条