Mózg + VPS za 30 rubli =?

Miło jest mieć pod ręką wszystkie niezbędne drobiazgi: dobry długopis i notatnik, zaostrzony ołówek, wygodną myszkę, kilka dodatkowych przewodów itp. Te niepozorne rzeczy nie przyciągają uwagi, a dodają komfortu życiu. Ta sama historia dotyczy różnych aplikacji mobilnych i stacjonarnych: do długich zrzutów ekranu, do zmniejszania rozmiaru obrazu, do obliczania finansów osobistych, słowników, tłumaczy, konwerterów itp. Czy mają Państwo taki sam? VPS - co jest niedrogie, zawsze pod ręką i niesie ze sobą wiele korzyści? Nie, nie ten, który masz w firmie, ale swój własny, „kieszonkowy”. Pomyśleliśmy, że bez małego VPS-a w 2019 roku będzie jakoś smutno, tak jak bez zwykłego wiecznego pióra na wykładzie. Dlaczego być smutnym? Jest lato. Jak lato? Lato dla informatyka: siedzenie w domu i praca nad ulubionymi projektami bez żalu. Ogólnie pomyśleliśmy i zrobiliśmy to.

Mózg + VPS za 30 rubli =?
Nadszedł komunizm, towarzysze.

On taki jest - nasz VPS na trzydziestkę

Przeczytaliśmy wiele artykułów konkurencji i użytkowników, którzy 3-4 lata temu pisali o tym, dlaczego niedrogi VPS nie jest potrzebny. No cóż, zgadza się, wtedy VPS „za grosze” był czystym marketingiem i nie mógł oferować normalnych możliwości pracy. Ale czasy się zmieniają, koszt zasobów wirtualnych staje się coraz niższy i za 30 rubli miesięcznie jesteśmy gotowi zaoferować to:

  • Procesor: Intel Xeon 2 GHz (1 rdzeń)
  • System Linux (do wyboru Debian, Ubuntu, CentOS)
  • 1 dedykowany adres IPv4
  • 10 GB miejsca na dane na szybkich dyskach SSD klasy korporacyjnej
  • Pamięć RAM: 512 MB
  • Naliczanie sekundowe
  • Nieograniczony ruch

Taryfa podlega dodatkowym ograniczeniom technicznym, szczegóły na stronie strona nasza fajna oferta - VPS za 30 rubli. 

Dla kogo jest odpowiedni ten serwer wirtualny? Tak dla prawie wszystkich: początkujących, entuzjastów, doświadczonych programistów, fanów majsterkowania, a nawet niektórych firm.

Do czego nadaje się ten VPS?

Uważamy, że czytelnicy Habra na pewno znajdą swój własny sposób na wykorzystanie tej konfiguracji, my jednak postanowiliśmy zebrać własny wybór pomysłów – a co jeśli ktoś tego potrzebuje, a panowie nie wiedzą?

  • Umieść swoją prostą stronę internetową, portfolio, CV z kodem itp. Oczywiście samodzielnie zaprojektowana strona internetowa robi pozytywne wrażenie na pracodawcy. Umieść go na swoim VPS i bądź odpowiedzialny za bezpieczeństwo i stabilność serwisu sam, a nie za personelem stałych dostawców hostingu.
  • Używaj VPS do celów edukacyjnych: hostuj swój projekt, przestudiuj funkcje serwera i systemu operacyjnego serwera, eksperymentuj z DNS, majsterkuj przy małej witrynie edukacyjnej.
  • Do telefonii. Czasami indywidualny przedsiębiorca, freelancer lub bardzo mała firma desperacko potrzebuje telefonii IP, a operatorzy tej właśnie telefonii są bardzo zachłanni. To proste: bierzemy nasz serwer, kupujemy numer od operatora telefonii IP, zakładamy wirtualną centralę i tworzymy numery wewnętrzne (jeśli zajdzie taka potrzeba). Oszczędności są ogromne.
  • Użyj serwera do testowania aplikacji.
  • Wykorzystaj serwer do eksperymentów DIY, obejmujących sterowanie i zbieranie danych z czujników systemu inteligentnego domu.
  • Niezwykłym sposobem wykorzystania jest umieszczenie na serwerze wirtualnego asystenta handlu giełdowego, czyli robota handlowego. Będziesz w pełni odpowiedzialny za stabilność i bezpieczeństwo serwera, co oznacza, że ​​otrzymasz kontrolowany instrument do handlu na giełdach. No cóż, gdyby ktoś był zainteresowany lub planował :)

Istnieją zastosowania dla takich VPS w sferze korporacyjnej. Oprócz wspomnianej już usługi telefonicznej można wdrożyć kilka ciekawych rzeczy. Na przykład:

  • Umieść małe bazy danych i informacje, które będą dostępne dla podróżujących pracowników na odległość, na przykład za pomocą FTP. Umożliwi to bardzo szybką wymianę świeżych analiz, zaktualizowanych konfiguracji dla sprzedawców, prezentacji itp.
  • Zapewnij tymczasowy dostęp użytkownikom lub klientom w celu zademonstrowania oprogramowania lub multimediów.

Jazda testowa VPS za 30 rubli - zrobiona dla Ciebie

30 rubli to tak mało, że nie chcesz nawet wyciągać karty, aby zapłacić i przetestować. My też czasami jesteśmy tacy leniwi, ale tym razem zrobiliśmy wszystko za Was. Przed wypuszczeniem serwerów do boju przeprowadziliśmy test, aby sprawdzić wszystkie szczegóły i pokazać, co potrafią serwery w tej taryfie. Żeby było ciekawiej dodaliśmy ekstremum i sprawdziliśmy jak zachowałaby się ta konfiguracja gdyby gęstość i obciążenie przekroczyły ustawione przez nas wartości. 

Host był obciążony szeregiem maszyn wirtualnych, które wykonywały różne zadania na procesorze i aktywnie korzystały z podsystemu dyskowego. Celem jest symulacja dużej gęstości rozmieszczenia i obciążenia porównywalnego lub większego niż obciążenie bojowe.

Oprócz stałego obciążenia zainstalowaliśmy 3 maszyny wirtualne, które zbierały syntetyczne metryki za pomocą sysbencha, których średnie wyniki podano poniżej, oraz 50 maszyn wirtualnych, które tworzyły dodatkowe obciążenie. Wszystkie testowe maszyny wirtualne miały tę samą konfigurację (1 rdzeń, RAM 512 GB, SSD 10 GB), jako system operacyjny wybrano standardowy obraz Debiana 9.6, który jest oferowany użytkownikom na RUVDS.

Symulowano obciążenie pod względem charakteru i wielkości porównywalnej do bojowej:

  • Niektóre maszyny wirtualne zostały uruchomione przy niskim obciążeniu
  • Niektóre maszyny uruchomiły skrypt testowy symulujący obciążenie procesora (za pomocą narzędzia stres)
  • Na pozostałej części maszyn wirtualnych uruchomiliśmy skrypt, który za pomocą dd kopiuje dane z wcześniej przygotowanych danych na dysk z limitem ustawionym za pomocą pv (przykłady można zobaczyć tutaj и tutaj).

Jak pamiętacie, mieliśmy trzy maszyny, które zbierały dane syntetyczne.

Na każdej maszynie cyklicznie co 15 minut wykonywany był skrypt uruchamiający standardowe testy sysbench dla procesora, pamięci i dysku.

Skrypt 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

Wyniki przedstawiono dla wygody w formacie sysbench, jednak ze wszystkich maszyn pobrano wartości średnie z całego okresu testowania, wynik można zobaczyć tutaj:

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

Wyniki mają charakter orientacyjny, ale nadal nie należy ich traktować jako QoS. 

Maszyny tworzące dodatkowe obciążenie

Oprogramowanie:

  • apt-get update
  • Aktualizacja apt-get
  • apt-get install python-pip
  • pip zainstaluj mysql-connector-python-rf

Zainstalowano MariaDB, jak to zrobić tutaj:

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

Baza testowa pobrana stąd:

Baza danych jest wdrażana zgodnie z opisem tutaj:

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

Mała baza testowa:

Stół 

Liczba wierszy 

Rozmiar danych (MB)

Rozmiar indeksu (KB)

Działy 

9

0.02

16.00

dział_emp 

331143 

11.52

5648.00

menedżer_działu 

24 

0.02

16.00

pracowników 

299379 

14.52

0.00

pensje 

2838426 

95.63

0.00 

tytuły 

442783 

19.56

0.00

Prymitywna usługa testowa jest napisana na kolanie w Pythonie i wykonuje cztery operacje:

  1. getState: zwraca status
  2. getEmployee: zwraca pracowników (+płace, +tytuły) z bazy danych
  3. patchEmployee: zmienia pola pracowników
  4. wstawSalary: wstawia wynagrodzenie

Źródło usługi (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')

Ostrzeżenie! W żadnym wypadku tej usługi nie należy traktować jako przykładu ani przewodnika!

Testy przeprowadzane są przy użyciu starego, dobrego JMeter. Uruchomiono serię testów trwających od 15 minut do 2 godzin, bez przerw, odsetek żądań był zróżnicowany, a przepustowość wahała się od 300 do 600 żądań na minutę. Liczba wątków od 50 do 500.

Z uwagi na to, że baza danych jest bardzo mała, komenda:

mysql -e "SHOW ENGINE INNODB STATUS"

Pokazuje, że:

Buffer pool hit rate 923 / 1000, young-making rate 29 / 1000 not 32 / 1000

Poniżej przedstawiono średni czas odpowiedzi na zapytania:

Etykieta

Średni

Mediana

90% linii

95% linii

99% linii

Min

Max

pobierz pracownika

37.64

12.57

62.28

128.5

497.57

5

4151.78

pobierz stan

17

7.57

30.14

58.71

193

3

2814.71

łatkaPracownik

161.42

83.29

308

492.57

1845.14

5

6639.4

umieścićWynagrodzenie

167.21

86.93

315.34

501.07

1927.12

7

6722.44

Na podstawie tych syntetycznych wyników może być trudno ocenić, czy ten VPS jest odpowiedni do Twoich konkretnych zadań, i ogólnie rzecz biorąc, wymienione metody ograniczają się do tych przypadków, z którymi musieliśmy sobie poradzić w takiej czy innej formie. Dlatego nasza lista jest wyraźnie nie wyczerpujące. Zapraszamy do wyciągnięcia własnych wniosków i przetestowania serwera za 30 rubli na rzeczywistych aplikacjach i zadaniach oraz zaproponowania w komentarzach opcji tej konfiguracji.

Źródło: www.habr.com

Dodaj komentarz