É 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.
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
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.
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:
getState: retorna o status
getEmployee: retorna funcionários (+salários, +cargos) do banco de dados
patchEmployee: altera os campos do funcionário
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.