Scaling out

Scaling out

Alasan yang sering dikutip untuk merancang aplikasi dengan menggunakan pola awan adalah kemampuan untuk scale out. Yaitu: menambahkan sumber daya tambahan, sesuai kebutuhan. Bandingkan strategi ini dengan kapasitas peningkatan sebelumnya dengan meningkatkan (scaling up) ukuran sumber daya yang ada. Untuk scale out, Anda harus:

  • Arsitek aplikasi Anda untuk memanfaatkan sumber daya tambahan.

  • Memungkinkan untuk menambahkan sumber daya baru ke aplikasi Anda.

Bagian Pengantar arsitektur aplikasi fraktal menjelaskan bagaimana membangun secara modular, membuat API, dan aspek arsitektur aplikasi lainnya. Sekarang Anda akan melihat mengapa strategi tersebut sangat penting. Dengan membuat aplikasi modular dengan layanan decoupled, Anda dapat mengidentifikasi komponen yang menyebabkan kemacetan kinerja aplikasi dan memperkecilnya. Sama pentingnya, Anda juga dapat menghapus sumber daya saat tidak diperlukan lagi. Sangat sulit untuk melebih-lebihkan penghematan biaya yang bisa diperoleh fitur ini, dibandingkan dengan infrastruktur tradisional.

Tentu saja, memiliki akses ke sumber daya tambahan hanyalah bagian dari rencana permainan; Sementara Anda dapat menambahkan atau menghapus sumber daya secara manual, Anda mendapatkan nilai lebih dan lebih responsif jika aplikasi meminta secara otomatis sumber daya tambahan saat dibutuhkan.

Bagian ini terus menggambarkan pemisahan layanan ke beberapa instance dan menyoroti beberapa pilihan yang telah kita buat yang memfasilitasi skalabilitas dalam arsitektur aplikasi.

Anda akan semakin banyak menggunakan sampai enam instance, jadi pastikan akun awan Anda memiliki kuota yang sesuai.

Bagian sebelumnya menggunakan dua mesin virtual - satu layanan 'control' dan satu 'worker'. Kecepatan di mana aplikasi Anda dapat menghasilkan fraktal bergantung pada jumlah pekerja. Dengan hanya satu pekerja, Anda hanya bisa menghasilkan satu fraktal sekaligus. Tidak lama lagi, Anda akan membutuhkan lebih banyak sumber daya.

Catatan

Jika Anda tidak memiliki aplikasi yang bekerja, ikuti langkah-langkah di Pengantar arsitektur aplikasi fraktal untuk membuatnya.

Membangkitkan muatan

Untuk menguji apa yang terjadi saat aplikasi Fractals di bawah muatan, Anda dapat:

  • Muatkan pekerja: Buat banyak tugas untuk memaksimalkan CPU dari instance pekerja yang ada

  • Muat API: Buat banyak permintaan layanan API

Buat lebih banyak tugas

Gunakan SSH dengan keypair SSH yang ada untuk login ke instance controller app-controller:

$ ssh -i ~/.ssh/id_rsa USERNAME@IP_CONTROLLER

Catatan

Ganti IP_CONTROLLER dengan alamat IP dari instance controller dan USERNAME dengan nama pengguna yang sesuai.

Panggil antarmuka command-line faafo untuk meminta pembangkit lima fraktal besar.

$ faafo create --height 9999 --width 9999 --tasks 5

Jika Anda memeriksa muatan pekerja, Anda bisa melihat instance tidak berjalan dengan baik. Pada instance flavor CPU tunggal, rata-rata muatan lebih besar dari 1 berarti server berada pada kapasitas.

$ ssh -i ~/.ssh/id_rsa USERNAME@IP_WORKER uptime
10:37:39 up  1:44,  2 users,  load average: 1.24, 1.40, 1.36

Catatan

Ganti IP_WORKER dengan alamat IP dari instance pekerja dan USERNAME dengan nama pengguna yang sesuai.

Buat lebih banyak permintaan layanan API

Beban API adalah masalah yang sedikit berbeda dari yang sebelumnya mengenai kapasitas untuk bekerja. Kita bisa mensimulasikan banyak permintaan ke API, sebagai berikut:

Gunakan SSH dengan keypair SSH yang ada untuk login ke instance controller app-controller:

$ ssh -i ~/.ssh/id_rsa USERNAME@IP_CONTROLLER

Catatan

Ganti IP_CONTROLLER dengan alamat IP dari instance controller dan USERNAME dengan nama pengguna yang sesuai.

Gunakan for loop until memanggil antarmuka command-line faafo untuk meminta kumpulan fraktal acak sebanyak 500 kali:

$ for i in $(seq 1 500); do faafo --endpoint-url http://IP_CONTROLLER create & done

Catatan

Ganti IP_CONTROLLER dengan alamat IP dari instance controller.

Jika Anda memeriksa beban pada instance layanan API app-controller , Anda melihat instance tidak berjalan dengan baik. Pada instance flavor CPU tunggal Anda, rata-rata beban lebih besar dari 1 berarti server berkapasitas.

$ uptime
10:37:39 up  1:44,  2 users,  load average: 1.24, 1.40, 1.36

Jumlah permintaan sebenarnya berarti beberapa permintaan fraktal mungkin tidak sampai ke antrian pesan untuk diproses. Untuk memastikan bahwa Anda dapat mengatasi permintaan, Anda juga harus meningkatkan kemampuan API dari aplikasi Fractals.

Scaling out

Hapus aplikasi yang ada

Lanjutkan dan hapus instance dan kelompok keamanan yang ada yang Anda buat di bagian sebelumnya. Ingat, saat kejadian di awan tidak lagi berfungsi, lepaskan mereka dan ciptakan kembali sesuatu yang baru.

for instance in conn.list_nodes():
    if instance.name in ['all-in-one','app-worker-1', 'app-worker-2', 'app-controller']:
        print('Destroying Instance: %s' % instance.name)
        conn.destroy_node(instance)


for group in conn.ex_list_security_groups():
    if group.name in ['control', 'worker', 'api', 'services']:
        print('Deleting security group: %s' % group.name)
        conn.ex_delete_security_group(group)

Kelompok keamanan ekstra

Saat Anda mengubah topologi aplikasi Anda, Anda harus memperbarui atau membuat grup keamanan. Di sini, Anda menciptakan kembali kelompok keamanan yang dibutuhkan.

api_group = conn.ex_create_security_group('api', 'for API services only')
conn.ex_create_security_group_rule(api_group, 'TCP', 80, 80)
conn.ex_create_security_group_rule(api_group, 'TCP', 22, 22)

worker_group = conn.ex_create_security_group('worker', 'for services that run on a worker node')
conn.ex_create_security_group_rule(worker_group, 'TCP', 22, 22)

controller_group = conn.ex_create_security_group('control', 'for services that run on a control node')
conn.ex_create_security_group_rule(controller_group, 'TCP', 22, 22)
conn.ex_create_security_group_rule(controller_group, 'TCP', 80, 80)
conn.ex_create_security_group_rule(controller_group, 'TCP', 5672, 5672, source_security_group=worker_group)

services_group = conn.ex_create_security_group('services', 'for DB and AMQP services only')
conn.ex_create_security_group_rule(services_group, 'TCP', 22, 22)
conn.ex_create_security_group_rule(services_group, 'TCP', 3306, 3306, source_security_group=api_group)
conn.ex_create_security_group_rule(services_group, 'TCP', 5672, 5672, source_security_group=worker_group)
conn.ex_create_security_group_rule(services_group, 'TCP', 5672, 5672, source_security_group=api_group)

Fungsi pembantu IP mengambang

Tentukan fungsi singkat untuk menemukan yang tidak digunakan atau mengalokasikan IP mengambang. Ini menghemat beberapa baris kode dan mencegah Anda mencapai kuota IP mengambang Anda terlalu cepat.

def get_floating_ip(conn):
    '''A helper function to re-use available Floating IPs'''
    unused_floating_ip = None
    for floating_ip in conn.ex_list_floating_ips():
        if not floating_ip.node_id:
            unused_floating_ip = floating_ip
            break
    if not unused_floating_ip:
        pool = conn.ex_list_floating_ip_pools()[0]
        unused_floating_ip = pool.create_floating_ip()
    return unused_floating_ip

Memisahkan database dan message queue

Sebelum Anda mengurangi layanan aplikasi Anda, seperti layanan API atau pekerja, Anda harus menambahkan database pusat dan instance pengiriman app-services . Database dan antrian pesan akan digunakan untuk melacak keadaan fraktal dan untuk mengkoordinasikan komunikasi antara layanan.

userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i database -i messaging
'''

instance_services = conn.create_node(name='app-services',
                                     image=image,
                                     size=flavor,
                                     ex_keyname='demokey',
                                     ex_userdata=userdata,
                                     ex_security_groups=[services_group])
instance_services = conn.wait_until_running([instance_services])[0][0]
services_ip = instance_services.private_ips[0]


Skala layanan API

Dengan beberapa pekerja yang memproduksi fraktal secepat mungkin, sistem harus dapat menerima permintaan fraktal secepat mungkin. Jika aplikasi kita menjadi populer, ribuan pengguna mungkin terhubung ke API untuk menghasilkan fraktal.

Berbekal security group, image, dan flavor size, Anda dapat menambahkan beberapa layanan API:

userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i faafo -r api -m 'amqp://guest:guest@%(services_ip)s:5672/' \
    -d 'mysql+pymysql://faafo:password@%(services_ip)s:3306/faafo'
''' % { 'services_ip': services_ip }
instance_api_1 = conn.create_node(name='app-api-1',
                                  image=image,
                                  size=flavor,
                                  ex_keyname='demokey',
                                  ex_userdata=userdata,
                                  ex_security_groups=[api_group])
instance_api_2 = conn.create_node(name='app-api-2',
                                  image=image,
                                  size=flavor,
                                  ex_keyname='demokey',
                                  ex_userdata=userdata,
                                  ex_security_groups=[api_group])
instance_api_1 = conn.wait_until_running([instance_api_1])[0][0]
api_1_ip = instance_api_1.private_ips[0]
instance_api_2 = conn.wait_until_running([instance_api_2])[0][0]
api_2_ip = instance_api_2.private_ips[0]

for instance in [instance_api_1,  instance_api_2]:
    floating_ip = get_floating_ip(conn)
    conn.ex_attach_floating_ip_to_node(instance, floating_ip)
    print('allocated %(ip)s to %(host)s' % {'ip': floating_ip.ip_address, 'host': instance.name})

Layanan ini bersifat klien, sangat tidak seperti para pekerja mereka tidak menggunakan antrian pesan untuk mendistribusikan tugas. Sebagai gantinya, Anda harus memperkenalkan semacam mekanisme load balancing untuk berbagi permintaan yang masuk antara berbagai layanan API.

Solusi sederhana adalah memberi setengah dari satu alamat teman Anda dan setengah lainnya, namun solusi itu tidak berkelanjutan. Sebagai gantinya, Anda bisa menggunakan DNS round robin untuk melakukannya secara otomatis. Namun, jaringan OpenStack dapat menyediakan Load Balancing sebagai Service, yang mana Jaringan menjelaskan.

Skala pekerja

Untuk meningkatkan kapasitas keseluruhan, tambahkan tiga pekerja:

userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i faafo -r worker -e 'http://%(api_1_ip)s' -m 'amqp://guest:guest@%(services_ip)s:5672/'
''' % {'api_1_ip': api_1_ip, 'services_ip': services_ip}
instance_worker_1 = conn.create_node(name='app-worker-1',
                                     image=image, size=flavor,
                                     ex_keyname='demokey',
                                     ex_userdata=userdata,
                                     ex_security_groups=[worker_group])
instance_worker_2 = conn.create_node(name='app-worker-2',
                                     image=image, size=flavor,
                                     ex_keyname='demokey',
                                     ex_userdata=userdata,
                                     ex_security_groups=[worker_group])
instance_worker_3 = conn.create_node(name='app-worker-3',
                                     image=image, size=flavor,
                                     ex_keyname='demokey',
                                     ex_userdata=userdata,
                                     ex_security_groups=[worker_group])

Menambahkan kapasitas ini memungkinkan Anda menangani jumlah permintaan fraktal yang lebih tinggi. Begitu instance pekerja ini dimulai, mereka mulai memeriksa antrian pesan untuk permintaan, mengurangi keseluruhan simpanan keseluruhan seperti pembukaan pendaftar baru di supermarket.

Proses ini jelas sangat manual. Mencari tahu bahwa kami membutuhkan lebih banyak pekerja dan kemudian memulai pekerjaan baru membutuhkan beberapa usaha. Idealnya sistem akan melakukan ini sendiri. Jika Anda membangun aplikasi Anda untuk mendeteksi situasi ini, Anda dapat memilikinya secara otomatis meminta dan menghapus sumber daya, yang menghemat usaha melakukan pekerjaan ini sendiri. Sebagai gantinya, layanan OpenStack Orchestration dapat memantau beban dan menghidupkan instance, jika sesuai. Untuk mengetahui cara mengaturnya, lihat Orchestration.

Verifikasi bahwa kami memiliki dampak

Pada langkah sebelumnya, Anda membagi beberapa layanan dan kapasitas yang diperluas. Untuk melihat fitur baru aplikasi Fractal, SSH ke salah satu instance aplikasi dan membuat beberapa fraktal.

$ ssh -i ~/.ssh/id_rsa USERNAME@IP_API_1

Catatan

Ganti IP_API_1 dengan alamat IP dari instance API pertama dan USERNAME dengan nama pengguna yang sesuai.

Gunakan perintah faafo create untuk menghasilkan fraktal.

Gunakan perintah faafo list untuk melihat perkembangan generasi fraktal.

Gunakan perintah faafo UUID untuk memeriksa beberapa fraktal.

Field generated_by menunjukkan pekerja yang menciptakan fraktal. Karena beberapa instance pekerja berbagi pekerjaan, fraktal dihasilkan lebih cepat dan pengguna mungkin tidak menyadarinya saat pekerja gagal.

root@app-api-1:# faafo list
+--------------------------------------+------------------+-------------+
|                 UUID                 |    Dimensions    |   Filesize  |
+--------------------------------------+------------------+-------------+
| 410bca6e-baa7-4d82-9ec0-78e409db7ade | 295 x 738 pixels | 26283 bytes |
| 66054419-f721-492f-8964-a5c9291d0524 | 904 x 860 pixels | 78666 bytes |
| d123e9c1-3934-4ffd-8b09-0032ca2b6564 | 952 x 382 pixels | 34239 bytes |
| f51af10a-084d-4314-876a-6d0b9ea9e735 | 877 x 708 pixels | 93679 bytes |
+--------------------------------------+------------------+-------------+

root@app-api-1:# faafo show d123e9c1-3934-4ffd-8b09-0032ca2b6564
+--------------+------------------------------------------------------------------+
| Parameter    | Value                                                            |
+--------------+------------------------------------------------------------------+
| uuid         | d123e9c1-3934-4ffd-8b09-0032ca2b6564                             |
| duration     | 1.671410 seconds                                                 |
| dimensions   | 952 x 382 pixels                                                 |
| iterations   | 168                                                              |
| xa           | -2.61217                                                         |
| xb           | 3.98459                                                          |
| ya           | -1.89725                                                         |
| yb           | 2.36849                                                          |
| size         | 34239 bytes                                                      |
| checksum     | d2025a9cf60faca1aada854d4cac900041c6fa762460f86ab39f42ccfe305ffe |
| generated_by | app-worker-2                                                     |
+--------------+------------------------------------------------------------------+
root@app-api-1:# faafo show 66054419-f721-492f-8964-a5c9291d0524
+--------------+------------------------------------------------------------------+
| Parameter    | Value                                                            |
+--------------+------------------------------------------------------------------+
| uuid         | 66054419-f721-492f-8964-a5c9291d0524                             |
| duration     | 5.293870 seconds                                                 |
| dimensions   | 904 x 860 pixels                                                 |
| iterations   | 348                                                              |
| xa           | -2.74108                                                         |
| xb           | 1.85912                                                          |
| ya           | -2.36827                                                         |
| yb           | 2.7832                                                           |
| size         | 78666 bytes                                                      |
| checksum     | 1f313aaa36b0f616b5c91bdf5a9dc54f81ff32488ce3999f87a39a3b23cf1b14 |
| generated_by | app-worker-1                                                     |
+--------------+------------------------------------------------------------------+

Fraktal sekarang tersedia dari host app-api manapun. Untuk memverifikasi, kunjungi http://IP_API_1/fractal/FRACTAL_UUID dan http://IP_API_2/fractal/FRACTAL_UUID. Anda sekarang memiliki banyak layanan web berlebihan. Jika gagal, Anda bisa menggunakan yang lain.

Catatan

Ganti IP_API_1 dan IP_API_2 dengan IP mengambang yang sesuai. Ganti FRACTAL_UUID dengan UUID dari fraktal yang ada.

Silakan menguji toleransi kesalahan. Mulai menghapus pekerja dan instance API. Selama Anda memilikinya, aplikasi Anda baik-baik saja. Namun, perhatikan satu titik lemah. Database berisi fraktal dan metadata fraktal. Jika Anda kehilangan instance itu, aplikasi akan berhenti. Bagian selanjutnya akan menjelaskan bagaimana mengatasi titik lemah ini.

Jika Anda memiliki penyeimbang beban, Anda dapat mendistribusikan beban ini di antara dua layanan API yang berbeda. Anda memiliki beberapa pilihan. Bagian Jaringan menunjukkan satu pilihan.

Secara teori, Anda bisa menggunakan skrip sederhana untuk memantau beban pada pekerja dan layanan API Anda dan memicu terciptanya instance, yang sudah Anda ketahui bagaimana melakukannya. Selamat! Anda siap membuat aplikasi cloud scalable.

Tentu saja, membuat sistem pemantauan untuk satu aplikasi mungkin tidak masuk akal. Untuk mempelajari cara menggunakan pemantauan OpenStack Orchestration dan kemampuan penskalaan otomatis untuk mengotomatisasi langkah-langkah ini, lihat Orchestration.

Langkah selanjutnya

Anda harus cukup percaya diri untuk memulai instance dan mendistribusikan layanan dari aplikasi di antara instances ini.

Seperti yang disebutkan di Pengantar arsitektur aplikasi fraktal, image fraktal yang dihasilkan disimpan pada sistem file lokal dari instance layanan API. Karena beberapa instance API Anda aktif dan berjalan, image fraktal tersebar di beberapa layanan API, yang menyebabkan sejumlah pengecualian IOError: [Errno 2] No such file or directory saat mencoba mendownload image fraktal dari instance layanan API yang tidak memiliki image fraktal pada sistem file lokalnya.

Lanjut ke Make it durable untuk belajar bagaimana menggunakan Object Storage untuk mengatasi masalah ini dengan cara yang elegan. Atau, Anda bisa melanjutkan ke salah satu bagian berikut:

Sampel kode lengkap

File ini berisi semua kode dari bagian tutorial ini. Sampel kode komprehensif ini memungkinkan Anda melihat dan menjalankan kode sebagai satu skrip.

Sebelum menjalankan skrip ini, konfirmasikan bahwa Anda telah menyetel informasi autentikasi, flavor ID, dan image ID.

# step-1
for instance in conn.list_nodes():
    if instance.name in ['all-in-one','app-worker-1', 'app-worker-2', 'app-controller']:
        print('Destroying Instance: %s' % instance.name)
        conn.destroy_node(instance)


for group in conn.ex_list_security_groups():
    if group.name in ['control', 'worker', 'api', 'services']:
        print('Deleting security group: %s' % group.name)
        conn.ex_delete_security_group(group)

# step-2
api_group = conn.ex_create_security_group('api', 'for API services only')
conn.ex_create_security_group_rule(api_group, 'TCP', 80, 80)
conn.ex_create_security_group_rule(api_group, 'TCP', 22, 22)

worker_group = conn.ex_create_security_group('worker', 'for services that run on a worker node')
conn.ex_create_security_group_rule(worker_group, 'TCP', 22, 22)

controller_group = conn.ex_create_security_group('control', 'for services that run on a control node')
conn.ex_create_security_group_rule(controller_group, 'TCP', 22, 22)
conn.ex_create_security_group_rule(controller_group, 'TCP', 80, 80)
conn.ex_create_security_group_rule(controller_group, 'TCP', 5672, 5672, source_security_group=worker_group)

services_group = conn.ex_create_security_group('services', 'for DB and AMQP services only')
conn.ex_create_security_group_rule(services_group, 'TCP', 22, 22)
conn.ex_create_security_group_rule(services_group, 'TCP', 3306, 3306, source_security_group=api_group)
conn.ex_create_security_group_rule(services_group, 'TCP', 5672, 5672, source_security_group=worker_group)
conn.ex_create_security_group_rule(services_group, 'TCP', 5672, 5672, source_security_group=api_group)

# step-3
def get_floating_ip(conn):
    '''A helper function to re-use available Floating IPs'''
    unused_floating_ip = None
    for floating_ip in conn.ex_list_floating_ips():
        if not floating_ip.node_id:
            unused_floating_ip = floating_ip
            break
    if not unused_floating_ip:
        pool = conn.ex_list_floating_ip_pools()[0]
        unused_floating_ip = pool.create_floating_ip()
    return unused_floating_ip

# step-4
userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i database -i messaging
'''

instance_services = conn.create_node(name='app-services',
                                     image=image,
                                     size=flavor,
                                     ex_keyname='demokey',
                                     ex_userdata=userdata,
                                     ex_security_groups=[services_group])
instance_services = conn.wait_until_running([instance_services])[0][0]
services_ip = instance_services.private_ips[0]


# step-5
userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i faafo -r api -m 'amqp://guest:guest@%(services_ip)s:5672/' \
    -d 'mysql+pymysql://faafo:password@%(services_ip)s:3306/faafo'
''' % { 'services_ip': services_ip }
instance_api_1 = conn.create_node(name='app-api-1',
                                  image=image,
                                  size=flavor,
                                  ex_keyname='demokey',
                                  ex_userdata=userdata,
                                  ex_security_groups=[api_group])
instance_api_2 = conn.create_node(name='app-api-2',
                                  image=image,
                                  size=flavor,
                                  ex_keyname='demokey',
                                  ex_userdata=userdata,
                                  ex_security_groups=[api_group])
instance_api_1 = conn.wait_until_running([instance_api_1])[0][0]
api_1_ip = instance_api_1.private_ips[0]
instance_api_2 = conn.wait_until_running([instance_api_2])[0][0]
api_2_ip = instance_api_2.private_ips[0]

for instance in [instance_api_1,  instance_api_2]:
    floating_ip = get_floating_ip(conn)
    conn.ex_attach_floating_ip_to_node(instance, floating_ip)
    print('allocated %(ip)s to %(host)s' % {'ip': floating_ip.ip_address, 'host': instance.name})

# step-6
userdata = '''#!/usr/bin/env bash
curl -L -s https://opendev.org/openstack/faafo/raw/contrib/install.sh | bash -s -- \
    -i faafo -r worker -e 'http://%(api_1_ip)s' -m 'amqp://guest:guest@%(services_ip)s:5672/'
''' % {'api_1_ip': api_1_ip, 'services_ip': services_ip}
instance_worker_1 = conn.create_node(name='app-worker-1',
                                     image=image, size=flavor,
                                     ex_keyname='demokey',
                                     ex_userdata=userdata,
                                     ex_security_groups=[worker_group])
instance_worker_2 = conn.create_node(name='app-worker-2',
                                     image=image, size=flavor,
                                     ex_keyname='demokey',
                                     ex_userdata=userdata,
                                     ex_security_groups=[worker_group])
instance_worker_3 = conn.create_node(name='app-worker-3',
                                     image=image, size=flavor,
                                     ex_keyname='demokey',
                                     ex_userdata=userdata,
                                     ex_security_groups=[worker_group])

# step-7
Creative Commons Attribution 3.0 License

Except where otherwise noted, this document is licensed under Creative Commons Attribution 3.0 License. See all OpenStack Legal Documents.