Мозг + VPS за 30 рублёў =?

Як прыемна, калі ўсе неабходныя дробязі пад рукой: добра пішучая ручка і нататнік, заменчаны аловак, зручная мышка, пара лішніх правадоў і г.д. Гэтыя незаўважныя штукі не звяртаюць на сябе ўвагі, але дадаюць жыццю камфорту. Такая ж гісторыя з рознымі мабільнымі і дэсктопнымі праграмамі: для доўгіх скрыншотаў, для памяншэння памеру карцінкі, для падліку асабістых фінансаў, слоўнікі, перакладчыкі, канвертары і г.д. А ці ёсць у вас такі VPS - які каштуе нядорага, заўсёды пад рукой і прыносіць шмат карысці? Не, не той які ў вас у кампаніі, а свой, "кішэнны". Мы падумалі, што без невялікага VPS у 2019 годзе неяк сумна, проста як без звыклай аўтаручкі на лекцыі. А чаго сумаваць? Лета ж. Ну як лета. Лета айцішніка: сядзець дома, пілаваць любімыя праекты без усялякага шкадавання. Увогуле, падумалі і зрабілі.

Мозг + VPS за 30 рублёў =?
Камунізм наступіў, таварышы

Ён такі свой - наш 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.

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

  • Частка віртуальных машын былі запушчаны з нізкай нагрузкай
  • Частка машын «круцілі» тэставы сцэнар, які імітуе нагрузку на працэсар (з выкарыстаннем утыліты стрэс)
  • На пакінутай частцы віртуалак мы запусцілі сцэнар, які выкарыстоўвае 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.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. 

Машыны ствараюць дадатковую нагрузку

софт:

  • apt-get абнаўлення
  • павышэнне здольнасці
  • APT-атрымаць ўстаноўку пітона-Піп
  • 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

Тэставая база маленькага аб'ёму:

табліца 

RowsCount 

Data size (MB)

Index size (KB)

ведамства 

9

0.02

16.00

dept_emp 

331143 

11.52

5648.00

dept_manager 

24 

0.02

16.00

супрацоўнікаў 

299379 

14.52

0.00

заробкі 

2838426 

95.63

0.00 

назвы 

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

Ніжэй прыведзены асераднёныя значэнні часу адказу на запыты:

этыкетка

сярэдні

медыяна

90% Line

95% Line

99% Line

Мін

макс

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

Крыніца: habr.com

Дадаць каментар