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, camaradesIl 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.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 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
:
, :
- getState:
- getEmployee: employee (+salaries, +titles)
- patchEmployee: employee
- insertSalary: salary
(dbtest.py)
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
:
, VPS , , . . 30 .