Este atât de frumos când toate lucrurile mici necesare sunt la îndemână: un pix și un bloc de note bune, un creion ascuțit, un mouse confortabil, câteva fire suplimentare etc. Aceste lucruri discrete nu atrag atenția, dar adaugă confort vieții. Aceeași poveste este cu diverse aplicații mobile și desktop: pentru capturi de ecran lungi, pentru reducerea dimensiunii unei imagini, pentru calcularea finanțelor personale, dicționare, traducători, convertoare etc. Ai una? VPS - care este ieftin, mereu la îndemână și aduce o mulțime de beneficii? Nu, nu pe cel pe care îl ai în compania ta, ci pe cel „de buzunar”. Ne-am gândit că fără un VPS mic în 2019 a fost oarecum trist, la fel ca fără stiloul obișnuit la o prelegere. De ce să fii trist? Este vară. Cum e vara? Vară pentru un specialist IT: stând acasă, lucrând la proiectele tale preferate fără niciun regret. În general, ne-am gândit și am făcut-o.
A sosit comunismul, tovarăși.
El este așa - VPS-ul nostru pentru treizeci
Am citit o mulțime de articole de la concurenți și utilizatori care au scris acum 3-4 ani despre de ce nu este nevoie de un VPS ieftin. Ei bine, așa este, atunci VPS „pentru un ban” era marketing pur și nu putea oferi oportunități normale de lucru. Dar vremurile se schimbă, costul resurselor virtuale devine din ce în ce mai mic, iar pentru 30 de ruble pe lună suntem gata să oferim acest lucru:
Procesor: Intel Xeon 2 GHz (1 nucleu)
Sistem Linux (Debian, Ubuntu, CentOS din care să alegeți)
1 adresă IPv4 dedicată
10 GB de stocare de date pe unități SSD rapide de clasă enterprise
RAM: 512 MB
Facturare pe secundă
Trafic nelimitat
Tariful este supus unor restricții tehnice suplimentare, detalii pe pagină oferta noastră cool - VPS pentru 30 de ruble.
Pentru cine este potrivit acest server virtual? Da pentru aproape toată lumea: începători, entuziaști, dezvoltatori experimentați, fani de bricolaj și chiar unele companii.
Pentru ce este potrivit acest VPS?
Credem că cititorii lui Habr își vor găsi cu siguranță propriul mod de a folosi această configurație, dar am decis să colectăm propria noastră selecție de idei - ce se întâmplă dacă cineva are nevoie de ea, dar bărbații nu știu?
Plasați site-ul dvs. simplu, portofoliul, CV-ul cu cod etc. Desigur, site-ul dvs. web creat face o impresie pozitivă asupra angajatorului. Plasați-l pe VPS-ul dvs. și fiți responsabil pentru securitatea și stabilitatea site-ului dvs., și nu de către personalul furnizorilor obișnuiți de găzduire.
Folosiți VPS în scopuri educaționale: găzduiți proiectul dvs., studiați caracteristicile sistemului de operare server și server, experimentați cu DNS, reparați cu un mic site educațional.
Pentru telefonie. Uneori, un antreprenor individual, freelancer sau o companie foarte mică are nevoie disperată de telefonie IP, iar operatorii chiar ai acestei telefonie sunt foarte lacomi. Este simplu: ne luăm serverul, cumpărăm un număr de la un operator de telefonie IP, instalăm un PBX virtual și creăm numere interne (dacă este necesar). Economiile sunt colosale.
Utilizați serverul pentru a vă testa aplicațiile.
Utilizați serverul pentru experimente DIY, inclusiv controlul și colectarea datelor de la senzorii sistemului de casă inteligentă.
O modalitate neobișnuită de a-l folosi este să plasați pe server un asistent de tranzacționare de schimb virtual, un robot de tranzacționare. Veți fi pe deplin responsabil pentru stabilitatea și securitatea serverului, ceea ce înseamnă că veți primi un instrument controlat pentru tranzacționarea pe bursele. Ei bine, în cazul în care cineva este interesat sau plănuiește :)
Există aplicații pentru astfel de VPS în sfera corporativă. Pe lângă serviciul telefonic deja menționat, puteți implementa mai multe lucruri interesante. De exemplu:
Plasați baze de date mici și informații care vor fi accesibile angajaților care călătoresc la distanță, de exemplu, folosind ftp. Acest lucru vă va permite să faceți schimb foarte rapid de analize proaspete, configurații actualizate pentru oamenii de vânzări, prezentări etc.
Oferiți acces temporar utilizatorilor sau clienților pentru a demonstra software-ul sau media.
Test drive VPS pentru 30 de ruble - gata pentru tine
30 de ruble sunt atât de puține încât nici măcar nu vrei să scoți un card pentru a plăti și a testa. Uneori suntem și noi atât de leneși, dar de data aceasta am făcut totul pentru tine. Înainte de a lansa serverele în luptă, am efectuat un test pentru a verifica toate detaliile și a arăta de ce sunt capabile serverele la acest tarif. Pentru a o face mai interesantă, am adăugat extrem și am verificat cum s-ar comporta această configurație dacă densitatea și sarcina ar depăși valorile pe care le-am stabilit.
Gazda se afla sub sarcina unui număr de mașini virtuale care executau diverse sarcini pe procesor și foloseau activ subsistemul de disc. Scopul este de a simula o densitate mare de plasare și o sarcină comparabilă sau mai mare decât cea de luptă.
Pe lângă încărcarea constantă, am instalat 3 mașini virtuale care au colectat valori sintetice folosind sysbench, ale căror rezultate medii au fost date mai jos și 50 de mașini virtuale care au creat încărcare suplimentară. Toate mașinile virtuale de testare au avut aceeași configurație (1 nucleu, RAM 512 GB, SSD 10 GB), imaginea standard debian 9.6 a fost selectată ca sistem de operare, care este oferită utilizatorilor pe RUVDS.
Sarcina a fost simulată ca natură și amploare comparabilă cu cea a luptei:
Unele mașini virtuale au fost lansate cu încărcare redusă
Unele mașini au rulat un script de testare care simulează încărcarea procesorului (folosind utilitarul stres)
Pe partea rămasă a mașinilor virtuale, am rulat un script care a folosit dd pentru a copia datele de pe date pre-preparate pe disc cu o limită setată folosind pv (pot fi văzute exemple aici и aici).
De asemenea, după cum vă amintiți, aveam trei mașini care colectau valori sintetice.
Pe fiecare mașină, a fost executat ciclic un script la fiecare 15 minute, care rulează teste standard sysbench pentru procesor, memorie și disc.
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
Rezultatele sunt prezentate pentru comoditate în format sysbench, dar valorile medii pentru întreaga perioadă de testare au fost luate de la toate mașinile, rezultatul poate fi văzut aici:
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
Rezultatele sunt orientative, dar totuși nu trebuie luate ca QoS.
Baza de date este implementată conform specificațiilor aici:
mysql -t < employees.sql
mysql -t < test_employees_sha.sql
Baza de testare mica:
Tabel
RowsCount
Dimensiunea datelor (MB)
Dimensiunea indexului (KB)
departamente
9
0.02
16.00
dept_emp
331143
11.52
5648.00
dept_manager
24
0.02
16.00
de angajați
299379
14.52
0.00
salarii
2838426
95.63
0.00
titluri
442783
19.56
0.00
Un serviciu de testare primitiv este scris pe genunchi în Python; efectuează patru operații:
getState: returnează starea
getEmployee: returnează angajații (+salarii, +titluri) din baza de date
patchEmployee: modifică câmpurile angajaților
insertSalary: introduce un salariu
Sursa serviciului (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')
Atenție! În niciun caz acest serviciu nu trebuie luat ca exemplu sau ghid!
Testele sunt efectuate folosind vechiul JMeter. Au fost lansate o serie de teste cu o durată de la 15 minute până la 2 ore, fără întreruperi, procentul de solicitări a variat, iar debitul a variat de la 300 la 600 de solicitări pe minut. Număr de fire de la 50 la 500.
Datorită faptului că baza de date este foarte mică, comanda:
mysql -e "SHOW ENGINE INNODB STATUS"
Arată că:
Buffer pool hit rate 923 / 1000, young-making rate 29 / 1000 not 32 / 1000
Mai jos sunt timpii medii de răspuns pentru solicitări:
Etichetă
In medie
Median
Linie 90%.
Linie 95%.
Linie 99%.
Min
max
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
patchAngajat
161.42
83.29
308
492.57
1845.14
5
6639.4
puneSalariu
167.21
86.93
315.34
501.07
1927.12
7
6722.44
Poate fi dificil pentru tine să judeci din aceste rezultate sintetice cât de potrivit este acest VPS pentru sarcinile tale specifice și, în general, metodele enumerate sunt limitate la acele cazuri cu care a trebuit să ne confruntăm într-o formă sau alta. Deci lista noastră este clar nu exhaustiv. Vă invităm să trageți propriile concluzii și să testați serverul pentru 30 de ruble pe aplicațiile și sarcinile dvs. reale și să vă sugerăm opțiunile pentru această configurație în comentarii.