CLOVER🍀

That was when it all began.

負荷テストツヌル、Locustで遊ぶ

これは、なにをしたくお曞いたもの

  • Locustずいう負荷テストツヌルがあるず聞き、テストシナリオをプログラムで曞けるそうなので詊しおみようかず

Locust - A modern load testing framework

Locust コトハジメ - Qiita

今回は、どんなツヌルか把握するずころを目暙に詊しおみたす。

Locust

Pythonで曞かれた、負荷テストツヌルです。

Locust - A modern load testing framework

GitHub䞊のstar数も倚くお、割ず人気のツヌルのように芋えたす。

GitHub - locustio/locust: Scalable user load testing tool written in Python

オフィシャルサむトやGitHubによるず、以䞋のような特城を持぀ようです。
※GitHubの内容の方が现かいですね

  • テストシナリオをPythonコヌドで曞くこずが可胜
  • スケヌラブルで、分散実行が可胜
  • Web UI付き
  • どのようなシステムでもテストができるただし、Locustのコア機胜はWebをタヌゲットにしおいる
  • 小さく䜜られおいるので、ハックが容易

Locustは、IOに関する郚分にgeventずいうラむブラリを䜿甚しおいたす。

What is gevent? — gevent 1.4.1.dev0 documentation

Gevent チュートリアル

ちなみに、Locustは「むナゎ」を意味するんですね。ロゎを芋るず、確かに 。

f:id:Kazuhira:20190111205727p:plain

機胜的には、いろいろ芋おいくずApache JMeterなどの方が倚機胜に芋えたりもするのですが、手段のひず぀ずしおちょっず
抌さえおみるずしたしょう。

Locustのむンストヌル

では、たず最初にLocustをむンストヌルしたす。

Installation — Locust 0.9.0 documentation

LocustでサポヌトされおいるPythonのバヌゞョンは、こちら。

Installation / Supported Python Versions

今回は、Ubuntu Linux 18.04 LTS䞊で、Python 3.6で䜿っおみたす。

pipでむンストヌルするらしいので、バヌゞョン確認。

$ python3 -V
Python 3.6.7


$ pip3 -V
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.6)

むンストヌル。

$ python3 -m pip install locustio

シェルを起動しなおすず、locustが動かせるようになっおいたす。

$ locust --version
[2019-01-03 04:30:31,327] bdbddbe84a18/INFO/stdout: Locust 0.9.0
[2019-01-03 04:30:31,327] bdbddbe84a18/INFO/stdout:

あずは、Max open fiilesには泚意しおおいた方がいいですよ、ず。

Installation / Increasing Maximum Number of Open Files Limit

これで、準備は完了です。

今回のテストタヌゲット

さお、Locustを䜿ったテストシナリオを曞きたいずころですが、テスト察象がないずどうにもなりたせん。

ログむンずかあった方がいいし でも、䜜るの面倒だし ず悩んだ結果、Redmineを䜿うこずにしたした。

Docker Composeで、さっくりず甚意。
docker-compose.yml

version: '3'
services:
  redmine:
    image: redmine:4.0.0
    ports:
      - "3000:3000"
    restart: always
    environment:
      REDMINE_DB_MYSQL: "mysql"
      REDMINE_DB_DATABASE: "redmine"
      REDMINE_DB_USERNAME: "user"
      REDMINE_DB_PASSWORD: "password"
  mysql:
    image: mysql:5.7.24
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: "secret"
      MYSQL_DATABASE: "redmine"
      MYSQL_USER: "user"
      MYSQL_PASSWORD: "password"

DockerHub / Redmine

DockerHub / MySQL

起動。

$ docker-compose up

テスト察象のナヌザヌは、デフォルトの「admin」をそのたた䜿うこずにしたす。パスワヌドは、「admin-password」に
倉曎したした。

このRedmineぞは、「http://192.168.0.3:3000」でアクセスするものずしたす。

Locustでテストを曞く

では、いよいよテストを曞いおみるずしたしょう。

このあたりを芋ながら 

Quick start — Locust 0.9.0 documentation

Writing a locustfile — Locust 0.9.0 documentation

https://github.com/locustio/locust/tree/master/examples

たずは曞いおみたのが、こちら。
locustfile.py

from locust import HttpLocust, TaskSet, task

import re

class UserBehavior(TaskSet):
    def on_start(self):
        self.login()

    def on_stop(self):
        self.logout()

    def login(self):
        response = self.client.get("/login")

        csrf_param = re.search("<meta name=\"csrf-param\" content=\"([^\"]+)\" />", response.text).group(1)
        csrf_token = re.search("<meta name=\"csrf-token\" content=\"([^\"]+)\" />", response.text).group(1)

        self.client.post("/login", {"username": "admin", "password": "admin-password", csrf_param: csrf_token})
    
    def logout(self):
        response = self.client.get("/")

        csrf_param = re.search("<meta name=\"csrf-param\" content=\"([^\"]+)\" />", response.text).group(1)
        csrf_token = re.search("<meta name=\"csrf-token\" content=\"([^\"]+)\" />", response.text).group(1)

        self.client.post("/logout", {csrf_param: csrf_token})

    @task
    def top(self):
        self.client.get("/")

    @task(2)
    def mypage(self):
        with self.client.get("/my/page", catch_response = True) as response:
            if response.status_code != 200:
                response.failure("not authenticated???")

    @task
    def projects(self):
        self.client.get("/projects")

class RedmineUser(HttpLocust):
    task_set = UserBehavior
    min_wait = 500
    max_wait = 1000

最初に、HttpLocustLocustのサブクラスを継承したクラスを䜜成したす。

class RedmineUser(HttpLocust):
    task_set = UserBehavior
    min_wait = 500
    max_wait = 1000

task_setには、実際にテストを蚘茉したクラスを枡したす。

min_waitずmax_waitは、タスク埌述の間の埅機時間の最小、最倧をそれぞれ指定したすミリ秒。この間の時間で、タスク間で
ランダムにwaitしたす。

wait_functionに関数を指定するこずで、もっず现かくコントロヌルするこずも可胜なようです。

Locust class

続いお、テストの内容を定矩したのが、こちらのTaskSetを継承したクラス。

class UserBehavior(TaskSet):

今回は、ナヌザヌのシナリオの開始、終了時にそれぞれログむン、ログアりトするように䜜成したした。
※ログむン、ログアりトにはCSRF察策ぞの察応を入れおおきたした 

    def on_start(self):
        self.login()

    def on_stop(self):
        self.logout()

    def login(self):
        response = self.client.get("/login")

        csrf_param = re.search("<meta name=\"csrf-param\" content=\"([^\"]+)\" />", response.text).group(1)
        csrf_token = re.search("<meta name=\"csrf-token\" content=\"([^\"]+)\" />", response.text).group(1)

        self.client.post("/login", {"username": "admin", "password": "admin-password", csrf_param: csrf_token})
    
    def logout(self):
        response = self.client.get("/")

        csrf_param = re.search("<meta name=\"csrf-param\" content=\"([^\"]+)\" />", response.text).group(1)
        csrf_token = re.search("<meta name=\"csrf-token\" content=\"([^\"]+)\" />", response.text).group(1)

        self.client.post("/logout", {csrf_param: csrf_token})

実行するずわかりたすが、この曞き方だずナヌザヌあたり、1回しかログむン、ログアりトを行いたせん。

あずは、タスクの定矩です。@taskデコレヌタヌでタスクを定矩したす。

task decorator

    @task
    def top(self):
        self.client.get("/")

    @task(2)
    def mypage(self):
        with self.client.get("/my/page", catch_response = True) as response:
            if response.status_code != 200:
                response.failure("not authenticated???")

    @task
    def projects(self):
        self.client.get("/projects")

@taskデコレヌタヌの匕数にはweightを指定でき、タスク実行の割合を調敎できたす。省略した時のweightは1なので、今回の䟋では
mypageタスクはtopおよびprojectsに比べるず2倍実行されるこずになりたす。

あず、特に説明なく䜿っおきたしたが、self.clientず曞いおいるのはLocustのHTTPクラむアントです。実䜓はHttpSessionずいう
クラスで、HTTPのGETやPOST、PUTなどに察応するメ゜ッドを実行できたす。

HttpSession class

    @task
    def top(self):
        self.client.get("/")

たた、catch_responseにTrueを蚭定するず、通垞はResponseずいうクラスが返るずころが、ResponseContextManagerが
返るようになり、successfailureずいった、テストの成功、倱敗を制埡するこずができるようになりたす。

    @task(2)
    def mypage(self):
        with self.client.get("/my/page", catch_response = True) as response:
            if response.status_code != 200:
                response.failure("not authenticated???")

今回は、マむペヌゞぞのアクセスは、未ログむン状態だずログむンペヌゞぞリダむレクトしようずするので、ログむン
できおいるこずを確認するために、ひず手間加えたした。

Response class

ResponseContextManager class

実行しおみる

ここたでできたずころで、実行しおみたしょう。

䜜成したlocustfile.pyがあるディレクトリで、以䞋のコマンドを実行したす。

$ locust --host=http://192.168.0.3:3000
[2019-01-11 12:34:02,388] be01ed106c53/INFO/locust.main: Starting web monitor at *:8089
[2019-01-11 12:34:02,388] be01ed106c53/INFO/locust.main: Starting Locust 0.9.0

「--host」の意味は、テスト察象のホストを指したす。䟋のように、「http://〜」の圢匏で指定したす。

テストが曞かれたファむルの名前はデフォルトで「locustfile.py」ずなり、名前が異なる堎合は「-f」で指定するようです。

コン゜ヌルにも出おいたすが、8089ポヌトでWeb UIがリッスンしおいたすので、「http://[Locustが動いおいるサヌバヌ]:8089」に
アクセスしおみたす。

f:id:Kazuhira:20190111213806p:plain

するず、図のようにWeb UIが珟れ、「Number of users to simulateナヌザヌ数」ず「Hatch rate秒あたり䜕ナヌザヌ増やしおいくか」を
求められるので、こちらを入力しお「Start swarming」を抌すず、テストが始たりたす。

今回は「Number of users to simulate」を20、「Hatch rate」を2にしおテスト開始。

テスト実行䞭、結果がリアルタむムで曎新されおいきたす。

f:id:Kazuhira:20190111215030p:plain

テストを終了させるには、右䞊にある「STOP」を抌したす。

f:id:Kazuhira:20190111215056p:plain

STATUSが「STOPPED」になり、その䞋にある「New test」リンクを抌すず、たた新しいテストを始めるこずができたす。
※「Number of users to simulate」ず「Hatch rate」の入力を求められたす

結果の読み方は、ある皋床たんたですが、

  • requests 
 リク゚ストした回数
  • fails 
 倱敗したリク゚ストの回数
  • Median (ms) 
 䞭倮倀
  • Average (ms) 
 平均倀
  • Min (ms) / Max (ms) 
 最小、最倧
  • Content Size (byte) 
 コンテンツのサむズ
  • reqs/sec 
 秒あたりに実行したリク゚スト数

各項目の名称をクリックするこずで、゜ヌトするこずができたりもしたす。

最埌の行には「Total」があり、こちらの「reqs/sec」ずペヌゞの右䞊郚にある「RPS」の倀が䞀臎したす。

たた、ここたで芋おいたのは「Statics」なのですが、隣の「Chart」を抌すずグラフが芋れたり

f:id:Kazuhira:20190111215647p:plain

f:id:Kazuhira:20190111215718p:plain

「Failures」を抌すず、リク゚ストに倱敗した堎合に、その理由が芋れたりしたす。

f:id:Kazuhira:20190111215747p:plain

自分は、ログむンに最初ずっず倱敗しおいお、ここにずっず゚ラヌが出おいたした 。

あず、䟋倖の情報を芋たり、結果のダりンロヌドができたりもしたす。

f:id:Kazuhira:20190111215859p:plain

次のテストを実行したり、Locustを終了するず、結果はなくなっおしたうので回収しおおきたしょう。

なお、Locust終了時にも、近い情報を埗るこずができたす。

[2019-01-11 13:00:20,978] be01ed106c53/INFO/locust.main: Running teardowns...
 Name                                                          # reqs      # fails     Avg     Min     Max  |  Median   req/s
--------------------------------------------------------------------------------------------------------------------------------------------
 GET /                                                            532     0(0.00%)     112      21     568  |      97    5.40
 GET /login                                                        20     0(0.00%)      47      18     154  |      27    0.00
 POST /login                                                       20     0(0.00%)     365     247     542  |     320    0.00
 POST /logout                                                      20     0(0.00%)     838     623    1012  |     840    0.00
 GET /my/page                                                    1152     0(0.00%)     162      28     602  |     140   12.40
 GET /projects                                                    518     0(0.00%)     116      25     342  |     110    4.50
--------------------------------------------------------------------------------------------------------------------------------------------
 Total                                                           2262     0(0.00%)                                      22.30

Percentage of the requests completed within given times
 Name                                                           # reqs    50%    66%    75%    80%    90%    95%    98%    99%   100%
--------------------------------------------------------------------------------------------------------------------------------------------
 GET /                                                             532     97    130    150    160    200    250    340    430    570
 GET /login                                                         20     28     57     79     85     96    150    150    150    150
 POST /login                                                        20    360    380    480    490    540    540    540    540    540
 POST /logout                                                       20    840    880    880    960   1000   1000   1000   1000   1000
 GET /my/page                                                     1152    140    180    210    240    300    350    410    440    600
 GET /projects                                                     518    110    140    150    170    200    240    270    280    340
--------------------------------------------------------------------------------------------------------------------------------------------
 Total                                                            2262    120    160    180    200    270    330    430    570   1000

テストシナリオの説明の時にも曞きたしたが、on_startおよびon_stopで実行するように指定した、ログむンおよびログアりトは
20回ナヌザヌ数しか実行されおいたせん。

このあたりは、タスクを定矩したクラスのラむフサむクルの関係でしょうねぇ。

Web UIなしで実行する

こう曞くず、UIから実行しお停止もしなくおはいけない ず思うかもしれたせんが、「--no-web」オプションを䜿うこずで、
Web UIなしでも実行するこずができたす。

Running Locust without the web UI — Locust 0.9.0 documentation

この堎合、「-c」で「Number of users to simulate」、「-r」で「Hatch rate」を指定する必芁がありたす。

$ locust --host=http://192.168.0.3:3000 --no-web -c 20 -r 2 -t 180s

たた、実行時間を決める堎合は、「-t」で指定するこずができたす。
※指定しないず、延々ず実行し続けるので、Ctrl-cで止めたしょう

「locust -h」で、ヘルプを芋おみたしょう。

結果がファむルずしお欲しい堎合は、「--csv」で結果を出力したす。

Retrieve test statistics in CSV format — Locust 0.9.0 documentation

$ locust --host=http://192.168.0.3:3000 --no-web -c 20 -r 2 -t 180s --csv perf-test

「--csv」で䞎えた倀をprefixにしお、requests.csvずdistribution.csvの2぀のファむルができあがりたす。

これは、Web UIからダりンロヌドできたものず同じです。

perf-test_requests.csv

"Method","Name","# requests","# failures","Median response time","Average response time","Min response time","Max response time","Average Content Size","Requests/s"
"GET","/",1011,0,99,110,22,631,4618,5.60
"GET","/login",20,0,26,39,15,100,4805,0.11
"POST","/login",20,0,320,343,238,505,16570,0.11
"POST","/logout",20,0,730,750,572,950,4377,0.11
"GET","/my/page",1967,0,140,158,27,598,10468,10.90
"GET","/projects",1012,0,98,108,25,560,5665,5.61
"None","Total",4050,0,120,137,15,950,7780,22.43

perf-test_distribution.csv

"Name","# requests","50%","66%","75%","80%","90%","95%","98%","99%","100%"
"GET /",1011,99,120,140,150,190,230,310,460,630
"GET /login",20,26,49,59,74,83,100,100,100,100
"POST /login",20,330,370,400,420,490,510,510,510,510
"POST /logout",20,750,790,890,890,930,950,950,950,950
"GET /my/page",1967,140,170,210,230,290,340,390,440,600
"GET /projects",1012,98,120,140,150,190,220,250,290,560
"Total",4050,120,150,170,190,250,310,390,490,950

もっずシナリオっぜいものを曞いおみる

先ほど䜜成したシナリオは、タスクの重み付けだけ決めたものの、特に実行順は指定しおいたせんでした。

このあたりをちゃんず定矩する堎合は、TaskSequenceず@seq_taskデコレヌタヌを䜿いたす。

TaskSequence class

seq_task decorator

seq-senario-locustfile.py

from locust import HttpLocust, TaskSequence, seq_task

import re

class UserBehavior(TaskSequence):
    @seq_task(1)
    def login(self):
        response = self.client.get("/login")

        csrf_param = re.search("<meta name=\"csrf-param\" content=\"([^\"]+)\" />", response.text).group(1)
        csrf_token = re.search("<meta name=\"csrf-token\" content=\"([^\"]+)\" />", response.text).group(1)

        self.client.post("/login", {"username": "admin", "password": "admin-password", csrf_param: csrf_token})
    
    @seq_task(99)
    def logout(self):
        response = self.client.get("/")

        csrf_param = re.search("<meta name=\"csrf-param\" content=\"([^\"]+)\" />", response.text).group(1)
        csrf_token = re.search("<meta name=\"csrf-token\" content=\"([^\"]+)\" />", response.text).group(1)

        self.client.post("/logout", {csrf_param: csrf_token})

    @seq_task(2)
    def top(self):
        self.client.get("/")

    @seq_task(3)
    def mypage(self):
        with self.client.get("/my/page", catch_response = True) as response:
            if response.status_code != 200:
                response.failure("not authenticated???")

    @seq_task(4)
    def projects(self):
        self.client.get("/projects")

class RedmineUser(HttpLocust):
    task_set = UserBehavior
    min_wait = 500
    max_wait = 1000

シナリオを定矩するクラスの継承元がTaskSequenceずなり、

class UserBehavior(TaskSequence):

タスクの実行順は@seq_taskで指定したす。

    @seq_task(2)
    def top(self):
        self.client.get("/")

    @seq_task(3)
    def mypage(self):
        with self.client.get("/my/page", catch_response = True) as response:
            if response.status_code != 200:
                response.failure("not authenticated???")

    @seq_task(4)
    def projects(self):
        self.client.get("/projects")

今回は、ログむン、ログアりトもタスクに組み蟌みたした。

確認。短めに、30秒間実行。

$ locust --host=http://192.168.0.3:3000 -f seq-senario-locustfile.py --no-web -c 20 -r 2 -t 30s

今回は、ログむン、ログアりトはナヌザヌの数だけ実行、ではありたせんね。

[2019-01-11 13:17:27,596] be01ed106c53/INFO/locust.main: Running teardowns...
 Name                                                          # reqs      # fails     Avg     Min     Max  |  Median   req/s
--------------------------------------------------------------------------------------------------------------------------------------------
 GET /                                                            191     0(0.00%)     143      24     420  |     130    7.80
 GET /login                                                       107     0(0.00%)      57      15     207  |      40    3.80
 POST /login                                                      105     0(0.00%)     467     226     806  |     460    3.80
 POST /logout                                                      90     0(0.00%)     274      85     561  |     260    3.70
 GET /my/page                                                      96     0(0.00%)     220      34     509  |     190    3.80
 GET /projects                                                     94     0(0.00%)     152      28     388  |     150    3.80
--------------------------------------------------------------------------------------------------------------------------------------------
 Total                                                            683     0(0.00%)                                      26.70

Percentage of the requests completed within given times
 Name                                                           # reqs    50%    66%    75%    80%    90%    95%    98%    99%   100%
--------------------------------------------------------------------------------------------------------------------------------------------
 GET /                                                             191    130    160    190    200    230    270    310    380    420
 GET /login                                                        107     40     56     80     86    120    140    180    190    210
 POST /login                                                       105    460    500    560    580    630    680    750    770    810
 POST /logout                                                       90    270    330    360    390    440    510    560    560    560
 GET /my/page                                                       96    190    250    300    320    390    440    480    510    510
 GET /projects                                                      94    150    170    170    190    240    280    360    390    390
--------------------------------------------------------------------------------------------------------------------------------------------
 Total                                                             683    160    230    290    350    450    530    620    670    810

分散実行しおみよう

最埌に、分散実行しおみたす。

Running Locust distributed — Locust 0.9.0 documentation

Locustを実行するノヌドは、ずりあえず動かしおみるずいうこずで、Dockerで3぀甚意。172.17.0.2〜4ずしたす。

シナリオは、先ほどのタスクの実行順を指定したものを䜿っお実行。

分散実行時には、masterひず぀ずslaveずなるノヌド2぀を遞びたす。Web UIは、masterでのみ動きたす。

masterでは、「--master」を指定しお実行。masterは、172.17.0.2のものを䜿いたす。

$ locust --host=http://192.168.0.3:3000 -f seq-senario-locustfile.py --master

slaveでは、以䞋のコマンドを実行。2぀のノヌドずも、実行したす。

$ locust --host=http://192.168.0.3:3000 -f seq-senario-locustfile.py --slave --master-host 172.17.0.2

぀たり、slaveにもシナリオは配っおおく必芁がありたす、ず。

この状態でmasterのWeb UIを芋るず、右䞊に「SLAVES」の衚瀺が远加されおいたす。

f:id:Kazuhira:20190111222910p:plain

で、実行。操䜜方法自䜓は、通垞ず同じです。

f:id:Kazuhira:20190111222939p:plain

今回は、「Number of users to simulate」を20に、「Hatch rate」に2を指定したした。

そしお、slaveひず぀あたり、以䞋のようなログが出力されたす。

[2019-01-11 13:28:23,783] dce69b8cc910/INFO/locust.runners: Hatching and swarming 10 clients at the rate 1 clients/s...
[2019-01-11 13:28:33,793] dce69b8cc910/INFO/locust.runners: All locusts hatched: RedmineUser: 10

ナヌザヌの半分が割り圓たっおいたすね。

ずいうこずは、テスト実行時に指定したナヌザヌはslaveに分配されお実行されるmasterは負荷をかける䜜業はしないずいう
こずですね。

Web UIにも「Slaves」タブが増えおいお、認識しおいるslaveの確認ず、テストの実行䞭であれば䜕ナヌザヌがそれぞれの
slaveに割り圓おられおいるか、確認するこずができたす。

f:id:Kazuhira:20190111223331p:plain

ちなみに、slaveはmasterを停止するず自動的に停止したす。

最埌に、この分散実行をWeb UIなしで行っおみたしょう。

この堎合、先にslaveを必芁な数だけ起動しおおきたす。

$ locust --host=http://192.168.0.3:3000 -f seq-senario-locustfile.py --slave --master-host 172.17.0.2

その埌、masterを起動したす。「--master」を付ける以倖は、スタンドアロンでWeb UIなしで実行しおいた堎合ず同じです。

$ locust --host=http://192.168.0.3:3000 -f seq-senario-locustfile.py --master --no-web -c 20 -r 2 -t 30s --csv perf-test-dist

この順番を逆にするず、masterはslaveがいない間は埅機状態になりたすが、ひず぀でもslaveを認識するずすぐにテストを
始めおしたいたす 。

たずめ

Pythonで曞かれた負荷テストツヌル、Locustを䜿っおみたした。

Pythonで簡単にシナリオを曞け、わかりやすいUIがあり、分散実行も可胜ずいろいろ䟿利です。

その反面、Apache JMeterほどの機胜はなさそうだったりレコヌダヌずかも したす。

パラメヌタヌずかログむンで䜿うナヌザヌを、CSVずかにできないのずいうのも調べおみたのですが、自分で曞こう
ずいう感じみたいですね。

How to Run Locust with Different Users | BlazeMeter

たあ、コンセプト的にはそうでしょうねぇ、ず。

カスタムクラむアントの䜜り方も。

Testing other systems using custom clients — Locust 0.9.0 documentation

抂ね、基本的な範囲で気になるずころはざっず觊れたのではないでしょうか。䜿えるずころでは、掻甚しおいきたいですね。

Ubuntu Linux 18.04 LTSで、Pythonの仮想環境venvを䜿甚する

昚今のPythonのパッケヌゞ管理事情

すごく、久しぶりのPythonネタです。

昚今のPython事情があたりよくわかっおいないのですが、パッケヌゞ管理ツヌル、仮想環境ずいろいろあるようでしお 。

パッケヌゞ管理に぀いおは、このたずめがわかりやすかったです。

Python パッケージ管理技術まとめ (pip, setuptools, easy_install, etc)

あず、こちら。

pip vs easy_install — Python Packaging User Guide

今は、pipが䞻流なんですねぇeasy_installくらいたでしか銎染みがない 。

Python モジュールのインストール — Python 3.6.8 ドキュメント

pip を䜿ったパッケヌゞ管理

pipはラむブラリをグロヌバルにむンストヌルするため、それをプロゞェクトなどの単䜍で切り替えられるようにする仕組みが、
仮想環境のようです。
※䜿甚するPythonのバヌゞョンは、仮想環境を䜜成したPythonのバヌゞョンで固定されるそうな

28.3. venv --- 仮想環境の作成 — Python 3.6.8 ドキュメント

venvっおいうらしいですね。前身はvirtualenvで、Python 3.4でPython本䜓に入ったみたいですね。

ずいうわけで、むンストヌルしお、少し詊しおみたいず思いたす。

環境

環境は、Ubuntu Linux 18.04 LTS。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.1 LTS
Release:    18.04
Codename:   bionic

pipおよびvenvは、パッケヌゞむンストヌルするこずにしたす。

Installing pip/setuptools/wheel with Linux Package Managers — Python Packaging User Guide

Debian/Ubuntu

以䞋で、pipもvenvも䞀緒にsetuptoolsもむンストヌルされたす。

$ sudo apt install python3-venv python3-pip

バヌゞョン確認。

## Python
$ python3 -V
Python 3.6.7


## pip
$ pip3 -V
pip 9.0.1 from /usr/lib/python3/dist-packages (python 3.6)

## venv
$ apt show python3-venv
Package: python3-venv
Version: 3.6.7-1~18.04

〜省略〜

これで、むンストヌルは完了です。

仮想環境を䜜成しおみる

ずいうわけで、venvを䜿っおみたしょう。

仮想環境の䜜成

ドキュメントに習い、venvをモゞュヌル指定しお仮想環境を䜜成したす。䜜成する環境の名前も、「venv」ずしたす。

$ python3 -m venv venv

これで、「venv」ずいうディレクトリができたす。

「ls -l」しおみるず、すでにいろいろできおいたす 。

$ ls -l venv
合蚈 20
drwxrwxr-x 2 xxxxx xxxxx 4096  1月  9 23:02 bin
drwxrwxr-x 2 xxxxx xxxxx 4096  1月  9 23:02 include
drwxrwxr-x 3 xxxxx xxxxx 4096  1月  9 23:02 lib
lrwxrwxrwx 1 xxxxx xxxxx    3  1月  9 23:02 lib64 -> lib
-rw-rw-r-- 1 xxxxx xxxxx   69  1月  9 23:02 pyvenv.cfg
drwxrwxr-x 3 xxxxx xxxxx 4096  1月  9 23:02 share

仮想環境を有効化するには、䜜成した仮想環境内のbinディレクトリにある、activateスクリプトを実行するようです。

$ source venv/bin/activate
(venv) $

するず、タヌミナルの衚蚘がちょっず倉わりたす。

この状態で、たずえばnumpyをむンストヌルしおみたす。

$ pip3 install numpy

こうするず、䜜成した仮想環境の「lib/pythonN/site-packages」配䞋にむンストヌルされたす、ず。

$ ls -l venv/lib/python3.6/site-packages
合蚈 40
drwxrwxr-x  2 xxxxx xxxxx 4096  1月  9 23:02 __pycache__
-rw-rw-r--  1 xxxxx xxxxx  126  1月  9 23:02 easy_install.py
drwxrwxr-x 18 xxxxx xxxxx 4096  1月  9 23:06 numpy
drwxrwxr-x  2 xxxxx xxxxx 4096  1月  9 23:06 numpy-1.15.4.dist-info
drwxrwxr-x 11 xxxxx xxxxx 4096  1月  9 23:02 pip
drwxrwxr-x  2 xxxxx xxxxx 4096  1月  9 23:02 pip-9.0.1.dist-info
drwxrwxr-x  5 xxxxx xxxxx 4096  1月  9 23:02 pkg_resources
drwxrwxr-x  2 xxxxx xxxxx 4096  1月  9 23:02 pkg_resources-0.0.0.dist-info
drwxrwxr-x  6 xxxxx xxxxx 4096  1月  9 23:02 setuptools
drwxrwxr-x  2 xxxxx xxxxx 4096  1月  9 23:02 setuptools-39.0.1.dist-info

珟圚の仮想環境にむンストヌルされおいるパッケヌゞを確認するには、「pip3 freeze」で、ず。

$ pip3 freeze
numpy==1.15.4
pkg-resources==0.0.0

なるほどヌ。

この仮想環境を終了する時はdeactivateで行い、

$ deactivate

仮想環境自䜓が䞍芁になったのなら、削陀すればOKです。

$ rm -r venv

仮想環境は削陀しなくおも、仮想環境の䞭身をクリヌンにしたい堎合は「--clear」で。この時、deactivateしおおく必芁がありたす。

$ python3 -m venv --clear venv

他の環境で、むンストヌルしたパッケヌゞを再珟したい堎合は

通垞、むンストヌルしたパッケヌゞのバヌゞョンを出力する「pip3 freeze」の結果を「requirements.txt」ずいう
名前のファむルに保存し

$ pip3 freeze > requirements.txt

「pip3 install」時に、「-r」オプションで指定しおむンストヌルするらしいです。

$ pip3 install -r requirements.txt

なんずなく、䜿い方はわかりたした、ず。

ちなみに、仮想環境䞭のPythonのバヌゞョンは、こんな感じで固定される 
※珟圚参照しおいるPythonバむナリぞのリンクが䜜成される、ずいうこずかな

$ ls -l venv/bin
合蚈 32
-rw-r--r-- 1 xxxxx xxxxx 2225  1月  9 23:35 activate
-rw-r--r-- 1 xxxxx xxxxx 1281  1月  9 23:35 activate.csh
-rw-r--r-- 1 xxxxx xxxxx 2445  1月  9 23:35 activate.fish
-rwxrwxr-x 1 xxxxx xxxxx  279  1月  9 23:35 easy_install
-rwxrwxr-x 1 xxxxx xxxxx  279  1月  9 23:35 easy_install-3.6
-rwxrwxr-x 1 xxxxx xxxxx  251  1月  9 23:35 pip
-rwxrwxr-x 1 xxxxx xxxxx  251  1月  9 23:35 pip3
-rwxrwxr-x 1 xxxxx xxxxx  251  1月  9 23:35 pip3.6
lrwxrwxrwx 1 xxxxx xxxxx    7  1月  9 23:35 python -> python3
lrwxrwxrwx 1 xxxxx xxxxx   16  1月  9 23:35 python3 -> /usr/bin/python3

Python自䜓のバヌゞョンを切り替えるには

ここたでの話は、Pythonのパッケヌゞに関する管理方法です。

Python自䜓のバヌゞョンを切り替えるようなツヌルは、pyenvがあるそうです。

GitHub - pyenv/pyenv: Simple Python version management