Introducción
En el mundo actual, las máquinas de la infraestructura de servidores se encuentran en centros de datos locales, centros de datos privados o centros de datos en nubes públicas. Estas máquinas pueden ser máquinas físicas de metal desnudo, máquinas virtuales (VM) con hipervisores o pequeños contenedores como contenedores Docker sobre máquinas físicas o virtuales. Estas máquinas pueden estar físicamente en el laboratorio local. En un escenario de centro de datos privado, donde sus propios hosts adquiridos se colocan en un espacio físico compartido en un centro de datos de terceros y se conectan de forma remota. Mientras que en los centros de datos públicos como AWS, GCP, Azure, OCI las máquinas se reservan o se crean bajo demanda para las necesidades altamente escalables que se conectan de forma remota. Cada uno de ellos tiene sus propias ventajas en cuanto a escalabilidad, seguridad, fiabilidad, gestión y costes asociados a estas infraestructuras.
Los equipos del entorno de desarrollo de productos pueden necesitar muchos servidores durante el proceso SDLC. Digamos que uno ha elegido el centro de datos privado con sus propias máquinas físicas junto con servidores Xen. Ahora, el reto es cómo se gestiona el ciclo de vida de las máquinas virtuales para el suministro o la terminación en entornos similares a la nube con procesos ágiles y sencillos.
El objetivo de este documento es proporcionar el modelo básico de infraestructura, la arquitectura, las API mínimas y fragmentos de código de ejemplo para que se puedan crear fácilmente entornos de infraestructura dinámicos.
Beneficios
Comprendamos primero la secuencia típica de los pasos seguidos en el proceso de infraestructura de este servidor. Puede recordarlo como se indica a continuación.
-
- Adquisición de las nuevas máquinas por IT
- Virtualización de host - Instalar servidor Xen y crear plantillas VM por TI
- Solicitud de máquinas virtuales estáticas por parte de los equipos de desarrollo y pruebas a través de tickets (por ejemplo, JIRA) a TI.
- Mantener las IPs de las máquinas virtuales recibidas en una base de datos o un archivo estático o codificadas en archivos de configuración o herramientas CI como en Jenkins config.xml.
- Supervisar las máquinas virtuales para comprobar que están en buen estado antes de utilizarlas para instalar los productos.
- Limpieza o desinstalación antes o después de la instalación de servidores
- Es posible que sea necesario limpiar el registro de Windows antes de instalar el producto.
- Se podría haber hecho una asignación fija de máquinas virtuales a un área o a un equipo o dedicarlas a un ingeniero
Ahora bien, ¿cómo puede hacer que este proceso sea más ágil? ¿Puedes eliminar la mayoría de los pasos anteriores con una simple automatización?
Sí. En nuestro entorno, tuvimos más de 1000 VMs y trató de lograr y sobre todo lo siguiente.
"VMs desechables bajo demanda según sea necesario durante la ejecución de las pruebas. Resuelva los problemas de limpieza de Windows con ciclos de pruebas regulares".
Como se puede ver a continuación, utilizando el servicio API de gestión dinámica de servidores VM, se pueden eliminar 6 de los 8 pasos y ofrece una visión ilimitada de la infraestructura para todo el equipo de producto. Sólo son necesarios los 2 primeros pasos: adquisición y virtualización del host. En efecto, esto ahorra tiempo y costes.
Modelo de infraestructura dinámica
La siguiente imagen muestra nuestra infraestructura propuesta para un entorno típico de productos de servidor donde 80% de contenedores Docker, 15% como VMs dinámicas y 5% como VMs estáticas agrupadas para casos especiales. Esta distribución se puede ajustar en función de lo que funcione mejor para su entorno.
A partir de aquí, hablaremos más sobre la parte del gestor de servidores Dynamic VM.
Arquitectura del Gestor Dinámico de Servidores
En el gestor de servidores de máquinas virtuales dinámicas, un sencillo servicio de API en el que se pueden exponer las siguientes API REST y que se pueden utilizar en cualquier parte del proceso automatizado. Como muestra la pila de tecnología, python 3 y Python basado en Xen APIs se utilizan para la creación real de VMs con XenServer host. Flask se utiliza para la creación de la capa de servicio REST. El sistema operativo puede ser cualquiera de las plataformas soportadas por su producto como windows2016, centos7, centos8, debian10, ubuntu18, oel8, suse15.
Guardar el historial de las máquinas virtuales para realizar un seguimiento del uso y el tiempo de aprovisionamiento o terminación puede ser analizado más a fondo. Para almacenar el documento json, se puede utilizar Couchbase enterprise server, que es una base de datos de documentos nosql.
API REST sencillas
| Método | URI(s) | Propósito |
| GET | /showall | Lista todas las máquinas virtuales en formato json |
| GET | /getavailablecount/ | Obtiene la lista de máquinas virtuales disponibles para el dado |
| GET | /getservers/?os=
/getservers/?os=&count= /getservers/?os=&count=&cpus=&mem= /getservers/?os=&expiresin= |
Disposiciones dadas VMs de .
También se puede admitir el recuento de cpus y el tamaño de mem. parámetro expiresin en minutos para obtener la expiración (auto terminación) de las VMs. |
| GET | /releaseservers/?os=
/releaseservers/?os=&count= |
Termina dado VMs de |
Requisitos previos para hosts Xen dirigidos a máquinas virtuales dinámicas
- Identificar hosts Xen VM dinámicos específicos
- Copiar/crear las plantillas VM
- Mueva estos Hosts Xen a una VLAN/Subred separada (trabaje con IT) para el reciclaje de IPs.
Aplicación
A alto nivel -
- Crear funciones cada API REST
- Llame a un servicio común para realizar diferentes acciones REST.
- Comprender la creación de Sesión Xen, obtener los registros, clonar VM desde plantilla, adjuntar el disco correcto, esperar la creación de VM e IP recibida; borrado de VMs, borrado de discos.
- Iniciar automáticamente un hilo para la expiración de las máquinas virtuales
- Leer la configuración común como el formato .ini
- Comprender el trabajo con la base de datos Couchbase y guardar documentos
- Pruebe todas las API con los sistemas operativos y parámetros necesarios.
- Solucionar problemas, si los hay
- Realice un POC con pocos hosts Xen
Los siguientes fragmentos de código pueden ayudarle a comprenderlo mejor.
Creación de API
|
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
@app.route('/showall/<string:os>') @app.route("/showall") def showall_service(os=None): count, _ = get_all_xen_hosts_count(os) log.info("--> count: {}".format(count)) all_vms = {} for xen_host_ref in range(1, count + 1): log.info("Getting xen_host_ref=" + str(xen_host_ref)) all_vms[xen_host_ref] = perform_service(xen_host_ref, service_name='listvms', os=os) return json.dumps(all_vms, indent=2, sort_keys=True) @app.route('/getavailablecount/<string:os>') @app.route('/getavailablecount') def getavailable_count_service(os='centos'): """ Calculate the available count: Get Total CPUs, Total Memory Get Free CPUs, Free Memory Get all the VMs - CPUs and Memory allocated Get each OS template - CPUs and Memory Available count1 = (Free CPUs - VMs CPUs)/OS_Template_CPUs Available count2 = (Free Memory - VMs Memory)/OS_Template_Memory Return min(count1,count2) """ count, available_counts, xen_hosts = get_all_available_count(os) log.info("{},{},{},{}".format(count, available_counts, xen_hosts, reserved_count)) if count > reserved_count: count -= reserved_count log.info("Less reserved count: {},{},{},{}".format(count, available_counts, xen_hosts, reserved_count)) return str(count) # /getservers/username?count=number&os=centos&ver=6&expiresin=30 @app.route('/getservers/<string:username>') def getservers_service(username): global reserved_count if request.args.get('count'): vm_count = int(request.args.get('count')) else: vm_count = 1 os_name = request.args.get('os') if request.args.get('cpus'): cpus_count = request.args.get('cpus') else: cpus_count = "default" if request.args.get('mem'): mem = request.args.get('mem') else: mem = "default" if request.args.get('expiresin'): exp = int(request.args.get('expiresin')) else: exp = MAX_EXPIRY_MINUTES if request.args.get('format'): output_format = request.args.get('format') else: output_format = "servermanager" xhostref = None if request.args.get('xhostref'): xhostref = request.args.get('xhostref') reserved_count += vm_count if xhostref: log.info("--> VMs on given xenhost" + xhostref) vms_ips_list = perform_service(xhostref, 'createvm', os_name, username, vm_count, cpus=cpus_count, maxmemory=mem, expiry_minutes=exp, output_format=output_format) return json.dumps(vms_ips_list) ... # /releaseservers/{username} @app.route('/releaseservers/<string:username>/<string:available>') @app.route('/releaseservers/<string:username>') def releaseservers_service(username): if request.args.get('count'): vm_count = int(request.args.get('count')) else: vm_count = 1 os_name = request.args.get('os') delete_vms_res = [] for vm_index in range(vm_count): if vm_count > 1: vm_name = username + str(vm_index + 1) else: vm_name = username xen_host_ref = get_vm_existed_xenhost_ref(vm_name, 1, None) log.info("VM to be deleted from xhost_ref=" + str(xen_host_ref)) if xen_host_ref != 0: delete_per_xen_res = perform_service(xen_host_ref, 'deletevm', os_name, vm_name, 1) for deleted_vm_res in delete_per_xen_res: delete_vms_res.append(deleted_vm_res) if len(delete_vms_res) < 1: return "Error: VM " + username + " doesn't exist" else: return json.dumps(delete_vms_res, indent=2, sort_keys=True) def perform_service(xen_host_ref=1, service_name='list_vms', os="centos", vm_prefix_names="", number_of_vms=1, cpus="default", maxmemory="default", expiry_minutes=MAX_EXPIRY_MINUTES, output_format="servermanager", start_suffix=0): xen_host = get_xen_host(xen_host_ref, os) ... def main(): # options = parse_arguments() set_log_level() app.run(host='0.0.0.0', debug=True) |
Creación sesión Xen
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
def get_xen_session(xen_host_ref=1, os="centos"): xen_host = get_xen_host(xen_host_ref, os) if not xen_host: return None url = "https://" + xen_host['host.name'] log.info("\nXen Server host: " + xen_host['host.name'] + "\n") try: session = XenAPI.Session(url) session.xenapi.login_with_password(xen_host['host.user'], xen_host['host.password']) except XenAPI.Failure as f: error = "Failed to acquire a session: {}".format(f.details) log.error(error) return error return session |
Lista de máquinas virtuales
|
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 |
def list_vms(session): vm_count = 0 vms = session.xenapi.VM.get_all() log.info("Server has {} VM objects (this includes templates):".format(len(vms))) log.info("-----------------------------------------------------------") log.info("S.No.,VMname,PowerState,Vcpus,MaxMemory,Networkinfo,Description") log.info("-----------------------------------------------------------") vm_details = [] for vm in vms: network_info = 'N/A' record = session.xenapi.VM.get_record(vm) if not (record["is_a_template"]) and not (record["is_control_domain"]): log.debug(record) vm_count = vm_count + 1 name = record["name_label"] name_description = record["name_description"] power_state = record["power_state"] vcpus = record["VCPUs_max"] memory_static_max = record["memory_static_max"] if record["power_state"] != 'Halted': ip_ref = session.xenapi.VM_guest_metrics.get_record(record['guest_metrics']) network_info = ','.join([str(elem) for elem in ip_ref['networks'].values()]) else: continue # Listing only Running VMs vm_info = {'name': name, 'power_state': power_state, 'vcpus': vcpus, 'memory_static_max': memory_static_max, 'networkinfo': network_info, 'name_description': name_description} vm_details.append(vm_info) log.info(vm_info) log.info("Server has {} VM objects and {} templates.".format(vm_count, len(vms) - vm_count)) log.debug(vm_details) return vm_details |
Crear VM
|
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 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
def create_vm(session, os_name, template, new_vm_name, cpus="default", maxmemory="default", expiry_minutes=MAX_EXPIRY_MINUTES): error = '' vm_os_name = '' vm_ip_addr = '' prov_start_time = time.time() try: log.info("\n--- Creating VM: " + new_vm_name + " using " + template) pifs = session.xenapi.PIF.get_all_records() lowest = None for pifRef in pifs.keys(): if (lowest is None) or (pifs[pifRef]['device'] < pifs[lowest]['device']): lowest = pifRef log.debug("Choosing PIF with device: {}".format(pifs[lowest]['device'])) ref = lowest mac = pifs[ref]['MAC'] device = pifs[ref]['device'] mode = pifs[ref]['ip_configuration_mode'] ip_addr = pifs[ref]['IP'] net_mask = pifs[ref]['IP'] gateway = pifs[ref]['gateway'] dns_server = pifs[ref]['DNS'] log.debug("{},{},{},{},{},{},{}".format(mac, device, mode, ip_addr, net_mask, gateway, dns_server)) # List all the VM objects vms = session.xenapi.VM.get_all_records() log.debug("Server has {} VM objects (this includes templates)".format(len(vms))) templates = [] all_templates = [] for vm in vms: record = vms[vm] res_type = "VM" if record["is_a_template"]: res_type = "Template" all_templates.append(vm) # Look for a given template if record["name_label"].startswith(template): templates.append(vm) log.debug(" Found %8s with name_label = %s" % (res_type, record["name_label"])) log.debug("Server has {} Templates and {} VM objects.".format(len(all_templates), len(vms) - len( all_templates))) log.debug("Choosing a {} template to clone".format(template)) if not templates: log.error("Could not find any {} templates. Exiting.".format(template)) sys.exit(1) template_ref = templates[0] log.debug(" Selected template: {}".format(session.xenapi.VM.get_name_label(template_ref))) # Retries when 169.x address received ipaddr_max_retries = 3 retry_count = 1 is_local_ip = True vm_ip_addr = "" while is_local_ip and retry_count != ipaddr_max_retries: log.info("Installing new VM from the template - attempt #{}".format(retry_count)) vm = session.xenapi.VM.clone(template_ref, new_vm_name) network = session.xenapi.PIF.get_network(lowest) log.debug("Chosen PIF is connected to network: {}".format( session.xenapi.network.get_name_label(network))) vifs = session.xenapi.VIF.get_all() log.debug(("Number of VIFs=" + str(len(vifs)))) for i in range(len(vifs)): vmref = session.xenapi.VIF.get_VM(vifs[i]) a_vm_name = session.xenapi.VM.get_name_label(vmref) log.debug(str(i) + "." + session.xenapi.network.get_name_label( session.xenapi.VIF.get_network(vifs[i])) + " " + a_vm_name) if a_vm_name == new_vm_name: session.xenapi.VIF.move(vifs[i], network) log.debug("Adding non-interactive to the kernel commandline") session.xenapi.VM.set_PV_args(vm, "non-interactive") log.debug("Choosing an SR to instantiate the VM's disks") pool = session.xenapi.pool.get_all()[0] default_sr = session.xenapi.pool.get_default_SR(pool) default_sr = session.xenapi.SR.get_record(default_sr) log.debug("Choosing SR: {} (uuid {})".format(default_sr['name_label'], default_sr['uuid'])) log.debug("Asking server to provision storage from the template specification") description = new_vm_name + " from " + template + " on " + str(datetime.datetime.utcnow()) session.xenapi.VM.set_name_description(vm, description) if cpus != "default": log.info("Setting cpus to " + cpus) session.xenapi.VM.set_VCPUs_max(vm, int(cpus)) session.xenapi.VM.set_VCPUs_at_startup(vm, int(cpus)) if maxmemory != "default": log.info("Setting memory to " + maxmemory) session.xenapi.VM.set_memory(vm, maxmemory) # 8GB="8589934592" or 4GB="4294967296" session.xenapi.VM.provision(vm) log.info("Starting VM") session.xenapi.VM.start(vm, False, True) log.debug(" VM is booting") log.debug("Waiting for the installation to complete") # Get the OS Name and IPs log.info("Getting the OS Name and IP...") config = read_config() vm_network_timeout_secs = int(config.get("common", "vm.network.timeout.secs")) if vm_network_timeout_secs > 0: TIMEOUT_SECS = vm_network_timeout_secs log.info("Max wait time in secs for VM OS address is {0}".format(str(TIMEOUT_SECS))) if "win" not in template: maxtime = time.time() + TIMEOUT_SECS while read_os_name(session, vm) is None and time.time() < maxtime: time.sleep(1) vm_os_name = read_os_name(session, vm) log.info("VM OS name: {}".format(vm_os_name)) else: # TBD: Wait for network to refresh on Windows VM time.sleep(60) log.info("Max wait time in secs for IP address is " + str(TIMEOUT_SECS)) maxtime = time.time() + TIMEOUT_SECS # Wait until IP is not None or 169.xx (when no IPs available, this is default) and timeout # is not reached. while (read_ip_address(session, vm) is None or read_ip_address(session, vm).startswith( '169')) and \ time.time() < maxtime: time.sleep(1) vm_ip_addr = read_ip_address(session, vm) log.info("VM IP: {}".format(vm_ip_addr)) if vm_ip_addr.startswith('169'): log.info("No Network IP available. Deleting this VM ... ") record = session.xenapi.VM.get_record(vm) power_state = record["power_state"] if power_state != 'Halted': session.xenapi.VM.hard_shutdown(vm) delete_all_disks(session, vm) session.xenapi.VM.destroy(vm) time.sleep(5) is_local_ip = True retry_count += 1 else: is_local_ip = False log.info("Final VM IP: {}".format(vm_ip_addr)) |
Borrar VM
|
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 |
def delete_vm(session, vm_name): log.info("Deleting VM: " + vm_name) delete_start_time = time.time() vm = session.xenapi.VM.get_by_name_label(vm_name) log.info("Number of VMs found with name - " + vm_name + " : " + str(len(vm))) for j in range(len(vm)): record = session.xenapi.VM.get_record(vm[j]) power_state = record["power_state"] if power_state != 'Halted': # session.xenapi.VM.shutdown(vm[j]) session.xenapi.VM.hard_shutdown(vm[j]) # print_all_disks(session, vm[j]) delete_all_disks(session, vm[j]) session.xenapi.VM.destroy(vm[j]) delete_end_time = time.time() delete_duration = round(delete_end_time - delete_start_time) # delete from CB uuid = record["uuid"] doc_key = uuid cbdoc = CBDoc() doc_result = cbdoc.get_doc(doc_key) if doc_result: doc_value = doc_result.value doc_value["state"] = 'deleted' current_time = time.time() doc_value["deleted_time"] = current_time if doc_value["created_time"]: doc_value["live_duration_secs"] = round(current_time - doc_value["created_time"]) doc_value["delete_duration_secs"] = delete_duration cbdoc.save_dynvm_doc(doc_key, doc_value) |
Uso histórico de máquinas virtuales
Es mejor mantener el historial de todas las VMs creadas y terminadas junto con otros datos útiles. Aquí está el ejemplo de documento json almacenado en el Couchbase, un base de datos Nosql gratuita servidor. Inserte un nuevo documento utilizando la clave como el uuid de referencia de xen opac cada vez que se aprovisione una nueva VM y actualice el mismo cada vez que se termine la VM. Rastrea el tiempo de uso de la VM y también cómo se ha realizado el aprovisionamiento/desaprovisionamiento por cada usuario.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
record = session.xenapi.VM.get_record(vm) uuid = record["uuid"] # Save as doc in CB state = "available" username = new_vm_name pool = "dynamicpool" doc_value = {"ipaddr": vm_ip_addr, "origin": xen_host_description, "os": os_name, "state": state, "poolId": pool, "prevUser": "", "username": username, "ver": "12", "memory": memory_static_max, "os_version": vm_os_name, "name": new_vm_name, "created_time": prov_end_time, "create_duration_secs": create_duration, "cpu": vcpus, "disk": disks_info} # doc_value["mac_address"] = mac_address doc_key = uuid cb_doc = CBDoc() cb_doc.save_dynvm_doc(doc_key, doc_value) |
|
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 |
class CBDoc: def __init__(self): config = read_config() self.cb_server = config.get("couchbase", "couchbase.server") self.cb_bucket = config.get("couchbase", "couchbase.bucket") self.cb_username = config.get("couchbase", "couchbase.username") self.cb_userpassword = config.get("couchbase", "couchbase.userpassword") try: self.cb_cluster = Cluster('couchbase://' + self.cb_server) self.cb_auth = PasswordAuthenticator(self.cb_username, self.cb_userpassword) self.cb_cluster.authenticate(self.cb_auth) self.cb = self.cb_cluster.open_bucket(self.cb_bucket) except Exception as e: log.error('Connection Failed: %s ' % self.cb_server) log.error(e) def get_doc(self, doc_key): try: return self.cb.get(doc_key) except Exception as e: log.error('Error while getting doc %s !' % doc_key) log.error(e) def save_dynvm_doc(self, doc_key, doc_value): try: log.info(doc_value) self.cb.upsert(doc_key, doc_value) log.info("%s added/updated successfully" % doc_key) except Exception as e: log.error('Document with key: %s saving error' % doc_key) log.error(e) |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
"dynserver-pool": { "cpu": "6", "create_duration_secs": 65, "created_time": 1583518463.8943903, "delete_duration_secs": 5, "deleted_time": 1583520211.8498628, "disk": "75161927680", "ipaddr": "x.x.x.x", "live_duration_secs": 1748, "memory": "6442450944", "name": "Win2019-Server-1node-DynVM", "origin": "s827", "os": "win16", "os_version": "", "poolId": "dynamicpool", "prevUser": "", "state": "deleted", "username": "Win2019-Server-1node-DynVM", "ver": "12" } |
Configuración
La configuración del servicio Dynamic VM server manager, como el servidor couchbase, los servidores xenhost, los detalles de la plantilla, la caducidad por defecto y los valores de tiempo de espera de red se pueden mantener en un simple formato .ini. La configuración se carga dinámicamente sin necesidad de reiniciar el servicio Dynamic VM SM.
Archivo de configuración de ejemplo: .dynvmservice.ini
|
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 |
[couchbase] couchbase.server=<couchbase-hostIp> couchbase.bucket=<bucket-name> couchbase.username=<username> couchbase.userpassword=<password> [common] vm.expiry.minutes=720 vm.network.timeout.secs=400 [xenhost1] host.name=<xenhostip1> host.user=root host.password=xxxx host.storage.name=Local Storage 01 centos.template=tmpl-cnt7.7 centos7.template=tmpl-cnt7.7 windows.template=tmpl-win16dc - PATCHED (1) centos8.template=tmpl-cnt8.0 oel8.template=tmpl-oel8 deb10.template=tmpl-deb10-too debian10.template=tmpl-deb10-too ubuntu18.template=tmpl-ubu18-4cgb suse15.template=tmpl-suse15 [xenhost2] host.name=<xenhostip2> host.user=root host.password=xxxx host.storage.name=ssd centos.template=tmpl-cnt7.7 windows.template=tmpl-win16dc - PATCHED (1) centos8.template=tmpl-cnt8.0 oel8.template=tmpl-oel8 deb10.template=tmpl-deb10-too debian10.template=tmpl-deb10-too ubuntu18.template=tmpl-ubu18-4cgb suse15.template=tmpl-suse15 [xenhost3] ... [xenhost4] ... |
Ejemplos
Ejemplo de llamadas a la API REST mediante curl
|
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 |
$ curl 'https://127.0.0.1:5000/showall' { "1": [ { "memory_static_max": "6442450944", "name": "tunable-rebalance-out-Apr-29-13:43:59-7.0.0-19021", "name_description": "tunable-rebalance-out-Apr-29-13:43:59-7.0.0-19021 from tmpl-win16dc - PATCHED (1) on 2020-04-29 20:43:33.785778", "networkinfo": "172.23.137.20,172.23.137.20,fe80:0000:0000:0000:0585:c6e8:52f9:91d1", "power_state": "Running", "vcpus": "6" }, { "memory_static_max": "6442450944", "name": "nserv-nserv-rebalanceinout_P0_Set1_compression-Apr-29-06:36:12-7.0.0-19022", "name_description": "nserv-nserv-rebalanceinout_P0_Set1_compression-Apr-29-06:36:12-7.0.0-19022 from tmpl-win16dc - PATCHED (1) on 2020-04-29 13:36:53.717776", "networkinfo": "172.23.136.142,172.23.136.142,fe80:0000:0000:0000:744d:fd63:1a88:2fa8", "power_state": "Running", "vcpus": "6" } ], "2": [ .. ] "3": [ .. ] "4": [ .. ] "5": [ .. ] "6": [ .. ] } $ curl 'https://127.0.0.1:5000/getavailablecount/windows' 2 $ curl 'https://127.0.0.1:5000/getavailablecount/centos' 10 $ curl 'https://127.0.0.1:5000/getservers/demoserver?os=centos&count=3' ["172.23.137.73", "172.23.137.74", "172.23.137.75"] $ curl 'https://127.0.0.1:5000/getavailablecount/centos' 7 $ curl 'https://127.0.0.1:5000/releaseservers/demoserver?os=centos&count=3' [ "demoserver1", "demoserver2", "demoserver3" ] $ curl 'https://127.0.0.1:5000/getavailablecount/centos' 10 $ |
Trabajos Jenkins con una única VM
|
1 2 3 4 5 6 7 8 |
curl -s -o ${BUILD_TAG}_vm.json "https://<host:port>/getservers/${BUILD_TAG}?os=windows&format=detailed" VM_IP_ADDRESS="`cat ${BUILD_TAG}_vm.json |egrep ${BUILD_TAG}|cut -f2 -d':'|xargs`" if [[ $VM_IP_ADDRESS =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then echo Valid IP received else echo NOT a valid IP received exit 1 fi |
Se necesitan trabajos Jenkins con varias máquinas virtuales
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
curl -s -o ${BUILD_TAG}.json "${SERVER_API_URL}/getservers/${BUILD_TAG}?os=${OS}&count=${COUNT}&format=detailed" cat ${BUILD_TAG}.json VM_IP_ADDRESS="`cat ${BUILD_TAG}.json |egrep ${BUILD_TAG}|cut -f2 -d':'|xargs|sed 's/,//g'`" echo $VM_IP_ADDRESS ADDR=() INDEX=1 for IP in `echo $VM_IP_ADDRESS` do if [[ $IP =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then echo Valid IP=$IP received ADDR[$INDEX]=${IP} INDEX=`expr ${INDEX} + 1` else echo NOT a valid IP=$IP received exit 1 fi done echo ${ADDR[1]} echo ${ADDR[2]} echo ${ADDR[3]} echo ${ADDR[4]} |
Consideraciones clave
He aquí algunas de mis observaciones anotadas durante el proceso y que es mejor manejar para hacerlo más fiable.
- Manejar el nombre/ID de Almacenamiento diferente entre diferentes Hosts Xen
- Mantenga un registro del nombre del dispositivo de almacenamiento VM en el archivo de configuración de entrada de servicio.
- Manejar plantillas parciales sólo disponibles en algunos hosts Xen durante el aprovisionamiento.
- Cuando las IP de red no están disponibles y las API de Xen obtienen la dirección por defecto 169.254.xx.yy en Windows. Espere hasta obtener una dirección que no sea 169 o hasta que se agote el tiempo de espera.
- Los servidores de lanzamiento deben ignorar la plantilla os ya que algunas de las plantillas podrían no estar allí Xen Hosts
- Provisión en una referencia específica de host Xen determinada
- Manejar No hay IPs disponibles o not obtener IPs de red para algunas de las VMs creadas.
- Plan para tener una subred diferente para VMs dinámicas dirigidas a Hosts Xen. La red por defecto DHCP IP arrendamiento expiración podría ser en días (digamos 7 días) y no se proporcionan nuevas IPs.
- Maneje la comprobación de capacidad para contar las IPs en progreso como IPs reservadas y debería mostrar un recuento menor que lleno en este momento. De lo contrario, tanto las peticiones en curso como las entrantes podrían tener problemas. Una o dos VMs (cpus/memoria/tamaño de disco) pueden estar en el buffer mientras se crea y comprueba si hay peticiones paralelas.
Referencias
Algunas de las referencias clave que ayudan mientras se crea el servicio de gestor dinámico de servidores VM.
- https://www.couchbase.com/downloads
- https://wiki.xenproject.org/wiki/XAPI_Command_Line_Interface
- https://xapi-project.github.io/xen-api/
- https://docs.citrix.com/en-us/citrix-hypervisor/command-line-interface.html
- https://github.com/xapi-project/xen-api-sdk/tree/master/python/samples
- https://www.citrix.com/community/citrix-developer/citrix-hypervisor-developer/citrix-hypervisor-developing-products/citrix-hypervisor-staticip.html
- https://docs.ansible.com/ansible/latest/modules/xenserver_guest_module.html
- https://github.com/terra-farm/terraform-provider-xenserver
- https://github.com/xapi-project/xen-api/blob/master/scripts/examples/python/renameif.py
- https://xen-orchestra.com/forum/topic/191/single-device-not-reporting-ip-on-dashboard/14
- https://xen-orchestra.com/blog/xen-orchestra-from-the-cli/
- https://support.citrix.com/article/CTX235403
Espero que hayan pasado un buen rato de lectura.
Descargo de responsabilidad: Por favor, vea esto como una referencia si usted está tratando con Xen Hosts. Siéntase libre de compartir si ha aprendido algo nuevo que nos pueda ayudar. Agradecemos sus comentarios positivos.
Gracias a Raju Suravarjjala, Ritam Sharma, Wayne Siu, Tom Thrush y James Lee por su ayuda durante el proceso.