[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 "推送完成!"