Cerbo + VPS por 30 rubloj =?

Estas tiel agrable kiam ĉiuj necesaj aĵoj estas ĉe mano: bona skribilo kaj notbloko, akrigita krajono, komforta muso, kelkaj kromaj dratoj, ktp. Ĉi tiuj nevideblaj aferoj ne altiras atenton, sed aldonas komforton al la vivo. La sama rakonto estas kun diversaj poŝtelefonaj kaj labortablaj aplikaĵoj: por longaj ekrankopioj, por redukti la grandecon de bildo, por kalkuli personajn financojn, vortarojn, tradukilojn, konvertilojn ktp. Ĉu vi havas unu? VPS - kiu estas malmultekosta, ĉiam ĉe la mano kaj alportas multajn avantaĝojn? Ne, ne tiun, kiun vi havas en via kompanio, sed vian propran, "poŝon". Ni pensis, ke sen malgranda VPS en 2019 ĝi estis iel malĝoja, same kiel sen la kutima fontoplumo ĉe prelego. Kial esti malĝoja? Estas somero. Kiel fartas somero? Somero por IT-specialisto: sidante hejme, laborante pri viaj plej ŝatataj projektoj sen ajna bedaŭro. Ĝenerale, ni pensis kaj faris ĝin.

Cerbo + VPS por 30 rubloj =?
Komunismo alvenis, kamaradoj.

Li estas tia - nia VPS por tridek

Ni legis multajn artikolojn de konkurantoj kaj uzantoj, kiuj skribis antaŭ 3-4 jaroj pri kial malmultekosta VPS ne necesas. Nu, ĝuste, tiam VPS "por penco" estis pura merkatado kaj ne povis oferti normalajn laborŝancojn. Sed la tempoj ŝanĝas, la kosto de virtualaj rimedoj pli kaj pli malaltiĝas, kaj por 30 rubloj monate ni pretas proponi ĉi tion:

  • Procesoro: Intel Xeon 2 GHz (1 kerno)
  • Linuksa sistemo (Debian, Ubuntu, CentOS por elekti)
  • 1 dediĉita IPv4-adreso
  • 10 GB da datumstokado sur rapidaj entreprenaj SSD-diskoj
  • RAM: 512 MB
  • Sekundo fakturado
  • Senlima trafiko

La tarifo estas submetita al pliaj teknikaj limigoj, detaloj pri paĝo nia bonega oferto - VPS por 30 rubloj. 

Por kiu taŭgas ĉi tiu virtuala servilo? Jes al preskaŭ ĉiuj: komencantoj, entuziasmuloj, spertaj programistoj, DIY-fanoj kaj eĉ iuj kompanioj.

Por kio taŭgas ĉi tiu VPS?

Ni pensas, ke la legantoj de Habr certe trovos sian propran manieron uzi ĉi tiun agordon, sed ni decidis kolekti nian propran elekton de ideoj - kio se iu bezonas ĝin, sed la viroj ne scias?

  • Metu vian simplan retejon, biletujon, vivresumon kun kodo ktp. Kompreneble, via propra desegnita retejo faras pozitivan impreson sur la dunganto. Metu ĝin sur vian VPS kaj estu respondeca pri la sekureco kaj stabileco de la retejo mem, kaj ne de la personaro de regulaj gastigaj provizantoj.
  • Uzu VPS por edukaj celoj: gastigu vian projekton, studu la funkciojn de la servilo kaj servila operaciumo, eksperimentu kun DNS, ĝuu malgrandan edukan retejon.
  • Por telefonio. Kelkfoje individua entreprenisto, liberlaboranto aŭ tre malgranda kompanio ege bezonas IP-telefonion, kaj la telefonistoj de ĉi tiu sama telefonio estas tre avidaj. Estas simple: ni prenas nian servilon, aĉetas numeron de IP-telefona telefonisto, starigas virtualan PBX kaj kreas internajn numerojn (se necese). La ŝparaĵoj estas kolosaj.
  • Uzu la servilon por testi viajn aplikojn.
  • Uzu la servilon por DIY-eksperimentoj, inkluzive de kontrolado kaj kolektado de datumoj de inteligentaj hejmaj sistemaj sensiloj.
  • Nekutima maniero uzi ĝin estas meti virtualan interŝanĝan asistanton, komercan roboton, sur la servilon. Vi estos plene respondeca pri la stabileco kaj sekureco de la servilo, kio signifas, ke vi ricevos kontrolitan instrumenton por komerci sur la borsmerkatoj. Nu, se iu interesiĝas aŭ planas :)

Estas aplikoj por tia VPS en la kompania sfero. Krom la jam menciita telefonservo, vi povas efektivigi plurajn interesajn aferojn. Ekzemple:

  • Metu malgrandajn datumbazojn kaj informojn, kiuj estos alireblaj por vojaĝantaj dungitoj malproksime, ekzemple, uzante ftp. Ĉi tio permesos al vi tre rapide interŝanĝi freŝajn analizojn, ĝisdatigitajn agordojn por vendantoj, prezentojn ktp.
  • Donu provizoran aliron al uzantoj aŭ klientoj por montri programaron aŭ amaskomunikilaron.

VPS-testveturo por 30 rubloj - farita por vi

30 rubloj estas tiom malmulte, ke vi eĉ ne volas eltiri karton por pagi kaj testi. Ankaŭ ni foje estas tiel maldiligentaj, sed ĉi-foje ni faris ĉion por vi. Antaŭ lanĉi la servilojn en batalon, ni faris provon por kontroli ĉiujn detalojn kaj montri, kion kapablas la serviloj ĉe ĉi tiu tarifo. Por fari ĝin pli interesa, ni aldonis ekstreman kaj kontrolis kiel ĉi tiu agordo kondutus se la denseco kaj ŝarĝo superus la valorojn, kiujn ni starigis. 

La gastiganto estis sub la ŝarĝo de kelkaj virtualaj maŝinoj, kiuj plenumis diversajn taskojn sur la procesoro kaj aktive uzis la disksubsistemon. La celo estas simuli altan densecon de lokigo kaj ŝarĝon komparebla al aŭ pli granda ol batala unu.

Krom la konstanta ŝarĝo, ni instalis 3 virtualajn maŝinojn, kiuj kolektis sintezajn metrikojn per sysbench, kies averaĝaj rezultoj estis donitaj sube, kaj 50 virtualajn maŝinojn, kiuj kreis plian ŝarĝon. Ĉiuj testaj virtualaj maŝinoj havis la saman agordon (1 kerno, RAM 512 GB, SSD 10 GB), la norma debian 9.6 bildo estis elektita kiel la operaciumo, kiu estas ofertita al uzantoj sur RUVDS.

La ŝarĝo estis simulita en naturo kaj grandeco komparebla al batalo:

  • Kelkaj virtualaj maŝinoj estis lanĉitaj kun malalta ŝarĝo
  • Kelkaj maŝinoj prizorgis testan skripton simulantan la ŝarĝon sur la procesoro (uzante la ilon premo)
  • Sur la restanta parto de la virtualaj maŝinoj, ni rulis skripton kiu uzis dd por kopii datumojn de antaŭpreparitaj datumoj al disko kun limo fiksita uzante pv (ekzemploj videblas tie и tie).

Ankaŭ, kiel vi memoras, ni havis tri maŝinojn, kiuj kolektis sintezajn metrikojn.

Sur ĉiu maŝino, skripto estis ekzekutita cikle ĉiujn 15 minutojn, kiu funkcias normajn sysbench-testojn por la procesoro, memoro kaj disko.

Skripto sysbench.sh

#!/bin/bash
date +"%Y-%m-%d %H:%M:%S" >> /root/sysbench/results.txt
sysbench --test=cpu run >> /root/sysbench/results.txt
sysbench --test=memory run >> /root/sysbench/results.txt
sysbench --test=fileio --file-test-mode=seqwr run >> /root/sysbench/results.txt
sysbench --test=fileio --file-test-mode=seqrd run >> /root/sysbench/results.txt
sysbench --test=fileio --file-test-mode=rndrw run >> /root/sysbench/results.txt

La rezultoj estas prezentitaj por oportuno en sysbench-formato, sed la averaĝaj valoroj por la tuta testa periodo estis prenitaj de ĉiuj maŝinoj, la rezulto povas esti vidita ĉi tie:

Sysbanch-avg.txtsysbench 0.4.12: multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Doing CPU performance benchmark

Threads started!
Done.

Maximum prime number checked in CPU test: 10000

Test execution summary:
total time: 19.2244s
total number of events: 10000
total time taken by event execution: 19.2104
per-request statistics:
min: 1.43ms
avg: 1.92ms
max: 47.00ms
approx. 95 percentile: 3.02ms

Threads fairness:
events (avg/stddev): 10000.0000/0.00
execution time (avg/stddev): 19.2104/0.00

sysbench 0.4.12: multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Doing memory operations speed test
Memory block size: 1K

Memory transfer size: 102400M

Memory operations type: write
Memory scope type: global
Threads started!
Done.

Operations performed: 104857600 (328001.79 ops/sec)

102400.00 MB transferred (320.32 MB/sec)

Test execution summary:
total time: 320.9155s
total number of events: 104857600
total time taken by event execution: 244.8399
per-request statistics:
min: 0.00ms
avg: 0.00ms
max: 139.41ms
approx. 95 percentile: 0.00ms

Threads fairness:
events (avg/stddev): 104857600.0000/0.00
execution time (avg/stddev): 244.8399/0.00

sysbench 0.4.12: multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
128 files, 16Mb each
2Gb total file size
Block size 16Kb
Periodic FSYNC enabled, calling fsync() each 100 requests.
Calling fsync() at the end of test, Enabled.
Using synchronous I/O mode
Doing sequential write (creation) test
Threads started!
Done.

Operations performed: 0 Read, 131072 Write, 128 Other = 131200 Total
Read 0b Written 2Gb Total transferred 2Gb (320.1Mb/sec)
20251.32 Requests/sec executed

Test execution summary:
total time: 6.9972s
total number of events: 131072
total time taken by event execution: 5.2246
per-request statistics:
min: 0.01ms
avg: 0.04ms
max: 96.76ms
approx. 95 percentile: 0.03ms

Threads fairness:
events (avg/stddev): 131072.0000/0.00
execution time (avg/stddev): 5.2246/0.00

sysbench 0.4.12: multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
128 files, 16Mb each
2Gb total file size
Block size 16Kb
Periodic FSYNC enabled, calling fsync() each 100 requests.
Calling fsync() at the end of test, Enabled.
Using synchronous I/O mode
Doing sequential read test
Threads started!
Done.

Operations performed: 131072 Read, 0 Write, 0 Other = 131072 Total
Read 2Gb Written 0b Total transferred 2Gb (91.32Mb/sec)
5844.8 Requests/sec executed

Test execution summary:
total time: 23.1054s
total number of events: 131072
total time taken by event execution: 22.9933
per-request statistics:
min: 0.00ms
avg: 0.18ms
max: 295.75ms
approx. 95 percentile: 0.77ms

Threads fairness:
events (avg/stddev): 131072.0000/0.00
execution time (avg/stddev): 22.9933/0.00

sysbench 0.4.12: multi-threaded system evaluation benchmark

Running the test with following options:
Number of threads: 1

Extra file open flags: 0
128 files, 16Mb each
2Gb total file size
Block size 16Kb
Number of random requests for random IO: 10000
Read/Write ratio for combined random IO test: 1.50
Periodic FSYNC enabled, calling fsync() each 100 requests.
Calling fsync() at the end of test, Enabled.
Using synchronous I/O mode
Doing random r/w test
Threads started!
Done.

Operations performed: 6000 Read, 4000 Write, 12800 Other = 22800 Total
Read 93.75Mb Written 62.5Mb Total transferred 156.25Mb (1341.5Kb/sec)
85.61 Requests/sec executed

Test execution summary:
total time: 152.9786s
total number of events: 10000
total time taken by event execution: 14.1879
per-request statistics:
min: 0.01ms
avg: 1.41ms
max: 210.22ms
approx. 95 percentile: 4.95ms

Threads fairness:
events (avg/stddev): 10000.0000/0.00
execution time (avg/stddev): 14.1879/0.00

La rezultoj estas indikaj, sed ankoraŭ ne devus esti prenitaj kiel QoS. 

Maŝinoj kiuj kreas plian ŝarĝon

Mola:

  • kapabla akiri ĝisdatigon
  • kapabla-ĝisdatigi
  • apt-get install python-pip
  • pip instali mysql-connector-python-rf

Instalita MariaDB, Kiel tie:

apt-get install libmariadbclient-dev
mysql -e "INSTALL PLUGIN blackhole SONAME 'ha_blackhole.so';" -- нужно для test_employees_sha

Testbazo prenita de ĉi tie:

La datumbazo estas deplojita kiel specifita tie:

mysql -t < employees.sql
mysql -t < test_employees_sha.sql

Malgranda testa bazo:

tablo 

RowsCount 

Grandeco de datumoj (MB)

Indeksa grandeco (KB)

fakoj 

9

0.02

16.00

dept_emp 

331143 

11.52

5648.00

dept_manaĝero 

24 

0.02

16.00

oficistoj 

299379 

14.52

0.00

salajroj 

2838426 

95.63

0.00 

titoloj 

442783 

19.56

0.00

Primitiva testservo estas skribita sur la genuo en Python; ĝi elfaras kvar operaciojn:

  1. getState: resendas la statuson
  2. getEmployee: resendas dungitojn (+salajrojn, +titolojn) el la datumbazo
  3. patchEmployee: ŝanĝas dungitajn kampojn
  4. insertSalary: enmetas salajron

Servofonto (dbtest.py)

#!/usr/bin/python
import mysql.connector as mariadb
from flask import Flask, json, request, abort
from mysql.connector.constants import ClientFlag

app = Flask(__name__)

def getFields(cursor):
    results = {}
    column = 0
    for d in cursor.description:
        results[d[0]] = column
        column = column + 1
    return results

PAGE_SIZE = 30

@app.route("/")
def main():
    return "Hello!"

@app.route("/employees/<page>", methods=['GET'])
def getEmployees(page):
    offset = (int(page) - 1) * PAGE_SIZE
    connection = mariadb.connect(user='admin', password='q5XpRomdSr', database='employees')
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM employees LIMIT {} OFFSET {}".format(PAGE_SIZE, offset))
    return {'employees': [i[0] for i in cursor.fetchall()]}

@app.route("/employee/<id>", methods=['GET'])
def getEmployee(id):
    id = int(id)
    connection = mariadb.connect(user='admin', password='q5XpRomdSr', database='employees')
    cursor = connection.cursor()
    cursor.execute("SELECT * FROM employees WHERE emp_no = {}".format(id))
    fields = getFields(cursor)
    employee = {}
    found = False
    for row in cursor.fetchall():
        found = True
        employee = {
            "birth_date": row[fields["birth_date"]],
            "first_name": row[fields["first_name"]],
            "last_name": row[fields["last_name"]],
            "gender": row[fields["gender"]],
            "hire_date": row[fields["hire_date"]]
        }
    if not found:
        abort(404)
    cursor.execute("SELECT * FROM salaries WHERE emp_no = {}".format(id))
    fields = getFields(cursor)
    salaries = []
    for row in cursor.fetchall():
        salary = {
            "salary": row[fields["salary"]],
            "from_date": row[fields["from_date"]],
            "to_date": row[fields["to_date"]]
        }
        salaries.append(salary)
    employee["salaries"] = salaries
    cursor.execute("SELECT * FROM titles WHERE emp_no = {}".format(id))
    fields = getFields(cursor)
    titles = []
    for row in cursor.fetchall():
        title = {
            "title": row[fields["title"]],
            "from_date": row[fields["from_date"]],
            "to_date": row[fields["to_date"]]
        }
        titles.append(title)
    employee["titles"] = titles
    return json.dumps({
        "status": "success",
        "employee": employee
    })

def isFieldValid(t, v):
    if t == "employee":
        return v in ["birdth_date", "first_name", "last_name", "hire_date"]
    else:
        return false

@app.route("/employee/<id>", methods=['PATCH'])
def setEmployee(id):
    id = int(id)
    content = request.json
    print(content)
    setList = ""
    data = []
    for k, v in content.iteritems():
        if not isFieldValid("employee", k):
            continue
        if setList != "":
            setList = setList + ", "
        setList = setList + k + "=%s"
        data.append(v)
    data.append(id)
    print(setList)
    print(data)
    connection = mariadb.connect(user='admin', password='q5XpRomdSr', database='employees', client_flags=[ClientFlag.FOUND_ROWS])
    cursor = connection.cursor()
    cursor.execute("UPDATE employees SET {} WHERE emp_no = %s".format(setList), data)
    connection.commit()
    if cursor.rowcount < 1:
        abort(404)
    return json.dumps({
        "status": "success"
    })

@app.route("/salary", methods=['PUT'])
def putSalary():
    content = request.json
    print(content)
    connection = mariadb.connect(user='admin', password='q5XpRomdSr', database='employees', client_flags=[ClientFlag.FOUND_ROWS])
    cursor = connection.cursor()
    data = [content["emp_no"], content["salary"], content["from_date"], content["to_date"]]
    cursor.execute("INSERT INTO salaries (emp_no, salary, from_date, to_date) VALUES (%s, %s, %s, %s)", data)
    connection.commit()
    return json.dumps({
        "status": "success"
    })


@app.route("/state", methods=['GET'])
def getState():
    return json.dumps({
        "status": "success",
        "state": "working"
    })

if __name__ == '__main__':
    app.run(host='0.0.0.0',port='5002')

Singardemo Neniam oni devas preni ĉi tiun servon kiel ekzemplon aŭ gvidilon!

Testoj estas faritaj uzante bonan malnovan JMeter. Serio de testoj daŭrantaj de 15 minutoj ĝis 2 horoj estis lanĉitaj, sen interrompoj, la procento de petoj variis, kaj trairo variis de 300 ĝis 600 petoj je minuto. Nombro da fadenoj de 50 ĝis 500.

Pro la fakto, ke la datumbazo estas tre malgranda, la komando:

mysql -e "SHOW ENGINE INNODB STATUS"

Montras ke:

Buffer pool hit rate 923 / 1000, young-making rate 29 / 1000 not 32 / 1000

Malsupre estas la mezaj respondtempoj por petoj:

Etikedo

Averaĝa

Meza

90% Linio

95% Linio

99% Linio

min

maks

getEmployee

37.64

12.57

62.28

128.5

497.57

5

4151.78

getState

17

7.57

30.14

58.71

193

3

2814.71

flikiloDungito

161.42

83.29

308

492.57

1845.14

5

6639.4

meti Salajron

167.21

86.93

315.34

501.07

1927.12

7

6722.44

Povas esti malfacile por vi juĝi laŭ ĉi tiuj sintezaj rezultoj, kiom taŭgas ĉi tiu VPS por viaj specifaj taskoj kaj, ĝenerale, la listigitaj metodoj estas limigitaj al tiuj kazoj, kiujn ni devis trakti en unu aŭ alia formo.Do nia listo estas klare ne ĝisfunda. Ni invitas vin eltiri viajn proprajn konkludojn kaj testi la servilon por 30 rubloj pri viaj realaj aplikoj kaj taskoj kaj sugesti viajn eblojn por ĉi tiu agordo en la komentoj.

fonto: www.habr.com

Aldoni komenton