通过上篇【通过域名+服务器搭建一个最基础的网站】 我们知道了如何通过搭建自己的博客,接下来稍微介绍下如何通过工作流做自动化部署。

开始

上篇中,已配置好 Nginx,并且当我们访问 https://blog.emooa.com 时,可以正确的访问到网站资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server {
listen 443 ssl;
server_name blog.emooa.com; // 替换成实际的域名地址

ssl_certificate cert/blog.emooa.com.pem; // 替换成实际的 pem
ssl_certificate_key cert/blog.emooa.com.key; // 替换成实际的 key

ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;

ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;

ssl_prefer_server_ciphers on;
error_page 404 /404.html;
location / {
root /home/blog; // 替换成实际的资源路径
index index.html index.htm;
# try_files $uri $uri/ /index.html;
}
}

我们注意到 root 指向 /home/blog,通常 Nginx 的默认文档根目录通常是 /usr/share/nginx/html/var/www,因为我的服务器是 Ubuntu,所以应该是 /var/www

为什么不是放在个人目录下?

个人用户的主目录下通常不是最佳做法,因为这些文件通常需要具有公共访问权限,以便 Web 服务器可以提供它们给外部访问者。用户主目录通常默认是不允许公共访问的,如果多个用户使用同一台服务器托管不同的网站或服务,通常无法访问其他用户的个人目录,因此需要在更适合公开访问的位置存储网站或服务文件。

比如我当前的账户为 hfs,于是在 /var/www 下新建个人目录,将 blog 文件夹移动到 /var/www/hfs 下,并修改 ngixn 配置

1
2
ssh hfs@*.*.*.*
sudo mv -r /home/blog /var/www/hfs/blog
1
2
3
4
location / {
root /var/www/hfs/blog; // 替换成实际的资源路径
index index.html index.htm;
}

我们继续访问 https://blog.emooa.com 不错,依然能访问到页面。

配置 Workflows

创建一个包含以下步骤的工作流程(workflow)文件(命名为 .github/workflows/deploy.yml)并将其添加到你的代码仓库中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
name: CI/CD

on:
push:
branches:
- master

jobs:
deploy:
runs-on: ubuntu-latest

env: # 在这里定义环境变量
USER_NAME: hfs
IP: *.*.*.*
SSH_PRIVATE_KEY: ****** // 私匙

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: 14

- name: Install dependencies
run: yarn

- name: Build
run: yarn build

- name: Deploy blog.emooa.com
run: |
mkdir -p ~/.ssh
echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
chmod 600 ~/.ssh/id_rsa
echo "Removing /var/www/$USER_NAME/blog"
ssh $USER_NAME@$IP "rm -f /var/www/$USER_NAME/blog || echo 'no such directory.'"

echo "Creating /var/www/$USER_NAME/blog"
ssh $USER_NAME@$IP "mkdir -p /var/www/$USER_NAME/blog"

echo "Copying 'public' directory to /var/www/$USER_NAME/blog"
scp -r public/* $USER_NAME@$IP:/var/www/$USER_NAME/blog/

  • 第一步使用 actions/checkout 动作来检出最新的代码。

  • 第二步用于安装应用程序的依赖项。你可以根据你的项目使用 npm 或 yarn。

  • 第三步用于构建你的应用程序。你需要替换为适用于你的项目的构建命令。

  • 第四步是将构建好的文件部署到你的服务器。你需要根据你的服务器和部署方式来编写对应的命令,例如使用 scp 或 rsync。

注意:通常情况下需要验证服务器具有正确的部署凭证,并且可以从 GitHub Actions 访问你的服务器。如果需要,你还需要设置 SSH 密钥或其他身份验证方法来与你的服务器通信。

配置 SSH 密匙认证

通常我们在建立 SSH 连接时,是需要输入密码的,但是由于在通过 workflow 工作流进行 ssh 连接时,不涉及到交互式操作,所以我们需要通过基于 SSH密匙的认证。

方式一:使用 SSH 密钥认证(推荐)

1、生成 SSH 密钥对

SSH 密钥认证更安全和便捷,通常是首选的方法。公钥文件(id_rsa.pub)包含了你的公钥,而密钥文件(id_rsa)则应该保持在本地,不要共享或泄露。密钥是用于身份验证的一半,而公钥是用于验证身份的另一半。当你尝试通过 SSH 连接到服务器时,你的密钥与服务器上存储的公钥进行匹配,以便验证你的身份。

首先,确保你的本地计算机上已经生成了 SSH 密钥对,包括公钥和密钥。如果你还没有生成密钥对,你可以在本地电脑使用以下命令生成:

1
ssh-keygen -t rsa -b 4096 -C "your_email@example.com"

2、添加公钥到服务器

这将生成一个 SSH 密钥对,为 id_rsa, id_rsa.pub。 然后,将你的公钥添加到服务器上的 ~/.ssh/authorized_keys 文件中。你可以使用以下命令将公钥复制到服务器:

1
ssh-copy-id $USER_NAME@$IP // 比如:ssh-copy-id hfs@$*.*.*.*

确保 $USER_NAME 和 $IP 替换为服务器的用户名和 IP 地址。此命令会讲公匙追加到服务器上的 authorized_key 文件中。

如果没有 ssh-copy-id 命令,你可以手动将公钥的内容复制到服务器的 ~/.ssh/authorized_keys 文件中。

1
cat ~/.ssh/id_rsa.pub | ssh $USER_NAME@$IP 'mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys'

这个命令将本地的公钥内容通过 SSH 连接发送到服务器,并追加到 `authorized_keys`` 文件中。
完成后,你应该能够无需密码进行 SSH 连接。

3、添加密钥到 Github Action

你应该注意到 yml 文件中的环境变量 SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }},这是由于在 `workflow`` 工作流连接服务器的时候,需要进行身份验证,我们已经在服务器添加了公钥,这时候就需要把密钥添加到工作流当中进行 SSH 认证。

  • 复制密钥文件 id_rsa 的内容读。

    1
    cat ~/.ssh/id_rsa
  • 在 GitHub 仓库的 “Settings” > “Secrets” 页面中,创建一个新的 Secret,命名为 SSH_PRIVATE_KEY(或其他你喜欢的名称),并将第一步中的密钥内容粘贴到 “Value” 字段中。
    iamge

  • GitHub Actions 的工作流程中,使用 Secrets 中的 SSH_PRIVATE_KEY 变量来设置密钥,然后用它进行 SSH 认证。示例步骤如下:

    1
    2
    3
    4
    5
    6
    7
    - name: Deploy blog.emooa.com
    env:
    SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
    run: |
    mkdir -p ~/.ssh
    echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
    chmod 600 ~/.ssh/id_rsa // 设置文件权限

4、首次验证主机密钥

通常,在第一次 SSH 连接时会要求我们确认服务器的公钥指纹。由于是 workflow 工作流非交互式部署,我们需要提前添加服务器的公钥到工作流的的 known_hosts 文件中,这个文件位于 ~/.ssh/known_hosts
可以在你的 GitHub Actions 步骤中使用 ssh-keyscan 命令获取服务器的公钥并追加到 known_hosts 文件。

如果本地主机不存在 IP 对应的公钥,可以执行 ssh USERNAME@IP 后根据提示生成。

1
ssh-keyscan $SSH_KNOWN_HOSTS >> ~/.ssh/known_hosts

方式二:服务器 SSH 服务器配置(不推荐)

确保服务器上的 SSH 服务器配置允许 SSH 密钥认证。在服务器上的 /etc/ssh/sshd_config 文件中,确保以下配置项没有被注释掉:

1
2
PubkeyAuthentication yes
PasswordAuthentication no

可能会遇到的问题

问题一 无法验证主机密钥

Run ssh $USER_NAME@$IP ‘mkdir -p /var/www/$USER_NAME/blog’
Host key verification failed.
Error: Process completed with exit code 255.

原因:通常出现在首次连接到远程服务器时,SSH 客户端无法验证目标服务器的主机密钥。这通常是因为 SSH 客户端没有保存目标服务器的公钥,或者保存的公钥不匹配。
解决:上面提到的 首次验证主机密钥

问题二 权限不足

cannot create directory ‘/var/www/hfs/blog’: Permission denied

1
2
3
4
Run ssh $USER_NAME@$IP 'mkdir -p /var/www/$USER_NAME/blog'
Host key verification failed.
Error: Process completed with exit code 255.

原因:遇到了文件权限问题,这意味着你的 SSH 用户在远程服务器上没有足够的权限来创建目录 /var/www/$USER_NAME/blog
解决:

  • 使用超级用户权限(例如 root 用户)登录到服务器。

  • 使用 chown 命令将目录的所有者更改为 $USER_NAME 用户。这将确保 $USER_NAME 用户具有修改该目录的权限

  • 使用 chmod 命令为目录添加写权限。你可以授予 $USER_NAME 用户写入权限,而其他用户只能读取或执行。

    1
    2
    sudo chown -R $USER_NAME:$USER_NAME /var/www/$USER_NAME
    sudo chmod -R 755 /var/www/$USER_NAME

问题三:如何查看日志

在 SSH 时可能出现一些不清楚具体原因导致的,可以通过打开日志来检查

1
ssh -v -i ~/.ssh/id_rsa $USER_NAME@$IP