缘起
工作以来,我对服务器部署、docker、nginx 这些东西只有一些模糊的印象(面试背的答案)。
但对一个程序员来说,这些又是必会的东西。所以买了服务器,部署了一个简单的 node 项目。
详细地记录下来,希望可以帮助到像我一样服务器小白的同学。
以后和面试官、后端同学聊服务器,就不会一脸懵逼了 =.=
购买服务器
我找到的便宜服务器是 阿里云云服务器ECS。
选择按量付费 - 共享型, 个人项目选最低配就行啦。不使用的时候关机,100 元就可以用很久。
建议大家选择 Ubantu 18,因为这个系统是网上教程最多最全的。
服务器配置
为应用单独创建 user
root 用户权限最高,如果误操作会影响整个系统。
所以第一步,就是为自己的应用创建单独的用户。
- 用密码登录服务器
ssh root@121.36.50.175
- 添加新用户
adduser blog
- 切换为 blog
su - blog
blog 用户的根目录是 /home/blog,root 是 /
使用 ssh 登录服务器
我们每次登录服务器都需要输入密码,太麻烦了。
我们给服务器上传 ssh pub key,使用 ssh 的方式登录服务器,就不用输入密码啦。
上传 ssh pub key
如果之前生成过 ssh pub key(比如登录 github),直接上传即可。
ssh-copy-id root@ip
ssh-copy-id blog@ip
配置环境
在命令行中输入 ssh root@你的 ip,用 root 登录服务器,安装软件 docker、node。
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
node 的安装步骤也一样,一步一步运行即可。
最后顺便安装下 yarn
# Using Ubuntu
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.list
sudo apt-get update && sudo apt-get install yarn
创建数据库 postgres
这是我的项目专用的数据库,放在 ~/blog-data 目录下
# 创建目录
mkdir blog-data
# 回到代码
cd app/nextjs-blog
# 创建博客项目数据库
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 化我们的 node 项目
下面回到我们的代码,将我们的应用 docker 化,并上传到 docker hub 上。
参考 node 官方文档。
Dockerfile
在根目录创建 Dockerfile:
# 使用 node v12
FROM node:12
# 程序目录
WORKDIR /usr/src/app
# 使用 yarn 安装依赖
COPY package.json ./
COPY yarn.lock ./
RUN yarn install
# 根目录所有代码拷贝到 /usr/src/app
COPY . .
# 保留端口 3000
EXPOSE 3000
# 启动代码
CMD ["yarn", "start"]
.dockerignore
在根目录创建 .dockerignore,它和 .gitignore 用法一样,忽略不需要上传的文件。
node_modules
*.log
构建自己的镜像
docker build -t Marica/node-web-app .
上传镜像
- 首先在 Docker Hub 上注册一个账号: 官网地址:https://hub.docker.com/
- 控制台使用
docker login登录账号 - 上传
docker push <hub-user>/<repo-name>
docker push Marica/next-blog
在服务器上拉取镜像
接下来回到服务器,我们直接在服务器上拉取 docker 镜像就好啦,非常方便。
# 因为我的项目,docker 容器中的 node 项目需要和 psql 通信,
# 所以添加 --network=host(后面会详细说明)
# --network=host 会导致端口映射失效,端口直接就是阿里云机器的端口,但这种模式比较容易理解
docker run --network=host -p 3000:3000 Marica/next-blog
添加阿里云安全策略
最后,添加阿里云安全策略,开放 80 端口。
添加 nginx
docker 启动命令 & nginx 配置
使用 docker 启动 nginx:
(后面会解释为什么使用 --network=host)
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
app/nextjs-blog 目录下创建 nginx.conf
server {
listen 80; # IPV 4
listen [::]:80; # IPV 6
server_name localhost;
# 配置 gzip
gzip on;
gzip_disable "msie6";
gzip_comp_level 6;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_proxied any;
gzip_types
text/plain
text/css
text/js
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
image/svg+xml/javascript;
# 静态文件代理
location ~ ^/nextjs-blog/_next/static/ {
root /usr/share/nginx/html/;
expires 30d;
}
# 把 80 反向代理到 3000
location / {
proxy_pass http://0.0.0.0:3000;
}
}
我们用 nginx 做这三件事:
1. 动静分离:
什么是「动态资源」?什么是「静态资源」?
- 动态资源指「接口」等后端资源,静态资源是
HTML,JavaScript,CSS,img等前端文件。 怎么实现「动静分离」? - 如果是「静态资源」的请求,就直接到
nginx配置的静态资源目录下面获取资源。 - 如果是「动态资源」的请求,
nginx利用反向代理的原理,把请求转发给后台应用去处理,从而实现动静分离。
在使用前后端分离之后,可以提升静态资源的访问速度。
因为 nginx 直接响应文件,而 node 还需要先读取静态文件再响应。
server {
# ...
location ~ ^/nextjs-blog/_next/static/ {
root /usr/share/nginx/html/;
expires 30d;
}
# ...
}
2. Gzip 压缩
Nginx 开启 Gzip 压缩功能, 对网站的 css、js、xml、html 等文件压缩,提高访问速度。
在用户接收到返回内容之前对其进行处理,以压缩后的数据展现给客户。
可以节约大量的出口带宽,提高传输效率。
server {
# ...
gzip on; # 开启gzip模块
gzip_disable "msie6"; # 指定哪些不需要 gzip 压缩的浏览器( IE5.5 和 IE6 SP1 使用 msie6 参数来禁止gzip压缩 )
gzip_comp_level 6; # 设置 gzip 压缩等级,等级越低压缩速度越快文件压缩比越小,反之速度越慢文件压缩比越大;等级1-9,最小的压缩最快 但是消耗cpu
gzip_min_length 1100; # 设置允许压缩的页面最小字节,当返回内容大于此值时才会使用gzip进行压缩, 当值为0时,所有页面都进行压缩。建议大于1k
gzip_buffers 16 8k; #设置gzip申请内存的大小
gzip_proxied any;
# nginx做为反向代理时启用, 根据某些请求和应答来决定是否在对代理请求的应答启用gzip压缩
# off(关闭所有代理结果的数据的压缩)
# expired(启用压缩,如果header头中包括"Expires"头信息)
# no-cache(启用压缩,header头中包含"Cache-Control:no-cache"),
# no-store(启用压缩,header头中包含"Cache-Control:no-store"),
# private(启用压缩,header头中包含"Cache-Control:private"),
# no_last_modefied(启用压缩,header头中不包含 "Last-Modified"),
# no_etag(启用压缩,如果header头中不包含"Etag"头信息),
# auth(启用压缩,如果header头中包含"Authorization"头信息)
# any - 无条件启用压缩
gzip_types # 压缩类型
text/plain
text/css
text/js
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
image/svg+xml/javascript;
# ...
}
3. 反向代理
反向代理是指先以代理服务器来接受客户端上的连接请求,然后将请求转发给内部网络上的服务器。
并将从服务器上得到的结果返回给客户端,此时代理服务器对外就表现为一个服务器。
可以使用多个 node 容器,实现负载均衡。
# 把 80 反向代理到 3000
location / {
proxy_pass http://0.0.0.0:3000;
}
--network=host
最后,回到之前遗留的问题,为什么使用 --network=host。
参考这位大佬的文章。
不使用 host
这种情况下 nginx.conf 中的 localhost 有问题。 由于 nginx 是运行在 docker 容器中的,这个localhost 是容器的 localhost,而不是宿主机的localhost。
host vs brige
Docker 容器运行的时候有 host、bridge、none 三种网络可供配置。
默认是 bridge,即桥接网络,以桥接模式连接到宿主机:
host是宿主网络,即与宿主机共用网络;
none则表示无网络,容器将无法联网。
使用 host
当容器使用 host 网络时,容器与宿主共用网络,这样就能在容器中访问宿主机网络, 那么容器的 localhost 就是宿主机的 localhost。
在 docker 命令中使用 --network host 来为容器配置host网络:
docker run --network=host Marica/next-blog
docker run --name nginx1 --network=host
上面的命令中,没有必要像前面一样使用 -p 80:80 映射端口,是因为本身与宿主机共用了网络,
容器中暴露端口等同于宿主机暴露端口。
使用 host 网络不需要修改 nginx.conf,仍然可以使用 localhost,因而通用性比上一种方法好。
(但是,由于 host 网络没有 bridge 网络的隔离性好,使用 host 网络安全性不如 bridge 高。)
一键部署
最后,根据自己的项目添加部署脚本 deploy.sh。
我的脚本是: (我选择直接在服务器上 docker 化 node 项目,省去了上传 docker hub 这一步,因为上传还挺废时间的)
#!/usr/bin/env bash
docker start 54f &&
cd /home/blog/app/nextjs-blog/ &&
git pull &&
yarn install --production=false &&
yarn build &&
yarn compile &&
yarn m:run &&
git reset --hard HEAD &&
docker build -t sunnyla/node-web-app . &&
docker kill app &&
docker rm app &&
docker run --name app --network=host -p 3000:3000 -d sunnyla/node-web-app &&
echo "ok!"
使用 SSH 在远程服务器上运行本地 Shell 脚本:ssh blog@dev1 'bash -s' < deploy.sh。
就可以实现一键部署啦!
作者水平有限,文章中如果有错误恳请各位大佬指出,感谢~!