NodeJS开发环境配置

安装nvm、node和npm

nvm是nodejs的多版本管理利器,node是nodejs的解释器,npm是nodejs的包管理工具。

1
2
3
4
5
6
# ubuntu安装之后会自动添加配置到~/.profile,可以直接cut到自己喜欢的比如~/.bashrc
$ wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.25.4/install.sh | bash
$ nvm install 0.12.4
$ nvm use 0.12.4
$ nvm alias default 0.12.4
$ node --version

由于网速问题,国内可以使用taobao的npm镜像

1
2
$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ cnpm install [package name]

Build工具:grunt/gulp, bower, yeoman

自动编译、依赖管理、自动化测试、打包发布、项目模板工具、文档自动生成…… ,这些基本上属于每个项目(不论语言差异)构建的标配,javascript目前也配齐了,于是乎开发效率直线提升:)

  • grunt是javascript的自动化构建工具,类似于Java的gradle/maven/ant,Python的tox,C/C++的make和scons等。
  • gulp也是构建工具,它采用类似jQuery的流式配置简化了任务的编写。
  • bower是一个依赖管理的工具,可以自动化安装bootstrap、angulajs、jquery等包,解决他们之间的依赖关系。
  • yeoman是一个生成项目框架scaffolding工具,遵循Convention over Configuration,用过RoR或者Django的应该都知道快速开发形式.d可以配合generator-webapp、generator-angular等模板快速生成项目结构。

安装上述工具到本地:

1
2
3
$ cnpm install -g yo bower grunt-cli gulp
# 安装yo项目模板
$ cnpm install -g generator-angular

生成一个HelloWorld

1
2
3
4
5
$ mkdir -p ~/dev/nodejs/helloworld && cd $_
$ yo angular
$ grunt serve
$ grunt test
$ grunt

这样就把一个前段项目的框架生成完毕了,可以进入开发阶段。

编辑器:Atom

使用chrome和nodejs开发的Atom,几个月前看还是离sublimetext挺远,现在看几乎快要完全超越!——ubuntu下的中文算是个麻烦事,自定义以后还凑合

参考配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@font-family: "DejaVu Sans Mono", "WenQuanYi Zen Hei";

.tree-view, .title, .current-path, .tooltip {
font-family: @font-family;
}

.terminal {
font-family: @font-family !important;
div {
white-space: nowrap;
}
}

// style the background and foreground colors on the atom-text-editor-element
// itself
atom-text-editor {
font-family: @font-family;
}

// To style other content in the text editor's shadow DOM, use the ::shadow
// expression
atom-text-editor::shadow .cursor {
font-family: @font-family;
}

.markdown-preview {
font-family: @font-family;
atom-text-editor {
font-family: @font-family;
}
}

Atom也有大量的插件可以使用,比如把hexo集成进来,可以少开个term了:)

  • VIM开启了编辑器的多模式状态,让敲键盘更尽兴;
  • SublimeText方便了编辑器的DIY,性能也很好;
  • Atom让Web开发更彻底,它本身就是个基于浏览器内核的工具;此外,它来自于Github:)

参考

  1. Atom Offical
  2. 文泉驿官网
  3. Taobao NPM
  4. Grunt Offical
  5. Bower Offical
  6. Yeoman Offical

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate # 'hexo g' for short

More info: Generating

Deploy to remote sites

1
$ hexo deploy # 'hexo d' for short

More info: Deployment

Useful hexo plugins

1
$ npm install hexo-generator-feed --save
1
$ npm install hexo-generator-sitemap --save

Put the whole source folder into git

1
...
# Dependency directory
node_modules
# hexo generated files, deployed to github.io pages
public
.deploy_git
...

Some notes about doc syntax

  1. use <!-- more --> to hide blog details below.
  2. use toc: false to hide the topic menu about the blog.
  3. use mathjax: true to enable LaTex math prents.

Awsome markdown editors:

Hadoop系列

大数据依旧在热炒,hadoop虽然不是唯一的代表,却也是各家必谈之资本,玩玩大数据。

通过ambari部署hadoop集群

在cloudlab119-123上部署集群,其中119作为ambari server控制端。

安装部署:

1
2
3
4
5
$ cd /etc/yum.repo.d
$ wget http://public-repo-1.hortonworks.com/ambari/centos6/2.x/updates/2.0.0/ambari.repo
$ yum install -y ambari-server
$ ambari-server setup # 按指示操作即可
$ ambari-server start # *:8080端口

配置,打开http://cloudlab119:8080,默认密码admin:admin。在cloudlab119上对所有节点做免密码认证:

1
2
3
$ vi /etc/hosts
$ ssh-keygen -t rsa -P '' -f ~/.ssh/hadoop119
$ for i in {119,120,121,122,123}; do ssh-copy-id -i ~/.ssh/hadoop119.pub root@cloudlab$i; done;

按提示建议操作,如ntpd、iptables、关闭THP:

1
2
3
4
5
$ chkconfig ntpd on
$ service ntpd start
$ service iptables stop # 生产环境中参照文档把对应端口打开
$ chkconfig iptables off
$ echo never > /sys/kernel/mm/redhat_transparent_hugepage/enabled # 关闭THP,如果提示文件系统readonly,重启机器再执行

按提示一步步操作即可,但中间如果出现下载包失败,则会导致整个安装失败,所以最好提前把官方源同步到本地,做镜像后安装。

1
2
3
4
5
6
7
8
9
10
$ yum install -y yum-utils createrepo
$ mkdir -p /var/www/html/ambari/centos6 && cd $_
$ reposync -r Updates-ambari-2.0.0
$ createrepo Updates-ambari-2.0.0
$ mkdir -p /var/www/html/hdp/centos6 && cd $_
$ reposync -r HDP-2.2
$ reposync -r HDP-UTILS-1.1.0.20
$ createrepo HDP-2.2
$ createrepo HDP-UTILS-1.1.0.20
$ vim /etc/yum.repo.d/ambari.repo # 修改baseurl指向本地镜像

安装之后,发现某几台服务器内存占用率非常高,可以通过Ambari增加Host节点,然后迁移部分的组件到新的机器。

通过Docker部署集群

可以自己做docker image,简单起见可以先用sequenceiq的image。

单节点测试:

1
2
3
4
5
$ docker pull sequenceiq/hadoop-docker:2.7.0
$ docker run -it sequenceiq/hadoop-docker:2.7.0 /etc/bootstrap.sh -bash
$ cd $HADOOP_PREFIX # /usr/local/hadoop
$ bin/hadoop jar share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.0.jar grep input output 'dfs[a-z.]+' # 统计key出现次数
$ bin/hdfs dfs -cat output/* # 在用户Home下的output下

多节点部署,可以用ambari镜像,准备好blueprint.json:

1
{ "host_groups" : [
    { "name" : "host_group_1",
      "components" : [
        { "name" : "ZOOKEEPER_SERVER" },
        { "name" : "ZOOKEEPER_CLIENT" },
        { "name" : "AMBARI_SERVER" },
        { "name" : "HDFS_CLIENT" },
        { "name" : "NODEMANAGER" },
        { "name" : "MAPREDUCE2_CLIENT" },
        { "name" : "APP_TIMELINE_SERVER" },
        { "name" : "DATANODE" },
        { "name" : "YARN_CLIENT" },
        { "name" : "RESOURCEMANAGER" } ],
      "cardinality" : "1" },
    { "name" : "host_group_2",
      "components" : [
        { "name" : "ZOOKEEPER_SERVER" },
        { "name" : "ZOOKEEPER_CLIENT" },
        { "name" : "SECONDARY_NAMENODE" },
        { "name" : "NODEMANAGER" },
        { "name" : "YARN_CLIENT" },
        { "name" : "DATANODE" }],
      "cardinality" : "1" },
    { "name" : "host_group_3",
      "components" : [
        { "name" : "ZOOKEEPER_SERVER" },
        { "name" : "ZOOKEEPER_CLIENT" },
        { "name" : "NAMENODE" },
        { "name" : "NODEMANAGER" },
        { "name" : "YARN_CLIENT" },
        { "name" : "DATANODE" }],
      "cardinality" : "1" } ],
  "Blueprints" : {
    "blueprint_name" : "blueprint-c1",
    "stack_name" : "HDP",
    "stack_version" : "2.2" } }

可以在ambari server上查找:http://172.17.0.13:8080/api/v1/blueprints

创建集群:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ docker pull sequenceiq/ambari:1.7.0
$ curl -Lo .amb https://github.com/sequenceiq/docker-ambari/raw/master/ambari-functions && . .amb
$ amb-start-cluster 3 # 注意默认使用的是sequenceiq/ambari:1.7.0-warmup,可以修改 .amb
$ amb-shell # 又启了个container
ambshell > host list
ambshell > blueprint add --url http://172.17.42.1/bp.json # 准备好json保存到某个能访问的http服务器
ambshell > cluster build --blueprint blueprint-c1
ambshell > cluster assign --hostGroup host_group_1 --host amb0.mycorp.kom
ambshell > cluster assign --hostGroup host_group_2 --host amb1.mycorp.kom
ambshell > cluster assign --hostGroup host_group_3 --host amb2.mycorp.kom
ambshell > blueprint show --id blueprint-c1
ambshell > cluster preview
ambshell > cluster create

中间安装过程如果出现失败,可以到ambari-server上看详细情况,当然也可以直接向之前方式一样,通过GUI安装部署;或者开着浏览器看amb-shell执行过程。
如果实在VM上远程使用,可以在docker所在机器上做NAT映射直接访问:

1
$ iptables -t nat -A PREROUTING -d 10.101.29.26 -p tcp --dport 8000 -j DNAT --to-destination 172.17.0.20:8080 # 可以用端口转发直接远程浏览器访问

做个简单的测试:

1
2
3
4
$ docker exec -it 9572938bb253 /bin/bash # 进入到容器内
bash # su hdfs # 切换到HDFS的超级用户
bash # hdfs dfsadmin -report
bash # hdfs dfs -ls /

HDFS

HDFS由NameNode、SNameNode和DataNode组成,HA的时候还有另一个NameNode(StandBy)。SNameNode专门从NameNode获取FSImage和Edits,为NameNode合并生成新的FSImage。

HDFS有个超级用户,就是启动NameNode的那个linux账号。

基本Shell操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ ps -elf | grep NameNode #看看哪个账号启动了NameNode,那个账号就是超级用户
$ su -s /bin/sh -c 'hadoop fs -ls /' hdfs #使用超级账号执行命令
$ su hdfs #切换到超级账号
$ useradd -G hdfs promise
$ hdfs dfs -mkdir /user/promise
$ hdfs dfs -chown promise:hdfs /user/promise
$ hdfs dfs -ls /user/
$ hdfs dfs -mkdir -p /user/promise/dev/hello
$ hdfs dfs -chmod -R 777 /user/promise/dev
$ hdfs dfs -ls /user/promise/dev/
$ echo “helloworld” > hello.txt
$ hdfs dfs -put hello.txt /user/promise/helloworld.txt
$ hdfs dfs -put - hdfs://192.168.182.119/user/promise/test.txt # 从stdin输入,Ctrl-D结束
$ hdfs dfs -cat hdfs://192.168.182.119/user/promise/test.txt
$ hdfs dfs -cat /user/promise/helloworld.txt
$ hdfs dfs -get /user/promise/helloworld.txt hello2.txt
$ hdfs dfs -tail -f /user/promise/helloworld.txt # 查看追加的文件写入
$ hdfs dfs -appendToFile - /user/promise/helloworld.txt

webhdfs操作

启用了webhdfs之后(hdfs-sitedfs.webhdfs.enabled=true),可以通过HTTP方式访问HDFS:

1
2
3
$ curl -i "http://192.168.182.119:50070/webhdfs/v1/user/promise/?op=LISTSTATUS"
$ curl -i -L "http://192.168.182.119:50070/webhdfs/v1/user/promise/helloworld.txt?op=OPEN" # 通过重定向到DataNode获取文件
$ curl -i -X PUT "http://192.168.182.119:50070/webhdfs/v1/user/promise/hello?user.name=promise&op=MKDIRS"

通过HTTP方式创建和追加文件都需要通过2阶段实现:先在NameNode上创建,获得DataNode URI后再向URI上传文件。

1
2
$ curl -i -X PUT "http://192.168.182.119:50070/webhdfs/v1/user/promise/hello/hi.txt?op=CREATE"
$ curl -i -X PUT -T hi.txt "http://192.168.182.119:50075/webhdfs/v1/user/promise/hello/hi.txt?user.name=promise&op=CREATE&namenoderpcaddress=192.168.182.119:8020&overwrite=false" # 需要声明账号

查看离线的FSImage和Edits:

1
2
3
4
$ hdfs oiv -p XML -i /hadoop/hdfs/namenode/current/fsimage_0000000000000018887 -o fsimage.xml # 生成XML格式
$ hdfs oiv -i /hadoop/hdfs/namenode/current/fsimage_0000000000000018887 # 在线查看形式
$ hdfs dfs -ls webhdfs://127.0.0.1:5978/
$ hdfs oev -i /hadoop/hdfs/namenode/current/edits_0000000000000000001-0000000000000005150 -o edits.xml # 查看edits

使用Java API

Java在大型软件系统开发中有利于更清晰的架构设计和团队分工,而且有大量的第三方优质框架可用;可惜在命令里写HelloWorld很啰嗦,方便起见,直接用maven生成基本文件结构。

1
2
3
4
5
6
7
$ export PATH=$PATH:/opt/apache-maven-3.3.3/bin/
$ export JAVA_HOME=/usr/jdk64/jdk1.7.0_67/
$ export MAVEN_OPTS="-Xms256m -Xmx512m"
$ mkdir ~/dev && cd $_
$ mvn archetype:generate -DgroupId=org.tecstack -DartifactId=hellohdfs -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false
$ cd hellohdfs && mvn package
$ vim ./src/main/java/org/tecstack/App.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package org.tecstack;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;

/**
* Hello HDFS
*
*/

public class App
{

public static void main( String[] args )
{

try {
Configuration conf=new Configuration();
FileSystem hdfs=FileSystem.get(conf);
Path dst =new Path("/user/promise/helloworld.txt");
FileStatus files[]=hdfs.listStatus(dst);
for(FileStatus file:files) {
System.out.println(file.getPath());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

pom.xml增加hadoop依赖包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

<modelVersion>4.0.0</modelVersion>
<groupId>org.tecstack</groupId>
<artifactId>hellohdfs</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>hellohdfs</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

打包运行:

1
2
3
4
5
6
7
$ mvn package
$ mkdir -p src/main/resources
$ scp cloudlab119:/etc/hadoop/conf/core-site.xml src/main/resources
$ mvn exec:java -Dexec.mainClass="org.tecstack.App" -Dexec.cleanupDaemonThreads=false
$ mvn dependency:copy-dependencies # 导出依赖的包
$ mvn resources:resources # 导出资源
$ mvn eclipse:eclipse # 生成eclipse工程文件,.project, .classpath

关于maven的exec插件,也可以通过配置pom.xml的plugin的参数简化执行:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.4.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>org.tecstack.App</mainClass>
<cleanupDaemonThreads>false</cleanupDaemonThreads>
</configuration>
</plugin>
</plugins>
</build>

之后运行只需要mvn exec:java,如果需要定制更多参数,比如JVM内存,单独启动进程执行等,可以使用exec:exec插件,具体看[codehaus官网][http://mojo.codehaus.org/exec-maven-plugin/usage.html]。

HUE交互界面

Hue是一个基于Django开发的webapp,通过WebHDFS或HttpFS的一种访问HDFS的数据,在HDFS HA部署方式中只能使用HttpFS。
如通过WebHDFS方式访问,需要修改hdfs-site.xml

1
2
3
4
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>

修改core-site.xml使Hue账号可以代理其他用户:

1
2
3
4
5
6
7
8
<property>
<name>hadoop.proxyuser.hue.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hue.groups</name>
<value>*</value>
</property>

修改完参数需要重新启动HDFS,通过Ambari操作很方便。

1
2
3
$ yum install -y hue hue-server
$ vim /etc/hue/conf/hue.ini # 修改各个服务的URI,可以通过Ambari看到服务所在的服务器位置;修改默认监听端口不与其它冲突
$ service hue start

默认登陆账号为admin:admin,可以通过hdfs为该账号创建一个专用的文件夹/user/hue进行测试,正式环境中可以考虑与其他身份系统对接。

1
2
3
4
$ su hdfs
$ hdfs dfs -mkdir /user/hue
$ hdfs dfs -chown -R admin:hadoop /user/hue
$ hdfs dfs -ls /user/

导入数据到HBase

通过hcat建立数据库表结构,通过pig导入数据。

建立数据文件data.tsv:

1
row1    c1    c2
row2    c1    c2
row3    c1    c2
row4    c1    c2
row5    c1    c2
row6    c1    c2
row7    c1    c2
row8    c1    c2
row9    c1    c2
row10    c1    c2

准备建表DDL,simple.ddl

1
CREATE TABLE simple_hcat_load_table (id STRING, c1 STRING, c2 STRING)
STORED BY 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'
WITH SERDEPROPERTIES ( 'hbase.columns.mapping' = 'd:c1,d:c2' )
TBLPROPERTIES (
'hbase.table.name' = 'simple_hcat_load_table'
);

准备导入脚本,simple.bulkload.pig

1
A = LOAD 'hdfs:///tmp/data.tsv' USING PigStorage('\t') AS (id:chararray, c1:chararray, c2:chararray);
-- DUMP A;
STORE A INTO 'hbase://simple_hcat_load_table' USING org.apache.hive.hcatalog.pig.HCatStorer();

把文件放到HDFS,建表,导入:

1
2
3
4
$ su - hdfs
$ hdfs dfs -put data.tsv /tmp/
$ hcat -f simple.ddl
$ pig -useHCatalog simple.bulkload.pig

通过Hue的HCat可以查看数据库和表数据,也可以通过HBase Shell:

1
2
3
4
5
$ su - hdfs
$ hbase shell
hbase > list # 查询所有表
hbase > scan 'simple_hcat_load_table' # 查询所有数据
hbase > describe 'simple_hcat_load_table'

也可以通过Hive Shell:

1
2
3
4
5
$ su - hdfs
$ hive
hive > show tables;
hive > select count(*) from simple_hcat_load_table;
hive > desc simple_hcat_load_table;

如果建表过程中出现类似java.lang.NoClassDefFoundError: org/apache/hadoop/hbase/HBaseConfiguration如下的找不到类情况,检查hcat的配置文件是否有HBase目录:vim /usr/bin/hcat

1
2
3
...
export HBASE_HOME=/usr/hdp/2.2.4.2-2/hbase
...

此外,由于Hue集成的HCat调用的是Hive下的hcat,需要在/etc/hive-hcatalog/conf/hcat-env.sh中指定HBASE_HOME:

1
2
3
...
HBASE_HOME=${HBASE_HOME:-/usr/hdp/current/hbase-client}
...

pig在使用MapReduce模式执行时,可以根据log打开MR Job跟踪,如果也出现java.lang.NoClassDefFoundError类错误,说明mapreduce服务的classpath有缺漏,可以通过Ambari修改MapReduce服务Advanced mapred-site配置中的mapreduce.application.classpath,比如出现HBase相关类找不到,则添加HBase相关的库/usr/hdp/2.2.4.2-2/hbase/lib/*,然后重启MapReduce服务即可。

如果出现执行权限错误,需要检查如下配置是否存在:
hive-site.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<property>
<name>hive.security.metastore.authorization.manager</name>
<value>org.apache.hadoop.hive.ql.security.authorization.StorageBasedAuthorizationProvider
</value>
</property>
<property>
<name>hive.security.metastore.authenticator.manager</name>
<value>org.apache.hadoop.hive.ql.security.HadoopDefaultMetastoreAuthenticator
</value>
</property>
<property>
<name>hive.metastore.pre.event.listeners</name>
<value>org.apache.hadoop.hive.ql.security.authorization.AuthorizationPreEventListener
</value>
</property>
<property>
<name>hive.metastore.execute.setugi</name>
<value>true</value>
</property>

webhcat-site.xml:
正式环境中可以明确具体使用的账号名称,最小化权限。

1
2
3
4
5
6
7
8
<property>
<name>webhcat.proxyuser.hue.hosts</name>
<value>*</value>
</property>
<property>
<name>webhcat.proxyuser.hue.groups</name>
<value>*</value>
</property>

core-site.xml:

1
2
3
4
5
6
7
8
<property>
<name>hadoop.proxyuser.hcat.group</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hcat.hosts</name>
<value>*</value>
</property>

使用小结

Hadoop系架构经过引入YARN,真正实现了数据和应用的分离,在YARN架构上可以实现出了原来的MapReduce之外的更多App,比如流式处理、实时处理、图计算等,如此可以真正实现“把应用挪到数据旁边高效执行”,构建大数据平台。此外,做数据分析时可以发现周边工具很丰富,比如hue统一的综合管理界面、从数据文件识别结构并管理数据的hcat、数据处理高级语言pig、支持SQL的Hive,使得在Hadoop平台上做数据分析非常方便(可以参考这里)。
另外,对比YARN利用OS进程隔离分配资源之外,Mesos结合了Container技术实现容器隔离分配资源,以此实现更通用的框架(用各种语言写的各种计算框架)。当然YARN也进入了DCE,通过docker实现容器隔离。不过正式应用还是有待Linux kernel本身功能的成熟,以及docker之类管理工具的完善。
最后,数据处理优选python scikit-learn系工具,当大到一定程度,或需要多人同时工作时,hadoop系平台是个不错的选择。

参考

  1. Ambari Official Docs
  2. HDFS Official Doc
  3. Hadoop-docker from sequenceiq
  4. Ambari Docs on Apache
  5. Sample BluePrint
  6. 安装配置Hue
  7. 手动安装HDP
  8. Ambari Blueprint
  9. 导入数据到HBase
  10. Apache Mesos
  11. YARN DCE

安装部署

IP 角色
192.168.182.156 tgtd
192.168.182.157 client1
192.168.182.158 client2
1
2
3
$ yum install -y scsi-target-utils
$ chkconfig tgtd on
$ service tgtd start

配置方法2种:

  1. tgtadm,在线修改
  2. conf配置文件

在服务端增加一个Target

主要流程:

  1. 建立target
  2. 为target增加backstorage
  3. 配置客户端访问target的控制策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ tgtadm --lld iscsi --op new --mode target --tid 1 --targetname iqn.2015-05-04.org.tecstack.storage.tg1 # add target
# 用losetup映射块设备作为backstorage
$ mkdir /opt/tgtstorage
$ dd if=/dev/zero of=/opt/tgtstorage/disk0.img bs=1M count=5120
$ losetup -f /opt/tgtstorage/disk0.img # 映射为设备
$ losetup -a # /dev/loop0
$ tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 1 --backing-store /dev/loop0
# 直接用块文件作为backstorage添加第二个块
$ dd if=/dev/zero of=/opt/tgtstorage/disk1.img bs=1M count=5120
$ tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 2 --backing-store /opt/tgtstorage/disk1.img
# 绑定客户端ip
$ tgtadm --lld iscsi --mode target --op bind --tid 1 --initiator-address=192.168.182.157
$ tgtadm --lld iscsi --mode target --op bind --tid 1 --initiator-address=192.168.182.158
$ tgt-admin --dump |grep -v default-driver > /etc/tgt/conf.d/my-targets.conf # 通过tgt-admin保存为配置文件,注意与tgtadm的区别!
$ tgtadm --lld iscsi --mode target --op show
$ tgt-admin --show # 和上一个命令一样,tgt-admin是tgtadm的perl封装

在服务端禁用某个客户端:

1
$ tgtadm --lld iscsi --mode target --op unbind --tid 1 --initiator-address=192.168.182.158 # unbind,使不可发现

在客户端使用target

iscsi存储使用的主要流程:

  1. 发现target
  2. login到target
  3. 使用存储管理工具使用块设备
1
2
3
4
5
6
7
8
9
10
$ iscsiadm --mode discovery --type sendtargets --portal 192.168.182.156
$ ls -lh /var/lib/iscsi/nodes/ # 可以查看到对应的target和卷资料
$ iscsiadm -m node # 查看当前机器上所有target
$ iscsiadm -m node -T iqn.2015-05-04.org.tecstack.storage.tg1 --login # 登陆target
$ fdisk -l # 登陆后就可以看到块设备
$ iscsiadm -m node -T iqn.2015-05-04.org.tecstack.storage.tg1 --logout #登出target
$ fdisk -l # 登出后设备移除
$ iscsiadm -m node -o delete -T iqn.2015-05-04.org.tecstack.storage.tg1 # 删除target
$ ls -lh /var/lib/iscsi/nodes/
$ iscsiadm -m node -T iqn.2015-05-04.org.tecstack.storage.tg1 -p 192.168.182.156 -- op update -n node.startup -v automatic # 自动login

当login到target之后,就可以使用,比如通过LVM:

1
2
3
4
5
6
7
8
9
10
11
12
$ fdisk -l # 增加了一个/dev/sdb块设备
$ pvcreate /dev/sdb
$ pvdisplay
$ vgcreate myiscsi /dev/sdb
$ vgdisplay
$ lvcreate -l 1024 -n vdisk0 myiscsi
$ lvdisplay
$ ls -lh /dev/myiscsi/vdisk0 # 生成的块设备位置
$ mkfs.ext4 /dev/myiscsi/vdisk0
$ mkdir -p /opt/myiscsidata
$ mount /dev/myiscsi/vdisk0 /opt/myiscsidata/
$ df -h

运行过程中为target添加后端存储,客户端需要logout后重新login才能看到。但是如果重新login后块设备名会变化,比如变成/dev/sdc。可以通过文件系统的UUID来识别设备并挂载:

1
$ tune2fs -l /dev/sdc

GFS2测试

创建和挂载iscsi块存储

在target端创建LUN

1
2
3
4
5
$ dd if=/dev/zero of=/opt/tgtstorage/disk_gfs.img bs=1M count=5120
$ tgtadm --lld iscsi --op new --mode logicalunit --tid 1 --lun 3 --backing-store /opt/tgtstorage/disk_gfs.img
$ tgtadm --lld iscsi --mode target --op bind --tid 1 --initiator-address=192.168.182.157
$ tgtadm --lld iscsi --mode target --op bind --tid 1 --initiator-address=192.168.182.158
$ tgt-admin --show

在client1和client2上挂载块设备:

1
2
3
4
$ iscsiadm --mode discovery --type sendtargets --portal 192.168.182.156
$ iscsiadm -m node
$ iscsiadm -m node -T iqn.2015-05-04.org.tecstack.storage.tg1 --login
$ fdisk -l

使用luci配置集群:

在target端配置:

1
2
$ yum -y install luci
$ service luci start

在client1和client2上配置:

1
2
3
$ yum install -y ricci
$ service ricci start
$ passwd ricci # 123456,节点密码

通过访问target的https(默认端口8084,账号同Linux本地root账号),创建集群,新增节点,选择下载包,勾选启用共享文件系统。管理工具会自动帮助安装:cman rgmanager lvm2-cluster sg3_utils gfs2-utils,并启动相关服务。

在一台集群节点上创建LVM逻辑卷,格式化为GFS文件系统

识别scsi和块设备的对应关系:

1
2
$ ls -l /dev/disk/by-path/*tecstack*
$ scsi_id -gu /dev/sdb

在client1上执行:

1
2
3
4
$ pvcreate /dev/sdb
$ vgcreate gfstest /dev/sdb
$ lvcreate -l 1024 -n gfsdisk0 gfstest
$ mkfs.gfs2 -j2 -p lock_dlm -t gfstest:gfs2 /dev/gfstest/gfsdisk0

在两台集群节点上同时挂载GFS文件系统

在client1和client2上执行:

1
2
$ mkdir /mnt/gfstest
$ mount /dev/gfstest/gfsdisk0 /mnt/gfstest

参考:

  1. creating and managing iscsi targets
  2. tgtadm man page
  3. iscsi使用案例
  4. GFS配置

Docker概述

Docker是基于Go语言开发的容器管理工具,而且抽象级别比lxc这些管理工具高,目前官方默认通过libcontainer管理容器。lxc是一些kernel patch(namespaces)和userspace tool(cgroup)的集合,通过cgroup这个用户态管理工具实现对隔离资源的管理。Docker相比LXC增加了镜像服务(通过UnionFS和DeviceMapper),同时简化了操作复杂度,可以很方便实现应用分发部署,甚至是扩容。目前也有一些周边管理工具(Compose、Flocker、weaver)在创新,在docker基础上实现更多实用功能,如迁移,流程编排,网络管理等。跟openstack相比,docker更年轻,也更轻量级,但两者在某些场景下又可以很好结合起来,比如通过docker将openstack的管理节点实现高可用、可扩展。

docker的部署在发行版上比较简单,但对内核版本有不同要求,比如在centos6.5上要求内核版本至少2.6.32-431。

1
2
3
4
5
$ yum install -y docker-io
$ chkconfig docker on
$ service docker start
$ docker run -it ubuntu /bin/bash
$ docker info #查看docker信息

Docker基本用法

基本命令使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ docker run ubuntu:14.04 /bin/echo 'Hello world'
$ docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done" # 后台运行
$ docker ps -a
$ docker logs instance_name
$ docker logs -f instance_name # 持续输出
$ docker stop/start/restart instance_name # stop容器后还能启动,但网络信息会变
$ docker ps -aq | xargs docker rm # 删除所有容器,运行中的会保留
$ docker images # -tree参数可以看层级关系,最大128
$ docker save -o ubuntu.tar ubuntu:14.04 # 把镜像保存到本地
$ tar -tf ubuntu.tar # 可以看到image的结构,包含了多层FS
$ docker rmi ubuntu
$ docker run -d -P training/webapp python app.py # 映射所有端口
$ docker port instance_name 5000 #查看被映射的端口
$ docker run -d -p 5000:5000 training/webapp python app.py # 映射5000端口
$ docker run -d -p 127.0.0.1:5000:5000/udp training/webapp python app.py # 映射本机UDP端口
$ docker top instance_name
$ docker inspect instance_name # 查看容器配置
$ docker inspect -f '{{ .NetworkSettings.IPAddress }}' instance_name # 查看具体的配置信息,如IP
$ docker pull centos
$ docker commit -m "some modify hints" -a "author info"
contain_id repo_name/image_name:tags
$ docker tag container_id repo_name/image_name:tags
$ docker exec -it CONTAINER_NAME /bin/bash # 1.3开始支持在运行的容器内执行命令

使用Dockerfile创建image

mkdir myimage && cd $_ && vim Dockerfile:

1
# This is a comment
FROM ubuntu:14.04
MAINTAINER Promise John <promise.jon@gmail.com>
RUN apt-get update && apt-get install -y nginx

使用Linking

Docker主要通过环境变量和/etc/hosts文件来提供访问途径:

1
2
3
4
5
6
$ docker run -d --name db training/postgres
$ docker run -d -P --name web --link db:db training/webapp python app.py
$ docker inspect -f "{{ .Name }}" instance_id # 查看容器名
$ docker inspect -f "{{ .HostConfig.Links }}" web
$ docker run --rm --name web2 --link db:db training/webapp env
$ docker run --rm --name web2 --link db:db training/webapp cat /etc/hosts

Storage

主要包括独立的卷和共享本地文件系统:

1
2
3
4
5
$ docker run -d -P --name web -v /webapp training/webapp python app.py # volumn不会随容器消失,作为数据独立存在
$ docker inspect web # 查看/var/lib/docker/volumes和vfs
$ docker run -d -P --name web -v /src/webapp:/opt/webapp training/webapp python app.py # 挂载
$ docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py # 只读挂载
$ docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash # 只挂载某个文件

Container间共享数据:

1
2
3
4
5
$ docker create -v /dbdata --name dbdata training/postgres /bin/true
$ docker run -d --volumes-from dbdata --name db1 training/postgres
$ docker run -d --volumes-from dbdata --name db2 training/postgres
$ docker run --volumes-from dbdata -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar /dbdata # 备份容器数据
$ docker run --volumes-from dbdata2 -v $(pwd):/backup busybox tar xvf /backup/backup.tar # 恢复数据

Docker本地仓库

Docker Hub基本用法:

1
2
3
4
$ docker search ubuntu
$ docker pull ubuntu:14.04
$ docker login # 配置文件在~/.dockercfg
$ docker push yourname/newimage

配置本地的registry:

1
2
3
4
5
6
7
8
9
$ docker run -d -p 5000:5000 registry
$ docker tag ubuntu:14.04 localhost:5000/ubuntu:14.04
$ docker push localhost:5000/ubuntu:14.04
$ curl -v http://localhost:5000/v1/repositories/ubuntu/tags/14.04 # 查看image id
$ docker run -d -p 5000:5000 \
-e STANDALONE=false \
-e MIRROR_SOURCE=https://registry-1.docker.io \
-e MIRROR_SOURCE_INDEX=https://index.docker.io \
registry # 作为官网镜像启动,代理模式

使用本地的registry:

可以使用--registry-mirror参数启动:

1
$ docker --registry-mirror=http://10.101.29.26 -d

Docker Compose

Docker Compose可以方便地实现应用的组合。

1
2
$ pip install docker-compose
$ mkdir compose && cd $_

增加app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask
from redis import Redis
import os
app = Flask(__name__)
redis = Redis(host='redis', port=6379)

@app.route('/')
def hello():
redis.incr('hits')
return 'Hello World! I have been seen %s times.' % redis.get('hits')

if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)

增加requirements.txt

1
flask
redis

增加Dockerfile

1
FROM python:2.7
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt

增加docker-compose.yml

1
web:
  build: .
  command: python app.py
  ports:
   - "5000:5000"
  volumes:
   - .:/code
  links:
   - redis
redis:
  image: redis

启动并管理服务:

1
2
3
4
$ docker-compose up -d
$ docker-compose ps
$ docker-compose run web env
$ docker-compose stop

小结

Docker在快速应用分发、扩展等方面有很高的自动化能力,但是跟主机虚拟化一样,网络也是需要重点考虑的难点。默认情况下都是通过本地的一个bridge提供容器之间的通信,通过NAT为应用提供地址,一般的应用部署足以应付,但如果要实现更复杂的网络场景,如多主机上container组成大规模的L3负载均衡集群、应用无缝迁移等,还是需要大量的定制,目前这方面的周边产品开发比较活跃。

参考

  1. Official Docs
  2. Docker with aufs and devicemapper
  3. LXC Tools
  4. Docker Compose

手动创建KVM虚拟机镜像

通过libvirt系的本地命令行管理工具,也可以方便地创建虚拟机。

  • qemu-img:生成虚拟机磁盘文件
  • virsh:命令行虚拟机管理工具

生成Domain XML文件

如果要用X11远程到物理机使用GUI工具,需要配置sshd,安装xauth

/etc/ssh/sshd_config:

1
...
X11Forwarding yes
X11UseLocalhost no
...
1
2
3
4
$ /etc/init.d/sshd reload
$ yum install -y xauth
$ ssh -X user@host
$ virt-manager&

也可以直接用官方的example修改配置:

1
<domain type = 'kvm'>
        <name>centos66</name>
        <memory>1048576</memory>
        <vcpu>1</vcpu>
        <os>
                <type arch = 'x86_64'machine = 'pc'>hvm</type>
                <boot dev = 'cdrom'/>
                <boot dev = 'hd'/>
        </os>
        <features>
                <acpi/>
                <apic/>
                <pae/>
        </features>
        <clock offset = 'utc'/>
        <on_poweroff>destroy</on_poweroff>
        <on_reboot>restart</on_reboot>
        <on_crash>destroy</on_crash>
        <devices>
                <emulator>/usr/libexec/qemu-kvm</emulator>
                <disk type = 'file' device = 'disk'>
                        <driver name = 'qemu' type = 'raw' cache='none'/>
                        <source file = '/opt/vmdisks/centos66.raw'/>
                        <target dev='vda' bus='virtio'/>
                </disk>
                <disk type = 'file' device = 'cdrom'>
                        <source file = '/opt/CentOS-6.6-x86_64-bin-DVD1.iso'/>
                        <target dev = 'hdb' bus = 'ide'/>
                </disk>
"centos66.xml" 48L, 1243C written                                                                     8,20-34       Top
                        <target dev = 'hdb' bus = 'ide'/>
                </disk>
                <interface type='network'>
                        <source network='default'/>
                        <model type='virtio'/>
                </interface>
                <input type='tablet' bus='usb'/>
                <input type='mouse' bus='ps2'/>
                <graphics type='vnc' port='-1' autoport='yes' listen='0.0.0.0'>
                        <listen type='address' address='0.0.0.0'/>
                </graphics>
        </devices>
</domain>

创建虚拟机

1
2
3
4
5
$ mkdir -p /opt/vmdisks
$ qemu-img create -f raw /opt/vmdisks/centos66.raw 8G
$ virsh create centos66.xml
$ ip addr
$ virsh vncdisplay centos66 #获取ip和vnc端口登陆

接下来就进入安装操作系统的界面,一路按指印即可。

远程管理libvirt主机

可以用qemu+ssh,节点间互信后迁移;也可以用qemu+tcp,修改/etc/libvirt/libvirtd.conf:

1
listen_tls = 0
listen_tcp = 1
tcp_port = "16509"
listen_addr = "0.0.0.0"
auth_tcp = "none" # 生产环境建议加上授权验证

修改/etc/init.d/libvirtd:

1
LIBVIRTD_CONFIG=/etc/libvirt/libvirtd.conf
LIBVIRTD_ARGS="--listen"
1
2
$ service libvirtd restart
$ ss -ln | grep 16509

在其他节点就可以连接(注意iptables):

1
$ virsh -c qemu+tcp://192.168.182.156/system

克隆虚拟机:

1
2
3
$ virsh define centos66.xml
$ virt-clone --original=centos66 --name=centos66_01 -f centos66_01.raw
$ virsh list --all

参考

  1. libvirt官方虚拟机规格XML格式说明

参考架构及部署规划

参考架构

  • 操作系统:CentOS6.5
  • 第三方yum源:epel, rdo

节点部署角色:

节点名 internal ip public ip Role
oscontroller 10.0.100.145 192.168.182.150 nova, glance, cinder, image, neutron, dashboard, heat
osnetwork 10.0.100.146 192.168.182.151 ML2, OVS, L2 Agent, L3 Agent, DHCP Agent
oscompute1 10.0.100.147 192.168.182.152 nova-compute
oscompute2 10.0.100.148 192.168.182.153 nova-compute
oskeystone 10.0.100.149 192.168.182.154 qpid/rabbitmq, keystone, mysql, memcached
osmeter 10.0.100.150 192.168.182.155 ceilometer, mongodb
osswift0 10.0.100.139 192.168.182.144 swift0, swift-proxy-server
osswift1 10.0.100.140 192.168.182.145 swift1
osswift2 10.0.100.141 192.168.182.146 swift2
osceph0 10.0.100.142 192.168.182.147 ceph0
osceph1 10.0.100.143 192.168.182.148 ceph1 # 暂时不用
osceph2 10.0.100.144 192.168.182.149 ceph2 # 暂时不用

参考部署架构

Read More

Ruby开发环境搭建

多版本管理RVM

1
2
3
4
5
6
7
8
9
$ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
$ curl -sSL https://get.rvm.io | bash -s stable
$ source /etc/profile.d/rvm.sh
$ sed -i 's!cache.ruby-lang.org/pub/ruby!ruby.taobao.org/mirrors/ruby!' $rvm_path/config/db # 使用taobao源下载ruby
$ rvm list known
$ rvm install 2.1.4
$ rvm docs generate-ri # 生成ruby文档
$ rvm use 2.1.4 --default # 设定默认ruby版本
$ rvm list # 查询已安装版本

gemset建立独立环境:

1
2
3
4
$ rvm use 2.1.4
$ rvm gemset create rails42
$ rvm use 2.1.4@rails42
$ rvm gemset list

Gem管理ruby包:

1
2
3
4
$ gem sources --remove https://rubygems.org/
$ gem sources -a https://ruby.taobao.org/
$ gem sources -l
$ gem search rails # search,install, etc

参考:

  1. RVM official site
  2. RVM Guide from ruby-china.org
  3. Gem Official Guide
  4. Ruby@Taobao.org

Vagrant部署

1
2
3
$ cd ~/dev
$ wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.2_x86_64.rpm
$ rpm -ivh vagrant_1.7.2_x86_64.rpm

添加vagrant boxes

1
2
$ vagrant box add hashicorp/precise32 # 支持virtualbox Hypervisor
$ vagrant box add centos64 http://citozin.com/centos64.box # 支持 libvirt KVM Hypervisor

编辑Vagrantfile

默认使用的是virtualbox作为Hypervisor:

1
2
3
Vagrant.configure("2") do |config|
config.vm.box = "hashicorp/precise32"
end

运行虚拟机

1
$ vagrant up

Vagrant使用KVM Hypervisor

安装vagrant-libvirt plugin:

1
2
$ yum install libxslt-devel libxml2-devel libvirt-devel
$ vagrant plugin install vagrant-libvirt --plugin-source https://ruby.taobao.org/

配置Vagrantfile:

1
2
3
4
5
Vagrant.configure("2") do |config|
config.vm.define :node1 do |node1|
node1.vm.box = "centos64"
end
end

启动虚拟机:

1
2
$ vagrant up --provider=libvirt
$ export VAGRANT_DEFAULT_PROVIDER=libvirt # Another way

小结

Vagrant的box不是对所有hypervisor都通用,比如官方的hashicorp/precise32就不支持KVM,如果环境只有KVM,那么可能需要自己制作对应的box,可以参考Vagrant-Libvirt Plugin,借助工具create_box.sh从qcow2 image制作。

此外,需要注意的是,KVM和VirtualBox不能同时启动虚拟机,否则会报类似如下错误:

先启动virtualbox虚拟机再启动KVM虚拟机:

1
There was an error talking to Libvirt. The error message is shown
below:

Call to virDomainCreateWithFlags failed: internal error Process exited while reading console log output: char device redirected to /dev/pts/2
kvm_create_vm: Device or resource busy
failed to initialize KVM: Operation not permitted
No accelerator found!

先启动KVM虚拟机再启动virtualbox虚拟机:

1
The guest machine entered an invalid state while waiting for it
to boot. Valid states are 'starting, running'. The machine is in the
'poweroff' state. Please verify everything is configured
properly and try again.

If the provider you're using has a GUI that comes with it,
it is often helpful to open that and watch the machine, since the
GUI often has more helpful error messages than Vagrant can retrieve.
For example, if you're using VirtualBox, run `vagrant up` while the
VirtualBox GUI is open.

参考:

  1. Vagrant Official Site
  2. Vagrant-Libvirt Plugin

Flocker安装部署

安装flocker-deploy:

1
2
3
4
$ pyenv virtualenv flocker
$ pyenv activate flocker
$ pip install https://storage.googleapis.com/archive.clusterhq.com/downloads/flocker/Flocker-0.3.2-py2-none-any.whl
$ which flocker-deploy # 验证flocker-deploy安装在$(dirname `pyenv which python`)目录下

安装flocker-node,本地安装方式官方目前只支持fedora 20的linux,暂时选择用最简单的vagrant来测试。

准备第三方库:

1
2
3
4
$ cd /etc/yum.repos.d
$ wget http://download.virtualbox.org/virtualbox/rpm/$ rhel/virtualbox.repo # 导入virtualbox的yum库,修改默认不启用:enabled=0
$ wget http://mirrors.zju.edu.cn/epel/6/i386/epel-release-6-8.noarch.rpm
$ rpm -ivh epel-release-6-8.noarch.rpm # 导入EPEL库

安装VirtualBox:

1
2
3
4
5
6
7
8
$ cd ~/dev # 工作目录
$ yum install dkms # 从EPEL安装
$ yum groupinstall "Development Tools"
$ yum install kernel-devel # 确保当前运行的uname -r与kernel-devel版本一致
$ yum --enablerepo virtualbox install VirtualBox-4.3
$ /etc/init.d/vboxdrv setup
$ wget http://dlc-cdn.sun.com/virtualbox/4.3.26/Oracle_VM_VirtualBox_Extension_Pack-4.3.26-98988.vbox-extpack
$ VBoxManage extpack install Oracle_VM_VirtualBox_Extension_Pack-4.3.26-98988.vbox-extpack

安装Vagrant:

1
2
3
4
5
6
$ wget https://dl.bintray.com/mitchellh/vagrant/vagrant_1.7.2_x86_64.rpm
$ rpm -ivh vagrant_1.7.2_x86_64.rpm
$ cd ~/dev && mkdir HelloFlocker && cd $_
$ wget http://flocker.readthedocs.org/en/latest/_downloads/Vagrantfile # See Below
$ vagrant up
$ vagrant status

使用官方的flocker box:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Vagrant.require_version ">= 1.6.2"
VAGRANTFILE_API_VERSION = "2"
ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox'
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "clusterhq/flocker-tutorial"
config.vm.box_version = "= 0.3.2"
if Vagrant.has_plugin?("vagrant-cachier")
config.cache.scope = :box
end
config.vm.define "node1" do |node1|
node1.vm.network :private_network, :ip => "172.16.255.250"
node1.vm.hostname = "node1"
end
config.vm.define "node2" do |node2|
node2.vm.network :private_network, :ip => "172.16.255.251"
node2.vm.hostname = "node2"
end
end

Flocker使用SSH通道:

1
2
3
$ eval $(ssh-agent) # 启用一个SSH Agent,可以用ssh-add测试是否启用
$ ssh-add ~/.vagrant.d/insecure_private_key
$ ssh -t root@172.16.255.250 flocker-reportstate --version # 检查virtualbox中虚拟机内的flocker-node版本

在虚拟机中下载docker比如官方的MySQL案例:

1
2
$ ssh -t root@172.16.255.250 docker pull mysql:5.6.17
$ ssh -t root@172.16.255.251 docker pull mysql:5.6.17

Flocker部署MySQL:

配置mysql-application.yml:

1
"version": 1
"applications":
  "mysql-volume-example":
    "image": "mysql:5.6.17"
    "environment":
      "MYSQL_ROOT_PASSWORD": "clusterhq"
    "ports":
    - "internal": 3306
      "external": 3306
    "volume":
      "mountpoint": "/var/lib/mysql"

配置mysql-deployment.yml:

1
"version": 1
"nodes":
  "172.16.255.250": ["mysql-volume-example"]
  "172.16.255.251": []

检查docker服务端镜像及容器启用情况:

1
2
3
4
$ ssh -t root@172.16.255.250 docker images
$ ssh -t root@172.16.255.250 docker ps
$ ssh -t root@172.16.255.251 docker images
$ ssh -t root@172.16.255.251 docker ps

通过Flocker-deploy部署应用:

1
2
3
4
$ pyenv activate flocker # 确保进入隔离python环境,之前安装的flocker-cli在该环境内
$ flocker-deploy mysql-deployment.yml mysql-application.yml
$ ssh root@172.16.255.250 docker ps # 可以看到MySQL容器已启动
$ ssh root@172.16.255.251 docker ps

使用MySQL服务:

1
2
3
$ mysql -uroot -pclusterhq -h172.16.255.250
$ # create some databases, tables and data.
$ # stay in the mysql client and keep connection.

通过Flocker-deploy迁移应用:

新增mysql-deployment-moved.yml

1
"version": 1
"nodes":
  "172.16.255.250": []
  "172.16.255.251": ["mysql-volume-example"]

执行迁移:

1
2
3
$ flocker-deploy mysql-deployment-moved.yml mysql-application.yml
$ ssh -t root@172.16.255.250 docker ps # 看到容器列表空
$ ssh -t root@172.16.255.251 docker ps # 看到容器启动

迁移过程中可以发现,mysql客户端连接的172.16.255.250服务端并没有断开。通过登录虚拟机可以找到原因(有兴趣的可以用tcpdump、ss来追踪):

1
2
$ ssh root@172.16.255.250
$ root@250: iptables -t nat -L

可以看到flocker是通过Linux Netfilter的NAT表进行了默认的转发:

1
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere             anywhere             ADDRTYPE match dst-type LOCAL
DNAT       tcp  --  anywhere             anywhere             tcp dpt:mysql ADDRTYPE match dst-type LOCAL /* flocker create_proxy_to */ to:172.16.255.251

Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
DOCKER     all  --  anywhere            !loopback/8           ADDRTYPE match dst-type LOCAL
DNAT       tcp  --  anywhere             anywhere             tcp dpt:mysql ADDRTYPE match dst-type LOCAL to:172.16.255.251

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  172.17.0.0/16        anywhere
MASQUERADE  tcp  --  anywhere             anywhere             tcp dpt:mysql

由此实现了应用的无缝迁移。

小结

Flocker实现的功能很有诱惑力,让docker具备了自动化的应用迁移能力,他俩关系有点像openstack和hypervisor的关系。但是,它目前还非常不成熟(0.4版在开发中),经常会遇到一些bug,就连官方也不推荐在生产环境使用,继续观望。

参考:

  1. Flocker Doc
  2. Web开发环境搭建
  3. CentOS下安装VirtualBox
  4. CentOS6.5菜鸟之旅:安装VirtualBox4.3
  5. 在验证应用无缝迁移过程中用到的工具:ss、tcpdump。