- 投稿日:2020-03-22T23:54:18+09:00
CentOS8 + PX-W3PE4 + docker-mirakurun-epgstationで作る自宅TV録画サーバー
概要
現在入手困難なTVチューナーカード、アースソフト社のPT3に代わり、PLEX社のPX-W3PE4を用いて自宅TV録画サーバーを構築します。録画ソフトウェアとしてl3tnun氏作成のdocker-mirakurun-epgstationを改変して利用し、できる限りサーバー環境を汚さず、手軽に作成します。
注意
本記事はある程度「分かっている」方向けとなります。できる限り詳細に説明はしますが、一部の説明を他の方の解説に任せたり説明を省いている点があることをご了承ください。
2020年3月時点でDockerはCentOS8で未サポートとなっていますが、稼働は確認しています。気になる方はCentOS7で作成してください。
また、サーバーは既に作成しており、記憶を辿って解説記事を書いていることから抜けや間違いがあると思われます。間違いあればご指摘いただければと思います。
おしながき
- 資材調達
- CentOS8インストール、初期設定。
- PLEX TVチューナー用非公式ドライバインストール
- Docker, docker-composeインストール
- docker-mirakurun-epgstation導入
1. 資材調達
必要な資材は次の通りとなります。
1.1 PC
私は中古のHP Z230 SFFを用いていますが録画のみの場合であればそこまでの性能は必要ありません。購入を予定しているTVチューナーの推奨性能以上のものを選んでください。今回の説明で用いるPX-W3PE4はIntel Core i3以上、メモリ4GB以上となっております。Dockerで運用することを考えてこれより多少良いものを選んでおけば確実でしょう。
PX-W3PE4, PX-Q3PE4の場合は PCIExpress端子に加え、内蔵USB端子が必要なことに注意してください。
1.2 PLEX製TVチューナー
PLEX PX-W3U4/Q3U4/W3PE4/Q3PE4 用の非公式版Linuxドライバに対応しているTVチューナーを購入。私はこの中で比較的動作が安定しているという噂のある「PX-W3PE4」を購入しました。
ここではPX-W3PE4を用いて説明します。PX-W3U4についてはここに記載した方法で視聴可能となると思われますが、PX-Q3U4, PX-Q3PE4はS4+T4チューナーとなるためここに記載した方法そのままでは視聴できません。当方所有していないため検証できません!
1.3 ICカードリーダー
TVチューナーに付属しているカードリーダーは非公式ドライバでは利用できません。USB接続タイプのICカードリーダーが別途必要になります。
私は昔ながらのSCR3310-NTTComを用いていますが、終売していますのでACR39-NTTComあたりを購入するとよいでしょう。私は使用していないため保証はできませんが、ネットでの報告ではPX-W3PE4での利用実績があるようです。
1.4 B-CASカード
暗号化されたストリームを復号するために必要。入手法については言及しません。
2. CentOS 8 インストール、初期設定
すべてのパーツを入手し、PCとして組み上げてから各種インストールを行います。
2.1 CentOS8インストール
まず、CentOSでは最新バージョンとなるCentOS8をインストールします。
- CentOS8のISOイメージをダウンロード
- RufusでUSBメモリに焼きこみ or DVDライティングソフトでDVD-RにISO焼きこみ
- 2.で作成したメディアから起動するようにBiosを設定、起動。
- 手順に従いインストール。
「ソフトウェアの選択」は「サーバー」を選択。
ここの手順に従って録画鯖のみ動かすのであれば「最小限のインストール」でも問題ないと思います。参考:「ネットワークエンジニアの日常 CentOS 8 をインストールする手順」
2.2 IPアドレス固定
最終的に録画鯖と同じネットワーク上にいる別PCからブラウザでEPGStationにアクセスし録画管理を行うため、ネットワーク内での鯖のIPアドレスを固定します。
鯖側で固定、ルーターからmacアドレスを用いて固定、いずれの方法でも問題ありません。詳細は省きます。
ここでは、鯖IPを「192.168.xxx.yyy」と固定したとして説明を続けます。
2.3 ファイアウォール・SELinux無効化
鯖より上位のルーター等で適切にファイアウォールが設定されているという前提で鯖のファイアウォール及びSELinuxを無効化します。セキュリティ上問題があるので、気になる方は無効にせず適切に設定の上ご利用ください。
以降、コマンドはすべてルート権限で実行します。
# echo ----ファイアウォール無効化---- # systemctl stop firewalld # systemctl disable firewalld # echo ----SELinux無効化---- # vi /etc/selinux/config # This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. #SELINUX=enforcing #この行をコメントアウト SELINUX=disabled #disabledに変更 :wq # echo ----再起動---- # reboot参考:ServerWorld CentOS8 初期設定:ファイアウォール
3. PLEX TVチューナー用非公式ドライバインストール
PLEX PX-W3U4/Q3U4/W3PE4/Q3PE4 用の非公式版Linuxドライバをリンクに従ってDKMSを利用してインストールします。
# echo ----必要ライブラリのインストール---- # dnf update # dnf install git unzip gcc make dkms kernel-devel kernel-headers # echo ----ファームウェアの抽出とインストール---- # git clone https://github.com/nns779/px4_drv # cd px4_drv/fwtool # make # wget http://plex-net.co.jp/plex/pxw3u4/pxw3u4_BDA_ver1x64.zip -O pxw3u4_BDA_ver1x64.zip # unzip -oj pxw3u4_BDA_ver1x64.zip pxw3u4_BDA_ver1x64/PXW3U4.sys # ./fwtool PXW3U4.sys it930x-firmware.bin # mkdir -p /lib/firmware # cp it930x-firmware.bin /lib/firmware/ # cd ../ # echo ----ドライバのインストール(DKMS使用)---- # cp -a ./ /usr/src/px4_drv-0.2.1 # dkms add px4_drv/0.2.1 # dkms install px4_drv/0.2.1 # echo ----再起動(一応)---- # rebootインストール後、リンク先の「3.確認」の手順を実施し、/dev/px4video0~4が作成されていることを確認。
参考:PLEX PX-W3U4/Q3U4/W3PE4/Q3PE4 用の非公式版Linuxドライバ
参考:How to Install Kernel Headers in CentOS 7
4. Docker, docker-composeインストール
docker-mirakurun-epgstationを利用するためにDocker, docker-composeをインストールしていきます。
4.1 Dockerをインストール
# echo ----リポジトリの追加---- # dnf dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # echo ----暫定対応:containerd.ioの直接インストール---- # wget https://download.docker.com/linux/centos/7/x86_64/stable/Packages/containerd.io-[最新バージョン].x86_64.rpm # dnf install containerd.io-[最新バージョン].x86_64.rpm # echo ----Dockerのインストール---- # dnf install docker-ce docker-ce-cli # echo ----Dockerの自動起動設定---- # systemctl start docker # systemctl enable docker # echo ----Dockerインストールの確認---- # docker --versionコマンド中の[最新バージョン]とは、
https://download.docker.com/linux/centos/7/x86_64/stable/Packages/
にある中でbeta, rcのついていない最新のcontainerd.io。
執筆時点での最新はcontainerd.io-1.2.13-3.1.el7.x86_64.rpm
4.2 docker-composeをインストール
# echo ----docker-composeインストール---- # curl -L "https://github.com/docker/compose/releases/download/[最新バージョン]/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose # chmod 755 /usr/local/bin/docker-compose # echo ----docker-composeインストールの確認---- # docker-compose --versionコマンド中の[最新バージョン]とは、
https://github.com/docker/compose/releases
にある中でbeta,rcのついていない最新のバージョン。
執筆時点での最新は1.25.4
5. docker-mirakurun-epgstation導入
docker-mirakurun-epgstationをgitクローンし、PLEX用の設定に書き換えて実行します。
5.1 USB型ICカードリーダーの接続場所を探す
後程コンテナに渡す際に必要になるUSB型ICカードリーダーがどのBusに刺さっているかをlsusbで探します。
lsusbを実行し、「SCM~」と記載のあるものがカードリーダーです。
このBusとDeviceの後に続く数字をメモっておきます。# lsusb ... Bus 003 Device 002: ID 04e6:511a SCM Microsystems, Inc. ...
5.2 docker-mirakurun-epgstationのクローン
# cd ~ # git clone https://github.com/l3tnun/docker-mirakurun-epgstation5.3 各種設定ファイルの書き換え
docker-mirakurun-epgstationはPT3を前提とした記載となっているため、PLEX用に各種設定ファイルを書き換えます。
vi
等お好みのエディタで書き換えてください。PX-W3U4, PX-W3PE4はこのままで良いですが、PX-Q3U4, PX-Q3PE4はS4+T4チューナーとなるためここに記載した方法では動作しません。
5.3.1
~/docker-mirakurun-epgstation/docker-compose.yml
services: mirakurun: ... devices: - /dev/pt3video0:/dev/pt3video0 - /dev/pt3video1:/dev/pt3video1 - /dev/pt3video2:/dev/pt3video2 - /dev/pt3video3:/dev/pt3video3 #- /dev/bus/usb/00x/00x # card readerこの箇所を次の通り書き換える。
services: mirakurun: ... devices: - /dev/px4video0:/dev/px4video0 - /dev/px4video1:/dev/px4video1 - /dev/px4video2:/dev/px4video2 - /dev/px4video3:/dev/px4video3 - /dev/bus/usb/[BUS]/[DEV] # card readerpt3の部分をすべてpx4に書き換え、#card reader行のコメントを外す。
この時、5.1で調べたBusとDeviceの数字をそれぞれ[BUS], [DEV]のところに記入。
その他、お好みで録画ファイルの位置を変更したり#restart: always
をアンコメント。5.3.2
~/docker-mirakurun-epgstation/mirakurun/conf/tuners.yml
PT3-S0, PT3-S1, PT3-T1, PT3-T2のCommand行
command: recpt1 --device /dev/pt3video0 <channel> - - command: recpt1 --device /dev/pt3video1 <channel> - - command: recpt1 --device /dev/pt3video2 <channel> - - command: recpt1 --device /dev/pt3video3 <channel> - -この箇所のpt3をpx4に書き換える。
command: recpt1 --device /dev/px4video0 <channel> - - command: recpt1 --device /dev/px4video1 <channel> - - command: recpt1 --device /dev/px4video2 <channel> - - command: recpt1 --device /dev/px4video3 <channel> - -BSアンテナのLNB電源をONにする場合は S0, S1に --lnb 15を追加。
command: recpt1 --device /dev/px4video0 --lnb 15 <channel> - - command: recpt1 --device /dev/px4video1 --lnb 15 <channel> - -BSアンテナを接続しない場合はS0, S1の
isDisable
をtrueに変更。#isDisabled: false isDisabled: true5.3.3
~/docker-mirakurun-epgstation/mirakurun/Dockerfile
もしかしたらこの変更は必要ないかもしれない[未検証]
recpt1のビルド前にpt1_dev.hにPLEX用のデバイス名を追加する。# recpt1 \ git clone https://github.com/stz2012/recpt1 /tmp/recpt1 && \ cd /tmp/recpt1/recpt1 && \ ./autogen.sh && \ ./configure && \ make && \ make install && \ \
この箇所の
cd /tmp/recpt1/recpt1 && \
と./autogen.sh && \
の間に2行追加。# recpt1 \ git clone https://github.com/stz2012/recpt1 /tmp/recpt1 && \ cd /tmp/recpt1/recpt1 && \ sed -i -e "/^char \*bsdev\[NUM_BSDEV\] = {$/a \ \ \ \ \"/dev/px4video1\",\n\ \ \ \ \"/dev/px4video0\"," pt1_dev.h && \ sed -i -e "/^char \*isdb_t_dev\[NUM_ISDB_T_DEV\] = {$/a \ \ \ \ \"/dev/px4video2\",\n\ \ \ \ \"/dev/px4video3\"," pt1_dev.h && \ ./autogen.sh && \ ./configure && \ make && \ make install && \ \
参考:Raspberry Pi 3 Model B で PLEX PX-W3U4 が動くという良い話
5.4 コンテナの起動
# cd ~/docker-mirakurun-epgstation # docker-compose up -d初回立ち上げはかなり長い時間がかかるので気長に待つ。
5.5 起動確認
ネットワーク上の別PCで
http://192.168.xxx.yyy:8888
にアクセスし、EPGStationが表示されることを確認。5.6 チャンネル設定
~/docker-mirakurun-epgstation/mirakurun/conf/channels.yml
を変更してチャンネル設定をする。
BS/CSは入力されたものをそのまま利用できる。
地上波は後述するチャンネルスキャンを用いれば自動設定ができるので、基本的にはチャンネルスキャンだけすればよい。チャンネルスキャン
# echo ----mirakurunチャンネルスキャン---- # curl -X PUT "http://192.168.xxx.yyy:40772/api/config/channels/scan" # echo ----完了後、コンテナの再起動---- # cd ~/docker-mirakurun-epgstation # docker-compose down # docker-compose up -dおわりに
Linux, PLEX, dockerで録画鯖を構築する手段、という記事がなかったので作成しました。
PT3が入手困難になって久しいので、代替手段を探している方々もきっといるはず…PT3に比べてPLEXのチューナーは動作が不安定、という話もありますが、非公式ドライバは安定して動作しているように見えます。私はかれこれこの鯖で1年ほど深夜アニメを録画していますが、悪天候時以外のドロップは今のところありません。途中CentOSを7から8に上げましたが。
5.3の書き換え部分についてgithubにdiffを設置してそのdiffを適用するような記事にすればもっと容易に導入可能になるかなとも思う。需要があるなら作ろうかな。
昨今映像サブスクリプション全盛でもはやTV録画に需要はないのかもしれないけど、何より無料で放送しているものを録画できるというのは大きなアドバンテージだと思う。皆様良き録画ライフを!
- 投稿日:2020-03-22T22:36:16+09:00
Docker Compose でWebアプリケーションを開発する環境(NGINX + uWSGI + PostgreSQL)を構築する。
はじめに
Docker Composeを使って、NGINX、uWSGI、PostgreSQLが動作するDocker コンテナを構築し、
Django(Python)で開発したWebアプリケーションをブラウザで動作確認するまでの手順を記載します。
※ 本投稿は、Mac OSを前提としています。
macOS Catalina 10.15.3で作業しています。
No. 項目 説明 1 Docker Compose 複数のDocker コンテナを一元管理するツールです。
本投稿では、NGINX、uWSGI、PostgreSQLが動作するDocker コンテナを
構築します。2 NGINX Webサーバです。
本投稿では、ここに静的ファイルを配置します。3 uWSGI アプリケーションサーバです。
Pythonで作成したWebアプリケーションを配置します。4 PostgreSQL DBサーバです。
Webアプリケーションで利用するデータを保持します。5 Django PythonのWebフレームワークです。 前提条件
Docker for Macをダウンロードする際、Dockerの公式サイトにログインする必要があります。
Dockerの公式サイトにログインするためのユーザを作成して下さい。1. Docker for Mac
最初にDocker for Macのインストールを行います。
Docker Composeは、Docker for Macに含まれています。1-1. ダウンロード
以下のサイトからダウンロードします。
https://docs.docker.com/docker-for-mac/install/(1) 「Download from Docker Hub」ボタンを押下します。
(2) 「Download Docker Desktop for Mac」ボタンを押下します。
Docker.dmg がダウンロードされます。
1-2. インストール
(1) Docker.dmgの実行
ダウンロードした Docker.dmg を実行して下さい。(2) Applicationsに移動
DockerをApplicationsに移動させます。
(3) Dockerの実行
アプリケーションからDockerを実行して下さい。1-3. 確認
(1) ターミナルを開き、"docker --version"を実行します。
インストールしたDockerのバージョンを確認します。$ docker --version Docker version 19.03.5, build 633a0ea(2) 次に、"docker-compose --version"を実行します。
Docker Composeのバージョンを確認します。$ docker-compose --version docker-compose version 1.25.4, build 8d51620a2. Docker コンテナの構築
NGINX、uWSGI、PostgreSQLが動作するDocker コンテナを構築します。
2-1. 作業ディレクトリの作成
任意の作業ディレクトリを作成して下さい。
本投稿では、"django"を作成します。$ mkdir django2-2. ファイル/ディレクトリの準備
Docker Composeの実行に必要なファイル、Webアプリケーションのソースコードを
配置するディレクトリなどを準備します。(1) 作業ディレクトリへの移動
$ cd django(2) requirements.txt の作成
$ touch requirements.txt(3) Dockerfile の作成
$ touch Dockerfile(4) docker-compose.yml の作成
$ touch docker-compose.yml(5) nginx ディレクトリの作成
$ mkdir nginx(6) nginx/conf ディレクトリの作成
$ mkdir nginx/conf(7) nginx/conf/mainproject_nginx.conf の作成
$ touch nginx/conf/mainproject_nginx.conf(8) nginx/uwsgi_params の作成
$ touch nginx/uwsgi_params(9) src ディレクトリの作成
$ mkdir src(10) src/static ディレクトリの作成
$ mkdir src/static(11) dbdata ディレクトリの作成
$ mkdir dbdata[ファイル/ディレクトリの説明]
No. 名称 説明 1 requirements.txt 利用するPythonのパッケージ(ライブラリ)を指定するファイル 2 Dockerfile Docker コンテナの構成情報を記述するためのファイル 3 docker-compose.yml アプリケーションを動かすための処理を記述するファイル 4 nginx NGINXのルートディレクトリ 5 nginx/conf NGINXの設定ファイルを配置するディレクトリ 6 nginx/conf/mainproject_nginx.conf NGINXの設定を記述するためのファイル
※ ファイル名は任意です、拡張子は".conf"にして下さい。7 nginx/uwsgi_params NGINXとuWSGIを連携させるためのファイル 8 src Webアプリケーションを配置するディレクトリ 9 src/static css、JavaScript、画像などの静的ファイルを配置するディレクトリ 10 dbdata PostgreSQLのデータファイルを保持するディレクトリ [ここまでのファイル構成]
$ tree . . ├── Dockerfile ├── dbdata ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mainproject_nginx.conf │ └── uwsgi_params ├── requirements.txt └── src └── static 5 directories, 5 files2-3. requirements.txtの設定
requirements.txtを開き、以下の内容を記述して保存します。
requirements.txtは、次に説明するDockerfileの中で利用しています。
("RUN pip install -r requirements.txt"の箇所)Django django-bootstrap4 uwsgi psycopg2[説明]
① Django
PythonのWebフレームワークのDjangoをインストールします。
② django-bootstrap4
DjangoでBootstrap4を利用するためのパッケージをインストールします。
※ Bootstrap4を利用しない場合、インストールする必要はありません。
最後にDjangoで作成したページにBootstrap4を適用する方法を参考に紹介します。
③ uwsgi
アプリケーションサーバをインストールします。
④ psycopg2
PythonからPostgreSQLに接続するためのドライバをインストールします。2-4. Dockerfileの設定
Dockerfileを開き、以下の内容を記述して保存します。
Dockerfileは、次に説明するdocker-compose.ymlの中で利用しています。
("build: ."の箇所)FROM python:3.7 ENV PYTHONUNBUFFERED 1 RUN mkdir /code WORKDIR /code COPY requirements.txt /code/ RUN pip install --upgrade pip RUN pip install -r requirements.txt COPY . /code/[説明]
① FROM python:3.7
Docker のベースイメージに"python3.7"を指定
"Docker Hub"のイメージから作成します。
② ENV PYTHONUNBUFFERED 1
Python の標準出力、標準エラー出力をバッファに溜め込まない設定となります。
バッファが有効になっていると、標準出力の途中経過が表示されず、
全てのタスクが終わった後に纒めて表示されます。
③ RUN mkdir /code
コンテナ上にcodeディレクトリを作成します。
④ WORKDIR /code
コンテナの作業ディレクトリにcodeディレクトリを指定します。
⑤ COPY requirements.txt /code/
ローカルマシンのrequirements.txtをコンテナのcodeディレクトリに追加します。
⑥ RUN pip install --upgrade pip
コンテナ上のpipを最新のバージョンにします。
⑦ RUN pip install -r requirements.txt
requirements.txtに記載しているPythonのパッケージ(ライブラリ)を
コンテナ上インストールします。
⑧ ローカルマシンのカレントディレクトリの内容をコンテナのcodeディレクトリに追加します。
2-5. docker-compose.ymlの設定
docker-compose.ymlを開き、以下の内容を記述して保存します。
version: '3.7' volumes: pgdata: driver_opts: type: none device: $PWD/dbdata o: bind services: nginx: image: nginx container_name: container.nginx ports: - "8000:8000" volumes: - ./nginx/conf:/etc/nginx/conf.d - ./nginx/uwsgi_params:/etc/nginx/uwsgi_params - ./src/static:/static - ./nginx/log:/var/log/nginx depends_on: - web web: build: . container_name: container.uwsgi command: uwsgi --ini /code/mainproject/django.ini volumes: - ./src:/code expose: - "8001" depends_on: - db db: image: postgres restart: always container_name: container.postgres ports: - "5432:5432" environment: POSTGRES_DB: "postgresdb" POSTGRES_USER: "admin" POSTGRES_PASSWORD: "test" POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=C" volumes: - pgdata:/var/lib/postgresql/data hostname: postgres[説明]
① version: '3.7'
docker-composeで使用するバージョンとなります。
バージョンによって、docker-compose.ymlの書き方が異なります。
※ Docker 本体のバージョンとの関連は「Compose file version 3 reference」を参照して下さい。
② volumes:
トップレベルのvolumesは、名前付きvolumeとなります。
ここでは、"pgdata"という名前付きvolumeを指定しています。
"device: $PWD/dbdata"は、ローカルマシンの作業ディレクトリ配下のdbdataディレクトリを表します。
③ services:
構築するコンテナを記述します。
ここでは、"nginx"、"web"、"db"を記述しています。
この名前は任意で指定が可能です。
例えば、"db"は、"database"などでも問題ありません。
③-1 nginx:
③-1-1 image: nginx
NGINXが動作するコンテナは、"Docker Hub"のイメージから作成します。
③-1-2 container_name: container.nginx
"container.nginx"という任意のコンテナ名を付けます。
③-1-3 ports:
NGINXのポート番号に8000ポートを指定します。
③-1-4 volumes:
ローカルマシンの作業ディレクトリ配下の各ファイル/ディレクトリと
NGINXのコンテナのファイル/ディレクトリのマウントを設定します。
a) ./nginx/conf:/etc/nginx/conf.d
これにより、"nginx/conf/mainproject_nginx.conf"が"/etc/nginx/conf.d"にマウントされます。
"/etc/nginx/conf.d"に配置した拡張子".conf"のファイルは、
親の設定ファイルにあたる"/etc/nginx/nginx.conf"によって読み込まれます。
* "/etc/nginx/nginx.conf"は、nginxのコンテナにデフォルトで存在します。
b) ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
"/etc/nginx/uwsgi_params"は、次に説明する"nginx/conf/mainproject_nginx.conf"の中で利用しています。
("include /etc/nginx/uwsgi_params;"の箇所)
c) ./src/static:/static
静的ファイルを配置するディレクトリをマウントします。
cssなどをNGINXから配信するための設定となります。
d) ./nginx/log:/var/log/nginx
ログファイルを配置するディレクトリをマウントします。
これにより、ローカル上でログを確認できるようになります。
③-1-5 depends_on:
"web"、つまりuWSGIのコンテナが起動された後に、NGINXのコンテナを起動するという指定です。
③-2 web:
③-2-1 build: .
uWSGIが動作するコンテナは、Dockerfileに記述したコンテナの構成情報で作成します。
"build: ."は、ビルドに使用するDockerfileのパスを表します。
つまり、このコンテナは、Dockerfileに記載したコンテナの構成情報で作成します。
③-2-2 container_name: container.uwsgi
"container.uwsgi"という任意のコンテナ名を付けます。
③-2-3 command: uwsgi --ini /code/mainproject/django.ini
コンテナが起動した後に、uWSGIを設定ファイル"/code/mainproject/django.ini"の
内容に従って、起動させるということを表します。
③-2-4 volumes:
ローカルマシンの作業ディレクトリ配下の各ディレクトリと
uWSGIのコンテナのディレクトリのマウントを設定します。
③-2-5 expose:
uWSGIのポート番号に8001ポートを指定します。
③-2-6 depends_on:
"db"、つまりPostgreSQLのコンテナが起動された後に、uWSGIのコンテナを起動するという指定です。
③-3 db:
③-3-1 image: postgres
PostgreSQLが動作するコンテナは、"Docker Hub"のイメージから作成します。
③-3-2 restart: always
ホストOSを起動したタイミングでコンテナを自動起動するという指定です。
③-3-3 container_name: container.postgres
"container.postgres"という任意のコンテナ名を付けます。
③-3-4 ports:
PostgreSQLのポート番号に5432ポートを指定します。
③-3-5 environment:
a) POSTGRES_DB: "postgresdb"
コンテナ起動時に作成するデフォルトのDBの名称を指定します。
指定しない場合、POSTGRES_USERの値が使われます。
b) POSTGRES_USER: "admin"
スーパユーザの名称を指定します。
指定しない場合、"postgres"が使われます。
c) POSTGRES_PASSWORD: "test"
スーパユーザのパスワードを指定します。
指定しない場合、POSTGRES_USERの値が使われます。
d) POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --locale=C"
DBを作成するコマンド(postgres initdb)を実行する際に渡す引数を指定します。
エンコーディングに"UTF-8"、ロケールに"C"を指定しています。
※ environmentの詳細は、「Supported tags and respective Dockerfile links 」を参照して下さい。
③-3-6 volumes:
名前付きvolumeの"pgdata"と
PostgreSQLコンテナの"/var/lib/postgresql/data"のマウントを設定します。
この設定により、DBのデータが永続化されます。
(コンテナを削除してもデータがローカルマシン上に残ります。)
③-3-7 hostname:
"postgres"という任意のホスト名を付けます。2-6. nginx/conf/mainproject_nginx.confの設定
nginx/conf/mainproject_nginx.confを開き、以下の内容を記述して保存します。
# the upstream component nginx needs to connect to upstream django { ip_hash; server web:8001; } # configuration of the server server { # the port your site will be served on listen 8000; # the domain name it will serve for server_name 127.0.0.1; # substitute your machine's IP address or FQDN charset utf-8; # max upload size client_max_body_size 75M; # adjust to taste location /static { alias /static; } # Finally, send all non-media requests to the Django server. location / { uwsgi_pass django; include /etc/nginx/uwsgi_params; # the uwsgi_params file you installed } }[説明]
① upstream django
細かい設定方法は割愛しますが、"server web:8001;"については、
docker-compose.ymlに記載したweb(ポート番号は、8001)を指しています。
つまり、uWSGIを指しています。
② server
こちらも細かい設定方法は割愛しますが、"location / {"については、
NGINXのルートにアクセスがあった場合、①で説明したuWSGIに処理を渡すという設定になります。
また、③-1-4で説明した/etc/nginx/uwsgi_paramsの設定ファイルに基づいて連携しています。
"location /static {"については、
静的ファイルについては、NGINXのstaticディレクトリに配置したものを使用するという設定です。2-7. nginx/uwsgi_paramsの設定
nginx/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;2-9. Djangoプロジェクトの作成
以下のコマンドを実行し、Djangoプロジェクトを作成します。
プロジェクト名は任意です。(ここでは、"mainproject"という名前にしました。)$ docker-compose run --rm web django-admin.py startproject mainproject .下記のようなWARNINGが出力されますが、「イメージが存在していなかったので、ビルドした」という
警告なので、問題ありません。WARNING: Image for service web was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.[実行結果の確認①(ファイル構成)]
ファイル構成は以下の構成となります。
※ dbdata配下には、マウントしたPostgreSQLのデータ(/var/lib/postgresql/data)が
大量に作成されるので、省略しています。$ tree . . ├── Dockerfile ├── dbdata │ ├── PG_VERSION │ ├── base │ │ └── ・・・ 省略 │ │ └── ・・・ 省略 │ └── ・・・ 省略 ├── docker-compose.yml ├── nginx │ ├── conf │ │ └── mainproject_nginx.conf │ └── uwsgi_params ├── requirements.txt └── src ├── mainproject │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py └── static[実行結果の確認②(Docker コンテナの状態)]
"docker ps -a"コマンドを実行し、Docker コンテナの状態を確認して下さい。
以下のようにPostgreSQLのコンテナが起動しています。
※ uWSGIのDocker コンテナについては、次に説明するuWSGIのiniファイルを作成していない為、
起動していません。(docker-compose.ymlの"command: uwsgi --ini /code/mainproject/django.ini"の箇所)
※ NGINXのDocker コンテナについては、uWSGIのDocker コンテナの起動が
前提条件となっていますので、こちらもまだ起動していません。$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9d68d40adee0 postgres "docker-entrypoint.s…" 25 hours ago Up 25 hours 0.0.0.0:5432->5432/tcp container.postgres2-10. uWSGIのiniファイルを作成します。
src/mainproject/django.iniを新規に作成し、以下の内容を記述します。
[uwsgi] socket = :8001 module = mainproject.wsgi wsgi-file = /code/mainproject/wsgi.py logto = /code/mainproject/uwsgi.log py-autoreload = 1[説明]
① socket = :8001
uWSGIを起動するポート番号を指定します。
② module = mainproject.wsgi
loadするwsgiモジュールを指定します。
③ wsgi-file = /code/mainproject/wsgi.py
loadするwsgiファイルを指定します。
④ logto = /code/mainproject/uwsgi.log
ログを出力する場所を指定します。
アプリケーションのエラーが発生した時などに確認するログとなります。
⑤ py-autoreload = 1
オートリロード機能の間隔を指定します。
この設定では、1秒ごとにリクエストがあればリロードするという設定になります。
※ 詳細は、「uWSGI Options 」を参照して下さい。2-11. ALLOWED_HOST、DB及び静的ファイルの設定を行う。
src/mainproject/settings.pyを開き、以下の修正を行い保存します。
(1) ALLOWED_HOSTの設定
下記の修正を行います。
公開するドメイン名を設定します。
※ Django 1.5以降のsettings.pyに、ALLOWED_HOSTSという項目が追加されました。
これを設定しない場合、デバックモードがtrueの時にエラーとなります。(修正前)
ALLOWED_HOSTS = [](修正後)
ALLOWED_HOSTS = ["*"](2) DBへの接続設定
下記の修正を行います。
docker-compose.ymlのdbのenvironmentに合わせます。
(修正前)DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), } }(修正後)
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql', 'NAME': 'postgresdb', 'USER': 'admin', 'PASSWORD': 'test', 'HOST': 'db', 'PORT': 5432, } }(3) 静的ファイルの設定
下記の修正を行います。(修正前)
# Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ STATIC_URL = '/static/'(修正後)
# Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/3.0/howto/static-files/ # All applications can use Static files of this directory STATICFILES_DIRS = ( os.path.join(BASE_DIR, "mainproject/static/"), ) # Collectstatic command put STATICFILES_DIRS and each application's Static files together into this directory STATIC_ROOT = os.path.join(BASE_DIR, 'static/') # Django template files read this directory to use Static files(example {% static 'style.css' %}) STATIC_URL = '/static/'[説明]
① STATICFILES_DIRS
パスの指定先は任意です。
ここでは、「2-9. Djangoプロジェクトの作成」で説明した
Djangoプロジェクト(mainproject)配下のstaticディレクトリを指定しています。
このディレクトリには、各アプリケーションで共通の静的ファイルを配置します。
後術しますが、本投稿では、faviconを配置します。
② STATIC_ROOT
後術するCollectstaticコマンドを実行した際に、
STATICFILES_DIRSで指定した共通の静的ファイルと
各アプリケーション配下のstaticディレクトリの静的ファイルを
収集し、配置するディレクトリを指定します。
BASE_DIRは、作業ディレクトリ(src)のことなので、STATIC_ROOTは、src/staticとなります。
「2-5. docker-compose.ymlの設定」で説明したNGINXの静的ファイルのマウント元と一致します。
("- ./src/static:/static"の./src/staticの箇所)
③ STATIC_URL
後術するDjangoのテンプレートファイルが静的ファイルを読み込むディレクトリ先となります。
「2-5. docker-compose.ymlの設定」で説明したNGINXの静的ファイルのマウント先と一致します。
("- ./src/static:/static"の/staticの箇所)※ 「Django Static Files 」を合わせて見ると理解しやすいと思います。
2-12. Docker コンテナを起動します。
以下のコマンドを実行します。
$ docker-compose up -d[実行結果の確認①(Docker コンテナの状態)]
"docker ps -a"コマンドを実行し、Docker コンテナの状態を確認して下さい。
以下のようにNGINX、uWSGI、PostgreSQLのコンテナが起動しています。$ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f41f7a5b634d nginx "nginx -g 'daemon of…" 10 minutes ago Up 10 minutes 80/tcp, 0.0.0.0:8000->8000/tcp container.nginx c12eee7ac189 django_web "uwsgi --ini /code/m…" 10 minutes ago Up 10 minutes 8001/tcp container.uwsgi 9d68d40adee0 postgres "docker-entrypoint.s…" 27 hours ago Up 27 hours 0.0.0.0:5432->5432/tcp container.postgres2-13. 動作確認
ブラウザを開き、"http://0.0.0.0:8000"にアクセスし、
Djangoのデフォルトのページが表示されることを確認します。
ここまでの手順で、NGINX、uWSGI、PostgreSQLが動作するDocker コンテナの構築が完了しました。
3. Django Webアプリケーションの作成
Djangoで"Hello world."を出力するWebアプリケーションを作成します。
3-1. Djangoアプリケーションの作成
以下のコマンドを実行し、Djangoアプリケーションを作成します。
アプリケーション名は任意です。(ここでは、"app"という名前にしました。)$ docker-compose run --rm web python manage.py startapp app[実行結果の確認①(ファイル構成)]
srcディレクトリ配下にappディレクトリが作成されます。$ tree src src ├── app │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── mainproject │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── settings.cpython-37.pyc │ │ ├── urls.cpython-37.pyc │ │ └── wsgi.cpython-37.pyc │ ├── asgi.py │ ├── django.ini │ ├── settings.py │ ├── urls.py │ ├── uwsgi.log │ └── wsgi.py ├── manage.py └── static 5 directories, 19 files3-2. ビューの作成
"Hello world."を出力するファイルです。
src/app/views.pyを開き、以下の修正を行い保存します。(修正前)
from django.shortcuts import render # Create your views here.(修正後)
from django.http import HttpResponse def index(request): return HttpResponse("Hello world.")3-3. URLの対応付け
作成したビューをブラウザからアクセスするために、 URLの対応付けを行う必要があります。
(1) appディレクトリの配下にurls.pyを新規作成します。
urls.pyを作成することで、URLの対応付けに必要なURLconfが作成されます。
以下の内容を記述します。from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ](2) mainprojectディレクトリの配下のurls.pyを更新します。
ルート(プロジェクト)のURLconfにapp.urlsモジュールの記述を反映させます。
具体的には、
① django.urls.includeのimportを追加
② urlpatternsのリストにinclude('app.urls')を挿入
を行います。
※ 以下の修正前後のファイルは、文頭のコメント部分は省略しています。
※ include()関数は他のURLconfへの参照が可能になります。
修正後の"path('app/', include('app.urls'))"の箇所は、
app/にアクセスがあった場合、app配下のURLconfを参照するということを表します。(修正前)
from django.contrib import admin from django.urls import path urlpatterns = [ path('admin/', admin.site.urls), ](修正後)
from django.contrib import admin from django.urls import include, path urlpatterns = [ path('app/', include('app.urls')), path('admin/', admin.site.urls), ]3-4. 動作確認
ブラウザを開き、"http://0.0.0.0:8000/app/"にアクセスし、
画面に"Hello world."が表示されることを確認します。4. Databaseの設定
DjangoでDatabase(PostgreSQL)を利用するための設定を行います。
※ 私がCafe好きなので、Cafeの情報を例に説明します。4-1. モデルの作成
Webアプリケーションで利用するモデルを最初に定義します。
ここでは、Area、Cafe、Utilityの3つのモデルを作成します。
src/app/models.pyを開き、以下の内容を記述して保存します。(修正前)
from django.db import models # Create your models here.(修正後)
from django.db import models class Area(models.Model): name = models.CharField(max_length=100) create_date = models.DateTimeField('date published') def __str__(self): return self.name; class Cafe(models.Model): area = models.ForeignKey(Area, on_delete=models.CASCADE) name = models.CharField(max_length=100) memo = models.CharField(max_length=400) website = models.URLField() image_path = models.CharField(max_length=400) create_date = models.DateTimeField('date published') def __str__(self): return self.name; class Utility(models.Model): key = models.CharField(max_length=100) value = models.CharField(max_length=100) create_date = models.DateTimeField('date published') def __str__(self): return self.key;[説明]
① Area
Cafeのエリア情報(場所)を管理するマスタです。
② Cafe
Cafeの情報を保持するテーブルです。
※ ForeignKeyを使用してリレーションシップを定義しています。
各Cafeが1つのAreaに関連付けられています。
③ Utility
汎用的なデータを保持するマスタです。
今回は、表示方法の切り替えに利用します。
データの変更で、制御することで、毎回プログラムの変更を行う必要がなくなります。4-2. モデルを有効にする。
(1) モデル有効化の準備
src/mainproject/settings.pyを開き、INSTALLED_APPSに「'app.apps.AppConfig',」を追加します。
後術するmigrateコマンドを実行すると、INSTALLED_APPSに記述されている
アプリケーションに対し、各アプリケーションに必要なテーブルを作成します。
※ app.apps.AppConfigは、src/app/apps.pyのAppConfigクラスを表します。(修正前)
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ](修正後)
INSTALLED_APPS = [ 'app.apps.AppConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ](2) マイグレーションファイルの作成
以下のコマンドを実行し、モデルの変更内容が記述されたマイグレーションファイルを作成します。$ docker-compose run --rm web python manage.py makemigrations app下記のようなメッセージが出力され、src/app/migrations/0001_initial.pyが作成されます。
※ src/app/migrations/0001_initial.pyは、手動で調整することも可能です。$ docker-compose run --rm web python manage.py makemigrations app Starting container.postgres ... done Migrations for 'app': app/migrations/0001_initial.py - Create model Area - Create model Utility - Create model Cafe(3) テーブルの作成
マイグレーションファイルを元にモデルのテーブルを作成する。
以下のコマンドを実行します。$ docker-compose run --rm web python manage.py migrate下記のようなメッセージが出力され、テーブルが作成されます。
$ docker-compose run --rm web python manage.py migrate Starting container.postgres ... done Operations to perform: Apply all migrations: admin, app, auth, contenttypes, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying app.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying sessions.0001_initial... OK(4) テーブルの確認
作成したテーブルをDBeaverというSQLクライアントツールを使って確認します。
DBeaverを使ったことがない方は、「DBeaverのインストールとPostgreSQLへの接続」を参考にして下さい。① DBeaverを起動します。
② 新しい接続を作成します。
以下の画面で、PostgreSQLに接続するDatabase、User、Password、Portの入力を行います。
※ 「2-11. ALLOWED_HOSTの設定、DBへの接続設定を行う。」の(2)で設定した内容を入力します。
③ テーブルの確認
下記の通り、app_area、app_cafe、app_utilityテーブルが作成されています。
(5) データの登録
下記のようなSQLを実行し、初期データを登録して下さい。insert into app_area (name, create_date) values ('清澄白河', now()); insert into app_area (name, create_date) values ('神保町', now()); insert into app_area (name, create_date) values ('代々木公園', now()); insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('ブルーボトルコーヒー', 'カフェモカはここが一番美味しい。', 'https://bluebottlecoffee.jp/', 'bluebottlecoffee_IMG.jpg', now(), '1'); insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('iki ESPRESSO', 'オセアニアンスタイルのカフェ。フードもコーヒーも美味しい。', 'https://www.ikiespresso.com/', 'ikiespresso_IMG.jpg', now(), '1'); insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('GLITCH COFFEE', 'コーヒーが好きになったきっかけのカフェ。一番好きです。', 'https://glitchcoffee.com/', 'glitchcoffee_IMG.jpg', now(), '2'); insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('DIXANS', 'とてもオシャレなカフェ。デザートが絶品です。', 'http://www.dixans.jp/', 'dixans_IMG.jpg', now(), '2'); insert into app_cafe (name, memo, website, image_path, create_date, area_id) values ('Fuglen Tokyo', 'コーヒーがとても美味しいです。代々木公園で遊んだ時は必ず寄ります。', 'https://fuglencoffee.jp/', 'fuglencoffee_IMG.jpg', now(), '3'); commit;5. 画面の作成
Databaseに保持した情報を出力するWebアプリケーションを作成します。
簡単な一覧画面と詳細画面を作成します。5-1. 一覧画面の作成
(1) src/appの配下に"templates"ディレクトリを作成し、
"templates"ディレクトリ配下に""list.html"を作成します。$ mkdir -p src/app/templates $ touch src/app/templates/list.html(2) src/app/templates/list.htmlを開き、以下の内容で保存します。
{% load static %} <html lang="ja"> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="{% static 'app_css/style.css' %}"> <link rel="shortcut icon" href="{% static 'pj_image/armx8-1ibhc-001.ico' %}" type="image/vnd.microsoft.icon"> </head> <body> <table border="1"> <thead> <tr> <th>カフェ</th> <th>特徴</th> <th>エリア</th> <th>サイト</th> </tr> </thead> <tbody> {% for cafe in cafe_list %} <tr> <td><a href="{% url 'app:detail' cafe.id %}">{{ cafe.name }}</a></td> <td>{{ cafe.memo }}</td> <td>{{ cafe.area.name }}</td> <td><a href="{{ cafe.website }}">{{ cafe.website }}</a></td> </tr> {% endfor %} </tbody> </table> </body> </html>5-2. 詳細画面の作成
(1) src/app/templatesの配下に"detail.html"を作成します。
$ touch src/app/templates/detail.html(2) src/app/templates/detail.htmlを開き、以下の内容で保存します。
{% load static %} <html lang="ja"> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="{% static 'app_css/style.css' %}"> <link rel="shortcut icon" href="{% static 'pj_image/armx8-1ibhc-001.ico' %}" type="image/vnd.microsoft.icon"> </head> <body> <h1>{{ cafe.name }}</h1> <h2><img src="{% static 'app_image/' %}{{ cafe.image_path }}" alt="{{ cafe.name }}のイメージ" title="{{ cafe.name }}のイメージ" width="384" height="384"></h2> <h2>特徴:{{ cafe.memo }}</h2> <h2>エリア:{{ cafe.area.name }}</h2> <h2>サイト:<a href="{{ cafe.website }}">{{ cafe.website }}</a></h2> <a href="{% url 'app:list' %}">戻る</a> </body> </html>5-3. ビューの編集
3-2.で作成した"Hello world."を出力するファイルを編集します。
src/app/views.pyを開き、以下の修正を行い保存します。(修正前)
from django.http import HttpResponse def index(request): return HttpResponse("Hello world.")(修正後)
from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from .models import Area, Cafe, Utility class IndexView(generic.ListView): template_name = 'list.html' context_object_name = 'cafe_list' def get_queryset(self): """Return the last five published records.""" return Cafe.objects.order_by('name')[:5] class DetailView(generic.DetailView): model = Cafe template_name = 'detail.html'5-4. URLの対応付け
3-3.で作成したURLを対応付けするファイルを編集します。
src/app/urls.pyを開き、以下の修正を行い保存します。
※ path()を追加し、新しいviewを app.urls モジュールと結びつけます。(修正前)
from django.urls import path from . import views urlpatterns = [ path('', views.index, name='index'), ](修正後)
from django.urls import path from django.contrib.auth.views import LoginView from . import views app_name = 'app' urlpatterns = [ path('', views.IndexView.as_view(), name='list'), path('<int:pk>/', views.DetailView.as_view(), name='detail'), ]5-5. 静的ファイルの設置
(1) appアプリケーションの静的ファイルのディレクトリを作成します。
$ mkdir -p src/app/static/app_css $ mkdir -p src/app/static/app_image $ mkdir -p src/app/static/app_js(2) appアプリケーションのcssを作成します。
src/app/static/app_css/style.cssを新規に作成し、以下の内容を記述します。table { background-color: #ffffff; border-collapse: collapse; /* セルの線を重ねる */ } th { background-color: #000080; color: #ffffff; /* 文字色指定 */ } th, td { border: solid 1px; /* 枠線指定 */ padding: 10px; /* 余白指定 */ }(3) DBに登録したCafeの画像ファイルをsrc/app/static/app_imageに配置します。
※ 画像ファイルは何でも良いので用意して下さい。
無くても、画像ファイルが表示されないだけなのでスキップしても問題ありません。
(4) mainprojectプロジェクトの静的ファイルのディレクトリを作成します。$ mkdir -p src/mainproject/static/pj_css $ mkdir -p src/mainproject/static/pj_image $ mkdir -p src/mainproject/static/pj_js
(5) faviconをsrc/mainproject/static/pj_imageに配置します。
※ faviconは何でも良いので用意して下さい。
無くても、faviconが表示されないだけなのでスキップしても問題ありません。
(6) 静的ファイルの収集を行う。
以下のコマンドを実行します。$ docker-compose run web ./manage.py collectstatic下記のようなメッセージが出力され、
「2-11. ALLOWED_HOST、DB及び静的ファイルの設定を行う。」で説明した通り、
STATICFILES_DIRSで指定した共通の静的ファイルと
各アプリケーション配下のstaticディレクトリの静的ファイルを収集します。
→ src/app/static/app_css/style.cssと
src/app/static/app_imageに配置した画像ファイルと
src/mainproject/static/pj_imageのfaviconが
src/staticにコピーされます。$ docker-compose run web ./manage.py collectstatic Starting container.postgres ... done 137 static files copied to '/code/static'.5-6. 動作確認
ブラウザを開き、"http://0.0.0.0:8000/app/"にアクセスし、
一覧画面と詳細画面が下記のように表示されていることを確認します。
(1) 一覧画面
NGINX、uWSGI、PostgreSQLが動作するDocker コンテナで、
DjangoのWebアプリケーションを動作確認するまでの手順が完了です。
Webアプリケーションを作る上で、基本的なことは紹介できたと思いますので、
本投稿が役に立てば幸いです。6. 参考情報(Django Admin)
本投稿では、DBeaverを使ってデータの登録を行いましたが、Django Adminという
Djangoに用意されている管理画面を使ってデータを登録することも可能です。6-1. 管理ユーザの作成
以下のコマンドを実行します。
$ docker-compose run --rm web python manage.py createsuperuserコマンドプロンプトで以下のように任意の情報を入力します。
Username:admin
Email address:admin@example.com
Password:admin$ docker-compose run --rm web python manage.py createsuperuser Starting container.postgres ... done Username (leave blank to use 'root'): admin Email address: admin@example.com Password: Password (again): The password is too similar to the username. This password is too short. It must contain at least 8 characters. This password is too common. Bypass password validation and create user anyway? [y/N]: y Superuser created successfully.6-2. モデルをDjango Adminに登録
「4-1. モデルの作成」で作成したモデルをDjango Adminで編集できるようにします。
src/app/admin.pyを開き、以下の修正を行い保存します。
(修正前)
from django.contrib import admin # Register your models here.(修正後)
from django.contrib import admin from .models import Area from .models import Cafe from .models import Utility admin.site.register(Area) admin.site.register(Cafe) admin.site.register(Utility)6-3. 動作確認
ブラウザを開き、"http://0.0.0.0:8000/admin/"にアクセスします。
Django Adminのログイン画面が開くので、
「6-1. 管理ユーザの作成」で作成した管理ユーザでログインして下さい。
ログイン後、Area、Cafe、Utilityのモデルの参照、追加、更新が出来ることを確認して下さい。7. 参考情報(Bootstrap4)
Bootstrap4を使用する方法を説明します。
「2-3. requirements.txtの設定」で説明した通り、
DjangoでBootstrap4を利用するためのパッケージをインストールしていますので、
以下の手順を行うことで、Bootstrap4のcss、Javascriptの利用が可能です。7-1. django-bootstrap4の設定
src/mainproject/settings.pyを開き、以下の修正を行い保存します。
(1) INSTALLED_APPSにdjango-bootstrap4を追加します。
(修正前)
INSTALLED_APPS = [ 'app.apps.AppConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ](修正後)
INSTALLED_APPS = [ 'app.apps.AppConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'bootstrap4', ](2) TEMPLATESのbuiltinsにbootstrap4.templatetags.bootstrap4を追加します。
※ これを追加することで、各htmlに{% load bootstrap4 %}を記述する必要がなくなります。(修正前)
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ](修正後)
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], 'builtins':[ 'bootstrap4.templatetags.bootstrap4', ], }, }, ]7-2. Utilityテーブルに制御フラグを追加(おまけ①)
UtilityテーブルにBootstrap4のcssを利用するかしないかを制御するデータを追加します。
このデータを使って、一覧画面のhtmlでBootstrap4のcssを読み込むか読み込まないか判別します。
* 使用しない場合、value列の値を”1”以外に設定します。insert into app_utility (key, value, create_date) values ('bootstrap_flg', '1', now()); commit;7-3. 一覧画面の修正
(1) src/mainproject/settings.pyを開き、以下の修正を行い保存します。(おまけ②)
get_context_dataを使って、「7-2. Utilityテーブルに制御フラグを追加」で
追加したデータをhtmlで利用できるようにしています。(修正前)
from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from .models import Area, Cafe, Utility class IndexView(generic.ListView): template_name = 'list.html' context_object_name = 'cafe_list' def get_queryset(self): """Return the last five published records.""" return Cafe.objects.order_by('name')[:5] class DetailView(generic.DetailView): model = Cafe template_name = 'detail.html'(修正後)
from django.http import HttpResponse, HttpResponseRedirect from django.shortcuts import get_object_or_404, render from django.urls import reverse from django.views import generic from .models import Area, Cafe, Utility class IndexView(generic.ListView): template_name = 'list.html' context_object_name = 'cafe_list' def get_queryset(self): """Return the last five published records.""" return Cafe.objects.order_by('name')[:5] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["bootstrap_flg"] = Utility.objects.all().filter(key='bootstrap_flg').first() return context class DetailView(generic.DetailView): model = Cafe template_name = 'detail.html'(2) src/app/templates/list.htmlを開き、以下の修正を行い保存します。
bootstrap_flg.value == "1"の時に、{% bootstrap_css %}を読み込むように記述しています。
今回、おまけ①②をすることで、テーブルのデータによって、
Bootstrap4のcssを読み込むか制御できるように対応しましたが、
単純に使用したい場合は、おまけ①②は飛ばし、{% bootstrap_css %}を
htmlに追加するだけで使用が可能です。
(修正前){% load static %} <html lang="ja"> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="{% static 'app_css/style.css' %}"> <link rel="shortcut icon" href="{% static 'pj_image/armx8-1ibhc-001.ico' %}" type="image/vnd.microsoft.icon"> </head> <body> <table border="1"> <thead> <tr> <th>カフェ</th> <th>特徴</th> <th>エリア</th> <th>サイト</th> </tr> </thead> <tbody> {% for cafe in cafe_list %} <tr> <td><a href="{% url 'app:detail' cafe.id %}">{{ cafe.name }}</a></td> <td>{{ cafe.memo }}</td> <td>{{ cafe.area.name }}</td> <td><a href="{{ cafe.website }}">{{ cafe.website }}</a></td> </tr> {% endfor %} </tbody> </table> </body> </html>(修正後)
{% load static %} <html lang="ja"> <head> <meta charset="utf-8"> <link rel="stylesheet" type="text/css" href="{% static 'app_css/style.css' %}"> {% if bootstrap_flg.value == "1" %} {% bootstrap_css %} {% endif %} <link rel="shortcut icon" href="{% static 'pj_image/armx8-1ibhc-001.ico' %}" type="image/vnd.microsoft.icon"> </head> <body> <table border="1"> <thead> <tr> <th>カフェ</th> <th>特徴</th> <th>エリア</th> <th>サイト</th> </tr> </thead> <tbody> {% for cafe in cafe_list %} <tr> <td><a href="{% url 'app:detail' cafe.id %}">{{ cafe.name }}</a></td> <td>{{ cafe.memo }}</td> <td>{{ cafe.area.name }}</td> <td><a href="{{ cafe.website }}">{{ cafe.website }}</a></td> </tr> {% endfor %} </tbody> </table> </body> </html>7-4. 動作確認
ブラウザを開き、"http://0.0.0.0:8000/app/"にアクセスし、
一覧画面にBootstrap4のcssが使用されていることを確認します。参考元
Compose file version 3 reference
Docker Composeチュートリアル: herokuのPostgresをローカルにさくっとコピーする
Supported tags and respective Dockerfile links
はじめての Django アプリ作成、その 1
SQLのSELECT文を、DjangoのQuerySet APIで書いてみた
Djangoにおけるクラスベース汎用ビューの入門と使い方サンプル
- 投稿日:2020-03-22T22:23:51+09:00
Vagrant使ってdocker+minikubeの環境作ろう
はじめに
今回は15Stepで習得 Dockerから入るKubernetes コンテナ開発からK8s本番運用までを
参考にWindows10の環境下にVirtualBox+Vagrantを構築して、さらにその上にUbuntsを起動させ、
そのUbuntsの上にdocker+Kubernetes(正確にはminikube)の実行環境を構築。なんでこんなに複雑かっていうと、書籍によると
・windowsでもMacでも動作するVagrantとVirtualBoxの仮想マシン上でLinuxサーバを動作させ
そこの上でdockerとminikubeを動かすので環境がキチンと揃う
・仮想マシン上のメモリ使用が少なく、安定する
・各種ソフトウェアをWindows上、Mac上にインストールしないため、PCのソフトウェア環境をクリーンに保てる
…だそうだ。なるほど、これはナイスですね。確かに、、、この環境だと、windows上でdocker+minikubeしたいなら
ソフトウェア 概要 Docker Toolbox Dockerの実行環境 VirtualBox Dockerを実行させるために必要 Vagrant VirtualBoxのフロントエンドとして使用 chocolatey win版のパッケージマネージャー。curlとkubectlをインストールするときに使用 kubectl コントローラ Minikube minikubeの実行環境 が、
ソフトウェア 概要 VirtualBox Dockerを実行させるために必要 Vagrant VirtualBoxのフロントエンドとして使用 git Docker+Minikubeを構築するためのVagrantファイルを入手するために使用 で済むのでPC環境をクリーンに保てるという理屈はよくわかる。
動作環境
・Windows10 Home(64bit)
手順(概要)
- VirtualBoxのインストール
- Vagrantのインストール
- Git for Windowsのインストール
- Vagrantファイル(docker+minikube)の入手&実行
- 動作確認
手順(詳細)
1. VirtualBoxのインストール
実際は前の記事でDockerToolBoxインストール時についでインストールされたのですが
VirtualBoxのバージョンアップしたときにインストールしなおした感じに名たのでその様子をUPします。リンク先では自動的にダウンロードが始まる。ダウンロードが完了したらexeファイルを起動
2. Vagrantインストール
ダウンロードサイト(https://www.vagrantup.com/downloads.html)へ
アクセスして、win10(64bit)のインストーラ入手
ここで突然windows Defenderが反応するが、落ち着いて[実行]
…で、インストール完了
3.Git for Windowsのインストール
ダウンロードサイト(https://git-scm.com/download/win)へアクセスしてダウンロード。
checkout as-is, commit as-is を選択して、[Next]クリック
…で、インストール完了
4. Vagrantファイル(docker+minikube)の実行
windows検索窓から"git"入力して、"git for windows"起動
githubからdocker+minikubeのvagrantファイルを入手
※このURLは書籍購入した人に公開している感じなのでさすがに隠した。。。
Vagrantファイルの実行。docker&minikubeのインストールはこれだけ。さすがvagrant。
$ vagrant upプロンプトが戻ってきたら、実行環境(=docker+minikubeのインストールされたUbunts)へログイン
$ vagrant ssh5.動作確認
Dockerのバージョンを確認してみる
$ docker versionminikubeを起動する。
sudo minikube startminikube上のノードの状態とpodの様子を確認。
minikubeという名前のノードが稼働しており、
minikubeにまつわる様々なpodが動作していることが確認できる。$ kubectl get node ←ノードの状態確認 $ kubectl get pod --all-namespeces ←ポッドの状態確認ここまでで、minikubeが動くことが確認できたので、ここからじっくりと
dockerのminikubeの動作がどんなもんか本を読みながら試していこうと思う。
- 投稿日:2020-03-22T21:49:01+09:00
DockerでDevice or resource busyと表示された時の対処法
問題が生じた状況
Docker Volumeを使ってる際に主に出ると思われる問題です。
自分はfirebaseのfirebase-tools.jsonというファイルをマウントする際に出た問題です。解決策
Docker Volumeでファイル自体をマウントするのではなくディレクトリをマウントするべきだった模様です。
かなり基礎的なミスな気がしますが、意外と検索してもこれを指摘する記事は出ませんでした。修正した方法
docker-compose.ymlvolumes: - - "./secrets/firebase-tools.json:~/.config/configstore/firebase-tools.json" + - "./secrets:/root/.config/configstore/"自分の状況では解決しなかった方法
とりあえず片っ端から試したけどダメだったコマンドたち
もし上記の解決策がダメだったら試す価値はあるかもしれません。chown
chown -R $USER /path
だめだったlsof
lsof +D /path
これもダメだったdocker volume prune
docker volume prune
これも効果なし
- 投稿日:2020-03-22T20:12:56+09:00
dockerの実行環境を作ろう
はじめに
今回は15Stepで習得 Dockerから入るKubernetes コンテナ開発からK8s本番運用までを
参考にWindows10の環境下にdockerの実行環境を構築。動作環境
・Windows10 Home(64bit)
手順(概要)
大雑把に言うとWindowsの場合、Docker Toolboxをインストールして完了。
以後、Dockerの起動をする際は”Docker Quickstart Terminal”を実行したらOK。
複数ターミナルを起動したいなと思ったら”Docker Quickstart Terminal”を何個も起動すればOK。
※複数ターミナルを起動した様子を最後に掲載手順(詳細)
1.Docker Toolboxのインストール
Docker Toolboxの公式サイトより"Toolbox Release"クリックしてダウンロードサイト(github)へアクセス
https://docs.docker.com/toolbox/toolbox_install_windows/
DockerToolbox-19.03.1.exeをクリックしてダウンロード。
"Git for Windows"をチェックして[Next]クリック
※書籍によると"Git for Windows"はチェック不要と書かれていたけど、
この後の"Docker QuickStart Terminal"の実行でうまくいかなかったので
あえて"Git for Windows"のチェックを入れた
[Finish]クリック。これでDocker Toolboxのインストール完了!
2.Docker Toolboxの動作確認
インストール完了したときに現れた"Docker Quickstart Terminal"起動
動作確認ってことでバージョン確認を実行
$ docker versionオマケ:”Docker Quickstart Terminal”を複数起動した様子
ためしに、1つめの”Docker Quickstart Terminal”からcentos:7をプルして、レポジトリの様子を表示した後に
2つめの”Docker Quickstart Terminal”を起動してからレポジトリの様子を表示したところ
1つめのターミナルでプルしたcentosが取れていることから、複数のターミナルで1つのDocker環境を
共有できていることがわかる。
- 投稿日:2020-03-22T17:20:36+09:00
docker のネットワーク (docker0) 設定変更 (icc 変更編)
本稿では、icc (Inter-Container Communication) を誤って設定してしまった場合に、icc 設定を削除し、ネットワーク設定を変更する方法を記載します。
以前、「docker のネットワーク (docker0) 設定変更 - Qiita」で docker ネットワーク(範囲)変更の方法を書きましたが、Raspbian (buster) の
docker-ce
では動作が異なり、変更ができませんでした (最悪、systemd による dockerd の起動が失敗し、立ち上がらなくなります)。調べたところ、これは Raspbian によるものではなく、テストで一時的に icc を設定していたものが残っていたためでした。
しかし、docker で icc=false に設定しても systemd 側にネットワーク設定が残っており、これを削除する必要がありました。現象
systemctl start docker
で起動させようとすると以下のメッセージを出力し、エラー終了する。Job for docker.service failed because the control process exited with error code. See “systemctl status docker.service” and “journalctl -xe” for details.
systemctl status docker.service
とjournalctl -xe
で参照しても、よくわからず、システムログを参照しました。
/var/log/daemon.log
に以下のような記録が出ていました。/var/log/daemon.logfailed to start daemon: Error initializing network controller: Error creating default "bridge" network: Poo l overlaps with other one on this address space各プラットフォームのログの場所とファイル名は「Read the logs - Configure and troubleshoot the Docker daemon | Docker Documentation] に記載があります。
1. dockerd の停止
dockerd を停止します。(念のため、container は事前にすべて停止してください)
systemctl stop docker
2. link デバイスの削除
docker_gwbridge
インタフェース(ブリッジ)を削除します。
- ブリッジインタフェースを確認します。
ip link show
- ブリッジインタフェースを削除します。
ip link del docker_gwbridge3. systemd の kv ファイルを削除と systemd のリロード
systemd に残っている Key-Value store の db を削除して、systemd のリロードを行います。
rm /var/lib/docker/network/files/local-kv.db systemctl daemon-reload4. dockerd の起動
dockerd の stop から 60秒以上経過していることを確認してください1。
必要に応じて、icc や bip などを設定します。
/etc/docker/daemon.json{ "icc": false, "bip": "172.17.1.254/24" }dockerd を起動します。
systemctl start docker
docker_gwbridge
が作成されていないことを確認します。ip addrまたは
ip link showdocker 側のネットワークデバイスを確認します。
docker network lsまたは
docker network inspect bridge終了。
さいごに
/var/lib/docker/network/files/local-kv.db
を削除するのが正しいオペレーションなのかどうか、わかっていません。- docker のドキュメントを調べてもなかなか正解が見当たらず、systemd 側を調べて KV store に思い当たり、削除を試みました。
- systemd の dockerd のインターバル設定に気づかず、すぐに start させてしまうと設定が正しくてもエラーになり、これで時間を費やしました。
dockerd の起動インターバルが 60s に設定されているため、すぐに起動すると、設定が正しくてもエラーになります。 ↩
- 投稿日:2020-03-22T17:15:58+09:00
DockerのApacheで動くCakePHP3の開発環境を用意する
ApacheでCakePHPを動かす必要が生じたので、Dockerで用意してみました。
DockerでApache、MySQLを用意し、CakePHP3のチュートリアルを動かせる状況まで持っていきます。他にsslの環境、xdebugを動かす環境もまとめてます。差し当たってこちらの記事を参考にさせて貰いました。
開発環境
- MacOS 10.15.3
- Docker 2.2.0.0
gitHub上に用意しましたので、ZIPファイルをダウンロードして、適当なところでフォルダを置いてターミナルからそのフォルダに入って下さい。以下のコマンドを実行すればOKです。
docker-compose buildイメージのビルドが正常に出来たら、以下のコマンドで実行します。
docker-compose up -dcomposerを用意する
composerをダウンロードして、上記のフォルダに置いて下さい。
僕のmacの環境ではデフォルトで入っているphpでは実行時にエラーが起きるようなので、brewからphpをインストールして、それがデフォルトになるようにexportを設定する必要がありました。brew install php@7.3 echo 'export PATH="/usr/local/opt/php@7.3/bin:$PATH"' >> ~/.zshrc echo 'export PATH="/usr/local/opt/php@7.3/sbin:$PATH"' >> ~/.zshrcチュートリアルに沿ってcmsプロジェクトを作る
チュートリアルにあるプロジェクトを用意するコマンドで^がありますが、これがあると動かないので省きました。
php composer.phar create-project --prefer-dist cakephp/app:3.8 cmsdbの設定をする
作成したプロジェクトとDockerのMySQLを紐付けます。
プロジェクトの設定ファイルを編集します。cms/config/app.php'Datasources' => [ 'default' => [ 'className' => Connection::class, 'driver' => Mysql::class, 'persistent' => false, 'host' => 'db', #編集 //'port' => 'non_standard_port_number', 'username' => 'my_app', 'password' => 'secret', 'database' => 'cake_cms',#編集(おまけ)ドメインの設定を変える
必須ではないですが、プロジェクトを本番環境ではドキュメントルートで呼び出したいと思いますので以下の設定を用意します。
docker/apache/site.conf<VirtualHost *:80> DocumentRoot /var/www/html/cms/webroot/ <Directory /var/www/html/> Options FollowSymLinks AllowOverride All Require all granted </Directory> </VirtualHost>docker/Dockerfile に以下を追加しておく
※gitHubにあげているやつは予め設定済みです。COPY ./docker/apache/site.conf /etc/apache2/sites-available/000-default.conf以上でlocalhostを呼び出せば以下が表示されるかと思います。
(おまけ)sslの設定
ご時世柄、httpsでの運用は必須ですので、開発時からsslでやりたいと思うこともあるはず。
数分でできる!mkcertでローカル環境へのSSL証明書設定
mkcertを使って、使いたいドメイン名を用意して、出来たファイルを
docker/apache/SSL/certsフォルダ配下に置いてください。
そしてその設定をdocker/apache/SSL/ssl.confファイルに設定を追記します。docker/apache/SSL/ssl.conf<VirtualHost *:443> DocumentRoot "/var/www/html/cms/webroot/" ServerName hoge.hoge.com #ErrorLog logs/ssl_error_log #TransferLog logs/ssl_access_log LogLevel warn SSLEngine on SSLProtocol all -SSLv3 SSLCertificateFile /var/www/html/docker/apache/SSL/certs/xxxxx.pem SSLCertificateKeyFile /var/www/html/docker/apache/SSL/certs/xxxxx-key.pem </VirtualHost>hoge.hoge.comやxxxxのところを書き換えてください。
docker/Dockerfile に以下を追加する。
COPY ./docker/apache/SSL/ssl.conf /etc/apache2/sites-available/ssl.conf RUN a2enmod ssl RUN a2ensite sslApacheにsslの設定を追加します。
これらも既に記述してありますので試したい場合はどうぞ。
(おまけ)xdebugの設定
こちらを参考にして動くように設定してあるので動くはずです。
- 投稿日:2020-03-22T17:08:36+09:00
Dockerコンテナ起動時に、フロント側コンテンツを制御する一事例
概要
1つのDockerイメージとして提供する、Java+React構成のアプリケーションについて、コンテナ起動時にビルド済みのReactコードを制御する方法を、具体例をもとに考えていきます。
はじめに
Twelve-Factor Appの「III. 設定」で述べられているように、アプリケーションの設定は環境変数に格納し、コードからは分離することが望ましいとされています。
つまり、Dockerコンテナとして動作するアプリケーションであれば、develop用、staging用、production用と各環境ごとで別のDockerイメージを用意するべきではなく、イメージ自体は1つにして、各環境ごとの差分はコンテナ実行時に与える環境変数で吸収するべきです。しかし、コンテナ起動するアプリケーションに、環境変数を反映させることが容易でない場合も多々あります。
例えば、ビルド実施済みのフロント側コンテンツに対して、環境変数での制御を行う方法は、グーグルで検索を行ってもほとんどヒットしません。そこで、本記事では、どうすればビルド済みのフロント側コンテンツに対して環境変数を反映させることができるかについて、Java+React構成のアプリケーションを一事例として説明していきます。
環境変数を反映させる戦略
コンテナ起動時にフロント側コンテンツに環境変数を反映させるために、Javaの起動時に読まれる環境変数を、HTMLのmetaタグ経由で、フロント資材に渡す、という戦略をとりました。
また、HTMLがレンダリングされるタイミングで、metaタグの内容が環境変数の値に書き換わるようにするために、Thymeleafを利用しました。構成
ここから説明で使用するコードは、以下のような
thymeleaf/my-app
配下でフロント側のコードを管理し、thymeleaf/thymeleaf
配下でバック側のコードを管理する構成です。└── thymeleaf ├── Dockerfile ├── my-app │ ├── README.md │ ├── package.json │ ├── public │ │ └── .... │ ├── src │ │ └── .... │ └── yarn.lock └── thymeleaf ├── pom.xml └── src ├── main │ ├── java │ │ └── .... │ └── resources │ └── .... └── test └── java └── ....コードの全量は以下に配置しています。
https://github.com/nannany/thymeleafここからどのようにDockerイメージを作成していくか示すために、まずDockerfileから説明していきます。
Dockerfile
Dockerfileは全体は以下のようです。
FROM node:13 AS front-build WORKDIR /work1 ADD my-app . RUN npx yarn && npx yarn build FROM maven:3.6 AS back-build WORKDIR /work2 ADD thymeleaf . RUN mkdir -p /work2/src/main/resources/static COPY --from=front-build /work1/build/ /work2/src/main/resources/static/ RUN mkdir -p /work2/src/main/resources/templates && \ sed -e "s!<meta name=\"from-environment\" content=\"\"/>!<meta name=\"from-environment\" th:content=\${@environment.getProperty('thymeleaf.test')}>!" \ /work2/src/main/resources/static/index.html > /work2/src/main/resources/templates/index.html && \ rm /work2/src/main/resources/static/index.html && \ mvn package FROM adoptopenjdk/openjdk11:jdk-11.0.6_10-alpine COPY --from=back-build /work2/target/thymeleaf-0.0.1-SNAPSHOT.jar . ENTRYPOINT ["java", "-jar", "thymeleaf-0.0.1-SNAPSHOT.jar"]このDockerfileは、以下の3ブロックに分かれています。
- Reactアプリケーションのビルド
- Javaアプリケーションのビルド
- 実行するイメージの作成
Reactアプリケーションのビルド
最初のブロックでは、Reactアプリケーションのビルドを行っています。
FROM node:13 AS front-build WORKDIR /work1 ADD my-app . RUN npx yarn && npx yarn build特記することはありません。
Javaアプリケーションのビルド
次のブロックでは、Javaアプリケーションのビルドを行っています。
FROM maven:3.6 AS back-build WORKDIR /work2 ADD thymeleaf . RUN mkdir -p /work2/src/main/resources/static COPY --from=front-build /work1/build/ /work2/src/main/resources/static/ RUN mkdir -p /work2/src/main/resources/templates && \ sed -e "s!<meta name=\"from-environment\" content=\"\"/>!<meta name=\"from-environment\" th:content=\${@environment.getProperty('thymeleaf.test')}>!" \ /work2/src/main/resources/static/index.html > /work2/src/main/resources/templates/index.html && \ rm /work2/src/main/resources/static/index.html && \ mvn packageReactアプリケーションのビルド成果物を
/work2/src/main/resources/static/
に配置して、/work2/src/main/resources/static/index.html
の<meta name="from-environment" content=""/>という記述を、
<meta name="from-environment" th:content=${@environment.getProperty('thymeleaf.test')}>に書き換えて、Thymeleafにレンダリングさせるため
/work2/src/main/resources/templates/index.html
に配置しています。なぜ元のindex.htmlに置換後のように書いていないかというと、
yarn build
時に、Thymeleaf記法で書いている部分のパースに失敗するからです。(おそらくwebpackが吐いているエラー)そのあとで
mvn package
をたたいてjarファイルを生成しています。実行イメージの作成
最後のブロックで、コンテナ起動時に
java -jar 作ったjarファイル
のプロセスを立ち上げるイメージを作成しています。FROM adoptopenjdk/openjdk11:jdk-11.0.6_10-alpine COPY --from=back-build /work2/target/thymeleaf-0.0.1-SNAPSHOT.jar . ENTRYPOINT ["java", "-jar", "thymeleaf-0.0.1-SNAPSHOT.jar"]特記することはありません。
React
index.html
のhead内に、Dockerfile内で置換していたmetaタグを記述します。<head> .... <meta name="from-environment" content=""/> .... </head>Dockerイメージに固める前の開発段階では、
.env
(もしくは.env.local
など)を使って環境変数を指定します。
.env
の記述は以下のようです。REACT_APP_LOCAL_SUBSTITUTE=localmetaタグや
.env
から値を取得する部分は、以下のようです。getMetaData() { const fromEnvironment = document.getElementsByName( "from-environment" )[0].content; return fromEnvironment === '' ? process.env.REACT_APP_LOCAL_SUBSTITUTE : fromEnvironment; }
from-environment
というnameのmetaタグのcontentが空文字であれば、.env
内の値を使い、それ以外の場合はmetaタグのcontentを使います。
このようにした理由としては、npm run start
で、Reactの動作のみ確かめたい場合にも、コードを書き換えたりせずに対応できるようにしたかったためです。Java
バックエンド側に関して特記することはないです。
記述も少ないです。
https://github.com/nannany/thymeleaf/tree/master/thymeleaf動かしてみる
上記のDockerfileをもとに作成したイメージについて、環境変数を与えてコンテナ起動してみます。(
test:dev
という名前でイメージを作りました)
thymeleaf_test
という環境変数に応じて、フロント側コンテンツが変更されるようになっています。まず、何も環境変数を与えないで動かしてみると、以下のように表示されます。
docker run --rm -p 80:8080 test:dev
次に、
thymeleaf_test
にtest
という値を入れて、コンテナ起動してみます。
docker run --rm -p 80:8080 test:dev
反映が確認できました。
おわりに
そもそもこのような構成で、1つのDockerイメージにしてデプロイしようと考えるのが間違っているという説はあります。
フロントとバックは別のイメージにして、フロント側にNext.jsなどを導入すれば容易に環境変数で制御できるといった記事を見たので、きっとそういう構成にするのがいいのでしょう。参考
https://itnext.io/frontend-dockerized-build-artifacts-with-nextjs-9463f3da3362
https://qiita.com/shibukawa/items/6a3b4d4b0cbd13041e53
https://12factor.net/ja/config
- 投稿日:2020-03-22T16:57:21+09:00
NSQ環境をDockerで構築する(『Go言語によるWebアプリケーション開発』第5章)
『Go言語によるWebアプリケーション開発』(Mat Ryer著、鵜飼文敏監訳、牧野聡訳; O'Reilly Japan)の「第5章 分散システムと柔軟なデータの処理」ではNSQというメッセージキューソフトウェアを利用して、Twitterのツイートを分析します。同書内ではNSQの構築をローカルマシンに直接インストールしているのですが、Dockerを利用して構築することも可能です。本記事では、Dockerを用いたNSQの構築手順について、簡単にまとめておきます。
前提
動作確認を行った環境は次の通りです。
- Windows 10 Home
- Docker ToolBox
また環境変数
DOCKER_HOST
は次の通りに設定されています。DOCKER_HOST=tcp://192.168.99.100:2376手順
まずはNSQのイメージを取得します。
docker pull nsqio/nsq次に
nsqlookupd
を起動します。ここではコンテナ名をgo-webapp-nsqlookupd
としています。docker run -d ^ --name go-webapp-nsqlookupd ^ -p 4160:4160 -p 4161:4161 ^ nsqio/nsq /nsqlookupd最後に
nsqd
を起動させれば完了です(コンテナ名はgo-webapp-nsqd
)。docker run -d ^ --name go-webapp-nsqd ^ -p 4150:4150 -p 4151:4151 ^ nsqio/nsq /nsqd ^ --broadcast-address=192.168.99.100 ^ --lookupd-tcp-address=192.168.99.100:4160わかりづらいのは
nsqlookupd
とnsqd
で別のコンテナを起動する必要があるということ。また--broadcast-address
や--lookupd-tcp-address
に指定する値もわかりづらいのですが、おおよそ上記の通りに設定すればOKです。おまけ
同書では
nsq_tail
を利用しているのですが、Dockerを利用している場合はnsq_tail
用にコンテナを作成します。たとえばvotes
というトピックを対象にする場合、以下のコマンドを実行します。docker run --rm nsqio/nsq /nsq_tail ^ --lookupd-http-address=192.168.99.100:4161 ^ -topic votes参考
- 投稿日:2020-03-22T15:58:46+09:00
Dockerのマルチステージビルドについて
マルチステージビルドとは?[1]
With multi-stage builds, you use multiple
FROM
statements in your Dockerfile. EachFROM
instruction can use a different base, and each of them begins a new stage of the build. You can selectively copy artifacts from one stage to another, leaving behind everything you don’t want in the final image.簡単にいうと,ひとつの Dockerfile で複数のビルドができ,各ビルドの段階で必要な部分だけコピーして(残りは破棄される),最終的に使用したいベースイメージにペーストできる機能です。
参考文献
- 投稿日:2020-03-22T15:51:14+09:00
Docker/Kubernetesなど コマンド一覧
仕事および趣味で利用した、Docker/Kubernetesのコマンドの備忘。
ついでに使わないとすぐ忘れるLinuxなどその他色々コマンドの情報のせます。。Docker
コマンド 概説 例 docker ps 稼働コンテナの一覧表示 実行中:docker ps
終了状態含む:docker ps -adocker images ローカルで保持しているイメージリストを表示 docker rmi [id名] ローカルのイメージを削除 削除できない場合は、 docker rmi [id名] -f で実行する
-f:強制的に削除docker build [-t リポジトリタグ] Dockerfileからイメージのビルド docker pull [コンテナイメージ] コンテナイメージのダウンロード docker run [コンテナイメージ] コンテナイメージを指定して起動 docker run -it [image名] shell コンテナを起動して対話型シェルを実行 docker run -it centos bash docker logs [Container ID] ログ表示 docker hisotry [images ID] イメージへの履歴 docker tag ローカルイメージ名:タグ リモートリポジトリ:タグ名 ローカルリポジトリのイメージをリモートリポジトリ名にタグ付け docker push タグ付けしたリモートリポジトリ名 リモートリポジトリへの登録 docker commit containerID image:tag コンテナイメージの登録 docker start containerID コンテナの開始 docker stop containerID コンテナの停止 docker stop $(docker ps -q) 稼働中の全コンテナの停止 docker network ls コンテナネットワークをリスト表示 docker network inspect [networkID] コンテナネットワークの詳細設定を表示 docker network create [ネットワーク名] コンテナネットワークを新規作成 docker network create mynet1 docker built -t イメージ名:tag名 DockerfileのPATH名 Dockerイメージの作成 docker build --tag mydaemon:0.1 ./Dockerfile Kubernetes
コマンド 概説 docker ps 稼働コンテナの一覧表示 TD TD TD TD Linux
ほぼCent0Sの例です。
Cent0S
コマンド 概説 例 systemctl list-unit-files -t service サービス一覧と自動起動設定の確認 (旧 chkconfig --list) cat /etc/redhat-release バージョン確認 yum list installed yumでインストールされた一覧の確認 nslookup dns(ip) DNSサーバーへの名前解決問い合わせ 参考
Docker reference
https://docs.docker.com/engine/reference/commandline/build/Docker コマンドチートシート
https://qiita.com/wMETAw/items/34ba5c980e2a38e548dbawkコマンド
https://www.atmarkit.co.jp/ait/articles/1706/08/news015.html
- 投稿日:2020-03-22T15:48:41+09:00
utunbu 19.10 への docker のインストール
utunbu 19.10 への docker のインストール
#!/bin/sh -e sudo true sudo apt-get update -y sudo apt-get install -y \ apt-transport-https \ ca-certificates \ curl \ gnupg-agent \ software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo apt-key fingerprint 0EBFCD88 sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" sudo apt-get update -y sudo apt-get install -y docker-ce docker-ce-cli containerd.io sudo systemctl start docker sudo systemctl enable docker sudo apt install -y docker-compose$(lsb_release -cs) に関して
$ sudo add-apt-repository \ "deb [arch=amd64] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable"ubuntu 19.10 では
$(lsb_release -cs)
の値はeoan
になります。新しい ubuntu のバージョンが出たばかりだと
$(lsb_release -cs)
を指定するとインストールできないことがあります。
$(lsb_release -cs)
の部分を例えばdisco
(=ubuntu 19.04) など古いリリースを指定することで対応するモジュールがリリースされていなくてもインストールできます。https://download.docker.com/linux/ubuntu/dists/ を確認することで対応するモジュールがあるか確認できます。
リンク
- 投稿日:2020-03-22T14:24:14+09:00
docker-composeでrailsをdocker化したらBundlerバージョンで怒られる
最近は railsもdocker化するのが多くみられるのですが、ハマりポイントも多いみたいですね。
特にdocker-composeを使ったときには、キャッシュがかかってうまく修正反映できないことがあります。エラーで怒られた。
例えば、既存のbundlerバージョンが合わなくて、bundler version 2.x
You must use Bundler 2 or greater with this lockfile.というエラー。
dockerでrubyのイメージを使うときには、bundlerも一緒に入ってるわけですが、
その時に作った時のGemfileに書かれているBundlerバージョンと合わないことがあります。解決法
その時には、Gemfile.lockを削除してからgemライブラリ達をビルドし直す必要があります。
$ rm Gemfile.lock $ sudo docker-compose build $ sudo docker-compose upこの時に気を付けなければいけないのは、docker-compose buildを使うこと、
どうやら、docker buildとdocker-compose buildはキャッシュが別物みたいです。
なので、いくらDockerでビルドし直しても、docker-compose には反映されなくて詰みます
(私は数時間失いました)。
エラーも出ないが、反映もされないのでキャッシュトラブルはとてもややこしい。参考
https://qiita.com/azul915/items/5b7063cbc80192343fc0
https://qiita.com/yoshijbbsk1121/items/87250501b32c6433943e
- 投稿日:2020-03-22T12:59:13+09:00
docker-desktop から docker コマンドを使う方法
# # Windows - Docker daemon 側の設定 # 以下の項目にチェックを入れる Docker Desktop > Settings > General > Expose daemon on tcp://localhost:2375 without TLS# # docker-desktop - Docker CLI 側の設定 # apk add docker export DOCKER_HOST=unix:///run/guest-services/docker.sockPowerShell に不慣れな場合、便利かなと思います。
WSL 2 にインストールされる2つの OS
Docker Desktop WSL 2 をインストールすると 2 つの OS がインストールされる。
docker-desktop
とdocker-desktop-data
です。PS > wsl -l -v NAME STATE VERSION * docker-desktop Running 2 docker-desktop-data Running 2 PS >詳細な役割については、以下の記事に書かれているがよくわからない。
〇 docker-desktop ってなんだ?
Docker daemon が動くOS。ディストリビューションは Alpine Linux っぽい。これは apk コマンドから推定しています。
Docker のコンテナは、こっちで主に管理している様子です。
たぶんそういう動作なんだろうなという理解をしています。確認した内容は、以下の通りです。
〇 docker-desktop-data ってなんだ?
Docker のボリュームは、こっちで主に管理する様子です。
dir \\wsl$\docker-desktop-data\mnt\wsl\docker-desktop-data\data\docker\volumesPS > dir \\wsl$\docker-desktop-data\mnt\wsl\docker-desktop-data\data\docker\volumes Directory: \\wsl$\docker-desktop-data\mnt\wsl\docker-desktop-data\data\docker\volumes Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 3/22/2020 8:56 PM fa5249be4b61730d27df1da8b71b190c06f4c2c4b13378542ceeefae38ab4b54 d----- 3/22/2020 4:06 PM 056cfbf651b419ba8f37f02bf32da6e082a93f9fd4e63690532582cbca56c61d d----- 3/22/2020 8:37 PM 自分が作成したボリュームがココにありました。 ------ 3/22/2020 8:57 PM 32768 metadata.db PS >VSCode の設定
追記: VS Code Server のアップデートが走ると VSCode が起動できなくなりました。詳細は下部に追記しています。
VSCode が docker-desktop から実行が可能です。どういう仕組みかはわからないのですが。WSL 2 凄すぎる...
/mnt/host/c/Users/ユーザ名/AppData/Local/Programs/Microsoft\ VS\ Code/bin/code .まとめ
~/.profile
に次のようなものを設定しておけばいいかなと...~/.profileexport DOCKER_HOST=unix:///run/guest-services/docker.sock alias code="/mnt/host/c/Users/ユーザ名/AppData/Local/Programs/Microsoft\ VS\ Code/bin/code"sh の設定は、ログイン時にまず /etc/profile が適用され、
続いて各ユーザのディレクトリの .profile (つまり、~/.profile)が適用されるよ!
シェルの設定ファイル ( sh, bash ) - suu-g's diary追記
VS Code Server のアップデートが走ると VSCode が起動できなくなりました。
$ code . Updating VS Code Server to version fe22a9645b44368865c0ba92e2fb881ff1afce94 ... 中略 Failed Connecting to update.code.visualstudio.com (104.42.78.153:443) wget: bad address 'az764295.vo.msecnd.net' ERROR: Failed to download https://update.code.visualstudio.com/commit:fe22a9645b44368865c0ba92e2fb881ff1afce94/server-li nux-x64/stable to /root/.vscode-server/bin/fe22a9645b44368865c0ba92e2fb881ff1afce94-1584858587.tar.gz $このあたりが原因かなと思っているのですが、解決できないでいます。
現状 VSCode を触りたい場合は Ubuntu から起動しています。その際の環境変数は、以下の通りです。
export DOCKER_HOST="tcp://0.0.0.0:2375"
- 投稿日:2020-03-22T09:05:43+09:00
ibmcom/db2のcontainer構築~ddl実行/データimportまでをbatファイルで自動化した
概要
db2(旧dashdb)のcontainerを構築してddlを実行し、データをimportするようなbatファイルを作ろうとしたところ、主にユーザー切り替え周りでつまづいたのでメモ
やりたいこと
以下を自動化したい!
1. dockerhubのibmcom/db2のcontainerを構築
2. 以下のようなddlを実行ddl.sqlCREATE TABLE TBL_A ( HOGE VARCHAR(50) NOT NULL, FUGA VARCHAR(50) NOT NULL UNIQUE );3. 以下のようなcsvファイルをimport
import.csvhoge1,fuga1 hoge2,fuga2やったこと①:batファイルの中身
以下、batファイルの中身を順番に説明
configファイルの読み込み
configファイルに外出しできていた方が色々と便利なので外出し
set CONFFILE=.\batconfig.ini if not exist %CONFFILE% ( echo ERROR: Not found %CONFFILE% exit /b 1 ) for /f "usebackq tokens=1,* delims==" %%a in ("%CONFFILE%") do ( set %%a=%%b )古いcontainerがいたら削除して新規作成
docker run
のコマンドはここ(→ https://hub.docker.com/r/ibmcom/db2 )のQuickStartに書いてある。
background実行させたいので、-it
オプションを削除して-d
オプションを追加。docker stop db2inst1 docker rm db2inst1 docker run -d --name db2inst1 --privileged=true -p 50000:50000 -e LICENSE=accept -e DB2INST1_PASSWORD=xxxxx ibmcom/db2 powershell sleep 60以下ログが出るまで待機させたいので、
powershell sleep 60
で待機している。(本当はdocker logs -f db2inst1
で監視すべきなのだろうが、そこまではやっていない。。。)(*) All databases are now active. (*) Setup has completed.ちなみに、ここで待たずに後続の処理をすると
db2: command not found
というエラーが出る。ddl/csvファイルをコピー
ファイルをcontainerの中にコピー
docker cp .\ddl.sql db2inst1:/mnt docker cp .\insert.csv db2inst1:/mntshell scriptをコピー
ddl実行用のshell scriptをcontainerにコピーして、権限を付与。
Windows環境化でshell scriptを編集していたのでsed
コマンドを使って改行コードを修正。docker cp .\doddl.sh db2inst1:/mnt docker exec -it db2inst1 bash -c "chmod a+x /mnt/doddl.sh" docker exec -it db2inst1 bash -c "sed -i 's/\r//' /mnt/doddl.sh"db2start/db2stopに権限を付与(ここ大事!!)
docker exec -it db2inst1 bash -c "chown db2inst1:db2iadm1 /database/config/db2inst1/sqllib/adm/db2start" docker exec -it db2inst1 bash -c "chown db2inst1:db2iadm1 /database/config/db2inst1/sqllib/adm/db2stop"この処理を実施しないと以下エラーが発生してしまっていたため、
db2start
db2stop
に権限を付与。-bash: /database/config/db2inst1/sqllib/adm/db2start: Permission denied -bash: /database/config/db2inst1/sqllib/adm/db2stop: Permission deniedshell script実行
container内でshell scriptを実行
docker exec -it db2inst1 bash -c "/mnt/ddl.sh"やったこと②:shell scriptの中身
root権限で実施したい処理
例えば、バックアップの保存とか
#!/bin/bash mkdir /mnt/ddl.bk cp /mnt/ddl.sql /mnt/ddl.bk/switch userして、処理実行
こんな感じでEOFで処理を渡す。
su - db2inst1 << EOF set -x db2 create db testdb db2 connect to testdb db2 -tvf /mnt/ddl.sql db2 import from /mnt/import.csv of del insert into TBL_A set +x EOF
- 投稿日:2020-03-22T08:58:02+09:00
Docker で zeit nowのデプロイ環境とNext.jsの開発環境を作ったメモ
概要
- zeit nowのデフォルトのnext.jsを修正
- 2020 年初頭における Next.js をベースとしたフロントエンドの環境構築 を参考にTypeScriptの環境構築
- dockerを利用して環境を作っている
- storybookはできてない
now
zeit now へのデプロイを確認
- 事前に、zeit へ github 連携を行っている。
- コンテナに bin/bash.sh でログイン。以下のコマンドを実行してデプロイしてみる。
now login # メールを確認する。メールは https://zeit.co/account の画面から確認可能 now init # nextjsのデフォルトプロジェクトを作成 cd nextjs # initで作ったディレクトリに移動する now # デプロイを行う。とりあえず、初期の状態でデプロイできることを確認
- このとき、/root/.local/share/now/ディレクトリに、config.json と auth.json が出来ているので、コンテナ外に保存しておく。認証用の情報
- nextjs フォルダに最初のファイルが出来ているので、コンテナ外に保存しておく。
- コンテナ作成時に、/root/.local/share/now/ディレクトリに先ほど保存したファイルを取り込むように Dockerfile を修正。
- 毎回、now login しなくてもよいようになる。
フォルダ構造変更
- src ファイル整理
now dev
でローカルで確認できるように docker-compose.yml を修正react ts
環境構築
2020 年初頭における Next.js をベースとしたフロントエンドの環境構築 を参考に環境を作っていく。
参考
now
dist now
設定なしでデプロイできる Zeit Now
Now でクラウドの複雑さから解放されよう、今すぐに
example
now deploynext
- 投稿日:2020-03-22T01:22:09+09:00
Docker を AWS にインストール
200321
注意点
なぜか動かない、という状況は AWS のセキュリティグループの設定が原因かもしれない。AWS の準備
EC2 をたてるまでの手順は、ここでは説明しない。注意が必要なのはセキュリティグループの設定で、デフォルトでは SSH 以外はすべてブロックされる。
AWS に docker をインストール
sudo yum update
sudo yum install -y docker
正常にインストールできたか確認できる。バージョンは 1.21.5 のようだ。
docker info
sudo がなくてもいいようにする
sudo service docker start
sudo usermod -a -G docker ec2-user
docker-compose インストール
docker-compose
sudo curl -L https://github.com/docker/compose/releases/download/1.21.0/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose
パーミッション変更が必要
'sudo chmod +x /usr/local/bin/docker-compose'
'docker-compose --version'Django のデプロイ
Docker を使った本番デプロイ
AWS 上でディレクトリ構造などを作ったりする必要はない。そんなことをやっていると手間ばかりかかってミスも連発する。ローカルの開発環境にも Docker を入れて、ここで完成したものをそのまま丸ごとコピーするのが正しい。
セキュリティグループの追加
EC2 のインスタンスを普通に立てただけでは SSH しかできない設定となってる。セキュリティグループに、HTTP プロトコル(ポート 80 または 8000)を通す設定をしておかないと絶対につながらず、時間を溶かすので注意が必要。
ローカルで開発したプロジェクトのデプロイ
ローカル環境で動作確認した django プロジェクトは、ディレクトリごと zip にしてホストに転送する。配置したい場所で unzip すると、そのままファイルができるので、あとは docker-comopose up すれば立ち上がる。
おわり
- 投稿日:2020-03-22T00:07:54+09:00
新たな言語 "Dark" を実際に触ってみて
はじめに
先日、私が以前に申請していたDarkのプライベートベータ版に漸く招待されたので、実際に触ってみた感想を述べようと思う。
1. Darkとは?
Darkとは、Ellen Chisa、そしてCircleCIの創業者であるPaul Biggarによって設立された会社で開発されている「偶発的な複雑さ」を無くし、バックエンドWebサービスを構築するための総合的なプログラミング言語であり、エディタであり、インフラストラクチャである。呼称するならば、総合的なソフトウェア開発プラットフォームだろう。Web上にエディタが展開され、そこで全ての開発を行える為、開発ツールやパブリッククラウドと言った多くのテクノロジーを直接触る必要は無い。
また、最大の特徴としてはデプロイレスだろう。デプロイレスとは、入力したものが即座にデプロイされ、本番環境ですぐに使用できる事である。Darkはインタープリタをクラウドで実行する。関数またはHTTP/イベントハンドラで新しいコードを作成すると、抽象構文ツリーの差分(エディターとサーバーが内部で使用するコードの表現)をサーバに送信し、次にそのコードを実行する。その為、デプロイメントはデータベースへの僅かな書き込みで済む為、瞬時且つアトミックである。この様に、デプロイを可能な限り最小化する事で最大50msの高速デプロイを実現している。
※デプロイレスに関する詳細は本記事下部の参考文献に記載
2. どんな開発に向いているのか
Darkは大きな目標として、「ソフトウェアの構築を100倍容易にすることでコーディングを民主化し、次の10億人がコーディングできるようにする」と挙げているが、初期段階では以下のような領域をターゲットにしている。
- モバイルアプリや、React, Vue, Anglarで記述された単一ページのWebアプリ等のクライアントアプリのバックエンド
- 既存のマイクロサービスベースのアーキテクチャで新しいサービスを構築すること
一方で、組み込みシステムや、定理証明、機械学習モデルなどは今後サポートする予定は無いとの事。
3. 実際に動かしてみる
実際にDarkのチュートリアルを参考にして、「日付レポート」のAPIを実装していく。
Darkにログインするとエディタが立ち上がる。画面は非常にシンプルで、サイドバー、ユーザ情報(右上)、ドキュメント(右下)のみである。基本的な操作は画面をクリックすると以下の図の様に機能の選択画面が表示される。開発は、この画面をキャンバスの様に使用して都度、自分の使用したいものを選択・追加してく事で開発していく。
3.1. Hello World
Hello Worldを作成してみる。手順は非常にシンプルである。
画面をクリックし、メニューが開かれた状態で
hello
と入力すると、New HTTP handler named /hello
とメニューに表示されるので、それを選択すると/hello
ハンドラが作成される。後は、リクエストメソッドと、valueを書くだけで完成である。作成したものは、右上の設定からOpen in new tab
で即時に確認する事ができる。また、valueが未記入の場合、
<Incomplete> Your code needs to return a value in the last expression
とエラーが表示される。※エンドポイントは、デフォルトでは
USERNAME-gettingstarted.builtwithdark.com
となるが、USERNAME-.builtwithdark.com
の2つ存在した。エンドポイント変更はユーザ画像をクリックし、Account/Canvas
から切り換える事ができる。【2020/03/22追記】
エンドポイントを書き換える事でプロジェクトを複数作成する事が可能で、新規プロジェクトを作成する際はエンドポイントを書き換えて更新すると良い。3.2. 演算
次に簡単な演算を行い、演算結果を渡してみる。
先程、同じ様にHTTPハンドラを作成し、リクエストメソッドを
GET
、ルートを/add
に設定した。演算処理は、演算子を使用して簡単に記述する事も可能だが、変数を宣言して行うことも可能である。
また、Darkではハンドラを作成すると、以下の様に各々の値を瞬時に確認する事ができる。
9(画像下部)
と出力されている場所は、このハンドラの戻り値である。5(画像左部)
はカーソルが置かれている位置の式の結果を表示している。整数の場合では利便性を感じないが、変数の場合は即座に結果を確認できる。3.3. REPL
REPLを作成する際も、同様に選択画面からREPLを選択し作成していく。今回は、敢えてエラーを起こしてその挙動も確認してみる事にする。
httpclient
と入力すると、HTTPClientのすべての標準ライブラリ関数、署名、およびdocstringが表示される。今回は、HTTPClient::post
を使用しようと思うので、httpclientpost
まで打つと表示された。Darkはオートコンプリートを行ってくれるので、正確なテキストを入力する必要は無い。
HTTPClient::post
を選択すると、受け取る為のパラメータ(URI
,Body
,Query
,Headers
)を自動的に作成され、それらを全て入力するとエディター内から式を実行できる実行ボタン(HTTPClient::post
の右側の灰色の三角形)が表示される。エンドポイントは今回、
USERNAME.builtwithdark.com
を使用したので、以下の様に入力する。https://USERNAME.builtwithdark.com/test
その他、パラメータを入力して実行しようとすると
404
がレスポンスとして表示される。これは、エンドポイント/test
がキャンバス上に存在しない為である。この結果は、サイドバーの404セクションからも確認できる。一方、キャンバス上ではエラーはこの様に表示される。
3.4. データストア
まず、先程のエンドポイントでのエラーを解決する為に、DarkではHTTPリクエストのトレースを使用してエンドポイントを構築する(トレース駆動開発)。404セクションにあるルート
/test
の右側にある青色の+ボタンを押すと、POST
メソッドのルート/test
が作成される。作成されたものを確認してみると、
Body
を含むREPLで設定したリクエスト情報が完全にトレースされている事が確認できる。このリクエストを直接処理し、
let data = request.body
を入力して取得した情報をレコードに保存する、ここでも、オートコンプリートを活用して記述していく。データストアは、HttpClientライブラリと同様に、空白に「DB」と入力すると、すべてのデータストア関数がプルアップされる。今回の場合では、
db::set
を使用する。データストアの作成は、サイドバーかキャンバス内をクリックして作成が可能である。今回は、
Requests
と言うデータストアを作成した。(小文字で入力して作成しても、最初の1文字目は大文字に変換してくれる様である)Darkのすべてのデータストアはキー値ベースであり、キーをレコードの一意の識別子として使用する。今回の場合であると、
data
、time
のキーを作成した場合は以下の様になる。{ key1: { data: { test: "test2" }, time: }, key2: { data: { test: "test1" }, time: } }最初のパラメータは、挿入するレコードである。これは、日付と時刻のスキーマと一致する。Darkでは、
DB::set
のすべてのフィールドを含める必要がある。上記のデータとDate::now
関数を挿入して時間を取得している。2つ目はキーであり、各オブジェクトに一意のキーが必要である。この場合、db
と入力すると、組み込みのdb::GenerateKey
関数が取得できる。3つ目は、データストア名を指定する。データストアの名前はオートコンプリートで表示される。最後に、
DB::set
を実行してみると以下の様にデータストアにレコードが挿入され、スキーマがロックされる。3.5. CRON
今回は、
dailyReport
と言うの名前のCRONを作成する。日付レポートを作成する為には、現在の時間と比較する必要がある。まず、変数
timeSeconds
を用意しDate::now
を設定する。次に、比較の為に秒単位の時間が必要の為、Date::toSeconds
も設定する。関数はパイプで繋げる事ができる。(|
と入力すると、|>
と表示される。)リクエストの取得は、
DB::getAll
を使用し、取得したいDBテーブル(Requests
)を指定する。リクエストはList型で取得される。入力後に、実行してみると以下の様にリクエストが取得されている事が確認できる。次に、
List::filter
を使用してリクエストのフィルタリングを行う。List::filter
では、フィルタリングするリスト名と、匿名関数を設定する。匿名関数では、リクエストが今日の情報であるかどうかを確認する。
変数
requestSeconds
は、リクエスト内に内包されている時間date
である。リクエストの時間は秒に変換する必要がある為、CRONの最初の行と似ているが、val.time
でリクエストの時間を取得しパイプでDate::toSeconds
を繋げて変換している。変数
difference
は、現在の時刻とリクエストの時間の差分を計算している。 そして、差分の結果が24時間以内(86400秒)であるかどうかを判別する。最後に、
emit
を使用して、バックグラウンドワーカーにリクエストを送信している。emit
では、イベント名(todayRequests
)と、WORKER名(StoreReport
)を指定して送信を行う。では、次にWORKERの作成について説明する。3.6. WORKER
CRONを実行すると、WORKERは存在していないので404セクションに表示される。その為、先程と同じ様に404セクションから
StoreResort
を選択すると、既に名前がついた状態で作成される。作成されたWORKERにカーソルを合わせると、CRONによってトリガーされイベントが表示される。実際に、WORKERの機能を実装すると以下のようになる。
レポートでは、
MM-DD-YYYY
の表記でデータを保存する為、現在時刻を取得し、それぞれを分割して変数humanDate
でフォーーマット変換を行った。※
+
はint型の場合でのみ使用できる様で、String型の場合は++
を使用する事で文字列を連結する事ができる。変数
data
では、リクエストに内包されているdata
のみを取得している。List::map
を使用する事で、リスト内のオブジェクトのコレクションに対して操作する事ができる。レポートの保存は、新たに
DailyReport
と言うデータストアを作成しているが、外部APIを使用する事も可能である。以上で、実装は終了である。ここまでの実装したものを並べると以下のようになる。
3.7. 動作検証
REPL内で記述した、BODY部分を
test 1
から、hello Dark!
に変更して実行してみる。実行すると、
/test
ハンドラでは新しいトレースドットが表示され、リクエストのBODYが、hello Dark!
になっている事が確認できる。データストアも同じく、DBに追加されたレコードが表示される。
CRONの方も確認してみると、DBのレコードが全て取得できている事が確認できているが、時系列毎にソートして表示されてはいない為探すのに苦労した。筆者が不必要に連打したのが原因でもあるが、レコードが増えていく事が想定されると目を通すのには可読性が悪く、少し労力が掛かるなと感じた。
WORKERも同じ事が言える。しかし、Eventは実行された時系列毎に保存されており、前回との差分の確認や以前のデータの内容を確認する事ができる。
最後に、日付レポートのデータストアも確認すると、日付毎にレコードが登録されている事が確認できる。
また、新たにREPLを作成し日時レポートとその内部の項目数を取得してみた。実行してみると、
DailyReport
から日付毎にレポートを取得する事ができた。試しに異なる日付のレコードも登録した所、そちらも同じく取得する事ができた。ハンドラなどを削除すると、Deletedセクションが表示される。Deleteセクションから削除されたハンドラやデータストアを見ることができ、復元すると即座にデプロイし直され元の状態に復元する事ができる。
4. 総評
初めてDarkを触り、直感的操作で開発を行う事ができた。確かに、初期段階ではDarkのツールセットについての学習コストは発生するものの、従来の開発言語を覚える程では現段階は無いと感じる。
また、コードを書くことと並行してトレーシングが行われ、Dark内で作成したものはオートセーブされる為、ウィンドウを閉じても再度立ち上げると以前の状態から再開する事ができるので逐一保存する面倒もない。
デプロイレスと言う指向は、これまでいくつかのサービスで実装しようと試みて来たが、Darkが初めての成功例ではないだろうか。開発者は、アプリケーションのコードを書くだけで良く、書き終わったコードは既にホストされていると言うのは非常に有り難い事である。CircleCIを開発してきたPaul Bigger氏だからこそ、デプロイに深く精通し、その中に存在する課題を解決する策として見出せたのだと思う。
ベータ版ではあるが、既にある程度の開発も可能でReact SPAや、Slackbotの開発もできるようなので今後試しに実装してみようと思う。
しかし、バグもまだ存在しているのでこれからの改善に大きく期待すると共に今後の開発に貢献していけたらと思う。
最後に、本記事を見てDarkを触れてみたいと感じた人が少しでも増えてくれれば幸いである。ベータ版は、Darkの公式サイトから登録が可能で招待されるまでに時間は掛かる(筆者はベータ版開始から大体2,3ヶ月待った)が是非この機会に登録して欲しい。
5. 謝辞
本記事を作成するに至り、快く承諾してくれたCEOのEllen Chisaに深く感謝する。
参考文献
- 投稿日:2020-03-22T00:00:02+09:00
Docker Compose upでMySQLが起動するまで待つ方法(2種類紹介)
docker-compose upで立ち上がらないGoのコンテナがありまして
docker-compose up -dでコンテナを立ち上げる際、
なぜかWeb側のコンテナが立ち上がらないという現象が発生していました。docker-compose up -d!
docker-compose up -d Creating network "api_network" with the default driver Creating redis ... done Creating mysql ... done Creating api ... doneしかし調べてみると、、、
docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------------- mysql .sh mysql ... Up 0.0.0.0:3306->3306/tcp, 33060/tcp redis .sh redis ... Up 0.0.0.0:6380->6380/tcp api app/build/api Exit 2webのコンテナがExitとなっており、立ち上がっていません..
docker-compose logs api api | panic: dial tcp 172.28.0.3:3306: connect: connection refusedなぜだーーー
これは割とあるあるな話のようで、
MySQLコンテナの立ち上がりが遅く、起動する前に接続しにいこうとしてしまい、
コケてしまうようです。ま、もう1回docker-compose upすれば済む話なのですが、
1発でなんとかせよ! というミッションがありまして、達成に向けて試行錯誤してみる事に。今回はシェルスクリプトを使う方法と、Goのコードから起動確認する方法を試してみました。
それぞれご紹介します!問題解決に到るまで、5名以上の方からアドバイスを頂けまして、
そのおかげで達成できました! 本当にありがとうございます!!下記の記事を参考にさせてもらいました ↓
http://docs.docker.jp/compose/startup-order.html
https://qiita.com/shiena/items/47437f4f7874bf70d664シェルスクリプトを使ってpingで接続を確認して繋ぎにいく
以下のようなシェルスクリプトを書きます
start.sh# !/bin/.sh # MySQLサーバーが起動するまでループで待機する until mysqladmin ping -h mysql --silent; do echo 'waiting for mysqld to be connectable...' sleep 2 done echo " go app is started!" exec app/build/apistart.shをwebのコンテナ内に入れます。今回はmntに入れました。
docker-compose.ymlを以下のように書き換えます。docker-compose.ymlvolumes: - ./api/scripts:/mnt entrypoint: mnt/start.shentrypointを.shにして、ループ処理をさせてから
1番最後のexecでコンテナを立ち上げるようにします。ただし、mysqladmin pingを実行する為には、
mysql-clientをインストールさせる必要があります。
Dockerfileにそれ用の記述を書き加えます。DockerfileRUN apk update \ && apk add mysql-client時は来た!
docker-compose up -d!docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------------- mysql .sh mysql ... Up 0.0.0.0:3306->3306/tcp, 33060/tcp redis .sh redis ... Up 0.0.0.0:6380->6380/tcp api ./api/scripts:start.sh Up 0.0.0.0:8080->8080/tcpやった! 成功です!
GoのコードでMySQLの立ち上がりを確認してから接続するようにする
下記の記事を参考にさせてもらいました ↓
https://qiita.com/Bmouthf/items/d3cfdbee74caeda77e3f
https://kleinblog.net/docker-golang-mysql-min/上の方法で上手くいったのですが、その為だけにmysql-clientをインストールさせるのは
ちょっと... という事で、GoのコードでMySQLコンテナの立ち上がりを待つようにします!mysqlへの接続は接続用の関数を呼び出して繋いでいたのですが、
少し書き加えてMySQLの立ち上がりを確認してから接続させるようにします。↓ mysqlの関数を呼び出して接続させている
main.godb := mysql.ConnectDB() db.LogMode(true) defer db.Close()importにgormを追加
import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" )以下のように書き足しました。
main.godb, connectcheck := gorm.Open("mysql") if connectcheck != nil { db = mysql.ConnectDB() db.LogMode(true) defer db.Close() }docker-compose up -d!
docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------------- mysql .sh mysql ... Up 0.0.0.0:3306->3306/tcp, 33060/tcp redis .sh redis ... Up 0.0.0.0:6380->6380/tcp api app/build/api Up 0.0.0.0:8080->8080/tcpよっしゃー!
今回は、Dokcer + Go + MySQLコンテナの組み合わせでした。
同じような現象に悩まれてる方が何らかの参考になりましたら幸いです!著者自身、DockerもGoも全然分かっていない未熟者でして不備もあるかと思います。
批評、マサカリ大歓迎です!
何かありましたら、ぜひご意見アドバイス等等下さいませ〜!!
- 投稿日:2020-03-22T00:00:02+09:00
docker-compose upでMySQLが起動するまで待つ方法(2種類紹介)
docker-compose upで立ち上がらないGoのコンテナがありまして
docker-compose up -dでコンテナを立ち上げる際、
なぜかWeb側のコンテナが立ち上がらないという現象が発生していました。docker-compose up -d!
docker-compose up -d Creating network "api_network" with the default driver Creating redis ... done Creating mysql ... done Creating api ... doneしかし調べてみると、、、
docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------------- mysql .sh mysql ... Up 0.0.0.0:3306->3306/tcp, 33060/tcp redis .sh redis ... Up 0.0.0.0:6380->6380/tcp api app/build/api Exit 2webのコンテナがExitとなっており、立ち上がっていません..
docker-compose logs api api | panic: dial tcp 172.28.0.3:3306: connect: connection refusedなぜだーーー
これは割とあるあるな話のようで、
MySQLコンテナのMySQL実行完了が遅く、起動する前に接続しにいこうとしてしまい、
コケてしまうようです。ま、もう1回docker-compose upすれば済む話なのですが、
1発でなんとかせよ! というミッションがありまして、達成に向けて試行錯誤してみる事に。今回はシェルスクリプトを使う方法と、Goのコードから起動確認する方法を試してみました。
それぞれご紹介します!問題解決に到るまで、5名以上の方からアドバイスを頂けまして、
そのおかげで達成できました! 本当にありがとうございます!!シェルスクリプトを使ってpingでMySQLを確認して繋ぎにいく
下記の記事を参考にさせてもらいました ↓
http://docs.docker.jp/compose/startup-order.html
https://qiita.com/shiena/items/47437f4f7874bf70d664以下のようなシェルスクリプトを書きます
start.sh# !/bin/.sh # MySQLサーバーが起動するまでループで待機する until mysqladmin ping -h mysql --silent; do echo 'waiting for mysqld to be connectable...' sleep 2 done echo " go app is started!" exec app/build/apistart.shをwebのコンテナ内に入れます。今回はmntに入れました。
docker-compose.ymlを以下のように書き換えます。docker-compose.ymlvolumes: - ./api/scripts:/mnt entrypoint: mnt/start.shentrypointを.shにして、ループ処理をさせてから
1番最後のexecでコンテナを立ち上げるようにします。ただし、mysqladmin pingを実行する為には、
mysql-clientをインストールさせる必要があります。
Dockerfileにそれ用の記述を書き加えます。DockerfileRUN apk update \ && apk add mysql-client時は来た!
docker-compose up -d!docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------------- mysql .sh mysql ... Up 0.0.0.0:3306->3306/tcp, 33060/tcp redis .sh redis ... Up 0.0.0.0:6380->6380/tcp api ./api/scripts:start.sh Up 0.0.0.0:8080->8080/tcpやった! 成功です!
GoのコードでMySQLの立ち上がりを確認してから接続するようにする
下記の記事を参考にさせてもらいました ↓
https://qiita.com/Bmouthf/items/d3cfdbee74caeda77e3f
https://kleinblog.net/docker-golang-mysql-min/上の方法で上手くいったのですが、その為だけにmysql-clientをインストールさせるのは
ちょっと... という事で、GoのコードでMySQLコンテナの立ち上がりを待つようにします!mysqlへの接続はそれ用の関数を呼び出して繋いでいたのですが、
少し書き加えてMySQLの立ち上がりを確認してから接続させるようにします。↓ mysqlの関数を呼び出して接続させている
main.godb := mysql.ConnectDB() db.LogMode(true) defer db.Close()importにgormを追加
import ( "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" )以下のように書き足しました。
main.godb, connectcheck := gorm.Open("mysql") if connectcheck != nil { db = mysql.ConnectDB() db.LogMode(true) defer db.Close() }docker-compose up -d!
docker-compose ps Name Command State Ports -------------------------------------------------------------------------------------------------- mysql .sh mysql ... Up 0.0.0.0:3306->3306/tcp, 33060/tcp redis .sh redis ... Up 0.0.0.0:6380->6380/tcp api app/build/api Up 0.0.0.0:8080->8080/tcpよっしゃー!
今回は、Dokcer + Go + MySQLコンテナの組み合わせでした。
同じような現象に悩まれてる方が何らかの参考になりましたら幸いです!著者自身、DockerもGoも全然分かっていない未熟者でして不備もあるかと思います。
批評、マサカリ大歓迎です!
何かありましたら、ぜひご意見アドバイス等等下さいませ〜!!