[07] キャッシュデータベース Redis の導入をする

# はじめに

Docker (docker-compose)と Linux に標準で搭載されているシェルを組み合せて、
下記 [概要](#概要)に挙げた Django 環境を 1コマンドで即座に立ち上げるためのコードとその解説です.

本記事の内容は、Zenn で[書籍](https://zenn.dev/robozushi10/books/3d32504b4b5707)として販売している内容と同一です.
Zenn でも書籍の内容は全て無料公開しています.

次の順でコードを作成してください.
動作するコードが作成できるように可能な限り注意を払って執筆しました.

・[[01] Dockerとシェルを活用して、1コマンドで Django を立ち上げる](https://qiita.com/robozushi10/items/fc8e40ad7ecc29131ea3/)
・[[02] SQLite3 を PostgreSQL へ置き換える](https://qiita.com/robozushi10/items/7dc07ab6412b285c75da)
・[[03] Django アプリの立ち上げもさせる](https://qiita.com/robozushi10/items/bda4e5f1c743c4c26af5)
・[[04] jwilder/dockerize を使ってセットアップ処理を改良する](https://qiita.com/robozushi10/items/3d955c5c50d845c73e82)
・[[05] runserver を Gunicorn へ置き換える](https://qiita.com/robozushi10/items/406ef206efaf8c1f5c39)
・[[06] "まっしろ"対策として Nginx を導入する](https://qiita.com/robozushi10/items/fc4e8d275f3673796038)
・本記事の内容


# 概要

今回は下図・下表の環境を構築する. (前章からの変更点は🔥である)
より具体的な変更点としては次である.
**・キャッシュデータベースとして Redis を導入する.**

## 概念図

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/3c09bedb-e7b4-9383-7bf8-c4f1496dae47.png)

:::message
図を簡単にするために、jwilder/dockerize の線は省略している.
:::

## コンテナ内の構成

|項目|値|補足|
|:--|:--|:--|
|Python|バージョン3.8.3|`python:3.8.3-slim-buster` を使う|
|Django|バージョン3.0.7||
|Webサーバ|Nginx|8000番ポートを使用する|
|アプリサーバ|gunicorn|53000番ポートを使用する. <br/>ただし、53000番ポートはホストには公開しない|
|データベース|PostgreSQL 12.0|ポートは次の通り<br/>・ホスト側「53432」<br/>・コンテナ側「5432」<br/>管理者アカウントは次の通り<br/>・アカウント「`admin`」<br/>・パスワード「`admin`」|
|キャッシュ用データベース🔥|Redis 6.2.2🔥|ポートは次の通り<br/>・ホスト側「53379」<br/>・コンテナ側「6379」🔥|
|settings.py|次の定義を追加する<br/>・`SESSION_ENGINE`<br/>・`CACHES`|ホスト上の `./web/assets/settings.py` が、<br/>コンテナ内に `/usr/src/app/config/settings.py` <br/>として配置される.|
|Django admin ページ||管理者アカウントは次の通り<br/>・アカウント「`admin`」<br/>・パスワード「`admin`」|
|アプリケーション|アプリ名「shop」|**各ユーザが使用したいアプリに置き換えてほしい**|
|コンテナ起動確認ツール|[jwilder/dockerize](https://github.com/jwilder/dockerize)||

# セットアップコード

前章で作成したコードに対して、本記事の内容を適用してください.

:::message
web/assets/settings.py より、「DEBUG = True」と「DEBUG = False」の両パターンでセットアップできることを確認している.
:::

## ファイル構成

上記 GitHub からコードを取得してきた直後の構成である.
前章から変更が生じたファイルには🏷️を、新規作成については🆕を付与している.

```bash
.
|-- cachedb 🆕
|   `-- Dockerfile 🆕
|-- db
|   `-- Dockerfile
|-- docker-compose.yml 🏷️
|-- nginx
|   |-- Dockerfile
|   `-- conf.d
|       `-- nginx.conf
|-- setupapp.sh ........... セットアップスクリプト
`-- web
    |-- Dockerfile
    `-- assets ............ コンテナへコピーされるデータ
        |-- entrypoint.sh
        |-- requirements.txt 🏷️
        |-- sample
        |   `-- shop
        |       |-- admin.py
        |       `-- models.py
        `-- settings.py 🏷️

```

## 解説

### cachedb/Dockerfile

新規作成.
特筆すべき事項なし.

```dockerfile
FROM redis:6.2.2-alpine

## プロキシサーバを使う場合
# ENV http_proxy="http://proxy.co.jp:8080"	
# ENV https_proxy="http://proxy.co.jp:8080"

# 時刻を日本に合わせる
RUN ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
```

### docker-compose.yml

・redis コンテナを新たに立ち上げる.
・永続ボリュームとしてホスト側に `./cachedb/data` を作成している

```diff
--- docker-compose.yml.ORIG
+++ docker-compose.yml
@@ -33,6 +33,18 @@
         POSTGRES_USER: 'admin'
         POSTGRES_PASSWORD: 'admin'
         PGDATA: '/data'
+    depends_on:
+      - cachedb
+  cachedb:
+    build: ./cachedb
+    image: myredis622
+    container_name: myredis622
+    restart: always
+    ports:
+      - "53379:6379"
+    volumes:
+      - ./cachedb/data:/data
+    command: redis-server --appendonly yes
   nginx:
     build: ./nginx
     image: mynginx0120
```

### setupapp.sh

・redis によるキャッシュデータを削除する.

```diff
@@ -4,6 +4,7 @@
 sudo rm -rf web/app
 sudo rm -rf db/data
 sudo rm -rf db/init
+sudo rm -rf cachedb/data
 
 # docker-compose.yml で書かれたサービスのコンテナ、イメージ、ネットワークを削除する
 docker-compose down --rmi all --volumes
```


### web/assets/requirements.txt

・redis 関連のパッケージを追加登録する

```diff
--- web/assets/requirements.txt.ORIG
+++ web/assets/requirements.txt
@@ -5,3 +5,5 @@
 psycopg2==2.8.5
 psycopg2-binary==2.8.5
 gunicorn==20.0.4
+django-redis==4.11.0
+redis==3.4.1

```

### web/assets/settings.py

・SESSION_ENGINE と CACHES を追加する
・パラメータは [こちらのサイト](https://intellectual-curiosity.tokyo/2018/11/10/django%E3%81%AE%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E8%A8%AD%E5%AE%9A/) を参考にした

```diff
--- web/assets/settings.py.ORIG
+++ web/assets/settings.py
@@ -85,6 +85,17 @@
     }
 }
 
+SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
+
+CACHES = {
+  'default': {
+    'BACKEND': 'django_redis.cache.RedisCache',
+    'LOCATION': 'redis://cachedb:6379/1',
+    'OPTIONS': {
+      'CLIENT_CLASS': 'django_redis.client.DefaultClient',
+    }
+  }
+}
 
 # Password validation
 # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators

```

# セットアップ方法

次の通りコマンドを実行する.

```
$ ./setupapp.sh
```

# セットアップ後

## ファイル構成

```bash
$ tree . --charset=c -a -L 5
.
|-- cachedb
|   |-- Dockerfile
|   `-- data
|       `-- appendonly.aof
|-- db
|   |-- Dockerfile
|   |-- data [error opening dir]
|   `-- init
|-- docker-compose.yml
|-- nginx
|   |-- Dockerfile
|   `-- conf.d
|       `-- nginx.conf
|-- setupapp.sh
`-- web
    |-- Dockerfile
    |-- app
    |   |-- .settings.py
    |   |-- config
    |   |   |-- __init__.py
    |   |   |-- asgi.py
    |   |   |-- settings.py
    |   |   |-- urls.py
    |   |   `-- wsgi.py
    |   |-- entrypoint.sh
    |   |-- log
    |   |   `-- gunicorn.log
    |   |-- manage.py
    |   |-- media
    |   |-- shop
    |   |   |-- __init__.py
    |   |   |-- admin.py
    |   |   |-- apps.py
    |   |   |-- migrations
    |   |   |   |-- 0001_initial.py
    |   |   |   `-- __init__.py
    |   |   |-- models.py
    |   |   |-- tests.py
    |   |   `-- views.py
    |   `-- static
    |       `-- admin
    |           |-- css .... これより下層の表示は省略している
    |           |-- fonts .. これより下層の表示は省略している
    |           |-- img .... これより下層の表示は省略している
    |           `-- js ..... これより下層の表示は省略している
    `-- assets
        |-- entrypoint.sh
        |-- requirements.txt
        |-- sample
        |   `-- shop
        |       |-- admin.py
        |       `-- models.py
        `-- settings.py

```

### 動作確認方法

次の点を確認すれば良い.

✔ Dockerコンテナが次のように起動していること

コンテナ「`jwilder_dockerize`」は使い捨て型なので、Exit 2 は意図した結果であり問題無し.

```bash
$ docker-compose ps
      Name                     Command               State                Ports             
--------------------------------------------------------------------------------------------
jwilder_dockerize   dockerize --help                 Exit 2                                 
mydjango307         /usr/src/app/entrypoint.sh ...   Up       53000/tcp                     
mynginx0120         /docker-entrypoint.sh ngin ...   Up       0.0.0.0:8000->1337/tcp, 80/tcp
mypostgres12        docker-entrypoint.sh postgres    Up       0.0.0.0:53432->5432/tcp       
myredis622          docker-entrypoint.sh redis ...   Up       0.0.0.0:53379->6379/tcp  
```

<br/>

✔ http://localhost:8000 にアクセスして次の画面が表示されること.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/d3ec0713-6dfa-369a-d110-cd8dde62e68f.png)

<br/>

✔ http://localhost:8000/admin にアクセスして、アカウント「`admin`」、パスワード「`admin`」でログインできること.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1246056/ccb608ea-fcd5-aa6a-f555-c8200d7337be.png)

<br/>

✔ `cachedb/data/appendonly.aof` が存在していること

<br/>

以上.