我的 Daocloud 持续集成开发之旅

[编者的话] 当文字偶遇代码,当程序插上了翅膀,让分享成为我们彼此沟通的语言。我们期待可以构建这样一个平台让开发者们看到你们的智慧,挖掘你们的才华,让彼此在开源的路上不再孤独。“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服务 service

  • DaoCloud 启动服务的日志 log

  • 以及DaoCloud持续集成界面 ci


本文来自“DaoCloud分享写作计划”,这项计划旨在为开发者提供一个平台,分享使用Docker的心得体会和技术经验。DaoCloud将为文章作者提供一定的物质奖励,具体方式请访问:DaoCloud写作分享计划 ,欢迎Docker爱好者和DaoCloud用户踊跃投稿。