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

ExmentのDocker環境を作ってみた

概要

ちょっとしたWeb用DBを作る場合、Exmentが便利そうだったので、気軽に試験できる使えるDocker環境を作ってみました。

使い方

定義ファイル取得

以下gitにcomposeを作成しました。
同ファイルをダウンロードします。

docker-exment

コンテナ起動

取得してきたパスで以下コマンドを実行してコンテナを起動します。

docker-compose up

初期設定

コンテナが起動すると以下パスに各種ページが起動しています。

パス 機能
http://localhost:8080/admin Exmentインストールページ
http://localhost:8888 phpMyAdmin

Exmentの初期設定を行うため「http://localhost:8080/admin 」へ接続します。
接続する以下のような画面が表示されるめたそのまま「Submit」します。

1.png

次にSQLの初期設定画面が表示されるます。

2.png

各項目に関して、以下表のように設定し「送信」を押します。

項目 設定値
ホスト名 db
データベース名 exment_database
ユーザー名 exment_user
パスワード secret

以下画面が表示されるので「インストール実行」を押します。

3.png

しばらくすると「Exment初期設定」画面が表示される。ここまで表示されれば環境構築は問題なく実行できている。あとはサイトの設定を行えばExmentが使用できる状態となります。

4.png

その他

この設定ファイルはExmentのお試し用に作りました。
セキュリティに関して十分に検討して作っていないため、本番適応する場合はセキュリティ面など再度検討してから使って下さい。

やり残し

本当は環境変数を指定してExmentのインストールバージョンを指定できるようにしようと思いました。
しかし、gitで落ちてくるファイルだと何らかのビルドを通さないと実行可能な状態にならなくて、ビルド方法が不明だったため、バージョン指定できる状態になっていません。


参考

Laravel製のオープンソースWebデータベースを開発した話

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

PHP7.4が出たので、すぐに使えるLAMP環境をdocker-compose形式で作成しました

Qiitaでポエムが増えてるので、自分もいいだろうと思って投稿。

どういう記事?

PHP7.4がでた。
CentOS8も出てた。
MySQL8も(結構前に)出てた。

じゃあ、自分の作業環境ほしいよね?
作るか。

コンセプトは、最低限の環境がすぐ使える

答えだけくれ!な人へ

  • LAMP_environment_by_docker-compose をクローン
  • docker-compose up -d --build
  • アクセス
    • docker for windowsの場合: localhost:8080
    • docker tool box の場合: 多分 192.168.99.102:8080docker-machine ipで出てくるIP使って
    • MacとLinuxは知らん。動作確認したらREADMEのプルリクください。

苦労した点

  • PHP7.4からなんかリポジトリ変わってた
  • CentOS8はyumじゃなくなってた。dnfだった。
    • でもyumも使える(リンクはられてた)
  • すでに用意されてるイメージは甘え。自分でDockerfile書くぞ!
  • MySQLは、パスワード系がめんどかったので甘えた。

使い方

  1. Dockerインストールしてね
  2. LAMP_environment_by_docker-composeをクローンしてね
  3. docker-compose up -d --build叩いてね
  4. htdocsフォルダにPHPとかおいてね
  5. localhostにアクセスしてね。ポートは8080
  6. なんか拡張したり間違ってたら遠慮なくプルリク送ってね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカルでcircleciコマンドを実行したとき、"git: not found"と表示され、エラーとなる

概要

ローカルでcircleci buildコマンドを使って設定ファイルを確認しようとしたところ、エラーでタスクが正常終了しない。

console
====>> Checkout code
  #!/bin/sh -eo pipefail
mkdir -p /root/project && cd /tmp/_circleci_local_build_repo && git ls-files | tar -T - -c | tar -x -C /root/project && cp -a /tmp/_circleci_local_build_repo/.git /root/project
/bin/sh: git: not found
tar: empty archive
tar: short read
Error: Exited with code 1
Step failed
Error: runner failed (exited with 101)
Task failed
Error: task failed

実行したconfig.ymlは下記の通り。

config.yml
version: 2
jobs:
  build:
    docker:
      - image: node:12.13.0-alpine
    steps:
      - checkout
      - run:
          name: Greeting
          command: echo Hello, world.
      - run:
          name: Print the Current Time
          command: date

原因

コンソールにも出力されているように、checkout時、コンテナ上でgitが実行できないことが原因。
今回使用しているimage:node:12.13.0-alpineには
gitはデフォルトでは入っていない。

対応

checkout実行前に、apkコマンドでgitを用意してあげる。

config.yml
version: 2
jobs:
  build:
    docker:
      - image: node:12.13.0-alpine
    steps:
      - run:
          name: add git
          command: apk -U add git
      - checkout
      - run:
          name: Greeting
          command: echo Hello, world.
      - run:
          name: Print the Current Time
          command: date

実行結果は以下の通り

console
====>> Checkout code
  #!/bin/sh -eo pipefail
mkdir -p /root/project && cd /tmp/_circleci_local_build_repo && git ls-files | tar -T - -c | tar -x -C /root/project && cp -a /tmp/_circleci_local_build_repo/.git /root/project
====>> Greeting
  #!/bin/sh -eo pipefail
echo Hello, world.
Hello, world.
====>> Print the Current Time
  #!/bin/sh -eo pipefail
date
Sun Dec  1 12:48:31 UTC 2019
Success!

無事、実行されました。

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

CUDAが動く Julia の深層学習フレームワーク Flux.jl の環境構築をDockerで行う.

本日は

  • Julia製の深層学習フレームワーク Flux.jl のアドベントカレンダー1日目の記事として執筆したものです.

一年前を振り返る(ポエム)

  • 私が Flux.jl を知ることになったのは1年前なんですが,レイヤーがCPUで動くんだけれどGPUで動かなかったりつらぽよ感がありましたが BatchNormレイヤーがGPUで動くようになったみたいなのでこれから始める方はそこまでストレスなくいけるんじゃないかなーと思います :innocent: .
  • まぁ,まだバージョンが0.10らしいのでこれからですよ.これから.

本題

  • 環境構築をDockerで行うことを考えます.かつ NVIDIA の GPU が動く環境を構築しましょう.
  • Dockerで. 「Docker CUDA」という言葉でぐぐると nvidia-docker っていう言葉/コマンドが出てくると思いますが, NVIDIA Docker って今どうなってるの? (19.11版) にもあるように名前がコロコロ変わって,NVIDIA Container Toolkit という名前になってるようです.(まぁ nvidia docker って言葉がけっこうシミわかってるので通じると思います)
  • まっさらなホスト環境(これからDockerを入れていこうというマシーン環境を指す.ここでは NVIDIAの GPU 付きUbuntuマシーンを想定)から環境構築を始めるときは 同記事のセクション Docker 19.03 以降の環境で前だけを見て生きる場合に従えばOKです.つまり

というステップを踏めばOKです.

Dockerfile

  • nvidia/cuda の DockerHub を探して適当なtagを探します.ここでは 10.0-cudnn7-devel-ubuntu18.04 としておきます.ドライバーが古いと怒られてしまう可能性があるので nvidia-smi などを見て対応するCUDAバージョンを確認するかかくじバージョンアップをしてください.
FROM nvidia/cuda:10.0-cudnn7-devel-ubuntu18.04

MAINTAINER SATOSHI TERASAKI

RUN apt-get update && apt-get install -y \
    build-essential \
    libatomic1 \
    python \
    gfortran \
    perl \
    wget \
    m4 \
    cmake \
    pkg-config \
    git

ARG JL_VERSION="v1.3.0"
ARG WDIR="/root"
ARG JL_BUILD_DIR=$WDIR/build
WORKDIR $JL_BUILD_DIR

RUN echo "\
CXXFLAGS=-D_GLIBCXX_USE_CXX11_ABI=0\n\
prefix=/usr/local/julia-$JL_VERSION\n\
USE_BINARYBUILDER = 0\n\
" > Make.user \
    && cat Make.user \
    && git clone --depth=1 -b $JL_VERSION https://github.com/JuliaLang/julia.git julia-$JL_VERSION\
    && cp Make.user $JL_BUILD_DIR/julia-$JL_VERSION \
    && cd julia-$JL_VERSION \
    && make -j $(nproc) \
    && make install

RUN rm -r $JL_BUILD_DIR
ENV PATH=/usr/local/julia-$JL_VERSION/bin:$PATH
# runtime test
RUN julia -e "using InteractiveUtils; versioninfo()"
RUN julia -e 'using Pkg; Pkg.add(["Flux","CuArrays"]); using Flux'

CMD ["julia"]
  • Juliaを無駄にソースからビルドしてるのは私の趣味なので公式ページのバイナリーのリンクを持ってきてコピーするでいいと思います.
  • 例えば docker-library/julia を参照

使い方

ホスト環境にログインして次を実行します.

$ ls
Dockerfile
$ docker build -t fluxgpu .
$ docker run --gpus all --rm -it fluxgpu
sudo docker run --gpus all --rm -it fluxgpu
               _
   _       _ _(_)_     |  Documentation: https://docs.julialang.org
  (_)     | (_) (_)    |
   _ _   _| |_  __ _   |  Type "?" for help, "]?" for Pkg help.
  | | | | | | |/ _` |  |
  | | |_| | | | (_| |  |  Version 1.3.0 (2019-11-26)
 _/ |\__'_|_|_|\__'_|  |
|__/                   |

julia> ; # to jump shell-mode
shell> nvidia-smi
Sun Dec  1 11:37:39 2019
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 410.48                 Driver Version: 410.48                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 1080    Off  | 00000000:03:00.0 Off |                  N/A |
|  0%   45C    P5    19W / 270W |      0MiB /  8119MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  GeForce GTX 1080    Off  | 00000000:04:00.0 Off |                  N/A |
| 37%   46C    P5    19W / 230W |      0MiB /  8118MiB |      4%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

上記のように nvidia-smi コマンドの出力が出ればOKです.

一度 REPL モードに戻って次を実行してみましょう:

julia> using Flux
julia> d=Dense(100,1000)|>gpu
Dense(100, 1000)

julia> x=rand(Float32,100)|>gpu;

julia> typeof(x)
CuArray{Float32,1,Nothing}

julia> d(x)|>typeof
CuArray{Float32,1,Nothing}

julia> d(x) |> size
(1000,)
  • これは Chainerでいえば chainer.links.Linear に相当するレイヤーですね.|> gpu とすることで Dense レイヤーと データ x をGPUに移しています.
  • 畳み込みのレイヤーを担当する Conv への入力インターフェースは注意が必要です.Chainerですと NCHW というデータレイアウトを採用していますが.Flux.jlでは逆で WHCN とすることになります.例えば x=rand(Float32,224,224,3,1)|>gpu というデータをJuliaでは投げることになります.

まとめ

  • GPU 付きの環境をDockerで構築し Flux.jl を1年ぶりに動かしました.BatchNorm のforwardが動くのでまともなレイヤーが組めそうですね.
  • 今回はとくにFluxのコンテキストはあまりないのでGPGPUをしたい人も各自でカスタマイズして活用できると思います.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ECS Fargateでブランチ毎にQA環境を作れるようにする

目次

  1. 前書き
    1. 概要
    2. 動機
  2. 本文
    1. 利用した主なツール
    2. 1の解説
    3. 2の解説
    4. 3の解説
    5. 4の解説
    6. 5の解説
  3. 終わりに
  4. 参考文献

前書き

概要

ECSでブランチごとの動作確認用の環境を準備したので、そのノウハウを共有します。
作った環境はこんな感じです。

Untitled Diagram (4).png

動機

ディレクターが、エンジニアが開発中のブランチの挙動確認をするのに「 git pull して docker-compose up して、メモリが足りなくてビルド終わらなくて…」というのを見て「大変だなー」と思い、パッと動作確認できる環境を作ろうと思いました。

本文

利用した主なツール

  • aws-cli
  • ecs-cli
  • docker
  • docker-compose

コンテナの構成

fargateで起動するコンテナ群はこんな感じです

コンテナ名 内容
app Nginx+プロダクションコード
mysql DB
redis バッチジョブ用のキュー

今回は、これを一つのタスクとして起動させています。
docker-compose.ymlで80番ポートを空けて インターネット → nginx → プロダクションコード という流れで通信が行われます。

またfargate特有の事情としては、コンテナ間通信の名前解決がコンテナ名でなくlocalhostとなることなので、それは注意が必要です。
(自分はここで結構ハマりました…)

1の解説

現在、ブランチがプッシュされたらCircleCIでphpunitを実行させています。
その工程に、docker imageのビルドとECRへのプッシュを追加します。
実際のコードはこんな感じです。

config.yml
      - run:
          name: Install Docker client
          command: |
            set -x
            VER="19.03.5"
            curl -L -o /tmp/docker-$VER.tgz https://download.docker.com/linux/static/stable/x86_64/docker-$VER.tgz
            tar -xz -C /tmp -f /tmp/docker-$VER.tgz
            mv /tmp/docker/* /usr/bin
      - run:
          name: Install ecs-cli
          command: |
            set -x
            curl -o /usr/bin/ecs-cli https://amazon-ecs-cli.s3.amazonaws.com/ecs-cli-linux-amd64-latest
            chmod +x /usr/bin/ecs-cli
      - run:
          name: push docker image to AWS ECR
          command: |
            BRANCH_HASH=`echo $CIRCLE_BRANCH | sed 's:/:_:g'`-`git rev-parse HEAD`
            docker build -f Dockerfile_Ecs -t {{registory_id}}.dkr.ecr.ap-northeast-1.amazonaws.com/{{repository}}:${BRANCH_HASH} .
            ecs-cli push {{registry_id}}.dkr.ecr.ap-northeast-1.amazonaws.com/{{repository}}:${BRANCH_HASH}

ECRの運用としては、一つのレポジトリに対して、「ブランチ名+コミットハッシュ」でタグ付けをしたイメージをプッシュすることにしています。
これでレポジトリにライフサイクルポリシーを設定することで、ECRの容量が肥大化することが防げます。
ただ、イメージのタグに / が使えないので _ で置換しています。

Dockerfile_Ecsの内容としては、ビルド済みのプロダクションコードを COPY して、nginxの設定ファイル等を 適切な場所に配置って感じです。

2の解説

ユーザーがブランチ名を引数としてスクリプトを実行したら、URLが出力されるようにします。
このURLからECSで起動したサービスにアクセスすることができれば、ユーザーはビルドやgit操作が不要となります。

3の解説

今回の肝となるのは、4の

ecs-cli compose --project-name ${branch+hash} service up

となります。
しかし、そのためには入力された、ブランチ名から ${branch+hash} を取得する必要があります。
そのために ECRでイメージ一覧を取得し、ブランチ名の前方一致でフィルターし、そのイメージのタグを取得することで ${branch+hash} を取得します。

4の解説

今回で一番重要なのが、この処理となります。
図ではスペースの都合でコマンドを省略しましたが、省略しないと下記になります。

BRANCH_HASH=${branch+hash} ecs-cli compose \
  -c {{cluster_name}} \
  -f docker-compose-ecs-base.yml -f docker-compose-ecs.yml \
  --ecs-params docker/ecs/ecs-params.yml \
  --project-name ${branch+hash} \
  service up \
  --launch-type FARGATE

環境変数 BRANCH_HASH に値を渡して、それを下記の docker-compose.yml で取得しています。

services:
  app:
    build:
      context: .
      dockerfile: Dockerfile_Ecs
    image: "{{registry_id}}.dkr.ecr.ap-northeast-1.amazonaws.com/{{repository}}:${BRANCH_HASH}"
    ports:
      - "80:80"
    command: docker/ecs/entrypoint-app.sh

これによって ecs-cli compose service up の実行時に、どのタグのイメージをECSで立てるかを決定しています。
ECSはこのコマンドを受けて、タスク定義の作成、ECRからimageの取得、サービス+タスクの起動を行ってくれます。
そして、最後に entrypoint-app.sh で、migration、シードデータの作成、nginx起動などのコンテナ起動に必要な処理を行っています。

5の解説

今回は、4でECSのサービス+タスクがパブリックサブネットで作成されています。
そのため、そのタスクにはパブリックIPが付与されています。
そのパブリックIPを aws ecs describe-tasks 等を利用して取得して、そのIPと ${branch+hash} を紐付けて、 Route53でAレコードを作成します。

ユーザーは、このAコードのURLを使うことで、ECSのサービスにアクセスできるようになります。

(パブリックIPが自動的に変更されて、URLとIPの紐付けが変わる可能性が高いような気はしますが、サービスは毎晩に削除しようと思ってるので、まぁ大丈夫かな?という予想です)

終わりに

続きの作業としては

  • slackopsにする
  • ECSのサービスを自動的に削除する
  • Route53のレコードを自動的に削除する

をやりたいなーと思います。

ただslackops対応が意外と難しい…

参考

下記を参考にさせていただきました。
ありがとうございました :bow:

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

Docker + Flask(Python) + Jupyter notebookによる仮想環境構築

毎回構築の方法を忘れるため、備忘録として。

ファイル構成

project
    L Dockerfile

Dockerfile

javaを利用するライブラリをインストールする可能性があるので、default-jdkを追加しています。

Dockerfile
FROM python:3.6
RUN apt-get update && apt-get install -y \
    default-jdk \
    build-essential \
    gfortran \
    libblas-dev \
    liblapack-dev \
    libxft-dev \
    swig \
    && rm -rf /var/lib/apt/lists/*
RUN echo 'export LD_LIBRARY_PATH="/usr/local/lib:$LD_LIBRARY_PATH"' >> ~/.bash_profile && \
    . ~/.bash_profile && \
    cd ~ &&\
    git clone https://github.com/taku910/mecab.git && \
    cd mecab/mecab && \
    ./configure  --enable-utf8-only && \
    make && \
    make check && \
    make install && \
    cd ../mecab-ipadic && \
    ./configure --with-charset=utf8 && \
    make && \
    make install &&\
    cd ~ &&\
    git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git && \
    cd mecab-ipadic-neologd && \
    ./bin/install-mecab-ipadic-neologd -n -y
RUN pip3 install --upgrade pyzmq --install-option="--zmq=bundled" && \
    pip3 install --upgrade jupyter && \
    pip3 install --upgrade \
    pandas \
    neologdn \
    Flask \
    numpy \
    Pillow \
    tensorflow \
ENV LD_LIBRARY_PATH "/usr/local/lib:$LD_LIBRARY_PATH"
VOLUME /notebook
WORKDIR /notebook
EXPOSE 8888
ENTRYPOINT jupyter notebook --ip=0.0.0.0 --allow-root --no-browser

Docker imageの作成

Dockerfileのディレクトリに移動して、docker buildコマンドを実行。構築に数分かかります。

$ cd project
$ docker build -t image_name --force-rm=true .

# (-t イメージ名) イメージ名を自分で決定
# (--force-rm=true) イメージのビルドに失敗したら、イメージを自動で削除する

Docker containerの作成

描きコマンドを実行すると、自動でJupyter notebookが起動するので、表示されたURLにアクセスするとJupyter notebookが利用できる。

# 上と同じディレクトリで下記コマンド実行
$ docker run -v `pwd`:/notebook -p 8888:8888 -p 5000:5000 -it --name container_name image_id /bin/bash

# http://127.0.0.0:8888/?token=####################
# こんな感じのアドレスが出てくるため、tokenごとコピペする。

追加のライブラリをインストール

Jupyter notebookで作業している時に、足りないライブラリをインストールしたい場合は、コンテナの外から、下のコマンドでコンテナの中に入る。

$ cd project
$ docker exec -it container_name /bin/bash

# 上記コマンドでコンテナ内に入ると、ターミナルが下のように切り替わる。
root@ユーザー名:/notebook# ここにコマンドを入力できるようになる。

# インストールの例
root@ユーザー名:/notebook# pip install numpy
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ぼくのかんがえたさいきょうのAPIドキュメント運用

ドキュメントちゃんと保守できてますか?

API開発とドキュメントの保守は切っても切れない問題です。

仕様の記述はもちろんのこと、サンプルを試せるAPIクライアントや、仕様に則った実装になっているかテストも自動化したいですよね。

本記事では、現在開発中のAPIアプリケーションで、実際に僕が試行錯誤していく中でたどり着いたベストプラクティスを紹介しようと思います。

アーキテクチャ

  • iOSアプリのバックエンドとしてJSONを返すAPIサーバー
  • Rails6 × MySQL5.7 on Docker

いつもの というお買い物アプリです。

 2019-12-01 13.34.00.png

ドキュメント何で書いてますか?

  • Excel => つらい
  • Markdown => つらい
  • 何らかのDSLを用いて生成するツール => :innocent:

素でマークダウンを書くのはつらみが深いので何かしらツールを使いましょう。
apiary, api blueprint, APIDOC, etc. 
いろいろありますが、僕が激しくおすすめするのは OpenAPI です。

理由としては、

  • Linux Foundation、Google、IBM、Microsoftなどが協力して仕様の策定に関わっていること
  • 歴史が長く周辺ツールが豊富で、拡張性があること
  • 表現力豊かな記述方法

が挙げられます。

Swagger? OpenAPI?

もともと Swagger という名前だったものが、 OpenAPIと名前を変えてバージョン3.0がリリースされました。
Swaggerと聞けば馴染みのある方も多いと思います。
基本的にSwaggerとOpenAPIを読み替えても問題はないのですが、(ドメインとか残ってるし => https://swagger.io/specification/)
Swaggerは2.xまでで、OpenAPIは3.0からになるので、Swaggerのバージョン3というものは厳密には存在しません。

OpenAPIが優れている理由

使ったことがない人向けに説明をしておくと、
OpenAPI とは、RESTful API を記述するためのフォーマットの標準であり、
その標準を用いて様々なことを解決するためのヘルパーツール群を提供するものです。

ツールは大きく3種類に分けられます。

  • Swagger Codegen
    • スタブサーバーとクライアントSDKの生成
  • Swagger Editor
    • 定義ファイルの編集が行えるリッチエディタ
    • シンタックスのチェックや補完、ホットリロードでのプレビューをサポート
  • Swagger UI
    • HTMLとしてドキュメントをビジュアライズする
    • 定義されたホストに対してリクエストを送信するAPIクライアントとしても使用できる(Postman的な)

この3つのオープンソースツールを拡張する形で、各言語ごとのラッパーやフレームワークへの組み込みをサポートするライブラリが数多く作成されています。(ex. swagger-blocks

自分が実現したいことに合わせて柔軟にモジュールを組み込むことができるので、どのプロジェクトにも導入がしやすいです。

また、先に述べたように、OpenAPI とはただ単にフォーマットを標準化した仕様なので、
ツールを通さなくてもその仕様に則って記述した yaml ファイルをただ共有するだけみたいな使い方もできます。
yamlさえあれば、受け取った人は何かのツールを使って自由に拡張利用できるので、非常にポータビリティが高いと言えます。Dockerファイルで環境のやりとりするみたいなイメージに近い :thinking:

そして錚々たる大企業たちがスポンサーとなっているため、業界の標準となっていくことは確実です。
OpenAPIの記述に慣れておくことは、エンジニアとして必要なスキルになってくるかと思っています。

実際に使ってみよう

今回実現したいことはこちらです。

  • ドキュメントをブラウザで手軽に確認したい
  • ドキュメントを楽に記述したい
  • ドキュメントをローカルサーバーのHTTPクライアントとして使いたい
  • レスポンスがドキュメントに則っているか自動テストしたい

これ全てOpenAPIでできます。

ただ実際にやろうとするとツールが豊富で選択肢が多い割に、3.0に対応していないものが多かったり、情報がまとまっていなかったり、ベストプラクティスにたどり着くまでに苦労したので、この記事が何かの助けになれば幸いです。

ちなみにですが、2.xと3.xでは破壊的な変更があるので、2系のツールで3系を動かすのは無理があります。
多くのツールで3に対応するissueが上がっているのですが、長い間放置されているものが多いため、3を使おうとすると選択肢は結構狭まります。

ドキュメントをブラウザで手軽に確認したい

まずはサンプルとなるエンドポイントを実装します。

config/routes.rb
Rails.application.routes.draw do
  resources :users
end
app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    user = {
      :name => "sakuraya",
      :age => 26
    }
    render :json => user
  end
end
$ curl localhost:3000/users
{"name":"sakuraya","age":26}

これを OpenAPI のドキュメントとして記述します。
yaml と json がサポートされていますが、特に理由がなければ yaml を使うことをお勧めします。

ファイル名は何でもいいんですが、 openapi.yml がスタンダードです。

doc/openapi.yml
openapi: 3.0.2
info:
  title: "ぼくのかんがえたさいきょうのAPIドキュメント運用"
  description: "サンプルアプリ"
  version: "1.0.0"
tags:
  - name: "users"
    description: "ユーザーAPI"
paths:
  /users:
    get:
      summary: "ユーザーを取得"
      description: "ユーザーを取得"
      tags:
        - "users"
      responses:
        200:
          description: "成功時"
          content:
            application/json:
              schema:
                type: "object"
                properties:
                  name:
                    description: "名前"
                    type: "string"
                    example: "sakuraya"
                  age:
                    description: "年齢"
                    type: "integer"
                    example: 26
                required:
                  - "name"
                  - "age"

最低限これだけ書けばOKです。

これをブラウザで見るためには、SwaggerUIというツールを使います。
SwaggerUIで調べると Node.js を使ってサーバーを立ち上げる例ばかりが出てきますが、
別で立てるのは面倒なので docker-compose で一緒に立ち上げてしまいます。
イメージが公開されているのでこれをベースにします。

docker-compose.yml
version: '3'

services:
  web: &app_base
    build:
      context: .
    ports:
      - 3000:3000
    command: bundle exec rails s -p 3000 -b 0.0.0.0
    volumes:
      - .:/myapp
      - bundle:/usr/local/bundle
    depends_on:
      - db
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
      TZ: Asia/Tokyo
    command: mysqld --character-set-server=utf8 --collation-server=utf8_general_ci
    volumes:
      - ./vendor/docker/db/data:/var/lib/mysql
      - ./vendor/docker/db/conf.d:/etc/mysql/conf.d
      - ./vendor/docker/db/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
    ports:
      - 3306:3306
  doc:
    image: swaggerapi/swagger-ui
    volumes:
      - ./doc/openapi.yml:/usr/share/nginx/html/openapi.yml
    environment:
      API_URL: openapi.yml
    ports:
      - 8080:8080
volumes:
  bundle:

doc の部分が SwaggerUI です。
doc 配下に置いたドキュメントファイルをマウントして、ファイル名を環境変数で指定することで、ドキュメントサーバーが立ち上がるようになっています。
これで http://localhost:8080 にアクセスすると、インタラクティブなUIでドキュメントが表示されます。

 2019-12-01 15.09.43.png

▼それぞれ対応するセクションがこうなっている。

2019-12-01_15_09_43.png

▼パスをクリックするとアコーディオンが開いて仕様が表示される。

 2019-12-01 15.09.53.png

▼上の画像は Example Value を表示したもので、 Schema をクリックすると、プロパティの説明、型、requiredの有無、nullableの有無など詳細が表示される。

 2019-12-01 15.10.01.png

ドキュメントサーバーとAPIサーバーの立ち上げをいっぺんに管理できるのが便利 :innocent:

ドキュメントを楽に記述したい

エディタごとにプラグインがそれぞれあるかと思います。
僕は普段 VS Code を使っているのでこれを入れています。

OpenAPI(Swagger)Editor

サイドバーから特定の場所にジャンプできる機能が重宝します。
OpenAPI Explorer.png

あとはプレビュー用として Swagger Viewer を入れておいてもいいかと思います。

 2019-12-01 15.38.55.png

ホットリロードで確認しながらできるので便利です。
一つだけ注意点があって、たまに表示がおかしくなったり、正しく表示されないことがあります($ref が展開されないなど)。
なので僕は書き方に慣れるまではこれを使っていましたが、いまはブラウザでまとめてチェックしています。

あとは書き方のTipsですが、 components を使って共通化していくと見通しが良くなります。

doc/openapi.uml
paths:
  /users:
    get:
      summary: "ユーザーを取得"
      description: "ユーザーを取得"
      tags:
        - "users"
      responses:
        200:
          description: "成功時"
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/User"
components:
  schemas:
    User:
      description: "ユーザー"
      type: "object"
      properties:
        name:
          description: "名前"
          type: "string"
          example: "sakuraya"
        age:
          description: "年齢"
          type: "integer"
          example: 26
      required:
        - "name"
        - "age"

 2019-12-01 15.44.28.png

ドキュメントをローカルサーバーのHTTPクライアントとして使いたい

せっかく docker-compose してるんだから、ローカルのサーバーに実際につないで動かしたいですよね?
API開発時のHTTPクライアントにはずっと Postman や、
最近だと REST Client なんかを使っていましたが、ドキュメントの変更に対する反映が面倒だったりするのが難点です。
OpenAPIを組み込めればそんな手間もなくなります。

SwaggerUI には Try it out というボタンがついています。
2019-12-01_15_09_53.png

このままでは利用できないので設定を行います。
servers というセクションを追記してください。

doc/openapi.yml
servers:
  - url: "http://localhost:3000"
    description: "local api server"

これがボタンを押した時のリクエスト先のベースURLとなります。

▼トップに servers の設定が反映されました。複数設定することができ、切り替えられるようになっています。

 2019-12-01 15.57.28.png

▼ボタンを押すとパラメータが編集できるようになり、 Execute ボタンが現れます。
(今回はパラメータないが適当に書くとこんな感じ。よしなにクエリパラメータに突っ込んでくれる。)

 2019-12-01 16.01.41.png

もう一つ設定が必要で、このままリクエストを送ってもこのようなエラーが出ます。

Access to fetch at 'http://localhost:3000/users?some_condition=true' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.Access to fetch at 'http://localhost:3000/users?some_condition=true' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

Cross Origin の制約を回避するために、Rails側に設定が必要です。
rack-cors という gem を使います。
ドキュメントサーバーを使うのは開発環境だけなので、 development.rb に記述します。

config/environments/development.rb
config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins "localhost:8080"
    resource "*", :headers => :any, :methods => :any
  end
end

これでサーバーを再起動すれば、リクエストできるようになります。

▼生成されたcurlコマンドと、レスポンスが表示されました。
 2019-12-01 16.58.41.png

いちいち Postman 開いてリクエスト書く必要がないので幸せ:innocent:

レスポンスがドキュメントに沿っているか自動テストしたい

これだけでもだいぶ開発が捗るようになったんですが、テストまでいきたいです。
ドキュメントで Integer になってるプロパティが String で返ってきたり、
required なプロパティがレスポンスになかったら落ちるようにしたいですよね。

rspecに組み込んでみます。

Gemfile
gem 'rspec-rails'
docker-compose run web bundle
docker-compose run web rails generate rspec:install
spec/requests/users_spec.rb
require "rails_helper"

RSpec.describe UsersController, :type => :request do
  describe "#index" do
    let(:path) { users_path }  
    it  do
      get path
      expect(JSON.parse(response.body)).to match(
        # いい感じにドキュメントのスキーマを検証したい
      )
    end
  end 
end

committee という gem を使います。
さらにそれを Rails 用にラップした committee-rails も使います。

Gemfile
gem 'committee'
gem 'committee-rails'

設定を追加。

spec/rails_helper.rb
config.add_setting :committee_options
config.committee_options = { :schema_path => Rails.root.join("doc", "openapi.yml").to_s }
include ::Committee::Rails::Test::Methods

テストを落とすために、 Integer であるはずの年齢を String に書き換えます。

app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    user = {
      :name => "sakuraya",
      :age => "26"
    }
    render :json => user
  end
end

テストをこう書きます。

spec/requests/users_spec.rb
require "rails_helper"

RSpec.describe UsersController, :type => :request do
  describe "#index" do
    let(:path) { users_path }  
    it do
      get path
      assert_request_schema_confirm
      assert_response_schema_confirm
    end
  end 
end

実行すると。。。

F

Failures:

  1) UsersController#index
     Failure/Error: assert_response_schema_confirm

     Committee::InvalidResponse:
       #/components/schemas/User/properties/age expected integer, but received String: 26
     # /usr/local/bundle/gems/committee-3.3.0/lib/committee/schema_validator/open_api_3/operation_wrapper.rb:35:in `rescue in validate_response_params'
     # /usr/local/bundle/gems/committee-3.3.0/lib/committee/schema_validator/open_api_3/operation_wrapper.rb:30:in `validate_response_params'
     # /usr/local/bundle/gems/committee-3.3.0/lib/committee/schema_validator/open_api_3/response_validator.rb:20:in `call'
     # /usr/local/bundle/gems/committee-3.3.0/lib/committee/schema_validator/open_api_3.rb:38:in `response_validate'
     # /usr/local/bundle/gems/committee-3.3.0/lib/committee/test/methods.rb:27:in `assert_response_schema_confirm'
     # ./spec/requests/users_spec.rb:9:in `block (3 levels) in <top (required)>'
     # ------------------
     # --- Caused by: ---
     # OpenAPIParser::ValidateError:
     #   #/components/schemas/User/properties/age expected integer, but received String: 26
     #   /usr/local/bundle/gems/openapi_parser-0.6.1/lib/openapi_parser/schema_validator.rb:62:in `validate_data'

Finished in 0.11266 seconds (files took 3.96 seconds to load)
1 example, 1 failure

Failed examples:

rspec ./spec/requests/users_spec.rb:6 # UsersController#index

無事落ちました!

▼requiredな値がないときはこうなったりします。

 1) UsersController#index
     Failure/Error: assert_response_schema_confirm

     Committee::InvalidResponse:
       #/components/schemas/User missing required parameters: age

テストまでかけるとか最高か:innocent:

普段の開発フローとしては、まずドキュメントを記述して仕様をレビュー => テスト書く => 実装というドキュメント&テスト駆動開発でやっています。

まとめ

今回は既存のプロジェクトに後から組み込んだので使ってませんが、
ドキュメントからモックサーバーを立ち上げたり、コードを生成したりするツールも言語ごとにいろいろあります。
うまく活用してAPIドキュメント運用のつらみから解放されましょう:innocent:

OpenAPI の記述方法については、OpenAPI-Specification を見ながら覚えていくのがオススメです。

[追記]
今回使ったサンプルコード

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

軽量なSambaのDockerイメージを作ってRaspberry Piで動かしてみた

はじめに

Raspberry PiをNASにするため,Dockerでsambaを立ち上げるべく,Alpine Linuxをベースイメージとして,(たぶん)軽量なイメージを作成してみました↓↓

Dockerでsambaを動かす場合はdperson/sambaが有名ですが,今回は勉強もかねてすべて自分でDockerfileを書いてみました.

環境

Raspberry Piで動かします。

  • マシン: Raspberry Pi 3B+
  • OS: hypriot OS
$ docker --version
Docker version 19.03.5, build 633a0ea

つくるもの

機能は単純に高望みせず,以下のような物を目指します.

  • ユーザのホームディレクトリを共有
  • 軽量にするためベースイメージはAlpine Linux
  • あくまで個人利用目的で複数ユーザ、セキュリティは考慮しない

つくったもの

構成は、

  • Dockerfile
  • smb.conf
  • start_samba_system.sh

の3ファイルです。

smb.confはbuild時にイメージに取り込まれ、デフォルトのsmb.confと置き換えられます。また、start_samba_system.shはsamba起動スクリプトです.

Dockerfile

FROM alpine:3.9
MAINTAINER HoriThe3rd

ENV USERNAME="name" \
    PASSWD="weak_passwd"

RUN apk update && apk --no-cache add samba

COPY ./smb.conf /etc/samba/
COPY ./start_samba_system.sh /usr/local/bin
RUN chmod 775 /usr/local/bin/start_samba_system.sh

EXPOSE 139 445
ENTRYPOINT [ "/bin/ash" ]
CMD [ "/usr/local/bin/start_samba_system.sh" ]

コンテナ起動時は、ENTRYPOINTで/bin/ashを呼び出し、CMDでstart_samba_system.shを走らせています.
このシェルスクリプト内で環境変数USERNAMEとPASSWDを使ってsambaユーザを作成し,sambaを起動します.docker run時に環境変数を設定することで任意のユーザ名とパスワードに対応します.

なお、Alpine Linuxを用いているので、シェルはbashではなくashが標準です。

samba起動スクリプト

ユーザの作成を行った後にsambaを起動しています.
シェバンが#!/bin/ashになっています.

#!/bin/ash

# Create an account
adduser -D $USERNAME
echo $USERNAME':'$PASSWD | chpasswd
echo -e $PASSWD'\n'$PASSWD | pdbedit -a -u $USERNAME

# Start daemons
nmbd restart -D
smbd restart -FS </dev/null

最後の行の</dev/nullはコンテナを起動した瞬間にexitedとなってしまうのを防ぐために入れています.こちらの記事を参考にさせていただきました.
Dockerでイメージ作成してsamba立ち上げる

smb.conf

ホームディレクトリを共有する設定をしたものを配置しました.

[homes]
   comment = Home Directories
   browseable = no
   writable = yes

ビルド

Dockerfileのあるディレクトリでビルドします。

docker build -t mysamba:1.0 . 

ビルド後のimageのサイズは、私の環境で32.1 MBと思いの外軽量に出来あがりました。dockerhubで有名なdperson/sambaが200 MB以上だったので大幅に軽くなっています。
もちろん、今回作成したものは必要最低限の構成なので、この比較はあくまで参考値とお考えください。

ラズパイのようなリソースの少ないマシンでは特に小型なのは助かります。

Run

自分でビルドしない場合は,次の"Dockerhubからダウンロードして実行する場合"を使ってください.

コンテナの/home/USERNAME/以下がsambaで共有されるので、-vオプションなどでホストのストレージと同期するようにして起動します。--restart=alwaysで再起動時に起動するようにしています。

docker run -d -p 139:139 -p 445:445 --name smb --restart=always -v <host path>:/home/USERNAME -e USERNAME="<your_name>" -e PASSWD="<your_pw>" mysamba:1.0

Dockerhubからダウンロードして実行する場合

Dockerhubにイメージをpushしていますので,以下のコマンドで実行できます.
ラズパイで実行する場合は,rpiタグで使用できます.latestはアーキテクチャがamd64なので通常のマシン用です(試していませんが・・・).

docker run -d -p 139:139 -p 445:445 --name smb --restart=always -v <host path>:/home/USERNAME -e USERNAME="<your_name>" -e PASSWD="<your_pw>" horithe3rd/lw_samba:rpi

おわりに

実際にDockerfileを手書きしてなかなか勉強になりました.

Alpine Linuxを用いることでかなり軽量なsambaイメージを作成することができました.ラズパイのようにリソースが少ないマシンで扱うにはやはりCentOSやUbuntuをベースにすると重たくなってしまうのでAlpine Linuxが便利ですね.

なお,今回作成したイメージはセキュリティなどは全く考えていないので,何かあっても作者は責任は取らないことはお約束です・・・

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

Docker + Wordpress 環境構築(Mac)

概要

wordpressの環境をdockerで用意する方法をまとめていきます。

前提

  • mysql 5.7
  • Docker for Mac 無ければDockerの公式からインストール

コンテナ作成

それではdocker-compose.ymlでコンテナを作成していきます。

適当にディレクトリを用意してdocker-compose.ymlファイル作成

$ mkdir docker
$ cd docker
$ vim docker-compose.yml

docker-compose.ymlファイルを下記のように記述

docker-compose.yml
version: '3.3'

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

   wordpress:
     image: wordpress:latest
     depends_on:
       - db
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
     volumes:
       - ./wordpress:/var/www/html
volumes:
    db_data:

Sequel ProでDBに接続したいので今回はmysql5.7系でインストールします。8.0系はSequel Proのバージョンによって動かないことがあるので対策が必要です(下記リンクを参考)

MySQL8.0.x に Sequel Pro で接続する
MySQL8.0でSequel Proを使えるようにする

またSequel Proに接続するためにpostsを追記していますが

ports:
 - "3306:3306"

3306が既にlocalでmysqlが起動している場合があるので、3306以外を使う必要があります。

volumes:
  - ./wordpress:/var/www/html 

上記はDockerのバインド・マウントを利用して、./wordpress(ホスト側のディレクトリ)と/var/www/html以下のwordpressのファイル群(コンテナ側のディレクトリ)を接続しアクセスできるようにします。

このバインド・マウントによりホスト側のwordpressのファイルを編集するとコンテナ側にも反映させることができます。

参考:
https://qiita.com/mom0tomo/items/7e611ac829863d4c5c82

docker-compose.ymlを作成したディレクトリ内でコンテナの作成と起動

$ docker-compose up -d

コンテナが生成されているか確認

$ docker-compose ps
       Name                  Command           State            Ports         
------------------------------------------------------------------------------
docker_db_1          docker-entrypoint.sh      Up      0.0.0.0:3306->3306/tcp,
                     mysqld                            33060/tcp              
docker_wordpress_1   docker-entrypoint.sh      Up      0.0.0.0:8000->80/tcp   
                     apach ...   

生成、起動されているのでブラウザからhttp://localhost:8000/アクセスすると...

スクリーンショット 2019-12-01 16.01.28.png

wordpressの画面が見れました。

Sequel Proで接続

最後にDB管理ツールのSequel Proでmysqlコンテナに接続していきます。
スクリーンショット 2019-12-01 16.13.07.png
docker-compose.ymlのDBの情報を入力して接続すると成功し

スクリーンショット 2019-12-01 16.14.15.png

デフォルトでテーブルがいくつか確認できます。

最後に

今回は以上です。

自分用のwordpressの環境が整ったので何か作成していきます。
ありがとうございました。

参考

https://qiita.com/mom0tomo/items/7e611ac829863d4c5c82
https://qiita.com/ucan-lab/items/b68db1db931c954da921

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

Docker+Laravel+OpenAPIGenerator

はじめに

OpenAPIGeneratorを使って生成されたLaravelのソースコードで環境構築してみました。
DockerでLaravel環境の構築~スタブサーバの生成、リクエストの確認まで行います。

プロジェクトディレクトリ構成

project/
 ├ www/                 # Laravel Project Container
 ├ generator/           # generator Container
 ├ docker-compose.yml
 ├ oas.yml

Sample OAS

プロジェクト直下にoas.ymlを用意します。
簡易的なCRUDが行えるAPIを想定しています。

oas.yml
openapi: 3.0.0
info:
  title: Task API
  version: 0.0.1
servers:
  - url: http://localhost
    description: Laravel Server
tags:
  - name: Task
paths:
  /api/task:
    get:
      tags: [ "Task" ]
      summary: Show Task List.
      description: Show Task List.
      operationId: listTask
      responses:
        '200':
          description: Successful response
    post:
      tags: [ "Task" ]
      summary: Create Task One 
      operationId: createTask
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                title:
                  type: string
                sort:
                  type: integer
      responses:
        '200':
          description: Successful response
  /api/task/{tid}:
    get:
      tags: [ "Task" ]
      summary: Show Task One.
      description: Show Task One.
      operationId: showTask
      parameters:
        - name: tid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response
    put:
      tags: [ "Task" ]
      summary: Update Task One.
      operationId: updateTask
      description: Update Task One.
      parameters:
        - name: tid
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                title:
                  type: string
                sort:
                  type: integer
      responses:
        '200':
          description: Successful response
    delete:
      tags: [ "Task" ]
      summary: Delete Task One.
      operationId: deleteTask
      description: Delete Task One.
      parameters:
        - name: tid
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: Successful response

Docker環境を用意する

docker-compose.ymlをproject直下に用意します。

docker-compose.yml
version: '3.4'
services:
  generator:
    build: ./generator
    volumes:
      - .:/app
    tty: true
    command: sh

  www:
    build: ./www
    volumes:
    - ./www:/var/www
    ports:
      - 80:80

generatorコンテナはOASからPHPコードを生成するコンテナになります。
wwwコンテナはApacheで動作するLaravel環境のコンテナになります。

続いて、generatorコンテナを構築するためのDockerfileをgenerator/配下に用意します。

FROM openjdk:8-jdk-alpine

WORKDIR /app

RUN apk --update add bash maven git
RUN rm -rf /var/cache/apk/*ls
RUN git clone https://github.com/openapitools/openapi-generator /generator
RUN cd /generator && mvn clean package

OpenAPIGeneratorはJVMでありJava環境が必要なためopenjdk:8-jdk-alpineをベースにしています。
ライブラリのインストーラとしてmavenを使用します。

続いて、wwwコンテナを構築するためのDockerfileをwww/配下に用意します。

FROM php:7.3-apache

RUN apt-get update && apt-get install -y git libzip-dev libxml2-dev
RUN docker-php-ext-configure zip --with-libzip
RUN docker-php-ext-install pdo_mysql mbstring zip xml
RUN curl -sS https://getcomposer.org/installer | php
RUN mv composer.phar /usr/local/bin/composer
RUN a2enmod rewrite && a2enmod headers
RUN sed -ri -e 's!/var/www/html!/var/www/public!g' /etc/apache2/sites-available/*.conf
RUN sed -ri -e 's!/var/www/!/var/www/public!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf
RUN usermod -u 1000 www-data && groupmod -g 1000 www-data
​
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin

WORKDIR /var/www
​
RUN composer global require "laravel/installer"

今回はApache上で動作するPHP(php:7.3-apache)をベースにしています。
Laravelを使いたいのでcomposerをインストールしています。
URLを書き換えやリダイレクトを有効にするため、a2enmod rewrite && a2enmod headersを実行しています。
sedでLaravel用のドキュメントルートに変更しています。
Apacheを動かすwww-dataユーザがLaravelから吐き出されるlogファイルにアクセスできるように権限を付与しています。

上記の準備ができたらコンテナを起動します。
各リソースのDLやInstall処理が一気に走るため結構な時間を要するかと思います。

$ docker-compose up -d --build

Laravel用のソースコードを自動生成する

OpenAPIGeneratorを使用してソースコードを生成します。
generatorコンテナに入って作業します。

$ docker-compose exec generator bash
bash-4.4# java -jar /generator/modules/openapi-generator-cli/target/openapi-generator-cli.jar generate \
   -i oas.yml \
   -g php-laravel \
   -o server-generate
bash-4.4# cp -rf server-generate/lib/. www

上記を実行すると、server-generateに自動生成されたPHPのソースコードが出来上がると思います。
生成されたソースコード一式はwwwコンテナへコピーします。
ちなみに、自動生成できるコードの種類はこちらから確認できます。

生成されたソースコードからLaravel環境を立ち上げる

$ docker-compose exec www bash
root@5295f267d6a1:/var/www# composer install
root@5295f267d6a1:/var/www# cp .env.example .env
root@5295f267d6a1:/var/www# php artisan key:generate

上記の実行が済めば、http://localhost にアクセスして下記の画面を確認することができると思います。
1.png

続いて、生成されたコードが機能しているのか確認してみます。
php artisan route:listを実行してAPIのエンドポイントを確認してみます。

$ docker-compose exec www bash
root@5295f267d6a1:/var/www# php artisan route:list
+--------+----------+----------------+------+------------------------------------------------+------------+
| Domain | Method   | URI            | Name | Action                                         | Middleware |
+--------+----------+----------------+------+------------------------------------------------+------------+
|        | GET|HEAD | /              |      | App\Http\Controllers\Controller@welcome        | web        |
|        | POST     | api/task       |      | App\Http\Controllers\TaskController@createTask | api        |
|        | GET|HEAD | api/task       |      | App\Http\Controllers\TaskController@listTask   | api        |
|        | DELETE   | api/task/{tid} |      | App\Http\Controllers\TaskController@deleteTask | api        |
|        | GET|HEAD | api/task/{tid} |      | App\Http\Controllers\TaskController@showTask   | api        |
|        | PUT      | api/task/{tid} |      | App\Http\Controllers\TaskController@updateTask | api        |
+--------+----------+----------------+------+------------------------------------------------+------------+

エンドポイントの確認が行えたので、続いてswagger-editorからGETリクエストを試したいと思います。
ただ、現時点のままだとCORSに引っかかりリクエストできません。
そのため、リクエストのテストを行う前にCORS対策を行います。
まずはbarryvdh/laravel-corsをインストールします。

$ docker-compose exec www bash
root@5295f267d6a1:/var/www# composer require barryvdh/laravel-cors

www\config\app.phpに下記を追加します。

'providers' => [
    // ...
    Barryvdh\Cors\ServiceProvider::class,
]

app/Http/Kernel.phpに下記を追加します。

'api' => [
    // ...
    \Barryvdh\Cors\HandleCors::class,
],

設定ファイルを以下のコマンドで作成します。

root@02eec0e47db9:/var/www# php artisan vendor:publish --provider="Barryvdh\Cors\ServiceProvider"

設定が完了したので、swagger-editorでリクエストテストを行ってみます。
222.png
無事にGETリクエスト出来たことを確認しました。

以上、Laravel用のスタブサーバの生成および構築までの流れでした。
自動生成されたコードはスタブなため、実処理は開発者が実装していくことになります。
何かの参考になれば幸いです。

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

DockerをWindowsノートPCで触ってみた

会社でGCPを使っていて、いろいろ勉強しなければなりません。恥ずかしい内容ですが、備忘録として残しておきます。将来、GCPでデータ処理、データ蓄積をしたいなー、とも思っています。自宅でWindowsノートPCでTutorial 動かしました。

Windowsの設定: Hyper-V をONにする

  • 「Windowsの機能の有効化または無効化」で Hyper-V をONにして、再起動する。
  • 「Windows 管理ツール」から「Hyper-V マネージャー」を起動してみる。(今回は関係ないですが、仮想マシンはまだ何もありません。)

2019-12-01.png

Docker Desktop for Windows を使ってみる。

  • Docker Hubで登録してからダウンロード。
    Downlaod Docker Desktop for Windows のボタンを押して指示に従っただけですが、CUIのdockerのツール一式がインストールされたようです。

  • 以下はtutorial をフォローしただけ。Power Shell で動かしました。

 git clone https://github.com/docker/doodle.git

./doodle/cheers2019 というディレクトリで、build して、ローカルで動かします。build してできるものはイメージと呼ばれるようです。ソースコードはGO lang で書かれていました。xxxxxはIDで、-t <イメージ名> です。

> docker build -t xxxxx/cheers2019 .

build する/イメージを作る(docker build)

build の設定は、同じディレクトリにあるDockerfile に書かれているものが読まれます。読んでもよく分かりません。

FROM golang:1.11-alpine AS builder
RUN apk add --no-cache git
RUN go get github.com/pdevine/go-asciisprite
WORKDIR /project
COPY cheers.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' -o cheers cheers.go

FROM scratch
COPY --from=builder /project/cheers /cheers
ENTRYPOINT ["/cheers"]

ローカルで動かす (docker run)

 Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
 Run a command in a new container

ローカルで動かします。-i はコンテナの標準入力、-tはtty を利用するオプションだそうです。--rm はコンテナ終了時にコンテナ自動的に削除します。

> docker run -it --rm xxxxx/cheers2019

イメージを共有できるようにする (docker push)

Docker Hub registry にpush(shipする?)し、共有できるようです。

> docker login; docker push xxxxx/cheers2019

サンプルには他にsummer2019, halloween2019, birthday2019があり、全て同じような内容なようでした。。。

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

Docker で Oracle Database 12c を動かす

なぜ Docker で動かしたのか

Oracle Database 12c の勉強をしようと思って macOS で動かすにはどうしたらいいのか探していたら。。。

macOS に対応したイメージがない!そして macOS で動かしている方がいらっしゃる!

ということで、検索して出てきたブログを参考にさせていただき、Dockerで動かそうと思い立ちました。(リンクしていいのかわからないので Oracle Database Docker などで検索してください。。)

Docker もよく聞くけど仕事で使ってないし触っておくか!と思ったのでちょうどよかったです。

これやったの去年の10月なので結構前ですね。。記事にするの遅すぎました。。
備忘録として残しておきます。

環境

当時の実行環境はこちらでした。
macOS High Sierra 10.13.6
メモリ 8GB

そしてかかった時間は120分以上。長かったです。

準備

私が実施した手順を実行するには、
Docker を動かす環境を作り、

  • Oracle
  • GitHub

上記のアカウントを作成しておくとスムーズです。

ダウンロードするもの

Oracle

Oracle Database ソフトウェア・ダウンロードから、バイナリファイルをダウンロードします。
今回は (12.1.0.2.0) - Standard Edition (SE2)Linux x86-64 を選択します。File 1, File 2 の両方をダウンロードします。

そして Oracle SQL Developerで動かすため、こちらもダウンロードしておきます。

GitHub

Oracle が Docker イメージを作成するスクリプト群を GitHub 上に公開しているので、oracle/docker-images より「Clone or download」 を実施します。私は git clone して持ってきました。

$ git clone https://github.com/oracle/docker-images.git

手順

参考ページを見ながら動かしてみました!
参考ページは Enterprise Edition でしたが、私は Standard Edition 2 なのでそこだけ注意します。

ディレクトリの作成

GitHub から Clone してくるだけだと、どうやらディレクトリが足りない・・・?
と思ったらドキュメントが古かった。。
GitHub が更新されてディレクトリ構造が変わってました。(去年なのでもう直ってるかも)

/docker-images/OracleDatabase/SingleInstance/dockerfiles/12.1.0.2/

ここに Oracle からダウンロードした zip ファイルを配置します。

コマンドの実行

zip ファイルを配置したディレクトリから一つ上の階層に移動してコマンドを実行します。
Standard Edition なのでオプションに -s を使用します。

$ ./buildDockerImage.sh -v 12.1.0.2 -s

このコマンドを実行すると諸々ダウンロードが始まります。24ステップくらいあって長かった記憶。

ダウンロード完了後は以下のコマンドを実行します。

$ docker run --name orcl -p 1521:1521 -p 5500:5500 -v /Users/UchidaKento/develop/docker/oracle-database/12c/oradata:/opt/oracle/oradata \
> -e ORACLE_SID=orcl -e ORACLE_PDB=pdb1 oracle/database:12.1.0.2-se2

このコマンドで失敗した場合は、

$ docker container ls -a

上記のコマンドを実行して NAME が orcl のコンテナがあることを確認し、

$ docker rm orcl

さらに上記コマンドでコンテナを消してから再実行してください。
成功すると以下のようにログが出力されます。
一行目の最後にパスワードが出力されているので忘れないよう注意!
また、

Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))

上記に SQL Developer で設定するホストとポートが出力されているのでこちらも忘れないよう注意してください。

ORACLE PASSWORD FOR SYS, SYSTEM AND PDBADMIN: oy6AobM8plM=1

LSNRCTL for Linux: Version 12.1.0.2.0 - Production on 07-OCT-2018 10:45:00

Copyright (c) 1991, 2014, Oracle.  All rights reserved.

Starting /opt/oracle/product/12.1.0.2/dbhome_1/bin/tnslsnr: please wait...

TNSLSNR for Linux: Version 12.1.0.2.0 - Production
System parameter file is /opt/oracle/product/12.1.0.2/dbhome_1/network/admin/listener.ora
Log messages written to /opt/oracle/diag/tnslsnr/5b1f3208ff47/listener/alert/log.xml
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1)))
Listening on: (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))

Connecting to (DESCRIPTION=(ADDRESS=(PROTOCOL=IPC)(KEY=EXTPROC1)))
STATUS of the LISTENER
------------------------
Alias                     LISTENER
Version                   TNSLSNR for Linux: Version 12.1.0.2.0 - Production
Start Date                07-OCT-2018 10:45:00
Uptime                    0 days 0 hr. 0 min. 0 sec
Trace Level               off
Security                  ON: Local OS Authentication
SNMP                      OFF
Listener Parameter File   /opt/oracle/product/12.1.0.2/dbhome_1/network/admin/listener.ora
Listener Log File         /opt/oracle/diag/tnslsnr/5b1f3208ff47/listener/alert/log.xml
Listening Endpoints Summary...
  (DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=EXTPROC1)))
  (DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=0.0.0.0)(PORT=1521)))
The listener supports no services
The command completed successfully
Copying database files
1% complete
2% complete
27% complete
Creating and starting Oracle instance
29% complete
32% complete
33% complete
34% complete
38% complete
42% complete
43% complete
45% complete
Completing Database Creation
48% complete
51% complete
53% complete
62% complete
64% complete
72% complete
Creating Pluggable Databases
78% complete
100% complete
Look at the log file "/opt/oracle/cfgtoollogs/dbca/ORCL/ORCL.log" for further details.

SQL*Plus: Release 12.1.0.2.0 Production on Sun Oct 7 11:11:12 2018

Copyright (c) 1982, 2014, Oracle.  All rights reserved.


Connected to:
Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production

SQL> 
System altered.

SQL> 
Pluggable database altered.

SQL> Disconnected from Oracle Database 12c Standard Edition Release 12.1.0.2.0 - 64bit Production
The Oracle base remains unchanged with value /opt/oracle
#########################
DATABASE IS READY TO USE!
#########################
The following output is now a tail of the alert.log:
Sun Oct 07 11:10:57 2018

XDB initialized.
Sun Oct 07 11:11:09 2018
Thread 1 advanced to log sequence 12 (LGWR switch)
  Current log# 3 seq# 12 mem# 0: /opt/oracle/oradata/ORCL/redo03.log
Sun Oct 07 11:11:12 2018
ALTER SYSTEM SET control_files='/opt/oracle/oradata/ORCL/control01.ctl' SCOPE=SPFILE;
   ALTER PLUGGABLE DATABASE PDB1 SAVE STATE
Completed:    ALTER PLUGGABLE DATABASE PDB1 SAVE STATE
Sun Oct 07 11:19:27 2018
Warning: VKTM detected a time drift.
Time drifts can result in an unexpected behavior such as time-outs. Please check trace file for more details.

これで Docker にイメージが作成され、接続準備が完了しました。

止めるときは以下のコマンドを実行します。

$ docker stop orcl

再度動かしたいときは以下のコマンドを実行します。

$ docker start orcl

SQL Developer の設定

スクリーンショット 2019-12-01 15.30.55.png

Docker で動かしている状態で SQL Developer に設定してあげると接続できます。

最後に

去年やったことの備忘録なので、少し変わっている箇所はあるかもしれません。
その場合は適宜読み替えていただければと思います。

もし間違っている箇所などございましたらご指摘ください。

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

Automatically Docker Daemon Boot on Windows Subsystem Linux(WSLにおけるdockerデーモンの自動起動)

動機

windows10 homeにwsl2でdockerを導入して、ついでにdocker-composeも積むことで最高の開発環境を作ろうとしたのだが、一度電源を落とすとログオンの度に手動で daemon くんを起動し直さなければならないことに気づいた。docker-compose.ymlrestart:always している関係上、できればWin10が再起動してしまってもログオンすればすぐに daemon くんには起きていてほしい……!

TL; DR

cf. Docker Running Seamlessly in Windows Subsystem Linux

1. sudo vi /usr/local/sbin/start_docker.sh

/usr/local/sbin/start_docker.sh
#!/usr/bin/env bash

# The first command will setup cgroups mounts on the subsystem
# (this only needs to be done once per reboot), and the second 
# will bring up the docker service using systemd.

sudo cgroupfs-mount
sudo service docker start

2. 実行権限を付与する

$ sudo chmod +x /usr/local/sbin/start_docker.sh
# Lock down edit privileges
$ sudo chmod 755 /usr/local/sbin/start_docker.sh

3. sudo vi /etc/sudoers

  • 最下部に以下の項目を追記
/etc/sudoers
# Enable docker services to start without sudo
yourUserName ALL=(ALL:ALL) NOPASSWD: /bin/sh /usr/local/sbin/start_docker.sh

4. タスクスケジューラでログオン時にwsl上のdockerを起動

  • 全般
    • 「最上位の特権で実行する」にチェック
  • トリガー
    • ログイン時 かつ 少し時間差を持って起動する
    • 同時にやると意図せず事故るかもしれないので
  • 操作
    • プログラム:C:\Windows\System32\bash.exe
    • 引数の追加:-c "sudo /bin/sh /usr/local/sbin/start_docker.sh"
  • 条件
    • 「AC電源に~」のチェックを外す
  • 設定
    • お好みで

5. 再起動してログオンし直したあと、sudo service docker status で動作を確認

$ sudo service docker status
 * Docker is running

私的解釈(自己中心的な解説)

1.sudo vi /usr/local/sbin/start_docker.sh

start_docker.sh というシェルスクリプトを /usr/local/sbin というディレクトリに置いている。書き込みにはroot権限が必要なことに注意したい。vi で開くと読み込み限定になっているため、:w !sudo tee % すればよさそう?(エディタ初心者並感)

2.実行権限を付与する

chmod コマンドを使用して、/usr/local/sbin/start_docker.sh の権限を変更している。オプションに +x を用いているので、"eXecution" を追加したことになる。これをただ単に付与するだけじゃなくて、対象ごとに数字で指定するやり方が chmod OOO ということになる。
この操作により、/usr/local/sbin に置かれているスクリプトをシステム側から実行できるようになる。

3.sudo vi /etc/sudoers

/etc/sudoers には sudo 周りのあれこれが設定されている(実行可能ユーザとかコマンドとか)。このファイルへの変更は即座に反映されるため、:w !sudo tee % する前に誤字脱字等がないか念入りに確認すること(二敗)。もしミスすると、sudo が使えません → 設定ファイルにエラーがあります!となって詰む。
もしそうなってしまったら、パスワードを再設定し直すときと同様に、powershellcmd.exe から wsl -u root してrootログインする解決法がある(sudo無しで /etc/sudoers を変更できるユーザであるrootくんに頼んで修正する)

4.タスクスケジューラでログオン時にwsl上のdockerを起動

autostartDocker001_LI.jpg
autostartDocker002_LI.jpg
autostartDocker003.png
autostartDocker004.png
autostartDocker005.png

おまけ

実は /usr/local/sbin/start_docker.sh の内容はdocker起動コマンドに限らずどんなものであれ実行できる(たとえば service に登録してあるものなど、常に sudo が必要なものすべて)。インタラクティブ実行にならないように注意しつつ、sshcron などをここに追記しておくことで、ほとんど普通のサーバーと変わりない振る舞いをさせることができる。
 上記の工程それぞれでファイル名を /usr/local/sbin/WSL_autostart.sh などと変更して、そのファイルに適宜必要なコマンドを追記していくといいだろう。

参照:

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

【Mac】Docker を使って最速で Python 3.x 環境を構築する

Mac は標準で Python 2.7 がインストールされていますので、Python 3.x の環境が必要な場合は、Pipenvpyenv を使って環境構築することが多いと思います。

しかし、インストールに失敗したり、設定に手間取ったりすることが多かったので、Docker を使った Python 3.x 環境の構築方法をまとめておきます。

動作環境

  • Mac OS X 10.14.6

Docker を使って Python 3.x 環境を構築

以下の URL から「Docker Desktop for Mac」をダウンロードし、インストールします。
https://hub.docker.com/editions/community/docker-ce-desktop-mac

Docker のインストールが完了したら、「docker version」コマンドを実行し、正しくインストールされているか確認してみましょう。

$ docker version
Client: Docker Engine - Community
 Version:           19.03.5
 API version:       1.40
 Go version:        go1.12.12
 Git commit:        633a0ea
 Built:             Wed Nov 13 07:22:34 2019
 OS/Arch:           darwin/amd64
 Experimental:      false

Server: Docker Engine - Community
 Engine:
  Version:          19.03.5
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.12
  Git commit:       633a0ea
  Built:            Wed Nov 13 07:29:19 2019
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.2.10
  GitCommit:        b34a5c8af56e510852c35414db4c1f4fa6172339
 runc:
  Version:          1.0.0-rc8+dev
  GitCommit:        3e425f80a8c931f88e6d94a8c831b9d5aa481657
 docker-init:
  Version:          0.18.0
  GitCommit:        fec3683

任意のフォルダを Python のプロジェクトフォルダとして作成し、そのフォルダ内に Python のソースファイルを配置します。
※この記事では、「/Users/username/work/python/helloworld」をプロジェクトフォルダとして使用します。

$ cd /Users/username/work/python/helloworld
$ echo 'print("Hello World!")' > helloworld.py

「docker run」コマンドを実行し、Python の実行環境となるコンテナを起動します。

以下のオプションを指定します。

  • -i (--interactive) : コンテナの標準入力にアタッチします。
  • -t (--tty) : 疑似ターミナル (pseudo-TTY) を割り当てます。
  • --rm : コンテナ終了時に、自動的にコンテナを削除します。
  • -v (--volume) : ローカルのフォルダをコンテナ上にマウントします。(<ローカル上のフォルダ>:<コンテナ上のフォルダ>)

Docker イメージは、Docker Hub で公開されている「python:3.7-alpine」を使用します。

コマンド末尾の「/bin/sh」は、コンテナ起動時に実行されるコマンドです。

$ docker run -it --rm -v /Users/username/work/python/helloworld:/helloworld python:3.7-alpine /bin/sh

コンテナが起動したら、以下のコマンドを実行して、正しく動作しているか確認してみましょう。

/ # ls
bin         etc         home        media       opt         root        sbin        sys         usr
dev         helloworld  lib         mnt         proc        run         srv         tmp         var
/ # python --version
Python 3.7.5

プロジェクトフォルダに移動し、Python ファイルを実行します。

/ # cd helloworld/
/helloworld # python helloworld.py 
Hello World!

「exit」コマンドを実行すると、コンテナを終了します。

/helloworld # exit

コンテナ起動時にパッケージをインストール

コンテナは起動時に初期化されるので、起動毎に必要なパッケージをインストールする必要があります。

それでは面倒なので、新しい Docker イメージを作成し、コンテナ起動時に必要なパッケージがインストールされるようにします。

プロジェクトフォルダ内に Dockerfile を作成します。

Dockerfile
# ベースとなる Docker イメージを指定
FROM python:3.7.5-alpine

# プロジェクトフォルダを指定
ARG project_dir=/helloworld/

# requirements.txt をコンテナにコピー
ADD requirements.txt $project_dir

# requirements.txt に書かれたパッケージをインストール
WORKDIR $project_dir
RUN pip install -r requirements.txt

「docker build」コマンドを実行し、新しい Docker イメージ「helloworld」を作成します。

$ docker build -t helloworld .

新しく作成した Docker イメージ「helloworld」を指定して「docker run」コマンドを実行すると、コンテナ起動時に requirements.txt に書かれたパッケージがインストールされます。

$ docker run -it --rm -v /Users/username/work/python/helloworld:/helloworld helloworld /bin/sh
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker/Rails/ReactをつかってHelloWorld!

初めまして。

プログラミングを始めてからあと四ヶ月で一年が経とうとしている。

本当に時間が立つのは早い...

今回は、RailsとReactを使って、HelloWorldをしてみる。

Railsを主にバックエンド、Reactをフロント、データベースはPostgresSQLを使用する。

1.Dockerで環境構築
2.RailsとReactの導入

  • RailsTutorialを完走し、簡易的なアプリ開発経験があるレベル。
  • ReactTutorial
  • Dockerインストール

追記(注意事項)

記事を書き終わった後に、RailsへのReact導入の方法がこれがベストではない感じがしてきました。

https://qiita.com/ry_2718/items/9b824a3f9ca750ce403e

rails new . --skip-coffee --skip-turbolinks --skip-sprockets --webpack=vue

rails 5.1からはこれでAssetpiplineの代わりにwebpackを導入することができるみたい。
情報収拾の仕方を改めて考えさせられました。最近になってからは公式リファレンスや、検索機能に1ヶ月以内などを指定して、英語の記事でもGoogle翻訳を駆使しながら頑張って読んでいる...。

Dockerで環境構築をする

Dockerとは?なぜDockerか。 (読まなくていい)(間違ってたらすいません)

そもそもOSとは

http://www.toha-search.com/it/os.htm

OSとはOperation System(オペレーティング・システム)の略で、アプリやデバイスを動作させるための基本となるソフトウェアのことです。 具体的には、キーボードやマウス・タッチパッドなどのデバイスから入力した情報をアプリケーションに伝え、またソフトウェアとハードウェアの連携を司る中枢的な役割を果たします。

つまり、OSはハードウェアや入力デバイス出力デバイス、アプリケーションなどを容易に操作するためのもの。

それで、このOSの上でどんな感じで仮想環境を作るかで違いがでる。

https://udemy.benesse.co.jp/development/web/docker.html

ハードウェアを仮想化し、複数のサーバを構築できる仕組みは変わりません。ただ、コンテナは1つのOSを共有して利用しているのに対し、仮想マシンはサーバごとにOSをインストールし動かしていきます

つまり、

  • 仮想マシンはホストOSの上でもう一つのOS(ゲストOS)を起動すること。(VirtualBoxとか)
    virtual boxとかを使ったことがある人はわかると思うが、仮想化させたいOSイメージを指定した後、設定で仮想化したOSが使用するハードディスクやメモリの分割を行う。<-結果的にゲストOSとホストOSが同時にメモリを占有するので処理が重たい

  • コンテナは仮想化をホストOSの上で行う(Dockerとか)
    コンテナでは、ホストOSの上で直接仮想化する(ゲストOSを建てない)ので非常に動作が軽い。Docker上であれば基本的に環境の差異による影響を受けない
    また、DockerにはDockerHubというのがあり、そこからすでに環境が構築されたテンプレートや、MySQLやRubyなどのツールや言語をDocker上にイメージとしてインストールしてくれる。

Dockerの基本コマンド

とりあえず目を通して、どんな動作を行うコマンドがあるかみてください。
初心者用Docker基本コマンド一覧(新旧スタイル対応)
DockerComposeの基本

Dockefileとdocker-compose.ymlの設定

まずはDockerで環境構築

$ mkdir myblog
$ cd myblog
$ touch {Dockerfile,docker-compose.yml}

Dockerfileはこの記事が非常にわかりやすいです。
Docker初心者がRails + PostgreSQL or MySQLで仮想環境構築した手順を丁寧にまとめる

Dockerfile 解説
FROM dockerhubからイメージをダウンロード
WORKDIR 作業ディレクトリの指定
RUN コマンドの実行
COPY 引数1を引数2にコピー

yarnインストール参考docker for macでrails × yarn × webpackerのfront環境を整える

myblog/Dockerfile
FROM ruby:2.5.5
RUN apt-get update && apt-get install -y build-essential nodejs libpq-dev 

#yarnインストール webpackで必要になります。
RUN curl apt-transport-https wget && \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

RUN mkdir /rails
WORKDIR /rails
COPY Gemfile /rails/Gemfile
COPY Gemfile.lock /rails/Gemfile.lock
RUN bundle install
COPY . /rails

docker-composeはこの記事が非常にわかりやすいです
docker-compose.ymlの書き方について解説してみた

docker-compose 解説
version docker-composeの文法はバージョンごとにことなるので指定が必要
servise 動かすアプリケーションの指定。ここでは、webとdb。
Service設定する際の項目について
docker-compose.yml
version: '3' 
services: 

  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/rails
    ports:
      - "3000:3000" #ポート3000番を開けてコンテナの3000番に転送
    depends_on:
      - db

  db:
    image: postgres
    volumes:
      - datavol:/var/lib/postgresql/data

volumes:
  datavol:

Railsアプリを作る。

以下のコマンドを入力

$ touch {Gemfile,Gemfile.lock}

$ echo "source 'https://rubygems.org' 
gem 'rails','5.1.4'
gem 'pg', '~> 0.20.0'" > Gemfile

$ docker-compose run web bundle exec rails new . --force --database=postgresql

$ docker-compose build

ここまででRailsサーバーを立ち上げる準備が整っているはずなので立ち上げてみる。

$ docker-compose up -d //サーバー起動
$ docker-compose run web rake db:create //db作成

ここにアクセス
みなさんは成功したでしょうか??....

Reactを導入

とりあえずRailsの初期画面から変更を行う。

コントローラーを作ろう

$ rails g controller StaticPages home about contact

ルートの設定

routes.rb
Rails.application.routes.draw do
  root 'static_pages#home'
  get '/about', to: 'static_pages#about'
  get '/contact', to: 'static_pages#contact'
end

これでReactでviewに変更を加える準備ができました。

gem追加

$ echo "gem 'webpacker'
gem 'react-rails'">>Gemfile

$ docker-compose run web bundle update

webpack設定

$ docker-compose run web rails webpacker:install 
$ docker-compose run web rails webpacker:install:react

ここまでくると、app/assets/javascriptというファイルが作成される。
この中のファイルがreactファイルになっている。

試しにrailsのviewに呼び出したいころではあるが、railsサーバーを再起動しないと反映されないのでrailsコンテナを再起動。

$ docker ps //稼働中のコンテナの表示
0739cbd77243        170064292a20        "bundle exec rails s…"   12 hours ago        Up 12 hours         0.0.0.0:3000->3000/tcp   myblog_web_1
0d302bae2084        postgres            "docker-entrypoint.s…"   13 hours ago        Up 13 hours         5432/tcp                 myblog_db_1

上の場合だと

0739cbd77243これがrailsコンテナのIDになるので、このIDを指定してコンテナの再起動をする

$ docker restart 0739cbd77243 //コンテナ起動

こうなったら成功です!おつかれさまでした!
スクリーンショット 2019-11-30 13.58.04.png

参考

Rails で postgresql を使う(インストールからマイグレーションまで)

Docker初心者がRails + PostgreSQL or MySQLで仮想環境構築した手順を丁寧にまとめる
既存のRailsアプリにReactを導入する方法

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

VPN ServerにはPritunlが便利

Free Wi-Fiなどを利用する際は、VPNに接続すると安全です。仕組みは、通信が暗号化されるトンネルのようなものを作り、そこにトラフィックを通すことで通信内容の傍受などを阻害する効果があります。つまり、Free Wi-Fiを利用して何らかのサービスにログインする際にidやpassを読み取られる危険などがあるわけですが、それを回避できます。

また、VPNを使うとVPN Serverを経由するため、IP AddressがそのServerのものに変わります。(ただし、使用するツールや環境変数に依存する場合あり)。

しかし、VPNはなかなか面倒で一番良いのは、サービスにインストールして使えるようにすることですが、費用がかかります。

今回は、Web UIから設定が行えるpritunlを使ってLocal NetworkにVPN Serverを立ててみようと思います。

https://github.com/Fridus/docker-pritunl

$ mkdir mongo
$ sudo docker-compose up -d
docker-compose.yml
network:
  image: busybox
  ports:
    - "9700:443"
    - "1194:1194/udp"
  restart: always
  tty: true
mongo:
  image: mongo
  volumes:
    - ./mongo:/data/db
  restart: always
  net: container:network
pritunl:
  image: fridus/pritunl
  privileged: true
  environment:
    - MONGO_URI=mongodb://127.0.0.1:27017/pritunl
  restart: always
  net: container:network

もしくはjippi/pritunlを使います。

#!/bin/bash

datadir="$(dirname $(readlink -f "$0"))/data"
echo "datadir=$datadir"

mkdir -p $datadir/{mongodb,pritunl}
touch $datadir/pritunl.conf

sudo docker run \
    --name=pritunl \
    --detach \
    --cap-add NET_ADMIN \
    --network=bridge \
    --restart=always \
    -v $datadir/mongodb:/var/lib/mongodb \
    -v $datadir/pritunl:/var/lib/pritunl \
    -v $datadir/pritunl.conf:/etc/pritunl.conf \
    -p 1194:1194/udp \
    -p xxxxxx:xxxxx/tcp \
    jippi/pritunl

次に、web UIから設定を行います。

$ ifconfig
192.168.1.4

$ chromium https://192.168.1.4:9700
$ chromium https://localhost:9700
    user,password:pritunl

# IP : 192.168.1.4
# User -> Add Organization, Add User
# Server -> Add Server(Port 1194/udp), Attach Organization, Start Server
# User -> download profile

あとは、VPN Clientをインストールして、DLしたprofileをインポートするだけです。clientは、Tunnelblickでもpritunlでもどちらでもいいですが、osによっていろいろなものがあります。

WANからアクセスする際は、ルーターにてポートフォワーディングなどをします。例えば、ルーターのGlobal IPが1.1.1.1だったとしましょう。そこで、1.1.1.1への特定のポートのアクセスを、ローカルネットワークの特定のポートに転送する設定です。(なお、現実では1.1.1.1はcloudflareのdnsです)

# global ipを調べる
$ curl -sL ipinfo.io

ただ、downloadしたprofile(xxx.ovpn)は、Local IPを指定していますので、設定ファイルを書き直さなければなりません。

$ aunpack default.tar
$ vim vpn_default.ovpn
- remote 192.168.1.4 1194 udp
+ remote 1.1.1.1 xxxxxx udp

xxxxxxのところは、特定されにくそうな番号を指定すると良いです。それをdockerで指定しているポート、ここでは1194ですが、そこに転送する設定を保存します。これは、ルーターのポート転送(ポートフォワーディング)などの項目になります。つまり、WAN側からの特定のポートへのアクセスに対して、LAN側の特定ポートに転送する処理です。

1.1.1.1:xxxxx/udp -> 192.168.1.4:1194/udp

このようにすることで、例えば、iOSのOpenVPNというアプリでvpn_default.ovpnを開き、キャリア回線(WAN)からVPN Serverに接続できるようになります。なお、ルータのポートを開放するため、比較的危険な設定になりますので注意してください。ただし、Global IPからLocal Networkにアクセスするにはこの方法が最も安全だと思います。通常、WAN側からLocal NetworkにSSHするような場合も多くの人はこういう方法を使ってるはず。

もし接続がうまくいかない場合は、pritunlの設定もルーターのGlobal IPを指定して、Restart Serverします。そうしないと、Virtual Networkが起動せず、Local IPで接続しているうちはVirtual Networkが立ち上がっているので、Globalからもアクセスできますが、それが切断されるとGlobalからの接続もできなくなってしまいます。

Local NetworkにVPN Serverを立ち上げるメリット

先程、WANからSSHする場合、通常はルータのポート転送(ポートフォワーディング)を利用すると言いました。

しかし、Local NetworkにVPN Serverを立てている場合は別です。

WANから自前のVPN Serverを通すと、自宅ルータのGlobal IPのみならずLocal IPも取得することになります。これを利用して、Local Networkにある各Serverにもいつもどおりアクセスできることになります。つまり、以下のようなSSHが通ります。

~/.ssh/config
Host usb
    HostName 192.168.1.33
    Port 22
    IdentityFile ~/.ssh/usb
    User syui

通常、WAN側からSSHするには、以下のような内容になります。わざわざ自宅のルータIP(Global IP)をHostNameに変更して、ポート転送の設定までしなければなりません。

~/.ssh/config
Host usb
    HostName 1.1.1.1 # Global IP
    Port 22222 # ポート転送のポート番号(22222 -> 192.168.1.33:22)
    IdentityFile ~/.ssh/usb
    User syui

なお、DDNSを利用している場合は別です。DDNSは、例えばルータ(自宅)のGlobal IP(変動する数字)を特定のドメイン名(固定の文字列)に変換します。よって、HostNameにはDDNSを書けばいいだけになります。VPNの設定ファイルでも同じ。

~/.ssh/config
Host usb
    HostName github.com.ddns.syui # DDNS
    Port 22222 # ポート転送のポート番号(22222 -> 192.168.1.33:22)
    IdentityFile ~/.ssh/usb
    User syui
xxx.opvn
- remote 1.1.1.1 1194 udp
+ remote github.com.ddns.syui 22222 udp

しかし、DDNSサービスは有料であることも多いので、私はGlobal IPを使うことが多いです。Global IPの変動は、privateのチャンネルなどを用意し、IPの変動があれば教えるようなcronを実行しておけばいいでしょう。

DDNS

DDNSには例えば以下のようなものがあります。ただ、安全性などは調査していません。ddns.pboehm.deではnameは10日間更新がないと自動削除されます。

https://github.com/pboehm/ddns

https://ddns.pboehm.de/

# example
myowntest.d.pboehm.de

ddns is built around a small webservice, so that you can update your IP address simply by calling an URL periodically through curl. Hosts that haven't been updated for 10 days will be automatically removed. This can be configured in your own instance.

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

FILEベースCMS GRAVのインストール(Docker)

この記事の目的

FILEベースCMS GRAVのインストール方法を記載しています。

今回は CentOS7 上の Docker 環境下に GRAV を入れて立ち上げてみます。

手順としてコンテナをビルドするところから実施する手順としました。理由は後述。

前提事項

以下の環境で実施しました。

ホストOS: CentOS7 7.7.1908 (Core)
Docker: 1.13.1

セットアップ

準備

Dockerfile 他のファイルを置くディレクトリを作成しておきます。

mkdir -p /home/vm/docker-grav

git clone

git clone コマンドで関連するファイルをダウンロード

今回は admin 画面も利用するので、Dockerfile.gravcoreadminファイルを利用します。

cd /home/vm/docker-grav
git clone https://github.com/dsavell/docker-grav.git .

cp -p Dockerfile.gravcoreadmin Dockerfile

コンテナのビルド

Docker コマンドでビルドを実施します。

docker build -t local/docker-grav:1.0 .

できたらイメージを確認します。

docker images

保存ディレクトリの作成

コンテンツを保存するフォルダやキャッシュ関連のフォルダを作成しておきます。

mkdir -p /home/vm/docker-grav/user
mkdir -p /home/vm/docker-grav/backup
mkdir -p /home/vm/docker-grav/cache

コンテナ実行

引数を渡してコンテナを実行します。
今回はローカルポート 9080 にバインドして起動しました。

docker run -d -p 9080:80 -v /home/vm/docker-grav/cache:/var/www/grav/cache -v /home/vm/docker-grav/backup:/var/www/grav/backup -v /home/vm/docker-grav/user:/var/www/grav/user local/docker-grav:1.0

確認

# docker ps
CONTAINER ID        IMAGE                   COMMAND             CREATED              STATUS              PORTS                           NAMES
9efae32cddc5        local/docker-grav:1.0   "/init-admin"       About a minute ago   Up About a minute   443/tcp, 0.0.0.0:9080->80/tcp   trusting_ritchie

使い方

ブラウザを起動してポート9080 でアクセスします。

image.png

終わりに

設定で各種プラグインやテーマを選択していくと、いくつかのPHPモジュールが必要となることがあります。今回も mbstring が足りず起動できなくなりました。その際には Dockerfile に php-mbstring を追加してビルドからやり直す必要があります。

GRAVはまだ活発に更新されているので、ちょくちょくと修正が入ります。

dsavell さんの Docker Hub も登録されているのですが、GRAVを更新すると動かなくなったりします。

現にこの記事を作成するにあたって、ほかの方の記事も参考にしたのですが、変更がありそのままでは動かなかったりするため、自分で Dockerfile を修正してビルドする手順に落ち着きました。

参考記事

https://github.com/dsavell/docker-grav

https://qiita.com/bremen/items/2d920a96701748673234

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

コンテナ型仮想化技術 Study07 / ストレージ

はじめに

お勉強のログです。Persistent Volume辺り。

関連記事

コンテナ型仮想化技術 Study01 / Docker基礎
コンテナ型仮想化技術 Study02 / Docker レジストリ
コンテナ型仮想化技術 Study03 / Docker Compose
コンテナ型仮想化技術 Study04 / Minikube & kubectl簡易操作
コンテナ型仮想化技術 Study05 / Pod操作
コンテナ型仮想化技術 Study06 / ReplicaSet, Deployment, Service
コンテナ型仮想化技術 Study06' / Kubernetesネットワーク問題判別
コンテナ型仮想化技術 Study07 / ストレージ
コンテナ型仮想化技術 Study08 / Statefulset, Ingress
コンテナ型仮想化技術 Study09 / Helm

参考情報

Persistent Volumes
【図解】初心者にも分かる iSCSI の仕組み ~NAS(NFS)との違いやメリット,デメリット~
ウマいストレージの選び方。

ストレージの種類の整理

基本、コンテナは独立した稼働環境で、ディスクのイメージもそれぞれ独立していますが、コンテナ間(Pod間)で共有したい情報もあります。その場合、各コンテナから共有できるディスクをアクセスする必要がありますが、共有のための具体的なストレージ実装を抽象化して柔軟に構成できる仕組みをKubernetesでは提供しており、永続ボリューム/Persistent Volumeなどと呼ばれているようです。
利用できるストレージの実装方式はいくつかあり、それぞれ特徴も異なります。例えば以下のようなものがあるようです。

  • emptyDir: 同一Pod内のコンテナでノード上のディスクを共有する仕組み。Podをまたぐと共有できない。Pod終了時に削除される。一時的な利用で使われる?
  • hostPath: 同一ノード内のコンテナでノード上のディスクを共有する仕組み。ノードのディスクを使うため、別のノードのPodとは共有できない。シングルノード前提でしか使えない。
  • NFS: ネットワーク経由で複数ノードからディスクを共有する仕組み。ノードを跨って複数コンテナからディスク共有可能。同時に複数のコンテナからReadWriteモードでマウント可能。
  • GlasterFS: OSSのスケーラブルな分散ファイルシステム。ノードを跨って複数コンテナからディスク共有可能。同時に複数のコンテナからReadWriteモードでマウント可能。
  • iSCSI: SCSIプロトコルをTCP/IPネットワーク経由で使用するためのプロトコル。ReadWriteモードで接続できるノードは1時点で1つ。

Force write, 排他制御, パフォーマンスあたりが気になる所ですが...

関連リソースの整理

Podで共有するストレージの構成の仕方として、大きくStatic Provisioning / Dynamic Provisioning という2種類の方法があります。
参考: Persistent Volumes

それぞれで定義しなければいけないリソースが若干異なります。

Static Provisioning

関連するリソースとしては、以下のようなイメージとなります。
image.png

  • PersistentVolume(PV): 使用するストレージサービス(NFS,GlasterFSなど)に応じて、それらのサービスを利用するための情報を管理します。これによりストレージサービスの実装が隠蔽されます。
  • PersistentVolumeClaim(PVC): 名前の通り、PersistentVolumeに対する"要求"を管理するものです。どのPVを利用して、どういうモード(Read/Write)でアクセスして、どのくらいのサイズを確保するか、といったことを定義します。

事前に、利用するストレージサービスの構成(NFSサーバーやGlusterFSのサーバー構成など)は行っておき、それに応じたPV, PVCをKubernetes Clusterリソースとして定義し、PodからはPVCを指定して共有ストレージを利用する、という流れになります。

Dynamic Provisioning

参考: ボリュームの動的プロビジョニング(Dynamic Volume Provisioning)

関連するリソースとしては、以下のようなイメージとなります。
image.png

  • StorageClass: PVを動的に作成するための情報を管理するリソース。使用するストレージサービスに応じて、PVを動的に作成(Provisioning)するためのProvisioner(pluginモジュール)などを指定することになります。

新たにStorageClassというリソースが関連します。GlusterFSなど動的にストレージを確保するストレージサービスと組み合わせることで、PVを静的に定義するのではなく、PVの定義含めてストレージを動的にProvisioningすることができます。

環境整備

仮想NFSサーバー

以下のVagrantファイルをそのまま使って、VirtualBox上にNFSサーバー用のゲストOSを立てます。
https://github.com/takara9/vagrant-nfs/

git clone して vagrant up

c:\y\Vagrant>git clone https://github.com/takara9/vagrant-nfs
Cloning into 'vagrant-nfs'...
remote: Enumerating objects: 26, done.
remote: Total 26 (delta 0), reused 0 (delta 0), pack-reused 26
Unpacking objects: 100% (26/26), done.

c:\y\Vagrant>cd vagrant-nfs

c:\y\Vagrant\vagrant-nfs>vagrant up
Bringing machine 'nfsserver' up with 'virtualbox' provider...
==> nfsserver: Importing base box 'ubuntu/xenial64'...
==> nfsserver: Matching MAC address for NAT networking...
==> nfsserver: Checking if box 'ubuntu/xenial64' version '20190613.1.0' is up to date...
==> nfsserver: A newer version of the box 'ubuntu/xenial64' for provider 'virtualbox' is
==> nfsserver: available! You currently have version '20190613.1.0'. The latest is version
==> nfsserver: '20191126.1.0'. Run `vagrant box update` to update.
==> nfsserver: Setting the name of the VM: vagrant-nfs_nfsserver_1574923150593_32019
==> nfsserver: Clearing any previously set network interfaces...
==> nfsserver: Preparing network interfaces based on configuration...
    nfsserver: Adapter 1: nat
    nfsserver: Adapter 2: hostonly
==> nfsserver: Forwarding ports...
    nfsserver: 22 (guest) => 2222 (host) (adapter 1)
==> nfsserver: Running 'pre-boot' VM customizations...
==> nfsserver: Booting VM...
==> nfsserver: Waiting for machine to boot. This may take a few minutes...
    nfsserver: SSH address: 127.0.0.1:2222
    nfsserver: SSH username: vagrant
    nfsserver: SSH auth method: private key
    nfsserver:
    nfsserver: Vagrant insecure key detected. Vagrant will automatically replace
    nfsserver: this with a newly generated keypair for better security.
    nfsserver:
    nfsserver: Inserting generated public key within guest...
    nfsserver: Removing insecure key from the guest if it's present...
    nfsserver: Key inserted! Disconnecting and reconnecting using new SSH key...
==> nfsserver: Machine booted and ready!
==> nfsserver: Checking for guest additions in VM...
    nfsserver: The guest additions on this VM do not match the installed version of
    nfsserver: VirtualBox! In most cases this is fine, but in rare cases it can
    nfsserver: prevent things such as shared folders from working properly. If you see
    nfsserver: shared folder errors, please make sure the guest additions within the
    nfsserver: virtual machine match the version of VirtualBox you have installed on
    nfsserver: your host and reload your VM.
    nfsserver:
    nfsserver: Guest Additions Version: 5.1.38
    nfsserver: VirtualBox Version: 6.0
==> nfsserver: Setting hostname...
==> nfsserver: Configuring and enabling network interfaces...
==> nfsserver: Mounting shared folders...
    nfsserver: /vagrant => C:/y/Vagrant/vagrant-nfs
==> nfsserver: Running provisioner: ansible_local...
    nfsserver: Installing Ansible...
Vagrant has automatically selected the compatibility mode '2.0'
according to the Ansible version installed (2.9.1).

Alternatively, the compatibility mode can be specified in your Vagrantfile:
https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode
    nfsserver: Running ansible-playbook...

PLAY [nfsserver] ***************************************************************

TASK [Gathering Facts] *********************************************************
ok: [nfsserver]

TASK [Create export dir] *******************************************************
changed: [nfsserver]

TASK [Ensure NFS utilities are installed.] *************************************
[DEPRECATION WARNING]: Invoking "apt" only once while using a loop via
squash_actions is deprecated. Instead of using a loop to supply multiple items
and specifying `name: "{{ item }}"`, please use `name: ['nfs-common', 'nfs-
kernel-server']` and remove the loop. This feature will be removed in version
2.11. Deprecation warnings can be disabled by setting
deprecation_warnings=False in ansible.cfg.
changed: [nfsserver] => (item=[u'nfs-common', u'nfs-kernel-server'])
[WARNING]: Updating cache and auto-installing missing dependency: python-apt

TASK [copy /etc/exports] *******************************************************
changed: [nfsserver]

TASK [restart nfs server] ******************************************************
changed: [nfsserver]

PLAY RECAP *********************************************************************
nfsserver                  : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

仮想GlusterFSクラスタ

以下のVagrantファイルをそのまま使って、VirtualBox上にGlusterFSのクラスタを構成します(heteki+Glusterクラスタx3の合計4ノード)。
https://github.com/takara9/vagrant-glusterfs

c:\y\Vagrant>git clone https://github.com/takara9/vagrant-glusterfs
Cloning into 'vagrant-glusterfs'...
remote: Enumerating objects: 50, done.
remote: Counting objects: 100% (50/50), done.
remote: Compressing objects: 100% (37/37), done.
remote: Total 126 (delta 26), reused 30 (delta 12), pack-reused 76 eceiving objects:  70% (89/
Receiving objects: 100% (126/126), 29.01 KiB | 345.00 KiB/s, done.
Resolving deltas: 100% (56/56), done.

c:\y\Vagrant>cd vagrant-glusterfs

c:\y\Vagrant\vagrant-glusterfs>vagrant up
Bringing machine 'gluster1' up with 'virtualbox' provider...
Bringing machine 'gluster2' up with 'virtualbox' provider...
Bringing machine 'gluster3' up with 'virtualbox' provider...
Bringing machine 'heketi' up with 'virtualbox' provider...
==> gluster1: Importing base box 'ubuntu/xenial64'...
==> gluster1: Matching MAC address for NAT networking...
==> gluster1: Checking if box 'ubuntu/xenial64' version '20190613.1.0' is up to date...
==> gluster1: A newer version of the box 'ubuntu/xenial64' for provider 'virtualbox' is
==> gluster1: available! You currently have version '20190613.1.0'. The latest is version
==> gluster1: '20191126.1.0'. Run `vagrant box update` to update.
==> gluster1: Setting the name of the VM: vagrant-glusterfs_gluster1_1574936789288_7817
==> gluster1: Clearing any previously set network interfaces...
==> gluster1: Preparing network interfaces based on configuration...
    gluster1: Adapter 1: nat
    gluster1: Adapter 2: hostonly
==> gluster1: Forwarding ports...
    gluster1: 22 (guest) => 2222 (host) (adapter 1)
==> gluster1: Running 'pre-boot' VM customizations...
==> gluster1: Booting VM...
==> gluster1: Waiting for machine to boot. This may take a few minutes...
    gluster1: SSH address: 127.0.0.1:2222
    gluster1: SSH username: vagrant
    gluster1: SSH auth method: private key
    gluster1:
    gluster1: Vagrant insecure key detected. Vagrant will automatically replace
    gluster1: this with a newly generated keypair for better security.
    gluster1:
    gluster1: Inserting generated public key within guest...
    gluster1: Removing insecure key from the guest if it's present...
    gluster1: Key inserted! Disconnecting and reconnecting using new SSH key...
==> gluster1: Machine booted and ready!
==> gluster1: Checking for guest additions in VM...
    gluster1: The guest additions on this VM do not match the installed version of
    gluster1: VirtualBox! In most cases this is fine, but in rare cases it can
    gluster1: prevent things such as shared folders from working properly. If you see
    gluster1: shared folder errors, please make sure the guest additions within the
    gluster1: virtual machine match the version of VirtualBox you have installed on
    gluster1: your host and reload your VM.
    gluster1:
    gluster1: Guest Additions Version: 5.1.38
    gluster1: VirtualBox Version: 6.0
==> gluster1: Setting hostname...
==> gluster1: Configuring and enabling network interfaces...
==> gluster1: Mounting shared folders...
    gluster1: /vagrant => C:/y/Vagrant/vagrant-glusterfs
==> gluster1: Running provisioner: shell...
    gluster1: Running: inline script
==> gluster1: Running provisioner: ansible_local...
    gluster1: Installing Ansible...
Vagrant has automatically selected the compatibility mode '2.0'
according to the Ansible version installed (2.9.1).

Alternatively, the compatibility mode can be specified in your Vagrantfile:
https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode
    gluster1: Running ansible-playbook...

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [gluster1]

TASK [Add GlusterFS Repository] ************************************************
changed: [gluster1]

TASK [Ensure PKG are installed.] ***********************************************
changed: [gluster1]

TASK [cp ssh-key] **************************************************************
changed: [gluster1]

PLAY RECAP *********************************************************************
gluster1                   : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

==> gluster2: Importing base box 'ubuntu/xenial64'...
==> gluster2: Matching MAC address for NAT networking...
==> gluster2: Checking if box 'ubuntu/xenial64' version '20190613.1.0' is up to date...
==> gluster2: Setting the name of the VM: vagrant-glusterfs_gluster2_1574936997945_17908
==> gluster2: Fixed port collision for 22 => 2222. Now on port 2200.
==> gluster2: Clearing any previously set network interfaces...
==> gluster2: Preparing network interfaces based on configuration...
    gluster2: Adapter 1: nat
    gluster2: Adapter 2: hostonly
==> gluster2: Forwarding ports...
    gluster2: 22 (guest) => 2200 (host) (adapter 1)
==> gluster2: Running 'pre-boot' VM customizations...
==> gluster2: Booting VM...
==> gluster2: Waiting for machine to boot. This may take a few minutes...
    gluster2: SSH address: 127.0.0.1:2200
    gluster2: SSH username: vagrant
    gluster2: SSH auth method: private key
    gluster2:
    gluster2: Vagrant insecure key detected. Vagrant will automatically replace
    gluster2: this with a newly generated keypair for better security.
    gluster2:
    gluster2: Inserting generated public key within guest...
    gluster2: Removing insecure key from the guest if it's present...
    gluster2: Key inserted! Disconnecting and reconnecting using new SSH key...
==> gluster2: Machine booted and ready!
==> gluster2: Checking for guest additions in VM...
    gluster2: The guest additions on this VM do not match the installed version of
    gluster2: VirtualBox! In most cases this is fine, but in rare cases it can
    gluster2: prevent things such as shared folders from working properly. If you see
    gluster2: shared folder errors, please make sure the guest additions within the
    gluster2: virtual machine match the version of VirtualBox you have installed on
    gluster2: your host and reload your VM.
    gluster2:
    gluster2: Guest Additions Version: 5.1.38
    gluster2: VirtualBox Version: 6.0
==> gluster2: Setting hostname...
==> gluster2: Configuring and enabling network interfaces...
==> gluster2: Mounting shared folders...
    gluster2: /vagrant => C:/y/Vagrant/vagrant-glusterfs
==> gluster2: Running provisioner: shell...
    gluster2: Running: inline script
==> gluster2: Running provisioner: ansible_local...
    gluster2: Installing Ansible...
Vagrant has automatically selected the compatibility mode '2.0'
according to the Ansible version installed (2.9.1).

Alternatively, the compatibility mode can be specified in your Vagrantfile:
https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode
    gluster2: Running ansible-playbook...

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [gluster2]

TASK [Add GlusterFS Repository] ************************************************
changed: [gluster2]

TASK [Ensure PKG are installed.] ***********************************************
changed: [gluster2]

TASK [cp ssh-key] **************************************************************
changed: [gluster2]

PLAY RECAP *********************************************************************
gluster2                   : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

==> gluster3: Importing base box 'ubuntu/xenial64'...
==> gluster3: Matching MAC address for NAT networking...
==> gluster3: Checking if box 'ubuntu/xenial64' version '20190613.1.0' is up to date...
==> gluster3: Setting the name of the VM: vagrant-glusterfs_gluster3_1574937198478_31337
==> gluster3: Fixed port collision for 22 => 2222. Now on port 2201.
==> gluster3: Clearing any previously set network interfaces...
==> gluster3: Preparing network interfaces based on configuration...
    gluster3: Adapter 1: nat
    gluster3: Adapter 2: hostonly
==> gluster3: Forwarding ports...
    gluster3: 22 (guest) => 2201 (host) (adapter 1)
==> gluster3: Running 'pre-boot' VM customizations...
==> gluster3: Booting VM...
==> gluster3: Waiting for machine to boot. This may take a few minutes...
    gluster3: SSH address: 127.0.0.1:2201
    gluster3: SSH username: vagrant
    gluster3: SSH auth method: private key
    gluster3:
    gluster3: Vagrant insecure key detected. Vagrant will automatically replace
    gluster3: this with a newly generated keypair for better security.
    gluster3:
    gluster3: Inserting generated public key within guest...
    gluster3: Removing insecure key from the guest if it's present...
    gluster3: Key inserted! Disconnecting and reconnecting using new SSH key...
==> gluster3: Machine booted and ready!
==> gluster3: Checking for guest additions in VM...
    gluster3: The guest additions on this VM do not match the installed version of
    gluster3: VirtualBox! In most cases this is fine, but in rare cases it can
    gluster3: prevent things such as shared folders from working properly. If you see
    gluster3: shared folder errors, please make sure the guest additions within the
    gluster3: virtual machine match the version of VirtualBox you have installed on
    gluster3: your host and reload your VM.
    gluster3:
    gluster3: Guest Additions Version: 5.1.38
    gluster3: VirtualBox Version: 6.0
==> gluster3: Setting hostname...
==> gluster3: Configuring and enabling network interfaces...
==> gluster3: Mounting shared folders...
    gluster3: /vagrant => C:/y/Vagrant/vagrant-glusterfs
==> gluster3: Running provisioner: shell...
    gluster3: Running: inline script
==> gluster3: Running provisioner: ansible_local...
    gluster3: Installing Ansible...
Vagrant has automatically selected the compatibility mode '2.0'
according to the Ansible version installed (2.9.1).

Alternatively, the compatibility mode can be specified in your Vagrantfile:
https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode
    gluster3: Running ansible-playbook...

PLAY [all] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [gluster3]

TASK [Add GlusterFS Repository] ************************************************
changed: [gluster3]

TASK [Ensure PKG are installed.] ***********************************************
changed: [gluster3]

TASK [cp ssh-key] **************************************************************
changed: [gluster3]

PLAY RECAP *********************************************************************
gluster3                   : ok=4    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

==> heketi: Importing base box 'ubuntu/xenial64'...
==> heketi: Matching MAC address for NAT networking...
==> heketi: Checking if box 'ubuntu/xenial64' version '20190613.1.0' is up to date...
==> heketi: Setting the name of the VM: vagrant-glusterfs_heketi_1574937397127_53647
==> heketi: Fixed port collision for 22 => 2222. Now on port 2202.
==> heketi: Clearing any previously set network interfaces...
==> heketi: Preparing network interfaces based on configuration...
    heketi: Adapter 1: nat
    heketi: Adapter 2: hostonly
==> heketi: Forwarding ports...
    heketi: 22 (guest) => 2202 (host) (adapter 1)
==> heketi: Running 'pre-boot' VM customizations...
==> heketi: Booting VM...
==> heketi: Waiting for machine to boot. This may take a few minutes...
    heketi: SSH address: 127.0.0.1:2202
    heketi: SSH username: vagrant
    heketi: SSH auth method: private key
    heketi:
    heketi: Vagrant insecure key detected. Vagrant will automatically replace
    heketi: this with a newly generated keypair for better security.
    heketi:
    heketi: Inserting generated public key within guest...
    heketi: Removing insecure key from the guest if it's present...
    heketi: Key inserted! Disconnecting and reconnecting using new SSH key...
==> heketi: Machine booted and ready!
==> heketi: Checking for guest additions in VM...
    heketi: The guest additions on this VM do not match the installed version of
    heketi: VirtualBox! In most cases this is fine, but in rare cases it can
    heketi: prevent things such as shared folders from working properly. If you see
    heketi: shared folder errors, please make sure the guest additions within the
    heketi: virtual machine match the version of VirtualBox you have installed on
    heketi: your host and reload your VM.
    heketi:
    heketi: Guest Additions Version: 5.1.38
    heketi: VirtualBox Version: 6.0
==> heketi: Setting hostname...
==> heketi: Configuring and enabling network interfaces...
==> heketi: Mounting shared folders...
    heketi: /vagrant => C:/y/Vagrant/vagrant-glusterfs
==> heketi: Running provisioner: shell...
    heketi: Running: inline script
==> heketi: Running provisioner: ansible_local...
    heketi: Installing Ansible...
Vagrant has automatically selected the compatibility mode '2.0'
according to the Ansible version installed (2.9.1).

Alternatively, the compatibility mode can be specified in your Vagrantfile:
https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode
    heketi: Running ansible-playbook...

PLAY [heketi] ******************************************************************

TASK [Gathering Facts] *********************************************************
ok: [heketi]

TASK [Add GlusterFS Repository] ************************************************
changed: [heketi]

TASK [Ensure PKG are installed.] ***********************************************
changed: [heketi]

TASK [cp id_rsa root] **********************************************************
changed: [heketi]

TASK [cp id_rsa vagrant] *******************************************************
changed: [heketi]

TASK [download] ****************************************************************
changed: [heketi]

TASK [extract tar] *************************************************************
changed: [heketi]

TASK [cp heketi.json] **********************************************************
changed: [heketi]

TASK [cp heketi.service for systemd] *******************************************
changed: [heketi]

TASK [reload systemd service] **************************************************
changed: [heketi]

TASK [Make sure a service is running] ******************************************
changed: [heketi]

PLAY RECAP *********************************************************************
heketi                     : ok=11   changed=10   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

==> heketi: Running provisioner: ansible_local...
Vagrant has automatically selected the compatibility mode '2.0'
according to the Ansible version installed (2.9.1).

Alternatively, the compatibility mode can be specified in your Vagrantfile:
https://www.vagrantup.com/docs/provisioning/ansible_common.html#compatibility_mode
    heketi: Running ansible-playbook...

PLAY [heketi] ******************************************************************

TASK [Gathering Facts] *********************************************************
ok: [heketi]

TASK [create gluster cluster] **************************************************
changed: [heketi]

TASK [add node] ****************************************************************
changed: [heketi] => (item=172.20.1.21)
changed: [heketi] => (item=172.20.1.22)
changed: [heketi] => (item=172.20.1.23)

TASK [add device 1] ************************************************************
changed: [heketi]

TASK [add device 2] ************************************************************
changed: [heketi]

TASK [add device 3] ************************************************************
changed: [heketi]

PLAY RECAP *********************************************************************
heketi                     : ok=6    changed=5    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0


シングルノード構成での操作例 (minikube)

Static Provisioning

NFSを共有ストレージとして利用してみます。
minikubeから上で作成したNFSを使用する想定です。

共有ストレージの準備

image.png

これは、上で作成したNFSをそのまま使います。

NFSサーバー(172.16.20.10)にsshで接続して、NFSとして公開しているファイルシステムの状況を確認してみます。

vagrant@nfsserver:~$ sudo exportfs -v
/export         172.16.20.0/24(rw,async,wdelay,insecure,root_squash,no_subtree_check,fsid=0,sec=sys,rw,root_squash,no_all_squash)

/exportというディレクトリが公開されてる状態です。

busyboxのコンテナを稼働させるPodから、NFSサーバーにpingが通ることを確認しておきます。

vagrant@minikube:~$ kubectl run -it busybox --restart=Never --rm --image=busybox sh
If you don't see a command prompt, try pressing enter.
/ # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:07
          inet addr:172.17.0.7  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:18 errors:0 dropped:0 overruns:0 frame:0
          TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:1516 (1.4 KiB)  TX bytes:868 (868.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # ping -c 3 172.16.20.10
PING 172.16.20.10 (172.16.20.10): 56 data bytes
64 bytes from 172.16.20.10: seq=0 ttl=62 time=0.756 ms
64 bytes from 172.16.20.10: seq=1 ttl=62 time=1.010 ms
64 bytes from 172.16.20.10: seq=2 ttl=62 time=1.384 ms

--- 172.16.20.10 ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 0.756/1.050/1.384 ms

ping通りましたが、あれ?PodにアサインされるIPアドレスって、クラスター内で閉じてるんじゃなかったっけ???外には出ていけるのか...

/ # ping -c 3 www.google.com
PING www.google.com (172.217.31.132): 56 data bytes
64 bytes from 172.217.31.132: seq=0 ttl=44 time=20.332 ms
64 bytes from 172.217.31.132: seq=1 ttl=44 time=19.837 ms
64 bytes from 172.217.31.132: seq=2 ttl=44 time=19.817 ms

--- www.google.com ping statistics ---
3 packets transmitted, 3 packets received, 0% packet loss
round-trip min/avg/max = 19.817/19.995/20.332 ms

名前解決もできてるな。
ちなみに、NFSサーバー側からこのPodのアドレス(172.17.0.7)にはPingは通らなかった。ま、それはそうだろうな。(でもPodが動いているノード上からはPing通った。)
うーん、ネットワークむつかしね。

とりあえずbusyboxからNFSへのpingが通るので準備としてはOKっぽい。

PersistentVolume(PV)作成

image.png

以下のマニフェストをそのまま使います。
https://github.com/takara9/codes_for_lessons/blob/master/step11/nfs/nfs-pv.yml

vagrant@minikube:~/codes_for_lessons/step11/nfs$ kubectl apply -f nfs-pv.yml
persistentvolume/nfs-1 created

vagrant@minikube:~/codes_for_lessons/step11/nfs$ kubectl get pv -o wide
NAME    CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS      CLAIM   STORAGECLASS   REASON   AGE   VOLUMEMODE
nfs-1   100Mi      RWX            Retain           Available                                   27s   Filesystem

PersistentVolumeClaim(PVC)作成

image.png

以下のマニフェストをそのまま使います。
https://github.com/takara9/codes_for_lessons/blob/master/step11/nfs/nfs-pvc.yml

vagrant@minikube:~/codes_for_lessons/step11/nfs$ kubectl apply -f nfs-pvc.yml
persistentvolumeclaim/nfs-1 created

vagrant@minikube:~/codes_for_lessons/step11/nfs$ kubectl get pv,pvc -o wide
NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS   REASON   AGE     VOLUMEMODE
persistentvolume/nfs-1   100Mi      RWX            Retain           Bound    default/nfs-1                           4m34s   Filesystem

NAME                          STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE   VOLUMEMODE
persistentvolumeclaim/nfs-1   Bound    nfs-1    100Mi      RWX                           33s   Filesystem

Podの作成

image.png

以下のマニフェストをそのまま使います。
https://github.com/takara9/codes_for_lessons/blob/master/step11/nfs/nfs-client.yml

Pod単体ではなくDeploymentの定義となってるので、Replicasetから複数Podが作成されることになります(replica数:2)。

vagrant@minikube:~/codes_for_lessons/step11/nfs$ kubectl get pv,pvc,pod -o wide
NAME                     CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM           STORAGECLASS   REASON   AGE   VOLUMEMODE
persistentvolume/nfs-1   100Mi      RWX            Retain           Bound    default/nfs-1                           22m   Filesystem

NAME                          STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE   VOLUMEMODE
persistentvolumeclaim/nfs-1   Bound    nfs-1    100Mi      RWX                           18m   Filesystem

NAME                              READY   STATUS    RESTARTS   AGE   IP           NODE       NOMINATED NODE   READINESS GATES
pod/nfs-client-7c4b8fc57f-nnjzp   1/1     Running   0          27s   172.17.0.8   minikube   <none>           <none>
pod/nfs-client-7c4b8fc57f-p4sn2   1/1     Running   0          27s   172.17.0.7   minikube   <none>           <none>

確認

これで、2つのPodからストレージが共有されているはず。

NFSサーバー側のステータスを見てみると...

vagrant@nfsserver:~$ showmount -a
All mount points on nfsserver:
172.16.20.1:/export

172.16.20.1からマウントしているように見える。このIPアドレスは何者?マルチノード構成だったら、これが複数になる???

1つめのPodに入って、マウント状況確認し、マウントされているディレクトリに適当なファイル作ってみます。

Pod1
vagrant@minikube:~$ kubectl exec -it nfs-client-7c4b8fc57f-nnjzp bash
root@nfs-client-7c4b8fc57f-nnjzp:/# df -h
Filesystem            Size  Used Avail Use% Mounted on
overlay               9.7G  4.1G  5.6G  43% /
tmpfs                  64M     0   64M   0% /dev
tmpfs                1000M     0 1000M   0% /sys/fs/cgroup
172.16.20.10:/export  9.7G  1.2G  8.5G  12% /mnt
/dev/sda1             9.7G  4.1G  5.6G  43% /etc/hosts
shm                    64M     0   64M   0% /dev/shm
tmpfs                1000M   12K 1000M   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                1000M     0 1000M   0% /proc/acpi
tmpfs                1000M     0 1000M   0% /proc/scsi
tmpfs                1000M     0 1000M   0% /sys/firmware
root@nfs-client-7c4b8fc57f-nnjzp:/# echo "aaaaa" > /mnt/test.txt
root@nfs-client-7c4b8fc57f-nnjzp:/# ls /mnt/
test.txt

もう1つのPodに入って、マウント状況確認し、マウントされているディレクトリのファイルを確認してみます。

Pod2
vagrant@minikube:~$ kubectl exec -it nfs-client-7c4b8fc57f-p4sn2 bash
root@nfs-client-7c4b8fc57f-p4sn2:/# df -h
Filesystem            Size  Used Avail Use% Mounted on
overlay               9.7G  4.1G  5.6G  43% /
tmpfs                  64M     0   64M   0% /dev
tmpfs                1000M     0 1000M   0% /sys/fs/cgroup
172.16.20.10:/export  9.7G  1.2G  8.5G  12% /mnt
/dev/sda1             9.7G  4.1G  5.6G  43% /etc/hosts
shm                    64M     0   64M   0% /dev/shm
tmpfs                1000M   12K 1000M   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                1000M     0 1000M   0% /proc/acpi
tmpfs                1000M     0 1000M   0% /proc/scsi
tmpfs                1000M     0 1000M   0% /sys/firmware
root@nfs-client-7c4b8fc57f-p4sn2:/# ls /mnt
test.txt
root@nfs-client-7c4b8fc57f-p4sn2:/# cat /mnt/test.txt
aaaaa

NFSサーバー上でも、当然同じファイルが確認できます。

NFS_Server
vagrant@nfsserver:~$ ls /export/
test.txt
vagrant@nfsserver:~$ cat /export/test.txt
aaaaa

疑問

あれ?そういえば、PVCのマニフェストではstorageでサイズらしきものを設定しているのだが、NFSで公開されたものをマウントする場合、普通、クライアント側でサイズなんて意識しないよなぁ。Static Provisioningの場合はこのサイズは意味無いのかな?

nfs-pvc.yml抜粋
..
  resources:
    requests:
      storage: "100Mi"
...

まとめ

image.png

※注意!
Static Provisioninの場合、PVCのstorageClassNameには必ず""(Null)を指定する必要があります。この項目を省略してしまうと、そのK8sクラスターのデフォルトのStorageClassを使用したDynamic Provisioningを行うものと認識されてしまいます。

マルチノード構成での操作例

Dyanamic Provisioning

環境としては、マルチノード構成のK8sクラスターと、上で作成したGlusterFSを使用します。
構成イメージとしては以下の通り。(いずれもWindowsのVirtualBox上のゲストOSとして構成)
image.png

共有ストレージの準備

image.png

共有ストレージとして、上のGlusterFSを使用します。GlusterFSはheketi経由でコントロールする想定なので、masterノードからheketiにpingが通ることを確認しておきます。

vagrant@master:~$ ping -c 3 172.20.1.20
PING 172.20.1.20 (172.20.1.20) 56(84) bytes of data.
64 bytes from 172.20.1.20: icmp_seq=1 ttl=63 time=0.643 ms
64 bytes from 172.20.1.20: icmp_seq=2 ttl=63 time=0.765 ms
64 bytes from 172.20.1.20: icmp_seq=3 ttl=63 time=0.788 ms

--- 172.20.1.20 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2006ms
rtt min/avg/max/mdev = 0.643/0.732/0.788/0.063 ms

StorageClass作成

image.png

Dynamic Provisioningでは、PV(Persistent Volume)を静的に定義するのではなく、リクエストに応じて動的にプロビジョニングすることになります。その基になるStorage Classというリソースを事前に定義しておきます。

Storage Classを作成するために、以下のYamlをそのまま適用します。
https://github.com/takara9/codes_for_lessons/blob/master/step11/glusterfs/gfs-sc.yml

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl apply -f gfs-sc.yml
storageclass.storage.k8s.io/gluster-heketi created

確認

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl get sc
NAME             PROVISIONER               AGE
gluster-heketi   kubernetes.io/glusterfs   26s

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl describe sc gluster-heketi
Name:            gluster-heketi
IsDefaultClass:  No
Annotations:     kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{},"name":"gluster-heketi"},"parameters":{"resturl":"http://172.20.1.20:8080","restuser":"admin","restuserkey":"admin"},"provisioner":"kubernetes.io/glusterfs"}

Provisioner:           kubernetes.io/glusterfs
Parameters:            resturl=http://172.20.1.20:8080,restuser=admin,restuserkey=admin
AllowVolumeExpansion:  <unset>
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>

PersistentVolumeClaim(PVC)作成

image.png

上のStorageClassを使用してPVをリクエストするためのPVCを作成します。
以下のYamlをそのまま適用します。
https://github.com/takara9/codes_for_lessons/blob/master/step11/glusterfs/gfs-pvc.yml

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl apply -f gfs-pvc.yml
persistentvolumeclaim/gvol-1 created

確認

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl get pvc
NAME     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS     AGE
gvol-1   Bound    pvc-152d576c-2ad9-11ea-8406-022ec1de99b7   10Gi       RWX            gluster-heketi   21s

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl describe pvc gvol-1
Name:          gvol-1
Namespace:     default
StorageClass:  gluster-heketi
Status:        Bound
Volume:        pvc-152d576c-2ad9-11ea-8406-022ec1de99b7
Labels:        <none>
Annotations:   kubectl.kubernetes.io/last-applied-configuration:
                 {"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"name":"gvol-1","namespace":"default"},"spec":{"accessModes...
               pv.kubernetes.io/bind-completed: yes
               pv.kubernetes.io/bound-by-controller: yes
               volume.beta.kubernetes.io/storage-provisioner: kubernetes.io/glusterfs
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      10Gi
Access Modes:  RWX
VolumeMode:    Filesystem
Events:
  Type       Reason                 Age   From                         Message
  ----       ------                 ----  ----                         -------
  Normal     ProvisioningSucceeded  43s   persistentvolume-controller  Successfully provisioned volume pvc-152d576c-2ad9-11ea-8406-022ec1de99b7 using kubernetes.io/glusterfs
Mounted By:  <none>

これにより、動的にPVが作成されます。
image.png

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl get pv
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM            STORAGECLASS     REASON   AGE
pvc-152d576c-2ad9-11ea-8406-022ec1de99b7   10Gi       RWX            Delete           Bound    default/gvol-1   gluster-heketi            4m27s

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl describe pv pvc-152d576c-2ad9-11ea-8406-022ec1de99b7
Name:            pvc-152d576c-2ad9-11ea-8406-022ec1de99b7
Labels:          <none>
Annotations:     Description: Gluster-Internal: Dynamically provisioned PV
                 gluster.kubernetes.io/heketi-volume-id: dfcee042efd048237f65705e680064f6
                 gluster.org/type: file
                 kubernetes.io/createdby: heketi-dynamic-provisioner
                 pv.beta.kubernetes.io/gid: 2000
                 pv.kubernetes.io/bound-by-controller: yes
                 pv.kubernetes.io/provisioned-by: kubernetes.io/glusterfs
Finalizers:      [kubernetes.io/pv-protection]
StorageClass:    gluster-heketi
Status:          Bound
Claim:           default/gvol-1
Reclaim Policy:  Delete
Access Modes:    RWX
VolumeMode:      Filesystem
Capacity:        10Gi
Node Affinity:   <none>
Message:
Source:
    Type:                Glusterfs (a Glusterfs mount on the host that shares a pod's lifetime)
    EndpointsName:       glusterfs-dynamic-152d576c-2ad9-11ea-8406-022ec1de99b7
    EndpointsNamespace:  0xc0006c9190
    Path:                vol_dfcee042efd048237f65705e680064f6
    ReadOnly:            false
Events:                  <none>

Pod作成

image.png

上のPVCを利用するPodを作成します。以下のDeployment用のYamlを適用することで、同じファイルを共有するPodのレプリカを2つ作成します。
https://github.com/takara9/codes_for_lessons/blob/master/step11/glusterfs/gfs-client.yml

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl apply -f gfs-client.yml
deployment.apps/gfs-client created

確認

vagrant@master:~/codes_for_lessons/step11/glusterfs$ kubectl get po  -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP            NODE    NOMINATED NODE   READINESS GATES
gfs-client-6947c4c7fd-5d8fd   1/1     Running   0          27s   10.244.2.26   node2   <none>           <none>
gfs-client-6947c4c7fd-jfnn4   1/1     Running   0          27s   10.244.1.37   node1   <none>           <none>

1つ目のPodに接続して、マウント先にファイルを作成してみます。

vagrant@master:~$ kubectl exec -it gfs-client-6947c4c7fd-5d8fd sh
# df -h
Filesystem                                        Size  Used Avail Use% Mounted on
overlay                                           9.7G  2.5G  7.3G  26% /
tmpfs                                              64M     0   64M   0% /dev
tmpfs                                             497M     0  497M   0% /sys/fs/cgroup
172.20.1.21:vol_dfcee042efd048237f65705e680064f6   10G   66M   10G   1% /mnt
/dev/sda1                                         9.7G  2.5G  7.3G  26% /etc/hosts
shm                                                64M     0   64M   0% /dev/shm
tmpfs                                             497M   12K  497M   1% /run/secrets/kubernetes.io/serviceaccount
tmpfs                                             497M     0  497M   0% /proc/acpi
tmpfs                                             497M     0  497M   0% /proc/scsi
tmpfs                                             497M     0  497M   0% /sys/firmware
# date > /mnt/test.txt
# cat /mnt/test.txt
Mon Dec 30 08:04:38 UTC 2019

もう一つのPodに接続して、マウント先ファイルを確認してみます。

vagrant@master:~$ kubectl exec -it gfs-client-6947c4c7fd-jfnn4 sh
# cat /mnt/test.txt
Mon Dec 30 08:04:38 UTC 2019

同じ内容のファイルが確認できました。意図した通りにファイルが共有できています!

まとめ

image.png

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

docker windows10home でWEBアクセスできなくてハマった話

docker windows10home でWEBアクセスできなくてハマった話

ビルドは、とりあえずできた。

参考にしたサイト
Docker | docker build と Dockerfile でイメージをビルドする基本
Dockerfileがあるディレクトリに移動して、以下のコマンドでOKでした。
$ docker build ./ -t hello1201_2

はまりポイント

$ docker run --init -d -e PORT=81 -p 0.0.0.0:8081:81 hello1201_2

このコマンド後、ブラウザで、http://0.0.0.0:8081
だと、アクセスできませんでした。
いろいろ試したけど、ダメめでした。

参考にしたのは、こちらのサイト
Docker入門日誌-その2- Webサーバ立ち上げ編
$ docker-machine ls
で出てくるURLを再確認します。

私の場合、
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default * virtualbox Running tcp://192.168.99.100:2376 v19.03.5
となっていたので、ブラウザで、http://192.168.99.100:8081
として、アクセスできました。

解決できない気がしてたけど、Google先生! ありがとう!
Google検索は、以下の通りでした。
”docker ローカルPC WEBアクセスできない”

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

Tensorflow DockerでTensorBoardを使う

環境

Ubuntu 18.04
Docker 19.03

手順

1. コンテナの起動。

$ docker run --runtime=nvidia -it -p [ホスト側のポート]:[コンテナ側のポート] tensorflow/tensorflow:latest-gpu-py3 bash

-pオプションで[ホスト側のポート]にアクセスすると、[コンテナ側のポート]にフォワードされるように起動する。
公開先のIPはデフォルトで0.0.0.0が指定されている。($ docker psで確認できる。)

【参考】
http://docs.docker.jp/engine/userguide/networking/default_network/binding.html
https://qiita.com/tifa2chan/items/a58e34019d4f10097a4d

2. tensorboardの起動

# tensorboard --port [コンテナ側のポート] --logdir [logのディレクトリパス] --bind_all

--bind_allもしくは--host=0.0.0.0でIPを指定。

【参考】
https://qrunch.net/@diatonic/entries/fPGdT6VC7FKEpCVm

3. ブラウザで開く。

http://0.0.0.0:[ホスト側のポート]をブラウザに入力する。

TensorBoard 2.0.0 http://0.0.0.0:[コンテナ側のポート]/と表示されるが、ホストからは直接コンテナのポートにアクセスできないので、コンテナ起動時に指定した[ホスト側のポート]からアクセスする。

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