搬家

转自微博:IT土豪刘老师问我,你有没有一种感觉,就是其他人活的比你轻松幸福一些。我说嗯,常有,这一定是错觉对吧。他说,哦,那倒不是,应该都是真的。

我也时常这么感慨啊,可是感慨归感慨,自己还是要过自己的生活。

这两天在打包,准备搬家,为了陪伴孩子上小学,搬去小学附近租房,这一去不知何时还能搬回到这里、会不会再搬回到这里,但可以确定的一点是,即使能回来,到那一天,这里也必然不似今天这般模样了。

这个家承载着我们不多的6年多的时光,但它又那么难以释怀,它陪伴斯屹从十月怀胎一直到准小学生,又陪着斯诺走过人生最初半年;这个家陪伴我们跨过30岁,从新婚夫妻到为人父母,从初涉职场到为人前辈,人生中,可以堪称黄金年华的6年又能有几个呢。

还记得,刚刚搬到这里的时候,我们给还没出生的斯屹买了一张童床,我拍了给小床组名叫“虚位以待”的照片,向周围的人宣告了斯屹即将到来,小床又重新装饰漂亮,传给了斯诺,伴她度过了最初的几个月。我们不会再带走这张小床了,将来斯诺能否记得这张小床和这个家呢。

还记得,5年前的结婚纪念日,就在这个家里,我悄悄为爱人送上了99朵玫瑰,因为是清早敲开鲜花市场的门买到的新鲜玫瑰,回来像择菜一般,一点点修剪满是刺的99朵玫瑰,之后两个人傻傻地、开心地和一桶红玫瑰合影。玫瑰早已谢了,我们也将要离开这里了,芬芳却不会离我们而去。

还记得,3年多前,斯屹3岁的时候也从奶奶家搬回这里,和我们重新住在一起,开始上幼儿园,为了能和爸爸妈妈在一起,他从一开始就很坚强地忍住不哭,一直成长到后来的飞一样地骑着红色自行车,在楼后运动场上飞驰的少年,这里还有他幼儿园的小伙伴们,斯屹一定不会忘记这里的,这里有他最无忧无虑的童年。

当然也还记得去年圣诞节前,刚刚出生的宝贝诺诺从医院回到家,我和斯屹妆点上最最漂亮的圣诞树,温馨幸福的一家三口变成了温馨幸福的一家四口,诺诺虽然还不会说话,但七个月来,诺诺与这个家不曾分开,诺诺的小车也跑遍了楼前楼后,这次我们当然也会带着诺诺一起走,小诺诺将来一定记不起这个家了,但爸爸妈妈和哥哥的爱,会如这里的温馨一直陪着她长大,或许将来有一天她还会回到这里。

离开真是件让人忧伤的事情,总有点不知前路在何方的感觉,可我们又不能停下,只希望我们一家继续相亲相爱,希望我们的生活也能越来越舒适幸福,希望孩子们健康快乐。

上面提到的几张照片的链接:http://ift.tt/1mVoSkw 相对于他爷爷奶奶的“天通苑家”,儿子总把这里叫“蓟门桥家”,相册就也叫这个名字了。

后记,上次丢了自行车,写了篇博客,后来读者纪阳老师居然送了我一辆自行车,自行车如今仍在,昨天我打包的时候竟然还找到了买车的凭证,在此谢谢纪阳老师啊;这次搬家,半夜coding了一阵之后乎有所感,又写了一篇,就不期待有人送房子了,还是自己努力工作吧,哈哈。

from 我有分寸: http://ift.tt/1tiEpTx

推荐两款值得买的 Android 应用

BetterBatteryStats 和 XPrivacy,一个对电池,一个对隐私,都是瞄着别的应用的应用,总体感觉比较值得买。其实我买过很多移动应用 iOS 和 Android 平台都有,算起来可能有两千块钱了,总的讲,Android 平台真正值得买的好用的应用并不是很多,而且有不少买了之后就不再装了…… 不过这两款我还是要推荐一下。

BetterBatteryStats

Play Store 链接:http://ift.tt/YSpqgR ,这个应用可以统计手机睡眠相关的情况,帮你发现省点的方法。

首先说我的一个观点——手机是用来拿在手里玩的,不是用来省电的,如果你什么应用都不用,什么功能都不开,屏幕亮度还调到最低,那么,为啥花四五千块钱买个手机呢,去买个我老婆他们公司(最近改叫微软移动的那家)生产的两百块钱的手机好了。不过,手机虽然是用来玩的,可也只是有电的时候才能玩,所以省电也是需要的,BBS这款应用,可以帮你确定什么应用阻碍了手机进入睡眠,确定是ROM的问题还是应用的问题,进一步解决。

比如说这个——

可以看到,一半的 PartialWakeLock 都来自淘宝无线,相比之下,微信就很少影响到手机的睡眠了,而且我得说,我在这段时间里,动不动就看看微信,可是从来没打开过淘宝,所以,这个结果就是——我卸掉了淘宝,之前还卸掉了奇艺。

当然,这些问题只是当时有,不一定现在的版本还有,本着授之以渔的精神,我把应用介绍给大家,大家自己看吧。

XPrivacy Pro

XPrivacy 是 Xposed 框架下的应用,不在 play store 里买的,网站在这里 http://www.xprivacy.eu/ ,这个程序是开源的,可以在 GitHub 上得到源代码,付费也就是捐款,捐款得到 license 放到主存里就可以了。与之类似,CM 的 PrivacyGuard 也是用来限制应用权限的,不过 XPrivacy Pro 感觉功能更丰富一些,包括可以导出导入、可以看详细的权限使用记录,有模板,支持各个API的管理,非常丰富。

隐私这东西,看不见摸不着,而且保护隐私和便利性常常是冲突的,比如,有好几个应用可以拦截短信读取验证码,省去了看-背-输 这个过程,这本来是好好的,但我偏偏不喜欢。因为我不想授权给应用来读我的短信(虽然我的短信差不多全是机器自动发来的)。当然,这个见仁见智了,说得通,只是看你信不信、乐意不乐意了,但我们希望把决定权握在自己手里,而不只是装或不装。

Android 相比于 iOS 系统对每个应用的每个权限的授权功能是非常不足的,XPrivacy 就在这方面可以为用户提供更好的保护。我的用法是——对于要用的应用,开放必须开放的和我认为可以开放的权限,对于可用可不用的应用,如果有莫名其妙的权限要求,那就卸掉。这里也有个例子,就是今天在微博上吵的——

支付宝(和其他阿里的无线应用,诸如淘宝、虾米)就这样,即使你没打开它,它也在后台读你的剪贴板。其实读设备ID的请求,差不多所有应用都有,这个用于应用统计也不违反政策,如果咱不喜欢,关掉就是了,可是读剪贴板是为什么呢?Pocket 会在打开的时候读,刚好可以帮你收藏拷进来的链接,这个可以理解,但在后台运行的支付宝和虾米服务就比较难理解了,完全不知所云。

额外的废话

作为混迹技术圈这么多年的人,我和阿里很多人都是关系远近不同的朋友,其中很多都是比我强很多的大牛,我和阿里,特别是淘宝、支付宝没有任何恩怨,我贴他们是因为我用着他们,没装的应用谁会注意到呢。

有人说他们可能是在做好事,在帮助我或其他小白,嗯,可能是,我没看代码不知道你们在做什么,但一者,建议你们公开解释下这是在干什么,最好在 play store 的应用介绍里直接说出来为什么用这个权限,很多应用都这么做了;二者,希望你们给用户选择的权利,不是所有用户都喜欢你们这样的行为的,毕竟我们知道国内偷取用户隐私的下三滥应用很多,希望你们作为大公司出来的应用,能多点节操。

有人说爱用不用,XXX还也读剪贴板呢,对,就是这个道理,在便利和其他方面权衡是我自己做的,但我觉得你们这方面做得不好,拿出来吐槽有什么不行呢,反正在我用的应用里,在后台乱读剪贴板的就你阿里一家。

最后,我想很贱地问一句,你们阿里无线996工作了这么长时间,Android应用就做成这样(包括电池那段和剪贴板这段),你们觉得你们对得起你们加班的时间么?

from 我有分寸: http://ift.tt/1j1bcDM

Spray 中协议处理 Pipelines 的实现

最近在玩 scala,用到了 spray 来处理 Http,看了一下代码觉得很神奇,这里抄一段 spray 1.3 中的协议处理 pipeline 的实现,原始文件在 spray 中的 spray-io/src/main/scala/spray/io/Pipelines.scala

在 spray-io 中,网络协议可以拆分成多级流水线来处理,从网络到应用逐级升高,在前一级中处理低级事务,屏蔽掉一些底层机制,把高层决断交给后一级处理;而从应用到网络逐级降低,在前一级接受处理高层命令,分解成低级命令,交给后一级执行。spray-io框架规定了每层的接口,并帮助实现了拼接,而不同协议就负责实现自己的每级流水线。

比如 Spray-can 的 Http 协议栈处理中,就通过很多级的拼接来实现了一个协议栈:

    ServerFrontend(settings) >>
      RequestChunkAggregation(requestChunkAggregationLimit) ? (requestChunkAggregationLimit > 0) >>
      PipeliningLimiter(pipeliningLimit) ? (pipeliningLimit > 0) >>
      StatsSupport(statsHolder.get) ? statsSupport >>
      RemoteAddressHeaderSupport ? remoteAddressHeader >>
      SSLSessionInfoSupport ? parserSettings.sslSessionInfoHeader >>
      RequestParsing(settings) >>
      ResponseRendering(settings) >>
      ConnectionTimeouts(idleTimeout) ? (reapingCycle.isFinite && idleTimeout.isFinite) >>
      PreventHalfClosedConnections(sslEncryption) >>
      SslTlsSupport(maxEncryptionChunkSize, parserSettings.sslSessionInfoHeader) ? sslEncryption >>
      TickGenerator(reapingCycle) ? (reapingCycle.isFinite && (idleTimeout.isFinite || requestTimeout.isFinite)) >>
      BackPressureHandling(backpressureSettings.get.noAckRate, backpressureSettings.get.readingLowWatermark) ? autoBackPressureEnabled

这其中每一行都是一级流水线,>> 就是流水线拼接的方法,它们实现了这样一对流水线:

  /**
   * The HttpServerConnection pipeline setup:
   *
   * |------------------------------------------------------------------------------------------
   * | ServerFrontend: converts HttpMessagePart, Closed and SendCompleted events to
   * |                 MessageHandlerDispatch.DispatchCommand,
   * |                 generates HttpResponsePartRenderingContext
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | HttpMessagePart                     | HttpResponsePartRenderingContext
   *    | IOServer.Closed                     | IOServer.Tell
   *    | IOServer.SentOK                     |
   *    | TickGenerator.Tick                  |
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | RequestChunkAggregation: listens to HttpMessagePart events, generates HttpRequest events
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | HttpMessagePart                     | HttpResponsePartRenderingContext
   *    | IOServer.Closed                     | IOServer.Tell
   *    | IOServer.SentOK                     |
   *    | TickGenerator.Tick                  |
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | PipeliningLimiter: throttles incoming requests according to the PipeliningLimit, listens
   * |                    to HttpResponsePartRenderingContext commands and HttpRequestPart events,
   * |                    generates StopReading and ResumeReading commands
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | HttpMessagePart                     | HttpResponsePartRenderingContext
   *    | IOServer.Closed                     | IOServer.Tell
   *    | IOServer.SentOK                     | IOServer.StopReading
   *    | TickGenerator.Tick                  | IOServer.ResumeReading
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | StatsSupport: listens to most commands and events to collect statistics
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | HttpMessagePart                     | HttpResponsePartRenderingContext
   *    | IOServer.Closed                     | IOServer.Tell
   *    | IOServer.SentOK                     | IOServer.StopReading
   *    | TickGenerator.Tick                  | IOServer.ResumeReading
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | RemoteAddressHeaderSupport: add `Remote-Address` headers to incoming requests
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | HttpMessagePart                     | HttpResponsePartRenderingContext
   *    | IOServer.Closed                     | IOServer.Tell
   *    | IOServer.SentOK                     | IOServer.StopReading
   *    | TickGenerator.Tick                  | IOServer.ResumeReading
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | SSLSessionInfoSupport: add `SSL-Session-Info` header to incoming requests
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | HttpMessagePart                     | HttpResponsePartRenderingContext
   *    | IOServer.Closed                     | IOServer.Tell
   *    | IOServer.SentOK                     | IOServer.StopReading
   *    | TickGenerator.Tick                  | IOServer.ResumeReading
   *    | SslTlsSupport.SSLSessionEstablished |
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | RequestParsing: converts Received events to HttpMessagePart,
   * |                 generates HttpResponsePartRenderingContext (in case of errors)
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | IOServer.Closed                     | HttpResponsePartRenderingContext
   *    | IOServer.SentOK                     | IOServer.Tell
   *    | IOServer.Received                   | IOServer.StopReading
   *    | TickGenerator.Tick                  | IOServer.ResumeReading
   *    | SslTlsSupport.SSLSessionEstablished |
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | ResponseRendering: converts HttpResponsePartRenderingContext
   * |                    to Send and Close commands
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | IOServer.Closed                     | IOServer.Send
   *    | IOServer.SentOK                     | IOServer.Close
   *    | IOServer.Received                   | IOServer.Tell
   *    | TickGenerator.Tick                  | IOServer.StopReading
   *    | SslTlsSupport.SSLSessionEstablished | IOServer.ResumeReading
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | ConnectionTimeouts: listens to Received events and Send commands and
   * |                     TickGenerator.Tick, generates Close commands
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | IOServer.Closed                     | IOServer.Send
   *    | IOServer.SentOK                     | IOServer.Close
   *    | IOServer.Received                   | IOServer.Tell
   *    | TickGenerator.Tick                  | IOServer.StopReading
   *    | SslTlsSupport.SSLSessionEstablished | IOServer.ResumeReading
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | SslTlsSupport: listens to event Send and Close commands and Received events,
   * |                provides transparent encryption/decryption in both directions
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | IOServer.Closed                     | IOServer.Send
   *    | IOServer.SentOK                     | IOServer.Close
   *    | IOServer.Received                   | IOServer.Tell
   *    | TickGenerator.Tick                  | IOServer.StopReading
   *    |                                     | IOServer.ResumeReading
   *    |                                    \/
   * |------------------------------------------------------------------------------------------
   * | TickGenerator: listens to Closed events,
   * |                dispatches TickGenerator.Tick events to the head of the event PL
   * |------------------------------------------------------------------------------------------
   *    /\                                    |
   *    | IOServer.Closed                     | IOServer.Send
   *    | IOServer.SentOK                     | IOServer.Close
   *    | IOServer.Received                   | IOServer.Tell
   *    | TickGenerator.Tick                  | IOServer.StopReading
   *    |                                     | IOServer.ResumeReading
   *    |                                    \/
   */

流水线本身的实现非常简单,>> 操作符只有17行代码,整个 trait 也只有 26 行

trait RawPipelineStage[-C <: PipelineContext] { left => 
  type CPL = Pipeline[Command] // alias for brevity 
  type EPL = Pipeline[Event] // alias for brevity 
 
  def apply(context: C, commandPL: CPL, eventPL: EPL): Pipelines 
 
  def >>[R <: C](right: RawPipelineStage[R]): RawPipelineStage[R] = 
    if (right eq EmptyPipelineStage) this 
    else new RawPipelineStage[R] { 
      def apply(ctx: R, cpl: CPL, epl: EPL) = { 
        var cplProxy: CPL = Pipeline.Uninitialized 
        var eplProxy: EPL = Pipeline.Uninitialized 
        val cplProxyPoint: CPL = cplProxy(_) 
        val eplProxyPoint: EPL = eplProxy(_) 
        val leftPL = left(ctx, cplProxyPoint, epl) 
        val rightPL = right(ctx, cpl, eplProxyPoint) 
        cplProxy = rightPL.commandPipeline 
        eplProxy = leftPL.eventPipeline 
        Pipelines( 
          commandPL = (if (leftPL.commandPipeline eq cplProxyPoint) rightPL else leftPL).commandPipeline, 
          eventPL = (if (rightPL.eventPipeline eq eplProxyPoint) leftPL else rightPL).eventPipeline) 
      } 
    } 
 
  def ?(condition: Boolean): RawPipelineStage[C] = macro RawPipelineStage.enabled[C] 
}

这里解释一下这个拼接的实现:

  • RawPipelineStage自己是 left(左级),被拼接的是right(右级),拼接的结果仍然是 RawPipeline,可以拼接下一级
  • RawPipelineStage 有 apply<span lang=”zh-CN” style=”font-family:

    SimSun”>方法,这个方法应该带有参数(Context,CPL, EPL),返回 Pipelines,Pipelines 就是两条流水线,Command Pipeline (CPL) 是下行的命令处理,Event Pipeline (EPL) 是上行的事件处理,每个实际都是一个 Command 或 Event 处理的方法(Command => Unit 或 Event => Unit)

  • 在拼接时,
    • 如果右侧是空的,那么拼接的结果就是自己
    • 如果右侧不是空的,那么拼接的结果是一个RawPipelineStage,大意是:
      • 构造一个新的RawPipelineapply方法,参数为 ctx, cpl, epl
      • 如果左级不改变 cmd pipeline 的话,就调用右级的 cpl,否则用左级的cpl,但把右级的 cpl 当参数传给左级;对 cmd 来说,右级是左级的下一级,左级处理过交给右级
      • 如果右级不改变 event pipeline 的话,就用左级的 epl,否则用右级的 epl,但把左级的 epl当作参数传给右级;对event来说,左级是右级的下一级,右级处理过交给左级
    • 以一个方向为例:
      • 首先初始化 cplProxy 是一个处理 Command 的 Pipeline
      • val cplProxyPoint 是调用 cplProxy 的调用方法
      • leftPL 调用了左级的 apply,生成一个 Pipelines,而在生成的过程中,CPL 参数传入的就是 cplProxyPoint,也就是会调用 cplProxy
      • 定 cpl 为 rightPL 的 command pipeline,也就是说,leftPL 的 cpl 可以使用 rightPL 的 cpl
      • 最后,如果 leftPL 的 cpl 就是 rightPL 的 cpl 的话,也就是说,左级对 command 处理没有任何附加操作,那么就直接使用右级的 command 处理,反之,就使用左级的处理,但是左级可以利用作为参数传进来的右级的command 处理
      • event 处理与此对称

最终调用 RawPipelineState apply 的方法是 spray-io ConnectionHandler.running 方法,送进来的是 baseCommandPipeline baseEventPipeline,分别作为两者的最后一级

  • baseCommandPipeline 送给最右级作为下一级
    • TcpWriteCommandTcpCloseCommand 送给connection
    • (Tcp.SuspendReading | Tcp.ResumeReading | Tcp.ResumeWriting)backpressure相关的送给connection
    • Pipeline.Tell(receiver, msg, sender)消息,为sendermsg送给receiver
  • baseEventPipeline送给最左级作为下一级
    • 处理 ConnectionClosed 消息,等待tcpConnection Actor结束来自杀
    • 丢弃消息,对于 Droppable 的不warning,否则warning

就是这样,完成,非常简洁,有些晦涩。

from 我有分寸: http://ift.tt/1rI9hx3

为什么说航空博物馆不适合给孩子做科普

自从几个月前带儿子去了一次航空博物馆之后,一直想写blog吐槽一下,不过一直没有档期,终于,今天我抽空写两句吧,有些东西或图以后慢慢补。

之前带儿子去过环铁的铁道博物馆,展区虽然不是很大,展品也算不上特别多,而且以国产为主,但是孩子可以全方位接触机车,还是挺好玩的。可是,相比之下航空博物馆虽然展区很大,但是既没有什么知识介绍,又没有历史渊源,没有什么太多体验机会,甚至安全方面做得也很不好,一言以蔽之——别带孩子去。

介绍缺失 + 谬误

航空博物馆有露天展区和洞库展区,有庞大的歼-6机群,也有各种发动机等展品,可是几乎没有任何知识介绍,本来这都是很好的科普材料,帮孩子们认识活塞发动机和涡轮喷气发动机的构造,了解飞机的各种功能。可是,因为连个标牌都没有,就有很多家长在给孩子瞎介绍,比如——

  • 有的把副油箱当炸弹
  • 有的把空速管当大炮
  • 有的把进气道当大炮
  • ……

这些孩子们可能要带着这些错误的认识好多年了,所以,如果各位不知道我上面说的都是什么东西,那最好也别带你的孩子去航空博物馆了。

这些只是不作为,还有些更离谱的,洞库里有一段科幻小视频,说的是未来某年的先进制造和设计方法,嗯,幻想这东西可以跑点火车哈,可是,你设计个扑翼机还要飞入外太空是闹哪样啊,在真空里扇翅膀真的能飞动么?!

来历不明的展品

馆藏的丰富程度确实是一个博物馆的硬实力,但是,即使是不很丰富的博物馆,用心做还是可以让大家有很多收获的。可航空博物馆里,确实藏了一些外国飞机,但是,很多飞机都是无型号、无来源、无介绍的展品,不知道摆放在这里干什么

  • 有一架飞机没有标型号,据我观察可能是米格-23,我国空军似乎从来没有这个机型,不知来源
  • 有一家飞机没有标型号,据我观察是达索幻影的某型号,我国空军似乎从来没有这个机型,不知来源
  • 有一架飞机没有标型号,据我观察是F-104,飞机上写着意大利空军赠送
  • 有一架飞机没有标型号,据我观察是F-5,应该是从湾湾飞来的
  • 有一架直升机,应该是米-24,不知来源
  • ……

这么多飞机都摆进来了,写个牌子,说说从哪来的,是啥很难么,如果不能说,还摆出来干啥呢?

缺乏安全保护

某客机的舷梯可以走上去,我就让我儿子上去拍照了,他上去我才发现,舷梯靠飞机那边,有一侧是没有保护,如果孩子玩得太 high,是可能掉下来的,心有余悸。在此提醒各位,如果你看了我的介绍,还想带着孩子去,那一定要看好了,如果想上啥,看清楚结实不结实,有没有栏杆啥的。

小结

嗯,图先没上,有空发一些,基本上展品乏善可陈,介绍基本没有,很多展品来历不明,维护状况也不怎么好,想想那么大片展区,转一圈累得够呛,可是收获却没啥,真的不怎么值。唯一的优点是,露天展区是免费的,只有进洞库或者进馆才要钱。总评必然是负分。

顺便说,参观航空博物馆那天是3月8日,看着很多客机,然后忽然听说马航客机失联,心里真不是滋味,加重了我的负面情绪……唉,飞机残骸还没找到,太可怜了……

from 我有分寸: http://ift.tt/1mW6xqE

近期云计算技术的发展与热点技术

从IaaS到PaaS

我这里不是说PaaS要取代IaaS,而是说,IaaS和PaaS两点之间已经连成一条渐变的线了,两者不再是泾渭分明的了,中间遍布了很多介于两者之间的服务,这之间的很多创新点都在过去一段时间里逐步成熟了。

IaaS与PaaS的差距在哪里,其中一个视角是他们所面向用户的不同——

  • PaaS 所面向的用户是开发人员,他们设计架构、写代码、有时候做集成测试和 Code Review,然后就发布代码,让用户使用了,也就是说,PaaS把代码变成产品(网站、服务或是什么其他的东西)
  • IaaS 所面向的用户则是运维人员,他们根据架构师或开发人员的要求构建基础设施,部署和配置软件系统和各种基础服务,部署和升级开发人员完成的代码,监控并处理故障,根据负载调整系统规模等

当然,各位可能已经看到,这里IaaS上运维人员的很多职责实际已经有服务在帮助完成了,比如预先部署好的数据库服务、消息队列服务、负载均衡服务、MapReduce服务等,监控系统运行的各种监控服务以及一些自动扩展和自动故障恢复服务等。

现在已经没有单纯占据资源层的“纯IaaS”服务了,构建在IaaS服务基础上的各种附加服务正在解放云上的运维人员,云服务的用户可以根据自己的能力、财力和具体需求,挑选一些服务,结合自己的工作量身打造适于自己的系统。

这样,云服务的层次就变得更丰富一些了

----------------------------
           PaaS
----------------------------
预部署服务 | 监控管理  |
-------------------  >IaaS+
       资源服务      |
----------------------------

并且,资源服务也不再只是传统的主机+对象存储+块存储+IP地址了,再过去一段时间,云服务的引领者亚马逊AWS一直在积极推进VPC(虚拟私有云),帮助用户可以通过子网结构化地组织系统,通过网关、路由、虚拟设备和各种其他规则,更安全、更有效地使用云上资源。这些日益丰富的资源让人们可以更方便地使用云服务,降低了云上运维的很多困难,不过也让很多选择和配置变得更加复杂了。

Orchestration

前面我提到,PaaS 帮助用户“把Code变成产品”,又说,IaaS用户相比之下还要做部署、升级、监控、事件处理这些事情,于是,一个自然地想法是,如果“部署…事件处理”这些事情可以变成Code,那么,用户所使用的到底是PaaS 还是 IaaS 呢?

Infrastructure as Code 是 DevOps 粉丝们特别喜欢的一个概念,代码化的描述,让整个系统的部署更加严谨可控,并且可以通过版本管理来追踪发现问题、调整部署。

在云上,由于亚马逊等主流云服务商丰富的API支持,甚至是监控、系统级的事件处理(比如硬件故障导致宕机)这些传统的运维工作也可以通过代码进行自动化的处理,这就演化出了近两年走红的 Orchestration 的概念.

----------------------------
           PaaS
----------------------------
      Orchestration
----------------------------
预部署服务 | 监控管理 |
-------------------  
          资源服务      
----------------------------

这实际是通过一些代码,把 IaaS 用得像 PaaS 服务一样。当然,这些代码不是非常容易写的,于是就有了很多帮助用户在 IaaS 的灵活和 PaaS 的易用之间进行折衷,我所在的 MadeiraCloud 也是在这一领域进行工作,通过图形化的交互界面,把复杂的集群部署和管理工作变得直观而高效。

当然,Orchestration 是帮用户简化不必要的劳动,通过各种附加资源和引导方法协助用户快速地进行系统设计和部署,避免因为繁琐而造成的出错,用户仍然需要相应的知识,理解他所做的选择,才能藉此灵活高效地使用云服务资源,Orchestration 并非要直接取代 PaaS。

Container & Docker

从去年云计算大会之后,Docker 所代表的 Container 技术快速走红,以至于不论是否明白 Docker 意味着什么的人,都在茶余饭后大谈 Docker 和 CoreOS 云云。

Container 技术实际历史非常悠久,而且不同的 Container 之间也有不少差别,作为 PaaS 服务商 dotCloud 开源的技术,Docker 所带来的影响,实际上更提现在运维方面,而不仅仅是在计算虚拟化的层面。

传统的云上部署,运维人员面临两个抉择:部署快、可重用、很少出错的自定义 Image;灵活、可以经常升级、调整的 DevOps 工具。两者各自的优点就是对方的缺点,如今,docker 的 OS Image 分层装配机制会让系统的部署工作产生这样的变化

|     /\         DevOps Tools   |        /\      <-- DevOps Tool
|    /  \    <-- e.g. chef,     |       /--\
|   /    \       puppet, salt   |==>   /    \    <-- Docker Layers
|  /------\                     |     /------\
| /        \ <-- OS Image       |    /        \  <-- Base OS Image
|/__________\                   |   /__________\

这是因为 docker 具有如下优点:

  • 分层装配,比 OS Image 更灵活
  • 每层更完备,recipe 会比传统 DevOps 工具短得多,更简单,更少犯错
  • 可以和集成测试结合起来
  • 增量分发,且装配速度比 DevOps 工具逐条执行快
  • 甚至可以撤回出错的变更,这一点 DevOps 工具的传统工作方式很难做到

此外,分层的文件系统让不同的 container 之间可以共享更多的文件,这样,从硬盘空间到内存缓存都可以极大的降低每个 container 的开销,使得“一个应用一个Container”的目标更容易实现。

小结

以上是我作为一个在云计算领域淫浸了几年,对运维领域有偏爱的开发者,对于云计算的近期发展趋势的观察,仅供参考,不周或不对的地方还请谅解,不对判断和预测承担任何责任。

from 我有分寸: http://ift.tt/1gVciAD

2013回望

先发文字版,配图和视频稍后上传

2013就这样,势不可挡地奔向了终点,当我们又一次将圣诞树装饰好的时候,去年的平安夜仍然历历在目,今年却换成了不一样的感动,从那时起,我便决定,要记下这份心境。今天,冬至日,竟得到难得的一个小时空闲,我想,我该放下一切,来回首这过去的一年了。

岁岁年年心不同

去年的圣诞,妻子、儿子和我,一家人围着点亮的圣诞树,唱起了“Jingle Bells”,我和爱人不由得相拥无言,结婚这些年,算不上颠沛流离,却也经历了不少风风雨雨,一家人享受着难得的平安夜,什么都没有发生,却又那么的不寻常,怎能不让人感动呢。

今年的圣诞节前,我们收获了一个健康的女儿,儿子也长大了,我和儿子一起采购了很多的饰品,把那颗圣诞树装饰得满满当当,彩灯再次亮起,我和儿子不由得再次唱起了“Jingle Bells”,儿子甚至还能记住那歌词,仿佛回到了去年。但就在这短短的一年里,我们居然又收获了一个女儿,我也让自己的职业生涯来了一个转弯,这都是在前一年还毫无打算的,也让我们对人生充满了敬畏。

看着绚丽的圣诞树,今天的我所想的,是珍惜眼前的每一天,珍惜身边的每个人,珍惜内心的每一分感动,投入地享受生活的每一个瞬间,让亲人和朋友也不后悔与我分享的每一刻。

我们的女儿

知道今天,在我的潜意识里,仍然感到

我孩子 == 我儿子 == 我唯一的儿子

可是,我知道,已经不同了,当我看到她的那一眼起,我的孩子已经包括了我的儿子和我的女儿,两个小生命了。女儿在圣诞节前刚刚回到家,她很健康,很精神,也很像她哥哥刚出生的时候,看着小小的她,我再次找到那种被依赖的感觉,一种因为付出而带来的幸福。

我和爱人并没有执着于“儿女双全”,也并不坚信独生子的孤独,我们偶尔只是希望自己可以有两个孩子,希望我们的爱有更多的寄托,怀孕甚至有些意外的成分,而且我们也没有预料到怀孕会给老婆带来如此大的风险,如果当初预计到的话,可能我们不会选择冒着生命危险生下她吧,这也算是她和我们特殊缘分的一部分吧。

从怀孕初期的险些流产,到怀孕末期子宫伤口的危险裂痕,这一切的坎坷,如今都已经过去,王斯诺已经不可逆转地健康地来到我们身边,而这些还要感谢用手术刀为我们迎来第二个小生命的杨孜大夫,王斯屹也是由她接出的,真的非常感谢她。

女儿如今刚刚6天大,希望一年后,她可以与我们一起围坐在圣诞树旁,两年后,能和我们一起唱圣诞歌,三年后,能和我们一起装饰圣诞树,年年快乐……

儿子的成长

孩子的成长,总是最让人赞叹的,他在你眼里,总是那么小,那么需要保护,以至于他的进步总是超出我们的预料,王斯屹的这一年,进步同样是这样的惊人。

在和我一起去马来西亚旅游之后,他的英语课表现开始有了巨大的进步,而且由于我们加强了复习和辅导,如今他在英语课上也表现十分突出,那次小广播更是表现得非常棒。

他在幼儿园的拼音课和思维课上也表现得很不错,甚至在晚上讲故事的时候,也能经常拼出一些字来,对于很少刻意教他识字的我们来说,这已经算是惊喜了。

而且,在其他方面,现在的斯屹,不仅能非常自如地骑他的自行车了,而且也学会了跳绳,游泳学习也取得了不少进步……

但是,对于我们这些父母而言,孩子最大的进步是他在性格和气质方面的进步,如今的王斯屹,言语行为之间表现出了相当的自信,更积极、更主动一直是我们对孩子的期望。

女儿出生之前,我们非常注重和儿子的沟通,一方面让他有准备要有一个妹妹出生,另一方面,也不断向他强化,尽管有妹妹出生,我们依然非常关心爱护他,他和妹妹之间没有冲突,让我们非常满意的是,王斯屹不仅对妹妹没有什么反感,甚至经常主动来帮爸爸妈妈照顾妹妹。

希望我们的儿子,在未来能够更加聪明、健康、自信、有进取心、有责任感、会照顾人,成为一个优秀的男子汉。

家庭的变化

孩子是家庭的重要部分,不是唯一,我们小家庭的另一个支柱就是我的爱人,她这一年,尤其是怀胎十月,真是太不容易了,中间损失了一次马来西亚海岛游。如今孩子已经健康出生,妈妈却还没完全复原,希望她能够快快康复。

由于之前怀斯屹的时候胎心不好,提前将斯屹剖宫产出,老婆的子宫上是有疤痕的,这在胎儿足月生产的时候,会有破裂大出血的风险,再次怀孕绝对是冒着生命危险的行动,这些“好了伤疤忘了疼”的妈妈,是她们的宝宝的勇敢的救世主。这种危险并不是一个虚幻的概率,而是切实的存在,斯诺出生后送入婴儿室处理,而她的妈妈迟迟在四十多分钟之后才从手术室推回病房,我在外面心急如焚。之后,杨孜大夫随这老婆一起来到病房,她告诉我老婆的子宫已经有了5cm的裂痕,极其危险,如果不是及时剖出的话,后果不堪设想。

我想我们不会再要下一个宝宝了,希望老婆身体尽快复原,我们明年全家一起去旅行。

此外,这一年间父母乃至奶奶的身体都在向着不好的方向发展,尤其是父亲的心脏和肾脏,都很让人揪心,希望他可以配合医生检查、治疗,我们还等着让他健康地看着孩子们长大,乃至结婚生子,一家人享天伦之乐呢。

职业生涯的改变

如果不是王斯诺的出生,5月17日离职盛大云,6月4日加入麦德云本应是我的年度头条新闻的。人的职业生涯随时可能改变,况且以盛大的背景,加入的那天起就只愿一起拼搏一阵,不曾期望共度余生。不过,从盛大离职,加入Madeira Cloud这家只有十几个人的小公司,仍然是让我始料未及的。

我本来尝试了一些跨过大公司的,不过,一方面过程并不顺利,另一方面我也反思自己,觉得自己的能力更适合于在小团队中发挥,所以还是决定加入了这家我看好的小公司,用我自己的话说,就是趁着自己还不算老,抓紧做点不靠谱的事情。这个转变过程,我还要特别感谢我爱人的支持和鼓励,人迈出自己的舒适区不是一件容易的事情,爱人的认同和支持,是我做出这个决定的坚强后盾。

各种未尽

2013年,各种变化让我应接不暇,现在我还处在一个“不稳定”的状态中,在家里,不断有新的小状况出现,需要一一处理,在工作中,我也还没有完全做到通过努力证明自己的选择是正确的,这种种未尽之处正是我接下来要努力征服的。

尾声

2013年,别了,这一年是如此的不同寻常,我会想你的;2014年,让我们更进一步。

from 我有分寸: http://wangxu.me/blog/p/799

【译文】LSFMM: 缓存 — dm-cache 与 bcache

原标题: LSFMM: Caching — dm-cache and bcache
原作者: Jake Edge
原文链接: https://lwn.net/Articles/548348/
原文发布时间: May 1, 2013
译者: 王旭 ( @gnawux )
译文时间: 2013年6月2日

LSFMM 2013 峰会上,Mike Snitzer, Kent Overstreet, Alasdair Kergon, 和 Darrick Wong 共同主持了一个讨论,内容是关于两个彼此独立的块设备层缓存方案 —— dm-cache 和 bcache。 Snitzer 首先介绍了 3.9 kernel 引入的 dm-cache。这个方案使用率内核中的 device mapper 框架,实现了快速设备对慢速的“原始”设备的 writeback 或 writethrough 缓存。

Snitzer 介绍到,dm-cache 的原理是使用 device mapper 核心,并在上面增加一个策略层。这个策略层“非常类似”一个插件接口,可以实现不同的策略。这些策略(以及缓存的内容)决定了缓存是否可以命中,是否需要进行数据迁移(在原始设备与缓存设备之间进行某个方向的数据移动)。目前已经实现了包括最近最少用(LRU)、最常使用(MFU)等策略,但目前只有缺省的“mq”策略合并到内核中了,以便减少起初需要测试到的策略数量。

文件系统可以为策略提供 hints,比如某些块脏了,或是某些块已经被放弃了。这些信息有助于策略更好地决定块要存储到的位置。

之后,Overstreet 介绍了 bcache 的状态更新,后者已经在排队进入 3.10 了。他介绍到,bcache 已经有“很多用户”了,而且代码已经比较稳定了。他最近主要是在进行 bug fix。与 dm-cache 实现的的分级存储不同,按照 Overstreet 的介绍,bcache 更像一个传统的缓存。它可以用来存储任何 extents,甚至是是一个扇区,而 dm-cache 只能对整块数据进行缓存。

就在峰会开始前,Wong 给多个邮件列表(包括 dm-devel, linux-bcache, linux-kernel)发出过一封 email 来比较 bcache, dm-cache, 和 EnhanceIO。他分别编译了带有上述各个特性的内核,进行了一些测试。他发现,EnhanceIO 是最慢的,bcache 是它的 4-6 倍,而 dm-cache 更快,达到了 15 倍,不过 dm-cache 无法完成有些测试。所有比较都在一块普通硬盘上进行了相同的测试,有时,由于未知的原因,dm-cache 的表现或多或少的和硬盘完全一样。他指出,有些测试会导致 mkfs 创建的 inode table 倍缓存住,而通常这并不是一种有效的使用缓存的方法。

Snitzer 表示,他曾试着去重现 Wong 的测试结果,但结果对 bcache 和 dm-cache 都得倒了更差的成绩。他说他希望和 Overstreet 一起去找出原因。而 Overstreet 则表示,不应该过多地看综合测试的成绩,这些测试是游泳的,但可能带有误导性。Ric Wheeler 提问,Snitzer 是否“在真实工作负载下看到了实质性的性能提升”,Snitzer提到,在不同的 Git 标签之间进行切换就是 dm-cache 可以完胜的一个例子,不过他需要一些帮助来获得更贴近实际使用的负载。

一个参会者问道,这些方案是否假设缓存设备总是存在。Snitzer 说,dm-cache 确实如此假设,但这是需要改进的地方。Overstreet 说, bcache 并不要求 cache 设备一直都在。因为 bcache 已经在实际产品中使用了,所以它有机会去碰到这些疑难场景,并可以处理这些缓存设备无法工作的情形。

Snitzer 表示,dm-cache 确实还有很多事情要做。起初,它是进行 cache 和原始设备的并行 IO,但最终不得不回到顺序 IO。而且,对于流水线中有 NVM 设备的情况下,存储层次也是如此。因为 dm “到处都是分层”,dm-cache 刚好适合进行存储分层,不过 Overstreet 指出,bcache 也可以做到分级存储。

这个讨论最终没有一个明确的结论,只是提到需要让两个方案的“真实世界”性能读数变得更好。同时也指出了,不同的测试者进行的测试得到的结论差异是巨大的,这也是真实世界的情况的一个写照。

from 我有分寸: http://wangxu.me/blog/p/787

兰卡威环岛自驾游

按:这是一次旅游的记录,不是攻略,所述内容仅供参考

5月12-18日,原定一家三口的马来西亚兰卡威之旅最后成行的是我和我儿子两个人,打破了我家以往旅游的规律——我家旅游不能只有男人——可以是我老婆一个人,也可以是老婆和其他一个或多个任意家庭成员。不论如何,兰卡威确实是个漂亮的小岛,整个环岛自驾游还是很让人兴奋的,将来有一天,我们一家三口还是会一起出来旅游的,希望还有机会再来这里。

首先简单标记一下我们在兰卡威岛的主要落脚点:

兰卡威自驾游主要活动区域

我们在岛上活动了5天,此外还有一晚住在吉隆坡,全程一共7天6晚,疲劳而充实,行程是这样的:

  • 11日晚上北京出发,亚航航班
  • 12日早上到达吉隆坡LCCT,中午到达市区、入住,下午在双子塔附近闲逛,看了看夜景
  • 13日上午出发,中午到KLIA,乘坐马航的航班,下午到兰卡威,租车,到珍南海岸,入住,在这里住两天
  • 14日环岛一圈,去了东方村坐缆车,去看了黑沙海滩,去看了总理展览馆,去游荡了红树林,看了喂老鹰
  • 15日去芭雅岛浮潜,看小鱼,当晚到兰卡苏卡码头,乘坐小船上了里巴克岛,晚上在海滩抓小螃蟹
  • 16日上午在里巴克岛休闲,下午上兰卡威岛,去海底世界看了一下
  • 17日上午在里巴克岛休闲,抓小螃蟹,游泳,中午下岛,去瓜镇入住,当天下午进行了一些采购,还看了兰卡威的标志性建筑,巨鹰广场
  • 18日上午去丹容鲁海滩看了两个小时海,然后又去看了下黑沙海滩,之后直奔机场,还车,乘坐马航班机回吉隆坡KLIA,转到LCCT,再乘坐晚上的班机回北京
  • 到北京已经是19日凌晨了

下面分几个主题讲讲

海滩

我们最早入住的是珍南海滩(Pantai Cenang)是兰卡威开发得最丰富的旅游区,有漂亮的海滩,各式餐馆,免税店和Mall,还有大型水族馆Under Water World。海滩不错,我和儿子去泡了泡,可惜时间有限,从去芭雅岛那天开始,就再也没工夫在这个海滩上转悠了。这里的缺点可能就是人比较多了,毕竟是开发比较充分的海滩嘛。

Untitled

黑沙海滩(Pantai Pasir Hitam)我们去过两次,第一次去的时候大概是午后的高潮位,基本没看到什么海滩,第二次去是要走之前,最后停留一下,因为这个海滩差不多就在丹容鲁海滩到机场的路上,这次看到海滩了,其实很漂亮,水非常清透——实际上整个兰卡威的海滩的海水都很透,黑色的沙滩是因为历史上火山喷发出来的石油类东西掺在沙子里。嗯,我不抽烟,没带打火机,所以也没去试试能不能点着哈。另外,这里还是每年帆船赛的时候。

Untitled

丹容鲁海滩(Pantai Tanjung Rhu),最后一天我们才去的这里,就是想看看就走的,不过这里很清净,沙滩、海面都很漂亮,可能是岛上最漂亮的海滩了,虽然没看到传说中的海豚,我们还是觉得最后一天多加了10块钱的油来这里一趟是很值的。

Untitled
Untitled

里巴克岛(Rebak Island),这里是 Taj Hotel 的独家海岛(滩),和其他海滩一样漂亮,人不多,小螃蟹之类的动物很多,居然还能抓到挺大号的寄居蟹,岛上很舒服,上岛的独家小码头兰卡苏卡码头(Langkasuka Port)景色也很漂亮。

Untitled
Untitled

其他还有传说中的 Pantai Teluk Datai 和 Pantai Teluk Burau 两片海滩,我们都没顾上去,不过后者我们在东方村应该俯瞰倒了。

主要观光活动

东方村的缆车挺不错的,风景非常好。

Untitled
Untitled

芭雅岛的浮潜不能错过,水非常清透,而且里面鱼很多,游得旁若无人,还有不咬人的小鲨鱼出没。

Untitled

蝙蝠洞、红树林。这里是必须租船出去的,按船租,1小时的套餐包含了蝙蝠洞、小鱼农场、红树林+遥望泰国,以及喂食老鹰,要价250马币,合500块钱人民币。我的感觉是这个价钱是值的,整个过程非常棒,尤其是可以近距离观察老鹰,当然,一条船可以坐不止七八个人,现场想要找人share一艘船非常困难,如果能在酒店找到同行的人的话会更值一些。兰卡威岛离泰国非常近,从红树林这里的河口可以直接遥望泰国,手机都收到“泰国移动欢迎您”的短信了。

Untitled

瓜镇的Eagle广场自然是要去的,巨大的老鹰就是兰卡威的地标,兰卡威的意思就是鹰。广场的喷泉里还有七八只大乌龟,嗯,类似最近北京立交桥匝道处经常有人提着的大龟那么大。

Untitled

购物

瓜镇的几家免税店里的巧克力都很便宜,我们买了一些,其他觉得没啥可买的了。某些免税店搞得杂货铺似的,一点都不高端大气,实在难以接受啊。

自驾相关

关于自驾,这里的车是右舵的,非常不适应,最后一天从黑沙海滩出来去机场的时候,还直接开到逆行了呢……不过这里的油很便宜,这么多天就用了40马币的油,合大概80块钱人民币。租车是70马币一天,自动档的,水平大概和夏利相仿吧,我还给儿子租了一个儿童座椅,多加了70,不过为了安全,我觉得这个钱不能省。

自驾必须有导航才爽,我带上了之前在淘宝九块九包邮买的吸盘夹子,把手机固定住用来导航,还是很物有所值的。期间前几天用的 Google Maps,后几天用的Waze。实际上 iOS 的 Google Maps 对自驾游基本够了,而且,只要计算路线的时候有网络接入就行,开车期间没有信号也没关系。Waze的好处是,有语音导航(iOS 的 Google Map 传说也有,没找到),显示信息比较丰富,而且也比较好玩。

各种槽点

说道槽点,先说说语言吧,据传说马来西亚有接近三成的华人,不过实际感受好像没那么多,到处都是印度味儿的英语…… 偶尔有几个用中文招揽生意的黑车,嗯。不过真到需要的时候努力找,还是能找到说中文的人的。

然后是电话,Digi 的套餐流量不错,450MB/week,打国内也蛮便宜,0.1马币1分钟,合人民币2毛钱一分钟,嗯,这是国际长途啊,就是信号太不好,在吉隆坡和瓜镇有3G,但珍南海滩就没有3G,甚至岛上有些地方都没有覆盖。更有甚者,我刚在微博上吐槽了他们的覆盖,结果半个岛的线路故障,持续了两天,连电话都打不了,大家一定选 Hotlink 啊。

吉隆坡相关

嗯,吉隆坡停留时间太短,没啥可说的,机场到市区非常非常远,亚航的 LCCT 和马航的 KLIA 是吉隆坡国际机场的两个航站楼,之间有大巴,大概15分钟车程。

总结

总的说,来兰卡威就是各种看海,而且海水也确实非常清澈,透明度很好。沙滩算不上非常细,但差不多每个沙滩上都能找到螃蟹这样的小动物,显得很有趣。

更多照片在 flickr 上

from 我有分寸: http://wangxu.me/blog/p/783

在新 Thinkpad S3 上使用 bcache 作为 Linux 的根分区

前两天采购了一台“全球首发”的 Thinkpad S3 超级本,激动之余,就把不会用的 Windows 8 给抹掉,装上 Linux 了。这台机器的配置如下

CPU i5-3337U
内存 8GB
芯片组 Intel HM77
显卡 CPU集成 +
Radeon HD 8670M
硬盘 500GB SATA + 24GB mSATA SSD

嗯,其他先不说了,亮点基本就这些。新机器,一大堆支持得不好的东西,比如 suspend to ram 我到现在还没配好,先不说了,这次主要说硬盘。

基本思路

这台机器有一块过小的 SSD 和一块主硬盘,因为SSD太小了,以至于我不想把哪个分区整体放到上面,于是,想到了分级存储的策略——使用SSD作为缓存,让热数据缓存在SSD上,冷数据留在机械硬盘上,是的,这个和被广泛宣传的 Apple 的 Fusion Drive 很类似,当然,Apple 不是第一个开发这种技术的公司。

在 Linux 上,最广为传颂的这类技术就是 bcache 了,但有一个问题,bcache 直到 3.10 才进入 kernel,而 3.10 到目前为止还只是 -rc3。也就是说,如果要装 Debian、Ubuntu、Fedora 的话,你至少要等半年,才能找到一张支持 bcache 的启动盘。不过,我还是想用下这个个 Distro,于是,就采用了这样一个思路(思路抄袭自这个 Arch 的 Wikipage,有调整):

  1. 先在 SSD 上装一个临时的系统
  2. 升级内核,支持 bcache
  3. 在主硬盘上做一个分区,作为 bcache 的后端存储
  4. 把装好的系统迁移到上面的 bcache 分区
  5. 修改 grub 重新启动,引导到 bcache 上去
  6. 干掉 SSD 上的分区,作为 bcache 的缓存

整体过程就是这样,中间波折无数,下面就介绍一下。具体的折腾过程中,有多次反复操作(不过装系统只有一次,我的系统观是——重装就是认输,只要有一丝希望,就反复折腾,拒绝重装,不抛弃、不放弃),后面的介绍省掉了一些无畏的折腾,尽量直接的介绍,不过各位重试的时候有什么我没写的问题,可以和我交流一下哈,没准我也遇到过了。

引导、初次安装、定制内核

装系统之前,我手头没有方便可用的 X86_64 架构的 Linux,只有一台 MBP,所以,有些东西比较费劲。启动盘的制作是在 MBP 上进行的,下载这个镜像

ubuntu-13.04-desktop-amd64.iso

不要那个 +mac 的,这样才能 UEFI 模式启动,在 Mac 下制作启动U盘的方法其实不困难:

hdiutil convert ubuntu-13.04-desktop-amd64.iso -format UDRW -o ubuntu-1304.img
dd if=ubuntu-1304.img.dmg of=/dev/disk2 bs=1m
diskutil eject /dev/disk2

简单地说就是,先从光盘变成镜像,然后再把镜像 dd 到 U 盘去,嗯,各位用的时候小心点顺序 disk2 是我的 U 盘,我的 Mac 物理上有 SSD 和 SATA,所以 U 盘是 disk2 。

这个镜像制作的 U 盘是可以在超级本上 UEFI 引导的,能 UEFI 引导非常重要,因为这样才能安装支持 EFI 的 grub,我一开始没注意这点,后来又修复了半天引导程序。

嗯,另外,分区的时候,要留出单独的 boot 分区,毕竟 grub 是不认 bcache 的。原有的 EFI 分区啥的也没有动,我把 grub 也做在 HDD 上了。

然后是定制内核,本来我注意到 Ubuntu PPA 里面有主线内核的 Builds,于是就下了 3.10.0-rc3 的 deb,启动之后发现没有 bcache,拿来 config 一看,居然没选上 bcache,摔。只好自己下 kernel 来 build。为了图省事,直接拿了 ubuntu 的 config,只多选上了 bcache (在 device drivers –> md 里面,和 raid 之类的在一起 ),不过,话说时间是守恒的,你在 config 的时候偷懒没做精简,就在 build 的时候遇到麻烦…… 那些没用的杂七杂八模块好废时间啊,build 了一个小时…… 下次一定好好精简。

顺便说一句,在 debian/ubuntu 下 build kernel,最好安装 kernel-package 包,然后用 fakeroot make-kpkg 来制作 kernel deb 包,这样比较好管理。我的新 kernel 看起来是这样的

gnawux@square:~$ ls src/*.deb
src/linux-image-3.10.0-rc3-bcache_3.10.0-rc3-bcache-10.00.Custom_amd64.deb
gnawux@square:~$ uname -a
Linux square 3.10.0-rc3-bcache #1 SMP Wed May 29 11:28:22 CST 2013 x86_64 x86_64 x86_64 GNU/Linux

-bcache 是我自己加的后缀,嗯,接下来重启到新内核,就支持 bcache 了

启用 bcache,引导系统

现在重启之后,系统已经支持 bcache 了。需要做三件事情

  1. 打开 bcache
  2. 把系统迁移上去
  3. 修改 grub,引导新系统

打开 bcache 之前,最好准备一下用户态工具

http://evilpiepirate.org/git/linux-bcache.git

git下来,然后 make install 就可以了,需要 uuid-dev 包。用户态工具主要包含了 udev 工具链上的规则,并且会加入到 initramfs 里面去,还包含了制作 bcache 分区的工具。

下面要制作 bcache 后端分区,后端就是机械硬盘上的大分区

make-bcache -B /dev/sda8

就酱紫就可以了,然后告诉 kernel 这货可以用了

echo /dev/sda8 > /sys/fs/bcache/register

不过我记得有了 udev rule,这个实际不用 echo 了,当然,多来一次没啥坏影响。

之后,在 /dev/bcache0 上 makefs,然后迁移系统。我是懒得再用 bootstrap 方式重装一个系统了,我选择直接复制,大概过程是这样的:

mount -o bind / /mnt/orig
mount /dev/bcache0 /mnt/new
cd /mnt/orig
tar -cvpf - * | tar -C /mnt/new/ -xf -

然后修改新系统的 fstab、修改 grub.cfg 的 kernel 命令行,让 root 指向 bcache0,然后重启就可以成功了。

成功引导之后,把 SSD 上的老的系统做掉,变成 bcache 的 cache 分区

make-bcache -C /dev/sdb1
echo /dev/sdb1 > /sys/fs/bcache/register

这时,/sys/fs/bcache/ 下就会出现这个 cache 分区的 UUID,比如

root@square:~# ls /sys/fs/bcache/
6a6b9acd-e205-4a10-a64f-6c048841c824  register  register_quiet

现在,让这个 cache 服务于 bcache0 ,这既可以在 bcache0 上操作,也可以在后端设备 sda8 (我的分区号是这个)上操作,比如

echo 6a6b9acd-e205-4a10-a64f-6c048841c824 > /sys/block/bcache0/bcache/attach

或者

echo 6a6b9acd-e205-4a10-a64f-6c048841c824 > /sys/block/sda/sda8/bcache/attach

之所以给出后面的方法,是因为如果由于没挂上 cache 之类的问题,bcache0 没有出现,仍然可以用后面的操作。现在再来,就已经完全打开 bcache 了。

修复关机和开机的问题

完全打开 bcache 之后,发现关机不能……开机没问题,但关机按电源是无法接受的啊,因为这之后有 IOError,判断应该是 driver 的某个问题导致导致的,比如和 bcache 在关机过程中的操作顺序有关系,研究之后,发现在关机的时候 detach 掉 cache 分区就可以正常关机了,编辑 /etc/init.d/umountroot 文件,在 ro remount 跟分区之前,加入一段

    while [ -e /sys/block/bcache0/bcache/cache ]
    do
            echo "detach bcache"
            echo "6a6b9acd-e205-4a10-a64f-6c048841c824" > /sys/block/bcache0/bcache/detach
            sleep 2
    done

这段的意思是,只要 cache 还在,或者说还没有完全把 cache 刷入磁盘,就一直 detach,这样,关机就正常了。

关机正常之后,开机进不了根分区了,似乎是只要关机前 detach 掉 cache,开机的时候 bcache0 就不会正常产生了,因为我们没有支持 bcache 的启动盘,所以必须在没有系统的情况下就地解决这个问题。这时,在 initramfs 的界面里,输入上面那句:

echo 6a6b9acd-e205-4a10-a64f-6c048841c824 > /sys/block/sda/sda8/bcache/attach

然后,ctrl-d 退出 busybox,就可以继续正常启动了,下面的操作显然是要对 initramfs 下手了。对于 ubuntu 的 initramfs-tools(8) 来说,启动脚本位于 /etc/initramfs-tools/scripts/ 下面,其中,local-top/ 目录中的脚本是在本地文件系统设备出现之前执行的,我们要在这里确保根分区设备 /dev/bcache0 就位,最后我是使用了这样一个脚本:

root@square:~# cat /etc/initramfs-tools/scripts/local-top/bcache
PREREQ="udev"
prereqs()
{
    echo "$PREREQ"
}
case $1 in
# get pre-requisites
prereqs)
    prereqs
    exit 0
    ;;
esac

#source /scripts/functions

timeout=16
while [ ! -d /sys/block/sda/sda8/bcache ] ;
do
   if [ $timeout -eq 0 ] ; then
    break
   fi
   echo  "$((timeout=timeout-1))" > /dev/null
   sleep 1
done

[ ! -e /sys/block/sda/sda8/bcache/cache ] || echo 6a6b9acd-e205-4a10-a64f-6c048841c824 > /sys/block/sda/sda8/bcache/attach

原理就是等待 bcache 设备出现,然后把 cache 给它 attach 上,然后用 update-initramfs 重新生成 initrd 就行了。

小结和一些有用的链接

总结起来就是——别怕出错,一直往下做,不管咋的,都会有办法的,计算机就在手上,电源都可以按,还怕啥。出了问题要冷静,找到问题,捅一下可能就好了,不行就多试几次。

给一个参考链接吧,用来修复Ubuntu的引导程序的,有时候有用,嗯

from 我有分寸: http://wangxu.me/blog/p/778

Protected: 两年之后,再次告别

This post is password protected. To view it please enter your password below:

Password:

from 我有分寸: http://wangxu.me/blog/p/773