月度归档:2016年04月

修改Apache2的Access日志格式

这是一个由实际需求引发的问题。公司服务器上原来有一个防止恶意抓取脚本,基本思路是从Apache2的access.log中获取到访问的来源ip,然后对ip进行分析,提取出其中的可疑ip加入Apache2的block ip中。

但是,由于Apache版本的升级还是其他不知道的什么缘故,脚本无法使用了。查到原因发现是因为类似以下的记录引起的:

记录1:

crawl-66-249-75-174.googlebot.com - - [27/Apr/2016:06:32:25 +0000] "GET /es/hipercor-vestidos/all/9/ HTTP/1.1" 302 584 "-" "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)"

记录2:

::1 - - [27/Apr/2016:00:35:12 +0000] "OPTIONS * HTTP/1.0" 200 125 "-" "Apache/2.4.7 (Ubuntu) (internal dummy connection)"

这是两条非常典型的非典型记录,主要问题处在第一个字段(Hostname)。默认的access.log中记录的第一个字段为来访的主机名,一般情况下是ip地址,但是在记录1中,显示的是一个完整的主机名。这是一个来自Google的bot,主机名为crawl-66-249-75-174.googlebot.com,ip地址被放在了中间。于是脚本识别就出现了问题。所以,我需要修改access日志,将第一个字段修改为ip而不是hostname。

网上查到apache2.conf中有一个字段是控制显示ip还是hostname,我找到了这个字段。却发现这个字段默认为off,即显示ip。

#
# HostnameLookups: Log the names of clients or just their IP addresses
# e.g., www.apache.org (on) or 204.62.129.132 (off).
# The default is off because it'd be overall better for the net if people
# had to knowingly turn this feature on, since enabling it means that
# each client request will result in AT LEAST one lookup request to the
# nameserver.
#
HostnameLookups Off

那么,问题肯定不出在这。所以我还是去查看官方文档。在mod_log_config模块中,定义了Access Log的格式。这段配置也在Apache2.conf文件中:

#
# The following directives define some format nicknames for use with
# a CustomLog directive.
#
# These deviate from the Common Log Format definitions in that they use %O
# (the actual bytes sent including headers) instead of %b (the size of the
# requested file), because the latter makes it impossible to detect partial
# requests.
#
# Note that the use of %{X-Forwarded-For}i instead of %h is not recommended.
# Use mod_remoteip instead.
#
LogFormat "%v:%p %h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %O" common
LogFormat "%{Referer}i -> %U" referer
LogFormat "%{User-agent}i" agent

我的日志格式是采用的combine方式,所以看这行:

LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined

我们关注第一个参数,%h。在官方文档中是这样描述这个字段的:

%h: Remote hostname. Will log the IP address if HostnameLookups is set to Off, which is the default. If it logs the hostname for only a few hosts, you probably have access control directives mentioning them by name. See the Require host documentation.

按照这段说明,既然我设置的HostnameLookups为Off,所以应当显示IP才对,但不不知道为什么没有生效。于是,我看到文档中的另一个字段:%a,描述如下:

%a: Client IP address of the request (see the mod_remoteip module).

记录请求IP,这应该就是我要的了。于是,修改配置如下:

LogFormat "%a %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined

现在,记录1的情况就已经解决了。下面来解决记录2的问题。

“::1”这个记录是IPv6的回环地址,相当于IPv4的127.0.0.1。而我们网站并不需要IPv6的支持,那么问题就简单了,只需要关闭Apache2对IPv6的支持即可。

在ports.conf中修改下面字段即可:

Listen 0.0.0.0:80

重新运行脚本,可以正常使用了。

自动化运维工具Ansible使用(三)

ad-hoc模式基本使用

ansible在使用ad-hoc模式执行命令时,相当于使用SSH连接各个服务器并在服务器上执行命令。格式为:

ansible <host-pattern> [options]

一个典型的ansible ad-hoc命令格式为:

ansible <host> -m module -a 'command' -f forks
# eg
ansible group-a -a 'date'
ansible all -m service -a 'name=mysql state=restarted'

上面给出了两个例子,第一个例子为在group-a组中所有服务器上执行date命令;第二个例子为在所有服务器上执行service mysql restart命令。

第一个例子只是简单的执行命令,这里不多介绍。而在第二个例子中,我们使用了ansible自带的service模式,这个模式中提供了restart选项,这样我们就可以直接使用这个命令来重启服务。

ansible自带的模式有很多,可以通过以下命令查看当前ansible支持的模块:

ansible-doc -l

逻辑运算

ansible对host的控制支持逻辑运算,举例如下:

目的:在group-a和group-b上执行date命令

ansible group-a:group-b -a 'date'

目的:在group-a和group-b上排除site-1执行date命令

ansible 'group-a:group-b:!site-1' -a 'date'

常用模块

下面用一些实例介绍比较常用的几个模块:

copy模块:

目的:把主控端/root目录下的a.sh文件拷贝到到指定节点上

ansible 111.111.111.111 -m copy -a 'src=/root/a.sh dest=/tmp/'

file模块:

目的:更改指定节点上/tmp/t.sh的权限为755,属主和属组为root

ansible all -m file -a "dest=/tmp/t.sh mode=755 owner=root group=root"

cron模块:

目的:在指定节点上定义一个计划任务,每隔3分钟到主控端更新一次时间

ansible all -m cron -a 'name="custom job" minute=*/3 hour=* day=* month=* weekday=* job="/usr/sbin/ntpdate 122.122.122.122"'

service模块:

目的:重启指定节点上的 mysql 服务

ansible all -m service -a 'name=mysql state=restarted'

script模块:

目的:在指定节点上执行/root/a.sh脚本(请注意,这个脚本存放在Server端而不是Client端)

ansible site-1 -m script -a '/root/a.sh'

ping模块:

目的:检查指定组机器是否还能连通

ansible group-a -m ping

command模块:

目的:在指定组服务器上运行git pull命令

ansible group-all -m command -a 'git pull origin master chdir=/srv/www/{{ domain }}'

get_url模块:

目的:在制定服务器上执行wget操作

ansible site-3 -m get_url -a 'url=http://www.google.com/favicon.ico dest=/tmp'

synchronize模块:

目的:同步Server和Client内容。将Server的/root/a目录推送到指定节点的/tmp目录下

ansible site-5 -m synchronize -a 'src=/root/a dest=/tmp/ compress=yes'

这里有几个参数可以使用:

mode=pull 更改推送模式为拉取模式
delete=yes 使两边的内容一样(即以推送方为主)
compress=yes 开启压缩,默认为开启
--exclude=.git 忽略同步.git结尾的文件

拉取

 目的:将site-3节点的/tmp/a目录拉取到主控节点的/root目录下
 命令:ansible site-3 -m synchronize -a 'mode=pull src=/tmp/a dest=/root/'

 查看进程:

目的:查看远程主机的httpd进程

ansible storm_cluster -m shell -a "ps -ef | grep httpd"
ansible storm_cluster -m raw -a "pa -ef | grep httpd"

以上就是一些常用的模式的使用,其他的模块和具体的说明信息见这里:Ansible Documentation – All Modules

参考:Ansible – exclude host from playbook execution

自动化运维工具Ansible使用(二)

Inventory配置

Inventory指的是Client服务器的信息。默认放在/etc/ansible/hosts文件中(如果不存在就创建一个)。这个文件存储了服务器的相关信息,使用类ini格式进行排版。可以进行分组,变量赋予等操作。下面给出典型的hosts文件进行说明:

# /etc/ansible/hosts

site-1 ansible_ssh_host=111.111.111.111 domain=zivers-1.com db=db-001
site-2 ansible_ssh_host=122.122.122.122 domain=zivers-2.com db=db-002
site-3 ansible_ssh_host=133.133.133.133 domain=zivers-3.com db=db-003
site-4 ansible_ssh_host=144.144.144.144 domain=zivers-4.com db=db-004
site-5 ansible_ssh_host=155.155.155.155 domain=zivers-5.com db=db-005

[group-all:children]
group-c
site-5

[group-a]
site-1
site-2

[group-b]
site-3
site-4

[group-c:children]
group-a
group-b

[group-b:vars]
zivers=false

在这个文件中,我们对Client服务器的信息进行了定义。

首先定义了5台Client服务器,并给予他们一些变量值。这五台服务器名分别为site-1,site-2,site-3,site-4,site-5,在执行ansible命令时就可以使用这个名字来直接执行,例如:

ansible site-1 -a 'date'

site-1后面就是有关这台服务器的一些变量配置,第一个是ansible自带的变量字段ansible_ssh_host,定义了服务器的IP地址。之后的两个变量domain和db是自定义的变量,这些变量可以在ansible执行的过程中很方便的使用。

比如,我的Client的服务器上,网站的文件被放在了网站域名对应的目录下:/var/www/site-1.com/。我现在需要查看这个目录下的内容,这样每个网站的命令并不相同,而我却需要批量执行:

ls /var/www/site-1.com/httpdocs
ls /var/www/site-2.com/httpdocs
ls /var/www/site-3.com/httpdocs
ls /var/www/site-4.com/httpdocs
ls /var/www/site-5.com/httpdocs

这时,我就可以使用刚才定义的变量来解决这个问题:

ansible group-all -a 'ls /var/www/{{ domain }}/httpdocs

这里我就使用了{{ domain }}这个变量来代替这些内容。

注意到,上面我的命令使用了group-all做为hosts。group-all是在hosts文件中定义的一个组。

Inventory中,组的定义和一般的ini文件相同,使用[ ]作为标识。组的内容可以是服务器也可以是其他组(也就是说,组与组之间可以嵌套),如果是嵌套,则需要加上”:children”进行说明。

最后,可以使用”:var”为组成员添加额外的属性。例如最后的:

[group-b:vars]
zivers=false

就为group-b中的所有成员添加了一个属性zivers,在命令中就可以使用这个属性了。调用方法依然是{{ zivers }}。

自动化运维工具Ansible使用(一)

简介

Ansible是一个使用Python语言的自动化运维工具。集合了众多运维工具,例如fabric, saltstack等的优点,实现了批量操作系统配置、批量程序的部署、批量运行命令等功能。

Ansible可以以两种方式运行:ad-hoc,playbooks。

  • ad-hoc(命令)模式和fabric非常类似,批量远程执行SSH命令。一些简单的命令可以用这个方式进行分发。例如:
ansible all -a 'date'

这条命令将在所有配置了的服务器上执行date命令并返回命令的返回值。

ad-hoc命令的典型格式是:

ansible hosts -m module -a 'command'
  • playbooks(剧本)模式则可以执行复杂的工作。将需要的任务用yaml格式写在文件中,然后执行。例如:
# test.yml
---
- hosts: 123.123.123.123
  remote_user: root
  tasks:
    - name: test connection
      ping:
      remote_user: root

然后执行:

ansible-playbook test.yml -f 10

即在所有服务器上执行ping这个操作。(“-f 10”表示并发10)

请注意,Ansible的命令分发基于SSH链接,所以虽然是C/S架构,但并不需要安装客户端。另外Ansible的分发可以并行执行。

安装

在Ubuntu上可以使用两种方式安装:

  • pip(不建议安装在virtualenv虚拟环境中):
sudo pip install ansible
  • apt
sudo apt-get install software-properties-common
sudo apt-add-repository ppa:ansible/ansible
sudo apt-get update
sudo apt-get install ansible

准备工作

为了使Ansible能够更加安全方便的执行,将Server的SSH Public Key添加到Clients的Authorized-Keys文件中。使得从Server端使用SSH登录到Client端时无需使用明文密码。这属于运维的基础知识,这里就不仔细说明了。

参考资料:Ansible Documentation

思考:美国ABPIA为何打击中国跨境电商

近日,美国ABPIA又开始了新一轮的“攻击”,对象依旧是中国跨境电商婚纱企业。据说这次有3000多家独立网站被告,他们的网站域名被强制转移、资金被强行冻结……总之,哀鸿遍野。

这不是第一次了!

在《揭秘跨境电商》一书中,我这样写到:

2012年,为了应对来自中国的假冒伪劣婚纱礼服产品,美国婚纱与礼服行业协会ABPIA成立,其主要任务就是利用法律手段打击销售这些产品的中国外贸电商网站。同年,美国最大的婚纱连锁店David’s Bridal起诉了包括米兰网在内的上百家电商网站,原因是这些网站涉嫌抄袭或盗用其婚纱设计作品等。2012年4月6日,米兰网域名milanoo.com的注册人被更新为David’s Bridal。虽然没有得到确切消息的证实,但外界普遍认为米兰网的域名可能是因David’s Bridal的起诉而被强制转移……

曾经拿到红杉资本千万美金投资的米兰网,是直接被美国同行David’s Bridal控告的。而很多中国出口跨境电商婚纱网站,则一直处于ABPIA的严密监视中。作为美国婚纱与礼服行业的协会组织,ABPIA成立于2012年7月,致力于打击各类危害婚纱礼服行业的侵权行为。它的发起人(也是ABPIA主席)Stephen Lang,拥有自己的婚纱礼服公司Mon Cheri Bridals。在他的带领下,ABPIA规模不断壮大,已拥有超过400名会员,涵盖婚纱礼服生产企业、零售企业和设计师等。

我们不妨来看下过去几年ABPIA的“战果”:

  • 2014年2月,ABPIA取得里程碑的胜利。法院对1100多家侵权婚纱网站签发PreliminaryInjunction Order,内容包括:第一,冻结所有这些网站在第三方支付公司的账户资金;第二,关闭这些网站域名。在美国,Preliminary Injunction是法院在最终判决前签发的一种禁止令。这种禁止令签发后,几乎意味着ABPIA将在随后的判决中获胜。
  • 2013年1月,在听取了ABPIA的证词后,法院签发了正式禁止令(Formal Injunction)。该禁止令要求被告(侵权网站)停止侵权活动,同时要求产业链相关的公司停止协助这些侵权活动,包括VeriSign/Go-Daddy等域名管理组织、Goggle等线上广告公司、PayPal/Visa/MasterCard等支付公司、FedEx/UPS/DHL等运输公司。这也是为什么PayPal主动冻结了相关网站的账户资金,Go-Daddy主动转移了相关网站域名。
  • 2012年年底,ABPIA指控了45家侵权网站(绝大部分是中国出口跨境电商婚纱网站)。法院立即签发了TRO(临时禁止令),意味着法院对此特别重视,在没有做任何听证会(hearing)的情况下就支持ABPIA。
  • 2015年7月,ABPIA得到法院的最终判决:超过1500家的侵权网站将被永久性关闭并转移给ABPIA;之前所冻结的资金约220万美元(大部分在PayPal账户中)归ABPIA所有。这个判决终结了ABPIA长达两年多的诉讼,也彻底摧毁了涉案中国婚纱网站拿回域名和PayPal账户资金的任何希望。

然而,这事情远没有结束。ABPIA主席Stephen Lang在拿到法院判决后说:

This decision is a landmark for ourorganization,but it’s only the first step in an aggressive path we are takingto cut off the flow of counterfeit formalwear products into this country.

于是,一份更长的名单出来了。这一次,3000多个网站,如果不出意外,将同样经历Preliminary Injunction Order到最终判决的法律程序。有了之前的案例判决,这次审判的速度或许会更快。ABPIA将笑到最后。

回到题目,ABPIA为何如此“残忍”地打击中国跨境电商?上文说了这么多,答案已经非常明确:中国商家侵权了。

在美国,一件设计师品牌的婚纱的售价在1500美元左右。中国商家模仿后,价格低至300美元。而即使是卖300美元,中国商家的利润依旧高的惊人,因为:

  1. 使用B2C跨境电商的销售方式,中间流通成本很低;
  2. 相对于美国,中国的劳动力成本依旧存在;
  3. 中国商家没有设计投入,甚至连网站模特图片都是从其他网站拷贝的。对于婚纱行业来说,第三点省下的成本或许最多。要知道,对于一个独立网站来说,聘请知名设计师设计婚纱的费用和聘请外国模特拍摄网站图片的费用都在千万元以上。

这种赤裸裸的侵权,造成了中美婚纱企业竞争的严重不公平局面。在中国外贸婚纱网站赚得盆满钵满的背后,美国婚纱礼服行业损失巨大。根据ABPIA提供的数据,2012年约有50万至60万的侵权婚纱商品被销售至北美,给行业带来的损失约为3亿美元。换位思考一下,假如你是一家美国婚纱企业,你能不生气?

这一类的生意,我们中国商家已经做的太多了。短期来看,企业主挣得了财富,国家挣得了外汇和就业人口。然而,长期来看,这是中国制造业的毒瘤。因为:

第一,它影响了中国制造企业的信誉。一家企业的侵权造假或许对一国企业的整体信誉不会有多大的影响,但一批企业的侵权造假将贬低整个中国企业的信誉。当整体信誉受损后,企业将莫名其妙地增加很多无形的成本:你需要花费更多的营销费用来改善自身形象,你需要花费更多的时间来解释自己与其他中国产品的不同……有些中国企业则通过雇佣外国员工或设立外国公司,以隐去“中国背景”,避免任何不必要的麻烦。这难道不是中国制造的悲哀?

第二,它阻碍了中国制造企业的创新。当一家企业可以在市场中轻易挣钱的时候,它是没有动力去创新的。中石油、中石化、中移动等等无一不是如此。同样,如果中国的这些婚纱企业可以通过不公平竞争轻易挣得利润,它们还会去改变生产方式、创新管理模式吗?如果中国政府允许这样的企业长期存在,那么,中国外贸的转型升级将毫无希望。要知道,大多数情况下,创新需要的是反向推动力,而不是政府庇护下的正向牵引力。

从我的观察来看,中国政府已经开始从长期发展的角度来对待这些问题了。在去年12月,李克强总理三天内两次强调对知识产权的保护,不仅在国务院常务会议上要求确定改革完善知识产权制度的措施,而且要求在“十三五”规划中添加知识产权保护的相关内容。从“大众创业,万众创新”的角度看,保护知识产品一定是实现真正创新的前提。

因此,对于美国ABPIA起诉中国数千家婚纱网站,我并不痛心。无风不起浪,如果中国婚纱企业问心无愧,ABPIA也没必要费劲折腾这么多年。据我了解,在前一批被判决的一千多家婚纱网站中,只有两家企业进行了回应,分别是www.dressespro.com和www.trendress.com。这两家公司委托律师寻求场外和解(confidential settlements),都遭遇了拒绝。也就是说,ABPIA将利用严厉的法律手段惩处一切侵害他们知识产权的企业。

全球经济的一体化已经发展到这一步了,中国外贸企业难道不该清醒了?!

(本文作者李鹏博系上海哦啦网络科技有限公司总裁,业内畅销书《揭秘跨境电商》作者,中国贸促会跨境电商水平测试专家委员会副主任委员,马述忠工作室创始成员。)

使用gevent来提高Python的并发效率

gevent是一个基于libev的并发库。它为各种并发和网络相关的任务提供了整洁的API。在gevent中用到的主要模式是Greenlet, 它是以C扩展模块形式接入Python的轻量级协程。 Greenlet全部运行在主程序操作系统进程的内部,但它们被协作式地调度。

为了提高服务器的IO效率,可以在uwsgi服务上加载gevent框架来实现并发。而且gevent提供了一个异常方便了monkey模块,可以在不修改原来的使用python标准库函数的程序的情况下,将程序转换成可以使用gevent框架的异步程序。

由于无需修改代码,部署起来十分简单。这里是在virtualenv环境下配置的:

(env) pip install gevent

然后在uwsgi的app配置文件etc/uwsgi/apps-available/your_app.ini中加入下面的配置即可:

gevent = 100
gevent-monkey-patch = True

重启uwsgi生效

sudo service uwsgi restart

参考资料:

 

 

一次Django站点部署纪要

公司服务器要迁移到更高的配置,所以需要升级服务器。正好之前的服务器上的系统和各种软件版本都已经过时,于是就一并升级了。在过程中还是遇到蛮多坑的。

由于Django版本升级过程复杂,还要涉及到很多代码的修改,所以这次配置还是维持了古老的1.6.5版本不变。

过程摘录如下:

tools

sudo apt-get install sysv-rc-conf
sudo apt-get install git
sudo apt-get install python-dev

Nginx

sudo vim /etc/apt/sources.list.d/nginx.list

add follow lines in /etc/apt/sources.list.d/nginx.list

deb http://nginx.org/packages/mainline/ubuntu/ trusty nginx
deb-src http://nginx.org/packages/mainline/ubuntu/ trusty nginx
wget -q -O- http://nginx.org/keys/nginx_signing.key | sudo apt-key add -
sudo apt-get update
sudo apt-get install nginx
sudo vim /etc/nginx/conf.d/proj_name.conf

/etc/nginx/conf.d/proj_name.conf

server {
    listen 80;
    server_name www.domain_name.com;
    charset utf-8;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /s/ {
        alias /srv/www/site_dir/static/;
    }

    location / {
        proxy_read_timeout      300;
        include       uwsgi_params;
        uwsgi_pass    unix:///srv/www/proj_name/proj_name.sock;
    }

    gzip            on;
    gzip_min_length 1000;
    gzip_proxied    expired no-cache no-store private auth;
    gzip_types      text/plain text/css application/x-javascript;
}

MySQL

# apt-get install software-properties-common
sudo add-apt-repository -y ppa:ondrej/mysql-5.7
sudo apt-get update
sudo apt-get install mysql-server
# then add user and restore databases

redis

sudo add-apt-repository ppa:chris-lea/redis-server
sudo apt-get update
sudo apt-get install redis-server

code

cd /srv/www/
git clone repo site_dir
# modify settings.py
# DATABASE fields
# Redis fields
"LOCATION": "redis://127.0.0.1:6379/1",

virtualenv

sudo apt-get install python-pip
pip install virtualenv

# enable virtrualenv
cd /srv/www/site_dir
virtualenv env
source /env/bin/activate

django

#env
pip install django==1.6.5

modules

# env
pip install django-redis
pip install django-robots
pip install django-crontab==0.6.0
pip install django-contrib-comments==1.6.1
pip install django-recaptcha
pip install south
pip install django-social-auth
pip install django-widget-tweaks
pip install unidecode

MySQL-Python

sudo apt-get install python-dev
sudo apt-get install libmysqlclient-dev
pip install mysql-python

PIL

sudo apt-get build-dep python-imaging
sudo apt-get install libjpeg8 libjpeg62-dev libfreetype6 libfreetype6-dev
pip install PILLOW

uWSGi

pip install uwsgi
cd /etc/uwsgi/apps-available
vi proj_name.ini

/etc/uwsgi/apps-available/proj_name.ini

[uwsgi]
project = proj_name
base = /srv/www

chdir = %(base)/%(project)
home = %(base)/%(project)/env
module = app_name.wsgi:application

master = true
processes = 20

socket = %(base)/%(project)/%(project).sock
#socket = 127.0.0.1:1234
chmod-socket = 664
#chmod-socket = 666
vacuum = true
logto = %(base)/%(project)/logs/uwsgi.log
enable-threads  = true
plugins = python
uid = www-data
gid = www-data
chown-socket = www-data
cd /etc/uwsgi/apps-enabled
ln -s ../apps-available/proj_name.ini proj_name.ini

locale警告信息修复

在维护系统后,忽然出现了这样一条和语言相关的系统警告信息:locale: Cannot set LC_ALL to default locale: No such file or directory。在进行各种操作的时候时不时的就跳出来。

于是,我开始查错,首先看看系统的locale配置:

root@localhost:~# locale
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=en_US.UTF-8
LANGUAGE=en_US:
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=zh_CN.UTF-8
LC_TIME=zh_CN.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=zh_CN.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=zh_CN.UTF-8
LC_NAME=zh_CN.UTF-8
LC_ADDRESS=zh_CN.UTF-8
LC_TELEPHONE=zh_CN.UTF-8
LC_MEASUREMENT=zh_CN.UTF-8
LC_IDENTIFICATION=zh_CN.UTF-8
LC_ALL=

提示LC_ALL无法找到。看配置中确实这一栏为空。然后查询一下系统已经安装的locale:

root@localhost:~# locale -a
C
C.UTF-8
en_AG
en_AG.utf8
en_AU.utf8
en_BW.utf8
en_CA.utf8
en_DK.utf8
en_GB.utf8
en_HK.utf8
en_IE.utf8
en_IN
en_IN.utf8
en_NG
en_NG.utf8
en_NZ.utf8
en_PH.utf8
en_SG.utf8
en_US.utf8
en_ZA.utf8
en_ZM
en_ZM.utf8
en_ZW.utf8
POSIX

发现了一个明显的问题,配置中的zh_CN.UTF-8并没有安装。于是安装:

sudo locale-gen zh_CN.UTF-8

问题解决。

网上还有一些其他的解决方法和问题原因说明,我没尝试过但是应该有参考价值:

http://www.360doc.com/content/14/0428/16/17044736_372964025.shtml

http://askubuntu.com/questions/162391/how-do-i-fix-my-locale-issue

EC2(AWS)配置升级记要

随着公司网站的请求量不断增长,作为公司图片服务器的EC2的负载日益增高。load average经常能够达到>3的水平。CPU负载也经常会达到>80%,所以需要对服务器进行一些配置上的升级了。

20160405111140

4月1日进行的配置升级,升级前CPU的占用率甚至到达过97%

于是,我们就开始跟着官方文档的说明开始升级AWS配置。在这个文档里,主要说了三件事:

  1. 升级配置需要关机
  2. EBS volume(EBS卷)才可以直接升级,如果是instance store volume(实例存储卷),则升级的同时会抹掉其中的数据,所以要事先备份
  3. 如果是Public IP(公共IP),则实例升级后将会更换IP;如果是Elastic IP(弹性IP),则升级后可以重新将实例与弹性IP绑定,这样IP就不会更换。

先解释以下以上信息中的几个名词:

卷类型:EBS卷,实例存储卷。这个是在创建实例的时候确定的,一般来说像AWS提供的T2, M3, M4, C2 等等这些实例要么是默认EBS卷,要么是强制EBS卷,所以都可以直接升级。具体确定可以在EC2控制面板页面左侧选项中选择INSTANCES -> Instances -> 选中你的实例 > Description,这里的信息中有一条是Root device type,如果显示ebs,则为EBS卷。

IP类型:公共IP,弹性IP。实例创建后,都会自动分配一个公共IP,所以不做配置的情况下是只有公共IP的。如果购买了弹性IP,则需要在升级实例后在NETWORK & SECURITY -> Elastic IPs页面重新绑定IP和实例。

对于这次升级,公司的服务器是使用的M3.large型号,没有配置弹性IP,额外挂载了一个500G的外部存储磁盘。考虑到实际情况,最后的升级方案是:

  1. 将实例从M3.large升级到M3.xlarge
  2. 升级后IP被替换,需要重新在CloudFlare中绑定IP
  3. 重新挂载500G的外部存储磁盘

20160405120400

下面开始执行升级:

首先,为了防止在备份过程中出现意外导致数据丢失,先对数据进行备份(磁盘快照)。选择ELASTIC BLOCK STORE -> Volume,选择系统磁盘创建快照。由于500G的磁盘只是卸载和挂载操作,所以就不备份了。

备份完成后,在Instances中选中我们需要升级的实例,Actions -> Instance State ->  Stop。关机后,再选择Actions -> Instance Settings -> Change Instance Type,选择需要升级到的实例配置,重新启动。这个选项在机器没有关机时候是灰色不可选状态,所以升级需要关机。

20160405122229

重新启动后,实例升级就已经完成了。现在还需要做两件事:1. 挂载外部存储磁盘;2. 修改DNS解析中使用的服务器IP。

我们先进行磁盘挂载,在ELASTIC BLOCK STORE -> Volumes 中选择我们的外部存储磁盘(500G),执行Actions -> Detach Volume。执行完后,外部存储磁盘便取消了与升级前实例之间的挂载。

20160405122712

然后,我们执行Actions -> Attach Volume,在弹出的操作框中,选择我们升级后的实例ID进行挂载。完成后,我们就可以在服务器上看到这个磁盘了。

20160405123048

图中的/dev/xvdf即外部磁盘

上图是后截的,所以这里显示已经被mount到了/data目录,刚挂载上的时候应该是空的。

然后我们删除掉之前的挂载链接,重新挂载:

# 这里直接是用root用户演示,实际操作中加sudo执行
rm -rf /data
mkdir /data
mount /dev/xvdf /data

这样外部磁盘挂载就完成了。服务器已经Ready。最后在DNS解析处更改一下IP地址绑定就完成了整个升级操作。