26
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Nginx + Nuxt.js + Django REST frameworkの実装

Last updated at Posted at 2020-05-09

初めに

前回投稿の続きで、Nuxt.js + Django REST frameworkで動くアプリケーションをnginxに乗せていきます。

###構成イメージ
下記4つのコンテナがあります。
前回投稿からnginxコンテナを加えています。
image.png

・初回ページ表示
①nginx経由でnuxtコンテナにアクセス
②nuxtコンテナでSSRする。この際APIを叩きdjangoからDBの情報を取得する。
③レンダリングしたページをユーザに返す
image.png

・ページ中でAPIを叩いた場合
nginx経由でdjangoコンテナにアクセス、結果をユーザに返す。
image.png

###環境
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での設定で進めます。

./nuxt/front/nuxt.config.js
export default {
  mode: 'universal', //SSRに変更

#nginxの構築
##docker-compose.ymlの編集
docker-compose.ymlにnginxの設定を追加していきます。
80番ポートを割り当てます。

./docker-compose.yml
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/ にアクセスして下記画面が表示されれば成功です。
image.png

#Django側の変更
##nginx設定ファイルの追加
下記ディレクトリ及びファイルを追加していきます。

.
└─nginx
   ├─uwsgi_params
   └─conf
       └─app_nginx.conf

以下はuWSGIを使用するための設定です。

uwsgi_params
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で向かうように設定しました。
参考

./nginx/conf/app_nginx.conf
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を追加します。

./dockerfiles/django_docker/requirement.txt
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コンテナから参照できるようにします。

./docker-compose.yml
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に下記を追加します。

./django/composeexample/setting.py
ALLOWED_HOSTS = ["localhost","django"]

##Nuxt修正
前回投稿時はSPAにて作成していましたが、SSRさせるためにmountedをasync dataに変更します。

./nuxt/front/pages/index.vue
//~略~

<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に向かうようにします。

./nuxt/front/nuxt.config.js
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経由でアクセスできています。

image.png

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を編集していきます。

./django/myapp/views.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
    

./django/myapp/urls.py
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ページを更新すると追加されていることを確認できます。

image.png

##TOPページにフォームを作る
Webページ上からPOSTできるように簡単なフォームを作成します。

./nuxt/front/pages/index.vue
<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送信できます。
ページ更新することで再度レンダリングが走り、表示が更新されます。

Microsoft-Edge-2020-05-09-06-14-40.gif

##参考
Django+Nginx+MySQLの開発環境をDockerで構築する
ちゃんと運用するときのuWSGI設定メモ
EC2上で Django + Nginx + uWSGI を試す

26
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
26
23

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?