É bom quando todas as pequenas coisas necessárias estão à mão: uma caneta e um caderno bem escritos, um lápis afiado, um mouse confortável, alguns fios extras, etc. Essas coisas discretas não atraem a atenção, mas acrescentam uma vida de conforto. A mesma história com vários aplicativos móveis e de desktop: para capturas de tela longas, reduzir o tamanho da imagem, calcular finanças pessoais, dicionários, tradutores, conversores, etc. Você tem esse 
VPS - barato, sempre à mão e com muitos benefícios? Não, não o que você tem na sua empresa, mas o seu próprio "bolso". Pensávamos que, sem um pequeno VPS em 2019, era de alguma forma triste, assim como sem a caneta-tinteiro comum em uma palestra. Por que ficar triste? O verão é. Bem, como o verão. Verão de TI: sente-se em casa, corte projetos favoritos sem nenhum arrependimento. Em geral, pensado e feito.
 O comunismo chegou, camaradas
O comunismo chegou, camaradasEle é seu - nosso VPS por trinta
Lemos artigos de concorrentes e usuários que escreveram de 3 a 4 anos atrás sobre por que o VPS barato não é necessário. Bem, certo, então o VPS "por um centavo" era puro marketing e não podia oferecer oportunidades normais de trabalho. Mas os tempos estão mudando, o custo dos recursos virtuais está diminuindo e, por 30 rublos por mês, estamos prontos para oferecer isso:
- Processador: Intel Xeon 2 GHz (1 núcleo)
- Sistema Linux (Debian, Ubuntu, CentOS para escolher)
- 1 endereço IPv4 dedicado
- 10 GB para armazenamento de dados em SSDs rápidos de classe empresarial
- RAM: 512 Mb
- Faturamento por segundo
- Tráfego ilimitado
A tarifa está sujeita a restrições técnicas adicionais, detalhes na 
página da nossa oferta legal - VPS por 30 rublos.
Quem deve usar um servidor virtual? Sim, quase todos: iniciantes, entusiastas, desenvolvedores experientes, fãs de bricolage e até algumas empresas.
Para que esse VPS é adequado?
Achamos que os leitores de Habr definitivamente encontrarão sua própria maneira de aplicar essa configuração, mas eles decidiram montar sua própria seleção de idéias - caso contrário, de repente alguém precisa disso, mas os homens não sabem?
- Publique seu site, portfólio, currículo simples com código e assim por diante. Obviamente, seu próprio site projetado causa uma impressão positiva no empregador. Coloque-o no seu VPS e seja responsável pela segurança e estabilidade do site por conta própria, e não pela equipe de hospedagem compartilhada.
- Use o VPS para fins educacionais: publique seu projeto, estude os recursos do servidor e do sistema operacional do servidor, experimente o DNS, explore um pequeno site de treinamento.
- Para telefonia. Às vezes, um empreendedor individual, freelancer ou uma empresa muito pequena realmente precisa de telefonia IP, e os operadores dessa telefonia são muito gananciosos. É simples: pegamos nosso servidor, compramos um número da operadora de telefonia IP, configuramos um PBX virtual e criamos números internos (se necessário). A economia é colossal.
- Use um servidor para testar seus aplicativos.
- Use o servidor para experiências de bricolage, inclusive para gerenciar e coletar dados dos sensores de um sistema doméstico inteligente.
- Um método incomum de aplicação é colocar no servidor um assistente virtual de negociação de câmbio, um robô de negociação. Você será totalmente responsável pela estabilidade e segurança do servidor, o que significa que você obterá um instrumento controlado para negociação nas bolsas de valores. Bem, de repente alguém gosta ou planeja :-)
Existe um aplicativo VPS no campo corporativo. Além do serviço telefônico já mencionado, você pode implementar várias peças interessantes. Por exemplo:
- Colocar pequenos bancos de dados e informações que estarão disponíveis para viajar funcionários à distância, por exemplo, usando o ftp. Isso permitirá que você troque rapidamente novas análises, configurações atualizadas para vendedores, apresentações etc.
- Conceda acesso temporário a usuários ou clientes para demonstrar software ou multimídia.
Test drive VPS por 30 rublos - feito para você
30 rublos é tão pequeno que até a relutância em conseguir um cartão para pagar e testar. Às vezes também somos preguiçosos, mas desta vez fizemos tudo por você. Antes de iniciar os servidores em batalha, realizamos um teste para verificar todos os detalhes e mostrar do que os servidores são capazes nessa tarifa. Para torná-lo mais interessante, adicionamos esportes radicais e verificamos como essa configuração se comporta se a densidade e a carga excederem os valores definidos por nós.
O host estava sob carga de várias máquinas virtuais que executavam várias tarefas no processador e usavam ativamente o subsistema de disco. O objetivo é simular uma alta densidade de posicionamento e uma carga comparável ou superior à de combate.
Além da carga constante, instalamos 3 máquinas virtuais que coletam métricas sintéticas usando o sysbench, cujos resultados médios foram apresentados abaixo e 50 máquinas virtuais que criaram carga adicional. Todas as máquinas virtuais de teste tinham a mesma configuração (1 núcleo, 512 GB RAM, 10 GB SSD); a imagem padrão do debian 9.6, oferecida aos usuários no RUVDS, foi escolhida como sistema operacional.
A carga foi simulada em natureza e tamanho comparável ao combate:- Algumas das máquinas virtuais foram iniciadas com baixa carga.
- Algumas máquinas "distorceram" um cenário de teste que simula a carga no processador (usando o utilitário de estresse )
- No restante das máquinas virtuais, executamos um script que usa dd para copiar dados de dados pré-preparados para o disco com um conjunto de restrições usando pv (exemplos podem ser encontrados aqui e aqui ).
Além disso, como você se lembra, tínhamos três carros coletando métricas sintéticas.Um script era executado em cada máquina ciclicamente a cada 15 minutos, que executa testes padrão do sysbench para o processador, a memória e o disco.
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 .