引子

一段时间没处理腾讯云服务器的事情,有两台,以为那台在用的有效期更长,结果忽视了腾讯的提醒,再到最后,过期几个月了。导致站点数据完全丢失,包括N多的图片,有的是技术文章的图片,更要命的还有很多生活图片、视频、旅行图片,仅有的可能是旅行原图还有可能找得到,但是站点里的图都是经过了挑选、截取后的,再去补充图,非常难搞。

多年的心血,很受伤。

建站5.0

已另买了一台阿里服务器,五年七百多块,比较便宜了,因为几乎是最低配的,开了按流量计费。

先把思路搞清楚:

  • 文章一般本地电脑有复本,没有问题;
  • 图片一定是双备,打算发布一个简易程序,它来处理:一方面上传到百度网盘作为备份,二方面上传到其它minio文件服务器,这样既不占个人云服务器磁盘空间,又不产生阿里云网络流量;
  • 用docker建所有运行环境,包括:nginx、hugo、定制程序等,后续更方便迁移和发布,Hugo的主题选用loveit,作者的博客在这里,使用了很多组件,感觉还可以。

但是该主题有点问题:

  • 生成的目录貌似不稳定,一会有,一会没有;
  • 有的功能鸡肋还不准确,比如字数不知道怎么统计来的,但基本错的,还有预计阅读时间什么的;
  • 页面内容稍显复杂.

还是原来的那款minimal更符合我的喜好,但是官方居然下架了,找到另一个国人的,比较清爽,该主题的github地址在这里

首页右侧有标签样的东西,其解释的是categories,另外tag解释在每篇文章的最后。

1 安装java/hugo/nginx/git

这个比较简单,其中hugo是下载的安装包,链接在这里

安装了阿里云的免费SSL,在配置的时候要注意,域名解析是否先配置到位了,疏漏将浪费很多时间。

另外,还有一个很奇葩的情况:xshell以及mobaxterm用私钥怎么都连不上远程服务器,包括重新生成,并且公钥也已经导入到服务器的.ssh目录下,但是filezilla却可以。这个情况原因不明。

hugo启动时,关闭ssh连接就自动中断了,用下面这个命令行:nohup hugo server -D » log.log 2>&1 &

自hugo 0.6起,就不支持在md中夹杂着raw html,那么以前的就不作用了,比如:

<p align="center">
<video id="video" controls="" preload="none" poster="https://yidu.seaeverit.com/cslib_image/roadl/2f67acf37e5e324eb10a2d12baa3df9f.png" width="540" height="960">
  <source id="mp4" src="https://yidu.seaeverit.com/cslib_image/roadl/37187b5fb4af2b30770790cfc00591b4.mp4" type="video/mp4">
  <!--<source id="webm" src="http://media.w3.org/2010/05/sintel/trailer.webm" type="video/webm">
  <source id="ogv" src="http://media.w3.org/2010/05/sintel/trailer.ogv" type="video/ogg">
  <p>Your user agent does not support the HTML5 Video element.</p>-->
</video>
</p>

这里找到线索,官方的方法在这里,比如:

新增shortcodes文件:/layouts/shortcodes/youtube.html,其内容为:

<div class="embed video-player">
<iframe class="youtube-player" type="text/html" width="640" height="385" src="https://www.youtube.com/embed/{{ index .Params 0 }}" allowfullscreen frameborder="0">
</iframe>
</div>

在md中只要这样写就好了:

[{{]< youtube 09jf3ow9jfw >[}}] # 去掉中括号,这是为了防止内容脚本化

则会生成:

<div class="embed video-player">
    <iframe class="youtube-player" type="text/html"
        width="640" height="385"
        src="https://www.youtube.com/embed/09jf3ow9jfw"
        allowfullscreen frameborder="0">
    </iframe>
</div>

如法刨制,比如新增mp4.html:

<p align="center">
<video controls="" preload="none" poster="{{ index .Params 0 }}" width="640" height="352">
  <source src="{{ index .Params 1 }}" type="video/mp4">
</video>
</p>

在md中这样写就可以了:

[{{]< mp4 "https://yidu.seaeverit.com/cslib_image/roadl/728ee0c0b9a28b0f0cef20bd6ab5d61b.png" "https://yidu.seaeverit.com/cslib_image/roadl/eba4a465eb5bb302aa215a0335452ab3.mp4" "640" "352" >[}}]

完美解决心病,注意param中如果有特殊字符,比如:,用引号引起参数即可。

2 搭建代码托管环境

2.1 配置git仓库

自己建一个远程git仓库,本来打算搞个gitlab,但服务器配置太低,CPU超限。

参考自这里

$ groupadd git # 添加用户组
$ adduser --system --home /home/git --shell /usr/bin/git-shell --group git # 添加用户
$ ssh-keygen -t rsa -b 2048 -f privatealiyun230205.pem # 将阿里云生成的访问文件,转成rsa对privatealiyun230205.pem/privatealiyun230205.pem.pub
Generating public/private rsa key pair.
privatealiyun230205.pem already exists.
Overwrite (y/n)? yes
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in privatealiyun230205.pem.
Your public key has been saved in privatealiyun230205.pem.pub.
The key fingerprint is:
SHA256:qk+EUMX/79LaV5Z5PmEwVgndj+RQHHRuzQ4uIMqpkmQ daniel@daniel.local
The key's randomart image is:
+---[RSA 2048]----+
|    .o.      =*.+|
|   .  .     . oBo|
|  .    .. .  +o.*|
|   . o o.. . =o+.|
|  E . = S.  o + +|
| o . o .  .  . =+|
|  o . o    o  .+o|
|   . o    ..o ...|
|    ...   .+o.  .|
+----[SHA256]-----+

$ cd /home/git && mkdir .ssh
$ touch .ssh/authorized_keys # 授权文件,将公钥内容导入
$ chmod -R 0700 .ssh # 授权
$ chown -R git:git .ssh 

创建空仓库:

:/ git init --bare projects.git # 加Bare,表示创建裸仓库,一般用于远程仓库,在工作目录下,只有.git目录,没有可以浏览和删除的本地仓库
hint: Using 'master' as the name for the initial branch. This default branch name
hint: is subject to change. To configure the initial branch name to use in all
hint: of your new repositories, which will suppress this warning, call:
hint:
hint:   git config --global init.defaultBranch <name>
hint:
hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
hint: 'development'. The just-created branch can be renamed via this command:
hint:
hint:   git branch -m <name>
Initialized empty Git repository in /home/git/projects.git/

2.2 安装轻量代码托管gitea

下载页面在这里,居然用的是Caddy服务器,下载的版本是这样的

与gitlab/github等对比说明链接在[这里](https://docs.gitea.io/zh-tw/comparison/

赋权:chmod +x gitea-1.19-linux-amd64 运行:./gitea-1.19-linux-amd64 web 报错:Gitea is not supposed to be run as root. Sorry. If you need to use privileged TCP ports please instead use setcap and the cap_net_bind_service permission 原因是gitea不支持root用户,使用git用户 chown -R git:git ./gitea-1.19-linux-amd64 su git # 切换为git用户,但是上文已经将git用户禁用了shell

改回git用户的配置:

vim /etc/passwd
git:x:116:126::/home/git:/bin/bash

再运行还是一堆错误:

./gitea-1.19-linux-amd64 web
2023/02/25 16:29:38 cmd/web.go:105:runWeb() [I] Starting Gitea on PID: 17004
2023/02/25 16:29:38 ...s/setting/setting.go:253:newFileProviderFromConf() [E] Unable to check if /root/program/custom/conf/app.ini is a file. Error: stat /root/program/custom/conf/app.ini: permission denied
2023/02/25 16:29:38 ...es/setting/server.go:356:loadServerFrom() [E] Unable to check if /root/program/custom/robots.txt is a file. Error: stat /root/program/custom/robots.txt: permission denied
2023/02/25 16:29:38 .../setting/packages.go:66:loadPackagesFrom() [E] Unable to create chunked upload directory: /root/program/data/tmp/package-upload (mkdir /root/program: permission denied)
2023/02/25 16:29:38 ...s/install/setting.go:21:PreloadSettings() [I] AppPath: /root/program/gitea-1.19-linux-amd64
2023/02/25 16:29:38 ...s/install/setting.go:22:PreloadSettings() [I] AppWorkPath: /root/program
2023/02/25 16:29:38 ...s/install/setting.go:23:PreloadSettings() [I] Custom path: /root/program/custom
2023/02/25 16:29:38 ...s/install/setting.go:24:PreloadSettings() [I] Log path: /root/program/log
2023/02/25 16:29:38 ...s/install/setting.go:25:PreloadSettings() [I] Configuration file: /root/program/custom/conf/app.ini
2023/02/25 16:29:38 ...s/install/setting.go:26:PreloadSettings() [I] Prepare to run install page
2023/02/25 16:29:38 ...ation/translation.go:59:func1() [F] Failed to list locale files: unable to check if custom directory "/root/program/custom/options/locale" is a directory. stat /root/program/custom/options/locale: permission denied 

从错误信息看,因为程序是放在/root/program下,这个root目录归root用户的,可能就权限问题。在它下面创建不了custom/log/data等目录。

可能需要先进行一些配置,目前看就是创建一些目录,参考这里,如下:

mkdir -p /var/lib/gitea/{custom,data,log}
chown -R git:git /var/lib/gitea/
chmod -R 750 /var/lib/gitea/
# 下面这三行可能跟解决问题无关,是gitea程序目录所在,当然,下述启动命令有指定到这个目录下写app.ini文件,但并没有生成该文件,目录仍为空
mkdir /etc/gitea
chown root:git /etc/gitea
chmod 770 /etc/gitea

再次运行,错误依旧,这样看来文件夹虽然创建了,但是gitea程序并不会指向这些目录,如何处理?重要信息——在上述官方网页中,找到命令行启动的要点,即通过下面命令行成功启动:

GITEA_WORK_DIR=/var/lib/gitea/ ./gitea-1.19-linux-amd64 web -c /etc/gitea/app.ini
2023/02/25 16:34:24 cmd/web.go:105:runWeb() [I] Starting Gitea on PID: 17035
2023/02/25 16:34:24 ...s/install/setting.go:21:PreloadSettings() [I] AppPath: /root/program/gitea-1.19-linux-amd64
2023/02/25 16:34:24 ...s/install/setting.go:22:PreloadSettings() [I] AppWorkPath: /var/lib/gitea/
2023/02/25 16:34:24 ...s/install/setting.go:23:PreloadSettings() [I] Custom path: /var/lib/gitea/custom
2023/02/25 16:34:24 ...s/install/setting.go:24:PreloadSettings() [I] Log path: /var/lib/gitea/log
2023/02/25 16:34:24 ...s/install/setting.go:25:PreloadSettings() [I] Configuration file: /etc/gitea/app.ini
2023/02/25 16:34:24 ...s/install/setting.go:26:PreloadSettings() [I] Prepare to run install page
2023/02/25 16:34:24 ...s/install/setting.go:29:PreloadSettings() [I] SQLite3 is supported
2023/02/25 16:34:24 cmd/web.go:220:listen() [I] [63f9c810-6] Listen: http://0.0.0.0:3000
2023/02/25 16:34:24 cmd/web.go:224:listen() [I] [63f9c810-6] AppURL(ROOT_URL): http://localhost:3000/
2023/02/25 16:34:24 ...s/graceful/server.go:62:NewServer() [I] [63f9c810-6] Starting new Web server: tcp:0.0.0.0:3000 on PID: 17035

可以设置GITEA_WORK_DIR的环境变量。

现在看来,CPU的消耗比较docker+gitlab少了很多,希望不到撑到阿里云说CPU分消耗完而几近断服。

    0[*                                                                                       0.7%]   Tasks: 50, 93 thr; 1 running
    1[                                                                                        0.0%]   Load average: 0.04 0.02 0.00
  Mem[||||||||||||||||||||#####*********************************************************358M/1.64G]   Uptime: 22:59:17
  Swp[                                                                                       0K/0K]

把gitea配置为自启服务,参考官方文档,链接在这里

root@daniel:~/program# cp gitea-1.19-linux-amd64 /usr/local/bin/gitea # 先把gitea拷贝到自启服务中指定的目录
root@daniel:~/program# chown -R git:git /usr/local/bin/gitea
root@daniel:~/program# systemctl enable gitea # 执行需要一点时间
Created symlink /etc/systemd/system/multi-user.target.wants/gitea.service → /etc/systemd/system/gitea.service.
root@daniel:~/program# sudo systemctl start gitea

初次访问gitea,进入了一个配置页面,主要要求配置mysql,这样接下来安装mysql

其它配置信息:

仓库根目录:/var/lib/gitea/data/gitea-repositories
LFS根目录:/var/lib/gitea/data/lfs
日志根目录:/var/lib/gitea/log

2.3 gitea钩子搞定自动发布博文

也想像之前搞的gitlab一样,做一个hook,捕获push event,然后check out代码至hugo目录,这样hugo会自动刷新站点。

以下参考自这里

实现更新md自动checkout到hugo的content目录,以实现自动发布更新。代码库路径:/var/lib/gitea/data/gitea-repositories/daniel/roadl.com.git,其子目录:cd hooks/post-receive.d/

钩子文件post-receive的内容(注意这个文件的所有者设为git,且打开执行权限):

#!/bin/bash

gbError="\033[1;31m[ERROR]\033[0m"
gbWarning="\033[1;33m[WARNING]\033[0m"
gbInfo="\033[1;32m[INFO]\033[0m"
gbGood="\033[1;32m[GOOD]\033[0m"

echo -e "\n$gbInfo Hugo blog post-receive git hook is running...\n"

# markdown工程代码库
bare_repo=/var/lib/gitea/data/gitea-repositories/daniel/roadl.com.git
# 先把东西全部checkout出来,TODO是否能部分checkout?
cloned_repo=/var/lib/gitea/data/gitea-repositories/daniel/roadl.com
# 仅处理博文目录
blog_dir=/var/www/roadl.com/content/posts

# 先删掉历史checkout
rm -rf $cloned_repo
git clone $bare_repo $cloned_repo

cd $cloned_repo
# 下面两行不执行了,hugo不像hexo,可以先走develop模式
#chmod +x deploy.sh
#./deploy.sh

#if [ $? -eq 0 ]; then
    # 删掉站点内容
    rm -rf ${blog_dir}/*
    # 仅拷贝部分checkout内容至站点
    cp -rf content/posts/* ${blog_dir}/
#fi

把目录文件夹blog_dir设为git用户所有,因为他要在里面进行复制、以及删除操作:

sudo chown -R git:git /var/root/roadl.com/content/posts

上述变更所有者还是没有用,因为父级等还是属于root的。后来把roadl.com移动到/var/www下就可以了。这时,一旦在idea提交md的更新,就立即能看到最新的网页。

附,人家的deploy.sh是放在工程根目录下的,内容为:

#!/bin/bash

file_log=deploy.log
file_fifo=run.fifo
rm -f "$file_log" "$file_fifo"
mkfifo $file_fifo
cat $file_fifo | tee -a $file_log &
exec 3>&1
exec 4>&2
exec 1>$file_fifo
exec 2>&1

gbError="\033[1;31m[ERROR]\033[0m"
gbWarning="\033[1;33m[WARNING]\033[0m"
gbInfo="\033[1;32m[INFO]\033[0m"
gbGood="\033[1;32m[GOOD]\033[0m"

current_time=$(date "+%Y-%m-%d %H:%M:%S")
echo -e "\n <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< $current_time >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> \n"
echo -e "$gbInfo Start to build and deploy hexo blog...\n"

hexo clean
hexo douban -b
hexo g
# hexo deploy

if [ $? -eq 0 ]; then
    echo -e "\n$gbGood Successfully built and deployed hexo blog!"
else
    echo -e "\n$gbError Faild to deploy hexo blog, please check it!"
fi

printf "\015"
exec 1>&3
exec 2>&4
rm -f "$file_fifo"

3 安装mysql

见另外的文章,链接在这里,后来在配置gitea时,发现字符集超出限定范围,于是重装5.7,安装方法有点不同,类似于mysql 8的安装:

bin/mysqld --initialize-insecure --user=root --basedir=/root/program/mysql-5.7.37-linux-glibc2.12-x86_64 --datadir=/root/program/mysql-5.7.37-linux-glibc2.12-x86_64/data

然后设定为自启:

root@daniel:~/program/mysql-5.7.37-linux-glibc2.12-x86_64# cp support-files/mysql.server /etc/init.d/mysql.server ## 拷贝前修改basedir、datadir
root@daniel:~/program/mysql-5.7.37-linux-glibc2.12-x86_64# update-rc.d mysql.server defaults  # 需要一点时间
root@daniel:~/program/mysql-5.7.37-linux-glibc2.12-x86_64# systemctl daemon-reload # 使修改生效,需要一点时间