<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  xmlns="http://purl.org/rss/1.0/"
  xmlns:dc="http://purl.org/dc/elements/1.1/"
  xmlns:admin="http://webns.net/mvcb/"
  xmlns:content="http://purl.org/rss/1.0/modules/content/"
  xmlns:sy="http://purl.org/rss/1.0/modules/syndication/">
  <channel rdf:about="http://www.fraction.jp/log/category/59/">
    <title>Program/技術 -- BONNOH FRACTION 14</title>
    <link>http://www.fraction.jp/log/category/59/</link>
    <description>世の中に寝るより楽はなかりけり&lt;br /&gt;浮世の馬鹿は起きて働く</description>
    
    <dc:creator>Yuanying</dc:creator>
	<dc:date>2018-11-06T09:52:26+09:00</dc:date>
	<admin:generatorAgent rdf:resource="http://webby.rubyforge.org/?v=0.9.4"/>
    <items>
      <rdf:Seq>
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2018/11/05/zfs-on-linux" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2018/10/22/k8s-1-12-1" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2018/04/19/japan-container-days-v18-04" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2018/02/23/k8s-service-type-loadbalancer-by-hand" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2017/12/07/kubelet-tls-bootstrap" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2017/11/15/openstack-summit-sydney" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2017/10/25/openstack-jp-study-group" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2017/10/12/self-hosted-kubernetes" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2017/07/31/glusterfs-on-k8s" />
        <rdf:li rdf:resource="http://www.fraction.jp/log/archives/2017/05/14/openstack-summit-boston" />
      </rdf:Seq>
    </items>
  </channel>
  <item rdf:about="http://www.fraction.jp/log/archives/2018/11/05/zfs-on-linux">
    <title>ZFS on Linux</title>
    <link>http://www.fraction.jp/log/archives/2018/11/05/zfs-on-linux</link>
    <description>お家 Kubernetes クラスターのストレージに NFS on ZFS on Linux を利用することにした。メインは rook で払い出した ceph を利用することにして、こちらは主にバックアップ用途。以下が作業ログ。ほとんど 「UBUNTU 16.04のZFSで/HOMEを冗長化」の丸パクリ。システム構成CPU: Intel Celeron J3160RAM: DDR3L 8 GiBDisk:Western Digital Red 6 TB WD60EFRX x 3Western Di...</description>
    <content:encoded><![CDATA[
        

<p class='image'>
<img  src='/log/2018/11/lsblk.png'
      width='745' 
      height='452' 
      alt=''  />
</p>


<p>お家 Kubernetes クラスターのストレージに NFS on ZFS on Linux を利用することにした。
メインは rook で払い出した ceph を利用することにして、こちらは主にバックアップ用途。</p>

<p>以下が作業ログ。
ほとんど 「<a href="https://tech.nosuz.jp/2016/05/ubuntu-16-04-zfs/">UBUNTU 16.04のZFSで/HOMEを冗長化</a>」の丸パクリ。</p>

<h2>システム構成</h2>

<ul>
<li>CPU: <a href="https://ark.intel.com/ja/products/91533/Intel-Celeron-Processor-J3160-2M-Cache-up-to-2-24-GHz-">Intel Celeron J3160</a></li>
<li>RAM: DDR3L 8 GiB</li>
<li>Disk:

<ul>
<li>Western Digital Red 6 TB WD60EFRX x 3</li>
<li>Western Digital Blue 6 TB WD60EZRZ x 3</li>
</ul>
</li>
<li>OS: Ubuntu 18.04</li>
</ul>


<p>ということで、WD の Red x3, Blue x3 の計 6 台を利用して <code>raidz2</code> 構成のストレージを作る。</p>

<h2>zfs のインストール</h2>

<pre><code># apt install zfsutils-linux
</code></pre>

<h2>プールの作成</h2>

<p>システムの該当するディスクを事前に <code>ls /dev/disk/by-id</code> で調べておき、gpt でラベル。</p>

<pre><code># parted /dev/disk/by-id/ata-WDC_WD60EFRX-68L0BN1_WD-WX11D3678X8H mklabel gpt
# parted /dev/disk/by-id/ata-WDC_WD60EFRX-68L0BN1_WD-WX11D36JRS44 mklabel gpt
# parted /dev/disk/by-id/ata-WDC_WD60EFRX-68L0BN1_WD-WX21D48FDCVA mklabel gpt
# parted /dev/disk/by-id/ata-WDC_WD60EZRZ-00GZ5B1_WD-WX21D68PLLUX mklabel gpt
# parted /dev/disk/by-id/ata-WDC_WD60EZRZ-00GZ5B1_WD-WX31D88KSYCH mklabel gpt
# parted /dev/disk/by-id/ata-WDC_WD60EZRZ-00RWYB1_WD-WX31D254PV7U mklabel gpt
</code></pre>

<p>そしてプールの作成。</p>

<pre><code># zpool create -o ashift=12 -o autoexpand=on \
    tank raidz2 \
    ata-WDC_WD60EFRX-68L0BN1_WD-WX11D3678X8H \
    ata-WDC_WD60EFRX-68L0BN1_WD-WX11D36JRS44 \
    ata-WDC_WD60EFRX-68L0BN1_WD-WX21D48FDCVA \
    ata-WDC_WD60EZRZ-00GZ5B1_WD-WX21D68PLLUX \
    ata-WDC_WD60EZRZ-00GZ5B1_WD-WX31D88KSYCH \
    ata-WDC_WD60EZRZ-00RWYB1_WD-WX31D254PV7U
</code></pre>

<p>オプションはそのまま参考エントリのまま。 <code>-o ashift</code> でセクターサイズの指定、 <code>-autoexpand=on</code> で将来拡張する時の保険。</p>

<pre><code># zpool status
pool: tank
state: ONLINE
scan: none requested
config:

    NAME                                          STATE     READ WRITE CKSUM
    tank                                          ONLINE       0     0     0
      raidz2-0                                    ONLINE       0     0     0
        ata-WDC_WD60EFRX-68L0BN1_WD-WX11D3678X8H  ONLINE       0     0     0
        ata-WDC_WD60EFRX-68L0BN1_WD-WX11D36JRS44  ONLINE       0     0     0
        ata-WDC_WD60EFRX-68L0BN1_WD-WX21D48FDCVA  ONLINE       0     0     0
        ata-WDC_WD60EZRZ-00GZ5B1_WD-WX21D68PLLUX  ONLINE       0     0     0
        ata-WDC_WD60EZRZ-00GZ5B1_WD-WX31D88KSYCH  ONLINE       0     0     0
        ata-WDC_WD60EZRZ-00RWYB1_WD-WX31D254PV7U  ONLINE       0     0     0

errors: No known data errors
</code></pre>

<h2>ファイルシステムの作成</h2>

<p>普通は用途ごとに細かくオプションを変えたファイルシステムを切り出すのだろうけど、
面倒だったので <code>export</code> という名前のファイルシステムを作成して、後々、nfs で共有予定。</p>

<pre><code># zfs create -o atime=on -o relatime=on \
      tank/export
# mkdir /export
# zfs set mountpoint=/export tank/export
</code></pre>

<p>21TB のファイルシステムができました。</p>

<pre><code># zfs list
NAME          USED  AVAIL  REFER  MOUNTPOINT
tank          959K  21.0T   192K  /tank
tank/export   192K  21.0T   192K  /export
</code></pre>

    ]]></content:encoded>
    <dc:subject>日記</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2018-11-05T13:00:29+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2018/10/22/k8s-1-12-1">
    <title>自宅クラスターを k8s 1.12.1 にアップグレード</title>
    <link>http://www.fraction.jp/log/archives/2018/10/22/k8s-1-12-1</link>
    <description>自宅の PC で Kubernetes クラスターを運用し始めてから、一年経った。Happy birthday my cluster! Today is the 1st anniversary of my #kubernetes cluster. Any gifts are welcome lol pic.twitter.com/xM5J0G4O3B&amp;mdash; Yuanying (@yuanying) 2018年10月10日 最初の投稿では運用し始めたクラスターのバージョンは伺い知れぬが、Tw...</description>
    <content:encoded><![CDATA[
        <p>自宅の PC で <a href="/log/archives/2017/10/12/self-hosted-kubernetes">Kubernetes クラスターを運用し始めて</a>から、
一年経った。</p>

<blockquote class="twitter-tweet" data-cards="hidden" data-lang="ja"><p lang="en" dir="ltr">Happy birthday my cluster! Today is the 1st anniversary of my <a href="https://twitter.com/hashtag/kubernetes?src=hash&amp;ref_src=twsrc%5Etfw">#kubernetes</a> cluster. Any gifts are welcome lol <a href="https://t.co/xM5J0G4O3B">pic.twitter.com/xM5J0G4O3B</a></p>&mdash; Yuanying (@yuanying) <a href="https://twitter.com/yuanying/status/1049889780761329665?ref_src=twsrc%5Etfw">2018年10月10日</a></blockquote>


<p> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p>

<p>最初の投稿では運用し始めたクラスターのバージョンは伺い知れぬが、
Twitter 上の<a href="https://twitter.com/yuanying/status/920644764747182080">最初のアップグレードのつぶやき</a>が、v1.7 -> v1.8 であったので、
v1.7 のいくつかだったのであろう。</p>

<p class='image'>
<img  src='/log/2018/10/k8s-1.12.1.png'
      width='661' 
      height='452' 
      alt=''  />
</p>


<p>そんなこんなで、今日、v1.11.2 だったクラスターを v1.12.1 にアップグレードした。
かれこれ 5 マイナーバージョンのアップグレードである。</p>

<p>以下、今回のアップグレードでやったこと。</p>

<h2>apiserver の修正</h2>

<p><code>--insecure-port</code> のフラグが (v1.11からだが) deprecated になったのに合わせて削除。</p>

<h2>controller-manager の修正</h2>

<p><a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.12.md#known-issues">Changelog の Known Issues に書いてあって</a>
なんじゃこりゃ、と思っていた、</p>

<blockquote><p>kube-controller-manager currently needs a writable --cert-dir (default is /var/run/kubernetes) for generating self-signed certificates, when no --tls-cert-file or --tls-private-key-file are provided.</p></blockquote>

<p>に思いっきり当たる。</p>

<p>そもそも、v1.12 から controller-manager が公開している、metrics 取得のための API が、
secure になった、というか TLS 接続がデフォルトになったのだが、
(それに合わせて色々フラグが増えているのだけれども、一見見るとなんじゃこれと思う)
そのための証明書や鍵を設定していないと自己署名証明書を所定のディレクトリに自動生成してしまう、というのがこの issue。</p>

<p>一見すると特になんの問題もなさそうなのだが、どうやら hyperkube のイメージでは、
その「所定のディレクトリ」が read only であるらしく、
自動生成することができずに controller-manager の起動に失敗してしまうのだ！</p>

<p>回避方法は、一番真っ当な解決策として、「妥当な証明書をちゃんと設定してやる」というものが挙げられるが、
今の所、特に controller-manager のメトリクスは収集してないので、適当な empty dir をマウントして逃げた。
(<a href="https://github.com/kubernetes-incubator/bootkube/pull/1003/files#diff-1a342de84d1bbb83d110cdca78afa143R456">こんな感じ</a>)</p>

<p>そもそも<a href="https://github.com/kubernetes/kubernetes/issues/68973">自己署名の証明書をディスクに書き出す必要もなく、メモリに持てばいいじゃない</a>、
などの議論がされている模様。</p>

<h2>etcd を v3.2.18 から v3.2.24 へ</h2>

<p>一応、ミニマムな<a href="https://kubernetes.io/docs/tasks/administer-cluster/configure-upgrade-etcd/#prerequisites">推奨バージョンは 3.2.10</a>
とのことだが、
v1.12 における<a href="https://github.com/kubernetes/kubernetes/pull/68318">デフォルトの etcd version が v3.2.24 になった</a>ことに合わせて、
etcd のバージョンを初めて上げた。</p>

<p>と言っても、etcd 自体は static pod で起動しているので、
やったことはマニフェストのバージョンを上げて、一台ずつ手でマニフェストを交換してやっただけ。
すんなり動いた。(めんどくさかったのでバックアップすら取っていない。)</p>

<h2>checkpointer のバージョンアップ</h2>

<p>self-hosted k8s をやってる人じゃないと関係ないけど、
checkpointer のバージョンを 018007e77ccd61e8e59b7e15d7fc5e318a5a2682 に上げた。
上げないとクラスター再起動が失敗する模様。</p>

<p>なんか、checkpointer は k8s のバージョンごとに上げないとダメな感じよね。</p>

<p>以上。</p>

<p>CoreDNS のバージョンは後で上げる。</p>

    ]]></content:encoded>
    <dc:subject>日記</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2018-10-22T11:37:29+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2018/04/19/japan-container-days-v18-04">
    <title>Japan Container Days v18.04</title>
    <link>http://www.fraction.jp/log/archives/2018/04/19/japan-container-days-v18-04</link>
    <description>Japan Container Days v18.04にて登壇してきた。発表内容は Helmを利用したKubernetes as a Serviceの実現。解説記事を書こう書こうと思いつつも書けずにいる。...</description>
    <content:encoded><![CDATA[
        

<p class='image'>
<img  src='/log/2018/04/30740921_1789655144431091_4228610794747633663_n.jpg'
      width='640'
      height='640'
      alt=''  />
</p>


<p><a href="https://containerdays.jp">Japan Container Days v18.04</a>
にて登壇してきた。
発表内容は <a href="https://speakerdeck.com/yuanying/helm-woli-yong-sita-kubernetes-as-a-service-falseshi-xian">Helmを利用したKubernetes as a Serviceの実現</a>。</p>

<script async class="speakerdeck-embed" data-id="c7718bafb81240d19246cbcb0e7e89a3" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>


<p>解説記事を書こう書こうと思いつつも書けずにいる。</p>

    ]]></content:encoded>
    <dc:subject>日記</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2018-04-19T16:10:32+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2018/02/23/k8s-service-type-loadbalancer-by-hand">
    <title>Kubernetes の Service type LoadBalancer を手作業で作る</title>
    <link>http://www.fraction.jp/log/archives/2018/02/23/k8s-service-type-loadbalancer-by-hand</link>
    <description>Kubernetes の Service type LoadBalancer を手作業で作るベアメタルで Kubernetes を運用している皆さん、外部からどうやって Kubernetes 上の Service に接続しようかと困っていませんか？具体的にいうと Service type LoadBalancer が無いので困っていませんか？Kubernetes の Service type LoadBalancer に対応していないインフラストラクチャ上で type LoadBalancer な...</description>
    <content:encoded><![CDATA[
        <p>Kubernetes の Service type LoadBalancer を手作業で作る
ベアメタルで Kubernetes を運用している皆さん、外部からどうやって Kubernetes 上の Service に接続しようかと困っていませんか？具体的にいうと <code>Service type LoadBalancer</code> が無いので困っていませんか？</p>

<p>Kubernetes の <code>Service type LoadBalancer</code> に対応していないインフラストラクチャ上で <code>type LoadBalancer</code> な Service を作成するとこんな感じで、</p>

<pre><code>$ kubectl get svc
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx     LoadBalancer   10.254.0.220   &lt;pending&gt;     80:30692/TCP   11s
</code></pre>


<p>いつまでたっても EXTERNAL-IP が <code>&lt;pending&gt;</code> のままでロードバランサーが作られることはありません。というのも Kubernetes の中でロードバランサーを作成し、Service の EXTERNAL-IP をロードバランサーの IP アドレスに設定する、という処理を担当する controller が動いていないからです。（いないから。</p>

<p>そこで今回のこの記事の趣旨は、いないなら手作業でやってしまえば良いじゃない！です。手順は以下。</p>

<ol>
<li>前準備</li>
<li>Pod/Service の作成</li>
<li>ロードバランサーの作成</li>
<li>Service へロードバランサーの情報を設定</li>
</ol>


<h2>前準備</h2>

<p>とりあえずテスト用に <code>lbtest</code> という名前の <code>Namespace</code> と、後々の作業のために <code>lbtest</code> 上の <code>default</code> サービスアカウントに <code>cluster-admin</code> 権限を与えておきます。</p>

<pre><code>$ cat << EOF | kubectl create -f -
&#045;&#045;&#045;
kind: Namespace
apiVersion: v1
metadata:
  name: lbtest
&#045;&#045;&#045;
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: lbtest
subjects:
- kind: ServiceAccount
  namespace: lbtest
  name: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
EOF
</code></pre>


<h2>Pod/Service の作成</h2>

<p>テスト用に nginx が動いている <code>Pod</code> と、その <code>Pod</code> にアクセスするための、<code>type: LoadBalancer</code> な <code>Service</code> を作成します。</p>

<pre><code>$ cat << EOF | kubectl create -f -
&#045;&#045;&#045;
apiVersion: v1
kind: Pod
metadata:
  name: nginx
  namespace: lbtest
  labels:
    app: nginx
spec:
  containers:
    - name: nginx-container
      image: nginx
      ports:
      - containerPort: 80
&#045;&#045;&#045;
kind: Service
apiVersion: v1
metadata:
  name: nginx
  namespace: lbtest
spec:
  selector:
    app: nginx
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80
  type: LoadBalancer
EOF
</code></pre>


<p>作ったサービスを確認すると、案の定 EXTERNAL-IP が <code>&lt;pending&gt;</code> のままですね。</p>

<pre><code>$ kubectl get svc -n lbtest
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx     LoadBalancer   10.254.0.220   &lt;pending&gt;     80:30692/TCP   11s
</code></pre>


<h2>ロードバランサーの作成</h2>

<p>ちなみに、Kubernetes の <code>type: LoadBalncer</code> な <code>Service</code> のロードバランサーがどうやって<code>Pod</code> に対してロードバランシングを行なっているかというと、
一番簡単な説明が Tim Hockin が Google Cloud Next '17 で行なった <a href="https://www.youtube.com/watch?v=y2bhV81MfKQ">The ins and outs of networking in Google Container Engine and Kubernetes</a>というプレゼンを見るのが早いのだけれども、すごい大雑把にいうとこんな感じ。</p>

<p class='image'>
<img  src='/log/2018/02/service-type-lb.png'
      width='800'
      height='451'
      alt=''  />
</p>


<p>つまりロードバランサーは <code>Service</code> の <code>NodePort</code> にバランシングを行えば良いということになる。先ほど作った <code>Service</code> は <code>30692</code> の <code>NodePort</code> で待ち受けているようなので、、</p>

<p><code>Node</code> の IP Address を確認し、</p>

<pre><code>$ kubectl get nodes
NAME             STATUS    ROLES     AGE       VERSION
172.18.201.121   Ready     <none>    23d       v1.9.2
172.18.201.122   Ready     <none>    23d       v1.9.2
172.18.201.123   Ready     <none>    23d       v1.9.2
</code></pre>


<p>以下のような HAProxy の設定を書いて HAProxy を起動する。</p>

<pre><code>$ cat << EOF > haproxy.cfg
global
    maxconn 256

defaults
    mode http
    timeout client     120000ms
    timeout server     120000ms
    timeout connect      6000ms

listen http-in
    bind *:80
    server server1 172.18.201.121:30692
    server server2 172.18.201.122:30692
    server server3 172.18.201.123:30692
EOF
$ docker run -d --name haproxy \
  -p 80:80 \
  -v $(pwd)/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
  haproxy:1.8
</code></pre>


<p>localhost に対して curl を叩いて見ると、ロードバランサーがちゃんと動作していることがわかります。</p>

<pre><code>$ curl 127.0.0.1:80                                                                                                      (cluster/lbtest)
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Welcome to nginx!&lt;/title&gt;
&lt;style&gt;
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Welcome to nginx!&lt;/h1&gt;
&lt;p&gt;If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.&lt;/p&gt;

&lt;p&gt;For online documentation and support please refer to
&lt;a href="http://nginx.org/"&gt;nginx.org&lt;/a&gt;.&lt;br/&gt;
Commercial support is available at
&lt;a href="http://nginx.com/"&gt;nginx.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thank you for using nginx.&lt;/em&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>


<h2>Service へロードバランサーの情報を設定</h2>

<p>さて、細かいことを気にしなければ(笑) これで Kubernetes の <code>Service</code> に対して外部のロードバランサーが設定できたことになるのですが、</p>

<pre><code>$ kubectl get svc -n lbtest
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx     LoadBalancer   10.254.0.220   &lt;pending&gt;     80:30692/TCP   2h
</code></pre>


<p>やっぱり <code>Service</code> の EXTERNAL-IP が <code>&lt;pending&gt;</code> になったままになっているのが気になる人が出てくる気もします。ので、ここを修正します。</p>

<p>まず、<code>lbtest</code> Namespace の <code>default</code> サービスアカウントのアクセストークンをどうにかしてとってきます。</p>

<pre><code>$ TOKEN=$(kubectl describe secret \
  $(kubectl get secrets | grep default | cut -f1 -d ' ') | \
  grep -E '^token' | \
  cut -f2 -d':' | tr -d '\t' | tr -d ' ')
</code></pre>


<p>このアクセストークンが有効かどうかちょっと確認してみましょう。</p>

<pre><code>$ curl -k https://${KUBERNETES_API_ENDPOINT}/api/v1/namespaces \
  --header "Authorization: Bearer $TOKEN"
{
  "kind": "NamespaceList",
  "apiVersion": "v1",
  "metadata": {
    "selfLink": "/api/v1/namespaces",
    "resourceVersion": "3765645"
... （中略）
}%
</code></pre>


<p>良さそうです。</p>

<p>ちょっと調べたところ、Kubernetes の <code>Service</code> のステータス (EXTERNAL-IP の情報を含んでいる属性)の情報を書き換えるには単純に <code>Service</code> の該当属性を書き換えるだけではダメらしく、<code>service/status</code> サブリソースを書き換える必要があるようです。</p>

<p>とりあえず現状の <code>service/status</code> をとってきます。</p>

<pre><code>$ curl -k --header "Authorization: Bearer $TOKEN" \
  https://${KUBERNETES_API_ENDPOINT}/api/v1/namespaces/lbtest/services/nginx/status \
  > nginx-status.json
</code></pre>


<p>そしてとってきた情報の中の、<code>/status/loadBalancer</code> の項目を修正します。</p>

<pre><code>
$ diff -u nginx-status.json.old nginx-status.json
--- nginx-status.json.old   2018-02-23 14:01:55.000000000 +0900
+++ nginx-status.json   2018-02-23 14:01:44.000000000 +0900
@@ -31,7 +31,7 @@
   },
   "status": {
     "loadBalancer": {
-
+      "ingress": [ { "ip": "127.0.0.1" } ]
     }
   }
 }
</code></pre>


<p>ここの <code>ip</code> にはロードバランサーのアドレスを設定します。それではこの情報を使って <code>Service</code> を更新しましょう！</p>

<pre><code>$ curl -k --header "Authorization: Bearer $TOKEN" \
  https://${KUBERNETES_API_ENDPOINT}/api/v1/namespaces/lbtest/services/nginx/status \
  -X PUT -d @nginx-status.json -H 'content-type:application/json'
</code></pre>


<p>これで <code>Service</code> の情報は更新されました。</p>

<pre><code>$ kubectl get svc -n lbtest
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx     LoadBalancer   10.254.0.220   127.0.0.1     80:30692/TCP   2h
</code></pre>


<p>ちゃんと更新されてますね、素晴らしい！これであなたのベアメタルなKubernetesクラスターでも何も気にすることなく <code>Service type: LoadBalancer</code> を使いたい放題ですね！やった！</p>

<h2>補足</h2>

<p>この記事は <a href="https://metallb.universe.tf">MetalLB</a> のソースコードを読んで冗談で書いたものです。今までは特に詳細を気にすることなく、<code>CloudProvider</code> インタフェースの <code>LoadBalancer()</code> を実装しなくちゃいけない (must) なのかと思ってましたがそんなことなかったんですね。ちゃんと確認するもんです。</p>

<h2>補足2</h2>

<p>上記を自動化すれば一応使い物になる <code>Service type: LoadBalancer</code> controller ができるかも？</p>

    ]]></content:encoded>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2018-02-23T16:17:29+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2017/12/07/kubelet-tls-bootstrap">
    <title>kubeadm join Deep Dive</title>
    <link>http://www.fraction.jp/log/archives/2017/12/07/kubelet-tls-bootstrap</link>
    <description>KubeCon 参加中にKubernetes Advent Calendarの7日目の記事投稿！注: ほぼドキュメントベースで記事を書いているので、バージョン混在してるかも。背景もともと、OpenStack Magnumというプロジェクトで OpenStack 上に Kubernetes をデプロイするサービスの開発を行っていたこともあって、Kubernetes のデプロイ周りは結構気になることが多い。最近だと、自宅クラスター用ベアメタル上に Kubernetes をデプロイするRemoraという...</description>
    <content:encoded><![CDATA[
        

<p class='image'>
<img  src='/log/2017/12/IMG_4765.JPG'
      width='640'
      height='480'
      alt=''  />
</p>


<p>KubeCon 参加中に
<a href="https://qiita.com/advent-calendar/2017/kubernetes">Kubernetes Advent Calendar</a>の7日目の記事投稿！</p>

<p><strong>注: ほぼドキュメントベースで記事を書いているので、バージョン混在してるかも。</strong></p>

<h2>背景</h2>

<p>もともと、<a href="https://github.com/openstack/magnum">OpenStack Magnum</a>
というプロジェクトで OpenStack 上に Kubernetes をデプロイするサービスの開発を行っていたこともあって、
Kubernetes のデプロイ周りは結構気になることが多い。</p>

<p>最近だと、
<del>自宅クラスター用</del>ベアメタル上に Kubernetes をデプロイする
<a href="https://github.com/nec-openstack/remora">Remora</a>
というツールを細々と開発しているのだけれども、
worker ノードをクラスタに繋げる際に worker ノード用の TLS 証明書を個別に用意するのはめんどくさいなあと思い始めて来たこともあって、
<code>kubeadm join</code> が具体的にどんなことをしているのかが気になって来た。</p>

<p>で、調べて見たところ <code>kubeadm join</code> は
<a href="https://kubernetes.io/docs/admin/kubelet-tls-bootstrapping/">kubelet TLS bootstrapping</a>
という仕組みを使っていることがわかった。</p>

<h2>kubelet TLS bootstrapping</h2>

<p><code>kubelet TLS bootstrapping</code> とは、
kubeadm が新しい worker ノード (kubelet) を安全にクラスターにつなげる際に使っている仕組み。
<a href="https://github.com/kubernetes/kubernetes/issues/5754#issuecomment-287789950">以下の二つのパートに分けられる</a>。</p>

<ul>
<li><a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/cluster-lifecycle/bootstrap-discovery.md">Discovery</a>

<ul>
<li>Join するクラスターへの接続情報をどうやって Worker ノードに伝えるか？</li>
</ul>
</li>
<li><a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/cluster-lifecycle/kubelet-tls-bootstrap.md">Certificate</a>

<ul>
<li>Worker ノードがクラスターへ接続する際の TLS 証明書発行の仕組み。</li>
</ul>
</li>
</ul>


<p>TLS クライアント認証に詳しい人には以下のように言うと簡単かもしれない。
Discovery はクライアント認証に利用する CA 証明書を取得するパート、
Certificate はクライアント証明書を取得するパート。</p>

<p>具体的には以下のフロー。</p>

<pre><code>$ sudo kubeadm join --token 43a25d.420ff2e06336e4c1 172.31.7.230:6443 --skip-preflight-checks
[kubeadm] WARNING: kubeadm is in beta, please do not use it for production clusters.
[preflight] Skipping pre-flight checks
[discovery] Trying to connect to API Server "172.31.7.230:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://172.31.7.230:6443"
[discovery] Cluster info signature and contents are valid, will use API Server "https://172.31.7.230:6443"
[discovery] Successfully established connection with API Server "172.31.7.230:6443"
[bootstrap] Detected server version: v1.7.6
[bootstrap] The server supports the Certificates API (certificates.k8s.io/v1beta1)
[csr] Created API client to obtain unique certificate for this node, generating keys and certificate signing request
[csr] Received signed certificate from the API server, generating KubeConfig...
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"

Node join complete:
* Certificate signing request sent to master and response
  received.
* Kubelet informed of new secure connection details.

Run 'kubectl get nodes' on the master to see this machine join.
</code></pre>


<p>この、二つのパートを裏側で支えているもう一つの重要なパーツが、<code>Bootstrap Token</code> である。
(以降、Token/Bootsrap Token)。</p>

<h2>Bootstrap Token</h2>

<p><a href="https://kubernetes.io/docs/admin/authentication/#bootstrap-tokens">Bootstrap Token</a> は主に以下の二つの用途で利用される Token。
詳しい仕組みは<a href="https://kubernetes.io/docs/admin/bootstrap-tokens/">公式ドキュメント</a>
に書かれており、以下の文章は公式文章を要約したものとなる。</p>

<ul>
<li>Discovery で見つかった Kubernetes Cluster の検証。

<ul>
<li>本当に Discovery で見つけたクラスターは、自分が繋がりたかったクラスターなのか？</li>
</ul>
</li>
<li>Kubernetes の API に CSR を送るつける際の認証。</li>
</ul>


<p>この二つの用途は本来は別の用途であるので、それぞれ別の Token を利用することも可能。</p>

<h3>Token Format</h3>

<p>Bootstrap Token は正規表現で <code>[a-z0-9]{6}\.[a-z0-9]{16}</code> で表すことができる。
例えば、<code>abcdef.0123456789abcdef</code>。
ID 部分と Secret 部分がピリオドで繋がっている構造となる。</p>

<h3>Token の作成・登録</h3>

<p>kubeadm を利用していると、
<code>kubeadm join</code> で利用する Token は <code>kubeadm init</code> を実行した際に表示されているものを使うことが多いと思われるが、
実際のところこの Token は上記のフォーマットに則っていればユーザが作ったものでも良い。</p>

<p>以下のフォーマットのマニフェストを作成し、Kubernetes に登録する。</p>

<pre><code>apiVersion: v1
kind: Secret
metadata:
  name: bootstrap-token-07401b
  namespace: kube-system
type: bootstrap.kubernetes.io/token
data:
  description: base64(The default bootstrap token generated by 'kubeadm init'.)
  token-id: base64(07401b)
  token-secret: base64(f395accd246ae52d)
  expiration: base64(2017-03-10T03:22:11Z)
  usage-bootstrap-authentication: base64(true)
  usage-bootstrap-signing: base64(true)
  auth-extra-groups: base64(system:bootstrappers:group1,system:bootstrappers:group2)
</code></pre>


<p><code>usage-bootstrap-authentication</code> と <code>usage-bootstrap-signing</code>
は上記で述べた、「Kubernetes の API に CSR を送るつける際の認証」に利用するのか、
それとも「Discovery で見つかった Kubernetes Cluster の検証」に利用するのか、のフラグ。
普通はどっちも <code>true</code> に設定しておくものではなかろうか。</p>

<p>ちなみに、<code>controller-manager</code> に <code>--controllers=*,tokencleaner</code> と設定しておくと
expiration を超えた Token を自動で削除してもらえる。</p>

<h3>Token による認証</h3>

<p>この Token は <a href="https://kubernetes.io/docs/admin/authentication/#bootstrap-tokens">Kubernetes の API へのリクエスト時の認証に Bearler Token として利用することができる</a>。
(現時点ではまだ alpha feature なので api-server 起動時に <code>--experimental-bootstrap-token-auth</code> フラグを立てる必要がある)
この Token で api-server により、
<code>system:bootstrappers</code> グループに属している
<code>system:bootstrap:&lt;Token ID&gt;</code> ユーザとして認証される。</p>

<h2>Discovery</h2>

<p>通常、<code>kubeadm join</code> する際にはクラスターの IP アドレスおよび Port、そして Token を指定することになるが、今回はその際の Discovery 手順について。Join するクラスターへの接続情報をどうやって Worker ノードに伝えるか？について述べる。</p>

<h3>ClusterInfo</h3>

<p>まず、<code>kubeadm join</code> する前に、管理者は <code>cluster-info</code> と言う名前の <code>ConfigMap</code> を <code>kube-public</code> 名前空間に登録しておく必要がある。
<code>kubeadm</code> を使った場合には <code>kubeadm init</code> した際に自動的に登録されている(と思う)。</p>

<pre><code>apiVersion: v1
kind: ConfigMap
metadata:
  name: cluster-info
  namespace: kube-public
data:
  jws-kubeconfig-07401b: eyJhbGciOiJIUzI1NiIsImtpZCI6IjA3NDAxYiJ9..tYEfbo6zDNo40MQE07aZcQX2m3EB2rO3NuXtxVMYm9U
  kubeconfig: |
    apiVersion: v1
    clusters:
    - cluster:
        certificate-authority-data: <really long certificate data>
        server: https://10.138.0.2:6443
      name: ""
    contexts: []
    current-context: ""
    kind: Config
    preferences: {}
    users: []
</code></pre>


<p>登録する <code>ConfigMap</code> はこんな感じ。重要な点は、</p>

<ul>
<li>通常、オレオレ CA に sign されているエンドポイントなため、insecure である。</li>
<li><code>kube-public</code> に登録されているリソースであり、リソースの名前もわかっているので以下のコマンドで取得できる。デザインプロポーザルには認証なしで取得できるとある。

<ul>
<li><code>curl -k https://${IP_ADDRESS}:${PORT}/api/v1/namespaces/kube-public/configmaps/cluster-info</code></li>
</ul>
</li>
<li>クラスターに API リクエストを送る際に必須な情報である、API エンドポイント、および CA 証明書の情報が含まれた <code>kubeconfig</code> を含んでいる。</li>
</ul>


<p>これがセキュリティとか関係なく、kubernetes の API に繋がりたい、と言うだけだったら話はこれでおしまいなのだけれども、そもそもオレオレ証明書で署名された insecure な通信で得られた情報なので、この <code>kubeconfig</code> に含まれている情報が本物なのかどうかは定かではない。
そこで <a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/cluster-lifecycle/bootstrap-discovery.md">Discovery API</a> のデザインプロポーザルには、
この <code>kubeconfig</code> をどう検証するのか？についての説明がある。</p>

<h3>ClusterInfo の検証</h3>

<p><code>kubeconfig</code> は標準的な <code>JWS</code> で、"detached content" として署名されており、
ClusterInfo の中には <code>bootstrap-token-${TOKEN_ID}</code> と言う名前で Token ごとに利用する <code>JWS</code> が登録されている。(この <code>bootstrap-token-${TOKEN_ID}</code> は <code>bootstrapsigner</code> と言うコントローラが Bootstrap Token と ClusterInfo を常時監視して生成している。)</p>

<p>JWS のヘッダをデコードしてみると、使われている暗号化のアルゴリズムがわかる。</p>

<pre><code>$ echo "eyJhbGciOiJIUzI1NiIsImtpZCI6IjA3NDAxYiJ9" | base64 -D
{"alg":"HS256","kid":"07401b"}%
</code></pre>


<p>デザインプロポーザルには、
<code>JWS</code> の仕様に則って <code>kubeconfig</code> を base64 でエンコードしたものを <code>Bootstrap Token</code> を共通鍵として <code>HS256</code> scheme を利用して検証できる、とある。</p>

<p>コードにすると<a href="http://y0m0r.hateblo.jp/entry/20130520/1369063734">こんな感じ</a>。</p>

<pre><code>require 'openssl'

secret_key = 'Bootstrap Token'
message = 'Base64 encoded kubeconfig without trailing `=`'

puts OpenSSL::HMAC::hexdigest(
  OpenSSL::Digest::SHA256.new,
  secret_key,
  message
)
# => bcb5a198056e36cbb27c499528e0e1345ee25c7b5d7b67508c96122b1e38e904
</code></pre>


<p>ここで出力された結果が、<code>JWS</code> の後半部分と一緒ならば、検証成功。
ここで得られた <code>kubeconfig</code> が概ね正しいと言うことになる。</p>

<h3>CA pinning</h3>

<p>上記で <em>概ね正しい</em> と述べたのは、
例えばネットワークや一部のノードのセキュリティが脆弱で、
Bootstrap Token が盗まれてしまった場合、
「中間者攻撃が可能になってしまう」と言う問題があるから。
(複数のノードで使う前提の秘密鍵が漏れてしまったら、攻撃者は偽の<code>kubeconfig</code> を署名できてしまうため。)
それを回避するため、<code>kubeadm</code> の v1.8 からはデフォルトで、
<code>--discovery-token-ca-cert-hash</code> と言うオプションが使われるようになった。</p>

<p>この hash は以下のコマンドで求められる。</p>

<pre><code>$ openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | \
  openssl rsa -pubin -outform der 2>/dev/null | \
  openssl dgst -sha256 -hex | \
  sed 's/^.* //'
</code></pre>


<p>この値を一緒に使うことで取得された CA 証明書が正しいと信頼することができる。</p>

<p>(微妙によくわかってないのが、<code>kubeadm join</code> 時の IP アドレスと Port から Kubernetes の API エンドポイントはわかるんだから、あとは CA 証明書を取得できればいいだけで、、<code>kubeconfig</code> 自体の検証は必要なのか？と言うこと。誰か知ってたら教えてください。)</p>

<h2>Certificate</h2>

<p>Worker ノードと Kubernetes は TLS クライアント認証を使って認証を行なっている。
この Certificate のパートでは Worker ノード用の TLS 証明書を、どう取得するのか？
について述べる。</p>

<h3>Certificate API</h3>

<p>Kubernetes はクラスターレベルでの TLS 認証を行うための証明書を取得するために、
<a href="https://github.com/ietf-wg-acme/acme/">ACME</a> に似た
<a href="https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/">Certificate API</a>
を用意してる。
ユーザまたは Worker ノードは API に対して <code>CSR</code> リソースを作成してクライアント証明書を取得する。
コードを読んだところ最新の kubelet は bootstrap 時に</p>

<ul>
<li>kubeconfig ファイルが作成されておらず、</li>
<li>bootstrap kubeconfg が設定されていた場合、</li>
</ul>


<p>自分で <code>CSR</code> を作成して証明書を取得しに行くらしい。賢い。</p>

<h3>CSR の種類</h3>

<p><a href="https://kubernetes.io/docs/admin/kubelet-tls-bootstrapping/#approval-controller">多分</a> <code>csrapproving</code> コントローラが、
API に登録された <code>CSR</code> を以下の三つのサブリソースに分類する。</p>

<ul>
<li>nodeclient

<ul>
<li>新しく <code>O=system:nodes</code> かつ <code>CN=system:node:(node name)</code> な証明書のための CSR。</li>
</ul>
</li>
<li>selfnodeclient

<ul>
<li>クライアント証明書を更新するために、同じ <code>O</code> (グループ) かつ <code>CN</code> (ユーザ名) を持っているユーザが発行した CSR。</li>
</ul>
</li>
<li>selfnodeserver

<ul>
<li><code>kubelet</code> 自身のサーバ証明書を更新/登録する際の CSR。(alpha feature)</li>
</ul>
</li>
</ul>


<p>ちょっとここら辺からコードおよびドキュメントベースで調べていて間違いが含まれているかもしれないけど、
とりあえず書くだけ書いて後で確認後に修正する。</p>

<h3>Certificate API 利用時の認証・認可</h3>

<p>そもそも、bootstrap 時に Worker ノードが <code>CSR</code> リソースを作成できねばならないのだが、
どのような RBAC リソースによって制御されているかと言うと、</p>

<ul>
<li>ClusterRole: <code>system:node-bootstrapper</code>

<ul>
<li>certificatesigningrequests の create/get/list/watch を許可。</li>
<li>Kubernetes がデフォルトで作成している。</li>
</ul>
</li>
<li>ClusterRoleBinding: <code>kubeadm:kubelet-bootstrap</code>

<ul>
<li>RoleRef: <code>system:node-bootstrapper</code></li>
<li>Subject Group: <code>system:bootstrappers:kubeadm:default-node-token</code></li>
<li>kubeadm が作成している。</li>
<li>ドキュメントでは Bootstrap Token で認証されたユーザは自動的に <code>system:boostrappers</code> グループに属すことになると書いてあったのでそれが <code>system:node-bootstrapper</code> ロールに結びつくのかと思いきや、違うっぽい。</li>
</ul>
</li>
<li>Group: <code>system:bootstrappers:kubeadm:default-node-token</code>

<ul>
<li>Bootstrap Token 自体は kubeadm が登録するのだが、Token登録時に extraGroups が設定でき、その際に Token で認証されたユーザはこのグループに所属されるように設定される。</li>
</ul>
</li>
</ul>


<p>以上、三つのリソースで認可制御されている。</p>

<p><code>CSR</code> は登録されても自動で approve され証明書が発行されるわけではなく、
管理者が手動で、<code>kubectl certificates approve</code> してやる必要がある。</p>

<p>が、以下のサブリソースの作成権限を与えておくと自動で approve されるらしい。</p>

<ul>
<li>certificatesigningrequests/nodeclient</li>
<li>certificatesigningrequests/selfnodeclient</li>
</ul>


<p>kubeadm は上記のサブリソース作成権限を以下の <code>ClusterRole</code> および <code>CluserRoleBinding</code> を作成することで、
証明書の発行および更新を自動で行われるようにしている。</p>

<ul>
<li>ClusterRole: <code>system:certificates.k8s.io:certificatesigningrequests:nodeclient</code>

<ul>
<li>v1.8 から Kubernetes 本体で自動で作成されるようになった。</li>
<li>certificatesigningrequests/nodeclient の作成権限を持つ Role</li>
</ul>
</li>
<li>ClusterRole: <code>system:certificates.k8s.io:certificatesigningrequests:selfnodeclient</code>

<ul>
<li>v1.8 から Kubernetes 本体で自動で作成されるようになった。</li>
<li>certificatesigningrequests/selfnodeclient の作成権限を持つ Role</li>
</ul>
</li>
<li>ClusterRoleBinding: <code>kubeadm:node-autoapprove-bootstrap</code>

<ul>
<li><code>system:bootstrappers:kubeadm:default-node-token</code> グループ (Bootstrap Token で認証されると自動で入るグループ) に上記 <code>system:certificates.k8s.io:certificatesigningrequests:nodeclient</code> を付与する。</li>
</ul>
</li>
<li>ClusterRoleBinding: <code>kubeadm:node-autoapprove-certificate-rotation</code>

<ul>
<li><code>system:nodes</code> グループに自動で <code>system:certificates.k8s.io:certificatesigningrequests:selfnodeclient</code> を付与する。</li>
</ul>
</li>
</ul>


<p>なので、kubeadm でクラスター作成したけど、
Token 使って勝手にワーカーノードを追加されるのは困るよ！と言う人は
<code>kubeadm:node-autoapprove-bootstrap</code> を
<a href="https://kubernetes.io/docs/reference/generated/kubeadm/#turning-off-auto-approval-of-node-client-certificates">削除すると良い</a>。</p>

<h3>クライアント証明書の取得</h3>

<p>一旦 approve された <code>CSR</code> は、そのリソースエンドポイントから<a href="https://kubernetes.io/docs/api-reference/v1.8/#certificatesigningrequestcondition-v1beta1-certificates">証明書を取得することができる</a>。</p>

<h2>まとめ</h2>

<p>と、言うことで以上で kubeadm を使って Kubelet がクラスターに繋がるためのエンドポイント情報と証明書をどうセットアップするのか、と言うことがわかった。</p>

<p>たかだか <code>kubeadm join</code> コマンドだろ、と思っていたが、
API のエンドポイントを発見して、
クラスターに Worker ノードが繋がるための証明書を取得するだけでも大変なんだなあ。</p>

    ]]></content:encoded>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2017-12-07T11:25:33+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2017/11/15/openstack-summit-sydney">
    <title>シドニー出張</title>
    <link>http://www.fraction.jp/log/archives/2017/11/15/openstack-summit-sydney</link>
    <description>今年の春に引き続き、オーストラリアはシドニーに出張してきた。今回の発表は初日だったためもあってか残りの二日は 羽を伸ばしてちゃんと仕事をしてきた。結局3人しか集まらなかったものの、OpenStacker を集めて早朝ランをするというイベントもこなし、仲良くなった中国人は HyperHQ の CTO というオマケもあってか現時点では未公開情報なども聞くことができて有意義だった。まあ、その未公開情報を知ってたとしても何もできないのだが…。まだ今のところ、自分の中でバンクーバーにはまだ劣るが、、なかなか...</description>
    <content:encoded><![CDATA[
        <p><a href="/log/archives/2017/05/14/openstack-summit-boston">今年の春に引き続き</a>、
オーストラリアはシドニーに出張してきた。</p>

<p class='image'>
<img  src='/log/2017/11/IMGP2866.JPG'
      width='720'
      height='480'
      alt=''  />
</p>


<p><a href="https://www.openstack.org/videos/sydney-2017/which-is-the-best-way-to-install-kubernetes-on-openstack">今回の発表</a>は初日だったためもあってか残りの二日は <del>羽を伸ばして</del>
ちゃんと仕事をしてきた。</p>

<p class='image'>
<img  src='/log/2017/11/IMG_4022.JPG'
      width='640'
      height='480'
      alt=''  />
</p>


<p>結局3人しか集まらなかったものの、OpenStacker を集めて早朝ランをするというイベントもこなし、
仲良くなった中国人は HyperHQ の CTO というオマケもあってか現時点では未公開情報なども聞くことができて有意義だった。
まあ、その未公開情報を知ってたとしても何もできないのだが…。</p>

<p class='image'>
<img  src='/log/2017/11/IMG_0413.JPG'
      width='640'
      height='480'
      alt=''  />
</p>




<p class='image'>
<img  src='/log/2017/11/IMGP2969.JPG'
      width='720'
      height='480'
      alt=''  />
</p>




<p class='image'>
<img  src='/log/2017/11/IMGP2921.JPG'
      width='720'
      height='480'
      alt=''  />
</p>




<p class='image'>
<img  src='/log/2017/11/IMGP2990.JPG'
      width='720'
      height='480'
      alt=''  />
</p>




<p class='image'>
<img  src='/log/2017/11/IMG_2405.JPG'
      width='640'
      height='480'
      alt=''  />
</p>




<p class='image'>
<img  src='/log/2017/11/IMG_0556.JPG'
      width='640'
      height='480'
      alt=''  />
</p>


<p>まだ今のところ、自分の中で<a href="/log/archives/2015/05/25/openstack-summit-vancouver">バンクーバー</a>
にはまだ劣るが、、なかなかいい場所だった。シドニー、今度は家族で行きたい。</p>

    ]]></content:encoded>
    <dc:subject>日記</dc:subject>
    <dc:subject>Program/Python</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2017-11-15T16:15:35+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2017/10/25/openstack-jp-study-group">
    <title>OpenStack ユーザ会 第36回勉強会</title>
    <link>http://www.fraction.jp/log/archives/2017/10/25/openstack-jp-study-group</link>
    <description>OpenStack ユーザ会 第36回勉強会に参加。っつーか発表してきた。タイトルは「Kubernetes on Kunernetes on OpenStack」。内容は Self-Hosted Kubernetes の紹介と、OpenStack 上で Self-Hosted K8s をデプロイする自作ツールの紹介。...</description>
    <content:encoded><![CDATA[
        <p><a href="https://openstack.jp/archives/465">OpenStack ユーザ会 第36回勉強会</a>に参加。っつーか発表してきた。
タイトルは「<a href="https://docs.google.com/presentation/d/1VKk89MaNkGRSlpBsOOHJt8cLD6mpZ5V55GEJqIDu2Sk/edit#slide=id.g28eb8071bc_0_1119">Kubernetes on Kunernetes on OpenStack</a>」。</p>

<p class='image'>
<img  src='/log/2017/10/Image-uploaded-from-iOS-3.jpg'
      width='480'
      height='640'
      alt=''  />
</p>


<p>内容は Self-Hosted Kubernetes の紹介と、
OpenStack 上で Self-Hosted K8s をデプロイする自作ツールの紹介。</p>

    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2017-10-25T23:34:30+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2017/10/12/self-hosted-kubernetes">
    <title>Self-Hosted Kubernetes</title>
    <link>http://www.fraction.jp/log/archives/2017/10/12/self-hosted-kubernetes</link>
    <description>自宅の Minnowboard クラスターに Kubernetes をインストールしようとしてそろそろ一年。。。CoreOS on Minnowboardetcd3 on CoreOSMinnowboard using SSDGlusterFS on Kubernetes思ったよりも時間がかかってしまったがようやくクラスターを構成できた。当初思ってたように全て Minnowboard に載せるというのは実際にアプリを乗せた際、(gitlab やら Gluster) メモリが問題になりそうだったので...</description>
    <content:encoded><![CDATA[
        <p>自宅の Minnowboard クラスターに Kubernetes をインストールしようとしてそろそろ一年。。。</p>

<ul>
<li><a href="/log/archives/2017/03/17/coreos-on-minnowboard">CoreOS on Minnowboard</a></li>
<li><a href="/log/archives/2017/03/30/etcd3-on-coreos">etcd3 on CoreOS</a></li>
<li><a href="/log/archives/2017/03/31/minnowboard-using-ssd">Minnowboard using SSD</a></li>
<li><a href="/log/archives/2017/07/31/glusterfs-on-k8s">GlusterFS on Kubernetes</a></li>
</ul>


<p>思ったよりも時間がかかってしまったがようやくクラスターを構成できた。
当初思ってたように全て Minnowboard に載せるというのは実際にアプリを乗せた際、
(gitlab やら Gluster) メモリが問題になりそうだったので、
結局ワーカーノードは ECS LIVA Z の Pentium モデルと相成った。</p>

<p class='image'>
<img  src='/log/2017/10/IMG_6335.JPG'
      width='480'
      height='640'
      alt=''  />
</p>


<p>ワーカーノードには USB でストレージを繋いで <a href="http://www.gluster.org">Gluster</a>
で管理。今流行りのハイパーコンバージド？
結局 Gluster 公式のプロビジョナーである <a href="/log/archives/2017/07/31/glusterfs-on-k8s">Heketi に不満を感じた</a>
ので<a href="https://github.com/kubernetes-incubator/external-storage/tree/master/gluster/glusterfs">自分でプロビジョナーを書いてしまった</a>。
その話を書こう書こうと思っていたのだけどめんどくさくてブログには書いておらず。</p>

<p class='image'>
<img  src='/log/2017/10/k8s-screen.png'
      width='640'
      height='783'
      alt=''  />
</p>


<p>なぜ自宅 Kubernetes クラスターの構築にこんなに時間がかかってしまったかというと、
主に Kubernetes のデプロイに使うスクリプトに意外に時間を食ってしまったから。</p>

<p>Kubespray やら Kops やら kubeadm やら Rancher やらを色々検討はしてみたものの、
自分好みのクラスターを構築しようと思ったら自分で構築するのが一番という身もふたもない結論に。
これも結局<a href="https://github.com/nec-openstack/remora">デプロイツールを自分で書いた</a>。</p>

<ul>
<li><a href="https://github.com/nec-openstack/remora">Remora -- command line tool and library to manage Kubernetes</a></li>
</ul>


<p>最初は <a href="https://github.com/kelseyhightower/kubernetes-the-hard-way">Kubernetes The Hard Way</a>
の内容を スクリプト化して、k8s 各コンポーネントの ClusterRoleBinding をちゃんと設定してあげたもの程度だったのだが、
Self-Hosted Kubernetes 化して keepalived/haproxy で冗長化、
DNS に CoreDNS 採用とかしてたら手間取った。</p>

<p>Self-Hosted は面白かったので次の <a href="https://openstack-jp.connpass.com/event/68475/">日本OpenStackユーザ会 第36回勉強会</a>
で話す予定。もし枠が空いていたら次次回の Kubernetes Meetup の LT でも。</p>

<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//rcm-fe.amazon-adsystem.com/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=bonnohfract00-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=as_ss_li_til&amp;asins=B01MS4M6NT&amp;linkId=81679af9cdcad9b5f069ac235faacbcf"></iframe>


<iframe style="width:120px;height:240px;" marginwidth="0" marginheight="0" scrolling="no" frameborder="0" src="//rcm-fe.amazon-adsystem.com/e/cm?lt1=_blank&amp;bc1=000000&amp;IS2=1&amp;bg1=FFFFFF&amp;fc1=000000&amp;lc1=0000FF&amp;t=bonnohfract00-22&amp;o=9&amp;p=8&amp;l=as4&amp;m=amazon&amp;f=ifr&amp;ref=as_ss_li_til&amp;asins=B072MK19SR&amp;linkId=9382280ddf2f510a199ecd3e854e305d"></iframe>


    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2017-10-12T14:34:30+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2017/07/31/glusterfs-on-k8s">
    <title>GlusterFS on Kubernetes</title>
    <link>http://www.fraction.jp/log/archives/2017/07/31/glusterfs-on-k8s</link>
    <description>自宅の Minnowboard クラスターに Kubernetes をインストールしようとしてはや半年。CoreOS on Minnowboardetcd3 on CoreOSMinnowboard using SSDなぜかずっとコンテナのボリュームどうしようか、、と悩んで止まってましたが、ようやく目処がついてきたので進捗を。GlusterFSとりあえずボリュームには GlusterFS を使うことにした。NFS、という選択肢もあったが可用性を担保しようとするとストレージを RAID で構成しなけ...</description>
    <content:encoded><![CDATA[
        <p>自宅の Minnowboard クラスターに Kubernetes をインストールしようとしてはや半年。</p>

<ul>
<li><a href="/log/archives/2017/03/17/coreos-on-minnowboard">CoreOS on Minnowboard</a></li>
<li><a href="/log/archives/2017/03/30/etcd3-on-coreos">etcd3 on CoreOS</a></li>
<li><a href="/log/archives/2017/03/31/minnowboard-using-ssd">Minnowboard using SSD</a></li>
</ul>


<p>なぜかずっとコンテナのボリュームどうしようか、、と悩んで止まってましたが、
ようやく目処がついてきたので進捗を。</p>

<p class='image'>
<img  src='/log/2017/07/pr-gluster-simple-provisioner.png'
      width='700'
      height='495'
      alt=''  />
</p>


<h2>GlusterFS</h2>

<p>とりあえずボリュームには GlusterFS を使うことにした。
NFS、という選択肢もあったが可用性を担保しようとするとストレージを RAID で構成しなければならなかったり(RAID はなんか好きじゃない。)、
結局 NFS Server が SPOF になるんじゃないかという点が気に食わなかった。
もちろん、NFS Server を Act-Standby にしたり最近は Failover できるらしいが、
とりあえず無視する。なんとなく Gluster が使いたかった。</p>

<p>Gluster を使えば、</p>

<ul>
<li>Replicate を使えば RAID0 っぽいことがソフトウェアレベルでできる。</li>
<li>容量が足りなくなったら単純にサーバ/ディスク追加で対処できる。</li>
<li>ディスク故障による交換が簡単そう。</li>
</ul>


<p>すごい！</p>

<h3>使い方</h3>

<p>動的に Gluster のボリュームをプロビジョニングしてコンテナのボリュームとして使う方法もあるのだけど、
どうも微妙なので、とりあえずはすでに存在する Gluster のボリュームを Kubernetes の
Persistent Volume として登録する方法。</p>

<ol>
<li>Endpoint の作成</li>
<li>Service の作成</li>
<li>Persistent Volume の作成</li>
</ol>


<p>ぶっちゃけ kubernetes/examples の
<a href="https://github.com/kubernetes/examples/blob/master/staging/volumes/glusterfs/README.md">GlusterFS</a>
の内容そのままだが。</p>

<h3>Endpoint の作成</h3>

<p>ボリュームを提供する Gluster Cluster のエンドポイント情報を Kubernetes に教えるために、
<code>Endpoint</code> を作る必要がある。</p>

<pre><code>kind: Endpoints
apiVersion: v1
metadata:
  name: glusterfs-cluster
subsets:
- addresses:
  - ip: "192.168.1.111"
  ports:
  - port: 1
- addresses:
  - ip: "192.168.1.112"
  ports:
  - port: 1</code></pre>


<p><code>subsets</code> の <code>addresses</code> にそれぞれ GlusterFS Cluster のノードのアドレスを書く。
<code>ports</code> 内の <code>port</code> は <code>1-65535</code> 以内の数値ならなんでもいいらしい。</p>

<h3>Service の作成</h3>

<p>作った <code>Endpoint</code> を永続化するために関連する <code>Service</code> を作っておく必要があるらしいが、
具体的にはどういう意味があるのかわかっていない。
とりあえず <code>Service</code> を作らずに <code>Endpoint</code> だけで運用しているが今の所困ったことはないが…。</p>

<pre><code>kind: Service
apiVersion: v1
metadata:
  name: glusterfs-cluster
spec:
  ports:
  - port: 1</code></pre>


<p>普通の <code>Service</code> と違うところは <code>selector</code> が無いところで、
そのおかげでKubernetes はこの <code>Service</code> に関連する <code>Endpoint</code> を勝手にいじったりすることがなくなる。</p>

<h3>Persistent Volume の作成</h3>

<p><code>Endpoint</code> さえ作ってしまえばあとは <code>PV</code> を作れば良いだけ。</p>

<pre><code>kind: PersistentVolume
apiVersion: v1
metadata:
  name: gluster-volume
spec:
  accessModes:
  - ReadWriteMany
  capacity:
    storage: 2Gi
  glusterfs:
    endpoints: glusterfs-cluster
    path: test-volume
    readOnly: false</code></pre>


<ul>
<li><strong>endpoints</strong>: 上記で作成した <code>Endpoint</code> の名前。</li>
<li><strong>path</strong>: path とあるが事前に作成した Gluster Volume の名前。</li>
<li><strong>readOnly</strong>: 読み込み専用にしたければどうぞ。</li>
</ul>


<p>とりあえず、これを PV の数だけ一個一個手作業でやって行くのは面倒。</p>

    ]]></content:encoded>
    <dc:subject>Linux</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2017-07-31T21:33:30+09:00</dc:date>
  </item>
  <item rdf:about="http://www.fraction.jp/log/archives/2017/05/14/openstack-summit-boston">
    <title>ボストン出張</title>
    <link>http://www.fraction.jp/log/archives/2017/05/14/openstack-summit-boston</link>
    <description>去年秋に引き続き、アメリカはマサチューセッツ、ボストンに出張してきた。さすがにバブルは去ったのか、OpenStack 関連で海外出張するのもこれで最後かな。(まあ、前回も言ってるけど。)...</description>
    <content:encoded><![CDATA[
        <p><a href="/log/archives/2016/10/31/openstack-summit-barcelona">去年秋に引き続き</a>、
アメリカはマサチューセッツ、ボストンに出張してきた。</p>

<p class='image'>
<img  src='/log/2017/05/IMG_5783.JPG'
      width='640'
      height='480'
      alt=''  />
</p>


<p>さすがにバブルは去ったのか、OpenStack 関連で海外出張するのもこれで最後かな。
(まあ、前回も言ってるけど。)</p>

    ]]></content:encoded>
    <dc:subject>日記</dc:subject>
    <dc:subject>Program/Python</dc:subject>
    <dc:subject>Program/技術</dc:subject>
    <dc:creator>Yuanying</dc:creator>
    <dc:date>2017-05-14T16:04:09+09:00</dc:date>
  </item>
</rdf:RDF>
