MENU

heroku container:push でエラー

先日からherokuCLIをアップデートしたあたりからherokuにデプロイしようとする度に以下のエラーが出るようになってしまいました。

(node:26614) Error Plugin: heroku-container-tools: files attribute must be specified in /Users/username/.local/share/heroku/node_modules/heroku-container-tools/package.json
module: @oclif/config@1.6.27
plugin: heroku-container-tools
root: /Users/username/.local/share/heroku/node_modules/heroku-container-tools
See more details with DEBUG=*
(node:26614) Error Plugin: heroku-container-tools: files attribute must be specified in /Users/username/.local/share/heroku/node_modules/heroku-container-tools/package.json
module: @oclif/plugin-legacy@1.0.15
plugin: heroku-container-tools
root: /Users/username/.local/share/heroku/node_modules/heroku-container-tools
See more details with DEBUG=*
Uninstalling heroku-container-tools... done

エラーメッセージを読んでもよくわからなかったのでいろいろ検索したところ以下の記事がヒットしました。
Latest Docker update broken Heroku cli?
症状は違いますがheroku-container-toolsが悪さをしているようだったので、以下コマンドでアンインストールし、heroku-container-registryをインストールし直したところ、エラーは出なくなりました。

$ heroku plugins:uninstall heroku-container-tools
$ heroku plugins:install heroku-container-registry

Node.jsの環境をDockerで構築&herokuにデプロイ

はじめに

それぞれ他サイトを参照して作ったのみですが、自身の備忘までに。

会社でSkyway使うかも?という話が出てきたため、検証環境を作るのが目的です。 * 他にもいろいろパッケージ入れて検証するかもーついでにnodejsとかの勉強もしなきゃ * 最近Docker触ってないしDockerで環境つくりたい * というかherokuにDockerのコンテナをデプロイできるようになったみたいだし使ってみたい ということで、node.jsの環境をDocker上に構築し、それをHerokuにデプロイするまでを試してみたいと思います。

Skywayとは?

NTT Comunicationsが開発している、WebRTCのためのプラットフォームです。  

公式サイト
Enterprise Cloud Skyway

SkyWayとは

ビデオ会議やコンタクトセンター、遠隔作業支援、オンライン教育、ライブ配信など、さまざまな機会において、オンラインでのリアルタイムコミュニケーションのニーズが高まっています。 ビデオ・音声通話、データ通信といったリアルタイムコミュニケーションの標準技術である「WebRTC」が登場し、リアルタイムコミュニケーションがより実現しやすくなってきました。 SkyWayを利用すれば、WebRTCに必要なサーバを構築・運用することなく、手軽にビデオ・音声通話、データ通信を利用できます。 自社サービスの開発・提供に専念して、イノベーションに集中することができます。

要は、Skywayを使えばWebRTCのアプリが簡単に開発できるというものです。
WebRTCはクライアント間で直接通信する方法ですが、そのためにはシグナリングサーバーと呼ばれる、クライアント間の接続する仲介をするサーバーを準備する必要があるのですが(これがまた複雑、らしい)、Skywayではそういったシグナリング等の、WebRTCに必要なサーバサイドの機能をPFとして公開してくれているため、我々開発者はクライアント側の実装に集中できるのです。
自分もまだそんなに詳しくないのでこの辺で。。

Node.jsのDocker環境

まずはDocker上の環境を作っていきます。

とはいえこれまでほとんどNode.jsなんて触ったことがなく全く知識が無いため、 とりあえず先人の行いに習って作ってみます。
以下を参考にしました。
【初心者向け】Dockerで手軽にNode.js開発環境構築 (2)
参考というかほとんど完コピだけど。。
以下のDockerfiledocker-compose.ymlを作成

FROM node:8.9.4-alpine

ENV NODE_ENV=development

RUN npm install -g express-generator@4.15.0

WORKDIR /app

EXPOSE 3000
version: '3'
services:
  webserver:
    build: ./
    image: node-express-dev:1.0
    container_name: node
    tty: true
    volumes:
      - ./:/app
    ports:
      - "8080:3000"

ファイルの配置は記事とは変えてすべて同じディレクトリにしています。

MyApp
 ├─ Dockerfile
 └─ docker-compose.yml

そしてビルドと起動

$ docker-compose build
$ docker-compose up -d

コンテナに接続し、express-generatorでアプリの雛形を作成します。
参照先サイトでは、オプションにpugを指定しています。
pugはテンプレートエンジンですが、構文が馴染めなそうだったので、今回はhtmlライクに書けそうなejsを指定しました。

$ docker exec -it node /bin/sh
# express -f --view=ejs /app
# npm install

雛形が生成され、こんな構成になりました。

MyApp
├── Dockerfile
├── app.js
├── bin
│   └── www
├── docker-compose.yml
├── node_modules
├── package-lock.json
├── package.json
├── public
│   ├── images
│   ├── javascripts
│   └── stylesheets
├── routes
│   ├── index.js
│   └── users.js
└── views
    ├── error.ejs
    └── index.ejs

以下コマンドで開発用サーバを起動します。

# npm start

http://localhost:8080をブラウザで開いてExpressのスタートページが表示されれば成功です。
Dockerコンテナ上ではポート3000番で立ち上がっていますが、ブラウザの8080番ポートが3000番に転送されています。

Skywayのチュートリアル

さて、とりあえずnode.jsとWebサーバが立ち上がる環境はDocker上に構築出来たので、次はいよいよskywayのチュートリアルアプリを立ち上げていきます。
Javascript SDKの概要は以下公式ページに記載があります。
Skyway JavaScript SDK チュートリアル

とりあえず、今回はまず動くものを動作させたいので、チュートリアルのコードをそのまま使います。
Github上に公開されているサンプルコードを使用します。
今回はP2Pの1対多通信のものを使用しました。
github : skyway/skyway-js-sdk/examples/p2p-broadcast

先程構築したnode環境ではview/index.ejsがホームにて表示されるページのテンプレートになっています。
↑のgithubに上がっていたindex.htmlの内容をそのままvies/index.ejsに貼り付けます。
script.jsstyle.cssはそれぞれ、public/javascripts/script.jspubic/stylesheets/style.cssに配置します。  

また、APIキーをSkywayのダッシュボードから取得し、public/javascripts/key.jsに記載します。

javascript window.__SKYWAY_KEY__ = '<YOUR_KEY_HERE>'

index.ejsの読み込み先のパスも変更

<head>
  <meta charset="utf-8">
  <title>SkyWay - Broadcast example</title>
  <link rel="stylesheet" href="/stylesheets/style.css">
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script type="text/javascript" src="//cdn.webrtc.ecl.ntt.com/skyway-latest.js"></script>
  <script type="text/javascript" src="/javascripts/key.js"></script>
  <script type="text/javascript" src="/javascripts/script.js"></script>
</head>

ここまで出来れば、http://localhost:8080にてSkywayのサンプルが動作するはずです。

browserifyの導入

一旦ここまでで、Skywayの動作確認は出来ますが、jQueryやSkywayのJavascript SDKCDNから読み込んでいます。
ここままでも問題ないのですが、後々いろんなパッケージを導入することを考えると、(今回もWebRTCでの配信→音声認識とかを試したいなーと思ってるので)
パッケージのバージョン管理が大変になったり、何よりも今回はnode.jsの環境構築の勉強も兼ねていたので、 * npmでパッケージインストール * browserifyでコンパイル

という流れでパッケージ管理ができるよう変更したいと思います。

まずはnpmでjQueryとskyway-jsをインストールします。

# npm install jquery --save
# npm install -D browserify

パッケージをインストールしたら、script.js内でそれらのライブラリをrequireする分を記載します。

'use strict';
// Imports jQuery
const $ = require('jQuery');
$(function() {
  // Imports the skyway library
  const Peer = require('skyway-js');

読み込みも簡単ですね。
ただしこれだけだとrequireをブラウザが解釈できないので、動作しません。
browserifyコンパイルする必要があります。
一旦上記script.jsscript/ディレクトリを作成し、その配下で管理、開発、コンパイルしたらpublic/javascripts/script.jsが生成されるようにします。
コマンドは以下。

# $(npm bin)/browserify script/script.js -o public/javascripts/script.js

これでpublic/javascripts/script.jsコンパイルされた形で生成されました。 再びnpm startコマンドでサーバを立ち上げ、http://localhost:8080をブラウザで開いてExpressのスタートページが表示されれば成功です。

Herokuへのデプロイ

ひとまず、Docker上に環境を作ることができたので、いよいよHerokuの環境にデプロイをしていきます。

以下のサイトを参考にしました。 * Container Registry & Runtime (Docker Deploys) * Heroku で Docker を使う場合の諸注意

herokuにDockerコンテナをデプロイするためには以下の注意点があるみたいです。

  1. PORT 環境変数で Listen する Webアプリケーションであること
  2. Network link は未サポート
  3. Default working directory = '/'。変更するときは WORKDIR を指定する。
  4. CMDが必須。ENTRYPOINTはオプション。
  5. VOLUME、EXPOSE、STOPSIGNAL、SHELL、HEALTHCHECKは未サポート

今回のサンプルはPORTが3000で動作し、起動コマンドはnpm startなので、以下をDockerfileに追記します。

ENV PORT 3000
CMD ["npm", "start"]

あとは以下コマンドでHerokuにデプロイします。

$ heroku login # herokuにログイン
$ heroku container:login # Heroku 上の Container Registry へログイン
$ heroku create # Heorkuアプリの作成
$ heroku container:push web # コンテナをにContainer RegistryにPush
# 2018/5/15の変更にて以下のコマンドが必要になった
$ heroku container:release web # heroku アプリをデプロイ

heroku openでherokuアプリがブラウザで開けば成功です。

※5/15時点で変更があり、heroku container:push webだけではherokuアプリにはデプロイされなくなったようです。実際にアプリにデプロイするにはheroku container:release webを実行する必要があります。

Pushing images to Container Registry, either via the heroku container:push CLI command or using docker push, no longer releases those images to your application. To create a new release using the images pushed to Container Registry, run heroku container:release (specifying the process types you would like to release). Separating push and release allows you to:

  1. Push several images and then release them all at the same time.
  2. Use the release phase feature (run tasks before a new release of your app is deployed).
  3. Push one image and release it to multiple process types with different CMDs, via heroku.yml or API.

Pushing images to Container Registry no longer creates a release

まとめ

Herokuはこれまでもとても便利だったので大変重宝しておりましたが、自分自身余りgitを使わないし、グローバル環境で動作試験をしたいときもわざわざgit commitしてログに残るのが嫌だったので、Dockerコンテナを気軽にデプロイ出来るのはとても便利だなーと思いました。

参考サイト

プライバシーポリシー

こんにちは管理人のti_takaです。
下記、「プライバシーポリシー」に関して記載致しましたので、ご一読願います。

当サイトに掲載されている広告について

当サイトでは、第三者配信の広告サービス(Googleアドセンスもしもアフィリエイト)を利用しています。 このような広告配信事業者は、ユーザーの興味に応じた商品やサービスの広告を表示するため、当サイトや他サイトへのアクセスに関する情報 『Cookie』(氏名、住所、メール アドレス、電話番号は含まれません) を使用することがあります。 またGoogleアドセンスに関して、このプロセスの詳細やこのような情報が広告配信事業者に使用されないようにする方法については、こちらをクリックしてください。

当サイトが使用しているアクセス解析ツールについて

当サイトでは、Googleによるアクセス解析ツール「Googleアナリティクス」を利用しています。
このGoogleアナリティクスはトラフィックデータの収集のためにCookieを使用しています。
このトラフィックデータは匿名で収集されており、個人を特定するものではありません。

この機能はCookieを無効にすることで収集を拒否することが出来ますので、お使いのブラウザの設定をご確認ください。
この規約に関して、詳しくはこちら、またはこちらをクリックしてください。

当サイトへのコメントについて

当サイトでは、スパム・荒らしへの対応として、コメントの際に使用されたIPアドレスを記録しています。

これはブログの標準機能としてサポートされている機能で、スパム・荒らしへの対応以外にこのIPアドレスを使用することはありません。

また、メールアドレスとURLの入力に関しては、任意となっております。
全てのコメントは管理人であるti_takaが事前にその内容を確認し、承認した上での掲載となりますことをあらかじめご了承下さい。

加えて、次の各号に掲げる内容を含むコメントは管理人の裁量によって承認せず、削除する事があります。

  • 特定の自然人または法人を誹謗し、中傷するもの。
  • 極度にわいせつな内容を含むもの。
  • 禁制品の取引に関するものや、他者を害する行為の依頼など、法律によって禁止されている物品、行為の依頼や斡旋などに関するもの。
  • その他、公序良俗に反し、または管理人によって承認すべきでないと認められるもの。

免責事項

当サイトで掲載している画像の著作権・肖像権等は各権利所有者に帰属致します。
権利を侵害する目的ではございません。
記事の内容や掲載画像等に問題がございましたら、各権利所有者様本人が直接メールでご連絡下さい。
確認後、対応させて頂きます。

当サイトからリンクやバナーなどによって他のサイトに移動された場合、移動先サイトで提供される情報、サービス等について一切の責任を負いません。

当サイトのコンテンツ・情報につきまして、可能な限り正確な情報を掲載するよう努めておりますが、誤情報が入り込んだり、情報が古くなっていることもございます。

当サイトに掲載された内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。

プライバシーポリシーの変更について

当サイトは、個人情報に関して適用される日本の法令を遵守するとともに、本ポリシーの内容を適宜見直しその改善に努めます。

修正された最新のプライバシーポリシーは常に本ページにて開示されます。

運営者:ti_taka

[Python]Apache + Gunicorn + hug で簡単APIサーバ

先日、Apacheとmod_wsgiPythonを動かし、APIサーバを作成してみました。

kittagon.hateblo.jp

しかし、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が原因で、どうやらよくあることらしいです。

Linux:CentOS6でmod_proxyが動かない

こちらを参考にしました。

/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サーバで動作させられません。

SYSTEMCTLでDJANGOを自動起動する

ここに記載されているとおりに実行すれば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を運用していこうと思います。

参考ページ

*比較 Apache+gunicornのベンチマークを取った方がいました。

さくら VPS 1G の CentOS で Apache + 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使った方が簡単でした。

kittagon.hateblo.jp

Vagrantを使ってCentOSVMを作成

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系をインストールします。

Apacheyumでインストールすればいいのですが、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とでは若干挙動が違うようです。

解決策を知っている方が居ましたら教えて下さい。

参考ページ

銭湯で痴漢された話

僕は昔からホモにモテる。何故かわからないけど。 昔は運動もしていたし、体を使うバイトもしていたので割りと筋肉質でムチムチした体をしていたからかも。

blog.livedoor.jp

ちょっと前こんな記事を見て、昔銭湯で痴漢されそうになったことを思い出しました。 しかもスーパー銭湯の系列店も同じだし。。。。

犯行現場はやはり泡風呂

先の記事でも泡風呂について言及されてましたが、自分が被害にあったのも泡風呂でした。

友人三人でスーパー銭湯に行き、泡風呂に↓のように座っていました。

┌────────────────┐
│                │
│                │
│  他 友 友 自  他    │
└────────────────┘

友人三人で並んでお湯に浸かり、自分が端でした。

ゆっくりと他愛もない会話をしながらお湯に浸かっていると、前に伸ばしている自分の膝周りちょいちょい触れるものが。。。

銭湯でたまに体が当たってしまうのはよくあることなので、その時点では何も思わず、「なんか当たんだけど〜、狭いわ〜」と友達にもぼやきながら、足を動かして跳ね除け?たりしていました。

しかし、跳ね除けても跳ね除けても何かが足に触れてきます。自分と隣の人との間には十分なスペースがあり、普通にじっとしていれば体が触れ合うような距離ではなかったのです。

しかも、触れてくる場所が少しずつ上に来て、太もも周りを触られている感触が。

気付いても何も出来ない

「隣のおっさんがこっちに手伸ばして触ろうとしてきてね?!」

そう気づいてしまったときに僕は恐怖に陥りました。

隣のおっさんは普通の何食わぬ顔で前を向きお湯に浸かっているように見えるけど、お湯の中をちらっと見ると不自然な体勢になりながらも手をこちらに伸ばしてきている!!!

恐怖を感じながらも抵抗する?ことも出来ないので、隣の友人に「サウナ行こうぜ!」と言って逃げました。

その後友人に恐怖を語ったものの、笑いものにされたのは言うまでもありません。。。

まとめ

銭湯コワイ ホモコワイ いや、痴漢コワイ

電車で痴漢されて助けを呼べない女性の気持ちが分かったなぁ〜。。