初めに
前回投稿の続きで、Nuxt.js + Django REST frameworkで動くアプリケーションをnginxに乗せていきます。
###構成イメージ
下記4つのコンテナがあります。
前回投稿からnginxコンテナを加えています。
・初回ページ表示
①nginx経由でnuxtコンテナにアクセス
②nuxtコンテナでSSRする。この際APIを叩きdjangoからDBの情報を取得する。
③レンダリングしたページをユーザに返す
・ページ中でAPIを叩いた場合
nginx経由でdjangoコンテナにアクセス、結果をユーザに返す。
###環境
Window10 Pro
Docker desktop v2.1.0.5
Python 3.6.9
django 2.2.7
djangorestframework 3.10.3
Nuxt.js v2.11.0
nginx 1.13.12
###ディレクトリ構造
.
├─django
│ ├─manage.py
│ ├─composeexample
│ │ ├─settings.py
│ │ ├─urls.py
│ │ ├─wsgi.py
│ │ └─__init__.py
│ └─myapp
│ ├─migrations
│ ├─admin.py
│ ├─apps.py
│ ├─models.py
│ ├─renderers.py
│ ├─serializers.py
│ ├─tests.py
│ ├─urls.py
│ ├─views.py
│ └─__init__.py
│
├─docker-compose.yml
│
├─dockerfiles
│ ├─django_docker
│ │ ├─dockerfile
│ │ └─requirements.txt
│ └─nuxt_docker
│ └─dockerfile
│
├─mysql
│ └─conf.d
│
├─nginx
│ ├─uwsgi_params
│ └─conf
│ └─app_nginx.conf
│
└─nuxt
└─front
└─以下略
#前回からの変更点
前回投稿ではSPAで設定していましたが、今回はSSRでの設定で進めます。
export default {
mode: 'universal', //SSRに変更
#nginxの構築
##docker-compose.ymlの編集
docker-compose.ymlにnginxの設定を追加していきます。
80番ポートを割り当てます。
version: '3'
services:
db:
image: mysql:latest
restart: always
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: test
MYSQL_USER: test
MYSQL_DATABASE: test
MYSQL_PASSWORD: test
ports:
- 3306:3306
expose:
- 3306
volumes:
- mysqldata:/var/lib/mysql
- ./mysql/conf.d:/etc/mysql/conf.d
web:
container_name: django
build: ./dockerfiles/django_docker
command:
python3 manage.py runserver 0.0.0.0:8000
volumes:
- ./django:/code
ports:
- "8000:8000"
depends_on:
- db
front:
container_name: nuxt
build: ./dockerfiles/nuxt_docker
tty: true
volumes:
- ./nuxt:/code
ports:
- "3000:3000"
#ここから
nginx:
image: nginx:1.13
container_name: nginx
ports:
- "80:80"
#ここまで
volumes:
mysqldata:
ここで一度nginxを立ち上げてみます。
docker psにてUpしていることを確認します。
> docker-compose up -d nginx
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
aa3fe475db52 nginx:1.13 "nginx -g 'daemon of…" About a minute ago Up About a minute 0.0.0.0:80->80/tcp nginx
http://localhost/ にアクセスして下記画面が表示されれば成功です。
#Django側の変更
##nginx設定ファイルの追加
下記ディレクトリ及びファイルを追加していきます。
.
└─nginx
├─uwsgi_params
└─conf
└─app_nginx.conf
以下はuWSGIを使用するための設定です。
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
以下はnginx用の設定です。
パスによって宛先を分けていて、/api及び/adminへの通信はDjangoコンテナの8000番ポートに向かうように、
それ以外はnuxtのコンテナの3000番ポートにHTTPで向かうように設定しました。
参考
upstream django {
ip_hash;
server web:8000;
}
server {
listen 80;
server_name 127.0.0.1;
charset utf-8;
location / {
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 1m;
proxy_connect_timeout 1m;
proxy_pass http://nuxt:3000;
}
location ~* /api|admin/ {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
}
server_tokens off;
##uWSGIの導入
Django側にuWSGIを導入するために設定を変更していきます。
requirement.txtにuwsgiを追加します。
Django>=2.0,<3.0
djangorestframework
django-webpack-loader
django-cors-headers
mysqlclient
uwsgi #追加
docker-compose.ymlを編集します。
djangoの起動コマンドを開発用のrunserverからuwsgiに切り替えます。
--socket :8000 --http :8001
としているのは、nginx経由でアクセスする時は8000番にWSGIで、
Nuxtからアクセスする時は8001番にHTTPでアクセスできるようにするためです。
また先ほどの設定ファイルをnginxコンテナから参照できるようにします。
version: '3'
services:
db:
image: mysql:latest
restart: always
container_name: mysql
environment:
MYSQL_ROOT_PASSWORD: test
MYSQL_USER: test
MYSQL_DATABASE: test
MYSQL_PASSWORD: test
ports:
- 3306:3306
expose:
- 3306
volumes:
- mysqldata:/var/lib/mysql
- ./mysql/conf.d:/etc/mysql/conf.d
web:
container_name: django
build: ./dockerfiles/django_docker
####runserverからuwsgiコマンドに変更
command:
uwsgi --socket :8000 --http :8001 --module config.wsgi --py-autoreload 1 -b 32768
####
volumes:
- ./django:/code
ports:
- "8000:8000"
depends_on:
- db
front:
container_name: nuxt
build: ./dockerfiles/nuxt_docker
tty: true
####nuxtの起動コマンドを追加
command: >
bash -c 'cd front &&
yarn dev'
####
volumes:
- ./nuxt:/code
ports:
- "3000:3000"
nginx:
image: nginx:1.13
container_name: nginx
ports:
- "80:80"
####以下追加
volumes:
- ./nginx/conf:/etc/nginx/conf.d
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
depends_on:
- web
####
volumes:
mysqldata:
setting.pyのALLOWED_HOSTSに下記を追加します。
ALLOWED_HOSTS = ["localhost","django"]
##Nuxt修正
前回投稿時はSPAにて作成していましたが、SSRさせるためにmountedをasync dataに変更します。
//~略~
<script>
import Logo from '~/components/Logo.vue'
import axios from 'axios' //追記
export default {
components: {
Logo
},
//ここから
data() {
return {
dat: []
}
},
async mounted(){
const url = "/api/get_person/"
const response = await this.$axios.get(url)
this.dat = response.data
}
//ここまで
//↓↓↓に変更
//ここから
async asyncData({ $axios }){
const url = "api/get_person/"
const response = await $axios.get(url)
return { dat: response.data}
//ここまで
}
</script>
//~略~
またnuxt.conf.jsも修正します。
axiosのbaseURLをhttp://django:8001 に、browserBaseURLをhttp://localhost に変更します。
baseURLはNuxtコンテナからSSR時(サーバ側)にAPIを叩くときに使用し、
browserBaseURLはブラウザ(クライアント側)でAPIを叩くときに使用します。
NuxtでAPIを叩くときはDjangoに直接向かうようにし、
ブラウザの場合はNginxに向かうようにします。
export default {
//~略~
axios: {
baseURL: "http://django:8001",
browserBaseURL: "http://localhost",
},
//~略~
この状態でビルドします。
4つコンテナが立ち上がれば成功です。
> docker-compose up -d --build
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
400297780ac2 qiita1_web "uwsgi --socket :800…" 9 minutes ago Up 9 minutes 0.0.0.0:8000->8000/tcp django
a7ca824f3526 nginx:1.13 "nginx -g 'daemon of…" 37 minutes ago Up 37 minutes 0.0.0.0:80->80/tcp nginx
c96258eee9b0 qiita1_front "docker-entrypoint.s…" 37 minutes ago Up 37 minutes 0.0.0.0:3000->3000/tcp nuxt
58377df67b22 mysql:latest "docker-entrypoint.s…" 37 minutes ago Up 37 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp mysql
http://localhost にアクセスして、TOPページが表示されればnginx経由でアクセスできています。
http://localhost/api/get_person でnginx経由でAPIの結果も取得できます。
> curl http://localhost/api/get_person
StatusCode : 200
StatusDescription : OK
Content : {"person_data": [{"id": 1, "person_name": "takashi", "person_age": 26}, {"id": 2, "pe
rson_name": "naoto", "person_age": 32}, {"id": 3, "person_name": "tomoko", "person_ag
e": 15}]}
RawContent : HTTP/1.1 200 OK
Connection: keep-alive
Allow: GET, HEAD, OPTIONS
X-Frame-Options: SAMEORIGIN
Vary: Cookie, Origin
Content-Length: 179
Content-Type: application/json; charset=utf-8
Date: Fri, 08...
Forms : {}
Headers : {[Connection, keep-alive], [Allow, GET, HEAD, OPTIONS], [X-Frame-Options, SAMEORIGIN]
, [Vary, Cookie, Origin]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : System.__ComObject
RawContentLength : 179 mysql
これにて初期表示の部分は完成です。
##API編集
続いてAPI部分を作成していきます。
前回投稿時はGETのエンドポイントしか作成しなかったので、POSTのエンドポイントも作成します。
urls.py及びview.pyを編集していきます。
from django.shortcuts import render
from django.views.generic import ListView
from myapp.models import Person_data
from rest_framework import status
from rest_framework.generics import ListAPIView, CreateAPIView #追加
from rest_framework.permissions import AllowAny
from .renderers import PersonJSONRenderer
from .serializers import PersonSerializer
# Create your views here.
class PersonListApiView(ListAPIView):
model = Person_data # モデルを指定
queryset = Person_data.objects.all()
permission_classes = (AllowAny, )
renderer_classes = (PersonJSONRenderer, )
serializer_class = PersonSerializer
####以下追加
class PersonCreateApiView(CreateAPIView):
model = Person_data
queryset = Person_data.objects.all()
permission_classes = (AllowAny, )
renderer_classes = (PersonJSONRenderer, )
serializer_class = PersonSerializer
from django.urls import path
from .views import PersonListApiView, PersonCreateApiView #追加
urlpatterns = [
path('get_person/', PersonListApiView.as_view()),
path('post_person/', PersonCreateApiView.as_view()), #POST追加
]
これだけでAPIを追加できます。
POSTして201が返ることを確認します。
(windows power shellの例です)
> curl -Method POST -Body @{person_name="takaishi"; person_age=11;} http://localhost/api/post_person/
StatusCode : 201
StatusDescription : Created
Content : {"person_data": {"id": 18, "person_name": "takaishi", "person_age": 11}}
RawContent : HTTP/1.1 201 Created
Connection: keep-alive
Allow: POST, OPTIONS
X-Frame-Options: SAMEORIGIN
Vary: Cookie, Origin
Content-Length: 72
Content-Type: application/json; charset=utf-8
Date: Fri, 08 ...
Forms : {}
Headers : {[Connection, keep-alive], [Allow, POST, OPTIONS], [X-Frame-Options, SAMEO
RIGIN], [Vary, Cookie, Origin]...}
Images : {}
InputFields : {}
Links : {}
ParsedHtml : System.__ComObject
RawContentLength : 72
TOPページを更新すると追加されていることを確認できます。
##TOPページにフォームを作る
Webページ上からPOSTできるように簡単なフォームを作成します。
<template>
<div class="container">
<div>
<logo />
<h1 class="title">
front
</h1>
<h2 class="subtitle">
My doozie Nuxt.js project
</h2>
<div class="links">
<a
href="https://nuxtjs.org/"
target="_blank"
class="button--green"
>
Documentation
</a>
<a
href="https://github.com/nuxt/nuxt.js"
target="_blank"
class="button--grey"
>
GitHub
</a>
<div v-for="d in dat.person_data" :key=d.person_name align="center">
<h2>
{{d.person_name}} {{d.person_age}}
</h2>
</div>
<!-- ここから追記 -->
<input type="text" v-model="name">
<input type="text" v-model="age">
<button v-on:click="post">ADD</button>
<!-- ここまで追記 -->
</div>
</div>
</div>
</template>
<script>
import Logo from '~/components/Logo.vue'
export default {
components: {
Logo
},
//ここから追記
data() {
return {
name: '',
age: '',
}
},
methods: {
post () {
const url = "api/post_person/"
axios.defaults.xsrfCookieName = 'csrftoken'
axios.defaults.xsrfHeaderName = "X-CSRFTOKEN"
axios.post(url, {person_name: this.name , person_age: this.age})
}
},
//ここまで追記
async asyncData({ $axios }){
const url = "api/get_person/"
const response = await $axios.get(url)
return { dat: response.data}
}
}
</script>
//~略~
これでTOPページからPOSTできます。
入力して、ADDボタンを押してPOST送信できます。
ページ更新することで再度レンダリングが走り、表示が更新されます。
##参考
Django+Nginx+MySQLの開発環境をDockerで構築する
ちゃんと運用するときのuWSGI設定メモ
EC2上で Django + Nginx + uWSGI を試す