Next.js + Typerom 实践 - 博客系统(四) 部署

2,045 阅读8分钟

最近用 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 高。)