Translate

2017年12月26日火曜日

Proxy管理下の Ubuntu Server 16.04.3 LTS導入済みマシン2台をつかってKubernetes環境を構築する

GeForce1050Ti搭載のGPU物理マシンが3台あるので、
これにDocker環境をあげて機械学習用に使うつもりなのだけど、
せっかく3台あるので前から気になっていた Kubernetes 環境で
立ち上げてみようかと、試してみた。

でも、あまりインストールに関する記事がない..
これからは Kubernetes だ!
とかいう記事はいくつかあるのだけど、
マイナーなTensorFlowとくらべても少ないなあ..

..ということで試行錯誤しながら、
インストール手順を作成してみた。

-----

1. 前提


マスター、ノード2台のマシンが用意されている状態とする。
Proxy Server proxy.server.com:8080 、
NTP Server ntp.server とする。

  • ホスト名 master
    •  OS: Ubuntu Server 16.04.3 LTS
    •  インストール時に Open SSH Server パッケージをチェック追加した最小構成
    •  IPアドレス:10.1.1.1/24
    •  sudo 可能なユーザでログイン可能

  • ホスト名 node01
    •  OS: Ubuntu Server 16.04.3 LTS
    •  インストール時に Open SSH Server パッケージをチェック追加した最小構成
    •  IPアドレス:10.1.1.2/24
    •  sudo 可能なユーザでログイン可能

※動作確認のため、上記マシンとLAN接続されたブラウザが動作するPCが別途必要


2. マスターの準備


  • master へログイン
  • sudo su -
  • echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
  • sysctl -p
  • vi /etc/apt/apt.conf
# 以下の2行を追加
Acquire::https::Proxy "http://proxy.server:8080/";
Acquire::http::Proxy "http://proxy.server:8080/";
  • apt update && apt -y upgrade && apt install -y ntpdate
  • ntpdate ntp.server
  • vi /etc/hosts
# 最終行に以下の2行を追加
10.1.1.1  master
10.1.1.2  node01
  • vi ~/.curlrc
# 新規追加
proxy=http://proxy.server:8080/
  • curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
  • vi /etc/apt/sources.list.d/kubernetes.list
# 新規追加
deb http://apt.kubernetes.io/ kubernetes-xenial main
  • apt update && apt install -y docker-engine
  • mkdir /etc/systemd/system/docker.service.d
  • vi /etc/systemd/system/docker.service.d/http-proxy.conf
# 新規追加、環境変数 NO_PROXY のエントリはネットワーク構成に合わせて変更
[Service]
Environment="HTTP_PROXY=http://solidproxy.exa-corp.co.jp:8080" "HTTPS_PROXY=http://solidproxy.exa-corp.co.jp:8080" "NO_PROXY=localhost,127.0.0.1,10.1.1.0/24,master,node01"
  • systemctl daemon-reload
  • systemctl restart docker
  • docker run hello-world
  • aptitude install -y kubelet kubeadm kubectl kubernetes-cni
  • vi ~/.bashrc
# 最終行へ追加、環境変数 NO_PROXY のエントリはネットワーク構成に合わせて変更
export http_proxy=http://solidproxy.exa-corp.co.jp:8080
export https_proxy=http://solidproxy.exa-corp.co.jp:8080
export no_proxy=localhost,127.0.0.1,10.1.1.0/24,master,node01
  • exit → 一旦rootから抜ける
  • sudo su -
  • swapoff -a
  • free -m
  • kubeadm init
# 実行最後にでてくるkubeadmコマンドをコピー(後でnode01上で実行する)
You can now join any number of machines by running the following on each node
as root:

  kubeadm join --token xxxxxxxxxxxxxxxxxx10 10.1.1.109:6443 --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  • exit
  • sudo cp /etc/kubernetes/admin.conf $HOME/
  • sudo chown $(id -u):$(id -g) $HOME/admin.conf
  • export KUBECONFIG=$HOME/admin.conf
  • kubectl get nodes → kubernetes バージョンを確認、ここでは 1.9.0 とする
  • kubectl apply -f https://cloud.weave.works/k8s/v1.9/net

3. ノードの準備


  • node01 へログイン
  • sudo su -
  • echo "net.ipv6.conf.all.disable_ipv6 = 1" >> /etc/sysctl.conf
  • sysctl -p
  • vi /etc/apt/apt.conf
# 以下の2行を追加
Acquire::https::Proxy "http://proxy.server:8080/";
Acquire::http::Proxy "http://proxy.server:8080/";
  • apt update && apt -y upgrade && apt install -y ntpdate
  • ntpdate ntp.server
  • vi /etc/hosts
# 最終行に以下の2行を追加
10.1.1.1  master
10.1.1.2  node01
  • vi ~/.curlrc
# 新規追加
proxy=http://proxy.server:8080/
  • curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
  • vi /etc/apt/sources.list.d/kubernetes.list
# 新規追加
deb http://apt.kubernetes.io/ kubernetes-xenial main
  • apt update && apt install -y docker-engine
  • mkdir /etc/systemd/system/docker.service.d
  • vi /etc/systemd/system/docker.service.d/http-proxy.conf
# 新規追加、環境変数 NO_PROXY のエントリはネットワーク構成に合わせて変更
[Service]
Environment="HTTP_PROXY=http://solidproxy.exa-corp.co.jp:8080" "HTTPS_PROXY=http://solidproxy.exa-corp.co.jp:8080" "NO_PROXY=localhost,127.0.0.1,10.1.1.0/24,master,node01"
  • systemctl daemon-reload
  • systemctl restart docker
  • docker run hello-world
  • aptitude install -y kubelet kubeadm kubectl kubernetes-cni
  • vi ~/.bashrc
# 最終行へ追加、環境変数 NO_PROXY のエントリはネットワーク構成に合わせて変更
export http_proxy=http://solidproxy.exa-corp.co.jp:8080
export https_proxy=http://solidproxy.exa-corp.co.jp:8080
export no_proxy=localhost,127.0.0.1,10.1.1.0/24,master,node01
  • exit → 一旦rootから抜ける
  • sudo su -
  • swapoff -a
  • free -m
  • kubeadm join --token xxxxxxxxxxxxxxxxxx10 10.1.1.109:6443 --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

4. 動作確認


  • masterへログイン
  • kubectl create namespace sock-shop
  • kubectl create -f "https://github.com/microservices-demo/microservices-demo/blob/master/deploy/kubernetes/complete-demo.yaml?raw=true"
  • kubectl describe svc front-end -n sock-shop
# NodePortを確認
Name:                     front-end
Namespace:                sock-shop
Labels:                   name=front-end
Annotations:             
Selector:                 name=front-end
Type:                     NodePort
IP:                       10.98.214.109
Port:                       80/TCP
TargetPort:               8079/TCP
NodePort:                   30001/TCP
Endpoints:               
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                  

  • 動作確認用PCのブラウザから http://10.1.1.1:30001/ を開く
→weave socksサイトが開いたら成功

A. 参考





以上
-----

docker ps すると、もうワケワカラン状態に..

Kubernetes環境だと複雑になりすぎてコッチが困るので、
機械学習の実行環境レベルだと Docker Compose までにしといたほうがいいかもなあ..



2017年12月19日火曜日

MS Bot Framework を使っていたら、突然Bot Framework State serviceを廃止する旨のメールが英語で飛んできた



Slackのボットを作ったりするのに MS Bot Framework を使っているのだけど、
突然表題ののような英語のメールが飛び込んできた。




ので、Google先生のちからを借りて勝手に翻訳してみた。
以下、翻訳文である。
#参照される方は at your own risk でお願いします。


Subject: Action required: The Bot Framework State service will retire in March 2018; update your bots to use your own state storage

件名:アクション必須:Bot Framework Stateサービスは2018年3月に廃止されます。 独自の状態記憶域を使用するようにボットを更新してください。


When you store your own state data, you have better control and more privacy over your bot’s state data. We strongly recommend that you store this data in your own storage accounts and subscriptions. Because of this, we’ll retire the Bot Framework State service on March 31, 2018.

独自の状態データを保存すると、ボットの状態データに対してより優れた制御とプライバシーが得られます。 このデータは、独自のストレージアカウントとサブスクリプションに保存することを強くお勧めします。 このため、2018年3月31日にBot Framework Stateサービスを廃止します。

You’re receiving this email because you’re currently using the Microsoft Bot Framework State service.

現在あなたは、Microsoft Bot Framework Stateサービスを使用しているので、このメールを送信させていただいました。

What is the Bot Framework State service?

By default, bots built on the BotBuilder SDK and Azure Bot Service Preview have stored their conversation state in a service called the Bot Framework State service. Within the Bot Framework, the Bot State service lets your bot store and retrieve state data that is related to the conversations that your bot conducts using the Bot Connector service. The Bot State service will be retired on March 31, 2018.


Bot Framework State service とは何?

BotBuilder SDKとAzure Bot Service Previewで構築されたボットは、デフォルトでは Bot Framework State service と呼ばれるサービスに対話状態を保存しています。ボットフレームワーク内で、 Bot Framework State service を使用すると、ボットコネクタサービスを使用してボットが行う会話に関連する状態データをボットストアして取り出すことができます。 Bot Stateサービスは2018年3月31日に廃止されます。


What does this change mean for you?
  • You need to store your bot state information in your own storage accounts and subscriptions, providing you more control and privacy over your bot’s state data
  • Your bots will need to be updated to use their own state storage to continue operating after March 31, 2018.
  • Starting on December 13, 2017, you’ll receive build warnings as well as warnings in the Bot Framework emulator.

この変更はあなたにとって何を意味するのか?

  • ボットの状態情報を独自のストレージアカウントとサブスクリプションに保存する必要があります。これにより、ボットの状態データに対してより多くの制御とプライバシーを提供します。
  • 2018年3月31日以降も動作を継続するために、ボットを独自のステートストレージを使用して更新する必要があります。
  • 2017年12月13日より、Bot Frameworkエミュレータでビルド警告と警告が表示されます。



Recommended action
  • Update your bots to use their own state storage. We recommend the following:
  • Saving state data in SQL with .NET
  • Saving state data with BotBuilder-Azure in Node.js
  • Saving state data with BotBuilder-Azure in .NET

推奨される行動

  • 独自の状態記憶域を使用するようにボットを更新する。以下を推奨します:
  • .NETでSQLを使用した状態データの保存を行う
  • BotBuilder-Azure in Node.js で状態データを保存する
  • BotBuilder-Azure in .NET で状態データを保存する



For more information, please visit the Manage state data documentation webpage.

詳細については、状態データの管理ドキュメンテーションWebページをご覧ください。

For questions or feedback, please visit Stack Overflow or contact Azure support.
 
ご質問やご意見は、Stack OverflowまたはAzureサポートにお問い合わせください。

Thank you,

ありがとうございます。

Your Azure Team

あなたのAzureチームよ

たしかにBot Frameworkって常に相手が同じ場合の前提で実装できるようになっていて
便利だったんだよね..
ただ、なんでかリバースProxy型のサイトが間に入っている構成になっていて
ここが有料になったら怖いなあ..とは思ってたんだよね..

いやらしいトコ突いてきたねえ..これまでの実装を維持したいならAzure上のコンテナを使えってか..
そうじゃなきゃ自分でセッション管理してみれば?ときた..


..Azure、ズッチーなあ!(山本高広風に)

NVIDIA Docker のバージョンが上がり Docker Composeでも対応できるようになった件

たまたまAWSの x2.large 上にTensorFlow実行環境を作成しようと、
ひさしぶりにNVIDIA Dockerのサイトへ行ったら..



バージョン、上がっとるがな..


ということで、慌ててREADME.mdを勝手に翻訳してみた。
#参照の際は at your own risk でお願いします。

-----(2017/12/17時点の NVIDIA Docker README.md)-----
警告:このプロジェクトはアルファ版(libnvidia-container)をベースにしています。すでに1.0よりも安定していますが、テスト支援を必要としています。

ライセンス:New BSD
ドキュメント:wiki
パッケージ:リポジトリ



ドキュメント

完全なドキュメントおよびFAQは、(GitHubの)リポジトリWikiを参照してください。


クイックスタート

既に使用されているディストリビューション上にNVIDIAドライバサポートされているバージョンDockerがインストールされていることを確認してください(前提条件を参照)。

※翻訳時点の前提条件
  • カーネルバージョンが3.10より大きい GNU/Linux x86_64
  • 1.12以降のバージョンのDocker
  • Fermi(2.1)より後のアーキテクチャを実装したNVIDIA GPU
  • NVIDIA ドライバ 361.93 (古いドライバはテストしていません)

/etc/docker/daemon.json をカスタムしている場合は、 nvidia-docker2 パッケージがこのファイルを上書きする可能性があります。

Xenial x86_64

# nvidia-docker 1.0 がインストール済みの場合:
# nvidia-docker および既存のGPUコンテナをすべて削除する必要があります
docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f
sudo apt-get purge -y nvidia-docker

# パッケージ利己地鶏の追加
curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | \
  sudo apt-key add -
curl -s -L https://nvidia.github.io/nvidia-docker/ubuntu16.04/amd64/nvidia-docker.list | \
  sudo tee /etc/apt/sources.list.d/nvidia-docker.list
sudo apt-get update

# nvidia-docker2 のインストールと Dokcer デーモン設定のリロード
sudo apt-get install -y nvidia-docker2
sudo pkill -SIGHUP dockerd

# 最新の公式CUDAイメージ上で nvidia-smi を実行し正常動作を確認
docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi


CentOS/RHEL 7 x86_64

# nvidia-docker 1.0 がインストール済みの場合:
# nvidia-docker および既存のGPUコンテナをすべて削除する必要があります
docker volume ls -q -f driver=nvidia-docker | xargs -r -I{} -n1 docker ps -q -a -f volume={} | xargs -r docker rm -f
sudo yum remove nvidia-docker

# パッケージ利己地鶏の追加
curl -s -L https://nvidia.github.io/nvidia-docker/centos7/x86_64/nvidia-docker.repo | \
  sudo tee /etc/yum.repos.d/nvidia-docker.repo

# nvidia-docker2 のインストールと Dokcer デーモン設定のリロード
sudo yum install -y nvidia-docker2
sudo pkill -SIGHUP dockerd

# 最新の公式CUDAイメージ上で nvidia-smi を実行し正常動作を確認
docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi

その他のディストリビューション/アーキテクチャ

Wikiのインストール章を参照してください。

問題(Issue)および貢献

どんな変更を行う場合でも、事前に Contributor License Agreement の署名付きコピーを digits@nvidia.com へ提出する必要があります。
  • 新しい問題を提出してお知らせください
  • あなたは pull request を開くことによって貢献することができます
-----(2017/12/17時点の NVIDIA Docker README.md)-----


..ん?

docker run --runtime=nvidia って指定すれば
nvidia-docker コマンドでなくてもいいんだ..

ちょっと検索すると、次のブログ記事を見つけた

[WonderPlanet Tech Blog] nvidia-dockerをdocker-composeから使う
http://tech.wonderpla.net/entry/2017/11/14/110000

..いつのまに devices なんてセクションが
docker-compose.yml にかけるようになったんだ..

まあ、おかげでDocker Compose できるようになったし..

あと、公式ではないけど nvidia-docker-compose なるコマンドを
GitHub で公開しているヒトもいた。

nvidia-docker-compose
https://github.com/eywalker/nvidia-docker-compose




公式でもなさそうなのだけど、
興味があったのでREADME.mdを翻訳してみた。
#こちらもat your own risk でお願いします。

-----(2017/12/18時点の nvidia-docker-compose README.md)-----
nvidia-docker-compose とは、nvidia-docker で動作するGPU 対応の Docker コンテナが docker-compose で動作するようにラップする単純なPythonスクリプトです。

依存関係

nvidia-docker-compose は以下のパッケージを必要とします。
  • Docker Engine
  • nvidia-docker
以下で説明するインストール手順のなかで自動的にインストールされる、docker-composePyYAMLJinja2 python パッケージにも依存関係があります。

nvidia-docker-compose および nvidia-docker2

既にご存じの方もおられるとおもいますが、nvidia-docker2 リリースが動作するNVIDIAでは、Dockerインフラにより緊密に統合されています(詳細は、プロジェクトリポジトリ https://github.com/NVIDIA/nvidia-dockermaster ブランチを参照してください)。この新しい統合の大きな利点の一つは、GPU で docker-compose を起動するために nvidia-docker-compose を必要としないことです。nvidia-docker2docker-compose で動作させる方法は、Issue#23 を参照してください。
/etc/docker/daemon.json に以下のような記述をおこないます
{
    "default-runtime": "nvidia",
    "runtimes": {
        "nvidia": {
            "path": "/usr/bin/nvidia-container-runtime",
            "runtimeArgs": [ ]
        }
    }
}

インストールする前に

nvidia-docker-composenvidia-docker が適切に機能するかどうかに依存しますが、とりわけ、nvidia-docker を実行すると自動的に作成される追加の Docker ボリュームに依存します。 nvidia-docker-compose をインストールして実行する前に、 nvidia-docker を少なくとも1回はテストして、すべてのボリュームが正しくセットアップされ、正しく機能していることを確認してください。特に、次のコマンドを実行することをお勧めします。
$ nvidia-docker run --rm nvidia/cuda nvidia-smi
使用可能なすべてのGPUがマシン上に正しく表示されている場合は、作業を進めます。 そうでない場合は、nvidia-docker-compose を使用する前にnvidia-docker のドキュメントを参照し、正しく機能することを確認してください。

インストール

スクリプトをインストールするには、次のコマンドを実行して下さい:
$ pip install nvidia-docker-compose
もし、システムが提供するPythonを使用しているのであれば、先頭に sudo を付けて実行する必要があります。

nvidia-docker-compose の利用

nvidia-docker-compose は、docker-compose のドロップイン代替コマンドです。 docker-compose と同じオプションで簡単に実行できます:
$ nvidia-docker-compose ...
システム構成に依存してsudoを付けて実行する必要があるかもしれません(i.e. dockerを実行する際にsudoを先頭につけている場合等)。
nvidia-docker-compose を実行すると、YAML型式設定ファイル nvidia-docker-compose.yml をローカルに作成します。nvidia-docker-comose 実行中にこのファイルを削除してもかまいません。Git リポジトリ内で .gitignore を使用している場合は、nvidia-docker-compose.yml をこのファイルに追加することをお勧めします。nvidia-docker-compose.yml が生成されたら、このファイルを使ってGPU対応のコンテナを標準の docker-compose コマンドでで起動することもできます。次のようにファイルを指定して実行してください:

$ docker-compose -f nvidia-docker-compose.yml ...

マルチGPUセットアップによる柔軟な実行

最初のGPUである /dev/nvidia0 などを選択して記述することによって、各コンテナに対するGPUの可視性を制御できます。ただし、手動でこれを行うことは、あなたが nvidia-docernvidia-docker-compose の機能を妨害する可能性があることを意味しており、 docker-compose.yml のどのサービスがどのGPUで実行すべきかを事前に指定する方法はありませんでした。これは、異なるマシンは異なる数のGPUを搭載している事実がさらに複雑にさせています、そしてこのため 2 GPU 搭載マシン上で devices セクションの下に /dev/nvidia4 と記述されたサービスを実行するとエラーが発生します。

GPUターゲットの指定

nvidia-docker-compose バージョン0.4.0から新たに devices ヘッダの下に /dev/nvidia* を含めることでGPUをサービスへ指定できるようになりました。次のように指定します
version: "2"
services
  process1:
    image: nvidia/cuda
    devices:
      - /dev/nvidia0
  process2:
    image: nvidia/cuda
    devices:
      - /dev/nvidia1
      - /dev/nvidia2
上記は、サービス process1 は最初のGPU( /dev/nvidia0 )のみを、サービス process2 は2番めと3番めのGPU ( /dev/nvidia1/dev/nvidia2 )をそれぞれ参照するように定義しています。devices セクションに /dev/nvidia* を指定しない場合は、これまでのバージョンの場合と同様にすべての有効なGPUが自動的に割り当てられます。

docker-compose.yml ファイルでJinja2 を使う

バージョン0.4.0からの新機能
ターゲットマシン上で利用可能なGPUの数と同じ数のコンテナコンテナを(同じ構成で)起動したいという比較的一般的な使用例をサポートするために、今度は nvidia-docker-compose にてJinja2の利用をサポートします。GPUターゲティングを指定する機能と組み合わせることで、GPUの可用性に柔軟性を持たせた docker-compose 設定を作成できるようになりました。次のテンプレートを準備し、docker-compose.yml.jinja として保存します:
version: "2"
services:
  {% for i in range(N_GPU) %}
  notebook{{i}}:
    image: eywalker/tensorflow:cuda
    ports:
      - "300{{i}}:8888"
    devices:
      - /dev/nvidia{{i}}
    volumes:
      - ./notebooks:/notebooks
  {% endfor %}
そして、このjinja2 テンプレートを -t / --template フラグをつけて指定し実行します:
$ nvidia-docker-compose --template docker-compose.yml.jinja ...
Jinja テンプレートを処理して、次のような docker-compose.yml へ展開します:
version: "2"
services:
  notebook0:
    image: eywalker/tensorflow:cuda
    ports:
      - "3000:8888"
    devices:
      - /dev/nvidia0
    volumes:
      - ./notebooks:/notebooks
  notebook1:
    image: eywalker/tensorflow:cuda
    ports:
      - "3001:8888"
    devices:
      - /dev/nvidia1
    volumes:
      - ./notebooks:/notebooks
  notebook2:
    image: eywalker/tensorflow:cuda
    ports:
      - "3002:8888"
    devices:
      - /dev/nvidia2
    volumes:
      - ./notebooks:/notebooks
上記は、3GPUマシンで実行、展開した例です。Jinja変数 N_GPU には自動的にシステム上の有効なGPU数が反映されます。この docker-compose.yml は次に nvidia-docker-compose によりほかの設定ファイルと同じように処理されGPUが使用可能なコンテナを起動します。

Composeファイルのみ生成する

GPU対応の Compose ファイルの作成だけおこない後でコンテナ起動したい場合は、-G / --generate フラグをつけて nvidia-docker-compose を実行すると、docker-compose を実行せずに作成ファイルを生成しただけで終了します。
$ nvidia-docker-compose -G ...

その他のコマンドラインオプション

代替の nvidia-docker-plugin ホストアドレスの指定や(デフォルトのnvidia-docker-compose.yml の代替として)代わりのターゲットの Docker Compose ファイルを指定などのようなその他の設定については、コマンドラインヘルプを参照してください:
$ nvidia-docker-compose -h

使い方

nvidia-docker-compose は、2つのアクションを実行するシンプルなPythonスクリプトです:
  • docker-compose 設定ファイル(デフォルトは docker-compose.yml)の構文を解釈して、GPUが有効なコンテナを実行するのに必要な設定がなされている新たなYAML型式の nvidia-docker-compose.yml を作成します。設定パラメータは nvidia-docker-plugins から読み込まれます。
  • 新たに生成された設定ファイル nvidia-docker-compose.yml docker-compose を実行します。
-----(2017/12/18時点の nvidia-docker-compose README.md)-----

ここまでやるとやり過ぎに見えるけど、
Jinjaテンプレートを使って
有効なGPU数を自動で展開して個別のDockerコンテナを作ってくれる
機能は便利だ..




..でもなあ..そろそろコンテナを
Docker Composeでなくて
Kubernetes で管理しようかなと
思ってたところなんだよなあ..

コンテナ貸しパブリックIaaSはこぞってKuberenetes使ってるし...

Docker Compose はそろそろオワコンかな..と思ってたところなんだよなあ..



p.s.

ちなみに..TensorFlowも1.4.1マイナーバージョンアップしてました..

既存アプリケーションをK8s上でコンテナ化して動かす場合の設計注意事項メモ

既存アプリをK8sなどのコンテナにして動かすには、どこを注意すればいいか..ちょっと調べたときの注意事項をメモにした。   1. The Twelve Factors (日本語訳からの転記) コードベース   バージョン管理されている1つのコードベースと複数のデプロイ 依存関係 ...