Cérebro + VPS por 30 rublos =?

É tão bom quando todas as pequenas coisas necessárias estão à mão: uma boa caneta e um bloco de notas, um lápis apontado, um mouse confortável, alguns fios extras, etc. Essas coisas imperceptíveis não atraem a atenção, mas acrescentam conforto à vida. A mesma história acontece com vários aplicativos móveis e de desktop: para capturas de tela longas, para reduzir o tamanho de uma imagem, para calcular finanças pessoais, dicionários, tradutores, conversores, etc. Você tem um? VPS - que é barato, está sempre à mão e traz muitos benefícios? Não, não aquele que você tem na sua empresa, mas o seu, de “bolso”. Achamos que sem um pequeno VPS em 2019 seria um tanto triste, assim como sem a caneta-tinteiro usual em uma palestra. Por que ficar triste? É verão. Como está o verão? Verão para um especialista em TI: sentado em casa, trabalhando em seus projetos favoritos sem nenhum arrependimento. Em geral, pensamos e fizemos.

Cérebro + VPS por 30 rublos =?
O comunismo chegou, camaradas.

Ele é assim - nosso VPS por trinta

Lemos muitos artigos de concorrentes e usuários que escreveram há 3 a 4 anos sobre por que um VPS barato não é necessário. Bem, isso mesmo, 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á cada vez menor e por 30 rublos por mês estamos prontos para oferecer isto:

  • Processador: Intel Xeon 2 GHz (1 núcleo)
  • Sistema Linux (Debian, Ubuntu, CentOS para escolher)
  • 1 endereço IPv4 dedicado
  • 10 GB de armazenamento de dados em unidades SSD rápidas de classe empresarial
  • RAM: 512 MB
  • Faturamento por segundo
  • Tráfego ilimitado

A tarifa está sujeita a restrições técnicas adicionais, detalhes sobre página nossa oferta legal - VPS por 30 rublos. 

Para quem este servidor virtual é adequado? Sim para quase todos: iniciantes, entusiastas, desenvolvedores experientes, fãs de DIY e até algumas empresas.

Para que este VPS é adequado?

Achamos que os leitores de Habr certamente encontrarão sua própria maneira de usar essa configuração, mas decidimos coletar nossa própria seleção de ideias - e se alguém precisar, mas os homens não souberem?

  • Coloque seu site simples, portfólio, currículo com código, etc. É claro que o seu próprio site causa uma impressão positiva no empregador. Coloque-o no seu VPS e seja você mesmo responsável pela segurança e estabilidade do site, e não pela equipe de provedores de hospedagem regulares.
  • Use VPS para fins educacionais: hospede seu projeto, estude os recursos do servidor e do sistema operacional do servidor, experimente DNS, mexa em um pequeno site educacional.
  • Para telefonia. Às vezes, um empresário individual, um freelancer ou uma empresa muito pequena precisa desesperadamente de telefonia IP, e os operadores dessa mesma telefonia são muito gananciosos. É simples: pegamos nosso servidor, compramos um número de uma operadora de telefonia IP, montamos um PABX virtual e criamos números internos (se necessário). A economia é colossal.
  • Use o servidor para testar seus aplicativos.
  • Use o servidor para experimentos DIY, incluindo controle e coleta de dados de sensores de sistemas domésticos inteligentes.
  • Uma maneira incomum de usá-lo é colocar um assistente de negociação de bolsa virtual, um robô de negociação, no servidor. Você será totalmente responsável pela estabilidade e segurança do servidor, o que significa que receberá um instrumento controlado para negociação nas bolsas de valores. Bem, caso alguém esteja interessado ou planejando :)

Existem aplicações para esse tipo de VPS na esfera corporativa. Além do já mencionado serviço telefônico, você pode implementar várias coisas interessantes. Por exemplo:

  • Coloque pequenos bancos de dados e informações que serão acessíveis aos funcionários que viajam à distância, por exemplo, usando 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 demonstração de software ou mídia.

Test drive VPS por 30 rublos - feito para você

30 rublos é tão pouco que você nem quer tirar um cartão para pagar e testar. Às vezes também somos preguiçosos, mas desta vez fizemos tudo por você. Antes de lançar os servidores para a batalha, realizamos um teste para verificar todos os detalhes e mostrar o que os servidores são capazes com esta tarifa. Para torná-lo mais interessante, adicionamos extremo e verificamos como essa configuração se comportaria se a densidade e a carga ultrapassassem os valores que definimos. 

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 maior que a de combate.

Além da carga constante, instalamos 3 máquinas virtuais que coletaram métricas sintéticas usando o sysbench, cujos resultados médios são apresentados a seguir, e 50 máquinas virtuais que criaram carga adicional. Todas as máquinas virtuais de teste tinham a mesma configuração (1 núcleo, RAM 512 GB, SSD 10 GB), a imagem padrão debian 9.6 foi selecionada como sistema operacional, que é oferecido aos usuários no RUVDS.

A carga foi simulada em natureza e magnitude comparável ao combate:

  • Algumas máquinas virtuais foram lançadas com carga baixa
  • Algumas máquinas executaram um script de teste simulando a carga do processador (usando o utilitário estresse)
  • No restante das máquinas virtuais, executamos um script que usava dd para copiar dados de dados pré-preparados para o disco com um limite definido usando pv (exemplos podem ser vistos aqui и aqui).

Além disso, como você lembra, tínhamos três máquinas que coletavam métricas sintéticas.

Em cada máquina, um script foi executado ciclicamente a cada 15 minutos, que executa testes padrão do sysbench para processador, memória e 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

Os resultados são apresentados por conveniência no formato sysbench, mas os valores médios de todo o período de testes foram retirados de todas as máquinas, o resultado pode ser visto aqui:

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

Os resultados são indicativos, mas ainda assim não devem ser considerados como QoS. 

Máquinas que criam carga adicional

Programas:

  • apt-get update
  • atualização apt-get
  • apt-get install python-pip
  • pip instalar mysql-connector-python-rf

MariaDB instalado, como fazer aqui:

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

Base de teste realizada por isso:

O banco de dados é implantado conforme especificado aqui:

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

Base de teste pequena:

mesa 

Contagem de linhas 

Tamanho dos dados (MB)

Tamanho do índice (KB)

departamentos 

9

0.02

16.00

dept_emp 

331143 

11.52

5648.00

gerente de departamento 

24 

0.02

16.00

colaboradores 

299379 

14.52

0.00

salários 

2838426 

95.63

0.00 

títulos 

442783 

19.56

0.00

Um serviço de teste primitivo é escrito em Python; ele executa quatro operações:

  1. getState: retorna o status
  2. getEmployee: retorna funcionários (+salários, +cargos) do banco de dados
  3. patchEmployee: altera os campos do funcionário
  4. insertSalary: insere um salário

Fonte de serviço (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')

Atenção! Em nenhuma circunstância este serviço deve ser tomado como exemplo ou guia!

Os testes são realizados usando o bom e velho JMeter. Foi lançada uma série de testes com duração de 15 minutos a 2 horas, sem interrupções, o percentual de solicitações variou e o throughput variou de 300 a 600 solicitações por minuto. Número de threads de 50 a 500.

Devido ao fato do banco de dados ser muito pequeno, o comando:

mysql -e "SHOW ENGINE INNODB STATUS"

Mostra que:

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

Abaixo estão os tempos médios de resposta para solicitações:

O rótulo

Média

Mediana

90%Linha

95%Linha

99%Linha

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

colocarSalário

167.21

86.93

315.34

501.07

1927.12

7

6722.44

Pode ser difícil para você julgar a partir destes resultados sintéticos quão adequado este VPS é para suas tarefas específicas e, em geral, os métodos listados são limitados aos casos que tivemos que lidar de uma forma ou de outra. Portanto, nossa lista é claramente não é exaustivo. Convidamos você a tirar suas próprias conclusões e testar o servidor por 30 rublos em seus aplicativos e tarefas reais e sugerir suas opções para esta configuração nos comentários.

Fonte: habr.com

Adicionar um comentário