Cervello + VPS per 30 rubli =?

È così bello quando tutte le piccole cose necessarie sono a portata di mano: una buona penna e un buon blocco note, una matita appuntita, un mouse comodo, un paio di fili extra, ecc. Queste cose poco appariscenti non attirano l'attenzione, ma aggiungono conforto alla vita. La stessa storia vale con varie applicazioni mobili e desktop: per screenshot lunghi, per ridurre le dimensioni di un'immagine, per calcolare le finanze personali, dizionari, traduttori, convertitori, ecc. Ne hai uno? VPS - quale costa poco, è sempre a portata di mano e porta tanti vantaggi? No, non quello che hai in azienda, ma il tuo, “tascabile”. Abbiamo pensato che senza un piccolo VPS nel 2019 fosse in qualche modo triste, proprio come senza la solita penna stilografica a lezione. Perché essere triste? È estate. Come va l'estate? Estate per uno specialista IT: seduto a casa, lavorando ai tuoi progetti preferiti senza alcun rimorso. In generale, l'abbiamo pensato e fatto.

Cervello + VPS per 30 rubli =?
Il comunismo è arrivato, compagni.

È così: il nostro VPS da trenta

Abbiamo letto molti articoli di concorrenti e utenti che hanno scritto 3-4 anni fa sul perché non è necessario un VPS economico. Ebbene è vero, allora il VPS “per un centesimo” era puro marketing e non poteva offrire normali opportunità di lavoro. Ma i tempi stanno cambiando, il costo delle risorse virtuali sta diventando sempre più basso e per 30 rubli al mese siamo pronti a offrire questo:

  • Processore: Intel Xeon 2 GHz (1 core)
  • Sistema Linux (Debian, Ubuntu, CentOS tra cui scegliere)
  • 1 indirizzo IPv4 dedicato
  • 10 GB di spazio di archiviazione dati su veloci unità SSD di classe enterprise
  • RAM: 512 MB
  • Fatturazione al secondo
  • Traffico illimitato

La tariffa è soggetta ad ulteriori restrizioni tecniche, dettagli su pagina la nostra fantastica offerta: VPS per 30 rubli. 

A chi è adatto questo server virtuale? Sì a quasi tutti: principianti, appassionati, sviluppatori esperti, appassionati del fai da te e anche alcune aziende.

Per cosa è adatto questo VPS?

Pensiamo che i lettori di Habr troveranno sicuramente il proprio modo di utilizzare questa configurazione, ma abbiamo deciso di raccogliere la nostra selezione di idee: e se qualcuno ne avesse bisogno, ma gli uomini non lo sanno?

  • Inserisci il tuo semplice sito web, portfolio, curriculum con codice, ecc. Naturalmente, il tuo sito web progettato fa un'impressione positiva sul datore di lavoro. Inseriscilo sul tuo VPS e sii responsabile della sicurezza e della stabilità del sito tu stesso e non lo staff dei normali provider di hosting.
  • Utilizza VPS per scopi didattici: ospita il tuo progetto, studia le funzionalità del server e del sistema operativo del server, sperimenta il DNS, armeggia con un piccolo sito educativo.
  • Per la telefonia. A volte un singolo imprenditore, libero professionista o una piccola azienda ha un disperato bisogno della telefonia IP e gli operatori di questa stessa telefonia sono molto avidi. È semplice: prendiamo il nostro server, acquistiamo un numero da un operatore di telefonia IP, configuriamo un PBX virtuale e creiamo numeri interni (se necessario). Il risparmio è colossale.
  • Utilizza il server per testare le tue applicazioni.
  • Utilizza il server per esperimenti fai-da-te, incluso il controllo e la raccolta di dati dai sensori del sistema di casa intelligente.
  • Un modo insolito di utilizzarlo è posizionare sul server un assistente commerciale di scambio virtuale, un robot commerciale. Sarai pienamente responsabile della stabilità e della sicurezza del server, il che significa che riceverai uno strumento controllato per fare trading sui mercati azionari. Bene, nel caso qualcuno sia interessato o stia pianificando :)

Esistono applicazioni per tali VPS nella sfera aziendale. Oltre al già citato servizio telefonico potete implementare diverse cose interessanti. Per esempio:

  • Posizionare piccoli database e informazioni che saranno accessibili ai dipendenti in viaggio a distanza, ad esempio utilizzando ftp. Ciò ti consentirà di scambiare molto rapidamente nuove analisi, configurazioni aggiornate per addetti alle vendite, presentazioni, ecc.
  • Concedere accesso temporaneo a utenti o client per dimostrare software o supporti.

Test drive VPS per 30 rubli: fatto per te

30 rubli sono così pochi che non vuoi nemmeno tirare fuori la carta per pagare e testare. Anche noi a volte siamo così pigri, ma questa volta abbiamo fatto tutto per te. Prima di lanciare i server in battaglia, abbiamo effettuato un test per verificare tutti i dettagli e mostrare di cosa sono capaci i server a questa tariffa. Per renderlo più interessante, abbiamo aggiunto estremo e verificato come si comporterebbe questa configurazione se la densità e il carico superassero i valori impostati. 

L'host era sotto il carico di una serie di macchine virtuali che eseguivano varie attività sul processore e utilizzavano attivamente il sottosistema del disco. L'obiettivo è simulare un'alta densità di posizionamento e un carico paragonabile o superiore a quello di combattimento.

Oltre al carico costante, abbiamo installato 3 macchine virtuali che raccoglievano parametri sintetici utilizzando sysbench, i cui risultati medi sono stati forniti di seguito, e 50 macchine virtuali che hanno creato un carico aggiuntivo. Tutte le macchine virtuali di prova avevano la stessa configurazione (1 core, RAM 512 GB, SSD 10 GB), come sistema operativo è stata selezionata l'immagine standard Debian 9.6, che viene offerta agli utenti su RUVDS.

Il carico è stato simulato per natura e grandezza paragonabile al combattimento:

  • Alcune macchine virtuali sono state avviate con un carico ridotto
  • Alcune macchine hanno eseguito uno script di test simulando il carico sul processore (utilizzando l'utility stress)
  • Sulla restante parte delle macchine virtuali, abbiamo eseguito uno script che utilizzava dd per copiare i dati da dati pre-preparati su disco con un limite impostato utilizzando pv (si possono vedere esempi qui и qui).

Inoltre, come ricorderete, avevamo tre macchine che raccoglievano parametri sintetici.

Su ciascuna macchina è stato eseguito ciclicamente ogni 15 minuti uno script che esegue test sysbench standard per processore, memoria 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

I risultati sono presentati per comodità in formato sysbench, ma i valori medi per l'intero periodo di test sono stati presi da tutte le macchine, il risultato può essere visto qui:

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

I risultati sono indicativi, ma non devono essere considerati come QoS. 

Macchine che creano carico aggiuntivo

Software:

  • apt-get update
  • apt-get upgrade
  • apt-get install python-pip
  • pip installa mysql-connector-python-rf

MariaDB installato, come fare qui:

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

Base di prova presa quindi:

Il database viene distribuito come specificato qui:

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

Piccola base di prova:

Table 

Conteggio righe 

Dimensione dei dati (MB)

Dimensione dell'indice (KB)

dipartimenti 

9

0.02

16.00

dip_emp 

331143 

11.52

5648.00

dept_manager 

24 

0.02

16.00

dipendenti 

299379 

14.52

0.00

stipendi 

2838426 

95.63

0.00 

titoli 

442783 

19.56

0.00

Un servizio di test primitivo è scritto on the knee in Python; esegue quattro operazioni:

  1. getState: restituisce lo stato
  2. getEmployee: restituisce i dipendenti (+stipendi, +titoli) dal database
  3. patchEmployee: modifica i campi dei dipendenti
  4. insertSalary: inserisce uno stipendio

Origine del servizio (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')

Attenzione! In nessun caso questo servizio deve essere preso come esempio o guida!

I test vengono eseguiti utilizzando il buon vecchio JMeter. Sono stati lanciati una serie di test della durata da 15 minuti a 2 ore, senza interruzioni, la percentuale di richieste variava e il throughput variava da 300 a 600 richieste al minuto. Numero di thread da 50 a 500.

Dato che il database è molto piccolo, il comando:

mysql -e "SHOW ENGINE INNODB STATUS"

Mostra che:

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

Di seguito i tempi medi di risposta alle richieste:

Discografica

Media

Mediano

Linea 90%.

Linea 95%.

Linea 99%.

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

Potrebbe essere difficile per te giudicare da questi risultati sintetici quanto sia adatto questo VPS per i tuoi compiti specifici e, in generale, i metodi elencati sono limitati a quei casi che abbiamo dovuto affrontare in una forma o nell'altra. Quindi la nostra lista è chiaramente non esaustivo. Ti invitiamo a trarre le tue conclusioni e a testare il server per 30 rubli sulle tue applicazioni e attività reali e suggerire le tue opzioni per questa configurazione nei commenti.

Fonte: habr.com

Aggiungi un commento