[Django3.0] staticの静的ファイルをS3に画像をアップロード

dev環境だとstaticに画像を置けば良かったが、prd環境だと画像保存がcollectstaticで上手くいかないのでS3を使いたい。まずはテストする。

$ django-admin startproject statictest
$ cd statictest
$ python3 manage.py startapp testApp

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'testApp',
]

statictest/urls.py

from django.contrib import admin
from django.urls import path, include

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

testApp/urls.py

from django.urls import path
from django.conf import settings
from . import views

urlpatterns = [
	path('', views.index, name='index'),
]

testApp/templates/testApp/index.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<link rel="stylesheet" href="{% static 'testApp/css/base.css' %}">
</head>
<body>
	<header>
	<h1>test1</h1>
	</header>
	<div>
		test page!
	</div>
</body>
</html>

testApp/static/testApp/css/base.css

h1 {
	color: red;
}

views.py

from django.shortcuts import render
from django.http import HttpResponse

def index(request):
	return render(request, 'testApp/index.html')

$ python3 manage.py runserver 192.168.33.10:8000

これをsettings.pyのDEBUG=Falseにすると、static fileを読み込まなくなる。

DEBUG = False

### AWS S3
– S3fullAccessのユーザ作成
accesskey, secretkey, regionを取得
– testapp-djangoのbucket作成

$ pip3 install boto3
$ pip3 install django-storages

settings.py

INSTALLED_APPS = [
    //
    'testApp',
    'storages' # 追加
]

# STATIC_URL = '/static/'

STATICFILES_DIRS = [os.path.join(BASE_DIR, 'testApp/static')]
AWS_ACCESS_KEY_ID = 'AKIAXXXXXXXXXXXXXX'
AWS_SECRET_ACCESS_KEY = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'
AWS_STORAGE_BUCKET_NAME = 'static-test-reiwa'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = None
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

S3で対象のbucketをpublicにして、フォルダをmake publicに変更する

– STATICFILES_DIRS = [os.path.join(BASE_DIR, ‘static’)] だとファイルがないとエラーになるので注意。

なるほどー

[aws ec2] Ubuntu20.04 x Apache2 x Django3.0 x MySQL8系 環境構築

結論から言うと、ec2 x Djangoでapache2 or Nginxを使う場合、仮想環境を作らないと、Djangoのパスが通らずに、「ModuleNotFoundError: No module named ‘django’」とエラーになる。開発環境では、manage.py runserverで確認すれば良かったが、ec2にデプロイしてapache2もしくはNginxを通す場合は、仮想環境の構築が必須。
ec2でapache2もしくはNginxを構築する前に、uWSGIの基礎を学んでから構築した方が、理解が深まるし、トラブルシューティングしやすくなる。
それを省略したので、結局2日かかった orz…

以下が構築手順。Ubuntu20.04のインスタンスが出来ている状態から始める。

### 1.インスタンスログイン
$ ssh ubuntu@${public ip} -i ~/.ssh/*.pem
$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python3-pip

### 2.MySQL8系インストールとdjango用のdb作成
$ sudo apt install mysql-client-core-8.0
$ sudo apt-get update
$ sudo apt install mysql-server
$ sudo service mysql start
$ sudo mysql_secure_installation
$ sudo mysql -u root -p
mysql>set global validate_password.length=6;
mysql>set global validate_password.policy=LOW;
mysql>CREATE USER ‘admin’@’%’ IDENTIFIED BY ‘hogehoge’;
mysql>GRANT ALL PRIVILEGES ON *.* TO ‘admin’@’%’ WITH GRANT OPTION;
mysql>FLUSH PRIVILEGES;
mysql>create database hoge;

### 3.apacheインストール
$ sudo apt update
$ sudo apt install apache2
$ sudo ufw app list
$ sudo ufw allow ‘Apache Full’
$ sudo ufw status
$ sudo systemctl status apache2

### 4.git clone
// 所有権
$ sudo chown ubuntu /home
$ git clone https://github.com/hoge/hoge.git
$ cd hoge

#### 5.django商用設定
settings.py

DEBUG = False
ALLOWED_HOSTS = ['*']  #もしくはEC2のIP
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
migration

// migration
$ python3 manage.py makemigrations hoges
$ python3 manage.py migrate

### 6. 仮想環境構築(!!重要!!)
$ sudo apt install -y python3-wheel python3-venv python3-dev
$ python3 -m venv env
$ source env/bin/activate
$ pip install wheel
// 各種ライブラリインストール 省略
$ apt-get install apache2-dev
$ sudo pip3 install mod_wsgi
$ mod_wsgi-express module-config
LoadModule wsgi_module “/usr/local/lib/python3.8/dist-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so”
WSGIPythonHome “/usr”
$ python3 manage.py collectstatic
$ deactivate

### 7. Apache設定
$ sudo vi /etc/apache2/sites-available/django.conf

LoadModule wsgi_module /usr/local/lib/python3.8/dist-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so

WSGIPythonHome /usr
WSGIScriptAlias / /home/ubuntu/hoge/hoge/wsgi.py
WSGIPythonPath /home/ubuntu/hoge:/home/ubuntu/hoge/env/lib/python3.8/site-packages

<Directory /home/ubuntu/hoge/hoge>
  <Files wsgi.py>
    Require all granted
  </Files>
</Directory>

Alias /static/ /home/ubuntu/hoge/hoge/
<Directory /home/ubuntu/hoge/static>
  Require all granted
</Directory>

$ sudo a2dissite 000-default
$ sudo a2ensite django
$ sudo systemctl restart apache2
$ sudo systemctl enable apache2

### 8.挙動確認
EC2のpublic IPを叩く

### 9.AMI作成
– インスタンスのバックアップ

お疲れ様でした。

ちなみにこれ、仮想環境構築をすっ飛ばしてインスタンス作成を5〜6回ぐらいやり直してapacheのエラーログ見て悶絶してた。まあ、ここを乗り切ればハードルは一気に下がりますね。

[uWSGI] DjangoをUbuntu+Nginx+uWSGIの構成で動かしたい

### uWSGIとは?
-WSGIは仕様で、uWSGIは既存のWebサーバに機能を追加
-WebサーバにNginx, WSGIコンテナにuWSGI(Nginxとは別プロセス)
-Apache2, Nginx, cherokee, lighttpdに対応
-gunicornというWSGIも有名
-Apacheは同時接続数が極端に多くなると対応できないが、Nginxはレスポンスが早い

### Django構築
$ django-admin startproject testSite
$ python3 manage.py startapp testapp

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'testapp',
]
ALLOWED_HOSTS = ['*']

views.py

from django.http import HttpResponse
# Create your views here.

def hello(request):
	return HttpResponse("hello, Nginx!")

urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.hello, name='hello'),
]
urlpatterns = [
    path('admin/', admin.site.urls),
    path('testapp/', include('testapp.urls')),
]

### UbuntuにNginx
$ sudo apt update
$ apt show nginx
Package: nginx
Version: 1.18.0-0ubuntu1
$ sudo apt install nginx
$ nginx -v

### djangoでuWSGI
$ python3 manage.py runserver 192.168.33.10:8000

$ uwsgi –http :8000 –module testSite.wsgi

testApp/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;

testApp/testSite_nginx.conf

upstream django {
	server 192.168.33.10:8001;
}

server {
	listen 8000;
	server_name 192.168.33.10;
	charset utf-8;

	location /static {
		alias /home/vagrant/other/testSite/static;
	}

	location / {
		uwsgi_pass django;
		include /home/vagrant/other/testSite/testSite/uwsgi_params;
	}
}

$ sudo ln -s /home/vagrant/other/testSite/testSite/testSite_nginx.conf /etc/nginx/sites-enabled/

setting.py

STATIC_ROOT = os.path.join(BASE_DIR, "static/")

$ python3 manage.py collectstatic

$ sudo nginx
failed (Result: exit-code)

$ sudo lsof -i:80
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 75758 root 6u IPv4 87712 0t0 TCP *:http (LISTEN)
nginx 75758 root 7u IPv6 87713 0t0 TCP *:http (LISTEN)
nginx 75759 www-data 6u IPv4 87712 0t0 TCP *:http (LISTEN)
nginx 75759 www-data 7u IPv6 87713 0t0 TCP *:http (LISTEN)
nginx 75760 www-data 6u IPv4 87712 0t0 TCP *:http (LISTEN)
nginx 75760 www-data 7u IPv6 87713 0t0 TCP *:http (LISTEN)

$ uwsgi –socket :8001 –module testSite.wsgi buffer-size=32768

うーん、ちょっとよくわからんな。。

uWSGIを使っていこう

uWSGI(Web Server Gateway Interface)とはPythonのアプリとWebサーバのプロトコルのこと。
Django, Flask, BottleはこのWSGIの規約にそっている

uWSGIはアプリケーションサーバの一種
通信は
– UNIXドメインソケット(高速)
– HTTP

$ pip3 install uWSGI

一緒にflaskもインストールします。
$ pip3 install flask

run.py

from flask import Flask, jsonify

app = Flask(__name__)

@app.route('/')
def api_sample():
	result = {"code": "001", "name": "apple"}
	return jsonify(ResultSet=result)

if __name__ == '__main__':
	app.run(host="0.0.0.0")

ん、どこにuWSGIが使われているんだ??

続いてuWSGIコマンドを実行する
$ uwsgi –http=0.0.0.0:8080 –wsgi-file=run.py –callable=app

なんだこれ? サーバ機能やな

### 設定ファイルから実行する
uwsgi.ini

[uwsgi]
wsgi-file=run.py
callable=app

http=0.0.0.0:8080

$ uwsgi uwsgi.ini

### iniファイルの設定項目

daemonize = /var/log/uwsgi.log
log-reopen = true
log-maxsize = 8000000
logfile-chown = on
logfile-chmod = 644

current_release = /opt/apps/current/sample_api
chdir = %(current_release)
wsgi-file = %(current_release)/run.py

processes = 4
threads = 2
thunder-lock = true
max-requests = 3000
max-requests-delta = 300
master = True

なるほど、簡易webサーバを作ってるみたいやな

[aws ec2]ubuntu20.04にningxを入れて起動させたい

1. SSHログイン
ssh ubuntu@* -i ~/.ssh/*.pem

2. apt update
$ sudo apt update
$ sudo apt upgrade

3. mysql
$ sudo apt install mysql-client-core-8.0
$ sudo apt-get update
$ sudo apt install mysql-server
$ mysqld –version
$ sudo service mysql start
$ sudo mysql_secure_installation
$ sudo mysql -u root -p
mysql> create database hanbai;

4. Nginx install
$ sudo apt install nginx

5. 仮想環境作成
$ sudo apt install python3-venv python3-pip python3-dev
$ sudo chown ubuntu /home
$ git clone https://github.com/*/*.git
$ cd hanbai
$ ls
README.md db.sqlite3 hanbai manage.py sales
$ python3 -m venv vdjango

$ . vdjango/bin/activate
(vdjango)$ pip3 install Django==3.0.4
(vdjango)$ pip3 install uwsgi

$ sudo ufw allow 80

6. Nginx設定
$ cd /etc/nginx/conf.d
$ sudo vi project.conf

server{
    listen 80;
    server_name ${publicIp};

    location / {
        proxy_pass http://127.0.0.1:8000;
    }
}

7. iniファイル
$ ls
README.md db.sqlite3 hanbai manage.py sales vdjango
$ vim django.ini

[uwsgi]
module          =  project.wsgi:application
master          =  true
pidfile         =  django.uwsgi.pid
enable-threads  = true
http            =  127.0.0.1:8000
processes       =  5
harakiri        =  50
max-requests    =  5000
vacuum          =  true
home            =  vdjango
daemonize       =  django.uwsgi.log

8. library install & migration
// 省略

9. Nginxとuwsgi起動
$ sudo service nginx start
$ sudo apt-get install -y uwsgi
$ sudo apt install uwsgi-plugin-python3

うーん、
$ sudo python3 manage.py runserver 0.0.0.0:8000 で8000ポート開けても動くんだけど、なんか違うんだよな。。

[aws ec2]Apache2を入れるが、Internal Server Error

### apache2 インストール
$ sudo apt update
$ sudo apt install apache2
$ sudo ufw app list
$ sudo ufw allow ‘Apache Full’
$ sudo ufw status
$ sudo systemctl status apache2
$ hostname -I

### mod_wsgi
$ apt-get install apache2-dev
$ pip3 install mod_wsgi

### settings.py

ALLOWED_HOSTS = ['*'] 

$ mod_wsgi-express module-config
LoadModule wsgi_module “/home/ubuntu/.local/lib/python3.8/site-packages/mod_wsgi/server/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so”
WSGIPythonHome “/usr”

### migrate
$ python3 manage.py makemigrations sales
$ python3 manage.py migrate

### apache設定
sudo vi /etc/apache2/sites-available/000-default.conf

LoadModule wsgi_module "/usr/lib/apache2/modules/mod_wsgi-py38.cpython-38-x86_64-linux-gnu.so"
WSGIScriptAlias / /var/www/hanbai/hanbai/wsgi.py
WSGIPythonHome "/usr"
WSGIPythonPath "/var/www/hanbai"

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        # DocumentRoot /var/www/hanbai

        # WSGIScriptAlias / /var/www/hanbai/hanbai/wsgi.py
        # WSGIPythonPath /var/www/hanbai/        
        <Directory /var/www/hanbai/hanbai/>
            <Files wsgi.py>
                Order deny,allow
                AllowOverride None
                require all granted
            </Files>
        </Directory>
</VirtualHost>

$ sudo /etc/init.d/apache2 restart

$ /var/log/apache2/error.log
[client 59.126.236.35:46554] from django.core.wsgi import get_wsgi_application
[Wed Oct 28 23:46:17.546723 2020] [wsgi:error] [pid 10444:tid 139970643724032] [client 59.126.236.35:46554] ModuleNotFoundError: No module named ‘django’

$ pip3 freeze | grep wsgi
mod-wsgi==4.7.1

WSGIPythonPath /var/www/hanbai:/home/ubuntu/.local/lib/python3.8/site-packages

何故だ。。Djangoをインストールした場所が悪かった?
もう一回やるか。。

[aws ec2]ubuntuにsshする際にPermission denied (publickey).

$ ssh -i ~/.ssh/*.pem ec2-user@${public ip} -vvv
ec2-user@${public ip}: Permission denied (publickey).

セキュリティグループのinbound rulesでsshを許可しているのに何故だろうと調べていたところ、sshでログインする際のubuntuのuser nameはec2-userではなく、ubuntuらしい。

[公式]Linux インスタンス用ユーザーガイド

$ ssh ubuntu@${public ip} -i ~/.ssh/*.pem
Welcome to Ubuntu 20.04.1 LTS (GNU/Linux 5.4.0-1024-aws x86_64)

なるほど、わかりませんね。

[Django3.0] MySQL8にrootでmigrationしようとしてAccess deniedとなる時

bionicからfocal(Ubuntu20.04)で作り直している手順で、mysql8にmigrationしようとすると、Access denied for user ‘root’@’localhost'”となった。

### focalにMySQL 8.20 インストール
$ sudo apt install -y mysql-server
$ mysqld –version
$ sudo service mysql start
$ sudo mysql_secure_installation
$ sudo mysql -u root -p

### Django
$ pip3 install Django==3.0.4

### library インストール
// 省略

### git clone
$ git clone https://github.com/*/${appName}.git
$ cd ${appName}
$ git branch dev
$ git checkout dev

### init.py追記

import pymysql
pymysql.install_as_MySQLdb()

$ pip3 install PyMySQL

### migration
$ python3 manage.py makemigrations sales

->django.db.utils.OperationalError: (1698, “Access denied for user ‘root’@’localhost'”)

なんですと???
MySQL8はrootユーザで外部からアクセスできないらしい。
という事で、Django用のユーザを作ってあげる必要がある。

$ sudo mysql –u root -p
mysql> SHOW VARIABLES LIKE ‘validate_password%’;
mysql> set global validate_password.length=6;
mysql> set global validate_password.policy=LOW;
mysql> CREATE USER ‘django’@’%’ IDENTIFIED BY ‘hogehoge’;
mysql> GRANT ALL PRIVILEGES ON . TO ‘django’@’%’ WITH GRANT OPTION;
mysql> FLUSH PRIVILEGES;

settings.pyを修正して再度migration
$ python3 manage.py makemigrations sales
$ python3 manage.py migrate
$ python3 manage.py createsuperuser
$ python3 manage.py runserver 192.168.33.10:8000
動作確認

ヒョーーーーー、とりあえず20.04、centos8を普段使いの環境にしたから一安心^^

[Ubuntu20.04] Focal Fossaを始めよう

awsでもUbuntu20.04がbionicよりも上に来てるので、これからはbionicではなく、focalを使っていきたいと思う

$ mkdir focal
$ cd focal
$ vagrant init ubuntu/focal64

vagrantfile35行目のポートフォワーディングコメントアウト削除

config.vm.network "private_network", ip: "192.168.33.10"

if Vagrant.has_plugin?("vagrant-vbguest")
   config.vbguest.auto_update = false  
end

$ vagrant up
$ vagrant ssh
$ python3 -V
Python 3.8.5

$ sudo apt update
$ sudo apt upgrade
$ sudo apt install python3-pip
$ pip3 -V
$ git version

うーん、なんか良さそう

[Django3.0] tesseractで画像の文字を解析するページを実装したい

1) コーディングする前に、まず画面設計から作ります。
– イメージとしてはポップアップ

2) 続いて、Webpack&SASS環境でドラッグ&ドロップのFrontEndを書いていく
– 画像ファイルの制御は file.type.match(‘image.*’)とする。
– 例えばjpegなら、file typeは’image/jpeg’となる。

var imageType = 'image.*';

				if(! file.type.match(imageType)){
					alert('画像を選択してください');
					$('#input_file').val('');
					$('#drop_area').css('border', '1px dashed #aaa');
					return;
				}


良い感じ。これをDjangoに実装する

3) Django
### template
– aタグのリンクをwindow.openで設定する

<a href="javascript:window.open('/read', null, 'top=0,left=0,width=500px,height=650');">画像から商品名を読み取る</a>

– radioのvalueはlangの値を入れます。

<div class="row col-md-12 mt-10">
	  			<div class="form-check col-md-2">
				  <input class="form-check-input" type="radio" name="lang" value="jpn" id="lang" checked>
				  <label class="form-check-label" for="lang">日本語</label>
				</div>
				<div class="form-check col-md-2">
				  <input class="form-check-input" type="radio" name="lang" value="eng" id="lang">
				  <label class="form-check-label" for="lang">英語</label>
				</div>
				<div class="form-check col-md-2">
				  <input class="form-check-input" type="radio" name="lang" value="chi_sim" id="lang">
				  <label class="form-check-label" for="lang">簡体字中国語</label>
				</div>
				<div class="form-check col-md-2">
				  <input class="form-check-input" type="radio" name="lang" value="chi_tra" id="lang">
				  <label class="form-check-label" for="lang">繁体字中国語</label>
				</div>
				<div class="form-check col-md-2">
				  <input class="form-check-input" type="radio" name="lang" value="spa" id="lang">
				  <label class="form-check-label" for="lang">スペイン語</label>
				</div>
			</div>

views.py
– Postされた画像は request.FILES[‘*’]で受け取る。
– 条件分岐で、request.FILES[‘read_img’]とすると、MultiValueDictKeyErrorになるので、request.FILES.get(‘read_img’, False)とする。

from PIL import Image
import sys
import pyocr
def img_read(request):
	if(request.method == 'POST' and request.FILES.get('read_img', False)):
		tools = pyocr.get_available_tools()
		img = request.FILES['read_img']
		langs = request.POST['lang']
		img = Image.open(img)
		txt = tools[0].image_to_string(img,lang=langs,builder=pyocr.builders.TextBuilder(tesseract_layout=6))
		params = {
			'txt': txt,
		}
		return render(request, 'sales/img_read.html', params)
	else:
		return render(request, 'sales/img_read.html')

出来たーーーーーーーーーーーーーー^^
きゃっ♩ きゃっ🎵 きゃっ🎶
早速git pushしよー