preface

在上一章节中,我们可以在无web管理界面上创建并启动虚拟机,虽然可以这么做,但是敲命令太繁琐,所以此时我们可以安装openstack web管理界面,通过web界面的图形化操作openstack,更加方便的使用openstack。

Horizon简介

  1. 提供一个web界面操作openstack的系统。
  2. 使用Django框架基于Openstack API 开发
  3. 支持Session存储在DB、Memcached。
  4. 支持集群。
动手安装

由于Horizon也要依赖于apache等web服务,所以为了避免冲突,不要再linux-node1上安装,我们在linux-node2上安装它
1.yum安装它

[root@linux-node2 ~]# yum install openstack-dashboard

2.更改配置

[root@linux-node2 ~]# vim /etc/openstack-dashboard/local_settings
OPENSTACK_HOST = "192.168.56.11"       # keystone主机
SESSION_ENGINE = \'django.contrib.sessions.backends.cache\'   # 启用session共享
ALLOWED_HOSTS = ["*",]           # 允许所有主机访问
CACHES = {       # 设置memcached缓存session信息
    \'default\': {
        \'BACKEND\': \'django.core.cache.backends.locmem.LocMemCache\',
        \'LOCATION\': \'192.168.56.11:11211\'
    },
}
OPENSTACK_KEYSTONE_URL = "http://%s:5000/v3" % OPENSTACK_HOST   #启用第3版认证API:
OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT = True    # 启用对域的支持
OPENSTACK_API_VERSIONS = {      #配置API版本:
    "data-processing": 1.1,
    "identity": 3,
    "image": 2,
    "volume": 2,
    "compute": 2,
}
OPENSTACK_KEYSTONE_DEFAULT_DOMAIN = \'default\'   # 通过仪表盘创建用户时的默认域配置为 default
OPENSTACK_KEYSTONE_DEFAULT_ROLE = "_member_"   #通过仪表盘创建的用户默认角色配置为 user 
OPENSTACK_NEUTRON_NETWORK = {    # 我们选中的是自提供网络,所以需要禁用支持3层网络服务
    \'enable_router\': False,
    \'enable_quotas\': False,
    \'enable_ipv6\': False,
    \'enable_distributed_router\': False,
    \'enable_ha_router\': False,
    \'enable_lb\': False,
    \'enable_firewall\': False,
    \'enable_vpn\': False,
    \'enable_fip_topology_check\': False,
    }
TIME_ZONE = "Asia/Shanghai"    # 配置时区为上海

3.启动http服务

[root@linux-node2 openstack-dashboard]# systemctl restart httpd.service 
[root@linux-node2 openstack-dashboard]# systemctl enable httpd.service

4.访问Horizon(Dashboard),访问URL:http://192.168.56.12/dashboard/auth/login/?next=/dashboard/
5.登陆Dashboard,域为default,用户名是admin,密码是admin。
5.1.如果提示身份令牌过期,那么就重启下keystone服务(systemctl restart httpd.service),如果这样不行的话,重启下系统就可以了
5.2.登陆上去以后,在点击管理成员后报错,提示说 keystone缺少 memmber 角色,那么就在keystone服务器上增加一条 member 角色,命令如下:

[root@linux-node1 keystone]# openstack role create _member_
+-----------+----------------------------------+
| Field     | Value                            |
+-----------+----------------------------------+
| domain_id | None                             |
| id        | 9fe2ff9ee4384b1894a90878d3e92bab |
| name      | _member_                         |
+-----------+----------------------------------+

6.手动在原先的网络基础上添加IP段,操作步骤如下:
6.1.管理员身份登陆 ,登陆后点击左侧面板上的管理员-->系统-->网络-->点击需要增加IP段的网络-->子网-->编辑子网-->子网详情--> 分配地址池里添加需要添加IP地址段。
同时可以在数据库查看表里面的数据验证下:

[root@linux-node1 image]# mysql -uneutron -pneutron 
MariaDB [(none)]> use neutron;
MariaDB [neutron]> select * from ipallocations;
+--------------------------------------+----------------+--------------------------------------+--------------------------------------+
| port_id                              | ip_address     | subnet_id                            | network_id                           |
+--------------------------------------+----------------+--------------------------------------+--------------------------------------+
| 1a069091-4b79-4f0e-b197-cf62d5c9aee2 | 192.168.56.108 | a15e0cf4-e456-4047-8793-ed366dce5087 | 5f693f46-8f51-47be-a04a-d3ddf7bb2899 |
| 3a76a4ae-472e-4d1a-bf66-52ab314bbf96 | 192.168.56.107 | a15e0cf4-e456-4047-8793-ed366dce5087 | 5f693f46-8f51-47be-a04a-d3ddf7bb2899 |
| e2ee384f-578b-40d5-998d-5316359a7cee | 192.168.56.100 | a15e0cf4-e456-4047-8793-ed366dce5087 | 5f693f46-8f51-47be-a04a-d3ddf7bb2899 |
+--------------------------------------+----------------+--------------------------------------+--------------------------------------+
3 rows in set (0.01 sec)

MariaDB [neutron]> select * from ipallocationpools;
+--------------------------------------+--------------------------------------+----------------+----------------+
| id                                   | subnet_id                            | first_ip       | last_ip        |
+--------------------------------------+--------------------------------------+----------------+----------------+
| 36f36478-4497-42d7-ad99-a13ea59b3d40 | a15e0cf4-e456-4047-8793-ed366dce5087 | 192.168.56.50  | 192.168.56.99  |
| ffdaf384-1b5f-4dce-8beb-f92a11594445 | a15e0cf4-e456-4047-8793-ed366dce5087 | 192.168.56.100 | 192.168.56.200 |
+--------------------------------------+--------------------------------------+----------------+----------------+

虚拟机创建流程

image

个人总结为四个阶段

  1. keystone认证阶段
  2. nova组件内部信息交互
  3. nova与其他组件的信息交互
  4. 执行创建虚拟机
具体流程如下:

image

  1. 用户名密码验证后,keystone返回一个token,此时你有个token了,就可以带着token访问nova-api了
  2. nova-api拿到你的token后向keystone验证你的token是否合法。验证成功后,把要创建虚拟机的信息(信息包含虚拟机内存大小,CPU数量等)写入到数据库里面,也写入到消息队列里面。
  3. nova-schedule监听到消息队列有创建虚拟机的消息来了,那么就连接数据库,查询计算节点信息(包括但不限于节点数量,节点资源使用情况等)后调度在某个计算节点上创建这个虚拟机。
  4. nova-computer监听到消息队列后创建虚拟机的消息后便开始创建虚拟机了,于是它开始通过nova-conductor连接数据库,查询创建虚拟机的信息(比如VCPU数量,内存大小等)。获取后就开始创建。
  5. nova-computer开始创建虚拟机了,首先向glance获取镜像,glance拿着nova-computer提交过来的token去验证,验证成功后便返回镜像信息给nova-computer。
  6. nova-computer向neutron申请网络功能,比如ip地址分配。neutron还是得先向keystone验证nova-computer的token,验证成功后才给nova-computer创建。
  7. nova-computer向cinder申请硬盘,cinder向keystone验证nova-computer的token,验证成功后才给创建硬盘。
  8. 当所有准备工作就绪以后,nova-computer就会调用libvirt去使用kvm创建虚拟机,通过轮询的方式不断查询虚拟机的状态,展现到Horizon上面且会把状态信息写入到数据库里。

深入了解虚拟机

我们继续在linux-node1上安装nova软件,之所以再次安装nova软件,是因为想看看nova-scheduler调度。
1.安装nova软件

[root@linux-node1 ~]# yum -y install openstack-nova-compute
[root@linux-node1 ~]# systemctl start openstack-nova-compute
[root@linux-node1 ~]# systemctl enable openstack-nova-compute

2.修改配置文件,因为是在之前的基础上修改过来的,所以呢这里只是添加了novncproxy_base_url和virt_type

[root@linux-node1 openrc]# vim /etc/nova/nova.conf 
[DEFAULT]
transport_url=rabbit://openstack:openstack@192.168.56.11
enabled_apis=osapi_compute,metadata
auth_strategy = keystone   
use_neutron=True 
firewall_driver = nova.virt.firewall.NoopFirewallDriver
[api_database]
connection=mysql+pymysql://nova:nova@192.168.56.11/nova_api

[database]
connection = mysql+pymysql://nova:nova@192.168.56.11/nova
[ephemeral_storage_encryption]
[glance]
api_servers= http://192.168.56.11:9292

[vnc]
vncserver_listen=0.0.0.0
vncserver_proxyclient_address=192.168.56.11
novncproxy_base_url=http://192.168.56.11:6080/vnc_auto.html
[keystone_authtoken]
auth_uri = http://192.168.56.11:5000
auth_url = http://192.168.56.11:35357
memcached_servers = 192.168.56.11:11211
auth_type = password
project_domain_name = default
user_domain_name = default
project_name = service
username = nova
password = nova
[libvirt]
virt_type=kvm
[neutron]
url = http://192.168.56.11:9696     
auth_url = http://192.168.56.11:35357
auth_type = password
project_domain_name = Default
user_domain_name = Default
region_name = RegionOne
project_name = service
username = neutron
password = neutron
service_metadata_proxy = True
metadata_proxy_shared_secret = oldboy 
[oslo_concurrency]
lock_path = /var/lib/nova/tmp

3.查看novalist

[root@linux-node1 openrc]# source admin_openrc 
[root@linux-node1 openrc]# nova service-list
+----+------------------+-------------------------+----------+---------+-------+----------------------------+-----------------+
| Id | Binary           | Host                    | Zone     | Status  | State | Updated_at                 | Disabled Reason |
+----+------------------+-------------------------+----------+---------+-------+----------------------------+-----------------+
| 1  | nova-conductor   | linux-node1.example.com | internal | enabled | up    | 2017-01-31T04:01:38.000000 | -               |
| 3  | nova-consoleauth | linux-node1.example.com | internal | enabled | up    | 2017-01-31T04:01:41.000000 | -               |
| 4  | nova-scheduler   | linux-node1.example.com | internal | enabled | up    | 2017-01-31T04:01:41.000000 | -               |
| 7  | nova-compute     | linux-node2.example.com | default  | enabled | up    | 2017-01-31T04:01:40.000000 | -               |
| 8  | nova-compute     | linux-node1.example.com | nova     | enabled | up    | 2017-01-31T04:01:40.000000 | -               |
+----+------------------+-------------------------+----------+---------+-------+----------------------------+-----------------+

4.在Horizon上创建虚拟机,我这里创建了2台,保持刚才默认的m1.nano配置(m1.nano是云主机的配置,创建命令如下:openstack flavor create --id 0 --vcpus 1 --ram 64 --disk 1 m1.nano
5.在linux-node1上查看镜像实例和虚拟机实例

  • 镜像实例信息
[root@linux-node1 openrc]# cd /var/lib/glance/images/   # 查看镜像实例
[root@linux-node1 images]# ll -rth     # 每一个目录名字就是镜像ID,我们通过openstack image list可以对比发现镜像ID一致
total 12980
-rw-r----- 1 glance glance 13287936 Jan  3 14:46 5c242396-3c7b-4bc6-bec1-3df8eb57d53d   
[root@linux-node1 images]# openstack image list
+--------------------------------------+--------+--------+
| ID                                   | Name   | Status |
+--------------------------------------+--------+--------+
| 5c242396-3c7b-4bc6-bec1-3df8eb57d53d | cirros | active |
+--------------------------------------+--------+--------+
  • 虚拟机实例
    虚拟机实例ID可以在dashboard(horizon)上面查看到的,在一台虚拟机概况里面就可以看到虚拟机ID
[root@linux-node1 images]# virsh  list --all
 Id    Name                           State
----------------------------------------------------
 1     instance-00000003              running
 -     CentOs-7-x86_64                shut off    # 之前通过kvm创建的CentOs7,和Openstack没关系

[root@linux-node1 images]# cd /var/lib/nova/instances/
[root@linux-node1 instances]# ll -rt
total 4
drwxr-xr-x 2 nova nova 53 Feb  6 22:09 _base   # 
drwxr-xr-x 2 nova nova 91 Feb  7 10:33 locks
-rw-r--r-- 1 nova nova 46 Feb  7 10:33 compute_nodes
drwxr-xr-x 2 nova nova 69 Feb  7 10:54 da0e8cfc-46f9-416d-a828-397d8d7e4023   # 这个目录名字就是虚拟机ID
[root@linux-node1 instances]# cd _base/
[root@linux-node1 _base]# tree
.
└── 7312bbe40cc8bc765d86d2dbd58d0a57a83ee215

6.进入虚拟机实例目录里面查看信息

[root@linux-node2 instances]# cd bfa193b8-cf7c-4a5f-b73a-7089a0e567db/   # 这个虚拟机调度到node2上创建了,目录还是/var/lib/nova/instance下面查看
[root@linux-node2 bfa193b8-cf7c-4a5f-b73a-7089a0e567db]# ll -rt
total 2212
-rw-r--r--. 1 nova nova      79 Feb  7 10:54 disk.info     #磁盘信息文件
-rw-r--r--. 1 nova nova    2643 Feb  7 10:54 libvirt.xml   # 虚拟机配置文件,通过libvirt创建
-rw-r--r--. 1 nova qemu   19268 Feb  7 10:55 console.log  # 控制台日志输出
-rw-r--r--. 1 qemu qemu 2293760 Feb  7 10:55 disk  # 磁盘文件
  • disk文件解读
    大家可以看到磁盘文件大小为2.2M,虚拟机大小肯定不止2.2M的,那么肯定是有疑问的,我们看看这个文件的内容
[root@linux-node2 bfa193b8-cf7c-4a5f-b73a-7089a0e567db]# file disk
disk: QEMU QCOW Image (v3), has backing file (path /var/lib/nova/instances/_base/7312bbe40cc8bc765d86d2dbd58d0a57a), 1073741824 bytes

[root@linux-node2 bfa193b8-cf7c-4a5f-b73a-7089a0e567db]# qemu-img info disk
image: disk
file format: qcow2
virtual size: 1.0G (1073741824 bytes)   # 虚拟机大小为1G
disk size: 2.1M    # 实际大小为2.1M
cluster_size: 65536
backing file: /var/lib/nova/instances/_base/7312bbe40cc8bc765d86d2dbd58d0a57a83ee215
Format specific information:
    compat: 1.1
    lazy refcounts: false

通过file disk这个命令查看磁盘文件后,我们发现这个QCOW还有一个文件是在后端存放的,存放地址是在/var/lib/nova/instances/_base/7312bbe40cc8bc765d86d2dbd58d0a57a,这个QCOW是显示拷贝的镜像,他有这么一个特性,虚拟机的现有磁盘文件(disk)只记录比之前的镜像文件不一样的信息,所谓的之前的镜像文件就是创建虚拟机的镜像文件,我们这里使用的是cirros,所以disk文件记录是与cirros镜像文件不一样的信息,所以disk文件大小为2.2M也是可以理解的了。

我们看看disk的后端文件:

[root@linux-node2 instances]# cd _base/
[root@linux-node2 _base]# qemu-img info 7312bbe40cc8bc765d86d2dbd58d0a57a83ee215
image: 7312bbe40cc8bc765d86d2dbd58d0a57a83ee215
file format: raw
virtual size: 39M (41126400 bytes)
disk size: 18M

这里可以看出后端文件的镜像格式为raw,raw性能最好,不支持加密,压缩。而qcow支持加密,压缩等,性能稍弱。

  • disk.info文件解读
    字典格式内容,key为磁盘文件位置,value为磁盘格式
[root@linux-node2 bfa193b8-cf7c-4a5f-b73a-7089a0e567db]# cat disk.info  
{"/var/lib/nova/instances/bfa193b8-cf7c-4a5f-b73a-7089a0e567db/disk": "qcow2"}
  • libvirt.xml 解读
    因为是通过libvirt创建的虚拟机,所以关于libvirt.xml文件的配置我们可以参考这个博文:http://www.cnblogs.com/liaojiafa/p/6194056.html
    这个libvirt.xml别去修改,修改完重启虚拟机后又恢复到最初的配置状态,除非改完别重启就行。
    我们可以看看在qemu下面关于这个虚拟机的XML配置
[root@linux-node2 bfa193b8-cf7c-4a5f-b73a-7089a0e567db]# cd /etc/libvirt/qemu/
[root@linux-node2 qemu]# ls
instance-00000004.xml  networks
[root@linux-node2 qemu]# cat instance-00000004.xml
.....省略几万字

7.浅谈虚拟机的网络

[root@linux-node1 instances]# brctl show
bridge name	bridge id		STP enabled	interfaces
brq33b9ef50-35		8000.000c29c37394	no		eth0
							tap6a790b22-29
							tapd605dd99-52
virbr0		8000.525400babde7	yes		virbr0-nic

通过brctl show可以查看到桥接网卡,brq33b9ef50 桥接到了eth0上,那么怎么理解这个呢?我们可以这么理解,我们把show出来的整个看做为一个交换机,eth0是插在这个交换机的某一个口,tap6a790b22-29也是插在这个交换机的某一个口,但是tap6a790b22-29是成对出现的,一端插在这个交换机上,一端插在虚拟机上的。所以本机只能连接上本机创建的虚拟机。

8.解读metadata
说道在创建虚拟机的时候,我们那时上传了公钥,那么问题来了,虚拟机是如何获取到这个公钥信息的呢?下面我们先看看虚拟机的console.log,

[root@linux-node1 da0e8cfc-46f9-416d-a828-397d8d7e4023]# pwd    # 这个路径下面
/var/lib/nova/instances/da0e8cfc-46f9-416d-a828-397d8d7e4023

[root@linux-node1 da0e8cfc-46f9-416d-a828-397d8d7e4023]# grep checking console.log
checking http://169.254.169.254/2009-04-04/instance-id     # 找到这个URL

拿到这个URL后,我们可以连接上虚拟机,通过curl获取我们所需的资料:

$ curl http://169.254.169.254/2009-04-04/meta-data/    # 罗列出所需要的信息
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
hostname
instance-action
instance-id
instance-type
local-hostname
local-ipv4
placement/
public-hostname
public-ipv4
public-keys/
reservation-id
$ curl http://169.254.169.254/2009-04-04/meta-data/public-keys/0/openssh-key       # 获取公钥信息
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCchdZOe760nvviGqVqUHebS4nBF0KGJ+6P831glLbDXaMQ8pBzFMgpaiLyiLh4eOPPLmOJQC2jtzaq515XhQoOQ/fHjh7diw6e8NIIhyPwiwx7yvB8xD8m4QtL0ewwwo0OQEgCeXoLIRqn0/i+nIlji6Im6kJMu1WJQmR5PUyVJbLJV8uUabB7UCcIRBRdvgRiFKuWtZ45TeNEGrdtmKAmjMpcZUENE9txXwuRaGNdnx/aEH6C9uzX1kWE8FfpvemBdInKrq0+m1+dC0x6u0zMsfCd0JHV1K/14wJH84oX+9j3hSxqTABQGOwCAXzox6HX5AJk/bP/JsFNP1HdY4C5 root@linux-node1.example.com

$ curl http://169.254.169.254/2009-04-04/meta-data/hostname       # 获取主机名

这个metadata是由dhcp配置文件来控制的,如下所示:

[root@linux-node1 tmp]# vim /etc/neutron/dhcp_agent.ini
enable_isolated_metadata = True     # 这个配置项

关于169.254.169.251这个IP,我们在虚拟机上查看下这个路由信息

$ ip ro li
default via 192.168.56.2 dev eth0
169.254.169.254 via 192.168.56.100 dev eth0    # 访问169.254.169.254 其实就是访问了 192.168.56.100 , 192.168.56.100是我们DHCP服务器的地址
192.168.56.0/24 dev eth0  src 192.168.56.108

在宿主机上查看:

[root@linux-node1 da0e8cfc-46f9-416d-a828-397d8d7e4023]# ip netns li
qdhcp-33b9ef50-354b-457b-8ea2-8498ec64835b (id: 0)
[root@linux-node1 da0e8cfc-46f9-416d-a828-397d8d7e4023]# ip netns exec qdhcp-33b9ef50-354b-457b-8ea2-8498ec64835b ip ad li
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ns-d605dd99-52@if5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP qlen 1000   
    link/ether fa:16:3e:a1:e7:54 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 192.168.56.100/24 brd 192.168.56.255 scope global ns-d605dd99-52     #
       valid_lft forever preferred_lft forever 
    inet 169.254.169.254/16 brd 169.254.255.255 scope global ns-d605dd99-52   # 有169.254.169.254这个IP
       valid_lft forever preferred_lft forever
    inet6 fe80::f816:3eff:fea1:e754/64 scope link
       valid_lft forever preferred_lft forever

通过上面的列子,因为就知道了虚拟机的公钥是怎么来的了。
关于169.254.169.254,是因为以前亚马逊镜像是使用169.254.169.254这个ip,后来Openstack为了和亚马逊的兼容,所以也是用了这个IP