20200322のdockerに関する記事は20件です。

CentOS8 + PX-W3PE4 + docker-mirakurun-epgstationで作る自宅TV録画サーバー

概要

現在入手困難なTVチューナーカード、アースソフト社のPT3に代わり、PLEX社のPX-W3PE4を用いて自宅TV録画サーバーを構築します。録画ソフトウェアとしてl3tnun氏作成のdocker-mirakurun-epgstationを改変して利用し、できる限りサーバー環境を汚さず、手軽に作成します。

注意

本記事はある程度「分かっている」方向けとなります。できる限り詳細に説明はしますが、一部の説明を他の方の解説に任せたり説明を省いている点があることをご了承ください。

2020年3月時点でDockerはCentOS8で未サポートとなっていますが、稼働は確認しています。気になる方はCentOS7で作成してください。

また、サーバーは既に作成しており、記憶を辿って解説記事を書いていることから抜けや間違いがあると思われます。間違いあればご指摘いただければと思います。

おしながき

  1. 資材調達
  2. CentOS8インストール、初期設定。
  3. PLEX TVチューナー用非公式ドライバインストール
  4. Docker, docker-composeインストール
  5. 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をインストールします。

  1. CentOS8のISOイメージをダウンロード
  2. RufusでUSBメモリに焼きこみ or DVDライティングソフトでDVD-RにISO焼きこみ
  3. 2.で作成したメディアから起動するようにBiosを設定、起動。
  4. 手順に従いインストール。

「ソフトウェアの選択」は「サーバー」を選択。
ここの手順に従って録画鯖のみ動かすのであれば「最小限のインストール」でも問題ないと思います。

参考:「ネットワークエンジニアの日常 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

参考:CentOS8にDockerを入れる

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

参考:Install Docker Compose

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-epgstation

5.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 reader

pt3の部分をすべて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: true

5.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録画に需要はないのかもしれないけど、何より無料で放送しているものを録画できるというのは大きなアドバンテージだと思う。皆様良き録画ライフを!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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」ボタンを押下します。
  FireShot Capture 192 - Install Docker Desktop on _ - https___docs.docker.com_docker-for-mac_install_.png

 (2) 「Download Docker Desktop for Mac」ボタンを押下します。
  Docker.dmg がダウンロードされます。
  FireShot Capture 193 - Docker Hub - https___hub.docker.com__overlay=onboarding.png

1-2. インストール

 (1) Docker.dmgの実行
  ダウンロードした Docker.dmg を実行して下さい。

 (2) Applicationsに移動
  DockerをApplicationsに移動させます。
  スクリーンショット 2020-02-09 18.57.37.png

 (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 8d51620a

2. Docker コンテナの構築

 NGINX、uWSGI、PostgreSQLが動作するDocker コンテナを構築します。

2-1. 作業ディレクトリの作成

 任意の作業ディレクトリを作成して下さい。
 本投稿では、"django"を作成します。

$ mkdir django

2-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 files

2-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.postgres

2-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.postgres

2-13. 動作確認

 ブラウザを開き、"http://0.0.0.0:8000"にアクセスし、
 Djangoのデフォルトのページが表示されることを確認します。
  FireShot Capture 001 - Django_ the Web framework for perfectionists with deadlines. - 0.0.0.0.png

 ここまでの手順で、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 files

3-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)で設定した内容を入力します。
    スクリーンショット 2020-03-09 0.44.16.png

  ③ テーブルの確認
   下記の通り、app_area、app_cafe、app_utilityテーブルが作成されています。
    スクリーンショット 2020-03-09 0.52.03.png

 (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) 一覧画面
  FireShot Capture 008 -  - 0.0.0.0.png

 (2) 詳細画面
  FireShot Capture 009 -  - 0.0.0.0.png

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のモデルの参照、追加、更新が出来ることを確認して下さい。

 (1) ログイン画面
  FireShot Capture 006 - Log in - Django site admin - 0.0.0.0.png

 (2) 管理画面
  FireShot Capture 007 - Site administration - Django site admin - 0.0.0.0.png
  

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が使用されていることを確認します。

 FireShot Capture 011 -  - 0.0.0.0.png

参考元

Compose file version 3 reference
Docker Composeチュートリアル: herokuのPostgresをローカルにさくっとコピーする
Supported tags and respective Dockerfile links
はじめての Django アプリ作成、その 1
SQLのSELECT文を、DjangoのQuerySet APIで書いてみた
Djangoにおけるクラスベース汎用ビューの入門と使い方サンプル

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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)

手順(概要)

  1. VirtualBoxのインストール
  2. Vagrantのインストール
  3. Git for Windowsのインストール
  4. Vagrantファイル(docker+minikube)の入手&実行
  5. 動作確認

手順(詳細)

1. VirtualBoxのインストール

実際は前の記事でDockerToolBoxインストール時についでインストールされたのですが
VirtualBoxのバージョンアップしたときにインストールしなおした感じに名たのでその様子をUPします。

バージョンアップの案内に記載されているURLをクリック
コメント 2020-03-21 141817.png

リンク先では自動的にダウンロードが始まる。ダウンロードが完了したらexeファイルを起動
コメント 2020-03-21 141915.png

インストール開始。[Next]クリック
コメント 2020-03-21 141928.png

[Next]クリック
コメント 2020-03-21 141946.png

[Next]クリック
コメント 2020-03-21 141957.png

[Install]クリック
コメント 2020-03-21 142138.png

[インストール]クリック
コメント 2020-03-21 142449.png

[Finish]クリック
コメント 2020-03-21 142508.png

2. Vagrantインストール

ダウンロードサイト(https://www.vagrantup.com/downloads.html)へ
アクセスして、win10(64bit)のインストーラ入手
コメント 2020-03-21 144619.png

ダウンロードしたインストーラを起動
コメント 2020-03-21 144718.png

ここで突然windows Defenderが反応するが、落ち着いて[実行]
コメント 2020-03-21 144749.png

[Next]クリック
コメント 2020-03-21 144819.png

チェックして、[Next]クリック
コメント 2020-03-21 144847.png

[Next]クリック
コメント 2020-03-21 144904.png

[Install]クリック
コメント 2020-03-21 144921.png

…で、インストール完了

3.Git for Windowsのインストール

ダウンロードサイト(https://git-scm.com/download/win)へアクセスしてダウンロード。
コメント 2020-03-21 142723.png

インストーラを起動
コメント 2020-03-21 142829.png

[Next]クリック
コメント 2020-03-21 143014.png

[Next]クリック
コメント 2020-03-21 143110.png

[Next]クリック
コメント 2020-03-21 143144.png

checkout as-is, commit as-is を選択して、[Next]クリック
コメント 2020-03-21 143804.png

[Install]クリック
コメント 2020-03-21 143206.png

…で、インストール完了

4. Vagrantファイル(docker+minikube)の実行

windows検索窓から"git"入力して、"git for windows"起動
コメント 2020-03-22 221038.png

githubからdocker+minikubeのvagrantファイルを入手
※このURLは書籍購入した人に公開している感じなのでさすがに隠した。。。
コメント 2020-03-21 144044.png

Vagrantファイルの実行。docker&minikubeのインストールはこれだけ。さすがvagrant。

$ vagrant up

コメント 2020-03-21 165405.png

プロンプトが戻ってきたら、実行環境(=docker+minikubeのインストールされたUbunts)へログイン

$ vagrant ssh

コメント 2020-03-21 165401.png

5.動作確認

Dockerのバージョンを確認してみる

$ docker version

コメント 2020-03-21 165532.png

minikubeを起動する。

sudo minikube start

コメント 2020-03-21 165613.png

minikube上のノードの状態とpodの様子を確認。
minikubeという名前のノードが稼働しており、
minikubeにまつわる様々なpodが動作していることが確認できる。

$ kubectl get node ←ノードの状態確認
$ kubectl get pod --all-namespeces ←ポッドの状態確認

コメント 2020-03-21 165941.png

ここまでで、minikubeが動くことが確認できたので、ここからじっくりと
dockerのminikubeの動作がどんなもんか本を読みながら試していこうと思う。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DockerでDevice or resource busyと表示された時の対処法

問題が生じた状況

Docker Volumeを使ってる際に主に出ると思われる問題です。
自分はfirebaseのfirebase-tools.jsonというファイルをマウントする際に出た問題です。

解決策

Docker Volumeでファイル自体をマウントするのではなくディレクトリをマウントするべきだった模様です。
かなり基礎的なミスな気がしますが、意外と検索してもこれを指摘する記事は出ませんでした。

修正した方法

docker-compose.yml
    volumes:
-      - "./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
これも効果なし

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/
コメント 2020-03-21 133856.png

DockerToolbox-19.03.1.exeをクリックしてダウンロード。
コメント 2020-03-21 134001.png

ダウンロードしたファイルを実行してインストール開始。
コメント 2020-03-21 134219.png

[Next]クリック
コメント 2020-03-21 134241.png
[Next]クリック
コメント 2020-03-21 134256.png

"Git for Windows"をチェックして[Next]クリック
※書籍によると"Git for Windows"はチェック不要と書かれていたけど、
 この後の"Docker QuickStart Terminal"の実行でうまくいかなかったので
 あえて"Git for Windows"のチェックを入れた
コメント 2020-03-21 134328.png

[Next]クリック
コメント 2020-03-21 134402.png

[Next]クリック
コメント 2020-03-21 134415.png

[インストール]クリック
コメント 2020-03-21 134521.png

[Finish]クリック。これでDocker Toolboxのインストール完了!
コメント 2020-03-21 134548.png

2.Docker Toolboxの動作確認

インストール完了したときに現れた"Docker Quickstart Terminal"起動
コメント 2020-03-21 134640.png

しばし待つ。。。
コメント 2020-03-21 134857.png

クジラのAAが出たら成功!
コメント 2020-03-21 135216.png

動作確認ってことでバージョン確認を実行

$ docker version

コメント 2020-03-21 135250.png

オマケ:”Docker Quickstart Terminal”を複数起動した様子

ためしに、1つめの”Docker Quickstart Terminal”からcentos:7をプルして、レポジトリの様子を表示した後に
2つめの”Docker Quickstart Terminal”を起動してからレポジトリの様子を表示したところ
1つめのターミナルでプルしたcentosが取れていることから、複数のターミナルで1つのDocker環境を
共有できていることがわかる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.servicejournalctl -xe で参照しても、よくわからず、システムログを参照しました。

/var/log/daemon.log に以下のような記録が出ていました。

/var/log/daemon.log
failed 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_gwbridge

3. systemd の kv ファイルを削除と systemd のリロード

systemd に残っている Key-Value store の db を削除して、systemd のリロードを行います。

rm /var/lib/docker/network/files/local-kv.db
systemctl daemon-reload

4. 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 show

docker 側のネットワークデバイスを確認します。

docker network ls

または

docker network inspect bridge

終了。

さいごに

  • /var/lib/docker/network/files/local-kv.db を削除するのが正しいオペレーションなのかどうか、わかっていません。
  • docker のドキュメントを調べてもなかなか正解が見当たらず、systemd 側を調べて KV store に思い当たり、削除を試みました。
  • systemd の dockerd のインターバル設定に気づかず、すぐに start させてしまうと設定が正しくてもエラーになり、これで時間を費やしました。

  1. dockerd の起動インターバルが 60s に設定されているため、すぐに起動すると、設定が正しくてもエラーになります。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 -d   

composerを用意する

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 cms

dbの設定をする

作成したプロジェクトと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を呼び出せば以下が表示されるかと思います。
スクリーンショット 2020-03-22 16.37.26.png

(おまけ)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 ssl

Apacheにsslの設定を追加します。

これらも既に記述してありますので試したい場合はどうぞ。

(おまけ)xdebugの設定

こちらを参考にして動くように設定してあるので動くはずです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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ブロックに分かれています。

  1. Reactアプリケーションのビルド
  2. Javaアプリケーションのビルド
  3. 実行するイメージの作成

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 package

Reactアプリケーションのビルド成果物を/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=local

metaタグや.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

範囲を選択_019.png

次に、thymeleaf_testtestという値を入れて、コンテナ起動してみます。

docker run --rm -p 80:8080 test:dev

範囲を選択_020.png

反映が確認できました。

おわりに

そもそもこのような構成で、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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

わかりづらいのはnsqlookupdnsqdで別のコンテナを起動する必要があるということ。また--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

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerのマルチステージビルドについて

マルチステージビルドとは?[1]

公式ドキュメント

With multi-stage builds, you use multiple FROM statements in your Dockerfile. Each FROM 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 で複数のビルドができ,各ビルドの段階で必要な部分だけコピーして(残りは破棄される),最終的に使用したいベースイメージにペーストできる機能です。

参考文献

[1]. Docker に Composer をインストールするベストプラクティス(と解説)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker/Kubernetesなど コマンド一覧

仕事および趣味で利用した、Docker/Kubernetesのコマンドの備忘。
ついでに使わないとすぐ忘れるLinuxなどその他色々コマンドの情報のせます。。

Docker

コマンド 概説
docker ps 稼働コンテナの一覧表示 実行中:docker ps
終了状態含む:docker ps -a
docker 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/34ba5c980e2a38e548db

awkコマンド
https://www.atmarkit.co.jp/ait/articles/1706/08/news015.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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/ を確認することで対応するモジュールがあるか確認できます。

リンク

https://docs.docker.com/install/linux/docker-ce/ubuntu/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.sock

PowerShell に不慣れな場合、便利かなと思います。

WSL 2 にインストールされる2つの OS

Docker Desktop WSL 2 をインストールすると 2 つの OS がインストールされる。docker-desktopdocker-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\volumes
PS > 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 に次のようなものを設定しておけばいいかなと...

~/.profile
export 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"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ibmcom/db2のcontainer構築~ddl実行/データimportまでをbatファイルで自動化した

概要

db2(旧dashdb)のcontainerを構築してddlを実行し、データをimportするようなbatファイルを作ろうとしたところ、主にユーザー切り替え周りでつまづいたのでメモ

やりたいこと

以下を自動化したい!
1. dockerhubのibmcom/db2のcontainerを構築
2. 以下のようなddlを実行

ddl.sql
 CREATE TABLE
    TBL_A ( 
    HOGE VARCHAR(50) NOT NULL,
    FUGA VARCHAR(50) NOT NULL UNIQUE );

3. 以下のようなcsvファイルをimport

import.csv
hoge1,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:/mnt

shell 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 denied

shell 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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker で zeit nowのデプロイ環境とNext.jsの開発環境を作ったメモ

概要

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 deploy

next

2020 年初頭における Next.js をベースとしたフロントエンドの環境構築

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 すれば立ち上がる。

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

新たな言語 "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にログインするとエディタが立ち上がる。画面は非常にシンプルで、サイドバー、ユーザ情報(右上)、ドキュメント(右下)のみである。基本的な操作は画面をクリックすると以下の図の様に機能の選択画面が表示される。開発は、この画面をキャンバスの様に使用して都度、自分の使用したいものを選択・追加してく事で開発していく。

Screen Shot 2020-03-19 at 4.09.18.png

3.1. Hello World

Hello Worldを作成してみる。手順は非常にシンプルである。

ezgif.com-video-to-gif.gif

画面をクリックし、メニューが開かれた状態で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に設定した。

演算処理は、演算子を使用して簡単に記述する事も可能だが、変数を宣言して行うことも可能である。

ezgif.com-video-to-gif.gif

また、Darkではハンドラを作成すると、以下の様に各々の値を瞬時に確認する事ができる。

Screen Shot 2020-03-20 at 15.20.26.png

9(画像下部)と出力されている場所は、このハンドラの戻り値である。5(画像左部)はカーソルが置かれている位置の式の結果を表示している。整数の場合では利便性を感じないが、変数の場合は即座に結果を確認できる。

3.3. REPL

REPLを作成する際も、同様に選択画面からREPLを選択し作成していく。今回は、敢えてエラーを起こしてその挙動も確認してみる事にする。

httpclientと入力すると、HTTPClientのすべての標準ライブラリ関数、署名、およびdocstringが表示される。今回は、HTTPClient::postを使用しようと思うので、httpclientpostまで打つと表示された。Darkはオートコンプリートを行ってくれるので、正確なテキストを入力する必要は無い。

HTTPClient::postを選択すると、受け取る為のパラメータ(URI,Body,Query, Headers)を自動的に作成され、それらを全て入力するとエディター内から式を実行できる実行ボタン(HTTPClient::postの右側の灰色の三角形)が表示される。

ezgif.com-video-to-gif (1).gif

エンドポイントは今回、USERNAME.builtwithdark.comを使用したので、以下の様に入力する。

https://USERNAME.builtwithdark.com/test

その他、パラメータを入力して実行しようとすると404がレスポンスとして表示される。これは、エンドポイント/testがキャンバス上に存在しない為である。この結果は、サイドバーの404セクションからも確認できる。

Screen Shot 2020-03-20 at 16.47.00.png

一方、キャンバス上ではエラーはこの様に表示される。

Screen Shot 2020-03-20 at 16.47.34.png

3.4. データストア

まず、先程のエンドポイントでのエラーを解決する為に、DarkではHTTPリクエストのトレースを使用してエンドポイントを構築する(トレース駆動開発)。404セクションにあるルート/testの右側にある青色の+ボタンを押すと、POSTメソッドのルート/testが作成される。

ezgif.com-video-to-gif (2).gif

作成されたものを確認してみると、Bodyを含むREPLで設定したリクエスト情報が完全にトレースされている事が確認できる。

Screen Shot 2020-03-20 at 17.09.33.png

このリクエストを直接処理し、let data = request.bodyを入力して取得した情報をレコードに保存する、ここでも、オートコンプリートを活用して記述していく。

ezgif.com-video-to-gif (3).gif

データストアは、HttpClientライブラリと同様に、空白に「DB」と入力すると、すべてのデータストア関数がプルアップされる。今回の場合では、db::setを使用する。

データストアの作成は、サイドバーかキャンバス内をクリックして作成が可能である。今回は、Requestsと言うデータストアを作成した。(小文字で入力して作成しても、最初の1文字目は大文字に変換してくれる様である)

Darkのすべてのデータストアはキー値ベースであり、キーをレコードの一意の識別子として使用する。今回の場合であると、datatimeのキーを作成した場合は以下の様になる。

{
  key1: {
          data: {
                  test: "test2"
                },
          time: 
        },
  key2: {
          data: {
                  test: "test1"
                },
          time: 
        }
}

最初のパラメータは、挿入するレコードである。これは、日付と時刻のスキーマと一致する。Darkでは、DB::setのすべてのフィールドを含める必要がある。上記のデータとDate::now関数を挿入して時間を取得している。2つ目はキーであり、各オブジェクトに一意のキーが必要である。この場合、dbと入力すると、組み込みのdb::GenerateKey関数が取得できる。3つ目は、データストア名を指定する。データストアの名前はオートコンプリートで表示される。

最後に、DB::setを実行してみると以下の様にデータストアにレコードが挿入され、スキーマがロックされる。

Screen Shot 2020-03-20 at 17.59.47.png

3.5. CRON

今回は、dailyReportと言うの名前のCRONを作成する。

日付レポートを作成する為には、現在の時間と比較する必要がある。まず、変数timeSecondsを用意しDate::nowを設定する。次に、比較の為に秒単位の時間が必要の為、Date::toSecondsも設定する。関数はパイプで繋げる事ができる。(|と入力すると、|>と表示される。)

リクエストの取得は、DB::getAllを使用し、取得したいDBテーブル(Requests)を指定する。リクエストはList型で取得される。入力後に、実行してみると以下の様にリクエストが取得されている事が確認できる。

Screen Shot 2020-03-21 at 13.41.48.png

次に、List::filterを使用してリクエストのフィルタリングを行う。List::filterでは、フィルタリングするリスト名と、匿名関数を設定する。

Screen Shot 2020-03-21 at 20.44.58.png

匿名関数では、リクエストが今日の情報であるかどうかを確認する。

変数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によってトリガーされイベントが表示される。

Screen Shot 2020-03-21 at 17.08.24.png

実際に、WORKERの機能を実装すると以下のようになる。

Screen Shot 2020-03-21 at 17.44.18.png

レポートでは、MM-DD-YYYYの表記でデータを保存する為、現在時刻を取得し、それぞれを分割して変数humanDateでフォーーマット変換を行った。

+はint型の場合でのみ使用できる様で、String型の場合は++を使用する事で文字列を連結する事ができる。

変数dataでは、リクエストに内包されているdataのみを取得している。List::mapを使用する事で、リスト内のオブジェクトのコレクションに対して操作する事ができる。

レポートの保存は、新たにDailyReportと言うデータストアを作成しているが、外部APIを使用する事も可能である。

以上で、実装は終了である。ここまでの実装したものを並べると以下のようになる。

Screen Shot 2020-03-21 at 18.17.39.png

3.7. 動作検証

REPL内で記述した、BODY部分をtest 1から、hello Dark!に変更して実行してみる。

Screen Shot 2020-03-21 at 18.43.25.png

実行すると、/testハンドラでは新しいトレースドットが表示され、リクエストのBODYが、hello Dark!になっている事が確認できる。

Screen Shot 2020-03-21 at 18.49.31.png

データストアも同じく、DBに追加されたレコードが表示される。

Screen Shot 2020-03-21 at 18.53.09.png

CRONの方も確認してみると、DBのレコードが全て取得できている事が確認できているが、時系列毎にソートして表示されてはいない為探すのに苦労した。筆者が不必要に連打したのが原因でもあるが、レコードが増えていく事が想定されると目を通すのには可読性が悪く、少し労力が掛かるなと感じた。

Screen Shot 2020-03-21 at 19.02.00.png

WORKERも同じ事が言える。しかし、Eventは実行された時系列毎に保存されており、前回との差分の確認や以前のデータの内容を確認する事ができる。

Screen Shot 2020-03-21 at 19.07.59.png

最後に、日付レポートのデータストアも確認すると、日付毎にレコードが登録されている事が確認できる。

Screen Shot 2020-03-21 at 23.53.42.png

また、新たにREPLを作成し日時レポートとその内部の項目数を取得してみた。実行してみると、DailyReport
から日付毎にレポートを取得する事ができた。試しに異なる日付のレコードも登録した所、そちらも同じく取得する事ができた。

Screen Shot 2020-03-21 at 20.48.28.png

ハンドラなどを削除すると、Deletedセクションが表示される。Deleteセクションから削除されたハンドラやデータストアを見ることができ、復元すると即座にデプロイし直され元の状態に復元する事ができる。

ezgif.com-video-to-gif (1).gif

4. 総評

初めてDarkを触り、直感的操作で開発を行う事ができた。確かに、初期段階ではDarkのツールセットについての学習コストは発生するものの、従来の開発言語を覚える程では現段階は無いと感じる。

また、コードを書くことと並行してトレーシングが行われ、Dark内で作成したものはオートセーブされる為、ウィンドウを閉じても再度立ち上げると以前の状態から再開する事ができるので逐一保存する面倒もない。

デプロイレスと言う指向は、これまでいくつかのサービスで実装しようと試みて来たが、Darkが初めての成功例ではないだろうか。開発者は、アプリケーションのコードを書くだけで良く、書き終わったコードは既にホストされていると言うのは非常に有り難い事である。CircleCIを開発してきたPaul Bigger氏だからこそ、デプロイに深く精通し、その中に存在する課題を解決する策として見出せたのだと思う。

ベータ版ではあるが、既にある程度の開発も可能でReact SPAや、Slackbotの開発もできるようなので今後試しに実装してみようと思う。

しかし、バグもまだ存在しているのでこれからの改善に大きく期待すると共に今後の開発に貢献していけたらと思う。

最後に、本記事を見てDarkを触れてみたいと感じた人が少しでも増えてくれれば幸いである。ベータ版は、Darkの公式サイトから登録が可能で招待されるまでに時間は掛かる(筆者はベータ版開始から大体2,3ヶ月待った)が是非この機会に登録して欲しい。

5. 謝辞

本記事を作成するに至り、快く承諾してくれたCEOのEllen Chisaに深く感謝する。

参考文献

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 2

webのコンテナが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/api

start.shをwebのコンテナ内に入れます。今回はmntに入れました。
docker-compose.ymlを以下のように書き換えます。

docker-compose.yml
volumes:
      - ./api/scripts:/mnt
    entrypoint: mnt/start.sh

entrypointを.shにして、ループ処理をさせてから
1番最後のexecでコンテナを立ち上げるようにします。

ただし、mysqladmin pingを実行する為には、
mysql-clientをインストールさせる必要があります。
Dockerfileにそれ用の記述を書き加えます。

Dockerfile
RUN 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.go
db := mysql.ConnectDB()
    db.LogMode(true)
    defer db.Close()

importにgormを追加

import (
"github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

以下のように書き足しました。

main.go
db, 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も全然分かっていない未熟者でして不備もあるかと思います。
批評、マサカリ大歓迎です!
何かありましたら、ぜひご意見アドバイス等等下さいませ〜!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 2

webのコンテナが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/api

start.shをwebのコンテナ内に入れます。今回はmntに入れました。
docker-compose.ymlを以下のように書き換えます。

docker-compose.yml
volumes:
      - ./api/scripts:/mnt
    entrypoint: mnt/start.sh

entrypointを.shにして、ループ処理をさせてから
1番最後のexecでコンテナを立ち上げるようにします。

ただし、mysqladmin pingを実行する為には、
mysql-clientをインストールさせる必要があります。
Dockerfileにそれ用の記述を書き加えます。

Dockerfile
RUN 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.go
db := mysql.ConnectDB()
    db.LogMode(true)
    defer db.Close()

importにgormを追加

import (
"github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/mysql"
)

以下のように書き足しました。

main.go
db, 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も全然分かっていない未熟者でして不備もあるかと思います。
批評、マサカリ大歓迎です!
何かありましたら、ぜひご意見アドバイス等等下さいませ〜!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む