降低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 平台上自动回收实例及镜像的功能即可实现。
应用的增强与扩展