Hè cusì bellu quandu tutte e cose necessarie sò in manu: una bona penna è un bloccu note, un lapis affilatu, un mouse còmode, un paru di fili extra, etc. Queste cose inconspicuous ùn attrae micca l'attenzione, ma aghjunghjenu cunfortu à a vita. A listessa storia hè cù parechje applicazioni mobili è desktop: per screenshots longu, per riduce a dimensione di una stampa, per calculà e finanze persunale, dizziunari, traduttori, cunvertitori, etc. Avete unu ? VPS - chì hè prezzu, sempre à manu è porta assai benefici ? Innò, micca quellu chì avete in a vostra cumpagnia, ma u vostru propiu, "pocket". Avemu pensatu chì senza un picculu VPS in 2019 era in qualchì manera triste, cum'è senza a solita penna stilografica in una conferenza. Perchè esse tristi? Hè veranu. Com'è l'estate ? L'estate per un specialista in IT: pusatu in casa, travagliendu nantu à i vostri prughjetti preferiti senza alcunu rimpianti. In generale, avemu pensatu è fattu.
U cumunismu hè ghjuntu, camaradi.
Hè cusì - u nostru VPS per trenta
Avemu lettu assai articuli da i cuncurrenti è l'utilizatori chì anu scrittu 3-4 anni fà per quessa chì un VPS di prezzu ùn hè micca necessariu. Ebbè, hè ghjustu, allora VPS "per un centesimu" era marketing pura è ùn pudia micca offre opportunità di travagliu normale. Ma i tempi cambianu, u costu di risorse virtuali hè diventatu più bassu è più bassu, è per 30 rubles à u mese simu pronti à offre questu:
Processore: Intel Xeon 2 GHz (1 core)
Sistema Linux (Debian, Ubuntu, CentOS da sceglie)
1 indirizzu IPv4 dedicatu
10 GB di almacenamiento di dati nantu à unità SSD veloci di classe impresa
RAM: 512 MB
Per seconda fattura
Traffico illimitatu
A tarifa hè sottumessa à restrizioni tecniche supplementari, dettagli nantu pagina a nostra offerta cool - VPS per 30 rubles.
À quale hè adattatu stu servitore virtuale? Iè à quasi tutti: principianti, dilettanti, sviluppatori sperimentati, fan di DIY è ancu alcune cumpagnie.
A cosa hè adattatu stu VPS?
Pensemu chì i lettori di Habr anu da truvà u so propiu modu d'utilizà sta cunfigurazione, ma avemu decisu di cullà a nostra propria selezzione di idee - chì si qualchissia hà bisognu, ma l'omi ùn sanu micca?
Pone u vostru situ web simplice, cartera, curriculum vitae cù codice, etc. Di sicuru, u vostru propiu situ web cuncepitu face una impressione positiva nantu à u patronu. Pone lu in u vostru VPS è esse rispunsevuli di a sicurità è a stabilità di u situ stessu, è micca da u persunale di i fornituri di hosting regulare.
Aduprate VPS per scopi educativi: ospitate u vostru prughjettu, studià e caratteristiche di u servitore è u sistema operatore di u servitore, sperimentate cù DNS, tinker cun un picculu situ educativu.
Per a telefonia. Calchì volta un entrepreneur individuale, freelancer o una sucietà assai chjuca hà bisognu disperatamente di telefonia IP, è l'operatori di questa telefonia sò assai avari. Hè simplice: pigliamu u nostru servitore, cumprà un numeru da un operatore di telefonia IP, stallate un PBX virtuale è creanu numeri internu (se necessariu). U risparmiu hè colossali.
Aduprate u servitore per pruvà e vostre applicazioni.
Aduprate u servitore per esperimenti DIY, cumpresu u cuntrollu è a cullizzioni di dati da i sensori di u sistema di casa intelligente.
Un modu inusual di usà hè di mette un assistente di scambiu virtuale, un robot di cummerciale, nantu à u servitore. Serete pienamente rispunsevuli di a stabilità è a sicurità di u servitore, chì significa chì riceverete un strumentu cuntrullatu per u cummerciu in i mercati di borsa. Ebbè, in casu chì qualchissia hè interessatu o pianificà :)
Ci sò applicazioni per tali VPS in l'esfera corporativa. In più di u serviziu telefuninu digià citatu, pudete implementà parechje cose interessanti. Per esempiu:
Pone picculi basa di dati è informazioni chì saranu accessibili à l'impiegati chì viaghjanu à distanza, per esempiu, usendu ftp. Questu permetterà di scambià assai rapidamente analitiche fresche, cunfigurazioni aghjurnate per i venditori, presentazioni, etc.
Dà un accessu tempurale à l'utilizatori o clienti per dimustrà software o media.
Test drive VPS per 30 rubli - fattu per voi
30 rubles hè cusì pocu chì ùn vulete mancu piglià una carta per pagà è pruvà. A volte simu cusì pigri ancu, ma sta volta avemu fattu tuttu per voi. Prima di lancià i servitori in battaglia, avemu fattu una prova per verificà tutti i dettagli è dimustrà ciò chì i servitori sò capaci di sta tarifa. Per fà più interessante, avemu aghjustatu estremu è verificatu cumu si comporta sta cunfigurazione se a densità è a carica superavanu i valori chì avemu stabilitu.
L'ospitu era sottu à a carica di una quantità di macchine virtuali chì anu realizatu diversi travaglii nantu à u processatore è attivamente utilizatu u sottosistema di discu. L'obiettivu hè di simulà una alta densità di piazzamentu è una carica paragunabile o più grande di una di cummattimentu.
In più di a carica constante, avemu installatu 3 macchine virtuali chì cullighjanu metriche sintetiche cù sysbench, i risultati mediu di quale sò stati dati quì sottu, è 50 macchine virtuali chì creanu una carica supplementaria. Tutte e macchine virtuali di prova avianu a listessa cunfigurazione (1 core, RAM 512 GB, SSD 10 GB), l'imagine standard di Debian 9.6 hè stata scelta cum'è u sistema operatore, chì hè offertu à l'utilizatori in RUVDS.
A carica hè stata simulata in natura è magnitudine paragunabili à cumbattimentu:
Alcune macchine virtuali sò state lanciate cù una carica bassa
Alcune macchine eseguiu un script di prova chì simulava a carica nantu à u processatore (usendu l'utilità ddoi)
Nant'à a parte restante di e macchine virtuali, avemu eseguitu un script chì usava dd per copià e dati da dati pre-preparati à u discu cù un limitu stabilitu cù pv (esempii ponu vede ccà и ccà).
In ogni macchina, un script hè statu eseguitu ciclicamente ogni 15 minuti, chì esegue testi standard di sysbench per u processatore, a memoria è u discu.
Script 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
I risultati sò presentati per comodità in u formatu sysbench, ma i valori medii per tuttu u periodu di prova sò stati pigliati da tutte e macchine, u risultatu pò esse vistu quì:
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
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
I risultati sò indicativi, ma ancu ùn deve esse pigliatu cum'è QoS.
A basa di dati hè implementata cum'è specificata ccà:
mysql -t < employees.sql
mysql -t < test_employees_sha.sql
Piccola basa di prova:
Table
RowsCount
Dimensione di dati (MB)
Taille de l'index (KB)
dipartimenti
9
0.02
16.00
dipartimentu_emp
331143
11.52
5648.00
dept_manager
24
0.02
16.00
Dipoi sempre
299379
14.52
0.00
stipendi
2838426
95.63
0.00
tituli
442783
19.56
0.00
Un serviziu di teste primitivu hè scrittu annantu à u ghjinochju in Python; esegue quattru operazioni:
getState: torna u statutu
getEmployee: torna l'impiegati (+salari, +tituli) da a basa di dati
patchEmployee: cambia i campi di l'impiegati
insertSalary : inserisce un salariu
Fonte di serviziu (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')
Attenzione! In nisun casu, stu serviziu ùn deve esse pigliatu cum'è un esempiu o una guida!
I testi sò realizati cù un bonu vechju JMeter. Una seria di teste chì duranu da 15 minuti à 2 ore sò state lanciate, senza interruzioni, u percentualità di richieste variava, è u throughput variava da 300 à 600 richieste per minutu. Numero di fili da 50 à 500.
A causa di u fattu chì a basa di dati hè assai chjuca, u cumandimu:
mysql -e "SHOW ENGINE INNODB STATUS"
Mostra chì:
Buffer pool hit rate 923 / 1000, young-making rate 29 / 1000 not 32 / 1000
Quì sottu sò i tempi medii di risposta per e dumande:
Etichette
Average
Mediu
90% Linea
95% Linea
99% Linea
Min
Max
get Employee
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
patch Impiegatu
161.42
83.29
308
492.57
1845.14
5
6639.4
mette Salariu
167.21
86.93
315.34
501.07
1927.12
7
6722.44
Pò esse difficiule per voi di ghjudicà da questi risultati sintetici quantu hè adattatu questu VPS per i vostri compiti specifichi è, in generale, i metudi listati sò limitati à quelli casi chì avemu avutu à trattà in una forma o l'altru. chjaramente micca esaustivu. Vi invitemu à piglià e vostre cunclusioni è pruvà u servitore per 30 rubles nantu à e vostre applicazioni è e vostre attività reali è suggerisce e vostre opzioni per questa cunfigurazione in i cumenti.