まっさらな環境を渡されて、Pythonの仮想環境を作り、Pythonを実行するまで。

上司「まっさらの環境用意したから、Python動くようにしといて」
わい「うぃす」

まっさらな状態から構築した事ないから緊張.....

#やりたい事
まっさらな`CentOS7`にPythonの仮想環境を作って動かす。
Pythonのバージョンは`3.6`。

これは余談だけど、
ネットを見てると「とりあえず入れる」みたいな記事が多くて、とりあえずって何だよと思ったので、最低限しか入れたくない。
例えば「python-devel」とか、必要かどうか分からないけどとりあえず入れるみたいなのはしたくない。
(結論から言うと、なくても大丈夫)

#前提
```sh
# Cent0Sのバージョン
$ cat /etc/redhat-release 
CentOS Linux release 7.4.1708 (Core) 

# Pythonのバージョン
# 初期状態でも入ってるが、2系なので要件に合わない
$ python -V
Python 2.7.5
```

#手順
```sh
# ログイン
$ ssh root@xxx.xxx.xxx.xxx


# yumを最新にする(yumから取ってくるパッケージを最新のものにしたいため)
$ yum -y update


# CentOS公式のパッケージだけでは事足りないので、IUSリポジトリを取得してくる
# ref: http://everything-you-do-is-practice.blogspot.jp/2017/09/centos-epel-ius-remi-centos-5x-centos.html
$ yum install https://centos7.iuscommunity.org/ius-release.rpm


# Pythonの3系で使えるものを探す
$ yum search python3 | grep u.x86_64
python34-gammu.x86_64 : Python bindings for Gammu
python34u.x86_64 : Version 3 of the Python programming language aka Python 3000
python35u.x86_64 : Version 3.5 of the Python programming language
python36u.x86_64 : Interpreter of the Python programming language
uwsgi-plugin-python34u.x86_64 : uWSGI - Plugin for Python support
uwsgi-plugin-python35u.x86_64 : uWSGI - Plugin for Python support
uwsgi-plugin-python36u.x86_64 : uWSGI - Plugin for Python support


# 要件に合う3.6があるので、これをインストール
# なかったらIUS以外のリポジトリを取得して再度確認すればOK
$ yum install python36u.x86_64

# uWSGIとか使うと、Cコンパイラとか開発者ツールとか必要になるので入れておく
# （`pip install uWSGI`が出来なくて詰んでたけど、これらを入れれば解決する事が分かったので、先に入れておく方がいいかなと）
$ yum install gcc
$ yum install python36u-devel

# パッケージ管理をしたいため、pipをインストール
$ yum install python-pip


# 仮想環境を作りたいため、virtualenvwrapperをインストール
$ pip install virtualenvwrapper


# virtualenvwrapperのインストール先を調べる
$ which virtualenvwrapper.sh
/usr/bin/virtualenvwrapper.sh


# ログイン時に環境変数を設定し、仮想環境を立ち上げたいため、ログインシェルに追記(先頭の#は外して下さい)
# ログインシェルには「.bashrc」「.zsh」「.profile」などあるが、今回は「.bashrc」を使用して説明する
$ vim .bashrc

## Virtualenvwrapper
#if [ -f /usr/bin/virtualenvwrapper.sh ]; then
#    export WORKON_HOME=$HOME/.virtualenvs
#    source /usr/bin/virtualenvwrapper.sh
#fi


# 一旦ログアウトしないと反映されないため、一旦ログアウト
$ exit


# 再度ログイン
$ ssh root@xxx.xxx.xxx.xxx


# python3.6のインストール先を調べる
$ whereis python3.6
/usr/bin/python3.6


# Pythonの仮想環境を作る
# `env_name`には好きな環境名を入れる。何でもいい。
$ mkvirtualenv env_name --python /usr/bin/python3.6


# 環境が作られると、PS1($)の前に(環境名)が出てくる
(env_name) $ 


# Pythonのバージョンを確認してみる
(env_name) $ python -V
Python 3.6.4


# 仮想環境から離脱して、Pythonのバージョンを確認してみる
(env_name) $ deactivate
$ python -V
Python 2.7.5


# これで、実際の環境では2.7だが、仮想環境だと3.6が使えるようになった。


# 後は確認。
# 仮想環境に入って、インタプリタを立ち上げて、適当に実行確認。
$ workon env_name
(env_name) $ python
Python 3.6.4 (default, Dec 19 2017, 14:48:12) 
[GCC 4.8.5 20150623 (Red Hat 4.8.5-16)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> print("Hello world:)")
Hello world:)
>>> 
>>> 1 + 2 + 3
6
>>> 
>>> x = 1 + 2 + 3
>>> print(x)
6
>>> 
>>> exit()
```

以上、最低限のインストール手順と確認手順。

#Tips: 別環境で`pip install`したモジュール群を、新環境にも適用したい
例えば、自分の環境でがちゃがちゃ開発していて、それをサーバにサーバに上げるといった際、

「何を`pip install`したか忘れた」
「`pip list`で調べられるけど、一個一個installするのは面倒」

という事があるかと思います。
僕はありました。

そんな時のためのTipsです。


```sh
# >>別環境

# pip listの出力結果を、整形し、ファイルに保存
#  例えば、「pip (9.0.1)」を「pip<=9.0.1」にするだけ
$ pip list | sed -e 's/ (/<=/' | sed -e 's/)//' > pip_install.txt

# 別環境から新環境に移す
$ scp pip_install.txt root@xxx.xxx.xxx.xxx:/root/


# >>新環境

# 新環境にログイン
$ ssh root@xxx.xxx.xxx.xxx

# 仮想環境にログイン
$ workon env_name

# ファイルを利用してpip install
(env_name) $ pip install -r pip_install.txt
```

これで、一個一個調べ直してインストールし直さなくても、一気に抜いて、一気に入れられる。

##2018/06/01：問題発生
例のごとく、`pip list`で取ったやつを、`sed -e ...`で整形して、`>`でテキストファイルに吐こうとしたら失敗した。
いや、失敗って言い方が合ってるのか分からないけど、思った形に整形できてなかった。

###原因：出力形式（` --format {format_name}`）
`pip list`には出力形式が４つあるらしい。
知らんかった。

それぞれ、以下のような形式になる。

```sh
$ pip list --format legacy
Django (2.0.5)
django-jet (1.0.7)
future (0.16.0)
idna (2.6)
numpy (1.14.1)


$ pip list --format columns
Package         Version  
--------------- ---------
Django          2.0.5    
django-jet      1.0.7    
future          0.16.0   
idna            2.6      
numpy           1.14.1   


$ pip list --format freeze
Django==2.0.5
django-jet==1.0.7
future==0.16.0
idna==2.6
numpy==1.14.1


$ pip list --format json
[{"name": "Django", "version": "2.0.5"}, {"name": "django-jet", "version": "1.0.7"}, {"name": "future", "version": "0.16.0"}, {"name": "idna", "version": "2.6"}, {"name": "numpy", "version": "1.14.1"}]
```

で、

上で紹介した`sed`を使ったコマンドだが、`legacy`じゃないと意味がない。
なので、実行するコマンドを、以下に変更する必要がある。

```sh
# これだとフォーマットが分からないのでダメ
# $ pip list | sed -e 's/ (/<=/' | sed -e 's/)//' > pip_install.txt

# フォーマットを`legacy`にしてから、`sed`で整形する
$ pip list --format legacy | sed -e 's/ (/<=/' | sed -e 's/)//' > pip_install.txt
```

##2018/06/06：追記
上記の手順で`Python`は動くようになるが、`Django`などを使ってWebサービスを展開しようとした時に困ったので追記。
やりたい事としては、**「サーバ上でDjangoプロジェクトを稼働」**とし、ドメイン名などの設定はしないものとする。

ローカルだと http://localhost:8000 とかでアクセスできるけど、
サーバに上げると、localhostじゃ動かんしね。

###前提
以下の構成とします。

- Webサーバ：
    - `nginx`
- Appサーバ
    - `uWSGI`

###`nginx`
インスコして、`conf`ファイル書いて、終わり。
後は起動させておく。

```sh
# nginxをインストール
$ yum install nginx
```

```conf:/etc/nginx/conf.d/hogehoge.conf
upstream hogehoge {
  server localhost:8000;
}

server {
  listen 80;
  server_name localhost;
  error_log  /var/log/nginx/error.log warn;

  location / {
    include uwsgi_params;
    uwsgi_pass hogehoge;
  }
}
```

```sh
# 起動
$ service nginx start
```


###uWSGI

こいつが少し厄介で、こいつだけ入れても動かない。
Cコンパイラの`gcc`と、開発者ツールの`python-devel`を入れて、やっと動く。
（「まっさらな環境を...」なので、`gcc`とか入ってないはず。僕はこれに気付かず半日潰した）

インスコできたら、`ini`ファイルを作って、ざざざっと中身を書く。
ファイルの場所は適当でいい。好きなところに作ればOK。

```sh
# yumでCコンパイラをインスコ
$ yum install gcc

# yumでPython開発者ツールをインスコ
$ yum install python-devel

# pipでuwsgiをインスコ(Pythonの仮想環境があるならそこでやってね)
$ pip install uwsgi
```

```ini:/hoge/fuga/uwsgi.ini
[uwsgi]
chdir=/root/Project/    <===動かしたいDjangoプロジェクトまでのフルパス
module=Project.wsgi:application    <===Djangoプロジェクト内のwsgi.py内のapplication
master=True
pidfile=/tmp/project-master.pid
http=0.0.0.0:8000
vacuum=True
max-requests=5000
daemonize=/var/log/uwsgi/%n.log
buffer-size=10240
```

```sh
# 起動コマンドはこんな感じ（iniファイルと同じディレクトリで実行した場合）
$ uwsgi --ini uwsgi.ini
```

###さて、準備は整った！

**と思ったじゃろ???**

まだです。

**「ファイアウォールの設定」**をしておく必要がある。
ついでに**「SELinuxも無効化」**しておく。

これに関しては外部サイトをご覧あれ。

- SELinuxの無効化
    - https://eng-entrance.com/linux-selinux
- ファイアウォールの設定
    - http://andosho.blog.fc2.com/blog-entry-16.html
    - http://te2u.hatenablog.jp/entry/2013/09/12/215031

ファイアウォールの設定については、
外部サイト内でポート番号`80`/`443`を許可しているが、`8000`も許可しておく必要がある。
(`80`で受け取って、サーバ内で`8000`にリダイレクト、、、とか出来るはずだけど分からなかったので、`8000`で受け取るようにしてる)

SELinuxの無効化については、
必要か否か分からないのだが、僕もこいつのせいで死ぬほどハマって苦労させられたので、無効化にしておいた方がよいという判断の下。。。
ハマった時の話： https://qiita.com/hnkyi/items/5f00d9eb5d7a91ef0b59


###さて、準備は整った！(本当)
あとは、`nginx`起動して、`uwsgi`起動して、 http://サーバのIP:8000 にアクセスすればOK。


# 参考
[https://qiita.com/_rdtr/items/5f3a9a9e2cb5a24f284e](https://qiita.com/_rdtr/items/5f3a9a9e2cb5a24f284e)
[http://everything-you-do-is-practice.blogspot.jp/2017/09/centos-epel-ius-remi-centos-5x-centos.html](http://everything-you-do-is-practice.blogspot.jp/2017/09/centos-epel-ius-remi-centos-5x-centos.html)