Raspberry Pi 2 Model B + docker-compose上に、Django + PostgreSQLなアプリをデプロイしてみた

Raspberry Pi 2 Model Bを使ってDjangoアプリを作ろうかなと思いました。

環境構築をどうするかと考えたところ、RaspbianでDockerを動かせると知りました。
Docker comes to Raspberry Pi - Raspberry Pi

Dockerが動くならdocker-composeもいけるのではと思い、調べてみたところ動かしている方々がいました。

そこで、Raspberry Pi 2 Model B + docker-compose上に、Django + PostgreSQLなアプリをデプロイしてみました。

 
目次

 

環境

 
なお、DjangoアプリやDockerまわりの設定は、公式チュートリアルを改変したものです。
Quickstart: Compose and Django | Docker Documentation

 

Raspbian 2018-04-18のセットアップ

以前の記事を参考にセットアップしました。
Python2 + Scapyで、Raspberry Pi 2 Model B をブリッジにできるか試してみた #router_jisaku - メモ的な思考的な

内容としては、

です。

 

デプロイするものを作成

以下の

  • Djangoアプリ
  • Dockerfile
  • docker-compose.yml

GitHubにpushしておきます。

 

Djangoアプリ

今回作成するDjangoアプリは

  • localhost:8000/myapp/<pk> にアクセスするとTemplateViewを返す
  • Django adminを利用可能
  • MacではSQLite、Raspbian上ではPostgreSQLを使用
  • makemigrationsも実施済
  • 環境変数により、DBバックエンドをSQLitePostgreSQLのどちらかを利用する

という簡単なものです。

リポジトリにpushしてあるので、今回は省略します。
https://github.com/thinkAmi-sandbox/rpi_docker_django_postgres-sample

 
なお、requirements.txtも用意しましたが、PostgreSQL用のライブラリとして psycopg2-binary を指定しました。

リリースノートによると、2.7.4からパッケージが分かれ、 psycopg2-binary を使うように書かれているためです。
http://initd.org/psycopg/articles/2018/02/08/psycopg-274-released/

そのため、psycopg2を指定するとワーニングが出たため、 psycopg2-binary を指定しています。

 

Dockerfile

Docker公式チュートリアルのものです。

FROM python:3
ENV PYTHONUNBUFFERED 1
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/
RUN pip install -r requirements.txt
ADD . /code/

 

docker-compose.ymlの作成

こちらは、公式チュートリアルのものを一部修正しています。

  • 外部からPostgreSQLに接続できるよう、dbにポートを追加
  • Docker上かどうかを判断するため、 IS_DOCKER という環境変数を設定
    • 良い方法でないかもしれませんが、今回はお手軽に...
version: '3'

services:
  db:
    image: postgres
  web:
    build: .
    command: python3 manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8000:8000"
    depends_on:
      - db

 

RaspbianにDockerまわりをインストール

  • Docker
  • docker-compose

をインストールします。

 

Docker

公式ブログの通り、Dockerをインストールします。

pi@raspberrypi:~ $ curl -sSL https://get.docker.com | sh

# Executing docker install script, commit: 36b78b2
+ sudo -E sh -c apt-get update -qq >/dev/null
+ sudo -E sh -c apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null
+ sudo -E sh -c curl -fsSL "https://download.docker.com/linux/raspbian/gpg" | apt-key add -qq - >/dev/null
Warning: apt-key output should not be parsed (stdout is not a terminal)
+ sudo -E sh -c echo "deb [arch=armhf] https://download.docker.com/linux/raspbian stretch edge" > /etc/apt/sources.list.d/docker.list
+ [ raspbian = debian ]
+ sudo -E sh -c apt-get update -qq >/dev/null
+ sudo -E sh -c apt-get install -y -qq --no-install-recommends docker-ce >/dev/null
+ sudo -E sh -c docker version
Client:
 Version:      18.05.0-ce
 API version:  1.37
 Go version:   go1.9.5
 Git commit:   f150324
 Built:        Wed May  9 22:24:36 2018
 OS/Arch:      linux/arm
 Experimental: false
 Orchestrator: swarm

Server:
 Engine:
  Version:      18.05.0-ce
  API version:  1.37 (minimum version 1.12)
  Go version:   go1.9.5
  Git commit:   f150324
  Built:        Wed May  9 22:20:37 2018
  OS/Arch:      linux/arm
  Experimental: false
If you would like to use Docker as a non-root user, you should now consider
adding your user to the "docker" group with something like:

  sudo usermod -aG docker pi

Remember that you will have to log out and back in for this to take effect!

WARNING: Adding a user to the "docker" group will grant the ability to run
         containers which can be used to obtain root privileges on the
         docker host.
         Refer to https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface
         for more information.

 
インストール時のログに従い、 docker グループに pi ユーザを追加します。

pi@raspberrypi:~ $ sudo usermod -aG docker pi

 
念のため、再起動しておきます。

pi@raspberrypi:~ $ sudo shutdown -r now 

 

docker-compose

docker-composeはpipでインストールします。

なお、 sudo 忘れると docker-compose コマンドが使えなかったので、忘れずにつけておきます。

pi@raspberrypi:~ $ sudo pip install docker-compose
...
Successfully installed PyYAML-3.12 backports.ssl-match-hostname-3.5.0.1 cached-property-1.4.2 docker-3.3.0 docker-compose-1.21.2 docker-pycreds-0.2.3 dockerpty-0.4.1 docopt-0.6.2 functools32-3.2.3.post2 jsonschema-2.6.0 texttable-0.9.1 websocket-client-0.47.0

 

ソースコードをclone

Raspbian上の適当なディレクトリに、GitHubリポジトリをcloneしておきます。

pi@raspberrypi $ git clone https://github.com/thinkAmi-sandbox/rpi_docker_django_postgres-sample.git .

 

manage.pyの実行

1回限りの作業

  • migrate
  • loaddata
  • createsuperuser

docker-compose run で実行していきます。

 

migrate

初めて docker-compose コマンドを使ったため、migrate前に色々な設定が走ります *1

pi@raspberrypi $ docker-compose run web python manage.py migrate

Creating network "docker_django_default" with the default driver
Creating docker_django_db_1 ... done
Building web
Step 1/8 : FROM python:3
 ---> 98281429ea18
Step 2/8 : ENV PYTHONUNBUFFERED 1
 ---> Running in 913b4e88ef19
Removing intermediate container 913b4e88ef19
---> 52f2af605dd4
Step 3/8 : RUN mkdir /code
---> Running in 272a8670ae9e
Removing intermediate container 272a8670ae9e
---> ab158a0f4822
Step 4/8 : WORKDIR /code
Removing intermediate container d991836249af
---> 35eec4df510b
Step 5/8 : ADD requirements.txt /code/
---> 0f31d94edaf2
Step 6/8 : RUN pip install -r requirements.txt
---> Running in ecf1decd7b32
Collecting django (from -r requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/23/91/2245462e57798e9251de87c88b2b8f996d10ddcb68206a8a020561ef7bd3/Django-2.0.5-py3-none-any.whl (7.1MB)
Collecting psycopg2-binary (from -r requirements.txt (line 2))
 Downloading https://files.pythonhosted.org/packages/77/09/4991fcd9a8f4bea1ee3948e1729fa17c184d25bd10809bacc143626361b9/psycopg2-binary-2.7.4.tar.gz (426kB)
Collecting pytz (from django->-r requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/dc/83/15f7833b70d3e067ca91467ca245bae0f6fe56ddc7451aa0dc5606b120f2/pytz-2018.4-py2.py3-none-any.whl (510kB)
Building wheels for collected packages: psycopg2-binary
 Running setup.py bdist_wheel for psycopg2-binary: started
 Running setup.py bdist_wheel for psycopg2-binary: still running...
 Running setup.py bdist_wheel for psycopg2-binary: still running...
 Running setup.py bdist_wheel for psycopg2-binary: finished with status 'done'
 Stored in directory: /root/.cache/pip/wheels/60/a9/b1/390d13a4c6ae769c74c56efdd25573d76b9fba441430189658
Successfully built psycopg2-binary
Installing collected packages: pytz, django, psycopg2-binary
Successfully installed django-2.0.5 psycopg2-binary-2.7.4 pytz-2018.4
Removing intermediate container ecf1decd7b32
---> 4cb1031555c5
Step 7/8 : ADD . /code/
 ---> cfa31851aba0
Step 8/8 : ENV IS_DOCKER true
 ---> Running in 9d76ae22228f
Removing intermediate container 9d76ae22228f
 ---> dd8368ef0606
Successfully built dd8368ef0606
Successfully tagged docker_django_web:latest
WARNING: Image for service web was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, myapp, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying myapp.0001_initial... OK
  Applying sessions.0001_initial... OK

 

fixture
pi@raspberrypi $ docker-compose run web python manage.py loaddata initial_data

Starting docker_django_db_1 ... done
Installed 1 object(s) from 1 fixture(s)

 

createsuperuser
pi@raspberrypi $ docker-compose run web python manage.py createsuperuser

Starting rpi_docker_django_postgres_sample_db_1 ... done
Username (leave blank to use 'root'): admin
Email address: admin@example.com
Password: 
Password (again): 
Superuser created successfully.

 

docker-composeによる起動

docker-compose up で起動します。

pi@raspberrypi $ docker-compose up

docker_django_db_1 is up-to-date
Creating docker_django_web_1 ... done
Attaching to docker_django_db_1, docker_django_web_1
db_1   | The files belonging to this database system will be owned by user "postgres".
db_1   | This user must also own the server process.
...
db_1   | 2018-05-24 12:39:39.750 UTC [1] LOG:  database system is ready to accept connections
web_1  | Performing system checks...
web_1  | 
web_1  | System check identified no issues (0 silenced).
web_1  | May 24, 2018 - 12:42:26
web_1  | Django version 2.0.5, using settings 'myproject.settings'
web_1  | Starting development server at http://0.0.0.0:8000/
web_1  | Quit the server with CONTROL-C.

 

接続確認

curl

問題なく動作していました。

$ curl http://192.168.10.201:8000/myapp/1/

<div class="container">
    ふー
</div>

 

Django admin

Django adminのログイン〜データ作成まで問題なくできました。

f:id:thinkAmi:20180524215923p:plain:w300

 
追加したデータへのcurlも成功しました。

$ curl http://192.168.10.201:8000/myapp/2/
<div class="container">
    ばー
</div>

 

PostgreSQL

psql を使って、登録したデータを確認します。

 

接続

まず、Raspbian上で docker-compose psPostgreSQLのポートがRaspbianに転送されているかを確認します。

# docker-compose.ymlファイルが存在するディレクトリにいることを確認
pi@raspberrypi $ ls -al
...
-rw-r--r-- 1 pi pi  282 May 26 12:30 docker-compose.yml

# PostgreSQLの5432ポートが、Raspbianの9876ポートに転送されていることを確認
pi@raspberrypi $ docker-compose ps
       Name                      Command               State           Ports         
-------------------------------------------------------------------------------------
docker_django_db_1    docker-entrypoint.sh postgres    Up      0.0.0.0:9876->5432/tcp
docker_django_web_1   python3 manage.py runserve ...   Up      0.0.0.0:8000->8000/tcp

 
次に、RaspbianもしくはMac上で psql を使って接続します。

# Macの場合
$ psql -h 192.168.10.201 -U postgres -p 9876
psql (10.3, server 10.4 (Debian 10.4-1.pgdg90+1))
Type "help" for help.

postgres=# 

 

テーブル一覧とデータの確認

\dt で、テーブルの一覧を確認します。Djangoのテーブルがあるようです。

postgres=# \dt
                   List of relations
 Schema |            Name            | Type  |  Owner   
--------+----------------------------+-------+----------
 public | auth_group                 | table | postgres
 public | auth_group_permissions     | table | postgres
 public | auth_permission            | table | postgres
 public | auth_user                  | table | postgres
 public | auth_user_groups           | table | postgres
 public | auth_user_user_permissions | table | postgres
 public | django_admin_log           | table | postgres
 public | django_content_type        | table | postgres
 public | django_migrations          | table | postgres
 public | django_session             | table | postgres
 public | myapp_foo                  | table | postgres
(11 rows)

 
続いて、データを確認します。

postgres=# select * from myapp_foo;
 id | name 
----+------
  1 | ふー
  2 | ばー
(2 rows)

 

psqlの終了

データが確認できたので、 \qpsqlを終了します。

postgres=# \q

 

停止

Ctrl + C にて停止します。

^CGracefully stopping... (press Ctrl+C again to force)
Stopping docker_django_web_1 ... done
Stopping docker_django_db_1  ... done

 

いらないものを削除

docker-compose down にて削除します。

# 現在の環境を確認
pi@raspberrypi $ docker-compose ps

       Name                      Command                State     Ports
-----------------------------------------------------------------------
docker_django_db_1    docker-entrypoint.sh postgres    Exit 0          
docker_django_web_1   python3 manage.py runserve ...   Exit 137        
pi@raspberrypi:~/work/docker_django $ docker-compose images
     Container           Repository        Tag       Image Id      Size 
------------------------------------------------------------------------
docker_django_db_1    postgres            latest   af4e361a5ae8   218 MB
docker_django_web_1   docker_django_web   latest   d0958cc8b3f7   619 MB

# いらないものを削除
pi@raspberrypi $ docker-compose down
Removing docker_django_web_1     ... done
Removing docker_django_web_run_4 ... done
Removing docker_django_web_run_3 ... done
Removing docker_django_web_run_2 ... done
Removing docker_django_web_run_1 ... done
Removing docker_django_db_1      ... done
Removing network docker_django_default


# 削除されていることを確認
pi@raspberrypi $ docker-compose ps

Name   Command   State   Ports
------------------------------

pi@raspberrypi $ docker-compose images
Container   Repository   Tag   Image Id   Size
----------------------------------------------

## Dockerイメージは残っていることに注意
pi@raspberrypi $ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker_django_web   latest              d0958cc8b3f7        About an hour ago   649MB
postgres            latest              af4e361a5ae8        9 days ago          228MB
python              3                   98281429ea18        2 weeks ago         608MB

 

Raspbianシャットダウン

すべて終わったので、Raspbianをシャットダウンしておきます。

pi@raspberrypi $ sudo shutdown -h now 
Connection to 192.168.10.201 closed by remote host.
Connection to 192.168.10.201 closed.

 

ソースコード

GitHubに上げてあります。
https://github.com/thinkAmi-sandbox/rpi_docker_django_postgres-sample

 

参考

*1:時々、 「django.db.utils.OperationalError: could not connect to server: Connection refused」が発生します。ただ、再度実行すると成功します。タイミングなのでしょうか...