一、静态站点部署(前端)
如果你要部署到github pages或者你不用SSR(服务端渲染),那就构建(SSG)静态站点
配置
nextjs配置SSG(静态站点)next.config.mjs
,其他前端框架如vue\react按照官网打包配置,在本地打包一下看输出的目录是啥
const nextConfig = {
output: "export",
};
export default nextConfig;
github设置服务器需要的账号密码等信息
设置工作流
项目根目录创建.github\workflows\main.yml
# 定义一个名字
name: deploy-website
# 指定在main分支push时执行
on:
push:
branches: [main]
# 指定任务
jobs:
npm-build:
name: npm-build工作
runs-on: ubuntu-latest # 指定操作系统
# 步骤
steps:
# uses指定用什么包
# name指定名称
# run指定操作
- name: checkout code
uses: actions/checkout@v4.1.7
- name: install nodejs
uses: actions/setup-node@v4.0.3
with:
node-version: "20.x"
- name: instal deps
run: npm install # 安装依赖
- name: build app
run: npm run build # 构建应用
- name: copy dist file with ssh
uses: easingthemes/ssh-deploy@main # 每个插件参数不一样,需要看文档
# 这个文件会公开
env:
REMOTE_HOST: ${{ secrets.REMOTE_HOST }} # 指定 SSH 主机
REMOTE_USER: ${{ secrets.REMOTE_USER }} # 指定 SSH 用户名
SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY}} # 指定 SSH 密码
ARGS: "-avzr --delete" # 指定 rsync 参数
SOURCE: './out' # 源目录,也就是build构建后的目录,如果直接上传整个项目,那打包就没有意义了
TARGET: '/home/dist' # 指定远程服务器上的目标目录,需要提前创建
当你push后会自动打包并将打包后的文件复制到服务器
Docker部署Nginx
- 提前安装Docker,当然你直接装nginx也是可以的
先创建一个容器生成配置文件
docker run --name nginx -p 9000:80 -d nginx
创建目录
mkdir -p /etc/nginx/conf/
复制配置文件到宿主机
docker cp nginx:/etc/nginx/nginx.conf /etc/nginx/conf/nginx.conf
docker cp nginx:/etc/nginx/conf.d /etc/nginx/conf/conf.d
删除nginx
docker stop nginx
docker rm nginx
启动
注意最后一个挂载目录挂载
index.html
所在的目录
docker run \
-p 80:80 \
--name nginx \
--restart always \
-v /etc/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
-v /etc/nginx/conf/conf.d:/etc/nginx/conf.d \
-v /var/log:/var/log/nginx \
-v /home/dist/out:/usr/share/nginx/html \
-d nginx:latest
更新后内容没有显示需要重启nginx
docker restart nginx
此时访问你的ip端口就能看到页面了,但它是http
的,因为它没有SSL证书
使用openssl签名证书
安装openssl
apt install openssl
生成一个 2048 位的 RSA 私钥文件private.key
openssl genrsa -out private.key 2048
生成一个证书签名请求(CSR)文件 cert.csr
openssl req -new -days 3650 -key private.key -out cert.csr
填写一些必要的信息
Country Name (2 letter code) [AU]:CN
State or Province Name (full name) [Some-State]:cci
Locality Name (eg, city) []:cci
Organization Name (eg, company) [Internet Widgits Pty Ltd]:组织名称
Organizational Unit Name (eg, section) []:组织单位名称
Common Name (e.g. server FQDN or YOUR name) []:普通名字
Email Address []:邮件地址
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:密码
An optional company name []:公司名
从一个cert.csr
生成一个自签名的证书cacert.pem
openssl x509 -req -days 3650 -in cert.csr -out cacert.pem -signkey private.key
此时可以看到三个文件
- cacert.pem:私钥文件,私钥用于对数据进行签名,确保数据的完整性和真实性,并解密由对应的公钥加密的数据
- cert.csr:证书签名请求文件,包含了你的公钥和关于你身份的信息,如组织名称、通用名、国家等
- private.key:根证书文件或 CA 证书文件
ls
更改nginx配置
vim /etc/nginx/conf/conf.d/default.conf
修改端口443 ssl
,证书文件和私钥文件位置,其他的遵循openssl规范即可,可以使用这里给出的配置
每一个server块都是一个虚拟主机,你可以用多个server块在一台服务器上部署多个网站
server{
listen 80;
server_name xxx.xyz www.xxxx.xyz xxx.xxx.xyz;# 填你的域名或IP
return 301 https://$server_name$request_uri;# 将所有80的http请求重定向到443https
}
server {
listen 443 ssl;
server_name localhost;
# 证书文件
ssl_certificate /etc/nginx/cacert.pem;
# 私钥文件
ssl_certificate_key /etc/nginx/private.key;
# ssl验证
ssl_session_timeout 5m;
# 安全链接的加密协议
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
# 加密算法
ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;
# 使用服务器首选算法
ssl_prefer_server_ciphers on;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
删除容器后重新配置
docker stop nginx
docker rm nginx
启动
注意添加了端口和挂载的私钥和证书
docker run \
-p 443:443 \
-p 80:80 \
--restart always \
--name nginx \
-v /etc/nginx/conf/nginx.conf:/etc/nginx/nginx.conf \
-v /etc/nginx/conf/conf.d:/etc/nginx/conf.d \
-v /etc/nginx/conf/cacert.pem:/etc/nginx/cacert.pem \
-v /etc/nginx/conf/private.key:/etc/nginx/private.key \
-v /var/log:/var/log/nginx \
-v /home/dist/out:/usr/share/nginx/html \
-d nginx:latest
此时变成了https
,由于我们是自己签名的所以仍然会提示不安全,但我们的传输协议已经变成了https
,会对数据包进行加密
想不提示就得充值
可以看到证书信息已经有了
二、镜像部署(前后通吃)
适合SSR、前端全栈应用,还可以用Docker、k8s部署
这里虽然没有给出后端例子,go、rust、java等等只要按照你在本地部署的流程编写workflows
流程即可
- 1. 编写Dockerfile构建镜像
- 2. 本地部署
- 3. 编写workflows:登录Dockerhub、打包镜像、上传镜像、拉取镜像、删除旧容器、部署新容器
环境准备
服务器提前安装Docker
docker --version
创建Docker hub
仓库(也可以用阿里云的镜像仓库)
github actions设置
Dockerhub
创建token
github创建对应的密钥信息
用自己的服务器执行部署流程(github免费每月2000分钟额度)
这里用我们自己的服务器指定github actions后续部署命令,你也可以把打包和执行流程都放在自己服务器上
如果执行config.sh
报错Must not run with sudo
RUNNER_ALLOW_RUNASROOT=true ./config.sh
启动服务
sudo ./svc.sh install
sudo ./svc.sh start
此时可以看到服务器已经加入runner了
在工作流中指定即可用自己的服务器托管
runs-on: self-hosted
配置
项目根目录新建Dockerfile
FROM node:alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm","run", "start"]
- FROM 指定基础镜像
- WORKDIR 设置工作目录
- RUN 执行shell命令
- ENV 设置环境变量
- COPY 复制文件到镜像中
- USER 指定用户
- EXPOSE 暴露端口
- CMD 容器启动时的执行命令,docker run的参数会覆盖CMD指定的参数,多个CMD,只有最后一个CMD才多个
- ENTRYPOINT 容器启动时的入口点命令,docker run的参数不会覆盖ENTRYPOINT指定的参数
本地部署
先在本地试一下打包正不正常,npm run build
,例如把@
转换为./
,语法降级等操作在这一步先完成,在.next
文件夹下就是打包后的文件
docker build -t nextjs-tgm .
创建容器,此时直接访问http://localhost:3000/
就可以看到你的项目
docker run -d \
--restart always \
--name nextjs-tgm \
-p 3000:3000 \
nextjs-tgm
停止删除
docker stop nextjs-demo
docker rm nextjs-tgm
设置
项目根目录.github\workflows\main.yml
# 定义一个名字
name: deploy-website
# 指定在main分支push时执行
on:
push:
branches: [main]
# # 你也可以指定打标签的时候触发构建
# # tags:
# # - 'v*' # 当有以 'v' 开头的 tag
# 指定任务
jobs:
build:
name: build 工作
runs-on: ubuntu-latest # 指定操作系统
# 步骤
steps:
# uses指定用什么包
# name指定名称
# run指定操作
- name: checkout code
uses: actions/checkout@v4.1.7
- name: Login to Docker Hub # 登录Docker Hub
run: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
- name: Build and push Docker image
run: docker build -t vcciccv/nextjs-app . # 指定镜像名字
- name: Publish image to docker hub
run: docker push vcciccv/nextjs-app:latest # 推送镜像到Docker Hub
deploy:
needs: build # 前一项执行结束再执行这一项
runs-on: self-hosted # 指定在自己的服务器上执行
steps:
- name: Login to Docker Hub # 登录Docker Hub
run: echo "${{ secrets.DOCKERHUB_TOKEN }}" | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
- name: Pull image from docker hub
run: docker pull vcciccv/nextjs-app:latest # 拉取镜像到本地
- name: Delete old container
run: docker rm -f nextjs-app # 删除旧容器
- name: Run docker container
run: docker run -d -p 3000:3000 --name nextjs-app --restart always vcciccv/nextjs-app:latest # 运行镜像
CICD
推送mian
分支的代码就可以看到工作流了
在你的Docker hub也有镜像
nginx设置
当然你也可以不用
nginx
,将端口映射到80
(http)或443
(https),人生苦短何必为难自己,此时通过ip:3000已经可以访问
这时候需要将nginx
加入自定义网络,自定义网络可以通过容器名通信
# 列出所有网络
docker network ls
# 创建网络
docker network create pub-network
# 删除网络
docker network rm pub-network
# 将一个容器连接到一个网络
docker network connect pub-network my_container
# 将一个容器从一个网络断开
docker network disconnect pub-network my_container
在.github\workflows\main.yml
的docker run当中也要加上这段
或者用上面给出的命令直接加入网络,但这里仍然要配置,下次就不用手动加了
--network pub-network
编辑配置文件
请先查看前面的nginx配置
vim /etc/nginx/conf/conf.d/default.conf
设置上游服务器,我们使用的是容器名
upstream nextjs_upstream {
server nextjs-app:3000;
}
设置反向代理
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
location / {
proxy_pass http://nextjs_upstream;
}
重启
docker restart nginx
现在实现了Nginx
反向代理,不用将3000端口暴露给公网了
Nginx优化(前端,可选)
1、 使用Nginx
缓存静态资源,这里只给出nextjs
的优化
创建缓存目录
mkdir -p /var/cache/nginx
在配置文件最上面添加
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=STATIC:10m inactive=7d use_temp_path=off;
proxy_cache_path
:定义缓存的存储路径为/var/cache/nginx
levels=1:2
:设置缓存的目录层级结构,这里表示分为两级目录keys_zone=STATIC:10m
:创建一个名为STATIC
的缓存区,大小为 10MB,用于存储缓存的键值对信息inactive=7d
:如果缓存项在 7 天内未被访问,则会被清除use_temp_path=off
:不使用临时目录来存储缓存数据
设置每个页面的缓存(仅nextjs),npm run build后.next/static
是next为每个页面构建的静态资源
location /_next/static {
proxy_cache STATIC;
proxy_pass http://nextjs_upstream;
}
指定静态资产缓存路径(放在哪里就指定哪里)
location /public {
proxy_cache STATIC;
proxy_ignore_headers Cache-Control;
proxy_cache_valid 60m;
proxy_pass http://nextjs_upstream;
}
2、 开启 gzip 压缩
文件在发送给用户之前被压缩
gzip on;
gzip_proxied any;
gzip_comp_level 4;
gzip_types text/css application/javascript image/svg+xml;
- 指定压缩的代理条件、压缩级别和要压缩的文件类型
所有配置
server{
listen 80;
server_name 域名或服务器ip;
return 301 https://$server_name$request_uri;
}
upstream nextjs_upstream {
server nextjs-app:3000;
}
server {
listen 443 ssl;
server_name localhost;
# 证书文件
ssl_certificate /etc/nginx/cacert.pem;
# 私钥文件
ssl_certificate_key /etc/nginx/private.key;
# ssl验证
ssl_session_timeout 5m;
# 安全链接的加密协议
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
# 加密算法
ssl_ciphers EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH;
# 使用服务器首选算法
ssl_prefer_server_ciphers on;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
gzip on;
gzip_proxied any;
gzip_comp_level 4;
gzip_types text/css application/javascript image/svg+xml;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
location /_next/static {
proxy_cache STATIC;
proxy_pass http://nextjs_upstream;
}
location /public {
proxy_cache STATIC;
proxy_ignore_headers Cache-Control;
proxy_cache_valid 60m;
proxy_pass http://nextjs_upstream;
}
location / {
proxy_pass http://nextjs_upstream;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}
Nginx还可以配置错误页面,可以用前面给出的SSG的方法生成
其实不想用docker还可以直接用nodejs+nginx,为了节省生命我就不弄了