djangoをdockerコンテナで利用(12) – Google Cloudに実行環境作成

資産や取引の登録・更新画面がdjangoでできつつある。

ようやっとphpから脱却できるメドがついた。
あとは作っていくだけ。

ローカルでの開発環境は使えているので、次は本番環境を用意する。

開発してテスト終わったら、git管理のソースをgoogle cloudの中のサーバで動かせるようにする。

コンテナ作って永続化領域に作ったソースを置き、sslのコンテナ経由で公開させてブラウザで使う。

今回はその環境作成メモ。

ローカルlinuxはubuntu20が動いていて、google cloudからコピーしたphp/mariadbのコンテナが動いている。他にも練習用のコンテナが一時的に動いてる。

google cloudにはローカルlinuxと同じubuntu20が動いていて、その中にphp/mariadb/gitlab/rdpのコンテナが動いている。

ここにdjangoとsslのコンテナを追加して、rdpのコンテナからssl経由でdjangoのコンテナをブラウザ経由で使う。

djangoのソースはgit管理していて、ローカルlinuxで開発した結果をgoogle cloud内に持って行って永続化領域にコピーして使う。

できた結果をgoogle cloudのrdpコンテナに接続して使ったらこんな感じ。

django-GCP

どっかで悩むかと思ったら、スンナリできた。
docker様ありがたや、ありがたや。

準備

djangoの管理テーブルを丸ごと開発環境からコピーし、djangoとssl公開用のコンテナを作って、google cloud内のrdp環境にあるブラウザで表示できるようにする。

ホンマにちゃんと動き続けられるのか心配やけど、やりながら考えていく。

データベースの準備

データベースはmariadb利用。

開発用DBはnariDB_Djangoって名前。nariDB_1stっていう本番データベースのコピーも含んでローカルlinuxのnafslinux.intra.gavann-it.comで持たせてる。

django-GCP

一番上にあるgcp-gvis-docker-mariadbがgoogle cloudにある本番データベースで、nariDB_1stって名前で持たせているので、ここにdjango用の管理テーブルを作る。

いきなり作って失敗したらツラいので、ローカルlinuxにある同じnariDB_1stで練習してからgoogle cloudの本番データベースに対して実施する。

この作業では、作業で失敗しないようにコピー元のデータベースは読み込み専用で開くこと、コピー先のデータベースはmysqldumpで事前にバックアップを取ってあることに必ず留意した。

djangoが管理している対象テーブルの特定

自分の業務テーブルは13個。
GVISで始まる名前のテーブルで、資産管理とか取引とかがblobまじりのテーブルに入ってる。

djangoはmigrateとかして自動生成され、専用のテーブルを10個使ってる。
auth_xxxdjango_xxxって名前のがそれ。

django-GCP

ホンマa5sqlあると助かる。作者さんありがとう。

対象テーブルのcreate

定義はローカル開発環境のものを10個そのままa5sqlで参照してそのまま使う。

よく見ると外部キーみたいなのがついてる。

たとえばauth_permissionのテーブルにcontent_type_idって列があって、FOREIGN KEYの箇所からdjango_content_typeってテーブルのidって列を参照してる。

CREATE TABLE `auth_permission` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `content_type_id` int(11) NOT NULL,
  `codename` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `auth_permission_content_type_id_codename_01ab375a_uniq` (`content_type_id`,`codename`),
  CONSTRAINT `auth_permission_content_type_id_2f476e4b_fk_django_co` FOREIGN KEY (`content_type_id`) REFERENCES `django_content_type` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=141 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
;

こういうときは、FOREIGN KEYの記述で参照される側から順番にcreate tableしてデータを入れて行かないと、うまくレコードが入っていかない。

どのテーブルを参照しているのか目で追うと、実行する順番は以下のようになった。

CREATE TABLE `auth_group` (
  :(中略)
CREATE TABLE `django_content_type` (
  :(中略)
CREATE TABLE `auth_permission` (
  :(中略)
CREATE TABLE `auth_group_permissions` (
  :(中略)
CREATE TABLE `auth_user` (
  :(中略)
CREATE TABLE `auth_user_groups` (
  :(中略)
CREATE TABLE `auth_user_user_permissions` (
  :(中略)
CREATE TABLE `django_admin_log` (
  :(中略)
CREATE TABLE `django_migrations` (
  :(中略)
CREATE TABLE `django_session` (
  :(中略)

まずはcreate tableだけ一括で実行した。

django-GCP

djangoの管理テーブルがちゃんとできてる。

django-GCP

対象テーブルのcreateをやり直したいとき

やり直したいときにdrop tableするときは参照する側を先にdropしていく。

テーブル捨てるsql用意したのがこんな順番のsql。
create tableはdropと反対順に実行していかないとエラーになる。

drop TABLE `django_session` ;
drop TABLE `django_migrations` ;
drop TABLE `django_admin_log` ;
drop TABLE `auth_user_user_permissions`
drop TABLE `auth_user_groups` ;
drop TABLE `auth_user` ;
drop TABLE `auth_group_permissions` ;
drop TABLE `auth_permission` ;
drop TABLE `django_content_type` ;
drop TABLE `auth_group` ;

FOREIGN KEYうざいわぁ。

対象テーブルのデータ流し込み

テーブル定義して、a5sqlの「スキーマ間データの転送」で転送する。

django-GCP

転送ボタンを押したときのテーブル選択画面では、一括で実行するとエラーが出るので、create tableしたときの順番で1つずつ流し込んだ。FOREIGN KEYで参照されてないものなら同時に選択してもいい。

django-GCP

そんなに件数ないからテーブル1つ数秒で終わる。
「処理時間 02:06」ってあるのは2分6秒じゃなくて2秒06。

django-GCP

たとえばdjango_sessionのテーブルとかだとゴニョゴニョと英数字入ってた。

django-GCP

docker-compose.ymlの準備

ローカルlinuxで最初は悩みながらやってたけど、1回やった内容なのでdocker-compose.ymlはローカル開発環境で使ってる内容を下敷きにし、ホスト名とポート番号だけ書き換えてgoogle cloudの中の定義に追記する。

  :(中略)
  sv_https-portal:
    image: steveltn/https-portal:1
    ports:
      - "40080:80"
      - "40443:443"
    environment:
      DOMAINS: 'gcp-gvis-dklinux.intra.gavann-it.com -> http://svdjango:8080'
      STAGE: 'local' # or 'production'
    volumes:
      - ./nariDockerDat/sv_django-ssl_certs:/var/lib/https-portal
    depends_on:
      - sv_django
    extra_hosts:
      - gcp-gvis-dklinux.intra.gavann-it.com:192.168.100.118
  :(中略)
  sv_django:
    image: sv_django:4
    build: ./nariDockerDat/sv_django-uwsgi-nginx
    hostname: svdjango
    volumes:
      - ./nariDockerDat/sv_django-uwsgi-nginx/app:/code/app
    ports:
      - "48080:8080"
    depends_on:
      - sv_mariadb
    extra_hosts:
      - gcp-gvis-dklinux.intra.gavann-it.com:192.168.100.118
  :(中略)

去年に作り始めたときはdjangoのコンテナは3.2ぐらいやったけど、今は4.0.1になってるから、イメージのタグは4にしといた。

volumesに書いた場所が永続化領域で「:」の右側の記述がコンテナへのマウント先。

portsは「:」の左が公開ポートで、右がコンテナ利用のポート。
コンテナ間はコンテナ利用のポートで疎通する。

google cloud側のポート番号は1万番台をgit、2万番台を練習用、3万番台がphpなので4万番台をdjango用に使う。

depends_onは他のどのコンテナの起動を待ってから起動するかを書いてある。

extra_hostsはコンテナの中の/etc/hostsに書き込んでくれる。

django側の準備

さっきホスト名とポート番号を書き換えてdocker-compose.yml追記した。
django側もそれ用に更新が必要。

settings.pyにgoogle cloudにあるホスト名gcp-gvis-dklinux.intra.gavann-it.comを追記する。

1つ目はALOOWED_HOSTS。
これがないとページがそもそも開かない。

ALLOWED_HOSTS = ['nafslinux.intra.gavann-it.com'
        ,'gcp-gvis-dklinux.intra.gavann-it.com']

2つ目はCSRF関連。
これがないとCSRFエラーdoes not match any trusted originsってエラーが出る。

django4からホスト名だけじゃなく、https://とか書かないといけなくなった。

CSRF_TRUSTED_ORIGINS = [
    'https://nafslinux.intra.gavann-it.com:30443',
    'https://gcp-gvis-dklinux.intra.gavann-it.com:40443',
]

3つ目はデータベース接続先。
本番環境なんやから、変更しとく。

django-GCP

変更したらdockerコンテナの永続化領域に置いとく。

dockerでビルドとコンテナ作成

前にやったので悩まずに進む。

djangoのビルドとコンテナ起動確認

ビルドしてdockerイメージ作る。

Dockerfileはdockerhubにあるイメージをダウンロードして何をインストールするかを書いておくレシピ。

djangoでの開発過程で、ローカルlinuxで手動apt-getしてフォント組み込んだりpdf関連のモジュール組み込みもしたので、apt-getの内容をその都度追記してある。

apt-getでインストールしている箇所はこんな感じ。その後にpipでdjangoにモジュール組み込みしてる。

RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y \
  git \
  vim \
  python3 \
  python3-dev \
  python3-setuptools \
  python3-pip \
  nginx \
  supervisor \
  fonts-ipaexfont-gothic \
  fonts-ipaexfont-mincho \
  poppler-utils \
  libmysqlclient-dev && \
  rm -rf /var/lib/apt/lists/*

RUN pip3 install --upgrade pip
RUN pip3 install -U pip setuptools

requirement.txtにpipで入れるモジュールのバージョン番号が書いてあって、RUN pip3って箇所でローカルlinuxと完全に揃ってくれる。

requirement.txtはこんな感じ。pip3でfreezeって指示すると作ってくれる内容。

初期の頃はこのファイル10行ぐらいしかなかったのに、いつのまにか倍以上になってる。デカなったなぁ。

asgiref==3.5.0
backports.zoneinfo==0.2.1
cycler==0.11.0
Django==4.0.1
django-ipware==4.0.2
django-markdownx==3.0.1
django-widget-tweaks==1.4.12
fonttools==4.29.0
importlib-metadata==4.10.1
kiwisolver==1.3.2
Markdown==3.3.6
matplotlib==3.5.1
numpy==1.22.1
packaging==21.3
pdf2image==1.16.0
Pillow==9.0.0
PyMySQL==1.0.2
pyparsing==3.0.7
python-dateutil==2.8.2
pytz==2021.3
six==1.16.0
sqlparse==0.4.2
supervisor==4.2.4
uWSGI==2.0.20
zipp==3.7.0

5分も待たずにビルドは終わる。このビルドのときに流れてく文字を見てると、スイスイ構築してくれてるのが気持ちいい。

nari@gcp-gvis-dklinux:/gvis/nari/nariDocs/Docker/nariDockerDat/sv_django-uwsgi-nginx$ ls
Dockerfile  app             requirements.txt     uwsgi.ini
README.md   nginx-app.conf  supervisor-app.conf  uwsgi_params
nari@gcp-gvis-dklinux:/gvis/nari/nariDocs/Docker/nariDockerDat/sv_django-uwsgi-nginx$ docker build -f Dockerfile -t sv_django:4 . 
Sending build context to Docker daemon  557.1kBSending build context to Docker daemon  6.075MB
Step 1/24 : FROM ubuntu:20.04
 ---> 7e0aa2d69a15
Step 2/24 : MAINTAINER Dockerfiles
 ---> Running in 289455d65721

  :(中略)

Step 19/24 : COPY app/requirements.txt /code/app/
 ---> ea78e586f3fe
Step 20/24 : RUN pip3 install -r /code/app/requirements.txt
 ---> Running in c4feef6330a9
Collecting asgiref==3.5.0
  Downloading asgiref-3.5.0-py3-none-any.whl (22 kB)
Collecting backports.zoneinfo==0.2.1
  Downloading backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl (74 kB)
     qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq 74.0/74.0 KB 4.2 MB/s eta 0:00:00
Collecting cycler==0.11.0
  Downloading cycler-0.11.0-py3-none-any.whl (6.4 kB)
Collecting Django==4.0.2
  Downloading Django-4.0.2-py3-none-any.whl (8.0 MB)
     qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq 8.0/8.0 MB 55.0 MB/s eta 0:00:00

  :(中略)

Removing intermediate container e159551f2686
 ---> 581d9a7a1a9f
Step 24/24 : CMD ["supervisord", "-n"]
 ---> Running in 281ff2430e5c
Removing intermediate container 281ff2430e5c
 ---> 16f7abc7f05a
Successfully built 16f7abc7f05a
Successfully tagged sv_django:4
nari@gcp-gvis-dklinux:/gvis/nari/nariDocs/Docker/nariDockerDat/sv_django-uwsgi-nginx$

コンテナ起動できるか試す。SSL用のコンテナはdjangoのコンテナ起動に依存するようにしてるから、SSL用コンテナをビルドしてコンテナ起動が終わってからctrl+cで停止する。

nari@gcp-gvis-dklinux:/gvis/nari/nariDocs/Docker$ docker-compose up sv_django
docker_sv_mariadb_1 is up-to-date
Creating docker_sv_django_1 ... 
Creating docker_sv_django_1 ... doneAttaching to docker_sv_django_1
sv_django_1        | 2022-02-25 05:02:58,701 CRIT Supervisor is running as root.  Privileges were not dropped because no user is specified in the config file.  If you intend to run as root, you can set user=root in the config file to avoid this message.
sv_django_1        | 2022-02-25 05:02:58,701 INFO Included extra file "/etc/supervisor/conf.d/supervisor-app.conf" during parsing
sv_django_1        | 2022-02-25 05:02:58,707 INFO RPC interface 'supervisor' initialized
sv_django_1        | 2022-02-25 05:02:58,707 CRIT Server 'unix_http_server' running without any HTTP authentication checking
sv_django_1        | 2022-02-25 05:02:58,707 INFO supervisord started with pid 1
sv_django_1        | 2022-02-25 05:02:59,711 INFO spawned: 'app-uwsgi' with pid 12
sv_django_1        | 2022-02-25 05:02:59,714 INFO spawned: 'nginx-app' with pid 13
sv_django_1        | 2022-02-25 05:03:01,522 INFO success: app-uwsgi entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
sv_django_1        | 2022-02-25 05:03:01,522 INFO success: nginx-app entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
^CGracefully stopping... (press Ctrl+C again to force)
Stopping docker_sv_django_1 ... 
Stopping docker_sv_django_1 ... donenari@gcp-gvis-dklinux:/gvis/nari/nariDocs/Docker$

SSL利用コンテナのビルドとコンテナ起動確認

djangoコンテナの起動が確認できたら、sv_https-portalのコンテナを作るために起動しておく。

$ cd /docker
$ docker-compose up -d sv_django
Starting docker_sv_django_1 ... done
$

sv_https-portalのコンテナを作り、起動できることをログで確認したらいったんctrl+cで停止する。

$ cd /docker
$ docker-compose up sv_https-portal
Pulling sv_https-portal (steveltn/https-portal:1)...
1: Pulling from steveltn/https-portal
45b42c59be33: Pull complete

    :(中略)

4253af2c8ad9: Pull complete
Digest: sha256:102bf41b771756d591d0615ad47828c94e131d53eeea0a85c072db86498b6faf
Status: Downloaded newer image for steveltn/https-portal:1
Creating docker_sv_https-portal_1 ... done
Attaching to docker_sv_https-portal_1
sv_https-portal_1     | [s6-init] making user provided files available at /var/run/s6/etc...exited 0.
sv_https-portal_1     | [s6-init] ensuring user provided files have correct perms...exited 0.
sv_https-portal_1     | [fix-attrs.d] applying ownership & permissions fixes...
sv_https-portal_1     | [fix-attrs.d] done.
sv_https-portal_1     | [cont-init.d] executing container initialization scripts...
sv_https-portal_1     | [cont-init.d] 00-welcome: executing...
sv_https-portal_1     |
sv_https-portal_1     | ========================================
sv_https-portal_1     | HTTPS-PORTAL v##19.0
sv_https-portal_1     | ========================================
sv_https-portal_1     |
sv_https-portal_1     | [cont-init.d] 00-welcome: exited 0.
sv_https-portal_1     | [cont-init.d] 20-setup: executing...
sv_https-portal_1     | DH parameters appear to be ok.

    :(中略)

sv_https-portal_1     | RSA key ok
sv_https-portal_1     | Signing skipped for nafslinux.intra.gavann-it.com, it expires at 36500 days from now.
sv_https-portal_1     | [cont-init.d] 20-setup: exited 0.
sv_https-portal_1     | [cont-init.d] 30-set-docker-gen-status: executing...
sv_https-portal_1     | [cont-init.d] 30-set-docker-gen-status: exited 0.
sv_https-portal_1     | [cont-init.d] done.
sv_https-portal_1     | [services.d] starting services
sv_https-portal_1     | [services.d] done.

コンテナを起動する

docker-composeでまとめて起動させてみる。

$ cd /docker
$ docker-compose up -d

djangoサイトへ接続

rdpのコンテナにつないでブラウザ起動し、django管理サイトが応答していることを確認する。

django-GCP

ログインできてるってことは、ローカルlinuxから引っ張ってきた管理テーブルがきちっと入ってるってことやね。csrfのエラーも出てへん。

django-GCP

djangoのアプリケーションへSSL接続すると、オレオレ証明書なので「潜在的な・・・」って脅しが表示され、「詳細情報」から「危険を承知で続行」を選ぶ。

django-GCP

ログインなしで開けるように作った状態確認のページを確認してみると、djangoのシステム情報、データベース情報取れてるやん!

django-GCP

円グラフの処理と日本語フォントもjpeg埋め込みして表示できてるやん!!!

django-GCP

データベースのレコードも拾えてるやん!

django-GCP

ログイン画面入ってメニュー画面表示できてるやん!!

検索結果のページネーションもちゃんとできてるやん!!

django-GCP

業務テーブルに入ってるblob列にあるpdfをブラウザでインライン表示できてるやん!!!

django-GCP

業務テーブルに格納したログも検索して表示できるやん!!

django-GCP

思ったとおり動くって、めっちゃ気持ちいい。

次のテーマ

できてなかったことが1つあった。
ヘッダの中に「ログアウト」のリンクがあるのが使えなかった。

これはgoogle cloudとローカルlinuxでヘッダを切り替える必要がある。

他にも切り替えなきゃいけないものとして、ローカル開発環境はcssで画面を緑色系にしてたり、DBは開発用のDBインスタンスを使ってる。

今は2つのファイルを切り替えて使ってるけど、どうやって切り替えるかなぁ。

cssコピーしたりsedで接続先書き換えするような、スクリプトを用意するとかしかないかなぁ。

もう少し考えてみるか。

タイトルとURLをコピーしました