降低IT成本:OpenStack自动回收与镜像最佳实践
技术背景
本文所介绍的解决方案应用于 OpenStack 云管理平台,使用 Python 编程语言实现必要的功能,利用邮件服务器和 Web 服务器完成用户自服务,作为自动回收机制的补充。
自动回收的价值
提高 IT 资源的利用率
通过自动回收 OpenStack 中的实例和镜像,避免存在和积累闲置的实例和镜像,使得 IT 资源(CPU、内存、存储、网络等)保持有效的使用状态,最大程度地提高 IT 资源利用率,保护用户的 IT 投资。
提高 Cloud 上的系统性能
对于一个特定的 Cloud 平台,它的最大负载性能是有限的,而闲置的实例和镜像仍会占用部分这有限的性能,这在一定程度上会影响同一平台上其它系统的性能。以存储 I/O 性能为例,越是少量的系统的进行并发读写,对其中单个系统而言所拥有的性能越是良好。
节省 IT 成本
启用自动回收可以避免用户在 IT 投资上的无谓的浪费,无须盲目地增加投资而一样可以达到负载的需求。
如何实现自动回收
自动回收的条件和前提
- 使用 OpenStack 云管理平台
- 可以利用一个现有的邮件服务器
- OpenStack 用户需要有电子邮件地址信息
- OpenStack 数据库中记录有对实例和镜像的最后修改时间,对此值的修改不应影响其它任何功能。
自动回收机制的设计
自动回收通过 web service 实现检测实例和镜像的状态,完成与相关用户的交互,最终达到回收的作用,工作原理如图1所示。
图 1. 自动回收工作原理图

本解决方案中通过 Jenkins 每天触发并完成一次回收工作,实际应用中可根据需要采用不同的触发方式和频率。
在 OpenStack 的数据库中记录资源信息及所有者信息,因此可以准确地与资源对应的所有者通过电子邮件进行交互。
通 过比较资源的最后修改日期和当前日期,可以识别出将过期或已过期(周期可配置,详见清单1)的资源:对于已过期的资源,将执行自动清除并通知所有者;对于 将过期的资源,将提醒所有者进行延期或主动清除操作。操作方式仅需点击一次邮件中的链接。若直至最后一次通知,所有者仍未进行任何操作响应,意味着该资源 将正式过期并被自动清除。
自动回收的具体实现
首先,对于自动回收服务是可以根据不同的环境和需求进行配置的,如清单1所示。当自动回收服务(见下文中 Python 实现的 web service)启动时,将读取该配置使得回收工作能正常进行。
清单 1. 定义自动回收配置
[SERVICE] port=set me [WHITELIST] users=set me or keep empty instances=set me or keep empty images=set me or keep empty [LIFETIME] keep_days=set me remind_days=set me [DATABASE] host=set me nova_user=set me nova_pwd=set me keystone_user=set me keystone_pwd=set me [CLOUD] OS_USERNAME=set me OS_PASSWORD=set me OS_TENANT_NAME=set me OS_AUTH_URL=set me [EMAIL] smtp_server=set me smtp_user=set me smtp_pwd=set me
[SERVICE]: 自动回收 web service 的相关配置
- Port: web service 的服务端口
[WHITELIST]: 白名单配置,用于避免某些资源被自动回收
- Users: 指定用户清单,他们的资源将被视为永不过期
- Instances: 指定实例清单,视为永不过期
- Images: 指定镜像清单,视为永不过期
[LIFETIME]: 资源的生命周期配置
- Keep_days: 资源可保持的时长,以天为单位
- Remind_days: 通知时间范围,以天为单位,指在资源生命周期最后一段时间内开始对所有者进行通知提醒
[DATABASE]: OpenStack 数据库的连接配置
- Host: 数据库服务器地址
- Nova_user: nova 数据库的用户名
- Nova_pwd: nova 数据库的用户密码
- Keystone_user: keystone 数据库的用户名
- Keystone_pwd: keystone 数据库的用户密码
[CLOUD]: OpenStack 云的管理帐号信息
- Os_auth_url: OpenStack 的授权验证服务地址
- Os_tenant_name: OpenStack 中需要进行管理的 tenant 名称
- Os_username: OpenStack 中用于管理以上 tenant 的用户名
- Os_password: OpenStack 中上述用户的密码
其次,通过运行简单的 Python 程序(见清单 2),即可启动一个 web server,也可以将该 web service 运行为 linux 系统服务(见清单 3)。
清单 2. 启动轻量 web 服务器
if __name__ == '__main__':
serveraddr = ('0.0.0.0', int(SERVICE_PORT))
srvr = HTTPServer(serveraddr, RequestHandler)
srvr.serve_forever() 清单 3. 创建 linux 系统服务
function start()
{
echo "Starting OS_Recycle ..."
daemon "python ${PY_FILE} ${CONFIG_FILE} >> ${LOG_FILE} 2>&1 &"
sleep 2
status
} 在以上 web service 中,为所需要的功能实现 REST API,本解决方案中为优化用户体验,对资源的延期及回收操作同样采用 GET 响应。见清单 4。
清单 4. 定义 REST API 实现功能
def do_GET(self):
self._writeheaders()
self.apiGET()
def apiGET(self):
path=self.path.split('/')
if len(path) == 2 and path[1] == 'instances':
self.wfile.write(json.dumps(self.getInstances()))
...
def getInstances(self):
#connect to nova database and keystone database to get data
...
instJson=json.loads('[]')
for inst in instances:
for user in users:
if user[0] == inst[1]:
instJson.append(json.loads('{"created_at":"%s", "user_id":"%s",
"hostname":"%s", "ip_address":"%s", "uuid":"%s",
"user_name":"%s"}' %
(inst[0], inst[1], inst[2], inst[4], inst[3], user[1])
))
break
return instJson 程序中对于资源及过期信息的查询操作,匀直接通过连接数据库查询实现,DB2 SQL 命令示例:
novaSql = "SELECT char(a.created_at),a.user_id,a.hostname,a.uuid,cast(b.NETWORK_INFO as varchar(1000)) from instances a left join INSTANCE_INFO_CACHES b on a.uuid=b.INSTANCE_UUID \ where a.project_id = '%s' and a.deleted_at is null" % CLOUD_OS_TENANT_ID
对于资源的延期操作,即更新资源的最后修改时间为当前时间,意味着重置生命周期,DB2 SQL 命令示例:
novaSql = "update instances set created_at=current timestamp where uuid='%s'" % uuid
而对于资源的清除操作,则需要通过调用 OpenStack 相关的 REST API 实现,见清单 5。
清单 5. 调用 OpenStack REST API 实现删除操作
def deleteInstance(self,uuid):
req_url = '%s/tokens' % CLOUD_OS_AUTH_URL
resp, resp_body = httplib2.Http().request(req_url, 'POST',
headers={'Accept':'application/json', 'Content-Type':'application/json'},
body='{"auth": {"tenantName": "%s", "passwordCredentials": {"username": "%s", "password": "%s"}}}' %
(CLOUD_OS_TENANT_NAME, CLOUD_OS_USERNAME, CLOUD_OS_PASSWORD)
)
if resp['status'] != '200':
return False
resp_json = json.loads(resp_body)
token_id = resp_json['access']['token']['id']
for service in resp_json['access']['serviceCatalog']:
if service['name'] == 'nova':
nova_admin_url = service['endpoints'][0]['adminURL']
break
if not nova_admin_url:
return False
resp, resp_body = httplib2.Http().request('%s/servers/%s' % (nova_admin_url, uuid), 'DELETE',
headers={'Accept':'application/json', 'X-Auth-Project-Id':'%s' % CLOUD_OS_TENANT_NAME,
'X-Auth-Token':'%s' % token_id }
)
if resp['status'] != '204':
return False
return True 在资源将要过期或已被自动回收时,需要对所有者进行邮件通知,在邮件中提供必要的 link 供所有者进行操作。示例见清单 6。注意本程序中使用的是 SMTP,并且要求应用程序能够正常连接到指定的邮件服务器,而该邮件服务器亦能正常发送邮件到达资源所有者。
清单 6. 邮件通知资源所有者
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
...
SMTP_SERVER='set me'
SMTP_USER='set me'
SMTP_PWD='set me'
msg = MIMEMultipart()
msg["From"] = SMTP_USER
msg["To"] = 'set me'
msg["Subject"] = "set me"
msg.attach(MIMEText("set me"))
mailServer = smtplib.SMTP(SMTP_SERVER, timeout=20)
mailServer.ehlo()
mailServer.starttls()
mailServer.login(SMTP_USER, SMTP_PWD)
mailServer.sendmail(SMTP_USER, msg["To"], msg.as_string())
mailServer.quit() 至此,OpenStack 平台上自动回收实例及镜像的功能即可实现。
应用的增强与扩展