Python command line

最近為了想上手Python,所以想說用Python寫個簡易的cli 工具當作目標來當練習;這次就來記錄怎麼一步一步的寫出這個cli程式。
程式的需求,是希望可以設定一個定期備份wordpress 內容的服務,並且在備份完了以後,可以上傳到指定的AWS s3.

Requirements

  • 提供flag 可以讓使用者指定要備份的資料夾
  • 提供flag 可以讓使用者指定要上傳的s3 路徑
  • 備份的資料夾必須先經過壓縮後,才會上傳到s3

Dependencies

  • Python 3+
  • Poetry
  • Pip

Poetry 是目前主流的Python套件管理工具,而這次這個repo主要就是使用poetry來管理其相依的library。

Implementation

Cli flags

Config flags 的部分,主要是透過Click這個套件來實作。這個套件在使用上相當容易上手,透過decorator的方式就可以指定程式在執行時,需要帶入哪些指令。

像是下面這段程式碼,就指定了程式在執行時,需要帶入-s--s3_path 作為s3 的路徑;即使我現在還不是很熟decorator,也可以輕易的讓程式支援flag。

@click.command()
@click.option(
     "-s",
     "--s3_path",
     default="",
     envvar="S3_PATH",
     help="The s3 path for uploading. (e.g. s3://path/to/upload )",
     required=True
 )

Entrypoint

除了讓程式支援flag,我們這邊也需要設定程式在執行時的進入點,通常會是一個自定義的function,其參數會是我們先前所設定的flags。

以下面的程式碼為例,我們指定了cli() 作為我們的這個程式的進入點,並且程式必須有2個參數,分別是inputs3_path。(參數的名稱必須與@click.option中的一致)

def cli(input, s3_path):
     if s3_path.find("s3://") != 0:
         logging.error("incorrect s3 path format")
         exit(1)

if name == "main":
    cli()

Config Poetry cli entrypoint

透過先前的設定,我們已經可以透過一般python 的執行方式(python cli.py)來執行我們的程式了,但為了讓我們的cli程式在執行時可以更像一個執行檔,我們可以透過Poetry來包裝我們的程式,這樣就不需要透過pyhton xxx.py的方式執行了!

Poetry entrypoint function

首先,我們必須先變動我們的程式路徑,並且指定一個可以當進入點的function,這邊我們就可以設定一個新的main(),而其function body就是執行我們先前的cli()

def main():
     cli()

接下來,我們必須在project 根資料夾下,新增一個子資料夾,並且把我們的cli.py移至這個子資料夾下,新的路徑結構為下

backup_to_s3_repo/
  poetry.lock
  pyproject.toml
  README.md
  backup_to_s3/
    __init__.py
    cli.py

Poetry entrypoint configuration

在這邊還需要在設定pyproject.toml,主要是為了讓poetry在執行時,可以用”alias”的方式來執行我們的程式,相關的pyproject.toml的內容如下:

下面的設定,可以讓我們執行poetry run backup_to_s3時,實際上去執行backup_to_s3/cli.py中的main()

[tool.poetry.scripts]
 backup_to_s3 = "backup_to_s3.cli:main"

Build and install

在完成前面所提到的設定之後,我們已經可以透過python backup_to_s3/cli.pypoetry run backup_to_s3來執行我們的程式了;但為了讓我們的程式可以更像一般的系統預設的執行程,我們可以透過下面列出的方式來實作。

Build the poetry project

透過poetry build,poetry 會幫我們把整個poetry project與相關的套件封裝成一個tar.gz 檔,並儲存於dist/資料夾下。

在整個封裝的過程中,poetry會幫我們建立對應的.whlsetup.py檔,以便於我們發佈或分享這個tar.gz 檔。

Install poetry build file by pip

在有了上述的tar.gz檔以後,我們就可以透過pip install的方式,將整個backup_to_s3程式安裝成像系統內建的指令程式了,詳細的指令可以參考下面

pip install dist/backup_to_s3-0.1.0.tar.gz

最後,完整的source code 可以參考這裡:
https://github.com/bamoo456/backup-to-s3

Reference

EBS 定價

最近主管告知了上個月的AWS帳單,EC2的帳單暴漲到了$2000!
主管提到了,這是他預期內的金額,不過提醒我要記得關閉最近測試開的那些機器與資源。當然,這金額對我來說是個非常意外的數字,之前的測試都是測試完以後我就馬上關掉機器了,每次測試時間最長也不超過3小時!

後來主管一路追查下去,原來我之前開了一堆io2的EBS,在測試完了以後也沒有刪除,而這些EBS應該就是造成帳單暴漲的原因!!!

這次的失誤,讓我知道以後要記得去看一下EBS是否有忘了刪掉的disk;不過也趁這機會好好研究一下EBS的計價方式:

EBS Volume Charges

AWS的這篇文章中提到的例子,可以看出只要EBS被provision以後,其provision的容量(GB)與IOPS都會被納入計價:

舉個例子,如果我在一台EC2上掛了一個100GB, 10000 IOPS的io2 EBS,則即使我只使用EBS上其中1GB做資料儲存,我一個月需要在EBS上付的金額為:

10000 IOPS * 0.065 USD + 100GB * 0.125 USD = 662.5 $USD

真的認真算下來,才知道io2的EBS計價那麼貴,這次真的是花錢好好的上了一課0rz

Reference

測試Cassandra

最近公司的某個專案可能會用到Cassandra,所以在真的用之前還是先來做個簡易的Benchmark,來看看它是否真的能符合我們的需求(主要是write heavey的情境)。

測試#1

測試情境

  • cluster nodes: AWS EC2 i3.large * 3
  • benchmark node: AWS EC2 c5.2xlarge.

測試 Query如下:

INSERT INTO status (id1 id2, id3, succeed) VALUES ($id1, $id2, $id3, false)

測試程式會不停的隨機產生上述Query,並寫進Cassandra cluster中,並觀察throughput如何:

測試結果

結果蠻不如預期的,目前在concurrent connection = 500的情況下,也只達到9xx左右的qps

Next

目前初步檢視了目前的cluster 設定,發現先前的測試EC2 node主要還是使用EBS gp2,其IOPS感覺只能到100 IOPS左右,再參考了這篇AWS的文章後,感覺可以照著這樣的設定再來測試一次看看。

測試#2

測試情境

沿續上次的測試,但重新設定Cluster 使用EC2 i3.large * 3,但這次則是mount了 NVME SSD (local instance store) 到Cassandra data folder ,並使用了一樣的測試程式與Query。

測試結果

一樣不如預期,qps 也還是只能打到9xx 左右,不過這次有觀察到Cassandra node的cpu 在測試期間都滿載。

Next

從上次的測試來看,cpu cores 必須要加大了,感覺目前是卡在cpu這邊。

測試#3

測試情境

沿續上次的測試,但重新設定Cluster 使用EC2 c5.4xlarge * 3,但這次則是mount了 NVME SSD(EBS io2) 到Cassandra data folder ,並使用了一樣的測試程式與Query。

CREATE INDEX succeed ON status (succeed);

測試結果

若沒有index 的情況下,insert qps 目前可以到 11xxx 左右;若加了index,則insert qps 目前可以到8xxx左右。

測試#4

測試情境

沿續上次的測試,但重新設定Cluster 使用EC2 c5.4xlarge * 8,但這次則是mount了 NVME SSD(EBS io2, iops=5000) 到Cassandra data folder ,並使用了一樣的測試程式與Query與測試#3中的index。

測試結果

加了index,則insert qps 目前可以到14xxx左右,這次觀察到cluster cpu 大概只有跑到50%左右,看來benchmark client 必須要升級了。

測試#5

測試情境

沿續上次的測試,這次升級了Benchmark client 使用c5a.8xlarge,測試cluster的部分則是使用前面測試所留下來的EC2 c5a.4xlarge * 8.

測試結果

這次觀察到即使在使用concurrent connection = 2000的情況下,cluster cpu 大概只有跑到70%左右,而整體的qps約在16xxx;

從這邊來看感覺又是卡在IOPS=5000的部分,目前每個cluster node 可以設定的EBS 為io2, IOPS=5000,IOPS設太高的話會在一開始無法同時開8個EC2 c5a.4xlarge

測試#6

測試情境

沿續上次的測試,這次試著再加了3 台 c5a.4xlarge到Cassandra cluster中,即是最後的cluster node 數量為

c5a.4xlarge * 10

測試結果

這次觀察在同一台機器,並且開啟2個benchmark 程式並使用concurrent connection = 2000的情況下,cluster cpu 大概只有跑到70%左右,而整體的qps約在20xxx;