オフライン環境でpyenv、pip、pythonを使用できるまでの完全ガイドブック

## 対象者
前提知識無しで、ネットワークに接続させたくないマシンに、様々なライブラリを導入し、pythonファイルを自由に実行できるようにしたい方へ

## はじめに
この記事を書こうと思った理由は、僕のように前提知識がほぼ皆無の方に向けられた、０からオフライン環境で自由にpythonファイルを実行するまでまとめられた資料が無いように感じ、（部分的なものは数多くあります）
オンライン環境ではかなり簡単に環境構築できたものの、オフライン環境ではオンライン環境程、単純ではなく、osの理解やその他諸々の理解が必要で、環境構築するまで2~3日も掛かかってしまった為です。

また分からない箇所は、その都度調べ、理解しながら環境を構築したので、余分な処理が紛れ込んでる可能性がありますが、その際はコメントして頂けるととても嬉しいです。

## 必要なもの

・オンライン環境下にあるマシン
・オフライン環境のマシン

## マシンのOSの状況

僕のマシンでは、

・オンライン環境下にあるマシン（macOS)
・オフライン環境のマシン（CentOS Linux）

## 読者へ

もしこの流れでもエラーが生じてしまった場合、コメントにエラー内容を残しておいて下さい。
確認できる時に回答させて頂きます。

## 前提条件

オンライン環境下にあるマシンとオフライン環境のマシンとはSSHで接続してある事

## 目次

・オンライン環境での事前環境作成(pyenvまで）
・オフライン環境での環境作成(pyenvまで）(僕が結構、詰まった箇所）
・オンライン環境でのpip作成
・オフライン環境でのpip作成
・オンライン環境での必要なライブラリのダウンロード(僕が結構、詰まった箇所）
・オフライン環境での必要なライブラリのダウンロード(僕が結構、詰まった箇所）

## オンライン環境での事前環境作成(pyenvまで）

本作業はオンライン環境下でのマシンで行ってください。

以下の例では、python3.6.5を入れます。

まずはどこでも良いので、.pyenvフォルダを格納する為のフォルダを作成します。
今回は、desktop/pyenvに保存する事にします。

ターミナル

```
$ mkdir ~/desktop/pyenv
$ cd ~/desktop/pyenv
```

###.pyenvフォルダをclone

ターミナル

```
$ git clone git://github.com/yyuu/pyenv.git .pyenv
```

### python3.6.5用のプラグインファイルを確認

このファイルを参照してリモートから取得するので対象ファイル名を確認しておきましょう

```
$ cat .pyenv/plugins/python-build/share/python-build/3.6.5
#require_gcc
install_package "openssl-1.0.2k" "https://www.openssl.org/source/old/1.0.2/openssl-1.0.2k.tar.gz#6b3977c61f2aedf0f96367dcfb5c6e578cf37e7b8d913b4ecb6643c3cb88d8c0" mac_openssl --if has_broken_mac_openssl
install_package "readline-8.0" "https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz#e339f51971478d369f8a053a330a190781acb9864cf4c541060f12078948e461" mac_readline --if has_broken_mac_readline
if has_tar_xz_support; then
  install_package "Python-3.6.5" "https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz#f434053ba1b5c8a5cc597e966ead3c5143012af827fd3f0697d21450bb8d87a6" ldflags_dirs standard verify_py36 copy_python_gdb ensurepip
else
  install_package "Python-3.6.5" "https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tgz#53a3e17d77cd15c5230192b6a8c1e031c07cd9f34a2f089a731c6f6bd343d5c6" ldflags_dirs standard verify_py36 copy_python_gdb ensurepip
fi
```

※brewを使用してしまうと、上記コマンドは打てません。

### 関連ファイルダウンロード

wgetを使用して、対象ファイルをリモートから取得します。
wgetコマンドが使えない方は<a href="https://webkaru.net/dev/mac-wget-command-install/">こちら</a>を参照して、wgetコマンドを使用できるようにするか、
対象ファイルを別の形でダウンロードできた状態にして下さい。
wgetコマンド用いる際は下記のようにダウンロードします。

```
$ wget https://www.openssl.org/source/old/1.0.2/openssl-1.0.2k.tar.gz
$ wget https://ftpmirror.gnu.org/readline/readline-8.0.tar.gz
$ wget https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz
$ wget https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tgz
```

### チェックサムを確認

md5sumを用いて行います。
md5sumコマンドが入力できない方は、md5sha1sumをインストールし、md5sumコマンドを使用できるようにして下さい。
brewコマンドを使用できるようにしておくと便利です。

```
$ md5sum *
f965fc0bf01bf882b31314b61391ae63  openssl-1.0.2k.tar.gz
????????????????????????????????  readline-8.0.tar.gz
????????????????????????????????  Python-3.6.5.tar.xz
????????????????????????????????  Python-3.6.5.tgz
```

上記のような感じで、確認できます。

### pluginファイル修正

①httpsをhttpに変更
②URLを「http://localhost/ 」に変更
③チェックサムをダウンロードしてきたファイルの値に変更

```
$ cp .pyenv/plugins/python-build/share/python-build/3.6.5 .
$ vi 3.6.5
#require_gcc
install_package "openssl-1.0.2k" "http://localhost/openssl-1.0.2k.tar.gz#f965fc0bf01bf882b31314b61391ae63" mac_openssl --if has_broken_mac_openssl
install_package "readline-8.0" "http://localhost/readline-8.0.tar.gz#????????????????????????????????" mac_readline --if has_broken_mac_readline
if has_tar_xz_support; then
  install_package "Python-3.6.5" "http://localhost/Python-3.6.5.tar.xz#????????????????????????????????" ldflags_dirs standard verify_py36 copy_python_gdb ensurepip
else
  install_package "Python-3.6.5" "http://localhost/Python-3.6.5.tgz#????????????????????????????????" ldflags_dirs standard verify_py36 copy_python_gdb ensurepip
fi
```

### アーカイブ化

```
$ tar cvfz pyenv_files.tar.gz readline-8.0.tar.gz openssl-1.0.2k.tar.gz Python-3.6.5.tar.xz Python-3.6.5.tgz 3.6.5
$ tar cvfz pyenv_local.tar.gz .pyenv
```

###  まとめたアーカイブをオフライン環境に転送

pyenv_files.tar.gz
pyenv_local.tar.gz
の2つをscpなどでオフライン環境下のhomeに転送して下さい。

ここで「オンライン環境での事前環境作成(pyenvまで）」は終了です。

## オフライン環境での環境作成(pyenvまで）

## アーカイブファイルの展開

※転送した箇所に移動して下さい。

homeに転送した場合は、一応、下記コマンドを打ちます。

```
$ cd ~/
```

先ほど固めたファイルをここで展開

```
$ tar xvfz pyenv_local.tar.gz
$ tar xvfz pyenv_files.tar.gz 
```

## 設定ファイルの記述

```
$ echo $SHELL
```

と入力し、

①実行結果が /bin/bash の場合

```
$ echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bash_profile
$ echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
$ . ~/.bash_profile
```

②実行結果が /bin/zsh の場合

```
$ echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
$ echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
$ echo 'eval "$(pyenv init -)"' >> ~/.zshrc
$ source ~/.zshrc
```

何をしたのかと言うと、「.bash_profile」もしくは「.zshrc」というファイルに設定用のコードを追加しました。

何が起きているのかを厳密に知りたい方は、<a href="https://qiita.com/panda11/private/67d1e09ec8261e4c19f4">こちら</a>を参照して下さい。

### 修正済みプラグインの配布

```
mv 3.6.5 .pyenv/plugins/python-build/share/python-build/
```

### git 構築

オフライン環境下のマシンでgitコマンドを使用できない方は、<a href="https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB">こちら</a>を参照してインストールして下さい。
コマンド一つです。

```
$ cd ~
$ git init 
$ git add Python-3.6.5.tgz openssl-1.0.2k.tar.gz readline-8.0.tar.gz Python-3.6.5.tar.xz
$ git commit -m'init'
```

### httpサーバー起動

bzip2-develをインストールし、使用できるようになると思います。

##### 使用できない方
<a href="http://www.s-yata.jp/docs/libbzip2/">こちら</a>からインストールできます
僕の場合は、オフライン環境では、Red Hat 系のcentOS linuxなので、
$ sudo yum install bzip2-devel
こちらのコマンド行けました。
linuxはosの種類によって、パッケージ管理ツールが変わってくるので、注意です。

##### 使用できる方

※80番ポートで起動する場合、root権限が必要

```
$ sudo python -m SimpleHTTPServer 80 &
```

もしエラーが出た場合は、<a href="http://oxydol.hatenablog.com/entry/2014/03/12/112654">こちら</a>を確認し、プロセスを切って再度行って下さい。

### pyenvをインストール

```
$ pyenv install 3.6.5
$ pyenv versions
* system (set by /home/hoge/.pyenv/version)
  3.6.5
$ pyenv global 3.6.5
$  pyenv versions
  system
* 3.6.5 (set by /home.hoge.pyenv/version)
$ which python; python -V
~/.pyenv/shims/python
Python 3.6.5
```

これでpython 3.6.5がオフライン上で使用できるようになりました。
もしここまででエラーなどが生じてしまっていたら、コメントに残しておいて下さい。


## オンライン環境でのpip作成

<a href="https://pypi.org/project/pip/#files">こちら</a>からご自身が導入したいpipのバージョンのファイルをダウンロードして下さい。
僕は、pip-20.3.3-py2.py3-none-any.whl をダウンロードしました。

このファイルをscpでオフライン環境のマシンに持っていきます。
僕は、自身で作成したフォルダの~/.local/libフォルダに転送しましたが、お好きな位置で大丈夫です。

## オフライン環境でのpip作成

.bash_profileでpathの設定をしているので、pipコマンドを入力する階層はどこでも大丈夫です。

ここからpip-20.3.3-py2.py3-none-any.whlをinstallするのですが、

①pipのupdateで行う（<a href="http://www.curict.com/item/5a/5ac72d2.html">参照する際のおすすめ記事</a>）
②一旦pipをuninstallしてから再installする（<a href="https://tech-diary.net/python-library-offline-install/">参照する際のおすすめ記事</a>）
③パスを指定して普通にinstallする

どの方法でもおそらく行えます。
今回は僕が実行できた「③パスを指定して普通にinstallする」を記述します。

##### ③について
pip-20.3.3-py2.py3-none-any.whl をダウンロードした階層に移動します

僕の場合は、pip-20.3.3-py2.py3-none-any.whlが~/.local/libに存在するので、下記コマンドを行います。

```
$ pip install --no-deps ~/.local/lib/pip-20.3.3-py2.py3-none-any.whl
```

結果として、
僕の場合は、pip-20.3.3-py2.py3-none-any.whl をダウンロードしたので、

```
$ pip list
pip 20.3.3 from ~(python 3.6)
```

となっていれば成功です。
要は、pipのバージョンがダウンロードしたものに変更されていれば成功です。


##オンライン環境での必要なライブラリのダウンロード

ここでは、numpyやpandasなど使用したいライブラリをオフライン環境下で使用できるようなダウンロード方法の記述になります。

### ダウンロードしてきたライブラリを格納するフォルダの作成

僕は、desktopにpip-cacheというフォルダ名で作成しましました。

```
$ cd ~/desktop
$ mkdir pip-cache
``` 

### 任意のライブラリをダウンロード

ここでpip downloadを行うのですが、オプションの記述が重要です。

一般的なオプションを付けない記述では、下記のようになります。

```
$ pip download -d ./pip-cache numpy #numpyをダウンロードする場合
```

これでpip-cacheフォルダにnumpyがダウンロードされるのですが、このnumpyをオフライン環境下のマシンに持っていき、pip install numpyというようなコマンドを打つとエラーが生じます。

理由は単純で、platformの違いです。
無知な僕はここに気付くのに少し時間がかかりました。

もっと具体的に記述すると、上記のpip downloadはオンライン環境下のマシンで行いました。
そして、僕のオンライン環境下のマシンのosの種類はmacOSです。
しかし、僕のオフライン環境下のマシンのosの種類はcentOS linuxです。

--platformのオプションを付けないでdownloadすると、macOS用のnumpyがインストールされるので、その理由で、オフライン環境下のマシンのcentOS linuxでは動かなかったという事です。

従って、下記のように、platformをcentOS linux用に宣言してあげます。

```
$ pip download --platform manylinux1_x86_64 -d ./pip-cache numpy #numpyをダウンロードする場合
```

これでオフライン環境下のマシンで動くようになりました。

### requirements.txtでダウンロード

ここも少し詰まった場面になります。
僕自身がこの時、本来行いたかったのは、こちらのrequirements.txtのダウンロードの方で、一般的な記述は、

requirements.txtをdesktopに置いたので、

```
$ pip download -d ./pip-cache -r ~/desktop/requirements.txt  #requirements.txt をダウンロードする場合
```

先程の--platformオプションを付けて、

```
$ pip download --platform manylinux1_x86_64 -d ./pip-cache -r ~/desktop/requirements.txt  #requirements.txt をダウンロードする場合
```

しかし、この記述ではエラーが吐かれるんですね。
この理由も分かってしまえば至って、シンプルなのですが、ライブラリの中にはplatformを記述する必要がないものがあるんですね。（互換性があるものとないものがある）

例えば、numpyの場合、
![スクリーンショット 2020-12-27 16.52.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677453/ce599c2b-3f38-b187-48fe-032d5fce7b6e.png)

次に、astorの場合、
![スクリーンショット 2020-12-27 16.53.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/677453/9a7d1b07-805c-35d6-2851-8f024529b3d4.png)

このように,numpyではplatformの記載がありますが、astorではplatformの記載がないです。
これが原因で、僕のrequirement.txtには、互換性があるものと無いものが入り乱れていました。

何か良いオプションは無いものかとエラー内容から確認した所、あるではありませんか、パッケージの依存関係をインストールしないオプションが、そう、--no-depsを使用します。

```
$ pip download --no-deps --platform manylinux1_x86_64 -d ./pip-cache -r ~/desktop/requirements.txt  #requirements.txt をダウンロードする場合
```

これで無事にできました。

### オフライン環境下のマシンにダウンロードしたライブラリをまとめて転送

そして、これまで同様、今回は、pip-cacheに必要なライブラリがダウンロードできたので、それをtarでアーカイブ化して、scpで転送します。この時、requirements.txtも送っておいた方が良いです。
pipの時と同様、僕は、自身で作成した.local/libに転送しました

##オフライン環境での必要なライブラリのダウンロード

それでは、転送した任意の階層に移動して、まとめて送られてきたアーカイブを解凍していきましょう。
僕の場合は、.local/libに転送したので、

```
$ cd ~/.local/lib
```

```
$ tar xf pip-cache.tar
```

最後にpip installを行います。
僕はrequirement.txtはhomeに転送していたので、下記の記述となります。

```
pip install --no-index --find-links=~/.local/lib/pip-cache -r ~/requirements.txt
```

ちなみに、--no-index --find-linksというオプションは、通常の参照先（PyPI）を検索しないで、ローカルにあるディレクトリの中身だけ（僕の場合は、~/.local/lib/pip-cacheの中身だけ）でインストールを実行します。

--no-index --find-linksが無いと、オフラインの場合はエラーが生じると思われますので注意が必要です。

以上で、オフライン環境下で必要なライブラリのインストールが完了しました。

## まとめ

この記事では、オフライン環境にpyenv、pip、pythonをインストールできたので、好きなようにpythonを動かせるようになりました。

また、この部分はもっと簡単にできるなど、色々ご意見ありましたら、コメントに残して頂けると、とても嬉しいです。

ここまで読んで頂き、ありがとうございました。
社内のネットワークに接続しないマシンでpythonを動かす事になった方や、研究のセキュリティの都合上、ネットワークに接続しないgpuなどのマシンを使用する方など、参考になっていれば幸いです。

# おまけ

## pyenv+virtualenvで仮想環境を実現
こちらのサイトが非常に分かりやすいです。
但し、オンラインのマシン用にはなります。
しかし、ここまで理解して読まれた方は、下記サイトの確認だけで簡単にオフライン環境でも実現できますので、是非試してください。
https://qiita.com/overflowfl/items/9bc7c9504655f6c17f4e


## 参考URL
https://pip.pypa.io/en/stable/reference/pip_download/
https://webkaru.net/dev/mac-wget-command-install/
https://qiita.com/panda11/private/67d1e09ec8261e4c19f4
https://git-scm.com/book/ja/v2/%E4%BD%BF%E3%81%84%E5%A7%8B%E3%82%81%E3%82%8B-Git%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB
http://www.s-yata.jp/docs/libbzip2/
http://edo.blog.jp/archives/1819066.html
https://tech-diary.net/python-library-offline-install/
https://tnamao.hatenablog.com/entry/2017/06/29/182050