[ English | Indonesia | Deutsch | 日本語 ]

Menyesuaikan OpenStack Compute (nova) Scheduler

Banyak proyek OpenStack memungkinkan penyesuaian fitur tertentu menggunakan arsitektur driver. Anda dapat menulis driver yang sesuai dengan antarmuka tertentu dan menghubungkannya melalui konfigurasi. Misalnya, Anda dapat dengan mudah memasang penjadwal baru untuk Compute. Penjadwal yang ada untuk Compute adalah fitur lengkap dan didokumentasikan dengan baik di Scheduling. Namun, tergantung pada kasus penggunaan pengguna Anda, penjadwal yang ada mungkin tidak memenuhi persyaratan Anda. Anda mungkin perlu membuat penjadwal baru.

Untuk membuat penjadwal, Anda harus mewarisi dari kelas nova.scheduler.driver.Scheduler. Dari lima metode yang dapat Anda timpa, Anda must mengganti dua metode yang ditandai dengan tanda bintang (*) di bawah ini:

  • update_service_capabilities

  • hosts_up

  • group_hosts

  • * schedule_run_instance

  • * select_destinations

Untuk mendemonstrasikan penyesuaian OpenStack, kami akan membuat contoh penjadwal Compute yang secara acak menempatkan instance pada subset host, tergantung pada alamat IP asal permintaan dan awalan nama host. Contoh seperti itu bisa berguna ketika Anda memiliki sekelompok pengguna di subnet dan Anda ingin semua instance mereka dimulai dalam beberapa subset dari host Anda.

Peringatan

Contoh ini hanya untuk tujuan ilustrasi. Seharusnya tidak digunakan sebagai scheduler untuk Compute tanpa pengembangan dan pengujian lebih lanjut.

Ketika Anda bergabung dengan sesi layar yang stack.sh dimulai dengan screen -r stack, Anda akan disambut dengan banyak jendela layar (screen windows):

0$ shell*  1$ key  2$ horizon  ...  9$ n-api  ...  14$ n-sch ...
shell

Shell tempat Anda bisa menyelesaikan pekerjaan

key

Layanan keystone

horizon

Aplikasi web dasbor horizon

n-{name}

Layanan nova

n-sch

Layanan penjadwal nova

To create the scheduler and plug it in through configuration

  1. Kode untuk OpenStack hidup di /opt/stack, jadi buka direktori nova dan edit modul penjadwal Anda. Ubah ke direktori tempat nova diinstal:

    $ cd /opt/stack/nova
    
  2. Buat file kode sumber Python ip_scheduler.py:

    $ vim nova/scheduler/ip_scheduler.py
    
  3. Kode yang ditunjukkan di bawah ini adalah driver yang akan menjadwalkan server ke host berdasarkan alamat IP seperti yang dijelaskan di awal bagian. Salin kode ke ip_scheduler.py. Setelah selesai, simpan dan tutup file.

    # vim: tabstop=4 shiftwidth=4 softtabstop=4
    # Copyright (c) 2014 OpenStack Foundation
    # All Rights Reserved.
    #
    #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    #    not use this file except in compliance with the License. You may obtain
    #    a copy of the License at
    #
    #         http://www.apache.org/licenses/LICENSE-2.0
    #
    #    Unless required by applicable law or agreed to in writing, software
    #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    #    License for the specific language governing permissions and limitations
    #    under the License.
    
    """
    IP Scheduler implementation
    """
    
    import random
    
    from oslo_config import cfg
    
    from nova.compute import rpcapi as compute_rpcapi
    from nova import exception
    from nova.openstack.common import log as logging
    from nova.openstack.common.gettextutils import _
    from nova.scheduler import driver
    
    CONF = cfg.CONF
    CONF.import_opt('compute_topic', 'nova.compute.rpcapi')
    LOG = logging.getLogger(__name__)
    
    class IPScheduler(driver.Scheduler):
        """
        Implements Scheduler as a random node selector based on
        IP address and hostname prefix.
        """
    
        def __init__(self, *args, **kwargs):
            super(IPScheduler, self).__init__(*args, **kwargs)
            self.compute_rpcapi = compute_rpcapi.ComputeAPI()
    
        def _filter_hosts(self, request_spec, hosts, filter_properties,
            hostname_prefix):
            """Filter a list of hosts based on hostname prefix."""
    
            hosts = [host for host in hosts if host.startswith(hostname_prefix)]
            return hosts
    
        def _schedule(self, context, topic, request_spec, filter_properties):
            """Picks a host that is up at random."""
    
            elevated = context.elevated()
            hosts = self.hosts_up(elevated, topic)
            if not hosts:
                msg = _("Is the appropriate service running?")
                raise exception.NoValidHost(reason=msg)
    
            remote_ip = context.remote_address
    
            if remote_ip.startswith('10.1'):
                hostname_prefix = 'doc'
            elif remote_ip.startswith('10.2'):
                hostname_prefix = 'ops'
            else:
                hostname_prefix = 'dev'
    
            hosts = self._filter_hosts(request_spec, hosts, filter_properties,
                hostname_prefix)
            if not hosts:
                msg = _("Could not find another compute")
                raise exception.NoValidHost(reason=msg)
    
            host = random.choice(hosts)
            LOG.debug("Request from %(remote_ip)s scheduled to %(host)s" % locals())
    
            return host
    
        def select_destinations(self, context, request_spec, filter_properties):
            """Selects random destinations."""
            num_instances = request_spec['num_instances']
            # NOTE(timello): Returns a list of dicts with 'host', 'nodename' and
            # 'limits' as keys for compatibility with filter_scheduler.
            dests = []
            for i in range(num_instances):
                host = self._schedule(context, CONF.compute_topic,
                        request_spec, filter_properties)
                host_state = dict(host=host, nodename=None, limits=None)
                dests.append(host_state)
    
            if len(dests) < num_instances:
                raise exception.NoValidHost(reason='')
            return dests
    
        def schedule_run_instance(self, context, request_spec,
                                  admin_password, injected_files,
                                  requested_networks, is_first_time,
                                  filter_properties, legacy_bdm_in_spec):
            """Create and run an instance or instances."""
            instance_uuids = request_spec.get('instance_uuids')
            for num, instance_uuid in enumerate(instance_uuids):
                request_spec['instance_properties']['launch_index'] = num
                try:
                    host = self._schedule(context, CONF.compute_topic,
                                          request_spec, filter_properties)
                    updated_instance = driver.instance_update_db(context,
                            instance_uuid)
                    self.compute_rpcapi.run_instance(context,
                            instance=updated_instance, host=host,
                            requested_networks=requested_networks,
                            injected_files=injected_files,
                            admin_password=admin_password,
                            is_first_time=is_first_time,
                            request_spec=request_spec,
                            filter_properties=filter_properties,
                            legacy_bdm_in_spec=legacy_bdm_in_spec)
                except Exception as ex:
                    # NOTE(vish): we don't reraise the exception here to make sure
                    #             that all instances in the request get set to
                    #             error properly
                    driver.handle_schedule_error(context, ex, instance_uuid,
                                                 request_spec)
    

    Ada banyak informasi berguna dalam context, request_spec, dan filter_properties yang dapat Anda gunakan untuk memutuskan di mana harus menjadwalkan instance. Untuk mengetahui lebih lanjut tentang properti apa yang tersedia, Anda dapat memasukkan pernyataan log berikut ke dalam metode schedule_run_instance dari penjadwal di atas:

    LOG.debug("context = %(context)s" % {'context': context.__dict__})
    LOG.debug("request_spec = %(request_spec)s" % locals())
    LOG.debug("filter_properties = %(filter_properties)s" % locals())
    
  4. Untuk menghubungkan penjadwal ini ke nova, edit satu file konfigurasi, /etc/nova/nova.conf:

    $ vim /etc/nova/nova.conf
    
  5. Temukan konfigurasi scheduler_driver dan ubah seperti ini:

    scheduler_driver=nova.scheduler.ip_scheduler.IPScheduler
    
  6. Mulai ulang layanan penjadwal nova untuk membuat nova menggunakan penjadwal Anda. Mulailah dengan beralih ke layar (screen) n-sch:

    1. Tekan Ctrl+A diikuti oleh 9.

    2. Tekan Ctrl+A diikuti oleh N sampai Anda mencapai layar n-sch.

    3. Tekan Ctrl+C untuk membunuh layanan.

    4. Tekan Up Arrow untuk memunculkan perintah terakhir.

    5. Tekan Enter untuk menjalankannya.

  7. Uji penjadwal Anda dengan nova CLI. Mulailah dengan beralih ke layar shell dan selesai dengan beralih kembali ke layar n-sch untuk memeriksa output log:

    1. Teken  Ctrl+A diikuti oleh 0.

    2. Pastikan Anda berada di direktori devstack:

      $ cd /root/devstack
      
    3. Sumber openrc untuk mengatur variabel lingkungan Anda untuk CLI:

      $ . openrc
      
    4. Masukkan ID image untuk satu-satunya image yang diinstal ke dalam variabel lingkungan:

      $ IMAGE_ID=`openstack image list | egrep cirros | egrep -v "kernel|ramdisk" | awk '{print $2}'`
      
    5. Boot server uji:

      $ openstack server create --flavor 1 --image $IMAGE_ID scheduler-test
      
  8. Beralih kembali ke layar n-sch. Di antara pernyataan log, Anda akan melihat baris:

    2014-01-23 19:57:47.262 DEBUG nova.scheduler.ip_scheduler
    [req-... demo demo] Request from xx.xx.xx.xx scheduled to devstack-havana
    _schedule /opt/stack/nova/nova/scheduler/ip_scheduler.py:76
    

Peringatan

Pengujian fungsional seperti ini bukan pengganti untuk pengujian unit dan integrasi yang tepat, tetapi berfungsi untuk membantu Anda memulai.

Pola serupa dapat diikuti dalam proyek lain yang menggunakan arsitektur driver. Cukup buat modul dan kelas yang sesuai dengan antarmuka driver dan hubungkan melalui konfigurasi. Kode Anda berjalan ketika fitur itu digunakan dan dapat memanggil layanan lain yang diperlukan. Tidak ada kode core proyek yang disentuh. Cari nilai "driver" di file konfigurasi .conf proyek di /etc/<project> untuk mengidentifikasi proyek yang menggunakan arsitektur driver.

Ketika penjadwal Anda selesai, kami menganjurkan Anda untuk membuka sumbernya dan memberi tahu komunitas di milis OpenStack. Mungkin orang lain membutuhkan fungsi yang sama. Mereka dapat menggunakan kode Anda, memberikan umpan balik, dan mungkin berkontribusi. Jika ada cukup dukungan untuk itu, mungkin Anda dapat mengusulkan agar ditambahkan ke penjadwal Compute resmi <https://opendev.org/openstack/nova/src/branch/master/nova/scheduler> `_.