[Python]Apache + Gunicorn + hug で簡単APIサーバ
先日、Apacheとmod_wsgiでPythonを動かし、APIサーバを作成してみました。
しかし、hugに備わっているBasic認証を追加することは出来ませんでした。
その後Gunicornを試してみたら、驚くほど簡単に実現できてしまいました。。
最初からこっちにすればよかった。。
前提
Pythonの環境とアプリケーションは前回のものを使います。
import sys,json,os import hug import util from dotenv import load_dotenv dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) HUG_USER_NAME = os.environ.get("HUG_USER_NAME") HUG_PASSWORD = os.environ.get("HUG_PASSWORD") authentication = hug.authentication.basic(hug.authentication.verify(HUG_USER_NAME, HUG_PASSWORD)) @hug.get("/hoge",,requires=authentication) def hoge(text): hoge_list = util.getHogeList(text) return json.dumps({"hoge":hoge_list})
前回記述したsys.path.append('/var/www/app')
はなくても今回は正常に動作しました。
また、動作させるアプリケーションは、gunicornを起動する際にパラメータとして渡すので、application = __hug_wsgi__
も不要です。
Gunicornのインストール
pipでインストールします。
$ pip3.6 install gunicorn
これで完了です。
$ cd /var/www/app/ # gunicornの起動 $ gunicorn app:__hug_wsgi__ [2017-09-12 22:54:04 +0000] [3994] [INFO] Starting gunicorn 19.7.1 [2017-09-12 22:54:04 +0000] [3994] [INFO] Listening at: http://127.0.0.1:8000 (3994) [2017-09-12 22:54:04 +0000] [3994] [INFO] Using worker: sync [2017-09-12 22:54:04 +0000] [3997] [INFO] Booting worker with pid: 3997
アプリがポート8000で起動します。
$ curl http://localhost:8000/hoge?text=hoge
結果が帰ってくればOKです。(Basic認証をかけているので認証エラーとなりますが。)
Apacheの設定
次にApacheの設定をします。
/etc/httpd/confd/httpd.conf
に以下の1行を記述し、リクエストをすべてgunicornに流すよう設定します。
ProxyPass / http://localhost:8000/
gunicornを起動した状態で、ブラウザからhttp://192.168.33.10/hoge?text=hoge
にアクセスして認証画面がでて認証できればOK!なのですが、なにやらまた、「Internal Server Error」が。。。
前回とまた同じです。
SELinuxの無効化
Apacheのエラーログを見ると、
[Tue Sep 12 12:03:36.233033 2017] [proxy:error] [pid 5746] (13)Permission denied: AH00957: HTTP: attempt to connect to 127.0.0.1:8000 (localhost) failed [Tue Sep 12 12:03:36.233187 2017] [proxy:error] [pid 5746] AH00959: ap_proxy_connect_backend disabling worker for (localhost) for 60s [Tue Sep 12 12:03:36.233202 2017] [proxy_http:error] [pid 5746] [client 192.168.33.1:62957] AH01114: HTTP: failed to make connection to backend: localhost
proxyがうまくいっていないみたいです。
エラーログを調べてみると色々情報がありました。
SELinuxが原因で、どうやらよくあることらしいです。
こちらを参考にしました。
/var/log/audit/audit.log
を見てみると、たしかに以下のエラーが。
type=AVC msg=audit(1505218092.201:865): avc: denied { name_connect } for pid=5745 comm="httpd" dest=8000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:soundd_port_t:s0 tclass=tcp_socket
/etc/sysconfig/selinux
を修正して、SELINUX=disabled
とします。
OSを再起動して、再度gunicornを立ち上げると、無事動作しました。
Basic認証も問題なく、きちんと認証できます。
gunicornの自動起動設定
OSを立ち上げたときにApacheは自動起動するよう設定しましたが、gunicornも自動起動するように設定しないとアプリをWebサーバで動作させられません。
ここに記載されているとおりに実行すればOKです。
$ cd /etc/systemd/system/ $ vim app.service
今回gunicornのパスは/bin/gunicorn
で、アプリケーションのディレクトリは/var/www/app
なのでapp.service
の中身は以下のようになりました。
[Unit] Description=gunicorn daemon After=network.target [Service] User=apache Group=apache WorkingDirectory=/var/www/app ExecStart=/bin/gunicorn schedule:__hug_wsgi__ [Install] WantedBy=multi-user.target
自動起動するように設定します。
$ systemctl enable app $ systemctl start app
OSを再起動し、ブラウザからhttp://192.168.33.10/hoge?text=hoge
にアクセスして結果が出ればOKです。
まとめ
前回mod_wsgiでアプリケーションを動作させたよりももっと簡単に実現できました。
また、前回躓いたBasic認証も正常に動作させることが出来ました。
今回はgunicornを運用していこうと思います。
参考ページ
[Python]CnetOS7 + Apache + mod_wsgi + hug で簡単APIサーバ作成
簡単なAPIサーバを作りたいなーと思い、やってみました。
以前はPHPを少ししか書けなかったけど、もう少し色々な言語を使いたいと思いPythonで実装してみることにしました。
色々と調べてみるとPythonにもフレームワークは色々とあるようで、DjangoとかFlaskとかありましたが、簡単に実装できそうなhugを使うことにしました。
PHPだとWebサーバ上で動かすのは簡単だったのですが、Pythonだと割と大変だったので備忘録として残しておきます。
wsgiサーバはpythonだとgunicornとかuwsgiとかがポピュラーみたいでしたが、何やら設定が難しそうな印象を受けたので、なんとなく簡単そうなmod_wsgiを使うことにしました。
最終的にやりたかったBASIC認証までを追加することは出来ませんでしたが。。。
知っている方がいたら教えてほしいです。。
※20170914 追記 gunicorn使った方が簡単でした。
Vagrantを使ってCentOSのVMを作成
Vagrantfileはこんな感じです。
Vagrant.configure("2") do |config| config.vm.box = "centos/7" config.vm.network "private_network", ip: "192.168.33.10" "192.168.0.100" , :bridge => "en0: Wi-Fi (AirPort)" config.vm.synced_folder "./", "/vagrant", type: "virtualbox", :owner => "vagrant", :groupe => "vagrant", :mount_options => ['dmode=755', 'fmode=655'] config.vm.provision "shell", run: "always", inline: "systemctl restart network.service" end
「Vagrant+Laravelでパーミッションで詰まった」とか、「vagrant + centos7 でprivate_networkで設定したIPに接続ができない」とかで躓いた経緯があったから、
config.vm.synced_folder "./", "/vagrant", type: "virtualbox", :owner => "vagrant", :groupe => "vagrant", :mount_options => ['dmode=755', 'fmode=655'] config.vm.provision "shell", run: "always", inline: "systemctl restart network.service"
とかを記載しています。
Python3系とApacheのインストール
hugはPython3系でしか動かないので、まずはPython3系をインストールします。
Apacheはyumでインストールすればいいのですが、Pythonに関してはソースからコンパイルしたりyumで入れたりとか色々やり方はあったのですが、最終的に「Python 3 を CentOS 7 に yum でインストールする手順」のやり方を参考にしたら上手くいきました。
[Python] mod_wsgiを使ってPython3.6をApacheで動かす(CentOS6系)
このあたりを参考にソースからコンパイルする方法も試してみましたが、何故かpipでmod_wsgiをインストールするところでつまずいてしまいました。Cent0S6系と7系の違い?
とりあえず下記コマンドでインストールします。
# 色々インストール $ yum groupinstall -y "Development tools" $ yum install -y zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel # Apacheのインストール $ yum install -y httpd httpd-devel # OS起動時にApacheの起動を有効化 $ systemctl enable httpd.service # Apacheの起動 $ systemctl start httpd.service # Pyton3.6.xのインストール $ yum install -y https://centos7.iuscommunity.org/ius-release.rpm $ yum install -y python36u python36u-libs python36u-devel python36u-pip $ python3.6 -V Python 3.6.2
無事Python3.6とApacheがインストール出来ました。
mod_wsgiのインストール
こちらもソースからインストールしたりpip経由でのインストールがあるみたいですが、あえてソースからインストールするメリットも分からなかったので、pip経由でインストールすることにしました。
先程の[Python] mod_wsgiを使ってPython3.6をApacheで動かす(CentOS6系)の後半辺りを参考にしています。
$ pip3.6 install mod_wsgi $ python3.6 -c "import sys; print(sys.path)" ['', '/usr/lib64/python36.zip', '/usr/lib64/python3.6', '/usr/lib64/python3.6/lib-dynload', '/usr/lib64/python3.6/site-packages', '/usr/lib/python3.6/site-packages']
インストールが完了し、パケージのインストール先が/usr/lib64/python3.6/site-packages
だと分かります。
$ cd /usr/lib64/python3.6/site-packages $ ll total 8 drwxr-xr-x. 6 root root 4096 Sep 5 13:19 lxml drwxr-xr-x. 2 root root 131 Sep 5 13:19 lxml-3.8.0.dist-info drwxr-xr-x. 6 root root 84 Sep 5 09:15 mod_wsgi drwxr-xr-x. 2 root root 141 Sep 5 09:15 mod_wsgi-4.5.18-py3.6.egg-info drwxr-xr-x. 2 root root 6 Jul 19 04:01 __pycache__ -rw-r--r--. 1 root root 119 Jul 8 03:33 README.txt $ cd mod_wsgi/server $ ll total 1048 -rw-r--r--. 1 root root 1558 Sep 5 09:15 apxs_config.py -rw-r--r--. 1 root root 3563 Mar 31 2016 environ.py -rw-r--r--. 1 root root 128159 Aug 29 09:28 __init__.py drwxr-xr-x. 4 root root 60 Sep 5 09:15 management -rwxr-xr-x. 1 root root 932872 Sep 5 09:15 mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so drwxr-xr-x. 2 root root 101 Sep 5 09:15 __pycache__
ここにある、mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
を使います。
パスは/usr/lib64/python3.6/site-packages/mod_wsgi/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.soserver
です。
Apacheの設定
これで必要なものはインストール出来たので、Apacheの設定をしていきます。
/etc/httpd/conf.d/
配下にwsgi.conf
設定ファイルを配置し、wsgiの設定を記述します。
LoadModule wsgi_module /usr/lib64/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so WSGIDaemonProcess myapp user=apache group=apache WSGIProcessGroup myapp WSGISocketPrefix /var/run/wsgi WSGIScriptAlias / /var/www/app/app.py <Directory /var/www/app/> Options ExecCGI MultiViews Indexes MultiViewsMatch Handlers AddHandler wsgi-script .py AddHandler wsgi-script .wsgi DirectoryIndex index.html index.py app.py Order allow,deny Allow from all </Directory>
これを記述したらApacheを再起動しておきます。
$ apachectl restart
これで、ルートディレクトリ/
を開いたときに、/var/www/app/app.py
を動作させるという設定になりました。
hugでapp.pyの作成
とりあえずこんな感じで書きました。
import sys,json,os import hug import util @hug.get("/hoge") def hoge(text): hoge_list = util.getHogeList(text) return json.dumps({"hoge":hoge_list})
今回はGETで受け取ったパラメータを他の関数に渡して処理してもらい、その結果を返すAPIを想定しているので、この様な書き方にしています。
実際に処理する関数はutil.pyに記載しています。
以下コマンドで、開発用サーバを起動します。
$ hug -f app.py /#######################################################################\ `.----``..-------..``.----. :/:::::--:---------:--::::://. .+::::----##/-/oo+:-##----::::// `//::-------/oosoo-------::://. ## ## ## ## ##### .-:------./++o/o-.------::-` ``` ## ## ## ## ## `----.-./+o+:..----. `.:///. ######## ## ## ## ``` `----.-::::::------ `.-:::://. ## ## ## ## ## #### ://::--.``` -:``...-----...` `:--::::::-.` ## ## ## ## ## ## :/:::::::::-:- ````` .:::::-.` ## ## #### ###### ``.--:::::::. .:::.` ``..::. .:: EMBRACE THE APIs OF THE FUTURE ::- .:- -::` ::- VERSION 2.3.1 `::- -::` -::-` -::- \########################################################################/ Copyright (C) 2016 Timothy Edmund Crosley Under the MIT License Serving on port 8000...
開発用のサーバがポート8000番で立ち上がるので、
$ curl http://localhost:8000/hoge?text=hoge
で結果が帰ってくればOKです。
また、http://192.168.33.10:8000/hoge?text=hoge
にアクセスでも見れます。
開発用サーバではなく、mod_wsgi
経由でApacheでも動作しているか確認したいので、mod_wsgiでも動くようにします。
mod_wsgiで動作させるには、mod_wsgiに対応したapplicationを作る必要があります。
mod_wsgiでhugを使い、APIを作るやPython hug with apache mod_wsgiを参照。
app.py
の最下部に以下の一文を追記します。
application = __hug_wsgi__
ブラウザで、http://192.168.33.10/hoge?text=hoge
にアクセスして同じ結果が帰ってくればOK。
・・・なのですが、何やら「Internal Server Error」のエラーが。
Apacheのエラーログを見てみると、
[Sat Sep 09 13:28:00.511142 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] mod_wsgi (pid=7361): Target WSGI script '/var/www/app/app.py' cannot be loaded as Python module. [Sat Sep 09 13:28:00.511233 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] mod_wsgi (pid=7361): Exception occurred processing WSGI script '/var/www/app/app.py'. [Sat Sep 09 13:28:00.511368 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] Traceback (most recent call last): [Sat Sep 09 13:28:00.511434 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] File "/var/www/app/app.py", line 4, in <module> [Sat Sep 09 13:28:00.511441 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] import util [Sat Sep 09 13:28:00.511469 2017] [wsgi:error] [pid 7361] [remote 192.168.33.1:57939] ModuleNotFoundError: No module named 'util'
なにやらutil.pyが読み込めていない模様。
Pythonで「ImportError: No module named …」が出た時の3つの対処法を参考に、sys.path.append('/path/to/dir')
をapp.py
に追記すると直りました。
mod_wsgiでは若干挙動が違う?
Basic認証を追加
The guiding thought behind the architectureを参照すると、getの引数にhug.authentication.verify("userid","password")
を与えるとBasic認証が出来るようになるみたいです。
そのままユーザーID、パスワードを記述するのも良くないので、.env
ファイルを作成してそこに環境変数を書くとします。
【GitHub】に載せたくない環境変数の書き方 Python
こちらにdotenv
の使い方が載っています。
$ pip3.6 install python-dotenv
.env
の中身は↓にしました。
HUG_USER_NAME=uname HUG_PASSWORD=pass
この中の環境変数は、以下のように取り出します。
import os from os.path import join, dirname from dotenv import load_dotenv dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) HUG_USER_NAME = os.environ.get("HUG_USER_NAME") HUG_PASSWORD = os.environ.get("HUG_PASSWORD")
これらの変数を用いて以下のようにBasic認証の設定を記述します。
import sys,json,os sys.path.append('/var/www/app') import hug import util from dotenv import load_dotenv dotenv_path = join(dirname(__file__), '.env') load_dotenv(dotenv_path) HUG_USER_NAME = os.environ.get("HUG_USER_NAME") HUG_PASSWORD = os.environ.get("HUG_PASSWORD") authentication = hug.authentication.basic(hug.authentication.verify(HUG_USER_NAME, HUG_PASSWORD)) @hug.get("/hoge",,requires=authentication) def hoge(text): hoge_list = util.getHogeList(text) return json.dumps({"hoge":hoge_list}) application = __hug_wsgi__
開発用サーバを立ち上げて、ブラウザからhttp://192.168.33.10:8000/hoge?text=hoge
に認証画面が出て認証できればOKです。
Apacheからの接続はどうかというと、http://192.168.33.10/hoge?text=hoge
に接続すると、無事認証画面が出ました!!!
が、、IDとパスワードを入れても認証出来ません。。。。
いくら調べても原因が分からない。。なんでなんだ~。。。。
今回はここで断念しました。。。
まとめ
mod_wsgiとhugを使って、簡単なAPIサーバを立ち上げることが出来ました。
ただ、最終的にやりたかったBasic認証はなぜかmod_wsgiだと正常に動作しませんでした。
hugに組み込まれている開発用のサーバとmod_wsgiとでは若干挙動が違うようです。
解決策を知っている方が居ましたら教えて下さい。
参考ページ
- hug:Embrace the APIs of the future
- Vagrant+Laravelでパーミッションで詰まった
- vagrant + centos7 でprivate_networkで設定したIPに接続ができない
- Python 3 を CentOS 7 に yum でインストールする手順
- [Python] mod_wsgiを使ってPython3.6をApacheで動かす(CentOS6系)
- mod_wsgiでhugを使い、APIを作る
- Python hug with apache mod_wsgi
- Pythonで「ImportError: No module named …」が出た時の3つの対処法
- The guiding thought behind the architecture
- 【GitHub】に載せたくない環境変数の書き方 Python
銭湯で痴漢された話
僕は昔からホモにモテる。何故かわからないけど。 昔は運動もしていたし、体を使うバイトもしていたので割りと筋肉質でムチムチした体をしていたからかも。
ちょっと前こんな記事を見て、昔銭湯で痴漢されそうになったことを思い出しました。 しかもスーパー銭湯の系列店も同じだし。。。。
犯行現場はやはり泡風呂
先の記事でも泡風呂について言及されてましたが、自分が被害にあったのも泡風呂でした。
友人三人でスーパー銭湯に行き、泡風呂に↓のように座っていました。
┌────────────────┐
│ │
│ │
│ 他 友 友 自 他 │
└────────────────┘
友人三人で並んでお湯に浸かり、自分が端でした。
ゆっくりと他愛もない会話をしながらお湯に浸かっていると、前に伸ばしている自分の膝周りちょいちょい触れるものが。。。
銭湯でたまに体が当たってしまうのはよくあることなので、その時点では何も思わず、「なんか当たんだけど〜、狭いわ〜」と友達にもぼやきながら、足を動かして跳ね除け?たりしていました。
しかし、跳ね除けても跳ね除けても何かが足に触れてきます。自分と隣の人との間には十分なスペースがあり、普通にじっとしていれば体が触れ合うような距離ではなかったのです。
しかも、触れてくる場所が少しずつ上に来て、太もも周りを触られている感触が。
気付いても何も出来ない
「隣のおっさんがこっちに手伸ばして触ろうとしてきてね?!」
そう気づいてしまったときに僕は恐怖に陥りました。
隣のおっさんは普通の何食わぬ顔で前を向きお湯に浸かっているように見えるけど、お湯の中をちらっと見ると不自然な体勢になりながらも手をこちらに伸ばしてきている!!!
恐怖を感じながらも抵抗する?ことも出来ないので、隣の友人に「サウナ行こうぜ!」と言って逃げました。
その後友人に恐怖を語ったものの、笑いものにされたのは言うまでもありません。。。
まとめ
銭湯コワイ ホモコワイ いや、痴漢コワイ
電車で痴漢されて助けを呼べない女性の気持ちが分かったなぁ〜。。
Nexus5XにAndroidOを手動インストールした手順
先日GoogleがAndroidOの開発者プレビューを公開しました。
会社での検証が必要だったので、手動インストールをしてみました。
そもそも普段からiPhoneユーザーだしAndroidを使ったことも無いので、とにかく調べながらの実践だったので、最終的に何が正解だったのかわかりませんでした。
しかし、どうにかアップデートできたので備忘録として記しておきます。
端末
- Nexus5X(Android7.1.2)
- MacBookAir 13-inch, Early 2015(10.11.6 OSX El Capitan)
AndroidStudio AndroidSDKのインストール
何やらAndroidを手動でインストールするためには、AndroidSDKが必要で、adb
?とか言うコマンドで実施するとのこと。
AndroidStudioは必要ではないっぽいけど、後々アプリ開発もするかもしれなかったのでAndroidStudio経由でAndroidSDKをDLしました。
HomebrewにてAndroidStudioをインストール
$ brew cask install android-studio
インストールされたAndroidStudioを初回起動すると、AndroidSDKをインストールするかを聞かれるので、適当にダウンロードします。 (この辺りはどうやったか詳細を忘れました。)
するとSDKがDLされます。多分デフォルトだと大体同じかと思われます。 (自分の環境だと```$HOME/Library/Android/sdk/)にインストールされました。
このsdkフォルダ以下のplatform-toolsへのパスを通します。
$ echo "export PATH=$PATH:$HOME/Library/Android/sdk/platform-tools" >> ~/.bash_profile $ source ~/.bash_profile
これでadb
などのコマンドが使えるようになります。
Nexus5Xを開発者モードにしUSBデバッグをオンにする
以下を参考に実施。
Android端末を「デバッグモード」(USBデバッグ)に設定する
システムイメージのダウンロード
以下より、端末に合ったファイルをダウンロードします。
Android O のダウンロード | Android Developers
Nexus5Xの場合はbullhead-opp1.170223.012-factory-338a95b1.zip
というファイルで1.5Gバイトほどのサイズでした。
リカバリーモードを起動
ここからは以下を参考に実施。
Nexus端末を手動でAndroid5.0にアップデート | うめの記録帳
端末を「adb sideload」を使って手動で即座にアップデートする方法
NEXUSデバイスをSDK、adb sideloadを利用して最新ビルドのAndroidOSに手動で強制的にアップデートする方法
Nexus5Xの電源を切り、ボリュームダウンボタンを押し続けながら電源ボタン長押しで起動すると、以下のようにファストブートモードの画面になります。
ボリュームボタンのアップとダウンで選択が出来るので、リカバリーモードに合わせ、電源ボタンで選択します。すると以下のような画面になります。
電源ボタンを押しながらボリュームアップボタンを押してリカバリーメニューを表示します。
ボリュームボタン上下で、Apply update from ADB
を選択すると、以下のような画面になります。
ここからがPC側の作業で、ターミナルを起動し、先程ダウンロードしてきたシステムファイルbullhead-opp1.170223.012-factory-338a95b1.zip
があるディレクトリに移動します。
ここで、以下コマンドを実行します。
$ adb sideload bullhead-opp1.170223.012-factory-338a95b1.zip
これでアップデート出来るはずだと思っていたが、以下のようなメッセージが出て終わってしまった。
loading: 'bullhead-opp1.170223.012-factory-338a95b1.zip'... connecting... Total xfer: 0.00x
Nexus5X側では以下のようなエラーメッセージが。。。
次に、以下のページを参考に、別のやり方を試してみた。
Android 6.0 Marshmallowを手動アップデート/インストールする方法
何やら、oem unlock
というのが必要らしい。
以下を参考に実施。
Nexus5X/Nexus6Pのブートローダーアンロック方法・手順。
Nexus5Xの設定→開発者向けオプション→OEMロック解除を有効にする
再度Nexus5Xでファストブートモードを起動し、PCに接続します。 ターミナルより、以下コマンドを実行します。
$ fastboot oem unlock
すると以下のようにロックを解除しても良いか聞かれるので、ボリュームボタンでYesを選択し、電源ボタンで決定します。
その後しばらくすると再起動し、端末が初期化されます。
再度ファストブートモードを起動すると、下部のDEVICE STATEがunlockedになりました。
PC側で、bullhead-opp1.170223.012-factory-338a95b1.zip
を解凍して出来たディレクトリの中に移動し、中にある./flash-all.sh
を実行する。
$ cd bullhead-opp1.170223.012 $ ./flash-all.sh
Nexus5X側では以下のようにdownloadingという文字が表示され、正常に進行していることが分かります。
これにて無事アップデートが完了しました。
参考
想像力がないことについて
だいぶ前の記事だがこれを読みました。あまりに自分と同じだったので「オレのことか?」と思いびっくりしました。 ただ自分は、小さい頃特にゲームばかりをやっていたわけでもなく、そこまで多い方とまでは言えないがそれなりに読書は好きだったので本も読んでいました。 なので、ゲームをやりすぎたことが想像力がない原因と結論付けることに違和感を感じました。 そこで、今回は想像力がないことの原因について考えてみました。
想像力がないことは悪いことだけではないのでは?
まず前提として、自分は想像力がないという自覚があるが、それは純粋に想像することができないという意味ではなく、想像する力を常日頃から働かせる力がない、つまり想像する習慣がないという状態だという認識です。 何が言いたいかというと、自分の場合は想像しようと思えばできると思っている、ということ。(それが本当にそうかは別として)
では、なぜできないか?
それは結局そういう習慣がないから、ということに尽きるが、習慣がないのはないなりの理由があると考えます。 理由としては、想像する必要がないような何かがあったから想像しなかった、ということが考えられます。 想像力がないということは、捉え方を変えれば既知の情報を用いて効率的に大枠を捉えているとも言えます。 想像力がないのは、ゼロから情景を構築しないで済むように、既知の景色で補完しているということなのではないでしょうか。
でもそれはつまり先入観をもって物事を捉えているだけにすぎない
既知の情報を元に目の前の事象に触れている。つまり、想像力がない=先入観、バイアスを持って物事を捉えること 複眼的思考法では、冒頭部分でこのバイアスを持って物事を捉えることの意味と、危険性について論じています。
知的複眼思考法 誰でも持っている創造力のスイッチ (講談社+α文庫)
- 作者: 苅谷剛彦
- 出版社/メーカー: 講談社
- 発売日: 2002/05/20
- メディア: 文庫
- 購入: 64人 クリック: 567回
- この商品を含むブログ (213件) を見る
先入観をもって物事を捉えるのは効率的で、素早い判断をするには役に立つ。 この説明には納得がいきます。 自分も想像力はないが、本を読むのは早い方でした。
しかし、やはりこれだと細い部分は抜け落ちてしまう。
入社してすぐのときに、かんたんな開発の案件に参画しました。 要件が定まらない中でも大枠を捉えてプロトタイプをどんどん作って開発を進めたことで、比較的スムーズに開発をすすめることができました。 しかし最終局面になったときに、使用するライブラリのクリティカルな仕様を読み落としていることに気づき、大変な目にあいました。 (幸い開発量としては大きい案件ではなかったので休日出勤をすることで解決できたが。。) 多分これも、自分の上記のような性質によるものだと思います。 一部の情報と自分の先入観から大枠を構築するので、確かに行動のスピードは早くはなるが、細かい部分を見落としがちになってしまう、ということなのかもしれません。
まとめ。
想像力がないことは、先入観を持って物事を見ていることと一緒
先入観を持つことで素早い判断ができるようになるが、それでは本質を見落としかねない
自分が得た情報をしっかり検証し、そこから考えを構築することが大切
結局ぼんやりと考えていることを書いただけになってしまったので、次はこうした症状を持っている場合にどうしたら治すことができるのか、といった方法論に落とし込みたいと思います。
Nginxとffmpegで映像プレイヤーの検証環境を構築(RTMP、HLS)
背景
FlowPlayerを仕事で使うことになり、デザインの変更やFlash Failbackの検証などを行うための軽量な環境が欲しかった。
やりたいこと
- 映像ソースを送る側と、それを配信するサーバーをローカル環境上に立ち上げたい(LIVE配信で!!)
- 同一LANの他の端末から接続できるようにしたい
- RTMPとHLSどちらも検証したい
- コマンドひとつで環境を立ち上げたい!
構成
そこで以下のような構成を作ることにしました。
コンテナ1の作成(Web、配信サーバ)
要件
- Webサーバの機能を持つこと
- RTMPサーバの機能を持つこと
- HLSのセグメント化もできること
設定ファイル
nginxのRTMPモジュールを使えば、nginxだけで上記の機能のすべてを賄えるとのこと。
インストール方法はこちらを参照。
これをベースにDockerfileを作成します。
FROM centos:latest RUN yum update -y && \ yum -y install gcc pcre-devel openssl openssl-devel wget unzip tar git make && \ useradd --shell /sbin/nologin nginx && \ wget http://nginx.org/download/nginx-1.10.2.tar.gz && \ tar -zxvf nginx-1.10.2.tar.gz && \ wget https://github.com/arut/nginx-rtmp-module/archive/master.zip && \ unzip master && \ cd nginx-1.10.2 && \ ./configure \ --user=www-data \ --group=www-data \ --with-http_ssl_module \ --with-http_realip_module \ --add-module=../nginx-rtmp-module-master && \ make && \ make install COPY ./nginx.conf /usr/local/nginx/conf/nginx.conf WORKDIR /usr/local/nginx/html EXPOSE 80 1935 ENTRYPOINT ["/usr/local/nginx/sbin/nginx", "-g", "daemon off;", "-c", "/usr/local/nginx/conf/nginx.conf"]
RTMPサーバの設定については、以下の記述をnginx.conf
に追記すればOKのようです。
参考:nginx-rtmp-module/wiki
rtmp { server { listen 1935; application live { live on; wait_video on; exec ffmpeg -i rtmp://localhost/live/$name -c:a libfdk_aac -b:a 32k -c:v libx264 -b:v 128K -f flv rtmp://localhost/hls/$name_low -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 256k -f flv rtmp://localhost/hls/$name_mid -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 512K -f flv rtmp://localhost/hls/$name_hi; } application hls { live on; hls on; hls_path /usr/local/nginx/html/hls; hls_nested on; hls_fragment 5s; hls_variant _low BANDWIDTH=160000; hls_variant _mid BANDWIDTH=320000; hls_variant _hi BANDWIDTH=640000; } } }
まずrtmp://{IPアドレス}/live/{ストリーム名}
にストリームを送ると、
それを受け取ったあと、
exec ffmpeg -i rtmp://localhost/live/$name -c:a libfdk_aac -b:a 32k -c:v libx264 -b:v 128K -f flv rtmp://localhost/hls/$name_low -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 256k -f flv rtmp://localhost/hls/$name_mid -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 512K -f flv rtmp://localhost/hls/$name_hi;
上記が実行されrtmp://localhost/hls/$name_low
、rtmp://localhost/hls/$name_mid
、rtmp://localhost/hls/$name_hi
の3ストリームに更に流されるという仕組みのようです。
そして、下記の部分でHLSについての設定が記述してあるので、ここでセグメント化がされるという流れみたいです。
application hls { live on; hls on; hls_path /usr/local/nginx/html/hls; hls_nested on; hls_fragment 5s; hls_variant _low BANDWIDTH=160000; hls_variant _mid BANDWIDTH=320000; hls_variant _hi BANDWIDTH=640000; }
しかし、自分の環境ではこのHLS化の部分がうまく動作してくれなかったので、nginxでストリームを受け取ってからHLSに流すのではなく、最初から映像ソースを送信するffmpeg側でRTMP+HLS×3の4ストリーム流すことで対応しました。
最終的に、nginx.conf
は以下のようになりました。
user nginx nginx; worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; location / { root /usr/local/nginx/html; index index.html index.htm; expires -1; sendfile off; } location /hls { types { application/vnd.apple.mpegurl m3u8; } root /usr/local/nginx/html; add_header Cache-Control no-cache; # To avoid issues with cross-domain HTTP requests (e.g. during development) add_header Access-Control-Allow-Origin *; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } include servers/*; } rtmp { server { listen 1935; application live { live on; wait_video on; } application hls { live on; hls on; hls_path /usr/local/nginx/html/hls; hls_nested on; hls_fragment 5s; hls_variant _low BANDWIDTH=160000; hls_variant _mid BANDWIDTH=320000; hls_variant _hi BANDWIDTH=640000; } } }
参考
コンテナ2の作成(映像ソース)
要件
このあたり↓を参考に設定させていただきました。
テスト用の自動配信環境をDockerとFFmpegで構築してみる
というかDockerfile
に関しては全く同じです(汗)。
FROM jrottenberg/ffmpeg:3.2-centos COPY source.mp4 ./ COPY start.sh ./ COPY ./ffserver.conf /etc/ffserver.conf EXPOSE 80 1935 ENTRYPOINT bash start.sh
ただし、前述のようにffmpeg側で4ストリーム流す必要があったので、実行するシェルスクリプトを以下のように書き換えました。
#!/bin/bash ffmpeg -re -stream_loop -1 -i 'source.mp4' -acodec libfdk_aac -vcodec libx264 -f flv "${TARGET_RTMP_IP}/live/{設定したストリーム名}" \ -c:a libfdk_aac -b:a 32k -c:v libx264 -b:v 128K -f flv "${TARGET_RTMP_IP}/hls/{設定したストリーム名}_low" \ -c:a libfdk_aac -b:a 64k -c:v libx264 -b:v 256k -f flv "${TARGET_RTMP_IP}/hls/{設定したストリーム名}_mid" \ -c:a libfdk_aac -b:a 128k -c:v libx264 -b:v 512K -f flv "${TARGET_RTMP_IP}/hls/{設定したストリーム名}_hi"
この状態でDockerコンテナを立ち上げれば映像ソースを以下の4つに対して延々と配信してくれるはずです。
${TARGET_RTMP_IP}/live/{設定したストリーム名} ${TARGET_RTMP_IP}/hls/{設定したストリーム名}_low ${TARGET_RTMP_IP}/hls/{設定したストリーム名}_mid ${TARGET_RTMP_IP}/hls/{設定したストリーム名}_hi
docker-composeでの立ち上げ
ffmpeg(コンテナ2)からnginx(コンテナ1)に映像ストリームを送る際にはコンテナ間通信となるため、コンテナをリンクさせる必要がある(??Dockerまだあまり詳しくないので多分です。。。)ので、docker-composeを使ってコンテナをリンクさせて立ち上げるようにします。
結果的に以下のようなdocker-compose.yml
となりました。
version: '2' services: nginx: build: context: ./ dockerfile: ./Dockerfile_nginx container_name: nginx ports: - "80:80" - "1935:1935" volumes: - ./:/usr/local/nginx/html ffmpeg: build: context: ./ dockerfile: ./Dockerfile_ffmpeg container_name: ffmpeg environment: - TARGET_RTMP_IP=rtmp://{Dockerが動作するゲストOSのIPアドレス}:1935 links: - nginx
TARGET_RTMP_IP
として映像ソースの送信先を環境変数として渡しています。
コンテナ2で記述したシェルスクリプトがこの宛先に向けて映像を送ります。
{Dockerが動作するゲストOSのIPアドレス}
については、ゲストOSのIPアドレスなので、次項で設定するVagrant上のCentOSのIPアドレスを記載します。
参考
ゲストOSの立ち上げ
要件
- LAN内の外部端末から接続できるような基盤とすること
- 立ち上げる際にDockerも自動で実行できること
ここまでで、メインの機能となるDockerコンテナは作成できました。 そしていよいよそのDockerの土台となるゲストOSを立ち上げてきます。
Dockerのコンテナに別の端末から接続する際には、そのDockerが動作しているOSのIPアドレスを指定すれば可能なようです。 Docker の基本学習 ~ コンテナ間のリンク
また、Vagrantで立ち上げる仮想OSも以下の記述を加えるだけで簡単に外部からのアクセスが可能になるようです。
config.vm.network "public_network", :ip => "{IPアドレス}" , :bridge => "en0: Wi-Fi (AirPort)"
[Vagrant] LAN内の別マシンから仮想マシンにアクセスする
よって、Vagrantで立ち上げるOSに上記の設定を加えれば、その上で動作しているDockerコンテナのサーバにも外部から接続ができるようになるということです。
更に、Vagrantで仮想OSを立ち上げる際に、同時にDocker、docker-composeも実行したいです。
dockerをVagrantのprovisionerとして使う方法は公式に記述してあります。 DOCKER PROVISIONER
また、docker-composeをprovisionerとして使うためのプラグインもあり、
$ vagrant plugin install vagrant-docker-compose
でインストールできるようです。 leighmcculloch/vagrant-docker-compose
設定
結果的に以下のようなVagrantfileになりました。
Vagrant.configure("2") do |config| config.vm.box = "bento/centos-7.2" # LAN内部に公開するため、public_networkを選択 # IPアドレスはホストOSと同じセグメントのIPアドレスでないと接続できない config.vm.network "public_network", :ip => "192.168.0.100" , :bridge => "en0: Wi-Fi (AirPort)" config.vm.synced_folder "./", "/vagrant", type: "virtualbox" # provisionerとしてdockerとdocker-composeが使えるようにする # 起動のたびにコンテナを起動 config.vm.provision :docker config.vm.provision :docker_compose, yml: "/vagrant/docker-compose.yml", run: "always" end
参考
- Docker の基本学習 ~ コンテナ間のリンク
- [Vagrant] LAN内の別マシンから仮想マシンにアクセスする
- DOCKER PROVISIONER
- leighmcculloch/vagrant-docker-compose
プレイヤーを作る
今回はFlowPlayerの検証のために環境を作ることが目的だったので、FlowPlayerのセットアップ方法に従いindexhtml
を作成しました。
まずは、flowplayerのライブラリを読み込みます。
<head> <!-- flow player --> <link rel="stylesheet" href="//releases.flowplayer.org/6.0.5/skin/minimalist.css"> <script src="//code.jquery.com/jquery-1.12.4.min.js"></script> <script src="//releases.flowplayer.org/6.0.5/flowplayer.min.js"></script>
今回はFlowPlayer上でHLSも使いたいので、hls.jsも読み込みます。
<!-- load the Flowplayer hlsjs engine, including hls.js --> <script src="//releases.flowplayer.org/hlsjs/flowplayer.hlsjs.min.js"></script> <!-- optional: the quality selector plugin for quality selection when hlsjs is not supported --> <script src="//releases.flowplayer.org/quality-selector/flowplayer.quality-selector.min.js"></script> </head>
プレイヤーを作成するには、<div>
要素に対して、jsでソースを指定します。
RTMPは下記のように指定。
var rtmp_player_flv = document.getElementById("rtmp_player_flv"); flowplayer(rtmp_player_flv, { autoplay : true, clip: { sources: [ { type: "video/flash", rtmpt : false, src: "rtmp://{配信サーバのIPアドレス}:1935/live/mdst" }, ] } });
HLSは下記のように指定。
var hls_player = document.getElementById("hls_player"); flowplayer(hls_player,{ autoplay : true, live : true, clip: { sources: [ { type: "application/x-mpegurl", src: "http://{配信サーバのIPアドレス}/hls/mdst.m3u8" }, ] }, });
最終的にindex.html
は以下のような形になりました。
<!DOCTYPE html> <html lang="en" class=""> <head> <!-- flow player --> <link rel="stylesheet" href="//releases.flowplayer.org/6.0.5/skin/minimalist.css"> <script src="//code.jquery.com/jquery-1.12.4.min.js"></script> <script src="//releases.flowplayer.org/6.0.5/flowplayer.min.js"></script> <!-- load the Flowplayer hlsjs engine, including hls.js --> <script src="//releases.flowplayer.org/hlsjs/flowplayer.hlsjs.min.js"></script> <!-- optional: the quality selector plugin for quality selection when hlsjs is not supported --> <script src="//releases.flowplayer.org/quality-selector/flowplayer.quality-selector.min.js"></script> </head> <body> <div> <h2>RTMP</h2> <div id="rtmp_player_flv"></div> </div> <div> <h2>HTTP Live Streaming</h2> <div id="hls_player"></div> </div> <p id="vinfo" class="info"> </p> <script type="text/javascript"> flowplayer(function (api) { api.on("load", function (e, api, video) { $("#vinfo").text(api.engine.engineName + " engine playing " + video.type); }); }); var rtmp_player_flv = document.getElementById("rtmp_player_flv"); flowplayer(rtmp_player_flv, { autoplay : true, clip: { sources: [ { type: "video/flash", rtmpt : false, src: "rtmp://{配信サーバのIPアドレス}:1935/live/mdst" }, ] } }); var hls_player = document.getElementById("hls_player"); flowplayer(hls_player,{ autoplay : true, live : true, clip: { sources: [ { type: "application/x-mpegurl", src: "http://{配信サーバのIPアドレス}/hls/mdst.m3u8" }, ] }, }); </script> </body> </html>
起動
起動する際は、Vagrantfile
があるディレクトリで下記のコマンドを実行します。
$ vagrant plugin install vagrant-docker-compose $ vagrant up
まとめ
これにて、コマンド一つ(実際には2つ)で動画配信と視聴の環境を構築することができました。
あとはindex.html
を修正することで様々なプレイヤーの検証を行うことができます。
応用情報技術者に2週間で合格できた背景と方法
先日12月16日に合格発表があり、無事合格していました。
8月に試験を申し込んだあと、そのことを忘れていたので試験勉強を始めたのは10月に入ってからでした。。。。
実質2週間程度しか勉強時間はなかったのですが、なんとか合格することができました。
今回はその経緯と勉強方法について書きたいと思います。
なぜ応用情報技術者試験を受験したか
理由としては以下の3つです。
- 昨年、基本情報技術者試験を受験してから一年が経っていたため、次の資格を取得したかった
- 通信系の会社にいるが、情報系の学部を卒業していないので、体系的に学ぶために資格勉強を活用したかった
- 来年にでもネットワークスペシャリストを取得したいが、その前段階として応用を取得しておきたかった
午前 | 午後 | |
---|---|---|
出題形式 | 多肢選択式(四肢択一) | 記述式 |
出題数 解答数 |
出題数:80問 解答数:80問 |
出題数:11問 解答数:5問 |
合格基準は午前試験、午後試験でそれぞれ60点/100点を取得することです。
午前試験で60点が取得できていない場合、午後試験については採点すらも行われません。
試験範囲としては、以下の範囲が出題されます。
- 基礎理論
- アルゴリズムとプログラミング
- コンピュータ構成要素
- システム構成要素
- ソフトウェア
- ハードウェア
- ヒューマンインターフェース
- マルチメディア
- データベース
- ネットワーク
- セキュリティ
- システム開発技術
- ソフトウェア開発管理技術
- プロジェクトマネジメント
- サービスマネジメント
- システム監査
- システム戦略
- システム企画
- 経営戦略マネジメント
- 技術戦略マネジメント
- ビジネスインダストリ
- 企業活動
- 法務
特に16以降なんかは、正直なんか別の人が考えてよ・・・と言いたくなってしまう気がします。。。
とはいえ、応用情報技術者資格を持つ人材に対して期待されていることは、
基本戦略立案又はITソリューション・製品・サービスを実現する業務に従事し、独力で次のいずれかの役割を果たす。(1) 需要者(企業経営、社会システム)が直面する課題に対して、情報技術を活用した戦略を立案する。(2) システムの設計・開発を行い、又は汎用製品の最適組合せ(インテグレーション)によって、信頼性・生産性の高いシステムを構築する。また、その安定的な運用サービスを実現する。
もちろん、これらの範囲を専門家並に理解しなくても問題ないのでご安心ください。
自分のもともとの学習レベル
先に、「学習時間が2週間程度しかなかった」と書いたのですが、もちろん完全にゼロからのスタートで2週間で合格したわけではありません。
- 仕事でもある程度ネットワークの設計などを行っている
- 学生時代にプログラマとしてのバイト経験があり、また趣味でもプログラムを多少書く
- 直前にドットコムマスター を受験して合格していた
- 基本情報技術者を一年前に取得
- プログラム、データベース、ネットワーク周りはある程度理解している。
- 法律的な部分であったり、CPUの構成がなんたらとかという部分は、一度ある程度勉強したことがある
勉強方法
前述のような状態からのスタート、時間も限られていたのでひたすら過去問を解くという戦略を取りました。
午後試験は4問選択だったので、プログラミング、データベース、ネットワークを選択すればまぁ解けるだろうという甘い考えでした。
それ以上に、午前試験で60点以上を取得しないと無条件で不合格になってしまうので、そちらの対策に全力を注いだわけです。
毎日こちら↓のサイトを使って過去問を解き、5年分は解きました。
応用情報技術者試験ドットコム
また、下記の本も購入しわからないことは調べて、理解を深めました。
まぁ、一応本は買いましたが、正直使用頻度も少なかったですし、買わなくても大丈夫だったと思います。
こうしてひたすら過去問を解いたことで、概ね出題されるパターンも分かってきて、よく出る知識については効率よく覚えることができました。
午後試験の選択問題もすんなりと解くことができ、無事合格することができました。
まとめ
応用情報技術者試験は上述の通り、試験範囲も広いですし、午後試験も基本情報技術者試験に比べ記述式の分量が多く、とても大変な試験に見えるかもしれませんが、受けてみるとさらっと合格することができました。
その要因としてはやはり、ある程度技術的な知識を持っていたということが大きいと思います。
そういう意味では、もともと技術職の方であったり、そういった勉強をしている方なら、とてもかんたんに取ることができる資格なので、範囲の広さに怯える必要は無いと思います。
逆に技術にあまり詳しくない方は、午前試験に加えて午後試験の記述対策もしっかりと行う必要があるので、結構たいへんかもしれません。