[Linux基础]-17-Nginx+Git Hooks自建仓库自动部署指南

引言

本文详细讲解了 Nginx 配合自建 Git 仓库与 Git Hooks 实现静态网站全自动部署的完整教程,将本地仓库代码推送到远程服务器自建仓库中。包含 Ubuntu 服务器环境搭建、Git 裸仓库配置、post-receive 自动部署脚本编写、Nginx 安全加固与 HTTPS 证书配置全流程。本方案无需依赖 Jenkins 等复杂 CI/CD 工具,纯原生组件即可实现代码 git push 推送即网站自动上线,适合个人站长、前端开发者学习使用。

文章目录

0×1.服务器端配置

1.安装必要软件

					
					# 本文使用ubuntu作为服务器,其他发行版请替换安装命令
					sudo apt update && sudo apt install -y git nginx

					# Rockylinux
					dnf install git nginx

					# Arch
					pacman -S git nginx
					
					

2.创建目录结构

					
					# 我们需要两个目录:
					# Git 仓库目录:用于接收你本地推送的代码(裸仓库)。
					# Web 根目录:Nginx 读取文件的地方。

					# 1. 创建一个存放裸仓库的目录 (假设放在你的用户目录下)
					mkdir -p /home/kh987/www.git

					# 2. 初始化裸仓库
					cd /home/kh987/www.git
					git init --bare

					# 3. 创建 Nginx Web 目录 (如果在其他用户权限目录下,需要使用 sudo)
					sudo mkdir -p /home/hk987/www.html
					# 重要:将该目录的所有权改为你的用户,让 Git 钩子有权限写入
					# 请将下面的 $USER 替换为你 Ubuntu 登录的用户名
					sudo chown -R $USER:$USER /home/hk987/www.html
					
					

3.配置 Git 自动部署钩子(核心步骤)

					
					# 当你本地推送代码时,Git 会触发这个脚本,自动把代码同步到 Web 目录。
					# 裸仓库(/home/hk987/www.git)中不会保存任何项目代码,项目代码会根据下面这个钩子文件的指定,自动保存到WEB_DIR目录中
					# 文件名 post-receive:是固定的,绝对不能改!这是 Git 钩子(Hook)的标准命名之一。

					# GIT_WORK_TREE="$WEB_DIR" git checkout -f
					# 核心命令:这是整个脚本最关键的一行。
					# GIT_WORK_TREE="$WEB_DIR":临时设置 Git 的 “工作目录” 为网站根目录。
					# git checkout -f:强制检出(Checkout)最新的代码。
					# -f(Force):强制覆盖,不管 WEB_DIR 里有什么修改或冲突,直接用仓库里的最新版本覆盖掉,保证部署的是干净的代码。

					# 使用vim创建文件并写入内容
					vim /home/hk987/www.git/hooks/post-receive

					#!/bin/bash

					WEB_DIR="/home/hk987/www.html"
					GIT_WORK_TREE="$WEB_DIR" git checkout -f

					echo "The code was successfully deployed to $WEB_DIR"

					# 给文件添加执行权限
					sudo chmod +x /home/hk987/www.git/hooks/post-receive
					
					

4.申请与配置 Let's Encrypt HTTPS 证书

					
					# 申请证书(先将域名DNS解析指向服务器IP),再执行下面的脚本,得到证书文件,需要先安装好nginx
					vim setup_ssl.sh

					#!/bin/bash

					echo "=================="
					# 脚本名称:setup_ssl.sh
					# 功能:全自动为 Nginx 申请 Let's Encrypt 证书并配置自动续期
					# 系统:Ubuntu/Debian
					echo "=================="

					set -euo pipefail

					# --------------------------
					# 1. 权限与环境检查
					# --------------------------
					echo -e "${YELLOW}[1/6] 正在检查环境...${NC}"

					# 检查是否为 root
					if [ "$EUID" -ne 0 ]; then
					  echo -e "${RED}请使用 sudo 运行此脚本: sudo bash $0${NC}"
					  exit 1
					fi

					# 检查 Nginx 是否已安装
					if ! command -v nginx &> /dev/null; then
					    echo -e "${RED}错误:未检测到 Nginx,请先安装 Nginx。${NC}"
					    exit 1
					fi
					echo -e "${GREEN}环境检查通过。${NC}"

					# --------------------------
					# 2. 获取用户输入
					# --------------------------
					echo ""
					echo -e "${YELLOW}[2/6] 请输入配置信息:${NC}"
					read -p "请输入你的域名 (例如 example.com): " DOMAIN
					read -p "请输入你的邮箱 (用于证书过期提醒): " EMAIL

					# 简单的非空校验
					if [ -z "$DOMAIN" ] || [ -z "$EMAIL" ]; then
					    echo -e "${RED}域名和邮箱不能为空!${NC}"
					    exit 1
					fi

					# 询问是否包含 www 前缀
					read -p "是否同时为 www.$DOMAIN 也申请证书? (y/n): " INCLUDE_WWW

					DOMAIN_ARGS="-d $DOMAIN"
					if [[ $INCLUDE_WWW =~ ^[Yy]$ ]]; then
					    DOMAIN_ARGS="-d $DOMAIN -d www.$DOMAIN"
					fi

					# --------------------------
					# 3. 安装 Certbot
					# --------------------------
					echo ""
					echo -e "${YELLOW}[3/6] 正在更新软件源并安装 Certbot...${NC}"
					apt update -y
					apt install -y certbot python3-certbot-nginx
					echo -e "${GREEN}Certbot 安装完成。${NC}"

					# --------------------------
					# 4. 生成强 DH 参数 (可选但推荐,提升安全性)
					# --------------------------
					echo ""
					echo -e "${YELLOW}[4/6] 正在生成强加密参数 (这一步可能需要几分钟,请耐心等待)...${NC}"
					if [ ! -f /etc/ssl/certs/dhparam.pem ]; then
					    openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048
					    echo -e "${GREEN}DH 参数生成完成。${NC}"
					else
					    echo -e "${GREEN}DH 参数已存在,跳过生成。${NC}"
					fi

					# --------------------------
					# 5. 申请证书
					# --------------------------
					echo ""
					echo -e "${YELLOW}[5/6] 正在向 Let's Encrypt 申请证书...${NC}"
					echo -e "域名: $DOMAIN_ARGS"
					echo -e "邮箱: $EMAIL"

					# 核心命令:--nginx 表示自动配置 Nginx
					certbot run --nginx \
					    --non-interactive \
					    --agree-tos \
					    --no-eff-email \
					    --email "$EMAIL" \
					    $DOMAIN_ARGS

					if [ $? -eq 0 ]; then
					    echo -e "${GREEN}证书申请成功!${NC}"
					else
					    echo -e "${RED}证书申请失败,请检查域名 DNS 是否解析正确。${NC}"
					    exit 1
					fi

					# --------------------------
					# 6. 配置自动续期
					# --------------------------
					echo ""
					echo -e "${YELLOW}[6/6] 正在配置自动续期任务...${NC}"

					# Certbot 通常会自动安装 systemd timer,但我们确保它是激活的
					systemctl enable --now certbot.timer

					# 测试续期是否正常 (模拟运行)
					echo -e "${YELLOW}正在测试续期流程 (dry run)...${NC}"
					certbot renew --dry-run

					echo ""
					echo "=================="
					echo -e "${GREEN} ✅ 全部配置完成!${NC}"
					echo ""
					echo " 你的网站已启用 HTTPS。"
					echo " 证书路径: /etc/letsencrypt/live/$DOMAIN/"
					echo " 自动续期已通过 systemd timer 配置,无需手动操作。"
					echo ""
					echo " 建议运行: sudo nginx -t 检查配置,然后 sudo systemctl restart nginx"
					echo "=================="

					#执行脚本,根据提示输入域名与邮箱,获得证书文件
					chmod +x setup_ssl.sh && ./setup_ssl.sh
					
					

5.Nginx 配置文件详解

Nginx主配置

					
					vim /etc/nginx/nginx.conf

					# ==================
					# 1. 基础运行配置
					# ==================

					# Nginx 运行时使用的系统用户 (通常是 www-data,保证权限安全)
					user www-data;

					# 工作进程数,auto 表示自动匹配 CPU 核心数 (充分利用多核性能)
					worker_processes auto;

					# Nginx 主进程 PID 文件存放路径
					pid /run/nginx.pid;

					# 加载动态模块 (Ubuntu/Debian 默认路径)
					include /etc/nginx/modules-enabled/*.conf;

					# ==================
					# 2. 事件驱动配置
					# ==================
					events {
					    # 每个工作进程允许的最大并发连接数
					    worker_connections 600;

					    # 使用 Linux 最高效的 I/O 多路复用模型 (性能优化核心)
					    use epoll;
					}

					# ==================
					# 3. HTTP 核心配置
					# ==================
					http {
					    # --------------------------
					    # 3.1 基础网络与文件类型
					    # --------------------------

					    # 开启高效文件传输模式
					    sendfile on;

					    # 优化 TCP 数据包发送 (减少网络开销)
					    tcp_nopush on;
					    tcp_nodelay on;

					    # 客户端与服务器保持连接的超时时间 (65秒是业界标准)
					    keepalive_timeout 65;

					    # 提高文件类型哈希表的最大容量 (防止文件名过长报错)
					    types_hash_max_size 2048;

					    # 【核心安全】隐藏 Nginx 版本号 (防止攻击者针对特定版本漏洞攻击)
					    server_tokens off;

					    # 引入 MIME 类型定义文件 (告诉浏览器文件是 CSS/JS 还是图片)
					    include /etc/nginx/mime.types;

					    # 默认 MIME 类型 (如果无法识别,当作二进制流处理)
					    default_type application/octet-stream;

					    # --------------------------
					    # 3.2 日志配置
					    # --------------------------

					    # 访问日志存放路径
					    access_log /var/log/nginx/access.log;

					    # 错误日志存放路径 (只记录警告及以上级别)
					    error_log /var/log/nginx/error.log warn;

					    # --------------------------
					    # 3.3 Gzip 压缩配置 (性能优化)
					    # --------------------------

					    # 开启 Gzip 压缩 (减少带宽消耗,提升加载速度)
					    gzip on;

					    # 在响应头中添加 "Vary: Accept-Encoding" (兼容多端)
					    gzip_vary on;

					    # 无论后端服务器返回什么头,都强制压缩
					    gzip_proxied any;

					    # 压缩级别 (1-9,6 是压缩率和 CPU 消耗的最佳平衡点)
					    gzip_comp_level 6;

					    # 指定需要压缩的文件类型 (图片/视频通常已压缩,不再重复压缩)
					    gzip_types
					        text/plain
					        text/css
					        text/xml
					        text/javascript
					        application/json
					        application/javascript
					        application/xml+rss
					        font/truetype
					        font/opentype
					        image/svg+xml;

					    # --------------------------
					    # 3.4 引入子配置文件
					    # --------------------------

					    # 引入 conf.d 目录下的通用配置
					    include /etc/nginx/conf.d/*.conf;

					    # 引入 sites-enabled 目录下的站点配置 (我们的站点配置软链接在这里)
					    include /etc/nginx/sites-enabled/*;
					}
					
					

用户网站配置:

					
					vim /etc/nginx/sites-available/hk987

					# ==================
					# 服务器块 1:HTTP (80端口)
					# ==================
					server {
					    listen 80;
					    listen [::]:80;
					    server_name hk987.xyz www.hk987.xyz;
					    # 当你申请证书时,Certbot 会在 /var/www/html/.well-known/acme-challenge/ 下生成一个随机的小文件
					    # Let's Encrypt 的服务器会通过 HTTP 访问你的域名,看看能不能读到这个文件,如果能读到,证明你是这个域名的主人
					    # 这个/var/www/html目录,就是给 Let's Encrypt 临时生成验证文件用的
					    location /.well-known/acme-challenge/ {
					        root /var/www/html;
					        allow all;
					    }

					    location / {
					        return 301 https://hk987.xyz$request_uri;
					    }
					}

					# ==================
					# 服务器块 2:HTTPS - www 跳转
					# ==================
					server {
					    listen 443 ssl http2;
					    listen [::]:443 ssl http2;
					    server_name www.hk987.xyz;

					    # 只保留证书和 include
					    ssl_certificate /etc/letsencrypt/live/hk987.xyz/fullchain.pem;
					    ssl_certificate_key /etc/letsencrypt/live/hk987.xyz/privkey.pem;
					    include /etc/letsencrypt/options-ssl-nginx.conf;
					    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

					    return 301 https://hk987.xyz$request_uri;
					}

					# ==================
					# 服务器块 3:HTTPS - 主服务
					# ==================
					server {
					    listen 443 ssl http2;
					    listen [::]:443 ssl http2;
					    server_name hk987.xyz;

					    # 基础配置
					    root /home/hk987/www.html;
					    index index.html;
					    charset utf-8;

					    # SSL 配置 (只留这 4 行,其他全删)
					    ssl_certificate /etc/letsencrypt/live/hk987.xyz/fullchain.pem;
					    ssl_certificate_key /etc/letsencrypt/live/hk987.xyz/privkey.pem;
					    include /etc/letsencrypt/options-ssl-nginx.conf;
					    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

					    # --------------------------
					    # 安全头 (这些不是 ssl_ 开头,不会冲突,全部保留)
					    # --------------------------
					    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
					    add_header X-Frame-Options "DENY" always;
					    add_header X-Content-Type-Options "nosniff" always;
					    add_header X-XSS-Protection "1; mode=block" always;
					    add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; object-src 'none'; base-uri 'self';" always;
					    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

					    # --------------------------
					    # 路由规则
					    # --------------------------
					    location / {
					        try_files $uri $uri/ =404;

					        # 禁止访问隐藏文件
					        location ~ /\. {
					            deny all;
					            access_log off;
					            log_not_found off;
					        }

					        # 静态资源缓存
					        location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
					            expires 1y;
					            add_header Cache-Control "public, immutable";
					        }
					    }
					}
					
					

6.重启服务

					
					# 启用配置并重启 Nginx:
					# 1. 删除默认的 Nginx 配置链接 (可选,防止冲突)
					sudo unlink /etc/nginx/sites-enabled/default

					# 2. 链接我们的网站配置
					sudo ln -s /etc/nginx/sites-available/hk987 /etc/nginx/sites-enabled/

					# 3. 测试配置语法
					sudo nginx -t

					# 4. 重启 Nginx
					sudo systemctl restart nginx
					
					

0×2.本地配置

1.初始化本地 Git 仓库

					
					# 本地配置,如果服务器使用私钥ssh登陆

					# 初始化本地仓库
					mkdir hk987.xyz
					cd hk987.xyz

					git init
					git add .
					git commit -m "Initial commit"

					# 在本地仓库中查看当前分支名 (假设输出是 master)
					git branch
					
					

2.配置 SSH 密钥认证

如果没有密钥,生成一个(推荐用更安全的 ed25519)

					
					# 一路回车即可,也可以设置一个密码保护私钥
					ssh-keygen -t ed25519 -C "your_email@example.com"

					# 将公钥添加到 Ubuntu 服务器
					# 你需要把本地的公钥内容,放到服务器的 ~/.ssh/authorized_keys 文件里
					# 查看刚才生成的公钥内容,假设公钥名称是id_ed25519.pub
					cat ~/.ssh/id_ed25519.pub

					# 这里有个一键命令(假设你现在还能用密码登录服务器):
					# 在本地执行,替换用户名和IP(假设你刚才生成的公钥保存在~/.ssh/id_ed25519.pub)
					ssh-copy-id -i ~/.ssh/id_ed25519.pub your_user@your_server_ip
					
					

3.关联远程仓库与推送代码

					
					# 在本地项目目录中,关联远程仓库
					git remote add origin www.hk987.xyz:/home/hk987/www.git

					# --------------------------
					# 配置 SSH 连接信息
					# --------------------------
					# 注意:这会覆盖你本地的 ~/.ssh/config 文件
					# 如果不想每次都覆盖,可以把这一段注释掉,只在第一次运行时执行
					# Host后面是你自定义的服务器的“昵称”,和上面origin后面的关联
					#如果不设置这个昵称,则需要user@ip:/home/hk987/www.git去关联远程仓库
					echo "Host www.hk987.xyz
					    HostName hk987.xyz
					    User root
					    Port 2333
					    IdentityFile ~/.ssh/id_ed25519.pub
					    ServerAliveInterval 60
					    IdentitiesOnly yes" > /home/mm/.ssh/config

					# 推送代码
					git add -A
					git commit -m 'update' || true
					git push origin master
					
					

4.自动化一键推送脚本(可选)

					
					# 使用脚本推送,保存成987.sh

					vim 987.sh

					#!/bin/bash
					# ==================
					# 推送到自建服务器的 Git 脚本
					# 功能:
					# 1. 自动配置 SSH 连接信息
					# 2. 提交本地变更
					# 3. 仅在 origin 不存在时才添加远程仓库
					# 4. 推送到服务器
					# 5. 假设服务器用root登陆且ssh端口改成了2333
					# ==================

					# --------------------------
					# 1. 配置 SSH 连接信息
					# --------------------------
					# 注意:这会覆盖你本地的 ~/.ssh/config 文件
					# 如果不想每次都覆盖,可以把这一段注释掉,只在第一次运行时执行
					echo "Host www.hk987.xyz
					    HostName hk987.xyz
					    User root
					    Port 2333
					    IdentityFile ~/.ssh/id_ed25519.pub
					    ServerAliveInterval 60
					    IdentitiesOnly yes" > /home/mm/.ssh/config

					# 设置 SSH 配置文件权限(必须是 600,否则 SSH 会报错)
					chmod 600 /home/mm/.ssh/config

					# --------------------------
					# 2. 提交本地变更
					# --------------------------
					# 添加所有变更(包括新增、删除、修改)
					git add -A

					# 提交变更(如果没有变更,也不会报错退出)
					git commit -m 'update' || true

					# --------------------------
					# 3. 智能添加远程仓库(核心修改部分)
					# --------------------------
					# 检查本地是否已经存在名为 "origin" 的远程仓库
					if ! git remote | grep -q "^origin$"; then
					    echo "未检测到 origin 远程仓库,正在添加..."
					    git remote add origin www.hk987.xyz:/home/hk987/www.git
					else
					    echo "检测到 origin 远程仓库已存在,跳过添加步骤"
					fi

					# --------------------------
					# 4. 推送到远程服务器
					# --------------------------
					echo "正在推送到远程仓库..."
					git push origin master

					echo "推送完成!"