Мозг + VPS за 30 рублей =?

в 9:30, , рубрики: ruvds, vds для обучения, vps, VPS 30 рублей, Блог компании RUVDS.com, виртуализация, Серверное администрирование, хостинг

Как приятно, когда все необходимые мелочи под рукой: хорошо пишущая ручка и блокнот, заточенный карандаш, удобная мышка, пара лишних проводов и т.д. Эти незаметные штуки не обращают на себя внимания, но добавляют жизни комфорта. Такая же история с различными мобильными и десктопными приложениями: для длинных скриншотов, для уменьшения размера картинки, для подсчёта личных финансов, словари, переводчики, конверторы и т.д. А есть ли у вас такой VPS — который стоит недорого, всегда под рукой и приносит много пользы? Нет, не тот который у вас в компании, а свой, «карманный». Мы подумали, что без небольшого VPS в 2019 году как-то грустно, прямо как без привычной авторучки на лекции. А чего грустить? Лето же. Ну как лето. Лето айтишника: сидеть дома, пилить любимые проекты безо всякого сожаления. В общем, подумали и сделали.

Мозг + VPS за 30 рублей=? - 1
Коммунизм наступил, товарищи

Он такой свой — наш 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

Memory transfer size: 102400M

Memory operations type: write
Memory scope type: global
Threads started!
Done.

Operations performed: 104857600 (328001.79 ops/sec)

102400.00 MB transferred (320.32 MB/sec)

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. 

Машины создающие дополнительную нагрузку

Софт:

  • apt-get update
  • apt-get upgrade
  • apt-get install python-pip
  • pip install mysql-connector-python-rf

Установлен MariaDB, Как тут:

apt-get install libmariadbclient-dev
mysql -e "INSTALL PLUGIN blackhole SONAME 'ha_blackhole.so';" -- нужно для test_employees_sha

Тестовая база взята отсюда:

База развернута как указано тут:

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

Примитивный тестовый сервис написан на коленке на питоне, он выполняет четыре операции:

  1. getState: возвращает статус
  2. getEmployee: возвращает из базы employee (+salaries, +titles)
  3. patchEmployee: изменяет поля employee
  4. 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 рублей на ваших реальных приложениях и задачах и предложить в комментариях свои варианты для такой конфигурации.

Автор: ru_vds

Источник


* - обязательные к заполнению поля