クラウド VPS byGMOでDocker+Nginx+Django+WordPressを動かしてみた!

※ 当サイトではアフィリエイト広告を利用しています。リンクは広告リンクも含みます。

この記事は約18分で読めます。
広告

GMOクラウドのVPS [最新版Plesk] 詳細はこちら
Nginxを通してport8000、8001でDjango、8080でWordPressにアクセスできるようにつくりました。
ドメインは取っていません。IPアドレスでアクセスします。
完全に実験用なので実用的ではありません。
できるだけシンプルにして
VPSのポートとコンテナのポートの関係、
リバースプロキシーとは何やっているのか、
Nginxの設定などを実験して確認する目的でやっています。

Nginxをよく理解してないまま試行錯誤で取り合えず動いただけなので、
このまま活用するのは危険ですし解説も間違ってるかもしれません。

この記事の続きです。

広告

Nginx(ネットワークコンテナ)

さくらのVPS のさくらのVPS 1G で同じようなことをやりました。↓

今回はDjangoでgunicornを使わないでやってみました。
前回作ったWordPressとDjangoのコンテナ、イメージ、ボリュームを削除した状態からです。

rootに作ると分かりにくいので新しくディレクトリ作って、そこの下で作業しました。
PowershellでSSH接続した状態で
mkdir docker
cd docker
ブリッジネットワークを作ります
docker network create --driver bridge shared
mkdir shared
cd shared

Nginxのコンテナを作ります
Vscodeを立ち上げsharedフォルダを開く
以下ファイルを新規作成
docker-compose.yml
conf.d/default.conf
version: '3'
services:

  proxy_prod:
    image: nginx:latest
    container_name: 'proxy'
    volumes:
      - ./conf.d/:/etc/nginx/conf.d
    ports:
      - "8000:8000"
    restart: always

networks:
  default:
    name: shared
    external: true

ポイント解説
container_name: ‘proxy’
コンテナ名は、Dockerコマンドによっては実行すると違う名前に変わっていることがあるので
必ずdocker ps -aでコンテナ名を確認したほうがいい。

volumes: – ./conf.d/:/etc/nginx/conf.d
Vsコードで作ったファイルがボリュームの中の/etc/nginx/conf.dにコピーされる。
個人的認識としては(間違っているかも)
このコードで以下の3つがつながると考えてます。
VPS(Vsコードなどで編集できる)の./conf.d/
ボリューム(コンテナが消えても残ってる)の/etc/nginx/conf.d
コンテナ(実際に動いているところ)の/etc/nginx/conf.d
また同じような命令のCOPYとの違いは
COPY命令はホスト上のファイルをイメージ内に「コピーする」
イメージはコンテナの元なのでコンテナにコピーしてることになる。
またservices:の中にvolumes:だけしかないとボリューム名が数字になってます。
docker volume lsで一覧表示すると
数字の名前のボリュームがたくさんあってなぞでしたが、そういうことでした。
名前付きVolumeのやり方は調べてください。

ports: – “8000:8000”
左側の8000がVPSのポートを表し、右側のコンテナの中のportをくっつける。

networks: default: name: shared external: true
このコンテナがブリッジネットワーク名sharedに所属する。
external: trueは既にブリッジネットワーク名sharedを別で作ったことを示す。
(docker network create –driver bridge shared)で作った。

server {
    listen       8000;

    server_name  "";

    location / {
        proxy_pass   http://django_nginx_web_run_d4b0aaff5e6e:8000;
        proxy_set_header Host $host;
        # proxy_set_header Host 'tekitou';
    }

}

django_nginx_web_run_d4b0aaff5e6eはDjangoのコンテナ名です。
後でDjnagoのコンテナを作ってから書き直すので最初は何でも大丈夫です。
ポイント解説
listen 8000;
proxyコンテナの8000番ポートを監視。docker-compose.ymlのportsでVPSの8000番とくっついているので実施はVPSの8000番を見てる。

server_name “”;(ここの説明は未確認なので間違ってるかも)
ここに同じ8000番でも処理したいサブドメインを書いて、同じようにいくつも作れば
それぞれproxy_passで設定したところにリクエストを渡す。
今は””なので8000番は全部、同じDjangoのコンテナにリクエストを渡す。

location / {
VPSのルートにアクセスがきたらproxy_passのところへリクエストを渡す。

proxy_pass http://django_nginx_web_run_d4b0aaff5e6e:8000;
Djangoのコンテナdjango_nginx_web_run_d4b0aaff5e6eへリクエストを渡す。
NginxのコンテナproxyとDjangoのコンテナdjango_nginx_web_run_d4b0aaff5e6eは同じブリッジネットワークsharedに所属していないと送れないはずなので
docker inspect sharedで”Containers”: {の中にproxyとdjango_nginx_web_run_d4b0aaff5e6eがあるか確認する。
Docker Compose入門 (3) ~ネットワークの理解を深める~が参考になります。
(1)から全部みたほうがいい。

proxy_set_header Host $host;は後述

sharedディレクトリで以下実行してコンテナを作ります。
docker-compose up -d 
目次へ

Django

NginxとDjangoをつなげるときgunicornがないとダメかと思ってましたが
ローカル環境で使うDjangoのrunserverで動かしてもつながりました。
それでいいのかは分かりませんが・・・

コード

cd ..
でdockerディレクトリに移動
mkdir django
cd shared
でsharedに戻っておく
Vsコードで新しいウインドウを開いてリモート接続
docker/djangoのディレクトリを開く
以下3つのファイルを作る
FROM python:3
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/
requirements.txt
Django
version: "3.9"

services:
  web:
    container_name: 'django77'
    build: .
    volumes:
      - .:/code
    expose:
      - 8000
    restart: always
networks:
  default:
      name: shared
      external: true

ポイント解説
container_name: ‘django77’
後述しますがdocker-compose runだと勝手に別の名前がコンテナに付けられます。
多分、runだとrunを実行するたびにコンテナが生成されるみたいなので、
どこかにコンテナ名が登録されていてダブっているということでエラーになるのかもしれない。
1回目は大丈夫かも。

expose: – 8000
ports:- “8000:8000″はVPSのポートとくっつけたが
exposeの場合、所属するブリッジネットワークsharedに対して開放しているだけだと思われます。
参考:expose – 挙動
ただ今回は、exposeしなくてもコマンドのpython manage.py runserver 0.0.0.0:8000のときに開放されるっぽい。gunicornも同じ。

restart: always
これがないとコンテナが止まってました。
ちなみにコンテナが止まってるかの判断は
docker lsとdocker ls -aで一覧表示されるものを比較して-aにしか表示されないものは停止してます。

networks: default: name: shared external: true
このコンテナが他で作ったブリッジネットワーク名sharedに所属する。
目次へ

実行

docker-compose up -dだと、そのあと
docker container exec -it コンテナ名 bashでコンテナの中に入るとき
エラー(OCI runtime exec failed: exec failed: unable to start container process: error writing config to pipe: write init-p: broken pipe: unknown)になったので

取り合えず
docker-compose run web bash
そうするとコンテナ名がdjango77にはならず
django_nginx_web_run_d4b0aaff5e6eのように新たな名前が付けられる。
docker-compose runはコンテナを生成してしまうので、
(参考:https://zenn.dev/suzuki_hoge/books/2022-03-docker-practice-8ae36c33424b59/viewer/2-6-container-exec)
また実行したら名前の違うコンテナがてきてしまうので注意です。
なので修正した場合コンテナをいちいち消してrunする形になります。
名前が変わるのでproxy_passのところも変更が必要になります。
#プロジェクト作成
django-admin startproject composeexample .

#実行(このままにしておく)
python manage.py runserver 0.0.0.0:8000
(manage.pyがないというエラーが出たらcd composeexampleしてもう1回)
停止したい場合
CTRL+C
コンテナから出る場合
exit
再開するには止まっているので
docker ps -a
でコンテナ名を見る。
docker start コンテナ名(例django_nginx_web_run_d4b0aaff5e6e) bash
コンテナの中に入る場合
docker container exec -it コンテナ名(例django_nginx_web_run_d4b0aaff5e6e)bash
Vsコードのsharedを開いてるほうで
docker inspect shared
で今、作ったDjangoのコンテナ名が入っているか確認。
conf.d/default.conf(前述)のproxy_passをdjangoのコンテナ名に書き換える。
proxy_set_header Host $host;は
プロクシーサーバに送られるリクエストヘッダの フィールドを再定義です。
$host変数はクライアントがアクセスしようとしているサーバーのIPアドレスになってました。
これを付けないとDjangoに渡されるHostがdjango_nginx_web_run_d4b0aaff5e6eになって
urlに'_'を使っているというエラーをDjangoが出します。

docker restart proxy
でconf.d/default.confの変更が反映されます。

IPアドレス:8000にアクセスすると
Invalid HTTP_HOST header:~が出ます。
Djangoのsettings.pyのALLOWED_HOSTS = ['IPアドレス']に修正して保存
再度IPアドレス:8000にアクセスするとDjangoの画面が出ます。
ちなみに$host(IPアドレス)ではなくproxy_set_header Host 'tekitou';でも
proxy_passでDjangoのコンテナ名を設定してればリクエストは届きます。
この場合Djangoのsettings.pyもALLOWED_HOSTS = ['tekitou' ]になります。
$hostの場合はVPSのIPアドレス例'153.122.198.209'を書きます。
改めてVsコードでリモート接続するとrunserverが動きっぱなしで
docker container exec -it コンテナ名 bash
でコンテナの中にはいることはできたがCTRL+Cでrunserverが止まらなかった。

ps axで動いてるものを確認して
pkill -f runserverで停止することができた。
参考;https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q10254361205
この状態だとexitして
再度docker container exec -it コンテナ名 bashしてもコンテナの中に入れた。
目次へ

python manage.py runserver 0.0.0.0:8000について実験

次にpython manage.py runserver 0.0.0.0:8000の0.0.0.0を変更して実験した。
0.0.0.0だと自分のIPが何でもいいことになるので、
自分のIPがVPSのIPアドレスとDjangoのコンテナのIPアドレスで実験した。
まずVPSのIPアドレスに変更すると
python manage.py runserver VPSのIPアドレス:8000
Error: That IP address can't be assigned to.

docker inspect sharedで調べたDangoのコンテナの"IPv4Address"に変更すると
python manage.py runserver 192.168.176.2:8000
うまくつながりました。
もしやるなら0.0.0.0よりコンテナのIPアドレスに限定した方がよさそうです。
参考:https://jisou-programmer.beproud.jp/%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF/105-127.0.0.1%E3%81%A80.0.0.0%E3%81%AE%E9%81%95%E3%81%84.html

gunicornで動かしてみる

新しいコンテナの8001番でgunicornでdjangoを動かしてみます。

cd ..
でdockerディレクトリに移動
mkdir django_guni
cd django
でdjangoに戻っておく
Vsコードで新しいウインドウを開いてリモート接続
docker/django_guniのディレクトリを開く
Dockerfileは上と同じ

requirements.txtはgunicorn追加
Django
gunicorn

docker-compose.ymlは以下変更したものを作る
略
    container_name: 'django88'
略
    expose:
      - 8001
略

docker-compose run web bash
#プロジェクト作成
django-admin startproject composeexample .

Vsコードのsharedを開いてるほうで
docker inspect shared
で今、作ったDjangoのコンテナ名が入っているか確認。
conf.d/default.confで以下追加。proxy_passを今作ったdjangoのコンテナ名を書く。
server {
    listen       8001;

    server_name  "";

    location / {
        proxy_pass   http://django_guni_web_run_2d56eb78967a:8001;
        proxy_set_header Host $host;
    }

}
docker-compose.ymlで8001ポートを解放
略
    ports:
      - "8000:8000"
      - "8001:8001"
略
変更を更新
docker-compose up -d 
docker restart proxy

django_guniを開いているVsコードで
Djangoのsettings.pyのALLOWED_HOSTS = ['IPアドレス']に修正して保存
gunicorn composeexample.wsgi:application --bind 0.0.0.0:8001
0.0.0.0は先ほどと同じようにdjango_guniのコンテナのIPアドレスでもいい。
IPアドレス:8001にアクセスするとdjangoの画面が出ます。

今回のDjangoのコンテナを1個追加したら
メモリは455/1024MBから561/1024MBになった。
100M増えてるので単純に同じような要領でさらに4個作ったら
メモリ1Gプランなので限界になりそうです。
ディスク容量は3.14/50から3.2/50 GBになったけど余裕です。
目次へ

WordPress

8080番でWordPressをつなげます。
dockerの下に
mkdir wordpress
cd wordpress
以下のdocker-compose.ymlを作成
version: '3'

services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
    #  ports:
    #    - "8000:80"←これをやるとNginxを介さずVPSのポートとくっついてしまう
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    db_data:
networks:
  default:
      name: shared
      external: true

expose -80をしなくてもWordPressはデフォルトで解放されているっぽい

docker-compose up -d 
Vsコードのsharedを開いてるほうで
docker inspect shared
で今、作ったWordPressのコンテナ名が入っているか確認。
conf.d/default.confで以下追加。proxy_passを今作ったWordPressのコンテナ名を書く。
server {
    listen       8080;

    server_name  "";

    location / {
        proxy_pass   http://wordpress-wordpress-1:80;
        proxy_set_header Host "VPSのIPアドレス:8080";
    }

}

docker-compose.ymlで8080ポートを解放
略
    ports:
      - "8000:8000"
      - "8001:8001"
      - "8080:8080"
略
変更を更新
docker-compose up -d 
docker restart proxy
IPアドレス:8080にアクセスするとWordPressの画面が出ます。

proxy_set_header Host "VPSのIPアドレス:8080";は以下の現象の対策
WordPressをつなげようとしたときクライアントへの応答ヘッダのlocationが
proxy_passで設定したコンテナ名(今回wordpress-wordpress-1)になってしまった。
これが変更できないと301リダイレクトのときなど
クライアントが応答ヘッダのlocationにアクセスしようとすると
proxy_passで設定したアドレス(http://wordpress-wordpress-1)にアクセスしようとすることになる。
こんなドメインをとってないのでつながらない。
またproxy_set_header Host $host;ではポート番号がないので上記を使ってる。

WordPressを追加したらメモリ655/1024 MB ディスク容量4.66/50 GB
個人メモ:イチゲブログを移行したらメモリ622 / 1024 MBディスク容量5.22 / 50 GB
目次へ

所感

Nginxでやっていることが何となくわかってきたが
前述したようにDjangoとWordPressのHOSTヘッダで、はまった。
WordPressやDjangoのコンテナが自分のドメインをproxy_passで設定したものの方を採用して処理してしまわないように気を付けないといけません。
今回、Hostのところだけ書き換えたけど、
いろいろやっていくうちに他のところでも同じことが起きそうです。
ドメインを取ってサブドメインをコンテナの名前にすればスムーズにいくことを願うが、
この辺は、何かとついて回りそうな問題である。
さらにセキュリティ面についても、いろいろ検討しなくてはならず
目標のVPSでWordPressとDjangoのサイトを作るためには、まだまだいろいろありそうです。

イチゲをOFUSEで応援する(御質問でもOKです)Vプリカでのお支払いがおすすめです。
MENTAやってます(ichige)

コメント

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