[编者的话] 当文字偶遇代码,当程序插上了翅膀,让分享成为我们彼此沟通的语言。我们期待可以构建这样一个平台让开发者们看到你们的智慧,挖掘你们的才华,让彼此在开源的路上不再孤独。“DaoCloud分享写作计划”已全面启动,欢迎投稿。
作者:乔立勇
我要做什么呢?
最近开始接触docker,作为一个软件geek,觉得这个是一个再方便不过的东西了。于是开始着手准备搞一个好玩的玩意。
开始吧,开始吧!
互联网时代讲究的就是效率,所以开发周期要快,要支持持续集成,要向后兼容。。。
版本控制工具+代码托管服务器,当然是git+Github了,开发语言python。 原材料都准备好了,做啥菜呢?
最初的想法是想做一个真随机数服务,linux系统里有随机数发生器,但是都是一些伪随机算法,所以如何用真的呢,参考了一篇IBMer的文章,英特尔是有这样的硬件去解决的。
但是目前docker好像不支持直接使用硬件设备?好吧,直接放弃了。 现在不是都说xaas么,基于此,下一个想法就是提供一个随机数验证码服务(random code as a service)
功能:
提供REST API服务,服务提供随机生成验证码并保证在某一个时间段内有效,提供增,删,查的功能。 看似易,实现难。
实现:
先找一个可用的api框架吧,第一选择是flask,原因是简单,容易上手。 很快就实现了创建随机码,以及查询功能,具体实现见代码,或者git log,很简单,我的想法是先把流程跑通,以后再慢慢迭代。
好了,代码有了,找一个Docker 服务器吧,Daocloud是基于docker提供服务的,是一家创业公司,功能和用户体验都不错,而且免费。
注册,登录,选择代码构建,创建新项目,然后指定git repository地址,okay了,然后干什么呢?
这货有持续集成和镜像构建:
持续集成需要一个yml文件, 也很简单明了,只包含一下几个部分:
env
:指定所测试中需要的环境变量install
:安装测试中必需的软件包before_script
:测试之前的准备工作script
:执行测试的脚本
image: ubuntu:14.04
env:
- db_backend = "mc"
- host = "10.10.71.37"
- password = "pTMRG4zepK"
- port = "49508"
- db = 1
install:
- apt-get update
- apt-get install python-pip -y
- apt-get install python-flask -y
- apt-get install git -y
- apt-get install curl -y
- apt-get install python-jsonschema -y
- apt-get install python-redis -y
- pip install python-memcached
- pip install logging
- echo $MYENV
- echo "This is an install segment"
- echo "Here, we usually run scripts to setup a base environment"
before_script:
- echo we use $mc as db backend
- echo "This is an before_script segment"
script:
- echo "first to run unit test"
- bash unit_test.sh
- python run.py &
- echo "This is an script segment"
- echo "Run test cases here"
- cd tests/functional/
- sleep 10 ; bash test.sh
- echo "Below shows how to use services"
有了这货,就可以放心的开发了,但是测试脚本要自己写,于是增加功能/单元测试脚本。
在代码根目录放置一个daocloud.yml
,定义好测试步骤,每次git push
都会触发daocloud 自动进行测试。
ps,我猜DaoCloud 后台一定是启动了一个Docker服务跑测试。
测试很快,大约3分钟,主要是准备环境和pull代码,如果有一个定制好的image(比如服务所必须的安装包都已经装好了),那么测试所需要时间就仅仅是pull代码和跑测试case的时间了,应该跟本地测没区别。
测试通过了,之后就是如何构建镜像了。
Docker需要一个Dockerfile用来进行镜像的构建
# random_app
#
# VERSION 0.0.1
FROM ubuntu
MAINTAINER Eli qiao <taget@xxx.com>
LABEL Description="Base image run randmo app" Vendor="Eli's BOOK" Version="1.0"
RUN apt-get update && apt-get install python-pip python-flask python-jsonschema python-redis -y
RUN pip install python-memcached
RUN pip install logging
# this is log file
RUN touch /random-debug.log
ADD run.py /run.py
ADD run.sh /run.sh
ADD app/ /app/
EXPOSE 5001
ENTRYPOINT ["bash", "/run.sh"]
使用 ADD
命令 copy 源代码到镜像文件,EXPOSE
的端口是服务端口,这个端口会自动跟域名绑定,所以通过域名访问的时候是不需要加端口号的,Docker 做了端口转发。ENTRYPOINT
是启动服务命令。关于如何写dockerfile可以参考Docker官方文档。
着急火撩地build完image,然后部署服务,居然访问不了。经过调试发现居然犯了一个愚蠢的错误,服务的listen 地址居然是本机,迅速改成 0.0.0.0
,重新build image,okay了。
至此,我的服务在DaoCloud服务中跑起来了,那么如何玩呢?
该服务提供了验证码 as service ,用户可以通过一个REST风格的API发起一个post请求,random_app
会帮你创建一个随机验证码,并将该验证码缓存到服务器,你可以在 time_out
有效时间里进行get,或者delete操作。是一个面向服务提供商/app的验证码服务。
以下是几个例子:
- 创建长度为2的验证码,有效时间为100秒。
curl -X POST -H "Content-Type: application/json" http://taget-ra.daoapp.io/random -d '{"length": 2, "time_out": 100}'
返回结果为:
{
"code": "38",
"time_out": 100,
"uuid": "3982956138870"
}
- 查询该验证码
curl -H "Content-Type: application/json" http://taget-ra.daoapp.io/random/38
- 删除该验证码
curl -X DELETE -H "Content-Type: application/json" http://taget-ra.daoapp.io/random/38
迭代与持续优化
服务起来了,下面就该进行持续开发优化了。
1. 作为一个开放的api服务,是不是对于用户的请求加以限制呢?对,必须的。
举个例子,用户说我要创建一个长度为10000000位的验证码,保存时间为10000000000秒。这请求不靠谱吧。 针对此,改进措施为通过python-jsonschema对输入的请求进行限制。
create_random = {
'type': 'object',
'properties': {
'length': {
'type': 'integer',
'minimum': 0,
'maximum': 12,
},
'time_out': {
'type': 'integer',
'minimum': 1,
'maximum': 65535,
},
},
'required': ['length'],
'additionalProperties': False,
}
好了,用户再也不能干坏事了。
2. 服务上线了,如果我想改接口,老用户就不能用了怎么办?
对于所有的API服务,为了兼容行,都要提供版本化的功能。
如何版本化呢?http请求都有header,虽然request body 都是一样的,我们可以在header中增加特定的选项提供区别,同时在服务端进行验证,根据不同的版本提供不同的服务,拒绝不支持的版本请求。
具体实现:
在header请求中增加X-Version,在服务端,定义个新的数据模型,对请求进行版本化,并提供不同版本之间的比较。
所以,对于创建请求就变成了如下:
curl -X POST -H "Content-Type: application/json" -H "X-Version: 1.011" http://taget-ra.daoapp.io/random -d
'{"length": 2, "time_out": 100}'
{
"error": "1.011000 is not support! min=1.000000, max=1.010000"
}
3. log,api服务没log让运营的怎么活?
一句话,加。(好像应该是一个字!)
代码不表。对于Docker 服务,只有一个entry point,也就是说只支持一条启动命令,咋办呢?
Docker启动的服务都是把std标准输出打出来,所以我们可以通过把要启动的服务,以及要观察的日志文件都放到一个bash脚本里,比如:
#!/bin/bash
set -x
# start service
python /run.py &
# tail for log file
tail -f /random-debug.log
TODO,如果以后日志太多怎么办?
- 放到 /var/log/ 里,并配置logrotation
- 服务去take care
4. 支持多后端
DaoCloud自带了服务集成功能,马上创建了一个redis的服务。服务启动后,会生成一个内部ip,一个port,还有一个访问密码,都是以环境变量形式提供的。所以我们的应用可以通过获取对应的环境变量提取这些变量值。
现在要做的就是让我们的服务根据配置选择不同的后端。
做法如下:
- 增加一个支持redis的driver,实现了和memorycache相同的接口。
- 传入db_backend变量,通过他去制定后端类型。
- 在manager层面驱动底层实现调用不同的driver。
最后将redis服务与我们创建的服务绑定,并通过环境变量传入相应的配置 random_app就支持了redis服务,最终实现了数据与业务的分离
下面,服务已经开始了,欢迎赏玩 http://taget-ra2.daoapp.io/
PS:在Linux下,创建一个随机验证码的命令为:
curl -X POST -H "Content-Type: application/json" http://taget-ra2.daoapp.io/random -d '{"length": 2, "time_out": 100}'
以下为图片展示:
绑定redis服务
DaoCloud 启动服务的日志
以及DaoCloud持续集成界面
本文来自“DaoCloud分享写作计划”,这项计划旨在为开发者提供一个平台,分享使用Docker的心得体会和技术经验。DaoCloud将为文章作者提供一定的物质奖励,具体方式请访问:DaoCloud写作分享计划 ,欢迎Docker爱好者和DaoCloud用户踊跃投稿。