搜狐云景paas平台实践之路

前言:

        搜狐云景作为搜狐的paas平台,在2014年5月22日的云计算大会上正式发布了公测。初测,注册用户必须先申请邀请码参与公测会赠送用户100元电子券,经过实名认证之后会再赠送100电子券,目测可以对试用用户基本app够跑半年。

        除了用户中心的一些基本安全信息设置和各种账单外,我想主要对其控制台的使用进行研究一番。

废话不多说,在绑定邮箱并充值10元成正式用户之后,无阻挡进行各种测试吧。

dashboard很清新干净,是一个对用户基本消费情况和使用资源服务的基本概览。


---------------------------------------------------

 

一、来创建个应用

        这里需要选择选择应用的基本类型是web或后台worker;

        运行环境需要事先选择好,其java就提供三种不同的运行环境,支持语言类型也挺多的,基本的主流语言都有支持到了;

        实例类型也就是容器类型,使用linux lxc技术,提供了六种不同的容器来运行我们的程序。


创建成功之后,会有一些默认的配置。

 [plain] view plaincopy在CODE上查看代码片派生到我的代码片
  1. 域名:cstest.sohuapps.com  
  2. 版本:1  
  3. 实例配置:3-8,可以自定义配置  
  4. 容器类型:C2,可以自定义配置  
  5. 黏性会话:关闭,可以自定义配置  
  6. 外网访问:关闭,可以自定义配置  
  7. git服务:git@git.cloudscape.sohu.com:cs/cstest.git,据了解后续会开启git push自动部署功能。  
  8. 自动调度:开启,可以自定义规则引擎,这个比较牛逼,目测国内paas还没有支持到这点的。</span>  

 

笔者发现,

 

如果我们没有上传程序包,它会根据我们创建应用选择的运行环境提供一个默认的程序包放到应用的版本1中。

启动之后,访问域名 http://cstest.sohuapps.com 就能看到一个很简单的hello world!

--------------------------------------------------

二、详解代码包

经过比较深入的了解,代码包结构首先无论哪种语言都只能是zip包的结构,解开之后的标准目录如下:


不难发现,有这样几个文件或者目录:

 

app.yaml 文件,
承载整个app运行的核心配置文件
app 项目运行的核心代码目录
页面代码目录,对于Java,是WEB-INF所在的目录;
对于PHP, 是PHP页面所在目录。
conf 个性化配置目录;
包括,nginx server段自定义配置、nginx server段自定义配置、自定义host配置等
bin 用户自定义脚本目

 

 

然后,我要重点剖析一下这个app.yaml文件,它的配置有无、是否正确、具体参数设置直接决定app的 运行状况和监控状态。

对于搜狐云景paas上提供的八种运行环境,它们的app.yaml配置都是不一样的,甚至 于python与python on webpy的app.yaml配置都是不一样的。

下面我对于云景提供的六种语言,八类环境的配置进行详细介绍,并为每个运行环境提供一个可运行的模板程序包,为大家提供参考。

 

2.0 全部参数

首先贴上一张提供的所有参数配置,但并不是每个运行的语言都能使用

 

2.1 Java on jetty

  1. appid: 666666666  
  2. start: $JETTY_START  
  3. tcp_health: false  
  4. tcp_health_port: [8001, 8002]  
  5. try_restart: {fall: 10, enable: true}  
  6. web_health: true  
  7. web_health_conf:  
  8.     path: /  
  9.     success: [200, 201, 403, 301, 302]  
  10.     timeout: 500ms  
 

2.2 Java on resin

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. appid: 666666666  
  2. start: $RESIN_START  
  3. tcp_health: false  
  4. tcp_health_port: [8001, 8002]  
  5. try_restart: {fall: 10, enable: true}  
  6. web_health: true  
  7. web_health_conf:  
  8.     path: /  
  9.     success: [200, 201, 403, 301, 302]  
  10.     timeout: 500ms  
 

2.3 Java on tomcat

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. appid: 666666666  
  2. start: $TOMCAT_START  
  3. tcp_health: false  
  4. tcp_health_port: [8001, 8002]  
  5. try_restart: {fall: 10, enable: true}  
  6. web_health: true  
  7. web_health_conf:  
  8.     path: /  
  9.     success: [200, 201, 403, 301, 302]  
  10.     timeout: 500ms  
 
 

2.4 PHP

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. appid: 666666666  
  2. start: $PHP_FPM_START  
 
 

2.5 LUA

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. appid: 666666666  
  2. start: $LUA_START  
 
 

2.6 NODEJS

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. appid: 666666666  
  2. web_health: true  
  3. web_health_conf:   
  4.     path: /  
  5.     timeout: 500ms  
  6.     success: [200,201,403,301,302]  
  7. try_restart:   
  8.     enable: true  
  9.     fall: 10  
  10. start: $NODEJS $USER_DIR/app/main.js  
 

2.7 PYTHON

[java] view plaincopy在CODE上查看代码片派生到我的代码片

  1. appid: 666666666  
  2. start: $UWSGI_START  
 
 

2.8 PYTHON with WEBPY

[plain] view plaincopy在CODE上查看代码片派生到我的代码片

  1. appid: 666666666  
  2. start: $PYTHON $USER_DIR/bin/main.py 8080  
  3. try_restart: {fall: 10, enable: true}  
  4. web_health: true  
  5. web_health_conf:  
  6.     path: /  
  7.     success: [200, 201, 403, 301, 302]  
  8.     timeout: 500ms  
 

2.9 RUBY ON RAILS

[plain] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. appid: 666666666  
  2. web_health: true  
  3. start: /opt/apps/ruby/bin/unicorn_rails -c /opt/src/app/unicorn.rb -D  
  4. stop: kill $(cat /opt/conf/unicorn.pid)  

 
 

总结一下,

appid是貌似9位随机数字,start是其根据配置命令启动的参数,这俩参数是必须滴。
从其app.yaml的配置来看,其格式要求是很严格的,必须严格按照 yaml的格式填写,包括空格,缩进等方面,比这就在这方面吃过亏。
其次,从其每种运行环境的配置来看,各项参数并不是在每个运行环境和语言中都能支持到的,需要做些调整,笔者最大化的使用了这些参数。
最后,要使用示例代码只需要,修改zip包中app.yaml中的appid为你的appid即可。
 
---------------------------------------------------------------------
 
 
 

三、高级进阶

提供语言支持太多,不能面面俱到,下面以java为例,介绍一下搜狐云景提供的一些高级功能。

3.1 日志输出

        搜狐云景对日志输出位置要严格要求的,不能随便自定义路径,只能输出到 /opt/logs目录下,而且自定义的日志文件是不会被采集和保存的,如果想要被云景paas收集并保存必须写到它们预定义的环境变量 ${LOG_FILE}中。
        由于笔者使用log4j作为日志输出,因此,给大家提供一个log4j的配置片段,如下:
 
  1. log4j.rootCategory=INFO,FILE  
  2.   
  3. log4j.appender.A1=org.apache.log4j.ConsoleAppender   
  4. log4j.appender.A1.layout=org.apache.log4j.PatternLayout   
  5. log4j.appender.A1.layout.ConversionPattern=[%d{yyyy-MM-dd HH\:mm\:ss}]%-5p %c(line\:%L) %x-%m%n  
  6.   
  7. log4j.appender.FILE=org.apache.log4j.DailyRollingFileAppender   
  8. log4j.appender.FILE.Threshold = INFO  
  9. log4j.appender.FILE.File=${LOG_FILE}  
  10. log4j.appender.FILE.layout=org.apache.log4j.PatternLayout  
  11. log4j.appender.FILE.layout.ConversionPattern=[%d{yyyy-MM-dd HH\:mm\:ss}]%-5p %c(line\:%L) %x-%m%n  

哈,是不是很简单呢。
但是,要知道如果要使用log4j的配置,还需要做一些特定的配置,比如需要配置一个servlet来加载logj.properties文件,不多说,自行搜索,笔者后面也会附上代码包供各位看官细细研究。
 
使用的方式不外乎如下:
 
  1. Logger log = LoggerFactory.getLogger(LogTest.class);  
  2.   
  3. log.info("info| " + Thread.currentThread().getName() + " is running!");  
  4. log.warn("warn| there is some warning messages");  
  5. log.debug("debug| I am doing system debug");  
  6. log.error("error| sorry ,some errors happend!");  
  7. log.trace("trace| unknown messages following!");  


3.2 服务使用

        纵观搜狐云景提供的服务,mysql,memcache,redis,storage等基本服务都支持,满足了我的大部分需求,笔者仅以使用redis为例介绍一下如何高大上的使用redis的缓存服务功能。
首先需要到服务中去申请一个redis服务,记住是可以自定义服务名称的哦。
        
 
然后笔者具体介绍一下代码中的使用方案。
        它们会提供一个URL告诉我们可用的服务列表,然后也提供了获取服务列表认证所需要的UID和password。
笔者建议使用提供的uid和password来在自己的程序中动态获取,http rest的get请求方式,你懂得。
 
        贴上两段代码,来获取redis实例,并构建使用它。
  1. //(a)、获取redis服务列表  
  1. static List<RedisInstanceNode> getNodes(String uid, String password) throws ServiceException {  
  2.   
  3.         Map<String, String> params = ParamUtils.getDefaultParams();  
  4.         params.put("uid", uid);  
  5.         params.put("password", password);  
  6.   
  7.         String endpoint = "http://internal.cloudscape.sohu.com";  
  8.         String action = "/redis/service_instance/nodes";  
  9.         String url = endpoint + action;  
  10.   
  11.         int timeout = HttpUtils.getRestTimeout();  
  12.         if (httpService == null) {  
  13.             httpService = new HttpServiceImpl();  
  14.         }  
  15.         HttpService.HttpResult result = httpService.httpGet(url, params, timeout);        
  16.   
  17.         String info = result.getResult();  
  18.         Map<String, JsonNode> res = JsonUtils.readValueAsJson(info);  
  19.         JsonNode nodesJson = res.get(ApiKeys._nodes);  
  20.         if (nodesJson == null || nodesJson.isNull()) {  
  21.             log.info("open api return error message, appinfos is empty , code: " + result.getCode() + ", message: "  
  22.                     + res.get(ApiKeys._message));  
  23.             return Collections.emptyList();  
  24.         }  
  25.   
  26.         List<RedisInstanceNode> list = (List<RedisInstanceNode>) JsonUtils.readValueAsList(  
  27.                 JsonUtils.writeValueAsString(nodesJson), ArrayList.class, RedisInstanceNode.class);  
  28.   
  29.         return list;  
  30.     }  
 
  1. (b)、根据返回的实例nodes列表构建redis 连接池,笔者使用了强大的jedis。
  1. private static ShardedJedisPool pool;  
 
  1. for (RedisInstanceNode redis : nodes) {  
  2.             String ip = redis.getIp();  
  3.             int port = redis.getPort();  
  4.             int master = redis.getIsMaster();  
  5.             JedisShardInfo jsi;  
  6.             if (master == 1) {  
  7.                 jsi = new JedisShardInfo(ip, port, "master");  
  8.                 jsi.setPassword(key);  
  9.                 jsi.setTimeout(3600000);  
  10.                 shards.add(jsi);  
  11.             } else {  
  12.                 continue;  
  13.             }  
  14.         }  
  15.   
  16.         JedisPoolConfig jpc = new JedisPoolConfig();  
  17.         jpc.setMaxActive(500);// 最大活动实例数目  
  18.         jpc.setMaxIdle(200);// 最大停止实例数目  
  19.         jpc.setMaxWait(5000);// 最大等待时间  
  20.         jpc.setTestOnBorrow(false);  
  21.   
  22.         pool = new ShardedJedisPool(jpc, shards);  
 
  1. //(c)、使用连接池,进行数据set与get  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. try {  
  2.             jedis = pool.getResource();  
  3.             jedis.setex("test-key", defaultTimeout, "test-value");  
  4.         } catch (Exception ex) {  
  5.             log.error(ex.getMessage(), ex);  
  6.             if (jedis != null) {  
  7.                 pool.returnBrokenResource(jedis);  
  8.             }  
  9.         } finally {  
  10.             if (jedis != null) {  
  11.                 pool.returnResource(jedis);  
  12.             }  
  13.         }  
好了,这样就能使用redis服务了,其余的几类服务类似吧。
 
 

3.3 ssh登录

首先说,这个功能必须赞一个,敢于把ssh功能开放出来,是一个很大的勇气。
上个图,

可以看到可以为每个实例单独的开启SSH管理功能,这就相当于是个VPS拉,前提是要上传了公钥之后才能使用这么强大的功能。
根据提供的ssh命令,可以登录到容器进行查看了。
      
 
不过,别失望,很多命令是不能使用的哦,看来是对命令进行了精简呢。不过这已经是很强大了,进去能看到我们的自己的日志了和进程等信息。
 
期待未来有更好更强大的功能。
 
虽然有这么多好处,但是还是有一些不足要吐一吐的。
 
  • 1、很抱歉,ssh的管理功能不能每个实例都能打开,最多只能起两个,好在可以关闭,自己能自由切换。
  • 2、然后,其停止和卸载app的地方隐藏的很深啊,类似于github上的要delete一个项目一样,要深层隐藏,可能为了确保用户不误操作吧。
  • 3、配置app.yaml比较麻烦,有个模板对应改是好点的。
  • 4、服务开放的虽然基本的够用,但是还是少了点,像很多cdn呐,mongodb啊,存储类的服务都没有,期待很快开放。
 联系email:pautcher@gmail.com
 
未完,待续......
 
以上内容,纯属笔者千辛万苦呕心沥血书写,如有雷同,纯属不幸。
 
posted @ 2014-06-13 09:50  pautcher  阅读(788)  评论(1编辑  收藏  举报