cloudstack-4.15版本最全入门笔记(2022最新版)

4,575 阅读15分钟

cloudstack简介

CloudStack是一个开源的具有高可用性及扩展性的云计算平台。目前Cloudstack支持管理大部分主流的hypervisors,如KVM,XenServer,VMware,Oracle VM,Xen等。同时CloudStack是一个开源云计算解决方案。可以加速高伸缩性的公共和私有云(IaaS)的部署、管理、配置。使用CloudStack作为基础,数据中心操作者可以快速方便的通过现存基础架构创建云服务。

cloudstack概念架构

1614317659268

  • 用户:通过网络访问属于“我”的虚拟机、存储空间、管理“我”的个人模板。
  • 管理员:部署配置,管理账户,监控资源,安排作业,排除故障。
  • 开发人员:开发计费、监控、统计报表等功能模块,定制图形界面、工作流。

cloudstack部署架构

1614318138954

上图中的各个组件介绍如下:

  • Regions:为了提高云的可靠性,可以选择将资源分为多个地理区域。区域是CloudStack部署中最大的可用组织单位。一个区域由几个可用性zones组成,其中每个zone大致相当于一个数据中心。每个Regions均由其在一个zone中运行的管理服务器集群控制。regions中的zones通常相隔非常近。Regions 是用于提供容错和灾难恢复的有用技术。

  • Zone:Zone 对应于现实中的一个数据中心。

  • Pod:Pod 对应着一个机架。同一个 pod 中的机器在同一个子网(网段)中。

  • Cluster:Cluster 是多个主机组成的一个集群。同一个 cluster 中的主机有相同的硬件,相同的 Hypervisor,和共用同样的存储。同一个 cluster 中的虚拟机,可以实现无中断服务地从一个主机迁移到另外一个上。

  • Host:Host 就是运行虚拟机(VM)的主机。

  • CloudStack存储按用途分为主存储(Primary Storage)二级存储(Secondary Storage),主存储用来存储虚拟机的卷,二级存储用来存放虚拟机的模板,ISO镜像和快照

  • Primary storage:一级存储与 cluster 关联,它为该 cluster 中的主机的全部虚拟机提供磁盘卷。一个 cluster 至少有一个一级存储,且在部署时位置要临近主机以提供高性能。

  • Secondary storage:二级存储与 zone 关联,它存储模板文件,ISO 镜像和磁盘卷快照。

    • 模板:可以启动虚拟机的操作系统镜像,也包括了诸如已安装应用的其余配置信息。
    • ISO 镜像:包含操作系统数据或启动媒质的磁盘镜像。
    • 磁盘卷快照:虚拟机数据的已储存副本,能用于数据恢复或者创建新模板。

即从包含关系上来说,一个regions包含多个zone,一个 zone 包含多个 pod,一个 pod 包含多个 cluster,一个 cluster 包含多个 host。

cloudstack和kvm一起部署的架构

1614319012106

如上所述:在每个kvm的宿主机上都需要部署agent程序。

如果部署vmware的产品就必须部署vcenter server。

cloudstack部署实践

关于这部分内容,请阅读官方文档

主要是介绍cloudstack支持的存储协议、架构模式、机器配置等建议。

cloudstack和openstack比较

难易度适合规模参考资料是否开源市场占有率
cloudstack组件少,较易小、中、大较少
openstack组件多,较难中、大较多

部署cloudstack

环境准备

OS主机名IPrulesservicesexplain
cenots 7.7management10.10.10.5管理节点chronyd、cloudstack-management、mariadb、nginx用于管理整个cloudstack,最少2c4g
cenots 7.7kvm110.10.10.88计算节点kvm用于运行租户创建的虚拟机,建议4c4g
cenots 7.7nfs10.10.10.87存储节点nfs提供主存储、二级存储,最少2g1c,一块100G磁盘

系统初始化

系统初始化这一小节,如果没有特别说明,均需在所有节点执行。

修改主机名


$ hostnamectl set-hostname management

$ hostnamectl set-hostname kvm1

$ hostnamectl set-hostname nfs
source /etc/profile

关闭防火墙及selinux

在生产环境中,建议防火墙放行内网网段即可。selinux设置为permissive模式。(视频资料中说selinux设置为disable会有问题,具体待验证。)


$ systemctl stop firewalld && systemctl disable firewalld

setenforce 0

sed -i 's#^SELINUX=.*#SELINUX=permissive#g' /etc/selinux/config

sed -i 's#^SELINUX=.*#SELINUX=permissive#g' /etc/sysconfig/selinux

配置yum源

这一步主要是将默认的国外yum源配置为国内阿里的yum,如果你有自己内网的yum源服务器,请跳过此步骤。

# 更换为阿里云源
wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
cat > /etc/yum.repos.d/cloudstack.repo << 'EOF'
[cloudstack]
name=cloudstack
baseurl=http://download.cloudstack.org/centos/$releasever/4.15/
enabled=1
gpgcheck=0
EOF

配置nfs服务

(此步骤在nfs节点执行)

 yum -y install nfs-utils

cat >> /etc/exports << EOF

/export/secondary *(rw,async,no_root_squash,no_subtree_check)

/export/primary *(rw,async,no_root_squash,no_subtree_check)

EOF
# 创建共享目录
mkdir /export/{primary,secondary}
# 启动nfs
systemctl enable rpcbind nfs
systemctl start rpcbind nfs
exportfs -a 表示将所有的更新
其他任意节点确保可查看到共享目录:
showmount -e 10.10.10.87

安装cloudstack-management

yum install -y cloudstack-management cloudstack-common

导入数据库(此处使用已有的mysql作为存储)


# 可通过 cloudstack-setup-databases --help 查看其他命令选项及其含义
cloudstack-setup-databases cloud:password00038@10.10.10.5:3306 --deploy-as=usertest:password00038
# cloud:password00038:表示执行成功后,mysql数据库将创建一个密码为password00038的cloud用户供管理节点使用。
# --deploy-as:指定你本次使用哪个数据库用户导入数据

额外提下:如果server要做高可用,在另一节点只需要执行cloudstack-setup-databases cloud:password00038@10.10.10.5:3306不要加不要加不要加--deploy-as参数

输出如下信息,表示数据库初始化成功:

1614335594963

启动manager server

# 第一次启动请这样启动

$ cloudstack-setup-management

# 执行后将输出如下信息,则表示启动成功:

Starting to configure CloudStack Management Server:

Configure Firewall ... [OK]

Configure CloudStack Management Server ...[OK]

CloudStack Management Server setup is Done!
# 后续启动方式请使用
$ systemctl restart cloudstack-management

访问dashboard

访问management的8080端口,可以看到如下界面,则表示management安装成功(默认用户名/密码:admin/password):

image-20220210170709372

上传系统虚拟机模板

可参考管理服务器安装 — 文档 (apache.org)

此处使用单独的 NFS 服务器,在server上执行挂载

mount -t nfs 10.10.10.87:/export/secondary /mnt/secondary

cloudstack默认依赖几个虚机运行,当cloudstack添加区域后,他将自动启动相关虚机,所以还需要将镜像导入到二级存储中。

下载虚拟机系统模板,放到二级存储目录上

#官方下载地址http://cloudstack.apt-get.eu/systemvm/4.15/
wget http://cloudstack.apt-get.eu/systemvm/4.15/systemvmtemplate-4.15.0-kvm.qcow2.bz2 
wget http://cloudstack.apt-get.eu/systemvm/4.15/systemvmtemplate-4.15.0-vmware.ova
wget http://cloudstack.apt-get.eu/systemvm/4.15/systemvmtemplate-4.15.0-ovm.raw.bz2
wget http://cloudstack.apt-get.eu/systemvm/4.15/systemvmtemplate-4.15.0-xen.vhd.bz2
wget http://cloudstack.apt-get.eu/systemvm/4.15/systemvmtemplate-4.15.0-hyperv.vhd.zip

#kvm
/usr/share/cloudstack-common/scripts/storage/secondary/cloud-install-sys-tmplt \
-m /export/secondary  -f /root/systemvmtemplate-4.15.0-kvm.qcow2.bz2  -h kvm -F

#vmware
/usr/share/cloudstack-common/scripts/storage/secondary/cloud-install-sys-tmplt \
-m /export/secondary  -f /root/systemvmtemplate-4.15.0-vmware.ova  -h vmware -F

输出如下,则表示上传成功:

1614339908396

如果您使用的是单独的 NFS 服务器,请务必执行此步骤,不然下次重启server时候会报错卡主

# 卸载辅助存储并删除创建的目录
umount /mnt/secondary
rmdir /mnt/secondary

报错如下:

 [o.a.c.s.NfsMountManager] (main:null) (logid:) Clean up mounted NFS mount points used in current session.

配置计算节点

注:此步骤只需在计算节点上执行。

修改网络配置

主要是生成一个网桥设备,以便VM都可以连接到此网桥。

# 备份源网卡配置文件
$ cp /etc/sysconfig/network-scripts/ifcfg-ens33{,_$(date +%F_%H).bak}
# 修改网卡配置文件
cat > /etc/sysconfig/network-scripts/ifcfg-ens33 << EOF
TYPE=Ethernet
DEVICE=ens33
ONBOOT=yes
BRIDGE=cloudbr0
EOF

cat > /etc/sysconfig/network-scripts/ifcfg-cloudbr0 << EOF
DEVICE=cloudbr0
TYPE=Bridge
ONBOOT=yes
BOOTPROTO=static
IPADDR=10.10.10.22
GATEWAY=10.10.10.1
DNS1=223.5.5.5
EOF
# 修改完成后,务必检查是否修改正确,网卡名称、IP地址与你的实际环境是否一致
# 确认无误后,重启网络服务

$ systemctl restart network
安装cloudstack-agent
yum -y install cloudstack-agent cloudstack-common
确认加载kvm模块
$ lsmod | grep kvm # 输出如下,则表示已加载
[root@WT-TEST-10-22 ~]# lsmod | grep kvm
kvm_intel             188688  15 
kvm                   636969  1 kvm_intel
irqbypass              13503  21 kvm
[root@WT-TEST-10-22 ~]# 
安装ibvirt

yum -y install qemu-kvm libvirt python-virtinst bridge-utils

修改libvirt默认配置

修改vnc默认监听地址

sed -i 's/^#vnc_listen =.*/vnc_listen = "0.0.0.0"/g' /etc/libvirt/qemu.conf

# 指定cloudstack-management地址
sed -i "s/host=.*/host=10.10.10.5/g" /etc/cloudstack/agent/agent.properties
# 重启libvirt
systemctl restart libvirtd
启动cloudstack-agent并加入开机自启

$ systemctl start cloudstack-agent && systemctl enable cloudstack-agent

规划cloustack网络

在cloudstack中,提供了两种网络:基本网络和高级网络。这篇博文将基于基本网络进行配置,如果要配置高级网络,那么这篇博文不用继续往下看了,移步即可。

基本网络

基本网络模式采用传统扁平网络,与现有网络完美兼容,其网络通信拓扑如下:

1614324293753

基础网络模式只提供了简单的网络模型,管理网络、来宾网络、存储网络、V-Route(只提供了DNS、dhcp,并不提供网关服务)等。

高级网络

高级网络模式中,每个租户获得一个或多个来宾网络,每个网络属于独立的VLAN,由虚拟路由器为这些来宾网络提供网关服务。

虚拟路由器(系统虚拟机提供)在高级网络中十分重要,它将成为租户私有网络与公共网络之间的接口,并未租户私有网络提供各种网络服务,包括NAT、静态NAT、DHCP、DNS、Load Balancing、Port Forwording、Firewalls、Site-to-Site 虚拟专用网、VPC等。

高级网络架构图如下:1614324480867

两种不同的网络,对来宾网络(Guest Network)采用的隔离方式不同,在基本模式下,采用安全组(Security Group)方式进行隔离;而在高级网络中,采用VLAN方式进行隔离。

具体网络规划(内网实验用)

角色网络
management+nfs10.10.10.5
kvm110.10.10.22
虚拟机(来宾网络)10.10.10.150-10.10.10.160
创建zone

登录到cloudstack-management的管理控制台,进行如下操作,以便添加第一个zone:

1、网络选择基本的: image-20211126114504059 2、资源域详细信息:

网络方案介绍:

  • DefaultSharedNetworkOfferingWithSGService:带有安全组的网络方案,推荐选择此选项。

  • DefaultSharedNetworkOffering:不带安全组功能。

  • DefaultSharedNetscalerEIPandELBNetworkOffering:如果你在CLOUDSTACK内安装了Citrix NetScaler应用,并且你需要Elastic IP和Elastic Load Balancing这些功能的话,那就选择这个选项。EIP 和ELB技术在安全组启用的情况下,可以提供1:1的NAT映射和负载均衡功能。此功能需要citrix硬件支持,一般很少用。

  • QuickCloudNoServices:表示什么都不用,新增选项,不建议使用。

image-20220211100054911

3、网络

image-20211126114526260

4、添加提供点

image-20220211100245480

5、设置来宾网络

image-20211126114424578

6、设置群集名称

image-20220211100614341

7、添加kvm主机节点

image-20220211100749497

8、配置主存储

image-20220211101037328

9、配置二级存储

image-20220211101119055

11、启用资源域

激动人心的时刻到了,这一步,将检验你之前所有的操作是否正确。

image-20220211101612943

验证cloudstack可用性

至此,你必须保证下面的任意资源都为“UP” 状态,如下:

1、资源域状态为“Enabled”

2、提供点状态状态为“Enabled”

3、群集状态“Enabled”

4、主机状态必须为 “Up”

5、主存储状态必须为 “Up”

6、二级存储状态必须为 “Up”

7、系统VM必须正常状态必须为 “Up”

至此,可以说你的集群完全可用了,但虚拟路由器现在数量为 “0”,当我们创建第一个实例后,虚拟路由器就被创建了。

注册ISO

现在,我们就来启动一个实例进行验证。需要自行准备一张系统盘。最好是centos的。附:centos镜像下载链接

注册ISO镜像支持两种方式,一种是从本地上传,一种是从一个URL主机,为了速度考虑,建议自行准备http服务器,然后配置为文件服务器,进行注册ISO(本地上传功能不靠谱)。

1、安装nginx

任意节点安装即可,既然博文开头规划在了management节点,那就在这个节点吧。

server {
	listen 80;
	server_name  10.10.10.5;
location / {
root /data;
autoindex on;
autoindex_exact_size off;
autoindex_localtime on;
}
}
#可以看到在正常下载即可:
wget http://10.10.10.5/CentOS-7-x86_64-DVD-2009.iso

2、定义允许哪些网络可以访问二级存储(不配置iso安装的时候会拒绝

secstorage.allowed.internal.sites

image-20220211140151997

3、重启cloudstack-management生效

systemctl restart cloudstack-management

重启后,需要稍等片刻,待8080端口在监听,才可以继续访问控制台:

4、注册ISO

访问控制台,进行如下操作:

image-20220211110611827

查看镜像上传进度:

自行刷新页面,直至完成:

稍等片刻,看到镜像状态如下,则表示成功:

image-20220210174132652

修改下全局配置

  • cpu.overprovisioning.factor

    CPU超配倍数 一般设置3-4倍,内存超配倍数,如果准备运行虚拟机大多是Linux无界面系统,可以设置2倍,如果运行Windows等有图形界面的。最好选择不修改,因为经过个人测试,图形界面虚拟机相应的qemu进程真的会占用宿主机分配的那么多内存。当然,这有可能是因为我使用kvm 作为虚拟机管理程序的原因,Xenserver或者VMware可能会好一点。

  • mem.overprovisioning.factor

    内存超配倍数

  • secstorage.allowed.internal.sites 允许下载模板或ISO的网段。。。可以设置成0.0.0.0/0表示允许所有网段,或者设置成 自己网络所在网段如 192.168.199.0/24

  • kvm.snapshot.enabled

    是否启用KVM 的VM快照功能

  • expunge.delay

    删除vm后,卷清理等待时间

  • enable.dynamic.scale.vm 配置动态 CPU 和内存扩展

创建vm虚拟机

网页捕获_10-2-2022_181546_10.10.10.5

启动VM后,进入虚机控制台,则可以和正常一样装系统了

image-20220211111151250

此处省略一万字...............

取消附加ISO

当装完系统后,重启VM,默认还会进入装系统的界面,我们需要进行如下操作,才可以正常使用虚机,如下(需在VM关机状态下执行):

image-20220211112640542

安装centos系统后 ,没有IP地址

  • 配置网卡及初始化系统
#必须要修改网络接口的配置文件,编辑/etc/sysconfig/network-scripts/ifcfg-eth0文件
TYPE=Ethernet
BOOTPROTO=dhcp
NAME=eth0
DEVICE=eth0
ONBOOT=yes
DNS1=223.5.5.5
PEERDNS="no"  #该配置可以禁用dhclient-script更新 resolv.conf文件,否则手动修改dns后重启系统后被改回虚拟路由器的地址,还有另外一种方法见https://forums.centos.org/viewtopic.php?t=24741#p116748
#----------系统初始化操作可以放在此处---------------------
#移除udev持久设备规则
rm -f /etc/udev/rules.d/70*
rm -f /var/lib/dhclient/*
#移除SSH Keys这步是为了确认所有要作为模板的VMs的SSH Keys都不相同,否则这样会降低虚拟机的安全性。
rm -f /etc/ssh/*key*
#清除日志文件,从主模板移除旧的日志文件是一个好习惯。
cat /dev/null > /var/log/audit/audit.log 2>/dev/null1
cat /dev/null > /var/log/wtmp 2>/dev/null
logrotate -f /etc/logrotate.conf 2>/dev/null
rm -f /var/log/*-* /var/log/*.gz 2>/dev/null
#清除用户历史bash命令。
history -c
unset HISTFILE
#关闭selinux和防火墙
sed -i "s/SELINUX=enforcing/SELINUX=disable/g" /etc/selinux/config
systemctl stop firewalld.service;systemctl disable firewalld.service

  • 关闭VM
  • 基于关闭vm的模板并且创建新模板

从当前实例进来,点击**卷**

image-20220211135258662

基于卷创建模板

image-20220211135335267

image-20220211135414820

等待最终模板的状态是"Download Complete"

image-20220211140055511

从此可以使用"tmp_centos7"模板创建虚拟机了

API操作

官网

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @Time    : 2022/2/11 14:41
# @Author  : XuLiLiang
# @Email   : xuliliang@epailive.com
# @File    : cloudstack-api.py
import base64
import hashlib
import hmac
import json
import urllib

import httpx

import requests
from urllib import parse

baseurl = 'http://10.10.10.5:8080/client/api?'
api_key = '6QAhoFguQkIo-pxyggaaVRlsiSAD--Xrg18_5SDB4xptddp1PEcNCe5RcryXLhUKXox6Rujlcun4A8kRcbX23g'
secret_key = 'kVN31BNvJzl-60KgI2Y_n9DDk13Loe2PMn85g2VZoM8R6LgKYqZ5XOszjTAiENgSnsacjkmLMoX2f0754Z38eA'


class CloudApi:
    def __init__(self):
        self.baseurl = baseurl
        self.api_key = api_key
        self.secret_key = secret_key

    def GetSing(self, params,args=None):
        """
         封装request
        """

        params['response'] = 'json'
        params['apikey'] = self.api_key
        request_str = '&'.join(['='.join([k, parse.quote_plus(params[k])]) for k in params.keys()])
        sig_url = urllib.parse.urlencode(params)
        sig_str = '&'.join(['='.join([k.lower(), parse.quote_plus(params[k].lower().replace('+', '%20'))]) for k in
                            sorted(params.keys())])
        # sig1 = hmac.new(self.secret_key, sig_str, hashlib.sha1).digest()
        # sig2 = base64.encodebytes(hmac.new(self.secret_key, sig_str, hashlib.sha1).digest())
        # sig3 = base64.encodebytes(hmac.new(self.secret_key, sig_str, hashlib.sha1).digest()).strip()
        sig = parse.quote_plus(base64.b64encode(hmac.new(self.secret_key.encode('utf-8'), sig_str.encode('utf-8'), hashlib.sha1).digest()).strip())
        req = self.baseurl + request_str +args + '&signature=' + sig
        return req

    def listVirtualMachines(self,name=''):
        """
        列出vms
        """
        params = {
            'command': 'listVirtualMachines',
            'name':name
        }
        req = self.GetSing(params)
        s = requests.get(req).content.decode("utf-8")
        res = json.loads(s)
        intances=[]
        if not res['listvirtualmachinesresponse']['virtualmachine']:
            print("VM is \033[31mnot exist\033[0m!")
            return False
        else:
            if name:
                for instance in res['listvirtualmachinesresponse']['virtualmachine']:
                    try:
                        if name==instance['name']:
                            instance_info = [instance['id'], instance['name'], instance['nic'][0]['ipaddress'],
                                             instance['state']]
                            intances.append(instance_info)
                            break
                    except Exception as e:
                        raise e
            else:
                for instance in res['listvirtualmachinesresponse']['virtualmachine']:
                    if instance['state'] != 'Error' :
                        instance_info = [instance['id'], instance['name'],instance['nic'][0]['ipaddress'],
                                     instance['state']]
                        intances.append(instance_info)
        # print(intances)
        return intances


    def listTemplates(self):
        """
        获取模板列表
        """
        args_dict = {
            'command': 'listTemplates',
            'templatefilter': 'all',
        }
        req = self.GetSing(args_dict)
        httpx.AsyncClient()
        s = httpx.get(req)
        response = json.loads(s.content.decode("utf-8"))['listtemplatesresponse']['template']
        for x in response:
            print(x['id'], x['name'])

    def getZones(self):
        """
        获取zones
        """
        args_dict = {
            'command': 'listZones',
        }
        req = self.GetSing(args_dict)
        s = requests.get(req)
        response = json.loads(s.content.decode("utf-8"))['listzonesresponse']['zone']
        for x in response:
            print (x['id'])
            return (x['id'])

    def listServiceOfferings(self):
        """
        列出计算方案
        """
        args_dict = {
            'command': 'listServiceOfferings',
        }
        req = self.GetSing(args_dict)
        s = requests.get(req)
        response = json.loads(s.content.decode("utf-8"))['listserviceofferingsresponse']['serviceoffering']
        for x in response:
            print(x['id'], x['name'])
        # print(response)

    def listDiskOfferings(self):
        """
        列出所有磁盘方案
        """
        args_dict = {
            'command': 'listDiskOfferings',
        }
        req = self.GetSing(args_dict)
        try:
            with httpx.Client() as client:
                s = client.get(req, timeout=10)
                response = json.loads(s.content.decode("utf-8"))
                print(response)
        except Exception as e:
            raise e


    def deployVirtualMachine(self):
        """
        创建vm
        """
        import uuid
        suid = str(uuid.uuid4())
        vm_name = f"VM-{suid}"
        filepath = 'init.sh'
        with open(filepath, 'rb') as f1:
            base64_str = base64.encodebytes(f1.read())
            u_data = base64_str.decode('utf-8')
            # print(u_data)

        # user_data = """#!/bin/bash
        # env
        # echo "export AAAAAA=1234567890" >> ~/.bashrc
        # echo "export BBBBBB=0123456789" >> ~/.bashrc
        # source ~/.bashrc
        # env
        # """
        # bs = str(base64.b64encode(user_data.encode("utf-8")), "utf-8")
        # print(bs)
        args_dict = {
            'command': 'deployVirtualMachine',
            "diskofferingid": '6f17d452-b307-4005-93db-5f4e9060f1af',
            "name": vm_name,
            "serviceofferingid": '7e792106-53f6-402d-a671-80e86cc00486',
            "templateid": '4ecb75d3-c060-4ccf-af88-717e2987389e',
            "zoneid": '2d6c3c0a-6c42-4c10-8273-5879bf02cc70',

        }
        data=f"&userdata={u_data}"

        req = self.GetSing(args_dict,data)
        try:
                s = requests.get(req)
                response = json.loads(s.content.decode("utf-8"))
                print(response)
        except Exception as e:
            raise e

    def actionVirtualMachine(self, id, action):
        status = {
            'destroy': 'destroy',
            'recover': 'recover',
            'start': 'start',
            'stop': 'stop',
            'reboot': 'reboot',
            'restore':'restore',
            'running':'running',
        }
        host_info=self.listVirtualMachines(id)
        print(host_info)
        if host_info[0][3] == status[action]:
            print("The \033[36m%s\033[0m Status is \033[33m%s\033[0m !")
            import sys
            sys.exit(500)

        args_dict = {
            'command': status[action]+'VirtualMachine',
            'id': host_info[0][0],
        }
        req = self.GetSing(args_dict)
        try:
            s = requests.get(req)
            res = json.loads(s.content.decode("utf-8"))
            print(res)
        except Exception as e:
            raise e

    def queryAsyncJobResult(self,id):
        args_dict={
            'command': 'queryAsyncJobResult',
            'jobid':id,
        }
        req = self.GetSing(args_dict)
        try:
            with httpx.Client() as client:
                s = client.get(req, timeout=10)
                response = json.loads(s.content.decode("utf-8"))
                print(response)
                return response
        except Exception as e:
            raise e
    def listNetworks(self):
        args_dict={
            'command': 'listNetworks',
        }
        req = self.GetSing(args_dict)
        try:
            with httpx.Client() as client:
                s = client.get(req, timeout=10)
                response = json.loads(s.content.decode("utf-8"))
                print(response)
                return response
        except Exception as e:
            raise e


if __name__ == '__main__':
    api = CloudApi()
    # 0 - Job表示程序还在进行
    # 1 - Job表示成功地完成了
    # 2 - Job表示执行失败
    # api.listVirtualMachines()
    # api.actionVirtualMachine('centos','destroy')
    # api.listClusters()
    # api.listServiceOfferings()
    # api.listTemplates()
    # api.getZones()
    api.deployVirtualMachine()
    # api.listDiskOfferings()
    # api.listNetworks()
    # api.queryAsyncJobResult('cedd5f88-dfba-4356-9df3-c13e1d3fe47b')

cloud-init实战

在云平台中,创建云主机的时候希望能够对主机进行一些初始化操作,如配置ip,主机名,密码,ssh登录等。openstack或cloudstack上可以使用cloud-init进行初始化。

cloud-init官方文档 阿里云官方文档 安装cloud-init

前提条件 • 一台KVM机器,用于制作初始的centos7镜像 • 已为Linux云服务器绑定弹性IP。 • 已登录Linux云服务器。 • Linux云服务器的网卡属性为DHCP方式。

以下均在模板机器上配置

1. 下载cloud-init
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum install cloud-init -y 
cloud-init init --local
#设置开机自启动
systemctl enable cloud-init-local.service cloud-init.service cloud-config.service cloud-final.service
2. 安装相应的包
# ACPI服务是控制重启和关闭实例
yum -y install acpid
systemctl enable acpid
# 安装cloud-utils-growpart 允许分区调整
yum -y install cloud-utils-growpart
3. 配置Cloud-Init工具
  1. 用户可以根据需要根据用户类型配置登录云服务器的用户权限。使用root帐户登录,需要开启root用户的ssh权限,并开启密码远程登录。
  • 若用户选择注入密码,则通过自己注入的密码进行远程SSH或noVNC登录。
  • 若用户选择注入密钥,则通过自己注入的密钥进行远程SSH登录。

配置ssh文件

vim /etc/ssh/sshd_config
PasswordAuthentication yes

2 . 编辑配置文件/etc/cloud/cloud.cfg

在cloud.cfg文件增加该配置之后,cloud-init不会管理/etc/sysconfig/network-scripts/下网络配置,需要自行管理。 建议提前配置好网卡配置文件为dhcp获取,否则在私有云上创建的实例可能会导致获取不到IP地址;原因是cloud-init中的自动配置网卡文件可能会导致mac地址不一致

network:
  config: disabled

最终配置如下

users:
  - name: root
    ssh_authorized_keys: #配置远程控制节点的ssh-keygen,
      - ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAvGQtIxNSwb3DqHJuDqBW0Gz6uc/B1gc4Un+7tQgMEcBbsOtaeLlmfF8D0DT4Tk349f2wmP12TyjqG+fekXB4HvrcXq/xVzloWalN0bzXNhVhy4ymXTq4lc6vYEkXLIyWN57FFp6NHbiKHBWkKHAYraYnf5a7wWOpPTq20bdHaIi4Pw9ADxWpQMVxyeMhBW9ZvBgPr4aGpkrKmARZShmLwEWpSHxRJymErnL6p/64s3xfRY4L9YdVLGBZfrzsoNVD20tTw1ctDswEkvTp46SNLK8BcM3qIk0WUpMEcNus7WtG4pQr9cob3qVAiMYGUP5KOiDa4PdjccvWP+bkDTjzsQ== root@kaifa-dev02

disable_root: 0
ssh_pwauth: 1
preserve_hostname: flase
manage_etc_hosts: true

manage_resolv_conf: true
resolv_conf:
  nameservers:
    - 8.8.8.8
    - 8.8.4.4
  options:
    rotate: true
    timeout: 1
network:
  config: disabled
runcmd:
  - [ sh, -c, echo "=========Welcome To cloudstack'=========" > /root/runcmd.log ]
cloud_init_modules:
 - ssh
 - migrator
 - bootcmd
 - write-files
 - growpart
 - resizefs
 #- set_hostname
 #- update_hostname
 - update_etc_hosts
 - rsyslog
 - users-groups

cloud_config_modules:
 - mounts
 - locale
 - set-passwords
 - yum-add-repo
 - package-update-upgrade-install
 - timezone
 - puppet
 - chef
 - salt-minion
 - mcollective
 - disable-ec2-metadata
 - runcmd
 - ntp-conf

cloud_final_modules:
 - rightscale_userdata
 - scripts-per-once
 - scripts-per-boot
 - scripts-per-instance
 - scripts-user
 - ssh-authkey-fingerprints
 - keys-to-console
 - phone-home
 - final-message
 - power-state-change

system_info:
   distro: centos
   paths:
      cloud_dir: /var/lib/cloud/
      templates_dir: /etc/cloud/templates/
   ssh_svcname: sshd

chpasswd:
   list: |
     root:123456
   expire: False
timezone: Asia/Shanghai
packages: 
  - wget
  - zip
  - iftop
  - htop
  - curl
  - bind-utils

日志输出路径配置文件

#日志处理方式handlers**
#文件路径:/etc/cloud/cloud.cfg.d/05_logging.cfg
[logger_cloudinit]
   level=DEBUG
   qualname=cloudinit
   handlers=cloudLogHandler
   propagate=1
#最后一行增加输出的日志文件
output: {all: '| tee -a /var/log/cloud-init-output.log'}   

image-20220216172801332

检查Cloud-Init工具相关配置是否成功

执行以下命令,无错误发生,说明Cloud-Init配置成功

[Docker][root VM-438178de-b93e-400a-8981-581bfdcba864 ~]$ cloud-init init --local
Cloud-init v. 19.4 running 'init-local' at Wed, 16 Feb 2022 09:29:47 +0000. Up 1026.33 seconds.
[Docker][root VM-438178de-b93e-400a-8981-581bfdcba864 ~]$ 

设置完成后关闭虚拟机,准备下一阶段生成镜像

history -c
shutdown -h now

基于此卷创建模板

基于HAproxy高可用

安装

yum -y install haproxy

/etc/haproxy/haproxy.cfg完整配置文件如下

global
    # to have these messages end up in /var/log/haproxy.log you will
    # need to:
    #
    # 1) configure syslog to accept network log events.  This is done
    #    by adding the '-r' option to the SYSLOGD_OPTIONS in
    #    /etc/sysconfig/syslog
    #
    # 2) configure local2 events to go to the /var/log/haproxy.log
    #   file. A line like the following can be added to
    #   /etc/sysconfig/syslog
    #
    #    local2.*                       /var/log/haproxy.log
    #
    log         127.0.0.1 local2

    chroot      /var/lib/haproxy
    pidfile     /var/run/haproxy.pid
    maxconn     4000
    user        haproxy
    group       haproxy
    daemon

    # turn on stats unix socket
    stats socket /var/lib/haproxy/stats

defaults
    mode                    http
    log                     global
    option                  httplog
    option                  dontlognull
    option http-server-close
    option forwardfor       except 127.0.0.0/8
    option                  redispatch
    retries                 3
    timeout http-request    10s
    timeout queue           1m
    timeout connect         10s
    timeout client          1m
    timeout server          1m
    timeout http-keep-alive 10s
    timeout check           10s
    maxconn                 3000


listen web-ui-80
		bind *:80
		option httpchk OPTIONS /client
		log global
		mode http
		option httplog
		option forwardfor
		balance source
		cookie JSESSIONID insert preserve  
		server msg01 10.10.10.5:8080 cookie JSESSIONID_SERVER_1  check inter 50000 rise 2 fall 5
		server msg02 10.10.10.68:8080 cookie JSESSIONID_SERVER_1  check inter 50000 rise 2 fall 5
 
listen web-ui-8080
		bind *:8080
		option httpchk OPTIONS /client
		log global
		mode http
		option httplog
		option forwardfor
		balance source
		cookie JSESSIONID insert preserve  
		server msg01 10.10.10.5:8080 cookie JSESSIONID_SERVER_1  check inter 50000 rise 2 fall 5
		server msg02 10.10.10.68:8080 cookie JSESSIONID_SERVER_1  check inter 50000 rise 2 fall 5


重启haproxy

systemctl restart haproxy

访问 本机ip 即可进入CloudStack的UI界面 ,修改 全局设置 host 值为 本机ip

密码管理

CloudStack提供可选的密码重置功能,允许用户设置临时的admin或root密码,也可以从CloudStack UI中重置现有的admin或root密码,小主在UI中没找到重置按钮在哪 要启用重置密码功能,您需要下载一个附加脚本来给模板打补丁

模板vm中执行

    #下载脚本
    wget https://download.cloudstack.org/templates/4.2/bindir/cloud-set-guest-password.in 
    mv cloud-set-guest-password.in cloud-set-guest-password
    mv cloud-set-guest-password /etc/init.d/
    chmod +x /etc/init.d/cloud-set-guest-password
    #加到开机自启动
    chkconfig --add cloud-set-guest-password

模板中启用密码功能

基于此卷创建模板时启动密码功能,问题目前是每次重启时都会重置密码

image.png

SSH 密钥验证

除了用户名和密码认证外,CloudStack 支持使用 SSH 密钥登录,可以使用 createSSHKeyPair API 生成 SSH 密钥。

创建支持 SSH 密钥的实例模板

  1. 下载脚本从SSH 密钥生成脚本下载到您创建的实例。

    wget http://downloads.sourceforge.net/project/cloudstack/SSH%20Key%20Gen%20Script/cloud-set-guest-sshkey.in?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fcloudstack%2Ffiles%2FSSH%2520Key%2520Gen%2520Script%2F&ts=1331225219&use_mirror=iweb
    
  2. 将文件复制到 /etc/init.d。

    cp cloud-set-guest-sshkey.in /etc/init.d/
    
  3. 授予对脚本的必要权限:

    chmod +x /etc/init.d/cloud-set-guest-sshkey.in
    
  4. 在启动操作系统时运行脚本:

    chkconfig --add cloud-set-guest-sshkey.in
    
  5. 停止实例。

  6. 基于此卷创建模板

创建 SSH 密钥对

登录UI,生成ssh秘钥对

image.png

创建实例

选择SSH key pairs

image.png