delhi09の勉強日記

技術トピック専用のブログです。自分用のメモ書きの投稿が多いです。あくまで「勉強日記」なので記事の内容は鵜呑みにしないでください。

「WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.」について

Amazon Linux 2上でpip installしようとしたところ、以下の警告に遭遇したので、調べたことを書く。

WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead

まとめ

  1. root(sudo)でpip install作業をすることに対するセキュリティ的な警告ではない。
  2. パッケージをグローバルインストールすることでパッケージがコンフリクトするリスクがあることを警告している。
  3. pip installにはグローバルインストール(デフォルト)と--userをオプションに付けるローカルインストールが存在する。
  4. 上記1〜3を理解した上でパッケージをグローバルインストールしたい場合は無視してよい(と私は理解した)。

環境

事象

rootユーザーでpip installしようとしたところ、以下のように前述の警告が出た。(インストール自体はできる。)

[root@ip-172-31-27-206 ~]# pip3 install django
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting django
  Using cached https://files.pythonhosted.org/packages/9d/04/04abb097c84c770180eeebe7ed920ce42f9917ab5ad4de01ff8ed11bc25b/Django-3.0.6-py3-none-any.whl
Requirement already satisfied: pytz in /usr/local/lib/python3.7/site-packages (from django)
Requirement already satisfied: asgiref~=3.2 in /usr/local/lib/python3.7/site-packages (from django)
Requirement already satisfied: sqlparse>=0.2.2 in /usr/local/lib/python3.7/site-packages (from django)
Installing collected packages: django
Successfully installed django-3.0.6
[root@ip-172-31-27-206 ~]#

rootユーザーで直接pip installしようとしたことに対する警告なのかと思い、ec2-userからsudoで実行してみたが、同じ警告が出る。

[ec2-user@ip-172-31-27-206 ~]$ sudo pip3 install django
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting django
  Using cached https://files.pythonhosted.org/packages/9d/04/04abb097c84c770180eeebe7ed920ce42f9917ab5ad4de01ff8ed11bc25b/Django-3.0.6-py3-none-any.whl
Requirement already satisfied: asgiref~=3.2 in /usr/local/lib/python3.7/site-packages (from django)
Requirement already satisfied: pytz in /usr/local/lib/python3.7/site-packages (from django)
Requirement already satisfied: sqlparse>=0.2.2 in /usr/local/lib/python3.7/site-packages (from django)
Installing collected packages: django
Successfully installed django-3.0.6
[ec2-user@ip-172-31-27-206 ~]$

とりあえずインストールはできるので、検証用の環境であれば気にしなくてよいかもしれないが、ちゃんとしたサーバー構築の際にはこういうのは大事なので、調べてみることにした。

パッケージがインストールされるディレクト

そもそも、pip installしたパッケージはどこに保存されているのかというところから確認してみる。

以下のようにpip showを実行すると、パッケージは/usr/local/lib64/python3.7/site-packages/〜配下に保存されていることが分かる。

[root@ip-172-31-27-206 ~]# pip3 show django | grep "Location"
Location: /usr/local/lib64/python3.7/site-packages
[root@ip-172-31-27-206 ~]#

ls/usr/local/lib64/python3.7/site-packages/〜配下を確認すると、確かにインストールしたパッケージが保存されていることを確認できる。

[root@ip-172-31-27-206 ~]# ls /usr/local/lib64/python3.7/site-packages
56fa58acf89604978a41__mypyc.cpython-37m-x86_64-linux-gnu.so
django
Django-3.0.6.dist-info
mypy
mypy-0.770.dist-info
mypyc
regex
regex-2020.5.14.dist-info
typed_ast
typed_ast-1.4.1.dist-info
[root@ip-172-31-27-206 ~]#

rootユーザー(sudo)以外ではインストールできない。

「Running pip install with root privileges is generally not a good idea.」と警告されるが、以下の通り、/usr/local/lib64/python3.7/site-packagesはrootユーザー以外には書き込み権限がないので、rootユーザー(sudo)以外ではpip installで該当ディレクトリ配下にパッケージをインストールすることはできない。

[root@ip-172-31-27-206 ~]# ls -ld /usr/local/lib64/python3.7/site-packages
drwxr-xr-x 11 root root 266 May 21 15:27 /usr/local/lib64/python3.7/site-packages
[root@ip-172-31-27-206 ~]#

実際に、ec2-userでsudoなしでpip installしようとすると、「Permission denied」が出て失敗する。

[ec2-user@ip-172-31-27-206 ~]$ pip3 install requests
Collecting requests
  Downloading https://files.pythonhosted.org/packages/1a/70/1935c770cb3be6e3a8b78ced23d7e0f3b187f5cbfab4749523ed65d7c9b1/requests-2.23.0-py2.py3-none-any.whl (58kB)
    100% |████████████████████████████████| 61kB 3.0MB/s
Collecting chardet<4,>=3.0.2 (from requests)
  Downloading https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl (133kB)
    100% |████████████████████████████████| 143kB 5.1MB/s
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 (from requests)
  Downloading https://files.pythonhosted.org/packages/e1/e5/df302e8017440f111c11cc41a6b432838672f5a70aa29227bf58149dc72f/urllib3-1.25.9-py2.py3-none-any.whl (126kB)
    100% |████████████████████████████████| 133kB 7.6MB/s
Collecting idna<3,>=2.5 (from requests)
  Downloading https://files.pythonhosted.org/packages/89/e3/afebe61c546d18fb1709a61bee788254b40e736cff7271c7de5de2dc4128/idna-2.9-py2.py3-none-any.whl (58kB)
    100% |████████████████████████████████| 61kB 9.3MB/s
Collecting certifi>=2017.4.17 (from requests)
  Downloading https://files.pythonhosted.org/packages/57/2b/26e37a4b034800c960a00c4e1b3d9ca5d7014e983e6e729e33ea2f36426c/certifi-2020.4.5.1-py2.py3-none-any.whl (157kB)
    100% |████████████████████████████████| 163kB 6.6MB/s
Installing collected packages: chardet, urllib3, idna, certifi, requests
Exception:
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/pip/basecommand.py", line 215, in main
    status = self.run(options, args)
  File "/usr/lib/python3.7/site-packages/pip/commands/install.py", line 365, in run
    strip_file_prefix=options.strip_file_prefix,
  File "/usr/lib/python3.7/site-packages/pip/req/req_set.py", line 784, in install
    **kwargs
  File "/usr/lib/python3.7/site-packages/pip/req/req_install.py", line 854, in install
    strip_file_prefix=strip_file_prefix
  File "/usr/lib/python3.7/site-packages/pip/req/req_install.py", line 1069, in move_wheel_files
    strip_file_prefix=strip_file_prefix,
  File "/usr/lib/python3.7/site-packages/pip/wheel.py", line 345, in move_wheel_files
    clobber(source, lib_dir, True)
  File "/usr/lib/python3.7/site-packages/pip/wheel.py", line 316, in clobber
    ensure_dir(destdir)
  File "/usr/lib/python3.7/site-packages/pip/utils/__init__.py", line 83, in ensure_dir
    os.makedirs(path)
  File "/usr/lib64/python3.7/os.py", line 221, in makedirs
    mkdir(name, mode)
PermissionError: [Errno 13] Permission denied: '/usr/local/lib/python3.7/site-packages/chardet'
[ec2-user@ip-172-31-27-206 ~]$

/usr/local/〜配下は何を配置する場所か?

脇道に逸れてしまうが、Linux/usr/localというディレクトリの配下は、そもそも何を配置する場所なのか?というのも気になったので、ついでに調べてみた。

日本語の記事では以下の2つがとても分かりやすかった。
dqn.sakusakutto.jp

qiita.com

また、以下はLinux Foundationの公式ドキュメントである。
refspecs.linuxfoundation.org

The /usr/local hierarchy is for use by the system administrator when installing software locally. It needs to be safe from being overwritten when the system software is updated. It may be used for programs and data that are shareable amongst a group of hosts, but not found in /usr.


Locally installed software must be placed within /usr/local rather than /usr unless it is being installed to replace or upgrade software in /usr.

/usr/local階層は、ソフトウェアをローカルにインストールするときにシステム管理者が使用するためのものです。システムソフトウェアが更新されたときに上書きされないようにする必要があります。ホストのグループ間で共有可能であるが、/usrにはないプログラムおよびデータに使用できます。


ローカルにインストールされたソフトウェアは、/usrのソフトウェアを置換またはアップグレードするためにインストールされている場合を除き、/usrではなく/usr/local内に配置する必要があります。
(Google翻訳)

「local」という単語からローカルインストールをイメージしてしまうので、直観に反していて紛らわしいが、ユーザー間で共有したいソフトウェアを配置する際には、Linuxの世界では標準的に/usr/local配下を使用するのだというくらいに理解しておくことにした。

pipの--userオプションについて

「`pip3 install --user` instead」の意味について調べていると、以下の記事を見つけた。
pyteyon.hatenablog.com

pipには--userというオプションがあり、これを付けるとユーザーのホームディレクトリ配下にパッケージがインストールされるとのことである。

実際にやってみた。

[ec2-user@ip-172-31-27-206 ~]$ pip3 install --user requests
Collecting requests
  Using cached https://files.pythonhosted.org/packages/1a/70/1935c770cb3be6e3a8b78ced23d7e0f3b187f5cbfab4749523ed65d7c9b1/requests-2.23.0-py2.py3-none-any.whl
Collecting idna<3,>=2.5 (from requests)
  Using cached https://files.pythonhosted.org/packages/89/e3/afebe61c546d18fb1709a61bee788254b40e736cff7271c7de5de2dc4128/idna-2.9-py2.py3-none-any.whl
Collecting chardet<4,>=3.0.2 (from requests)
  Using cached https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 (from requests)
  Using cached https://files.pythonhosted.org/packages/e1/e5/df302e8017440f111c11cc41a6b432838672f5a70aa29227bf58149dc72f/urllib3-1.25.9-py2.py3-none-any.whl
Collecting certifi>=2017.4.17 (from requests)
  Using cached https://files.pythonhosted.org/packages/57/2b/26e37a4b034800c960a00c4e1b3d9ca5d7014e983e6e729e33ea2f36426c/certifi-2020.4.5.1-py2.py3-none-any.whl
Installing collected packages: idna, chardet, urllib3, certifi, requests
Successfully installed certifi-2020.4.5.1 chardet-3.0.4 idna-2.9 requests-2.23.0 urllib3-1.25.9
[ec2-user@ip-172-31-27-206 ~]$ 

今回はインストールに成功した。

確かに、ホームディレクトリの.local配下にパッケージがインストールされている。

[ec2-user@ip-172-31-27-206 ~]$ pwd
/home/ec2-user
[ec2-user@ip-172-31-27-206 ~]$ tree -L 4 .local
.local
├── bin
│   └── chardetect
└── lib
    └── python3.7
        └── site-packages
            ├── certifi
            ├── certifi-2020.4.5.1.dist-info
            ├── chardet
            ├── chardet-3.0.4.dist-info
            ├── idna
            ├── idna-2.9.dist-info
            ├── requests
            ├── requests-2.23.0.dist-info
            ├── urllib3
            └── urllib3-1.25.9.dist-info

14 directories, 1 file
[ec2-user@ip-172-31-27-206 ~]$

pip showで確認すると、Locationも/home/ec2-user/.local配下になっている。

[ec2-user@ip-172-31-27-206 ~]$ pip3 show requests | grep "Location"
Location: /home/ec2-user/.local/lib/python3.7/site-packages
[ec2-user@ip-172-31-27-206 ~]$

同じパッケージを「--user」を付けずにインストールしてみるとどうなるか?

今度は、同じパッケージを「--user」を付けずにインストールしてみるとどうなるか(インストールできるのか?コンフリクトしないのか?)というのが気になったので、再度rootユーザーになって実験してみた。

[root@ip-172-31-27-206 ~]# pip3 install requests
WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead.
Collecting requests
  Downloading https://files.pythonhosted.org/packages/1a/70/1935c770cb3be6e3a8b78ced23d7e0f3b187f5cbfab4749523ed65d7c9b1/requests-2.23.0-py2.py3-none-any.whl (58kB)
    100% |████████████████████████████████| 61kB 2.6MB/s
Collecting certifi>=2017.4.17 (from requests)
  Downloading https://files.pythonhosted.org/packages/57/2b/26e37a4b034800c960a00c4e1b3d9ca5d7014e983e6e729e33ea2f36426c/certifi-2020.4.5.1-py2.py3-none-any.whl (157kB)
    100% |████████████████████████████████| 163kB 5.8MB/s
Collecting idna<3,>=2.5 (from requests)
  Downloading https://files.pythonhosted.org/packages/89/e3/afebe61c546d18fb1709a61bee788254b40e736cff7271c7de5de2dc4128/idna-2.9-py2.py3-none-any.whl (58kB)
    100% |████████████████████████████████| 61kB 9.3MB/s
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 (from requests)
  Downloading https://files.pythonhosted.org/packages/e1/e5/df302e8017440f111c11cc41a6b432838672f5a70aa29227bf58149dc72f/urllib3-1.25.9-py2.py3-none-any.whl (126kB)
    100% |████████████████████████████████| 133kB 7.8MB/s
Collecting chardet<4,>=3.0.2 (from requests)
  Downloading https://files.pythonhosted.org/packages/bc/a9/01ffebfb562e4274b6487b4bb1ddec7ca55ec7510b22e4c51f14098443b8/chardet-3.0.4-py2.py3-none-any.whl (133kB)
    100% |████████████████████████████████| 143kB 7.7MB/s
Installing collected packages: certifi, idna, urllib3, chardet, requests
Successfully installed certifi-2020.4.5.1 chardet-3.0.4 idna-2.9 requests-2.23.0 urllib3-1.25.9
[root@ip-172-31-27-206 ~]#

インストールには成功した。
pip showで確認すると、rootユーザーの場合は向き先は/usr/local/lib/python3.7/site-packagesの方を向いている。

[root@ip-172-31-27-206 ~]# pip3 show requests | grep "Location"
Location: /usr/local/lib/python3.7/site-packages
[root@ip-172-31-27-206 ~]#

ec2-userでは/home/ec2-user/.localの方を向いている。

[ec2-user@ip-172-31-27-206 ~]$ pip3 show requests | grep "Location"
Location: /home/ec2-user/.local/lib/python3.7/site-packages
[ec2-user@ip-172-31-27-206 ~]$

探索の優先順位に関するドキュメントの記述は見つけられなかったが、ローカルとグローバルで同じパッケージがインストールされている場合は、ローカルインストールしたuserでログインしている場合には、ローカルインストールした方が使われる仕様になっているようである。

なぜ警告が出力されるのか?

エラーメッセージで検索したところ、最初に以下のissueがヒットした。
github.com

このissueには、さらに以下のissueを読めとコメントがあるので、流し読みしたところ、警告が出力される経緯がなんとなく理解できた。
github.com

上記のissueは--userオプションをデフォルトにしようという提案である。(2020/5/22時点では未実装)

以下のコメントにあるように、この提案に至った背景は、元々はセキュリティの問題ではなく、パッケージをグローバルに管理することによるコンフリクトを防ごうという意図である。

sudo and security is really a secondary issue IMO.
this is primarily about preventing the conflict between OS packaging and pip in the system python.
Currently, linux users are often placed in an impossible situation.

--userをデフォルトにするまでの橋渡しとして、警告を出すようにしているのだろうか。

また、FedoraはデフォルトでPython3系がインストールされているらしいので、OSのシステム管理で使用されているパッケージとアプリケーションで使用するパッケージがコンフリクトしてしまうということがあるのかもしれない。(isuueを挙げている人もFedoraを使用していた。)

www.python.jp

この辺は私はFedora使ったことがないので分からないが、機会があれば検証してみたい。

Amzon Linux2の場合、デフォルトでインストールされているPythonは2系(2020/5/22時点)なので、Python3はアプリケーション用途専用で使用できるので、この点は考慮しなくて良い。

まとめ

「WARNING: Running pip install with root privileges is generally not a good idea. Try `pip3 install --user` instead」の意味、及び結論については以下の通り。

  1. root(sudo)でpip install作業をすることに対するセキュリティ的な警告ではない。
  2. パッケージをグローバルインストールすることでパッケージがコンフリクトするリスクがあることを警告している。
  3. pip installにはグローバルインストール(デフォルト)と--userをオプションに付けるローカルインストールが存在する。
  4. 上記1〜3を理解した上でパッケージをグローバルインストールしたい場合は無視してよい(と私は理解した)。