最近用 Next.js 和 typeorm 完成了前端小白的第一个全栈项目,本文会记录我在做项目的过程中学习到的一些知识点,和遇到的那些奇奇怪怪的 Bug
Github - 献上源码地址
博客系统 - 献上预览地址,喜欢的话就留下一篇博客吧
Next.js + typerom 实践 - 博客系统(一) 初始化项目
Next.js + typerom 实践 - 博客系统(二) 初始化数据库
Next.js + typerom 实践 - 博客系统(三) 操作数据库
上篇文章中已经介绍了如果使用操作数据库,如果你已经完成了博客系统,那我们就开始部署吧
服务器配置
我直接跳过了购买服务器的阶段,主要原因是我使用的是同学的服务器,没有自己购买(又省一笔钱),所以购买服务器部分就没有和大家分享了
我使用的服务器系统是 Centos 7, 建议大家选择的时候选择 Ubantu 18,因为这个系统在网上是最多教程的
为引用单独创建一个 User
初始登录的时候,我们都是 root 用户,但是这个用户权限太高了,如果误操作的话会影响到整个系统
那么我们先来创建一个用户吧
ssh root@服务器ip // 登录服务器
adduser blog // 创建名字为 blog 的用户
su - blog // 切换为 blog 用户
SSH无密码登录
每次登录服务器都有输入密码,相信大家都觉得这样很麻烦
我们可以将自己机器上现有的 公钥 传到服务器上,登录的时候,服务器会向我们发送一段随机字符串,用户用自己的私钥加密后,再发回来。服务器用事先储存的公钥进行解密,如果成功,就允许登录
如果之前已经生成过公钥(比如登录 github),就可以直接上传了
ssh-copy-id root@ip
ssh-copy-id blog@ip
如果没有生成过公钥的话,那就先用 ssh-keygen
来生成吧
配置环境
在部署的过程中,我们需要用到 docker、node、yarn 所以这些应用我们要先按照好
docker
按照 docker 官网说明依次运行命令即可:
sudo apt-get update
sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -sudo apt-key fingerprint 0EBFCD88
sudo add-apt-repository \
"deb [arch=amd64] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) \
stable"
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io
这个时候 blog 用户还没有权限使用 docker ,我们需要给 blog 用户添加权限
usermod -a -G docker blog
# 切换到 blog 用户
su - blog
node && yarn
curl -sL https://deb.nodesource.com/setup_12.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo apt-get install gcc g++ make
curl -sL https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.listsudo apt-get update && sudo apt-get install yarn
创建数据库
这个是我们项目专用的数据库,所以我们放在 blog 用户的目录里面
# 创建目录
mkdir blog-data
# 创建博客项目数据库
docker run --network=host -v /home/blog/blog-data:/var/lib/postgresql/data -p 5432:5432 -e POSTGRES_USER=blog -e POSTGRES_HOST_AUTH_METHOD=trust -d postgres:12.2
docker 化项目
我们还要给服务器配置 git ,这个相信大家都会,我就不多描述了
配置完成后,在 blog 目录下创建 app, 然后把代码放到 app 文件里面
docker 化的步骤我们可以参考这个文档
Dockerfile
Dockerfile 用于描述部署的步骤
# 使用 node v12
FROM node:12
# 程序目录
WORKDIR /usr/src/app
# 使用 yarn 安装依赖
COPY package.json ./
COPY yarn.lock ./
RUN yarn install
# 复制项目代码
COPY . .
# 重新将 ts 转为 js(这是一个大坑,后面会结算)
RUN npx babel ./src --out
# 保留端口 3000
EXPOSE 3000
# 启动代码
CMD [ "yarn", "start" ]
.dockerignore
在项目根目录创建 .dockerignore
,它的作用和 .gitignore
用法相同,忽略不需要传入 docker 的文件
node_modules
*.log
构建镜像
docker build -t blog/node-web-app .
运行镜像
# 因为我的项目,docker 容器中的 node 项目需要和 psql 通信,
# 所以添加 --network=host (后面会解释)
docker run --network=host -p 3000:3000 Marica/next-blog
这个时候,我们在服务器上使用 curl localhost:3000
如果出现了正确的 HTML,那就代表部署成功了
但是这时候直接用浏览器访问,如果失败了,那有可能是 3000 端口并没有开放
添加阿里云安全策略
为什么在 docker 化的过程中,需要重新将 ts 转为 js 呢?
首先说说,如果 docker run
之后,curl localhost3000
失败后,要怎么查找问题
我们可通过 docker logs [docker_id]
来查看日志
这是一个成功的例子,可以看到 next start 之后成功开启了服务器
再来看看,Dockerfile 中没有加 RUN npx babel ./src --out
的后果
这个是我之前遇到的问题,从报错信息来看 cannot find module 那就是某个文件找不到了
但是我们出来 .dockerignore 里面的文件外,都复制进docker容器了
于是我直接进入 docker 容器,一层一层的找这个目录,发现还真的是有,那就很奇怪了
百思不得其解
那既然文件找到了,只好看看报错的文件了 1606662298636-CreateUser.js
,进入这个文件,我找到引用 interopRequireDefault
文件的地方,终于有答案了
原来 babel
将 ts 转成 js 的时候,require 函数写得是绝对路径,那么我们将应用移到 docker 容器里面,路径自然是发生了变化,所以不用再次运行 RUN npx babel ./src --out
添加 nginx
docker 启动命令 & nginx 配置
使用 docker
启动 nginx
docker run --name nginx1 --network=host \
# 因为 nginx 无法访问到外部文件,所以需要 -v 将外部文件映射到容器内部
# 容器外 nginx.conf 映射到容器内
default.conf-v /home/blog/nginx.conf:/etc/nginx/conf.d/default.conf \-v /home/blog/app/nextjs-blog/.next/static/:/usr/share/nginx/html/_next/static/ \-d nginx:1.19.1
在项目的根目录中创建,nginx.conf
server {
listen 80; #IPV 4
listen [::]:80; #IPV 6
# 配置gzip
gzip on;
gzip_proxied any;
gzip_types text/plain text/xml text/css application/x-javascript;
gzip_vary on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_static on;
# 静态文件代理
location ~ ^/_next/static/ {
root /usr/share/nginx/html/;
}
# 将 80 端口反向代理到 3000 端口
location / {
proxy_pass http://0.0.0.0:3000;
}
}
我们用 nginx 做的三件事
动静分离
动静分离是将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问。
要理解动静分离,我们首先要知道动态资源和静态资源
- 动态资源指的是接口 api
- 静态资源指的是
HTML CSS JavaScript img
等文件
怎么实现动静分离
静态资源要与后台应用分开部署
next 框架打包的时候会帮我们把静态的文件放到 /.next/static/
下,所以我们需要将 /.next/static
文件放到docker容器的 /usr/share/nginx/html/
文件里(静态文件分开部署)
当接受到 /_next/static/
路径的请求是,就可以判断为静态文件,直接转发的/usr/share/nginx/html/
目录
如果是动态资源,则将请求转发到后台应用
server {
...
# 静态文件代理
location ~ ^/_next/static/ {
root /usr/share/nginx/html/;
}
...
}
Gzip 压缩
nginx
开启 Gzip
压缩功能后,对网站的 css、js、html
等文件进行压缩后传输,提供访问速度
server {
...
# 配置gzip
gzip on;
gzip_proxied any;
gzip_types text/plain text/xml text/css application/x-javascript;
gzip_vary on;
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
gzip_static on;
...
}
反向代理
反向代理是指先以代理服务器来接受客户端上的连接请求,然后将请求转发给内部网络上的服务器。
并将从服务器上得到的结果返回给客户端,此时代理服务器对外就表现为一个服务器。
可以使用多个 node 容器,实现负载均衡。
# 把 80 反向代理到 3000
location / {
proxy_pass http://0.0.0.0:3000;
}
--network=host
如果不使用 --network=host
的话会出现什么问题呢?
curl localhost:3000
的时候会出现 502 Bad Gateway
,这种情况下 nginx.conf 中的 localhost 有问题。 由于 nginx 是运行在 docker 容器中的,这个localhost 是容器的 localhost,而不是宿主机的localhost。
Docker容器运行的时候有host、bridge、none三种网络可供配置
bridge 默认是 bridge,即桥接网络,以桥接模式连接到宿主机:
host host是宿主网络,即与宿主机共用网络;
none none则表示无网络,容器将无法联网。
使用 host
当 docker 容器使用 host 连接方式的时候,容器与宿主共用网络,这样就能从容器中访问宿主网络了
容器中的 localhost 就等于宿主的 localhost 了
在 docker 命令中使用 --network host 来为容器配置host网络:
docker run --network=host blog/next-blog
docker run --name nginx1 --network=host
使用 host 网络不需要修改 nginx.conf,仍然可以使用 localhost,因而通用性比上一种方法好。 (但是,由于 host 网络没有 bridge 网络的隔离性好,使用 host 网络安全性不如 bridge 高。)