最近の投稿 Recent posts

docker上でdjango+mysqlで環境構築すると、access deniedエラーになる件

詳細

Docker上でdjango+mysqlの環境構築をするとエラーになる件

環境

  • Macbook Air(M1)
  • Docker Desktop for Mac (ver.20.10.6)
  • Django == 3.0
  • Python3 (3.6-slim-buster)
  • MySQL 5.7

結論から言うと

MySQLイメージを使う場合は、{既存のDBファイルを含むローカルフォルダ}:/var/lib/mysqlとしてバインドするとハマります。 新しい名前付きボリュームor空ディレクトリをバインドすることで、このエラーを回避できます。

やりたいこと

Pythonイメージ内でdjangoプロジェクトをビルドし、djangoからMySQLをDBとして使用したいです。 その際、MySQLのデータを永続化させるために既にDB関連のデータを含んでいるローカルのフォルダ(下記のmysql/data_db)をマウントしようとしてハマりました。

はじめに試したディレクトリ構造は以下のようになっています。

my-docker-project/
|__django-project/
|     |__src/...
|     |__Dockerfile
|
|__mysql/
|    |__data_db/...
|    |__init_db/...
|
|__docker-compose.yml

また、docker-compose.ymlは下記の通りです。

version: "3"

services:
  mysql-db:
    image: amd64/mysql:5.7
    environment:
      - MYSQL_DATABASE=python_db
      - MYSQL_USER={ユーザー名}
      - MYSQL_PASSWORD={パスワード}
      - MYSQL_ROOT_PASSWORD=password
    volumes:
      - ./mysql/data_db:/var/lib/mysql
      - ./mysql/init_db:/docker-entrypoint-initdb.d
    privileged: true
    ports:
      - 3306:3306

  django-project:
    build: ./django-project
    restart: always
    command: bash -c "python3 /mysite/local_manage.py makemigrations && python3 /mysite/local_manage.py migrate && python3 /mysite/local_manage.py runserver 0.0.0.0:8000"
    ports:
      - 8000:8000
    volumes:
      - ./django-project/mysite/history:/mysite/history
      - ./django-project/mysite/media:/mysite/media
    depends_on:
      - mysql-db

以上の情報をもとに、django側のsettings.pyを以下の通りに設定しました。

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'python_db',
        'USER': 'root',
        'HOST': 'mysql-db',
        'PASSWORD': 'password',
        'PORT': '3306'
    }
}

症状

表記の通りの環境構築をDocker上で行いdocker compose up --buildすると、mysqlにアクセスできず以下のエラーに見舞われます。

django.db.utils.OperationalError: (1045, "Access denied for user 'root'@'192.168.32.3' (using password: YES)")

原因

調べて見ると以下の記事に行き当たりました。 stackoverflowの記事

上記の記事によると、 Do note that none of the variables below will have any effect if you start the container with a data directory that already contains a database: any pre-existing database will always be left untouched on container startup. とのことでした。すなわち、一度docker buildなどでDBファイルシステムが構成されてしまったディレクトリをバインドしようとすると、DBコンテナの初期化に失敗するらしいです。

対処法

docker-compose.yml内で/var/lib/mysqlに紐付けるフォルダを、docker compose up --buildする前に空にしておくことでうまくいきます。すなわち、

  1. $ rm -r ./mysql/data_dbでdata_dbを削除
  2. $ mkdir ./mysql/data_dbで再度ディレクトリ作成
  3. $ docker compose up --buildでビルド

という手順を踏むことになります。これはうまくいくことを確認しました。

Django + Ace(django_ace) + DockerでオンラインPython実行環境を実装する

詳細

タイトルの通り,Django + Ace + DockerでオンラインPython実行環境を実装します. 一回で収まらないかもしれないので「その1」としています.

参考にしたサイト

環境

  • Django==3.0
  • django_ace==1.0.11
  • Python==3.6.8
  • Docker Pythonイメージ: python:3.7-slim-buster

構成の説明

  • Django: フレームワークとして使用
  • Ace: Webページ上でテキスト編集する際のシンタックスハイライト等に使用
  • django-ace: AceをDjango内のView定義から呼び出せるようにしたもの
  • Docker: 実行環境をサーバー内で隔離しないと悪意あるコードが実行できてしまうので,Dockerコンテナを使用

環境構築&必要ライブラリ群のインストール

$ python3 -m venv venv
$ source venv/bin/activate
$ pip install Django==3.0
$ pip install django-ace==1.0.11

プロジェクトの作成

$ django-admin startproject project
$ cd project
$ django-admin startapp editor
$ mkdir history #実行するスクリプトの保存先

この時点で,ディレクトリは以下のような構成になっているはずです.

project/
 ├ editor/
 ├ project/
 ├ history/
 └ manage.py

フォームビューの作成

エディタ部分を実装するために,フォームビューを実装します. CDN等で提供されるAceを埋め込んで利用しても良いですが,ここではdjango-aceを使用します.

#forms.py
from django import forms
from django_ace import AceWidget

class EditorForm(forms.Form):
    code = forms.CharField(
        widget = AceWidget(
            mode="python",
            theme="twilight",
            width="100%"
        )
    )

EditorFormを作成したら,Viewを追加していきます. 下記のコードでは,index.htmlをテンプレートとして,HTML中のformから実行コードを取得します. 取得したコードは,historyフォルダ内に保存され,dockerコンテナ内で実行されます.

  • start_docker(code): dockerコンテナに実行するコードを投げ,実行するコード.
  • Home(views.FormVIew): html内のフォームから取得したコードを,start_dockerに投げます.
# views.py
from .forms import EditorForm

FILE_DIR = os.path.join(settings.BASE_DIR, 'history')
DOCKER_CMD = 'docker run -i --rm --name tmp_container -v {}:/usr/src/myapp -w /usr/src/myapp python:3.7 python {}'

def start_docker(code):
    # dockerコンテナ内でコードを実行する
    file_name = '{}.py'.format(datetime.datetime.now().isoformat())
    file_path = os.path.join(FILE_DIR, file_name)

    with open(file_path, 'w', encoding='utf-8') as file:
        file.write(code)

    cmd = DOCKER_CMD.format(FILE_DIR, file_name)
    ret = subprocess.run(
        cmd, timeout=15, shell=True,
        stdout=subprocess.PIPE, stderr=subprocess.STDOUT
    )

    return ret.stdout.decode()

class Home(generic.FormView):
    template_name = 'index.html'
    form_class = EditorForm
    sucess_url = reverse_lazy('home')

    def form_valid(self, form):
        # 送信ボタンで呼ばれる
        code = form.cleaned_data['code']
        output = start_docker(code)
        context = self.get_context_data(form=form, output=output)
        return self.render_to_response(context)
<!--index.htmlの一部のみ抜粋-->
<head>
....
{{ form.media}}
</head>

<body>
....
<div class="col-12">
    <form action="" method="POST">{% csrf_token %}
        {{ form.code }}
        <button class="btn btn-primary" type="submit">Run</button>
    </form>
</div>

<div class="col-12">
    <label for="exampleFormControlTextarea1" class="form-label">実行結果</label>
    <textarea class="form-control" id="exampleFormControlTextarea1" rows="3" disabled>{{ output }}</textarea>
</div>
...
</body>

以上をいい感じにまとめると,下記のようなコード編集画面が現れます.(あとで追記)

ページ 4 / 4 (全 17 件)

About

IMAXおじさんが(主に)技術系記事を備忘録として残していくブログです.

Category

  1. Tech
  2. Daily
  3. Job
  4. Other