在Docker容器环境中用Let's Encrypt部署HTTPS

昨天尝试了用Let's Encrypt给本站部署了HTTPS,感觉这个服务真的是非常方便。网上有很多关于Let's Encrypt的文章,不过本站因为部署在docker容器中,关于这种架构的文章不多,我把自己的思路写下来吧。

先来说点基础的话题。

HTTPS可以提供全面的密码学保护,换句话说,访问HTTPS网站时,用户和网站之间传输的数据不会被第三方窃取和监听。这里的第三方包括黑客、运营商、监管部门,有你想得到的也有你想不到的。

HTTPS是基于公钥密码的,简单说就是服务器提供一个公钥,用户用这个公钥加密数据后发给服务器,然后服务器用自己的私钥解密。这里面有一个问题,即如何确定服务器的公钥是真的,如果第三方拦截并伪造服务器的公钥,用户用第三方的假公钥加密数据,那么这些数据就会被第三方解密并窃取。

为了避免这样的问题,就出现了证书。证书本身是个数字签名,就像给服务器的公钥盖个公章,用户看到公章就知道公钥是真的了。但是,这个问题没有根本解决,怎么知道公章本身是不是真的呢?因为公章的本质是数字签名,于是浏览器里面内置了一份“可信公章清单”,凡是在这份清单里的签名都认为是可信的。

继续阅读

固态硬盘(SSD)为什么需要TRIM?

固态硬盘(SSD)突破了传统硬盘的速度瓶颈,并且工作稳定、无噪音、耗电少,随着成本的下降,大有取代传统硬盘的势头。使用SSD的人大多听说过TRIM,操作系统一定要支持TRIM才能让SSD的性能实现最优化,那么TRIM到底是什么东西,为什么SSD需要TRIM而传统硬盘却不需要呢?

TRIM的出现,实际上是由于SSD在执行数据删除、覆盖操作时和传统硬盘在原理上的差异所造成的。我们知道,存储设备(无论是SSD还是传统硬盘)只负责最底层的物理存储,并不知道所存储的数据到底有什么意义,反正操作系统让我读或写什么数据我照办就可以了,而将物理设备中的数据组织成目录、文件并赋予它们意义的,是文件系统(File System)负责的工作。文件系统是操作系统的一部分,由操作系统负责管理,不同的操作系统支持的文件系统不一样,例如较古老的FAT16/32、Windows的NTFS、OS X的HFS+、Linux的ext(ext2/3/4)等等。

那么为什么说问题出在数据的删除和覆盖上面呢?我们来看一下删除文件的时候操作系统是如何处理的吧。首先,一个文件在物理设备上是存放在多个数据块中的,这些块可以是不连续的,文件系统中会有一张表(例如FAT中的文件分配表)来管理每个文件的数据所对应的存储位置。那么,在删除的文件的时候,操作系统只要将该文件所对应的数据块在文件系统中标记为“空闲”就可以了,根本不需要实际去清除数据块中存放的数据。

结果:存储设备只知道哪些地方存了数据,但不知道这个数据到底还有没有用(因为文件删除之后,数据实际上可能还留在数据块中),数据有没有用只有操作系统才知道。

当我们需要存放新文件时,那些已经标记为“空闲”的数据块就会被当作空的数据块来使用(尽管里面实际上不是空的),对于操作系统来说,它们和原本就没有内容的空数据块是完全一样的。但这样的设计必须建立在一个前提下:

对于物理存储设备来说,“写入空白数据块”和“覆盖已有内容的数据块”所需要的操作是完全相同的。

上述前提对于传统硬盘来说是完全成立的,传统硬盘的工作方式跟磁带差不多,数据的记录是根据介质上某个记录单元的磁化方向来完成的。也就是说,在写入数据时,磁头只要将指定的记录单元(扇区)磁化为所需的状态即可,完全不必关心这个单元原本是怎样的状态。

然而,上述前提在SSD上却是不能成立的!为什么呢?因为在SSD中,只有空数据块才能直接执行写入操作;而对于非空数据块,需要先执行擦除操作之后才能进行写入。

乍看之下,SSD和传统硬盘的区别仅仅是多了一步擦除操作而已,但实际上并非如此,更要命的还在后头。在SSD中,数据存储的最小单位是页面(page),一个页面的大小一般是4KB,若干个页面又被组合成块(block),一个块的大小一般是512KB。由于硬件方面的限制,SSD单独对某个页面进行读/写的操作,但擦除操作却只能对整个块进行,也就是说,一旦擦除就必须一次性擦除整个块。想想看,如果操作系统要让SSD改写某个页面的数据,SSD需要执行怎样的操作呢:

  1. 将要改写的目标页面所在的整个块的数据读取到缓存。
  2. 在缓存中修改目标页面的数据。
  3. 对整个块执行擦除操作。
  4. 将缓存中的数据重新写入整个块中。

这就意味着,如果我要修改某个4KB大小的页面,就必须把512KB大小的整个块都折腾一遍,大家应该可以想象出这将带来何等巨大的性能和寿命上的损失。

正是出于上述原因,SSD中提供了一个TRIM命令,操作系统在删除文件时可以通过向SSD发送TRIM命令告诉它哪些数据块中的数据已经不再使用了。SSD在收到TRIM命令后,通常会在定期的垃圾收集操作中重新组织这些区块,为将来写入数据做好准备,不过每一款SSD在底层对TRIM命令的执行机制都不尽相同,但无论如何,通过TRIM能够显著改善SSD的性能和寿命。当然,大家可能已经发现了,有了TRIM,删除的文件数据会被SSD自动回收,这意味着以往在传统硬盘上能够使用的一些数据恢复(反删除)软件,在SSD上可能就不再管用了。

既然TRIM如此重要,我应该如何启用呢?一般来说,只要你所使用的操作系统支持TRIM,就无需额外的操作。支持TRIM的操作系统:Windows 7及后续版本、OS X 10.6.8及后续版本、Linux内核版本2.6.33及后续版本。

对于Windows XP用户,系统不支持TRIM,可以选择SSD厂商(如Intel)提供的软件来实现定期的垃圾回收,模拟TRIM的效果。对于Mac用户,OS X系统虽然支持TRIM,但只支持苹果自家的SSD,如果你安装了第三方品牌的SSD,系统是无法启用TRIM的,通过TRIM Enabler软件给内核打个补丁,就可以启用TRIM了。

用Ruby写了一个自动连接循环音频片段的脚本

从3月开始我接了一个给游戏作曲的项目。大家知道游戏的BGM一般是循环播放的,因此作曲时也要考虑到这一特性,最后输出的音频素材(wav文件)应该切成两个Part,其中Part-A是一个引子(不进入循环的部分),Part-B是主循环体,A到B、B到B应该都可以无缝衔接,在游戏中播放出来应该是“A-B-B-B-B-...”这样无限循环下去。

为什么要说这个呢?因为除了给游戏输出素材之外,我还需要输出一份原声碟用的音轨,按照游戏原声碟的惯例,一首曲子一般是输出“A-B-B”(即循环两次),最后的部分再做一个淡出(fade out)效果。在有很多首曲子的情况下,一首一首到DAW里面去输出太麻烦了,既然我已经输出好了A、B素材,那么如果我写一个脚本,自动按照“A-B-B-淡出”的形式合并起来不就好了吗?

于是我想到用Ruby来完成这个工作。首先,找一个可以处理wav文件的gem:wav-file;接下来我们来想想如何实现两个操作:连接和淡出。连接很简单,只要将两个wav文件的数据块连起来就可以了;淡出也不难,wav文件中每个采样的值代表该采样的振幅(-32767~32767,以16位采样为例),将每个采样的值按一定比例缩小,最后缩小到0,就实现淡出了。 继续阅读

实战Mac mini自制Fusion Drive

去年10月苹果发布新款iMac和Mac mini的时候,顺便介绍了其新的Fusion Drive方案。所谓Fusion Drive,就是将一块SSD与一块普通硬盘合起来作为一个逻辑卷(Core Storage)来使用(用户看到的是一块硬盘,容量等于两块硬盘之和),而OSX系统会动态地在两块物理硬盘之间移动数据,使得系统启动和平时经常使用的文件放在SSD中,而将其他较大的不经常使用的文件放在普通硬盘中,以实现性能的最优化。有关Fusion Drive的一些介绍,可以参见这篇文章

上周刚刚从日本买了一台Mac mini(最低配置,500G硬盘),家里台机中正好有一块128G的SSD,既然Fusion Drive在硬件层面上实际上就是两块硬盘,那么我把这块SSD装进去就可以自制Fusion Drive了。

开始之前,先说点基础的。Mac mini中确实有两个硬盘仓位(不然官方的Fusion Drive也无法实现),一上一下,由于拆机是从Mac mini的底部进行的(也就是底朝天反过来工作的),因此下仓位的硬盘先露出来,上仓位的硬盘则藏在下仓位硬盘的下方。如果你的Mac mini原配的硬盘是装在上仓位,那么恭喜你,你只要把SSD往下仓位一塞就行了,工作量不大;如果原配的硬盘是装在下仓位(比如我买到的这台),那么你就要像我一样把整个Mac mini拆个精光才行了。虽然Mac mini提供了两个仓位,但是SATA线(苹果定制的特殊款式)却只提供了一条,因此你需要为第二块硬盘额外配一条线(两个仓位的线样式不同),我是从这里买的,顺便还附带一些螺丝和拆机工具,注意根据你原配硬盘的仓位位置选择对应的线,商品页面上有说明。 继续阅读

两本新译作:《代码的未来》、《大数据的冲击》

我在图灵完成的第3、4本译作很快就要上市了,一本是Ruby语言创始人松本行弘(Matz)的《代码的未来》(まつもとゆきひろ:コードの未来),另一本是野村综研顾问城田真琴的《大数据的冲击》(ビッグデータの衝撃),这两本都是值得推荐的好书。

在说这两本书之前,不得不说起我的上一本译作《Android应用开发入门》。这是我在图灵翻的第一本,也是唯一一本英文书,同时它也是我翻的最头大的一本书,因为这本书写的真不怎么样,看来Apress的书里面水货也还是不少的,然后正好让我摊上了一回。

《代码的未来》从2012年8月开始翻译,到2012年12月完成全书3/4时,出于出版社的请求和安排暂时搁置,转而优先完成《大数据的冲击》的翻译工作。后者于2013年2月翻译完成,交给野村综研(上海)进行审读,《代码的未来》翻译工作最终于2013年4月完成。正是由于如此复杂的过程,才导致大家看到我一个人同时出版了两本译作这一貌似很奇葩的局面。

《代码的未来》是Matz对编程语言过去的总结、对未来的展望,以及对目前的云计算、大数据、多核时代下所运用的各种技术的吐槽。作为一种流行编程语言的创始人,Matz的观点可以说非常具有参考价值,再加上广泛的话题和深入的探讨,无论对于专业码农还是业余技术爱好者来说,这都是一本帮助拓展视野的好书。当然,正是由于这本书话题广泛又不失深入,也为翻译带来了很大的难度,我想没有一个人敢说自己了解书中所介绍的所有这些技术,这是一个相当大的挑战。好在Matz是一个非常Nice的人,我和他通了很多封邮件讨论翻译过程中所遇到的问题,他每次都给予耐心的解答。

2012年11月,中国Ruby大会在上海举办,Matz应邀参加并发表关于Ruby 2.0的演讲,还带来了mruby原型开发机给大家演示。Ruby大会之前我有幸对Matz进行了一次专访,并发表在了图灵社区我的博客中。这次专访谈到了很多有趣的话题,我也做了精心的准备,可以说效果是非常好的,也为这本书的翻译带来了一些帮助。

《大数据的冲击》中对技术的介绍较少,或者说,技术并不是这本书的重点。由于作者是野村综研(亚洲最大的咨询公司)的高级顾问,因此这本书充满了咨询公司所特有的高屋建瓴的风格。书中主要从商业案例、运用模式、隐私问题、人才培养等方面对当下大数据的运用进行了分析,能够让读者对“大数据”这个看似很虚的名词有一个更加系统的理解,并充分体会到数据存储、挖掘、分析对决策的重要性。这本书的写作风格严谨客观,每一个观点都有资料和文献进行佐证,每个图表都注明了来源,最后还有一个长长的参考文献列表(感谢图灵没有在中文版里把这个参考文献列表给省略掉),这也很能体现资深咨询顾问的专业性。

最后希望大家喜欢这两本书。

打造一个自动追新番的下载机:补完篇

前面两篇我们讨论了如何使用Raspberry Pi和Flexget实现自动订阅新番并下载的功能,在使用的过程中我们还可以通过一些技巧来实现一些其他的功能,这次给大家介绍几个我自己用到的例子。

我家有个高清播放机,它也兼做NAS(网络共享服务器),里面安装了一块1TB的硬盘。Raspberry Pi是用SD卡作为存储介质的,存储空间比较有限,那么能不能把文件自动下载到NAS里面去呢?当然可以,只要让Transmission的下载完成目录指向NAS中的相应位置就行了,大家知道Linux中的mount和symlink是很强大的,我们可以通过它们来实现这个功能。

首先我们将NAS中用于存放下载文件的目录挂载到Raspberry Pi中。先创建一个目录作为挂载的容器,并将这个目录的权限设置为所有用户可读写:

$ sudo mkdir /mnt/nas
$ sudo chmod 777 /mnt/nas

接下来编辑/etc/fstab,添加下面这一行代码来挂载NAS的共享目录,这个文件中所指定的内容会在每次系统启动时自动挂载。在这里我们假设NAS采用的是Samba(Windows Share)协议来共享文件的(记得将IP、用户名、密码、共享目录名称改为你自己的):

//10.0.0.200/share	/mnt/nas	cifs	username=nmt,password=1234	0	0

接下来运行mount命令来挂载:

$ sudo mount -a

现在访问一下/mnt/nas看看,应该可以看到NAS上的文件了。如果不想改动Transmission的配置文件,可以直接在Transmission配置中默认下载目录的地方做一个symlink(类似Windows中的快捷方式)指向NAS中的指定目录。例如,Transmission默认下载目录为/home/pi/downloads/completed,我们想要将下载的文件放到NAS的/Download目录下,首先我们可以将原来的completed目录改个名,然后建立一个名叫complete的symlink即可:

$ cd /home/pi/downloads
$ mv complete complete_bak
$ ln -s /mnt/nas/Download ./completed

继续阅读

打造一个自动追新番的下载机:Flexget篇

上一篇讲到如何给Raspberry Pi配置Wifi,有了Wifi,我就可以把Raspberry Pi放在任何地方,只要有一根电源线就可以了。接下来我们来看如何实现自动追新番的功能。

要实现这样的功能,有个很好用的东西叫做Flexget。Flexget是一套用Python编写的软件,它可以实现智能过滤和下载所需的资源。简单来说,Flexget的工作流分为input、filter、output三个部分:input就是输入源,比如RSS甚至是网页;filter就是过滤器,可以从源中过滤出我们需要的条目;output就是将过滤出来的条目进行输出,比如输出给P2P下载软件来下载。这三个工作流都是依靠插件来完成的,除了官方插件之外,还有很多第三方插件可以使用,灵活性非常高。

Flexget是基于Python的,而Raspberry Pi的官方Debian系统中已经内置了Python,因此我们可以直接来安装Flexget。除了Raspberry Pi之外,理论上任何支持Python的设备都可以运行Flexget,这里面也包括一些路由器和NAS设备。首先我们来安装Python的包管理程序pip:

$ sudo apt-get install python-pip

然后通过pip来安装Flexget:

$ sudo pip install flexget

由于Raspberry Pi的CPU比较烂,这一步需要的时间比较长,请耐心等待。安装完成之后我们就可以来编写Flexget的配置文件啦。Flexget的配置文件采用YAML来编写,不过你不必深入理解YAML,因为它是一种非常简单易读的格式,看几个例子就会了。配置文件需要放在“~/.flexget/”路径中并命名为“config.yml”,可以用nano来创建并编辑一个新的配置文件: 继续阅读