大约两,三年前就开始在VPS上跑Steem的程序了,后来有了Hive链,也开始跑Hive的程序。其实Steem/Hive的Python库还是很好用的。于是就有了几个Cronjob,点点赞,领取一些收益之类的。但近期随着对AWS服务了解的越来越多。感觉再在VPS跑Cronjob似乎有点Out了。于是开始考虑把一系列的Cronjob都搬到AWS上来,这样做最大的优势就是避免了以后服务器的维护成本。不用再考虑升级Ubuntu的各个版本/打各种安全补丁了。同时,也能节省一些银子。相信我,AWS的一系列Serverless服务真正用起来之后,在这些服务上的花费真的比想象中的要低不少。
下面就以一个最简单的Beem/Hive程序为例来说明。假设我们想把下面这个Python程序挪到AWS Lambda中去:
from beem.vote import Vote
vote = Vote(u"@aafeng/bolton-abbey|abit")
print(vote.json())
上面这个程序非常简单,只是返回一个包含A神对我的一篇文章的点赞数据。
下面来说说如何在AWS Lambda中运行这个程序。整个过程中遇到了两个坑,下面会详细说。
这里有一个前提条件,就是已经安装了serverless框架。如果没有安装,请先自行安装,并配置好AWS的账号信息(~/.aws/credentials)。
这一步需要在一个AWS Linux的EC2中运行,后面说到第一个坑的时候会详细介绍。当然,在网上也看到有人说可以创建一个AWS Linux的docker镜像,然后在docker镜像中运行pip来安装Python依赖库。但我并没有尝试。应该也可以吧。
如果我们想要写一系列的Beem/Hive程序,肯定不想把依赖库打包到每一个Lambda中。因此这里可以创建一个AWS中共享的依赖库(Layer)。
运行:
sls create --template aws-python3 --name beem-layer --path beem-layer
这就会创建如下目录和文件:
beem-layer
├── handler.py
└── serverless.yml
接下来创建目录:
cd beem-layer
mkdir libs
touch libs/requirements.txt
目录结构如下:
→ beem-layer tree .
.
├── handler.py
├── libs
│ └── requirements.txt
└── serverless.yml
requirements.txt内容如下:
beem
安装依赖库:
cd libs
pip install -t python/lib/python3.8/site-packages -r requirements.txt
修改serverless.yml文件如下:
service: my-layers
frameworkVersion: '2'
provider:
name: aws
runtime: python3.8
region: eu-west-1
layers:
beem-dependencies:
path: libs
compatibleRuntimes:
- python3.8
description: "Python beem dependencies"
在上面并没有定义Lambda函数,只是定义了layer,因此无需修改默认的handler.py,因为根本不会用到这个文件。
部署:
sls deploy
可以看到,很容易就在AWS中创建了一个layer。
注意:这里有第一个坑! 在后面的应用中使用这个layer的时候,总是提示找不到.so库。花了不少时间才搞清楚其原因在于:在安装Python依赖库时候的环境是Ubuntu 18,而在运行Lambda时候的环境则是Amazon Linux。两者不匹配自然就找不到所有的依赖库了。
因此最为简单的办法就是在Amazon EC2 (with Amazon Linux)中安装Python的依赖库。这样就保持了开发/部署环境和运行环境的一致。
首先运行如下命令创建serverless APP:
sls create --template aws-python3 --name beem-app --path beem-app
接下来修改serverless.yml:
service: beem-app
frameworkVersion: '2'
provider:
name: aws
runtime: python3.8
region: eu-west-1
functions:
hello:
handler: handler.hello
layers:
- "YOUR_LAYER_ARN"
修改handler.py:
import json
from beem.vote import Vote
def hello(event, context):
vote = Vote(u"@aafeng/bolton-abbey|abit")
response = {
"statusCode": 200,
"body": vote.json()
}
return response
部署:
sls deploy
运行
sls invoke -f hello
结果,遇到了第二个坑。提示错误信息如下:
{
"errorMessage": "[Errno 30] Read-only file system: '/home/sbx_user1051'",
"errorType": "OSError",
"stackTrace": [
" File \"/var/task/handler.py\", line 6, in hello\n vote = Vote(u\"@aafeng/bolton-abbey|abit\")\n",
" File \"/opt/python/lib/python3.8/site-packages/beem/vote.py\", line 52, in __init__\n self.blockchain = blockchain_instance or shared_blockchain_instance()\n",
" File \"/opt/python/lib/python3.8/site-packages/beem/instance.py\", line 29, in shared_blockchain_instance\n default_chain = get_default_config_store()[\"default_chain\"]\n",
" File \"/opt/python/lib/python3.8/site-packages/beem/storage.py\", line 46, in get_default_config_store\n return generate_config_store(SqliteConfigurationStore, blockchain=\"hive\")(*args, **kwargs)\n",
" File \"/opt/python/lib/python3.8/site-packages/beemstorage/sqlite.py\", line 200, in __init__\n SQLiteFile.__init__(self, *args, **kwargs)\n",
" File \"/opt/python/lib/python3.8/site-packages/beemstorage/sqlite.py\", line 61, in __init__\n os.makedirs(self.data_dir)\n",
" File \"/var/lang/lib/python3.8/os.py\", line 213, in makedirs\n makedirs(head, exist_ok=exist_ok)\n",
" File \"/var/lang/lib/python3.8/os.py\", line 213, in makedirs\n makedirs(head, exist_ok=exist_ok)\n",
" File \"/var/lang/lib/python3.8/os.py\", line 213, in makedirs\n makedirs(head, exist_ok=exist_ok)\n",
" File \"/var/lang/lib/python3.8/os.py\", line 223, in makedirs\n mkdir(name, mode)\n"
]
}
仔细读一下,原来是beem在运行的时候会尝试装载其配置文件,这个文件为~/.local/share/beem/beem.sqlite,如果该文件不存在,beem会尝试创建这个文件。而Lambda运行时只有对”/tmp“目录有写入权限的。因此出现了访问权限问题。
最快捷的解决方法就是直接修改文件:python/lib/python3.8/site-packages/beemstorage/sqlite.py
#self.sqlite_file = os.path.join(self.data_dir, self.storageDatabase)
self.data_dir = "/tmp/"
self.sqlite_file = "/tmp/beem.sqlite"
然后回到Amazon Linux,重新部署依赖库。
然后再调用Lambda:
sls invoke -f hello
返回结果为:
{
"statusCode": 200,
"body": {
"percent": 5000,
"reputation": "119989167017696",
"rshares": "13053196760301",
"time": "2020-10-27T11:27:18",
"voter": "abit",
"weight": 1202589,
"authorpermvoter": "@aafeng/bolton-abbey|abit"
}
}