以 Docker 方式部署基于 MinIO 与 Thumbor 的对象存储服务

作者:wiLdGoose 发布时间:July 14, 2021 分类:技术 Technology

穷人的孩子早当家是有一定道理的。由于用不起某里云的 OSS (对象存储)服务,自己折腾了一番。期间踩坑若干,故自行记录聊以慰籍。

技术选型是采用 MinIO 与 Thumbor 无疑,网上相关的文章也很多。我的故事分为 5 个节点:

0x00、最初的记忆——Docker 安装

是的,我一开始打算以 Docker 方式部署。

MinIO 的 Docker 部署方式可以完全参照官方文档进行:

docker run \
  -d \
  -p 9000:9000 \
  -p 9001:9001 \
  --name minio \
  -e MINIO_ROOT_USER='xxx' \
  -e MINIO_ROOT_PASSWORD='xxx' \
  minio/minio server /data --console-address ':9001'

MINIO_ROOT_USER 与 MINIO_ROOT_PASSWORD 是登录 MinIO Console 的账号密码,记得替换。

而 Thumbor 则是选择了一个叫做 Minimal Compact Thumbor 的镜像,真的是简单:

docker run -p 8888:80 minimalcompact/thumbor

一行搞定。

搞到这里,MinIO 已通过 Docker 方式跑起来了,本地测试 Thumbor 发现无法处理 MinIO 提供的私有地址,比如以下这种情况。

通过调用 MinIO 的 getPresignedObjectUrl 接口获取的私有地址:

http://127.0.0.1:9000/test/test.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=348201GIYPQI49I606RF%2F20210713%2Fxxx%2Fs3%2Faws4_request&X-Amz-Date=20210713T042509Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=xxx

Thumbor 处理的地址:

http://127.0.0.1:8888/unsafe/500x0/http://127.0.0.1:9000/test/test.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=348201GIYPQI49I606RF%2F20210713%2Fxxx%2Fs3%2Faws4_request&X-Amz-Date=20210713T042509Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=xxx

结果报 400 错误。

0x01、曲径通幽——通过 Python-pip 安装 Thumbor

因为折腾失败了,一顿分析后感觉应该编辑下 thumbor.conf,可能有关。又看了几篇文章,大部分都以 Python-pip 方式安装的,所以实验如下:

yum install python-pip -y
pip install thumbor

好了,失败。原因是操作系统 CentOS 7.x 自带 Python 2。而官方文档说了需要 Python 3.7 及以上:

Thumbor v7.0.0 and later only supports python 3.7+. This change was important to improve our codebase and ensure it’s easier to change in future releases. More breaking changes will come, but we do not anticipate any as big as this one. Please refer to release notes for details on how to upgrade.

好的,那就安装 Python 3。首先安装一些依赖:

yum install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make libffi-devel python-pip -y

下载源码包,安装 Python 3.7:

wget -c https://www.python.org/ftp/python/3.7.11/Python-3.7.11.tgz
tar xzf pPython-3.7.11.tgz
cd Python-3.7.11
./configure prefix=/usr/local/python3 && make && make install

一顿刷屏之后,success 了。建两个软链,也可以不做:

ln -s /usr/local/python3/bin/python3.7 /usr/bin/python3.7
ln -s /usr/local/python3/bin/pip3.7 /usr/bin/pip3.7

验证一下:

python3.7 -V
Python 3.7.11

如果没建软链接,要这样:

/usr/local/python3/bin/python3.7 -V

还可以给 pip 升下级:

/usr/local/python3/bin/python3.7 -m pip install --upgrade pip

继续,大活来了:

pip install thumbor3.7

安装完成后生成配置文件:

thumbor-config > ./thumbor.conf

我就死在这儿了,一直报:

-bash: thumbor-config: command not found

一番折腾后无果,欲弃之。

0x02、回头草——Docker 真香定律

考虑到后续需要分布式部署,决定回归 Docker 的怀抱。总结当前的两个问题:

- MinIO 以 Docker 方式部署,网上有人说容器停了会丢数据;
- Thumbor 无法解析 MinIO 的私有地址。

第一个问题好办,想办法持久化存储就行,可以通过 -v 指定映射关系:

docker run \
  -d \
  -p 9000:9000 \
  -p 9001:9001 \
  --name minio \
  -v /data/minio:/data \
  -e MINIO_ROOT_USER='xxx' \
  -e MINIO_ROOT_PASSWORD='xxx' \
  minio/minio server /data --console-address ':9001'

/data/minio 是宿主机的数据目录,MinIO 搞定,剩下就是接口调试。

第二个问题在我一顿翻文档之后发现:

Thumbor only understands properly encoded URIs. In order to use the URI above (or any other for that matter), we first need to encode it.

真相大白。古人云,一定要看文档,真的没错。

0x03、强迫症——难以忍受的“unsafe”

由于本地 80、443 端口不能在公网开放,故通过另一台主机的 Nginx 配置 3 个域名,反向代理到这台主机,分别对应 MinIO 的 9000、9001 端口及 Thumbor 的 8888 端口。

现在 Thumbor 处理的地址是这样的:

https://oss.dev.xxx.com/unsafe/500x0/api.minio.dev.xxx.com%2Fbucket%2Ftest%2Ftest.jpg%3FX-Amz-Algorithm%3DAWS4-HMAC-SHA256%26X-Amz-Credential%3DQ6OYL97O80V8FKTIET78%252F20210713%252Fcnc-dev%252Fs3%252Faws4_request%26X-Amz-Date%3D20210713T074421Z%26X-Amz-Expires%3D604800%26X-Amz-SignedHeaders%3Dhost%26X-Amz-Signature%3Da11b4cdf85c47d6a8e90712ea7e9e5813d4ee8b39a9103b1be690c0ea7f6a949

正在大功告成的时候,前端弟兄说“unsafe”太难受了,能不能改掉。我尝试在 Nginx 上代理到 x.x.x.x:8888/unsafe,但是不行,会出现 400。

继续看文档,最终找到这么一章可以完美解决这个强迫症症状。

最终,Thumbor 容器的启动命令改造为:

docker run \
  -d \
  -p 8888:80 \
  --name thumbor \
  -e SECURITY_KEY=‘xxx’ \
  -e ALLOW_UNSAFE_URL=False \
  -e THUMBOR_NUM_PROCESSES=5 \
  minimalcompact/thumbor

其中:

- SECURITY_KEY 密钥交给前端就能根据文档进行后续处理;
- ALLOW_UNSAFE_URL 是允许 unsafe 链接,此处关闭;
- THUMBOR_NUM_PROCESSES 大概是多线程数量,未测试。

0x04、最终还是用了 Thumbor AWS

本来故事到这儿,已经可以歇了——至少昨天是这样的。后来同志们折腾半天发现不用 unsafe,拼接私有地址,还是 400 错误。

看到 Docker 中的日志是这样的:

2021-07-16 02:32:26 thumbor:WARNING Malformed URL: /ZM83RRQJ8B08K79PCZs1wcC1MLk=/1000x0/api.minio.dev.xxx.com%2Ftest%2F2021%2F07%2F15%2F778aec46abda4e1fc25d9e7dfc6802de.jpg%3FX-Amz-Algorithm%3DAWS4-HMAC-SHA256%26X-Amz-Credential%3DQ6OYL97O80V8FKTIET78%252F20210716%252Fcnc-dev%252Fs3%252Faws4_request%26X-Amz-Date%3D20210716T023226Z%26X-Amz-Expires%3D1800%26X-Amz-SignedHeaders%3Dhost%26X-Amz-Signature%3Da2939aad07f44238db934c3798cdaa40944e426c78892f8e38bd2d3b6b5f7cfc

网上也有类似的讨论,好像也没个结论。

看来私有地址的路是走不通了,网上文章有提到 Thumbor AWS 可以完美匹配 MinIO。可惜几乎都是以 pip install tc_aws 的形式来安装的,不适用。

结合了一些资料后,自己折腾出一个脚本:

docker run \
  -d \
  -p 8888:80 \
  --name thumbor \
  -e SECURITY_KEY='xxx' \
  -e ALLOW_UNSAFE_URL=False \
  -e AWS_ACCESS_KEY_ID='111' \
  -e AWS_SECRET_ACCESS_KEY='222' \
  -e TC_AWS_REGION='cnc-dev' \
  -e TC_AWS_ENDPOINT='http://127.0.0.1:9000' \
  -e LOADER='tc_aws.loaders.s3_loader' \
  minimalcompact/thumbor

其中:AWS_ACCESS_KEY_ID 等于启动 MinIO 容器命令中的 MINIO_ROOT_USER,AWS_SECRET_ACCESS_KEY 等于启动 MinIO 容器命令中的 MINIO_ROOT_PASSWORD。

结果是报 599 错误:

2021-07-16 03:03:48 thumbor:ERROR ERROR retrieving image from S3 1.jpg: {'ResponseMetadata': {'HTTPStatusCode': 599, 'HTTPHeaders': {}}, 'Error': {'Message': '', 'Code': '599'}}

一番搜索,599 是 Python Tornado 报的,这里有关于 Tornado 599 的讨论。说是 599 属于连接超时。

然后我就清醒了,脚本改成:

docker run \
  -d \
  -p 8888:80 \
  --name thumbor \
  -e SECURITY_KEY='xxx' \
  -e ALLOW_UNSAFE_URL=False \
  -e AWS_ACCESS_KEY_ID='111' \
  -e AWS_SECRET_ACCESS_KEY='222' \
  -e TC_AWS_REGION='cnc-dev' \
  -e TC_AWS_ENDPOINT='http://x.x.x.x:9000' \
  -e LOADER='tc_aws.loaders.s3_loader' \
  minimalcompact/thumbor

这里的 x.x.x.x 是宿主机网卡绑定的 IP 地址。

一次性成功,最终的 URI 格式是这样的:

https://oss.xxx.com/kL1goioOIwzaBZZTbxoO2Py20LM=/300x0/BUCKET-NAME/FILE-NAME.png

真的可以休息了。

TODO:分布式待折腾。

acme.sh 默认 CA 更新为 ZeroSSL 引起的问题

作者:wiLdGoose 发布时间:July 12, 2021 分类:技术 Technology

今天通过 acme.sh 给新域名申请 SSL 证书,遇到报错:

[Mon Jul 12 15:53:31 CST 2021] Using CA: https://acme.zerossl.com/v2/DV90
[Mon Jul 12 15:53:31 CST 2021] No EAB credentials found for ZeroSSL, let's get one
[Mon Jul 12 15:53:31 CST 2021] acme.sh is using ZeroSSL as default CA now.
[Mon Jul 12 15:53:31 CST 2021] Please update your account with an email address first.
[Mon Jul 12 15:53:31 CST 2021] acme.sh --register-account -m [email protected]
[Mon Jul 12 15:53:31 CST 2021] See: https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA
[Mon Jul 12 15:53:31 CST 2021] Please add '--debug' or '--log' to check more details.
[Mon Jul 12 15:53:31 CST 2021] See: https://github.com/acmesh-official/acme.sh/wiki/How-to-debug-acme.sh
Error: Create Let's Encrypt SSL Certificate failed!

有人说需要在 ~/.acme.sh/account.conf 中添加:

ACCOUNT_EMAIL='[email protected]'

但是需要邮箱地址终究是麻烦,希望切换回 Let's Encrypt,可以这样:

# .acme.sh/acme.sh --set-default-ca --server letsencrypt
[Mon Jul 12 15:54:19 CST 2021] Changed default CA to: https://acme-v02.api.letsencrypt.org/directory

官方文档上也都写了,可以猛戳此处

不过据说 ZeroSSL 支持泛域名比较爽,有空再折腾一下,先学党国维稳。

墙内外服务器数据异地备份方案

作者:wiLdGoose 发布时间:May 15, 2020 分类:技术 Technology

我们的宣传口号是:与墙斗,其乐无穷。

1、墙内服务器

1.1、需求与掣肘

需要对服务器数据进行异地备份,数据量级:GB 级。收费的阿里云 OSS 等用不起,土豪请自便。免费的 Google Drive 连不上。

1.2、方案选型

一番折腾后最终选定七牛云免费 10G 对象存储。不够用就建多个账号,用不同的 butket 来备份。

1.3、准备工具

  • 七牛云账号一枚
  • 安装 qshell
  • 本地备份脚本,若无可参考本文 2.4 部分

1.4、实施细节

下载并设置脚本:

wget -c http://devtools.qiniu.com/qshell-linux-x64-v2.4.1.zip
unzip qshell-linux-x64-v2.4.1.zip
mv qshell-linux-x64-v2.4.1 qshell
chmod +x qshell

前往七牛云密钥管理,找到 AccessKey 与 SecretKey:

添加七牛云账号鉴权:

/path/to/qshell account <AccessKey> <SecretKey> <账户标识>

确认一下:

/path/to/qshell user ls
Name: xxxx
AccessKey: xxxx
SecretKey: xxxx

七牛官方建议大于 100MB 的文件使用 rput 来代替 fput 来实现上传。由于备份文件普通较大,这里统一使用 rput:

/path/to/qshell rput xxxx qshell-linux-x64-v2.4.1.zip /root/qshell-linux-x64-v2.4.1.zip
Uploading /root/qshell-linux-x64-v2.4.1.zip => xxxx : qshell-linux-x64-v2.4.1.zip ...

Put file /root/qshell-linux-x64-v2.4.1.zip => xxxx : qshell-linux-x64-v2.4.1.zip success!
Hash: lhKbzJ8255w0m4BKgtYpPthvU5Mc
Fsize: 9739680 ( 9.29 MB )
MimeType: application/zip
Last time: 23.54 s, Average Speed: 413.8 KB/s

上传多个备份文件,我的思路是:

  • 用 bash 脚本跑批
  • 事先 tar 到一个文件中,再用 qshell 上传

提供上述第二个方案的初步思路:

tar czf /www/backup/`date +%Y%m%d`.tar.gz /www/backup/site/*`date +%Y%m%d`* /www/backup/database/*`date +%Y%m%d`*
/path/to/qshell rput xxxx `date +%Y%m%d`.tar.gz /www/backup/`date +%Y%m%d`.tar.gz
rm -f /www/backup/`date +%Y%m%d`.tar.gz

至于循环备份,七牛云提供对象存储的生命周期管理。设置一下就行了:

Update:这个生命周期管理不靠谱,发现设置为一天删除也会一直存下去的情况。手动修改前述脚本如下:

#!/bin/bash
date=`date +%Y%m%d`
deldate=`date -d -5day +%Y%m%d`
/path/to/qshell delete xxxx $deldate.tar.gz
tar czf /www/backup/$date.tar.gz /www/backup/site/*$date* /www/backup/database/*$date*
/path/to/qshell rput xxxx $date.tar.gz /www/backup/$date.tar.gz
rm -f /www/backup/$date.tar.gz

2、墙外服务器

2.1、需求与掣肘

也是需要对服务器数据进行异地备份,数据量级:GB 级。优先选择免费的存储,回国较快即可。

2.2、方案选型

经测试,Google Drive 是第一选择。

2.3、准备工具

2.4、实施细节

若暂无本地备份策略,直接按这个链接的步骤操作即可;若已有本地备份策略,可在 backup.sh 的 BACKUP 数组变量中指定需要异地备份的文件路径,诸如:

BACKUP[0]="/data/backup/www_bak_`date +%Y%m%d`.tgz"
BACKUP[1]="/data/backup/db_bak_`date +%Y%m%d`.tgz"

可根据实际情况修改变量。

关于循环备份的事,若非自有脚本解决,可使用 backup.sh 的相关功能,配置以下两项即可:

LOCALAGEDAILIES(指定多少天之后删除本地旧的备份文件,默认为 7 天)
DELETE_REMOTE_FILE_FLG(删除 Google Drive 或 FTP 上备份文件的 FLG,true 为删除,false 为不删除)

最后,crontab 就略了。别忘记添加环境变量:

SHELL=/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
MAILTO=root
HOME=/root

我在实际配置过程中,曾遇到这样的报错:

Backup progress start
mysql is not installed. Install it and try again

最后在上面的环境变量基础上加上 mysql 的路径得以解决。

crontab -e

头部添加环境变量:

SHELL=/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/mysql/bin
MAILTO=root
HOME=/root

- EOF -

部署 MongoDB 的 Web 可视化工具:admin-mongo

作者:wiLdGoose 发布时间:March 12, 2020 分类:技术 Technology

不知道有多少同学遇到过类似反人类的需求:在 Web 上对 MongoDB 进行可视化管理。之前在服务器上用 node 跑了一套 adminMongo,甚是不稳定。用户端一些随机操作容易导致这个服务宕掉,查日志是这个样子的:

/adminMongo/node_modules/nedb/lib/datastore.js:77
    if (err) { throw err; }
               ^

Error: "toString()" failed
    at Buffer.toString (buffer.js:495:11)
    at tryToString (fs.js:453:15)
    at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:444:12)

Google 半天资料也较少,遂欲弃之。

期间也折腾了一下 phpmoadmin,说实话距离我美好的想象相去甚远。

后来重新折腾了一遍 admin-mongo,感觉是我以前的打开方式不对。记录之,用于自我马克。

操作系统 CentOS 7,以 yum 方式安装 node:

yum install nodejs -y

理论上会为您安装好 node 与 npm。确认一下:

node --version
npm --version

我这里的情况是:

node --version
v6.17.1
npm --version
3.10.10

继续安装 pm2:

npm install -g pm2

确认一下:

pm2 --version

我这边是:

pm2 --version
3.3.1

然后就可以直接安装 admin-mongo:

npm install -g admin-mongo

安装目录是 /usr/lib/node_modules/admin-mongo,因此:

cd /usr/lib/node_modules/admin-mongo
pm2 start app.js --name admin-mongo

看到类似这样的信息:

[PM2] Starting /usr/lib/node_modules/admin-mongo/app.js in fork_mode (1 instance)
[PM2] Done.
┌─────────────┬────┬─────────┬──────┬───────┬────────┬─────────┬────────┬─────┬───────────┬──────┬──────────┐
│ App name    │ id │ version │ mode │ pid   │ status │ restart │ uptime │ cpu │ mem       │ user │ watching │
├─────────────┼────┼─────────┼──────┼───────┼────────┼─────────┼────────┼─────┼───────────┼──────┼──────────┤
│ admin-mongo │ 0  │ 0.0.23  │ fork │ 18021 │ online │ 0       │ 0s     │ 0%  │ 13.3 MB   │ root │ disabled │
└─────────────┴────┴─────────┴──────┴───────┴────────┴─────────┴────────┴─────┴───────────┴──────┴──────────┘

到这里 admin-mongo 就开始提供服务了,监听端口 TCP 1234。验证一下:

netstat -antlp | grep 1234
tcp        0      0 0.0.0.0:1234            0.0.0.0:*               LISTEN      18021/node /usr/lib

好,完美。赶紧做个反代用起来,不想被直接访问到的话,我用了比较猥琐的办法:

echo 用户名:密码 > /usr/local/nginx/conf/vhost/xxx.passwd
vim /usr/local/nginx/conf/vhost/xxx.conf

在 location / 中增加:

auth_basic "请替换成您的登录提示";
auth_basic_user_file /usr/local/nginx/conf/vhost/xxx.passwd;

最后 server nginx reload 一下完事儿。

通过 lftp 备份文件及删除过期文件的自动化小脚本

作者:wiLdGoose 发布时间:August 27, 2019 分类:技术 Technology

可以看作是这篇文章的升级版本。这里感谢 druggo 留言提醒,让我决定抽时间折腾一下 lftp。

整个过程其实非常简单,照例自己马克一下。

安装 lftp 客户端:

yum -y install lftp

安装后使用过程中遇到报错:

Fatal Error: Certificate Verification: Not Trusted

原因是远端 FTP 服务器使用了自签发 SSL 证书,可以这样解决:

vim /etc/lftp.conf

在文件最后增加一行:

set ssl:verify-certificate no

改造后的脚本:

#!/bin/bash
date=`date +%Y%m%d`
deldate=`date -d -{NUMBER}day +%Y%m%d`
lftp ftp://{FTP_USER}:{FTP_PASSWD}@{HOST}:{PORT} <<!
cd /
lcd /data/backup
mrm *$deldate*
mput *$date*
exit
!

当然 lftp 还有一个更好用的指令:mirror。这里不用的原因是本地存储的时间与远端不一致。

依然是 3 点注意事项:

1、脚本中花括号内容替换为对应的变量;
2、cd 是远程的目录,lcd 是本地目录;
3、备份文件名需符合 date +%Y%m%d 这样的规则。

  1. 1
  2. 2
  3. 3
  4. 4
  5. ...
  6. 14