快速实现静态站点的自动化更新

在现代前后端分离或静态站点,将网站代码安全快速的推送到 Web 服务器提高效率的必由之路。

架构

开发者将网站代码 Push 到 GitLab 的主分支。GitLab CI 启动一个临时的轻量级 Runner,通过 SSH 触发 Web Server 主动拉取 Git 代码。专用的 pushgit 容器与 Nginx 容器分离,只有静态资源/代码所在的目录是共享挂载的。CI 只与 pushgit 容器通信,没有任何 Web Server 的其它权限。

Web Server 端配置与搭建

pushgit 容器

这个容器用于接受 GitLab CI 的SSH访问,触发拉取 Git 更新网站文件。

  • 生成容器中 SSHd 的主机密钥存放在 host_key 目录下,保持容器重建后的主机密钥不变
  • 生成一对用户密钥存放在 id_key 目录下,以后容器就是用这个私钥到 GitLab 上拉取文件
  • authorized_keys 存放的是 GitLab CI 的 SSH 公钥,CI 用这个公钥来触发更新

docker-compose.yml:

  pushgit:
    build: ./pushgit
    image: pushgit
    container_name: pushgit
    restart: unless-stopped
    ports:
      - 2222:22
    environment:
      - TZ=Asia/Shanghai
    volumes:
      - ./yaoge1:/var/www/yaoge1:rw
      - ./yaoge2:/var/www/yaoge2:rw

Dockerfile:

FROM alpine:latest

RUN sed -i 's#https\?://dl-cdn.alpinelinux.org/alpine#https://mirrors.cernet.edu.cn/alpine#g' /etc/apk/repositories

RUN apk update --no-cache \
 && apk upgrade --no-cache \
 && apk add --no-cache tzdata ca-certificates openssh-server openssh-client git \
 && update-ca-certificates \
 && rm -rf /var/cache/apk/*

COPY --chmod=0600 host_key/ssh_host_*_key /etc/ssh/
COPY --chmod=0644 host_key/ssh_host_*_key.pub /etc/ssh/
RUN echo "PasswordAuthentication no" > /etc/ssh/sshd_config.d/disable_password.conf \
 && echo "StrictHostKeyChecking accept-new" > /etc/ssh/ssh_config.d/accept_hostkey.conf

RUN adduser -D -u 1000 -h /home/pushgit -s /home/pushgit/pushgit.sh pushgit && passwd -u pushgit
COPY --chown=pushgit:pushgit --chmod=0700 pushgit.sh /home/pushgit/
RUN install -d -m 0700 -o pushgit -g pushgit /home/pushgit/.ssh
COPY --chown=pushgit:pushgit --chmod=0600 id_key/id_* /home/pushgit/.ssh/
COPY --chown=pushgit:pushgit --chmod=0600 authorized_keys /home/pushgit/.ssh/
RUN ssh-keyscan git.yaoge123.com >> /home/pushgit/.ssh/known_hosts \
 && chown pushgit:pushgit /home/pushgit/.ssh/known_hosts \
 && chmod 0644 /home/pushgit/.ssh/known_hosts

CMD ["/usr/sbin/sshd", "-De"]

pushgit.sh:

#!/bin/sh
set -e
echo "$2: "
cd "/var/www/$2" || { echo "Directory not found!"; exit 1; }
git config --global safe.directory "/var/www/$2"
git fetch --all
git reset --hard @{upstream}
git clean -fd

authorized_keys:

restrict,command="yaoge1" ssh-ed25519 AxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxZ gitlab-yaoge1
restrict,command="yaoge2" ssh-ed25519 AxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxU gitlab-yaoge2

Nginx 配置

server {
    listen 80;
    listen [::]:80;
    server_name yaoge1.yaoge123.com;
    server_tokens off;
    return 301 https://$server_name$request_uri;
}

server {
    listen *:443 ssl;
    listen [::]:443 ssl;
    server_name yaoge1.yaoge123.com;
    server_tokens off;
    
    include ssl/acme-challenge.conf;
    include ssl/yaoge123_com.conf;

    location ~ /\.git {
        return 404;
    }

    root /var/www/yaoge1;
}

GitLab 端配置

创建一个用户(比如 WebServerPush),把上述 id_key/id_*.pub 加入用户的 SSH Keys 下,把这个用户加入到项目成员中,给 Reporter 角色。这个用户是公用的,所有类似的项目都添加这个用户即可。

生成一对网站项目专用的 key,其中公钥放到上述 authorized_keys 中,私钥经BASE64编码后存到CI/CD变量中。

变量键值 (Key)说明
SSH_PRIVATE_KEY_BASE64SSH 私钥的Base64编码,选择 Masked and hidden 和 Protect variable
SERVER_IPWeb Server 的 IP
SERVER_PORTpushgit 容器暴露的 SSH 端口(如上文示例的 2222)
SSH_USERpushgit 容器内的操作账户(如上文示例的 pushgit)

.gitlab-ci.yml

deploy_to_server:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache openssh-client coreutils
    - echo "$SSH_PRIVATE_KEY_BASE64" | base64 -d > ./ssh_key
    - chmod 400 ./ssh_key
  script:
    - ssh -o StrictHostKeyChecking=accept-new -i ./ssh_key -T -p $SERVER_PORT $SSH_USER@$SERVER_IP

下面只要在 Web Server 上用 git clone 初始化一下即可,后续只要 git 推送了新的文件,就会自动触发更新。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理