网站搭建总结

191 阅读4分钟

背景

搭建一个网站需要哪些准备哪些内容?

  1. 准备一台云服务器
  2. 前端、后端、数据库
  3. docker,用来更快捷的部署
  4. 域名、ssl证书,便于安全的访问

购买服务器

地址:阿里云云服务器ECS快速购买

  • 选择地区,选一个离自己最近的
  • 选择产品规格,我选了2核2G的,个人搭着玩,这个配置暂时够用了
  • 选择操作系统,目前默认的Alibaba Cloud Linux 3.2,选这个就行,千万别选一些很老的操作系统,此处点名centos7,在centos7上运行不了node.js18
  • 分配公网IPv4地址,勾选,阿里云会分配一个公网ip,就能通过这个ip访问你的服务了
  • 支付后,就会获得一个云服务器实例,

登录服务器

  1. 绑定ssh密钥。Alibaba Cloud Linux 3.2此操作系统,不允许使用密码登录,只能使用ssh密钥进行登录。参考文档,大部分问题都能在产品文档中找到,不行,就问客服。这一步会下载一个包含私钥的文件
  2. 菜单栏选择实例,点击操作列中的远程连接

  1. 选择使用ssh登录,导入在第一步得到的私钥

  1. 登录成功,进入服务器页面!

QQ_1732259420683.png

  1. 云服务器有安全组的概念,就是对外部开放哪些端口,后面部署应用时会用到

应用间的交互约定

  1. 宿主机443端口运行着nginx,由nginx根据路径分发到对应的应用中
  2. blog-app-fe博客展示前端,使用/blog前缀,运行在服务器3001端口
  3. blog-app-backend博客后台,使用/blogApi前缀,运行在服务器3003端口

部署前端应用

打包配置

assets需要配置路径/blog的路径前缀

export default defineConfig(({ mode }) => {
    const isDevelopment = mode === 'development';
    return {
        // 其他省略
        // 开发环境进行转发
        server: {
            proxy: {
                '/blogApi': {
                    target: 'http://localhost:4000',
                    changeOrigin: true,
                    rewrite: path => path.replace(/^\/blogApi/, ''), // 可选: 修改路径
                },
            },
        },
        build: {
            // 加上前缀
            assetsDir: 'blog/assets',
            rollupOptions: {
                output: {
                    manualChunks(id) {
                        // 例如,将 'lodash' 和 'axios' 拆分为独立的包
                        if (id.includes('node_modules/element-plus')) {
                            return 'element-plus'; // 生成 lodash.js
                        }
                        if (id.includes('node_modules/pdf-vue3')) {
                            return 'pdf-vue3'; // 生成 axios.js
                        }
                    },
                },
            },
        },
    };
});

打包后的index.html,这样请求这些资源就会加上blog前缀,可以通过nginx来转发到正确的路径

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <link rel="icon" href="/favicon.ico">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>影月的个人博客</title>
    <script type="module" crossorigin src="/blog/assets/index-Kd-06mGt.js"></script>
    <link rel="modulepreload" crossorigin href="/blog/assets/element-plus-B7Zl2J6f.js">
    <link rel="stylesheet" crossorigin href="/blog/assets/element-plus-DzIbptqk.css">
    <link rel="stylesheet" crossorigin href="/blog/assets/index-CnUOfbc3.css">
  </head>
  <body>
    <div id="app"></div>
  </body>
</html>

构建镜像,完成部署

编写dockerfile用来构建镜像

  1. 使用指定版本的工具进行打包,避免出现版本不兼容的情况
  2. 把打包产物放在nginx服务器上
  3. 启动nginx服务器
# 使用官方 Node.js 镜像作为构建基础镜像
FROM node:18.17.1 AS builder

# 安装 pnpm
RUN npm install -g pnpm@8.7.0

# 设置工作目录
WORKDIR /app

# 复制 package.json 和 pnpm-lock.yaml(如果有的话)到工作目录
COPY package*.json pnpm-lock.yaml* ./

# 安装应用依赖
RUN pnpm install

# 复制应用源代码到工作目录
COPY . .

# 构建应用
RUN pnpm run build

# 使用 Nginx 镜像作为生产环境镜像
FROM nginx:alpine

# 复制自定义 Nginx 配置文件
COPY nginx.conf /etc/nginx/nginx.conf

# 将构建产物从 /app/dist 复制到 Nginx 的默认服务目录
COPY --from=builder /app/dist /usr/share/nginx/html

# 暴露端口 3001
EXPOSE 3001

# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]

把镜像上传到dockerhub

# 构建镜像并上传
docker buildx build --platform linux/amd64 -t mhhongfe/blog-app-fe:20241121 --push .

# 在服务器上拉取镜像
docker pull mhhongfe/blog-app-fe:20241121

# 程序在容器内的3001端口运行,容器在宿主机的3001端口运行
docker run -p 3000:3001 mhhongfe/blog-app-fe:1.0

# 如果之前有相同端口的容器在运行,需要停止之前的容器
docker stop container_id

容器内的nginx配置

因为使用的是history api的路由,因此需要配置try files避免找不到资源

# 全局上下文
worker_processes 1;  # 设置工作进程数

# 事件块
events {
    worker_connections 1024;  # 设置每个工作进程的最大连接数
}

# HTTP 块
http {
    include mime.types;
    default_type application/octet-stream;

    # 服务器块
    server {
        listen 3001;
        server_name localhost;

        # 根目录的静态文件
        location / {
            root /usr/share/nginx/html;
            try_files $uri /index.html;  # 确保单页面应用的路由正常工作
        }
    }
}

部署后端应用

集成数据库

1. 本地安装数据库

# 安装
brew install mysql
# 启动数据库
brew services start mysql
# 登陆数据库
mysql -u root -p

2. 建表
目前博客仅有三张表,用户表、分类表、文章表

-- 创建用户表
CREATE TABLE users (
    user_id INT AUTO_INCREMENT PRIMARY KEY,
    -- 用户ID,主键,自增  
    user_name VARCHAR(50) NOT NULL UNIQUE,
    -- 用户名,非空且唯一  
    user_password VARCHAR(255) NOT NULL,
    -- 密码,非空  
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
desc articles;
-- 创建分类表
CREATE TABLE category (
    id INT AUTO_INCREMENT PRIMARY KEY,
    category_name VARCHAR(50) NOT NULL UNIQUE,
    category_desc VARCHAR(100) NOT NULL,
    create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 创建文章表
CREATE TABLE articles (
    id INT AUTO_INCREMENT PRIMARY KEY,
    -- 唯一标识,每个博客文章的自增 ID
    title VARCHAR(255) NOT NULL,
    -- 文章标题,最大长度为 255 字符
    content MEDIUMTEXT NOT NULL,
    -- 文章内容,存储中大型文本
    article_desc VARCHAR(100),
    author VARCHAR(100),
    -- 作者名称,最大长度为 100 字符
    category_id INT,
    created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    -- 文章创建时间,默认值为当前时间
    updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    -- 文章更新时间
    view_count INT DEFAULT 0 -- 浏览次数,初始为 0
);

3. express连接数据库
express中使用mysql2连接数据库

pnpm install mysql2

添加数据库配置

const mysql = require("mysql2/promise");

// 需要把这里换成真实的服务器信息
// 设置MySQL连接池
const pool = mysql.createPool({
    host: "searverIp",
    port: 3306,
    user: "root",
    password: "xxxxxx",
    database: "xxxxx",
    waitForConnections: true,
    connectionLimit: 10, // 连接池中允许的最大连接数
    queueLimit: 0, // 队列中允许的最大请求数,0表示无限制
});

module.exports = pool;

在接口中访问数据库

var express = require("express");
const pool = require("../config/dbConfig");
var router = express.Router();

router.get("/list", async (req, res) => {
    let connection;
    try {
        // 从连接池中获取一个连接
        connection = await pool.getConnection();
        const sqlQuery = `SELECT * FROM category`;
        const [rows, fields] = await connection.execute(sqlQuery);
        res.status(200).json({
            code: 200,
            msg: "查询成功",
            data: transformArray(rows),
        });
    } catch (err) {
        // 处理错误
        console.error("Error executing query:", err);
        res.status(500).json({ error: "Internal Server Error" });
    } finally {
        // 确保连接被释放回连接池(如果它仍然存在)
        if (connection) {
            connection.release();
        }
    }
});

服务器上连接数据库

在服务器上安装mysql,参考在Linux实例中安装MySQL数据库,登陆、建表过程如上节,不再赘述。

express应用部署在容器中,mysql部署在宿主机上,可能会导致连接不上,需要开放mysql被连接的权限

  1. mysql默认只能被自身ip连接,需要开放被外部ip连接,在/etc/my.cnf文件上修改

    [mysqld]
    bind-address = 0.0.0.0
    
  2. 授予特定用户从指定ip访问数据库的权限

    # 如果没有用户,需要先创建用户 
    CREATE USER 'app_user'@'your_container_ip' IDENTIFIED BY '@Hmh6861860@'; 
    # 授予这个用户对数据任意操作的权限,登陆mysql后操作,在>mysql>下进行 
    GRANT ALL PRIVILEGES ON your_database.* TO 'app_user'@'your_container_ip'; 
    FLUSH PRIVILEGES;
    
  3. 开放安全组 允许容器ip访问3306端口

编写dockerfile

# 使用官方 Node.js 镜像作为构建基础镜像
FROM node:18.17.1 AS builder

# 安装 pnpm
RUN npm install -g pnpm@8.7.0

# 设置工作目录
WORKDIR /app

# 复制 package.json 和 pnpm-lock.yaml(如果有的话)到工作目录
COPY package*.json pnpm-lock.yaml* ./

# 安装应用依赖
RUN pnpm install

# 复制应用源代码到工作目录
COPY . .

# 暴露端口 3003
EXPOSE 3003

# 启动应用程序
CMD ["npm", "run", "start"]

使用nginx转发应用

宿主机上的nginx配置

server {
    listen 443 ssl;
    location /favicon.ico {
        proxy_pass http://localhost:3001/favicon.ico;  # 转发到宿主机的 3001 端口
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /blog/ {
        proxy_pass http://localhost:3001/blog/;  # 转发到宿主机的 3001 端口
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    location /blogApi/ {
         # 去除路径前缀 /blogApi
         rewrite ^/blogApi(/.*)$ $1 break;

         # 将请求转发到本地的 3003 端口
         proxy_pass http://localhost:3003;

         # 保留请求头信息
         proxy_set_header Host $host;
         proxy_set_header X-Real-IP $remote_addr;
         proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

使用https访问网站

使用https的前提是拥有域名和ssl证书

  1. 购买域名
  2. 配置dns解析,关联ip和域名
  3. 购买ssl证书,这一步能拿到ssl证书密钥文件
  4. 上传密钥到服务器,通过vscode连接服务器,可以直接把本地文件拖动到服务器上
  5. nginx配置ssl信息
server {
    listen 443 ssl;
    server_name www.xxx.com;  # 这里使用你的域名

    # SSL 证书和私钥路径
    ssl_certificate /etc/ssl/certs/personal-site.pem;
    ssl_certificate_key /etc/ssl/certs/personal-site.key;

    # 启用 SSL 协议
    ssl_protocols TLSv1.2 TLSv1.3;
}

参考