go caddy server install on Centos 6 and init.d config

# step 1, install caddyserver
curl -s https://getcaddy.com | bash
groupadd -g 33 www-data
useradd \
  -g www-data --no-user-group \
  --home-dir /var/www --no-create-home \
  --shell /usr/sbin/nologin \
  --system --uid 33 www-data

mkdir /etc/caddy
chown -R root:www-data /etc/caddy
mkdir /etc/ssl/caddy
chown -R www-data:root /etc/ssl/caddy
chmod 0770 /etc/ssl/caddy

# step 2, download sysvinit file
wget https://raw.githubusercontent.com/mholt/caddy/master/dist/init/linux-sysvinit/caddy -O /etc/init.d/caddy

# step 3, install daemon
cd /usr/local/src
wget http://developer.axis.com/download/distribution/apps-sys-utils-start-stop-daemon-IR1_9_18-2.tar.gz
tar zxvf apps-sys-utils-start-stop-daemon-IR1_9_18-2.tar.gz
cd apps/sys-utils/start-stop-daemon-IR1_9_18-2
gcc start-stop-daemon.c -o start-stop-daemon
cp start-stop-daemon /usr/sbin/

# step 4, start service
service caddy start

webp与png、jpg相互转换,Convert WEBP to JPG,Convert PNG to webp

主要使用webp-tools

#ubuntu
apt-get install webp

#centos
yum -y install libwebp-devel libwebp-tools

会生成几个工具。

cwebp → WebP encoder tool
dwebp → WebP decoder tool
vwebp → WebP file viewer
webpmux → WebP muxing tool
gif2webp → Tool for converting GIF images to WebP

# convert from webp to png
dwebp mycat.jpg -o mycat.png

# Convert from JPG to WebP
cwebp some.jpg -o target.webp

dwebp可以将webp图片转换成无损的png图片格式,有了png,则可以使用imagemagic之类的工具再转换成jpg.
cwebp可以将jpg转换成webp,将png转换成webp

青云、阿里云、腾讯云磁盘速度测试

阿里云,香港,SSD云盘

[root@ali03 ~]# time dd if=/dev/zero of=/test.dbf bs=8k count=300000
300000+0 records in
300000+0 records out
2457600000 bytes (2.5 GB) copied, 29.2894 s, 83.9 MB/s

腾讯云,新加坡,默认

[root@VM_0_2_centos ~]# time dd if=/dev/zero of=/test.dbf bs=8k count=300000
300000+0 records in
300000+0 records out
2457600000 bytes (2.5 GB) copied, 3.70669 s, 663 MB/s

real    0m3.744s
user    0m0.040s
sys 0m2.450s

青云,北京3区,默认

[root@JY11 ~]# time dd if=/dev/zero of=/test.dbf bs=8k count=300000
300000+0 records in
300000+0 records out
2457600000 bytes (2.5 GB) copied, 17.3187 s, 142 MB/s

real    0m17.365s
user    0m0.033s
sys 0m1.644s

Linux / UNIX Crontab File Location Crontab配置文件路径

一般是用crontab -e来增删计划任务,如果要导入任务,则可以通过复制文件的方式来操作

Mac OS X – /usr/lib/cron/tabs/ (user cron location /usr/lib/cron/tabs/vivek)
FreeBSD/OpenBSD/NetBSD – /var/cron/tabs/ (user cron location /var/cron/tabs/vivek)
CentOS/Red Hat/RHEL/Fedora/Scientific Linux – /var/spool/cron/ (user cron location /var/spool/cron/vivek)
Debian / Ubuntu Linux – /var/spool/cron/crontabs/ (user cron location /var/spool/cron/crontabs/vivek)
HP-UX Unix – /var/spool/cron/crontabs/ (user cron location /var/spool/cron/crontabs/vivek)
IBM AIX Unix – /var/spool/cron/ (user cron location /var/spool/cron/vivek)

OneApm 的使用总结

我司的部分业务从去年起,开始接入OneApm,到目前已经有50台机器的规模。
业务层的Nginx、PHP、Mysql,到Redis、ElasticSearch监控,到业务模拟测试可用度,到自定义SDK实现的特殊业务监控,目前都有涉及到(http://www.4wei.cn/archives/1002578)。

总的来说,好评满满,DevOps时代不可或缺的必备工具服务。

QQ20160414-0

20+监控服务器,Nginx、Mysql、ES、REDIS、PHP_FPM等数个平台服务的监控

QQ20160414-1

配置自定义仪表盘

QQ20160414-2

设置告警策略

CentOS远程重装系统

公司内网的一台Linux弱口令机器让扫出来了,数据洗劫一空,悲剧啊
还好不是什么重要的东西,赶紧重装系统。

人不在公司,只能使用远程重装,方案如下:

Fedora 14需要下载的文件在http://nas1.itc.virginia.edu/fedora/releases/14/Fedora/x86_64/os/images/…
64位Centos需要下载的文件在 http://mirror.centos.org/centos-5/5/os/x86_64/images/pxeboot/

修改服务器的/boot/grub/grub.conf文件,在这个配置文件中,添加如下启动项:

title CentOS Reinstall
root (hd0,0)
kernel /vmlinuz vnc vncpassword=12345678 headless ip=10.36.64.238 netmask=255.255.255.0 gateway=10.36.64.254 dns=10.36.10.8 hostname=0map238 ksdevice=eth0 method=http://mirrors.163.com/centos/6/os/x86_64/ lang=
en_US keymap=us
initrd /initrd.img

在"hiddenmenu" 和 "title CentOS..." 增加一项:
method中的url为安装centos的源

重启后使用vnc软件连接服务器 IP:1 ,并输入刚刚设置的vnc密码即可进行centos的重装。这个过程中机器需要下载一些镜像,大约要等待15分钟,VNC才可以访问。

使用OneApm CI SDK实现业务响应速度监控

目前负责的业务处于急速扩张期,每周会增加几台服务器,业务日访问量在5000万左右。

目前的监控需求在可用性和响应速度。运营的需求要求每次接口响应控制在0.1-0.5s内。

在现在的基础监控中,可以分析出http业务的可用性,但没法监控具体的响应时间。
最开始使用的方法为在后端添加serverId和处理时间并放到http header中,通过curl去获得后端信息和处理时间,采样后生成报表。
听起来就比较复杂。
再后来,改成通过nginx记录upstream的response time,采样生成可视化。
由于nginx日志分布于不同的机器中,收集数据相对来讲还是比较麻烦的。

后来将一部分监控迁到OneAPM上,折腾一段时间以后,无意中发现了CI有一个SDK,学习了一下,发现可以实现一些业务层的监控。
经过一段时间的试验,发现CI SDK使用起来非常方便,能方便的接入丰富的业务数据。

其实现的原理为隔一段时间采样一个数据,上报给CI,CI会根据输入的数值,生成线性报表。
下面是一些使用的经验,分享给大家。

以下是抄了OneApm的官方介绍,供参考。

==============================

OneApm CI是什么
http://docs-ci.oneapm.com/

Cloud Insight SDK For Python
http://docs-ci.oneapm.com/api/python.html

Cloud Insight 集成了数十种互联网流行基础组件的监控,开箱即用,您只需要进行最小化的配置就可以实现复杂的基础组件监控, 免除了传统基础组件监控中的复杂流程。Cloud Insight 采集这些基础组件的权威指标,所有指标都是标准化的格式,您再也不需要 自己定义各种指标,以及思考如何进行各种指标的组合展示,一切就只有两步,安装探针,查看仪表盘,如果您使用的是阿里云、UCloud 等云厂商,甚至不需要安装探针,Cloud Insight 会直接通过这些云厂商提供的 API 收集服务器以及基础组件的性能和健康情况的 指标,并友好的展示在定义好的仪表盘中。

OneApm CI还提供了SDK,可以实现自定义的业务数据监控,比如业务的订单趋势,响应速度曲线等。配合CI的丰富报表功能,非常轻松的实现自定义业务监控。

==============================

按照文档,安装CI, 及CI SDK,这里不再赘述。
step 1, OneAPM CI Agent 安装
step 2, OneApm CI SDK For Python安装
step 3, 编写一段简单的python 代码

#!/usr/bin/env python
# -*- coding:utf8 -*-

import commands
import socket
from oneapm_ci_sdk import statsd

hostname = socket.gethostname()
error, result = commands.getstatusoutput(""" tail /opt/dataroot/nginx/access.log |grep "download" | awk -F '" "' '{print $7}' """)
if ( error == 0 ):
        code = result.split("\n")
        statsd.gauge('php_response_time.download.%s' % hostname, float( max(code) ) )
        print 'php_response_time.%s : %04f' % ( hostname, float(max(code)) )

上述代码实现的逻辑为调用系统命令,获取nginx中,php的responsetime,从几个响应时间中取最大值。

step 4, 部署收集数据

#获取Nginx Upstream response time
* * * * * for i in $(seq 3); do sleep 15; python /opt/ci_check_php_response_time.py; done

添加上述计划任务,每秒统计三次响应最慢的请求。由于默认的系统计划任务无法实现按秒制定任务,通过sleep间接实现。

step 5, 查看报表

进入 https://cloud.oneapm.com/dashboard#/,在最右下角找到自定义仪表盘,
效果如下:

QQ20160318-0

根据官方文档的说明,未来还会开放不需要SDK的API,非常适合做一些私有业务的监控和告警,非常期待。

Cloud Insight API

API 提供了更加强大的功能,您可以不受任何约束的发送符合Cloud Insight数据格式标准的任意数据,无论本机是否安装了Cloud Insight Agent。目前API正在开发之中。

在几个月的使用中,OneApm CI和AI,给我们提供了非常多的帮助,慢慢的使用才发现OneApm的强大,感谢 OneApm 志利在使用过程中给我们的帮助,不管是晚上还是周末,响应速度非常快,非常感动。
下次分享一下在使用AI的过程中,通过AI定位ThinkPHP中Redis连接异常的一个故障处理。

Update:

CI在这个月发布了新版,也提供了新的PHPSDK,可以愉快的从之前的python sdk切过来了。同时,也增加了仪表盘分享功能。

aws ami 执行yum出现"Protected multilib versions: openssl-1.0.1e-42.el6_7.2.x86_64 != openssl-1.0.1e-42.el6.i686"

[txtplain]
--> Finished Dependency Resolution
Error: Multilib version problems found. This often means that the root
cause is something else and multilib version checking is just
pointing out that there is a problem. Eg.:

1. You have an upgrade for openssl which is missing some
dependency that another package requires. Yum is trying to
solve this by installing an older version of openssl of the
different architecture. If you exclude the bad architecture
yum will tell you what the root cause is (which package
requires what). You can try redoing the upgrade with
--exclude openssl.otherarch ... this should give you an error
message showing the root cause of the problem.

2. You have multiple architectures of openssl installed, but
yum can only see an upgrade for one of those arcitectures.
If you don't want/need both architectures anymore then you
can remove the one with the missing update and everything
will work.

3. You have duplicate versions of openssl installed already.
You can use "yum check" to get yum show these errors.

...you can also use --setopt=protected_multilib=false to remove
this checking, however this is almost never the correct thing to
do as something else is very likely to go wrong (often causing
much more problems).

Protected multilib versions: openssl-1.0.1e-42.el6_7.2.x86_64 != openssl-1.0.1e-42.el6.i686
[/txtplain]

原因未知,卸载掉openssl-1.0.1e-42.el6.i686即可

Tail for php,php实现tail命令

监控文件变化,可以通过tail,inotify来实现,也可以通过awk转发变化的内容到外部命令中。

下面是通过popen来调用系统命令,性能内存开销相对比较小。

```php
$handle = popen("tail -f /var/log/your_file.log 2>&1", 'r');
while(!feof($handle)) {
$buffer = fgets($handle);
echo "$buffer\n";
flush();
}
pclose($handle);
```

Linux Shell 文本处理工具集锦

本文将介绍Linux下使用Shell处理文本时最常用的工具:
find、grep、xargs、sort、uniq、tr、cut、paste、wc、sed、awk;
提供的例子和参数都是最常用和最为实用的;
我对shell脚本使用的原则是命令单行书写,尽量不要超过2行;
如果有更为复杂的任务需求,还是考虑python吧;

继续阅读Linux Shell 文本处理工具集锦

Android ADB命令大全

(通过ADB命令查看wifi密码、MAC地址、设备信息、操作文件、查看文件、日志信息、卸载、启动和安装APK等)

ADB很强大,记住一些ADB命令有助于提高工作效率。

  1. 获取序列号:
     adb get-serialno
  2. 查看连接计算机的设备:
     adb devices
  3. 重启机器:
     adb reboot
  4. 重启到bootloader,即刷机模式:
     adb reboot bootloader
  5. 重启到recovery,即恢复模式:
     adb reboot recovery
  6. 查看log:
     adb logcat
  7. 终止adb服务进程:
     adb kill-server
  8. 重启adb服务进程:
     adb start-server
  9. 获取机器MAC地址:
     adb shell  cat /sys/class/net/wlan0/address
  10. 获取CPU序列号:
    adb shell cat /proc/cpuinfo
  11. 安装APK:
    adb install <apkfile> //比如:adb install baidu.apk
  12. 保留数据和缓存文件,重新安装apk:
    adb install -r <apkfile> //比如:adb install -r baidu.apk
  13. 安装apk到sd卡:
    adb install -s <apkfile> // 比如:adb install -s baidu.apk
  14. 卸载APK:
    adb uninstall <package> //比如:adb uninstall com.baidu.search
  15. 卸载app但保留数据和缓存文件:
    adb uninstall -k <package> //比如:adb uninstall -k com.baidu.search
  16. 启动应用:
    adb shell am start -n <package_name>/.<activity_class_name>
  17. 查看设备cpu和内存占用情况:
    adb shell top
  18. 查看占用内存前6的app:
    adb shell top -m 6
  19. 刷新一次内存信息,然后返回:
    adb shell top -n 1
  20. 查询各进程内存使用情况:
    adb shell procrank
  21. 杀死一个进程:
    adb shell kill [pid]
  22. 查看进程列表:
    adb shell ps
  23. 查看指定进程状态:
    adb shell ps -x [PID]
  24. 查看后台services信息:
    adb shell service list
  25. 查看当前内存占用:
    adb shell cat /proc/meminfo
  26. 查看IO内存分区:
    adb shell cat /proc/iomem
  27. 将system分区重新挂载为可读写分区:
    adb remount
  28. 从本地复制文件到设备:
    adb push <local> <remote>
  29. 从设备复制文件到本地:
    adb pull <remote>  <local>
  30. 列出目录下的文件和文件夹,等同于dos中的dir命令:
    adb shell ls
  31. 进入文件夹,等同于dos中的cd 命令:
    adb shell cd <folder>
  32. 重命名文件:
    adb shell rename path/oldfilename path/newfilename
  33. 删除system/avi.apk:
    adb shell rm /system/avi.apk
  34. 删除文件夹及其下面所有文件:
    adb shell rm -r <folder>
  35. 移动文件:
    adb shell mv path/file newpath/file
  36. 设置文件权限:
    adb shell chmod 777 /system/fonts/DroidSansFallback.ttf
  37. 新建文件夹:
    adb shell mkdir path/foldelname
  38. 查看文件内容:
    adb shell cat <file>
  39. 查看wifi密码:
    adb shell cat /data/misc/wifi/*.conf
  40. 清除log缓存:
    adb logcat -c
  41. 查看bug报告:
    adb bugreport
  42. 获取设备名称:
    adb shell cat /system/build.prop
  43. 查看ADB帮助:
    adb help
  44. 跑monkey:
    adb shell monkey -v -p your.package.name 500

More:

Linux清除日志

cat /dev/null > /var/log/wtmp
cat /dev/null > /var/log/messages
cat /dev/null > /var/log/secure
cat /dev/null > /var/log/lastlog
rm -f /var/log/wtmp*
rm -f /root/.bash_history
touch .hushlogin
history -c

cat /dev/null > /var/log/syslog
cat /dev/null > /var/adm/sylog
cat /dev/null > /var/log/wtmp
cat /dev/null > /var/log/maillog
cat /dev/null > /var/log/messages
cat /dev/null > /var/log/openwebmail.log
cat /dev/null > /var/log/maillog
cat /dev/null > /var/log/secure
cat /dev/null > /var/log/httpd/error_log
cat /dev/null > /var/log/httpd/ssl_error_log
cat /dev/null > /var/log/httpd/ssl_request_log
cat /dev/null > /var/log/httpd/ssl_access_log

兼容所有linux操作系统的shell

#!/bin/bash
#########################################
#Function:    update time
#Usage:       bash update_time.sh
#Author:      Customer service department
#Company:     Alibaba Cloud Computing
#Version:     3.0
#########################################

check_os_release()
{
  while true
  do
    os_release=$(grep "Red Hat Enterprise Linux Server release" /etc/issue 2>/dev/null)
    os_release_2=$(grep "Red Hat Enterprise Linux Server release" /etc/redhat-release 2>/dev/null)
    if [ "$os_release" ] && [ "$os_release_2" ]
    then
      if echo "$os_release"|grep "release 5" >/dev/null 2>&1
      then
        os_release=redhat5
	os_type=redhat
        echo "$os_release"
      elif echo "$os_release"|grep "release 6" >/dev/null 2>&1
      then
        os_release=redhat6
	os_type=redhat
        echo "$os_release"
      else
        os_release=""
        echo "$os_release"
      fi
      break
    fi
    os_release=$(grep "Aliyun Linux release" /etc/issue 2>/dev/null)
    os_release_2=$(grep "Aliyun Linux release" /etc/aliyun-release 2>/dev/null)
    if [ "$os_release" ] && [ "$os_release_2" ]
    then
      if echo "$os_release"|grep "release 5" >/dev/null 2>&1
      then
        os_release=aliyun5
	os_type=redhat
        echo "$os_release"
      elif echo "$os_release"|grep "release 6" >/dev/null 2>&1
      then
        os_release=aliyun6
	os_type=redhat
        echo "$os_release"
      else
        os_release=""
        echo "$os_release"
      fi
      break
    fi
    os_release=$(grep "CentOS release" /etc/issue 2>/dev/null)
    os_release_2=$(grep "CentOS release" /etc/*release 2>/dev/null)
    if [ "$os_release" ] && [ "$os_release_2" ]
    then
      if echo "$os_release"|grep "release 5" >/dev/null 2>&1
      then
        os_release=centos5
	os_type=redhat
        echo "$os_release"
      elif echo "$os_release"|grep "release 6" >/dev/null 2>&1
      then
        os_release=centos6
	os_type=redhat
        echo "$os_release"
      else
        os_release=""
        echo "$os_release"
      fi
      break
    fi
    os_release=$(grep -i "ubuntu" /etc/issue 2>/dev/null)
    os_release_2=$(grep -i "ubuntu" /etc/lsb-release 2>/dev/null)
    if [ "$os_release" ] && [ "$os_release_2" ]
    then
      if echo "$os_release"|grep "Ubuntu 10" >/dev/null 2>&1
      then
        os_release=ubuntu10
	os_type=ubuntu
        echo "$os_release"
      elif echo "$os_release"|grep "Ubuntu 12.04" >/dev/null 2>&1
      then
        os_release=ubuntu1204
	os_type=ubuntu
        echo "$os_release"
      elif echo "$os_release"|grep "Ubuntu 12.10" >/dev/null 2>&1
      then
        os_release=ubuntu1210
	os_type=ubuntu
        echo "$os_release"
      else
        os_release=""
        echo "$os_release"
      fi
      break
    fi
    os_release=$(grep -i "debian" /etc/issue 2>/dev/null)
    os_release_2=$(grep -i "debian" /proc/version 2>/dev/null)
    if [ "$os_release" ] && [ "$os_release_2" ]
    then
      if echo "$os_release"|grep "Linux 6" >/dev/null 2>&1
      then
        os_release=debian6
	os_type=debian
        echo "$os_release"
      else
        os_release=""
        echo "$os_release"
      fi
      break
    fi
    break
    done
}

modify_rhel5_yum()
{
  wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyuncs.com/repo/Centos-5.repo
  sed -i 's/aliyun/aliyuncs/g' /etc/yum.repos.d/CentOS-Base.repo
  sed -i 's/\$releasever/5/' /etc/yum.repos.d/CentOS-Base.repo
  yum clean metadata
  yum makecache
  cd ~
}

modify_rhel6_yum()
{
  wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyuncs.com/repo/Centos-6.repo
  sed -i 's/aliyun/aliyuncs/g' /etc/yum.repos.d/CentOS-Base.repo
  sed -i 's/\$releasever/6/' /etc/yum.repos.d/CentOS-Base.repo
  yum clean metadata
  yum makecache
  cd ~
}

update_ubuntu10_apt_source()
{
echo -e "\033[40;32mBackup the original configuration file,new name and path is /etc/apt/sources.list.back.\n\033[40;37m"
cp -fp /etc/apt/sources.list /etc/apt/sources.list.back
cat > /etc/apt/sources.list <<EOF
#ubuntu
deb http://cn.archive.ubuntu.com/ubuntu/ maverick main restricted universe multiverse
deb-src http://cn.archive.ubuntu.com/ubuntu/ maverick main restricted universe multiverse
#163
deb http://mirrors.163.com/ubuntu/ maverick main universe restricted multiverse
deb-src http://mirrors.163.com/ubuntu/ maverick main universe restricted multiverse
deb http://mirrors.163.com/ubuntu/ maverick-updates universe main multiverse restricted
deb-src http://mirrors.163.com/ubuntu/ maverick-updates universe main multiverse restricted
#lupaworld
deb http://mirror.lupaworld.com/ubuntu/ maverick main universe restricted multiverse
deb-src http://mirror.lupaworld.com/ubuntu/ maverick main universe restricted multiverse
deb http://mirror.lupaworld.com/ubuntu/ maverick-security universe main multiverse restricted
deb-src http://mirror.lupaworld.com/ubuntu/ maverick-security universe main multiverse restricted
deb http://mirror.lupaworld.com/ubuntu/ maverick-updates universe main multiverse restricted
deb http://mirror.lupaworld.com/ubuntu/ maverick-proposed universe main multiverse restricted
deb-src http://mirror.lupaworld.com/ubuntu/ maverick-proposed universe main multiverse restricted
deb http://mirror.lupaworld.com/ubuntu/ maverick-backports universe main multiverse restricted
deb-src http://mirror.lupaworld.com/ubuntu/ maverick-backports universe main multiverse restricted
deb-src http://mirror.lupaworld.com/ubuntu/ maverick-updates universe main multiverse restricted
EOF
apt-get update
}

update_ubuntu1204_apt_source()
{
echo -e "\033[40;32mBackup the original configuration file,new name and path is /etc/apt/sources.list.back.\n\033[40;37m"
cp -fp /etc/apt/sources.list /etc/apt/sources.list.back
cat > /etc/apt/sources.list <<EOF
#12.04
deb http://mirrors.aliyuncs.com/ubuntu/ precise main restricted universe multiverse
deb http://mirrors.aliyuncs.com/ubuntu/ precise-security main restricted universe multiverse
deb http://mirrors.aliyuncs.com/ubuntu/ precise-updates main restricted universe multiverse
deb http://mirrors.aliyuncs.com/ubuntu/ precise-proposed main restricted universe multiverse
deb http://mirrors.aliyuncs.com/ubuntu/ precise-backports main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ precise main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ precise-security main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ precise-updates main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ precise-proposed main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ precise-backports main restricted universe multiverse
EOF
apt-get update
}

update_ubuntu1210_apt_source()
{
echo -e "\033[40;32mBackup the original configuration file,new name and path is /etc/apt/sources.list.back.\n\033[40;37m"
cp -fp /etc/apt/sources.list /etc/apt/sources.list.back
cat > /etc/apt/sources.list <<EOF
#12.10
deb http://mirrors.aliyuncs.com/ubuntu/ quantal main restricted universe multiverse
deb http://mirrors.aliyuncs.com/ubuntu/ quantal-security main restricted universe multiverse
deb http://mirrors.aliyuncs.com/ubuntu/ quantal-updates main restricted universe multiverse
deb http://mirrors.aliyuncs.com/ubuntu/ quantal-proposed main restricted universe multiverse
deb http://mirrors.aliyuncs.com/ubuntu/ quantal-backports main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ quantal main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ quantal-security main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ quantal-updates main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ quantal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyuncs.com/ubuntu/ quantal-backports main restricted universe multiverse
EOF
apt-get update
}

config_time_zone()
{
  if [ "$os_type" == "redhat" ]
  then
    if [ -e "/usr/share/zoneinfo/Asia/Shanghai" ]
    then
      echo -e "\033[40;32mStep1:Begin to config time zone.\n\033[40;37m"
      cp -fp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
      echo -e "ZONE=\"Asia/Shanghai\"\nUTC=false\nARC=false">/etc/sysconfig/clock
    fi
  elif [ "$os_type" == "ubuntu" ] || [ "$os_type" == "debian" ]
  then
    echo -e "\033[40;32mStep1:Begin to config time zone.\n\033[40;37m"
    cp -fp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
  fi
}

update_debian_apt_source()
{
cat >> /etc/apt/sources.list <<EOF
#debian6
deb http://mirrors.aliyuncs.com/debian/ squeeze main non-free contrib
deb http://mirrors.aliyuncs.com/debian/ squeeze-proposed-updates main non-free contrib
deb-src http://mirrors.aliyuncs.com/debian/ squeeze main non-free contrib
deb-src http://mirrors.aliyuncs.com/debian/ squeeze-proposed-updates main non-free contrib
EOF
apt-get update
}

install_ntp()
{
  case "$os_release" in
  redhat5|centos5|aliyun5)
    modify_rhel5_yum
    if ! yum install ntp -y
    then
      echo "Can not install ntp.Script will end."
      rm -rf $LOCKfile
      exit 1
    fi
    ;;
  redhat6|centos6|aliyun6)
    modify_rhel6_yum
    if ! yum install ntp -y
    then
      echo "Can not install ntp.Script will end."
      rm -rf $LOCKfile
      exit 1
    fi
    ;;
  ubuntu10)
    update_ubuntu10_apt_source
    if ! apt-get install ntp ntpdate -y
    then
      echo "Can not install ntp.Script will end."
      rm -rf $LOCKfile
      exit 1
    fi
    ;;
 ubuntu1204)
   update_ubuntu1204_apt_source
   if ! apt-get install ntp ntpdate -y
   then
     echo "Can not install ntp.Script will end."
     rm -rf $LOCKfile
     exit 1
   fi
   ;;
 ubuntu1210)
   update_ubuntu1210_apt_source
   if ! apt-get install ntp ntpdate -y
   then
     echo "Can not install ntp.Script will end."
     rm -rf $LOCKfile
     exit 1
   fi
   ;; 
 debian6)
   update_debian_apt_source
   if ! apt-get install ntp ntpdate -y
   then
     echo "Can not install ntp.Script will end."
     rm -rf $LOCKfile
     exit 1
   fi
   ;;
 esac
}

mod_config_file()
{
  if [ "$os_type" == "redhat" ]
  then
     if ! grep "aliyun.com" /etc/ntp/step-tickers >/dev/null 2>&1
     then
       echo -e "ntp1.aliyun.com\nntp1.aliyun.com\nntp1.aliyun.com\n0.asia.pool.ntp.org\n210.72.145.44">>/etc/ntp/step-tickers
     fi
  fi
  if ! grep "aliyun.com" /etc/ntp.conf >/dev/null 2>&1
  then
    echo -e "server ntp1.aliyun.com prefer\nserver ntp2.aliyun.com\nserver ntp3.aliyun.com\nserver 0.asia.pool.ntp.org\nserver 210.72.145.44">>/etc/ntp.conf
  fi
}

install_chkconfig()
{
  if [ "$os_type" == "redhat" ] || [ "$os_type" == "centos" ]
  then
     yum install chkconfig -y
  elif [ "$os_type" == "ubuntu" ] || [ "$os_type" == "debian" ]
  then
     apt-get install rcconf dialog whiptail -y --force-yes --fix-missing
  fi
}

####################Start###################
#check lock file ,one time only let the script run one time 
LOCKfile=/tmp/.$(basename $0)
if [ -f "$LOCKfile" ]
then
  echo -e "\033[1;40;31mThe script is already exist,please next time to run this script.\n\033[0m"
  exit
else
  echo -e "\033[40;32mStep 0.No lock file,begin to create lock file and continue.\n\033[40;37m"
  touch $LOCKfile
fi

#check user
if [ $(id -u) != "0" ]
then
  echo -e "\033[1;40;31mError: You must be root to run this script, please use root to install this script.\n\033[0m"
  rm -rf $LOCKfile
  exit 1
fi
check_os_release
config_time_zone

echo -e "\033[40;32mStep2:Check ntp package and if not to install it.\n\033[40;37m"
install_ntp

echo -e "\033[40;32mStep3:Modify the ntp config file.\n\033[40;37m"
mod_config_file

echo -e "\033[40;32mStep4:Begin to update time...\n\033[40;37m"
ntpdate -u ntp1.aliyun.com
ntpdate -u ntp2.aliyun.com

echo -e "\033[40;32mStep5:Restart ntp service...\n\033[40;37m"
if [ "$os_type" == "redhat" ] || [ "$os_type" == "centos" ]
then
   service ntpd restart
elif [ "$os_type" == "ubuntu" ] || [ "$os_type" == "debian" ]
then
   service ntp restart
fi

install_chkconfig
if [ "$os_type" == "redhat" ] || [ "$os_type" == "centos" ]
then
   chkconfig --level 2345 ntpd on
elif [ "$os_type" == "ubuntu" ] || [ "$os_type" == "debian" ]
then
   rcconf --on ntp
fi
echo -e "\033[40;32mStep6:The NTP service is configured to start automatically at runlevels 2345.\n\033[40;37m"
rm -rf $LOCKfile

git修改文件大小写的快捷方法

这个问题有点点麻烦,手工mv以后,在git是直接忽略的。

两步走,使用git mv -f来移动文件,-f是强制覆盖已经存在的文件,然后修改git忽略大小写的设置:ignorecase = false

You can try:
1. "git mv -f foo.txt Foo.txt"
2. set `ignorecase' to false in the config file

vim .git/config
ignorecase = false
git mv -f Foo foo
git commit -am "recase foo"

当然,也有其它同学的办法,但这样会产生两次commit

mv foo foo2
git add -A
git commit -m "renaming"
mv foo2 FOO
git add -A
git commit --amend -m "renamed foo to FOO"

利用Docker构建开发环境

最近接触PAAS相关的知识,在研发过程中开始使用Docker搭建了自己完整的开发环境,感觉生活在PAAS时代的程序员真是幸福,本文会简要介绍下Docker是什么,如何利用Docker来搭建自己的开发环境(本文主要是面向Mac OS X),以及期间所遇到的一些坑和解决方案。(本文会要求你对PAAS、LXC、CGroup、AUFS有一定的了解基础,请自行Google )

大背景–虚拟化技术历史

计算机虚拟化技术由来已久,从硬件仿真到全虚拟化,再到准虚拟化和操作系统虚拟化,各种技术粉墨登场,种类繁多,说实在的有点眼花缭乱和复杂;但用户的核心诉求一直是比较简单的,降低信息技术(IT)的运营成本,提高资源利用率,提高安全性和可靠性等等;虽说用户的核心诉求比较简单,但每个时代的需求场景却是不同的。在大型机时代,虚拟化技术被用来支持多个用户能够同时使用大型机,在x86架构时代,随着企业服务的大规模部署,虚拟化技术主要是用来提高企业资源的利用率,而现如今,随着云计算时代的到来,人们对应用的安全性、隔离性越来越高,对于部署的标准化以及虚拟机的性能要求越来越高。现如今,一种叫Linux容器的虚拟化技术逐渐得到广泛的应用,它的优点有许多,本文不一一赘述,有太多的文章可以参考。

什么是Docker?

docker的英文本意是码头工人,也就是搬运工,这种搬运工搬运的是集装箱(Container),集装箱里面装的可不是商品货物,而是任意类型的App,Docker把App(叫Payload)装在Container内,通过Linux Container技术的包装将App变成一种标准化的、可移植的、自管理的组件,这种组件可以在你的latop上开发、调试、运行,最终非常方便和一致地运行在production环境下。

Docker的核心底层技术是LXC(Linux Container),Docker在其上面加了薄薄的一层,添加了许多有用的功能。这篇stackoverflow上的问题和答案很好地诠释了Docker和LXC的区别,能够让你更好的了解什么是Docker, 简单翻译下就是以下几点:

  • Docker提供了一种可移植的配置标准化机制,允许你一致性地在不同的机器上运行同一个Container;而LXC本身可能因为不同机器的不同配置而无法方便地移植运行;
  • Docker以App为中心,为应用的部署做了很多优化,而LXC的帮助脚本主要是聚焦于如何机器启动地更快和耗更少的内存;
  • Docker为App提供了一种自动化构建机制(Dockerfile),包括打包,基础设施依赖管理和安装等等;
  • Docker提供了一种类似git的Container版本化的机制,允许你对你创建过的容器进行版本管理,依靠这种机制,你还可以下载别人创建的Container,甚至像git那样进行合并;
  • Docker Container是可重用的,依赖于版本化机制,你很容易重用别人的Container(叫Image),作为基础版本进行扩展;
  • Docker Container是可共享的,有点类似github一样,Docker有自己的INDEX,你可以创建自己的Docker用户并上传和下载Docker Image;
  • Docker提供了很多的工具链,形成了一个生态系统;这些工具的目标是自动化、个性化和集成化,包括对PAAS平台的支持等;

那么Docker有什么用呢?对于运维来说,Docker提供了一种可移植的标准化部署过程,使得规模化、自动化、异构化的部署成为可能甚至是轻松简单的事情;而对于开发者来说,Docker提供了一种开发环境的管理方法,包括映像、构建、共享等功能,而后者是本文的主题。

Docker的安装和构成

Docker官方本身提供了非常具体的安装教程,这里不说具体的安装过程,请参考Docker安装(Mac系统),重要的是描述下原理和安装完成后的结构,好对Docker更好的了解。 由于LXC本身不支持Mac内核,因此需要跑一个VirtualBox虚拟机(TinyCoreLinux)来安装,幸好Docker社区提供了一个非常方便的工具boot2docker(其实就是一个VBoxManage的包装shell脚本),用于安装Mac下的整个Docker环境。具体的结构如下:

docker-install

如图所示,安装完成后,具体情况如下:

  • 在Mac的home目录~/.boot2docker下创建了虚拟机所需要的文件,其中boot2docker.iso是虚拟机映像,这是一个由CD-ROM引导的TinyCoreLinux系统;而boot2docker-vm.vmdk文件则是你的虚拟机磁盘,你所有的持久化数据都存放在这里,包括docker创建的lxc容器等文件。
  • 在Mac下,docker被分为客户端docker-client和服务端docker-daemon两部分,如果是在linux(比如ubuntu),实际上则是同一个可执行文件同时充当客户端和服务端。docker-daemon可以监听unix scoket,也可以在tcp socket(默认端口为4234),docker-client会通过一个叫DOCKER_HOST的环境变量读取服务地址和端口,因此你应该在你的bash_profile文件里面添加这么一行:
    export DOCKER_HOST=tcp://127.0.0.1:4243

docker-daemon跑在虚拟机上,这个程序实际上就是接收docker-client发送过来的消息命令,创建、启动和销毁lxc容器,以及docker本身的版本管理、映像存储等等 运行你的第一个docker容器 安装完成后,就差不多可以开始创建和运行docker容器了,在这之前,你首先得下载一个Image,什么是Image?我们先来了解docker的2个基础概念:ImageContainer

Container和Image 在Docker的世界里,Image是指一个只读的层(Layer),这里的层是AUFS里的概念,最直观的方式就是看一下docker官方给出的图:

docker-filesystems-multilayer

Docker使用了一种叫AUFS的文件系统,这种文件系统可以让你一层一层地叠加修改你的文件,最底下的文件系统是只读的,如果需要修改文件,AUFS会增加一个可写的层(Layer),这样有很多好处,例如不同的Container可以共享底层的只读文件系统(同一个Kernel),使得你可以跑N多个Container而不至于你的硬盘被挤爆了!这个只读的层就是Image!而如你所看到的,一个可写的层就是Container。

那Image和Container的区别是什么?很简单,他们的区别仅仅是一个是只读的层,一个是可写的层,你可以使用docker commit 命令,将你的Container变成一个Image,也就是提交你所运行的Container的修改内容,变成一个新的只读的Image,这非常类似于git commit命令,感觉真棒!

实际上这就是Docker对Container映像的版本管理基石,AUFS文件系统实在是太美妙了,更多细节可以参考DotCloud的这篇文章

运行和退出

在了解了Image和Container的概念后,我们可以开始下载一个Image,Docker的好处就是提供了一个类似github的Image仓库管理,你可以非常方便pull别人的Image下来运行,例如,我们可以下载一个ubuntu Image:

docker pull ubuntu:13.10

这里的13.10是一个Tag,类似于git的tag,这里的tag可以为你制定一个ubuntu的版本。下载完成后,执行docker images命令可以列出你已经下载或者自己构建的image:(请允许我使用可爱的马赛克 :) )

QQ20140322-1

你可以看到ubuntu:13.10的大小为178MB,以及它的IMAGE ID。 现在我们开始运行一个Container,命令很简单,例如我们想运行一个执行Shell终端的Container:

QQ20140322-2

如你看到的,你已经进入到一个Shell里面,可以执行你想执行的任何命令,就和在ubuntu里面一样,进去后默认是在根目录/下,可以看到经典的unix/linux目录结构,以及你所运行的bash版本等信息。你可以给你的Container定一个名字,通过–name选项,例如这里命名了shell,日后你就可以直接用这个名字引用Contanier。

退出一个Container也很简单,你直接exit就好了。 其他更多的命令这里不做赘述,因为官方的文档已经非常全面,这里只是给一个直观的初步印象。下面进入主题。

利用Docker搭建开发环境

我们先看看程序员在搭建开发环境时遇到的一些问题:

  • 软件安装麻烦,比如很多公司都使用redhat,一般开发人员又不给root,安装一个nginx或者是mysql都得自己下载编译安装 权限问题,没有root,一些软件无法运行,例如dnsmasq;
  • 没有root,无法修改hosts,无法netstat -nptl,无法tcpdump,无法iptable
  • 隔离性差,例如不同的开发人员如果在同一台主机环境下共享开发,虽然是用户隔离,但端口如果不规范可能会冲突;同一个Mysql如果权限管理不好很有可能误删别人的数据
  • 可移植性差,例如和生产环境不一致,开发人员之间也无法共享;更严重的情况是当有新人入职时,通常需要又折腾一遍开发环境,无法快速搭建

这些问题可以通过在本地搭建虚拟机来解决,但虚拟机是一个很笨重的解决方案,Docker是一个非常轻量级的方案,而且还拥有虚拟机没有的一些功能,例如标准化Image,Image共享等,更重要的是,利用Docker,你可以运行非常多的容器,在你的Mac下搭建一个分布式的开发环境根本不是什么大的问题,而且对内存、磁盘和cpu的消耗相比传统的虚拟机要低许多,这些都要归功于AUFS和LXC这两大神奇的技术。

构建基础Image

想要搭建一个节省磁盘空间和扩展性良好的开发环境,最重要的第一步就是构建一个基础性的Image,比如你的主要开发语言是Ruby,那么你肯定需要一个已经安装好以下工具的基础Image:

  • ruby
  • bundler
  • gem

然后在此基础上,你可以扩展这个基础的Image(下面叫base)为不同的开发环境,例如rails,或者是nats。当然,你的这个base也可以从别人的Image扩展而来,还记得我们刚刚pull下来的ubuntu:13.10这个Image吗?你可以从这个Image扩展开始构建你的base,如何做呢?Docker提供了一种标准化的DSL方式,你只需要编写一个Dockerfile,运行docker build指令,就可以构建你自己的Image,这有点像Makefile和make命令一样,只是大家要构建的内容和构建语言不同。

Dockerfile的语法请参考Dockerfile Reference,这里给出上面提到的Ruby开发的base Dockerfile示例:

FROM ubuntu:13.10
RUN apt-get update
RUN apt-get install -y ruby ruby-dev gem
RUN gem install bundler

这里只用到了很简单的2个指令:FROM和RUN,FROM指定了我们要扩展的Image,RUN指定我们要运行的命令,这里是安装ruby,gem、bundler等软件。写好Dockerfile后,运行以下指令就可以创建你的base image了:

docker build --rm -t dev:base .

-t 选项是你要构建的base image的tag,就好比ubuntu:13.10一样 –rm 选项是告诉Docker在构建完成后删除临时的Container,Dockerfile的每一行指令都会创建一个临时的Container,一般你是不需要这些临时生成的Container的 如你所想,我们可以像运行ubuntu:13.10那样运行我们的base了:

docker run -i -t --name ruby dev:base irb

这里我们使用dev:base这个Image运行了一个irb解释器(Ruby的交互式解释器)。 在构建完base之后,你可以依样画葫芦构建你的rails环境,很简单,只需要FROM dev:base,然后RUN安装你的rails组件就可以了,不再赘述。最终你可能构建的开发环境是这样的:

docker-dev

如上图所示,base和service都是从ubutnu:13.10继承而来,他们作为不同的基础开发环境,base是ruby开发环境(也许命名为dev:ruby更为合适?),而service是一些基础数据服务,例如mysql,memcache,我建议将这些第三方组件集中在一个Container中,因为他们的环境不经常修改,可以作为一种底层服务Container运行,除非你需要构建分布式的服务,例如memcache集群,那可以继续拆分。

指定Image入口

当你构建完你的base Image和其他应用的Image之后,你就可以启动这些Image了,还记得前面我们给出的运行命令吗?

docker run -i -t --name shell dev:base /bin/bash

这里我们运行了一个bash,这样你就可以在shell里面执行你所想要执行的任何命令了,但是我们有时候并不想每次都启动一个shell,接着再在shell里面启动我们的程序,比如一个mysql,而是想一启动一个容器,mysql服务就自动运行了,这很简单,Dockerfile提供了CMD和ENTRYPOINT这2个指令,允许你指定一个Image启动时的默认命令。CMD和ENTRYPOINT的区别是CMD的参数可以由docker run指令指定的参数覆盖,而ENTRYPOINT则不可以。例如我们想运行一个memcached服务,可以这么写Dockerfile:

FROM ubuntu:13.10
RUN apt-get install -y memcached CMD memcached -u root -p 40000

或者可以这么写:

FROM ubuntu:13.10
RUN apt-get install -y memcached ENTRYPOINT ["memcached", "-u", "root", "-p", "40000"]

注意不要把memcached启动为后台进程,即加上-d选项,否则docker启动的container会马上stop掉,这点我也觉得比较意外。 接着我们build这个Image:

docker build -t dev:memcache . 

这样,当你build完你的Image后,你可以直接将该Image运行为一个容器,它会自动启动mysql服务:

docker run --name memcache_service -d dev:memcache 

注意使用-d (detach) 选项,这样这个container就会作为后台进程运行了,接着你可以使用docker ps命令查看是否有在运行。

磁盘映射

大部分时候你会需要把你host主机(宿主)上的目录映射到Container里面,这样你就非常方便地在host主机上编辑代码,然后直接就可以在Container里面运行它们,而不用手动copy到Container里面再重启Container。按理将host的目录映射到guest(指Container)上应该是一件很容易的事情,就好像VMWare那样,但可惜的是,由于Mac上的Docker多了一层虚拟机,因此多了一层周折,你必须先VM上的目录通过sshfs mount到host(指Mac)上,然后再将你的目录或文件copy到这个mount的目录,再将VM上的这个目录映射到Container里,听起来比较拗口,画个图会清晰很多。

docker-disk-map

如上图所示,VM里面的/mnt/sda1/dev/目录(你需要自己创建)通过sshfs命令mount到了host主机(Mac)的~/workspace/dev/目录 ,而VM里的/mnt/sda1/dev/目录又被映射到了Container的/src/目录下,这样你就可以在Container里面的/src/目录下访问你的host文件了。具体如何做呢?首先你需要安装sshfs命令,然后将VM的password写到一个文件中,例如~/.boot2docker/b2d-passwd,在用sshfs命令mount起VM的/mnt/sda1/dev目录:

brew install sshfs
cat tcuser &gt; ~/.boot2docker/b2d-passwd
sshfs docker@localhost:/mnt/sda1/dev ~/workspace/dev -p 2022 -o reconnect -o password_stdin &lt; ~/.boot2docker/b2d-passwd

这样你就可以在你的Container的/src目录下看到你host里的文件了。 磁盘映射还有2个地方需要注意:

  • 你的文件实际上是存储在VM里面的,也就是说你需要将你的目录或者文件copy到VM里面,你sshfs之后,就是copy到~/workspace/dev目录下
  • 千万不要sshfs mount非/mnt/sda1下的目录,因为VM里面跑的是TinyCoreLinux,这个OS的rootfs是临时性的(放在内存的,实际上就是boot2docker.iso文件里面的一个rootfs),因此其根目录/下的东西(包括/home)根本不会持久化,只有/mnt/sda1这个目录下的才能持久化。如果你放在/home目录下,只要VM一重启,就会丢失的,/mnt/sda1则不会,实际上就是那个~/.boot2docker-vm.vmdk文件挂载到了/mnt/sda1目录下

端口映射

和磁盘映射一样,你有时候会需要将Container的端口映射到host主机上,同样蛋疼的是,由于多了一层VM,端口映射也显得比较麻烦。首先你需要设置VirtualBox的端口映射,然后再将Container的端口映射到你的VM里面:

docker-port-map

具体是这么做的,通过2条命令:

boot2docker ssh -L 8000:localhost:8000
docker run -i -t -p 8000:8000

也就是说在docker run的时候通过-p选项指定要映射的端口到VM,而boot2docker ssh命令则是将VM的8000端口映射到了host(Mac)的8000端口,这样你就可以通过Mac的localhost:8000访问Container的8000端口了。 其实,有另一种解决方案就是你不用映射到host(Mac),而是直接登录到VM里面进行访问就好了,boot2docker ssh就可以登录到VM,这样就类似于你的host是ubuntu,但这种解决方案的问题是这个ubuntu太弱了(TinyCoreLinux),如果你在这个ubuntu里面开发代码,或者是运行浏览器,是非常蛋疼的事情,关键还是这个ubuntu是每次重启都会复原的!所以我建议还是做多一层映射好了。 最后,实际上在VM里面,你是可以直接访问所有的Container的端口的,因为VM到Container的网络都是桥接的。

其他的一些坑

在使用的过程中,还遇到一些不少的坑:

  1. /etc/hosts文件无法修改,这样你就不能自己做域名解析
  2. VM的系统时间是UTC +0000的,而且貌似无法修改
  3. Container的IP无法指定为静态IP,因此每次重启Container时,IP可能会变化

第1个问题的解决方案是通过安装dnsmasq软件来做域名解析:

# 首先,在你的Container里面安装dnsmasq软件:
apt-get install dnsmasq
# 将以下文本添加到 /etc/dnsmasq.conf文件的最后:
listen-address=127.0.0.1 resolv-file=/etc/resolv.dnsmasq.conf conf-dir=/etc/dnsmasq.d user=root
# 接着在/etc/dnsmasq.d/目录下新建一个文件,随意起个名字
vi /etc/dnsmqsq.d/dns.conf
# 指定你要映射的域名,例如google.com,则将下面贴进dns.conf文件
address="/google.com/172.17.0.4"
# 最后退出容器,重启启动容器时,通过-dns选项指定域名服务器
docker run -i -t -dns 127.0.0.1 -dns 8.8.8.8 dev:base /bin/bash
# 一定要注意上面添加google的域名服务器8.8.8.8,否则你访问不了外网
# 进去Container后,启动dnsmasq,这样你就能够ping google.com了
/etc/init.d/dnsmasq start

第2个问题的解决方案就稍微麻烦些,起码我没有找到更好的解决方案,我是将boot2docker.iso文件重新制作一次来解决这个问题的:

# 首先你需要将boot2docker.iso文件mount到一个目录下
hdiutil mount ~/.boot2docker/boot2docker.iso
# 系统会mount到/Volumes/boot2docker目录下,然后你最好将这下面的东西copy出来到一个另外的目录,这样我们好制作一张新的ISO
cp -r /Volumes/boot2docker/* ~/tmp/
# 接着我们修改以下文件
vi ~/tmp/boot/isolinux/isolinux.cfg
# 将其中的以下这行修改:
append loglevel=3 user=docker console=ttyS0 console=tty0 nomodeset norestore base
# 修改为:(其实就是加了tz的启动参数),然后保存
append tz=CST-8 loglevel=3 user=docker console=ttyS0 console=tty0 nomodeset norestore base
# 接着你必须在ubuntu环境下重新制作ISO文件,你可以利用docker跑一个ubuntu,哈哈,假设你将boot2docker目录copy到了ubuntu的/src/目录下,那么接着这么做
# 安装xorriso命令
apt-get install xorriso
# 构建ISO映射
xorriso -as mkisofs -J -R -V boot2docker -no-emul-boot -boot-load-size 4 -boot-info-table -b boot/isolinux/isolinux.bin -c boot/isolinux/boot.cat -o /boot2docker.iso /src/
# 这样就生成了/boot2docker.iso文件,最后你就可以替换到VM的启动ISO文件了,然后重启VM了
boot2docker restart
# 最后你必须设置你的VM为正确的时间,使用date -s 命令,最后用date命令查看,你就能看到CST时区的正确时间了
Sun Mar 30 00:27:13 CST 2014
# 对于你启动的container,你都必须重新设置TZ环境变量,否则即使VM是CST-8,你的container还是UCT +00:00的时间
export TZ='CST-8'

第三个问题暂时无法解决(可能需要编辑底层的LXC配置文件)。

docker的限制以及后续的一些想法

docker其实还是有一些限制的:

  • 要求你的环境是Linux的,而且内核必须很新(>= 2.6.27 (29)),这其实是LXC本身的限制,和docker无关
  • docker的Container目前host是不能修改的,当然有解决方案(dnsmasq)
  • docker的Container也暂时无法指定静态IP

用docker作为开发环境甚至是生产环境其实还有很多地方值得尝试:

  • 在团队内部构建本地的仓库,标准化所有的开发环境,使得团队的新人可以快速上手
  • 在生产环境部署docker,这其实是PAAS的虚拟化和自动化的一种方式,利用LXC和Docker能够更便捷地实施PAAS
  • 尝试用docker做分布式集群模拟和测试,成本会更加低廉,更加容器维护

参考文章

  1. Linux虚拟化技术
  2. 利用Linux容器实现可移植的应用部署
  3. 如何修改host
  4. Building a Development With Docker
  5. boot2docker的build
  6. PAAS Under the Hood