Brain + VPS với giá 30 rúp =?

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

Brain + VPS với giá 30 rúp =?
Коммунизм наступил, товарищи

Он такой свой — наш VPS за тридцатку

Мы начитались статей конкурентов и пользователей, которые 3-4 года назад писали о том, почему не нужен недорогой VPS. Ну правильно, тогда VPS «за копейки» был чистой воды маркетингом и не мог предложить нормальные рабочие возможности. Но времена меняются, стоимость виртуальных ресурсов становится всё ниже и за 30 рублей в месяц мы готовы предложить вот что:

  • Процессор: Intel Xeon 2 ГГц (1 ядро)
  • Система Linux (Debian, Ubuntu, CentOS на выбор)
  • 1 выделенный IPv4-адрес
  • 10 Гб для хранения данных на быстрых SSD-дисках корпоративного класса
  • RAM: 512MB
  • Посекундная тарификация
  • lưu lượng truy cập không giới hạn

На тариф действуют дополнительные технические ограничения, подробности на trang нашего классного предложения — 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.

Нагрузку имитировали по характеру и величине сопоставимую с боевой:

  • Часть виртуальных машин были запущены с низкой нагрузкой
  • Часть машин «крутили» тестовый сценарий, имитирующий нагрузку на процессор (с использованием утилиты căng thẳng)
  • На оставшейся части виртуалок мы запустили сценарий, использующий dd для копирования данных из заранее заготовленных данных на диск с ограничением, заданным с помощью pv (примеры можно посмотреть đây и đây).

Ещё, как вы помните, у нас были три машины, собирающие синтетические метрики.

На каждой машине циклически раз в 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.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

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. 

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

Phần mềm:

  • apt-get update
  • nâng cấp apt-get
  • apt-get cài đặt python-pip
  • pip install mysql-connector-python-rf

Установлен MariaDB, Как đây:

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

Тестовая база взята do đó:

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

mysql -t < employees.sql
mysql -t < test_employees_sha.sql

Тестовая база маленького объема:

Bàn 

RowsCount 

Data size (MB)

Index size (KB)

phòng ban 

9

0.02

16.00

dept_emp 

331143 

11.52

5648.00

dept_manager 

24 

0.02

16.00

nhân viên 

299379 

14.52

0.00

tiền lương 

2838426 

95.63

0.00 

trò chơi 

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')

Cảnh báo! Ни в коем случае не стоит воспринимать этот сервис как пример или пособие!

Тесты выполняются с помощью старого доброго 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

Ниже приведены усредненные значения времени ответа на запросы:

nhãn

Trung bình

trung tuyến

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 рублей на ваших реальных приложениях и задачах и предложить в комментариях свои варианты для такой конфигурации.

Nguồn: www.habr.com

Thêm một lời nhận xét