我的环境是这样:

一台4G内存的MacBook,一台16G内存的Thinkpad。

我的devstack需要8G内存,放在Thinkpad上,然后我需要把虚拟机(devstack)中的/opt/stack文件夹挂载在我的MacBook上。于是就想到了sshfs。

sshfs官网介绍是这样说的:

SSH is a secure protocol for communicating between machines. SSHFS is a tool that uses SSH to enable mounting of a remote filesystem on a local machine; the network is (mostly) transparent to the user. Because SSHFS authenticates connections, you can be sure that only those who should have access to remote directories can mount them (as long as everything is configured properly). Because SSH encrypts connections, no one can see your files as they are transferred over the network. And because SSHFS is built using FUSE, even your own root user can only see your files by logging in to your account with su.

准备工作(devstack上)

  1. 修改 root 密码
1
sudo passwd root  
  1. 以其他账户登录,通过 sudo nano 修改 /etc/ssh/sshd_config :
1
2
3
xxx@ubuntu14:~$ su - root
Password:
root@ubuntu14:~# vi /etc/ssh/sshd_config
  1. 注释掉 #PermitRootLogin without-password,添加 PermitRootLogin yes
1
2
3
4
5
# Authentication:
LoginGraceTime 120
#PermitRootLogin without-password
PermitRootLogin yes
StrictModes yes
  1. 重启 ssh 服务
1
2
3
4
root@ubuntu14:~# sudo service ssh restart
ssh stop/waiting
ssh start/running, process 1499
root@ubuntu14:~#

Macbook

网站下载
下载:osxfuse-2.7.3.dmg http://osxfuse.github.io/
下载:sshfs-2.5.0.pkg https://github.com/osxfuse/sshfs/releases/
都安装了就搞定了

然后可以在MacBook上执行下面命令进行挂载

1
sshfs root@192.168.51.101:/opt/stack /Users/JianqingJiang/Desktop/Dev/vm_devstack/

取消挂载的命令

1
2
sh-3.2# diskutil umount /Users/JianqingJiang/Desktop/Dev/vm_devstack/
Unmount successful for /Users/JianqingJiang/Desktop/Dev/vm_devstack/

不过我遇到了一个问题:
sshfs挂载好的devstack的/opt/stack文件在Mac图形化界面不显示,在terminal中显示

image

在界面上尝试了各种办法还是不行

image

购买国外vps

由于kolla中的image太庞大了,超过10G。而且存放镜像的国外服务器会被block掉,之前试了使用vps搭建pptp的VPN,但是速度只有小几百k。网络出现波动还会断开连接…
所以建议购买比较高性能的vps,在vps中下载好然后再拉回本地。之前购买了1G内存,20G硬盘的阿里北美vps,可惜由于性能原因,下载过程中导致了vps宕机。我查了半天错,以为是Kolla本身的问题。于是我购买了digitalocean的vps,选择2G内存,40G硬盘。可以按小时计费,这点比较好。
vps

vps的带宽和CPU使用情况

vps

build image

Kolla 依赖于以下几个主要组件

  • Ansible > 1.9.4, < 2.0
  • Docker > 1.10.0
  • docker-py > 1.7.0
  • python jinja2 > 2.6.0

几点说明:

  • 机器使用的是vmware虚拟机进行的测试。配置上使用 16G RAM, 4 CPU, 2 网卡的配置
  • 由于使用了 Docker,所以对于底层系统并还没什么要求,本文使用 CentOS 7 系统。
  • Kolla master 分支上使用的是 RDO master 上的源,打包极不稳定,时常会有 Bug 出现。所以本文使用的是 CentOS + 源码的安装方式

加入 Docker 的源

1
2
3
4
5
6
7
8
sudo tee /etc/yum.repos.d/docker.repo << 'EOF'
[dockerrepo]
name=Docker Repository
baseurl=https://yum.dockerproject.org/repo/main/centos/$releasever/
enabled=1
gpgcheck=1
gpgkey=https://yum.dockerproject.org/gpg
EOF

加入 EPEL 源

1
yum install -y epel-release

安装 Kolla 所需依赖

1
2
yum install -y ansible docker-engine git gcc python-setuptools
easy_install -U pip

Docker 现在使用了 shared mount 功能,默认没有打开,需要手动修改 Docker 启动文件 /usr/lib/systemd/system/docker.service 的 MountFlags

1
sed -i 's/MountFlags.*/MountFlags=shared/' /usr/lib/systemd/system/docker.service

启动 Docker 服务

1
2
3
systemctl daemon-reload
systemctl enable docker
systemctl start docker

下载 Kolla 代码并安装依赖

1
2
3
4
git clone https://github.com/openstack/kolla.git
cd kolla
yum install python-devel
pip install -r requirements.txt -r test-requirements.txt tox

Build Docker Image

以下如果没有特别说明,所有的操作都是在 Kolla 项目的目录里进行 首先要先生成并修改配置文件

1
2
tox -e genconfig
cp -rv etc/kolla /etc/

然后修改 /etc/kolla/kolla-build.conf 文件,它是用来控制 kolla build 过程的。修改后,其主要内容如下 :

1
2
3
4
5
[DEFAULT]
base = centos
install_type = source
namespace = lokolla
push = false

目前使用mitaka 分支,master分支不稳定

1
git checkout origin/stable/mitaka

接下来就是进行漫长的 build, 这个过程主要依赖机器性能和网速。如果快的话,20 多分钟就完成。如果有问题的话,会很久。不过依赖于 Docker Build 的 Cache 功能,就算重跑的话,之前已经 Build 好的也会很快完成。

1
./tool/build.py -p default

参数中的 -p default 是指定了只 build 主要的 image, 包括 : mariadb, rabbitmq, cinder, ceilometer, glance, heat, horizon, keystone, neutron, nova, swift 等 . 这些可以只生成的 kolla-build.conf 里找到。

所有image全部下载完成之后如图

iamge

docker save image

由于Kolla的image数量太多,一个个保存太耗费时间,于是就写了个shell脚本

1
2
3
4
5
6
7
8
9
docker images > /root/kolla/images.txt
awk '{print $1}' /root/kolla/images.txt > /root/kolla/images_cut.txt
while read LINE
do
#echo $LINE
docker save $LINE > /home/$LINE.tar
echo ok
done < /root/kolla/images_cut.txt
echo finish

使用rsync拷贝到本地

1
2
CentOS/Fedora/RHEL: yum install rsync
vi /etc/rsyncd.conf

拷贝如下条目,path自己改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
motd file = /etc/rsyncd.motd
log file = /var/log/rsyncd.log
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock

[kolla]
   path = /home/lokolla
   comment = My Very Own Rsync Server
   uid = nobody
   gid = nobody
   read only = no
   list = yes
   auth users = bnc
   secrets file = /etc/rsyncd.scrt

编辑下面文件

1
vi /etc/rsyncd.secrets

输入密码

1
bnc:bnc

/etc/rsyncd.secrets 文件权限必须是600,创建好该文件后可以执行: chmod 600 /etc/rsyncd.secrets

如果开启了iptables防火墙,请将873端口加入防火墙允许规则

1
2
iptables -I INPUT -p tcp --dport 873 -j ACCEPT
iptables -I OUTPUT -p tcp --sport 873 -j ACCEPT

遇到报错

1
2
3
4
rsync error: error starting client-server protocol (code 5) at main.c(1516) [Receiver=3.0.9]
原因及解决办法:
    SELinux;(下面这条命令在服务器端执行)
    setsebool -P rsync_disable_trans on

最后拷贝image到本地成功,速度也OK

1
rsync -vzrtopg --progress -e ssh --delete root@45.55.240.159:/home/lokolla /home

copy

docker load image

docker的镜像被拷贝回本地了。接下去就是把image load回来

同样,也写了shell脚本

1
2
3
4
5
6
7
8
9
10
#!/bin/sh
#============ get the file name ===========
Folder_A="/home/lokolla"
for file_a in ${Folder_A}/*; do
    temp_file=`basename $file_a`
#   echo $temp_file
docker load < /home/lokolla/$temp_file
echo ok
done
echo finish

image

然后docker images一下就发现镜像都OK了

run container

globals.yml

依然是先修改配置文件,与 Deploy 相关的主要是两个配置文件 /etc/kolla/passwords.yml 和 /etc/kolla/globals.yml。他们为 ansible 提供一些变量的设定。主要需要修改的是 globals.yml 文件。修改后,其主要内容为 :

1
2
3
4
5
6
7
8
9
config_strategy: "COPY_ALWAYS"
kolla_base_distro: "centos"
kolla_install_type: "source"
kolla_internal_address: "10.2.0.254"
network_interface: "eth0"
neutron_external_interface: "eth1"
openstack_logging_debug: "true"
enable_cinder: "no"
enable_heat: "no"

passwords.yml

这个密码文件可以使用工具自动生成,也可以手动输入
但是手动输入需要注意格式,在:后需要空一格再输入;而且ssh_key也比较麻烦
所以推荐使用工具自动生成
但是直接输入

1
kolla-genpwd

会提示找不到此命令。这也是官方文档坑的地方,改为:

1
./root/kolla/.tox/genconfig/bin/kolla-genpwd

prechecks

kolla 使用一个名为 kolla-ansible 的封装脚本,可以使用

1
./tools/kolla-ansible prechecks 

来检查一个机器是否满足安装条件。

precheck在其他地方都没什么问题,在IP上会进行下面两步检查,这个IP需要是node之间同一网段的,并且还需要不能被使用的IP地址

1
2
TASK [prechecks : Checking if kolla_internal_vip_address and kolla_external_vip_address are not pingable from any node] 
TASK [prechecks : Checking if kolla_internal_vip_address is in the same network as network_interface on all nodes]

Deploy部署

使用

1
./tools/kolla-ansible deploy

来开始正式安装。

在deploy中,Kolla返回这个log,提示需要在外部的registry。但是all-in-one的安装并不需要registry,这个bother了我很久
我在社区反馈了个bug
https://bugs.launchpad.net/kolla/+bug/1595128

1
Unknown error message: Tag 2.0.2 not found in repository docker.io/kollaglue/centos-source-heka

思路如下:那么我在本地自建一个registry,把下载好的image全部pull到本地的registry。然后把Kolla的外部registry指向我本地的registry

奇怪的是,这个path貌似改不了,比如下载好的image tag是lokolla,这边查找的tag是kollaglue。即使我在/etc/kolla/kolla-build.conf中已经

1
2
[DEFAULT]
namespace = lokolla

这样编辑了

为了指向我的本地registry,在/etc/kolla/kolla-build.conf添加下面的条目

1
2
docker_registry: "localhost:4000"
openstack_release: "latest"

搭建本地registry

1
docker run -d -p 4000:5000 --restart=always --name registry registry:2

注意:kolla使用了4000端口
Kolla looks for the Docker registry to use port 4000. (Docker default is port 5000)
docker 暴露registry端口的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
EXPOSE (incoming ports)

  Dockefile在网络方面除了提供一个EXPOSE之外,没有提供其它选项。下面这些参数可以覆盖Dockefile的expose默认值:

	--expose=[]: Expose a port or a range of ports from the container
            without publishing it to your host
	-P=false   : Publish all exposed ports to the host interfaces
	-p=[]      : Publish a container᾿s port to the host (format:
             ip:hostPort:containerPort | ip::containerPort |
             hostPort:containerPort | containerPort)
             (use 'docker port' to see the actual mapping)
	--link=""  : Add link to another container (name:alias)
    --expose可以让container接受外部传入的数据。container内监听的port不需要和外部host的port相同。比如说在container内部,一个HTTP服务监听在80端口,对应外部host的port就可能是49880.
  操作人员可以使用--expose,让新的container访问到这个container。具体有三个方式:
  1. 使用-p来启动container。
  2. 使用-P来启动container。
  3. 使用--link来启动container。
  如果使用-p或者-P,那么container会开发部分端口到host,只要对方可以连接到host,就可以连接到container内部。当使用-P时,docker会在host中随机从49153 和65535之间查找一个未被占用的端口绑定到container。你可以使用docker port来查找这个随机绑定端口。
  当你使用--link方式时,作为客户端的container可以通过私有网络形式访问到这个container。同时Docker会在客户端的container中设定一些环境变量来记录绑定的IP和PORT。

编辑 /etc/sysconfig/docker

1
2
# CentOS
other_args="--insecure-registry 192.168.1.100:4000"

编辑 /etc/systemd/system/docker.service.d/kolla.conf

1
2
3
4
5
6
7
# CentOS
[Service]
EnvironmentFile=/etc/sysconfig/docker
# It's necessary to clear ExecStart before attempting to override it
# or systemd will complain that it is defined more than once.
ExecStart=
ExecStart=/usr/bin/docker daemon -H fd:// $other_args

重启docker

1
2
3
4
# CentOS
systemctl daemon-reload
systemctl stop docker
systemctl start docker

在本地的registry如图所示

registry

停止registry的命令

1
docker stop registry && docker rm -v registry

写了shell脚本自动化把image push到docker仓库(registry)中,为了满足报错的log,并进行了tag的转换(lokolla->kollaglue)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
docker images > /root/kolla/images.txt
while read LINE
do
echo $LINE > /root/kolla/meta.txt
awk '{print $1}' /root/kolla/meta.txt > /root/kolla/images_1.txt
awk '{print $3}' /root/kolla/meta.txt > /root/kolla/images_3.txt
str=$(cat /root/kolla/images_1.txt)
echo /kollaglue/${str#*/} > /root/kolla/images_1_new.txt
docker tag $(cat /root/kolla/images_3.txt) localhost:4000$(cat /root/kolla/images_1_new.txt)
echo tag ok
docker push localhost:4000$(cat /root/kolla/images_1_new.txt)
echo push ok
#echo $(cat /root/kolla/images_3.txt)
#echo $(cat /root/kolla/images_1.txt)
#echo $(cat /root/kolla/images_1_new.txt)
done < /root/kolla/images.txt
echo finish

重新使用 ./tools/kolla-ansible deploy 来开始正式安装。
使用 ./tools/kolla-ansible post-deploy 来生成 /etc/kolla/admin-openrc.sh 文件用来加载认证变量。 最后container全部up起来
registry

在浏览器上输入kolla_internal_address: “192.168.52.197”
域default,用户名密码在/etc/kolla/admin-openrc.sh中
登陆后的界面如下
registry

这样就成功了,不过接下去还有很多“坑要踩”,趁着kolla项目代码量没上去还是值得好好研究一下的。容器化已经势不可挡了。

常见问题

  • 今天Kolla镜像又下载不了了。以为是网络问题。进入openstack-kolla的irc聊天组。大牛基本都在这里聊天,发现有公告,原来是服务器正在重建。
1
static.openstack.org (which hosts logs.openstack.org and tarballs.openstack.org among others) is currently being rebuilt. As jobs can not upload logs they are failing with POST_FAILURE. This should be resolved soon. Please do not recheck until then.
  • 如果在deploy中出现这个问题。在/etc/hosts中加入”127.0.0.1 localhost”即可
1
2
3
4
5
6
7
8
9
10
TASK: [rabbitmq | fail msg="Hostname has to resolve to IP address of api_interface"] ***
failed: [localhost] => (item={'cmd': ['getent', 'ahostsv4', 'localhost'], 'end': '2016-06-24 04:51:39.738725', 'stderr': u'', 'stdout': '127.0.0.1       STREAM localhost\n127.0.0.1       DGRAM  \n127.0.0.1       RAW    \n127.0.0.1       STREAM \n127.0.0.1       DGRAM  \n127.0.0.1       RAW    ', 'changed': False, 'rc': 0, 'item': 'localhost', 'warnings': [], 'delta': '0:00:00.033351', 'invocation': {'module_name': u'command', 'module_complex_args': {}, 'module_args': u'getent ahostsv4 localhost'}, 'stdout_lines': ['127.0.0.1       STREAM localhost', '127.0.0.1       DGRAM  ', '127.0.0.1       RAW    ', '127.0.0.1       STREAM ', '127.0.0.1       DGRAM  ', '127.0.0.1       RAW    '], 'start': '2016-06-24 04:51:39.705374'}) => {"failed": true, "item": {"changed": false, "cmd": ["getent", "ahostsv4", "localhost"], "delta": "0:00:00.033351", "end": "2016-06-24 04:51:39.738725", "invocation": {"module_args": "getent ahostsv4 localhost", "module_complex_args": {}, "module_name": "command"}, "item": "localhost", "rc": 0, "start": "2016-06-24 04:51:39.705374", "stderr": "", "stdout": "127.0.0.1       STREAM localhost\n127.0.0.1       DGRAM  \n127.0.0.1       RAW    \n127.0.0.1       STREAM \n127.0.0.1       DGRAM  \n127.0.0.1       RAW    ", "stdout_lines": ["127.0.0.1       STREAM localhost", "127.0.0.1       DGRAM  ", "127.0.0.1       RAW    ", "127.0.0.1       STREAM ", "127.0.0.1       DGRAM  ", "127.0.0.1       RAW    "], "warnings": []}}
msg: Hostname has to resolve to IP address of api_interface

FATAL: all hosts have already failed -- aborting

PLAY RECAP ********************************************************************
           to retry, use: --limit @/root/site.retry

localhost                  : ok=87   changed=24   unreachable=0    failed=1
  • 有时端口号可能被占用也会在precheck中提示,通过端口号找到进程之后就可以用kill -9来杀死了
1
2
yum install net-tools
netstat -apn|grep 5000(端口号)

工具

在/root/kolla的目录下有几个工具,可以在你deploy到一半中断的时候把本机环境清理一下

1
2
3
4
5
6
tools/cleanup-containers
tools/cleanup-host
#有时需要配合这两个命令:
docker kill $(docker ps -a -q) //杀死所有正在运行的containner
docker rm $(docker ps -a -q)   //删除已经被杀死的container

版本变动

官网上说现在ansible版本>2.0也OK,但是我实际操作的时候首先precheck这里提示错误,deploy的时候也出错

1
Some implemented distro versions of Ansible are too old to use distro packaging. Currently, CentOS and RHEL package Ansible >2.0 which is suitable for use with Kolla. Note that you will need to enable access to the EPEL repository to install via yum – to do so, take a look at Fedora’s EPEL docs and FAQ.

VPS

可以购买AWS(使用visa信用卡可以免费一年)
阿里云的北美服务器,或着digitalocean的vps
我的环境:CentOS7(服务器端)

服务器端

使用一键安装脚本


wget http://mirrors.linuxeye.com/scripts/vpn_centos.sh
chmod +x ./vpn_centos.sh
./vpn_centos.sh

脚本代码如下


#!/bin/bash
[ $(id -u) != "0" ] && { echo -e "\033[31mError: You must be root to run this script\033[0m"; exit 1; } 

export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
clear
printf "
#######################################################################
#    LNMP/LAMP/LANMP for CentOS/RadHat 5+ Debian 6+ and Ubuntu 12+    #
#            Installs a PPTP VPN-only system for CentOS               #
# For more information please visit http://blog.linuxeye.com/31.html  #
#######################################################################
"

[ ! -e '/usr/bin/curl' ] && yum -y install curl

VPN_IP=`curl ipv4.icanhazip.com`

VPN_USER="linuxeye"
VPN_PASS="linuxeye"

VPN_LOCAL="192.168.0.150"
VPN_REMOTE="192.168.0.151-200"


while :; do echo
    read -p "Please input username: " VPN_USER 
    [ -n "$VPN_USER" ] && break
done

while :; do echo
    read -p "Please input password: " VPN_PASS
    [ -n "$VPN_PASS" ] && break
done
clear


if [ -f /etc/redhat-release -a -n "`grep ' 7\.' /etc/redhat-release`" ];then
    #CentOS_REL=7
    if [ ! -e /etc/yum.repos.d/epel.repo ];then
        cat > /etc/yum.repos.d/epel.repo << EOF
[epel]
name=Extra Packages for Enterprise Linux 7 - \$basearch
#baseurl=http://download.fedoraproject.org/pub/epel/7/\$basearch
mirrorlist=https://mirrors.fedoraproject.org/metalink?repo=epel-7&arch=\$basearch
failovermethod=priority
enabled=1
gpgcheck=0
EOF
    fi
    for Package in wget make openssl gcc-c++ ppp pptpd iptables iptables-services 
    do
        yum -y install $Package
    done
    echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
elif [ -f /etc/redhat-release -a -n "`grep ' 6\.' /etc/redhat-release`" ];then
    #CentOS_REL=6
    for Package in wget make openssl gcc-c++ iptables ppp 
    do
        yum -y install $Package
    done
    sed -i 's@net.ipv4.ip_forward.*@net.ipv4.ip_forward = 1@g' /etc/sysctl.conf
    rpm -Uvh http://poptop.sourceforge.net/yum/stable/rhel6/pptp-release-current.noarch.rpm
    yum -y install pptpd
else
    echo -e "\033[31mDoes not support this OS, Please contact the author! \033[0m"
    exit 1
fi

echo "1" > /proc/sys/net/ipv4/ip_forward

sysctl -p /etc/sysctl.conf

[ -z "`grep '^localip' /etc/pptpd.conf`" ] && echo "localip $VPN_LOCAL" >> /etc/pptpd.conf # Local IP address of your VPN server
[ -z "`grep '^remoteip' /etc/pptpd.conf`" ] && echo "remoteip $VPN_REMOTE" >> /etc/pptpd.conf # Scope for your home network

if [ -z "`grep '^ms-dns' /etc/ppp/options.pptpd`" ];then
     cat >> /etc/ppp/options.pptpd << EOF
ms-dns 223.5.5.5 # Aliyun DNS Primary
ms-dns 114.114.114.114 # 114 DNS Primary
ms-dns 8.8.8.8 # Google DNS Primary
ms-dns 209.244.0.3 # Level3 Primary
ms-dns 208.67.222.222 # OpenDNS Primary
EOF
fi

echo "$VPN_USER pptpd $VPN_PASS *" >> /etc/ppp/chap-secrets

ETH=`route | grep default | awk '{print $NF}'`
[ -z "`grep '1723 -j ACCEPT' /etc/sysconfig/iptables`" ] && iptables -I INPUT 4 -p tcp -m state --state NEW -m tcp --dport 1723 -j ACCEPT
[ -z "`grep 'gre -j ACCEPT' /etc/sysconfig/iptables`" ] && iptables -I INPUT 5 -p gre -j ACCEPT 
iptables -t nat -A POSTROUTING -o $ETH -j MASQUERADE
iptables -I FORWARD -p tcp --syn -i ppp+ -j TCPMSS --set-mss 1356
service iptables save
sed -i 's@^-A INPUT -j REJECT --reject-with icmp-host-prohibited@#-A INPUT -j REJECT --reject-with icmp-host-prohibited@' /etc/sysconfig/iptables 
sed -i 's@^-A FORWARD -j REJECT --reject-with icmp-host-prohibited@#-A FORWARD -j REJECT --reject-with icmp-host-prohibited@' /etc/sysconfig/iptables 
service iptables restart
chkconfig iptables on

service pptpd restart
chkconfig pptpd on
clear

echo -e "You can now connect to your VPN via your external IP \033[32m${VPN_IP}\033[0m"

echo -e "Username: \033[32m${VPN_USER}\033[0m"
echo -e "Password: \033[32m${VPN_PASS}\033[0m"

执行结束后即安装完成

客户端

在root权限下执行以下命令


yum install pptp  
modprobe nf_conntrack_pptp  
echo '用户名 PPTP 密码 *' >> /etc/ppp/chap-secrets   

在/etc/ppp/peers/目录下新建一个文本,拷贝下面文本(本文为linuxconfig)


pty "pptp 123.123.1.1(vps ip) --nolaunchpppd"
name 用户名
password 密码
remotename PPTP
require-mppe-128
file /etc/ppp/options.pptp
ipparam linuxconfig

连接VPN


pppd call linuxconfig

断开VPN


pkill pppd

PS:
log在/var/log/messages目录下
可以看到ip a显示下多了一个ppp0接口(如果这个接口时down的则重复尝试断开和连接步骤)
配置路由


ip route add default dev ppp0

这样所有流量全部由VPN转发
可以使用curl www.google.com测试VPN是否生效

curl

这样就可以配置完成了
还有一些小trick就是注意vps上的DNS和镜像的源,厂商一般就更改为自己的,注意替换 这样一般是OK了,但是网速仍然才小几百k…科学上网技术仍有待提高

smile

什么是magnum?

Mangum现在应该是OpenStack里边比较热门的一个和Docker集成的新项目。Magnum是去年巴黎峰会后开始的一个新的专门针对Container的一个新项目,用来向用户提供容器服务。从去年11月份开始在stackforge提交第一个 patch,今年3月份进入OpenStack namespace,这个项目应该是OpenStack社区从stackforge迁移到OpenStack namespace最快的一个项目。Magnum现在可以为用户提供Kubernetes as a Service、Swarm as a Service和这几个平台集成的主要目的是能让用户可以很方便的通过OpenStack云平台来管理k8s,swarm,这些已经很成型的Docker集群管理系统,使用户很方便的使用这些容器管理系统来提供容器服务。

使用devstack安装magnum

magnum依赖于nova,glance,heat,barbican,neutron这些组件来模拟一个物理的环境,在裸机上部署magnum社区还在开发中
推荐使用Ubuntu14.04(Trusty)和Fedora 20/21
首先 Clone devstack


cd ~
git clone https://git.openstack.org/openstack-dev/devstack

配置devstack,enable heat和neutron


cd devstack
cat > local.conf << END
[[local|localrc]]
 # Modify to your environment
FLOATING_RANGE=192.168.1.224/27
PUBLIC_NETWORK_GATEWAY=192.168.1.225
PUBLIC_INTERFACE=em1

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

enable_service rabbit

 # Ensure we are using neutron networking rather than nova networking
 # (Neutron is enabled by default since Kilo)
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 neutron

 # Enable heat services
enable_service h-eng
enable_service h-api
enable_service h-api-cfn
enable_service h-api-cw

 # Enable barbican services
enable_plugin barbican https://git.openstack.org/openstack/barbican

FIXED_RANGE=10.0.0.0/24

Q_USE_SECGROUP=True
ENABLE_TENANT_VLANS=True
TENANT_VLAN_RANGE=

PHYSICAL_NETWORK=public
OVS_PHYSICAL_BRIDGE=br-ex

 # Log all output to files
LOGFILE=$HOME/logs/devstack.log
SCREEN_LOGDIR=$HOME/logs

VOLUME_BACKING_FILE_SIZE=20G
END

创建local.sh,使的magnum能够使用devstack创建的网络


cat > local.sh << 'END_LOCAL_SH'
 #!/bin/sh
ROUTE_TO_INTERNET=$(ip route get 8.8.8.8)
OBOUND_DEV=$(echo ${ROUTE_TO_INTERNET#*dev} | awk '{print $1}')
sudo iptables -t nat -A POSTROUTING -o $OBOUND_DEV -j MASQUERADE
END_LOCAL_SH
chmod 755 local.sh

运行devstack


./stack.sh

source环境变量


source ~/devstack/openrc admin admin

把Fedora Atomic micro-OS存在glance中


cd ~
wget https://fedorapeople.org/groups/magnum/fedora-21-atomic-5.qcow2
glance image-create --name fedora-21-atomic-5 \
                    --visibility public \
                    --disk-format qcow2 \
                    --os-distro fedora-atomic \
                    --container-format bare < fedora-21-atomic-5.qcow2

创建keypair来使用baymodel


test -f ~/.ssh/id_rsa.pub || ssh-keygen -t rsa -N "" -f ~/.ssh/id_rsa
nova keypair-add --pub-key ~/.ssh/id_rsa.pub testkey

为magnum创建MySql数据库

 
mysql -h 127.0.0.1 -u root -ppassword mysql <<EOF
CREATE DATABASE IF NOT EXISTS magnum DEFAULT CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON magnum.* TO
    'root'@'%' IDENTIFIED BY 'password'
EOF

安装magnum


cd ~
git clone https://git.openstack.org/openstack/magnum
cd magnum
sudo pip install -e .

配置magnum


# create the magnum conf directory
sudo mkdir -p /etc/magnum
# copy sample config and modify it as necessary
sudo cp etc/magnum/magnum.conf.sample /etc/magnum/magnum.conf
# copy policy.json
sudo cp etc/magnum/policy.json /etc/magnum/policy.json
# enable debugging output
sudo sed -i "s/#debug\s*=.*/debug=true/" /etc/magnum/magnum.conf
# set RabbitMQ userid
sudo sed -i "s/#rabbit_userid\s*=.*/rabbit_userid=stackrabbit/" \
         /etc/magnum/magnum.conf
# set RabbitMQ password
sudo sed -i "s/#rabbit_password\s*=.*/rabbit_password=password/" \
         /etc/magnum/magnum.conf
# set SQLAlchemy connection string to connect to MySQL
sudo sed -i "s/#connection\s*=.*/connection=mysql:\/\/root:password@localhost\/magnum/" \
         /etc/magnum/magnum.conf
# set Keystone account username
sudo sed -i "s/#admin_user\s*=.*/admin_user=admin/" \
         /etc/magnum/magnum.conf
# set Keystone account password
sudo sed -i "s/#admin_password\s*=.*/admin_password=password/" \
         /etc/magnum/magnum.conf
# set admin Identity API endpoint
sudo sed -i "s/#identity_uri\s*=.*/identity_uri=http:\/\/127.0.0.1:35357/" \
         /etc/magnum/magnum.conf
# set public Identity API endpoint
sudo sed -i "s/#auth_uri\s*=.*/auth_uri=http:\/\/127.0.0.1:5000\/v2.0/" \
         /etc/magnum/magnum.conf
# set oslo messaging notifications driver (if using ceilometer)
sudo sed -i "s/#driver\s*=.*/driver=messaging/" \
         /etc/magnum/magnum.conf

安装magnum客户端


cd ~
git clone https://git.openstack.org/openstack/python-magnumclient
cd python-magnumclient
sudo pip install -e .

为magnum配置数据库


magnum-db-manage upgrade

配置keystone的endpoint


openstack service create --name=magnum \
                          --description="Magnum Container Service" \
                          container
openstack endpoint create --region=RegionOne \
                          --publicurl=http://127.0.0.1:9511/v1 \
                          --internalurl=http://127.0.0.1:9511/v1 \
                          --adminurl=http://127.0.0.1:9511/v1 \
                          magnum

启动magnum


magnum-api
magnum-conductor

Magnum关于DevStack启动的代码解读

代码结构如下
├── devstack
│   ├── lib
│   │   └── magnum
│   ├── plugin.sh
│   ├── README.rst
│   ├── settings
magnum中定义了magnum所创建文件的路径以及git镜像时的路径


MAGNUM_REPO=${MAGNUM_REPO:-${GIT_BASE}/openstack/magnum.git}
MAGNUM_BRANCH=${MAGNUM_BRANCH:-master}
MAGNUM_DIR=$DEST/magnum

GITREPO["python-magnumclient"]=${MAGNUMCLIENT_REPO:-${GIT_BASE}/openstack/python-magnumclient.git}
GITBRANCH["python-magnumclient"]=${MAGNUMCLIENT_BRANCH:-master}
GITDIR["python-magnumclient"]=$DEST/python-magnumclient
MAGNUM_STATE_PATH=${MAGNUM_STATE_PATH:=$DATA_DIR/magnum}

MAGNUM_AUTH_CACHE_DIR=${MAGNUM_AUTH_CACHE_DIR:-/var/cache/magnum}
MAGNUM_CONF_DIR=/etc/magnum
MAGNUM_CONF=$MAGNUM_CONF_DIR/magnum.conf
MAGNUM_POLICY_JSON=$MAGNUM_CONF_DIR/policy.json
MAGNUM_API_PASTE=$MAGNUM_CONF_DIR/api-paste.ini

定义好路径之后就创建各种配置文件。并进行检查。如果不存在则创建该文件,并赋予权限


function configure_magnum {
    # Put config files in ``/etc/magnum`` for everyone to find
    if [[ ! -d $MAGNUM_CONF_DIR ]]; then
        sudo mkdir -p $MAGNUM_CONF_DIR
        sudo chown $STACK_USER $MAGNUM_CONF_DIR
    fi

由于magnum的认证需要依赖keystone。那么需要对mysql进行操作。需要创建服务并返回endpoint


function create_magnum_accounts {

    create_service_user "magnum" "admin"

    if [[ "$KEYSTONE_CATALOG_BACKEND" = 'sql' ]]; then

        local magnum_service=$(get_or_create_service "magnum" \
            "container" "Magnum Container Service")
        get_or_create_endpoint $magnum_service \
            "$REGION_NAME" \
            "$MAGNUM_SERVICE_PROTOCOL://$MAGNUM_SERVICE_HOST:$MAGNUM_SERVICE_PORT/v1" \
            "$MAGNUM_SERVICE_PROTOCOL://$MAGNUM_SERVICE_HOST:$MAGNUM_SERVICE_PORT/v1" \
            "$MAGNUM_SERVICE_PROTOCOL://$MAGNUM_SERVICE_HOST:$MAGNUM_SERVICE_PORT/v1"
    fi

}

然后使用类似于shell的文件写入命令进行配置文件的写入操作


function create_magnum_conf {

    # (Re)create ``magnum.conf``
    rm -f $MAGNUM_CONF
    iniset $MAGNUM_CONF DEFAULT debug "$ENABLE_DEBUG_LOG_LEVEL"
    iniset $MAGNUM_CONF oslo_messaging_rabbit rabbit_userid $RABBIT_USERID
    iniset $MAGNUM_CONF oslo_messaging_rabbit rabbit_password $RABBIT_PASSWORD
    iniset $MAGNUM_CONF oslo_messaging_rabbit rabbit_host $RABBIT_HOST

    iniset $MAGNUM_CONF database connection `database_connection_url magnum`
    iniset $MAGNUM_CONF api host "$MAGNUM_SERVICE_HOST"
    iniset $MAGNUM_CONF api port "$MAGNUM_SERVICE_PORT"

magnum可以选择多个底层OS


function magnum_register_image {
    local magnum_image_property="--property os_distro="

    local atomic="$(echo $MAGNUM_GUEST_IMAGE_URL | grep -io 'atomic' || true;)"
    if [ ! -z "$atomic" ]; then
        magnum_image_property=$magnum_image_property"fedora-atomic"
    fi
    local ubuntu="$(echo $MAGNUM_GUEST_IMAGE_URL | grep -io "ubuntu" || true;)"
    if [ ! -z "$ubuntu" ]; then
        magnum_image_property=$magnum_image_property"ubuntu"
    fi
    local coreos="$(echo $MAGNUM_GUEST_IMAGE_URL | grep -io "coreos" || true;)"
    if [ ! -z "$coreos" ]; then
        magnum_image_property=$magnum_image_property"coreos"
    fi

    openstack --os-url $GLANCE_SERVICE_PROTOCOL://$GLANCE_HOSTPORT --os-image-api-version 1 image set $(basename "$MAGNUM_GUEST_IMAGE_URL" ".qcow2") $magnum_image_property
}

安装magnum客户端


function install_magnumclient {
    if use_library_from_git "python-magnumclient"; then
        git_clone_by_name "python-magnumclient"
        setup_dev_lib "python-magnumclient"
    fi
}

启动magnum服务,传递port,protocol,tls等信息。进程直接通信需要tls安全传输层协议


function start_magnum_api {
    # Get right service port for testing
    local service_port=$MAGNUM_SERVICE_PORT
    local service_protocol=$MAGNUM_SERVICE_PROTOCOL
    if is_service_enabled tls-proxy; then
        service_port=$MAGNUM_SERVICE_PORT_INT
        service_protocol="http"
    fi

为了满足进程之间通信。还需要对iptables进行配置。对keystone和magnum的通信进行accept


function configure_iptables {
    if [ "$MAGNUM_CONFIGURE_IPTABLES" != "False" ]; then
        ROUTE_TO_INTERNET=$(ip route get 8.8.8.8)
        OBOUND_DEV=$(echo ${ROUTE_TO_INTERNET#*dev} | awk '{print $1}')
        sudo iptables -t nat -A POSTROUTING -o $OBOUND_DEV -j MASQUERADE
        # bay nodes will access magnum-api (port $MAGNUM_SERVICE_PORT) to get CA certificate.
        sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $MAGNUM_SERVICE_PORT -j ACCEPT || true
        sudo iptables -I INPUT -d $HOST_IP -p tcp --dport $KEYSTONE_SERVICE_PORT -j ACCEPT || true
    fi
}

在plugin.sh中如果magnum的api和conduct服务启动,那么将会安装magnum和magnum-client,以及获取magnum_image等操作。
另外对keystone的配置文件进行修改,创建magnum的account


elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then
        echo_summary "Configuring magnum"
        configure_magnum

        # Hack a large timeout for now
        iniset /etc/keystone/keystone.conf token expiration 7200

        if is_service_enabled key; then
            create_magnum_accounts
        fi

在settings中则为一系列配置参数。用于服务的开启和关闭


# Enable Neutron which is required by Magnum and disable nova-network.
disable_service n-net
enable_service q-svc
enable_service q-agt
enable_service q-dhcp
enable_service q-l3
enable_service q-meta
# Note: Default template uses LBaaS.
enable_service q-lbaas
enable_service neutron

# Enable Heat services
enable_service h-eng
enable_service h-api
enable_service h-api-cfn
enable_service h-api-cw

devstack的代码只是一小部分。不过也能从这里看出magnum是如何运行的,在OpenStack的峰会上容器越来越火,看好Kolla,magnum以及Murano。

Kolla介绍

Kolla项目利用Docker、Docker-Compose、Ansible来完成部署OpenStack,目前Kolla已经能够完成一个all-in-one的开发环境的部署。从Kolla项目spec中的描述来看,主要是利用Docker容器的隔离性来达到OpenStack的原子升级、回退在升级。整个升级、回退的过程更容易控制影响范围,降低整个OpenStack的运维复杂度。Kolla 提供了生产级别的 OpenStack Service Containers。基于社区的最佳实践,提供了更好,更快,更可靠的 , 操作 OpenStack 的部署工具。
解决的问题:
平滑的升级 / 回滚 OpenStack
隔离 OpenStack 的不同组件的依赖环境。尤其是那些使用同一模块不同版本的情况。 保证环境的一致性。解决由于安装时间不同,造成的包版本不一致的情况。
支持多种安装源:源代码安装,CentOS binary 安装等。可以替代掉 devstack。 其实这些问题只要是由 Docker 来解决的。这也是 Kolla 使用 Docker 的原因。
Kolla定义了容器集合及容器两个概念
容器集合具有以下属性:
容器集由一个或多个容器子集或者一个或多个容器组成;
一个容器集提供一个逻辑上的的服务;
启动、停止和版本的控制以容器集为粒度;
以容器集的粒度来完成安装;
包含容器子集的容器集也以一个独立单元启动,包含所有的容器子集及容器子集内的所有容器;
容器集合并不是原子的管理单位;
容器集必须提供进行服务状态监控的钩子;

容器具有以下属性

容器集能够原子的升级和回退; 容器包含一个单调增长的计数器,用来标志容器的存活时间;
容器的职责是单一的;
只赋予容器刚刚好的权限;
需要包含检查容器健康状态的脚本;
必须包含pid为1的进程用来回收推出的子进程;
Kolla的容器集集包含的容器有以下OpenStack组件:
Database control:
galera
mariadb
mongodb

Messaging control:
rabbitmq

High availability control:
HAProxy keepalived

OpenStack interface:
keystone
glance-api
nova-api
ceilometer-api
heat-api

OpenStack control:
glance-controller
glance-registry
nova-controller
nova-conductor
nova-scheduler
metadata-service
cinder-controller
neutron-controller
neutron-server
ceilometer-controller
ceilometer-alarm
ceilometer-base
ceilometer-central
ceilometer-collector
ceilometer-notification
heat-controller
heat-engine

OpenStack compute operation: nova-compute
nova-libvirt
neutron-agents-linux-bridge
neutron-agents-ovs

OpenStack network operation:
dhcp-agent
l3-agent
metadata-agent
lbaas-agent
fwaas-agent

OpenStack storage operation:
Cinder
Swift
swift-account
swift-base
swift-container
swift-object
swift-proxy-server

现在 Kolla 主要分成两个部分。
1.Docker Images Build
2.Ansible 部署 ( 这部分将来会迁移到 kolla-ansible 项目 )

Docker Image Build

主要使用 Jinja2 模板生成 Dockerfile 文件。如果是源代码安装,还会把代码下载好。之后会通过Docker-py驱动Docker进行镜像构建。构建完成,还可以根据配置将镜像推送的指定的 Registry 服务器。
现在支持的不同的 Linux 发行版本 (Redhat 系和 Debian 系 ) 和不同的 OpenStack 安装包 ( 源代码和二进制包 )
下图是现在所有镜像的依赖关系。

依赖关系

Ansible 部署

提供一个完整的Ansible Playbook,来部署Docker的镜像。并提供all-in-one和multihost的环境
使用到的技术Docker/LXC、Ansible、Python(docker-py、Jinja2)

ansible – Contains Ansible playbooks to deploy Kolla in Docker containers.

demos – Contains a few demos to use with Kolla.
dev/heat – Contains an OpenStack-Heat based development environment.
dev/vagrant – Contains a vagrant VirtualBox/Libvirt based development environment.
doc – Contains documentation.
etc – Contains a reference etc directory structure which requires configuration of a small number of configuration variables to achieve a working All-in-One (AIO) deployment.
docker – Contains jinja2 templates for the docker build system.
tools – Contains tools for interacting with Kolla.
specs – Contains the Kolla communities key arguments about architectural shifts in the code base.
tests – Contains functional testing tools.

setup.cfg入口文件


[files]
packages =      //包名
kolla
data_files =     //pbr指定的数据文件
share/kolla/ansible = ansible/*
share/kolla/devenv = devenv/*
share/kolla/docker = docker/*
share/kolla/doc = doc/*
share/kolla/etc_examples = etc/*
share/kolla/vagrant = vagrant/*
share/kolla = tools/init-runonce
share/kolla = tools/openrc-example
share/kolla = setup.cfg

scripts =
tools/kolla-ansible  //该脚本通过ansible与koll进行交互。使用命令为:ansible-playbook -i $INVENTORY $EXTRA_OPTS 

$PLAYBOOK
[entry_points]
console_scripts =
kolla-build = kolla.cmd.build:main   //kolla程序的main主函数
oslo.config.opts =                  //oslo.config通用库用于解析命令行和配置文件中的配置选项
kolla = kolla.opts:list_opts    //字符串列表类型

[global]
setup-hooks =
pbr.hooks.setup_hook

ansible包

ansible简介

ansible是个什么东西呢?官方的title是“Ansible is Simple IT Automation”——简单的自动化IT工具。这个工具的目标有这么几项:
自动化部署APP;自动化管理配置项;自动化的持续交互;自动化的(AWS)云服务管理。
所有的这几个目标从本质上来说都是在一个台或者几台服务器上,执行一系列的命令而已。通俗的说就是批量的在远程服务器上执行命令 。当然,最主要的是它是基于paramiko开发的。这个paramiko是什么呢?它是一个纯Python实现的ssh协议库。因此fabric和ansible还有一个共同点就是不需要在远程主机上安装client/agents,因为它们是基于ssh来和远程主机通讯的。简单归纳一下:
Ansible —基于 Python paramiko 开发,分布式,无需客户端,轻量级,配置语法使用YMAL及Jinja2模板语言,更强的远程命令执行
Ansible工作机制
Ansible 在管理节点将 Ansible 模块通过 SSH 协议(或者 Kerberos、LDAP)推送到被管理端执行,执行完之后自动删除,可以使用 SVN 等来管理自定义模块及编排
Ansible优点:
ansible在特性上似乎并不抢眼,配置管理方面(playbook)绝对比不过老大哥puppet,批量执行方面也只是多线程,不像saltstack那么高大上,不过ansible搜索热度高出saltstack三倍多,显然靠的不是吹牛,至少,ansible至少不会悄悄的丢机器,而且仅依赖ssh,与登录授权管理系统天然集成,简单即有效,没有比这更美妙的事情了。
Ansible
由上面的图可以看到Ansible的组成由 5 个部分组成:
Ansible:核心
Modules:包括Ansible自带的核心模块及自定义模块
Plugins:完成模块功能的补充,包括连接插件、邮件插件等
Playbooks:网上很多翻译为剧本,个人觉得理解为编排更为合理;定义 Ansible 多任务配置文件,有Ansible自动执行
Inventory:定义Ansible管理主机的清单
Ansible代码包很庞大:这是因为OpenStack的服务的配置文件都在这个自动化安装的库里面。
代码结构如下:


#ansible的code tree如下
├── ansible
│   ├── action_plugins
│   │   └── merge_configs.py
│   ├── certificates.yml
│   ├── group_vars
│   │   └── all.yml
│   ├── inventory
│   │   ├── all-in-one
│   │   └── multinode
│   ├── library
│   │   ├── bslurp.py
│   │   ├── kolla_docker.py
│   │   └── merge_configs.py
│   ├── mariadb_recovery.yml
│   ├── post-deploy.yml
│   ├── prechecks.yml
│   ├── roles
│   │   ├── ceph
│   │   ├── ceph_pools.yml
│   │   ├── certificates
│   │   ├── cinder
│   │   ├── elasticsearch
│   │   ├── glance
│   │   ├── haproxy
│   │   ├── heat
│   │   ├── horizon
│   │   ├── ironic
│   │   ├── iscsi
│   │   ├── keystone
│   │   ├── kibana
│   │   ├── magnum
│   │   ├── manila
│   │   ├── mariadb
│   │   ├── memcached
│   │   ├── neutron
│   │   ├── nova
│   │   ├── prechecks
│   │   ├── rabbitmq
│   │   └── swift
│   │   
│   └── site.yml

action_plugin中在merge_configs.py作用是导入template模板,并且run


  def read_config(self, source, inject, config):
        # 检查配置文件是否存在,并且读取配置信息
        if os.access(source, os.R_OK):
            # 读取目录中模板的数据 & 存在resultant中
            resultant = template.template_from_file(self.runner.basedir,
                                                    source, inject)

            # 把resultant中的数据转成String之后存在fakefile中,并关闭文件
            fakefile = StringIO(resultant)
            config.readfp(fakefile)
            fakefile.close()
           
  def run(self, conn, tmp, module_name, module_args, inject,
            complex_args=None, **kwargs):   
            #在run函数中导入模块信息,读取source信息,存放在fakefile中最后生成confg文件
            for source in sources:
            #读source
            source = template.template(self.runner.basedir, source, inject)

            try:
                self.read_config(source, inject, config)
            except Exception as e:
                return ReturnData(conn=conn, comm_ok=False,
                                  result={'failed': True, 'msg': str(e)})
        
           fakefile = StringIO()
            #从一个fake模拟文件中生成配置文件
           config.write(fakefile)

介绍一下YML文件的概念:
YML文件格式是YAML(YAML Aint Markup Language)编写的文件格式,YAML是一种直观的能够被电脑识别的的数据数据序列化格式,容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入,比如: C/C++, Ruby, Python, Java, Perl, C#, PHP等。
在all.yml作用是提供文件目录、以及各种配置信息(如IP地址、端口号、进程id等等)


#摘取了一部分
#文件目录
node_templates_directory: "/usr/share/kolla/templates"
#kolla的内部ip地址
kolla_internal_vip_address: ""
#docker仓库信息
docker_registry_username:
#OpenStack各个服务的api接口
api_interface: ""
#OpenStack服务端口号
rabbitmq_port: "5672"
#OpenStack服务是否开启
enable_keystone: "yes"

在inventory文件夹中有all-in-one和multinode两个配置文件,这两个文件中的信息可以更改,从而部署符合需求的OpenStack环境,有点类似于DevStack中的local.conf

截取其中一部分:


# 在multinode下需要根据实际生产环境配置hostname
control01
control02
control03
# Glance
[glance-api:children]
glance
[glance-registry:children]
glance
# Nova
[nova-api:children]
nova
[nova-conductor:children]
nova

在library文件夹下的bslurp.py的作用是从其他node中gfetch文件然后再push到其他的node中


#从其他的node中拷贝
def copy_from_host(module):
    compress = module.params.get('compress')
    #取到src文件,存入src
    src = module.params.get('src')
    #如果不存在,则报错
    if not os.path.exists(src):
        module.fail_json(msg="file not found: {}".format(src))
    #如果没有权限,则报unreadable
    if not os.access(src, os.R_OK):
        module.fail_json(msg="file is not readable: {}".format(src))
    
    mode = oct(os.stat(src).st_mode & 0o777)
    #read file
    with open(src, 'rb') as f:
        raw_data = f.read()
    #进行校验
    sha1 = hashlib.sha1(raw_data).hexdigest()
    #压缩文件
    data = zlib.compress(raw_data) if compress else raw_data
    #调用exit_json退出
    module.exit_json(content=base64.b64encode(data), sha1=sha1, mode=mode,
                     source=src)
    #然后拷贝文件到目的主机

在library文件夹下的kolla_docker.py的作用则是为了kolla控制Docker
tls是为了在远端配置Docker的认证
Docker daemon在0.10后支持–tlsverify 来提供加密的远端连接


def generate_tls(self):
        #tls协议,获取tls_cert、tls_key、tls_cacert
        tls = {'verify': self.params.get('tls_verify')}
        tls_cert = self.params.get('tls_cert'),
        tls_key = self.params.get('tls_key'),
        tls_cacert = self.params.get('tls_cacert')

        if tls['verify']:
        #进行check_file
            if tlscert:
                self.check_file(tls['tls_cert'])
                self.check_file(tls['tls_key'])
                tls['client_cert'] = (tls_cert, tls_key)
            if tlscacert:
                self.check_file(tls['tls_cacert'])
                tls['verify'] = tls_cacert
        #返回配置文件
        return docker.tls.TLSConfig(**tls)

接下去有很多check方法,有check文件、镜像、容器、容器的比对,还有compare优先级、镜像,pid,运行环境等


def check_volume(self):
        #比对vol的name
        for vol in self.dc.volumes()['Volumes'] or list():
            if vol['Name'] == self.params.get('name'):
                return vol

    def check_container(self):
        #比对container的name
        find_name = '/{}'.format(self.params.get('name'))
        for cont in self.dc.containers(all=True):
            if find_name in cont['Names']:
                return cont

代码check和compare结束之后是pull_image方法、remove_container方法、create_container、start_container等对容器一系列操作的方法


#根据前面定义的options创建容器
#类似于命令中的各种创建参数
def create_container(self):
        self.changed = True
        options = self.build_container_options()
        self.dc.create_container(**options)
#根据container的name删除容器
def remove_container(self):
        if self.check_container():
            self.changed = True
            self.dc.remove_container(
                container=self.params.get('name'),
                force=True
            )
#还有start_container方法
def start_container(self):
        if not self.check_image():
            self.pull_image()
		#先检查仓库中有没有改image
		#没有的话就去pull
        container = self.check_container()
        #如果container已经存在,但是进行container的check有差别
        #那么删除这个container,再次进行check
        if container and self.check_container_differs():
            self.remove_container()
            container = self.check_container()
        #如果container不存在,创建该container
        if not container:
            self.create_container()
            container = self.check_container()
        #container存在但没有up状态,那么将其状态转换成up
        if not container['Status'].startswith('Up '):
            self.changed = True
            self.dc.start(container=self.params.get('name'))
        #如果container没有detach断开,那么进入wait状态,调用fail_json方法,传递fail的参数
        # We do not want to detach so we wait around for container to exit
        if not self.params.get('detach'):
            rc = self.dc.wait(self.params.get('name'))
            if rc != 0:
                self.module.fail_json(
                    failed=True,
                    changed=True,
                    msg="Container exited with non-zero return code"
                )
            #如果返回参数remove_on_exit,那么删除该container
            if self.params.get('remove_on_exit'):
                self.remove_container()

下面是roles文件夹,里面内容很庞大。里面是各种组件的yml的配置文件,如ceph,cinder,glance,nova,neutron等。我就neutron配置文件做一下分析。其他的应该是类似的。

在这里Ansible使用Playbook,采用YAML语法结构,这些配置文件易于阅读并加以配置。通过playbook自动化了它的执行,这些playbook是指定要执行的每个任务和它们的属性的YAML文件。
Ansible还使用了清单(inventory)来将用户提供的主机映射到基础架构中的具体端点。不同于静态hosts文件,Ansible支持动态内容。内置的列表包含一个 Docker插件,该插件可查询Docker守护进程并向Ansible playbook共享大量信息。

Neutron文件夹下有4个文件夹,分别是default、meta、tasks和templates
defaults和meta文件下的是main.yml,主要功能是提供了neutron下各个组件的配置信息,和这些组件在docker仓库的存放及调用的路径以及tag


#其中这里的组件都使用了template文件模版,进行文档内变量的替换的模块。
neutron_dhcp_agent_image
neutron_dhcp_agent_tag
neutron_dhcp_agent_image_full
neutron_l3_agent_image
neutron_l3_agent_tag
neutron_l3_agent_image_full
neutron_linuxbridge_agent_image
neutron_linuxbridge_agent_tag
neutron_linuxbridge_agent_image_full
neutron_metadata_agent_image
neutron_metadata_agent_tag
neutron_metadata_agent_image_full
neutron_openvswitch_agent_image
neutron_openvswitch_agent_tag
neutron_openvswitch_agent_image_full
neutron_server_image
neutron_server_tag
neutron_server_image_full
openvswitch_db_image
openvswitch_db_tag
openvswitch_db_image_full
openvswitch_vswitchd_image
openvswitch_vswitchd_tag
openvswitch_vswitchd_image_full:

在task目录:在task目录下面就是如何根据配置文件启动docker image
默认ansible的所有task是在我们的配置的管理机器上面运行的,当在一个独立的群集里面配置,那是适用的。而有一些情况是,某些任务运行的状态是需要传递给其他机器的,在同一个任务你需要在其他机器上执行,这时候你就要用task委托。

在bootstrap_mons.yml中提供了Neutron数据库的creating和用户的创建,并赋予权限的配置信息
几乎所有的模块都是会outputs一些东西,甚至debug模块也会。结果变量还有其他的用途,譬如需要保存结果变量,然后在我的playbook的其他地方给使用。register就提供保存结果这个功能。


- name: Creating Neutron database
  command: docker exec -t kolla_toolbox /usr/bin/ansible localhost
    -m mysql_db
    -a "login_host=''
        login_user=''
        login_password=''
        name=''"
- name: Creating Neutron database user and setting permissions
  command: docker exec -t kolla_toolbox /usr/bin/ansible localhost
    -m mysql_user
    -a "login_host=''
        login_user=''
        login_password=''
        name=''
        password=''
        host='%'
        priv='.*:ALL'
        append_privs='yes'"
#保存结果
  register: database_user_create

在bootstrap_service.yml中包含了启动一个neutron服务所需要的依赖配置,其中有使用delegate_to关键字便可以配置任务在其他机器上执行。
其他模块还是在所有配置的管理机器上运行的,当到了这个关键字的任务就是使用委托的机器上运行。而facts还是适用于当前的host。


---
- name: Running Neutron bootstrap container
  kolla_docker:
#启动一个docker
    action: "start_container"
    common_options: ""
    detach: False
#所需要的环境 config_strategy
    environment:
      KOLLA_BOOTSTRAP:
      KOLLA_CONFIG_STRATEGY: ""
#启动所需要的镜像
    image: ""
    labels:
      BOOTSTRAP:
    name: "bootstrap_neutron"
    restart_policy: "never"
#配置信息及log的目录
    volumes:
      - "/neutron-server/:/:ro"
      - "kolla_logs:/var/log/kolla/"
  run_once: True
#delegate_to关键字便可以配置任务在其他机器上执行
  delegate_to: ""

在config-neutron-fake.yml中提供了代码的鲁棒性
为了保证config的文件都是存在的,如neutron的neutron.conf和ml2_conf.ini等

在config.yml中则是将每个小的agent,server作为item进行划分,再分别把配置信息写入neutron的各个配置文件中。也就是kolla项目所说的原子级别的OpenStack环境部署


- name: Copying over neutron.conf
  merge_configs:
    vars:
      service_name: ""
    sources:
      - "/templates/neutron.conf.j2"
      - "/etc/kolla/config/global.conf"
      - "/etc/kolla/config/database.conf"
      - "/etc/kolla/config/messaging.conf"
      - "/etc/kolla/config/neutron.conf"
      - "/etc/kolla/config/neutron/.conf"
      - "/etc/kolla/config/neutron//neutron.conf"
    dest: "//neutron.conf"
  with_items:
    - "neutron-dhcp-agent"
    - "neutron-l3-agent"
    - "neutron-linuxbridge-agent"
    - "neutron-metadata-agent"
    - "neutron-openvswitch-agent"
    - "neutron-server"

在deploy.yml中将boot一个组件所需要的配置信息都include进来,包括有
ironic-check.yml
config.yml
config-neutron-fake.yml
bootstrap.yml
start.yml

在do_reconfigure.yml中则是需要保证所有deploy完成的服务全都up。如果没有up的话就执行action: “restart_container”


- name: Restart containers running neutron-server and neutron agents
  kolla_docker:
    name: ""
    action: "restart_container"
  when:
    - config_strategy == 'COPY_ALWAYS'
    - inventory_hostname in groups[item[0]['group']]
    - item[1]['KOLLA_CONFIG_STRATEGY'] != 'COPY_ONCE'
    - item[2]['rc'] == 1
  with_together:
    - [{ name: neutron_server, group: neutron-server },
       { name: neutron_dhcp_agent, group: neutron-dhcp-agent },
       { name: neutron_l3_agent, group: neutron-l3-agent },
       { name: neutron_metadata_agent, group: neutron-metadata-agent }]
    - neutron_container_envs.results
    - neutron_check_results.results

ironic-check.yml则是检查ironic服务必须启动,neutron的plugin必须使用OpenvSwitch
main.yml提供了一个action.yml的配置文件入口
pull.yml中提供了docker的pull方法的配置信息,使用了”pull_image”的方法,docker_common_options下的参数,以及pull使用的image。
这样就把各个服务都启动起来了。


- name: Pulling neutron-dhcp-agent image
  kolla_docker:
    action: "pull_image"
    common_options: ""
    image: ""
#使用模板中的neutron-dhcp-agent的配置参数
  when: inventory_hostname in groups['neutron-dhcp-agent']

reconfigure.yml中调用之前的do_reconfigure.yml中的配置参数
模块的每次使用都会被ansible标记为”changed”状态。

pull.yml则是对容器化后的OpenStack中的服务的镜像进行pull操作。通过SSH到目的主机上
register.yml则是完成组件在keystone上的注册操作
start.yml则是完成容器的开启配置
upgrade.yml则是完成容器的升级,include config.yml bootstarp_service.yml以及start.yml

在templates文件夹下是jinjia2的模板文件

Jinja2是Python下一个被广泛应用的模版引擎,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能。其中最显著的一个是增加了沙箱执行功能和可选的自动转义功能,这对大多应用的安全性来说是非常重要的。
他基于unicode并能在python2.4之后的版本运行,包括python3。
下面是neutron-l3-agent的配置模板


{
    "command": "neutron-l3-agent --config-file /etc/neutron/neutron.conf --config-file /etc/neutron/l3_agent.ini --config-file /etc/neutron/fwaas_driver.ini --config-file /etc/neutron/plugins/ml2/ml2_conf.ini",
    "config_files": [
        {
            "source": "/neutron.conf",
            "dest": "/etc/neutron/neutron.conf",
            "owner": "neutron",
            "perm": "0600"
        },
        {
            "source": "/ml2_conf.ini",
            "dest": "/etc/neutron/plugins/ml2/ml2_conf.ini",
            "owner": "neutron",
            "perm": "0600"
        },
        {
            "source": "/fwaas_driver.ini",
            "dest": "/etc/neutron/fwaas_driver.ini",
            "owner": "neutron",
            "perm": "0600"
        },
        {
            "source": "/l3_agent.ini",
            "dest": "/etc/neutron/l3_agent.ini",
            "owner": "neutron",
            "perm": "0600"
        }
    ]
}

已经完成的BluePrints:

2015/12废弃使用Ansible自带的 Docker module。使用自己实现的docker模块 (见REF1), 并已经完成了所有ansible roles的改造。主要原因是:
官方Docker module有bug(见REF2), 在docker 1.8.3及以上的版本工作不正常 , 而且进展缓慢。而Kolla使用的Docker版本只能锁定在 1.8.2,不能进行升级。
Ansible使用的是 GPL,而OpenStack项目使用的Apache License。不能直接修改 Ansible 的代码放到 Kolla 里使用。不想受限制于官方功能开发。有些想用的功能 (比方说新加进去的common_option, 官方是不会增加的)。
使用 oslo.config 模块替代掉了原来的 argparser 代码(见REF3)。并加上了使用 oslo-config-generator 的功能自动生成配置文件。
完善 Ubuntu Binary Container 的安装 (见REF6、7)
由于 Ubuntu 并没有类似 RDO 的 relorean 源。倒是有一个 Mitaka Staging(见REF5) 源,但是好久没有更新。所以社区之前一直没有加上这部分功能。现在虽然加上了,但是也是基于 Liberty 版本的。并不是 Master 上的代码。
另一方面,Ubuntu 方面也正在积极的解决这个问题 (见REF4)。相信不久就会有升级的 playbook 基本完成(见REF8)

近期规划

拆分ansible部分到新的项目kolla-ansible。kolla项目只用来做docker images build.部署工作由kolla-ansible, kolla-mesos9等工具来实现。

链接:
Kolla Wiki Page
1.Add docker module in Kolla
https://review.openstack.org/#/c/248812/
2.Docker 1.8.3 is broken under certain conditions
https://github.com/ansible/ansible-modules-core/issues/2257
3.use oslo.config instead of raw argparse.ArgumentParser
https://review.openstack.org/#/c/260389/
4.Mail List: Adding Ubuntu Liberty to Kolla-Mitaka
http://lists.openstack.org/pipermail/openstack-dev/2015-December/083089.html
5.Staging PPA for mitaka
https://launchpad.net/~ubuntu-cloud-archive/+archive/ubuntu/mitaka-staging
6.Gerrit: Base and openstack-base ubuntu binary containers
https://review.openstack.org/#/c/261957/
7.BP:Ubuntu binary containers
https://blueprints.launchpad.net/kolla/+spec/binary-ubuntu
8.Gerrit: Playbook for keystone upgrade
https://review.openstack.org/#/c/257568/
9.kolla-mesos
https://github.com/openstack/kolla-mesos