はじめに
今回は、Django REST Frameworkを使った、
Vue.jsをSPAとして運用できる環境の作り方をシェアできればと思います。
Docker Composeを使うので、本番で使うときはGKEなどの
モダンなコンテナ型オーケストレーションツールを選択可能かつ、開発でも
メンバーの増員などに柔軟に対応できる環境を作れたらと思います。
今回は抜粋で書いているので、
不足等ございましたら、ご連絡よろしくお願いいたします。
構成
構成は下記のような形にします。
本来だと下記に+DBもあるような構成が基本ですが、
コンテナ上で動かす場合と、クラウドのDBサービスを使用する場合など
多岐の選択があるため、今回はなしとします。
サーバー構成
サーバー名 | 名称 | ポート番号 |
---|---|---|
開発サーバー(Vue.js) | front | 8080 |
APIサーバー(Django) | back | 8001 |
Webサーバー(Nginx) | web | 8000 |
Docker Compose ファイルで骨組みを作る
最初にdocker-compose.ymlファイルと各種サーバーのDockerfileを作成して、
骨組みを形作っていきます。
1. docker-compose.ymlを作成
注意点としては、バックエンド側のstaticファイルをnginxに置いて
配信可能とする点です。
今回はAPIサーバーとしての使用のため、不要と思われますが、
djangoのデバッグ画面(管理画面など)を表示する際に使用できるので、
volumesにて、同期します。
基本的に起動は、拡張していくことも考えて、
Shellファイルに落とし込んでいきます。
(直で記載でも良いとは思います。)
version: '3'
services:
web:
build:
context: ./
dockerfile: ./web/Dockerfile
environment:
TZ: 'Asia/Tokyo'
ports:
- 8000:8000
volumes:
- ./web/logs/nginx/:/var/log/nginx/
- ./web/uwsgi_params:/etc/nginx/uwsgi_params
- ./back/static:/var/www/static/
depends_on:
- back
back:
build:
context: ./back
dockerfile: Dockerfile
command: 'sh /server/start.sh'
expose:
- "8001"
volumes:
- ./back:/server/
front:
build:
context: ./front
command: 'sh /app/start.sh'
volumes:
- ./front:/app/:cached
- ./front/node_modules:/app/node_modules
ports:
- "8080:8080"
2. DockerFile フロントエンド側を作成
npmを使用するために、nodeイメージを入れます。
後ほど立ち上げるためにコメントアウトなども挟むので、
一旦スキップしても構いません。
FROM node:10.7.0
WORKDIR /app
RUN npm install -g @vue/cli
ADD ./package.json /app/package.json
RUN npm install
ADD ./start.sh /app/start.sh
3. DockerFile バックエンド側を作成
バックエンド側をゴリゴリ書いていきます。
ライブラリのインストールも都度できるように
requirements.txt経由でインストールします。
FROM python:3.7
ENV PYTHONUNBUFFERED 1
WORKDIR /server
ADD . /server/
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
4. DockerFile Webサーバー側を作成
設定ファイル、Vue.jsで作成しビルドしたものを格納します。
バージョンはお好みのものをご使用ください。
FROM nginx:1.11.7
# 設定ファイル
ADD ./web/nginx.conf /etc/nginx/nginx.conf
ADD ./web/default.conf /etc/nginx/sites-available/default
ADD ./web/default.conf /etc/nginx/sites-enabled/default
ADD ./web/uwsgi_params /etc/nginx/uwsgi_params
RUN mkdir /var/www
RUN mkdir /var/www/front
RUN mkdir /var/www/static
ここまで作成するとある程度骨組み部分は出来上がった状態になります。
ここから、実際にサーバーとして使用できるように
各サーバー内に手を加えて初期構築をしていきます。
フロントエンド側を作成
Vue.jsを使えるようにVue CLIを導入して、
使用できる状態にしていきます。
エラーを防ぐために一旦Dockerfileで書いた下記をコメントアウトします
FROM node:10.7.0
WORKDIR /app
RUN npm install -g @vue/cli
# ADD ./package.json /app/package.json
# RUN npm install
# ADD ./start.sh /app/start.sh
Vue CLIを入れることで、Vueの開発環境を簡単に作成することができます。
早速Terminalからプロジェクトを作成し、開発サーバーとして立ち上げてみましょう。
各種設定方法を聞かれますが、enterで進んでいくとデフォルトのものがインストールされます。
// hoge-projectにはプロジェクト名を入力してください。
$ docker-compose run front vue create hoge-project
先ほどDockerfileに入力したコメントアウトを外して、
start.shを軽く記載した後、立ち上げてみましょう。
(プロジェクト先に向き先を当てるため、ファイルの向き先も変更するか、
プロジェクトフォルダを移動して、動くか確認してみてください。)
npm run serve
$ docker-compose build front
$ docker-compose up -d front
ローカルホストで確認すると無事立ち上がっているかと思います。
バックエンド側を作成
次にDjango側を作成していきます。
ターミナルからDjangoアプリの初期作成コマンドを打ちます。
$ docker-compose exec back django-admin startproject mysite
次にアプリケーションを作成します。
最後の確認の際に使います。
$ docker-compose exec back python mysite/manage.py startapp testapi
起動用のシェルも書いておきます。
#!/bin/bash
sleep 5
python manage.py makemigrations
python manage.py migrate
python manage.py collectstatic --noinput
uwsgi --socket :8001 --module mysite.wsgi
Django REST Frameworkを使用するので
インストールする設定ファイルに記載します。
django>=2.1.10
uwsgi==2.0.17.1
djangorestframework==3.8.2
一旦ここまでで、最後に疎通確認するので、
次に進みます。
Webサーバーを立てる
nginx.conf, default.conf, uwsgi_paramsなどの
設定ファイルを作成していきます。
1. nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
daemon off;
events {
worker_connections 65535;
multi_accept on;
use epoll;
}
http {
sendfile on;
tcp_nopush on;
tcp_nodelay on;
types_hash_max_size 2048;
client_max_body_size 20M;
keepalive_timeout 3600;
proxy_connect_timeout 3600;
proxy_send_timeout 3600;
proxy_read_timeout 3600;
send_timeout 3600;
client_body_timeout 300;
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format with_time '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" $request_time';
access_log /dev/stdout with_time;
error_log stderr;
gzip on;
gzip_disable "msie6";
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
limit_req_zone $binary_remote_addr zone=perip:10m rate=5r/s;
limit_req_status 429;
}
2. default.conf
# development
upstream webserver {
ip_hash;
server back:8001;
}
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 8000;
server_name 127.0.0.1;
client_header_buffer_size 1k;
large_client_header_buffers 8 32k;
add_header Strict-Transport-Security 'max-age=31536000';
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
error_page 500 502 503 504 /50x.html;
# フロントエンド
location / {
root /var/www/front;
try_files $uri $uri/ /index.html;
}
# バックエンドサーバー 静的ファイル群
location /static {
alias /var/www/static;
}
# バックエンドサーバー
location /back/ {
include /etc/nginx/uwsgi_params;
uwsgi_pass webserver;
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-Host $server_name;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
# バックエンド adminサーバー
location /admin/ {
include /etc/nginx/uwsgi_params;
uwsgi_pass webserver;
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-Host $server_name;
}
location = /50x.html {
root /usr/share/nginx/html;
}
}
3. 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;
問題ないかテスト
試しにVue.js側でAxiosを導入して、
リクエストを送ってみて、きちんとAPIサーバーから値が帰ってくるか、
確認してみます。
1. Vue.js側のAxios通信の作成
試しにボタンからAPIサーバーへリクエストを送る処理を書いていきます。
戻り値を表示する部分も用意します。
$ docker-compose run front npm install -S axios
インストールしたAxiosをmain.jsにセッティングします。
import Vue from 'vue'
import App from './App.vue'
import axios from 'axios'
Vue.config.productionTip = false
Vue.prototype.$axios = axios
new Vue({
render: h => h(App),
}).$mount('#app')
初期作成されたHelloWorld.vueに追記していきます。
<template>
<div class="hello">
<h1>{{ msg }}</h1>
<!-- 下記記載 -->
<h2>ここに結果が表示されます → {{ result }}</h2>
<button @click="getAPI()">クリック!</button>
<p>
For a guide and recipes on how to configure / customize this project,<br>
check out the
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
</p>
<!-- ~~~ 割愛 ~~~ -->
</template>
<script>
export default {
name: 'HelloWorld',
props: {
msg: String
},
// 下記記載
data () {
return {
result: 'No Result',
url: 'http://localhost:8000/back/testapi/get/'
}
},
methods: {
getAPI () {
this.$axios.get(this.url)
.then(response => {
this.result = response.data.message
})
}
}
}
</script>
2. Django側のレスポンス処理の作成
URLConfへの記載、Views.pyにてレスポンス処理を書いていきます。
今回はhello worldを返すようにします。
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
path('back/testapi', include('testapi.urls', namespace='testapi')),
]
from django.urls import include, path
from .views import *
app_name = 'testapi'
urlpatterns = [
path('get/', GetTestAPI.as_view()),
]
from django.shortcuts import render
from rest_framework import status, viewsets, filters
from rest_framework import permissions
from rest_framework.response import Response
class GetTestAPI(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, format=None):
return Response(data={'status': 'Hello World!'}, status=status.HTTP_200_OK)
# ~~ 省略 ~~
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'rest_framework',
'testapi'
]
# ~~ 省略 ~~
3. 疎通
サーバーを立ち上げてブラウザ上から確認してみます。
$ docker-compose up -d
まとめ
現在SPA+APIサーバー環境をDocker上で構築することで、
短い時間で、簡単に構築することができます。
現在弊社では、HRモンスターと呼ばれる
採用の新しいスタイルを提供するサービスをローンチいたしました。
ローンチ後のさらなる機能追加、改善などのPDCAサイクルを回すべく、
エンジニアを募集しております。
https://www.wantedly.com/projects/341182
Kubernetes、Vue.js(Javascript)、Django(Python)といったモダンな技術を使って、
開発しておりますので、もしご興味がある方はぜひ、ご応募お待ちしております。