Introdução
No mundo atual, as máquinas de infraestrutura de servidor estão em data centers locais, data centers privados ou data centers de nuvem pública. Essas máquinas são máquinas físicas bare metal, máquinas virtuais (VMs) com hipervisores ou pequenos contêineres, como contêineres docker, em cima de máquinas físicas ou virtuais. Essas máquinas podem estar fisicamente no laboratório local. Em um cenário de data center privado, em que seus próprios hosts adquiridos são colocados em um espaço físico compartilhado em um data center de terceiros e conectados remotamente. Já nos data centers públicos, como AWS, GCP, Azure e OCI, as máquinas são reservadas ou criadas sob demanda para as necessidades altamente escalonáveis que se conectam remotamente. Cada um deles tem suas próprias vantagens em termos de escalabilidade, segurança, confiabilidade, gerenciamento e custos associados a essas infraestruturas.
As equipes do ambiente de desenvolvimento de produtos podem precisar de muitos servidores durante o processo de SDLC. Digamos que alguém tenha escolhido o data center privado com suas próprias máquinas físicas e servidores Xen. Agora, o desafio é como o ciclo de vida das VMs é gerenciado para provisionamento ou encerramento, de forma semelhante aos ambientes de nuvem com processos enxutos e ágeis.
O objetivo deste documento é fornecer o modelo básico de infraestrutura, a arquitetura, as APIs mínimas e os exemplos de trechos de código para que seja possível criar facilmente ambientes de infraestrutura dinâmicos.
Benefícios
Primeiro, vamos entender a sequência típica das etapas seguidas no processo de infraestrutura desse servidor. Você pode relembrá-la da seguinte forma.
-
- Aquisição de novas máquinas pela TI
- Virtualização de host - Instalar o Xen Server e criar modelos de VM por TI
- Solicitação de VMs estáticas pelas equipes de desenvolvimento e teste por meio de tíquetes (digamos, JIRA) para a TI
- Mantenha os IPs de VM recebidos em um banco de dados ou em um arquivo estático ou codificados em arquivos de configuração ou ferramentas de CI, como no config.xml do Jenkins
- Monitore as VMs quanto a verificações de integridade para garantir que elas estejam íntegras antes de usá-las para instalar os produtos
- Limpeza ou desinstalação antes ou depois das instalações do servidor
- O Windows pode precisar de alguma limpeza do registro antes de instalar o produto
- A alocação fixa de VMs para uma área ou uma equipe ou dedicada a um engenheiro pode ter sido feita
Agora, como você pode tornar esse processo mais enxuto e ágil? Você pode eliminar a maioria das etapas acima com uma automação simples?
Sim. Em nosso ambiente, tínhamos mais de 1.000 VMs e tentamos alcançar e, principalmente, o seguinte.
"VMs descartáveis sob demanda, conforme necessário durante a execução dos testes. Resolva problemas de limpeza do Windows com ciclos de teste regulares."
Como você pode ver abaixo, usando o serviço de API do gerenciador de servidor de VMs dinâmicas, 6 das 8 etapas podem ser eliminadas e isso proporciona uma visão ilimitada da infraestrutura para toda a equipe de produtos. Somente as duas primeiras etapas - aquisição e virtualização de host - são necessárias. De fato, isso economiza em termos de tempo e custo!
Modelo de infraestrutura dinâmica
A imagem abaixo mostra nossa infraestrutura proposta para um ambiente típico de produto de servidor, com 80% de contêineres do docker, 15% como VMs dinâmicas e 5% como VMs estáticas em pool para casos especiais. Essa distribuição pode ser ajustada com base no que funciona melhor em seu ambiente.
Daqui em diante, discutiremos mais sobre a parte do gerenciador do servidor de VM dinâmica.
Arquitetura do Dynamic Server Manager
No gerenciador de servidor de VMs dinâmicas, um serviço de API simples em que as APIs REST abaixo podem ser expostas e usadas em qualquer lugar no processo automatizado. Como mostra a pilha de tecnologia, as APIs Xen baseadas em python 3 e Python são usadas para a criação real de VMs com o host XenServer. O Flask está sendo usado para a criação da camada de serviço REST. O sistema operacional pode ser qualquer uma das plataformas compatíveis com seu produto, como windows2016, centos7, centos8, debian10, ubuntu18, oel8, suse15.
Salve o histórico das VMs para rastrear o uso e o tempo de provisionamento ou encerramento que pode ser analisado posteriormente. Para armazenar o documento json, pode ser usado o servidor corporativo Couchbase, que é um banco de dados de documentos nosql.
APIs REST simples
| Método | URI(s) | Finalidade |
| OBTER | /showall | Lista todas as VMs no formato json |
| OBTER | /getavailablecount/ | Obtém a lista de contagem de VMs disponíveis para o fornecido |
| OBTER | /getservers/?os=
/getservers/?os=&count= /getservers/?os=&count=&cpus=&mem= /getservers/?os=&expiresin= |
Provisões dadas VMs de .
contagem de cpus e tamanho de memória também podem ser suportados. parâmetro expiresin em minutos para obter a expiração (encerramento automático) das VMs. |
| OBTER | /releaseservers/?os=
/releaseservers/?os=&count= |
Encerra determinadas VMs de |
Pré-requisitos para hosts Xen direcionados a VMs dinâmicas
- Identificar hosts Xen de VM dinâmicos direcionados
- Copiar/criar os modelos de VM
- Mova esses hosts Xen para uma VLAN/sub-rede separada (trabalhe com a TI) para reciclar os IPs
Implementação
Em um nível alto -
- Criar funções para cada API REST
- Chame um serviço comum para executar diferentes ações REST.
- Entenda a criação da sessão Xen, obtendo os registros, clonando a VM a partir do modelo, anexando o disco correto, aguardando a criação da VM e o recebimento do IP; exclusão de VMs, exclusão de discos
- Iniciar um thread para expiração automática de VMs
- Ler a configuração comum, como o formato .ini
- Compreender o trabalho com o banco de dados Couchbase e salvar documentos
- Teste todas as APIs com os sistemas operacionais e parâmetros necessários
- Corrigir problemas, se houver
- Executar um POC com poucos hosts Xen
Os trechos de código abaixo podem ajudá-lo a entender ainda melhor.
Criação de APIs
|
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) |
Criação da sessão 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 |
Listar VMs
|
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 |
Criar 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)) |
Excluir 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) |
Histórico de uso de VMs
É melhor manter o histórico de todas as VMs criadas e encerradas junto com outros dados úteis. Aqui está o exemplo de um documento json armazenado no Couchbase, um banco de dados Nosql gratuito servidor. Insira um novo documento usando a chave como o uuid de referência do xen opac sempre que uma nova VM for provisionada e atualize o mesmo sempre que a VM for encerrada. Acompanhe o tempo de uso da VM e também como o provisionamento/encerramento foi feito por cada usuário.
|
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" } |
Configuração
A configuração do serviço do gerenciador de servidores Dynamic VM, como o servidor couchbase, os servidores xenhost, os detalhes do modelo, a expiração padrão e os valores de tempo limite da rede podem ser mantidos em um formato .ini simples. Qualquer novo host Xen recebido, basta adicionar como uma seção separada. A configuração é carregada dinamicamente sem reiniciar o serviço Dynamic VM SM.
Exemplo de arquivo de configuração: .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] ... |
Exemplos
Exemplo de chamadas à API REST usando 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 $ |
Trabalhos Jenkins com uma ú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 |
São necessários trabalhos Jenkins com várias VMs
|
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]} |
Principais considerações
Aqui estão algumas de minhas observações feitas durante o processo e é melhor lidar com elas para torná-las mais confiáveis.
- Manipular nome/ID de armazenamento diferente entre diferentes hosts Xen
- Mantenha o controle do nome do dispositivo de armazenamento da VM no arquivo de configuração de entrada do serviço.
- Lidar com modelos parciais disponíveis apenas em alguns hosts Xen durante o provisionamento
- Quando os IPs de rede não estão disponíveis e as APIs do Xen obtêm o padrão 169.254.xx.yy no Windows. Aguarde até obter o endereço não 169 ou o tempo limite.
- Os servidores de versão devem ignorar o modelo do sistema operacional, pois alguns dos modelos podem não estar presentes Xen Hosts
- Provisão em uma referência específica de um determinado Xen Host
- Manipular Não há IPs disponíveis ou nnão está conseguindo obter IPs de rede para algumas das VMs criadas.
- PO Xen Hosts é uma sub-rede diferente para VMs dinâmicas direcionadas a hosts Xen. A expiração do contrato de aluguel de IP DHCP da rede padrão pode ser em dias (digamos, 7 dias) e não são fornecidos novos IPs.
- Trate a verificação de capacidade para contar os IPs em andamento como IPs reservados e deve mostrar uma contagem menor do que a total no momento. Caso contrário, as solicitações em andamento e as solicitações de entrada poderão ter problemas. Uma ou duas VMs (tamanhos de cpus/memória/disco) podem estar no buffer durante a criação e a verificação de solicitações paralelas.
Referências
Algumas das principais referências que ajudam na criação do serviço de gerenciador de servidor de VM dinâmico.
- 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 você tenha tido um bom momento de leitura!
Isenção de responsabilidade: Veja isso como uma referência se estiver lidando com Xen Hosts. Sinta-se à vontade para compartilhar se tiver aprendido algo novo que possa nos ajudar. Seu feedback positivo é muito bem-vindo!
Agradecemos a Raju Suravarjjala, Ritam Sharma, Wayne Siu, Tom Thrush e James Lee por sua ajuda durante o processo.