2012年12月

昨天晚上申请的一个新域名,其实是有一个好的网站的想法,发扬我想到就做的优良传统,立马就注册了这个相应的域名, codinuts.com 为 *coding nuts* 的全体词,意思是 “*我为编码狂*”, 网站程序准备使用刚刚开始接触的 Golang 进行开发,数据库现在还没有想好使用什么,我其实现在正好也在研究 Amazon AWS,所以,数据库准备使用 AWS RDS,静态文件准备使用AWS S3,而网站的VPS则使用AWS EC2,恩,是一个不错的搭配。

该网站的功能简单来说就是:用户提出实际问题,用户用编码的方式解决实际问题。任何一个实际问题都可以让任何用户来解决,他们可以将他的解决方法公布出来,而每一个解决方法又可以被别的用户优化。更具体的东西,等以后再慢慢地说, 这里只是记录一下下想法,如果你发现这篇文章的发布时间已经是很久以前了,不防看看 Codinuts.com 是否已经上线了……

在 Unix Shell 中, 命令替换(Command Substitution) 是一个非常强大的概念,它被用来将某一个命令的输出作为参数直接插入到另一个命令中,如下例中,我将 date 命令的值赋于 *$today$ 变量,然后再将其输出:

cox@Cox:~$ today=$(date)
cox@Cox:~$ echo "$today"
2012年 11月 21日 星期三 01:39:31 CST

在上面的示例中,我们可以把两人行命令简化为一行:

cox@Cox:~$ echo $(date)
2012年 11月 21日 星期三 01:57:20 CST

或者像下面这样更复杂的使用:

cox@Cox:~$ echo "今天是 $(date +%A),现在时间是 $(date +%H:%M)"
今天是 星期三,现在时间是 01:59

这个示例中, echo 命令调用了 date 命令两个,第一次打印出星期几,第二次打印出当前时间,上面出现的三个命令的顺序是:

第一个 date ⇒ 第二个 date ⇒ echo

当然了,上面只是示例,其实我们有更简单的办法来打印出同样的信息:

cox@Cox:~$ date "+今天是 %A,现在时间是 %H:%M"
今天是 星期三,现在时间是 02:02

但是有一个问题需要注意,嵌套命令的结果会被进行 单词分割,除非所有的内容都被包裹在一个 双引号 中,否则这会导致有些时候你得到的并不是你自己想的,比如下面这行命令:

cox@Cox:~$ ls | grep Ubuntu
Ubuntu One
cox@Cox:~$ for i in $(ls | grep Ubuntu); do echo $i; done
Ubuntu
One

看到他们的不同了没有?我的当前目录下面本身是有一个名为 Ubuntu One 的目录,但是第二个命令本身的意途是想遍历当前目录中所有名称包含 Ubuntu 的文件,但是 Ubuntu One 却被当作两个来处理了,这是因为这个目录中有一个空格的存在,如前面所说的,结果会被进行单词分割,空格使得这本身是一个目录的名称被分割为两个了,对于这个问题,应该使用下面这样的命令去实现:

cox@Cox:~$ for i in Ubuntu*; do echo $i; done
Ubuntu One

命令是可以无限制嵌套的,比如下面这个示例:

cox@Cox:~$ echo $(echo (这是第一层嵌套($(echo 这是第二层嵌套))))
(这是第一层嵌套(这是第二层嵌套))

替换命令可以创建 Subshell ,这很有用,因为 subshell 所作的任何修改都只对它本身有效,不会污染程序的全局环境,比如下面这个示例:

cox@Cox:~$ var=$(cd ../../usr/bin/; pwd)
cox@Cox:~$ echo "$var"
/usr/bin
cox@Cox:~$ pwd
/home/cox

在命令替换中,父命令会去除子命令的输出结果中所有的换行:

cox@Cox:~$ ls -lh | grep Ubuntu
drwxrwxr-x 7 cox  cox      4.0K 11月 20 19:16 Ubuntu One
drwxrwxr-x 2 cox  cox      4.0K 11月 21 02:20 Ubuntu Two
cox@Cox:~$ echo $(ls -lh | grep Ubuntu)
drwxrwxr-x 7 cox cox 4.0K 11月 20 19:16 Ubuntu One drwxrwxr-x 2 cox cox 4.0K 11月 21 02:20 Ubuntu Two

通用性

$(command) 命令可以在 KornShellBASH以及PosixShell 中使用,老一些的 shell (比如BourneShell)则需要使用返引号"",形式是 *command*。

就像我以前说过的,我所想要的一个博客系统应该是最简单的,我只需要发布一些文章和几个静态页面而已,WordPress或者Drupal这种东西实在是太大了,所以我一直都是使用TextPattern作为我的博客发布系统的,从今天开始,我开始转到一个基本Python的纯静态发布系统 - Felix Felicis

静态发布是我最喜欢的方式(没有之一),我喜欢的网站发布系统应该是这样的,内容可以以最简单的方式管理,根据模板发布成为纯HTML页面,之后需要动态调用的(比如说评论需要服务器端处理)使用一个统一的接口(这似乎Movable Type是最合适的,但是它实在是太太大了),而现在这个就完全满足了我的要求。

今天一个下午的时间写了一个同步工具,我只需要在本地发布,然后该同步工具就会自动的将发布得到的HTML页面、CSS/JS文件、图片资源等等同步更新到我的一个S3桶里面去,而我的域名因为绑定到这个桶,所以就有了你现在看到的这个博客了,关于如何绑定域名到S3的桶,请看我前面专门介绍该问题的文章《Amazon S3的域名绑定》

我的同步工具现在还只是实现了将更新上至S3桶中,但是还没有任何其它优化,比如如果文件没有更新就不上传,如果本地已经不存在的,在S3服务器也同步删除掉那个对象等等功能,不过都还在开发中,应该今天晚上就会搞定的吧,刚刚开始使用S3没多久,还有很多事情需要做啊……评论我已经使用了DISQUS的服务,这样一来就省得我再在自己的服务器上写评论系统了,而且这个东西用起来并不比我写的差(应该说是好得多吧……)。

现在更新文章的方式再简单不过了:

  1. 在本地相应的目录中(比如: */home/cox/cox.im/content*)创建一个新文件(我取名为:*My Blog Is Using Static Publish System.md*)。
  2. 打开该文件,以下面这样的格式输入内容:
    # 我的博客现在已经开始使用静态发布系统进行更新了
    
    - url_title: my-blog-is-using-static-publishing-system
    - date: 2012-11-19 17:36:36
    - public: true
    - tags: Blog, Python, Amazon, S3, CMS, Boto
    - category: tech
    
    --------------------------------------------------------
    
    就像我以前说过的,我所想要的一个博客系统应该是最简单的,我只需要发布一些文章和几个
    静态页面而已,WordPress或者Drupal这种东西实在是太大了,所以我一直都是使用TextPattern
    作为我的博客发布系统的,从今天开始,我开始转到一个基本Python的纯静态发布系统。
  3. 保存该文件,在当前目录(*settings.py*)文件所在目录,执行下面命令:
    $ liquidluck build -v
  4. 重新发布完成之后,就可以使用Tornado服务器进行预览:
    $ liquidluck server
  5. 打开 http://127.0.0.1:8000 即可预览刚才的更新了,如果都没有问题,则执行我的同步工具:
    $ python sync.py
  6. 同步完成之后(这需要一些时间,但是如果是在服务器上面更新的话,那应该会快很多),即可打开自己的博客查看更新了http://cox.im/.

上面这是一种特定的格式,在第一个—————-*之前的所有内容都属于文章的Meta信息,之后的就是文章正文,Meta信息的格式就是上面这种的,我给本文定义了 *url_title*、*date*等属性,这些并不全都是必要的,但是 *date 如果有的话就称之为文章(博客文章),如果没有这个属性的话就是页面(静态页面是不需要跟踪时间的)。所有的这些属性都是可以在模板中使用的,模板引擎使用的是 *jinja2*。

我准备把现在使用的这个系统进行一些修改,增加一些功能,比如发布的时候可以选择发布在本地服务器、FTP发布、SHELL登陆远程服务器发布、S3发布等等,同时还需要增加一些其它更通用的细节功能吧,根本自己的需求来修改……

我一直使用的都是 MarkDown,使用起来很方便,当然 Felix Felicis 系统本身还支持reStructuredText,你也可以自己扩展适合你自己使用习惯的MarkUp Language。

这么算一下,已经有一个星期没有离开店子和仓库了,现在的生活就是这么简单,早上八九点醒来开始一天的工作,中午吃饭,再吃晚饭,再到晚上八九点,开始学习/写代码,晚上两人三点煮个饭拌点饭扫光,啊,不错,一天就这么过去了。

感觉得到自己在进步,虽然不知道这进步能给我带来什么,但是总不停在原地要好,虽然不在IT这个行业,但是一直还是放不下这个东西,或许这样更好,兴趣永远都还是我的兴趣,我可以学习一切我以前没有时间和精力去学习的东西,当然,不是为了工作,而仅仅只是兴趣。

只是感觉自己落后这个行业太远太远,所以,只能更加努力啊……

  • Python还是我使用的最主要的语言
  • 开始使用Bash脚本并开发了一些实用的服务器工作
  • 开始使用Golang
  • 开始使用Amazon AWS服务
  • 还是在使用Linode 的VPS服务
  • 还是回到Textpattern,最简单的才是我感觉最好的
  • 接了两人个小项目,不过最后的这一个钱还没收到
  • ……

一不小心又把博客域名给换了,以前的*note.costony.com*和*cox.antusoft.com*都会自动的转身到现在的这个新地址,对于使用 *cox.antusoft.com*转向过来的都可以对应到与cox.antusoft.com上面相同的文章上面,但是从note.costony.com上面转向过来的就不一定了,因为数据库使用的不是一样的。

其实我并不是一个米农,我只是想找一个最短的域名,自己好好地写写博客,但是在网上混了这么多年,也一直没有找到,或者说找到了要么别人注册着在用了,要么是我等草根买不起的,比如那个chenxi.com别人一出价就是四十万,按我现在的这点收入要不吃不喝100个月,可能买得起吗?

另外,虽然我一直最喜欢的网站内容发布方式是 Movable Type的那种纯静态的发布,但是因为它实在是太大了,我只是想写个博客,在Movable Type里面还得先建一个网站,然后在网站上面再建一个博客,太麻烦了,我还是一个ID控,这个让我对WordPress是直接Pass掉了的(虽然我也用过,但那都是以前的版本),现在的WordPress让我完全无法控制ID。

现在正在研究Golang以及Amazon AWS相关的产品,发现自己又落后了不少,得努力补习上来啊,博客的服务器转移到了 Amazon EC2上面,应该是可以免费使用一年吧,不过不知道像我这种资源乱用的人是不是每个月都会超出使用限制,甚至现在那个限制是多少我都没有认真的去研究过,反正应该不多吧。

另外,我现在正在准备把博客的前端界面换一下下,感觉还是有点儿太复杂了,换个更简单的。

Amazon EC2 的默认用户名为 *ec2-user*,在默认情况下,我们是没有办法修改这个用户的名称的,原因是这样的:1) 我们只能使用该用户登陆服务器;2) 登陆之后就无法修改它的用户名,但是我们还是有别的办法来修改它的。

直接不管它,创建一个新用户

这个方法其实足够了,创建了一个新的 EC2 Instance之后,使用下面的命令修改 root 帐户的密码:

[ec2-user@coxantu ~]$ sudo passwd root

这会提示你输入新密码以及重复输入一次,之后使用下面命令创建一个新用户:

[ec2-user@coxantu ~]$ su
密码:
[root@coxantu ec2-user]# useradd antu -p password

之后如下命令,修改 */etc/ssh/sshd_config* 文件

[root@coxantu ec2-user]# vi /etc/ssh/sshd_config

找到下面这部分并作相应的修改:

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes

修改为

# To disable tunneled clear text passwords, change to no here!
PasswordAuthentication yes

之后重启 *sshd* 服务:

[root@coxantu ec2-user]# /etc/init.d/sshd restart
停止 sshd:                                                [确定]
正在启动 sshd:                                            [确定]

之后我们再退出,重新使用密码认证的方式就可以登陆服务器了,比如:

cox@CoxStation:~$ ssh cox@ec2-23-22-97-113.compute-1.amazonaws.com
cox@ec2-23-22-97-113.compute-1.amazonaws.com's password:

   __|  __|_  )
   _|  (     /   Amazon Linux AMI
  ___|___|___|

https://aws.amazon.com/amazon-linux-ami/2012.09-release-notes/
There are 3 security update(s) out of 29 total update(s) available
Run "sudo yum update" to apply all updates.
[cox@coxantu ~]$

这个时候我们也没有必要再去删除前面那一个默认用户了,当然,如果你想你的系统“干干净净”的,那你直接删除它即可。

rdesktop 可以在Linux中连接Windows远程桌面,不过像我这种天天挂Q的人还是很不方便,毕竟要么在远程桌面和Linux桌面中切换,要么在那个远程桌面的小窗口里面上Q,而QQ的Linux版本用起来还是很不爽,所以,在前面的文章里面带过 Seamless 这个事情之后,我还是在今天好好的研究了一下下这个东西。

如果你也和我一样想使用这个东西,可以从「Cendio 网站」下载,不过我在网上找了很多资料都直接提供了 Seamless的下载地址,但是这个地址却总是下载不了,会转向到http://www.cendio.com/seamlessrdp/,在这里面只能注册自己的邮箱地址然后下载 ThinLinc 软件的完整版,总共有90Mb,对于只想使用 seamlessrdp 的我来说太大了,那个小东西只有几百Kb,为了方便大家,我把这个提取出来再发布了,可以从「http://dl.antusoft.com/tools/thinlinc/tl-wts-tools.exe」这个地址下载,它是一个安装文件,在Windows桌面安装之后,会一同安装 “*seamlessrdpshell.exe*”工具。

如果你使用的是默认的安装,那么该文件的路径为:“*C:Program FilesThinLincWTSToolsseamlessrdpshell.exe*”,这个地址需要记住,因为我们在后面会使用到它。

完成Windows端的软件安装之后,我们现在只需要在Linux端执行 rdesktop 时加入一个 -s 参数即可,当然,还需要指定要打开的Windows软件的路径,比如我现在想打开我仓库的二号机的远程桌面,并且运行“*D:Program FilesBAISON_PGBS3000+CLIENT_CSCKBSBusiness.exe*”程序,那么可以使用下面这行命令:

rdesktop -s "C:Program FilesThinLincWTSToolsseamlessrdpshell.exe D:Program FilesBAISON_PGBS3000+CLIENT_CSCKBSBusiness.exe" 192.168.1.5 -u <Windows Username> -p <Windows Password>

这会在桌面的右下角打开一个默认大小的窗口,并且只运行 BSBusiness.exe 程序(它会自动打开)。(将 修改为你的Windows登陆用户名, 修改为你的帐户密码即可)。

但是这还不是很好,我需要它的窗口能大一些,而且不想放在右下角,一出来就能在我想要的位置,比如正中间(或者左上角),我们可以将上面的命令做下面这样的修改:

rdesktop -g 1400x900 -s "C:Program FilesThinLincWTSToolsseamlessrdpshell.exe D:Program FilesBAISON_PGBS3000+CLIENT_CSCKBSBusiness.exe" 192.168.1.5 -u <Windows Username> -p <Windows Password>

这会打开一个尺寸为 1400px * 900px 大小的窗口,其窗口的右下角还是对在桌面的右下角,这你应该看到,参数 -g 1400x900 指定了打开了桌面的大小,或许你还可以再修改成为下面这个样子:

rdesktop -g 1400x900+100 -s "C:Program FilesThinLincWTSToolsseamlessrdpshell.exe D:Program FilesBAISON_PGBS3000+CLIENT_CSCKBSBusiness.exe" 192.168.1.5 -u <Windows Username> -p <Windows Password>

这个时候你看到窗口,它已经不在右下角了,而是以它的左上角为定位点,距桌面上边距离为0,而左边距离为100px进行定位,我们还可以再加一个参数,用来修改窗口的上边与桌面上边的距离:

rdesktop -g 1400x900+100+100 -s "C:Program FilesThinLincWTSToolsseamlessrdpshell.exe D:Program FilesBAISON_PGBS3000+CLIENT_CSCKBSBusiness.exe" 192.168.1.5 -u <Windows Username> -p <Windows Password>

这还不是我想要的结果,我想要的是没有Windows桌面的出现,我想让程序就像是安装在Linux系统中一样,这个时候我们可以使用下面这样的命令:

rdesktop -A -s "C:Program FilesThinLincWTSToolsseamlessrdpshell.exe D:Program FilesBAISON_PGBS3000+CLIENT_CSCKBSBusiness.exe" 192.168.1.5 -u <Windows Username> -p <Windows Password>

这只是把 -g 这个参数去掉,再加上了一个 -A 这个参数,结果应该看到只有程序窗口,不再有桌面背景之类的了。

该版本增加了开源的PHP内容管理系统的安装,系统默认提供了由安图软件提供的开源软件源中精选的一些开源PHP内容管理系统,用户还可以自己为该脚本增加支持的CMS系统,只需要增加remote_apps 变量的项目即可,具体方法请见脚本说明。

您可以直接使用下列命令获得该脚本:

wget http://dl.antusoft.com/tools/server-setup-tool/sst-v0.3.sh

或者直接点击下载链接下载到本地

#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH

# Check if the user if root
if [ $(id -u) != "0" ]; then
    echo "ERROR: You must login as root ro run this script!"
    exit 1
fi

#clear

# default global variables
declare -A vars
declare -A apps
# do you want to upgrade the server first before set it up?
vars=(
[upgrade_server]="n"
# the default website meta information
[default_website_owner]="cox"
[default_website_domain]="_"
[default_website_error_log]="n"
[default_website_access_log]="n"
[default_website_applications]=""
[default_website_error_log]="error.log"
[default_website_access_log]="access.log"
[default_website_log]="y"
# mysql database's root password
[mysql_root_password]="root"
# install nginx extras or not
[install_nginx_extras]="y"
[install_common_cgi_support]="y"
[install_perl_support]="y"
# install phpmyadmin to the document root of default website or not?
[install_phpmyadmin]="y"
[install_movabletype]="n"
)
export vars

apps=(
[apps_server_root]="http://dl.antusoft.com/"
[apps_server_root2]="http://dl.coin.so/"
[phpmyadmin]="tools/phpmyadmin/phpMyAdmin-3.5.3-all-languages.tar.gz"
[wordpress]="cms/wordpress/wordpress-3.4.2-zh_CN.tar.gz"
[wordpress_en]="cms/wordpress/wordpress-3.4.2.tar.gz"
[drupal]="cms/drupal/drupal-7.17.tar.gz"
[openatrium]="cms/drupal/dist/openatrium-6.x-1.5-core.tar.gz"
[dokuwiki]="cms/dokuwiki/dokuwiki-2012-10-13.tgz"
[textpattern]="cms/textpattern/textpattern-4.5.2.tar.gz"
[movabletype]="cms/movabletype/MT-5.2.tar.gz"
[mediawiki]=""
)

remote_apps=(
# you can add you own cms list here
# one record per line
# format:
# [CMS_NAME_KEY]="CMS_REMOTE_DOWNLOAD_URL"
# e.g.
[mediawiki]="http://download.wikimedia.org/mediawiki/1.20/mediawiki-1.20.0.tar.gz"
)

export remote_apps
export apps

# hightlight echo
function hightlight() {
    echo -e "33[40;31;5m $1 33[0m "
}

# separate line
function separate_line() {
if [ "$1" = "b" ]; then
echo "================================================================="
else
echo "-----------------------------------------------------------------"
fi
}

# function set variable to set vars
function set_variable() {
    separate_line
    # if not variable name
    if [ "$1" = "" ]; then
        hightlight "ERROR: no variable name!"
        return
    fi
    # if got a value
    if [ "$2" != "" ]; then
        vars[$1]=$2
    fi
    echo "Set the value of variable: $1"
    # echo the description of this value
    echo $3
    # get new value from user input
    read -p "current value is $(hightlight ${vars[$1]}) ): " v
    if [ "$v" != "" ]; then
        vars[$1]=$v
        echo "$1 => $v"
    else
        echo "no changes, default value will be used."
        echo "$1 => ${vars[$1]}"
    fi
}

function declare_variable() {
if [ "$2" != "" ]; then
    echo $2
else
    echo $1
fi
}

function check_user() {
echo $(cat /etc/passwd|grep $1|wc -l)
}

function create_user() {
if [ "$(check_user $1)" = "0" ]; then
useradd $1
fi
}

function copyright() {
separate_line b
echo "Server Setup Tool for Ubuntu - by Cox"
separate_line
echo "Version: 0.3"
echo "Modified: 2012/11/16"
echo "Url: http://cox.antusoft.com/project/ubuntu-server-setup-tool/"
separate_line
echo "Author: Cox Antu"
echo "Email: cox # antusoft.com"
echo "Url: http://cox.antusoft.com/"
echo "Mobile: 183-7488-8188"
echo "QQ: 54778899"
separate_line b
echo ""
}

function help() {
echo "-h --help for help"
echo "-s --setup for setup"
separate_line
}

function update_server() {
apt-get update
if [ "${vars[upgrade_server]}" = "y" ]; then
    echo "upgrading your server..."
    stty -echo
    echo y | apt-get upgrade
    stty echo
fi
separate_line
}

function install_mysql() {
echo "Installing mysql for you..."
echo y | apt-get install mysql-client mysql-server
separate_line
}

function install_nginx() {
echo "Installing nginx for you..."
if [ "${vars[install_nginx_extras]}" = "y" ]; then
    echo "You are going to install nginx-extras"
    echo y | apt-get install nginx-extras
else
    echo y | apt-get install nginx
fi
separate_line
}

function install_php() {
echo "Installing php support for nginx..."
echo y | apt-get install 
  php5 php5-fpm php5-mysql php5-curl php5-gd php5-idn 
  php-pear php5-imagick php5-imap php5-mcrypt php5-memcache 
  php5-mhash php5-ming php5-ps php5-pspell php5-recode 
  php5-snmp php5-sqlite php5-tidy php5-xmlrpc php5-xsl 
  php5-json

/etc/init.d/php5-fpm restart
separate_line
}

function php_socket() {
if [ -S /var/run/php5-fpm.sock ]; then
echo "unix:/var/run/php5-fpm.sock"
else
echo "127.0.0.1:9000"
fi
}

function install_common_cgi_support() {
echo "Installing common cgi support for nginx..."
echo y | apt-get install fcgiwrap
separate_line
}

function install_perl_support() {
echo "Installing perl support for nginx"
echo y | apt-get install 
  dbconfig-common libarchive-zip-perl libcache-perl 
  libclass-data-inheritable-perl libclass-errorhandler-perl 
  libclass-inspector-perl libclass-load-perl libclass-singleton-perl 
  libclass-trigger-perl libcommon-sense-perl libconvert-binhex-perl 
  libcrypt-dh-gmp-perl libdata-objectdriver-perl libdatetime-perl 
  libdatetime-timezone-perl libdbd-pg-perl libdbd-sqlite3-perl 
  libfile-nfslock-perl libgd-gd2-perl libheap-perl libimage-size-perl 
  libio-stringy-perl libipc-run-perl libjcode-pm-perl libjson-perl 
  liblist-moreutils-perl liblucene-queryparser-perl libmath-round-perl 
  liblwp-authen-wsse-perl libmime-charset-perl libmime-encwords-perl 
  libmodule-runtime-perl libnet-openid-common-perl libio-pty-perl 
  libossp-uuid-perl libossp-uuid16 libpackage-deprecationmanager-perl 
  libpackage-stash-perl libpackage-stash-xs-perl libparams-classify-perl 
  libparams-util-perl libparams-validate-perl libpq5 libsoap-lite-perl 
  libsub-install-perl libtask-weaken-perl libtext-simpletable-perl 
  libtry-tiny-perl liburi-fetch-perl libxml-atom-perl 
  libxml-xpath-perl libjson-xs-perl libxml-libxslt-perl 
  libdata-optlist-perl libdatetime-locale-perl libfcgi-perl 
  libmime-tools-perl libtheschwartz-perl libnet-openid-consumer-perl
separate_line
}

function setup_website_directly() {
# $1 => website domain name
# $2 => website owner
# $3 => applications list for install
# $4 => log or not to log
# $5 => error log
# $6 => access log
domain=$(declare_variable ${vars[default_website_domain]} $1)
owner=$(declare_variable ${vars[default_website_owner]} $2)
applications=$(declare_variable ${vars[default_website_applications]} $3)
log=$(declare_variable ${vars[default_website_log]} $4)
error=$(declare_variable ${vars[default_website_error_log]} $5)
access=$(declare_variable ${vars[default_website_access_log]} $6)

conf_file="/etc/nginx/sites-available/$domain"
listen="80"
server_name="$domain"
domain_root="/home/$owner/websites/$domain"
root="$domain_root/public"
logs_root="$domain_root/logs"

error_log="$logs_root/$error"
access_log="$logs_root/$access"
touch error_log
touch access_log
error_log="error_log $logs_root/$error"
access_log="access_log $logs_root/$access"
# if not to log the website
if [ "$log" = "n" ]; then
    error_log="#$error_log"
    access_log="#access_log"
fi
php_fastcgi_pass="$(php_socket)"

create_user $owner

mkdir -p $root
mkdir -p $logs_root

chown $owner:www-data -R $domain_root
chmod g+w -R $domain_root

cat >>$root/info.php<<EOF
<?php phpinfo(); ?>
EOF

# create configuration file
if [ -f $conf_file ]; then
    cp $conf_file $conf_file.bak
    cat /dev/null > $conf_file
fi

cat >>$conf_file<<EOF
server {
  listen 80;
  server_name $server_name;
  root $root;
  $error_log;
  $access_log;
  index index.php index.html index.htm;

  # php
  location ~ .php$ {
      try_files $uri = 404;
      fastcgi_pass $php_fastcgi_pass;
      fastcgi_index index.php;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      include fastcgi_params;
  }

  # perl cgi movable type
  location ~ ^/mt.*.cgi {
    # the exact pattern is required fo MT.
    fastcgi_split_path_info ^(/mt.*.cgi)(.*)$;

    fastcgi_pass unix:/var/run/fcgiwrap.socket;
    include fastcgi_params;

    # MT related
    fastcgi_param PERL5LIB $document_root/lib;
    fastcgi_param MT_HOME $document_root;
    fastcgi_param MT_CONFIG $document_root/mt-config.cgi;

    # fcgiwrap related
    fastcgi_param SCRIPT_NAME $document_root$fastcgi_script_name";
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name";
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
  }

}
EOF

cur_dir="$(pwd)"
cd /etc/nginx/sites-enabled/
if [ ! -L $domain ]; then
ln -s $conf_file
fi
/etc/init.d/nginx restart

echo "Setup website for $domain succeed!"
separate_line
}

# applications installer
function application_installer() {
# $1 => application name
# $2 => application domain name
# $3 => application owner
# $4 => application subfolder

app=$(declare_variable "textpattern" $1)
domain=$(declare_variable ${vars[default_website_domain]} $2)
owner=$(declare_variable ${vars[default_website_owner]} $3)
subfolder=$(declare_variable "" $4)

if [ "$1" == "" ]; then
  read -p "Do you want to select an application? (default to $app): " na
  if [ "$na" != "" ]; then
    app=$na
  fi
  read -p "Do you want to select an domain of this app? (default to $domain): " nd
  if [ "$nd" != "" ]; then
    domain=$nd
  fi
fi

read -p "do you want to install the application in a sub folder of your website
root? (default to n ):(y/N) " sf
if [ "$sf" != "" ]; then
subfolder=$app
fi

uri="${apps[$app]}"
url="${apps[apps_server_root]}$uri"
if [ "$uri" = "" ]; then
url="${remote_apps[$app]}"
fi
conf="/etc/nginx/sites-available/$domain"
root="/home/$owner/websites/$domain/public"

echo "URI: $uri"
echo "URL: $url"
echo "CONF: $conf"
echo "ROOT: $root"

# if the domain has been used
if [ -f "$conf" ]; then
hightlight "the domain has been used, do you want to choose another one?"
echo "If you want to select a new domain, just type it here,"
echo "or you can just pass this note and use this domain, if so, "
echo "i will make a backup of your exist website"
read -p "(default to $domain)" new_domain
  if [ "$new_domain" != "" ]; then
  # copy exist website
  # this will be separated to a standalone function
  # mv "$conf" "$root/$domain.conf"
  # cd "/home/$owner"
  # echo "backup exist website"
  # tar zcvf "$domain.backup.tar.gz" "/home/$owner/websites/$domain"
  # mv "$root" "$root.old"
  # setup_website_directly $domain $owner ""
    domain=$new_domain
    setup_website_directly $domain $owner ""
  fi
else
  setup_website_directly $domain $owner
fi

cd $root
wget -c $url

to="/tmp/application_installer/$app"
if [ ! -d $to ]; then
mkdir -p $to
fi

tar zxvf "$root/$(basename $url)" -C $to

sub=""
# how many files or folders?
dirs=($(ls $to))
if [ "${#dirs[@]}" = "1" ]; then
from="$to/${dirs[0]}/*"
else
from="$to/*"
fi
if [ "$subfolder" = "" ]; then
to="$root/"
else
to="$root/$subfolder/"
fi
if [ ! -d $to ]; then
mkdir -p $to
fi

echo "mv $from $to"
mv $from $to
echo "Install $app succeed"

echo "you can open your browser and visit : http://$domain/ to run
the application..."

}

function setup_website() {

read -p "(domain name): " domain
read -p "(owner): " owner
read -p "(applications): " applications
read -p "(log Y/n): " log
read -p "(error log): " error_log
read -p "(access log): " access_log

setup_website_directly $domain $owner $applications $log $error_log $access_log

}

function setup() {
copyright

# upgrade the server or not
set_variable upgrade_server "" "If you want to upgrade the server
 before set it up, please choose y"

# install common nginx or with extras
set_variable install_nginx_extras "" "Do you want to install the extra
 functions of nginx?"

# set the server's default domain name
set_variable default_website_domain "" "This should not be a domain you
 want to use in your pub site."

# set the owner of the default website
set_variable default_website_owner "" "The website's content will be
 stored in his home folder"

# set website logs
set_variable default_website_log "" "If you do not want to log your
  website, pleast choose n."
set_variable default_website_error_log
set_variable default_website_access_log

# install perl support or not?
set_variable install_perl_support "" "If you want to run perl
 applications in your server, you should choose y to install perl
 support for nginx server."

separate_line b

# update or upgrade the server
update_server

# install mysql server the client
install_mysql

# install nginx or nginx-extras
install_nginx

# install php and some extra libs
install_php
# install perl support or not
if [ "${vars[install_perl_support]}" = "y" ]; then
    install_perl_support
    install_common_cgi_support
fi

setup_website_directly
application_installer
# $1 => website domain name
# $2 => website owner
# $3 => applications list for install
# $4 => log or not to log
# $5 => error log
# $6 => access log
}

# ==================================================================== #
# Start the program

case "$1" in
--help|-h)
help
;;
--setup-website|-sw)
setup_website
;;
--setup|-s|--install|-i)
setup
;;
--install-app|-ia)
application_installer
;;
*)
setup
esac

一直都是使用的Linode的VPS,不过慢慢的,发现很不划算,我的网站访问量不是很大,但是图片量很大,比如我女儿的相册,上传的基本上都是原图或者大图,现在光图片就已经达到一个多G了,而总是出现的问题是除了VPS的空间容量不够用了之外,其它的资源都是有余很多的,所以我就想直接把静态文件都保存到Amazon S3服务上去,而我的服务器仅仅只做为程序的运行平台,而它的价格则低得多。

Amazon提供的费用计算器中粗略计算一了一下,差不多也就$12左右,我预定存储数据量为20Gb,每一个月的出流量为100Gb(这需要11.88美金,而其实我一个月也就几个G的出流量,按现在的实际情况来算的话,也就0.5美金不到,其它的都没什么钱了),从这个可以看出来,如果我的出流量不多的话,其实只需要5美金不到就足够了。

但是有一个问题是,Amazon并没有提供一种方法让你可以直接绑定自己的域名,所以,还是需要我们自己通过别的办法来实现这个,其实也是十分简单的事情,我们将使用到域名的CNAME记录,具体操作是这样的:

  1. 确定要绑定的域名,比如我这里是安图软件的文件下载站,域名使用 *download.antusoft.com*。
  2. 登陆 Amazon S3 控制台,创建一个新的*Buckets*,这里需要注册,名称选择你上面确定的域名,所以,我需要创建的Buckets的名称应该为:*download.antusoft.com*。
  3. 在本地创建两个文件 index.html 与 error.html ,并上传至刚才创建的 Bucket 的根目录中,并且设置这两个文件可以给公众访问(点文件上点击鼠标右键-> Make Public 或者选中文件,点击上方工具条中的 Actions,在下拉菜单中选择 Make Public)
  4. 打开改才创建的 Buckets 的属性设置面板,找到 Website 标签,将 Enable 复选框钩上(即开启网站服务),之后你还需要指定该网站的主索引文件和错误文件,我使用的分别是:
+ Index Document : *index.html*
+ Error Document : *error.html*
  1. 设置 download.antusoft.com 这个域名的CNAME记录至Amazon S3为你提供的那一个 URL 地址上,之后等待域名生效即可。

生效之后我们就可以使用 http://download.antusoft.com 这个地址来访问你刚才的S3 Bucket,整个过程我们其实可以把 Bucket 当作是一台静态文件存储服务器,我们只需要能通过我们的域名访问到这台“服务器”即可,而 Amazon S3 会将你域名的 CNAME 记录直接绑定到名为这个域名的Bucket上面。

今天在写我的 Ubuntu Server Setup Tool 时,因为在本地的环境下面 php5-fpm 默认监听 *127.0.0.1*,但是在我的 Linode VPS 上面,安装之后却默认监听 *unix:/var/run/php5-fpm.sock*,应该是版本不同的原因吧,所以我需要有一种办法,让程序知道是应该使用哪种监听方式(我们需要把这个写进 Nginx 的配置文件的),这个时候就用到我现在要说的这些东西了。

先来解决我的问题:我应该使用哪种监听方式?

因为,如果是以 Unix Socket 的方式监听的话,那肯定是需要有一个 Socket 文件的,而以IP+端口的方式却没有,所以,我现在可以把问题简单的转化为,是否存在一个相关的 Socket 文件,对于我现在要解决的这个问题,其实就是:是否存在 /var/run/php5-fpm.sock 文件,这样一来问题自然而然的就已经解决了:

#!/bin/bash
LISTEN_METHOD="127.0.0.1:9000"
if [ -S /var/run/php5-fpm.sock ]; then
  LISTEN_METHOD="unix:/var/run/php5-fpm.sock"
fi
echo $LISTEN_METHOD

执行包含上面这段代码的可执行文件,就可以知道是哪一直方式了(这个只是 apt-get 安装之后默认配置的位置,如果你修改了php5-fpm 的 Socket 文件的路径,那么这个需要修改一下下(不过如果你都知道修改了,那么我想你也应该知道使用哪种方式了吧,只是对于我的自动安装脚本来说,还是要自动选择比较好,就像上面这样的。

判断文件是否存在

要判断一个文件是否存在其实是很简单的一件事情,只需要像下面这样:

if [ ! -f /path/to/file ]; then
    touch /path/to/file
fi

在上面的示例中,会先判断 /path/to/file* 这个文件是否存在,如果不存在,则创建它,这里我们使用了“非”,即“!*”符号,如果去掉这个符号,那么就是“如果文件存在就怎么样”。

在上面的判断语句中 *-f* 是一个参数,它表示“文件存在,并且是一个普通文件”,与该参数具有同样功能的还有很多其它参数,但是不管参数蛤才能,后面部分都是你想判断的文件的路径。

与文件判断相关的参数

下面这些参数都是我们可能在 Shell 脚本中使用得到的:

参数|意义 -|- -a|文件存在 -b|文件存在并且是一个特殊的块文件 -c|文件存在并且是一个特殊字符文件 -d|文件存在并且是一个目录 -e|文件存在(与 -a 是一样的) -f|文件存在并且是一个普通文件 -g|文件存在,并且有setgid(2)设置 -G|文件存在,并且属于当前进程相同的组 -k|文件存在并且设置了sticky 标记 -L|文件存在,并且是一个动态链接 -O|文件存在,并且与当前进程属于相同的用户ID -p|文件存在,并且是先入先出(FIFO)的特殊文件或者命名管道 -r|文件存在,并且是当前进程可读取的 -s|文件存在,并且文件大小大于0 -S|文件存在,并且是一个Socket -t|文件描述符是对外开放的,并且关联一个终端设备 -u|文件存在,并且有 setuid(2) -w|文件存在,并且是当前进程可写的 -x|文件存在,并且是当前进程可执行的 -n|字符串长度不为0 -o|指定的选项已经设置 -z|字符串长度为0

上表中,-n*,-o,-z*是用来判断字符串或者选项的。

如果我们想直接在终端中判断而不使用 if 判断,那么我们还可以使用*test*命令,比如:

test -S /var/run/php5-fpm.sock

上面这样并不会显示任何内容,但是我们可以这样:

root@li485-100:~# test -S /var/run/php5-fpm.sock && echo "exist" || echo "Not exist"
exist
root@li485-100:~# test -S /var/run/php5-fpm.socket && echo "exist" || echo "Not exist"
Not exist

由于Nginx没有对CGI的原生支持,但是他同样还是可以运行类似Movable Type这样的程序(包括其它的一些基于CGI的程序),我们要使用到的中具就是 *fcgiwrap*,它就像是一个代理一样,把FCGI请求转发为CGI请求,所以,使用它,我们不需要修改一行程序代码。

首先需要安装 fcgiwrap:

root@CoxStation:~# aptitude install fcgiwrap

fcgiwrap* 可以以一个系统守护进程运行,并以一个Unix Socket文件访问(/var/run/fcgiwrap.socket)。现在我想让Movable Type的管理端通过 http://mt.csmumu.net访问到,下面这个就是完整的 server 配置代码。

server {
    server_name mt.csmumu.net;
    access_log /home/cox/logs/mt.csmumu.net.access.log;
    error_log /home/cox/logs/mt.csmumu.net.error.log;
    root   /home/cox/websites/mt.csmumu.net/public;

    location ~ ^/mt.*.cgi {
        ## The exact pattern is required for MT.
        fastcgi_split_path_info ^(/mt.*.cgi)(.*)$;

        include fastcgi_params;
        fastcgi_pass unix:/var/run/fcgiwrap.socket;

        ## MT-related
        fastcgi_param PERL5LIB $document_root/lib;
        fastcgi_param MT_HOME $document_root/;
        fastcgi_param MT_CONFIG $document_root/mt-config.cgi;

        ## fcgiwrap-related
        fastcgi_param SCRIPT_NAME     $document_root$fastcgi_script_name";
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name";
        fastcgi_param PATH_INFO       $fastcgi_path_info;
        fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
    }
}

感谢 CSS3 给我们带来了这么多的惊喜,这才让我可以完成今天的这个小作品,不使用任何 JavaScript 代码,实现可循环的幻灯片,它还带渐变等动画。不过这似乎是我对 CSS3 的一种乱用吧,因为比起使用 JavaScript 的各种库来说,使用 CSS3 来实现麻烦得多,而且它还有严重的浏览器兼容性问题,尤其是在神一样的国度,闲话少扯,下面入正题。

HTML 结构

整个 Slider 的HTML结构十分简单,就和我们平常写的 HTML 没有什么两样,下面是整个 Slider 的 HTML 代码:

<div>
  <div>
    <ul>
      <li>
        <a href="#"><img src="image-1.jpg" alt="First Image" /></a>
        <div>
          <h3>第一个幻灯片</h3>
          <p>这里一个幻灯片的介绍文字,您以前应该看到过很多这样的东西了吧?</p>
        </div>
      </li>
      <li>
        <a href="#"><img src="image-2.jpg" alt="Second Image" /></a>
        <div>
          <h3>第二个幻灯片</h3>
          <p>这里一个幻灯片的介绍文字,您以前应该看到过很多这样的东西了吧?</p>
        </div>
      </li>
      <li>
        <a href="#"><img src="image-3.jpg" alt="Third Image" /></a>
        <div>
          <h3>第三个幻灯片</h3>
          <p>这里一个幻灯片的介绍文字,您以前应该看到过很多这样的东西了吧?</p>
        </div>
      </li>
      <li>
        <a href="#"><img src="image-4.jpg" alt="Fourth Image" /></a>
        <div>
          <h3>第四个幻灯片</h3>
          <p>这里一个幻灯片的介绍文字,您以前应该看到过很多这样的东西了吧?</p>
        </div>
      </li>
      <li>
        <a href="#"><img src="image-5.jpg" alt="Fifth Image" /></a>
        <div>
          <h3>第五个幻灯片</h3>
          <p>这里一个幻灯片的介绍文字,您以前应该看到过很多这样的东西了吧?</p>
        </div>
      </li>
    </ul>
  </div><!-- /.slides -->
  <div></div><!-- /.progress-bar -->
</div><!-- /.slider -->
  1. div class=“slider”

    这是我们的幻灯片的主容器,整个幻灯片的所有内容都被放在该容器里面,我们将使用它*暂停动画的运行*。
  2. div class=“slides”

    我们所有的幻灯片都放在该容器中,在该容器中,存在一个无序列表,该列表的每一项目就是一张幻灯片。
  3. li

    一张幻灯片,我制作的这个幻灯片中包括一张图片,一个标题以及一段简短的介绍文字,这些都是我们最常用的格式。
  4. div class=“tooltip”

    这里面是幻灯片的标题以及介绍文字,你还可以根据需要添加一些你自己的的内容,比如让标题成为一个链接指向某一个网页。
  5. div class=“progress-bar”

    进度条,展示幻灯片动画的进度。

现在我们可以开始编辑 CSS 文件了。

 CSS 样式

我将所有的 CSS 样式都保存在名为 css3.css 的文件中。

.slider

我们使用这个类来创建幻灯片的整体结构:

.slider {
  font-size: 10px;
  background-color: #000;
  border: .5em solid #ddd;
  box-shadow: .1em .1em .5em rgba(0.0.0.0.7);
  height: 32em;
  width: 68em;
  margin: 5em auto 0;
  overflow: visible;
  position: relative;
}

.slides

从我们以前使用 JavaScript 实现的幻灯片的经验中,应该知道,我们总是需要有一个容器,它能完整的显示一个幻灯片,但同时它还需要可以隐藏掉没有激活的幻灯片,在我们的这个小项目中,.slides就是完成这个任务的。

.slides {
  overflow: hidden;
  height: 32em;
}

.slides ul & .slides li

最后我们还需要去除掉 ul 与 li 的默认样式,并且把所有的幻灯片都安排到一个初始位置,这个位置应该方便我们动画的完成,还不能让未激活的幻灯片显示出来。

.slides ul {
  margin: 0;
  padding: 0;
  list-style: none;
  position: relative;
}

.slides li {
  width: 68em;
  height: 32em;
  position: absolute;
  top: -32.5em;
}

CSS3 帧动画

在继续完成 CSS 代码之前,我们需要有一些关于当前这个小幻灯片的一些约定,这些约定也是我们需要使用 CSS3 去实现的,在本未例中,共有五张幻灯片,我准备按5秒一张的速度执行该幻灯片,那么总计将需要25秒完成整个幻灯片,但是我们需要知道 *1秒的时间能执行多少帧*。

所以,让我们做一个小小的算术题:

  1. 我们总个幻灯片使用的总幻灯片数目:*5*;
  2. 每一张幻灯片动画执行的时间:*5秒*;
  3. 整个幻灯片动画需要执行的时间:5张 x 5秒 = *25秒*;
  4. 每一秒动画执行的帖数为:155 / 25 = *4帧*。

现在我们可以来继续完成 CSS 了,我们将给所有幻灯片都设置为无限循环,因为每一张幻灯片都是在完成它自己的动画,是互不干扰的。

.slides li:first-child {
  animation: first 25s linear infinite;
}
.slides li:nth-child(2) {
  animation: second 25s linear infinite;
}
.slides li:nth-child(3) {
  animation: third 25s linear infinite;
}
.slides li:nth-child(4) {
  animation: fourth 25s linear infinite;
}
.slides li:nth-child(5) {
  animation: fifth 25s linear infinite;
}

现在我们已经为每一个幻灯片设定了一个专属于它的动画(CSS animation 的第一个参数即它所绑定的动画的名称),我们现在只需要再定义每一个名称所指定的动画的实现流程即可(注意:运行必须要先定义才能使用,所以我们应该把动画的定义放在 CSS 文件的最前面)。

/* ANIMATION */
@keyframes first {
  0%  { top:0px; }
  4%  { top:0px; }
  16% { top:0px; opacity:1; z-index:0; }
  20% { top:32.5em; opacity:0; z-index:0; }
  21% { top:-32.5em; opacity:0; z-index:-1; }
  92% { top:-32.5em; opacity:0; z-index:0; }
  96% { top:-32.5em; opacity:0; }
  100%{ top:0px; opacity:1; }

}
@keyframes second {
  0%  { top:-32.5em; opacity:0; }
  16% { top:-32.5em; opacity:0; }
  20% { top:0px; opacity:1; }
  24% { top:0px; opacity:1; }
  36% { top:0px; opacity:1; z-index:0; }
  40% { top:32.5em; opacity:0; z-index:0; }
  41% { top:-32.5em; opacity:0; z-index:-1; }
  100%{ top:-32.5em; opacity:0; z-index:-1; }
}
@keyframes third {
  0%  { top:-32.5em; opacity:0; }
  36% { top:-32.5em; opacity:0; }
  40% { top:0px; opacity:1; }
  44% { top:0px; opacity:1; }
  56% { top:0px; opacity:1; }
  60% { top:32.5em; opacity:0; z-index:0; }
  61% { top:-32.5em; opacity:0; z-index:-1; }
  100%{ top:-32.5em; opacity:0; z-index:-1; }
}
@keyframes fourth {
  0%  { top:-32.5em; opacity:0; }
  56% { top:-32.5em; opacity:0; }
  60% { top:0px; opacity:1; }
  64% { top:0px; opacity:1; }
  76% { top:0px; opacity:1; z-index:0; }
  80% { top:32.5em; opacity:0; z-index:0; }
  81% { top:-32.5em; opacity:0; z-index:-1; }
  100%{ top:-32.5em; opacity:0; z-index:-1; }
}
@keyframes fifth {
  0%  { top:-32.5em; opacity:0; }
  76% { top:-32.5em; opacity:0; }
  80% { top:0px; opacity:1; }
  84% { top:0px; opacity:1; }
  96% { top:0px; opacity:1; z-index:0; }
  100%{ top:32.5em; opacity:0; z-index:0; }
}

到现在为止,我们的幻灯片已经可以正常的工作了,那么接下面,把 .tooltip 和 .progress-bar 的问题也解决掉那就完美了。

进度条

进度条我准备放在幻灯片的最下面,它是一个从左至右慢慢被颜色填充的长方形容器,它的 CSS 样式如下:

@keyframes progressbar {
  0%, 20%, 40%, 60%, 80%, 100% { width:0%; opacity:0; }
  4%, 24%, 44%, 64%, 84% { width:0%; opacity:0.3; }
 16%, 36%, 56%, 76%, 96% { width:100%; opacity:0.7; }
 17%, 37%, 57%, 77%, 97% { width:100%; opacity:0.3; }
 18%, 38%, 58%, 78%, 98% { width:100%; opacity:0; }
}

[...]

.progress-bar {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: .5em;
  background-color: #000;
  animation: progressbar 25s ease-out infinite;
}

标题与介绍

幻灯片本身已经完成啦,现在我们再来给他的标题与介绍做一些细节上面的处理。

.tooltip {
    position: absolute;
    width: 100%;
    left: -100%;
    padding: .5em 0;
    bottom: 1em;
    color: #f0f0f0;
  background-color: rgba(0,0,0,0.7);
  transition: all 0.3s ease-in-out;
}

.slider:hover .tooltip {
  left: 0;
}

.tooltip h3 {
    padding-left: .5em;
  font-size: 1.6em;
  margin: 0;
}

.tooltip p {
  padding-left: .8em;
  margin: 0;
}

到现在为止我们的幻灯片已经完成做完了,最终的效果你可以下载本示例的打包文件在自己的电脑中测试,下载链接在本文的下方。

常见问题

  1. 我下载了打包文件可以运行,但是自己按照本文的代码写的去无法运行,这是怎么回事儿?

    这个问题很好理解啊,transition, animation 这些东西到现在都还没见着哪个浏览器直接支持,而是使用了自家的指令,比如对于 Chrome浏览器我们应该使用:*-webkit-animation*,但是对于火狐我们却又得使用 *-moz-animation*,但是在本文的文章中,我并没有把这每一个都写出来,而是直接写了 *animation*,所以,如果你接照我的文章来做的话,你需要自己加各种浏览器的不同版本的指令才行,IE的话直接跳过吧……

从05年使用Bluehost的虚拟主机开始,就知道可以通过服务器来达到翻山跃岭的目的,以前都是使用虚拟主机,然后配上主机服务器提供的Shell帐户在本地创建一个代理来达实现,记得那个时候用得最多的就是DreamHost的,当然,那还是一个流行着合租虚拟主机的时代。

从09年开始就慢慢地都转到VPS上面来了,到现在我已经只使用VPS了,以前最常使用的是CentOS作为服务器系统,后来慢慢地全部转到Ubuntu上面来了,现在又正准备转到openSUSE上面去,玩什么就转什么吧,毕竟我不是一个IT行业中人了,什么东西精不精对我来说没有太大的影响了,完全是自己的兴趣了。

时代不同,山还是那座山,岭还是那道岭,有些时间走不过还是得借一些工具,以致于本身因为安全考虑的VPN产品现在成了跳出牢笼的工具,一直以来我也是那种过不过都无所谓的人,所以,虽然我的服务器都在国外,我也从来都没有做过什么不符合政策的事情来,只是现在还是感觉,多走出去看看对自己的个人成长还是有好处的,所以,我还是想着,哪天还是整整这个吧,哪知一整,整去了我半条命。

闲话多说无益,解决不了的问题还是要解决,所以,还是回到正题来吧,不过首先申明,我这不是什么教程,纯粹只是记录,有可能我今天真的解决了我应该要解决的问题,但是并不一定就能成为你们的参考。

操作平台的选择

我是准备在openSUSE上面架设的,可是因为我还有两台Ubuntu的VPS。所以我还得解决Ubuntu的问题(好像这个更好解决一些?也许是因为可参考的资料多得多吧),软件的话我也就没啥选择了的,大家都在说openvpn,就这个了,其实我自己个人是没法儿去选择什么的,因为我对这一块是完全不通,所以只能随大流,而这里面的道道也就只能硬着头皮去学了。

使用openvpn

这个安装在两种系统里面都不难,比如下面这样的:

yast2 -i openvpn

或者在 Ubuntu 下面:

apt-get install openvpn

(*题外话*:我一朋友安装这个东西安装了一天,使用的是源码安装,唉,我还是没那么有勇气去玩源码安装,还是使用这种最简单的办法吧!)

配置openVPN

就是这个东西整死我了,尤其是对于我这种什么基础知识都没有的人来说就更是难上加难了啊,从昨天晚上8点多到现在,我还是没有搞定,现在也只能再继续去摸索了。

openSUSE下面的安装与配置

我还是先使用 openSUSE 吧,因为我更喜欢这个破东西一点。

安装 openVPN

我们可以先通过*zypper se*命令来查找一下下我们的软件源里面是否已经有openvpn了,运行下面这样的命令:

cox:~ # zypper se openvpn

这将会得到一个结果集,它在终端里面是一个表格,我们可以很轻松地看到 openvpn 这七个字母在那里啊,所以,我再直接安装了:

cox:~ # yast2 -i openvpn

包构建安装工具能为我们解决很多事情,比如包的依赖关系等。

复制需要的目录

我们现在需要把 */usr/share/openvpn* 复制到 */etc/* 目录下面,yast2 安装在安装 openvpn 是会创建一个 */etc/init.d/openvpn* 的启动脚本,该脚本会自动的搜索 */etc/openvpn* 中的配置文件:

cox:~ # cp -r /usr/share/openvpn /etc/
cox:~ # cd /etc/openvpn/
cox:/etc/openvpn # ls
easy-rsa
cox:/etc/openvpn #

创建主CA与KEY文件

现在我们修改工作目录到 */etc/openvpn/easy-rsa/2.0/* 下,修改 *./vars* 文件:

# These are the default values for fields
# which will be placed in the certificate.
# Don't leave any of these fields blank.
export KEY_COUNTRY="CN"
export KEY_PROVINCE="HN"
export KEY_CITY="ChangSha"
export KEY_ORG="AntuSoftInc"
export KEY_EMAIL="cox@antusoft.com"
export KEY_EMAIL=cox@antusoft.com
export KEY_CN=antusoft
export KEY_NAME=cox
export KEY_OU=antusoft
export PKCS11_MODULE_PATH=changeme
export PKCS11_PIN=1234

保存之后执行下面这样的命令:

cox:/etc/openvpn/easy-rsa/2.0 # . ./vars
NOTE: If you run ./clean-all, I will be doing a rm -rf on /etc/openvpn/easy-rsa/2.0/keys
cox:/etc/openvpn/easy-rsa/2.0 # ./clean-all
cox:/etc/openvpn/easy-rsa/2.0 # ./build-ca
Generating a 1024 bit RSA private key
.................................................++++++
..++++++
[......]
cox:/etc/openvpn/easy-rsa/2.0 #

创建服务器证书与key文件

在 */etc/openvpn/easy-rsa/2.0* 目录中执行下面这条命令来创建服务器端的证书与 key 文件:

cox:/etc/openvpn/easy-rsa/2.0 # ./build-key-server server
Generating a 1024 bit RSA private key
[,,,]
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
cox:/etc/openvpn/easy-rsa/2.0 #

创建客户端证书与key文件

与服务器证书创建在同样的一个目录中,运行下面这样的命令:

cox:/etc/openvpn/easy-rsa/2.0 # ./build-key-server server
Generating a 1024 bit RSA private key
[......]
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated
cox:/etc/openvpn/easy-rsa/2.0 #

创建Diffie-Hellman (DH) 文件

使用下面这样的命令创建Diffie-Hellman文件:

cox:/etc/openvpn/easy-rsa/2.0 # ./build-dh
Generating DH parameters, 1024 bit long safe prime, generator 2
This is going to take a long time
....................+...++*++*++*
cox:/etc/openvpn/easy-rsa/2.0 #

现在我们可以进入 *./vars* 目录,可以看到刚才生成的所有这些证书文件与密钥文件了:

cox:/etc/openvpn/easy-rsa/2.0 # cd ./keys/
cox:/etc/openvpn/easy-rsa/2.0/keys # ls
01.pem  ca.key      client.key  index.txt.attr      serial      server.csr
02.pem  client.crt  dh1024.pem  index.txt.attr.old  serial.old  server.key
ca.crt  client.csr  index.txt   index.txt.old       server.crt
cox:/etc/openvpn/easy-rsa/2.0/keys #

这些文件分别是:

  • *ca.crt*-服务端与客户端的根证书
  • *ca.key*-登陆机器专用的CA密钥文件
  • dh1024.pem*-服务器端DH文件,格式为*dh.pem
  • *server.crt*与*server.key*-服务器端证书与密钥文件(名称就是我们生成它时输入的)
  • *client.crt*与*client.key*-客户端证书与密钥文件(名称也是我们创建时指定的)

服务器端配置文件

安装了 openvpn 之后,会自动创建一个示例配置文件,它被保存在 */usr/share/docs/packages/openvpn/sample-config-files/* 目录中,我们为了简单,就不再自己全新的创建配置文件了,而是把这个示例配置文件复制到 */etc/openvpn* 目录稍作修改即可:

cox:/etc/openvpv # cp /usr/share/doc/packages/openvpn/sample-config-files/server.conf ./

编辑该文件,作如下所示的修改:

  • port 1194
  • proto udp
  • ca /etc/openvpn/easy-rsa/2.0/keys/ca.crt
  • cert /etc/openvpn/easy-rsa/2.0/keys/server.crt
  • key /etc/openvpn/easy-rsa/2.0/keys/server.key
  • dh /etc/openvpn/easy-rsa/2.0/keys/dh1024.pem
  • dev tun
  • server 10.8.0.0 255.255.255.0

修改完成之后,运行服务器端:

cox:/etc/openvpn # /etc/init.d/openvpn start
redirecting to systemctl
cox:/etc/openvpn #

客户端配置文件

与服务器端配置文件一样,客户端也有一个示例文件被保存在 server.conf 示例文件同一个目录下面,我们现在进入我现在的系统中,并且将前面生成的这些文件都保存到本地客户端电脑中的:*/home/cox/vpnconf*目录中。

需要创建一个地方:

remote 50.116.15.100 1194

上面的IP地址为无端服务器地址,如果你的和我不一样,那么就改成你的即可,商品是我们的服务器的配置文件里面定义,默认是1194我没有做任何修改,如果你做了什么修改,也需要改成你自己设置的端口号。

测试是否能成功连接上

root@Cox:/home/cox/vpnconf# openvpn ./client.conf
[...]
Fri Nov  9 11:16:37 2012 Initialization Sequence Completed
.

当出现了 *Initialization Sequence Completed*的时候,使用 *ifconfig* 查看到下面这样的信息:

tun0      Link encap:未指定  硬件地址 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
          inet 地址:10.8.0.6  点对点:10.8.0.5  掩码:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1500  跃点数:1
          接收数据包:1 错误:0 丢弃:0 过载:0 帧数:0
          发送数据包:2 错误:0 丢弃:0 过载:0 载波:0
          碰撞:0 发送队列长度:100
          接收字节:52 (52.0 B)  发送字节:104 (104.0 B)

再用一个更加直观的办法来检测一下,我的服务器IP地址是 50.116.15.100,我在服务器上面安装Nginx服务器,直接使用这个服务器IP地址是可以访问的,而现在我使用 http://10.8.0.1/也能访问到这个服务器了,因为对于我的桌面电脑与服务器来说,他们现在已经是点对点处在同一个网里面了。

Mac用户的客户端解决办法

不用多说了吧,就使用TunnelBlick即可,这个使用方法其实很简单,只需要把上面我们的 vpnconf目录成为一个特别的名称,比如:

AhaInsight.tblk

然后,把*client.crt*与*client.key*两个文件的名称都改为*AhaInsight*即可。

在你安装了TunnelBlick软件之后,以 .tblk 后缀结尾的目录会被视为一个TunnelBlick包文件,直接双击它即可把这个配置添加到TunnelBlick中去。

Ubuntu中OpenVPN的安装与

其实与在 OpenSUSE中是一样的,只是好像就easy-rsa/server.conf/client.conf这几个目录和文件的保存位置不一样,其它的都是一样的,自己玩吧。

通过openVPN共享服务器上网

其实我前面整那么多,发现对于我来说是没有任何用处的,我真正的需求在本文的最前面就已经说得很清楚了:

我要翻山跃岭,跃过长江大河,飘洋过海去看看外面的世界

而现在,我只是从黄花机场飞到了New York,但是却没签证,出不了机场的大门,啊,这不算去外面的世界看了,所以,我还需要做更多的事情,一些能真正让我《自由地飞翔》的事情,也让《我飞得更高》的事情,什么事?我现在还不知道,只能继续来摸索了。

我的一点点理解是这样的,要实现我所想实现的目标,那么可能需要满足下面这两个条件:

  1. 服务器端允许客户通过它访问外网
  2. 客户端要自己知道它是要通过服务器访问外网

把这样的想法转换成具体的实现,那么可能是这样的:

  1. 我打开某个网站
  2. 客户端告诉服务器哥要访问这个网站了
  3. 服务器把这些请求发送出去(这个时候服务器就像我们的TP-Link了吧)
  4. 服务器再把回复过来的东西发到客户端这里

啊,这些个都只是我的想法,不过也没有去看过相关的资料,还不知道是不是按着这样的思路下面,有时间再找找一些资料再来好好整整这个东西,至少现在VPN已经是连上了,至少,哥现在已经坐上灰机了。饿,想想不对,我只是买到机票了而已……

使用PPTP

实然之间,把脑子里面的水给掉跳了,发现原来还有PPTP这等东西,对于我们这种电脑白痴来讲,这东西应该简单得多吧,所以,我就又试了试使用PPTP,于是乎,我把VPS又重新给安装了,现在回到一个空白的系统中来。

安装PPTP

在 Ubuntu 下面的话,像下面这样的安装PPTP的服务端程序:

root@ubuntu:~# apt-get install pptpd

在SuSE中的话,这种事情还是交给 yast吧:

suse:~ # yast2 -i pptpd

哦,对了,这个是服务器端,如果要安装客户端的话,请使用:

suse:~ # yast2 -i pptp

而对于 Ubuntu,pptp客户端的名称应该是 pptp-linux

安装好了之后,又回到让人头痛的事情上面来了。

配制PPTP服务器端

PPTP我看了看说明,好像比OpenVPN简单得多啊。

创建用户及密码

用来登陆 PPTPD 服务器的用户与其登陆密码是保存在 */etc/ppp/chap-secrets* 文件中的,我们先进入到这个 */etc/ppp* 目录来,再修改这个文件:

root@ubuntu:~# cd /etc/ppp
root@ubuntu:/etc/ppp# ls
chap-secrets  ip-up      ipv6-down.d  options      pppoe_on_boot
ip-down       ip-up.d    ipv6-up      pap-secrets  resolv
ip-down.d     ipv6-down  ipv6-up.d    peers
root@ubuntu:/etc/ppp# emacs chap-secrets

看上面列出的第一个文件,就是我们需要修改的文件,打开它之后会发现,其实这个文件里面除了被注释掉的各种各样的说明之外,什么配置都没有,就当这里是一个空数据库吧,用来存储用户帐户与密码的空数据库,但是可以看到最后的一行标明了我们应该怎么写这个文件,如下所示:

#client         hostname        <password>      192.168.1.1
cox     pptpd       iamcox  *

对于上面我添加的这一行的意思是这样的:

  • cox : 用户名
  • iamcox : 登陆使用的密码明文

其它的就暂时不去管吧,如果要改也就只需要改这两样就成了。

添加DNS配置

我想使用Google的“死吧!”DNS服务,它的服务器IP地址是:*8.8.8.8*,在我的系统(openSUSE 12.2)里面需要修改 */etc/ppp/options* 文件,你的可能不一样,但是位置是一样的,有可能叫作:*pptpd-options*吧:

root@ubuntu:/etc/ppp# emacs options
[...]
ms-dns 8.8.8.8
[...]
netmask 255.255.255.0
[...]

修改PPTP的配置

PPTP的配置文件一般都是保存在 */etc* 这个目录的 *pptpd.conf* 文件中,打开并像下面这样的编辑它:

root@ubuntu:/etc/ppp# emacs /etc/pptpd.conf
localip 10.8.0.1
remoteip 10.8.1.100-199

修改完成之后启动 PPTPD:

root@ubuntu:/etc/ppp# /etc/init.d/pptpd start

配制并测试PPTP客户端

在客户端,我们需要先安装PPTP,安装方法在上面已经介绍了,这里又不再说了。

手工添加 /etc/ppp/peers/pptpd 文件

注意啦,注意啦,*/etc/ppp/peers/pptpd* 是一个文件,而不是一个目录,使用 *touch*命令也行,或者使用 *vi /etc/ppp/peers/pptpd* 或 *emacs /etc/ppp/peers/pptpd* 即可,其内容如下:

pty "pptp 50.116.15.100 -nolaunchpppd"
name cox
remotename pptpd
require-mppe-128
file /etc/ppp/options.pptp
ipparam pptpd

修改 *chap-secrets*(同服务器)

如果你本地电脑和我一样,与服务器用的是一样的话,那么这里和服务器上面是一样的。

测试连接

我们使用下面这种Debug方式来测试连接:

root@Cox:/etc/ppp# pon pptpd debug dump logfd 2 nodetach
[...]
rcvd [IPCP ConfAck id=0x2 <compress VJ 0f 01> <addr 10.8.0.100>]
local  IP address 10.8.0.1
remote IP address 10.8.0.100
Script /etc/ppp/ip-up started (pid 15997)
Script /etc/ppp/ip-up finished (pid 15997), status = 0x0

出现上面这样的信息,就证明我的客户端已经成功的连接上服务器了。现在使用 *ifconfig* 来查看一下下:

ppp0      Link encap:点对点协议
          inet 地址:192.168.1.234  点对点:192.168.0.234  掩码:255.255.255.255
          UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1496  跃点数:1
          接收数据包:5 错误:0 丢弃:0 过载:0 帧数:0
          发送数据包:5 错误:0 丢弃:0 过载:0 载波:0
          碰撞:0 发送队列长度:3
          接收字节:62 (62.0 B)  发送字节:68 (68.0 B)

配置IP转发

修改 */etc/sysctl.conf*文件,即去掉下面这一行前面的注释即可:

net.ivp4.ip_forward=1

修改完成之后重新加载IP转发配置:

root@ubuntu:/var/log# sysctl -p

 设置网络地址翻译

修改 iptables :

root@ubuntu:/var/log# iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

上述设置在重起后会丢失,因此需要修改 */etc/rc.local*,在 exit 0之前添加以下语句:

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

一些小问题

  1. 修改了 */etc/pptpd.conf* 文件之后出现:*Long config file line ignored.*错误

    这个问题整死我了,一直找不到原因,后来,无意之间发现,*/etc/pptpd.conf* 文件内容最后面必须保留有至少一个空行,否则就会出现这个问题,而这个问题的结果就是, */etc/init.d/pptpd start*无法成功执行。
  2. 连上VPN之后,但是却没法打开网站

    这个一般是因为服务器没有开启 53 DNS 查询端口,使用下面命令开启即可:
    iptables -A INPUT -p udp --dport 53 -j ACCEPT
    iptables -A OUTPUT -p udp --sport 53 -j ACCEPT

一直没有找到怎么从官方下载安装包哪里有得下,所以,还是直接使用了 *apt-get* 完成安装:

sudo apt-get install flashplugin-nonfree
sudo cp /usr/lib/flashplugin-installer/libflashplayer.so /usr/lib/chromium-browser/plugins
chromium-browser --enable-plugins

另外,Flash中文乱码解决方法: 终端命令:sudo gedit /etc/fonts/conf.d/49-sansserif.conf 将倒数第4行内容替换成:文泉驿正黑。

今天一整天的时间,就花在了这个显示器的检测上面了,从昨天开始就一直在准备着把Windows系统全部下掉,转而到Linux环境下,最开始我选择的是openSUSE,因为我的服务器也准备就使用这个系统,但是却被显示器的检测浪费了一整天的时间,最开始很自然的去找驱动安装,但是后来发现,不是驱动的问题,而是我的显示器根本就不能自动地向操作系统发送其信息(可能还是与驱动有关吧)。

在openSUSE下面整了整整一天没有任何收获的情况下,我试着重新安装了Ubuntu,这个是熟悉一些吧,几分钟时间搞定这个问题,下面是我的整个解决过程:

  1. 使用 xrandr 命令列出所有当前能检测到的分辨率,比如我的计算机安装完Ubuntu之后,是下面这样的一个情况:
    cox@Cox:~$ xrandr
    Screen 0: minimum 320 x 200, current 1920 x 1080, maximum 8192 x 8192
    VGA1 connected 1024x768+0+0 (normal left inverted right x axis y axis) 0mm x 0mm
        1024x768       60.0
        800x600        60.3     56.2
        848x480        60.0
        640x480        59.9

    从上面的信息中我们可以看到,我现在只识别到上面四种分辨率,现在使用的是1024x768的。
  2. 现在使用 xrandr 新添加显示模式(分辨率可以自己定,但是不要大于显示器的最大分辨率),我建议使用最佳分辨率,比如我的显示器的最佳分辨率是:1920x1080,那么就添加这个分辨率的即可,这里可能你会不知道怎么添加显示模式,其实很简单,还有一个名为 cvt 的工具,它会根据你所提供的分辨率来创建一个可用的显示模式:
    cox@Cox:~$ cvt 1920 1080
    # 1920x1080 59.96 Hz (CVT 2.07M9) hsync: 67.16 kHz; pclk: 173.00 MHz
    Modeline "1920x1080_60.00"  173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync

    默认的屏幕刷新率是 60.0Hz。
  3. 将得到的显示模式使用 xrandr 命令添加:
    cox@Cox:~$ su
    root@Cox:~# xrandr --newmode "1920x1080_60.00"  173.00  1920 2048 2248 2576  1080 1083 1088 1120 -hsync +vsync
    root@Cox:~# xrandr --addmode VGA1 1920x1080_60.00
    root@Cox:~# xrandr --output VGA1 --mode 1920x1080_60.00

    上面四行命令的作用分别为:
    1. 以Root身份登陆
    2. 创建新的显示模式
    3. 添加新的显示模式
    4. 设置刚才创建的显示模式为当前使用的模式
  4. 有可能每次开机之后都又不能使用设定好的分辨库了,那我们可以将上述命令添加到X图形界面的启动命令中去,使用如下命令:
    sudo gedit /etc/gdm/Lnit/Default

    将上面后三个步骤的所有命令都复制到新打开的文件中,然后再在最后面加上下面这一行命令:
    /sbin/initctl -q emit login-session-start DISPLAY_MANAGER=gdm
  5. 如果开机之后任务栏显示不正常,则可以使用下面脚本关闭并重新启动任务栏来纠正:
    gconftool-2 --shutdown
    rm -rf ~/.gconf/apps/panel
    pkill gnome-panel