0%

背景

OpenStack(以下简称OPS)是一个很庞大的系统,想要部署一个OPS需要部署很多组件。想必部署之路也是坑坑奇多。所以为了方便使用OPS,需要部署一个“all-in-one”环境的OPS

环境

  • CPU
    2U X86_64
  • Memory
    4G及以上,如果内存过小,会出现各种问题。(整个devstack-Openstack-Ocata + CentOS7.3 1611需要占用3.4G内存)
  • OS
    CentOS 7.3 1611
  • NET
    建议创建两个网络,一个用于连接外网用于做管理性工作;一个供neutron使用
  • OpenStack版本
    Ocata

部署

准备

关闭防火墙

关闭selinux

编辑/etc/selinux/configSELINUX=enforcing改为SELINUX=disabled

关闭iptables

执行命令sudo systemctl disable iptables,关闭iptables服务

上诉两处修改若想生效需要重启系统。

安装依赖软件

1
sudo yum install -y epel-release git net-tools

下载Devstack

从github上下载devstackgit clone https://github.com/openstack-dev/devstack.git,然后切换版本到Ocata git checkout stable/ocata,可使用git branch -av查看devstack所处分支

安装

创建stack用户

  1. 进入devstack目录
    1
    cd devstack
  2. 创建stack用户
    1
    sudo ./tools/create-stack-user.sh

切换用户

  1. 修改目录owner
    1
    sudo chown -R stack:stack ./devstack
  2. 修改/opt/stack目录访问权限
    1
    2
    sudo chmod 777 /opt/stack -R
    sudo mv ./devstack /opt/stack/
  3. 切换用户
    1
    sudo su - stack

撰写local.conf

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
[[local|localrc]]

DEST=/opt/stack/ocata
# use TryStack git mirror
GIT_BASE=http://git.trystack.cn
NOVNC_REPO=http://git.trystack.cn/kanaka/noVNC.git
SPICE_REPO=http://git.trystack.cn/git/spice/spice-html5.git

#OFFLINE=True
RECLONE=False

# Define images to be automatically downloaded during the DevStack built process.
DOWNLOAD_DEFAULT_IMAGES=False
IMAGE_URLS=",https://launchpad.net/cirros/trunk/0.3.0/+download/cirros-0.3.0-x86_64-disk.img"

HOST_IP=127.0.0.1


# Credentials
DATABASE_PASSWORD=pass
ADMIN_PASSWORD=pass
SERVICE_PASSWORD=pass
SERVICE_TOKEN=pass
RABBIT_PASSWORD=pass

HORIZON_BRANCH=stable/ocata
KEYSTONE_BRANCH=stable/ocata
NOVA_BRANCH=stable/ocata
NEUTRON_BRANCH=stable/ocata
GLANCE_BRANCH=stable/ocata
CINDER_BRANCH=stable/ocata


#keystone
KEYSTONE_TOKEN_FORMAT=UUID

##Heat
HEAT_BRANCH=stable/ocata
enable_service h-eng h-api h-api-cfn h-api-cw


## Swift
SWIFT_BRANCH=stable/ocata
ENABLED_SERVICES+=,s-proxy,s-object,s-container,s-account
SWIFT_REPLICAS=1
SWIFT_HASH=011688b44136573e209e


# Enabling Neutron (network) Service
disable_service n-net
enable_service q-svc
enable_service q-agt
enable_service q-dhcp
enable_service q-l3
enable_service q-meta
enable_service q-metering
enable_service neutron

## Neutron options
Q_USE_SECGROUP=True
FLOATING_RANGE="192.168.36.0/24"
FIXED_RANGE="10.0.0.0/24"
Q_FLOATING_ALLOCATION_POOL=start=192.168.36.55,end=192.168.36.100
PUBLIC_NETWORK_GATEWAY="192.168.36.1"
Q_L3_ENABLED=True
PUBLIC_INTERFACE=eth1
Q_USE_PROVIDERNET_FOR_PUBLIC=True
OVS_PHYSICAL_BRIDGE=br-ex
PUBLIC_BRIDGE=br-ex
OVS_BRIDGE_MAPPINGS=public:br-ex

# #VLAN configuration.
Q_PLUGIN=ml2
ENABLE_TENANT_VLANS=True

# Logging
LOGFILE=/opt/stack/logs/stack.sh.log
VERBOSE=True
LOG_COLOR=True
SCREEN_LOGDIR=/opt/stack/logs

安装OPS(all-in-one)

安装使用stack.sh脚本,卸载使用unstack.sh脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ ./stack.sh
...

=========================
DevStack Component Timing
=========================
Total runtime 1254

run_process 83
test_with_retry 4
pip_install 127
restart_apache_server 29
wait_for_service 19
yum_install 50
=========================



This is your host IP address: 127.0.0.1
This is your host IPv6 address: ::1
Horizon is now available at http://127.0.0.1/dashboard
Keystone is serving at http://127.0.0.1/identity/
The default users are: admin and demo
The password: pass

安装时遇到问题可从两个角度考虑,一个网络不通,一个内存不足。这里再次重申,内存一定要>=4G,如果网络不通或延时太大,建议使用国内镜像。

例如/opt/stack/.pip/pip.conf:

1
2
3
4
5
[global]

timeout = 6000
index-url = http://pypi.douban.com/simple
trusted-host = pypi.douban.com

horizon

安装成功后,访问http://x.x.x.x/dashboard, 用户民密码见local.conf

horizon_login

参考&鸣谢

cosbench-usage.png

COSBench是Intel团队基于java开发,对云存储的测试工具,全称是Cloud object Storage Bench。COSBench也分控制台和发起请求的driver,且driver可以分布式部署。可以支持swift、s3、Openstack等接口

获取

从github上下载release版本(https://github.com/intel-cloud/cosbench)。解压后可以获取COSBenchUserGuide.pdf(只有英文版)和workload配置文件(./conf/*.xml)。

部署&启动

COSBench的运行依赖于java和curl这两个包,并且调用linux的nc来做数据分析,如果没有安装nc,请手动安装。
本人将COSBench部署到docker中,为的是方便以后运行,不要将有限的生命耗费到无限的折腾当中。

docker镜像使用的是centos:7.3.1611,具体dockerfile如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM centos:7.3.1611

RUN yum update -y
RUN yum install -y java-1.8.0-openjdk

RUN yum clean all

EXPOSE 19088 18088

ADD ./cosbench.tar.gz /root/
ADD ./entry.sh /root/

ENTRYPOINT ["/root/entry.sh"]

entry.sh是容器的启动脚本,脚本中主要用于启动COSBench服务,具体实现如下:

1
2
3
4
5
6
7
#!/bin/sh

./start-all.sh

while true; do
sleep 10s
done

完成docker镜像的生成后,使用docker run -d -w /root/cosbench/ --rm -P s3test:performance命令运行容器。然后通过docker ps查看可访问的端口。

1
2
3
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ed37db78178a s3test:performance "/root/entry.sh" 19 hours ago Up 19 hours 0.0.0.0:32771->18088/tcp, 0.0.0.0:32770->19088/tcp unruffled_mahavira

在浏览器中输入http://x.x.x.x:32770/controller/index.html访问COSBench首页。COSBench是以”master-slave”模式运行的,19088端口对应的是master端口,18088对应的是driver端口,任务的运行是有master下发给driver,然后由driver执行的(driver可配置多个)。

关于workload配置文件的撰写请见COSBenchUserGuide.pdf,此处给出一个测试S3接口的例子

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
<?xml version="1.0" encoding="UTF-8" ?>
<workload name="S3Test" description="Performance">
<storage type="s3" config="accesskey=Ltiakby8pAAbHMjpUr3L;secretkey=qMTe5ibLW49iFDEHNKqspdnJ8pwaawA9GYrBXUYc;endpoint=http://los-cn-north-2.lecloudapis.com" />
<workflow>

<workstage name="init">
<work type="init" workers="1" config="cprefix=testp;containers=r(1,4)" />
</workstage>

<workstage name="prepare">
<work type="prepare" workers="1" config="cprefix=testp;containers=r(1,2);objects=r(1,800);sizes=c(4)MB" />
</workstage>

<workstage name="sequential_RO_8W_4MB">
<work name="sequential_RO_8W_4MB" workers="8" totalOps="1600">
<operation type="read" ratio="100" config="cprefix=testp;containers=c(1);objects=s(1,800);size=c(4)MB" />
</work>
</workstage>

<workstage name="random_RO_8W_4MB">
<work name="random_RO_8W_4MB" workers="8" totalOps="1600">
<operation type="read" ratio="100" config="cprefix=testp;containers=c(2);objects=u(1,800);size=c(4)MB" />
</work>
</workstage>

<workstage name="sequential_WO_8W_4MB">
<work name="sequential_WO_8W_4MB" workers="8" totalOps="1600">
<operation type="write" ratio="100" config="cprefix=testp;containers=c(3);objects=s(1,800);sizes=c(4)MB" />
</work>
</workstage>

<workstage name="random_WO_8W_4MB">
<work name="random_WO_8W_4MB" workers="8" totalOps="1600">
<operation type="write" ratio="100" config="cprefix=testp;containers=c(4);objects=u(1,800);sizes=c(4)MB" />
</work>
</workstage>

<workstage name="cleanup">
<work type="cleanup" workers="1" config="cprefix=testp;containers=r(1,4);objects=r(1,800)" />
</workstage>

<workstage name="dispose">
<work type="dispose" workers="1" config="cprefix=testp;containers=r(1,4)" />
</workstage>

<workstage name="init">
<work type="init" workers="1" config="cprefix=testp;containers=r(1,4)" />
</workstage>

<workstage name="prepare">
<work type="prepare" workers="1" config="cprefix=testp;containers=r(1,2);objects=r(1,800);sizes=c(64)KB" />
</workstage>

<workstage name="sequential_RO_32W_64KB">
<work name="sequential_RO_32W_64KB" workers="32" totalOps="1600">
<operation type="read" ratio="100" config="cprefix=testp;containers=c(1);objects=s(1,800);size=c(64)KB" />
</work>
</workstage>

<workstage name="random_RO_32W_64KB">
<work name="random_RO_32W_64KB" workers="32" totalOps="1600">
<operation type="read" ratio="100" config="cprefix=testp;containers=c(2);objects=u(1,800);size=c(64)KB" />
</work>
</workstage>

<workstage name="sequential_WO_32w_64KB">
<work name="sequential_WO_32w_64KB" workers="32" totalOps="1600">
<operation type="write" ratio="100" config="cprefix=testp;containers=c(3);objects=s(1,800);sizes=c(64)KB" />
</work>
</workstage>

<workstage name="random_WO_32W_64KB">
<work name="random_WO_32W_64KB" workers="32" totalOps="1600">
<operation type="write" ratio="100" config="cprefix=testp;containers=c(4);objects=u(1,800);sizes=c(64)KB" />
</work>
</workstage>

<workstage name="cleanup">
<work type="cleanup" workers="1" config="cprefix=testp;containers=r(1,4);objects=r(1,800)" />
</workstage>

<workstage name="dispose">
<work type="dispose" workers="1" config="cprefix=testp;containers=r(1,4)" />
</workstage>

</workflow>
</workload>

问题及解决

  1. COSBench首页时间显示没有按东八区显示
    cosbench-start.sh中找到/usr/bin/nohup java这一行,增加-Duser.timezone=Asia/Shanghai参数
  2. COSBench在测试过程中MD5校验失败问题
    在测试过程中GET操作失败,而且错误日志如下:
    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
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    2017-07-11 07:26:42,297 [INFO] [Log4jLogManager] - will append log to file /root/cosbench/log/mission/MA308B012AE.log
    2017-07-11 07:26:42,574 [INFO] [NoneStorage] - performing GET at /testp2/myobjects38
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects43
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp1/myobjects43
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects31
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp1/myobjects7
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects26
    2017-07-11 07:26:42,574 [INFO] [NoneStorage] - performing GET at /testp1/myobjects34
    2017-07-11 07:26:42,574 [INFO] [NoneStorage] - performing GET at /testp2/myobjects25
    2017-07-11 07:26:42,574 [INFO] [NoneStorage] - performing GET at /testp2/myobjects45
    2017-07-11 07:26:42,574 [INFO] [NoneStorage] - performing GET at /testp1/myobjects7
    2017-07-11 07:26:42,574 [INFO] [NoneStorage] - performing GET at /testp2/myobjects43
    2017-07-11 07:26:42,576 [INFO] [NoneStorage] - performing GET at /testp2/myobjects16
    2017-07-11 07:26:42,576 [INFO] [NoneStorage] - performing GET at /testp2/myobjects23
    2017-07-11 07:26:42,576 [INFO] [NoneStorage] - performing GET at /testp1/myobjects15
    2017-07-11 07:26:42,576 [INFO] [NoneStorage] - performing GET at /testp1/myobjects36
    2017-07-11 07:26:42,576 [INFO] [NoneStorage] - performing GET at /testp2/myobjects34
    2017-07-11 07:26:42,576 [INFO] [NoneStorage] - performing GET at /testp1/myobjects34
    2017-07-11 07:26:42,576 [INFO] [NoneStorage] - performing GET at /testp2/myobjects27
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects49
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp1/myobjects27
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects24
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp1/myobjects35
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects2
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects47
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp1/myobjects39
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp1/myobjects50
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp1/myobjects8
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects21
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects24
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects15
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp1/myobjects4
    2017-07-11 07:26:42,575 [INFO] [NoneStorage] - performing GET at /testp2/myobjects1
    2017-07-11 07:26:45,476 [ERROR] [AbstractOperator] - worker 8 fail to perform operation testp1/myobjects7
    com.amazonaws.AmazonClientException: Unable to verify integrity of data download. Client calculated content hash didn't match hash calculated by Amazon S3. The data may be corrupt.
    at com.amazonaws.services.s3.internal.DigestValidationInputStream.validateMD5Digest(DigestValidationInputStream.java:79)
    at com.amazonaws.services.s3.internal.DigestValidationInputStream.read(DigestValidationInputStream.java:61)
    at com.amazonaws.internal.SdkFilterInputStream.read(SdkFilterInputStream.java:72)
    at com.amazonaws.services.s3.model.S3ObjectInputStream.read(S3ObjectInputStream.java:155)
    at com.amazonaws.services.s3.model.S3ObjectInputStream.read(S3ObjectInputStream.java:147)
    at com.intel.cosbench.driver.operator.Reader.copyLarge(Reader.java:120)
    at com.intel.cosbench.driver.operator.Reader.doRead(Reader.java:92)
    at com.intel.cosbench.driver.operator.Reader.operate(Reader.java:69)
    at com.intel.cosbench.driver.operator.AbstractOperator.operate(AbstractOperator.java:76)
    at com.intel.cosbench.driver.agent.WorkAgent.performOperation(WorkAgent.java:197)
    at com.intel.cosbench.driver.agent.WorkAgent.doWork(WorkAgent.java:177)
    at com.intel.cosbench.driver.agent.WorkAgent.execute(WorkAgent.java:134)
    at com.intel.cosbench.driver.agent.AbstractAgent.call(AbstractAgent.java:44)
    at com.intel.cosbench.driver.agent.AbstractAgent.call(AbstractAgent.java:1)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:748)
    2017-07-11 07:26:45,476 [INFO] [NoneStorage] - performing GET at /testp1/myobjects14
    2017-07-11 07:26:46,880 [INFO] [NoneStorage] - performing GET at /testp2/myobjects14
    2017-07-11 07:26:49,806 [INFO] [NoneStorage] - performing GET at /testp1/myobjects43
    2017-07-11 07:26:51,514 [INFO] [NoneStorage] - performing GET at /testp2/myobjects31
    2017-07-11 07:26:54,021 [INFO] [NoneStorage] - performing GET at /testp1/myobjects5
    你可以通过增加-Dcom.amazonaws.services.s3.disableGetObjectMD5Validation=true参数来关闭MD5校验,据说此bug只在0.4.3.c4版本出现,我没验证过其它版本。

参考&鸣谢

今天是2017年6月12日,在此之前Docker就已经更名为moby了,就moby的的编译与之前docker的编译无异,为什么呢,因为无论是docker还是moby,都将自己的编译放在了容器中,编译所依赖的包也在容器中完成安装,目前moby(以下都以此替代docker)能直接编译出rpm、deb两种安装包,具体支持哪些平台,哪些操作系统,请见./contrib/builder/

本文主要以编译centos7能用的rpm包为例,对moby的编译配置文件进行了修改,从而使moby快速完成编译。

编译原理

1
2
3
4
5
6
7
8
 Debian:jessie
+-------------+ golang 1.7.5-alpine
| | +------------------------+
| Moby | ----- docker run -----> | docker manpage compile |
| Compile | +------------------------+ centos:7
| Container | +--------------------+
| | -------------- docker run ----------------------------> | build centos rpm |
+-------------+ +--------------------+

Moby先创建一个Debian的容器,编译Moby,然后该容器根据用户输入的参数决定使用哪些容器编译哪些安装包,在编译目标安装包之前会使用golang容器编译manpage。总之moby的所有编译工作都是在容器中进行的。

编译方法

环境

  • 宿主机
    RHEL7.2
  • docker
    17.04.0-ce

RPM (Only CentOS)

  • 获取 moby 代码
    1
    2
    3
    4
    # clone moby
    git clone https://github.com/moby/moby.git
    # 切换到17.04.0-ce版本
    git checkout -b building_v17.04.0-ce v17.04.0-ce
  • 修改Dockerfile
    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
    diff --git a/Dockerfile b/Dockerfile
    index 8a361ce..3d6b0bb 100644
    --- a/Dockerfile
    +++ b/Dockerfile
    @@ -34,8 +34,12 @@ COPY keys/launchpad-ppa-zfs.asc /go/src/github.com/docker/docker/keys/
    RUN apt-key add /go/src/github.com/docker/docker/keys/launchpad-ppa-zfs.asc
    RUN echo deb http://ppa.launchpad.net/zfs-native/stable/ubuntu trusty main > /etc/apt/sources.list.d/zfs.list

    +RUN apt-get clean
    +RUN apt-get update -o Acquire::http::No-Cache=True
    +
    # Packaged dependencies
    -RUN apt-get update && apt-get install -y \
    +# RUN apt-get update && apt-get install -y \
    +RUN apt-get install -y \
    apparmor \
    apt-utils \
    aufs-tools \
    @@ -112,10 +116,12 @@ ENV PATH /osxcross/target/bin:$PATH
    ENV SECCOMP_VERSION 2.3.2
    RUN set -x \
    && export SECCOMP_PATH="$(mktemp -d)" \
    - && curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
    + # && curl -fsSL "https://github.com/seccomp/libseccomp/releases/download/v${SECCOMP_VERSION}/libseccomp-${SECCOMP_VERSION}.tar.gz" \
    + && curl -fsSL "https://github.com/seccomp/libseccomp/archive/v${SECCOMP_VERSION}.tar.gz" \
    | tar -xzC "$SECCOMP_PATH" --strip-components=1 \
    && ( \
    cd "$SECCOMP_PATH" \
    + && ./autogen.sh \
    && ./configure --prefix=/usr/local \
    && make \
    && make install \
  • 去掉DM(DeviceMapper)支持
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    diff --git a/hack/make/.integration-daemon-start b/hack/make/.integration-daemon-start
    index 0bc8b96..bea0d70 100644
    --- a/hack/make/.integration-daemon-start
    +++ b/hack/make/.integration-daemon-start
    @@ -71,7 +71,7 @@ if [ -z "$DOCKER_TEST_HOST" ]; then
    ( set -x; exec \
    dockerd --debug \
    --host "$DOCKER_HOST" \
    - --storage-driver "$DOCKER_GRAPHDRIVER" \
    + # --storage-driver "$DOCKER_GRAPHDRIVER" \
    --pidfile "$DEST/docker.pid" \
    --userland-proxy="$DOCKER_USERLANDPROXY" \
    $storage_params \
  • 由于GFW的原因,去掉manpage中关于golang.org/x/sys的安装
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    diff --git a/man/glide.lock b/man/glide.lock
    index 5ec765a..2ecad45 100644
    --- a/man/glide.lock
    +++ b/man/glide.lock
    @@ -38,10 +38,6 @@ imports:
    version: dabebe21bf790f782ea4c7bbd2efc430de182afd
    - name: github.com/spf13/viper
    version: c1ccc378a054ea8d4e38d8c67f6938d4760b53dd
    -- name: golang.org/x/sys
    - version: 62bee037599929a6e9146f29d10dd5208c43507d
    - subpackages:
    - - unix
    - name: gopkg.in/yaml.v2
    version: a83829b6f1293c91addabc89d0571c246397bbf4
    - name: github.com/spf13/cobra
  • 删除amd64平台下的与centos7无关的目录
    1
    2
    3
    4
    5
    6
    7
    rm -rvf contrib/builder/rpm/amd64/amazonlinux-latest/
    rm -rvf contrib/builder/rpm/amd64/fedora-24/
    rm -rvf contrib/builder/rpm/amd64/fedora-25/
    rm -rvf contrib/builder/rpm/amd64/opensuse-13.2/
    rm -rvf contrib/builder/rpm/amd64/oraclelinux-6/
    rm -rvf contrib/builder/rpm/amd64/oraclelinux-7/
    rm -rvf contrib/builder/rpm/amd64/photon-1.0/
  • 修改contrib/builder/rpm/amd64/centos-7/Dockerfile
    由于docker 17.04.0-ce这个版本是由golang1.7.5这个版本所编译的,所以在centos容器中需要安装golang1.7.5,而golangtc.com上没有1.7.5这个版本,所以只能通过vpn去golang.org上获取
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    diff --git a/contrib/builder/rpm/amd64/centos-7/Dockerfile b/contrib/builder/rpm/amd64/centos-7/Dockerfile
    index 5986dfd..b1f024d 100644
    --- a/contrib/builder/rpm/amd64/centos-7/Dockerfile
    +++ b/contrib/builder/rpm/amd64/centos-7/Dockerfile
    @@ -9,7 +9,9 @@ RUN yum -y swap -- remove systemd-container systemd-container-libs -- install sy
    RUN yum install -y btrfs-progs-devel device-mapper-devel glibc-static libseccomp-devel libselinux-devel libtool-ltdl-devel pkgconfig selinux-policy selinux-policy-devel systemd-devel tar git cmake vim-common

    ENV GO_VERSION 1.7.5
    +ENV https_proxy 192.168.1.230:1080
    RUN curl -fSL "https://golang.org/dl/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
    +# RUN curl -fSL "https://www.golangtc.com/static/go/${GO_VERSION}/go${GO_VERSION}.linux-amd64.tar.gz" | tar xzC /usr/local
    ENV PATH $PATH:/usr/local/go/bin

    ENV AUTO_GOPATH 1
  • 使用Makefile编译Moby
    完成了上述Dockerfile的修改以后,可以使用make进行编译,由于我们需要编译的是rpm安装包,所以可直接使用make rpm进行。更多编译操作,可通过make help查看。

*** 目前,Moby的编译主要问题处在GFW对网络封锁上,如遇到其它问题,请第一时间确认该网络是否能访问,尤其是中国大陆地区。 ***

参考&鸣谢

nfs-ganesha.jpg

Ceph是统一存储,包括块、文件、对象。其中块存储必须映射给linux内核,然后才能用,而内核客户端代码的更新收到了linus的限制,已经好久没更新了。librbd又不能直接用,不过道是可以用nbd映射一下使用。cephfs目前还不太适合生产环境。目前*nix对nfs的支持还是很全面的,而对librbd、cephfs的支持就是大不一样了。so,使ceph支持nfs协议很有意义了,but!ceph是分布式存储,被ganesha一搞,出现了单节点问题,所以只能想办法从HA角度解决了,这也是一种无奈,唉。。。

(~~一丝光~~)从nfs v4.1开始支持并行存储,这或许是一缕新曙光。

NFS-Ganesha架构

ceph-nfs-service-by-rgw-with-nfs-ganesha.png

实施

RGW搭建

关于RGW的安装搭建请于Ceph官网查看

用户创建

创建一个S3用户专门服务于nfs-ganesha

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
$ sudo radosgw-admin --uid=nfs_ganesha_user --display="User for NFS-Ganesha"
{
"user_id": "nfs_ganesha_user",
"display_name": "User for NFS-Ganesha",
"email": "",
"suspended": 0,
"max_buckets": 1000,
"auid": 0,
"subusers": [],
"keys": [
{
"user": "nfs_ganesha_user",
"access_key": "xxxxxxxxxxxxxxxxxxxx",
"secret_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}
],
"swift_keys": [],
"caps": [],
"op_mask": "read, write, delete",
"default_placement": "",
"placement_tags": [],
"bucket_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"user_quota": {
"enabled": false,
"max_size_kb": -1,
"max_objects": -1
},
"temp_url_keys": []
}

useraccess_keysecret_key是后续nfs-ganesha配置是需要使用到的。

nfs-ganesha编译部署

获取Project

1
2
3
4
5
6
7
8
9
// clone nfs-ganesha project
$ git clone https://github.com/nfs-ganesha/nfs-ganesha.git
$ cd ./nfs-ganesha

// 切换到v2.4 stable版本
$ git checkout V2.4-stable

// 获取submodule libntirpc
$ git submodule update --init

在正式开始编译前,需要安装一些包,libntirpc强制使用了GSS,使用-DUSE_GSS=OFF是不能关闭GSS使用的。所以在编译nfs-ganesha时不需要关闭GSS。
本人编译使用的系统是rhel7.2,需要安装一些软件包krb5-libs-1.14.1-27.el7_3.x86_64krb5-devel-1.14.1-27.el7_3.x86_64libgssglue-0.4-2.el7.nux.x86_64libgssglue-devel-0.4-2.el7.nux.x86_64

编译安装

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
// libntirpc 编译 
$ cd ./nfs-ganesha/src/libntirpc
$ cmake ./
...
$ make
...

// nfs-ganesha 编译
$ cd ./nfs-ganesha
$ mkdir ./build
$ cd ./build
$ cmake -DUSE_NLM=OFF -DRGW_PREFIX=/usr -DUSE_FSAL_RGW=ON -DUSE_FSAL_CEPH=OFF -DCMAKE_INSTALL_PREFIX=/home/xxxxx/.local -DUSE_GSS=ON -DUSE_FSAL_ZFS=OFF -DUSE_NFSIDMAP=OFF -DUSE_FSAL_GLUSTER=OFF ../src
...
$ make
...
$ make install
...

// 为了能让ganesha正常运行,需要更新一下ld.cache,保证动态库可以正常加载
$ sudo echo "/home/xxxxx/.local/lib64/ganesha" >> /etc/ld.so.conf
$ sudo ldconfig -v

//生成RPM包
$ cpack -G RPM
...

配置启动

配置RGW的Keyring

librgw 访问ceph时会用到keyring,它回去/var/lib/ceph/radosgw/ceph-admin这个目录下去找keyring,这个目录需要用户自己创建并,copy一个keyring进去,这个keyring可以是admin,也可以是rgw实用的keyring,从权限管理角度建议使用rgw的keyring

撰写ganesha.conf.rgw

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
34
35
36
37
38
39
40
41
42
###################################################
#
# EXPORT
#
# To function, all that is required is an EXPORT
#
# Define the absolute minimal export
#
###################################################

EXPORT
{
# Export Id (mandatory, each EXPORT must have a unique Export_Id)
Export_Id = 1;

# Exported path (mandatory)
Path = "nfs_bucket";

# Pseudo Path (required for NFS v4)
Pseudo = "/nfs_bucket";

# Required for access (default is None)
# Could use CLIENT blocks instead
Access_Type = RW;
Protocols = 4;
Transports = TCP;

# Exporting FSAL
FSAL {
# Name = VFS;
Name = RGW;
User_Id = "xxxxxxxxxxx";
Access_Key_Id = "xxxxxxxxxxxxxxxxxxxx";
Secret_Access_Key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
}
}

RGW {
ceph_conf = "/etc/ceph/ceph.conf";
name = "client.admin";
cluster = "ceph";
}

运行ganesha进程

1
sudo ganesha.nfsd -f /home/xxxxx/.local/etc/ganesha/ganesha.conf.rgw -F -L /var/log/ganesha.log

客户端连接

1
sudo mount -t nfs4 192.168.1.82:/nfs_bucket /mnt

docker容器部署

镜像

ananace/nfs-ganesha-ceph

gannesha配置

配置上来讲与编译部署没有太大差别

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
34
35
# NFS protocol options
EXPORT
{
# Export Id (mandatory, each EXPORT must have a unique Export_Id)
Export_Id = 77;

# Exported path (mandatory)
Path = /;

# Pseudo Path (for NFS v4)
Pseudo = /;

# Access control options
Access_Type = RW;
Squash = No_Root_Squash;

# NFS protocol options
SecType = "sys";
Transports = TCP;
Protocols = 4;

# Exporting FSAL
FSAL {
Name = RGW;
User_Id = "admin";
Access_Key_Id = "8I4K2USDV5SK3UFLQUB0";
Secret_Access_Key = "A4JuvB468tmnDpmkZMfwesb2zmGZeSiCJlzJMALc";
}
}

RGW {
cluster = "ceph";
ceph_conf = "/etc/ceph/ceph.conf";
name = "client.rgw.host-10-100-13-111";
}

运行ganesha容器

由于Ganesha 的 FSAL 使用到了librgw,所以在镜像中会装好ceph-commonlibrgw2等。

1
docker run -d --net=host  -v /home/xxx/ceph/etc_ceph/:/etc/ceph:ro -v /home/xxx/ceph/ganesha/:/etc/ganesha:ro -v /home/xxx/ceph/var_lib_ceph/:/var/lib/ceph --name nfs -e GANESHA_BOOTSTRAP_CONFIG=no ananace/nfs-ganesha-ceph

docker 启动是需要设置环境变量GANESHA_BOOTSTRAP_CONFIG=no,默认配置为yes;若为yes的化,nfs-ganesha在启动的时候会重置/etc/ganesha/ganesha.conf配置文件。

参考&鸣谢

gpg_usage.png
加密的一个简单但又实用的任务就是发送加密电子邮件。多年来,为电子邮件进行加密的标准一直是PGP(Pretty Good Privacy)。程序员Phil Zimmermann特别为电子邮件的保密编写的PGP。这个软件非常好用,迅速流传开来,成了许多程序员的必备工具。但是,它是商业软件,不能自由使用。作为PGP的替代,如今已经有一个开放源代码的类似产品可供使用。GPG(Gnu Privacy Guard),它不包含专利算法,能够无限制的用于商业应用。

环境

  • RHEL7.2
    默认以安装GPG
  • GPG 2.0.22
    libgcrypt 1.5.3

使用方法

生成密钥

使用--gen-key生成一副新的密钥对

1
2
3
4
5
6
7
8
9
10
11
$ gpg --gen-key
gpg (GnuPG) 2.0.22; Copyright (C) 2013 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

请选择您要使用的密钥种类:
(1) RSA and RSA (default)
(2) DSA and Elgamal
(3) DSA (仅用于签名)
(4) RSA (仅用于签名)
您的选择?

第一段是版权声明,然后让用户自己选择加密算法。默认选择第一个选项,表示加密和签名都使用RSA算法。

1
2
RSA 密钥长度应在 1024 位与 4096 位之间。
您想要用多大的密钥尺寸?(2048)

这一步要让我们输入密钥长度,长度越长越安全,默认为2048。

1
2
3
4
5
6
7
8
您所要求的密钥尺寸是 2048 位              
请设定这把密钥的有效期限。
0 = 密钥永不过期
<n> = 密钥在 n 天后过期
<n>w = 密钥在 n 周后过期
<n>m = 密钥在 n 月后过期
<n>y = 密钥在 n 年后过期
密钥的有效期限是?(0)

如果密钥只是个人使用,并且你很确定可以有效保管私钥,建议选择第一个选项,即永不过期。注意,如果想设置在2年后过期,那么应该输入2y,然后回车 回答完上面三个问题以后,系统让你确认。

1
2
密钥于 日 10/ 8 11:20:32 2017 CST 过期
以上正确吗?(y/n)

到这里,我们对要生成的密钥的配置已经完成了,然后我们还需要一个标识,接下来按照要求依次输入就行了

1
2
3
4
5
6
7
8
9
10
11
12
You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>"

真实姓名:Zhang San
电子邮件地址:zhangsan@x163.com
注释:

您选定了这个用户标识:
“Zhang San <zhangsan@163.com>”

更改姓名(N)、注释(C)、电子邮件地址(E)或确定(O)/退出(Q)?

输入o确定,然后在弹出的窗口输入密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
我们需要生成大量的随机字节。这个时候您可以多做些琐事(像是敲打键盘、移动
鼠标、读写硬盘之类的),这会让随机数字发生器有更好的机会获得足够的熵数。
.+++++
...+++++
我们需要生成大量的随机字节。这个时候您可以多做些琐事(像是敲打键盘、移动
鼠标、读写硬盘之类的),这会让随机数字发生器有更好的机会获得足够的熵数。
....................+++++
...........+++++
gpg: /home/XXXX/.gnupg/trustdb.gpg:建立了信任度数据库
gpg: 密钥 74A64469 被标记为绝对信任
公钥和私钥已经生成并经签名。

gpg: 正在检查信任度数据库
gpg: 需要 3 份勉强信任和 1 份完全信任,PGP 信任模型
gpg: 深度:0 有效性: 1 已签名: 0 信任度:0-,0q,0n,0m,0f,1u
gpg: 下次信任度数据库检查将于 2017-10-08 进行
pub 2048R/74A64469 2016-10-08 [有效至:2017-10-08]
密钥指纹 = 2187 78CA 2E78 83C2 039C E47B D94A 622A 74A6 5569
uid Zhang San <zhangsan@163.com>
sub 2048R/490E5BC8 2016-10-08 [有效至:2017-10-08]

请注意上面的字符串”74A64469”,这是”用户ID”的Hash字符串,可以用来替代”用户ID”。到此为止,我们已经完成了生成公钥和私钥的任务了,文件在/home/XXXX/.gnupg/pubring.gpg

这时,最好再生成一张”撤销证书”,以备以后密钥作废时,可以请求外部的公钥服务器撤销你的公钥。使用--gen-revoke 74A64469进行,此处不再赘述。

查看密钥列表

使用--list-keys列出当前用户已有的密钥

1
2
3
4
5
6
$ gpg --list-keys
/home/zhoub/.gnupg/pubring.gpg
------------------------------
pub 2048R/6058BC49 2017-05-16
uid zhou bo (for coreos build) <zhoubofsy@hotmail.com>
sub 2048R/A47EF901 2017-05-16

第一行显示公钥文件名(pubring.gpg),第二行显示公钥特征(2048位,RSA,Hash字符串和生成时间),第三行显示”用户ID”,第四行显示私钥特征(格式与公钥相同)。
如果你要从密钥列表中删除某个密钥,可以使用delete-key参数gpg --delete-key [用户ID]

输出密钥

** 公钥导出 **
公钥文件(.gnupg/pubring.gpg)以二进制形式储存,armor参数可以将其转换为ASCII码显示。gpg --armor --output public-key.txt --export [用户ID]

** 私钥导出 **
export-secret-keys参数可以转换私钥

1
gpg --armor --output private-key.txt --export-secret-keys

上传公钥

公钥服务器是网络上专门储存用户公钥的服务器。send-keys参数可以将公钥上传到服务器gpg --send-keys [用户ID] --keyserver hkp://subkeys.pgp.net使用上面的命令,你的公钥就被传到了服务器subkeys.pgp.net,然后通过交换机制,所有的公钥服务器最终都会包含你的公钥。

由于公钥服务器没有检查机制,任何人都可以用你的名义上传公钥,所以没有办法保证服务器上的公钥的可靠性。通常,你可以在网站上公布一个公钥指纹,让其他人核对下载到的公钥是否为真。fingerprint参数生成公钥指纹。gpg --fingerprint [用户ID]

输入密钥

除了生成自己的密钥,还需要将他人的公钥或者你的其他密钥输入系统。这时可以使用import参数gpg --import [密钥文件]

为了获得他人的公钥,可以让对方直接发给你,或者到公钥服务器上寻找gpg --keyserver hkp://subkeys.pgp.net --search-keys [用户ID]
或者

1
2
3
4
5
6
$ gpg --recv-keys E82E4209
gpg: 下载密钥‘E82E4209’,从 hkp 服务器 keys.gnupg.net
gpg: 密钥 E82E4209:公钥“Vladimir 'phcoder' Serbinenko <phcoder@gmail.com>”已导入
gpg: 没有找到任何绝对信任的密钥
gpg: 合计被处理的数量:1
gpg: 已导入:1

加密和解密

使用--encrypt参数用于加密。假设有一个原始文件test.txt

** 使用公钥(Hash字符串)加密:**

1
$ gpg --recipient 74A64469 --output test_en.txt --encrypt test.txt
  • --recipient参数指定接收者的公钥
  • --output参数指定加密后的文件名
  • --encrypt参数指定源文件

** 使用私钥解密:**

1
2
3
4
5
6
7
8
9
10
11
$ gpg test_en.txt

bogon:Desktop XXXX$ gpg test_en.txt

您需要输入密码,才能解开这个用户的私钥:“Zhang San <zhangsan@163.com>”
2048 位的 RSA 密钥,钥匙号 490E5BC8,建立于 2016-10-08 (主钥匙号 74A64469)

gpg: 由 2048 位的 RSA 密钥加密,钥匙号为 490E5BC8、生成于 2016-10-08
“Zhang San <zhangsan@163.com>”
gpg: test_en.txt:未知的后缀名
请输入新的文件名 [test.txt]: test2.txt

签名

有时,我们不需要加密文件,只需要对文件签名,表示这个文件确实是我本人发出的。--sign参数用来签名

1
$ gpg --sign test.txt

然后生成了一个test.txt.gpg文件,我们打开这个文件后,发现这也是一个二进制的数据,这并不是加密后的数据,与上边的二进制数据不一样。
如果想生成ASCII码的签名文件,可以使用--clearsign参数

1
$ gpg --clearsign test.txt

然后生成了一个test.txt.asc文件。
如果想生成单独的签名文件,与文件内容分开存放,可以使用--detach-sign参数

1
$ gpg --detach-sign test.txt

多产生一个,sig的二进制签名文件,如果想采用ASCII码形式,要加上--armor参数。

签名+加密

如果想同时签名和加密,可使用这种格式gpg --local-user [发信者ID] --recipient [接收者ID] --armor --sign --encrypt test.txt

  • --local-user参数指定用发信者的私钥签名
  • --recipient参数指定用接收者的公钥加密
  • --armor参数表示采用ASCII码形式显示
  • --sign参数表示需要签名
  • --encrypt参数表示指定源文件

验证签名

收到别人签名后的文件,需要用对方的公钥验证签名是否为真。使用--verify参数用来验证

1
2
3
4
5
6
gpg --verify test.txt.sig test.txt
gpg: Signature made Wed 17 May 2017 04:12:50 PM CST using RSA key ID 74A64469
gpg: Good signature from "Zhang San <zhangsan@163.com>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg: There is no indication that the signature belongs to the owner.
Primary key fingerprint: 0B77 E327 B365 316D F112 4521 ABC0 9164 6058 BC49

参考&鸣谢

coreos_build.jpg

介绍

CoreOS,一个轻量的linux分布式操作系统。CoreOS中的应用都以容器形式在系统中运行。个人觉得CoreOS不能算是一个linux发行版,只能算是一种订制系统。(BTW,CoreOS的订制应该是源自Gentoo这个发行版)

环境

  • CoreOS的编译与操作系统无关(此处使用的是RHEL7.2),硬件要求必须是X86-64系统。
  • 编译系统必须安装 curl、git、python2、bzip2
  • CoreOS SDK编译有两种方法(Option1 & Option2),Option1 需要选择和需要编译的CoreOS相匹配的golang版本。
  • 使用Option2 方法安装,需要设置https_proxy代理(否则,无法法访问GoogleSource)

制作&安装

本人使用Option2方法完成CoreOS编译,并使用coreos-install安装CoreOS到xen vm上。

制作

准备

安装Repo

下载repo,用于下载CoreOS SDK

1
2
curl http://storage.googleapis.com/git-repo-downloads/repo > ./repo
chmod a+x ./repo

下载SDK

创建一个目录用于存放coreos sdk

1
2
mkdir coreos
cd coreos

repo初始化目录,并同步git repos

1
2
repo init -u https://github.com/coreos/manifest.git
repo sync

Chroot到coreos SDK

1
./chromite/bin/cros_sdk

编译

设置core用户登陆密码

设置core用户密码,已被系统安装完后登陆系统使用。

1
./set_shared_user_password.sh

选择目标编译平台

选择amd64-usrarm64-usr

1
./setup_board --default --board=amd64-usr

编译Packages

1
./build_packages

制作CoreOS镜像

可以选择编译开发版(dev)还是生成版(prod)

1
./build_image prod

制作可以引导CoreOS镜像

1
./image_to_vm.sh --board=amd64-usr --format=xen --from=/mnt/host/source/src/build/images/amd64-usr/developer-1409.0.0+2017-05-15-1456-a1/coreos_production_image.bin --disk_layout=base
  • --format 根据不同需要选择制作不同格式的可引导镜像
  • --disk_layout 默认是空,建议选择base

安装

*** 经过上述步骤的操作,已经将coreos镜像制作完成,并放置在”./src/build/images/amd64-usr”目录下。 ***

安装的过程需要使用liveCD启动,然后挂在需要安装的裸盘,使用coreos-install安装。镜像的传递是,先将镜像放置到一个webserver上,然后coreos-install再通过这个webserver下载、并安装。

*** 在将镜像放置到webserver之前,需要先将镜像压缩成bz2文件,然后再将bz2进行签名 ***(签名无法使用官方的私钥,只能自己申请,详细请见)。

步骤

*** 本人的安装步骤,不是唯一的、方便、快捷的方法,更多安装方式建议阅者自行调研 ***

下载coreos-install安装脚本

1
wget https://raw.githubusercontent.com/coreos/init/master/bin/coreos-install

修改coreos-install中的公钥

在coreos-install中有个变量GPG_KEY用于存储官方的公钥,需要将这个公钥换成你给镜像签名时对应的公钥。

** 此处略去俺公钥内容 **

镜像在下载到目标机后,coreos-install会对下载的镜像进行校验,这是不替换公钥,只能想办法让官方帮你签名

使用coreos-install安装CoreOS

使用liveCD启动系统后,先确认需要安装CoreOS的硬盘,然后执行coreos-install进行安装

1
sudo ./coreos-install -d /dev/xvda -V 1409.0.0 -o xen -v -b http://192.168.1.82:8000 -c /media/configdrive/openstack/latest/user_data
  • -d 指定安装目标设备
  • -V 指定安装版本,一定要与URL中的路径能匹配上
  • -o 镜像名称中需要指出是xenorkvmor…
  • -b 镜像下载URL

问题&解决

不能引导问题

若成功编译安装,但不能正常引导,可以对比做bzip2之前镜像的前512字节和安装后xvda的前512字节是否一致。本人遇到不能引导问题,是因为本应使用bzip2压缩的镜像使用了tar

参考&鸣谢

  • 感谢同事为我提供的VPN翻墙服务
  • 感谢同事为我提供的WebService服务,方便完成网络安装CoreOS
  • container linux document

iptables是一个配置Linux内核防火墙的命令行工具,是netfilter项目的一部分。术语iptables也经常代指该内核级防火墙。iptables用于ipv4,ip6tables用于ipv6。
( nftables已经包含在Linux kernel 3.13中,以后会取代iptables成为主要的Linux防火墙工具。 )

版本

iptables v1.4.21

介绍

理解 iptables 如何工作的关键是下面这张图。图中在上面的小写字母代表”表”,在下面的大写字母代表”链”。从任何网络端口进来的每一个 IP 数据包都要从上到下的穿过这张图。一种常见的困扰是认为 iptables 对从内部端口进入的数据包和从面向互联网端口进入的数据包采取不同的处理方式,相反,iptabales 对从任何端口进入的数据包都会采取相同的处理方式。可以定义规则使 iptables 采取不同的方式对待从不同端口进入的数据包。当然一些数据包是用于本地进程的,因此在图中表现为从顶端进入,到Local Process停止,而另一些数据包是由本地进程生成的,因此在图中表现为从Local Process发出,一直向下穿过该流程图。

iptables_traverse.jpg

  1. 数据包到达网络接口,比如 eth0
  2. 进入 raw 表的 PREROUTING 链,这个链的作用是赶在连接跟踪之前处理数据包
  3. 如果进行了连接跟踪,在此处理
  4. 进入 mangle 表的 PREROUTING 链,在此可以修改数据包,比如 TOS 等
  5. 进入 nat 表的 PREROUTING 链,可以在此做DNAT,但不要做过滤
  6. 决定路由,看是交给本地主机还是转发给其它主机
    一种情况是,数据包就是发给本地主机的:
  7. 进入 mangle 表的 INPUT 链,这里是在路由之后,交由本地主机之前,我们也可以进行一些相应的修改
  8. 进入 filter 表的 INPUT 链,在这里我们可以对流入的所有数据包进行过滤,无论它来自哪个网络接口
  9. 交给本地主机的应用程序进行处理
  10. 处理完毕后进行路由决定,看该往那里发出
  11. 进入 raw 表的 OUTPUT 链,这里是在连接跟踪处理本地的数据包之前
  12. 连接跟踪对本地的数据包进行处理
  13. 进入 mangle 表的 OUTPUT 链,在这里我们可以修改数据包,但不要做过滤
  14. 进入 nat 表的 OUTPUT 链,可以对防火墙自己发出的数据做 NAT
  15. 再次进行路由决定
  16. 进入 filter 表的 OUTPUT 链,可以对本地出去的数据包进行过滤
  17. 进入 mangle 表的 POSTROUTING 链,到这里已经做完了所有的路由决定,但数据包仍然在本地主机,我们还可以进行某些修改
    注意,这里不光对经过防火墙的数据包进行处理,还对防火墙自己产生的数据包进行处理
  18. 进入 nat 表的 POSTROUTING 链,在这里一般都是用来做 SNAT ,不要在这里进行过滤
  19. 进入出去的网络接口
    一种情况是,数据包要转发给其它主机:
  20. 进入 mangle 表的 FORWARD 链,这里也比较特殊,这是在第一次路由决定之后,在进行最后的路由决定之前,我们仍然可以对数据包进行某些修改
  21. 进入 filter 表的 FORWARD 链,在这里我们可以对所有转发的数据包进行过滤。需要注意的是:经过这里的数据包是转发的,方向是双向的
  22. 进入 mangle 表的 POSTROUTING 链,到这里已经做完了所有的路由决定,但数据包仍然在本地主机,我们还可以进行某些修改
  23. 进入 nat 表的 POSTROUTING 链,在这里一般都是用来做 SNAT ,不要在这里进行过滤
  24. 进入出去的网络接口

该流程图描述链了在任何接口上收到的网络数据包是按照怎样的顺序穿过表的交通管制链。第一个路由策略包括决定数据包的目的地是本地主机(这种情况下,数据包穿过 INPUT 链),还是其他主机(数据包穿过 FORWARD 链);中间的路由策略包括决定给传出的数据包使用那个源地址、分配哪个接口;最后一个路由策略存在是因为先前的 mangle 与 nat 链可能会改变数据包的路由信息。数据包通过路径上的每一条链时,链中的每一条规则按顺序匹配;无论何时匹配了一条规则,相应的 target/jump 动作将会执行。最常用的3个 target 是 ACCEPT, DROP ,或者 jump 到用户自定义的链。内置的链有默认的策略,但是用户自定义的链没有默认的策略。在 jump 到的链中,若每一条规则都不能提供完全匹配,那么数据包像这张图片描述的一样返回到调用链。在任何时候,若 DROP target 的规则实现完全匹配,那么被匹配的数据包会被丢弃,不会进行进一步处理。如果一个数据包在链中被 ACCEPT,那么它也会被所有的父链 ACCEPT,并且不再遍历其他父链。然而,要注意的是,数据包还会以正常的方式继续遍历其他表中的其他链。

iptables_user_kernel_traverse.png

表(Tables)

  • raw 用于配置数据包,raw 中的数据包不会被系统跟踪。
    作用:决定数据包是否被状态跟踪机制处理,优先级最高,设置raw时一般是为了不再让iptables做数据包的链接跟踪处理,提高性能
    内核模块:iptable_raw
    chains:OUTPUT, PREROUTING
  • filter 是用于存放所有与防火墙相关操作的默认表。
    作用:过滤数据包
    内核模块:iptables_filter
    chains:INPUT, FORWARD, OUTPUT
  • nat 用于网络地址转换(例如:端口转发)。
    作用:网络地址转换
    内核模块:iptables_nat
    chains:PREROUTING, POSTROUTING, OUTPUT
  • mangle 用于对特定数据包的修改。
    作用:修改数据包的服务类型、TTL、并且可以配置路由实现QOS
    内核模块:iptable_mangle
    chains:PREROUTING, POSTROUTING, INPUT, OUTPUT, FORWARD
  • security 用于强制访问控制网络规则。
    Todo…

表之间的顺序:raw –> mangle –> nat –> filter

链(Chains)

  • 表由链组成,链是一些按顺序排列的规则的列表。
    默认的filter表包含INPUTOUTPUTFORWARD3条内建的链,这3条链作用于数据包过滤过程中的不同时间点,nat表包含PREROUTINGPOSTROUTINGOUTPUT链。
  • 默认情况下,任何链中都没有规则,可以向链中添加自己想用的规则
    链的默认规则通常设置为ACCEPT,如果想确保任何包都不能通过规则集,那么可以重置为DROP。默认的规则总是在一条链的最后生效,所以在默认规则生效前数据包需要通过所有存在的规则。
  • 用户可以加入自己定义的链,从而使规则集更有效并且易于修改。

规则(Rules)

数据包的过滤基于 规则。规则由一个目标(数据包包匹配所有条件后的动作)和很多匹配(导致该规则可以应用的数据包所满足的条件)指定。一个规则的典型匹配事项是数据包进入的端口(例如:eth0 或者 eth1)、数据包的类型(ICMP, TCP, 或者 UDP)和数据包的目的端口。

目标使用-j或者--jump选项指定。目标可以是用户定义的链(例如,如果条件匹配,跳转到之后的用户定义的链,继续处理)、一个内置的特定目标或者是一个目标扩展。内置目标是 ACCEPT, DROP, QUEUE 和 RETURN,目标扩展是 REJECT and LOG。如果目标是内置目标,数据包的命运会立刻被决定并且在当前表的数据包的处理过程会停止。如果目标是用户定义的链,并且数据包成功穿过第二条链,目标将移动到原始链中的下一个规则。目标扩展可以被终止(像内置目标一样)或者不终止(像用户定义链一样)。

策略(police)

Usage

iptables_usage_main.jpg

iptables_usage_param.jpg

示例

Todo…

参考&鸣谢

依托现有虚拟化资源引入docker容器机制

xen_container_plan.png

  • scheduler
    根据VM宿主机的负载或其他情况,决定容器运行在哪个VM上。
  • monitor
    监控VM宿主机和容器健康情况,当有容器或宿主机宕掉时,报告想过情况,其他模块根据monitor的报告做出相应的响应。
  • controller
    控制容器的启动停止、迁移、扩展等操作
  • docker engine
    ……
  • pipework
    管理容器网络,目前pipework支持手动配置容器ip,网关及路由;对DHCP支持存在问题;(考虑将pipework以plugin方式加入docker engine)
  • xe-daemon
    采集容器监控数据,并通过xenbus将数据发给xenserver

方案

网络

xen_container_network_plan.png

  • 宿主机有两个网络,一个管理网,一个数据网,宿主机内的所有容器使用数据网络
  • 利用linux自带的bridge将宿主机的数据网与容器的veth pair相连,由此二层网络联通
  • 宿主机开通ip_forward,实现三层转发
  • 容器通过pipework脚本完成ip地址、路由、网关的配置

网络操作流程

  1. 根据网段创建bridge
    使用docker network create --gateway {宿主机IP} --subnet {宿主机子网} -d bridge -o com.docker.network.bridge.name="{网桥设备名称}" {DockerNetwork名称}创建bridge。
    eg:
    1
    2
    3
    4
    5
    $ docker network create --gateway 10.37.129.4 --subnet 10.37.129.0/24 -d bridge -o com.docker.network.bridge.name="xennet0" xennet
    $ brctl show
    bridge name bridge id STP enabled interfaces
    docker0 8000.0242df249633 no
    xennet0 8000.0242688f16c2 no
  2. 桥接出口网卡
    使用brctl addif {bridge名称} {网卡名称}将出口网卡添加到指定网桥中,此处应根据xscontainer使用用户的权限决定是否需要增加sudo操作
    eg:
    1
    2
    3
    4
    5
    $ sudo brctl addif xennet0 enp0s6
    $ brctl show
    bridge name bridge id STP enabled interfaces
    docker0 8000.0242df249633 no
    xennet0 8000.0242688f16c2 no enp0s6
  3. 创建固定IP容器
    创建容器需要分配固定IP,该IP需要业务层分配,并与bridge同网段。
    eg:
    1
    2
    3
    4
    5
    6
    7
    8
    $ docker run -d --name c1 --network xennet --ip 10.37.129.100 centos:7.3.1611 /usr/sbin/init
    $ docker run -d --name c2 --network xennet --ip 10.37.129.200 centos:7.3.1611 /usr/sbin/init
    $ brctl show
    bridge name bridge id STP enabled interfaces
    docker0 8000.0242df249633 no
    xennet0 8000.0242688f16c2 no enp0s6
    veth990ee82
    vethf1f6fcb

宿主机重启流程

*** 由于宿主机重启会导致bridge中记录的桥接出口网卡信息丢失,所以在宿主机重启后需要重新桥接出口网卡 ***

  1. 桥接出口网卡
    同上
  2. 恢复原有容器
    使用docker run or docker start恢复容器

共享存储

通过虚拟机挂载共享存储

  • 需要记录虚机挂载共享存储与容器的对应关系
  • 在创建、迁移、删除容器时,需要对相关存储资源进行分配或回收

容器直接挂载共享存储

  • 一般容器
    使用RBD/NBD-RBD块设备存储,或NFS存储。对于RBD/NBD-RBD方式需要添加驱动;对于NFS方式docker原生支持。
  • Registry容
    使用RGW对象存储(S3/Swift),测试中。。。

监控

XenContainer

调度

接口

服务发现(DNS)、负载均衡、容器镜像库均已容器形式提供服务。

xen_container_flow.png

  • scheduler、monitor、controller 从头写吧
  • docker engine 需要修改一些api接口
  • pipework 需要从shell改成golang,以plugin方式加入docker engine
  • xe-daemon 需要增加ip地址采集
  • DNS Container 从头写吧

*** 技术相关调研,请见下节“参” ***

PIPEWORK 是一个简单易用的Docker容器网络配置工具,由Docker的工程师Jérôme Petazzoni开发。通过使用ip、brctl、ovs-vsctl等命令来为Docker容器配置自定义的网桥、网卡、路由等。

docker-network-pipework-frame.png

  • 使用新建的br0网桥替代默认的docker0网桥
  • 容器网络类型选择none

配置步骤

  1. 在Github上获取pipework
    Todo
  2. 创建网桥
    1
    $ sudo brctl addbr br0
  3. 连接宿主机网卡
    1
    $ sudo brctl addif eth1
  4. 设置网桥ip,并激活网桥
    1
    2
    $ sudo ip addr add 192.168.6.66/24 dev br0
    $ sudo ip link set dev br0 up
  5. 创建容器,网络类型指定为none
    1
    $ docker run -it --rm --net none --name nonenet_1 192.168.6.109:5000/my_ubuntu:v1 /bin/bash
  6. 使用PIPEWORK创建容器网络
    1
    2
    3
    4
    5
    $ sudo ./pipework br0 nonenet_1 192.168.6.67/24@192.168.6.1
    $ brctl show
    bridge name bridge id STP enabled interfaces
    br0 8000.8ef4503cf344 no eth1
    veth1pl7289

鸣谢&参考

什么是服务发现

容器可以对其它容器提供服务,如mysql(数据库服务)、nginx、mangodb等等。这些服务都可以通过IP+Port方式访问。由于容器会经常重建迁移,所以IP会发生变化,如果调用服务的程序使用IP访问就会很不方便,为此可以使用DNS、zookeeper、etcd等技术实现服务访问与IP的解耦。这种解耦方式叫做服务发现。

版本

  • Kubernetes v1.4.0
  • kube-dns 1.9(image tag)

Kubernetes服务发现概述

k8s-service-discovery.png

  • Kube-dns 以容器的形式为K8s集群提供DNS服务
  • Etcd用于存储K8s中的服务、容器等信息,Kube-dns的数据也是从Etcd中获取的
  • healthz 监控DNS Pod中的Kube-dns、dnsmasq容器
  • dnsmasq以代理形式将外部访问转发给Kube-dns容器,走10053端口

Kube-dns

k8s-kube-dns-analysis.png

  • miekg监听10053端口,接收dnsmasq转发过来的DNS请求
  • KubeDNS中的Cache模块从Etcd中获取Service和Namespace信息,用户完成Service域名到Service地址的映射
  • SkyDNS Server 完成DNS协议交互逻辑
    该模块在KubeDNS启动时将自身注册到miekg中;并实现接口ServeDNS,当miekg收到DNS请求后,将调用该接口进行处理。
  • KubeDNS中模块实现了backend接口
    在ServeDNS处理DNS请求时,需要调用backend接口的Records和ReverseRecord method,backend接口会比对Cache中的内容,然后返回结果。

参考&鸣谢