É tan bonito cando hai todas as pequenas cousas necesarias a man: un bo bolígrafo e un bo bloc de notas, un lapis afiado, un rato cómodo, un par de cables extra, etc. Estas cousas discretas non chaman a atención, pero engaden confort á vida. A mesma historia ocorre con varias aplicacións móbiles e de escritorio: para capturas de pantalla longas, para reducir o tamaño dunha imaxe, para calcular finanzas persoais, dicionarios, tradutores, conversores, etc. Tes un coma este? Estudantes - que é barato, sempre a man e trae moitos beneficios? Non, non a que tes na túa empresa, senón a túa propia, de “peto”. Pensamos que sen un pequeno VPS en 2019 era algo triste, igual que sen a pluma estilográfica habitual nunha conferencia. Por que estar triste? É verán. Que tal o verán? Verán para un especialista en informática: sentado na casa, traballando nos teus proxectos favoritos sen ningún arrepentimento. En xeral, pensamos e fixémolo.
O comunismo chegou, compañeiros.
El é así: o noso VPS para trinta
Lemos moitos artigos de competidores e usuarios que escribiron hai 3-4 anos sobre por que non se necesita un VPS barato. Ben, iso é certo, entón VPS "por un centavo" era puro marketing e non podía ofrecer oportunidades laborais normais. Pero os tempos están cambiando, o custo dos recursos virtuais é cada vez máis baixo e por 30 rublos ao mes estamos preparados para ofrecer isto:
Procesador: Intel Xeon 2 GHz (1 núcleo)
Sistema Linux (Debian, Ubuntu, CentOS para escoller)
1 enderezo IPv4 dedicado
10 GB de almacenamento de datos en unidades SSD rápidas de clase empresarial
Memoria RAM: 512 MB
Facturación por segundo
Tráfico ilimitado
A tarifa está suxeita a restricións técnicas adicionais páxina a nosa oferta xenial - VPS por 30 rublos.
Para quen é axeitado este servidor virtual? Si para case todos: principiantes, entusiastas, desenvolvedores experimentados, fans do bricolaxe e incluso algunhas empresas.
Para que é axeitado este VPS?
Pensamos que os lectores de Habr definitivamente atoparán o seu propio xeito de usar esta configuración, pero decidimos recoller a nosa propia selección de ideas: e se alguén o necesita, pero os homes non o saben?
Coloca o teu sitio web sinxelo, carteira, currículo con código, etc. Por suposto, o teu propio sitio web deseñado fai unha impresión positiva no empresario. Colócao no teu VPS e sexa responsable da seguridade e estabilidade do sitio, e non polo persoal dos provedores de hospedaxe habituais.
Use VPS con fins educativos: aloxe o seu proxecto, estude as características do servidor e do sistema operativo do servidor, experimente con DNS, retoque cun pequeno sitio educativo.
Para telefonía. Ás veces, un empresario individual, autónomo ou unha empresa moi pequena necesita desesperadamente a telefonía IP, e os operadores desta mesma telefonía son moi codiciosos. É sinxelo: collemos o noso servidor, compramos un número a un operador de telefonía IP, configuramos unha central virtual e creamos números internos (se é necesario). O aforro é enorme.
Use o servidor para probar as súas aplicacións.
Use o servidor para experimentos de bricolaxe, incluído o control e a recollida de datos dos sensores do sistema doméstico intelixente.
Unha forma inusual de usalo é colocar un asistente de negociación de intercambio virtual, un robot comercial, no servidor. Serás totalmente responsable da estabilidade e seguridade do servidor, o que significa que recibirás un instrumento controlado para negociar nas bolsas. Pois por se alguén está interesado ou planeando :)
Existen aplicacións para este tipo de VPS no ámbito corporativo. Ademais do servizo telefónico xa mencionado, podes implementar varias cousas interesantes. Por exemplo:
Coloque pequenas bases de datos e información que serán accesibles para os empregados que viaxan a distancia, por exemplo, mediante ftp. Isto permitirache intercambiar moi rapidamente análises novas, configuracións actualizadas para vendedores, presentacións, etc.
Dar acceso temporal aos usuarios ou clientes para demostrar software ou medios.
Test drive VPS por 30 rublos - feito por ti
30 rublos son tan poucos que nin sequera queres sacar unha tarxeta para pagar e probar. Ás veces tamén somos tan preguiceiros, pero esta vez fixemos todo por ti. Antes de lanzar os servidores á batalla, realizamos unha proba para comprobar todos os detalles e mostrar de que son capaces os servidores a esta tarifa. Para facelo máis interesante, engadimos extremo e comprobamos como se comportaría esta configuración se a densidade e a carga superasen os valores que establecemos.
O host estaba baixo a carga dunha serie de máquinas virtuais que realizaban varias tarefas no procesador e usaban activamente o subsistema de disco. O obxectivo é simular unha alta densidade de colocación e unha carga comparable ou superior a unha de combate.
Ademais da carga constante, instalamos 3 máquinas virtuais que recolleron métricas sintéticas mediante sysbench, cuxos resultados medios se indican a continuación, e 50 máquinas virtuais que crearon carga adicional. Todas as máquinas virtuais de proba tiñan a mesma configuración (1 núcleo, RAM 512 GB, SSD 10 GB), seleccionouse a imaxe estándar debian 9.6 como sistema operativo, que se ofrece aos usuarios en RUVDS.
A carga foi simulada en natureza e magnitude comparable ao combate:
Algunhas máquinas virtuais lanzáronse con pouca carga
Algunhas máquinas executaron un script de proba que simulaba a carga do procesador (usando a utilidade estrés)
Na parte restante das máquinas virtuais, executamos un script que usaba dd para copiar datos de datos previamente preparados ao disco cun límite establecido usando pv (pódense ver exemplos). aquí и aquí).
Ademais, como lembras, tiñamos tres máquinas que recollían métricas sintéticas.
En cada máquina, executouse cíclicamente un script cada 15 minutos, que executa probas estándar de sysbench para o procesador, a memoria e o disco.
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
Os resultados preséntanse por comodidade en formato sysbench, pero os valores medios para todo o período de proba foron tomados de todas as máquinas, o resultado pódese ver aquí:
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
Os resultados son indicativos, pero aínda non se deben tomar como QoS.
A base de datos está implantada como se especifica aquí:
mysql -t < employees.sql
mysql -t < test_employees_sha.sql
Base de proba pequena:
Táboa
Conta de filas
Tamaño dos datos (MB)
Tamaño do índice (KB)
departamentos
9
0.02
16.00
departamento_emp
331143
11.52
5648.00
xerente_departamento
24
0.02
16.00
funcionarios
299379
14.52
0.00
salarios
2838426
95.63
0.00
títulos
442783
19.56
0.00
Un servizo de proba primitivo está escrito no xeonllo en Python; realiza catro operacións:
getState: devolve o estado
getEmployee: devolve os empregados (+salarios, +títulos) da base de datos
patchEmployee: cambia os campos dos empregados
inserirSalario: insire un salario
Orixe do servizo (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')
Atención! En ningún caso este servizo debe ser tomado como exemplo ou guía!
As probas realízanse usando un bo JMeter antigo. Púxose en marcha unha serie de probas de 15 minutos a 2 horas, sen interrupcións, a porcentaxe de solicitudes variou e o rendemento de 300 a 600 solicitudes por minuto. Número de fíos de 50 a 500.
Debido ao feito de que a base de datos é moi pequena, o comando:
mysql -e "SHOW ENGINE INNODB STATUS"
Mostra que:
Buffer pool hit rate 923 / 1000, young-making rate 29 / 1000 not 32 / 1000
Abaixo amósanse os tempos medios de resposta ás solicitudes:
etiqueta
media
Mediana
90% Liña
95% Liña
99% Liña
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
parcheEmpleado
161.42
83.29
308
492.57
1845.14
5
6639.4
poñerSalario
167.21
86.93
315.34
501.07
1927.12
7
6722.44
Pode ser difícil para vostede xulgar a partir destes resultados sintéticos o axeitado que é este VPS para as súas tarefas específicas e, en xeral, os métodos enumerados limítanse a aqueles casos que tivemos que tratar dunha forma ou outra. Polo que a nosa lista é claramente non exhaustivo. Invitámosche a sacar as túas propias conclusións e probar o servidor por 30 rublos nas túas aplicacións e tarefas reais e suxerir as túas opcións para esta configuración nos comentarios.