Vultr で GeoDjango アプリケーションを動かすまでの長かった道のり


このポストは、[Django Advent Calendar 2019 - Qiita](https://qiita.com/advent-calendar/2019/django)の１７日目の記事です。

こんちは！しんせいたろうです。[モグモグDjango](https://mogumogu-django.connpass.com/)の主宰してます。

現在、[アーバンデータチャレンジ](https://urbandata-challenge.jp/)に東東京のアザラシチームの一員として、DRFを頑張っています。

「アーバンデータチャレンジ」とは、地域課題の解決を目的とした地方自治体を中心とする公共データを活用した年間のイベント開催を伴う一般参加型コンテストです。東東京チームは定期的にがやがや会という楽しくもくもくする会を開催していますので、公共データや地域課題解決などに興味があるエンジニアの方、ぜひぜひお越しくださいませ。くわしくはこちら→[UDC東東京 - connpass]
(https://udceasttokyo.connpass.com/)

今回、ある公共データを地図にマッピングするアプリケーションを作成するチームに入っています。そこでGeoDjangoを使うことになり、そのサーバー側の設定を全くわからない状態から始めました。以下、サーバー設定＆最初のアプリケーション立ち上げまでの、辛かった道のりでメモしていたので、まとめて書きました。


## 目次

1. Vultr 登録
1. インスタンス作成
1. ウェブアプリケーションインストール
2. データベースインストール
3. データベース作成と設定
1. Django App 作成
1. 独自ドメイン登録

# Vultr 登録

今回、Vultrというインスタンス上にDjangoとPostgresを使って、地図アプリを作って見ることにしました。

まずは[ユーザー登録](https://www.vultr.com/?ref=8347480-4F)します。初めての人には50ドル分サービスがありますので、最初の50ドルまでは課金されずにフル機能使うことができます。使ってみてイマイチだな？と思ったらインスタンスをdestroy すれば一切お金はかかりません。

# インスタンス作成

## 作成

＋ボタンを押せば作成開始です。
![](https://i.imgur.com/dWkl6TA.jpg)

### インスタンスを選ぶ

今回は、Cloud Compute / Tokyo (region) / Ubuntu 18.04 x64 を選びました。
![](https://i.imgur.com/V5KlBGi.jpg)
![](https://i.imgur.com/XF9MSCx.jpg)

### サーバーサイズについて

今回は、25GBSSDを選びました。料金は、**＄5／MO ＄0.007／h** です。これは、**インスタンスを作っている間は毎時70セントはかかるよ。だけど、Max月5ドルだよ**。という意味です。何もしていなくても**インスタンスをDestroyするまでは課金**されます。しかし、どんなに使ってもMax5ドルまでということです。


![](https://i.imgur.com/dnr8NHG.jpg)
### Enter server  label に任意の名前をつけたらDeploy Now

shinseitaro という名前をつけてインスタンスを作りました。

![](https://i.imgur.com/QJSEQWC.jpg)

`Deploy Now` を押すと、インスタンス作成が始まります。


### Instance 確認
インスタンスが出来上がったら、下記のようにリストが表示されます。
自分のインスタンスを開きましょう。
![](https://i.imgur.com/s9sCTD5.jpg)


パスワードは、目のマークを押すと表示され、コピーマークを押すとコピーされます。
![](https://i.imgur.com/n7eKNlH.jpg)


## SSH 接続

ローカルマシンから、リモートインスタンスにSSH接続します。(ローカルマシンでSSHが使えることを前提に話しています。)

1. ローカルマシンから、`root@[Your IP Address]` で接続する。IP と password は先ほどのインスタンス確認画面でできます。

    ```bash
    $ ssh root@xx.xx.xx.xxx

    The authenticity of host 'xx.xx.xx.xxx (xx.xx.xx.xxx)' can't be established.
    ECDSA key fingerprint is xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    Are you sure you want to continue connecting (yes/no)? yes

    Warning: Permanently added 'xx.xx.xx.xxx' (ECDSA) to the list of known hosts.
    root@xx.xx.xx.xxx's password: 
    ```


## User 追加

+ **root で作業するのは危険なので、必ずユーザーを作成しましょう**　(下記例では shinseitaro で作成しています。適宜読み替えてください。)
+ **新規ユーザーを作成したあとは、必ず root では SSH 接続できない設定**をしましょう。
+ パスワードは十分複雑なものにしてください。[パスワード生成（パスワード作成）](https://www.luft.co.jp/cgi/randam.php)などを使うのが便利です。

1. ユーザー作成
2. ssh 接続したターミナルで、`adduser your-new-name`します。`user information` を聞かれますが、全部Enterを押して行けばOKです

    ```
    root@vultr:~# adduser shinseitaro
    
    Adding user `shinseitaro' ...
    Adding new group `shinseitaro' (1000) ...
    Adding new user `shinseitaro' (1000) with group `shinseitaro' ...
    Creating home directory `/home/shinseitaro' ...
    Copying files from `/etc/skel' ...
    New password: 
    Retype new password: 
    passwd: password updated successfully
    Changing the user information for shinseitaro
    Enter the new value, or press ENTER for the default
        Full Name []: 
        Room Number []: 
        Work Phone []: 
        Home Phone []: 
        Other []: 
    Is the information correct? [Y/n] Y

    ```
1. 新規ユーザーに sudo 権限を与えます

    ```
    root@vultr:~# gpasswd -a shinseitaro sudo
    Adding user shinseitaro to group sudo
    
    # いったんログアウト
    root@vultr:~# exit
    logout
    ```
1.  新規ユーザーで ssh 

    ```bash
    $ ssh shinseitro@xxx.xxx.xxx.xx
    $ shinseitaro@vultr:~$ 
    ```
1. rootでは SSH で入れないようにします。`/etc/ssh/sshd_config` を修正して root では SSH出来ないようにします。

    ```bash
    # 私は nano 使ってますが vim でもなんでもいいです
    shinseitaro@vultr:~$ sudo nano /etc/ssh/sshd_config
    
    # ここを yes から no へ変更
    PermitRootLogin no

    ```
1. 変更を反映

    ```bash
    shinseitaro@vultr:~$ sudo service ssh restart
    ```

## SSH 接続を公開鍵で行う（任意）

ターミナルを閉じると、毎回パスワードを確認されるのはめんどくさいので、公開鍵を使ってログインできるようにします。
【注意】：Windowsでの設定はわかりません。だれかコメントしといてくれると嬉しいです。

#### Vultr インスタンス側：

    ```
    $ mkdir /home/[username]/.ssh 
     ```


#### ローカルマシン側: 

1. ssh-keygenを使って公開/秘密鍵のペアを作成。

    ```bash
    $ mkdir $HOME/.ssh
    $ cd $HOME/.ssh
    $ ssh-keygen -t rsa -b 4096 -f id_rsa_vultr
    ```
    ファイル名`id_rsa_vultr` は何でもOKです。
1. Vultr へ 公開鍵を scp で送信する。

    ```bash
    $ scp id_rsa_vultr.pub [user_name]@[remote_ip_addr]:/home/[user_name]/.ssh/authorized_keys
    ```
1. ./ssh/config に設定を追加

    ```bash
    $ vi ~/.ssh/config
    
    # 下記貼り付け
    Host hoge
    HostName xx.xx.xx.xxx
    User your-username
    IdentityFile ~/.ssh/id_rsa_vultr
    ServerAliveInterval 60
    ServerAliveCountMax 3
    ```
    + `Host` は、ssh 接続するときに呼び出す設定名です。何でもOK。
    + `HostName` は Vulter インスタンスIP
    + `User` は、先程追加した新規ユーザ名
    + `IdentityFile` は、ssh-keygenしたとき作ったファイル名

#### Vultr インスタンス側：

```bash
$ chmod 644 ~/.ssh/authorized_keys
```
#### ローカルマシン側： 

接続確認。`./ssh/config`で `Host`に指定した名前でssh接続できます。

```bash
$ ssh hoge
Welcome to Ubuntu 18.04.3 LTS (GNU/Linux 4.15.0-55-generic x86_64)
:
:
```


# いろいろインストール

上記までで、インスタンスに関連する設定は終わりです。これからアプリケーション用のインストールや設定にはいります。

+ Python (miniconda)
+ Django 
+ Git 
+ psycopg2 (Python からPostgresSQLに接続するためのライブラリ)
+ GDAL (地図データを触るためのライブラリ。)



## Python 

色々悩んだ結果、miniconda で pythonをインストールすることにしました。（今となっては何に悩んだかさえ忘れました）

### miniconda をインストール

```bash
$ curl -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
$ bash ./Miniconda3-latest-Linux-x86_64.sh

# このあと色々と聞かれる。
Do you accept the license terms? [yes|no]
>>> yes

Miniconda3 will now be installed into this location:
/home/shinseitaro/miniconda3

>>> [Enter]押す

installation finished.
Do you wish the installer to initialize Miniconda3
by running conda init? [yes|no]
[no] >>> yes

# ながなが色々インストールされる。
# これが出たらインストール完了です
Thank you for installing Miniconda3!
```

### python3.7 の仮想環境をつくってアクティベートする。 

Pythonのバージョン、および仮想環境名はお好きなものに変えてください。

```bash
# bashrc を適用する
$ source ~/.bashrc

# プロンプトが下記の様に(base) yourname@vultrに変わればOK。
(base) shinseitaro@vultr:~$ 

# python3.7 の仮想環境をつくってアクティベート
(base) shinseitaro@vultr:~$ conda create --name py37 python=3.7 -y
(base) shinseitaro@vultr:~$ conda activate py37


# プロンプトが base から py37 に変わればOK。
(py37) shinseitaro@vultr:~$ 

# python 起動して3.7か確認
$ python 
Python 3.7.4 (default, Aug  9 2019, 18:51:30) 
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

# インストール用の miniconda.sh はもういらないので削除
(py37) shinseitaro@vultr:~$ rm Miniconda3-latest-Linux-x86_64.sh 

```

### 次回起動時からは、デフォルトで py37 が起動するように変更（任意）

この設定をすると、次回 ssh接続したときに、 (base) ではなく (py37) がデフォルトで起動します。

``` bash
$ nano ~/.bashrc

# 最後の行に追加して保存
source activate py37
```


## Django 

```bash
$ pip install Django==2.1.11
```
注意：現在Djangoのバージョン指定を行わないと、3.0がデフォルトでインストールされます。2系以下を選びたいときはバージョンを指定してください。

## psycopg2

```bash
$ conda install psycopg2 -y
```
メモ：pip で入れようと試行錯誤したのですが `ERROR: Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-install-lefl0f21/psycopg2/`を解決できず、conda にしました。


# データベースインストール

## PostgreSQL + PostGIS のインストールと設定

GeoDjangoアプリを作りたいので、以下をインストールして、設定していきます。

+ PostgreSQL 
+ PostGIS 
+ GEOS
+ GDAL
+ PROJ.4

## PostgreSQL

### レポジトリキーをインポート
```bash
$ apt-get install curl ca-certificates gnupg
$ curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
```

### /etc/apt/sources.list.d/pgdg.list を作成
 
:fire: 注意
[bionic]のところは、自分の`UBUNTU_CODENAME`を入れてください。`cat /etc/os-release`すればわかります。

```bash
root@vultr:~# echo "deb http://apt.postgresql.org/pub/repos/apt/ bionic-pgdg main" | sudo tee -a /etc/apt/sources.list.d/pgdg.list
```

### package listsを更新してインストール

```bash
$ apt-get update 
$ apt-get install postgresql-11 pgadmin4
```

### 確認

インストールすると、`postgres` ユーザというDB管理用のユーザが、インスタンスに自動で生成されます。`postgres`にスイッチしてDBの確認をします。

```bash
# shinseitaro から postgres にスイッチ
shinseitaro $ su - postgres

# スイッチしたらDB確認
postgres@vultr:~$ psql -V
psql (PostgreSQL) 11.5 (Ubuntu 11.5-1.pgdg18.04+1)

```

### postgres にパスワードを与える

`postgres`にパスワードを与えます。今後は `su` するときにこのパスワードを入力します。

```bash
$ passwd postgres
New password: 
Retype new password: 
passwd: password updated successfully
```

## PostGIS

```bash
$ apt-get install postgis -y
$ apt install postgresql-11-postgis-2.5
$ apt-get install postgresql-contrib 
$ apt install postgresql-11-postgis-2.5-scripts
```


## GEOS

```bash
$ apt-get install libgeos-dev -y
``` 

## PROJ.4 

```bash
$ apt-get install libproj-dev proj-data proj-bin -y
```


# データベース 作成

新規DBを作成します。`postgres` ユーザにスイッチして作業します。

```bash
$ su - postgres
postgres@vultr:~$ 
```

### 現在のDBを確認

まったく新しい現時点では下記のようなDBが存在すると思います。

混乱しがちですが、先程新規作成された `postgres` ユーザはマシンユーザーです。

このリストに乗っている、`Name privileges: postgres` と `Owner: postgres` はマシンユーザーのことではなく、データベース名とオーナー名とです。混乱しやすいので注意してください。


```bash
# psql に入る
postgres@vultr:~$ psql

# 現時点でのDataBaseリストを出力
postgres=# \l
                                List of databases
    Name     |    Owner    | Encoding |   Collate   |    Ctype    |   Access privileges   
-------------+-------------+----------+-------------+-------------+-----------------------
 postgres    | postgres    | UTF8     | en_US.UTF-8 | en_US.UTF-8 | 
 template0   | postgres    | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
             |             |          |             |             | postgres=CTc/postgres
 template1   | postgres    | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres          +
  
```

この オーナー`postgres`にパスワードを与えます。

```bash
postgres=# Alter Role postgres with password 'new_password';
postgres=# exit
postgres@vultr:~$ 
```
    
### 新規DBを作成

データベースとそのオーナーを作成します。
例として `shinseitaro` オーナーを作成して `testdb1` というデータベースを作り、そのオーナーに指定しています。

```bash
# postgres ユーザにログインしている状態。shinseitaro 作成します。
postgres@vultr:~$ createuser shinseitaro

# testdb1を作成。オーナーをshinseitaroに指定。
postgres@vultr:~$ createdb testdb1 --owner shinseitaro

# password 設定 
postgres@vultr:~$ psql -c "ALTER USER shinseitaro WITH PASSWORD 'some_pwd'"
ALTER ROLE
```

### pg_hba.conf 編集

では、`shinseitaro` で `testdb1` にログインしてみます。すると、`psql: FATAL:  Peer authentication failed `が発生します。

```bash
postgres@vultr:~$ psql -U shinseitaro -d testdb1
psql: FATAL:  Peer authentication failed for user "shinseitaro"
``` 

これは、OS側（ここでは postgres）のユーザ名と、PostgreSQL にログインしようとしているユーザー名( shinseitaro )が一致していないときに起きるエラーです。これをPeer認証と言います。これをパスワード認証に変更して、ログインできるようにします。

この設定は、Web AppとDBが違うマシン上にあるときにも必要です。その場合はIPアドレスを設定する必要があります。


### pg_hba.conf 確認

postgres ユーザをexit

```bash
postgres@vultr:~$ exit
```

pg_hba.conf ファイルを探す. わたしの場合はここにありました。このパスで進めます

```bash
$ find /etc/ -name pg_hba.conf
/etc/postgresql/11/main/pg_hba.conf
```

確認

```bash
$ cat /etc/postgresql/11/main/pg_hba.conf
# Database administrative login by Unix domain socket
local   all             postgres                                peer

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     peer
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
# IPv6 local connections:
host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     peer
host    replication     all             127.0.0.1/32            md5
host    replication     all             ::1/128                 md5
```

### pg_hba.conf 編集

現在設定されている、

```
local   all             all                                     peer
local   replication     all                                     peer
```
`peer` を `md5` に変更します。

またIPアドレスの指定が必要な場合は、

```
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
```
ここにIPアドレスを追加していきます。

こうすれば、追加されたインスタンスからこのPostgresサーバー（インスタンス）にアクセスできるようになります。DjangoのMigrationで、`Connection refused`や`SSL OFF` といったエラーが発生する場合は、ここにIPが追加されていない可能性が高いです。

よって最終的にはこのようになります。

```
# Database administrative login by Unix domain socket
local   all             postgres                                peer

# TYPE  DATABASE        USER            ADDRESS                 METHOD

# "local" is for Unix domain socket connections only
local   all             all                                     md5
# IPv4 local connections:
host    all             all             127.0.0.1/32            md5
host    all             all             xxx.xxx.xxx.xx/32       md5
host    all             all             zzz.zzz.zz.zzz/32        md5

# IPv6 local connections:
host    all             all             ::1/128                 md5
# Allow replication connections from localhost, by a user with the
# replication privilege.
local   replication     all                                     md5
host    replication     all             127.0.0.1/32            md5
host    replication     all             ::1/128                 md5

```

【注意】postgresは md5 にしないよう注意してください。

```
local   all             postgres                                peer
```

### 設定を有効化する

```
$ service postgresql restart
```

確認

``` bash
$ psql -U shinseitaro -d testdb1
testdb1=> 

# table表示を試してみる．何も作ってないので無いよ，と言われる．
testdb1=> \d
Did not find any relations.
testdb1=> 
```

### postgresql.conf 修正

Web AppとDBが違うマシン上にあるときは、postgresql.conf 修正も必要です。`listen_addresses`にIPを追加します。

確認

Postgres がどのアドレスをlistenしてるか確認します。`localhost`だけですね。これを変更します。

```bash
$ lsof -i :5432
COMMAND    PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
postgres 11840 postgres    5u  IPv6 490443      0t0  TCP localhost:postgresql (LISTEN)
postgres 11840 postgres    6u  IPv4 490444      0t0  TCP localhost:postgresql (LISTEN)

```

postgresql.confのパスを探す

```bash
$ find /etc/ -name postgresql.conf
/etc/postgresql/11/main/postgresql.conf 

```

修正する。ここでは、全てを受け入れる `*` にしているが、ちゃんとアドレス書いたほうがいいと思います。

```bash
$ nano /etc/postgresql/11/main/postgresql.conf 

# listen_addresses を変更する。 
#listen_addresses = 'localhost' 
listen_addresses = '*' 
```

設定を有効化して確認。さきほどは `localhost` だったところが、`*` になっていると思います。

```
$ service postgresql restart

$ lsof -i :5432
COMMAND    PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
postgres 15606 postgres    5u  IPv4 542478      0t0  TCP *:postgresql (LISTEN)
postgres 15606 postgres    6u  IPv6 542479      0t0  TCP *:postgresql (LISTEN)
```


### PostGIS Extention インストール

やっとPostgresの設定が終わりました。しかし空間データをDBで扱うためには、PostgreSQL+PostGISのインストールだけではだめです。拡張機能をインストールする必要があります。

:fire: 注意

+ 拡張機能は、**それぞれのDBに**インストールします。
+ `postgres` ユーザに追加権限があります。
+ `postgres` ユーザは `postgres` というDBを所有していますが、このDBには決して拡張機能を追加してはいけません。

例では、データベース`testdb1`に空間データを扱うためのエクステンションを追加しています。

```bash
$ su - postgres

# Extention を追加していく。
$ psql -d testdb1 -c "CREATE EXTENSION postgis;"
CREATE EXTENSION

# 以下同じように全部追加
$ psql -d testdb1 -c "CREATE EXTENSION postgis_topology;"
$ psql -d testdb1 -c "CREATE EXTENSION postgis_sfcgal;"
$ psql -d testdb1 -c "CREATE EXTENSION fuzzystrmatch;"
$ psql -d testdb1 -c "CREATE EXTENSION address_standardizer;"
$ psql -d testdb1 -c "CREATE EXTENSION address_standardizer_data_us;"
$ psql -d testdb1 -c "CREATE EXTENSION postgis_tiger_geocoder;"
```

全部追加したらExtensionを表示してみましょう。

```bash
$ psql -U shinseitaro -d testdb1

psql (11.5 (Ubuntu 11.5-1.pgdg18.04+1))
Type "help" for help.

# 拡張機能リストを見る
testdb1=> \dx
                                                                        List of installed extensions
                Name             | Version |   Schema   |                                                     Description                                                     
------------------------------+---------+------------+---------------------------------------------------------------------------------------------------------------------
    address_standardizer         | 2.5.2   | public     | Used to parse an address into constituent elements. Generally used to support geocoding address normalization step.
    address_standardizer_data_us | 2.5.2   | public     | Address Standardizer US dataset example
    fuzzystrmatch                | 1.1     | public     | determine similarities and distance between strings
    plpgsql                      | 1.0     | pg_catalog | PL/pgSQL procedural language
    postgis                      | 2.5.2   | public     | PostGIS geometry, geography, and raster spatial types and functions
    postgis_sfcgal               | 2.5.2   | public     | PostGIS SFCGAL functions
    postgis_tiger_geocoder       | 2.5.2   | tiger      | PostGIS tiger geocoder and reverse geocoder
    postgis_topology             | 2.5.2   | topology   | PostGIS topology spatial types and functions
(8 rows)


```



# Django App 作成



やっとここまで来ましたYo!長すぎて頭おかしくなりそうです。

ここからは、[mamix1116さん](https://twitter.com/mamix1116)が以前書いた、[GeoDjangoでWeb地図をつくろう！](https://medium.com/infradatachallenge/idc-river-ce0f6cd3da43)にフォローしてGeoDjangoアプリを作成していきます。やっとDjangoよ！


### 作業のすすめかた

今回は実験的に、Vultr上に作ったプロジェクトディレクトリを、ローカルマシンにマウントして、コーディングしていくことにします。よって、

+ Vultr側にプロジェクトディレクトリ（今回は `code`）
+ ローカルにマウント用のディレクトリ（今回は `/home/azarashi/vultr`）

を用意して進めます。この方法は、ローカルでコーディングをしてデプロイする普通の作業工程では必要ありません。デプロイする人は無視してください。

また、Vultrで実行するコマンドは、 :v: を、ローカルで実行するときは、 :house: をつけています。ファイルの修正時には何もつけていません。

### 事前準備

#### sshfs インストール

sshfs を使ってVultrのディレクトリを，ローカルにマウントします．sshfs のインストール方法は，以下を参照して下さい。

+ （Ubuntu）：ソフトウェアの管理＞sshfs 検索　＞インストール
+ (Mac):[Macでsshfsを使ってローカルからリモートのファイルを触る - Qiita](https://qiita.com/ysk24ok/items/bb148530a55a4e55d99b)
+ (win): [Windows10からLinuxにSSHで接続し、ネットワークドライブとしてマウントして使うとすごく便利だった](https://lil.la/technology/technology-memo/post-3822)


### startproject と startapp

Vultr上でDjangoのプロジェクトを作成して、`map`というアプリケーションを作成します。


:v: Vultr

```bash
$ mkdir ~/code
$ cd code 
$ pwd
/home/shinseitaro/code

# わたしは、あきよこせんせい信者なので、設定ファイルを全部 config 以下に置くように設定します。
$ django-admin startproject config .
$ ls
config  manage.py

# 地図用のアプリケーション
$ python manage.py startapp map
$ ls
config  manage.py  map
```

### Vultrに作ったディレクトリをローカルにマウント（任意）

sshfs コマンドは，`sshfs [youname]@[IP-Adress]:./code [Local Directory Path]` です．

:house: Local

```
$ mkdir /home/azarashi/vultr 
$ sshfs shinseitaro@xxx.xxx.xxx.xx:./code /home/azarashi/vultr 

# cd して ls すると、先程 Vultr上で作ったDjangoプロジェクトファイルが見えるはずです。
$ cd /home/azarashi/vultr 
$ ls
config  manage.py  map

```

マウントされたディレクトリに入っているファイル等は、ローカルマシンのファイル等と同じように新規作成、修正、削除できます。
`/home/azarashi/vultr` を VSCode や Pycharm で開けば、あたかもローカルで作業しているかのようにインスタンス上のファイルを操作できます。

なお、アンマウントする場合は，`fusermount -u` します。

:house: Local

```
$ fusermount -u /home/azarashi/vultr 
```

もし `fusermount` して，`【fusermount: failed to unmount  Device or resource busy】`　といったエラーが出る場合は `umount` を試してみてください。

```
$ sudo umount  -l /home/azarashi/vultr 
```

### コーディング作業

#### セッティング

`config/settings.py` 修正：

```python
INSTALLED_APPS = [
    ...
    'django.contrib.gis', # 追加
    'map',# 追加
]


# DBは PostgreSQLに変更
DATABASES = {
    # 'default': {
    #     'ENGINE': 'django.db.backends.sqlite3',
    #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    # }
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.postgis',
        'NAME': 'testdb1',
        'USER': 'shinseitaro',
        'PASSWORD': 'xxxxxx',
        'HOST': 'xxx.xxx.xx.xxx',
        'PORT': 5432,
    }    
}

```

#### createsuperuser

:v: Vultr

```bash
$ python manage.py migrate
$ python manage.py createsuperuser 
$ python manage.py runserver xx.xx.xxx.xx:8000
```
IPアドレス:8000 にアクセスするとロケットが飛んでいるはずです。

#### 地図データをUploadする

今回は、保育所のデータをサンプルとして作成しました。

https://drive.google.com/file/d/18IniZGxDsQzE6MBcq2BEPU58DN-zowI1/view?usp=sharing

`map/data` ディレクトリを作成して、このjsonファイルを格納します。

#### ogrinspect を使って、json 構造から、GeoDjango model クラスを自動作成

`ogrinspect` オプションを使って model class を自動生成するには、以下のコマンドを使います。

```bash
$ python manage.py ogrinspect --srid=XXXX path/to/geojson modelname
```

今回の場合は、

```bash
$ python manage.py ogrinspect --srid=6668 ./map/data/201810-2-1-hoikusyo.geojson Hoikusho
``` 

これを実行すると、

```bash
# This is an auto-generated Django model module created by ogrinspect.
from django.contrib.gis.db import models


class Hoikusho(models.Model):
    owned = models.CharField(max_length=0)
    name = models.CharField(max_length=0)
    postalcode = models.CharField(max_length=0)
    address = models.CharField(max_length=0)
    gcs = models.CharField(max_length=0)
    tel = models.CharField(max_length=0)
    capacity = models.IntegerField()
    geom = models.PointField(srid=6668)
```

と出力されますので、これを `map/models.py` にコピペします。

> SRID: 
> srid に関してはこちらが詳しいので参照してください→[空間参照系の概要 - Qiita](https://qiita.com/boiledorange73/items/b98d3d1ef3abf7299aba)
> どのSRIDを使う必要があるのかは、データを作った人に聞くしかありません。もしわからない場合は、よく使われる上記記事中に出てくる、[わが国の陸域でよく使われる空間参照系](https://qiita.com/boiledorange73/items/b98d3d1ef3abf7299aba#%E3%82%8F%E3%81%8C%E5%9B%BD%E3%81%AE%E9%99%B8%E5%9F%9F%E3%81%A7%E3%82%88%E3%81%8F%E4%BD%BF%E3%82%8F%E3%82%8C%E3%82%8B%E7%A9%BA%E9%96%93%E5%8F%82%E7%85%A7%E7%B3%BB)を一つずつ試して地図に出して、自分が知っている地図上の住所から、「なんとなくこれがあってそうだなー」とあたりをつける、という地道な作業を行うらしいです。なんかもやもやする。。。 

#### モデル

自動出力された model に適切なオプションを追加しながら、`map/models.py` にコピペします。

```python
# map/models.py
from django.contrib.gis.db import models

class Hoikusho(models.Model):
    owned = models.CharField(max_length=50)
    name = models.CharField(max_length=50)
    postalcode = models.CharField(max_length=10)
    address = models.CharField(max_length=250)
    gcs = models.CharField(max_length=10)
    tel = models.CharField(max_length=50)
    capacity = models.IntegerField()
    geom = models.PointField(srid=6668)

    def __str__(self):
        return self.name
    
```

ここでは主に `max_length` オプションの変更を行っています。もとの geojsonデータを見て、だいたいの感じでオプションを与えます。


#### migrations & migrate 

:v: Vultr

```bash
$ python manage.py makemigrations
Migrations for 'map':
  map/migrations/0001_initial.py
    - Create model Hoikusho

$ python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, map, sessions
Running migrations:
  Applying map.0001_initial... OK

```

#### データベースにデータをインポート

`django.contrib.gis.utils` の `LayerMapping` クラスは、空間データをGeoDjango モデルに適用するツールを提供しています。
geojsonのキーワードと、GeoDjangoのフィールド名をマッピングした辞書を渡すと、データベースにデータをアップロードしてくれます。


今回のgeojsonデータはこういう形で

```json
{
   "type": "FeatureCollection",
   "features": [
  {
    "type": "Feature",
    "geometry": {
       "type": "Point",
       "coordinates":  [ 139.777421,35.699016 ]
    },
    "properties": {
    "owned":"区市町村",
    "name":"いずみこども園",
    "postalcode":"101-0024",
    "address":"千代田区神田和泉町１",
    "gcs":"JGD2011",
    "tel":"03-3866-9938",
    "capacity":36
    }
  },

```


モデルはこういう形なので、

```python
class Hoikusho(models.Model):
    owned = models.CharField(max_length=50)
    name = models.CharField(max_length=50)
    postalcode = models.CharField(max_length=10)
    address = models.CharField(max_length=250)
    gcs = models.CharField(max_length=10)
    tel = models.CharField(max_length=50)
    capacity = models.IntegerField()
    geom = models.PointField(srid=6668)
```

下記のような辞書を渡すスクリプトを書きます。スクリプトファイルは `map/load_hoikusho.py` にしました。（任意）


```python
#  map/load_hoikusho.py

import os 
from django.contrib.gis.utils import LayerMapping
from map.models import Hoikusho

mapping = {
	"owned": "owned", 
	"name": "name", 
	"postalcode": "postalcode",
	"address": "address", 
	"gcs": "gcs", 
	"tel": "tel", 
	"capacity": "capacity", 
	"geom": "Point", 
	
}

# `201810-2-1-hoikusyo.geojson` へのファイルパス
geojson_file = os.path.abspath(
	os.path.join(
		os.path.dirname(__file__), 'data', '201810-2-1-hoikusyo.geojson'
	)
)

# 実行
def run(verbose=True):
	lm = LayerMapping(
		Hoikusho, 
		geojson_file,
		mapping, 
		transform=False, 
		encoding='UTF-8',)
	lm.save(strict=True, verbose=verbose)
	
```

これをshellで実行します。

:v: Vultr

```
$ python manage.py shell

>>> from map import load_hoikusho
>>> load_hoikusho.run()
Saved: Hoikusho object (1)
Saved: Hoikusho object (2)
Saved: Hoikusho object (3)
......
Saved: Hoikusho object (2778)
Saved: Hoikusho object (2779)
>>> exit()
``` 

### 管理画面の設定

地図を管理画面で確認できるように設定します。

```python
# map/admin.py

# from django.contrib import admin
from django.contrib.gis import admin
from map.models import Hoikusho

# オープンストリートマップにマッピングする
admin.site.register(Hoikusho, admin.OSMGeoAdmin)
```

### url 設定

```python
# config/urls.py

# from django.contrib import admin
from django.contrib.gis import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
]

```

### 表示

:v: Vultr 

```
$ python manage.py runserver xx.xxx.xx.xx:8000
```

`xx.xxx.xx.xx:8000/admin` で地図が表示されていれば成功です！


# 独自ドメインで表示

いつまでも IP アドレスのままではかっこ悪いので、自分のドメインで表示させるための設定を行います。すでにドメインは持っていることを前提にします。最近よく、[porkbun.com](https://porkbun.com/)でドメインを買っています。ぶたさんがかわいいので。


1. Django設定：今回はDjangoの簡易ウェブサーバを使い続けることにしたので変更なし。
2. レジストラで Name Server の設定変更
    + ns1.vultr.com / ns2.vultr.com を追加してください
3. Vultr 設定
    1. https://my.vultr.com/dns/ > `Add Domain` ボタン（右上にある）を押す。
    2. Domainと、自分のインスタンスのIPアドレスを入れる
4. firewall設定
    1. https://my.vultr.com/firewall/
    2. Add Firewall Group 
    3. `Add Firewall Group` に適当な名前をつける。
    4. `Action` の `+` ボタンを押す
    5. Protocol を `HTTP` Port を `80` にして Action の `+` ボタンを押す。注意：HTTP を選んでも、表示上は `TCP` になる。これでOK。
    6. `Firewall group updated. It may take up to 120 seconds for these changes to apply.`と出たらOK
5. `npam` して確認。`80/tcp`が追加されていたら80番ポートが開いたということなのでOK

    :house: Local

    ```bash
    $ nmap -v xx.xx.xx.xxx

    Starting Nmap 7.60 ( https://nmap.org ) at 2019-12-14 22:48 JST
    Initiating Ping Scan at 22:48
    Scanning xx.xx.xx.xxx [2 ports]
    Completed Ping Scan at 22:48, 0.00s elapsed (1 total hosts)
    Initiating Parallel DNS resolution of 1 host. at 22:48
    Completed Parallel DNS resolution of 1 host. at 22:48, 0.00s elapsed
    Initiating Connect Scan at 22:48
    Scanning xx.xx.xx.xxx.vultr.com (xx.xx.xx.xxx) [1000 ports]
    Discovered open port 22/tcp on xx.xx.xx.xxx
    Completed Connect Scan at 22:48, 4.41s elapsed (1000 total ports)
    Nmap scan report for xx.xx.xx.xxx.vultr.com (xx.xx.xx.xxx)
    Host is up (0.0032s latency).
    Not shown: 998 filtered ports
    PORT   STATE  SERVICE
    22/tcp open   ssh
    80/tcp closed http # ←ここ！
    ```

    runserver すると `80/tcp closed http` が `open` に変わります。

6. runserver する。ただし **sudo をつけてかつ、python へのFull path** を指定しなくてはいけない.
    :v: Vultr 

    ```
    $ sudo /home/shinseitaro/miniconda3/envs/py37/bin/python manage.py runserver xx.xx.xx.xxx:80
    ```

7. もう一度 `nmap` すると open に変わっているはずです。
8. 指定したドメインで開いてみてください。

おしまい。


あしたは、[@kaizumakiさん](https://twitter.com/kaizumaki)です。