2 0 1 6 年 7 月 2 4 日 井 上 誠 一 郎
Demystifying Kubernetes
自己紹介: 井上誠一郎
• 元アリエルネットワークCTO (先月まで)
• ワークスアプリケーションズ エグゼクティブフェロー
• 主な著書
• 「P2P教科書」
• 「パーフェクトJava」
• 「パーフェクトJavaScript」
• 「実践JS サーバサイドJavaScript入門」
• 「パーフェクトJava EE」(来月出版)
今日のセッションの目的
•複雑怪奇なKubernetesをひ
もといていきます
•Kubernetesのバージョンは
v1.2.6ベースで説明します
複雑さをひもとくための工夫
•Kubernetes固有の概念/用語
がたくさんあるので、説明の
ために簡易化していきます
•概念を下から積み上げていき
ます
Kubernetes理解に必要な知識(私見)
Dockerの理解
Dockerのネットワークの理解
flanneldの理解
コンテナとpodの関係の理解
podとサービスの関係の理解
Kubernetesのネットワークの理解
(DNSとルーティング)
Kubernetesのツールの理解
この順序で説明
コンテナに関しての簡易化
• 理屈で言えばコンテナはひとつのOS相当なので、
ひとつのコンテナ内で多数のプロセス、たとえ
ばロードバランサ、アプリケーションサーバ、
データベースすべてを動かせます
• しかし、Kubernetesの思想的には、ひとつのコ
ンテナで動くプロセスの数を最小限にして、代
わりに、コンテナ群を管理します
• 今回の説明上も、ひとつのコンテナ上で動くの
はひとつのプロセス、というモデルを前提にし
ます(Kubernetesの必須要件ではありませんが)
Kubernetesの動作の分解の前に
•そもそもKubernetesは何を
してくれるのか?
•Kubernetesを使うと何がう
れしいのか?
Kubernetesがやってくれること
• 複数ホストにコンテナをデプロイ
• コンテナ間のネットワーク管理(名前
解決含む)
• コンテナの死活監視
• コンテナの負荷分散
• コンテナのリソースアロケーション
• 複数ホストにコンテナをデプロイ
• どのホストにどのコンテナ(=プロセス)を配備するかを隠蔽
• コンテナ間のネットワーク管理(名前解決含む)
• サービスディスカバリ相当の機能
• コンテナの死活監視
• コンテナ(=プロセス)が死んだら、コンテナを自動で新規起動
• コンテナの負荷分散
• 同一機能の複数コンテナ(=プロセス)へのアクセスをバランシン
グする機能(あまりリッチではない)
• コンテナのリソースアロケーション
• コンテナごとにCPUリソースやメモリリソースの割り当てを指定
できる機能(あまりリッチではない)
Kubernetesがやってくれること
Kubernetesがない世界
プロセスA プロセスB
依存
実行環境
開発者
プロセスB
プロセスB
プロセスB
プロセスB
deploy
LB
プロセスA
LB
プロセスA
configuration
(個々のエンドポイントを設定)
Kubernetesがある世界
プロセスA プロセスB
依存
実行環境
開発者
プロセスB
プロセスB
プロセスB
LB
プロセスA
Kubernetes
プロセスB群のサービス名を定義
プロセスAにサービス名を与える
用語と概念の整理
• Dockerプロセスが動いているOSをホスト(マシン)と呼
びます
• ひとつのホストの上で、コンテナが複数稼働します
• Kubernetesの世界から見ると、ホストマシンが物理マ
シンか仮想マシンかはどうでもいい話です。気にしない
でください
• 同様に、ホストマシンがネットワークのどこにいるか
(プライベートネットワーク or グローバルIPを持つ)も
Kubernetesの世界からはスコープ外なので、気にしな
くて結構です
ホストマシンについて
• Kubernetesで最初に混乱するのが、Dockerのネット
ワークまわりです
• flanneldの世界とKubernetesの話が混ざると混乱する
ので、話を分離します
• まずflanneldだけに話を限定します
Dockerのネットワークまわりの話とflanneld
• flanneldがないと、あるホストの上で動い
ているコンテナは、他のホストの上で動
いているコンテナのIPアドレスにアクセ
スできません
• 正確に言えば、ある設定をすれば、相手
側ホストのIPアドレス経由で、リモート
のコンテナにアクセスできます
• ただ、基本的には少々面倒な世界です
flanneldの役割(1)
• flanneldは各ホスト上で動くデーモンプロセスです。
flanneldがいれば、ホスト群の上のコンテナ群はコンテ
ナ自身のIPアドレスを使って相互にアクセスできます
• コンテナたちは一意なIPアドレスを持つようになります
• 一見すると、ホスト間のコーディネーションが必要そうですが、
仕組みはもっと単純で、flanneldプロセス群が同じデータストア
(etcd)でルーティングテーブルを共有しているだけです
• なお、この領域には、Docker Swarmなど類似機能の他
技術も存在します
flanneldの役割(2)
1. (そもそもDockerのネットワーク補助のためなの
で)Docker自身をインストール
2. 共有データストアとしてetcdが必要なので、どこかに
etcdを起動(etcd自体は分散KVSですが、動作確認だ
けであれば、単体でどこかで起動していれば充分)
3. etcdにflanneldが使うネットワークアドレスを登録
(どのネットワークアドレスを使うかは利用者の自由)
例
$ etcdctl set /coreos.com/network/config
'{ "Network": "10.1.0.0/16" }‘
flanneldを動かす手順の概要(1)
4. 各ホストでflanneldデーモンプロセスを起動
例
同一ホストでetcdが動いている場合は単に
$ sudo bin/flanneld
別ホスト(IPアドレスが10.140.0.14)でetcdが動いている
場合は
$ sudo bin/flanneld -etcd-endpoints
'http://10.140.0.14:4001,http://10.140.0.14:2379'
flanneldを動かす手順の概要(2)
5. 個々のflanneldは、どのサブネットを確保したかの情報を
/run/flannel/subnet.env に書き出す
例
$ cat /run/flannel/subnet.env
FLANNEL_NETWORK=10.1.0.0/16
FLANNEL_SUBNET=10.1.19.1/24
FLANNEL_MTU=1432
FLANNEL_IPMASQ=true
6. dockerプロセスに対して、上記のサブネットを使うように指示し
て起動
$ source /run/flannel/subnet.env
$ sudo docker -d --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}
flanneldを動かす手順の概要(3)
ifconfigでdocker0やflanneldのネットワークアドレスを確認(出力例
は抜粋)
(下記例の場合、このホスト上のコンテナ群は 10.1.19.0/24 のネット
ワークを構成)
$ ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:a5:18:b3:73
inet addr:10.1.19.1 Bcast:0.0.0.0
Mask:255.255.255.0
flannel0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00-00
inet addr:10.1.19.0 P-t-P:10.1.19.0
Mask:255.255.0.0
flanneldの動作確認(1)
ルーティングテーブルの確認:
$ netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt
Iface
0.0.0.0 10.140.0.1 0.0.0.0 UG 0 0 0
ens4
10.1.0.0 0.0.0.0 255.255.0.0 U 0 0 0
flannel0
10.1.19.0 0.0.0.0 255.255.255.0 U 0 0 0
docker0
10.140.0.1 0.0.0.0 255.255.255.255 UH 0 0 0
ens4
他のホスト上のコンテナのIPアドレス(たとえば10.1.79.2)に向けて接続できれば
OK
flanneldの動作確認(2)
• なかなか面倒...
• まず、dockerのデーモンプロセス起動より先に
flanneldプロセスの起動が必要
• かつ、flanneldプロセス起動時に、etcdの固定アドレス
を外から与える必要がある
• 更に、flanneldの書き出した /run/flannel/subnet.env
の結果を、dockerのデーモンプロセスの起動時に渡す
必要がある
OS起動時のflanneldの自動起動(1)
systemdであれば
$ sudo vi /lib/systemd/system/docker.service
で
EnvironmentFile=-/run/flannel/subnet.env
を追記(先頭のハイフンの意味は、ファイルが存在しなければ
無視する、というオプション)
#下記に書き換え ExecStart=/usr/bin/docker daemon -H
fd:// $DOCKER_OPTS
ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS -
-bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}
OS起動時のflanneldの自動起動(2)
• Kubernetes理解のための、flanneldの最小理解
• flanneldを使うと、Kubernetes管理下にいる各コ
ンテナは一意なIPアドレスを持ち、相互にリーチャ
ブルになる
• flanneldネットワークは、プライベートネットワー
クなので、Kubernetesの外側からは叩けない
• このネットワークは、ホストのIPアドレスのネット
ワークとは別物
• (後述する)KubernetesのサービスのIPアドレスのネッ
トワークとも別物(ここがわかりづらい)
ここまでのまとめ
flanneldをいったん頭から追い出して、
Kubernetesの概念の整理をします
• ノードはKubernetes固有の用語です
• 頭が混乱したら、「ホストマシン = ノー
ド」と考えても、大きくは外しません
• より正確には、
• ノードは、「マスタノード」と「ワー
カーノード」の2種類に分けられます
ホストマシンとノードについて(1)
• ワーカーノードは、Dockerプロセスが稼働するホスト
で、その上でKubernetes管理下のコンテナ群が稼働し
ます。こちらは「ワーカーノード = ホスト」の理解で問
題ありません
• マスタノードのほうは、いくつかのKubernetesのサー
バプロセス群のことです
• これらのプロセスはコンテナ上で動く必要はありません
• 「マスタノード」という用語はややミスリーディングに思います
(マスタプロセスのほうが適切)
# 古い文書ではワーカーノードがMinionと呼ばれていま
す
ホストマシンとノードについて(2)
• podはコンテナをグループ化したKubernetes固有の概念です
• あるpodインスタンス内のコンテナは、同一のホスト上で稼働しま
す。かつ、(Dockerネットワーク的な意味で)同じIPアドレスを共有
します
• 密結合なプロセス群、言い換えると、死ぬ時は一緒に死んでほしい
ようなプロセス群をpodにまとめます
• しかし、今日の説明上は、ひとつのpod内にはひとつのコンテナ、
というモデルにします
• 既に説明したように、ひとつのコンテナにひとつのプロセスのモデ
ルにしているので、今日の説明では、ひとつのpodはひとつのプロ
セスと対応します
• (pauseという特別なプロセスのコンテナがありますが本質的ではないので
説明を割愛します)
コンテナとpod
• レプリケーションコントローラ(以下rcと略)は、podを複数インス
タンス化するKubernetes固有の概念です
• 実運用上、podを単一インスタンスで使ってもKubernetesの旨味
が少ないので、必要なレプリカ数を指定したrcを設定するのが普通
です
• rcは指定したレプリカ数分のpodをインスタンス化します。今回の
文脈では、レプリカ数分のプロセス起動になります
• プロセスがどのホストで起動するかは実行時に決まります
(Kubernetesが空いているホストを見つけます)
• rcは指定レプリカ数分のpodを維持します。つまり、仮にどこかの
pod(=コンテナ=プロセス)が死ぬと、自動で新しいpodが起動しま
す
podとレプリケーションコントローラ(=rc)
• Kubernetes v1.3以降、rcがレプリカセットと
Deploymentという新しい概念で置換されていくようで
す
• 今日の説明は rc のまま進めます
fyi, rcとレプリカセットとDeployment
• rcの機能により、podは複数インスタンス状態になります
• 今回の場合、同じプログラムから複数プロセスが起動すると考えてください
• かつこれらのプロセスがどのホストで動くかは実行時に決まるので、個々の
プロセスのIPアドレスは実行時に決まります
• サービスは、これらの複数インスタンスに対して単一IPアドレスを
割り当てるKubernetesの機能です
• 結果的に、サービスのIPアドレスに向けたアクセスが、背後の複数
podへ割り振られるのでロードバランサのように振る舞います
• 内部的には、各ワーカーノード上で動くkube-proxyというプロセ
スがiptablesにエントリを追加することで、サービスのIPアドレス
を作っています
• サービスのIPアドレスはifconfigなどでは見えないアドレスです(後述)
podとサービス
• Kubernetesの各サービスのIPアドレスの名前解決にはDNSを
使います
• SkyDNSというDNS実装が(事実上)Kubernetesに組み込まれ
ています
• 実行時に新しいサービスが起動すると、サービス名とIPアド
レスのエントリが自動でSkyDNSに登録されます(登録は
kube2dnsというプロセスが担当)
• 各pod上のプロセスは、サービス名さえわかればサービスに
アクセスできます(いわゆるサービスディスカバリ相当の機能
になります)
• サービス名の命名や、アプリにサービス名をどう与えるかは、
アプリケーション開発者の責務です
名前解決とSkyDNS
他に知っておくと良い細々したこと:
•Kubernetesの管理用コマンド
ラインツールです
•利用例は後ほど具体例の中で
紹介します
kubectl
• 分散KVS
• Kubernetesのマスタプロセスが使うデータストアです
• SkyDNSおよびflanneldもそれぞれetcdをデータストア
として使います。
• 今回の説明においては、etcdとは、どこかにあるただ
のデータストアと思えば充分です
• etcdはコンテナ上にある必要もないですし、マスタノードや
ワーカーノード上で必ずしも動かす必要はありません
• 今日の説明では、(便宜上)マスタノード上でコンテナを使って
etcdを起動します
etcd
•etcdの管理用コマンドライン
ツールです
•利用例は後ほど具体例の中で
紹介します
etcdctl
• Kubernetesの各種プロセスをコマンドラインの第一引
数で呼び分けられるプログラムです
• たとえば hyperkube kubelet と起動すると kubelet プ
ロセスを起動できます
• 利用は必須ではないですが、ラクなので今回は
hyperkubeを使います
hyperkube
• マスタプロセス群は、apiserverやcontroller-managerや
schedulerなどがあります
• これらは今後Kubernetesのバージョンが上がると色々と変化
すると思うので、一群のプロセス群があるということだけ理
解すれば充分です
• 唯一気にすべきプロセスはapiserverです
• kubectlはapiserverのREST APIを叩くプログラムです。apiserverの
アドレスを引数で与える必要があります(同一ホストで動いていれば
省略可能)
• apiserverはデータストアとしてetcdを使うので、apiserver起動の
前にetcd起動が必要で、かつapiserverは起動時にetcdのアドレス
を知っている必要があります
• 他のKubernetesプロセスたちは、起動時に、apiserverプロセスの
アドレスを知っている必要があります
Kubernetesのマスタプロセス群
• dockerデーモンプロセス: これがないとコンテナが動か
せないので当然必要です
• kubelet: ワーカーノードをワーカーノードたらしめるプ
ロセスです。pod(=コンテナ)の起動などを担います
• kube-proxy: サービスのIPアドレスを管理します(内部的
にiptablesを操作)
• flanneld: 異なるホスト上のコンテナ同士をネットワー
ク的につなぎます(説明済み)
各ワーカーノード上で動くプロセス群
Kubernetesを動かしてみます
• 単体ノード上で動かす手順
• http://kubernetes.io/docs/getting-started-guides/docker/
• http://kubernetes.io/docs/getting-started-guides/docker-
multinode/deployDNS/
• 複数ノードでの手順
• http://kubernetes.io/docs/getting-started-guides/docker-
multinode/master/
• http://kubernetes.io/docs/getting-started-guides/docker-
multinode/worker/
手順は下記を参考
• ふたつのホストを使います
• ホストのOSは Ubuntu 16.04 ですが、可能な限りディス
トリビューション依存のない説明をします
• 両方のホストがワーカーノードで、かつ片方のホスト上
で(コンテナを使って)マスタプロセス群を動かします
• ワーカーノード上のプロセス群(kubeletやkube-proxy)
も、コンテナを使って動かします
• これらのプロセスは、コンテナで動かすことが必須では
ありません。将来、普通にapt-getでインストールでき
るようになれば、そのほうがシンプルです
サンプルの構成
1. 全体の準備
2. マスタノードになるホスト上での作業
3. ワーカーノードになるホスト上での作業
4. 自作アプリのサービス化
手順のオーバービュー
kubectlコマンドをインストールします
基本的には、kubectlコマンドはどのマシンにインストールしても構いま
せん(マスタプロセスのIPアドレスにリーチャブルな場所であれば)。
$ export K8S_VERSION=1.2.6
$ curl http://storage.googleapis.com/kubernetes-
release/release/v${K8S_VERSION}/bin/linux/amd64/kubectl >
kubectl
$ sudo mv kubectl /usr/local/bin/
$ sudo chmod +x /usr/local/bin/kubectl
マスタプロセスと異なるホストにインストールした場合、kubectlのコマ
ンドラインオプションの-sでマスタプロセスのホストのIPアドレスとポー
ト(10.140.0.14:8080)を指定する必要があります(あるいは kubeconfig
ファイルで設定)
$ kubectl -s 10.140.0.14:8080 cluster-info
全体の準備
• 前述したように、マスタノードとい
う用語はややミスリーディングです
• いくつかのマスタプロセスを起動す
るノードを、単にマスタノードと呼
びます
• 今回の場合、このホストはワーカー
ノードにもなります
マスタノードになるホスト
Docker自身のインストール
$ sudo apt-get update; sudo apt-get -y upgrade;
sudo apt-get -y install docker.io
仮にホストでetcdが動いていると邪魔するので、プロセ
スがいないことを確認
仮に動いていたら、止めておく
$ sudo systemctl stop etcd
$ sudo systemctl disable etcd
マスタノードになるホスト上での作業(1)
環境変数設定(利便性のため)
$ export MASTER_IP=10.140.0.14 # ホストのIPアド
レス。ifconfigで確認
$ export K8S_VERSION=1.2.6
$ export ETCD_VERSION=2.2.5
$ export FLANNEL_VERSION=0.5.5
$ export FLANNEL_IFACE=ens4 # ifconfigで確認
$ export FLANNEL_IPMASQ=true
マスタノードになるホスト上での作業(2)
• flanneldを動かします。その前にflanneldが依存する
etcdを動かします
• これらはコンテナで動かす必要はないですが、ラクをす
るために両方ともコンテナで動かします
• ややトリッキーですが、flanneldとetcd専用のDocker
デーモンプロセスを起動します
• 手順の中の可変項目は、flanneldが使うネットワークア
ドレスの部分("10.1.0.0/16")です。これは好きに決めら
れます
マスタノードになるホスト上での作業(3)
専用Dockerデーモンプロセスの起動
$ sudo sh -c 'docker daemon -H
unix:///var/run/docker-bootstrap.sock -p
/var/run/docker-bootstrap.pid --iptables=false --
ip-masq=false --bridge=none --
graph=/var/lib/docker-bootstrap 2> /var/log/docker-
bootstrap.log 1> /dev/null &'
マスタノードになるホスト上での作業(4)
etcdプロセスの起動(コンテナ内)
$ sudo docker -H unix:///var/run/docker-bootstrap.sock
run -d --net=host ¥
gcr.io/google_containers/etcd-amd64:${ETCD_VERSION}
¥
/usr/local/bin/etcd ¥
--listen-client-
urls=http://127.0.0.1:4001,http://${MASTER_IP}:4001 ¥
--advertise-client-urls=http://${MASTER_IP}:4001
¥
--data-dir=/var/etcd/data
マスタノードになるホスト上での作業(5)
etcdへの初期データ投入
$ sudo docker -H unix:///var/run/docker-
bootstrap.sock run ¥
--net=host ¥
gcr.io/google_containers/etcd-
amd64:${ETCD_VERSION} ¥
etcdctl set /coreos.com/network/config
'{ "Network": "10.1.0.0/16" }'
マスタノードになるホスト上での作業(6)
通常Dockerデーモンを一時停止
$ sudo systemctl stop docker
flanneldプロセスの起動(コンテナ内)
$ sudo docker -H unix:///var/run/docker-bootstrap.sock
run -d ¥
--net=host --privileged ¥
-v /dev/net:/dev/net ¥
quay.io/coreos/flannel:${FLANNEL_VERSION} ¥
/opt/bin/flanneld ¥
--ip-masq=${FLANNEL_IPMASQ} ¥
--iface=${FLANNEL_IFACE}
マスタノードになるホスト上での作業(7)
flanneldのサブネットのネットワークアドレスを(通常の)Dockerデーモンプロセ
スに伝えます
$ sudo docker -H unix:///var/run/docker-bootstrap.sock exec flanneldコン
テナの出力ハッシュ値(=コンテナID) cat /run/flannel/subnet.env
入力例
$ sudo docker -H unix:///var/run/docker-bootstrap.sock exec
195ea9f70770ac20a3f04e02c240fb24a74e1d08ef749f162beab5ee8c905734 cat
/run/flannel/subnet.env
出力例
FLANNEL_NETWORK=10.1.0.0/16
FLANNEL_SUBNET=10.1.19.1/24
FLANNEL_MTU=1432
FLANNEL_IPMASQ=true
マスタノードになるホスト上での作業(8)
$ sudo vi /lib/systemd/system/docker.service
で
ExecStart=/usr/bin/docker daemon -H fd://
$DOCKER_OPTS --bip=10.1.19.1/24 --mtu=1432
と書き換え
マスタノードになるホスト上での作業(9)
Dockerデーモンプロセスを再起動します
$ sudo /sbin/ifconfig docker0 down
$ sudo brctl delbr docker0
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
マスタノードになるホスト上での作業(10)
Docker再起動前の確認(抜粋)
$ ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:25:65:c5:f3
inet addr:10.1.20.1 Bcast:0.0.0.0 Mask:255.255.255.0
Docker再起動後の確認(抜粋)
$ ifconfig
docker0 Link encap:Ethernet HWaddr 02:42:a5:18:b3:73
inet addr:10.1.19.1 Bcast:0.0.0.0 Mask:255.255.255.0
flannel0 Link encap:UNSPEC HWaddr 00-00-00-00-00-00-00-00-00-
00-00-00-00-00-00-00
inet addr:10.1.19.0 P-t-P:10.1.19.0 Mask:255.255.0.0
マスタノードになるホスト上での作業(11)
Docker再起動後のルーティングテーブルの確認
$ netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS
Window irtt Iface
0.0.0.0 10.140.0.1 0.0.0.0 UG 0 0
0 ens4
10.1.0.0 0.0.0.0 255.255.0.0 U 0 0
0 flannel0
10.1.19.0 0.0.0.0 255.255.255.0 U 0 0
0 docker0
10.140.0.1 0.0.0.0 255.255.255.255 UH 0 0
0 ens4
マスタノードになるホスト上での作業(12)
マスタプロセス群およびワーカーノードに必要なプロセス群(kubeletと
kube-proxyなど)をhyperkubeで起動します
$ sudo docker run ¥
--volume=/:/rootfs:ro --volume=/sys:/sys:ro ¥
--volume=/var/lib/docker/:/var/lib/docker:rw ¥
--volume=/var/lib/kubelet/:/var/lib/kubelet:rw ¥
--volume=/var/run:/var/run:rw ¥
--net=host --privileged=true --pid=host -d ¥
gcr.io/google_containers/hyperkube-amd64:v${K8S_VERSION} ¥
/hyperkube kubelet --allow-privileged=true ¥
--api-servers=http://localhost:8080 ¥
--v=2 --address=0.0.0.0 --enable-server ¥
--hostname-override=127.0.0.1 ¥
--config=/etc/kubernetes/manifests-multi --containerized ¥
--cluster-dns=10.0.0.10 --cluster-domain=cluster.local
マスタノードになるホスト上での作業(13)
Kubernetesのマスタプロセスの動作確認
$ kubectl cluster-info
Kubernetes master is running at http://localhost:8080
マスタノードになるホスト上での作業(14)
SkyDNSをpodとして動かします。
$ curl http://kubernetes.io/docs/getting-started-
guides/docker-multinode/skydns.yaml.in > skydns.yaml.in
$ export DNS_REPLICAS=1
$ export DNS_DOMAIN=cluster.local # 自分で決めてよいドメイ
ン名
$ export DNS_SERVER_IP=10.0.0.10 # 自分で決めてよいDNSサー
バのIPアドレス(KubernetesのサービスとしてIPアドレス)
$ sed -e
"s/{{ pillar¥['dns_replicas'¥] }}/${DNS_REPLICAS}/g;s/{{ pill
ar¥['dns_domain'¥] }}/${DNS_DOMAIN}/g;s/{{ pillar¥['dns_serve
r'¥] }}/${DNS_SERVER_IP}/g" skydns.yaml.in > ./skydns.yaml
マスタノードになるホスト上での作業(15)
rcおよびサービスの作成(skydns.yamlの中身はrcとサービ
ス)
$ kubectl create -f ./skydns.yaml
マスタノードになるホスト上での作業(15)
$ kubectl cluster-info
Kubernetes master is running at http://localhost:8080
KubeDNS is running at http://localhost:8080/api/v1/proxy/namespaces/kube-
system/services/kube-dns
確認(このfailureの原因は不明)
$ curl http://localhost:8080/api/v1/proxy/namespaces/kube-system/services/kube-dns
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "no endpoints available for service ¥"kube-dns¥"",
"reason": "ServiceUnavailable",
"code": 503
}
SkyDNSの動作確認(1)
$ kubectl get --all-namespaces svc
NAMESPACE NAME CLUSTER-IP EXTERNAL-IP PORT(S)
AGE
default kubernetes 10.0.0.1 <none> 443/TCP
2m
kube-system kube-dns 10.0.0.10 <none>
53/UDP,53/TCP 1m
$ kubectl get --all-namespaces ep
NAMESPACE NAME ENDPOINTS AGE
default kubernetes 10.140.0.14:6443 2m
kube-system kube-dns 10.1.19.2:53,10.1.19.2:53 1m
SkyDNSの動作確認(2)
$ dig @10.0.0.10 cluster.local.
(抜粋)
;; ANSWER SECTION:
cluster.local. 30 IN A 10.1.19.2
cluster.local. 30 IN A 127.0.0.1
cluster.local. 30 IN A 10.0.0.10
cluster.local. 30 IN A 10.0.0.1
$ dig @10.1.19.2 cluster.local.
も動くが、このIPアドレスは状況によって変わりうるので依存しては
いけない
SkyDNSの動作確認(3)
• 最初のホスト上でflanneldを設定しました
• このホスト上でマスタプロセス群を起動
しました(apiserverなど)
• このホストをワーカーノードにしました
(kubeletとkube-proxyの起動)
• SkyDNSをKubernetesのサービスとして
起動しました(10.0.0.10のIPアドレスでア
クセス可能)
ここまでのまとめ
Docker自身のインストール
$ sudo apt-get update; sudo apt-get -y upgrade;
sudo apt-get -y install docker.io
環境変数設定(利便性のため)
$ export MASTER_IP=10.140.0.14 # マスタノードのホ
ストのIPアドレス
$ export K8S_VERSION=1.2.6
$ export FLANNEL_VERSION=0.5.5
$ export FLANNEL_IFACE=ens4 # ifconfigで確認
$ export FLANNEL_IPMASQ=true
ワーカーノードになるホスト上での作業(1)
• マスタノードと同じく、flanneldをコンテナで起動します
• etcdはマスタノード上のetcdを参照します
• flanneldの起動はマスタノードと同じ手順です
専用Dockerデーモンプロセスの起動
$ sudo sh -c 'docker daemon -H unix:///var/run/docker-
bootstrap.sock -p /var/run/docker-bootstrap.pid --
iptables=false --ip-masq=false --bridge=none --
graph=/var/lib/docker-bootstrap 2> /var/log/docker-
bootstrap.log 1> /dev/null &'
通常Dockerデーモンを一時停止
$ sudo systemctl stop docker
ワーカーノードになるホスト上での作業(2)
flanneldプロセスの起動(コンテナ内)
$ sudo docker -H unix:///var/run/docker-
bootstrap.sock run -d ¥
--net=host --privileged -v /dev/net:/dev/net ¥
quay.io/coreos/flannel:${FLANNEL_VERSION} ¥
/opt/bin/flanneld ¥
--ip-masq=${FLANNEL_IPMASQ} ¥
--etcd-endpoints=http://${MASTER_IP}:4001 ¥
--iface=${FLANNEL_IFACE}
ワーカーノードになるホスト上での作業(3)
flanneldのサブネットのネットワークアドレスを(通常の)Dockerデーモンプロセスに
伝えます
$ sudo docker -H unix:///var/run/docker-bootstrap.sock exec flanneldコンテナ
の出力ハッシュ値(=コンテナID) cat /run/flannel/subnet.env
出力例
FLANNEL_NETWORK=10.1.0.0/16
FLANNEL_SUBNET=10.1.79.1/24
FLANNEL_MTU=1432
FLANNEL_IPMASQ=true
$ sudo vi /lib/systemd/system/docker.service
で
ExecStart=/usr/bin/docker daemon -H fd:// $DOCKER_OPTS --bip=10.1.79.1/24 --
mtu=1432
と書き換え
ワーカーノードになるホスト上での作業(4)
Dockerデーモンプロセスを再起動します
$ sudo /sbin/ifconfig docker0 down
$ sudo brctl delbr docker0
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
ワーカーノードになるホスト上での作業(5)
確認はifconfigおよびnetstat -rnで実施
再起動後のルーティングテーブルの確認
$ netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS
Window irtt Iface
0.0.0.0 10.140.0.1 0.0.0.0 UG 0 0
0 ens4
10.1.0.0 0.0.0.0 255.255.0.0 U 0 0
0 flannel0
10.1.79.0 0.0.0.0 255.255.255.0 U 0 0
0 docker0
10.140.0.1 0.0.0.0 255.255.255.255 UH 0 0
0 ens4
ワーカーノードになるホスト上での作業(6)
ワーカーノードに必要なプロセス(kubelet)をhyperkubeで起動します
$ sudo docker run ¥
--volume=/:/rootfs:ro --volume=/sys:/sys:ro ¥
--volume=/dev:/dev ¥
--volume=/var/lib/docker/:/var/lib/docker:rw ¥
--volume=/var/lib/kubelet/:/var/lib/kubelet:rw ¥
--volume=/var/run:/var/run:rw ¥
--net=host --privileged=true --pid=host -d ¥
gcr.io/google_containers/hyperkube-amd64:v${K8S_VERSION} ¥
/hyperkube kubelet ¥
--allow-privileged=true --api-servers=http://${MASTER_IP}:8080 ¥
--v=2 --address=0.0.0.0 --enable-server --containerized ¥
--cluster-dns=10.0.0.10 --cluster-domain=cluster.local
ワーカーノードになるホスト上での作業(7)
ワーカーノードに必要なプロセス(kube-proxy)を
hyperkubeで起動します
$ sudo docker run -d --net=host --privileged ¥
gcr.io/google_containers/hyperkube-
amd64:v${K8S_VERSION} ¥
/hyperkube proxy ¥
--master=http://${MASTER_IP}:8080 --v=2
ワーカーノードになるホスト上での作業(8)
•ふたつ目のホスト上でflanneldを
設定しました
•このホストをワーカーノードにし
ました(kubeletとkube-proxyの
起動)
ここまでのまとめ
$ kubectl -s 10.140.0.14:8080 cluster-info
Kubernetes master is running at 10.140.0.14:8080
KubeDNS is running at
10.140.0.14:8080/api/v1/proxy/namespaces/kube-
system/services/kube-dns
サービスの確認
$ kubectl -s 10.140.0.14:8080 get --all-namespaces svc
NAMESPACE NAME CLUSTER-IP EXTERNAL-IP PORT(S)
AGE
default kubernetes 10.0.0.1 <none> 443/TCP
47m
kube-system kube-dns 10.0.0.10 <none>
53/UDP,53/TCP 45m
Kubernetesの基本的な動作確認(1)
ノードの確認
$ kubectl get nodes
NAME STATUS AGE
127.0.0.1 Ready 52m
ubuntu16k5 Ready 16m
Kubernetesの基本的な動作確認(2)
自作Node.jsアプリをKubernetes上の
サービスとして動かしてみる
server.js =>
var http = require('http');
var handleRequest = function(request, response) {
response.writeHead(200);
response.end("Hello World");
}
var www = http.createServer(handleRequest);
www.listen(8888);
サンプルアプリケーションのDockerイメー
ジを準備(1)
Dockerfile =>
FROM node:latest
EXPOSE 8888
COPY server.js .
CMD node server.js
$ sudo docker build -t mynode:latest .
$ sudo docker images # 確認
サンプルアプリケーションのDockerイメー
ジを準備(2)
このDockerイメージを複数ホストから取得できるために
レジストリに登録する必要があります
(自前でdockerレジストリを立てるのがベストですが、今
回はDockerHubで代用します)
$ docker login
$ docker tag mynode:latest guest/mynode # guest
の部分はDockerHubのログインID
$ docker push guest/mynode
Dockerイメージをレジストリに登録
mynode.yaml =>
apiVersion: v1
kind: ReplicationController
metadata:
name: my-node
spec:
replicas: 2
template:
metadata:
labels:
app: sample
spec:
containers:
- name: mynode
image: guest/mynode
ports:
- containerPort: 8888
rc用の設定ファイル
ReplicationController(rc)の設定ファイル
このrcの識別名(開発者の命名)
レプリケーション数の指定
Dockerイメージの指定(on DockerHub)
ラベル(keyとvalue両方とも開発者の命名)
mynode-svc.yaml =>
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
app: sample
spec:
ports:
- port: 8888
selector:
app: sample
サービス用の設定ファイル(selectorで
mynode.yamlを参照する関係)
サービス(svc)の設定ファイル
このサービスの識別名(開発者の命名)
ラベル(開発者の命名)
rc(やpod)のラベルでセレクト
起動は
$ kubectl create -f mynode.yaml
$ kubectl create -f mynode-svc.yaml
削除する時は
$ kubectl delete -f mynode-svc.yaml
$ kubectl delete -f mynode.yaml
設定ファイルによる起動および削除
まずrcを起動(=暗黙にpodを起動)してみます
$ kubectl create -f mynode.yaml
rcを起動(=暗黙にpodを起動)
podの確認
$ kubectl get --all-namespaces po
NAMESPACE NAME READY STATUS RESTARTS AGE
default k8s-master-127.0.0.1 4/4 Running 0 50m
default k8s-proxy-127.0.0.1 1/1 Running 0 50m
default my-node-ejvv9 1/1 Running 0 10s
default my-node-lm62r 1/1 Running 0 10s
kube-system kube-dns-v10-suqsw 4/4 Running 0 48m
rcの確認
$ kubectl get --all-namespaces rc
NAMESPACE NAME DESIRED CURRENT AGE
default my-node 2 2 36s
kube-system kube-dns-v10 1 1 49m
podとrcの確認
サービス(svc)はまだ存在していません
$ kubectl get --all-namespaces svc
NAMESPACE NAME CLUSTER-IP EXTERNAL-IP PORT(S)
AGE
default kubernetes 10.0.0.1 <none> 443/TCP
51m
kube-system kube-dns 10.0.0.10 <none> 53/UDP,53/TCP
49m
エンドポイント(ep)もまだ存在しません
$ kubectl get --all-namespaces ep
NAMESPACE NAME ENDPOINTS AGE
default kubernetes 10.140.0.14:6443 51m
kube-system kube-dns 10.1.19.2:53,10.1.19.2:53 49m
サービスとエンドポイントの確認
サービスを起動してみます
$ kubectl create -f mynode-svc.yaml
サービス(svc)の起動
サービス確認
$ kubectl get --all-namespaces svc
NAMESPACE NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
default frontend 10.0.0.206 <none> 8888/TCP 21s
default kubernetes 10.0.0.1 <none> 443/TCP 53m
kube-system kube-dns 10.0.0.10 <none> 53/UDP,53/TCP 51m
エンドポイント確認
$ kubectl get --all-namespaces ep
NAMESPACE NAME ENDPOINTS AGE
default frontend 10.1.19.3:8888,10.1.79.2:8888 58s
default kubernetes 10.140.0.14:6443 53m
kube-system kube-dns 10.1.19.2:53,10.1.19.2:53 52m
サービスとエンドポイントの確認
サービスのIPアドレスにアクセス
$ curl http://10.0.0.206:8888
Hello World
アプリの動作確認
Podの確認
$ kubectl describe po my-node-ejvv9
Name: my-node-ejvv9
Namespace: default
Node: ubuntu16k5/10.140.0.15
Start Time: Wed, 20 Jul 2016 09:23:11 +0000
Labels: app=sample
Status: Running
IP: 10.1.79.2
Controllers: ReplicationController/my-node
Containers:
mynode:
Container ID:
docker://daf3fb9f2217ef40226aab040bd64d6f6f0d5bdcd0318e5628e9af07adcb650
4
Image: guest/mynode
以下略
podの詳細
レプリケーションの確認
$ kubectl describe rc my-node
Name: my-node
Namespace: default
Image(s): guest/mynode
Selector: app=sample
Labels: app=sample
Replicas: 2 current / 2 desired
Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed
No volumes.
Events:
FirstSeen LastSeen Count From
SubobjectPath Type Reason Message
--------- -------- ----- ---- -----------
-- -------- ------ -------
5m 5m 1 {replication-controller }
Normal SuccessfulCreate Created pod: my-node-ejvv9
5m 5m 1 {replication-controller }
Normal SuccessfulCreate Created pod: my-node-lm62r
レプリケーション(rc)の確認
サービスの確認
$ kubectl describe svc frontend
Name: frontend
Namespace: default
Labels: app=sample
Selector: app=sample
Type: ClusterIP
IP: 10.0.0.206
Port: <unset> 8888/TCP
Endpoints: 10.1.19.3:8888,10.1.79.2:8888
Session Affinity: None
No events.
サービスの詳細
エンドポイントの確認(:コンテナのIPアドレスの確認)
$ kubectl describe ep frontend
Name: frontend
Namespace: default
Labels: app=sample
Subsets:
Addresses: 10.1.19.3,10.1.79.2
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
<unset> 8888 TCP
No events.
エンドポイントの確認
$ kubectl scale --replicas=5
rc/my-node
replicationcontroller "my-node"
scaled
rcのスケールアウト検証
$ kubectl get --all-namespaces rc
NAMESPACE NAME DESIRED CURRENT
AGE
default my-node 5 5 7m
kube-system kube-dns-v10 1 1
55m
rcのスケールアウト検証の確認(1)
$ kubectl get --all-namespaces po
NAMESPACE NAME READY STATUS RESTARTS AGE
default k8s-master-127.0.0.1 4/4 Running 0 57m
default k8s-proxy-127.0.0.1 1/1 Running 0 57m
default my-node-3blsf 1/1 Running 0 39s
default my-node-a2015 1/1 Running 0 39s
default my-node-ejvv9 1/1 Running 0 7m
default my-node-lm62r 1/1 Running 0 7m
default my-node-u5gxt 1/1 Running 0 39s
kube-system kube-dns-v10-suqsw 4/4 Running 0 56m
rcのスケールアウト検証の確認(2)
$ kubectl get --all-namespaces ep
NAMESPACE NAME ENDPOINTS
AGE
default frontend
10.1.19.3:8888,10.1.19.4:8888,10.1.79.2:8888 + 2
more... 5m
default kubernetes 10.140.0.14:6443
58m
kube-system kube-dns
10.1.19.2:53,10.1.19.2:53
56m
rcのスケールアウト検証の確認(3)
$ kubectl describe ep frontend
Name: frontend
Namespace: default
Labels: app=sample
Subsets:
Addresses:
10.1.19.3,10.1.19.4,10.1.79.2,10.1.79.3,10.1.79.4
NotReadyAddresses: <none>
Ports:
Name Port Protocol
---- ---- --------
<unset> 8888 TCP
No events.
片方のホストに2つ(10.1.19.3,10.1.19.4)、もうひとつのホストに3つ
(10.1.79.2,10.1.79.3,10.1.79.4)のpodがある
RCのスケールアウト検証の確認(4)
podが増えようと、サービスとしての見え方は不変
$ kubectl get --all-namespaces svc
NAMESPACE NAME CLUSTER-IP EXTERNAL-IP
PORT(S) AGE
default frontend 10.0.0.206 <none>
8888/TCP 5m
default kubernetes 10.0.0.1 <none>
443/TCP 58m
kube-system kube-dns 10.0.0.10 <none>
53/UDP,53/TCP 56m
rcのスケールアウト検証の確認(5)
$ kubectl describe po my-node-3blsf
Name: my-node-3blsf
Namespace: default
Node: ubuntu16k5/10.140.0.15
Start Time: Wed, 20 Jul 2016 09:29:54 +0000
(略)
個々のpodの物理位置の確認(Nodeの行)
$ kubectl exec -it my-node-3blsf sh
=> 別ホスト上のpodへもログイン可能なので、Dockerレ
ベルのコンテナへのログイン(docker exec -it)より便利
です
特定podへログイン
$ kubectl logs my-node-3blsf
tailfも可能
$ kubectl logs -f my-node-3blsf
特定podのログの確認
$ sudo docker ps |grep mynode
9b8ecdb7a42f guest/mynode
"/bin/sh -c 'node ser" 15 minutes ago Up 15 minutes
k8s_mynode.6062cb3_my-node-a2015_default_841729f4-4e5c-11e6-
930a-42010a8c000e_a8947e68
50e8cd85abec guest/mynode
"/bin/sh -c 'node ser" 21 minutes ago Up 21 minutes
k8s_mynode.6062cb3_my-node-lm62r_default_94564430-4e5b-11e6-
930a-42010a8c000e_bcf722c3
fyi, Dockerレベルでコンテナの詳細を確認
$ sudo docker inspect 9b8ecdb7a42f
(略)
fyi, Dockerレベルで確認
• たとえばDockerレベルでコンテナを落としたり、ホス
トOSからDockerコンテナ上のプロセスをkill
• たとえば特定のマシン(VMインスタンス)を落とすテス
ト
=> レプリケーションの数が維持される
特定のpodを落とすテスト
• 自作アプリをサービスとして起動しました
• rcにより、pod(=コンテナ=プロセス)を複数インスタン
ス化しました
• podのインスタンス数がどうあれ、サービスにはサービ
スのIPアドレスで常にアクセス可能です
• 個々のpodの障害調査をしたければ、シェルで直接ログ
インしたりログの監視が可能です
ここまでのまとめ
Kubernetesのネットワーク
各pod内からはサービス名
つまり
curl http://frontend:8888
でお互いが見えます
=> DNSが返すIPアドレスは、サービスのIPアドレス
(DockerコンテナのIPアドレスでもないし、ホストのIPア
ドレスでもない)
名前解決(DNS)
$ dig @10.0.0.10 frontend.default.svc.cluster.local.
(略)
;; ANSWER SECTION:
frontend.default.svc.cluster.local. 30 IN A
10.0.0.206
=> DNSのFQDNは frontend.default.svc.cluster.local.
フォーマット:
サービス名.ネームスペース名.svc.cluster.local.
ホストからの名前解決
• ここまでの話:
• DNSでサービス名からサービスのIPアドレスがひけるようになり
ました
• ここからの話:
• このサービスのIPアドレスから、サービスにひもづくpodにたど
りつく必要があります
• rcのメカニズムにより、サービスにひもづくpodは複数ありうる
ので、どこかのpodにたどりつく必要があります
• 正確に言うと、pod内のコンテナにたどりつく必要があります
ネットワーク詳解
• サービスのIPアドレスはifconfigの世界には存在しないアドレ
スです
• iptablesで、サービスのIPアドレスからpod(=コンテナ)のア
ドレスへ宛先アドレスを書き換えます
• rcのメカニズムで複数のpodがある場合、iptablesのロード
バランス機能で振り分けます
• iptablesにサービスのIPアドレスを追加するのはkube-proxy
の役割です
• pod(=コンテナ)のアドレス宛のパケットをホストのIPアドレ
ス宛に変えるのはflanneldの役割です
• (このルーティングテーブルはetcdに入っています)
ネットワークの動作概要
10.0.0.206宛のパケットはiptablesでランダムに5つのどれかに行きます(rcのレプリカ数が5の場合):
$ sudo iptables-save | grep 10.0.0.206
-A KUBE-SERVICES -d 10.0.0.206/32 -p tcp -m comment --comment "default/frontend: cluster IP" -m tcp -
-dport 8888 -j KUBE-SVC-GYQQTB6TY565JPRW
$ sudo iptables-save |grep KUBE-SVC-GYQQTB6TY565JPRW
:KUBE-SVC-GYQQTB6TY565JPRW - [0:0]
-A KUBE-SERVICES -d 10.0.0.206/32 -p tcp -m comment --comment "default/frontend: cluster IP" -m tcp -
-dport 8888 -j KUBE-SVC-GYQQTB6TY565JPRW
-A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment "default/frontend:" -m statistic --mode random --
probability 0.20000000019 -j KUBE-SEP-IABZAQPI4OCAAEYI
-A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment "default/frontend:" -m statistic --mode random --
probability 0.25000000000 -j KUBE-SEP-KOOQP76EBZUHPEOS
-A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment "default/frontend:" -m statistic --mode random --
probability 0.33332999982 -j KUBE-SEP-R2LUGYH3W6MZDZRV
-A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment "default/frontend:" -m statistic --mode random --
probability 0.50000000000 -j KUBE-SEP-RHTBT7WLGW2VONI3
-A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment "default/frontend:" -j KUBE-SEP-DSHEFNPOTRMM5FWS
iptablesを確認(1)
$ sudo iptables-save |grep KUBE-SEP-DSHEFNPOTRMM5FWS
:KUBE-SEP-DSHEFNPOTRMM5FWS - [0:0]
-A KUBE-SEP-DSHEFNPOTRMM5FWS -s 10.1.79.4/32 -m comment
--comment "default/frontend:" -j KUBE-MARK-MASQ
-A KUBE-SEP-DSHEFNPOTRMM5FWS -p tcp -m comment --comment
"default/frontend:" -m tcp -j DNAT --to-destination
10.1.79.4:8888
-A KUBE-SVC-GYQQTB6TY565JPRW -m comment --comment
"default/frontend:" -j KUBE-SEP-DSHEFNPOTRMM5FWS
=> ある確率で 10.0.0.206:8888宛のパケットは 10.1.79.4:8888
宛のパケットへ変換される
iptablesを確認(2)
$ netstat -rn
Kernel IP routing table
Destination Gateway Genmask Flags MSS
Window irtt Iface
0.0.0.0 10.140.0.1 0.0.0.0 UG 0 0
0 ens4
10.1.0.0 0.0.0.0 255.255.0.0 U 0 0
0 flannel0
10.1.19.0 0.0.0.0 255.255.255.0 U 0 0
0 docker0
10.140.0.1 0.0.0.0 255.255.255.255 UH 0 0
0 ens4
=> 10.1.79.4:8888 宛のパケットはflanneldへ行く
ホストのルーティングテーブルを確認
$ etcdctl ls --recursive
/coreos.com/network/subnets
/coreos.com/network/subnets/10.1.19.0-24
/coreos.com/network/subnets/10.1.79.0-24
$ etcdctl get
/coreos.com/network/subnets/10.1.79.0-24
{"PublicIP":"10.140.0.15"}
=> "10.140.0.15"は、10.1.79.0/24のpod(コンテナ)が動
いているホストのIPアドレス
flanneldのルーティングテーブルを確認
•iptablesでサービスのIPアドレス
宛のパケットがpodのIPアドレス
宛になる
•pod宛のパケットはflanneldの力
で、そのpodが動いているホスト
宛のパケットになる
ここまでのまとめ
10.0.0.206をホストの外から叩けるようにするには、NodePort(or
LoadBalancer)でポートを外に見せる
mynode-svc.yaml =>
apiVersion: v1
kind: Service
metadata:
name: frontend
labels:
app: sample
spec:
type: NodePort
ports:
- port: 8888
selector:
app: sample
ホストの外にポートを公開するには
動作は異なりますが、感覚的には
Dockerでポートエクスポートする感じ
• Kubernetesは初見でかなり複雑怪奇に見えますが、き
ちんと分解すればなんとか理解できる(はず)
• ちょっとした考察
• おかしなことが起きた時、プロセスは潔く死んだほうが
Kubernetesに優しそうです
• iptablesを使っている限り、負荷分散が(ラウンドロビンより)
リッチになる見込みがない部分は賛否両論あるかもしれません
(LBとしては、ある種の割り切り)
まとめ
• メインのプログラミング言語: Java、JavaScript、Swift(一
部)
• 使っているミドルウェアの言語: Scala(Spark、Kafka)、
Go(Kubernetes)
• OS(Linux)、JVM、アルゴリズム、ミドルウェア、ネットワー
ク、ブラウザそれぞれの専門家は歓迎します
• 本物の機械学習ができるデータがあります(会社組織という
アーキテクチャが分析対象)
• 大規模開発(東京、大阪、上海、シンガポール、チェンナイ)
なので、日本なんてどうでもいいと思うタフさ(鈍感さ)と抽
象化して大枠をとらえる力があると良いと思います
最後にJTFのスポンサーなので人材募集
(株式会社ワークスアプリケーションズ)

Kubernetesにまつわるエトセトラ(主に苦労話)