Help us understand the problem. What is going on with this article?

Raspberry PiでWebサーバを構築し自作アプリを使う

目的

レンタルサーバ、固定IPなどの有料サービスを使用せず、自宅にWebサーバを構築し、下記の機能を実装する。
・家計簿登録Webアプリケーション
・TOEICや資格の勉強用Webアプリケーション
・ゲームアプリケーション

サーバー利用

下記URLから利用可能。
https://jnhm.wjg.jp

特記事項:
・公開しているソースコードの動作確認用程度でご使用ください。攻撃はしないでください。
・URLは変更の可能性があります。
・signupするとユーザ申請状態になり、承認するまではログインしても機能を利用できません。エラーする場合はすでに同名でアカウントが存在する場合が考えられます。
・手動で承認するためしばらく時間がかかる場合があります。
・こちらの独断でユーザーを削除する場合があります。
・定期的にメンテナンスするため一時的に利用できない場合があります。
・機能に関してご意見があればコメント欄にお願いします。

機器の準備

Raspberry Pi 3

コスト面を考慮し、WebサーバはRaspberry Pi 3を使用して構築する。Raspberry Piは本体価格が安価である上、消費電力が低いため常時起動に向いている。

外付けハードディスク

500GBの外付けハードディスクをデータベース用のストレージとして使用する。

サーバ外観

Raspberry Pi3(右)に外付けハードディスク(左)を接続。

無線ルータ

特に限定はしないが、ここでは次のものを使用している。
http://buffalo.jp/product/wireless-lan/ap/wcr-1166ds/

無償サービスの利用

Dynamic DNSを利用して動的IPであってもURLによるWebページアクセスが可能となるようにする。またSSL/TLS利用のため、無料サーバ証明書発行サービスを利用する。

myDNSでドメインの作成

自宅サーバのグローバルIPアドレスの名前解決をするためのサービスである。ドメインを作成するとURLによるwebアクセスが可能になる。
https://nw.myds.me/synology/setup-domain/

Let's Encryptにドメイン登録

サーバ証明書発行のサービスである。作成したドメインを登録することでSSL/TLSを利用したHTTPSでのweb閲覧が可能になる。インストール時にHTTPSのリダイレクト設定をしておく。HTTPでアクセスしてもHTTPSで接続されるようになる。
https://letsencrypt.org

/home/pi/letsencrypt/certbot-auto実行時メッセージ抜粋
Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 2

ルータの設定

Webサーバへのアクセス要求はルータのインターネット側インターフェースに割り当てられたグローバルIPに対し、ポート80(HTTP)もしくは443(HTTPS)で届くことになる。このアクセス要求をLAN内のRaspberry Piに転送しなくてはならない。そこでポートフォワーディングの設定を行う。ルータの設定画面でポート変換のメニューから、Internet側のポート443をIPアドレス192.168.11.19のポート443へ、Internet側のポート80をIPアドレス192.168.11.19のポート80へ変換するよう設定する。

Raspberry Piの設定

OSインストール

raspberry pi専用OSであるraspbianをインストールする。
https://qiita.com/westvirginia/items/edc3b56da7be58419d70

raspi-configの設定

raspberry piのパスワード変更やファイルシステム拡張などの設定をする。
http://igarashi-systems.com/sample/translation/raspberry-pi/configuration/raspi-config.html

LAN設定

/etc/network/interfacesに下記を追記。
ここではIPアドレスを192.168.11.19に固定している。

iface wlan0 inet manual
    address 192.168.11.19
    netmask 255.255.255.0
    gateway 192.168.11.1
    wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
    dns-nameservers 192.168.1.1

Apache2のインストール

次のコマンド実行でApache2をインストールする。

sudo apt-get update
sudo apt-get upgrade
sudo apt-get install apache2

PHPの設定

phpのインストールと有効化の設定をする。
https://qiita.com/miyamotok0105/items/851a5e27629bfd1e3226

mysqlのインストール

mysqlサーバとphpからmysqlへアクセスするためのパッケージをインストールする。

sudo apt-get install mysql-server 
sudo apt-get install php5-mysql

※現在はmariaDBに代替されている。
PHPの関数も変換が必要。
https://gray-code.com/php/using-mysql-and-mariadb-by-php/

SSL/TLSの設定

サーバ証明書と秘密鍵の有効化設定をする。
http://itemy.net/?p=1052

プライベート認証局の設定

インターネットからのアクセスと同様に、LAN内でもURLを使い、SSL/TLSを利用するためには、LAN内にDNSサーバとサーバ認証局が必要である。そこでOpenSSLを利用してプライベート認証局を設置する。ドメインの設定欄でwebサーバのURLを記入しておく。
http://dreamerdream.hateblo.jp/entry/2016/01/04/000000

myDNSへ定期的にIP更新通知する設定

固定IPを使わずに、動的IPのままでインターネットからWebサーバにアクセスするために必要な工程となる。cronに次のように記述することで自身のグローバルIPを定期的にmyDNSサービスに通知し、Aレコードを更新する。これによりグローバルIPが変更されても、作成したFQDNと新しいグローバルIPを対応させることができ、固定IPと同等の機能を構築できる。

00 00 * * * wget --http-user=(アカウント) --http-passwd=(パスワード) http://www.mydns.jp/login.html

サーバ証明書を定期的に更新する設定

Let's Encryptで発行されるサーバ証明書の有効期限は3ヶ月である。cronに次のように記載することで、定期的にサーバ証明書を更新する。

00 02 * * * /home/(ユーザ)/letsencrypt/certbot-auto renew --webroot -w /var/www/html -d (URL) 

メールサーバの設定

アプリの機能の一部としてメールサーバを使用するため設定する。
https://www.sbprojects.net/projects/raspberrypi/exim4.php
https://stackoverflow.com/questions/35130119/linux-configuration-ssmtp-cannot-open-smtp-gmail-com587

その他ツール類インストール

その他アプリの機能の一部として使用するものをインストールする。

sudo apt-get install expect
sudo apt-get install etherwake

CGIの設定

CGIを有効化する。

sudo ln -s /etc/apache2/mods-available/cgi.load/etc/apache2/mods-enabled/cgi.load

CGI設定ファイルの219行目のコメントアウトをはずす。

/etc/apache2/mods-available/mime.conf
AddHandler cgi-script .cgi

apache2設定ファイルの28行目のコメントアウトをはずす。

/etc/apache2/sites-available/000-default.conf  
Include conf-available/serve-cgi-bin.conf

apache2を再起動する。

sudo service apache2 restart

/usr/lib/cgi-bin配下に.cgiの拡張子で置いたファイルはスクリプトとして実行されるようになる。

ブラウザ経由でのスクリプト実行権限の設定

ブラウザからサーバにアクセスした場合、ユーザ名はwww-dataとなる。sudoでコマンドを実行するスクリプトをCGIから呼び出した場合、パスワードを問われることでCGIが止まってしまう。そこでパスワード入力が不用となるよう設定する。

/etc/sudoers
www-data ALL=(ALL) NOPASSWD:ALL

ブラウザ経由でのsshの設定

ブラウザ経由でサーバから別サーバにsshログインする際、参照される設定ファイルは/var/www/.sshに配置しなければならない。デフォルトでは初回ログイン時は警告が表示され、expectによるログインが止まってしまう。そこで警告を抑止する。

sudo mkdir -p /var/www/.ssh

ssh設定ファイルを作成し次のように記載する。

/var/www/.ssh/config
Host *
StrictHostKeyChecking no

セッションタイムアウト時間の延長

セッションタイムアウトの時間はデフォルトでは24分となっている。それではすぐに再ログインが必要になってしまうので、時間を延長する。ここでは1日(86400秒)を設定する。

/etc/php5/apache2/php.ini
session.gc_maxlifetime = 86400

外付けハードディスクをマウント

外付けハードディスクにmysqlのデータ蓄積用ディレクトリを割り当てる。まずハードディスクをフォーマットし、/mntにマウントする。/var/lib/mysql以下のファイルを権限ごと/mntにコピーし、ハードディスクを/var/lib/mysqlにマウントしなおす。

sudo mkfs -t ext4 /dev/sdb1
sudo mount /dev/sdb1 /mnt
sudo cp -arf /var/lib/mysql/* /mnt/.
sudo umount /mnt
sudo mount /dev/sdb1 /var/lib/mysql
sudo systemctl restart mysql.service

mysqlにハードディスクをオートマウントする

sudo apt-get install autofs
sudo systemctl enable autofs

blkid /dev/sda1で調べたuuidを/etc/fstabに書き込む

fstab記載例
UUID=1054819d-202a-4cf9-8817-adc947a325f5 /var/lib/mysql ext4 defaults 0 0

その後設定を反映する。

sudo mount -a

データベースの作成

mySQLでデータベースを作成する。

service
user
+----------+--------------+------+-----+---------+-------+
| Field    | Type         | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+-------+
| userID   | int(11)      | YES  |     | 0       |       |
| username | varchar(20)  | YES  | UNI | NULL    |       |
| passwd   | varchar(255) | YES  |     | NULL    |       |
+----------+--------------+------+-----+---------+-------+

servicelist
+-----------+--------------+------+-----+---------+-------+
| Field     | Type         | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| username  | varchar(200) | YES  | UNI | NULL    |       |
| serviceid | int(11)      | YES  |     | NULL    |       |
+-----------+--------------+------+-----+---------+-------+
hhab
goals
+--------+-----------+------+-----+---------+-------+
| Field  | Type      | Null | Key | Default | Extra |
+--------+-----------+------+-----+---------+-------+
| userID | char(100) | YES  |     | NULL    |       |
| goal   | int(11)   | YES  |     | NULL    |       |
+--------+-----------+------+-----+---------+-------+

useruse
+--------+-----------+------+-----+---------+-------+
| Field  | Type      | Null | Key | Default | Extra |
+--------+-----------+------+-----+---------+-------+
| userID | char(100) | YES  |     | NULL    |       |
| time   | datetime  | YES  |     | NULL    |       |
| money  | int(11)   | YES  |     | NULL    |       |
| item   | char(100) | YES  |     | NULL    |       |
| class  | int(11)   | YES  |     | NULL    |       |
+--------+-----------+------+-----+---------+-------+
english
qlist
+--------+--------------+------+-----+---------+----------------+
| Field  | Type         | Null | Key | Default | Extra          |
+--------+--------------+------+-----+---------+----------------+
| qID    | int(11)      | NO   | MUL | NULL    | auto_increment |
| qname  | varchar(200) | YES  |     | NULL    |                |
| answer | varchar(300) | YES  |     | NULL    |                |
| flag   | int(11)      | YES  |     | NULL    |                |
+--------+--------------+------+-----+---------+----------------+

user_qlist
+--------------+--------------+------+-----+---------+-------+
| Field        | Type         | Null | Key | Default | Extra |
+--------------+--------------+------+-----+---------+-------+
| username     | varchar(200) | YES  |     | NULL    |       |
| qID          | int(11)      | YES  |     | NULL    |       |
| answerrepeat | int(11)      | YES  |     | NULL    |       |
| useranswer   | varchar(300) | YES  |     | NULL    |       |
| answerdate   | datetime     | YES  |     | NULL    |       |
+--------------+--------------+------+-----+---------+-------+

wordlist
+----------+---------------+------+-----+---------+-------+
| Field    | Type          | Null | Key | Default | Extra |
+----------+---------------+------+-----+---------+-------+
| word     | varchar(200)  | YES  |     | NULL    |       |
| ex       | varchar(2000) | YES  |     | NULL    |       |
| username | varchar(20)   | YES  |     | NULL    |       |
| count    | int(11)       | YES  |     | NULL    |       |
| memo     | varchar(1000) | YES  |     | NULL    |       |
+----------+---------------+------+-----+---------+-------+
shikaku
wordlist
+-------+---------------+------+-----+---------+----------------+
| Field | Type          | Null | Key | Default | Extra          |
+-------+---------------+------+-----+---------+----------------+
| id    | int(11)       | NO   | MUL | NULL    | auto_increment |
| word  | varchar(200)  | YES  |     | NULL    |                |
| ex    | varchar(2000) | YES  |     | NULL    |                |
| fig   | varchar(300)  | YES  |     | NULL    |                |
+-------+---------------+------+-----+---------+----------------+
shogi
ban
+--------+-----------+------+-----+---------+-------+
| Field  | Type      | Null | Key | Default | Extra |
+--------+-----------+------+-----+---------+-------+
| id     | int(11)   | YES  |     | NULL    |       |
| banmen | char(200) | YES  |     | NULL    |       |
| oki    | char(200) | YES  |     | NULL    |       |
| teban  | int(11)   | YES  |     | NULL    |       |
+--------+-----------+------+-----+---------+-------+

chat
+-------+-----------+------+-----+---------+-------+
| Field | Type      | Null | Key | Default | Extra |
+-------+-----------+------+-----+---------+-------+
| id    | int(11)   | YES  |     | NULL    |       |
| name  | char(20)  | YES  |     | NULL    |       |
| msg   | char(200) | YES  |     | NULL    |       |
+-------+-----------+------+-----+---------+-------+

heya
+-------+-----------+------+-----+---------+-------+
| Field | Type      | Null | Key | Default | Extra |
+-------+-----------+------+-----+---------+-------+
| id    | int(11)   | YES  |     | NULL    |       |
| sente | char(200) | YES  |     | NULL    |       |
| gote  | char(200) | YES  |     | NULL    |       |
+-------+-----------+------+-----+---------+-------+

highlight
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
| ban   | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+

kifu
+--------+-----------+------+-----+---------+-------+
| Field  | Type      | Null | Key | Default | Extra |
+--------+-----------+------+-----+---------+-------+
| id     | int(11)   | YES  |     | NULL    |       |
| banmen | char(200) | YES  |     | NULL    |       |
| oki    | char(200) | YES  |     | NULL    |       |
+--------+-----------+------+-----+---------+-------+

resign
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id    | int(11) | YES  |     | NULL    |       |
| teban | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+

コードの作成

ユーザ登録・認証

https://github.com/jun6231jp/myservice/tree/top/top
ユーザ認証画面からログイン。新規ユーザ登録はsign upから。ユーザが登録するとユーザ名が管理者にメールで通知され、管理者がユーザにアクセス権限を与える。

管理者用の権限コントロールアプリ画面。
https://github.com/jun6231jp/myservice/tree/top/control
77A080AF-71D4-4A4D-9B22-EEF9E20CFDD0.jpeg

登録ユーザーごとに使用可能なアプリケーションをチェックボックスで選択する。

※管理者権限ではサーバ電源操作アプリも使用できる。外出先からでもスマホ画面操作することで、Raspberry piからLAN内にある自宅PCの起動、シャットダウン、サスペンドが可能。
使用するにはスクリプトの権限と所有者の設定をする必要がある。

sudo chmod 755 /home/user/*sh
sudo chmod 755 /home/user/*exp
sudo chown root:root /home/user/*sh
sudo chown root:root /home/user/*exp

スクリプト実行結果を格納する/home/httpd/msgファイルの権限と所有者の設定をする。

sudo mkdir -p /home/httpd
sudo touch /home/httpd/msg
sudo chmod 777 /home/httpd/msg
sudo chown root:root /home/httpd/msg

さらにVPNを構築することなくブラウザでファイル編集も可能にした。
993DD0B8-EA8B-43EE-A1E2-270D5B655320.jpeg

一般ユーザでログイン後はポータル画面に移動する。アクセス権限が与えられたアプリのみ画面に表示される。

家計簿アプリ

https://github.com/jun6231jp/myservice/tree/top/hhab
何にいくら使ったかを登録する画面。欄をクリックすると過去に入力した項目リストが選択候補として表示される。食費などの家計をポケットマネーで立て替えた場合など、後に精算が必要なものはチェックボックスにチェック。その他、月ごとの目標使用額の設定が可能。

使用履歴一覧。本日の使用額、今月の使用額が計算される他、目標額までの残額、チェックボックスでチェックをつけて登録した項目のうち未精算の項目合計額が計算される。

英語アプリ

https://github.com/jun6231jp/myservice/tree/top/english

TOEIC学習用アプリのトップ画面。

英単語検索アプリ画面。意味を調べたい英単語を入力。英単語の意味をWebllioから自動で検索し、英単語帳に登録する。手入力のメモも併せて登録可能。

英単語帳の画面。

TOEIC問題選択画面。メニュー右横の数字は学習回数。

学習を始める前にTOEIC教材のタイトルと解答を登録しておく。TOEIC registrationのボタンから登録画面に移動する。

登録後、メニュー画面に戻ると登録した問題メニューボタンが現れるのでクリック。教材の模範解答登録画面に移動する。

模範解答登録が完了すると背景が白に変わり、TOEIC回答欄画面になる。チェックボックスにチェックをつけて提出すると自動採点される。回答途中でコミットした場合、採点はされず、途中状態が保持される。回答再開した際に、前回中断状態から開始できる。

点数と解いた問題数はグラフ化される。グラフでは他ユーザの状態も見ることができる。
577A18A5-E716-441A-B1A5-A33C67B20BFA.jpeg

資格勉強用アプリ

https://github.com/jun6231jp/myservice/tree/top/shikaku

テーブル作成画面でテーブル名を入力する。
すでに他のユーザーが同じテーブル名で作成済みならテーブルを共有することができる。
15A880AE-4C00-475E-A509-58249B34682B.jpeg

テーブル登録画面ではタイトルとその説明を登録する。さらに図をつけたい場合は添付する。

登録した単語、説明文、図は表形式で記録される。

ゲームアプリ

https://github.com/jun6231jp/myservice/tree/top/game

オンライン将棋アプリを作成した。
※move.cは/home/user/C/move.outとしてコンパイルする必要あり。ban.phpと同じ階層にshogiというディレクトリを作成し、好きな駒の画像を保存する。命名ルールは先手の歩ならSfu.png、後手の成銀ならGngin.pngなど。

対局室選択画面。マシンスペック、ネットワーク負荷など考慮し3部屋まで。

対局画面。観戦も可能。

選択した駒は青枠でハイライト、最新手は黒枠でハイライトされる。チャット機能も付けた。

棋譜再生画面。対戦者も観戦者も棋譜の閲覧が可能。

さらにマインスイーパを作成した。初期画面が現れるのでリセットボタンからステージの設定をする。
8389356B-828D-4019-95D4-97A70832853E.jpeg

ステージサイズとボム数を設定する。
495EBA87-7EF9-4205-9433-D3B96206D05F.jpeg

フラグとオープンを使い分けながらマスをクリックする。
F6E34DAE-06FA-490E-A15F-F91451ACA162.jpeg

サイトに広告を掲載する

自作サイトに広告を掲載することで、広告収入を得ることも可能。Googleアフィリエイトはドメイン名がサブドメインでは使えない。MyDNSで付与されるドメインはサブドメイン(○○○.○○○.○○のような形式)であるため別のサービスを利用する必要がある。ここでは次のサービスを利用している。

https://www.a8.net/

例としてバナー集を作成した。
3019DAE2-FB9F-4ABF-ACF8-07A814C61118.jpeg

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした