Как приятно, когда все необходимые мелочи под рукой: хорошо пишущая ручка и блокнот, заточенный карандаш, удобная мышка, пара лишних проводов и т.д. Эти незаметные штуки не обращают на себя внимания, но добавляют жизни комфорта. Такая же история с различными мобильными и десктопными приложениями: для длинных скриншотов, для уменьшения размера картинки, для подсчёта личных финансов, словари, переводчики, конверторы и т.д. А есть ли у вас такой VPS — который стоит недорого, всегда под рукой и приносит много пользы? Нет, не тот который у вас в компании, а свой, «карманный». Мы подумали, что без небольшого VPS в 2019 году как-то грустно, прямо как без привычной авторучки на лекции. А чего грустить? Лето же. Ну как лето. Лето айтишника: сидеть дома, пилить любимые проекты безо всякого сожаления. В общем, подумали и сделали.
Коммунизм наступил, товарищи
Он такой свой — наш VPS за тридцатку
Мы начитались статей конкурентов и пользователей, которые 3-4 года назад писали о том, почему не нужен недорогой VPS. Ну правильно, тогда VPS «за копейки» был чистой воды маркетингом и не мог предложить нормальные рабочие возможности. Но времена меняются, стоимость виртуальных ресурсов становится всё ниже и за 30 рублей в месяц мы готовы предложить вот что:
Процессор: Intel Xeon 2 ГГц (1 ядро)
Система Linux (Debian, Ubuntu, CentOS на выбор)
1 выделенный IPv4-адрес
10 Гб для хранения данных на быстрых SSD-дисках корпоративного класса
Оперативная память: 512 Мб
Посекундная тарификация
Безлимитный трафик
На тариф действуют дополнительные технические ограничения, подробности на странице нашего классного предложения — VPS за 30 рублей.
Кому подойдёт такой виртуальный сервер? Да почти всем: новичкам, энтузиастам, опытным разработчикам, DIY-фанатам и даже некоторым компаниям.
Для чего подходит такой VPS?
Мы думаем, читатели Хабра точно найдут свой способ применения такой конфигурации, но решили собрать и свою подборку идей — а то вдруг кому-то надо, а мужики и не знают?
Разместить свой несложный сайт, портфолио, резюме с кодом и проч. Конечно, собственный оформленный сайт производит на работодателя позитивное впечатление. Разместите его на своём VPS и отвечайте за безопасность и стабильность сайта самостоятельно, а не силами сотрудников обычных хостингов.
Использовать VPS для учебных целей: разместить свой проект, изучить особенности функционирования сервера и серверной операционной системы, поэкспериментировать с DNS, поковырять маленький учебный сайт.
Для телефонии. Иногда индивидуальному предпринимателю, фрилансеру или очень небольшой компании крайне нужна IP-телефония, а операторы этой самой телефонии весьма жадные. Всё просто: берём свой сервер, покупаем номер у оператора IP-телефонии, настраиваем виртуальную АТС и создаём внутренние номера (при необходимости). Экономия колоссальная.
Использовать сервер для тестирования своих приложений.
Использовать сервер для DIY-экспериментов, в том числе для управления и сбора данных с датчиков системы умного дома.
Необычный способ применения — разместить на сервере виртуального помощника биржевой торговли, торгового робота. Вы будете полностью отвечать за стабильность и безопасность сервера, а значит, получите подконтрольный инструмент для торгов на фондовы рынках. Ну вдруг кто увлекается или планирует :-)
Есть такому VPS применение и в корпоративной сфере. Кроме уже названного телефонного сервиса, можно реализовать несколько интересных штук. Например:
Размещать небольшие базы данных и информацию, которые будут доступны командировочным сотрудникам на расстоянии, например, с помощью ftp. Это позволит очень быстро обменяться свежей аналитикой, обновлёнными конфигурациями для продажников, презентацией и т.д.
Давать временный доступ пользователям или клиентам для демонстрации программного обеспечения или мультимедиа.
Тест-драйв VPS за 30 рублей — сделали за вас
30 рублей это настолько мало, что даже неохота доставать карточку, чтобы оплатить и потестить. Мы иногда тоже такие ленивые, но в этот раз сделали всё за вас. Перед запуском серверов в бой мы провели тест, чтобы проверить все детали и показать, на что способны серверы на данном тарифе. Чтобы было интереснее, мы добавили экстрима и проверили как себя поведёт эта конфигурация, если плотность и нагрузка будут превышать установленные нами значения.
Хост находился под нагрузкой некоторого количества виртуальных машин, которые выполняли различные задачи на процессоре и активно использовали дисковую подсистему. Цель — смоделировать высокую плотность размещения и нагрузку сопоставимую или большую, чем боевая.
Помимо постоянной нагрузки поставили 3 виртуальные машины, собирающие синтетические метрики с помощью sysbench, усредненные результаты которых привели ниже, и 50 виртуальных машин, которые создавали дополнительную нагрузку. Все тестовые виртуальные машины имели одинаковую конфигурацию (1 ядро, RAM 512 Гб, SSD 10Гб), в качестве операционной системы выбран стандартный образ debian 9.6, который предлагается пользователям на RUVDS.
Нагрузку имитировали по характеру и величине сопоставимую с боевой:
Часть виртуальных машин были запущены с низкой нагрузкой
Часть машин «крутили» тестовый сценарий, имитирующий нагрузку на процессор (с использованием утилиты stress)
На оставшейся части виртуалок мы запустили сценарий, использующий dd для копирования данных из заранее заготовленных данных на диск с ограничением, заданным с помощью pv (примеры можно посмотреть здесь и здесь).
Ещё, как вы помните, у нас были три машины, собирающие синтетические метрики.
На каждой машине циклически раз в 15 минут выполнялся скрипт, который запускает стандартные тесты sysbench для процессора, памяти и диска.
Cкрипт 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
Результаты приведены для удобства в формате sysbench'а, но взяты средние значения за всё время тестирования со всех машин, итог можно посмотреть здесь:
Sysbanch-avg.txt
sysbench 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
Результаты показательны, но все же не стоит их воспринимать как QoS.
mysql -t < employees.sql
mysql -t < test_employees_sha.sql
Тестовая база маленького объема:
Table
RowsCount
Data size (MB)
Index size (KB)
departments
9
0.02
16.00
dept_emp
331143
11.52
5648.00
dept_manager
24
0.02
16.00
employees
299379
14.52
0.00
salaries
2838426
95.63
0.00
titles
442783
19.56
0.00
Примитивный тестовый сервис написан на коленке на питоне, он выполняет четыре операции:
getState: возвращает статус
getEmployee: возвращает из базы employee (+salaries, +titles)
patchEmployee: изменяет поля employee
insertSalary: выполняет вставку salary
Исходник сервиса (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')
Внимание! Ни в коем случае не стоит воспринимать этот сервис как пример или пособие!
Тесты выполняются с помощью старого доброго JMeter. Был запущен ряд тестов длительностью от 15 минут до 2 часов, без перерывов, изменялось процентное соотношение запросов, пропускная способность варьировалась от 300 до 600 запросов в минуту. Количество потоков от 50 до 500.
В силу того, что база совсем маленького размера, команда:
mysql -e "SHOW ENGINE INNODB STATUS"
Показывает что:
Buffer pool hit rate 923 / 1000, young-making rate 29 / 1000 not 32 / 1000
Ниже приведены усредненные значения времени ответа на запросы:
Label
Average
Median
90% Line
95% Line
99% Line
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
patchEmployee
161.42
83.29
308
492.57
1845.14
5
6639.4
putSalary
167.21
86.93
315.34
501.07
1927.12
7
6722.44
Возможно вам будет сложно по данным синтетическим результатам судить о том, насколько подходит данный VPS под ваши конкретные задачи и в целом, перечисленные способы ограничиваются теми кейсами, с которыми нам приходилось в том или ином виде сталкиваться.Так что наш список явно не исчерпывающий. Предлагаем вам самим сделать выводы и протестировать сервер за 30 рублей на ваших реальных приложениях и задачах и предложить в комментариях свои варианты для такой конфигурации.