20201013のdockerに関する記事は13件です。

Rustの開発環境を整えるまでにやったことメモ

環境

version 備考
windows10 Pro 1903 (OSビルド) 18362.1082
Docker Desktop 2.4.0.0(48506) wsl2で動作
vscode 1.49.3

コンテナで開発・ビルドできるようにする。
しばらく離れていた&環境が変わったので自分用備忘録
wsl2+Docker Desktopに関してはここここここを参考にした。

hyper-vからの乗り換えの場合、
wsl2を使えるようにしてからhyper-vを無効化したらエラーが出たので、

  1. (Docker Desktopアンインストール)
  2. hyper-v 無効化
  3. wsl2有効化
  4. (Docker Desktop再インストール)

とすると確実かもしれない。

本編

1. コンテナイメージの取得・確認

PowerShellにて

PS:> docker pull rust:latest
     (結果略)

PS:> docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
rust                latest              c69fb046b993        3 days ago          1.28GB

2. composeファイルの作成

vscodeで開発用コンテナを立ち上げるために、docker-composeファイルを作成します。
vscodeの機能で作成することもできますが、追加で少しやりたいことがあるため自作します。

docker-compose.local.yml
version: '3.7'

services:
  rust-dev:
    image: rust:latest
    container_name: rust_dev
    working_dir: /app
    volumes:
      - ./:/app
      - rust_dev_target:/app/target
    tty: true
    environment:
      USER: xxxxx # cargo で必要になる
      CARGO_BUILD_TARGET_DIR: /app/target

volumes:
  rust_dev_target:
    external: true

基本的な部分の解説はdocker-composeのリファレンスとか、その他の解説記事に任せるとして、
やりたかったことに関わる2点だけ

  1. environmentのCARGO_BUILD_TARGET_DIR
    この環境変数で指定したディレクトリに、targetが吐き出される
  2. volumes
    node_modulesを隔離するための方法から着想を得たやり方。
    CARGO_BUILD_TARGET_DIRでtargetフォルダをプロジェクトの外に出し、1
    そこにrust_dev_targetという外部ボリュームをマウントしなおしている。
    これによってコンテナを削除してもtargetの中身を維持することができる。

小技とか

composeファイル内で変数を使う

composeファイルと同じ階層に.envファイルを置いておくと、環境変数として自動で読み込んでくれる。2
これを使って一部置き換えておくことで、例えばマウント先のフォルダ変えたいなーってなったときに少しだけ便利。
composeファイル内での環境変数の参照は${~~}で出来る

.env
mnt=/app
docker-compose.local.yml
version: '3.7'

services:
  rust-dev:
    image: rust:latest
    container_name: rust_dev
    working_dir: ${mnt}
    volumes:
      - ./:${mnt}
      - rust_dev_target:${mnt}/target
    tty: true
    environment:
      USER: xxxxx 
      CARGO_BUILD_TARGET_DIR: ${mnt}/target

volumes:
  rust_dev_target:
    external: true

3. vscodeでコンテナを立ち上げる

  1. 適当なフォルダを作って上記のcomposeファイルを置き、codeで開く
  2. vscodeに拡張機能Remote - Containersを追加する。
  3. 左下に追加される緑のボタンを押し、
    Remote-Containers: Add Development Container Configuration Files...
    を選択する
  4. From 'docker-compose.local.yml'を選択する。
    すると、.devcontainerというフォルダにdevcontainer.jsondocker-compose.yml が作成されるのでそれぞれ編集する

    docker-compose.yml
    version: '3.7'
    services:
    rust-dev:
      # volumes: # この行コメントアウトした
        # Update this to wherever you want VS Code to mount the folder of your project
        # - .:/workspace:cached # この行もコメントアウト
    
      # Overrides default command so things don't shut down after the process ends.
      command: /bin/sh -c "while sleep 1000; do :; done"
    

    こちらはvolumesをコメントアウトするだけ。
    見やすさのために省略しているが、コメントに説明が書いてあるので、読んで適宜変更。

    devcontainer.json
    {
        "name": "rust?",
        "dockerComposeFile": [
            "..\\docker-compose.local.yml",
            "docker-compose.yml"
        ],
        "service": "rust-dev",
        "workspaceFolder": "/app",
        "settings": {
            "terminal.integrated.shell.linux": null,
            "editor.formatOnSave": true
        },
        "extensions": [
            "rust-lang.rust",
            "vadimcn.vscode-lldb"
        ]
    }
    
    

    分かる範囲で詳細
    • name
      これだけコメントがない。vscodeでの表示上の名前、だけだと思う。
    • dockerComposeFile
      composeファイルを読み込む順に書く。composeファイルをさらに分割している場合はここに書けばよい。
      基本的には変更する必要はない。
    • service
      composeファイルのservicesに定義した名前。複数定義している場合はメインとなるものを。
      基本的には変更する必要はない
    • workspaceFolder
      composeファイルで定義した、working_dirと同じ値を設定する。
      コメントアウトすると/を開くのでdocker-compose.local.ymlで定義した値が上書きされるっぽい
      じゃあworking_dir要らなかったじゃん。ソダネー
    • settings
      vscodeのsetting.jsonに書いている内容をそのまま書ける。
      コンテナ内部にだけ適用されるので、下記extensionsで追加する拡張機能に対する設定はここに書いておけば間違いない。
    • extensions
      コンテナに導入したい拡張機能のIDを書く。IDは拡張機能の名前の横に表示されている。
      image.png
      例)図の灰色ががっている部分がID
      とりあえずRust拡張機能とCodeLLDB。他にいいものがあれば知りたい。
    • forwardPorts
      コンテナのポート開放する際にはアンコメントする。composeファイルにportsで書いても同じ。

  5. volume作成

    PS:> docker volume create rust_dev_target
    
    PS:> docker volume ls
    DRIVER              VOLUME NAME
    local               rust_dev_target
    

    composeファイルでexternal:trueしているので先に作っておかないとエラーになる

  6. 左下の緑のボタンを押し、
    Remote-Containers: Reopen in Container
    を選択する。準備編終わり。

4. プロジェクトの作成

開発はThe Rust Programming Languageを参考にすれば問題ないと思う。
著者とコミュニティに感謝。
Cargoでプロジェクトを作成すればHello worldが出来る。

TERMINAL
root@02080d70998d:/app# cargo --version
cargo 1.47.0 (f3c7e066a 2020-08-28)
root@02080d70998d:/app# cargo new hello --bin
     Created binary (application) `hello` package
root@02080d70998d:/app# cd hello
root@02080d70998d:/app/hello# cargo run
()
Hello, world!

5. オートフォーマット

右下のベルマークのところに
Some Rust components not installed. Install?
と出るのでYesでインストール。
終わると保存したときに整形されるようになる。
コンテナを削除(orリビルド)した場合はもう一度やる必要がある。

6. マルチステージビルド

リリースビルドと同時にコンテナ化したいので専用のDockerfileとcomposeファイルを作る。
ついでにパッケージインストールはキャッシュさせて高速化したいのでそのあたりも対応。 3

Dockerfile
FROM rust:latest AS builder

WORKDIR /app
ENV USER=root

# RUN rustup target add x86_64-unknown-linux-musl

RUN cargo new my_prj
WORKDIR /app/my_prj

COPY ./hello/Cargo.toml ./hello/Cargo.lock ./
RUN cargo build --release 

COPY ./hello/src ./src
RUN cargo build --release 

FROM debian:stretch-slim AS prod
ENV TZ=Asia/Tokyo
COPY --from=builder /app/my_prj/target/release/ /usr/local/bin
WORKDIR /app
CMD ["hello"]

空プロジェクトを一度ビルドしてからsrcを入れ替えて再ビルドしている。
alpine向けにビルドしようとしてうまくいかなかったので調査中。
続いてcompose

docker-compose.build.yml
version: '3.7'

services:
  builder:
    build:
      context: .
      dockerfile: DockerFile
      target: builder
    image: rust-test:builder

  rust_test:
    build:
      context: .
      dockerfile: DockerFile
      target: prod
    image: rust-test:latest

builderサービスをtarget: builder4として定義してあげることで、マルチステージビルドにおけるビルドステージにあたるコンテナにタグをつけておくことができる。
こうすることでdanglingイメージ(<none>:<none>のこと)にならないので、ビルド途中の状態がキャッシュ出来る。

7. 実行用compose作成

rust-test:latestに必要な環境変数とか与えて実行できるようにする。
目新しいことがないので割愛

おわりに

開発用のDBも同時に立ち上がるようにしたり、他にもやったことがあるけど今回はここまで


  1. targetフォルダは、通常プロジェクトフォルダの内部にできるが、composeファイルにプロジェクトファイルの名称を入れたくなかったため外出ししている 

  2. 別フォルダに置く場合はenv_filesに指定する。 

  3. Dockerfile的にはキャッシュしていると思うが、Rustのビルドの仕組みに明るくないので保証はできない。 

  4. AS で付けた名前 

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

コンテナ同士の通信について

概要

docker-compose.ymlで定義したコンテナは同じネットワーク下に置かれる。コンテナは互いにその名前でアクセスできる。

/var/www/container1# ping container2
64 bytes from ...省略

docker-compose.yml

こんなときコンテナ1から2にアクセスしたい。

docker-compose.yml
version: "3"

services:
  container1:
    build:
      context: ./docker/container1
    ports:
      - "3000:3000"

  container2:
    build:
      context: ./docker/container2
    ports:
      - "8080:80"

なぜか

ネットワーク内のDNSサーバーが立っていて名前解決をしている。
通常コンテナからホストマシンのローカルipにはアクセスできないが、DNSサーバーがコンテナごとに割り当てられたポートで名前解決してくれる✨
(digコマンドで動いているDNSサーバーがわかる)

おまけ

直接コンテナ内のhostsにipとドメインを書いて名前解決する方法。
ホストマシンのipアドレスはwifiの設定でip4・ip6で動的に変わったりするので意図せず開発環境が動かなくなる可能性あり。

docker-compose.yml
# ports:
#    - "8080:80"
extra_hosts:
      - "localhost-dev:${ホストマシンのipアドレス}"
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

docker を用いてlaravel6+nuxt.js+mysql+nginx構築

laravel6+nuxt.js+mysqlの構築

laravel,mysql,nginxの構築は@simotarooさんの記事が優秀なのでこちらを参考にしてください
https://qiita.com/shimotaroo/items/29f7878b01ee4b99b951

本題に関係ないんですけどngixってエンジンエックスって読むんですね,,,(笑

nuxt.jsの構築

@simotarooさんの記事で作成したdocker-compose.ymlに PORT:3000, HOST: 0.0.0.0を追加
理由としてコンテナで設定されているポート番号とnode.jsのポート番号が違うままだと繋がらないためです

docker-compose.yml
#docker-compose.ymlのバージョン
version: '3.8'
#docker volumeの設定
volumes:
  docker-volume:

#services以下に各コンテナの設定を書く
services:
  #Webサーバーのコンテナ
  web:
    image: nginx:1.18
    ports:
      - '8000:80'
    depends_on:
      - app
    volumes:
      - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
      - .:/var/www/html
  #アプリケーションのコンテナ
  app:
    build: ./docker/php
    volumes:
      - .:/var/www/html
    environment:
      PORT: 3000 #追加
      HOST: 0.0.0.0 #追加
    ports:
      - 3000:3000
  #データベースのコンテナ
  db:
    image: mysql:5.7
    ports:
      - '3306:3306'
    environment:
      MYSQL_DATABASE: ######
      MYSQL_USER: #####
      MYSQL_PASSWORD: #######
      MYSQL_ROOT_PASSWORD: ##########
      TZ: 'Asia/Tokyo'
    volumes:
      - docker-volume:/var/lib/mysql

dockerfileに RUN npm install -g create-nuxt-app を追加

dockerfile
FROM php:7.2-fpm

#composerのインストール
COPY --from=composer:1.10 /usr/bin/composer /usr/bin/composer

#npmのインストール
COPY --from=node:10.22 /usr/local/bin /usr/local/bin
COPY --from=node:10.22 /usr/local/lib /usr/local/lib

#パッケージ管理ツールapt-getの更新と必要パッケージのインストール
RUN apt-get update \
&& apt-get install -y \
git \
zip \
unzip \
&& docker-php-ext-install pdo_mysql bcmath

RUN npm install -g create-nuxt-app #追加
#コンテナ内に入った時のディレクトリを指定
WORKDIR /var/www/html

追加後はdocker-compose buildし変更を反映

反映できたらdocker内で

tarminal
root@########:/var/www/html npx create-nuxt-app ディレクトリ名

で実行

tarminal
?  Successfully created project ディレクトリ名

  To get started:

    cd ディレクトリ名
    npm run dev

  To build & start for production:

    cd ディレクトリ名
    npm run build
    npm run start

成功したらこんな画面になります。
作成したディレクトリ内に入り,npm run devを実行後動作確認で以下のURLに入ってnode.jsの画面が出れば成功です
http://localhost:3000/
お疲れ様でした?

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

開発環境でのメール確認用にMailHogを利用する(Docker利用)

開発環境でメールの確認方法が面倒だったり、配信先に気を使う場合がありますが、MailHogというツールを使うとローカル内に閉じたメールの確認ができる環境を簡単に立ち上げることができます。

MailHogはGo言語でできているらしいです。

以前はMailCatcherという別の類似ツールを利用していましたが、MailHogの方が構築が容易だったので乗り換えました。

docker-composeで以下の構成で立ち上げます。

ファイル構成
.
├── docker-compose.yml
└── php
    ├── docker
    │   ├── Dockerfile
    │   └── php.ini
    └── mail_test.php
docker-compose.yml

8025番ポートでwebインターフェースを利用するのでポートを設定します

version: '3'
services:
 php:
  build:
   context: ./
   dockerfile: php/docker/Dockerfile
  volumes:
   - ./php/:/var/www/html/
 mailhog:
  image: mailhog/mailhog
  ports:
   - "8025:8025" #webインターフェース用ポート
Dockerfile

phpからのメール送信にmhsendmailが必要なので、Dockerfileでインストールします。

FROM php:5.6-apache
WORKDIR /var/www/html
RUN curl -sSL https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 -o mhsendmail \
    && chmod +x mhsendmail \
    && mv mhsendmail /usr/local/bin/mhsendmail
COPY ./php/docker/php.ini /usr/local/etc/php/
php.ini

[mail function]を書き換えておきます。

[mail function]
; For Unix only.  You may supply arguments as well (default: "sendmail -t -i").
; http://php.net/sendmail-path
; sendmail_path = /usr/sbin/sendmail -t -i
; 以下に書き換え
sendmail_path = "/usr/local/bin/mhsendmail --smtp-addr=mailhog:1025"
mail_test.php
<?php

$to      = "hoge@localhost.local";
$subject = "TEST";
$message = "メールテスト";
$headers = "From: from@example.com";

mb_send_mail($to, $subject, $message, $headers);
docker-compose で up
$ docker-compose up -d
Building php
Step 1/4 : FROM php:5.6-apache
 ---> 24c791995c1e
Step 2/4 : WORKDIR /var/www/html
 ---> Using cache
 ---> 1294d05c5c03
Step 3/4 : RUN curl -sSL https://github.com/mailhog/mhsendmail/releases/download/v0.2.0/mhsendmail_linux_amd64 -o mhsendmail     && chmod +x mhsendmail     && mv mhsendmail /usr/local/bin/mhsendmail
 ---> Using cache
 ---> db0719944c4e
Step 4/4 : COPY ./php/docker/php.ini /usr/local/etc/php/
 ---> 264eb166413a
Successfully built 264eb166413a
Successfully tagged mailhog_php:latest
WARNING: Image for service php was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
Pulling mailhog (mailhog/mailhog:)...
latest: Pulling from mailhog/mailhog
df20fa9351a1: Already exists
ed8968b2872e: Pull complete
a92cc7c5fd73: Pull complete
f17c8f1adafb: Pull complete
03954754c53a: Pull complete
60493946972a: Pull complete
368ee3bc1dbb: Pull complete
Digest: sha256:8d76a3d4ffa32a3661311944007a415332c4bb855657f4f6c57996405c009bea
Status: Downloaded newer image for mailhog/mailhog:latest
Creating mailhog_php_1     ... done
Creating mailhog_mailhog_1 ... done
mailhogのWEBインターフェース

localhost:8025で表示

image.png

phpのコンテナ内からメール送信スクリプトを実行
$ docker exec -it mailhog_php_1 /bin/bash
root@9c4b6a5df613:/var/www/html# php mail_test.php

windows上にメールの到着を通知が表示され

image.png
メールが到着していることが確認できます

image.png
image.png

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

Dockerでのエラー解消に繋がったコマンド

はじめに

Docker環境構築において、役に立ったコマンドを備忘録として残します。
作成したものの、エラーが解決出来なかったときにおすすめです。

コンテナの停止、削除

コンテナの停止

コンテナ確認
$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
797b4265f3d2        try_web             "/bin/sh -c 'rm -f t…"   24 minutes ago      Up 23 minutes       0.0.0.0:3000->3000/tcp              try_web_1

CONTAINER IDを指定します。

コンテナ停止
$ sudo docker stop 797b4265f3d2
$ sudo docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES

コンテナの削除

あくまで停止しただけなので、削除も行います。

コンテナ確認
$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES
797b4265f3d2        try_web             "/bin/sh -c 'rm -f t…"   24 minutes ago      Up 23 minutes       0.0.0.0:3000->3000/tcp              try_web_1

CONTAINER IDを指定します。

コンテナ削除
$ sudo docker rm 797b4265f3d2
$ sudo docker ps -a
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                               NAMES

imageの削除

コンテナは削除しましたが、imageは残っています。

imageの確認
$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
try_web             latest              c0ee642ca6bd        About an hour ago   1.01GB

IMAGE IDを指定します。

imageの削除
$ sudo docker rmi c0ee642ca6bd
$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

docker-compose exec app rails db:createのエラー

私の場合ですが、Access denied for user のエラーが出ていたため、
mysqlを確認しました。
# の後にmysql -u root -pと記述し、mysqlにログインします。

ターミナル
$ docker-compose up -d
$ docker-compose exec db bash
root@e5504121a08b:/# mysql -u root -p

終了する場合はexitと記述してください。

ここでパスワードが間違っていたため、
docker-compose exec app rails db:create
のエラーが出ていました。

まとめ

あくまでも一例のため、まだまだ便利なコマンドはあると思います。
私と同じ状況になった方の助けになれば幸いです。

またtwitterではQiitaにはアップしていない技術や考え方もアップしていますので、
よければフォローして頂けると嬉しいです。
詳しくはこちら https://twitter.com/japwork

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

dockerにDjango(python)環境を構築する方法

dockerにDjango(python)環境を構築する方法

docker上にDjangoを使ったプロジェクトとアプリを作成して、開発サーバーを使ってブラウザに表示する手順のまとめ。

手順

  1. PJディレクトリの作成
  2. requirements.txtファイルの作成
  3. dockerfileの作成
  4. docker-compose.ymlの作成
  5. 設定ファイルからコンテナを作成する
  6. settigs.pyの編集
  7. コンテナの起動
  8. appの作成
  9. views.pyの編集
  10. urls.pyの作成と編集


参考にしたサイト
Docker公式 Django作成
Django公式 初めてのApp作成

PJディレクトリの作成

mkdir django
cd django

requirements.txtファイルの作成

インストールするパッケージを指定したファイルを作成する。

touch requirements.txt
requirements.txt
Django>=3.0,<4.0
psycopg2-binary>=2.8

DjangoとPsycopgをインストールする。

Psycopgは、PostgreSQLを効率的に使うためのツール。
-binaryはOS専用言語版のためコンパイル不要。

dockerfileの作成

python3のイメージを作成するためのdockerfileを作成する。

touch dockerfile
dockerfile
FROM python:3
ENV PYTHONUNBUFFERED=1
RUN mkdir /code
WORKDIR /code
COPY requirements.txt /code/
RUN pip install -r requirements.txt
COPY . /code/

From イメージ名
指定したイメージをdockerhubからpullする。(dockerhubにない場合はローカルを探す)

ENV 変数名
環境変数の設定。PYTHONUNBUFFEREDという変数に1をいれる。
コンソールの標準出力(stdout)と標準エラー出力(stderr)がエラー発生時にすぐに出力されるようにする(バッファを無効にする。pythonの-uオプションと同じ)

PYTHONUNBUFFERED



RUN コマンド
コンテナ内に /code というディレクトリを作成する

WORKDIR ディレクトリ
指定したディレクトリ(/code)をルートディレクトリにする

COPY ホスト コンテナ
ホストのrequirements.txtを、コンテナの/code/配下にコピーする

pip install -r ファイルパス
指定ファイルに書かれたパッケージをインストールする。

ファイルはrequirements.txtを使用することが一般的。
「-r」 = 「--requirement」


docker-compose.ymlの作成

イメージを作成するためのdocker-composeファイルを作成する。複数のコンテナを含んだイメージを作成するためのファイル。(dockerfileは1つのコンテナのみ)

touch docker-compose.yml
docker-compose.yml
version: "3.8"

services:
  db:
    image: postgres
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
  web:
    build: .
    command: python manage.py runserver 0.0.0.0:8000
    volumes:
      - .:/code
    ports:
      - "8100:8000"
    depends_on:
      - db

イメージの中に2つのコンテナを用意する。

version: "3.8"
docker-compose.ymlの記述方法のバージョンを指定。
バージョンにより書き方が異なるため指定が必要。

services
作成するコンテナ。dbとwebの2つを作成する。

images
使用するイメージを指定。docker hubからpostgresをpullしてコンテナを作成する。

environment
環境変数を設定。DBをpostgresに設定。user名とPWは任意で。

build ディレクトリパス
指定したディレクトリ内のdockerfileからコンテナを作成する。

ファイル名の指定がない場合はdockerfileを選択。異なる名前をつけている場合はファイル名も指定する。

commnad
コンテナ内で実行するコマンド。localhost:8000のサーバーを起動。

manage.pyのrunserverを実行。
manage.pyはコマンドラインユーティリティ。便利なコマンドをインポートしてくる。

volumes: -ホスト:コンテナ
ホストのディレクトリをコンテナ内の指定したディレクトリと連動させる(マウントする)。

ホストのプロジェクトフォルダとコンテナ内の/codeを同期している。
同期したくないファイルは.dockerignoreを作成しファイル/フォルダ名を記述する。



ports: -"ホスト:コンテナ"
ホスト側のポートを、コンテナ側の指定したポートに繋ぐ。

localhost:8100にアクセスすると、コンテナ内の8000ポートに繋がる。

depends_on
webコンテナをdbコンテナと接続する。以前はlinkが使われていたが、v2.0以上は特に記述しなくとも、docker-compose.yml内のコンテナがネットワークで繋がる。(depends_onも不要?)


設定ファイルからコンテナを作成する

docker-compose run web django-admin startproject mysite

docker-compose run [サービス名] [コマンド]
指定したサービス名のコンテナを作成し、コマンドを実行する。

実行したフォルダ内にあるdocker-comopse.ymlファイルを元にイメージ作成、コンテナ作成・起動までを一気に行う。

ここではwebサービスを起動。

django-admin startporject PJ名
PJを作成する。

指定したPJ名(mysite)でディレクトリが作成される。

作成されるファイル
mysite/
    manage.py
    mysite/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py


settigs.pyの編集

mysite/settings.pyにDBの設定を書き込む。
初期設定はsqlite3が指定されているので上書きする。

mysite/settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'postgres',
        'USER': 'postgres',
        'PASSWORD': 'postgres',
        'HOST': 'db',
        'PORT': 5432,
    }
}
(参考)デフォルトの記述
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


コンテナの起動

docker-compose up

省略
web_1  | Django version 3.1.2, using settings 'mysite.settings'
web_1  | Starting development server at http://0.0.0.0:8100/
web_1  | Quit the server with CONTROL-C.

作成したコンテナを起動する。

指定したサーバーが立ち上がる。状態を見るには別のbash windowを開いてdocker psを実行する。

$ docker ps
CONTAINER ID        IMAGE                       COMMAND                  CREATED             STATUS              PORTS                                            NAMES
9615ecd4098f        django_web                  "python manage.py ru…"   16 minutes ago      Up About a minute   0.0.0.0:8100->8000/tcp                           django_web_1

バックグラウンドで起動させたい場合は、up -dにする。

localhost:8100にアクセスするとページが開ける(コンテナ内のloaclhost:8000にアクセスできる)

image.png


appの作成

コンテナの中に入ってアプリを作成する。

##起動中のコンテナでbashを立ち上げる
$ docker exec -it django_web_1 bash
root@faef5f41e1c7


##django appの作成
root@faef5f41e1c7:/code# python manage.py startapp polls


##フォルダの確認
root@faef5f41e1c7:/code# ls
Dockerfile  docker-compose.yml  manage.py  mysite  polls  requirements.txt

コンテナ内でpollsの作成に成功。
ホスト側にもディレクトリが作成作成される。(volumeを指定して、同期しているため)

image.png

プロジェクトとappの違い

djangoのコマンドで、startprojectとstartappという似たコマンドを実行している。

django-admin startproject mysite
python manage.py startapp polls

projectは1つだけ存在するもので、appは複数存在することができる。

mysiteというプロジェクトの中にpollsというappを一つだけ作成した状態。


views.pyの編集

画面の表示を操作するため、先ほど作成したpollsフォルダ内のviews.pyを編集する。

コンテナ内にエディタはインストールしてないので、一旦コンテナから抜ける。

root@faef5f41e1c7:/code# exit



▼画面上に「Hello, This is Django Polls」と表示されるプログラムを記述。

polls/views.py
from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, This is Django Polls")


urls.pyの作成と編集

polls内のURLの設定と、プロジェクトとしてのurlの設定をする必要がある。

polls/urls.py
from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
mysite/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]

pollsのurls.pyをpathとして認識させ、polls/でアクセスできるようにする。

記述したら、localhost:8100/polls でページをロード。

image.png

無事表示完了。

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

docker-compose run, up, build, createの違い。それぞれの役割を知る。

docker-composeコマンドにrun, up, build, createなど、似たようなのがたくさんある。それぞれの役割を理解することで混乱を防ぐ。

コマンド 対象 備考
run イメージ&コンテナ作成 1つのサービスを指定。upは全サービス(コンテナ)を一括起動
up イメージ&コンテナ作成&起動 全コンテナを一括起動。起動まで行う
build イメージの作成 -
create コンテナの作成 -
up コンテナの起動 -



▼流れでコマンドを分類

イメージ作成 コンテナ作成 コンテナ起動
build create start
run
up



▼コマンドで見るとこんな感じ

docker-compose run/up = docker-compose build && docker-compose create && docker-compose start

docker-compose run
docker-compose up
docker-compose build
docker-compose create
docker-compose start

基本的に使うコマンドはdocker upになるはず。

runやupを実行する際に、イメージが既存の場合は再度作成しないため、dockerfileやdocker-compose.ymlに変更がある場合は、再ビルドでイメージを上書きする。

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

DockerでRailsアプリの開発環境構築【Docker, Rails, Puma, Nginx, MySQL】

こんにちは.
今回もRailsアプリの開発環境をDockerで構築する手順をまとめてみました.
前回はRails用とデータベース用の2つのコンテナを起動しましたが,今回は,WebサーバーとしてNginxのコンテナも起動してみました.

事前準備

環境

Ruby: 2.5.8
Rails: 5.2.4.4
MySQL: 5.7.31
Nginx: 1.19.2
Docker: 19.03.12
Docker Compose: 1.26.2

手順

1. ディレクトリ,ファイルの作成

全体の構成は以下の通りです.
それではこの構成図の通り,ディレクトリとファイルを作成していきます.

全体の構成
/test-app
├── Dockerfile
├── Dockerfile.nginx
├── docker-compose.yml
├── nginx.conf
├── Gemfile
└── Gemfile.lock

まずは, プロジェクトのルートディレクトリを作成します.

terminal
$ mkdir test-app

そして, ルートディレクトリの直下に

  • Dockerfile
  • Dockerfile.nginx
  • docker-compose.yml
  • nginx.conf
  • Gemfile
  • Gemfile.lock

これらを作成します.

terminal
$ cd test-app
$ touch Dockerfile Dockerfile.nginx docker-compose.yml nginx.conf Gemfile Gemfile.lock 

2. ファイルの編集

上記で作成したそれぞれのファイルの中身は以下のようになります.
(Gemfile.lockは空のままにします.)

Dockerfile
FROM ruby:2.5
RUN apt-get update && apt-get install -y \
    build-essential \
    node.js
WORKDIR /test-app
COPY . /test-app
RUN bundle install
Dockerfile.nginx
FROM nginx
RUN rm -f /etc/nginx/conf.d/*
COPY nginx.conf /etc/nginx/conf.d/test-app.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
containers/nginx/nginx.conf
# プロキシ先の指定
# Nginxが受け取ったリクエストをバックエンドのpumaに送信
upstream test-app {
  # ソケット通信したいのでpuma.sockを指定
  server unix:///test-app/tmp/sockets/puma.sock;
}

server {
  listen 80;
  # ドメインもしくはIPを指定
  server_name _;

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  # ドキュメントルートの指定
  root /test-app/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @test-app;
  keepalive_timeout 5;

  # リバースプロキシ関連の設定
  location @test-app {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://test-app;
  }

  location /favicon {
    empty_gif;
    access_log    off;
    log_not_found off;
  }
}
Gemfile
source 'https://rubygems.org'
gem 'rails', '~>5.2'
docker-compose.yml
version: '3'
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    command: bundle exec puma -C config/puma.rb
    volumes:
      - .:/test-app
    tty: true
    stdin_open: true
    depends_on:
      - db

  db:
    image: mysql:5.7
    environment:
      - 'MYSQL_ROOT_PASSWORD=password'
    volumes:
      - 'db-data:/var/lib/mysql'

  web:
    build:
      context: .
      dockerfile: Dockerfile.nginx
    volumes:
      - ./public:/test-app/public
      - ./tmp:/test-app/tmp
    ports:
      - 80:80
    depends_on:
      - app

volumes:
  db-data:

3. Appのコンテナ内にRailsのセットアップを行う

terminal
$ docker-compose run --rm app rails new . --force --database=mysql --skip-bundle

4. tmp/socketsフォルダ作成,作成されたファイルを編集

まず,tmpフォルダ内にsocketsフォルダを作成します.

そして,Railsのセットアップにより作成されたファイルのうち以下の3つを編集します.
- config/database.yml
- config/puma.rb
- config/environments/production.rb

config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password   #docker-compose.ymlのMYSQL_ROOT_PASSWORDの値を設定する
  host: db   #docker-compose.ymlのservice名と合わせる

development:
  <<: *default
  database: test-app_development
config/puma.rb
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count

port        ENV.fetch("PORT") { 3000 }

environment ENV.fetch("RAILS_ENV") { "development" }

pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
config/environments/production.rb
# Do not fallback to assets pipeline if a precompiled asset is missed.
config.assets.compile = true   #デフォルトではfalseなので,trueにかえる

このconfig/environments/production.rbの変更はしなくても開発環境の構築については問題はないが,あとあと本番環境にデプロイした際にアセットプリコンパイルのエラーが出たので,ここで変更しています.

5. コンテナの起動, DBの作成

terminal
$ docker-compose up -d --build
$ docker-compose exec app rails db:create

これで http://localhost にアクセスすると, Railsのホーム画面が表示されるはずです.

参考

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

マルチステージビルドなDockerfileのARGについて

TL;DR

マルチステージビルドな Dockerfile で ARG を使う場合は、ちゃんと各ステージごとに使う ARG を宣言しましょう!
また、複数のステージで使う ARG にデフォルト値があるのなら、グローバルな ARG を定義してそこにデフォルト値を記載しましょう!

こんな感じで作ればオッケーです!

Dockerfile
# 各ステージで共通して利用する ARG にデフォルト値があるのなら、最初の FROM より前に定義する。
ARG YOUR_ARG="Default value"

FROM alpine:latest as first_stage
# ARG は、利用する各ステージごとに宣言する必要がある。
ARG YOUR_ARG
RUN echo "1st stage: ${YOUR_ARG}"

FROM alpine:latest as second_stage
# ARG は、利用する各ステージごとに宣言する必要がある。
ARG YOUR_ARG
RUN echo "2nd stage: ${YOUR_ARG}"

マルチステージビルドと ARG の関係性について

マルチステージビルドでは、 ARG や ENV のスコープはステージごとに制限されているらしいです。
皆さんご存知でしたか?わたくしはステージ内で ARG の値を参照できていないことに気づいて初めて知りました。

こちらのコメントによれば、

Correct, Dockerfile instructions, including ENV vars and ARG are scoped per build-stage, and will not be preserved in the next stage; this is by design.
You can, however, set a global ARG (set before the first build-stage), and use that value in each build-stage;
(意訳)ENV や ARG を含む Dockerfile の命令はビルドステージごとのスコープになっており、次のステージに持ち越されません。これは設計上そのようになっています。しかしながら、グローバルな ARG(最初のビルドステージより前で定義する)を使えば、ビルドステージごとにその値を利用することができます。

とのことです。マジか。

公式ドキュメントにもしっかり載ってましたわ。

ARG 命令の変数スコープは、それが定義されたビルドステージが終了するときまでです。 複数のビルドステージにおいて ARG を利用する場合は、個々に ARG 命令を指定する必要があります。

最初にドキュメント読んだときは、ビルドステージの意味も分からないまま「あーそーゆーことね完全に理解した」と思ったような気が。。。

ARG の動きを見てみる

では実際にマルチステージビルドでの ARG の動きを見ていきましょう。

本稿の動作確認環境

本稿の執筆にあたっては、下記のバージョンで動作を確認しています。

# docker --version
Docker version 18.09.1, build 4c52b90

Dockerfile

今回の検証で使う Dockerfile は次の通りです。

Dockerfile
# ARG1 は、グローバルに宣言し、グローバルなデフォルト値を設定する。
ARG ARG1="arg1 global default value"
# ARG2 は、グローバルに宣言するが、グローバルなデフォルト値は設定しない。
ARG ARG2
# ARG3 は、グローバルに宣言しない。
# ARG ARG3


FROM alpine:latest as first_stage

# first_stage では、各 ARG を宣言し、スコープ内のデフォルト値を設定する。
ARG ARG1="arg1 first stage value"
ARG ARG2="arg2 first stage value"
ARG ARG3="arg3 first stage value"
RUN echo -e "first_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"


FROM alpine:latest as second_stage

# second_stage では、各 ARG を宣言するが、スコープ内のデフォルト値は設定しない。
ARG ARG1
ARG ARG2
ARG ARG3
RUN echo -e "second_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"


FROM alpine:latest as third_stage

# third_stage では、すべての ARG を宣言しない。
# ARG ARG1
# ARG ARG2
# ARG ARG3
RUN echo -e "third_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"

各 ARG は下記のように設定しています。

ARG 名 設定内容
ARG1 グローバルな ARG として設定。デフォルト値も指定。
ARG2 グローバルな ARG として設定。デフォルト値は指定しない。
ARG3 グローバルな ARG として設定しない。

また、各ステージは次のように設定しています。

Stage 名 設定内容
first_stage 各 ARG を宣言。ステージ内のデフォルト値を設定。
second_stage 各 ARG を宣言。ステージ内のデフォルト値は設定しない。
third_stage ARG を宣言しない。

--build-arg 指定なしでビルド

Docker Image のビルド時に、 --build-arg による値の指定を行わずにビルドしてみます。

# docker build . --no-cache
Sending build context to Docker daemon  14.85kB
...snip...
Step 7/14 : RUN echo -e "1st stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in 2bbe78634ee8
first_stage:
        ARG1=arg1 first stage value
        ARG2=arg2 first stage value
        ARG3=arg3 first stage value
...snip...
Step 12/14 : RUN echo -e "2nd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in 0c28af93ea9b
second_stage:
        ARG1=arg1 global default value
        ARG2=
        ARG3=
...snip...
Step 14/14 : RUN echo -e "3rd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in cbca9ed88691
third_stage:
        ARG1=
        ARG2=
        ARG3=
...snip...
  • first_stage では、いずれもステージ内のデフォルト値となりました。 ARG1 を見ると、グローバルなデフォルト値よりもステージ内のデフォルト値が優先されていることがわかります。これは、よりスコープが狭い方の値を優先的に使う動きになっており、理にかなっている動きと言えるでしょう。
  • second_stage では、 ARG1 のみグローバルなデフォルト値が表示されています。ステージ内で ARG を宣言はしたものの、何も値を指定しなければグローバルなデフォルト値を利用するということですね。 ARG2, ARG3 には何も表示されていませんが、これはその値を指定している場所がどこにもないからですね。
  • third_stage ではすべての値が表示されていません。 ARG1 のグローバルなデフォルト値すら表示されていないということは、 ARG を利用するためにはそのステージごとに宣言する必要があるということでしょう。

--build-arg 指定ありでビルド

今度は --build-arg による値の指定ありでビルドしてみます。

# docker build --build-arg ARG1="build arg1 value" --build-arg ARG2="build arg2 value" --build-arg ARG3="build arg3 value" . --no-cache
Sending build context to Docker daemon  14.85kB
...snip...
Step 7/14 : RUN echo -e "1st stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in 10b37c5a524b
first_stage:
        ARG1=build arg1 value
        ARG2=build arg2 value
        ARG3=build arg3 value
...snip...
Step 12/14 : RUN echo -e "2nd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in e70e3ff9fe9b
second_stage:
        ARG1=build arg1 value
        ARG2=build arg2 value
        ARG3=build arg3 value
...snip...
Step 14/14 : RUN echo -e "3rd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in e675e8f648e8
third_stage:
        ARG1=
        ARG2=
        ARG3=
...snip...
  • first_stage, second_stage ともにすべての値が --build-arg で指定した値になりました。これは公式ドキュメントの ARG のデフォルト値で説明されている内容と一致しますね。 --build-arg で指定された値はグローバルなデフォルト値及びステージ内のデフォルト値のどちらよりも優先されます。
  • third_stage ではまたもやすべての値が表示されていません。 --build-arg で値が指定されていても、そのステージ内で宣言されていなければやはり利用できないようです。

まとめ

  • マルチステージビルドな Dockerfile で ARG を利用する際には、すべてのステージでそれぞれ利用する ARG を宣言する必要がある。
  • 複数のステージで利用している ARG のデフォルト値を定義したければ、グローバルな ARG を定義(最初の FROM よりも前に定義)してそこにデフォルト値を記載すると良い。
  • ARG の値の優先度は
    1. --build-arg での指定値 [優先度高]
    2. ステージ内のデフォルト値
    3. グローバルなデフォルト値 [優先度低]

おしまい。

宣伝

マルチステージビルドの ARG の挙動に気づくきっかけとなったプロジェクトの宣伝です。

Minecraft の Bedrock Server を楽に運用しようぜ!という趣旨のプロジェクトです。
Minecraft ってたまーにアップデートが入るのですが、クライアントは大体勝手にアップデートされるものの、 Bedrock Server は自動的にアップデートされず、手動で新しいバージョンをダウンロードして Zip ファイルを展開してバイナリファイルを置き換えてサービスを再起動して・・・とかやる必要がありました。
このプロジェクトは、一度セットアップだけやってしまえば、あとは全自動で Bedrock Server をアップデートしてくれます!ついでに Docker を使って Bedrock Server を構築するので環境を汚しません!

よろしくお願いします!

本当におしまい。

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

マルチステージビルドなDockerfileのARGでやらかしがちな件について

TL;DR

マルチステージビルドな Dockerfile で ARG を使う場合は、スコープがステージ内に閉じていることに注意が必要です!ちゃんと各ステージごとに使う ARG を宣言しましょう!
また、複数のステージで使う ARG にデフォルト値があるのなら、グローバルな ARG を定義してそこにデフォルト値を記載しましょう!

こんな感じで作ればオッケーです!

Dockerfile
# 各ステージで共通して利用する ARG にデフォルト値があるのなら、最初の FROM より前に定義する。
ARG YOUR_ARG="Default value"

FROM alpine:latest as first_stage
# ARG は、利用する各ステージごとに宣言する必要がある。
ARG YOUR_ARG
RUN echo "1st stage: ${YOUR_ARG}"

FROM alpine:latest as second_stage
# ARG は、利用する各ステージごとに宣言する必要がある。
ARG YOUR_ARG
RUN echo "2nd stage: ${YOUR_ARG}"

マルチステージビルドと ARG の関係性について

マルチステージビルドでは、 ARG や ENV のスコープはステージごとに制限されているらしいです。
皆さんご存知でしたか?わたくしは作った Dockerfile のステージ内で ARG の値を参照できていないことに気づいて初めて知りました。

こちらのコメントによれば、

Correct, Dockerfile instructions, including ENV vars and ARG are scoped per build-stage, and will not be preserved in the next stage; this is by design.
You can, however, set a global ARG (set before the first build-stage), and use that value in each build-stage;
(意訳)ENV や ARG を含む Dockerfile の命令はビルドステージごとのスコープになっており、次のステージに持ち越されません。これは設計上そのようになっています。しかしながら、グローバルな ARG(最初のビルドステージより前で定義する)を使えば、ビルドステージごとにその値を利用することができます。

とのことです。マジか。

公式ドキュメントにもしっかり載ってましたわ。

ARG 命令の変数スコープは、それが定義されたビルドステージが終了するときまでです。 複数のビルドステージにおいて ARG を利用する場合は、個々に ARG 命令を指定する必要があります。

最初にドキュメント読んだときは、ビルドステージの意味も分からないまま「あーそーゆーことね完全に理解した」と思ったような気が。。。

ARG の動きを見てみる

では実際にマルチステージビルドでの ARG の動きを見ていきましょう。

本稿の動作確認環境

本稿の執筆にあたっては、下記のバージョンで動作を確認しています。

# docker --version
Docker version 18.09.1, build 4c52b90

Dockerfile

今回の検証で使う Dockerfile は次の通りです。

Dockerfile
# ARG1 は、グローバルに宣言し、グローバルなデフォルト値を設定する。
ARG ARG1="arg1 global default value"
# ARG2 は、グローバルに宣言するが、グローバルなデフォルト値は設定しない。
ARG ARG2
# ARG3 は、グローバルに宣言しない。
# ARG ARG3


FROM alpine:latest as first_stage

# first_stage では、各 ARG を宣言し、スコープ内のデフォルト値を設定する。
ARG ARG1="arg1 first stage value"
ARG ARG2="arg2 first stage value"
ARG ARG3="arg3 first stage value"
RUN echo -e "first_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"


FROM alpine:latest as second_stage

# second_stage では、各 ARG を宣言するが、スコープ内のデフォルト値は設定しない。
ARG ARG1
ARG ARG2
ARG ARG3
RUN echo -e "second_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"


FROM alpine:latest as third_stage

# third_stage では、すべての ARG を宣言しない。
# ARG ARG1
# ARG ARG2
# ARG ARG3
RUN echo -e "third_stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"

各 ARG は下記のように設定しています。

ARG 名 設定内容
ARG1 グローバルな ARG として設定。デフォルト値も指定。
ARG2 グローバルな ARG として設定。デフォルト値は指定しない。
ARG3 グローバルな ARG として設定しない。

また、各ステージは次のように設定しています。

Stage 名 設定内容
first_stage 各 ARG を宣言。ステージ内のデフォルト値を設定。
second_stage 各 ARG を宣言。ステージ内のデフォルト値は設定しない。
third_stage ARG を宣言しない。

--build-arg 指定なしでビルド

Docker Image のビルド時に、 --build-arg による値の指定を行わずにビルドしてみます。

# docker build . --no-cache
Sending build context to Docker daemon  14.85kB
...snip...
Step 7/14 : RUN echo -e "1st stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in 2bbe78634ee8
first_stage:
        ARG1=arg1 first stage value
        ARG2=arg2 first stage value
        ARG3=arg3 first stage value
...snip...
Step 12/14 : RUN echo -e "2nd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in 0c28af93ea9b
second_stage:
        ARG1=arg1 global default value
        ARG2=
        ARG3=
...snip...
Step 14/14 : RUN echo -e "3rd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in cbca9ed88691
third_stage:
        ARG1=
        ARG2=
        ARG3=
...snip...
  • first_stage では、いずれもステージ内のデフォルト値となりました。 ARG1 を見ると、グローバルなデフォルト値よりもステージ内のデフォルト値が優先されていることがわかります。これは、よりスコープが狭い方の値を優先的に使う動きになっており、理にかなっている動きと言えるでしょう。
  • second_stage では、 ARG1 のみグローバルなデフォルト値が表示されています。ステージ内で ARG を宣言はしたものの、何も値を指定しなければグローバルなデフォルト値を利用するということですね。 ARG2, ARG3 には何も表示されていませんが、これはその値を指定している場所がどこにもないからですね。
  • third_stage ではすべての値が表示されていません。 ARG1 のグローバルなデフォルト値すら表示されていないということは、 ARG を利用するためにはそのステージごとに宣言する必要があるということでしょう。

--build-arg 指定ありでビルド

今度は --build-arg による値の指定ありでビルドしてみます。

# docker build --build-arg ARG1="build arg1 value" --build-arg ARG2="build arg2 value" --build-arg ARG3="build arg3 value" . --no-cache
Sending build context to Docker daemon  14.85kB
...snip...
Step 7/14 : RUN echo -e "1st stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in 10b37c5a524b
first_stage:
        ARG1=build arg1 value
        ARG2=build arg2 value
        ARG3=build arg3 value
...snip...
Step 12/14 : RUN echo -e "2nd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in e70e3ff9fe9b
second_stage:
        ARG1=build arg1 value
        ARG2=build arg2 value
        ARG3=build arg3 value
...snip...
Step 14/14 : RUN echo -e "3rd stage:\n\tARG1=${ARG1}\n\tARG2=${ARG2}\n\tARG3=${ARG3}"
 ---> Running in e675e8f648e8
third_stage:
        ARG1=
        ARG2=
        ARG3=
...snip...
  • first_stage, second_stage ともにすべての値が --build-arg で指定した値になりました。これは公式ドキュメントの ARG のデフォルト値で説明されている内容と一致しますね。 --build-arg で指定された値はグローバルなデフォルト値及びステージ内のデフォルト値のどちらよりも優先されます。
  • third_stage ではまたもやすべての値が表示されていません。 --build-arg で値が指定されていても、そのステージ内で宣言されていなければやはり利用できないようです。

まとめ

  • マルチステージビルドな Dockerfile で ARG を利用する際には、すべてのステージでそれぞれ利用する ARG を宣言する必要がある。
  • 複数のステージで利用している ARG のデフォルト値を定義したければ、グローバルな ARG を定義(最初の FROM よりも前に定義)してそこにデフォルト値を記載すると良い。
  • ARG の値の優先度は
    1. --build-arg での指定値 [優先度高]
    2. ステージ内のデフォルト値
    3. グローバルなデフォルト値 [優先度低]

おしまい。

宣伝

マルチステージビルドの ARG の挙動に気づくきっかけとなったプロジェクトの宣伝です。

Minecraft の Bedrock Server を楽に運用しようぜ!という趣旨のプロジェクトです。
Minecraft ってたまーにアップデートが入るのですが、クライアントは大体勝手にアップデートされるものの、 Bedrock Server は自動的にアップデートされず、手動で新しいバージョンをダウンロードして Zip ファイルを展開してバイナリファイルを置き換えてサービスを再起動して・・・とかやる必要がありました。
このプロジェクトは、一度セットアップだけやってしまえば、あとは全自動で Bedrock Server をアップデートしてくれます!ついでに Docker を使って Bedrock Server を構築するので環境を汚しません!

よろしくお願いします!

本当におしまい。

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

CentOS6へのDockerインストール

古い情報がかなり多くはまらされた。。。
yum install dockerだと全く関係ない紛らわしいのが入っちゃう
service docker startでエラー発生)

epelインストールして
yum install docker-io --enablerepo=epel
でいけるという情報多いが
リポジトリ消えてるみたい

で今度はrpmで
wget https://yum.dockerproject.org/repo/main/centos/6/Packages/docker-engine-1.7.1-1.el6.x86_64.rpm
でいけるという情報もあったが
これもyum.dockerproject.orgのリンク切れてるっぽい

これならいけた

yum install https://get.docker.com/rpm/1.7.1/centos-6/RPMS/x86_64/docker-engine-1.7.1-1.el6.x86_64.rpm

参考
https://stackoverflow.com/questions/55134196/installing-docker-on-centos-6-after-removal-of-docker-io

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

CentOS 6へのDockerインストール

古い情報がかなり多くはまらされた。。。
yum install dockerだと全く関係ない紛らわしいのが入っちゃう
service docker startでエラー発生)

epelインストールして
yum install docker-io --enablerepo=epel
でいけるという情報多いが
リポジトリ消えてるみたい

で今度はrpmで
wget https://yum.dockerproject.org/repo/main/centos/6/Packages/docker-engine-1.7.1-1.el6.x86_64.rpm
でいけるという情報もあったが
これもyum.dockerproject.orgのリンク切れてるっぽい

これならいけた

yum install https://get.docker.com/rpm/1.7.1/centos-6/RPMS/x86_64/docker-engine-1.7.1-1.el6.x86_64.rpm

参考
https://stackoverflow.com/questions/55134196/installing-docker-on-centos-6-after-removal-of-docker-io

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

Docker で始める PySpark 生活

はじめに

「Spark?雷属性でかっこいい!」という煩悩のもと,Docker + Spark + Jupyter Notebook で簡単な機械学習を回して見ました.
お馴染みのタイタニックデータを使い,線形回帰による予測を行いました.

そもそも Spark とは

download.png

Spark とは分散処理ライブラリの一つです.
分散処理といえば Hadoop という方も多いと思いますが,自分の理解だと Spark は Hadoop の欠点を補ったライブラリになります.Hadoop が登場したのが 2006 年,その後,2014 年に Hadoop が登場しました.

Hadoop VS Spark

上記,Spark は Hadoop の欠点を補ったとありますが,双方にメリット・デメリットがあるため簡単に表にまとめます.

メリット デメリット
Hadoop 大量のデータを扱うことができる ストレージアクセスがあるためリアルタイムの処理が苦手
Spark オンメモリでの処理によりリアルタイムでの処理が得意 Hadoopほど大規模なデータは扱えない

つまり,大きすぎるデータはHadoop,リアルタイムでの処理がしたかったらSparkを用いると良いでしょう.

また,Hadoopのクエリエンジンは Presto,Hive ですが,Spark には多彩な API が用意されていて Python や Scala などの言語から簡単に呼び出すことができます.

Docker Setup

まず,下記のイメージをダウンロードして,ビルドを行います.

$ docker pull jupyter/pyspark-notebook

$ docker run --name spark_test -it -p 8888:8888 -v $PWD:/home/jovyan/work jupyter/pyspark-notebook

上記,表示される URL にアクセスすればノートを開くことができます.

※ イメージのサイズが 3GB あるので一応気をつけてください

Python

ライブラリ

import pandas as pd

import pyspark
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName('pandasToSparkDF').getOrCreate()

from pyspark.sql.functions import mean, col, split, regexp_extract, when, lit

from pyspark.ml import Pipeline
from pyspark.ml.feature import StringIndexer, VectorAssembler, QuantileDiscretizer
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.evaluation import MulticlassClassificationEvaluator

データ読み込み

# データ読み込み
titanic_df = spark.read.csv('./titanic/train.csv', header='True', inferSchema='True')

基本的なデータ処理

# 欠損値対応
titanic_df = titanic_df.na.fill({"Embarked" : 'S'})

# 不要のカラムを落とす
titanic_df = titanic_df.drop("Cabin")

# 定数による列追加
titanic_df = titanic_df.withColumn('Alone', lit(0))

# 条件による値の挿入
titanic_df = titanic_df.withColumn("Alone", when(titanic_df["Family_Size"] == 0, 1).otherwise(titanic_df["Alone"]))

# ラベルエンコード
indexers = [StringIndexer(inputCol=column, outputCol=column+"_index").fit(titanic_df) for column in ["Sex", "Embarked", "Initial"]]
pipeline = Pipeline(stages=indexers)
titanic_df = pipeline.fit(titanic_df).transform(titanic_df)

# テストスプリット
trainingData, testData = feature_vector.randomSplit([0.8, 0.2], seed=9)

他にもいろいろな処理が有りますが,いったんめぼしいものだけ置いておきます.
より詳しいデータ処理に関しては下記をご覧ください.
https://databricks-prod-cloudfront.cloud.databricks.com/public/4027ec902e239c93eaaa8714f173bcfc/5722190290795989/3865595167034368/8175309257345795/latest.html

学習

# 学習
lr = LogisticRegression(labelCol="Survived", featuresCol="features")
lrModel = lr.fit(trainingData)

# 推論
lr_prediction = lrModel.transform(testData)
lr_prediction.select("prediction", "Survived", "features").show()

# 評価
evaluator = MulticlassClassificationEvaluator(labelCol="Survived", predictionCol="prediction", metricName="accuracy")
lr_accuracy = evaluator.evaluate(lr_prediction)

print("Accuracy of LogisticRegression is = %g"% (lr_accuracy))
print("Test Error of LogisticRegression = %g " % (1.0 - lr_accuracy))

他にも下記のモデルをライブラリとして使用できます.

  • LogisticRegression
  • DecisionTreeClassifier
  • RandomForestClassifier
  • Gradient-boosted tree classifier
  • NaiveBayes
  • Support Vector Machine

感想

ローカルだと遅い!

当たり前ですけど,大規模データを処理してこその分散処理のため,この程度のデータでは恩恵は全くありませんでした.
大きすぎるデータに出会った時に速度や精度の比較を行って行けたらと思います.

今回の学び

  • Docker は便利
  • 分散フレームワーク体系の整理
  • PySpark ってこう動かんだ…
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む