Brain + VPS で 30 ルーブル =?

良質なペンとメモ帳、尖った鉛筆、快適なマウス、いくつかの予備のワイヤーなど、必要な小さなものがすべて手元にあると、とても快適です。 これらの目立たないものは、注目を集めませんが、生活に快適さを与えます。 同じことが、さまざまなモバイルおよびデスクトップ アプリケーションにも当てはまります。長いスクリーンショット、画像のサイズの縮小、家計簿の計算、辞書、翻訳、コンバータなどです。 持っていますか? VPS - 安価で常に手元にあり、多くのメリットをもたらすのはどれですか? いいえ、会社にあるものではなく、あなた自身の「ポケット」のものです。 2019 年に小型 VPS がないと、講演会でいつもの万年筆がないのと同じように、なんだか寂しいと思いました。 なぜ悲しいのでしょうか? 夏だよ。 夏はどうですか? IT スペシャリストの夏: 家に座って、好きなプロジェクトにじっくり取り組んでいます。 一般的に、私たちは考えてそれを実行しました。

Brain + VPS で 30 ルーブル =?
同志諸君、共産主義が到来した。

彼はそのような人です - XNUMX 人用の当社の VPS

私たちは、3 ~ 4 年前に、安価な VPS が必要ない理由について競合他社やユーザーが書いた記事をたくさん読みました。 そうです、「30 ペニーで」VPS は純粋なマーケティングであり、通常の労働機会を提供することはできませんでした。 しかし、時代は変わり、仮想リソースのコストはますます低くなっており、月額 XNUMX ルーブルで以下を提供する準備ができています。

  • プロセッサー:Intel Xeon 2GHz(1コア)
  • Linux システム (Debian、Ubuntu、CentOS から選択)
  • 1 つの専用 IPv4 アドレス
  • 高速エンタープライズクラスの SSD ドライブ上の 10 GB のデータ ストレージ
  • RAM: 512MB
  • 秒単位の請求
  • 無制限のトラフィック

料金には追加の技術的制限が適用されます。詳細については、 ページ 私たちの素晴らしいオファー - 30 ルーブルの VPS。 

この仮想サーバーは誰に適していますか? 初心者、愛好家、経験豊富な開発者、DIY ファン、さらには一部の企業など、ほぼすべての人に当てはまります。

この VPS は何に適していますか?

Habr の読者は間違いなくこの構成を使用する独自の方法を見つけると思いますが、私たちは独自に選択したアイデアを収集することにしました - 誰かがそれを必要としているのに男性が知らない場合はどうすればよいでしょうか?

  • シンプルなウェブサイト、ポートフォリオ、コード付き履歴書などを配置します。 もちろん、あなた自身がデザインしたウェブサイトは雇用主に好印象を与えます。 VPS に配置し、サイトのセキュリティと安定性については、通常のホスティング プロバイダーのスタッフではなく、自分で責任を負います。
  • VPS を教育目的に使用します。プロジェクトをホストし、サーバーとサーバー オペレーティング システムの機能を研究し、DNS を実験し、小規模な教育サイトをいじってみます。
  • 電話用。 個人の起業家、フリーランサー、または非常に小規模な会社が IP テレフォニーを切実に必要とする場合がありますが、このテレフォニーのオペレーターは非常に貪欲です。 それは簡単です。サーバーを取得し、IP テレフォニー オペレーターから番号を購入し、仮想 PBX をセットアップして、(必要に応じて) 内部番号を作成します。 節約効果は莫大です。
  • サーバーを使用してアプリケーションをテストします。
  • スマート ホーム システム センサーの制御やデータ収集など、DIY 実験にサーバーを使用します。
  • 珍しい使用方法は、仮想取引所取引アシスタントである取引ロボットをサーバー上に配置することです。 あなたはサーバーの安定性とセキュリティに対して全責任を負います。つまり、株式市場で取引するための管理された商品を受け取ることになります。 まあ、誰かが興味を持っているか、計画している場合に備えて:)

このような VPS は企業分野にも応用できます。 すでに述べた電話サービスに加えて、いくつかの興味深い機能を実装できます。 例えば:

  • たとえば、ftp を使用して、遠方から出張中の従業員がアクセスできる小さなデータベースと情報を配置します。 これにより、最新の分析、営業担当者向けの更新された設定、プレゼンテーションなどを非常に迅速に交換できるようになります。
  • ソフトウェアまたはメディアをデモンストレーションするために、ユーザーまたはクライアントに一時的なアクセスを許可します。

VPS のテストドライブは 30 ルーブル - 完了です

30 ルーブルはあまりにも少ないので、支払いやテストのためにカードを取り出したくさえありません。 私たちも時々とても怠け者になることがありますが、今回は私たちがあなたのためにすべてをしてくれました。 サーバーを戦闘に投入する前に、すべての詳細をチェックし、この料金でサーバーがどのような機能を備えているかを示すテストを実施しました。 より興味深いものにするために、エクストリームを追加し、密度と負荷が設定した値を超えた場合にこの構成がどのように動作するかをチェックしました。 

ホストは、プロセッサ上でさまざまなタスクを実行し、ディスク サブシステムをアクティブに使用する多数の仮想マシンの負荷を受けていました。 目標は、高密度の配置と戦闘と同等以上の負荷をシミュレートすることです。

一定の負荷に加えて、sysbench を使用して合成メトリクスを収集する 3 台の仮想マシンをインストールしました。その平均結果を以下に示します。また、追加の負荷を生成する 50 台の仮想マシンをインストールしました。 すべてのテスト仮想マシンは同じ構成 (1 コア、RAM 512 GB、SSD 10 GB) で、標準の debian 9.6 イメージがオペレーティング システムとして選択され、RUVDS でユーザーに提供されました。

負荷は戦闘に匹敵する性質と大きさでシミュレートされました。

  • 一部の仮想マシンが低負荷で起動されました
  • 一部のマシンは、プロセッサの負荷をシミュレートするテスト スクリプトを実行しました (ユーティリティを使用) ストレス)
  • 仮想マシンの残りの部分では、dd を使用して、pv を使用して制限を設定して、事前に準備されたデータからディスクにデータをコピーするスクリプトを実行しました (例を参照) ここで и ここで).

また、覚えているとおり、合成メトリクスを収集するマシンが XNUMX 台ありました。

各マシンでスクリプトが 15 分ごとに周期的に実行され、プロセッサ、メモリ、ディスクの標準的な sysbench テストが実行されました。

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 インストール python-pip
  • pip インストール 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

小規模なテストベース:

表 

行数 

データサイズ(MB)

インデックスサイズ (KB)

部署 

9

0.02

16.00

部門_emp 

331143 

11.52

5648.00

部門マネージャー 

24 

0.02

16.00

社員 

299379 

14.52

0.00

給与 

2838426 

95.63

0.00 

タイトル 

442783 

19.56

0.00

原始的なテスト サービスは Python で書かれており、次の XNUMX つの操作を実行します。

  1. getState: ステータスを返します
  2. getEmployee: データベースから従業員 (+ 給与、+ 役職) を返します。
  3. patchEmployee: 従業員フィールドを変更します
  4. insertSalary: 給与を挿入します

サービスソース (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')

警告! いかなる場合でも、このサービスを例やガイドとして取り上げてはなりません。

テストは古き良き JMeter を使用して実行されます。 15 分から 2 時間続く一連のテストが中断することなく開始され、リクエストの割合は変化し、スループットは 300 分あたり 600 リクエストから 50 リクエストまで変化しました。 スレッド数は 500 ~ XNUMX です。

データベースが非常に小さいため、コマンドは次のようになります。

mysql -e "SHOW ENGINE INNODB STATUS"

を示す:

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

リクエストの平均応答時間は次のとおりです。

ラベル

平均

中央値

90%ライン

95%ライン

99%ライン

最小値

最大値

従業員を取得する

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

パッチ従業員

161.42

83.29

308

492.57

1845.14

5

6639.4

給料を入れる

167.21

86.93

315.34

501.07

1927.12

7

6722.44

これらの総合結果から、この VPS が特定のタスクにどの程度適しているかを判断するのは難しいかもしれませんが、一般に、リストされている方法は、何らかの形で対処しなければならなかったケースに限定されています。明らかに網羅的ではありません。 独自の結論を導き出し、実際のアプリケーションとタスクで 30 ルーブルのサーバーをテストし、コメントでこの構成のオプションを提案してください。

出所: habr.com

コメントを追加します