部署rails应用程序 – 以及使用capistrano将部署过程自动化

这篇文章介绍如何将一个rails 4.1.0网站部署在一台ubuntu server 12.04 LTS 服务器上, rails 网站将运行在 apache 2.4.9 + phusion passenger 下, 并且使用 mysql 5.5 数据库. 部署过程最终通过capistrano自动化, 之后网站的更新操作只需要在开发机上运行一条指令就可以了.

项目代码修改: 后面配置服务器或者项目在服务器上运行时需要的调整

当你创建一个新的rails应用程序的时候, 一般会有三种配置: test, development, 以及production, 你也可以添加更多的环境配置. 因此对应的, rails项目可以运行在多种配置下, 也就会对同样的客户端请求有不同的处理方式.

test配置作用于使用rake test命令运行的unit tests (for models), functional tests (for controllers), 和integration tests.

接下来先简要介绍一下默认配置下development和production环境的区别, 没有认识到这些区别, 会让你开始使用production环境的时候觉得很莫名奇妙.

development.rb

1. # 修改了代码之后, 新的代码无需重启web服务器(比如rails server开启的WEBrick)就会生效. production环境值为true

2. # 如果设置为true, 则asset pipeline不会对app/assets下的脚本资源进行压缩, 这样会使得开发环境下调试的时候网站运行得更快. production环境使用默认值false, 然而就像下面说的, production环境的config.assets.compile也是false, 也就是不开启asset pipeline, 因此也就无所谓需不需要使用asset pipeline压缩脚本资源了.

production.rb

2. # 在发布到产品环境时, 我们一般会运行”rake assets:precompile”对app/assets下的资源进行预处理和压缩, 结果会存在public/assets目录下. 这项配置的意思是, 如果public/assets下没有找到客户端请求的文件, 要不要使用asset pipeline实时地对app/assets下的文件进行预处理等操作. 由于产品环境下已经做了预处理和压缩, 就没有必要开启这项设置了. development环境使用默认值true

3. # 不解析对public文件夹下的文件的访问, 这样在产品环境下就会由apache等web服务器来处理, 效率更高. 如果你在开发机上通过执行”RAILS_ENV=production rails server”将rails项目以服务器配置启动, 会发现无法加载public文件夹下的图片, 脚本等资源, 就是由于这个配置. development下使用默认值true.

前面提到的关于asset pipeline的配置, 还会影响rails如何生成对脚本资源的引用的链接

上面对rails的几种配置做了基本介绍, 结下来才是正题: 修改production环境的配置, 以便运行在产品环境下

使用mysql数据库

默认情况下, 3种环境都使用sqlite3数据库, sqlite3简单易用, 但用于产品环境的话效率太低, 因此产品环境需要改用mysql数据库.

数据库的准备在后面web服务器部分, 这里先进行配置. 修改config/database.yml文件, 将

替换为

提取敏感信息

注意这里没有直接将前面的数据库信息写到代码里面, 而是引用了环境变量. 在这里, 数据库名database的值通过名为RAILS_PRODUCTION_DB环境变量指定, 用户名和密码也分别引用了RAILS_PRODUCTION_USER和RAILS_PRODUCTION_PASSWORD.将敏感信息从代码里面移除是必要的, 因为你的代码很有可能需要和其他人共享, 而并不是所有具有代码访问权限的人都可以知道这些信息, 或者不同开发这需要有不同的配置, 也可以将这些信息独立出来.

这篇文章讲到了一些设置环境变量方法: Rails Environment Variables.

系统环境变量: 不可行

说到环境变量, 就会想到操作系统提供的环境变量设置 (关于ubuntu环境变量的资料请参考ubuntu wiki上的Environment variables). 然而当你的rails网站是运行在apache下的时候, 使用系统环境变量就会很麻烦而且不好理解. 首先apache一般不是通过shell启动, 因此设置shell环境变量的方法并不管用. 而当我在/etc/environment下设置所谓system-wide环境变量的时候, 只有重启了服务器才使这些环境变量生效, 这个回答提到如何在不重启系统的情况下使系统环境变量生效, 然而还是太复杂了.

apache环境变量: 不可行

另一种, 是直接在apache上设置”环境变量“, 这些环境变量直接对运行在其上的其他应用程序可见, 设置方法为修改apache的envvars文件, 在文件末尾添加如下代码. 

更多关于如何为包括apache在内的web服务器设置环境变量, 以及这些环境变量如何影响通过Phusion Passenger运行的web站点, 请参考Phusion Passenger users guide, Apache version 中的”Appendix D: About environment variables”.

这种方法的问题是, 当我们后面使用Capistrano来对服务器的数据库进行升级的时候, Capistrano是不知道apache的环境变量的, 因此它没法加载存在环境变量中的数据库连接信息.

使用Figaro Gem: 可行

在Gemfile中添加:

然后安装:

生成配置文件:

这会生成一个config/application.yml文件, 并将这个文件添加到.gitignore中以免这个包含敏感信息的配置文件被提交到代码库中. 我们后面会手动在服务器上创建这个文件, 你可以添加一个文件config/application.example.yml, 里面列出服务器需要设置的环境变量, 并提交到代码库中, 相当于一个文档. 这个配置文件采用key/value的形式设置环境变量.

打包依赖的gem: 可选

在一个rails应用程序中,  ruby gem bundler的作用在于记录当前程序使用了哪些gem, 并且对各个gem有哪些版本需求. 使用同样一份代码, 只要一台新的机子上安装了ruby和bundler, 就可以通过”bundle install”下载安装一套基本相同的gem. 之所以说基本相同, 是因为bundler可能没有明确指定需要某个版本的某个gem, 只是说某个gem要高于某个版本. bundler提供的”bundle package”功能, 会将当前项目使用的所有gem复制到vendor/cache目录下. 这样, 只要在另一台拥有同样代码机子上运行”bundle install –local”, 就可以得到同样版本的所有gem了, 也省去了重新下载的麻烦, 而且”bundle install”也会优先从vendor/cache目录下安装.

服务器基本环境配置

配置git server

在部署之前, 你很可能已经将代码上传到了一个开发组都可以访问的服务器上. 如果要实现自动化部署, 这个代码服务器还得是外网可以访问的, 或者至少是你的web 服务器可以访问的, 因为自动部署就是在web服务器上获取最新代码进行部署. 代码服务器和web服务器也可以是同一台物理服务器.

创建专门管理代码库的用户git, 并设置密码, 这将同时创建git组

配置git代码库

设置从开发机到git服务器的ssh keys

关于ssh keys的更详细资料可以参考Capistrano 的 1.1 SSH keys from workstation to servers 一节, 里面提到了一种更加安全的方法, 使得从A到B的ssh访问仅仅只是在一段时间内不需要重复输入密码, 一段时间过后, 即使电脑被盗, 也需要再次输入密码才能获取访问B的权限. 为了配置和之后的操作更简便, 我这里采用了配置之后永远不需要输入密码的方法.

更准确地说, 是从A上的用户u1, 到B上的某个用户u2的访问权限, 相当与从机器A的u1使用u2用户登录机器B.

这不仅是为了开发的需要, 以免去重复输入密码的麻烦. 如果没有为开发机到git服务器的访问设置ssh keys, 后面的自动部署将会直接失败, 甚至不提示你输入密码.

为开发机添加remote branch, 并且上传代码

配置web服务器

在web服务器上添加用于自动部署的用户, 并设置ssh keys

为了操作方便, 也是后面自动化部署的需要, 请按照前面讲到的设置开发机到git server的ssh访问的方法, 同样设置开发机到web服务器的deploy用户的ssh访问.

软件安装

apache, mysql

首先需要安装apache, mysql. 这些需要用root权限安装. 下面假设apache是用源代码编译安装的, 安装在/usr/local/apache2下. 如果是从软件库安装, 各配置文件目录及站点根目录会不同, 请参考ubuntu或其他linux版本的文档.

ruby and gems

然后是rails, ruby及rails的版本最好与开发机一致. 普遍的安装方法是通过RVM.

第1行安装RVM, 第2行安装当前系统下需要的基本运行环境, 第3行安装2.0.0版本的ruby, 请将版本号改为你需要的版本. RVM允许同时安装几个版本的ruby, 第4行的作用是默认使用2.0.0版本. 这里不直接通过RVM安装rails以及其他需要的gem, 为了避免重复下载, 并且保证服务器上使用的各种gem版本和开发机上的一样, 我们可以先安装bundler (上面第5行), 然后利用bundler安装包括rails在内的gem.

在使用bundler安装gem之前, 需要先从git服务器获取一份项目代码. 为了和后面自动化部署一致, 我们把代码放在/usr/local/apache2/htdocs/rails-app/current目录下. 这样, 在实现自动化部署之前, 我们可以用同样的apache配置, 使用这份手动部署的代码测试服务器的环境配置是否正确. 由于deploy用户没有足够权限, 我们需要先用root帐号创建需要的目录.

这里, chmod g+s的作用在于, 所有在${deploy_to}目录下的新文件和文件夹的group都将与${deploy_to}相同, 也就是说grup都是deploy. 而这些文件, 文件夹的owner仍然是创建他们的用户, 比如第7行创建的两个子文件夹releases和shared就都是owner为root, group为deploy. 这两个文件夹和我们马上要讲到的自动部署有关. umask 0002使得当前进程下之后新创建的文件和文件夹的owner和group有r, w, x权限, others有r, x权限.

以上创建了Capistrano需要的目录结构, 参照Capistrano文档的Authorisation一节. 每当后面使用Capistrano发布一个新的版本时, 它会在releases目录下以当前时间戳为名创建一个文件夹, 并从git server下载配置好的某个分支(比如master)的最新代码. 第一次部署时, 他还会在deploy_to目录下创建一个名为current的symbolic link, 指向releases下的当前版本, 每次更新的时候也会修改这个current, 使它指向最新版本. 我们先创建一个同名的文件夹用来存放手动部署的代码.

phusion passenger

我们在开发机上进行开发测试的时候, 一般是运行”rails server”打开一个网站后台. 这种方式是单线程的, 一次只能处理一个请求, 因此不适合用于产品环境. 由此也就有了phusion passenger, 它的作用相当于apache与rails程序之间的代理. 当apache收到来自客户端的请求时, 这些请求通过passenger分发给后台的多个rails进程, 这样就实现了多线程工作. 进行一下安装之前确保apache已经启动

第1行安装passenger, 第2行安装了passenger的apache模块, 安装程序会检测系统中有没有安装包括apache在内的它所依赖的软件, 如果没有检测的话, 会提示你是否需要由它从软件库下载安装. 由于我是从源代码编译安装的apache, 这里需要使用apxs2-path参数帮助安装程序找到系统中的apache. 如果你的apache是直接从代码库安装的, 这个参数就可以不指定了.

安装passenger的apache模块的时候会修改apache配置文件, 加载新安装的模块, 并且安装结束后会提示你将一些配置信息复制到apache配置文件里面, /usr/local/apache2/conf/httpd.conf:

设置环境变量

前面已经将rails项目中的敏感信息提取到了figaro生成的配置文件config/application.yml中, 因为我们没有将这个文件提交到代码库中, 因此在releases下的某个具体版本也不会有这个文件. 我们说过会在服务器手动添加, 然而我们也不能添加在releases下, 因为那样每次升级版本我们都得手动添加这样一个配置文件. 这就是前面创建的shared文件夹的用处, 我们后面讲自动化部署的时候会说到, Capistrano提供了一个:linked_files变量, 你可以设置一些存在于shared文件夹中的文件, 这样每次Capistrano发布一个新的版本的时候, 就会为每个:linked_files中指定的文件, 在releases下那个最新的版本代码下创建一个symbolic link, 指向对应的shared目录下的文件.

数据库连接

按照前面说的方法, 将数据库连接信息的环境变量添加到shared/config/application.yml中

SECRET_KEY_BASE环境变量

在config/secrets.yml配置文件下, 可以看到rails已经为test和development环境添加了secret_key_base, 然而production环境的值使用了一个名为SECRET_KEY_BASE的环境变量, 因此在服务器部署的时候, 还得根据前述方法添加一个SECRET_KEY_BASE环境变量, 它的值可以通过”rake secret”命令随机获得.

生成的secret比较长, 这里只截取一部分作为示例.

数据库准备

首先使用root账户登录mysql, 创建一个新的用户, 并且给这个新用户赋予你的rails网站将要用到的mysql数据库的操作权限. 下面假设这个数据库命名为rails_db, 通过rails_user帐号连接, 密码为rails_password

数据哭必须得在这边手动创建, Capistrano没有提供创建数据库的操作, 只会在发布新版本的时候升级数据库.

设置apache

下面的配置假定apache根目录设置为了/usr/local/apache2/htdocs, 并且服务器域名为www.mysite.com

这里有几点是需要注意的. 首先第5行里面我们把站点根目录设置在rails程序的public子目录下, 这是所有给予Rack的web程序的共同特点, 更多关于Rack的资料请参考Rails on Rack. 另外, 当我们后面实现自动化部署之后, 这个current目录会是一个symbolic link, 指向最新版本的代码, 因此发布新版本的时候我们不需要修改apache配置.

第6行指明我们的rails程序要运行在production环境下.

更多phusion passenger的apache配置请参考Phusion Passenger users guide, Apache version.

自动化部署

前面已经做好了服务器配置, 并且也手动部署了一次rails代码, 接下来可以先访问www.mysite.com看看你的部署是否成功, 成功之后就可以继续将代码部署这块自动化了.

————–好长的文章, 快要吐了. 坚持一下, 就要结束了, 想想我为了实现自动化部署花了多少精力找了这么多资料吧—————–

我们采用Capistrano来实现部署自动化, 它是用用ruby实现的, 配置文件也是用ruby, 但它可以用来部署任何语言写的web程序.

我们已经在前面的步骤中做了一些针对Capistrano的准备工作, 这里列出来, 如果你前面跳过了这些步骤, 请完成后再继续.

  • 在web-server上创建deploy用户
  • 设置从开发机到web-server上deploy用户的ssh访问, 这一步只需在需要进行部署的主机上进行就可以了, 如果希望每个项目成员都能更新版本, 就得为每个成员的主机设置ssh访问.
  • 在web-server上, apache的站点根目录下, 创建了一个deploy_to/{releases,shared}的目录结构, 并为deploy用户设置了足够的操作权限.

这些前面做的准备, 以及我们将要讲到的其他步骤, 基本上都可以在Capistrano网站上找到更加详细的说明. 如果碰到问题, 可以上那看看有没有什么建议.

如果你跟着上面的步骤, 手动部署代码在deploy_to/current这个文件夹用下, 则这里需要先将这个文件夹更名, 因为Capistrano将需要创建一个同名的symbolic link.

接下来的操作都只需要在开发机上进行就可以了.

安装Capistrano

在Gemfile里面添加如下代码

然后运行”bundle install”安装

如果你有几个rails项目都要使用Capistrano实现部署, 那么这条命令在一台机子上只需运行一次就可以了(运行多次当然也没有问题) , 因为它的作用就是检查当前rails项目会用到哪些gems, 然后下载安装那些没有安装过的. 一台电脑上下载安装的gems是给该机子上的所有rails项目共用的.

为rails项目初始化Capistrano配置

这会在当前项目下创建一个类似下面的目录结构

capistrano-initialization

 

Capistrano实际上是以任务的形式, 由开发机触发, 在服务器上执行命令的. Capistrano提供了版本部署所需要的大部分任务, 比如开发机上的一个”cap production deploy”命令, 就可以让配置好的服务器执行一系列命令, 从git server获取最新代码, 并放在指定的目录下, 从而实现对production环境的部署(deploy).

这里指的production环境, 和前面说的rails的production环境不太相同, 虽然都是指产品环境, 但作用的时间不一样. rails的development或者production环境, 是指同一份代码, 根据不同的配置有不同的运行方式. 而Capistrano的production环境 (以及另一个已经为你的rails项目生成了模板的staging环境, 甚至是你自定义的其他环境), 是指在发布的时候, 将要运行在某个rails环境下的这份代码要以怎样的方式部署, 比如传到哪个服务器, 放在哪个目录. 这些都可以在config/deploy/*.rb文件里面配置, 我们马上就会讲到.

这是代码部署任务, 你还可以执行数据库升级(migration)任务, 甚至可以通过在lib/capistrano/tasks下添加*.rake文件来添加新的任务. 你可以运行”cap -T”来看一下有当前rails项目定义了哪些任务.

加载需要的任务

前面说Capistrano是以任务的形式运行的, 默认情况下它提供了一些你可以直接执行的任务, 每个你可以直接运行的任务实际上可能同步或异步地执行了很多其他更小的任务. Capistrano提供了一种任务扩展的机制, 使得开发这不仅可以提供更多可直接调用的任务, 还可以使用某个任务预留的接口对该任务进行扩展, 比如在某个小任务执行之前或者之后做一些其他操作.

我们前面在安装capistrano gem的时候同时也安装了capistrano-rails和capistrano-rvm, 就是提供了一些扩展任务.

安装之后我们还需要在Capfile中将这些任务添加到当前rails项目下

“capistrano/rvm” 如果服务器使用RVM. 没有这行会导致Capistrano不能成功执行RVM提供的功能, 比如deploy:migrate的时候用到的rake.

“capistrano/bundler” 当项目代码的Gemfile中添加了一个新的gem的时候, 运行deploy的时候bundler会自动安装服务器上缺少的gems.

“capistrano/rails/assets” 包含了一些assets的任务, deploy的时候会执行compile assets操作

“capistrano/rails/migrations” 包含数据库相关的操作, deploy的时候会进行migrate

配置服务器的连接信息

对Capistrano如何运行有了大概的了解之后, 我们继续进行配置. 前面说过, Capistrano实际上是通过在开发机上发出指令, 由各服务器来执行, 因此需要配置各服务器的连接信息.

不同环境的专属配置

配置 config/deploy/production.rb文件, 这是针对production环境部署, 其他环境做相应配置

2: 设置部署代码的路径, 前面提到过, 新版本大夫吗会以时间戳命名放在这个目录在releases子目录下

6: 设置使用代码库的master分支, 代码库的地址在后面会讲述, 如果需要在执行部署的时候提示选择哪一个分支可以使用第5行的配置

8-15:  一种定义服务器连接的方法, Capistrano将服务器按功能分为3中角色——:app, :web 和 :db. :db服务器只有在前面说的Capfile中载入’capistrano/rails/migrations’时才有用, 用于指定数据库服务器连接信息, 一边进行数据库升级操作 (而且, 如果你没有指定:db连接, 却载入了capistrano/rails/migrations, 那你执行数据库升级人物deploy:migrate的时候甚至不会看到报错, 只会看到这个任务没有任何输出). “deploy@www.mysite.com”意思是使用deploy用户连接www.mysite.com, 服务器地址除了域名也可以用ip地址指定, 或者是一个hosts项如”web-server”. 一种角色可以指定多台物理服务器, 如

18-23: 另一种指定服务器连接的方式, 比如这里第23行指定所有服务器角色都使用www.mysite.com这台物理服务器, 并且用deploy用户登录进行部署.

公共配置

不同部署环境的公共配置可以放在文件config/deploy.rb中

第1行配置项目名称, 这个名称将用于命名一些临时文件/文件夹, 因此得是能作为合法文件名的名字.

第2行配置代码库的地址.

第4行的作用和我们前面手动部署的时候为config/application.yml文件添加symbolic link完全相同, 这里的文件地址是shared文件夹的. 也就是说, 每次使用deploy任务发布一个新的版本, 都会在新版本的config目录下闯将一个application.yml symbolic link, 指向shared/config/application.yml.

8-16: 设置在每次发布玩版本后重启rails网站, 第12行默认是被注释掉的. 这个任务也可以单独执行, 直接在服务器上做的一些修改就可以通过执行这个任务重启网站来使修改生效.

Capistrano使用ssh协议进行包括从开发及到 :web服务器, 从:web到git-server服务器之间的连接. 按照我们前面的ssh keys配置, 就可以做到整个过程无需输入密码, 整个过程很顺畅.

然而我们前面并没有设置从:web到git-server的ssh keys, 为什么也能直接连接而不需要输入密码呢? 这涉及到一个叫SSH Agent Forwarding的概念. 简单地说, 就是A连接了B, 并且想让B去连接C, 但是B又没有连接C的权限, 然而A是有连接C的权限的. 为了解决B到C的问题, 就可以在A连接B的时侯指定允许B使用A到C的访问权限. 比如下面的代码:

这里首先从开发机连接到web-server, 然后在web-server上执行单引号里面的命令, -A选项的作用就是SSH Agent Forwarding. 这也是Capistrano从web-server连接git-server获取最新代码的原理. 如果没有按照前面的步骤配置开发机到git-server的ssh keys, 就会在运行”cap production deploy”的时候出现如下错误:

发布新版本

至此, Capistrano的配置已经完成了, 可以试一下发布新版本.

如果你按照前面的文档加载了扩展任务, 执行deploy任务的时候, 会自动进行包括安装缺失的gems, 升级数据库, compile assets等这些操作, 做到一条命令无人值守的部署. 当然了, 这个命令执行时间也很短, 无人值守不在省时间,  关键在于减少出错.


花了3天多的时间写完这篇文档, 让原本很不好用的中文输入法也记住了不少词汇, 慢慢变得顺手了. 之前做部署的时候由于时间关系, 有些东西没有理解, 摸索着就用上了, 还有一些遗留的小问题也没有解决. 通过这次对文档的整理, 问题都解决了, 过程也清晰了很多.

这些操作方法, 会用, 和要写出来是完全不一样的. 要写, 就得理解得更加深入, 透彻, 这样逻辑才会清晰, 并且也可以让阅读文档的人知道个所以然, 知道某个操作的目的是什么, 如果不做又会怎么样.

第一次花这么长时间写文档, 希望受益的, 不只我一人.

apache: 文件下载配置

默认情况下, 当浏览器访问一个web服务器上的一些资源如html, txt和pdf 文件时, 会直接在浏览器页面打开显示这些文件的内容. 然而有时我们需要让浏览器直接下载保存这些文件.

之前对apache了解比较少, 上网搜了很久, 大多是用其他开发语言比如php, servelet来实现. 这种方法其实用于下载动态生成的文件比较合适, 对于静态文件, 还是直接用apache效率高一点.

以上是需求, 实现起来其实很简单. 比如要配置files目录下的文件都可以直接下载, 只需要在这个目录下创建一个名为.htaccess的文件, 文件里添加如下配置:

这个配置的意思是, apache向客户端发送当前目录下的资源是, 将Content-Type消息头的值设置为application/octet-stream

如果只需要配置当前目录下的某类文件可下载, 可以将前面的设置放在一个<Files>里面, 如下:

 

—–—–


我研究这个的主要目的是想提供一个chrome extension文件的下载, 由于现在chrome浏览器不支持自动安装从除了chrome web store之外的地方下载的extension安装文件, 它会给出一个不能安装的提示, 这个提示会给人以chrome不信任这个下载的extension的嫌疑, 因此我们需要让chrome不会自动安装, 只是下载文件就可以了. 参考chrome开发文档里面关于如何才能让chrome自动安装(这个文档是在chrome仍然支持自动安装从第三方下载安装程序的时候制作的)的资料, 我们只要不满足文档里面提到的条件就行了. 方法就是让web服务器在发送安装文件的时候, 指定X-Content-Type-Options消息头的值为nosniff, 结合上面说的配置文件下载方法, 可以将.htaccess改为:

 

—–真的完了—–

 

 

mysql 常用操作

create user 'name' identified by 'password';

This will create a user 'name' for host '%', with password 'password', and a row with these data is inserted in the mysql.user table.

 

grant all on 'db_name'.* to 'name';

This will grant all database privileges on dabatase 'db_name' to user 'name', and a row with these information is inserted in the mysql.db table.

 

load data local infile 'input_file_path' into table 'table_name';

Load data from local text file into the specified table, input text file is an tab seperated csv file by default, but you can specify its format by some options. For security reasons, you need to specify '–local-infile' option when connecting to mysql server using 'mysql' command line client program.

搭建ubuntu14.04下web开发环境

Install by package manager

Install from source

apache

Follow the instructions on http://httpd.apache.org/docs/current/install.html.

In the requirements step, I downloaded APR and APR-Util manully and put it into apache source as decribed, pcre has been installed already when I install apache.

In the configuration step, I run “./configure –enable-so” as documented by http://cn2.php.net/manual/en/install.unix.apache2.php.

Because option –prefix defaults to /usr/local/apache2, that is where apache2 get installed.

To start httpd server:

To stop httpd server:

 

mysql-server-5.5 can still be installed from aptitude

 

php5

After downloading the source code, run following confirutaion command as documented in http://www.php.net/manual/en/install.unix.apache2.php and http://www.php.net/manual/en/mysqli.installation.php. This selects the mysqli api and mysqlnd driver.

The make and install steps go normally after this configuration.

After installing, apache is configured to load the php module. The configuration you need to do manually is to tell apache to parse certain extensions as PHP. You can achieve this by put following configuration section at the end of apache2/conf/httpd.conf


Configuration done, you can restart apache, and check that php code file is executed correctly by apache web server.

在windows及linux下使用goagent代理

goagent是一个开源、运行在google app engine下的代理服务器,由于google对免费应用有限制24小时只能用1G的流量(这个限制是针对刚过去的24小时,也就是说即使你是在晚上11点到12点之间用了1G,你也得等到第二天晚上12点才能继续使用),因此普遍的使用goagent代理上网的方式就是自己搭建一个google app engine应用并且并且上传goagent源代码到服务器运行。这里就讲一下如何在windows和linux下部署goagent。

首先下载goagent

然后在用google app engine创建一个应用,这个我以后会写详细的文档。你需要注册google帐号(Gmail邮箱)才能创建应用,一个帐号限制只能创建10个应用,每个应用都有一个appid。

创建应用之后,就可以上传该应用的运行代码进行部署,对于我们,当然就是上传goagent了。

你下载的goagent解压之后可以看到两个文件夹,一个是local,一个是server。顾名思义,server是要上传并且运行在服务器端的,而local是运行在要代理上网的电脑上的。

在上传服务器代码之前,先要做一个配置修改。打开server/python/app.yaml文件,将application的值(也就是"application: "后面的内容)改为你前面创建的应用的appid。

保存之后即可上传,上传方式windows和linux稍有不同。linux下是在server目录下运行"python uploader.zip";windows用户直接双击运行server/uploader.bat文件就可以了,其实原理和linux下相似,只不过由于下载的goagent自带了一个python(在local文件夹),就不要求用户安装了,如果你打开uploader.bat文件,就会发现其实它也是运行了"python uploader.zip",只不过使用的是goagent一起打包的python。虽然操作方式不同,但后面都会要求你输入appid,以及你的google帐号的用户名和密码,提供这些信息之后就开始上传代码了,要上传的文件不大,因此正常几秒中就可以完成了。

上传之后可以在google app engine上确认一下你的应用已经部署成功并且正在运行。

服务器部署好并且正确运行之后,就可以配置运行客户端连接服务器了。

与服务器一样,客户端也得配置appid,配置方法是打开local/proxy.ini文件,修改其中"appid = "后面的内容。

配置之后即可运行,运行方式也是windows和liunx有所区分。windows直接运行local/goagent.exe文件,这会打开一个命令行窗口,要想隐藏这个窗口,点击右下角通知栏的goagent图标就行了。linux下的运行方式是用python执行local/proxy.py这个文件,即"python proxy.py"。

记住每次开机之后,要使用代理的话都得这样运行一下。

到这步的时候你的电脑只是有了和代理服务器通信的能力,但是如果你要你的浏览器使用代理上网的话,还得安装浏览器插件。

chrome浏览器可以安装Proxy SwitchySharp扩展,并且在配置里面的Import/Export中,点击"Restore From File"(从文件恢复)按钮,选择goagent/local/SwitchyOptions.bak文件并保存。装好后重启浏览器,在地址栏右边能看到一个地球样式的图标,点击这个图标可以选择需不需要使用代理上网。

 

 

 

使用阿里云

阿里云不只是云服务器一种产品,数据存储服务也将会是我要用到的服务之一,这里先介绍一下我正在试用的云服务器,更多的体验后续会更新。

首先要在阿里云注册一个帐号,在购买或者申请试用云服务器之前,必须得实名认证,这可以通过绑定已经实名认证过的支付宝帐号完成。

然后就可以申请试用云服务器,一般提交申请几分钟后就会审批完成,并且短信告知远程登录的IP地址,用户名和密码。申请的时候可以选择需要的配置,尽管选择高的没有关系,都是免费试用。 各种配置参数会互相限制,比如我用的是ubuntu 12.04 64bits,内存只能选1G以上的。

我选择用putty远程登录服务器,指定IP地址,输入用户名密码就可以了。连上之后的所有软件安装等操作都是在命令行完成,系统原本没有安装web服务器,需要自己安装。我装了apache2之后,就可以直接在浏览器访问了。

 

 

Android开发:在ubuntu下安装AMD显卡驱动程序

学习opengl 在线教程,编译glsdk/glload项目的时候出错

||=== glload, Debug ===|
/home/yanzs/opengl/tutorial/glsdk/glload/source/gll_c.c|23|fatal error: GL/glx.h: No such file or directory|
||=== Build finished: 1 errors, 0 warnings ===|

Android 开发是启动AVD出错

Starting emulator for AVD 'AVD_for_Samsung_Note_II'
X Error of failed request:  BadRequest (invalid request code or no such operation)
  Major opcode of failed request:  153 (GLX)
  Minor opcode of failed request:  19 (X_GLXQueryServerString)
  Serial number of failed request:  12
  Current serial number in output stream:  12

 

POSIX Shared Memory (/dev/shm) support is required for 3D applications

I found out from here that it is not enable.

http://ubuntuforums.org/showthread.php?p=8739305#post8739305

I ran the command mount | grep "shm"
and got.
none on /dev/shm type tmpfs (rw,nosuid,nodev)

 

 

I found the answer here

http://ubuntuforums.org/showthread.php?t=122712

To enable POSIX Shared Memory on your system, perform the following as root:

1. Add the following line to /etc/fstab (if it isn't there already): tmpfs /dev/shm tmpfs defaults 0 0
2. Mount shared memory as follows: mount /dev/shm
3. Issue the following command to check that it mounted properly: mount | grep "shm"

If the mount was successful, then the following output (or similar) should appear:
tmpfs on /dev/shm type tmpfs (rw)


I can now have Visual Effects

Bryan

 

the aticonfig command is available after I installed the Catalyst Control Center

 

 

在ubuntu下搭建PhoneGap android应用开发环境

最近开始开发一些简单的手机应用,之前试过在Eclipse里面用原生态的xml设计界面,感觉挺麻烦的。这次搜索了一下免费的跨平台应用开发引擎,发现PhoneGap使用html5, javascript和css,这些还算比较熟悉,所以就选择PhoneGap作为第一个应用的开发引擎,本文简单介绍一下ubuntu下如何搭建PhoneGap android应用的开发环境。

安装

nodejs以及npm

首先安装nodejs及其package manager (npm)

phonegap和cordova

然后用npm安装phonegap以及cordova, 根据phonegap的文档, 使用phonegap开发先得安装cordova’s command-line interface (CLI), 也就是我们这边安装的cordova, 我们后面也会提到CLI的用处。我安装的时候出现了”failed to fetch from registry …”的错误,因此才有下面的第一条命令,如果你直接执行第二条命令没有问题,那么第一步可以跳过。

android相关的开发环境

由于我们要创建android应用,接下来先下载Adroid ADT Bundle,Android ADT Bundle直接集合了Android SDK、Eclipse及其ADT插件等,安装起来比较简单。可以在这里下载linux版本,下载后解压即可。

android应用还得用ant编译,因此需要下载ant,下载后直接解压即可。

再就是要安装jdk,我直接用aptitude安装openjdk-7-jdk。

配置

软件安装完毕, 接下来要进行必要的配置.

添加ANDROID_HOME和ANT_HOME这两个环境变量, 并将android sdk及ant目录下的一些路径加入到PATH中. 打开~/.bashrc文件,在最后添加如下代码:

terminal下执行”source ~/.bashrc”使PATH改动生效,或者打开一个新的terminal.

创建第一个phonegap项目, 并添加android目标平台

这些准备工作完了以后就可以创建应用了,比如要在~/phonegap目录下创建一个名为first-app的应用,只需执行以下命令:

这会在phonegap下创建一个名为first-app的文件夹,其中的platforms子文件夹用于存放不同平台的项目文件,www目录即是html, javascript, css代码。项目刚创建的时候, platforms子目录是空的, 也就是说phonegap没有生成任何平台的代码. 你可以通过如下cordova的CLI来添加android平台的代码:

运行

接下来可以编译运行了,这有几种方法,一是用Eclipse打开android项目,二是直接用phonegap命令。

通过Eclipse编译运行

用Eclipse的话, 要知道phonegap创建的android项目文件放在platforms/android目录下,然后在Eclipse里面,File/New/Project… 选择Android Project from Existing Code并指定前述文件夹,编译之前可能得手动改一下两个项目之间的依赖关系: 选择主项目 => 右键属性 => Project dependencies => 勾上另一个项目。

如果要在android手机上调试,而Eclipse没有检测到手机,那么只要的adt-bundle的sdk/platform-tools下执行”adb devices”即可,这一命令列出已连接的手机,并且如果daemon没有启动的话,会将其启动。

另外,最初我用Eclipse编译的时候,还出现了一个错误:libstdc++.so.6: cannot open shared object file: No such file or directory. 解决方法是使用apt-get安装lib32stdc++6(不知到使用aptitude的话应该安装什么package,没有找到同名的)

通过phonegap命令运行

另一种方法是用phonegap编译/运行,指定run会先编译后运行。

~/phonegap/first-app$ phonegap build android
~/phonegap/first-app$ phonegap run android

我是在用Eclipse编译/运行成功之后才成功用phonegap编译的,因此前面说的使用Eclipse编译需要做的操作很可能对phonegap也是必要的。

以上就是使用phonegap创建一个Hello, World的过程。


参考文档

  1. Getting Started with Android
  2. Setting up PhoneGap on Ubuntu for Android app development

在ubuntu下使用 tesseract-ocr 文字识别工具(基础篇)

OCR的全称是Optical Character Recognition,是指识别计算机图片中的文字内容的过程。

Google Project下有一个C++开源项目叫做tesseract-ocr,这是一个命令行工具,运行时指定需要进行识别的图片,识别出来的文字存储在指定的文本文件中。它能识别包括中文在内的大多数语言,虽然初始安装时只能正确识别比较规范的字体,但可以通过训练提高其识别精确度。所谓训练,实际上就是通过一系列tesseract-ocr提供的工具,根据需要识别的语言及其字体的特点,生成一个以traineddata为扩展名的文件,文件名可随意,不过一般以语言缩写命名,比如训练英文生成的文件就是eng.traineddata,训练简体中文生成的文件就命名为chi_sim.traineddata。使用tesseract对某一图片进行文字识别时,需要指定识别成哪一种语言,然后tesseract就会读取对应该语言的traineddata进行识别。在我安装的ubuntu系统下,安装tesseract-ocr后,其使用的traineddata存放在/usr/local/share/tessdata/目录下。因此只要将你的训练结果复制/替换到这个目录下,tesseract就会根据你的训练结果来识别对应的语言了。由于安装和训练是独立的,你的训练结果可以在多个安装环境下使用;并且,如果有人共享了识别率较高的训练结果,你也不需要自己进行繁琐(是的,安装tesseract-ocr很简单,训练很麻烦)的训练了。

本文记录如何在ubuntu下安装,使用,训练tesseract-ocr。

词汇说明:

  • tesseract-ocr是项目名称,tesseract是安装tesseract-ocr后用于文字识别的命令行工具名称,本文不对两者进行严格的区分。

安装

用aptitude安装

如果你不打算自己训练tesseract(训练工具得通过源代码编译安装),或者想先体验一下tesseract的功能,那么最快速简单的方法就是使用aptitude了。关于aptitude的用法请参考官方User Manual

从源代码安装安装

在安装tesseract-ocr之前,如同官方wiki文档指出的那样,需要先安装一些Dependencies,包括:autotools-dev, libleptonica-dev, autoconf, automake, libtool, libpng12-dev, libtiff4-dev, zlib1g-dev, libicu-dev。这些可以用apt-get安装,也可以用aptitude,我使用的是aptitude。wiki中还提到了libjepg62-dev,但aptitude提示其与libtiff4-dev有冲突,因此我没有安装,但这并不影响本文提到的tesseract的使用及基础训练。

接下来下载代码,可以选择从svn下载,也可以直接下载代码压缩包。svn中包含了很多语言的traineddata,总共有600多M,而单纯的代码包只有几M。由于svn代码较新,相对代码压缩包来说,多了一些本文要用到的训练工具,因此我这里选择从svn下载安装。

代码checkout之后,在terminal下进入这个源代码文件夹,然后按顺序执行以下命令开始编译安装。

./autogen.sh
./configure
make
sudo make install
sudo ldconfig

到了这一步,你就可以看到 tesseract 已经被成功安装了

安装语言文件(Language Data)

在你用tesseract识别某一种语言之前,必须要确保你安装了对应的语言文件(Language Data,这是tesseract-ocr训练生成的文件,不是操作系统的语言包)。要查看是否安装了某种语言,只需要查看 /usr/local/share/tessdata/ 目录下是否有以该语言命名的traineddata就可以了,如果有eng.traineddata,那说名tesseract可以识别英文了,同样,chi_sim.traineddata对应于简体中文。

如果某种语言没有安装,安装方法也很简单,首先到官方下载列表里面找到并下载对应语言包。比如我要识别英文,而我安装的tesseract-ocr是3.02版本的,就下载对应版本的英文语言包 tesseract-ocr-3.02.eng.tar.gz。下载后解压,然后将解压出来的tesseract-ocr/tessdata/目录下的所有文件剪切到 /usr/local/share/tessdata/ 目录下即可。

如果你是通过build 从svn下载的代码 来安装的话,源代码目录下的tessdata子文件夹下就有tesseract能识别的各种语言的语言文件,直接将这里的语言文件复制到 /usr/local/share/tessdata/ 目录下就行了。或者你也可以用安装命令来安装语言文件,在源代码根目录执行以下命令进行安装:

  • 安装部分语言文件
    sudo make install LANGS="eng chi_sim"
  • 安装tesseract支持的所有语言文件
     sudo make install-langs

如何卸载通过编译源代码安装的tesseract-ocr

在接下来的使用、训练过程中,如果你想要试一下不同安装方法,你就需要知道如何卸载从源代码安装的tesseract-ocr。方法是在源代码根目录下执行以下代码以便卸载tesseract:

sudo make uninstall

卸载过程会删除包括 /usr/local/include/tesseract 目录下的头文件,/usr/local/bin 下的可执行文件,/usr/local/lib 下的库文件,以及 /usr/local/share/tessdata 下的训练文件在内的 tesseract 相关文件。

在源代码的training子目录下执行同样的代码即可卸载training tools。

使用

语言文件安装完成后,就可以开始识别了。比如我有下面这张图片 tesseract-line.png

要识别这张图片里面的英文,只需执行以下命令:

$ tesseract tesseract-line.png tesseract-line -l eng

这条命令里面,第一个参数指定需要进行文字识别的图片地址,第二个参数指定存放识别出文字的不包括扩展名的文件名,最后一个参数-l eng指定用什么语言库进行识别。比如这里指定了eng,tesseract就会根据eng.traineddata进行识别。实际上traineddata文件名可以随便取,只要使用tesseract进行文字识别的时候也用相同的名字就行了。

这里识别的结果存在一个名为tesseract-line.txt的文件中,可以看到tesseract识别这种字体清晰的图片还是很准确的:

 

训练 ,这才是硬骨头

前面试着用tesseract识别了一张字体清晰的图片,接下来试一下一个比较简单的验证码图片。

这几个字母经tesseract识别就成了MWLS,可见直接使用官方提供的语言文件时 tesseract 的识别精度是不足以实际应用的。所幸的是,tesseract-ocr提供了一系列工具用于生成自定义的语言文件,这一过程就叫做训练。

安装训练工具

在进行训练之前,首先需要安装tesseract-ocr提供的训练工具。

在编译安装训练工具之前,还得先安装这些包 : libicu-dev, ligpango1.0-dev, libcairo2-dev

另外,确保在源代码根目录下执行过以下几条命令

./autogen.sh
./configure
make

前面提到过,这些命令也是通过源代码安装tesseract前必须执行的。如果前面执行过,这里就不必再执行一遍了。这几条命令,前两条用于生成Makefile文件,包括根目录及各子目录(如training, ccutil),第三条命令是进行编译tesseract及其依赖的一些项目。由于训练工具也会依赖这些项目,因此在编译训练工具之前需要先编译这些依赖的项目。

接下来就可以进入到源代码的training子目录,然后执行以下命令编译并安装训练工具

$ sudo make install

训练过程初探,前面都是准备工作,这才是重点与难点。由于训练过程涉及到很多知识,这里只介绍完成训练所需要的最基本的操作方法,不细讲原理,也要求训练结果能否提高识别率。通过这一教程对训练过程有了初步的认识后,我会在另一篇文章里面进行更加详细的介绍。

  1. 生成训练图片
    首先用你要训练的语言创建一个文本文件,比如我要训练英文,就创建了一个名为training_text.txt的文本文件

    这个文本文件必须包含你希望tesseract识别的所有字符,并且为了提高识别精度,将来在要识别的图片中出现频率高的字符也应该在这个文本文件里出现更多次。接下来,需要用到一个叫做text2image的训练工具对这个文本文件进行分析,生成一张包含这些文字的图片,以及个字符在这张图片上的位置信息。text2image的用法为:

    $ text2image –text=training_text.txt –outputbase=[lang].[fontname].exp0 –font='Font Name' –fonts_dir=/path/to/your/fonts

    可以看到,这里需要指定一个truetype字体(字体文件以ttf为扩展名),这个字体就是你希望tesseract能够识别的字体。在ubuntu系统的/usr/share/fonts/truetype/freefont/目录下有一个FreeMono.ttf,这里就以这个字体为例进行训练

    $ text2image –text=training_text.txt –outputbase=eng.freemono.exp0 –font=FreeMono –fonts_dir=/usr/share/fonts/truetype/freefont/

    如果这一步执行成功,你会看到类系下面截图的信息,并且text2image会生成两个文件:eng.freemono.exp0.tifeng.freemono.exp0.box,前者是包含training_text.txt文字的图片文件,后者是一个文本文件,记录各字符在这个图片文件中的位置信息。如果执行成功,跳到第2步。

    如果你执行text2image时碰到类似于"text2image: error while loading shared libraries: libtesseract.so.3: cannot open shared object file: No such file or directory."的报错,那就先执行以下命令再重试,具体原因参考stackoverflow

    export LD_LIBRARY_PATH=/usr/local/lib

    如果执行text2image的时候命令行输出了几十行的dump信息,那很可能你的tesseract-ocr不是通过源代码编译安装的,我碰到个过这种情况,最后改成从源代码编译安装解决。
     

  2. 开始训练
    用tesseract进行训练的命令用法为:

    tesseract [lang].[fontname].exp[num].tif [lang].[fontname].exp[num] box.train.stderr

    用于我们前面生成的tif及box文件,就是:

    tesseract eng.freemono.exp0.tif eng.freemono.exp0 box.train.stderr

    正确执行的结果如下图:

    这一步会生成两个文本文件:eng.freemono.exp0.treng.freemono.exp0.txt,后者只有一些换行符,前者对应于box文件中各字符在tif图片文件中的形状信息,记录的方式实际上是将一个字符看成是一个多边形,而tr文件记录的就是多边形每条边的位置、方向、长度等信息。
     

  3. 生成字符集信息,这需要用到一个叫unicharset_extractor的训练工具
    具体用法为:

    unicharset_extractor lang.fontname.exp0.box lang.fontname.exp1.box …

    我们前面只生成了一个box文件,因此执行以下命令收集字符集信息:

    unicharset_extractor eng.freemono.exp0.box

    成功执行的结果如下图:

    这一步会生成一个名为unicharset的文本文件,正如其名字表明的,这个文件记录的是一个字符集,它存有box文件里面不重复的字符信息,每个单独字符占一行。
     

  4. 创建字体信息文件font_properties
    由于我们可以训练tesseract识别同一种语言的不同字体(这里只训练一种字体),我们需要提供字体相关的特性,这是通过一个叫做font_properties的文本文件标明的。这个文件的每一行以如下格式记录了一个字体的信息:

    <fontname> <italic> <bold> <fixed> <serif> <fraktur>

    本文的训练中使用了名为FreeMono的字体,因此font_properties里面需要有一行以FreeMono开头的字体信息。
    除了手动创建这个文件外,tesseract-ocr源码中也提供了一个这样的font_properties文件(training/langdata/font_properties),并且里面已经有了很多字体的信息,因此这里就不许要手动创建了,后面的步骤要用的这个文件的时候,直接指定使用这个文件就行了。源代码中的font_properties文件关于FreeMono字体的信息是:
    FreeMono 0 0 1 1 0
     

  5. 聚合
    shapeclustering, mftraining及cntraining的用法

    shapeclustering -F font_properties -U unicharset lang.fontname.exp0.tr lang.fontname.exp1.tr …
    mftraining -F font_properties -U unicharset -O lang.unicharset lang.fontname.exp0.tr lang.fontname.exp1.tr …
    cntraining lang.fontname.exp0.tr lang.fontname.exp1.tr …

    首先使用shapeclustering

    shapeclustering -F source/training/langdata/font_properties -U unicharset eng.freemono.exp0.tr

    成功执行结果如下图,这一步会输出一个名为shapetable的文件,下一步的mftraining会自动在当前目录加载这个文件。

    接下来执行mftraining

    mftraining -F source/training/langdata/font_properties -U unicharset -O eng.unicharset eng.freemono.exp0.tr

    成功执行结果如下图(输出结果有警告,但不影响),执行完成后会生成三个文件:eng.unicharsetinttemppffmtable

    最后执行cntraining

    cntraining eng.freemono.exp0.tr

    成功执行的结果如下图,这一步生成一个名为normproto的文件

     

  6. 合并生成traineddata文件
    首先将前面生成的几个文件(包括shapetable, normproto, inttemp, pffmtable)更名为以lang.开头(在这里,就是以eng.开头,比如eng.shapetable, eng.normproto等等),然后执行以下命令将它们合并成一个traineddata文件(eng.traineddata

     
  7. 至此,训练完毕,只需按照前述的安装语言文件的方法安装训练得到的eng.traineddata就可以开始使用你的训练成果了。需要注意的是,根据本文的训练生成的traineddata只能识别本文最初创建的training_data.txt中存在的字符,并且只能识别字体与FreeMono接近的文字。我试着用这个traineddata识别“使用”一节中提到过的tesseract-line.png文件,会得到如下结果:
    Pummg Bt a" mgemer
    可见这种简单的训练识别率是很低的。本文只是简单的介绍一下训练的过程,我之后会写一篇旨在提高识别精度的更具体的训练过程,敬请期待。

 


references:

How to use the tools provided to train Tesseract3 for a new language. 
Compilation guide for various platforms


写作心情:

连日阴雨,这两天偶尔还能看见太阳,这种天气最适合待在家里看电视剧了。
没有上班的日子,给自己定了工作时间。中午偶尔会多睡一个小时,但想着自己是在公司上班,也不会觉得内心不安了。淡定淡定,be patient, young one. 

解决因修改ubuntu系统文件导致无法登录系统的问题

今天试着改了~/.profile文件,这是一个当前用户登录时会执行的shell代码文件,由于改代码的时候有语法错误,导致我注销后就不能重新登录了。症状是输入密码回车后,屏幕闪一下,然后重新回到输入密码的界面。

既然是因为改动了~/.profile文件导致无法登录,只要修正这个文件就行了,试了以下几种方法,只有最后一个可行。

  • 试着用Guest账户登录,但没有修改权限。
  • 重启系统,在grub菜单选择进入Ubuntu (repair version),然后里面可以以root用户使用terminal,但提示read-only file system,因此也不能修改文件。
  • 重启系统,在grub菜单,进入grub命令模式,没有发现可以修改文本文件的工具,因此也不可行。
  • 重启系统,通过ubuntu的u盘安装盘(如果你用dvd安装,就从dvd启动),通过试用ubuntu进入ubuntu系统。由于我的电脑安装了windows和ubuntu双系统,因此通过u盘启动进入ubuntu后,既可以看到windows的分区,也可以看到ubuntu的分区(只有一个)。硬盘中的ubuntu分区会被u盘中的ubuntu系统mount成/media/下的一个文件夹,在这个文件夹下可以找到你修改的系统文件。要修改这个文件,打开terminal,执行以下代码用vi编辑器修改文件:
    sudo vi /media/mount-folder-name/home/yanzs/.profile

    在这里,mount-folder-name为硬盘中的ubuntu分区对应的mount文件夹名,yanzs为我修改.profile文件时登录的用户名,如果你修改的是其他系统文件,那么这里也可以用类似的方法修复。

 


references:

http://stackoverflow.com/questions/14458942/edit-a-file-using-grub