Cerveau + VPS pour 30 roubles =?

C'est bien quand toutes les petites choses nécessaires sont à portée de main: un stylo et un cahier bien écrit, un crayon aiguisé, une souris confortable, quelques fils supplémentaires, etc. Ces choses discrètes n'attirent pas l'attention, mais ajoutent une vie de confort. La même histoire avec diverses applications mobiles et de bureau: pour de longues captures d'écran, pour réduire la taille de l'image, pour calculer les finances personnelles, les dictionnaires, les traducteurs, les convertisseurs, etc. Avez-vous un tel VPS - qui est peu coûteux, toujours à portée de main et apporte de nombreux avantages? Non, pas celui que vous avez dans votre entreprise, mais votre propre "poche". Nous pensions que sans un petit VPS en 2019, c'était en quelque sorte triste, tout comme sans le stylo plume habituel lors d'une conférence. Pourquoi être triste? L'été est. Eh bien, comme l'été. IT summer: asseyez-vous à la maison, coupez vos projets préférés sans aucun regret. En général, pensé et fait.


Le communisme est venu, camarades

Il est le sien - notre VPS depuis trente


Nous avons lu des articles de concurrents et d'utilisateurs qui ont écrit il y a 3 ou 4 ans pourquoi les VPS bon marché n'étaient pas nécessaires. Eh bien, oui, alors VPS "pour un sou" était du marketing pur et ne pouvait pas offrir des opportunités de travail normales. Mais les temps changent, le coût des ressources virtuelles diminue et pour 30 roubles par mois, nous sommes prêts à offrir ceci:

  • Processeur: Intel Xeon 2 GHz (1 cĹ“ur)
  • Système Linux (Debian, Ubuntu, CentOS au choix)
  • 1 adresse IPv4 dĂ©diĂ©e
  • 10 Go pour le stockage de donnĂ©es sur des SSD rapides de classe entreprise
  • RAM: 512 Mo
  • Par seconde facturation
  • Trafic illimitĂ©

Le tarif est soumis à des restrictions techniques supplémentaires, détails sur la page de notre offre cool - VPS pour 30 roubles.

Qui devrait utiliser un tel serveur virtuel? Oui, presque tout le monde: débutants, passionnés, développeurs expérimentés, fans de bricolage et même certaines entreprises.

Ă€ quoi ce VPS convient-il?


Nous pensons que les lecteurs de Habr trouveront certainement leur propre façon d'appliquer une telle configuration, mais ils ont décidé de mettre en place leur propre sélection d'idées - si tout à coup quelqu'un en a besoin, mais les hommes ne savent pas?

  • Publiez votre site simple, votre portfolio, votre CV avec du code, etc. Bien sĂ»r, son propre site Web conçu fait une impression positive sur l'employeur. Placez-le sur votre VPS et soyez seul responsable de la sĂ©curitĂ© et de la stabilitĂ© du site, et non du personnel de l'hĂ©bergement mutualisĂ©.
  • Utilisez VPS Ă  des fins Ă©ducatives: publiez votre projet, Ă©tudiez les fonctionnalitĂ©s du serveur et du système d'exploitation du serveur, expĂ©rimentez le DNS, creusez un petit site de formation.
  • Pour la tĂ©lĂ©phonie. Parfois, un entrepreneur individuel, un pigiste ou une très petite entreprise a dĂ©sespĂ©rĂ©ment besoin de la tĂ©lĂ©phonie IP, et les opĂ©rateurs de cette tĂ©lĂ©phonie sont très gourmands. C'est simple: nous prenons notre serveur, achetons un numĂ©ro Ă  l'opĂ©rateur de tĂ©lĂ©phonie IP, mettons en place un PBX virtuel et crĂ©ons des numĂ©ros internes (si nĂ©cessaire). Les Ă©conomies sont colossales.
  • Utilisez un serveur pour tester vos applications.
  • Utilisez le serveur pour des expĂ©riences de bricolage, notamment pour gĂ©rer et collecter des donnĂ©es Ă  partir des capteurs d'un système de maison intelligente.
  • Une mĂ©thode d'application inhabituelle consiste Ă  placer sur le serveur un assistant virtuel de trading, un robot de trading. Vous serez entièrement responsable de la stabilitĂ© et de la sĂ©curitĂ© du serveur, ce qui signifie que vous obtiendrez un instrument contrĂ´lĂ© pour la nĂ©gociation sur les marchĂ©s boursiers. Eh bien, tout Ă  coup, quelqu'un aime ou planifie :-)

Il existe une telle application VPS dans le domaine de l'entreprise. En plus du service téléphonique déjà mentionné, vous pouvez mettre en œuvre plusieurs pièces intéressantes. Par exemple:

  • Pour placer de petites bases de donnĂ©es et informations qui seront disponibles pour les employĂ©s de voyage Ă  distance, par exemple, en utilisant ftp. Cela vous permettra d'Ă©changer rapidement de nouvelles analyses, des configurations mises Ă  jour pour les vendeurs, une prĂ©sentation, etc.
  • Accordez un accès temporaire aux utilisateurs ou aux clients pour faire la dĂ©monstration de logiciels ou multimĂ©dias.

Essai routier VPS pour 30 roubles - fait pour vous


30 roubles est si petit que même la réticence à obtenir une carte pour payer et tester. Nous sommes aussi parfois si paresseux, mais cette fois nous avons tout fait pour vous. Avant de démarrer les serveurs au combat, nous avons effectué un test pour vérifier tous les détails et montrer de quoi les serveurs sont capables à ce tarif. Pour le rendre plus intéressant, nous avons ajouté des sports extrêmes et vérifié comment cette configuration se comporte si la densité et la charge dépassent les valeurs définies par nous.

L'hôte était sous la charge d'un certain nombre de machines virtuelles qui effectuaient diverses tâches sur le processeur et utilisaient activement le sous-système de disque. Le but est de simuler une densité de placement élevée et une charge comparable ou supérieure à celle de combat.

En plus de la charge constante, nous avons installé 3 machines virtuelles collectant des métriques synthétiques à l'aide de sysbench, dont les résultats moyens ont été donnés ci-dessous, et 50 machines virtuelles qui ont créé une charge supplémentaire. Toutes les machines virtuelles de test avaient la même configuration (1 cœur, 512 Go de RAM, 10 Go de SSD), l'image Debian 9.6 standard, qui est proposée aux utilisateurs sur RUVDS, a été choisie comme système d'exploitation.

La charge a été simulée dans une nature et une taille comparables au combat:

  • Certaines machines virtuelles ont dĂ©marrĂ© avec une faible charge.
  • Certaines machines ont "tordu" un scĂ©nario de test qui simule la charge sur le processeur (en utilisant l'utilitaire de stress )
  • Sur le reste des machines virtuelles, nous avons exĂ©cutĂ© un script qui utilise dd pour copier les donnĂ©es des donnĂ©es prĂ©-prĂ©parĂ©es sur le disque avec un ensemble de restrictions utilisant pv (des exemples peuvent ĂŞtre trouvĂ©s ici et ici ).

De plus, comme vous vous en souvenez, nous avions trois voitures qui collectaient des mesures synthétiques.

Un script a été exécuté sur chaque machine de manière cyclique toutes les 15 minutes, qui exécute des tests sysbench standard pour le processeur, la mémoire et le disque.

Script 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 .

Source: https://habr.com/ru/post/fr464571/


All Articles