Es muy agradable tener a mano todas las pequeñas cosas necesarias: un buen bolígrafo y una libreta, un lápiz afilado, un ratón cómodo, un par de cables extra, etc. Estas cosas discretas no llaman la atención, pero añaden comodidad a la vida. Lo mismo ocurre con varias aplicaciones móviles y de escritorio: para capturas de pantalla largas, para reducir el tamaño de una imagen, para calcular finanzas personales, diccionarios, traductores, conversores, etc. ¿Tienes uno? VPS - ¿Qué es económico, está siempre a mano y aporta muchos beneficios? No, no el que tienes en tu empresa, sino el tuyo propio, “de bolsillo”. Pensamos que sin un pequeño VPS en 2019 sería algo triste, como sin la habitual pluma estilográfica en una conferencia. ¿Por qué estar triste? Es verano. ¿Cómo va el verano? Verano para un especialista en TI: sentado en casa, trabajando en tus proyectos favoritos sin ningún arrepentimiento. En general, lo pensamos y lo hicimos.
El comunismo ha llegado, camaradas.
Él es así: nuestro VPS por treinta.
Hemos leído muchos artículos de competidores y usuarios que escribieron hace 3 o 4 años sobre por qué no se necesita un VPS económico. Bueno, así es, entonces el VPS "por un centavo" era puro marketing y no podía ofrecer oportunidades laborales normales. Pero los tiempos están cambiando, el costo de los recursos virtuales es cada vez menor, y por 30 rublos al mes estamos listos para ofrecer esto:
Procesador: Intel Xeon 2 GHz (1 núcleo)
Sistema Linux (Debian, Ubuntu, CentOS para elegir)
1 dirección IPv4 dedicada
10 GB de almacenamiento de datos en unidades SSD rápidas de clase empresarial
RAM: 512 MB
Facturación por segundo
Tráfico ilimitado
La tarifa está sujeta a restricciones técnicas adicionales, detalles sobre página nuestra interesante oferta: VPS por 30 rublos.
¿Para quién es adecuado este servidor virtual? Sí a casi todo el mundo: principiantes, entusiastas, desarrolladores experimentados, aficionados al bricolaje e incluso algunas empresas.
¿Para qué es adecuado este VPS?
Creemos que los lectores de Habr definitivamente encontrarán su propia manera de usar esta configuración, pero decidimos recopilar nuestra propia selección de ideas: ¿qué pasa si alguien lo necesita, pero los hombres no lo saben?
Coloque su sitio web sencillo, portafolio, currículum con código, etc. Por supuesto, un sitio web diseñado por usted mismo causa una impresión positiva al empleador. Colóquelo en su VPS y sea responsable de la seguridad y estabilidad del sitio usted mismo, y no el personal de los proveedores de alojamiento habituales.
Utilice VPS con fines educativos: aloje su proyecto, estudie las características del servidor y el sistema operativo del servidor, experimente con DNS, juegue con un pequeño sitio educativo.
Para telefonía. A veces, un empresario individual, un autónomo o una empresa muy pequeña necesita desesperadamente telefonía IP, y los operadores de esta misma telefonía son muy codiciosos. Es muy sencillo: cogemos nuestro servidor, compramos un número a un operador de telefonía IP, configuramos una centralita virtual y creamos números internos (si es necesario). Los ahorros son colosales.
Utilice el servidor para probar sus aplicaciones.
Utilice el servidor para experimentos de bricolaje, incluido el control y la recopilación de datos de sensores de sistemas domésticos inteligentes.
Una forma inusual de utilizarlo es colocar un asistente de operaciones de intercambio virtual, un robot de operaciones, en el servidor. Usted será totalmente responsable de la estabilidad y seguridad del servidor, lo que significa que recibirá un instrumento controlado para operar en los mercados de valores. Bueno, por si alguien está interesado o planeando :)
Existen aplicaciones para este tipo de VPS en el ámbito corporativo. Además del servicio telefónico ya mencionado, puedes implementar varias cosas interesantes. Por ejemplo:
Coloque pequeñas bases de datos e información a la que puedan acceder los empleados que viajan a distancia, por ejemplo, mediante ftp. Esto le permitirá intercambiar muy rápidamente análisis nuevos, configuraciones actualizadas para el personal de ventas, presentaciones, etc.
Dar acceso temporal a usuarios o clientes para demostrar software o medios.
Prueba de manejo de VPS por 30 rublos: hecho para usted
30 rublos es tan poco que ni siquiera querrás sacar una tarjeta para pagar y realizar la prueba. A veces también somos muy vagos, pero esta vez hicimos todo por ti. Antes de lanzar los servidores a la batalla, realizamos una prueba para comprobar todos los detalles y mostrar de qué son capaces los servidores con esta tarifa. Para hacerlo más interesante, agregamos extremo y verificamos cómo se comportaría esta configuración si la densidad y la carga excedieran los valores que establecimos.
El host estaba bajo la carga de varias máquinas virtuales que realizaban diversas tareas en el procesador y utilizaban activamente el subsistema de disco. El objetivo es simular una alta densidad de colocación y una carga comparable o superior a la de combate.
Además de la carga constante, instalamos 3 máquinas virtuales que recopilaron métricas sintéticas usando sysbench, cuyos resultados promedio se detallan a continuación, y 50 máquinas virtuales que crearon una carga adicional. Todas las máquinas virtuales de prueba tenían la misma configuración (1 núcleo, RAM 512 GB, SSD 10 GB), se seleccionó la imagen estándar debian 9.6 como sistema operativo, que se ofrece a los usuarios en RUVDS.
La carga fue simulada en naturaleza y magnitud comparable al combate:
Algunas máquinas virtuales se iniciaron con poca carga.
Algunas máquinas ejecutaron un script de prueba que simulaba la carga en el procesador (usando la utilidad estrés)
En la parte restante de las máquinas virtuales, ejecutamos un script que usaba dd para copiar datos de datos preparados previamente al disco con un límite establecido usando pv (se pueden ver ejemplos aquí и aquí).
Además, como recordarán, teníamos tres máquinas que recopilaban métricas sintéticas.
En cada máquina, se ejecutó cíclicamente un script cada 15 minutos, que ejecuta pruebas de sysbench estándar para el procesador, la memoria y el disco.
Guión 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
Para mayor comodidad, los resultados se presentan en formato sysbench, pero los valores promedio para todo el período de prueba se tomaron de todas las máquinas; el resultado se puede 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
Los resultados son indicativos, pero aún así no deben tomarse como QoS.
La base de datos se implementa según lo especificado. aquí:
mysql -t < employees.sql
mysql -t < test_employees_sha.sql
Pequeña base de prueba:
Mesa
Número de filas
Tamaño de datos (MB)
Tamaño del índice (KB)
departamentos
9
0.02
16.00
departamento_emp
331143
11.52
5648.00
gerente_depto
24
0.02
16.00
personas
299379
14.52
0.00
salarios
2838426
95.63
0.00
títulos
442783
19.56
0.00
Un servicio de prueba primitivo está escrito en Python; realiza cuatro operaciones:
getState: devuelve el estado
getEmployee: devuelve empleados (+salarios, +títulos) de la base de datos
patchEmployee: cambia los campos de los empleados
insertSalary: inserta un salario
Fuente del servicio (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! ¡Bajo ninguna circunstancia se debe tomar este servicio como ejemplo o guía!
Las pruebas se realizan utilizando el viejo JMeter. Se lanzaron una serie de pruebas con una duración de 15 minutos a 2 horas, sin interrupciones, el porcentaje de solicitudes varió y el rendimiento varió de 300 a 600 solicitudes por minuto. Número de hilos de 50 a 500.
Debido a que la base de datos es muy pequeña, el comando:
mysql -e "SHOW ENGINE INNODB STATUS"
Muestra que:
Buffer pool hit rate 923 / 1000, young-making rate 29 / 1000 not 32 / 1000
A continuación se detallan los tiempos promedio de respuesta para las solicitudes:
Label
Normal
Mediana
Línea 90%
Línea 95%
Línea 99%
Min
Max
obtener empleado
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
ponerSalario
167.21
86.93
315.34
501.07
1927.12
7
6722.44
Puede resultarle difícil juzgar a partir de estos resultados sintéticos qué tan adecuado es este VPS para sus tareas específicas y, en general, los métodos enumerados se limitan a aquellos casos que tuvimos que abordar de una forma u otra. Por eso nuestra lista es claramente no exhaustivo. Lo invitamos a sacar sus propias conclusiones y probar el servidor por 30 rublos en sus aplicaciones y tareas reales y sugerirle sus opciones para esta configuración en los comentarios.