forked from kairen/kairen.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
571 lines (349 loc) · 339 KB
/
atom.xml
File metadata and controls
571 lines (349 loc) · 339 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>KaiRen's Blog</title>
<icon>https://www.gravatar.com/avatar/9156bf89edf2ea0f74c01bae4a478fff</icon>
<subtitle>KaiRen's Blog</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://kairen.github.io/"/>
<updated>2018-06-25T09:09:53.790Z</updated>
<id>https://kairen.github.io/</id>
<author>
<name>Kyle Bai</name>
<email>kyle.b@inwinstack.com</email>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>Prometheus Operator 介紹與安裝</title>
<link href="https://kairen.github.io/2018/06/23/devops/prometheus-operator/"/>
<id>https://kairen.github.io/2018/06/23/devops/prometheus-operator/</id>
<published>2018-06-23T04:23:01.000Z</published>
<updated>2018-06-25T09:09:53.790Z</updated>
<content type="html"><![CDATA[<p><a href="https://github.com/coreos/prometheus-operator" target="_blank" rel="noopener">Prometheus Operator</a> 是 CoreOS 開源的一套用於管理在 Kubernetes 上的 Prometheus 控制器,目標當然就是簡化部署與維護 Prometheus 上的事情,其架構如下所示:</p><p><img src="https://coreos.com/sites/default/files/inline-images/p1.png" alt=""></p><a id="more"></a><p>架構中的每一個部分都執行於 Kubernetes 的資源,這些資源分別負責不同作用與意義:</p><ul><li><strong><a href="https://coreos.com/operators/" target="_blank" rel="noopener">Operator</a></strong>:Operator 是整個系統的主要控制器,會以 Deployment 方式執行於 Kubernetes 叢集上,並根據自定義的資源(Custom Resource Definition,CRDs)來負責管理與部署 Prometheus Server。而 Operator 會透過監聽這些自定義資源的事件變化來做對應處理。</li><li><strong>Prometheus Server</strong>:由 Operator 依據一個自定義資源 Prometheus 類型中,所描述的內容而部署的 Prometheus Server 叢集,可以將這個自定義資源看作是一種特別用來管理 Prometheus Server 的 StatefulSets 資源。</li></ul><pre><code class="yaml=">apiVersion: monitoring.coreos.com/v1kind: Prometheusmetadata: name: k8s labels: prometheus: k8sspec: version: v2.3.0 replicas: 2 serviceMonitors: - selector: matchLabels: k8s-app: kubelet...</code></pre><ul><li><strong>ServiceMonitor</strong>:一個 Kubernetes 自定義資源,該資源描述了 Prometheus Server 的 Target 列表,Operator 會監聽這個資源的變化來動態的更新 Prometheus Server 的 Scrape targets。而該資源主要透過 Selector 來依據 Labels 選取對應的 Service Endpoint,並讓 Prometheus Server 透過 Service 進行拉取(Pull) Metrics 資料。</li></ul><pre><code class="yaml=">apiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: kubelet labels: k8s-app: kubeletspec: jobLabel: k8s-app endpoints: - port: cadvisor interval: 30s # scrape the endpoint every 10 seconds honorLabels: true selector: matchLabels: k8s-app: kubelet namespaceSelector: matchNames: - kube-system</code></pre><blockquote><p>這是一個抓取 Cadvisor metrics 的範例。</p></blockquote><ul><li><p><strong>Service</strong>:Kubernetes 中的 Service 資源,這邊主要用來對應 Kubernetes 中 Metrics Server Pod,然後提供給 ServiceMonitor 選取讓 Prometheus Server 拉取資料。在 Prometheus 術語中,可以稱為 Target,即被 Prometheus 監測的對象,如一個部署在 Kubernetes 上的 Node Exporter Service。</p></li><li><p><strong>Alertmanager</strong>:Prometheus Operator 不只提供 Prometheus Server 管理與部署,也包含了 AlertManager,並且一樣透過一個 Alertmanager 自定義資源來描述資訊,再由 Operator 依據描述內容部署 Alertmanager 叢集。</p></li></ul><pre><code class="yaml=">apiVersion: monitoring.coreos.com/v1kind: Alertmanagermetadata: name: main labels: alertmanager: mainspec: replicas: 3...</code></pre><h2 id="部署-Prometheus-Operator"><a href="#部署-Prometheus-Operator" class="headerlink" title="部署 Prometheus Operator"></a>部署 Prometheus Operator</h2><p>本節將說明如何部署 Prometheus Operator 來管理 Kubernetes 上的 Prometheus 資源。</p><h3 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h3><p>測試環境將需要一套 Kubernetes 叢集,作業系統採用<code>Ubuntu 16.04 Server</code>,測試環境為實體機器:</p><table><thead><tr><th>IP Address</th><th>Role</th><th>vCPU</th><th>RAM</th></tr></thead><tbody><tr><td>172.22.132.10</td><td>k8s-m1</td><td>8</td><td>16G</td></tr><tr><td>172.22.132.11</td><td>k8s-n1</td><td>8</td><td>16G</td></tr><tr><td>172.22.132.12</td><td>k8s-n2</td><td>8</td><td>16G</td></tr></tbody></table><blockquote><p>這邊<code>m</code> 為 K8s master,<code>n</code>為 K8s node。</p></blockquote><h3 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h3><p>開始安裝前需要確保以下條件已達成:</p><ul><li><p>所有節點以 kubeadm 部署成 Kubernetes v1.9+ 叢集。請參考 <a href="https://kairen.github.io/2016/09/29/kubernetes/deploy/kubeadm/">用 kubeadm 部署 Kubernetes 叢集</a>。</p></li><li><p>在 Kubernetes 叢集部署 Helm 與 Tiller server。</p></li></ul><pre><code class="shell=">$ wget -qO- https://kubernetes-helm.storage.googleapis.com/helm-v2.8.1-linux-amd64.tar.gz | tar -zx$ sudo mv linux-amd64/helm /usr/local/bin/$ kubectl -n kube-system create sa tiller$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller$ helm init --service-account tiller</code></pre><ul><li>在<code>k8s-m1</code>透過 kubectl 來建立 Ingress Controller 即可:</li></ul><pre><code class="shell=">$ kubectl create ns ingress-nginx$ wget https://kairen.github.io/files/manual-v1.10/addon/ingress-controller.yml.conf -O ingress-controller.yml$ sed -i ingress-controller.yml 's/192.16.35.10/172.22.132.10/g'$ kubectl apply -f ingress-controller.yml.conf</code></pre><h3 id="部署-Prometheus-Operator-1"><a href="#部署-Prometheus-Operator-1" class="headerlink" title="部署 Prometheus Operator"></a>部署 Prometheus Operator</h3><p>Prometheus Operator 提供了多種方式部署至 Kubernetes 上,一般會使用手動(or 腳本)與 Helm 來進行部署。</p><h4 id="手動-腳本-部署"><a href="#手動-腳本-部署" class="headerlink" title="手動(腳本)部署"></a>手動(腳本)部署</h4><p>透過 Git 取得最新版本腳本:</p><pre><code class="shell=">$ git clone https://github.com/camilb/prometheus-kubernetes.git$ cd prometheus-kubernetes</code></pre><p>接著執行<code>deploy</code>腳本來部署到 Kubernetes:</p><pre><code class="shell=">$ ./deployCheck for uncommitted changesOK! No uncommitted changes detectedCreating 'monitoring' namespace.Error from server (AlreadyExists): namespaces "monitoring" already exists1) AWS2) GCP3) Azure4) CustomPlease select your cloud provider:4Deploying on custom providers without persistenceSetting components versionEnter Prometheus Operator version [v0.19.0]:Enter Prometheus version [v2.2.1]:Enter Prometheus storage retention period in hours [168h]:Enter Prometheus storage volume size [40Gi]:Enter Prometheus memory request in Gi or Mi [1Gi]:Enter Grafana version [5.1.1]:Enter Alert Manager version [v0.15.0-rc.1]:Enter Node Exporter version [v0.16.0-rc.3]:Enter Kube State Metrics version [v1.3.1]:Enter Prometheus external Url [http://127.0.0.1:9090]:Enter Alertmanager external Url [http://127.0.0.1:9093]:Do you want to use NodeSelector to assign monitoring components on dedicated nodes?Y/N [N]:Do you want to set up an SMTP relay?Y/N [N]:Do you want to set up slack alerts?Y/N [N]:# 這邊會跑一下部署階段,完成後要接著輸入一些資訊,如 Grafana username and passwdEnter Grafana administrator username [admin]:Enter Grafana administrator password: ******...Done</code></pre><blockquote><p>沒有輸入部分請直接按<code>Enter</code>。</p></blockquote><p>當確認看到 Done 後就可以查看 <code>monitoring</code> namespace:</p><pre><code class="shell=">$ kubectl -n monitoring get poNAME READY STATUS RESTARTS AGEalertmanager-main-0 2/2 Running 0 4malertmanager-main-1 2/2 Running 0 3malertmanager-main-2 2/2 Running 0 3mgrafana-568b569696-nltbh 2/2 Running 0 14skube-state-metrics-86467959c6-kxtl4 2/2 Running 0 3mnode-exporter-526nw 1/1 Running 0 4mnode-exporter-c828w 1/1 Running 0 4mnode-exporter-r2qq2 1/1 Running 0 4mnode-exporter-s25x6 1/1 Running 0 4mnode-exporter-xpgh7 1/1 Running 0 4mprometheus-k8s-0 1/2 Running 0 10sprometheus-k8s-1 2/2 Running 0 10sprometheus-operator-f596c68cf-wrpqc 1/1 Running 0 4m</code></pre><p>查看 Kubernetes CRDs 與 SM:</p><pre><code class="shell=">$ kubectl -n monitoring get crdNAME AGEalertmanagers.monitoring.coreos.com 4mprometheuses.monitoring.coreos.com 4mservicemonitors.monitoring.coreos.com 4m$ kubectl -n monitoring get servicemonitorsNAME AGEalertmanager 1mkube-apiserver 1mkube-controller-manager 1mkube-dns 1mkube-scheduler 1mkube-state-metrics 1mkubelet 1mnode-exporter 1mprometheus 1mprometheus-operator 1m</code></pre><p>接著修改 Service 的 Grafana 的 Type:</p><pre><code class="shell=">$ kubectl -n monitoring edit svc grafana# 修改成 NodePort</code></pre><blockquote><p>也可以建立 Ingress 來存取 Grafana。</p><pre><code class="yaml=">apiVersion: extensions/v1beta1kind: Ingressmetadata: namespace: monitoring name: grfana-ingress annotations: ingress.kubernetes.io/rewrite-target: /spec: rules: - host: grafana.k8s-local.k2r2bai.com http: paths: - path: / backend: serviceName: grafana servicePort: 3000</code></pre></blockquote><p>:::info<br>這邊也可以建立 Prometheus Ingress 來使用 Web-based console。</p><pre><code class="yaml=">apiVersion: extensions/v1beta1kind: Ingressmetadata: namespace: monitoring name: prometheus-ingress annotations: ingress.kubernetes.io/rewrite-target: /spec: rules: - host: prometheus.k8s-local.k2r2bai.com http: paths: - path: / backend: serviceName: prometheus-k8s servicePort: 9090</code></pre><p>:::</p><p>最後就可以存取 Grafana 來查看 Metric 視覺化資訊了。</p><p><img src="https://i.imgur.com/39G6Zsm.png" alt=""></p><h4 id="Helm"><a href="#Helm" class="headerlink" title="Helm"></a>Helm</h4><p>首先透過 Helm 加入 coreos 的 repo:</p><pre><code class="shell=">$ helm repo add coreos https://s3-eu-west-1.amazonaws.com/coreos-charts/stable/</code></pre><p>然後透過 kubectl 建立一個 Namespace 來管理 Prometheus,並用 Helm 部署 Prometheus Operator:</p><pre><code class="shell=">$ kubectl create namespace monitoring$ helm install coreos/prometheus-operator \ --name prometheus-operator \ --set rbacEnable=true \ --namespace=monitoring</code></pre><p>接著部署 Prometheus、AlertManager 與 Grafana:</p><pre><code class="shell="># Prometheus$ helm install coreos/prometheus --name prometheus \ --set serviceMonitorsSelector.app=prometheus \ --set ruleSelector.app=prometheus \ --namespace=monitoring# Alert Manager$ helm install coreos/alertmanager --name alertmanager --namespace=monitoring# Grafana$ helm install coreos/grafana --name grafana --namespace=monitoring</code></pre><p>部署 kube-prometheus 來提供 Kubernetes 監測的 Exporter 與 ServiceMonitor:</p><pre><code class="shell=">$ helm install coreos/kube-prometheus --name kube-prometheus --namespace=monitoring</code></pre><p>完成後檢查安裝結果:</p><pre><code class="shell=">$ kubectl -n monitoring get po,svcNAME READY STATUS RESTARTS AGEpod/alertmanager-alertmanager-0 2/2 Running 0 1mpod/alertmanager-kube-prometheus-0 2/2 Running 0 31spod/grafana-grafana-77cfcdff66-jwxfp 2/2 Running 0 1mpod/kube-prometheus-exporter-kube-state-56857b596f-knt8q 1/2 Running 0 21spod/kube-prometheus-exporter-kube-state-844bb6f589-n7xfg 1/2 Running 0 31spod/kube-prometheus-exporter-node-665kc 1/1 Running 0 31spod/kube-prometheus-exporter-node-bjvbx 1/1 Running 0 31spod/kube-prometheus-exporter-node-j8jf8 1/1 Running 0 31spod/kube-prometheus-exporter-node-pxn8p 1/1 Running 0 31spod/kube-prometheus-exporter-node-vft8b 1/1 Running 0 31spod/kube-prometheus-grafana-57d5b4d79f-lq5cr 1/2 Running 0 31spod/prometheus-kube-prometheus-0 3/3 Running 1 29spod/prometheus-operator-d75587d6-qhz4h 1/1 Running 0 2mpod/prometheus-prometheus-0 3/3 Running 1 1mNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/alertmanager ClusterIP 10.99.170.79 <none> 9093/TCP 1mservice/alertmanager-operated ClusterIP None <none> 9093/TCP,6783/TCP 1mservice/grafana-grafana ClusterIP 10.100.217.27 <none> 80/TCP 1mservice/kube-prometheus ClusterIP 10.102.165.173 <none> 9090/TCP 31sservice/kube-prometheus-alertmanager ClusterIP 10.99.221.122 <none> 9093/TCP 32sservice/kube-prometheus-exporter-kube-state ClusterIP 10.100.233.129 <none> 80/TCP 32sservice/kube-prometheus-exporter-node ClusterIP 10.97.183.222 <none> 9100/TCP 32sservice/kube-prometheus-grafana ClusterIP 10.110.134.52 <none> 80/TCP 32sservice/prometheus ClusterIP 10.105.229.141 <none> 9090/TCP 1mservice/prometheus-operated ClusterIP None <none> 9090/TCP 1m</code></pre>]]></content>
<summary type="html">
<p><a href="https://github.com/coreos/prometheus-operator" target="_blank" rel="noopener">Prometheus Operator</a> 是 CoreOS 開源的一套用於管理在 Kubernetes 上的 Prometheus 控制器,目標當然就是簡化部署與維護 Prometheus 上的事情,其架構如下所示:</p>
<p><img src="https://coreos.com/sites/default/files/inline-images/p1.png" alt=""></p>
</summary>
<category term="DevOps" scheme="https://kairen.github.io/categories/DevOps/"/>
<category term="DevOps" scheme="https://kairen.github.io/tags/DevOps/"/>
<category term="Monitoring" scheme="https://kairen.github.io/tags/Monitoring/"/>
<category term="CNCF" scheme="https://kairen.github.io/tags/CNCF/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
</entry>
<entry>
<title>Prometheus 介紹與基礎入門</title>
<link href="https://kairen.github.io/2018/06/10/devops/prometheus-intro/"/>
<id>https://kairen.github.io/2018/06/10/devops/prometheus-intro/</id>
<published>2018-06-10T04:23:01.000Z</published>
<updated>2018-06-19T09:55:30.756Z</updated>
<content type="html"><![CDATA[<p>Prometheus 是一套開放式原始碼的<code>系統監控警報框架</code>與<code>TSDB(Time Series Database)</code>,該專案是由 SoundCloud 的工程師(前 Google 工程師)建立,Prometheus 啟發於 Google 的 Borgmon 監控系統。目前 Prometheus 已貢獻到 CNCF 成為孵化專案(2016-),其受歡迎程度僅次於 Kubernetes。</p><a id="more"></a><p>Prometheus 具備了以下特性:</p><ul><li>多維度資料模型<ul><li>時間序列資料透過 Metric 名稱與鍵值(Key-value)來區分。</li><li>所有 Metrics 可以設定任意的多維標籤。</li><li>資料模型彈性度高,不需要刻意設定為以特定符號(ex: ,)分割。</li><li>可對資料模型進行聚合、切割與切片操作。</li><li>支援雙精度浮點數類型,標籤可以設定成 Unicode。</li></ul></li><li>靈活的查詢語言(PromQL),如可進行加減乘除等。</li><li>不依賴分散式儲存,因為 Prometheus Server 是一個二進制檔,可在單個服務節點自主運行。</li><li>基於 HTTP 的 Pull 方式收集時序資料。</li><li>可以透過 Push Gateway 進行資料推送。</li><li>支援多種視覺化儀表板呈現,如 Grafana。</li><li>能透過服務發現(Service discovery)或靜態組態去獲取監控的 Targets。</li></ul><h2 id="Prometheus-架構"><a href="#Prometheus-架構" class="headerlink" title="Prometheus 架構"></a>Prometheus 架構</h2><p><img src="https://i.imgur.com/iJKoxdD.png" alt=""></p><p>Prometheus 生態圈中是由多個元件組成,其中有些是選擇性的元件:</p><ul><li><strong>Prometheus Server</strong>:收集與儲存時間序列資料,並提供 PromQL 查詢語言支援。</li><li><strong>Client Library</strong>:客戶端函式庫,提供語言開發來開發產生 Metrics 並曝露 Prometheus Server。當 Prometheus Server 來 Pull 時,直接返回即時狀態的 Metrics。</li><li><strong>Pushgateway</strong>:主要用於臨時性 Job 推送。這類 Job 存在期間較短,有可能 Prometheus 來 Pull 時就消失,因此透過一個閘道來推送。適合用於服務層面的 Metrics。</li><li><strong>Exporter</strong>:用來曝露已有第三方服務的 Metrics 給 Prometheus Server,即以 Client Library 開發的 HTTP server。</li><li><strong>AlertManager</strong>:接收來至 Prometheus Server 的 Alert event,並依據定義的 Notification 組態發送警報,ex: E-mail、Pagerduty、OpenGenie 與 Webhook 等等。</li></ul><h2 id="Prometheus-運作流程"><a href="#Prometheus-運作流程" class="headerlink" title="Prometheus 運作流程"></a>Prometheus 運作流程</h2><ol><li>Prometheus Server 定期從組態好的 Jobs 或者 Exporters 中拉取 Metrics,或者接收來自 Pushgateway 發送的 Metrics,又或者從其他的 Prometheus Server 中拉取 Metrics。</li><li>Prometheus Server 在 Local 儲存收集到的 Metrics,並運行已定義好的 alert.rules,然後紀錄新時間序列或者像 AlertManager 發送警報。</li><li>AlertManager 根據組態檔案來對接受到的 Alert event 進行處理,然後發送警報。</li><li>在視覺化介面呈現採集資料。</li></ol><p>Prometheus Server 拉取 Exporter 資料,然後透過 PromQL 語法進行查詢,再將資料給 Web UI or Dashboard。<br><img src="https://i.imgur.com/QkwEVge.png" alt=""></p><p>Prometheus Server 觸發 Alert Definition 定義的事件,並發送給 AelertManager。<br><img src="https://i.imgur.com/6V3RJOh.png" alt=""></p><p>AlertManager 依據設定發送警報給 E-mail、Slack 等等。<br><img src="https://i.imgur.com/mB789G2.png" alt=""></p><h2 id="Prometheus-資料模型與-Metric-類型"><a href="#Prometheus-資料模型與-Metric-類型" class="headerlink" title="Prometheus 資料模型與 Metric 類型"></a>Prometheus 資料模型與 Metric 類型</h2><p>本節將介紹 Prometheus 的資料模型與 Metrics 類型。</p><h3 id="資料模型"><a href="#資料模型" class="headerlink" title="資料模型"></a>資料模型</h3><p>Prometheus 儲存的資料為時間序列,主要以 Metrics name 以及一系列的唯一標籤(key-value)組成,不同標籤表示不同時間序列。模型資訊如下:</p><ul><li><strong>Metrics Name</strong>:該名稱通常用來表示 Metric 功能,例如 <code>http_requests_total</code>,即表示 HTTP 請求的總數。而 Metrics Name 是以 ASCII 字元、數字、英文、底線與冒號組成,並且要滿足<code>[a-zA-Z_:][a-zA-Z0-9_:]*</code> 正規表示法。</li><li><strong>標籤</strong>:用來識別同一個時間序列不同維度。如 <code>http_request_total{method="Get"}</code>表示所有 HTTP 的 Get Request 數量,因此當 <code>method="Post"</code> 時又是另一個新的 Metric。標籤也需要滿足<code>[a-zA-Z_:][a-zA-Z0-9_:]*</code> 正規表示法。</li><li><strong>樣本</strong>:實際的時間序列,每個序列包含一個 float64 值與一個毫秒的時間戳。</li><li><strong>格式</strong>:一般為<code><metric name>{<label name>=<label value>,...}</code>,例如:<code>http_requests_total{method="POST",endpoint="/api/tracks"}</code>。</li></ul><h3 id="Metrics-類型"><a href="#Metrics-類型" class="headerlink" title="Metrics 類型"></a>Metrics 類型</h3><p>Prometheus Client 函式庫支援了四種主要 Metric 類型:</p><ul><li><strong>Counter</strong>: 可被累加的 Metric,比如一個 HTTP Get 錯誤的出現次數。</li><li><strong>Gauge</strong>: 屬於瞬時、與時間無關的任意更動 Metric,如記憶體使用率。</li><li><strong>Histogram</strong>: 主要使用在表示一段時間範圍內的資料採樣。</li><li><strong>Summary</strong>: 類似 Histogram,用來表示一端時間範圍內的資料採樣總結。</li></ul><h2 id="Job-與-Instance"><a href="#Job-與-Instance" class="headerlink" title="Job 與 Instance"></a>Job 與 Instance</h2><p>Prometheus 中會將任意獨立資料來源(Target)稱為 Instance。而包含多個相同 Instance 的集合稱為 Job。如以下範例:</p><pre><code class="yml">- job: api-server - instance 1: 1.2.3.4:5670 - instance 2: 1.2.3.4:5671 - instance 3: 5.6.7.8:5670 - instance 4: 5.6.7.8:5671</code></pre><ul><li><strong>Instance</strong>: 被抓取目標 URL 的<code><host>:<port></code>部分。</li><li><strong>Job</strong>: 一個同類型的 Instances 集合。(主要確保可靠性與擴展性)</li></ul><h2 id="Prometheus-簡單部署與使用"><a href="#Prometheus-簡單部署與使用" class="headerlink" title="Prometheus 簡單部署與使用"></a>Prometheus 簡單部署與使用</h2><p>Prometheus 官方提供了已建構完成的二進制執行檔可以下載,只需要至 <a href="https://prometheus.io/download/" target="_blank" rel="noopener">Download</a> 頁面下載即可。首先下載符合作業系統的檔案,這邊以 Linux 為例:</p><pre><code class="sh">$ wget https://github.com/prometheus/prometheus/releases/download/v2.3.0/prometheus-2.3.0.linux-amd64.tar.gz$ tar xvfz prometheus-*.tar.gz$ tree prometheus-2.3.0.linux-amd64├── console_libraries # Web console templates│ ├── menu.lib│ └── prom.lib├── consoles # Web console templates│ ├── index.html.example│ ├── node-cpu.html│ ├── node-disk.html│ ├── node.html│ ├── node-overview.html│ ├── prometheus.html│ └── prometheus-overview.html├── LICENSE├── NOTICE├── prometheus # Prometheus 執行檔├── prometheus.yml # Prometheus 設定檔└── promtool # 2.x+ 版本用來將一些 rules 格式轉成 YAML 用。</code></pre><p>解壓縮完成後,編輯<code>prometheus.yml</code>檔案來調整設定:</p><pre><code class="yml">global: scrape_interval: 15s # 設定預設 scrape 的拉取間隔時間 external_labels: # 外通溝通時標示在 time series 或 Alert 的 Labels。 monitor: 'codelab-monitor'scrape_configs: # 設定 scrape jobs - job_name: 'prometheus' scrape_interval: 5s # 若設定間隔時間,將會覆蓋 global 的預設時間。 static_configs: - targets: ['localhost:9090']</code></pre><p>完成後,直接執行 prometheus 檔案來啟動伺服器:</p><pre><code class="sh">$ ./prometheus --config.file=prometheus.yml --storage.tsdb.path /tmp/data...level=info ts=2018-06-19T08:46:37.42756438Z caller=main.go:500 msg="Server is ready to receive web requests."</code></pre><blockquote><p><code>--storage.tsdb.path</code> 預設會直接存放在<code>./data</code>底下。</p></blockquote><p>啟動後就可以瀏覽 <code>:9090</code> 來查看 Web-based console。</p><p><img src="https://i.imgur.com/qgi39CC.png" alt=""></p><p>另外也可以進入 <code>:9090/metrics</code> 查看 Export metrics 資訊,並且可以在 console 來查詢指定 Metrics,並以圖表呈現。</p><p><img src="https://i.imgur.com/Rv6XW6f.png" alt=""></p><p>Prometheus 提供了 <a href="https://prometheus.io/docs/prometheus/latest/querying/basics/" target="_blank" rel="noopener">Functional Expression Language</a> 進行查詢與聚合時間序列資料,比如用<code>sum(http_requests_total{method="GET"} offset 5m)</code>來查看指定時間的資訊總和。</p><p>Prometheus 提供拉取第三方或者自己開發的 Exporter metrics 作為監測資料,這邊可以透過簡單的 <a href="https://github.com/prometheus/client_golang.git" target="_blank" rel="noopener">Go Client</a> 範例來簡單部署 Exporter:</p><pre><code class="sh">$ git clone https://github.com/prometheus/client_golang.git$ cd client_golang/examples/random$ go get -d$ go build</code></pre><p>完成後,開啟三個 Terminals 分別啟動以下 Exporter:</p><pre><code class="sh"># terminal 1$ ./random -listen-address=:8081# terminal 2$ ./random -listen-address=:8082# terminal 3$ ./random -listen-address=:8083</code></pre><blockquote><p>啟動後可以在<code>:8081</code>等 Ports 中查看 Metrics 資訊。</p></blockquote><p>確定沒問題後,修改<code>prometheus.yml</code>來新增 target,並重新啟動 Prometheus Server:</p><pre><code class="yaml">global: scrape_interval: 15s # 設定預設 scrape 的拉取間隔時間 external_labels: # 外通溝通時標示在 time series 或 Alert 的 Labels。 monitor: 'codelab-monitor'scrape_configs: # 設定 scrape jobs - job_name: 'prometheus' scrape_interval: 5s # 若設定間隔時間,將會覆蓋 global 的預設時間。 static_configs: - targets: ['localhost:9090'] - job_name: 'example-random' scrape_interval: 5s static_configs: - targets: ['localhost:8080', 'localhost:8081'] labels: group: 'production' - targets: ['localhost:8082'] labels: group: 'canary'</code></pre><p>啟動完成後,就可以 Web-console 的 Execute 執行以下來查詢:</p><pre><code class="sh">avg(rate(rpc_durations_seconds_count[5m])) by (job, service)</code></pre><p><img src="https://i.imgur.com/Bo7YGo5.png" alt=""></p><p>另外 Prometheus 也提供自定義 Group rules 來將指定的 Expression query 當作一個 Metric,這邊建立一個檔案<code>prometheus.rules.yml</code>,並新增以下內容:</p><pre><code class="yaml">groups:- name: example rules: - record: job_service:rpc_durations_seconds_count:avg_rate5m expr: avg(rate(rpc_durations_seconds_count[5m])) by (job, service)</code></pre><p>接著修改<code>prometheus.yml</code>加入以下內容,並重新啟動 Prometheus Server:</p><pre><code class="yaml">global: ...scrape_configs: ...rule_files: - 'prometheus.rules.yml'</code></pre><blockquote><p><code>global</code> 與 <code>scrape_configs</code> 不做任何修改,只需加入<code>rule_files</code>即可,另外注意檔案路徑位置。</p></blockquote><p>正常啟動後,就可以看到新的 Metric 被加入。<br><img src="https://i.imgur.com/LhKcGVK.png" alt=""></p>]]></content>
<summary type="html">
<p>Prometheus 是一套開放式原始碼的<code>系統監控警報框架</code>與<code>TSDB(Time Series Database)</code>,該專案是由 SoundCloud 的工程師(前 Google 工程師)建立,Prometheus 啟發於 Google 的 Borgmon 監控系統。目前 Prometheus 已貢獻到 CNCF 成為孵化專案(2016-),其受歡迎程度僅次於 Kubernetes。</p>
</summary>
<category term="DevOps" scheme="https://kairen.github.io/categories/DevOps/"/>
<category term="DevOps" scheme="https://kairen.github.io/tags/DevOps/"/>
<category term="Monitoring" scheme="https://kairen.github.io/tags/Monitoring/"/>
<category term="CNCF" scheme="https://kairen.github.io/tags/CNCF/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
</entry>
<entry>
<title>以 Keystone 作為 Kubernetes 使用者認證</title>
<link href="https://kairen.github.io/2018/05/30/kubernetes/k8s-integration-keystone/"/>
<id>https://kairen.github.io/2018/05/30/kubernetes/k8s-integration-keystone/</id>
<published>2018-05-30T09:08:54.000Z</published>
<updated>2018-06-05T07:35:48.431Z</updated>
<content type="html"><![CDATA[<p>本文章將說明如何整合 Keystone 來提供給 Kubernetes 進行使用者認證。但由於 Keystone 整合 Kubernetes 認證在 1.10.x 版本已從原生移除(<code>--experimental-keystone-url</code>, <code>--experimental-keystone-ca-file</code>),並轉而使用 <a href="https://github.com/kubernetes/cloud-provider-openstack" target="_blank" rel="noopener">cloud-provider-openstack</a> 中的 Webhook 來達成,而篇將說明如何建置與設定以整合該 Webhook。</p><a id="more"></a><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>本教學將以下列節點數與規格來進行部署 Kubernetes 叢集,作業系統以<code>Ubuntu 16.x</code>進行測試:</p><table><thead><tr><th>IP Address</th><th>Hostname</th><th>CPU</th><th>Memory</th></tr></thead><tbody><tr><td>172.22.132.20</td><td>k8s</td><td>4</td><td>8G</td></tr><tr><td>172.22.132.21</td><td>keystone</td><td>4</td><td>8G</td></tr></tbody></table><blockquote><ul><li><code>k8s</code>為 all-in-one Kubernetes 節點(就只是個執行 kubeadm init 的節點)。</li><li><code>keystone</code>利用 DevStack 部署一台 all-in-one OpenStack。</li></ul></blockquote><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>開始安裝前需要確保以下條件已達成:</p><ul><li><p><code>k8s</code>節點以 kubeadm 部署成 Kubernetes v1.9+ all-in-one 環境。請參考 <a href="https://kairen.github.io/2016/09/29/kubernetes/deploy/kubeadm/">用 kubeadm 部署 Kubernetes 叢集</a>。</p></li><li><p>在<code>k8s</code>節點安裝 openstack-client:</p></li></ul><pre><code class="sh">$ sudo apt-get update && sudo apt-get install -y python-pip$ export LC_ALL=C; sudo pip install python-openstackclient</code></pre><ul><li><code>keystone</code>節點部署成 OpenStack all-in-one 環境。請參考 <a href="https://docs.openstack.org/devstack/latest/" target="_blank" rel="noopener">DevStack</a>。</li></ul><h2 id="Kubernetes-與-Keystone-整合"><a href="#Kubernetes-與-Keystone-整合" class="headerlink" title="Kubernetes 與 Keystone 整合"></a>Kubernetes 與 Keystone 整合</h2><p>本節將逐節說明如何設定以整合 Keystone。</p><h3 id="建立-Keystone-User-與-Roles"><a href="#建立-Keystone-User-與-Roles" class="headerlink" title="建立 Keystone User 與 Roles"></a>建立 Keystone User 與 Roles</h3><p>當<code>keystone</code>節點的 OpenStack 部署完成後,進入到節點建立測試用 User 與 Roles:</p><pre><code class="sh">$ sudo su - stack$ cd devstack$ source openrc admin admin# 建立 Roles$ for role in "k8s-admin" "k8s-viewer" "k8s-editor"; do openstack role create $role; done# 建立 User$ openstack user create demo_editor --project demo --password secret$ openstack user create demo_admin --project demo --password secret# 加入 User 至 Roles$ openstack role add --user demo --project demo k8s-viewer$ openstack role add --user demo_editor --project demo k8s-editor$ openstack role add --user demo_admin --project demo k8s-admin</code></pre><h3 id="在-Kubernetes-安裝-Keystone-Webhook"><a href="#在-Kubernetes-安裝-Keystone-Webhook" class="headerlink" title="在 Kubernetes 安裝 Keystone Webhook"></a>在 Kubernetes 安裝 Keystone Webhook</h3><p>進入<code>k8s</code>節點,首先導入下載的檔案來源:</p><pre><code class="sh">$ export URL="https://kairen.github.io/files/openstack/keystone"</code></pre><p>新增一些腳本,來提供導入不同使用者環境變數給 OpenStack Client 使用:</p><pre><code class="sh">$ export KEYSTONE_HOST="172.22.132.21"$ export USER_PASSWORD="secret"$ for n in "admin" "demo" "demoadmin" "demoeditor" "altdemo"; do wget ${URL}/openrc-${n} -O ~/openrc-${n} sed -i "s/KEYSTONE_HOST/${KEYSTONE_HOST}/g" ~/openrc-${n} sed -i "s/USER_PASSWORD/${USER_PASSWORD}/g" ~/openrc-${n} done</code></pre><p>下載 Keystone Webhook Policy 檔案,然後執行指令修改內容:</p><pre><code class="sh">$ sudo wget ${URL}/webhook-policy.json -O /etc/kubernetes/webhook-policy.json$ source ~/openrc-demo$ PROJECT_ID=$(openstack project list | awk '/demo/ {print$2}')$ sudo sed -i "s/PROJECT_ID/${PROJECT_ID}/g" /etc/kubernetes/webhook-policy.json</code></pre><p>然後下載與部署 Keystone Webhook YAML 檔:</p><pre><code class="sh">$ wget ${URL}/keystone-webhook-ds.conf -O keystone-webhook-ds.yml$ KEYSTONE_HOST="172.22.132.21"$ sed -i "s/KEYSTONE_HOST/${KEYSTONE_HOST}/g" keystone-webhook-ds.yml$ kubectl create -f keystone-webhook-ds.ymlconfigmap "keystone-webhook-kubeconfig" createddaemonset.apps "keystone-auth-webhook" created</code></pre><p>透過 kubectl 確認 Keystone Webhook 是否部署成功:</p><pre><code class="sh">$ kubectl -n kube-system get po -l component=k8s-keystoneNAME READY STATUS RESTARTS AGEkeystone-auth-webhook-5qqwn 1/1 Running 0 1m</code></pre><p>透過 cURL 確認是否能夠正確存取:</p><pre><code class="sh">$ source ~/openrc-demo$ TOKEN=$(openstack token issue -f yaml -c id | awk '{print $2}')$ cat << EOF | curl -kvs -XPOST -d @- https://localhost:8443/webhook | python -mjson.tool{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "metadata": { "creationTimestamp": null }, "spec": { "token": "$TOKEN" }}EOF# output{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "metadata": { "creationTimestamp": null }, "spec": { "token": "gAAAAABbFi1SacEPNstSuSuiBXiBG0Y_DikfbiR75j3P-CJ8CeaSKXa5kDQvun4LZUq8U6ehuW_RrQwi-N7j8t086uN6a4hLnPPGmvc6K_Iw0BZHZps7G1R5WniHZ8-WTUxtkMJROSz9eG7m33Bp18mvgx-P179QiwNYxLivf_rjnxePmvujNow" }, "status": { "authenticated": true, "user": { "extra": { "alpha.kubernetes.io/identity/project/id": [ "3ebcb1da142d427db04b8df43f6cb76a" ], "alpha.kubernetes.io/identity/project/name": [ "demo" ], "alpha.kubernetes.io/identity/roles": [ "k8s-viewer", "Member", "anotherrole" ] }, "groups": [ "3ebcb1da142d427db04b8df43f6cb76a" ], "uid": "19748c0131504b87a4117e49c67383c6", "username": "demo" } }}</code></pre><h3 id="設定-kube-apiserver-使用-Webhook"><a href="#設定-kube-apiserver-使用-Webhook" class="headerlink" title="設定 kube-apiserver 使用 Webhook"></a>設定 kube-apiserver 使用 Webhook</h3><p>進入<code>k8s</code>節點,然後修改<code>/etc/kubernetes/manifests/kube-apiserver.yaml</code>檔案,加入以下內容:</p><pre><code class="yml">...spec: containers: - command: ... # authorization-mode 加入 Webhook - --authorization-mode=Node,RBAC,Webhook - --runtime-config=authentication.k8s.io/v1beta1=true - --authentication-token-webhook-config-file=/srv/kubernetes/webhook-auth - --authorization-webhook-config-file=/srv/kubernetes/webhook-auth - --authentication-token-webhook-cache-ttl=5m volumeMounts: ... - mountPath: /srv/kubernetes/webhook-auth name: webhook-auth-file readOnly: true volumes: ... - hostPath: path: /srv/kubernetes/webhook-auth type: File name: webhook-auth-file</code></pre><p>完成後重新啟動 kubelet(或者等待 static pod 自己更新):</p><pre><code class="sh">$ sudo systemctl restart kubelet</code></pre><h2 id="驗證部署結果"><a href="#驗證部署結果" class="headerlink" title="驗證部署結果"></a>驗證部署結果</h2><p>進入<code>k8s</code>節點,然後設定 kubectl context 並使用 openstack provider:</p><pre><code class="sh">$ kubectl config set-credentials openstack --auth-provider=openstack$ kubectl config \ set-context --cluster=kubernetes \ --user=openstack \ openstack@kubernetes \ --namespace=default$ kubectl config use-context openstack@kubernetes</code></pre><p>測試 demo 使用者的存取權限是否有被限制:</p><pre><code class="sh">$ source ~/openrc-demo$ kubectl get podsNo resources found.$ cat <<EOF | kubectl create -f -apiVersion: v1kind: Podmetadata: name: nginx-podspec: restartPolicy: Never containers: - image: nginx name: nginx-appEOF# outputError from server (Forbidden): error when creating "STDIN": pods is forbidden: User "demo" cannot create pods in the namespace "default"</code></pre><blockquote><p>由於 demo 只擁有 k8s-viewer role,因此只能進行 get, list 與 watch API。</p></blockquote><p>測試 demo_editor 使用者是否能夠建立 Pod:</p><pre><code class="sh">$ source ~/openrc-demoeditor$ cat <<EOF | kubectl create -f -apiVersion: v1kind: Podmetadata: name: nginx-podspec: restartPolicy: Never containers: - image: nginx name: nginx-appEOF# outputpod "nginx-pod" created</code></pre><blockquote><p>這邊可以看到 demo_editor 因為擁有 k8s-editor role,因此能夠執行 create API。</p></blockquote><p>測試 alt_demo 是否被禁止存取任何 API:</p><pre><code class="sh">$ source ~/openrc-altdemo$ kubectl get poError from server (Forbidden): pods is forbidden: User "alt_demo" cannot list pods in the namespace "default"</code></pre><blockquote><p>由於 alt_demo 不具備任何 roles,因此無法存取任何 API。</p></blockquote>]]></content>
<summary type="html">
<p>本文章將說明如何整合 Keystone 來提供給 Kubernetes 進行使用者認證。但由於 Keystone 整合 Kubernetes 認證在 1.10.x 版本已從原生移除(<code>--experimental-keystone-url</code>, <code>--experimental-keystone-ca-file</code>),並轉而使用 <a href="https://github.com/kubernetes/cloud-provider-openstack" target="_blank" rel="noopener">cloud-provider-openstack</a> 中的 Webhook 來達成,而篇將說明如何建置與設定以整合該 Webhook。</p>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="Keystone" scheme="https://kairen.github.io/tags/Keystone/"/>
</entry>
<entry>
<title>在 AWS 上建立跨地區的 Kubernetes Federation 叢集</title>
<link href="https://kairen.github.io/2018/04/21/kubernetes/aws-k8s-federation/"/>
<id>https://kairen.github.io/2018/04/21/kubernetes/aws-k8s-federation/</id>
<published>2018-04-21T09:08:54.000Z</published>
<updated>2018-04-26T03:52:52.475Z</updated>
<content type="html"><![CDATA[<p>本篇延續先前 On-premises Federation 與 Kops 經驗來嘗試在 AWS 上建立 Federaion 叢集,這邊架構如下圖所示:</p><p><img src="/images/kops-fed/fed-clusters.png" alt=""></p><a id="more"></a><p>本次安裝的軟體版本:</p><ul><li>Kubernetes v1.9.3</li><li>kops v1.9.0</li><li>kubefed v1.10</li></ul><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>測試環境為 AWS EC2 虛擬機器,共有三組叢集:</p><p>US West(Oregon) 叢集,也是 Federation 控制平面叢集:</p><table><thead><tr><th>Host</th><th>vCPU</th><th>RAM</th></tr></thead><tbody><tr><td>us-west-m1</td><td>1</td><td>2G</td></tr><tr><td>us-west-n1</td><td>1</td><td>2G</td></tr><tr><td>us-west-n2</td><td>1</td><td>2G</td></tr></tbody></table><p>US East(Ohio) 叢集:</p><table><thead><tr><th>Host</th><th>vCPU</th><th>RAM</th></tr></thead><tbody><tr><td>us-east-m1</td><td>1</td><td>2G</td></tr><tr><td>us-east-n1</td><td>1</td><td>2G</td></tr><tr><td>us-east-n2</td><td>1</td><td>2G</td></tr></tbody></table><p>Asia Pacific(Tokyo) 叢集:</p><table><thead><tr><th>Host</th><th>vCPU</th><th>RAM</th></tr></thead><tbody><tr><td>ap-northeast-m1</td><td>1</td><td>2G</td></tr><tr><td>ap-northeast-n1</td><td>1</td><td>2G</td></tr><tr><td>ap-northeast-n2</td><td>1</td><td>2G</td></tr></tbody></table><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>開始前,需要先安裝下列工具到操作機器上來提供使用:</p><ul><li><a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" target="_blank" rel="noopener">kubectl</a>:用來操作部署完成的 Kubernetes 叢集。</li><li><a href="https://github.com/kubernetes/kops" target="_blank" rel="noopener">kops</a>:用來部署與管理公有雲上的 Kubernetes 叢集。</li></ul><p>Mac OS X:</p><pre><code class="sh">$ brew update && brew install kops</code></pre><p>Linux distro:</p><pre><code class="sh">$ curl -LO https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64$ chmod +x kops-linux-amd64 && sudo mv kops-linux-amd64 /usr/local/bin/kops</code></pre><ul><li><a href="https://github.com/kubernetes/federation" target="_blank" rel="noopener">kubefed</a>:用來建立 Federation 控制平面與管理 Federation 叢集的工具。</li></ul><p>Mac OS X:</p><pre><code class="sh">$ git clone https://github.com/kubernetes/federation.git $GOPATH/src/k8s.io/federation$ cd $GOPATH/src/k8s.io/federation$ make quick-release$ cp _output/dockerized/bin/linux/amd64/kubefed /usr/local/bin/kubefed</code></pre><p>Linux distro:</p><pre><code class="sh">$ wget https://storage.googleapis.com/kubernetes-federation-release/release/v1.9.0-alpha.3/federation-client-linux-amd64.tar.gz$ tar xvf federation-client-linux-amd64.tar.gz$ cp federation/client/bin/kubefed /usr/local/bin/$ kubefed versionClient Version: version.Info{Major:"1", Minor:"9+", GitVersion:"v1.9.0-alpha.3", GitCommit:"85c06145286da663755b140efa2b65f793cce9ec", GitTreeState:"clean", BuildDate:"2018-02-14T12:54:40Z", GoVersion:"go1.9.1", Compiler:"gc", Platform:"linux/amd64"}Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.6", GitCommit:"9f8ebd171479bec0ada837d7ee641dec2f8c6dd1", GitTreeState:"clean", BuildDate:"2018-03-21T15:13:31Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}</code></pre><ul><li><a href="https://aws.amazon.com/cli/?nc1=h_ls" target="_blank" rel="noopener">AWS CLI</a>:用來操作 AWS 服務的工具。</li></ul><pre><code class="sh">$ sudo pip install awscli$ aws --versionaws-cli/1.15.4</code></pre><p>上述工具完成後,我們還要準備一下資訊:</p><ul><li>申請 AWS 帳號,並在 IAM 服務新增一個 User 設定存取所有服務(AdministratorAccess)。另外這邊要記住 AccessKey 與 SecretKey。<blockquote><p>一般來說只需開啟 S3、Route53、EC2、EBS、ELB 與 VPC 就好,但由於偷懶就全開。以下為各 AWS 服務在本次安裝的用意:</p><ul><li>IAM: 提供身份認證與存取管理。</li><li>EC2: Kubernetes 叢集部署的虛擬機環境。</li><li>ELB: Kubernetes 元件與 Service 負載平衡。</li><li>Route53: 提供 Public domain 存取 Kubernetes 環境。</li><li>S3: 儲存 Kops 狀態。</li><li>VPC: 提供 Kubernetes 與 EC2 的網路環境。</li></ul></blockquote></li></ul><p><img src="/images/kops/iam-user2.png" alt=""></p><ul><li>擁有自己的 Domain Name,這邊可以在 AWS Route53 註冊,或者是到 GoDaddy 購買。</li></ul><h2 id="部署-Kubernetes-Federation-叢集"><a href="#部署-Kubernetes-Federation-叢集" class="headerlink" title="部署 Kubernetes Federation 叢集"></a>部署 Kubernetes Federation 叢集</h2><p>本節將說明如何利用自己撰寫好的腳本 <a href="https://github.com/kairen/aws-k8s-federation" target="_blank" rel="noopener">aws-k8s-federation</a> 來部署 Kubernetes 叢集與 Federation 叢集。首先在操作節點下載:</p><pre><code class="sh">$ git clone https://github.com/kairen/aws-k8s-federation$ cd aws-k8s-federation$ cp .env.sample .env</code></pre><p>編輯<code>.env</code>檔案來提供後續腳本的環境變數:</p><pre><code class="sh"># 你的 Domain Name(這邊為 <hoste_dzone_name>.<domain_name>)export DOMAIN_NAME="k8s.example.com"# Regions and zonesexport US_WEST_REGION="us-west-2"export US_EAST_REGION="us-east-2"export AP_NORTHEAST_REGION="ap-northeast-1"export ZONE="a"# Cluster contexts nameexport FED_CONTEXT="aws-fed"export US_WEST_CONTEXT="us-west.${DOMAIN_NAME}"export US_EAST_CONTEXT="us-east.${DOMAIN_NAME}"export AP_NORTHEAST_CONTEXT="ap-northeast.${DOMAIN_NAME}"# S3 buckets nameexport US_WEST_BUCKET_NAME="us-west-k8s"export US_EAST_BUCKET_NAME="us-east-k8s"export AP_NORTHEAST_BUCKET_NAME="ap-northeast-k8s"# Get domain name idexport HOSTED_ZONE_ID=$(aws route53 list-hosted-zones \ | jq -r '.HostedZones[] | select(.Name=="'${DOMAIN_NAME}'.") | .Id' \ | sed 's/\/hostedzone\///')# Kubernetes master and node size, and node count.export MASTER_SIZE="t2.micro"export NODE_SIZE="t2.micro"export NODE_COUNT="2"# Federation simple apps deploy and service nameexport DNS_RECORD_PREFIX="nginx"export SERVICE_NAME="nginx"</code></pre><h3 id="建立-Route53-Hosted-Zone"><a href="#建立-Route53-Hosted-Zone" class="headerlink" title="建立 Route53 Hosted Zone"></a>建立 Route53 Hosted Zone</h3><p>首先透過 aws 工具進行設定使用指定 AccessKey 與 SecretKey:</p><pre><code class="sh">$ aws configureAWS Access Key ID [****************QGEA]:AWS Secret Access Key [****************zJ+w]:Default region name [None]:Default output format [None]:</code></pre><blockquote><p>設定的 Keys 可以在<code>~/.aws/credentials</code>找到。</p></blockquote><p>接著需要在 Route53 建立一個 Hosted Zone,並在 Domain Name 供應商上設定 <code>NameServers</code>:</p><pre><code class="sh">$ ./0-create-hosted-domain.sh# output...{ "HostedZone": { "ResourceRecordSetCount": 2, "CallerReference": "2018-04-25-16:16", "Config": { "PrivateZone": false }, "Id": "/hostedzone/Z2JR49ADZ0P3WC", "Name": "k8s.example.com." }, "DelegationSet": { "NameServers": [ "ns-1547.awsdns-01.co.uk", "ns-1052.awsdns-03.org", "ns-886.awsdns-46.net", "ns-164.awsdns-20.com" ] }, "Location": "https://route53.amazonaws.com/2013-04-01/hostedzone/Z2JR49ADZ0P3WC", "ChangeInfo": { "Status": "PENDING", "SubmittedAt": "2018-04-25T08:16:57.462Z", "Id": "/change/C3802PE0C1JVW2" }}</code></pre><p>之後將上述<code>NameServers</code>新增至自己的 Domain name 的 record 中,如 Godaddy:</p><p><img src="/images/kops-fed/godday-ns.png" alt=""></p><h3 id="在每個-Region-建立-Kubernetes-叢集"><a href="#在每個-Region-建立-Kubernetes-叢集" class="headerlink" title="在每個 Region 建立 Kubernetes 叢集"></a>在每個 Region 建立 Kubernetes 叢集</h3><p>當 Hosted Zone 建立完成後,就可以接著建立每個 Region 的 Kubernetes 叢集,這邊腳本已包含建立叢集與 S3 Bucket 指令,因此只需要執行以下腳本即可:</p><pre><code class="sh">$ ./1-create-clusters.sh....Cluster is starting. It should be ready in a few minutes....</code></pre><blockquote><p>這邊會需要等待一點時間進行初始化與部署,也可以到 AWS Console 查看狀態。</p></blockquote><p>完成後,即可透過 kubectl 來操作叢集:</p><pre><code class="sh">$ ./us-east/kc get no+ kubectl --context=us-east.k8s.example.com get noNAME STATUS ROLES AGE VERSIONip-172-20-43-26.us-east-2.compute.internal Ready node 1m v1.9.3ip-172-20-56-167.us-east-2.compute.internal Ready master 3m v1.9.3ip-172-20-63-133.us-east-2.compute.internal Ready node 2m v1.9.3$ ./ap-northeast/kc get no+ kubectl --context=ap-northeast.k8s.example.com get noNAME STATUS ROLES AGE VERSIONip-172-20-42-184.ap-northeast-1.compute.internal Ready master 2m v1.9.3ip-172-20-52-176.ap-northeast-1.compute.internal Ready node 20s v1.9.3ip-172-20-56-88.ap-northeast-1.compute.internal Ready node 22s v1.9.3$ ./us-west/kc get no+ kubectl --context=us-west.k8s.example.com get noNAME STATUS ROLES AGE VERSIONip-172-20-33-22.us-west-2.compute.internal Ready node 1m v1.9.3ip-172-20-55-237.us-west-2.compute.internal Ready master 2m v1.9.3ip-172-20-63-77.us-west-2.compute.internal Ready node 35s v1.9.3</code></pre><h3 id="建立-Kubernetes-Federation-叢集"><a href="#建立-Kubernetes-Federation-叢集" class="headerlink" title="建立 Kubernetes Federation 叢集"></a>建立 Kubernetes Federation 叢集</h3><p>當三個地區的叢集建立完成後,接著要在 US West 的叢集上部署 Federation 控制平面元件:</p><pre><code class="sh">$ ./2-init-federation.sh...Federation API server is running at: abba6864f490111e8b4bd028106a7a79-793027324.us-west-2.elb.amazonaws.com$ ./us-west/kc -n federation-system get po+ kubectl --context=us-west.k8s.example.com -n federation-system get poNAME READY STATUS RESTARTS AGEapiserver-5d46898995-tmzvl 2/2 Running 0 1mcontroller-manager-6cc78c68d5-2pbg5 0/1 Error 3 1m</code></pre><p>這邊會發現<code>controller-manager</code>會一直掛掉,這是因為它需要取得 AWS 相關權限,因此需要透過 Patch 方式來把 AccessKey 與 SecretKey 注入到 Deployment 中:</p><pre><code class="sh">$ ./3-path-federation.shSwitched to context "us-west.k8s.example.com".deployment "controller-manager" patched$ ./us-west/kc -n federation-system get po+ kubectl --context=us-west.k8s.example.com -n federation-system get poNAME READY STATUS RESTARTS AGEapiserver-5d46898995-tmzvl 2/2 Running 0 3mcontroller-manager-769bd95fbc-dkssr 1/1 Running 0 21s</code></pre><p>確認上述沒問題後,透過 kubectl 確認 contexts:</p><pre><code class="sh">$ kubectl config get-contextsCURRENT NAME CLUSTER AUTHINFO NAMESPACE ap-northeast.k8s.example.com ap-northeast.k8s.example.com ap-northeast.k8s.example.com aws-fed aws-fed aws-fed us-east.k8s.example.com us-east.k8s.example.com us-east.k8s.example.com* us-west.k8s.example.com us-west.k8s.example.com us-west.k8s.example.com</code></pre><p>接著透過以下腳本來加入<code>us-west</code>叢集至 aws-fed 的 Federation 中:</p><pre><code class="sh">$ ./4-join-us-west.sh+ kubectl config use-context aws-fedSwitched to context "aws-fed".+ kubefed join us-west --host-cluster-context=us-west.k8s.example.com --cluster-context=us-west.k8s.example.comcluster "us-west" created</code></pre><p>加入<code>ap-northeast</code>叢集至 aws-fed 的 Federation 中:</p><pre><code class="sh">$ ./5-join-ap-northeast.sh+ kubectl config use-context aws-fedSwitched to context "aws-fed".+ kubefed join ap-northeast --host-cluster-context=us-west.k8s.example.com --cluster-context=ap-northeast.k8s.example.comcluster "ap-northeast" created</code></pre><p>加入<code>us-east</code>叢集至 aws-fed 的 Federation 中:</p><pre><code class="sh">$ ./6-join-us-east.sh+ kubectl config use-context aws-fedSwitched to context "aws-fed".+ kubefed join us-east --host-cluster-context=us-west.k8s.example.com --cluster-context=us-east.k8s.example.comcluster "us-east" created</code></pre><p>完成後,在 Federation 建立 Federated Namespace,並列出叢集:</p><pre><code class="sh">$ ./7-create-fed-ns.sh+ kubectl --context=aws-fed create namespace defaultnamespace "default" created+ kubectl --context=aws-fed get clustersNAME AGEap-northeast 2mus-east 1mus-west 2m</code></pre><p>完成這些過程表示你已經建立了一套 Kubernetes Federation 叢集了,接下來就可以進行測試。</p><h2 id="測試叢集"><a href="#測試叢集" class="headerlink" title="測試叢集"></a>測試叢集</h2><p>首先建立一個簡單的 Nginx 來提供服務的測試,這邊可以透過以下腳本達成:</p><pre><code class="sh">$ ./8-deploy-fed-nginx.sh+ cat+ kubectl --context=aws-fed apply -f -deployment "nginx" created+ cat+ kubectl --context=aws-fed apply -f -service "nginx" created$ kubectl get deploy,svcNAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGEdeploy/nginx 3 3 3 3 3mNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEsvc/nginx LoadBalancer <none> a4d86547a4903... 80/TCP 2m</code></pre><blockquote><p>這裡的 nginx deployment 有設定<code>deployment-preferences</code>,因此在 scale 時會依據下面資訊來分配:</p><pre><code class="sh">{ "rebalance": true, "clusters": { "us-west": { "minReplicas": 2, "maxReplicas": 10, "weight": 200 }, "us-east": { "minReplicas": 0, "maxReplicas": 2, "weight": 150 }, "ap-northeast": { "minReplicas": 1, "maxReplicas": 5, "weight": 150 } } }</code></pre></blockquote><p>檢查每個叢集的 Pod:</p><pre><code class="sh"># us-west context(這邊策略為 2 - 10)$ ./us-west/kc get po+ kubectl --context=us-west.k8s.example.com get poNAME READY STATUS RESTARTS AGEnginx-679dc9c764-4x78c 1/1 Running 0 3mnginx-679dc9c764-fzv9z 1/1 Running 0 3m# us-east context(這邊策略為 0 - 2)$ ./us-east/kc get po+ kubectl --context=us-east.k8s.example.com get poNo resources found.# ap-northeast context(這邊策略為 1 - 5)$ ./ap-northeast/kc get po+ kubectl --context=ap-northeast.k8s.example.com get poNAME READY STATUS RESTARTS AGEnginx-679dc9c764-hmwzq 1/1 Running 0 4m</code></pre><p>透過擴展副本數來查看分配狀況:</p><pre><code class="sh">$ ./9-scale-fed-nginx.sh+ kubectl --context=aws-fed scale deploy nginx --replicas=10deployment "nginx" scaled$ kubectl get deployNAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGEnginx 10 10 10 10 8m</code></pre><p>再次檢查每個叢集的 Pod:</p><pre><code class="sh"># us-west context(這邊策略為 2 - 10)$ ./us-west/kc get po+ kubectl --context=us-west.k8s.example.com get poNAME READY STATUS RESTARTS AGEnginx-679dc9c764-4x78c 1/1 Running 0 8mnginx-679dc9c764-7958k 1/1 Running 0 50snginx-679dc9c764-fzv9z 1/1 Running 0 8mnginx-679dc9c764-j6kc9 1/1 Running 0 50snginx-679dc9c764-t6rvj 1/1 Running 0 50s# us-east context(這邊策略為 0 - 2)$ ./us-east/kc get po+ kubectl --context=us-east.k8s.example.com get poNAME READY STATUS RESTARTS AGEnginx-679dc9c764-8t7qz 1/1 Running 0 1mnginx-679dc9c764-zvqmx 1/1 Running 0 1m# ap-northeast context(這邊策略為 1 - 5)$ ./ap-northeast/kc get po+ kubectl --context=ap-northeast.k8s.example.com get poNAME READY STATUS RESTARTS AGEnginx-679dc9c764-f79v7 1/1 Running 0 1mnginx-679dc9c764-hmwzq 1/1 Running 0 9mnginx-679dc9c764-vj7hb 1/1 Running 0 1m</code></pre><blockquote><p>可以看到結果符合我們預期範圍內。</p></blockquote><p>最後因為服務是透過 ELB 來提供,為了統一透過 Domain name 存取相同服務,這邊更新 Hosted Zone Record 來轉發:</p><pre><code class="sh">$ ./10-update-fed-nginx-record.sh</code></pre><p>完成後透過 cURL 工作來測試:</p><pre><code class="sh">$ curl nginx.k8s.example.com...<title>Welcome to nginx!</title>...</code></pre><p>最後透過該腳本來清楚叢集與 AWS 服務上建立的東西:</p><pre><code class="sh">$ ./99-purge.sh</code></pre>]]></content>
<summary type="html">
<p>本篇延續先前 On-premises Federation 與 Kops 經驗來嘗試在 AWS 上建立 Federaion 叢集,這邊架構如下圖所示:</p>
<p><img src="/images/kops-fed/fed-clusters.png" alt=""></p>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="AWS" scheme="https://kairen.github.io/tags/AWS/"/>
<category term="Kops" scheme="https://kairen.github.io/tags/Kops/"/>
<category term="Federation" scheme="https://kairen.github.io/tags/Federation/"/>
</entry>
<entry>
<title>使用 Kops 部署 Kubernetes 至公有雲(AWS)</title>
<link href="https://kairen.github.io/2018/04/18/kubernetes/deploy/kops-aws/"/>
<id>https://kairen.github.io/2018/04/18/kubernetes/deploy/kops-aws/</id>
<published>2018-04-18T09:08:54.000Z</published>
<updated>2018-04-25T10:15:06.464Z</updated>
<content type="html"><![CDATA[<p><a href="https://github.com/kubernetes/kops" target="_blank" rel="noopener">Kops</a> 是 Kubernetes 官方維護的專案,是一套 Production ready 的 Kubernetes 部署、升級與管理工具,早期用於 AWS 公有雲上建置 Kubernetes 叢集使用,但隨著社群的推進已支援 GCP、vSphere(Alpha),未來也會有更多公有雲平台慢慢被支援(Maybe)。本篇簡單撰寫使用 Kops 部署一個叢集,過去自己因為公司都是屬於建置 On-premises 的 Kubernetes,因此很少使用 Kops,剛好最近社群分享又再一次接觸的關析,所以就來寫個文章。</p><p>本次安裝的軟體版本:</p><ul><li>Kubernetes v1.9.3</li><li>Kops v1.9.0</li></ul><a id="more"></a><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>開始使用 Kops 前,需要先安裝下列工具到操作機器上來提供使用:</p><ul><li><a href="https://kubernetes.io/docs/tasks/tools/install-kubectl/" target="_blank" rel="noopener">kubectl</a>:用來操作部署完成的 Kubernetes 叢集。</li><li><a href="https://github.com/kubernetes/kops" target="_blank" rel="noopener">kops</a>:本次使用工具,用來部署與管理公有雲上的 Kubernetes 叢集。</li></ul><p>Mac OS X:</p><pre><code class="sh">$ brew update && brew install kops</code></pre><p>Linux distro:</p><pre><code class="sh">$ curl -LO https://github.com/kubernetes/kops/releases/download/$(curl -s https://api.github.com/repos/kubernetes/kops/releases/latest | grep tag_name | cut -d '"' -f 4)/kops-linux-amd64$ chmod +x kops-linux-amd64 && sudo mv kops-linux-amd64 /usr/local/bin/kops</code></pre><ul><li><a href="https://aws.amazon.com/cli/?nc1=h_ls" target="_blank" rel="noopener">AWS CLI</a>:用來操作 AWS 服務的工具。</li></ul><pre><code class="sh">$ sudo pip install awscli$ aws --versionaws-cli/1.15.4</code></pre><p>上述工具完成後,我們還要準備一下資訊:</p><ul><li>申請 AWS 帳號,並在 IAM 服務新增一個 User 設定存取所有服務(AdministratorAccess)。另外這邊要記住 AccessKey 與 SecretKey。<blockquote><p>一般來說只需開啟 S3、Route53、EC2、EBS 與 ELB 就好,但由於偷懶就全開。</p></blockquote></li></ul><p><img src="/images/kops/iam-user2.png" alt=""></p><ul><li>擁有自己的 Domain Name,這邊可以在 AWS Route53 註冊,或者是到 GoDaddy 購買。</li></ul><h2 id="建立-S3-Bucket-與-Route53-Hosted-Zone"><a href="#建立-S3-Bucket-與-Route53-Hosted-Zone" class="headerlink" title="建立 S3 Bucket 與 Route53 Hosted Zone"></a>建立 S3 Bucket 與 Route53 Hosted Zone</h2><p>首先透過 aws 工具進行設定使用指定 AccessKey 與 SecretKey:</p><pre><code class="sh">$ aws configureAWS Access Key ID [****************QGEA]:AWS Secret Access Key [****************zJ+w]:Default region name [None]:Default output format [None]:</code></pre><blockquote><p>設定的 Keys 可以在<code>~/.aws/credentials</code>找到。</p></blockquote><p>完成後建立一個 S3 bucket 用來儲存 Kops 狀態:</p><pre><code class="sh">$ aws s3 mb s3://kops-k8s-1 --region us-west-2make_bucket: kops-k8s-1</code></pre><blockquote><p>這邊 region 可自行選擇,這邊選用 Oregon。</p></blockquote><p>接著建立一個 Route53 Hosted Zone:</p><pre><code class="sh">$ aws route53 create-hosted-zone \ --name k8s.example.com \ --caller-reference $(date '+%Y-%m-%d-%H:%M')# output{ "HostedZone": { "ResourceRecordSetCount": 2, "CallerReference": "2018-04-25-16:16", "Config": { "PrivateZone": false }, "Id": "/hostedzone/Z2JR49ADZ0P3WC", "Name": "k8s.example.com." }, "DelegationSet": { "NameServers": [ "ns-1547.awsdns-01.co.uk", "ns-1052.awsdns-03.org", "ns-886.awsdns-46.net", "ns-164.awsdns-20.com" ] }, "Location": "https://route53.amazonaws.com/2013-04-01/hostedzone/Z2JR49ADZ0P3WC", "ChangeInfo": { "Status": "PENDING", "SubmittedAt": "2018-04-25T08:16:57.462Z", "Id": "/change/C3802PE0C1JVW2" }}</code></pre><blockquote><p>請修改<code>--name</code>為自己所擁有的 domain name。</p></blockquote><p>之後將上述<code>NameServers</code>新增至自己的 Domain name 的 record 中,如 Godaddy:</p><p><img src="/images/kops/route53-hostedzone.png" alt=""></p><h2 id="部署-Kubernetes-叢集"><a href="#部署-Kubernetes-叢集" class="headerlink" title="部署 Kubernetes 叢集"></a>部署 Kubernetes 叢集</h2><p>當上述階段完成後,在自己機器建立 SSH key,就可以使用 Kops 來建立 Kubernetes 叢集:</p><pre><code class="sh">$ ssh-keygen -t rsa$ kops create cluster \ --name=k8s.example.com \ --state=s3://kops-k8s-1 \ --zones=us-west-2a \ --master-size=t2.micro \ --node-size=t2.micro \ --node-count=2 \ --dns-zone=k8s.example.com# output...Finally configure your cluster with: kops update cluster k8s.example.com --yes</code></pre><p>若過程沒有發生錯誤的話,最後會提示再執行 update 來正式進行部署:</p><pre><code class="sh">$ kops update cluster k8s.example.com --state=s3://kops-k8s-1 --yes# output...Cluster is starting. It should be ready in a few minutes.</code></pre><p>當看到上述資訊時,表示叢集已建立,這時候等待環境初始化完成後就可以使用 kubectl 來操作:</p><pre><code class="sh">$ kubectl get nodeNAME STATUS ROLES AGE VERSIONip-172-20-32-194.us-west-2.compute.internal Ready master 1m v1.9.3ip-172-20-32-21.us-west-2.compute.internal Ready node 22s v1.9.3ip-172-20-54-100.us-west-2.compute.internal Ready node 28s v1.9.3</code></pre><h2 id="測試"><a href="#測試" class="headerlink" title="測試"></a>測試</h2><p>完成後就可以進行功能測試,這邊簡單建立 Nginx app:</p><pre><code class="sh">$ kubectl run nginx --image nginx --port 80$ kubectl expose deploy nginx --type=LoadBalancer --port 80$ kubectl get po,svcNAME READY STATUS RESTARTS AGEpo/nginx-7587c6fdb6-7qtlr 1/1 Running 0 50sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEsvc/kubernetes ClusterIP 100.64.0.1 <none> 443/TCP 8msvc/nginx LoadBalancer 100.68.96.3 ad99f206f486e... 80:30174/TCP 28s</code></pre><p>這邊會看到<code>EXTERNAL-IP</code>會直接透過 AWS ELB 建立一個 Load Balancer,這時只要更新 Route53 的 record set 就可以存取到服務:</p><pre><code class="sh">$ export DOMAIN_NAME=k8s.example.com$ export NGINX_LB=$(kubectl get svc/nginx \ --template="{{range .status.loadBalancer.ingress}} {{.hostname}} {{end}}")$ cat <<EOF > dns-record.json{ "Comment": "Create/Update a latency-based CNAME record for a federated Deployment", "Changes": [ { "Action": "UPSERT", "ResourceRecordSet": { "Name": "nginx.${DOMAIN_NAME}", "Type": "CNAME", "Region": "us-west-2", "TTL": 300, "SetIdentifier": "us-west-2", "ResourceRecords": [ { "Value": "${NGINX_LB}" } ] } } ]}EOF$ export HOSTED_ZONE_ID=$(aws route53 list-hosted-zones \ | jq -r '.HostedZones[] | select(.Name=="'${DOMAIN_NAME}'.") | .Id' \ | sed 's/\/hostedzone\///')$ aws route53 change-resource-record-sets \ --hosted-zone-id ${HOSTED_ZONE_ID} \ --change-batch file://dns-record.json# output{ "ChangeInfo": { "Status": "PENDING", "Comment": "Create/Update a latency-based CNAME record for a federated Deployment", "SubmittedAt": "2018-04-25T10:06:02.545Z", "Id": "/change/C79MFJRHCF05R" }}</code></pre><p>完成後透過 cURL 工作來測試:</p><pre><code class="sh">$ curl nginx.k8s.example.com...<title>Welcome to nginx!</title>...</code></pre><h2 id="刪除節點"><a href="#刪除節點" class="headerlink" title="刪除節點"></a>刪除節點</h2><p>當叢集測完後,可以利用以下指令來刪除:</p><pre><code class="sh">$ kops delete cluster \ --name=k8s.example.com \ --state=s3://kops-k8s-1 --yesDeleted cluster: "k8s.k2r2bai.com"$ aws s3 rb s3://kops-k8s-1 --forceremove_bucket: kops-k8s-1</code></pre><p>接著清除 Route53 所有 record 並刪除 hosted zone:</p><pre><code class="sh">$ aws route53 list-resource-record-sets \ --hosted-zone-id ${HOSTED_ZONE_ID} |jq -c '.ResourceRecordSets[]' |while read -r resourcerecordset; do read -r name type <<<$(echo $(jq -r '.Name,.Type' <<<"$resourcerecordset")) if [ $type != "NS" -a $type != "SOA" ]; then aws route53 change-resource-record-sets \ --hosted-zone-id ${HOSTED_ZONE_ID} \ --change-batch '{"Changes":[{"Action":"DELETE","ResourceRecordSet": '"$resourcerecordset"' }]}' \ --output text --query 'ChangeInfo.Id' fidone$ aws route53 delete-hosted-zone --id ${HOSTED_ZONE_ID}</code></pre>]]></content>
<summary type="html">
<p><a href="https://github.com/kubernetes/kops" target="_blank" rel="noopener">Kops</a> 是 Kubernetes 官方維護的專案,是一套 Production ready 的 Kubernetes 部署、升級與管理工具,早期用於 AWS 公有雲上建置 Kubernetes 叢集使用,但隨著社群的推進已支援 GCP、vSphere(Alpha),未來也會有更多公有雲平台慢慢被支援(Maybe)。本篇簡單撰寫使用 Kops 部署一個叢集,過去自己因為公司都是屬於建置 On-premises 的 Kubernetes,因此很少使用 Kops,剛好最近社群分享又再一次接觸的關析,所以就來寫個文章。</p>
<p>本次安裝的軟體版本:</p>
<ul>
<li>Kubernetes v1.9.3</li>
<li>Kops v1.9.0</li>
</ul>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="AWS" scheme="https://kairen.github.io/tags/AWS/"/>
</entry>
<entry>
<title>整合 OpenLDAP 進行 Kubernetes 身份認證</title>
<link href="https://kairen.github.io/2018/04/15/kubernetes/k8s-integration-ldap/"/>
<id>https://kairen.github.io/2018/04/15/kubernetes/k8s-integration-ldap/</id>
<published>2018-04-15T09:08:54.000Z</published>
<updated>2018-04-20T07:35:30.448Z</updated>
<content type="html"><![CDATA[<p>本文將說明如何整合 OpenLDAP 來提供給 Kubernetes 進行使用者認證。Kubernetes 官方並沒有提供針對 LDAP 與 AD 的整合,但是可以藉由 <a href="https://kubernetes.io/docs/admin/authentication/#webhook-token-authentication" target="_blank" rel="noopener">Webhook Token Authentication</a> 以及 <a href="https://kubernetes.io/docs/admin/authentication/#authenticating-proxy" target="_blank" rel="noopener">Authenticating Proxy</a> 來達到整合功能。概念是開發一個 HTTP Server 提供 POST Method 來塞入 Bearer Token,而該 HTTP Server 利用 LDAP library 檢索對應 Token 的 User 進行認證,成功後回傳該 User 的所有 Group 等資訊,而這時可以利用 Kubernetes 針對該 User 的 Group 設定對應的 RBAC role 進行權限控管。</p><a id="more"></a><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>本教學將以下列節點數與規格來進行部署 Kubernetes 叢集,作業系統可採用<code>Ubuntu 16.x</code>與<code>CentOS 7.x</code>:</p><table><thead><tr><th>IP Address</th><th>Hostname</th><th>CPU</th><th>Memory</th></tr></thead><tbody><tr><td>192.16.35.11</td><td>k8s-m1</td><td>1</td><td>2G</td></tr><tr><td>192.16.35.12</td><td>k8s-n1</td><td>1</td><td>2G</td></tr><tr><td>192.16.35.13</td><td>k8s-n2</td><td>1</td><td>2G</td></tr><tr><td>192.16.35.20</td><td>ldap-server</td><td>1</td><td>1G</td></tr></tbody></table><blockquote><ul><li>這邊<code>m</code>為 K8s master,<code>n</code>為 K8s node。</li><li>所有操作全部用<code>root</code>使用者進行(方便用),以 SRE 來說不推薦。</li><li>可以下載 <a href="https://kairen.github.io/files/k8s-ldap/Vagrantfile">Vagrantfile</a> 來建立 Virtualbox 虛擬機叢集。不過需要注意機器資源是否足夠。</li></ul></blockquote><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>開始安裝前需要確保以下條件已達成:</p><ul><li><code>所有節點</code>需要安裝 Docker CE 版本的容器引擎:</li></ul><pre><code class="sh">$ curl -fsSL "https://get.docker.com/" | sh</code></pre><blockquote><p>不管是在 <code>Ubuntu</code> 或 <code>CentOS</code> 都只需要執行該指令就會自動安裝最新版 Docker。<br>CentOS 安裝完成後需要再執行以下指令:</p><pre><code class="sh">$ systemctl enable docker && systemctl start docker</code></pre></blockquote><ul><li>所有節點以 kubeadm 部署成 Kubernetes v1.9+ 叢集。請參考 <a href="https://kairen.github.io/2016/09/29/kubernetes/deploy/kubeadm/">用 kubeadm 部署 Kubernetes 叢集</a>。</li></ul><h2 id="OpenLDAP-與-phpLDAPadmin"><a href="#OpenLDAP-與-phpLDAPadmin" class="headerlink" title="OpenLDAP 與 phpLDAPadmin"></a>OpenLDAP 與 phpLDAPadmin</h2><p>本節將說明如何部署、設定與操作 OpenLDAP。</p><h3 id="部署"><a href="#部署" class="headerlink" title="部署"></a>部署</h3><p>進入<code>ldap-server</code>節點透過 Docker 來進行部署:</p><pre><code class="sh">$ docker run -d \ -p 389:389 -p 636:636 \ --env LDAP_ORGANISATION="Kubernetes LDAP" \ --env LDAP_DOMAIN="k8s.com" \ --env LDAP_ADMIN_PASSWORD="password" \ --env LDAP_CONFIG_PASSWORD="password" \ --name openldap-server \ osixia/openldap:1.2.0$ docker run -d \ -p 443:443 \ --env PHPLDAPADMIN_LDAP_HOSTS=192.16.35.20 \ --name phpldapadmin \ osixia/phpldapadmin:0.7.1</code></pre><blockquote><p>這邊為<code>cn=admin,dc=k8s,dc=com</code>為<code>admin</code> DN ,而<code>cn=admin,cn=config</code>為<code>config</code>。另外這邊僅做測試用,故不使用 Persistent Volumes,需要可以參考 <a href="https://github.com/osixia/docker-openldap" target="_blank" rel="noopener">Docker OpenLDAP</a>。</p></blockquote><p>完成後就可以透過瀏覽器來 <a href="https://192.16.35.20/" target="_blank" rel="noopener">phpLDAPadmin website</a>。這邊點選<code>Login</code>輸入 DN 與 Password。<br><img src="/images/k8s-ldap/ldap-login.png" alt=""></p><p>成功登入後畫面,這時可以自行新增其他資訊。<br><img src="/images/k8s-ldap/ldap-logined.png" alt=""></p><h3 id="建立-Kubenretes-Token-Schema"><a href="#建立-Kubenretes-Token-Schema" class="headerlink" title="建立 Kubenretes Token Schema"></a>建立 Kubenretes Token Schema</h3><p>進入<code>openldap-server 容器</code>,接著建立 Kubernetes token schema 物件的設定檔:</p><pre><code class="sh">$ docker exec -ti openldap-server sh$ mkdir ~/kubernetes_tokens$ cat <<EOF > ~/kubernetes_tokens/kubernetesToken.schemaattributeType ( 1.3.6.1.4.1.18171.2.1.8 NAME 'kubernetesToken' DESC 'Kubernetes authentication token' EQUALITY caseExactIA5Match SUBSTR caseExactIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )objectClass ( 1.3.6.1.4.1.18171.2.3 NAME 'kubernetesAuthenticationObject' DESC 'Object that may authenticate to a Kubernetes cluster' AUXILIARY MUST kubernetesToken )EOF$ echo "include /root/kubernetes_tokens/kubernetesToken.schema" > ~/kubernetes_tokens/schema_convert.conf$ slaptest -f ~/kubernetes_tokens/schema_convert.conf -F ~/kubernetes_tokensconfig file testing succeeded</code></pre><p>修改以下檔案內容,如以下所示:</p><pre><code class="sh">$ vim ~/kubernetes_tokens/cn=config/cn=schema/cn\=\{0\}kubernetestoken.ldif# AUTO-GENERATED FILE - DO NOT EDIT!! Use ldapmodify.# CRC32 e502306edn: cn=kubernetestoken,cn=schema,cn=configobjectClass: olcSchemaConfigcn: kubernetestokenolcAttributeTypes: {0}( 1.3.6.1.4.1.18171.2.1.8 NAME 'kubernetesToken' DESC 'Kubernetes authentication token' EQUALITY caseExactIA5Match SUBSTR caseExa ctIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )olcObjectClasses: {0}( 1.3.6.1.4.1.18171.2.3 NAME 'kubernetesAuthenticationO bject' DESC 'Object that may authenticate to a Kubernetes cluster' AUXILIAR Y MUST kubernetesToken )</code></pre><p>新增 Schema 物件至 LDAP Server 中:</p><pre><code class="sh">$ cd ~/kubernetes_tokens/cn=config/cn=schema$ ldapadd -c -Y EXTERNAL -H ldapi:/// -f cn\=\{0\}kubernetestoken.ldifSASL/EXTERNAL authentication startedSASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=authSASL SSF: 0adding new entry "cn=kubernetestoken,cn=schema,cn=config"</code></pre><p>完成後查詢是否成功新增 Entry:</p><pre><code class="sh">$ ldapsearch -x -H ldap:/// -LLL -D "cn=admin,cn=config" -w password -b "cn=schema,cn=config" "(objectClass=olcSchemaConfig)" dn -ZEnter LDAP Password:dn: cn=schema,cn=config...dn: cn={14}kubernetestoken,cn=schema,cn=config</code></pre><h3 id="新增測試用-LDAP-Groups-與-Users"><a href="#新增測試用-LDAP-Groups-與-Users" class="headerlink" title="新增測試用 LDAP Groups 與 Users"></a>新增測試用 LDAP Groups 與 Users</h3><p>當上面 Schema 建立完成後,這邊需要新增一些測試用 Groups:</p><pre><code class="sh">$ cat <<EOF > groups.ldifdn: ou=People,dc=k8s,dc=comou: PeopleobjectClass: topobjectClass: organizationalUnitdescription: Parent object of all UNIX accountsdn: ou=Groups,dc=k8s,dc=comou: GroupsobjectClass: topobjectClass: organizationalUnitdescription: Parent object of all UNIX groupsdn: cn=kubernetes,ou=Groups,dc=k8s,dc=comcn: kubernetesgidnumber: 100memberuid: user1memberuid: user2objectclass: posixGroupobjectclass: topEOF$ ldapmodify -x -a -H ldap:// -D "cn=admin,dc=k8s,dc=com" -w password -f groups.ldifadding new entry "ou=People,dc=k8s,dc=com"adding new entry "ou=Groups,dc=k8s,dc=com"adding new entry "cn=kubernetes,ou=Groups,dc=k8s,dc=com"</code></pre><p>Group 建立完成後再接著建立 User:</p><pre><code class="sh">$ cat <<EOF > users.ldifdn: uid=user1,ou=People,dc=k8s,dc=comcn: user1gidnumber: 100givenname: user1homedirectory: /home/users/user1loginshell: /bin/shobjectclass: inetOrgPersonobjectclass: posixAccountobjectclass: topobjectClass: shadowAccountobjectClass: organizationalPersonsn: user1uid: user1uidnumber: 1000userpassword: user1dn: uid=user2,ou=People,dc=k8s,dc=comhomedirectory: /home/users/user2loginshell: /bin/shobjectclass: inetOrgPersonobjectclass: posixAccountobjectclass: topobjectClass: shadowAccountobjectClass: organizationalPersoncn: user2givenname: user2sn: user2uid: user2uidnumber: 1001gidnumber: 100userpassword: user2EOF$ ldapmodify -x -a -H ldap:// -D "cn=admin,dc=k8s,dc=com" -w password -f users.ldifadding new entry "uid=user1,ou=People,dc=k8s,dc=com"adding new entry "uid=user2,ou=People,dc=k8s,dc=com"</code></pre><p>這邊可以登入 phpLDAPadmin 查看,結果如以下所示:<br><img src="/images/k8s-ldap/ldap-entry.png" alt=""></p><p>確認沒問題後,將 User dump 至一個文字檔案中:</p><pre><code class="sh">$ cat <<EOF > users.txtdn: uid=user1,ou=People,dc=k8s,dc=comdn: uid=user2,ou=People,dc=k8s,dc=comEOF</code></pre><blockquote><p>這邊偷懶直接用 cat。</p></blockquote><p>執行以下腳本來更新每個 LDAP User 的 kubernetesToken:</p><pre><code class="sh">$ while read -r user; dofname=$(echo $user | grep -E -o "uid=[a-z0-9]+" | cut -d"=" -f2)token=$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64 | tr -d "=+/" | dd bs=32 count=1 2>/dev/null)cat << EOF > "${fname}.ldif"$userchangetype: modifyadd: objectClassobjectclass: kubernetesAuthenticationObject-add: kubernetesTokenkubernetesToken: $tokenEOFldapmodify -a -H ldapi:/// -D "cn=admin,dc=k8s,dc=com" -w password -f "${fname}.ldif"done < users.txt# outputEnter LDAP Password:modifying entry "uid=user1,ou=Users,dc=k8s,dc=com"Enter LDAP Password:modifying entry "uid=user2,ou=Users,dc=k8s,dc=com"</code></pre><h2 id="部署-Kubernetes-LDAP"><a href="#部署-Kubernetes-LDAP" class="headerlink" title="部署 Kubernetes LDAP"></a>部署 Kubernetes LDAP</h2><p>當 Kubernetes 環境建立完成後,首先進入<code>k8s-m1</code>節點,透過 git 取得 kube-ldap-authn 原始碼專案:</p><pre><code class="sh">$ git clone https://github.com/kairen/kube-ldap-authn.git$ cd kube-ldap-authn</code></pre><blockquote><p>若想使用 Go 語言實作的版本,可以參考 <a href="https://github.com/kairen/kube-ldap-webhook" target="_blank" rel="noopener">kube-ldap-webhook</a>.</p></blockquote><p>新增一個<code>config.py</code>檔案來提供相關設定內容:</p><pre><code class="sh">LDAP_URL='ldap://192.16.35.20/ ldap://192.16.35.20'LDAP_START_TLS = FalseLDAP_BIND_DN = 'cn=admin,dc=k8s,dc=com'LDAP_BIND_PASSWORD = 'password'LDAP_USER_NAME_ATTRIBUTE = 'uid'LDAP_USER_UID_ATTRIBUTE = 'uidNumber'LDAP_USER_SEARCH_BASE = 'ou=People,dc=k8s,dc=com'LDAP_USER_SEARCH_FILTER = "(&(kubernetesToken={token}))"LDAP_GROUP_NAME_ATTRIBUTE = 'cn'LDAP_GROUP_SEARCH_BASE = 'ou=Groups,dc=k8s,dc=com'LDAP_GROUP_SEARCH_FILTER = '(|(&(objectClass=posixGroup)(memberUid={username}))(&(member={dn})(objectClass=groupOfNames)))'</code></pre><blockquote><p>變數詳細說明可以參考 <a href="https://github.com/kairen/kube-ldap-authn/blob/master/config.py.example" target="_blank" rel="noopener">Config example</a></p></blockquote><p>建立 kube-ldap-authn secret 來提供給 pod 使用,並部署 kube-ldap-authn pod 到所有 master 節點上:</p><pre><code class="sh">$ kubectl -n kube-system create secret generic ldap-authn-config --from-file=config.py=config.py$ kubectl create -f daemonset.yaml$ kubectl -n kube-system get po -l app=kube-ldap-authn -o wideNAME READY STATUS RESTARTS AGE IP NODEkube-ldap-authn-sx994 1/1 Running 0 13s 192.16.35.11 k8s-m1</code></pre><p>這邊若成功部署的話,可以用 curl 進行測試:</p><pre><code class="sh">$ curl -X POST -H "Content-Type: application/json" \ -d '{"apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "spec": {"token": "<LDAP_K8S_TOKEN>"}}' \ http://localhost:8087/authn# output{ "apiVersion": "authentication.k8s.io/v1beta1", "kind": "TokenReview", "status": { "authenticated": true, "user": { "groups": [ "kubernetes" ], "uid": "1000", "username": "user1" } }}</code></pre><p>在所有<code>master</code>節點上新增一個名稱為<code>/srv/kubernetes/webhook-authn</code>的檔案,並加入以下內容:</p><pre><code class="sh">$ mkdir /srv/kubernetes$ cat <<EOF > /srv/kubernetes/webhook-authnclusters: - name: ldap-authn cluster: server: http://localhost:8087/authnusers: - name: apiservercurrent-context: webhookcontexts:- context: cluster: ldap-authn user: apiserver name: webhookEOF</code></pre><p>修改所有<code>master</code>節點上的<code>kube-apiserver.yaml</code> Static Pod 檔案,該檔案會存在於<code>/etc/kubernetes/manifests</code>目錄中,請修改加入以下內容:</p><pre><code class="yaml">...spec: containers: - command: ... - --runtime-config=authentication.k8s.io/v1beta1=true - --authentication-token-webhook-config-file=/srv/kubernetes/webhook-authn - --authentication-token-webhook-cache-ttl=5m volumeMounts: ... - mountPath: /srv/kubernetes/webhook-authn name: webhook-authn readOnly: true volumes: ... - hostPath: path: /srv/kubernetes/webhook-authn type: File name: webhook-authn</code></pre><blockquote><p>這邊<code>...</code>表示已存在的內容,請不要刪除與變更。這邊也可以用 kubeadmconfig 來設定,請參考 <a href="https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/#config-file" target="_blank" rel="noopener">Using kubeadm init with a configuration file</a>。</p></blockquote><h2 id="測試功能"><a href="#測試功能" class="headerlink" title="測試功能"></a>測試功能</h2><p>首先進入<code>k8s-m1</code>,建立一個綁定在 user1 namespace 的唯讀 Role 與 RoleBinding:</p><pre><code class="sh">$ kubectl create ns user1# 建立 Role$ cat <<EOF | kubectl create -f -kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: readonly-role namespace: user1rules:- apiGroups: [""] resources: ["pods"] verbs: ["get", "watch", "list"]EOF# 建立 RoleBinding$ cat <<EOF | kubectl create -f -kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: readonly-role-binding namespace: user1subjects:- kind: Group name: kubernetes apiGroup: ""roleRef: kind: Role name: readonly-role apiGroup: ""EOF</code></pre><blockquote><p>注意!!這邊的<code>Group</code>是 LDAP 中的 Group。</p></blockquote><p>在任意台 Kubernetes client 端設定 Kubeconfig 來存取叢集,這邊直接在<code>k8s-m1</code>進行:</p><pre><code class="sh">$ cd$ kubectl config set-credentials user1 --kubeconfig=.kube/config --token=<user-ldap-token>$ kubectl config set-context user1-context \ --kubeconfig=.kube/config \ --cluster=kubernetes \ --namespace=user1 --user=user1</code></pre><p>接著透過 kubeclt 來測試權限是否正確設定:</p><pre><code class="sh">$ kubectl --context=user1-context get poNo resources found$ kubectl --context=user1-context run nginx --image nginx --port 80Error from server (Forbidden): deployments.extensions is forbidden: User "user1" cannot create deployments.extensions in the namespace "user1"$ kubectl --context=user1-context get po -n defaultError from server (Forbidden): pods is forbidden: User "user1" cannot list pods in the namespace "default"</code></pre><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><a href="https://github.com/osixia/docker-openldap" target="_blank" rel="noopener">https://github.com/osixia/docker-openldap</a></li><li><a href="https://icicimov.github.io/blog/virtualization/Kubernetes-LDAP-Authentication/" target="_blank" rel="noopener">https://icicimov.github.io/blog/virtualization/Kubernetes-LDAP-Authentication/</a></li><li><a href="https://github.com/torchbox/kube-ldap-authn" target="_blank" rel="noopener">https://github.com/torchbox/kube-ldap-authn</a></li></ul>]]></content>
<summary type="html">
<p>本文將說明如何整合 OpenLDAP 來提供給 Kubernetes 進行使用者認證。Kubernetes 官方並沒有提供針對 LDAP 與 AD 的整合,但是可以藉由 <a href="https://kubernetes.io/docs/admin/authentication/#webhook-token-authentication" target="_blank" rel="noopener">Webhook Token Authentication</a> 以及 <a href="https://kubernetes.io/docs/admin/authentication/#authenticating-proxy" target="_blank" rel="noopener">Authenticating Proxy</a> 來達到整合功能。概念是開發一個 HTTP Server 提供 POST Method 來塞入 Bearer Token,而該 HTTP Server 利用 LDAP library 檢索對應 Token 的 User 進行認證,成功後回傳該 User 的所有 Group 等資訊,而這時可以利用 Kubernetes 針對該 User 的 Group 設定對應的 RBAC role 進行權限控管。</p>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="LDAP" scheme="https://kairen.github.io/tags/LDAP/"/>
</entry>
<entry>
<title>Kubernetes v1.10.x HA 全手動苦工安裝教學(TL;DR)</title>
<link href="https://kairen.github.io/2018/04/05/kubernetes/deploy/manual-v1.10/"/>
<id>https://kairen.github.io/2018/04/05/kubernetes/deploy/manual-v1.10/</id>
<published>2018-04-05T09:08:54.000Z</published>
<updated>2018-04-16T09:55:11.954Z</updated>
<content type="html"><![CDATA[<p>本篇延續過往<code>手動安裝方式</code>來部署 Kubernetes v1.10.x 版本的 High Availability 叢集,主要目的是學習 Kubernetes 安裝的一些元件關析與流程。若不想這麼累的話,可以參考 <a href="https://kubernetes.io/docs/getting-started-guides/" target="_blank" rel="noopener">Picking the Right Solution</a> 來選擇自己最喜歡的方式。</p><p>本次安裝的軟體版本:</p><ul><li>Kubernetes v1.10.0</li><li>CNI v0.6.0</li><li>Etcd v3.1.13</li><li>Calico v3.0.4</li><li>Docker CE latest version</li></ul><a id="more"></a><p><img src="/images/kube/kubernetes-aa-ha.png" alt=""></p><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>本教學將以下列節點數與規格來進行部署 Kubernetes 叢集,作業系統可採用<code>Ubuntu 16.x</code>與<code>CentOS 7.x</code>:</p><table><thead><tr><th>IP Address</th><th>Hostname</th><th>CPU</th><th>Memory</th></tr></thead><tbody><tr><td>192.16.35.11</td><td>k8s-m1</td><td>1</td><td>4G</td></tr><tr><td>192.16.35.12</td><td>k8s-m2</td><td>1</td><td>4G</td></tr><tr><td>192.16.35.13</td><td>k8s-m3</td><td>1</td><td>4G</td></tr><tr><td>192.16.35.14</td><td>k8s-n1</td><td>1</td><td>4G</td></tr><tr><td>192.16.35.15</td><td>k8s-n2</td><td>1</td><td>4G</td></tr><tr><td>192.16.35.16</td><td>k8s-n2</td><td>1</td><td>4G</td></tr></tbody></table><p>另外由所有 master 節點提供一組 VIP <code>192.16.35.10</code>。</p><blockquote><ul><li>這邊<code>m</code>為主要控制節點,<code>n</code>為應用程式工作節點。</li><li>所有操作全部用<code>root</code>使用者進行(方便用),以 SRE 來說不推薦。</li><li>可以下載 <a href="https://kairen.github.io/files/manual-v1.10/Vagrantfile">Vagrantfile</a> 來建立 Virtualbox 虛擬機叢集。不過需要注意機器資源是否足夠。</li></ul></blockquote><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>開始安裝前需要確保以下條件已達成:</p><ul><li><code>所有節點</code>彼此網路互通,並且<code>k8s-m1</code> SSH 登入其他節點為 passwdless。</li><li>所有防火牆與 SELinux 已關閉。如 CentOS:</li></ul><pre><code class="sh">$ systemctl stop firewalld && systemctl disable firewalld$ setenforce 0$ vim /etc/selinux/configSELINUX=disabled</code></pre><ul><li><code>所有節點</code>需要設定<code>/etc/hosts</code>解析到所有叢集主機。</li></ul><pre><code>...192.16.35.11 k8s-m1192.16.35.12 k8s-m2192.16.35.13 k8s-m3192.16.35.14 k8s-n1192.16.35.15 k8s-n2192.16.35.16 k8s-n3</code></pre><ul><li><code>所有節點</code>需要安裝 Docker CE 版本的容器引擎:</li></ul><pre><code class="sh">$ curl -fsSL "https://get.docker.com/" | sh</code></pre><blockquote><p>不管是在 <code>Ubuntu</code> 或 <code>CentOS</code> 都只需要執行該指令就會自動安裝最新版 Docker。<br>CentOS 安裝完成後,需要再執行以下指令:</p><pre><code class="sh">$ systemctl enable docker && systemctl start docker</code></pre></blockquote><ul><li><code>所有節點</code>需要設定<code>/etc/sysctl.d/k8s.conf</code>的系統參數。</li></ul><pre><code class="sh">$ cat <<EOF > /etc/sysctl.d/k8s.confnet.ipv4.ip_forward = 1net.bridge.bridge-nf-call-ip6tables = 1net.bridge.bridge-nf-call-iptables = 1EOF$ sysctl -p /etc/sysctl.d/k8s.conf</code></pre><ul><li>Kubernetes v1.8+ 要求關閉系統 Swap,若不關閉則需要修改 kubelet 設定參數,在<code>所有節點</code>利用以下指令關閉:</li></ul><pre><code class="sh">$ swapoff -a && sysctl -w vm.swappiness=0</code></pre><blockquote><p>記得<code>/etc/fstab</code>也要註解掉<code>SWAP</code>掛載。</p></blockquote><ul><li>在<code>所有節點</code>下載 Kubernetes 二進制執行檔:</li></ul><pre><code class="sh">$ export KUBE_URL="https://storage.googleapis.com/kubernetes-release/release/v1.10.0/bin/linux/amd64"$ wget "${KUBE_URL}/kubelet" -O /usr/local/bin/kubelet$ chmod +x /usr/local/bin/kubelet# node 請忽略下載 kubectl$ wget "${KUBE_URL}/kubectl" -O /usr/local/bin/kubectl$ chmod +x /usr/local/bin/kubectl</code></pre><ul><li>在<code>所有節點</code>下載 Kubernetes CNI 二進制檔案:</li></ul><pre><code class="sh">$ mkdir -p /opt/cni/bin && cd /opt/cni/bin$ export CNI_URL="https://github.com/containernetworking/plugins/releases/download"$ wget -qO- --show-progress "${CNI_URL}/v0.6.0/cni-plugins-amd64-v0.6.0.tgz" | tar -zx</code></pre><ul><li>在<code>k8s-m1</code>需要安裝<code>CFSSL</code>工具,這將會用來建立 TLS Certificates。</li></ul><pre><code class="sh">$ export CFSSL_URL="https://pkg.cfssl.org/R1.2"$ wget "${CFSSL_URL}/cfssl_linux-amd64" -O /usr/local/bin/cfssl$ wget "${CFSSL_URL}/cfssljson_linux-amd64" -O /usr/local/bin/cfssljson$ chmod +x /usr/local/bin/cfssl /usr/local/bin/cfssljson</code></pre><h2 id="建立叢集-CA-keys-與-Certificates"><a href="#建立叢集-CA-keys-與-Certificates" class="headerlink" title="建立叢集 CA keys 與 Certificates"></a>建立叢集 CA keys 與 Certificates</h2><p>在這個部分,將需要產生多個元件的 Certificates,這包含 Etcd、Kubernetes 元件等,並且每個叢集都會有一個根數位憑證認證機構(Root Certificate Authority)被用在認證 API Server 與 Kubelet 端的憑證。</p><blockquote><p>P.S. 這邊要注意 CA JSON 檔的<code>CN(Common Name)</code>與<code>O(Organization)</code>等內容是會影響 Kubernetes 元件認證的。</p></blockquote><h3 id="Etcd"><a href="#Etcd" class="headerlink" title="Etcd"></a>Etcd</h3><p>首先在<code>k8s-m1</code>建立<code>/etc/etcd/ssl</code>資料夾,然後進入目錄完成以下操作。</p><pre><code class="sh">$ mkdir -p /etc/etcd/ssl && cd /etc/etcd/ssl$ export PKI_URL="https://kairen.github.io/files/manual-v1.10/pki"</code></pre><p>下載<code>ca-config.json</code>與<code>etcd-ca-csr.json</code>檔案,並從 CSR json 產生 CA keys 與 Certificate:</p><pre><code class="sh">$ wget "${PKI_URL}/ca-config.json" "${PKI_URL}/etcd-ca-csr.json"$ cfssl gencert -initca etcd-ca-csr.json | cfssljson -bare etcd-ca</code></pre><p>下載<code>etcd-csr.json</code>檔案,並產生 Etcd 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/etcd-csr.json"$ cfssl gencert \ -ca=etcd-ca.pem \ -ca-key=etcd-ca-key.pem \ -config=ca-config.json \ -hostname=127.0.0.1,192.16.35.11,192.16.35.12,192.16.35.13 \ -profile=kubernetes \ etcd-csr.json | cfssljson -bare etcd</code></pre><blockquote><p><code>-hostname</code>需修改成所有 masters 節點。</p></blockquote><p>完成後刪除不必要檔案:</p><pre><code class="sh">$ rm -rf *.json *.csr</code></pre><p>確認<code>/etc/etcd/ssl</code>有以下檔案:</p><pre><code class="sh">$ ls /etc/etcd/ssletcd-ca-key.pem etcd-ca.pem etcd-key.pem etcd.pem</code></pre><p>複製相關檔案至其他 Etcd 節點,這邊為所有<code>master</code>節點:</p><pre><code class="sh">$ for NODE in k8s-m2 k8s-m3; do echo "--- $NODE ---" ssh ${NODE} "mkdir -p /etc/etcd/ssl" for FILE in etcd-ca-key.pem etcd-ca.pem etcd-key.pem etcd.pem; do scp /etc/etcd/ssl/${FILE} ${NODE}:/etc/etcd/ssl/${FILE} done done</code></pre><h3 id="Kubernetes"><a href="#Kubernetes" class="headerlink" title="Kubernetes"></a>Kubernetes</h3><p>在<code>k8s-m1</code>建立<code>pki</code>資料夾,然後進入目錄完成以下章節操作。</p><pre><code class="sh">$ mkdir -p /etc/kubernetes/pki && cd /etc/kubernetes/pki$ export PKI_URL="https://kairen.github.io/files/manual-v1.10/pki"$ export KUBE_APISERVER="https://192.16.35.10:6443"</code></pre><p>下載<code>ca-config.json</code>與<code>ca-csr.json</code>檔案,並產生 CA 金鑰:</p><pre><code class="sh">$ wget "${PKI_URL}/ca-config.json" "${PKI_URL}/ca-csr.json"$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca$ ls ca*.pemca-key.pem ca.pem</code></pre><h4 id="API-Server-Certificate"><a href="#API-Server-Certificate" class="headerlink" title="API Server Certificate"></a>API Server Certificate</h4><p>下載<code>apiserver-csr.json</code>檔案,並產生 kube-apiserver 憑證:</p><pre><code class="sh">$ wget "${PKI_URL}/apiserver-csr.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=10.96.0.1,192.16.35.10,127.0.0.1,kubernetes.default \ -profile=kubernetes \ apiserver-csr.json | cfssljson -bare apiserver$ ls apiserver*.pemapiserver-key.pem apiserver.pem</code></pre><blockquote><ul><li>這邊<code>-hostname</code>的<code>10.96.0.1</code>是 Cluster IP 的 Kubernetes 端點;</li><li><code>192.16.35.10</code>為虛擬 IP 位址(VIP);</li><li><code>kubernetes.default</code>為 Kubernetes DN。</li></ul></blockquote><h4 id="Front-Proxy-Certificate"><a href="#Front-Proxy-Certificate" class="headerlink" title="Front Proxy Certificate"></a>Front Proxy Certificate</h4><p>下載<code>front-proxy-ca-csr.json</code>檔案,並產生 Front Proxy CA 金鑰,Front Proxy 主要是用在 API aggregator 上:</p><pre><code class="sh">$ wget "${PKI_URL}/front-proxy-ca-csr.json"$ cfssl gencert \ -initca front-proxy-ca-csr.json | cfssljson -bare front-proxy-ca$ ls front-proxy-ca*.pemfront-proxy-ca-key.pem front-proxy-ca.pem</code></pre><p>下載<code>front-proxy-client-csr.json</code>檔案,並產生 front-proxy-client 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/front-proxy-client-csr.json"$ cfssl gencert \ -ca=front-proxy-ca.pem \ -ca-key=front-proxy-ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ front-proxy-client-csr.json | cfssljson -bare front-proxy-client$ ls front-proxy-client*.pemfront-proxy-client-key.pem front-proxy-client.pem</code></pre><h4 id="Admin-Certificate"><a href="#Admin-Certificate" class="headerlink" title="Admin Certificate"></a>Admin Certificate</h4><p>下載<code>admin-csr.json</code>檔案,並產生 admin certificate 憑證:</p><pre><code class="sh">$ wget "${PKI_URL}/admin-csr.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ admin-csr.json | cfssljson -bare admin$ ls admin*.pemadmin-key.pem admin.pem</code></pre><p>接著透過以下指令產生名稱為 <code>admin.conf</code> 的 kubeconfig 檔:</p><pre><code class="sh"># admin set cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../admin.conf# admin set credentials$ kubectl config set-credentials kubernetes-admin \ --client-certificate=admin.pem \ --client-key=admin-key.pem \ --embed-certs=true \ --kubeconfig=../admin.conf# admin set context$ kubectl config set-context kubernetes-admin@kubernetes \ --cluster=kubernetes \ --user=kubernetes-admin \ --kubeconfig=../admin.conf# admin set default context$ kubectl config use-context kubernetes-admin@kubernetes \ --kubeconfig=../admin.conf</code></pre><h4 id="Controller-Manager-Certificate"><a href="#Controller-Manager-Certificate" class="headerlink" title="Controller Manager Certificate"></a>Controller Manager Certificate</h4><p>下載<code>manager-csr.json</code>檔案,並產生 kube-controller-manager certificate 憑證:</p><pre><code class="sh">$ wget "${PKI_URL}/manager-csr.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ manager-csr.json | cfssljson -bare controller-manager$ ls controller-manager*.pemcontroller-manager-key.pem controller-manager.pem</code></pre><blockquote><p>若節點 IP 不同,需要修改<code>manager-csr.json</code>的<code>hosts</code>。</p></blockquote><p>接著透過以下指令產生名稱為<code>controller-manager.conf</code>的 kubeconfig 檔:</p><pre><code class="sh"># controller-manager set cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../controller-manager.conf# controller-manager set credentials$ kubectl config set-credentials system:kube-controller-manager \ --client-certificate=controller-manager.pem \ --client-key=controller-manager-key.pem \ --embed-certs=true \ --kubeconfig=../controller-manager.conf# controller-manager set context$ kubectl config set-context system:kube-controller-manager@kubernetes \ --cluster=kubernetes \ --user=system:kube-controller-manager \ --kubeconfig=../controller-manager.conf# controller-manager set default context$ kubectl config use-context system:kube-controller-manager@kubernetes \ --kubeconfig=../controller-manager.conf</code></pre><h4 id="Scheduler-Certificate"><a href="#Scheduler-Certificate" class="headerlink" title="Scheduler Certificate"></a>Scheduler Certificate</h4><p>下載<code>scheduler-csr.json</code>檔案,並產生 kube-scheduler certificate 憑證:</p><pre><code class="sh">$ wget "${PKI_URL}/scheduler-csr.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ scheduler-csr.json | cfssljson -bare scheduler$ ls scheduler*.pemscheduler-key.pem scheduler.pem</code></pre><blockquote><p>若節點 IP 不同,需要修改<code>scheduler-csr.json</code>的<code>hosts</code>。</p></blockquote><p>接著透過以下指令產生名稱為 <code>scheduler.conf</code> 的 kubeconfig 檔:</p><pre><code class="sh"># scheduler set cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../scheduler.conf# scheduler set credentials$ kubectl config set-credentials system:kube-scheduler \ --client-certificate=scheduler.pem \ --client-key=scheduler-key.pem \ --embed-certs=true \ --kubeconfig=../scheduler.conf# scheduler set context$ kubectl config set-context system:kube-scheduler@kubernetes \ --cluster=kubernetes \ --user=system:kube-scheduler \ --kubeconfig=../scheduler.conf# scheduler use default context$ kubectl config use-context system:kube-scheduler@kubernetes \ --kubeconfig=../scheduler.conf</code></pre><h4 id="Master-Kubelet-Certificate"><a href="#Master-Kubelet-Certificate" class="headerlink" title="Master Kubelet Certificate"></a>Master Kubelet Certificate</h4><p>接著在所有<code>k8s-m1</code>節點下載<code>kubelet-csr.json</code>檔案,並產生憑證:</p><pre><code class="sh">$ wget "${PKI_URL}/kubelet-csr.json"$ for NODE in k8s-m1 k8s-m2 k8s-m3; do echo "--- $NODE ---" cp kubelet-csr.json kubelet-$NODE-csr.json; sed -i "s/\$NODE/$NODE/g" kubelet-$NODE-csr.json; cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=$NODE \ -profile=kubernetes \ kubelet-$NODE-csr.json | cfssljson -bare kubelet-$NODE done$ ls kubelet*.pemkubelet-k8s-m1-key.pem kubelet-k8s-m1.pem kubelet-k8s-m2-key.pem kubelet-k8s-m2.pem kubelet-k8s-m3-key.pem kubelet-k8s-m3.pem</code></pre><blockquote><p>這邊需要依據節點修改<code>-hostname</code>與<code>$NODE</code>。</p></blockquote><p>完成後複製 kubelet 憑證至其他<code>master</code>節點:</p><pre><code class="sh">$ for NODE in k8s-m2 k8s-m3; do echo "--- $NODE ---" ssh ${NODE} "mkdir -p /etc/kubernetes/pki" for FILE in kubelet-$NODE-key.pem kubelet-$NODE.pem ca.pem; do scp /etc/kubernetes/pki/${FILE} ${NODE}:/etc/kubernetes/pki/${FILE} done done</code></pre><p>接著執行以下指令產生名稱為<code>kubelet.conf</code>的 kubeconfig 檔:</p><pre><code class="sh">$ for NODE in k8s-m1 k8s-m2 k8s-m3; do echo "--- $NODE ---" ssh ${NODE} "cd /etc/kubernetes/pki && \ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../kubelet.conf && \ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../kubelet.conf && \ kubectl config set-credentials system:node:${NODE} \ --client-certificate=kubelet-${NODE}.pem \ --client-key=kubelet-${NODE}-key.pem \ --embed-certs=true \ --kubeconfig=../kubelet.conf && \ kubectl config set-context system:node:${NODE}@kubernetes \ --cluster=kubernetes \ --user=system:node:${NODE} \ --kubeconfig=../kubelet.conf && \ kubectl config use-context system:node:${NODE}@kubernetes \ --kubeconfig=../kubelet.conf && \ rm kubelet-${NODE}.pem kubelet-${NODE}-key.pem" done</code></pre><h4 id="Service-Account-Key"><a href="#Service-Account-Key" class="headerlink" title="Service Account Key"></a>Service Account Key</h4><p>Service account 不是透過 CA 進行認證,因此不要透過 CA 來做 Service account key 的檢查,這邊建立一組 Private 與 Public 金鑰提供給 Service account key 使用:</p><pre><code class="sh">$ openssl genrsa -out sa.key 2048$ openssl rsa -in sa.key -pubout -out sa.pub$ ls sa.*sa.key sa.pub</code></pre><h4 id="刪除不必要檔案"><a href="#刪除不必要檔案" class="headerlink" title="刪除不必要檔案"></a>刪除不必要檔案</h4><p>所有資訊準備完成後,就可以將一些不必要檔案刪除:</p><pre><code class="sh">$ rm -rf *.json *.csr scheduler*.pem controller-manager*.pem admin*.pem kubelet*.pem</code></pre><h4 id="複製檔案至其他節點"><a href="#複製檔案至其他節點" class="headerlink" title="複製檔案至其他節點"></a>複製檔案至其他節點</h4><p>複製憑證檔案至其他<code>master</code>節點:</p><pre><code class="sh">$ for NODE in k8s-m2 k8s-m3; do echo "--- $NODE ---" for FILE in $(ls /etc/kubernetes/pki/); do scp /etc/kubernetes/pki/${FILE} ${NODE}:/etc/kubernetes/pki/${FILE} done done</code></pre><p>複製 Kubernetes config 檔案至其他<code>master</code>節點:</p><pre><code class="sh">$ for NODE in k8s-m2 k8s-m3; do echo "--- $NODE ---" for FILE in admin.conf controller-manager.conf scheduler.conf; do scp /etc/kubernetes/${FILE} ${NODE}:/etc/kubernetes/${FILE} done done</code></pre><h2 id="Kubernetes-Masters"><a href="#Kubernetes-Masters" class="headerlink" title="Kubernetes Masters"></a>Kubernetes Masters</h2><p>本部分將說明如何建立與設定 Kubernetes Master 角色,過程中會部署以下元件:</p><ul><li><strong>kube-apiserver</strong>:提供 REST APIs,包含授權、認證與狀態儲存等。</li><li><strong>kube-controller-manager</strong>:負責維護叢集的狀態,如自動擴展,滾動更新等。</li><li><strong>kube-scheduler</strong>:負責資源排程,依據預定的排程策略將 Pod 分配到對應節點上。</li><li><strong>Etcd</strong>:儲存叢集所有狀態的 Key/Value 儲存系統。</li><li><strong>HAProxy</strong>:提供負載平衡器。</li><li><strong>Keepalived</strong>:提供虛擬網路位址(VIP)。</li></ul><h3 id="部署與設定"><a href="#部署與設定" class="headerlink" title="部署與設定"></a>部署與設定</h3><p>首先在<code>所有 master 節點</code>下載部署元件的 YAML 檔案,這邊不採用二進制執行檔與 Systemd 來管理這些元件,全部採用 <a href="https://kubernetes.io/docs/tasks/administer-cluster/static-pod/" target="_blank" rel="noopener">Static Pod</a> 來達成。這邊將檔案下載至<code>/etc/kubernetes/manifests</code>目錄:</p><pre><code class="sh">$ export CORE_URL="https://kairen.github.io/files/manual-v1.10/master"$ mkdir -p /etc/kubernetes/manifests && cd /etc/kubernetes/manifests$ for FILE in kube-apiserver kube-controller-manager kube-scheduler haproxy keepalived etcd etcd.config; do wget "${CORE_URL}/${FILE}.yml.conf" -O ${FILE}.yml if [ ${FILE} == "etcd.config" ]; then mv etcd.config.yml /etc/etcd/etcd.config.yml sed -i "s/\${HOSTNAME}/${HOSTNAME}/g" /etc/etcd/etcd.config.yml sed -i "s/\${PUBLIC_IP}/$(hostname -i)/g" /etc/etcd/etcd.config.yml fi done$ ls /etc/kubernetes/manifestsetcd.yml haproxy.yml keepalived.yml kube-apiserver.yml kube-controller-manager.yml kube-scheduler.yml</code></pre><blockquote><ul><li>若<code>IP</code>與教學設定不同的話,請記得修改 YAML 檔案。</li><li>kube-apiserver 中的 <code>NodeRestriction</code> 請參考 <a href="https://kubernetes.io/docs/admin/authorization/node/" target="_blank" rel="noopener">Using Node Authorization</a>。</li></ul></blockquote><p>產生一個用來加密 Etcd 的 Key:</p><pre><code class="sh">$ head -c 32 /dev/urandom | base64SUpbL4juUYyvxj3/gonV5xVEx8j769/99TSAf8YT/sQ=</code></pre><blockquote><p>注意每台<code>master</code>節點需要用一樣的 Key。</p></blockquote><p>在<code>/etc/kubernetes/</code>目錄下,建立<code>encryption.yml</code>的加密 YAML 檔案:</p><pre><code class="sh">$ cat <<EOF > /etc/kubernetes/encryption.ymlkind: EncryptionConfigapiVersion: v1resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: SUpbL4juUYyvxj3/gonV5xVEx8j769/99TSAf8YT/sQ= - identity: {}EOF</code></pre><blockquote><p>Etcd 資料加密可參考這篇 <a href="https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/" target="_blank" rel="noopener">Encrypting data at rest</a>。</p></blockquote><p>在<code>/etc/kubernetes/</code>目錄下,建立<code>audit-policy.yml</code>的進階稽核策略 YAML 檔:</p><pre><code class="sh">$ cat <<EOF > /etc/kubernetes/audit-policy.ymlapiVersion: audit.k8s.io/v1beta1kind: Policyrules:- level: MetadataEOF</code></pre><blockquote><p>Audit Policy 請參考這篇 <a href="https://kubernetes.io/docs/tasks/debug-application-cluster/audit/" target="_blank" rel="noopener">Auditing</a>。</p></blockquote><p>下載<code>haproxy.cfg</code>檔案來提供給 HAProxy 容器使用:</p><pre><code class="sh">$ mkdir -p /etc/haproxy/$ wget "${CORE_URL}/haproxy.cfg" -O /etc/haproxy/haproxy.cfg</code></pre><blockquote><p>若與本教學 IP 不同的話,請記得修改設定檔。</p></blockquote><p>下載<code>kubelet.service</code>相關檔案來管理 kubelet:</p><pre><code class="sh">$ mkdir -p /etc/systemd/system/kubelet.service.d$ wget "${CORE_URL}/kubelet.service" -O /lib/systemd/system/kubelet.service$ wget "${CORE_URL}/10-kubelet.conf" -O /etc/systemd/system/kubelet.service.d/10-kubelet.conf</code></pre><blockquote><p>若 cluster <code>dns</code>或<code>domain</code>有改變的話,需要修改<code>10-kubelet.conf</code>。</p></blockquote><p>最後建立 var 存放資訊,然後啟動 kubelet 服務:</p><pre><code class="sh">$ mkdir -p /var/lib/kubelet /var/log/kubernetes /var/lib/etcd$ systemctl enable kubelet.service && systemctl start kubelet.service</code></pre><p>完成後會需要一段時間來下載映像檔與啟動元件,可以利用該指令來監看:</p><pre><code class="sh">$ watch netstat -ntlpActive Internet connections (only servers)Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program nametcp 0 0 127.0.0.1:10248 0.0.0.0:* LISTEN 10344/kubelettcp 0 0 127.0.0.1:10251 0.0.0.0:* LISTEN 11324/kube-scheduletcp 0 0 0.0.0.0:6443 0.0.0.0:* LISTEN 11416/haproxytcp 0 0 127.0.0.1:10252 0.0.0.0:* LISTEN 11235/kube-controlltcp 0 0 0.0.0.0:9090 0.0.0.0:* LISTEN 11416/haproxytcp6 0 0 :::2379 :::* LISTEN 10479/etcdtcp6 0 0 :::2380 :::* LISTEN 10479/etcdtcp6 0 0 :::10255 :::* LISTEN 10344/kubelettcp6 0 0 :::5443 :::* LISTEN 11295/kube-apiserve</code></pre><blockquote><p>若看到以上資訊表示服務正常啟動,若發生問題可以用<code>docker</code>指令來查看。</p></blockquote><h3 id="驗證叢集"><a href="#驗證叢集" class="headerlink" title="驗證叢集"></a>驗證叢集</h3><p>完成後,在任意一台<code>master</code>節點複製 admin kubeconfig 檔案,並透過簡單指令驗證:</p><pre><code class="sh">$ cp /etc/kubernetes/admin.conf ~/.kube/config$ kubectl get csNAME STATUS MESSAGE ERRORcontroller-manager Healthy okscheduler Healthy oketcd-2 Healthy {"health": "true"}etcd-1 Healthy {"health": "true"}etcd-0 Healthy {"health": "true"}$ kubectl get nodeNAME STATUS ROLES AGE VERSIONk8s-m1 NotReady master 52s v1.10.0k8s-m2 NotReady master 51s v1.10.0k8s-m3 NotReady master 50s v1.10.0$ kubectl -n kube-system get poNAME READY STATUS RESTARTS AGEetcd-k8s-m1 1/1 Running 0 7setcd-k8s-m2 1/1 Running 0 57shaproxy-k8s-m3 1/1 Running 0 1m...</code></pre><p>接著確認服務能夠執行 logs 等指令:</p><pre><code class="sh">$ kubectl -n kube-system logs -f kube-scheduler-k8s-m2Error from server (Forbidden): Forbidden (user=kube-apiserver, verb=get, resource=nodes, subresource=proxy) ( pods/log kube-scheduler-k8s-m2)</code></pre><blockquote><p>這邊會發現出現 403 Forbidden 問題,這是因為 <code>kube-apiserver</code> user 並沒有 nodes 的資源存取權限,屬於正常。</p></blockquote><p>由於上述權限問題,必需建立一個<code>apiserver-to-kubelet-rbac.yml</code>來定義權限,以供對 Nodes 容器執行 logs、exec 等指令。在任意一台<code>master</code>節點執行以下指令:</p><pre><code class="sh">$ kubectl apply -f "${CORE_URL}/apiserver-to-kubelet-rbac.yml.conf"clusterrole.rbac.authorization.k8s.io "system:kube-apiserver-to-kubelet" configuredclusterrolebinding.rbac.authorization.k8s.io "system:kube-apiserver" configured# 測試 logs$ kubectl -n kube-system logs -f kube-scheduler-k8s-m2...I0403 02:30:36.375935 1 server.go:555] Version: v1.10.0I0403 02:30:36.378208 1 server.go:574] starting healthz server on 127.0.0.1:10251</code></pre><p>設定<code>master</code>節點允許 Taint:</p><pre><code class="sh">$ kubectl taint nodes node-role.kubernetes.io/master="":NoSchedule --allnode "k8s-m1" taintednode "k8s-m2" taintednode "k8s-m3" tainted</code></pre><blockquote><p><a href="https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/" target="_blank" rel="noopener">Taints and Tolerations</a>。</p></blockquote><h3 id="建立-TLS-Bootstrapping-RBAC-與-Secret"><a href="#建立-TLS-Bootstrapping-RBAC-與-Secret" class="headerlink" title="建立 TLS Bootstrapping RBAC 與 Secret"></a>建立 TLS Bootstrapping RBAC 與 Secret</h3><p>由於本次安裝啟用了 TLS 認證,因此每個節點的 kubelet 都必須使用 kube-apiserver 的 CA 的憑證後,才能與 kube-apiserver 進行溝通,而該過程需要手動針對每台節點單獨簽署憑證是一件繁瑣的事情,且一旦節點增加會延伸出管理不易問題; 而 TLS bootstrapping 目標就是解決該問題,透過讓 kubelet 先使用一個預定低權限使用者連接到 kube-apiserver,然後在對 kube-apiserver 申請憑證簽署,當授權 Token 一致時,Node 節點的 kubelet 憑證將由 kube-apiserver 動態簽署提供。具體作法可以參考 <a href="https://kubernetes.io/docs/admin/kubelet-tls-bootstrapping/" target="_blank" rel="noopener">TLS Bootstrapping</a> 與 <a href="https://kubernetes.io/docs/admin/bootstrap-tokens/" target="_blank" rel="noopener">Authenticating with Bootstrap Tokens</a>。</p><p>首先在<code>k8s-m1</code>建立一個變數來產生<code>BOOTSTRAP_TOKEN</code>,並建立<code>bootstrap-kubelet.conf</code>的 Kubernetes config 檔:</p><pre><code class="sh">$ cd /etc/kubernetes/pki$ export TOKEN_ID=$(openssl rand 3 -hex)$ export TOKEN_SECRET=$(openssl rand 8 -hex)$ export BOOTSTRAP_TOKEN=${TOKEN_ID}.${TOKEN_SECRET}$ export KUBE_APISERVER="https://192.16.35.10:6443"# bootstrap set cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../bootstrap-kubelet.conf# bootstrap set credentials$ kubectl config set-credentials tls-bootstrap-token-user \ --token=${BOOTSTRAP_TOKEN} \ --kubeconfig=../bootstrap-kubelet.conf# bootstrap set context$ kubectl config set-context tls-bootstrap-token-user@kubernetes \ --cluster=kubernetes \ --user=tls-bootstrap-token-user \ --kubeconfig=../bootstrap-kubelet.conf# bootstrap use default context$ kubectl config use-context tls-bootstrap-token-user@kubernetes \ --kubeconfig=../bootstrap-kubelet.conf</code></pre><blockquote><p>若想要用手動簽署憑證來進行授權的話,可以參考 <a href="https://kubernetes.io/docs/concepts/cluster-administration/certificates/" target="_blank" rel="noopener">Certificate</a>。</p></blockquote><p>接著在<code>k8s-m1</code>建立 TLS bootstrap secret 來提供自動簽證使用:</p><pre><code class="sh">$ cat <<EOF | kubectl create -f -apiVersion: v1kind: Secretmetadata: name: bootstrap-token-${TOKEN_ID} namespace: kube-systemtype: bootstrap.kubernetes.io/tokenstringData: token-id: ${TOKEN_ID} token-secret: ${TOKEN_SECRET} usage-bootstrap-authentication: "true" usage-bootstrap-signing: "true" auth-extra-groups: system:bootstrappers:default-node-tokenEOFsecret "bootstrap-token-65a3a9" created</code></pre><p>在<code>k8s-m1</code>建立 TLS Bootstrap Autoapprove RBAC:</p><pre><code class="sh">$ kubectl apply -f "${CORE_URL}/kubelet-bootstrap-rbac.yml.conf"clusterrolebinding.rbac.authorization.k8s.io "kubelet-bootstrap" createdclusterrolebinding.rbac.authorization.k8s.io "node-autoapprove-bootstrap" createdclusterrolebinding.rbac.authorization.k8s.io "node-autoapprove-certificate-rotation" created</code></pre><h2 id="Kubernetes-Nodes"><a href="#Kubernetes-Nodes" class="headerlink" title="Kubernetes Nodes"></a>Kubernetes Nodes</h2><p>本部分將說明如何建立與設定 Kubernetes Node 角色,Node 是主要執行容器實例(Pod)的工作節點。</p><p>在開始部署前,先在<code>k8-m1</code>將需要用到的檔案複製到所有<code>node</code>節點上:</p><pre><code class="sh">$ cd /etc/kubernetes/pki$ for NODE in k8s-n1 k8s-n2 k8s-n3; do echo "--- $NODE ---" ssh ${NODE} "mkdir -p /etc/kubernetes/pki/" ssh ${NODE} "mkdir -p /etc/etcd/ssl" # Etcd for FILE in etcd-ca.pem etcd.pem etcd-key.pem; do scp /etc/etcd/ssl/${FILE} ${NODE}:/etc/etcd/ssl/${FILE} done # Kubernetes for FILE in pki/ca.pem pki/ca-key.pem bootstrap-kubelet.conf; do scp /etc/kubernetes/${FILE} ${NODE}:/etc/kubernetes/${FILE} done done</code></pre><h3 id="部署與設定-1"><a href="#部署與設定-1" class="headerlink" title="部署與設定"></a>部署與設定</h3><p>在每台<code>node</code>節點下載<code>kubelet.service</code>相關檔案來管理 kubelet:</p><pre><code class="sh">$ export CORE_URL="https://kairen.github.io/files/manual-v1.10/node"$ mkdir -p /etc/systemd/system/kubelet.service.d$ wget "${CORE_URL}/kubelet.service" -O /lib/systemd/system/kubelet.service$ wget "${CORE_URL}/10-kubelet.conf" -O /etc/systemd/system/kubelet.service.d/10-kubelet.conf</code></pre><blockquote><p>若 cluster <code>dns</code>或<code>domain</code>有改變的話,需要修改<code>10-kubelet.conf</code>。</p></blockquote><p>最後建立 var 存放資訊,然後啟動 kubelet 服務:</p><pre><code class="sh">$ mkdir -p /var/lib/kubelet /var/log/kubernetes$ systemctl enable kubelet.service && systemctl start kubelet.service</code></pre><h3 id="驗證叢集-1"><a href="#驗證叢集-1" class="headerlink" title="驗證叢集"></a>驗證叢集</h3><p>完成後,在任意一台<code>master</code>節點並透過簡單指令驗證:</p><pre><code class="sh">$ kubectl get csrNAME AGE REQUESTOR CONDITIONcsr-bvz9l 11m system:node:k8s-m1 Approved,Issuedcsr-jwr8k 11m system:node:k8s-m2 Approved,Issuedcsr-q867w 11m system:node:k8s-m3 Approved,Issuednode-csr-Y-FGvxZWJqI-8RIK_IrpgdsvjGQVGW0E4UJOuaU8ogk 17s system:bootstrap:dca3e1 Approved,Issuednode-csr-cnX9T1xp1LdxVDc9QW43W0pYkhEigjwgceRshKuI82c 19s system:bootstrap:dca3e1 Approved,Issuednode-csr-m7SBA9RAGCnsgYWJB-u2HoB2qLSfiQZeAxWFI2WYN7Y 18s system:bootstrap:dca3e1 Approved,Issued$ kubectl get nodesNAME STATUS ROLES AGE VERSIONk8s-m1 NotReady master 12m v1.10.0k8s-m2 NotReady master 11m v1.10.0k8s-m3 NotReady master 11m v1.10.0k8s-n1 NotReady node 32s v1.10.0k8s-n2 NotReady node 31s v1.10.0k8s-n3 NotReady node 29s v1.10.0</code></pre><h2 id="Kubernetes-Core-Addons-部署"><a href="#Kubernetes-Core-Addons-部署" class="headerlink" title="Kubernetes Core Addons 部署"></a>Kubernetes Core Addons 部署</h2><p>當完成上面所有步驟後,接著需要部署一些插件,其中如<code>Kubernetes DNS</code>與<code>Kubernetes Proxy</code>等這種 Addons 是非常重要的。</p><h3 id="Kubernetes-Proxy"><a href="#Kubernetes-Proxy" class="headerlink" title="Kubernetes Proxy"></a>Kubernetes Proxy</h3><p><a href="https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/kube-proxy" target="_blank" rel="noopener">Kube-proxy</a> 是實現 Service 的關鍵插件,kube-proxy 會在每台節點上執行,然後監聽 API Server 的 Service 與 Endpoint 資源物件的改變,然後來依據變化執行 iptables 來實現網路的轉發。這邊我們會需要建議一個 DaemonSet 來執行,並且建立一些需要的 Certificates。</p><p>在<code>k8s-m1</code>下載<code>kube-proxy.yml</code>來建立 Kubernetes Proxy Addon:</p><pre><code class="sh">$ kubectl apply -f "https://kairen.github.io/files/manual-v1.10/addon/kube-proxy.yml.conf"serviceaccount "kube-proxy" createdclusterrolebinding.rbac.authorization.k8s.io "system:kube-proxy" createdconfigmap "kube-proxy" createddaemonset.apps "kube-proxy" created$ kubectl -n kube-system get po -o wide -l k8s-app=kube-proxyNAME READY STATUS RESTARTS AGE IP NODEkube-proxy-8j5w8 1/1 Running 0 29s 192.16.35.16 k8s-n3kube-proxy-c4zvt 1/1 Running 0 29s 192.16.35.11 k8s-m1kube-proxy-clpl6 1/1 Running 0 29s 192.16.35.12 k8s-m2...</code></pre><h3 id="Kubernetes-DNS"><a href="#Kubernetes-DNS" class="headerlink" title="Kubernetes DNS"></a>Kubernetes DNS</h3><p><a href="https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns" target="_blank" rel="noopener">Kube DNS</a> 是 Kubernetes 叢集內部 Pod 之間互相溝通的重要 Addon,它允許 Pod 可以透過 Domain Name 方式來連接 Service,其主要由 Kube DNS 與 Sky DNS 組合而成,透過 Kube DNS 監聽 Service 與 Endpoint 變化,來提供給 Sky DNS 資訊,已更新解析位址。</p><p>在<code>k8s-m1</code>下載<code>kube-proxy.yml</code>來建立 Kubernetes Proxy Addon:</p><pre><code class="sh">$ kubectl apply -f "https://kairen.github.io/files/manual-v1.10/addon/kube-dns.yml.conf"serviceaccount "kube-dns" createdservice "kube-dns" createddeployment.extensions "kube-dns" created$ kubectl -n kube-system get po -l k8s-app=kube-dnsNAME READY STATUS RESTARTS AGEkube-dns-654684d656-zq5t8 0/3 Pending 0 1m</code></pre><p>這邊會發現處於<code>Pending</code>狀態,是由於 Kubernetes Pod Network 還未建立完成,因此所有節點會處於<code>NotReady</code>狀態,而造成 Pod 無法被排程分配到指定節點上啟動,由於為了解決該問題,下節將說明如何建立 Pod Network。</p><h2 id="Calico-Network-安裝與設定"><a href="#Calico-Network-安裝與設定" class="headerlink" title="Calico Network 安裝與設定"></a>Calico Network 安裝與設定</h2><p>Calico 是一款純 Layer 3 的資料中心網路方案(不需要 Overlay 網路),Calico 好處是它整合了各種雲原生平台,且 Calico 在每一個節點利用 Linux Kernel 實現高效的 vRouter 來負責資料的轉發,而當資料中心複雜度增加時,可以用 BGP route reflector 來達成。</p><blockquote><p>本次不採用手動方式來建立 Calico 網路,若想了解可以參考 <a href="https://docs.projectcalico.org/v3.0/getting-started/kubernetes/installation/integration" target="_blank" rel="noopener">Integration Guide</a>。</p></blockquote><p>在<code>k8s-m1</code>下載<code>calico.yaml</code>來建立 Calico Network:</p><pre><code class="sh">$ kubectl apply -f "https://kairen.github.io/files/manual-v1.10/network/calico.yml.conf"configmap "calico-config" createddaemonset "calico-node" createddeployment "calico-kube-controllers" createdclusterrolebinding "calico-cni-plugin" createdclusterrole "calico-cni-plugin" createdserviceaccount "calico-cni-plugin" createdclusterrolebinding "calico-kube-controllers" createdclusterrole "calico-kube-controllers" createdserviceaccount "calico-kube-controllers" created$ kubectl -n kube-system get po -l k8s-app=calico-node -o wideNAME READY STATUS RESTARTS AGE IP NODEcalico-node-22mbb 2/2 Running 0 1m 192.16.35.12 k8s-m2calico-node-2qwf5 2/2 Running 0 1m 192.16.35.11 k8s-m1calico-node-g2sp8 2/2 Running 0 1m 192.16.35.13 k8s-m3calico-node-hghp4 2/2 Running 0 1m 192.16.35.14 k8s-n1calico-node-qp6gf 2/2 Running 0 1m 192.16.35.15 k8s-n2calico-node-zfx4n 2/2 Running 0 1m 192.16.35.16 k8s-n3</code></pre><blockquote><p>這邊若節點 IP 與網卡不同的話,請修改<code>calico.yml</code>檔案。</p></blockquote><p>在<code>k8s-m1</code>下載 Calico CLI 來查看 Calico nodes:</p><pre><code class="sh">$ wget https://github.com/projectcalico/calicoctl/releases/download/v3.1.0/calicoctl -O /usr/local/bin/calicoctl$ chmod u+x /usr/local/bin/calicoctl$ cat <<EOF > ~/calico-rcexport ETCD_ENDPOINTS="https://192.16.35.11:2379,https://192.16.35.12:2379,https://192.16.35.13:2379"export ETCD_CA_CERT_FILE="/etc/etcd/ssl/etcd-ca.pem"export ETCD_CERT_FILE="/etc/etcd/ssl/etcd.pem"export ETCD_KEY_FILE="/etc/etcd/ssl/etcd-key.pem"EOF$ . ~/calico-rc$ calicoctl node statusCalico process is running.IPv4 BGP status+--------------+-------------------+-------+----------+-------------+| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |+--------------+-------------------+-------+----------+-------------+| 192.16.35.12 | node-to-node mesh | up | 04:42:37 | Established || 192.16.35.13 | node-to-node mesh | up | 04:42:42 | Established || 192.16.35.14 | node-to-node mesh | up | 04:42:37 | Established || 192.16.35.15 | node-to-node mesh | up | 04:42:41 | Established || 192.16.35.16 | node-to-node mesh | up | 04:42:36 | Established |+--------------+-------------------+-------+----------+-------------+...</code></pre><p>查看 pending 的 pod 是否已執行:</p><pre><code class="sh">$ kubectl -n kube-system get po -l k8s-app=kube-dnskubectl -n kube-system get po -l k8s-app=kube-dnsNAME READY STATUS RESTARTS AGEkube-dns-654684d656-j8xzx 3/3 Running 0 10m</code></pre><h2 id="Kubernetes-Extra-Addons-部署"><a href="#Kubernetes-Extra-Addons-部署" class="headerlink" title="Kubernetes Extra Addons 部署"></a>Kubernetes Extra Addons 部署</h2><p>本節說明如何部署一些官方常用的 Addons,如 Dashboard、Heapster 等。</p><h3 id="Dashboard"><a href="#Dashboard" class="headerlink" title="Dashboard"></a>Dashboard</h3><p><a href="https://github.com/kubernetes/dashboard" target="_blank" rel="noopener">Dashboard</a> 是 Kubernetes 社區官方開發的儀表板,有了儀表板後管理者就能夠透過 Web-based 方式來管理 Kubernetes 叢集,除了提升管理方便,也讓資源視覺化,讓人更直覺看見系統資訊的呈現結果。</p><p>在<code>k8s-m1</code>透過 kubectl 來建立 kubernetes dashboard 即可:</p><pre><code class="sh">$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml$ kubectl -n kube-system get po,svc -l k8s-app=kubernetes-dashboardNAME READY STATUS RESTARTS AGEkubernetes-dashboard-7d5dcdb6d9-j492l 1/1 Running 0 12sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes-dashboard ClusterIP 10.111.22.111 <none> 443/TCP 12s</code></pre><p>這邊會額外建立一個名稱為<code>open-api</code> Cluster Role Binding,這僅作為方便測試時使用,在一般情況下不要開啟,不然就會直接被存取所有 API:</p><pre><code class="sh">$ cat <<EOF | kubectl create -f -apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: open-api namespace: ""roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-adminsubjects: - apiGroup: rbac.authorization.k8s.io kind: User name: system:anonymousEOF</code></pre><blockquote><p>注意!管理者可以針對特定使用者來開放 API 存取權限,但這邊方便使用直接綁在 cluster-admin cluster role。</p></blockquote><p>完成後,就可以透過瀏覽器存取 <a href="https://192.16.35.10:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/" target="_blank" rel="noopener">Dashboard</a>。</p><p>在 1.7 版本以後的 Dashboard 將不再提供所有權限,因此需要建立一個 service account 來綁定 cluster-admin role:</p><pre><code class="sh">$ kubectl -n kube-system create sa dashboard$ kubectl create clusterrolebinding dashboard --clusterrole cluster-admin --serviceaccount=kube-system:dashboard$ SECRET=$(kubectl -n kube-system get sa dashboard -o yaml | awk '/dashboard-token/ {print $3}')$ kubectl -n kube-system describe secrets ${SECRET} | awk '/token:/{print $2}'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtdG9rZW4tdzVocmgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYWJmMTFjYzMtZjRlYi0xMWU3LTgzYWUtMDgwMDI3NjdkOWI5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZCJ9.Xuyq34ci7Mk8bI97o4IldDyKySOOqRXRsxVWIJkPNiVUxKT4wpQZtikNJe2mfUBBD-JvoXTzwqyeSSTsAy2CiKQhekW8QgPLYelkBPBibySjBhJpiCD38J1u7yru4P0Pww2ZQJDjIxY4vqT46ywBklReGVqY3ogtUQg-eXueBmz-o7lJYMjw8L14692OJuhBjzTRSaKW8U2MPluBVnD7M2SOekDff7KpSxgOwXHsLVQoMrVNbspUCvtIiEI1EiXkyCNRGwfnd2my3uzUABIHFhm0_RZSmGwExPbxflr8Fc6bxmuz-_jSdOtUidYkFIzvEWw2vRovPgs3MXTv59RwUw</code></pre><blockquote><p>複製<code>token</code>,然後貼到 Kubernetes dashboard。注意這邊一般來說要針對不同 User 開啟特定存取權限。</p></blockquote><p><img src="/images/kube/kubernetes-dashboard.png" alt=""></p><h3 id="Heapster"><a href="#Heapster" class="headerlink" title="Heapster"></a>Heapster</h3><p><a href="https://github.com/kubernetes/heapster" target="_blank" rel="noopener">Heapster</a> 是 Kubernetes 社區維護的容器叢集監控與效能分析工具。Heapster 會從 Kubernetes apiserver 取得所有 Node 資訊,然後再透過這些 Node 來取得 kubelet 上的資料,最後再將所有收集到資料送到 Heapster 的後台儲存 InfluxDB,最後利用 Grafana 來抓取 InfluxDB 的資料源來進行視覺化。</p><p>在<code>k8s-m1</code>透過 kubectl 來建立 kubernetes monitor 即可:</p><pre><code class="sh">$ kubectl apply -f "https://kairen.github.io/files/manual-v1.10/addon/kube-monitor.yml.conf"$ kubectl -n kube-system get po,svcNAME READY STATUS RESTARTS AGE...po/heapster-74fb5c8cdc-62xzc 4/4 Running 0 7mpo/influxdb-grafana-55bd7df44-nw4nc 2/2 Running 0 7mNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE...svc/heapster ClusterIP 10.100.242.225 <none> 80/TCP 7msvc/monitoring-grafana ClusterIP 10.101.106.180 <none> 80/TCP 7msvc/monitoring-influxdb ClusterIP 10.109.245.142 <none> 8083/TCP,8086/TCP 7m···</code></pre><p>完成後,就可以透過瀏覽器存取 <a href="https://192.16.35.10:6443/api/v1/namespaces/kube-system/services/monitoring-grafana/proxy/" target="_blank" rel="noopener">Grafana Dashboard</a>。</p><p><img src="/images/kube/monitoring-grafana.png" alt=""></p><h3 id="Ingress-Controller"><a href="#Ingress-Controller" class="headerlink" title="Ingress Controller"></a>Ingress Controller</h3><p><a href="https://kubernetes.io/docs/concepts/services-networking/ingress/" target="_blank" rel="noopener">Ingress</a>是利用 Nginx 或 HAProxy 等負載平衡器來曝露叢集內服務的元件,Ingress 主要透過設定 Ingress 規格來定義 Domain Name 映射 Kubernetes 內部 Service,這種方式可以避免掉使用過多的 NodePort 問題。</p><p>在<code>k8s-m1</code>透過 kubectl 來建立 Ingress Controller 即可:</p><pre><code class="sh">$ kubectl create ns ingress-nginx$ kubectl apply -f "https://kairen.github.io/files/manual-v1.10/addon/ingress-controller.yml.conf"$ kubectl -n ingress-nginx get poNAME READY STATUS RESTARTS AGEdefault-http-backend-5c6d95c48-rzxfb 1/1 Running 0 7mnginx-ingress-controller-699cdf846-982n4 1/1 Running 0 7m</code></pre><blockquote><p>這裡也可以選擇 <a href="https://github.com/containous/traefik" target="_blank" rel="noopener">Traefik</a> 的 Ingress Controller。</p></blockquote><h4 id="測試-Ingress-功能"><a href="#測試-Ingress-功能" class="headerlink" title="測試 Ingress 功能"></a>測試 Ingress 功能</h4><p>這邊先建立一個 Nginx HTTP server Deployment 與 Service:</p><pre><code class="sh">$ kubectl run nginx-dp --image nginx --port 80$ kubectl expose deploy nginx-dp --port 80$ kubectl get po,svc$ cat <<EOF | kubectl create -f -apiVersion: extensions/v1beta1kind: Ingressmetadata: name: test-nginx-ingress annotations: ingress.kubernetes.io/rewrite-target: /spec: rules: - host: test.nginx.com http: paths: - path: / backend: serviceName: nginx-dp servicePort: 80EOF</code></pre><p>透過 curl 來進行測試:</p><pre><code class="sh">$ curl 192.16.35.10 -H 'Host: test.nginx.com'<!DOCTYPE html><html><head><title>Welcome to nginx!</title>...# 測試其他 domain name 是否會回傳 404$ curl 192.16.35.10 -H 'Host: test.nginx.com1'default backend - 404</code></pre><h3 id="Helm-Tiller-Server"><a href="#Helm-Tiller-Server" class="headerlink" title="Helm Tiller Server"></a>Helm Tiller Server</h3><p><a href="https://github.com/kubernetes/helm" target="_blank" rel="noopener">Helm</a> 是 Kubernetes Chart 的管理工具,Kubernetes Chart 是一套預先組態的 Kubernetes 資源套件。其中<code>Tiller Server</code>主要負責接收來至 Client 的指令,並透過 kube-apiserver 與 Kubernetes 叢集做溝通,根據 Chart 定義的內容,來產生與管理各種對應 API 物件的 Kubernetes 部署檔案(又稱為 <code>Release</code>)。</p><p>首先在<code>k8s-m1</code>安裝 Helm tool:</p><pre><code class="sh">$ wget -qO- https://kubernetes-helm.storage.googleapis.com/helm-v2.8.1-linux-amd64.tar.gz | tar -zx$ sudo mv linux-amd64/helm /usr/local/bin/</code></pre><p>另外在所有<code>node</code>節點安裝 socat:</p><pre><code class="sh">$ sudo apt-get install -y socat</code></pre><p>接著初始化 Helm(這邊會安裝 Tiller Server):</p><pre><code class="sh">$ kubectl -n kube-system create sa tiller$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller$ helm init --service-account tiller...Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.Happy Helming!$ kubectl -n kube-system get po -l app=helmNAME READY STATUS RESTARTS AGEtiller-deploy-5f789bd9f7-tzss6 1/1 Running 0 29s$ helm versionClient: &version.Version{SemVer:"v2.8.1", GitCommit:"6af75a8fd72e2aa18a2b278cfe5c7a1c5feca7f2", GitTreeState:"clean"}Server: &version.Version{SemVer:"v2.8.1", GitCommit:"6af75a8fd72e2aa18a2b278cfe5c7a1c5feca7f2", GitTreeState:"clean"}</code></pre><h4 id="測試-Helm-功能"><a href="#測試-Helm-功能" class="headerlink" title="測試 Helm 功能"></a>測試 Helm 功能</h4><p>這邊部署簡單 Jenkins 來進行功能測試:</p><pre><code class="sh">$ helm install --name demo --set Persistence.Enabled=false stable/jenkins$ kubectl get po,svc -l app=demo-jenkinsNAME READY STATUS RESTARTS AGEdemo-jenkins-7bf4bfcff-q74nt 1/1 Running 0 2mNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEdemo-jenkins LoadBalancer 10.103.15.129 <pending> 8080:31161/TCP 2mdemo-jenkins-agent ClusterIP 10.103.160.126 <none> 50000/TCP 2m# 取得 admin 帳號的密碼$ printf $(kubectl get secret --namespace default demo-jenkins -o jsonpath="{.data.jenkins-admin-password}" | base64 --decode);echor6y9FMuF2u</code></pre><p>完成後,就可以透過瀏覽器存取 <a href="http://192.16.35.10:31161" target="_blank" rel="noopener">Jenkins Web</a>。</p><p><img src="/images/kube/helm-jenkins-v1.10.png" alt=""></p><p>測試完成後,即可刪除:</p><pre><code class="sh">$ helm lsNAME REVISION UPDATED STATUS CHART NAMESPACEdemo 1 Tue Apr 10 07:29:51 2018 DEPLOYED jenkins-0.14.4 default$ helm delete demo --purgerelease "demo" deleted</code></pre><p>更多 Helm Apps 可以到 <a href="https://hub.kubeapps.com/" target="_blank" rel="noopener">Kubeapps Hub</a> 尋找。</p><h2 id="測試叢集"><a href="#測試叢集" class="headerlink" title="測試叢集"></a>測試叢集</h2><p>SSH 進入<code>k8s-m1</code>節點,然後關閉該節點:</p><pre><code class="sh">$ sudo poweroff</code></pre><p>接著進入到<code>k8s-m2</code>節點,透過 kubectl 來檢查叢集是否能夠正常執行:</p><pre><code class="sh"># 先檢查 etcd 狀態,可以發現 etcd-0 因為關機而中斷$ kubectl get csNAME STATUS MESSAGE ERRORscheduler Healthy okcontroller-manager Healthy oketcd-1 Healthy {"health": "true"}etcd-2 Healthy {"health": "true"}etcd-0 Unhealthy Get https://192.16.35.11:2379/health: net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)# 測試是否可以建立 Pod$ kubectl run nginx --image nginx --restart=Never --port 80$ kubectl get poNAME READY STATUS RESTARTS AGEnginx 1/1 Running 0 22s</code></pre>]]></content>
<summary type="html">
<p>本篇延續過往<code>手動安裝方式</code>來部署 Kubernetes v1.10.x 版本的 High Availability 叢集,主要目的是學習 Kubernetes 安裝的一些元件關析與流程。若不想這麼累的話,可以參考 <a href="https://kubernetes.io/docs/getting-started-guides/" target="_blank" rel="noopener">Picking the Right Solution</a> 來選擇自己最喜歡的方式。</p>
<p>本次安裝的軟體版本:</p>
<ul>
<li>Kubernetes v1.10.0</li>
<li>CNI v0.6.0</li>
<li>Etcd v3.1.13</li>
<li>Calico v3.0.4</li>
<li>Docker CE latest version</li>
</ul>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Docker" scheme="https://kairen.github.io/tags/Docker/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="Calico" scheme="https://kairen.github.io/tags/Calico/"/>
</entry>
<entry>
<title>使用 kubefed 建立 Kubernetes Federation(On-premises)</title>
<link href="https://kairen.github.io/2018/03/21/kubernetes/k8s-federation/"/>
<id>https://kairen.github.io/2018/03/21/kubernetes/k8s-federation/</id>
<published>2018-03-21T09:08:54.000Z</published>
<updated>2018-03-23T09:05:22.812Z</updated>
<content type="html"><![CDATA[<p><a href="https://kubernetes.io/docs/concepts/cluster-administration/federation/" target="_blank" rel="noopener">Kubernetes Federation(聯邦)</a> 是實現跨地區與跨服務商多個 Kubernetes 叢集的管理機制。Kubernetes Federation 的架構非常類似純 Kubenretes 叢集,Federation 會擁有自己的 API Server 與 Controller Manager 來提供一個標準的 Kubernetes API,以及管理聯邦叢集,並利用 Etcd 來儲存所有狀態,不過差異在於 Kubenretes 只管理多個節點,而 Federation 是管理所有被註冊的 Kubernetes 叢集。</p><a id="more"></a><p>Federation 使管理多個叢集更為簡單,這主要是透過兩個模型來實現:</p><ol><li><strong>跨叢集的資源同步(Sync resources across clusters)</strong>:提供在多個叢集中保持資源同步的功能,如確保一個 Deployment 可以存在於多個叢集中。</li><li><strong>跨叢集的服務發現(Cross cluster discovery:)</strong>:提供自動配置 DNS 服務以及在所有叢集後端上進行負載平衡功能,如提供全域 VIP 或 DNS record,並透過此存取多個叢集後端。</li></ol><p><img src="/images/kube/federation-api.png" alt=""></p><p>Federation 有以下幾個好處:</p><ol><li>跨叢集的資源排程,能讓 Pod 分配至不同叢集的不同節點上執行,如果當前叢集超出負荷,能夠將額外附載分配到空閒叢集上。</li><li>叢集的高可靠,能夠做到 Pod 故障自動遷移。</li><li>可管理多個 Kubernetes 叢集。</li><li>跨叢集的服務發現。</li></ol><blockquote><p>雖然 Federation 能夠降低管理多叢集門檻,但是目前依據不建議放到生產環境。以下幾個原因:</p><ul><li><strong>成熟度問題</strong>,目前還處與 Alpha 階段,故很多功能都還處於實現性質,或者不太穩定。</li><li><strong>提升網路頻寬與成本</strong>,由於 Federation 需要監控所有叢集以確保當前狀態符合預期,因是會增加額外效能開銷。</li><li><strong>跨叢集隔離差</strong>,Federation 的子叢集git有可能因為 Bug 的引發而影響其他叢集運行狀況。</li><li>個人用起來不是很穩定,例如建立的 Deployment 刪除很常會 Timeout。</li><li>支援的物件資源有限,如不支援 StatefulSets。可參考 <a href="https://kubernetes.io/docs/concepts/cluster-administration/federation/#api-resources" target="_blank" rel="noopener">API resources</a>。</li></ul></blockquote><p>Federation 主要包含三個元件:</p><ul><li><strong>federation-apiserver</strong>:主要提供跨叢集的 REST API 伺服器,類似 kube-apiserver。</li><li><strong>federation-controller-manager</strong>:提供多個叢集之間的狀態同步,類似 kube-controller-manager。</li><li><strong>kubefed</strong>:Federation CLI 工具,用來初始化 Federation 元件與加入子叢集。</li></ul><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>本次安裝作業系統採用<code>Ubuntu 16.04 Server</code>,測試環境為實體機器,共有三組叢集:</p><p>Federation 控制平面叢集(簡稱 F):</p><table><thead><tr><th>IP Address</th><th>Host</th><th>vCPU</th><th>RAM</th></tr></thead><tbody><tr><td>172.22.132.31</td><td>k8s-f-m1</td><td>4</td><td>16G</td></tr><tr><td>172.22.132.32</td><td>k8s-f-n1</td><td>4</td><td>16G</td></tr></tbody></table><p>叢集 A:</p><table><thead><tr><th>IP Address</th><th>Host</th><th>vCPU</th><th>RAM</th></tr></thead><tbody><tr><td>172.22.132.41</td><td>k8s-a-m1</td><td>8</td><td>16G</td></tr><tr><td>172.22.132.42</td><td>k8s-a-n1</td><td>8</td><td>16G</td></tr></tbody></table><p>叢集 B:</p><table><thead><tr><th>IP Address</th><th>Host</th><th>vCPU</th><th>RAM</th></tr></thead><tbody><tr><td>172.22.132.51</td><td>k8s-b-m1</td><td>8</td><td>16G</td></tr><tr><td>172.22.132.52</td><td>k8s-b-n1</td><td>8</td><td>16G</td></tr></tbody></table><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>安裝與進行 Federation 之前,需要確保以下條件達成:</p><ul><li>所有叢集的節點各自部署成一個 Kubernetes 叢集,請參考 <a href="https://kairen.github.io/2016/09/29/kubernetes/deploy/kubeadm/">用 kubeadm 部署 Kubernetes 叢集</a>。</li><li>修改 F、A 與 B 叢集的 Kubernetes config,並將 A 與 B 複製到 F 節點,如修改成以下:</li></ul><pre><code class="yaml">...... name: k8s-a-clustercontexts:- context: cluster: k8s-a-cluster user: a-cluster-admin name: a-cluster-contextcurrent-context: a-cluster-contextkind: Configpreferences: {}users:- name: a-cluster-admin user:...</code></pre><blockquote><p>這邊需要修改每個叢集 config。</p></blockquote><ul><li>接著在 F 叢集合併 F、A 與 B 三個 config,透過以下方式進行:</li></ul><pre><code class="sh">$ lsa-cluster.conf b-cluster.conf f-cluster.conf$ KUBECONFIG=f-cluster.conf:a-cluster.conf:b-cluster.conf kubectl config view --flatten > ~/.kube/config$ kubectl config get-contextsCURRENT NAME CLUSTER AUTHINFO NAMESPACE a-cluster-context k8s-a-cluster a-cluster-admin b-cluster-context k8s-b-cluster b-cluster-admin* f-cluster-context k8s-f-cluster f-cluster-admin</code></pre><ul><li>在 F 叢集安裝 kubefed 工具:</li></ul><pre><code class="sh">$ wget https://storage.googleapis.com/kubernetes-federation-release/release/v1.9.0-alpha.3/federation-client-linux-amd64.tar.gz$ tar xvf federation-client-linux-amd64.tar.gz$ cp federation/client/bin/kubefed /usr/local/bin/$ kubefed versionClient Version: version.Info{Major:"1", Minor:"9+", GitVersion:"v1.9.0-alpha.3", GitCommit:"85c06145286da663755b140efa2b65f793cce9ec", GitTreeState:"clean", BuildDate:"2018-02-14T12:54:40Z", GoVersion:"go1.9.1", Compiler:"gc", Platform:"linux/amd64"}Server Version: version.Info{Major:"1", Minor:"9", GitVersion:"v1.9.6", GitCommit:"9f8ebd171479bec0ada837d7ee641dec2f8c6dd1", GitTreeState:"clean", BuildDate:"2018-03-21T15:13:31Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}</code></pre><ul><li>在 F 叢集安裝 Helm 工具,並進行初始化:</li></ul><pre><code class="sh">$ wget -qO- https://kubernetes-helm.storage.googleapis.com/helm-v2.8.1-linux-amd64.tar.gz | tar -zxf$ sudo mv linux-amd64/helm /usr/local/bin/$ kubectl -n kube-system create sa tiller$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller$ helm init --service-account tiller# wait for a few minutes$ helm versionClient: &version.Version{SemVer:"v2.8.1", GitCommit:"6af75a8fd72e2aa18a2b278cfe5c7a1c5feca7f2", GitTreeState:"clean"}Server: &version.Version{SemVer:"v2.8.1", GitCommit:"6af75a8fd72e2aa18a2b278cfe5c7a1c5feca7f2", GitTreeState:"clean"}</code></pre><h2 id="部署-Kubernetes-Federation"><a href="#部署-Kubernetes-Federation" class="headerlink" title="部署 Kubernetes Federation"></a>部署 Kubernetes Federation</h2><p>由於本篇是使用實體機器部署 Kubernetes 叢集,因此無法像是 GCP 可以提供 DNS 服務來給 Federation 使用,故這邊要用 CoreDNS 建立自定義 DNS 服務。</p><h3 id="CoreDNS-安裝"><a href="#CoreDNS-安裝" class="headerlink" title="CoreDNS 安裝"></a>CoreDNS 安裝</h3><p>首先透過 Helm 來安裝 CoreDNS 使用到的 Etcd:</p><pre><code class="sh">$ helm install --namespace federation --name etcd-operator stable/etcd-operator$ helm upgrade --namespace federation --set cluster.enabled=true etcd-operator stable/etcd-operator$ kubectl -n federation get poNAME READY STATUS RESTARTS AGEetcd-operator-etcd-operator-etcd-backup-operator-577d56449zqkj2 1/1 Running 0 1metcd-operator-etcd-operator-etcd-operator-56679fb56-fpgmm 1/1 Running 0 1metcd-operator-etcd-operator-etcd-restore-operator-65b6cbccl7kzr 1/1 Running 0 1m</code></pre><p>完成後就可以安裝 CoreDNS 來提供自定義 DNS 服務了:</p><pre><code class="sh">$ cat <<EOF > Values.yamlisClusterService: falseserviceType: NodePortmiddleware: kubernetes: enabled: false etcd: enabled: true zones: - "kairen.com." endpoint: "http://etcd-cluster.federation:2379"EOF$ kubectl create clusterrolebinding federation-admin --clusterrole=cluster-admin --user=system:serviceaccount:federation:default$ helm install --namespace federation --name coredns -f Values.yaml stable/coredns# 測試 CoreDNS 可以查詢 Domain Name$ kubectl run -it --rm --restart=Never --image=infoblox/dnstools:latest dnstoolsdnstools# host kuberneteskubernetes.default.svc.cluster.local has address 10.96.0.1</code></pre><h3 id="安裝與初始化-Federation-控制平面元件"><a href="#安裝與初始化-Federation-控制平面元件" class="headerlink" title="安裝與初始化 Federation 控制平面元件"></a>安裝與初始化 Federation 控制平面元件</h3><p>完成 CoreDNS 後,接著透過 kubefed 安裝控制平面元件,由於使用到 CoreDNS,因此這邊要傳入相關 conf 檔,首先建立<code>coredns-provider.conf</code>檔案,加入以下內容:</p><pre><code class="sh">$ cat <<EOF > coredns-provider.conf[Global]etcd-endpoints = http://etcd-cluster.federation:2379zones = kairen.com.EOF</code></pre><blockquote><p>請自行修改<code>etcd-endpoints</code>與<code>zones</code>。</p></blockquote><p>檔案建立並確認沒問題後,透過 kubefed 工具來初始化主叢集:</p><pre><code class="sh">$ kubefed init federation \ --host-cluster-context=f-cluster-context \ --dns-provider="coredns" \ --dns-zone-name="kairen.com." \ --apiserver-enable-basic-auth=true \ --apiserver-enable-token-auth=true \ --dns-provider-config="coredns-provider.conf" \ --apiserver-arg-overrides="--anonymous-auth=false,--v=4" \ --api-server-service-type="NodePort" \ --api-server-advertise-address="172.22.132.31" \ --etcd-persistent-storage=true$ kubectl -n federation-system get poNAME READY STATUS RESTARTS AGEapiserver-848d584b5d-cwxdh 2/2 Running 0 1mcontroller-manager-5846c555c6-mw2jz 1/1 Running 1 1m</code></pre><blockquote><p>這邊可以改變<code>--etcd-persistent-storage</code>來選擇使用或不使用 PV,若使用請先建立一個 PV 來提供給 Federation Pod 的 PVC 索取使用,可以參考 <a href="https://kubernetes.io/docs/concepts/storage/persistent-volumes/" target="_blank" rel="noopener">Persistent Volumes</a>。</p></blockquote><h3 id="加入-Federation-的-Kubernetes-子叢集"><a href="#加入-Federation-的-Kubernetes-子叢集" class="headerlink" title="加入 Federation 的 Kubernetes 子叢集"></a>加入 Federation 的 Kubernetes 子叢集</h3><pre><code class="sh">$ kubectl config use-context federation# 加入 k8s-a-cluster$ kubefed join f-a-cluster \ --cluster-context=a-cluster-context \ --host-cluster-context=f-cluster-context# 加入 k8s-b-cluster$ kubefed join f-b-cluster \ --cluster-context=b-cluster-context \ --host-cluster-context=f-cluster-context$ kubectl get clusterNAME AGEf-a-cluster 57sf-b-cluster 53s</code></pre><h2 id="測試-Federation-叢集"><a href="#測試-Federation-叢集" class="headerlink" title="測試 Federation 叢集"></a>測試 Federation 叢集</h2><p>這邊利用 Nginx Deployment 來進行測試,先簡單建立一個副本為 4 的 Nginx:</p><pre><code class="sh">$ kubectl config use-context federation$ kubectl create ns default$ kubectl run nginx --image nginx --port 80 --replicas=4</code></pre><p>查看 Cluster A:</p><pre><code class="sh">$ kubectl --context=a-cluster-context get poNAME READY STATUS RESTARTS AGEnginx-7587c6fdb6-dpjv5 1/1 Running 0 25snginx-7587c6fdb6-sjv8v 1/1 Running 0 25s</code></pre><p>查看 Cluster B:</p><pre><code class="sh">$ kubectl --context=b-cluster-context get poNAME READY STATUS RESTARTS AGEnginx-7587c6fdb6-dv45v 1/1 Running 0 1mnginx-7587c6fdb6-wxsmq 1/1 Running 0 1m</code></pre><p>其他可測試功能:</p><ul><li>設定 Replica set preferences,參考 <a href="https://kubernetes.io/docs/tasks/administer-federation/replicaset/#spreading-replicas-in-underlying-clusters" target="_blank" rel="noopener">Spreading Replicas in Underlying Clusters</a>。</li><li>Federation 在 v1.7+ 加入了 <a href="https://kubernetes.io/docs/tasks/administer-federation/cluster/#clusterselector-annotation" target="_blank" rel="noopener">ClusterSelector Annotation</a></li><li><a href="https://kubernetes.io/docs/tasks/federation/set-up-placement-policies-federation/#deploying-federation-and-configuring-an-external-policy-engine" target="_blank" rel="noopener">Scheduling Policy</a>。</li></ul><h2 id="Refers"><a href="#Refers" class="headerlink" title="Refers"></a>Refers</h2><ul><li><a href="https://github.com/emaildanwilson/minikube-federation" target="_blank" rel="noopener">Minikube Federation</a></li><li><a href="http://cgrant.io/tutorials/gcp/compute/gke/global-kubernetes-three-steps/" target="_blank" rel="noopener">Global Kubernetes in 3 Steps</a></li></ul>]]></content>
<summary type="html">
<p><a href="https://kubernetes.io/docs/concepts/cluster-administration/federation/" target="_blank" rel="noopener">Kubernetes Federation(聯邦)</a> 是實現跨地區與跨服務商多個 Kubernetes 叢集的管理機制。Kubernetes Federation 的架構非常類似純 Kubenretes 叢集,Federation 會擁有自己的 API Server 與 Controller Manager 來提供一個標準的 Kubernetes API,以及管理聯邦叢集,並利用 Etcd 來儲存所有狀態,不過差異在於 Kubenretes 只管理多個節點,而 Federation 是管理所有被註冊的 Kubernetes 叢集。</p>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="Federation" scheme="https://kairen.github.io/tags/Federation/"/>
</entry>
<entry>
<title>利用 Kubeflow 來管理 TensorFlow 應用程式</title>
<link href="https://kairen.github.io/2018/03/15/tensorflow/kubeflow/"/>
<id>https://kairen.github.io/2018/03/15/tensorflow/kubeflow/</id>
<published>2018-03-15T09:08:54.000Z</published>
<updated>2018-04-17T07:42:10.097Z</updated>
<content type="html"><![CDATA[<p><a href="https://github.com/kubeflow/kubeflow" target="_blank" rel="noopener">Kubeflow</a> 是 Google 開源的機器學習工具,目標是簡化在 Kubernetes 上運行機器學習的過程,使之更簡單、可攜帶與可擴展。Kubeflow 目標不是在於重建其他服務,而是提供一個最佳開發系統來部署到各種基礎設施架構中,另外由於使用 Kubernetes 來做為基礎,因此只要有 Kubernetes 的地方,都能夠執行 Kubeflow。</p><center><img src="/images/kubeflow/logo.png" alt=""></center><a id="more"></a><p>該工具能夠建立以下幾項功能:</p><ul><li>用於建議與管理互動式 Jupyter notebook 的 JupyterHub。</li><li>可以設定使用 CPU 或 GPU,並透過單一設定調整單個叢集大小的 Tensorflow Training Controller。</li><li>用 TensorFlow Serving 容器來提供模型服務。</li></ul><p>Kubeflow 目標是透過 Kubernetes 的特性使機器學習更加簡單與快速:</p><ul><li>在不同基礎設施上實現簡單、可重複的攜帶性部署(Laptop <-> ML rig <-> Training cluster <-> Production cluster)。</li><li>部署與管理松耦合的微服務。</li><li>根據需求進行縮放。</li></ul><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>本次安裝作業系統採用<code>Ubuntu 16.04 Server</code>,測試環境為實體機器:</p><table><thead><tr><th>IP Address</th><th>Role</th><th>vCPU</th><th>RAM</th><th>Extra Device</th></tr></thead><tbody><tr><td>172.22.132.51</td><td>gpu-node1</td><td>8</td><td>16G</td><td>GTX 1060 3G</td></tr><tr><td>172.22.132.52</td><td>gpu-node2</td><td>8</td><td>16G</td><td>GTX 1060 3G</td></tr><tr><td>172.22.132.53</td><td>master1</td><td>8</td><td>16G</td><td>無</td></tr></tbody></table><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>使用 Kubeflow 之前,需要確保以下條件達成:</p><ul><li>所有節點正確安裝指定版本的 NVIDIA driver、CUDA、Docker、NVIDIA Docker,請參考 <a href="https://kairen.github.io/2018/02/17/container/docker-nvidia-install/">安裝 Nvidia Docker 2</a>。</li><li>(option)所有 GPU 節點安裝 cuDNN v7.1.2 for CUDA 9.1,請至 <a href="https://developer.nvidia.com/cudnn" target="_blank" rel="noopener">NVIDIA cuDNN</a> 下載。</li></ul><pre><code class="sh">$ tar xvf cudnn-9.1-linux-x64-v7.1.tgz$ sudo cp cuda/include/cudnn.h /usr/local/cuda/include/$ sudo cp cuda/lib64/libcudnn* /usr/local/cuda/lib64/</code></pre><ul><li>所有節點以 kubeadm 部署成 Kubernetes v1.9+ 叢集,請參考 <a href="https://kairen.github.io/2016/09/29/kubernetes/deploy/kubeadm/">用 kubeadm 部署 Kubernetes 叢集</a>。</li><li>Kubernetes 叢集需要安裝 NVIDIA Device Plugins,請參考 <a href="https://kairen.github.io/2018/03/01/kubernetes/k8s-device-plugin/">安裝 Kubernetes NVIDIA Device Plugins</a>。</li><li>建立 NFS server 並在 Kubernetes 節點安裝 NFS common,然後利用 Kubernetes 建立 PV 提供給 Kubeflow 使用:</li></ul><pre><code class="sh"># 在 master 執行$ sudo apt-get update && sudo apt-get install -y nfs-server$ sudo mkdir /nfs-data$ echo "/nfs-data *(rw,sync,no_root_squash,no_subtree_check)" | sudo tee -a /etc/exports$ sudo /etc/init.d/nfs-kernel-server restart# 在 node 執行$ sudo apt-get update && sudo apt-get install -y nfs-common</code></pre><ul><li>安裝<code>ksonnet 0.9.2</code>,請參考以下:</li></ul><pre><code class="sh">$ wget https://github.com/ksonnet/ksonnet/releases/download/v0.9.2/ks_0.9.2_linux_amd64.tar.gz$ tar xvf ks_0.9.2_linux_amd64.tar.gz$ sudo cp ks_0.9.2_linux_amd64/ks /usr/local/bin/$ ks versionksonnet version: 0.9.2jsonnet version: v0.9.5client-go version: 1.8</code></pre><h2 id="部署-Kubeflow"><a href="#部署-Kubeflow" class="headerlink" title="部署 Kubeflow"></a>部署 Kubeflow</h2><p>本節將說明如何利用 ksonnet 來部署 Kubeflow 到 Kubernetes 叢集中。首先在<code>master</code>節點初始化 ksonnet 應用程式目錄:</p><pre><code class="sh">$ ks init my-kubeflow</code></pre><blockquote><p>如果遇到以下問題的話,可以自己建立 GitHub Token 來存取 GitHub API,請參考 <a href="https://ksonnet.io/docs/tutorial#troubleshooting-github-rate-limiting-errors" target="_blank" rel="noopener">Github rate limiting errors</a>。</p><pre><code class="sh">ERROR GET https://api.github.com/repos/ksonnet/parts/commits/master: 403 API rate limit exceeded for 122.146.93.152.</code></pre></blockquote><p>接著安裝 Kubeflow 套件至應用程式目錄:</p><pre><code class="sh">$ cd my-kubeflow$ ks registry add kubeflow github.com/kubeflow/kubeflow/tree/master/kubeflow$ ks pkg install kubeflow/core$ ks pkg install kubeflow/tf-serving$ ks pkg install kubeflow/tf-job</code></pre><p>然後建立 Kubeflow 核心元件,該元件包含 JupyterHub 與 TensorFlow job controller:</p><pre><code class="sh">$ kubectl create namespace kubeflow$ kubectl create clusterrolebinding tf-admin --clusterrole=cluster-admin --serviceaccount=default:tf-job-operator$ ks generate core kubeflow-core --name=kubeflow-core --namespace=kubeflow# 啟動收集匿名使用者使用量資訊,如果不想開啟則忽略$ ks param set kubeflow-core reportUsage true$ ks param set kubeflow-core usageId $(uuidgen)# 部署 Kubeflow$ ks param set kubeflow-core jupyterHubServiceType LoadBalancer$ ks apply default -c kubeflow-core</code></pre><blockquote><p>詳細使用量資訊請參考 <a href="https://github.com/kubeflow/kubeflow/blob/master/user_guide.md#usage-reporting" target="_blank" rel="noopener">Usage Reporting</a>。</p></blockquote><p>完成後檢查 Kubeflow 元件部署結果:</p><pre><code class="sh">$ kubectl -n kubeflow get po -o wideNAME READY STATUS RESTARTS AGE IP NODEambassador-7956cf5c7f-6hngq 2/2 Running 0 34m 10.244.41.132 kube-gpu-node1ambassador-7956cf5c7f-jgxnd 2/2 Running 0 34m 10.244.152.134 kube-gpu-node2ambassador-7956cf5c7f-jww2d 2/2 Running 0 34m 10.244.41.133 kube-gpu-node1spartakus-volunteer-8c659d4f5-bg7kn 1/1 Running 0 34m 10.244.152.135 kube-gpu-node2tf-hub-0 1/1 Running 0 34m 10.244.152.133 kube-gpu-node2tf-job-operator-78757955b-2jbdh 1/1 Running 0 34m 10.244.41.131 kube-gpu-node1</code></pre><p>這時候就可以登入 Jupyter Notebook,但這邊需要修改 Kubernetes Service,透過以下指令進行:</p><pre><code class="sh">$ kubectl -n kubeflow get svc -o wideNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTORambassador ClusterIP 10.101.157.91 <none> 80/TCP 45m service=ambassadorambassador-admin ClusterIP 10.107.24.138 <none> 8877/TCP 45m service=ambassadork8s-dashboard ClusterIP 10.111.128.104 <none> 443/TCP 45m k8s-app=kubernetes-dashboardtf-hub-0 ClusterIP None <none> 8000/TCP 45m app=tf-hubtf-hub-lb ClusterIP 10.105.47.253 <none> 80/TCP 45m app=tf-hub# 修改 svc 將 Type 修改成 LoadBalancer,並且新增 externalIPs 指定為 Master IP。$ kubectl -n kubeflow edit svc tf-hub-lb...spec: type: LoadBalancer externalIPs: - 172.22.132.41...</code></pre><h2 id="測試-Kubeflow"><a href="#測試-Kubeflow" class="headerlink" title="測試 Kubeflow"></a>測試 Kubeflow</h2><p>開始測試前先建立一個 NFS PV 來提供給 Kubeflow Jupyter 使用:</p><pre><code class="sh">$ cat <<EOF | kubectl create -f -apiVersion: v1kind: PersistentVolumemetadata: name: nfs-pvspec: capacity: storage: 20Gi accessModes: - ReadWriteOnce nfs: server: 172.22.132.41 path: /nfs-dataEOF</code></pre><p>完成後連接 <code>http://Master_IP</code>,並輸入<code>任意帳號密碼</code>進行登入。</p><p><img src="/images/kubeflow/1.png" alt=""></p><p>登入後點選<code>Start My Server</code>按鈕來建立 Server 的 Spawner options,預設會有多種映像檔可以使用:</p><ul><li>CPU:gcr.io/kubeflow-images-staging/tensorflow-notebook-cpu。</li><li>GPU:gcr.io/kubeflow-images-staging/tensorflow-notebook-gpu。</li></ul><blockquote><p>這邊也使用以下 GCP 建構的映像檔做測試使用(GPU 當前為 CUDA 8):</p><ul><li>gcr.io/kubeflow/tensorflow-notebook-cpu:latest</li><li>gcr.io/kubeflow/tensorflow-notebook-gpu:latest</li></ul><p>若 CUDA 版本不同,請自行修改 <a href="https://github.com/GoogleCloudPlatform/container-engine-accelerators/blob/master/example/tensorflow-notebook-image" target="_blank" rel="noopener">GCP Tensorflow Notebook image</a> 或是 <a href="https://github.com/kubeflow/kubeflow/tree/master/components/k8s-model-server/images" target="_blank" rel="noopener">Kubeflow Tensorflow Notebook image </a>重新建構。</p><p>如果使用 GPU 請執行以下指令確認是否可被分配資源:</p><pre><code class="sh">$ kubectl get nodes "-o=custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu"NAME GPUkube-gpu-master1 <none>kube-gpu-node1 1kube-gpu-node2 1</code></pre></blockquote><p>最後點選<code>Spawn</code>來完成建立 Server,如下圖所示:</p><p><img src="/images/kubeflow/2.png" alt=""></p><blockquote><p>這邊先用 CPU 進行測試,由於本篇是安裝 CUDA 9.1 + cuDNN 7,因此要自己建構映像檔。</p></blockquote><p>接著等 Kubernetes 下載映像檔後,就會正常啟動,如下圖所示:</p><p><img src="/images/kubeflow/3.png" alt=""></p><p>當正常啟動後,點選<code>New > Python 3</code>建立一個 Notebook 並貼上以下範例程式:</p><pre><code class="python">from __future__ import print_functionimport tensorflow as tfhello = tf.constant('Hello TensorFlow!')s = tf.Session()print(s.run(hello))</code></pre><p>正確執行會如以下圖所示:</p><p><img src="/images/kubeflow/4.png" alt=""></p><blockquote><p>若想關閉叢集的話,可以點選<code>Control Plane</code>。</p></blockquote><p>另外由於 Kubeflow 會安裝 TF Operator 來管理 TFJob,這邊可以透過 Kubernetes 來手動建立 Job:</p><pre><code class="sh">$ kubectl create -f https://raw.githubusercontent.com/kubeflow/tf-operator/master/examples/tf_job.yaml$ kubectl get poNAME READY STATUS RESTARTS AGEexample-job-ps-qq6x-0-pdx7v 1/1 Running 0 5mexample-job-ps-qq6x-1-2mpfp 1/1 Running 0 5mexample-job-worker-qq6x-0-m5fm5 1/1 Running 0 5m</code></pre><p>若想從 Kubernetes 叢集刪除 Kubeflow 相關元件的話,可執行下列指令達成:</p><pre><code class="sh">$ ks delete default -c kubeflow-core</code></pre>]]></content>
<summary type="html">
<p><a href="https://github.com/kubeflow/kubeflow" target="_blank" rel="noopener">Kubeflow</a> 是 Google 開源的機器學習工具,目標是簡化在 Kubernetes 上運行機器學習的過程,使之更簡單、可攜帶與可擴展。Kubeflow 目標不是在於重建其他服務,而是提供一個最佳開發系統來部署到各種基礎設施架構中,另外由於使用 Kubernetes 來做為基礎,因此只要有 Kubernetes 的地方,都能夠執行 Kubeflow。</p>
<center><img src="/images/kubeflow/logo.png" alt=""></center>
</summary>
<category term="TensorFlow" scheme="https://kairen.github.io/categories/TensorFlow/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="TensorFlow" scheme="https://kairen.github.io/tags/TensorFlow/"/>
<category term="GPU" scheme="https://kairen.github.io/tags/GPU/"/>
<category term="DL/ML" scheme="https://kairen.github.io/tags/DL-ML/"/>
</entry>
<entry>
<title>Kubernetes NVIDIA Device Plugins</title>
<link href="https://kairen.github.io/2018/03/01/kubernetes/nvidia-device-plugin/"/>
<id>https://kairen.github.io/2018/03/01/kubernetes/nvidia-device-plugin/</id>
<published>2018-03-01T09:08:54.000Z</published>
<updated>2018-05-24T07:56:11.797Z</updated>
<content type="html"><![CDATA[<p><a href="https://kubernetes.io/docs/concepts/cluster-administration/device-plugins/" target="_blank" rel="noopener">Device Plugins</a> 是 Kubernetes v1.8 版本開始加入的 Alpha 功能,目標是結合 Extended Resource 來支援 GPU、FPGA、高效能 NIC、InfiniBand 等硬體設備介接的插件,這樣好處在於硬體供應商不需要修改 Kubernetes 核心程式,只需要依據 <a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/resource-management/device-plugin.md" target="_blank" rel="noopener">Device Plugins 介面</a>來實作特定硬體設備插件,就能夠提供給 Kubernetes Pod 使用。而本篇會稍微提及 Device Plugin 原理,並說明如何使用 NVIDIA device plugin。</p><p>P.S. 傳統的<code>alpha.kubernetes.io/nvidia-gpu</code>將於 1.11 版本移除,因此與 GPU 相關的排程與部署原始碼都將從 Kubernetes 核心移除。<br><a id="more"></a></p><h2 id="Device-Plugins-原理"><a href="#Device-Plugins-原理" class="headerlink" title="Device Plugins 原理"></a>Device Plugins 原理</h2><p>Device Plugins 主要提供了一個 <a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/resource-management/device-plugin.md" target="_blank" rel="noopener">gRPC 介面</a>來給廠商實現<code>ListAndWatch()</code>與<code>Allocate()</code>等 gRPC 方法,並監聽節點的<code>/var/lib/kubelet/device-plugins/</code>目錄中的 gRPC Server Unix Socket,這邊可以參考官方文件 <a href="https://kubernetes.io/docs/concepts/cluster-administration/device-plugins/" target="_blank" rel="noopener">Device Plugins</a>。一旦啟動 Device Plugins 時,透過 Kubelet Unix Socket 註冊,並提供該 plugin 的 Unix Socket 名稱、API 版本號與插件資源名稱(vendor-domain/resource,例如 nvidia.com/gpu),接著 Kubelet 會將這些曝露到 Node 狀態以便 Scheduler 使用。</p><p>Unix Socket 範例:</p><pre><code class="sh">$ ls /var/lib/kubelet/device-plugins/kubelet_internal_checkpoint kubelet.sock nvidia.sock</code></pre><p>一些 Device Plugins 列表:</p><ul><li><a href="https://github.com/NVIDIA/k8s-device-plugin" target="_blank" rel="noopener">NVIDIA GPU</a></li><li><a href="https://github.com/hustcat/k8s-rdma-device-plugin" target="_blank" rel="noopener">RDMA</a></li><li><a href="https://github.com/kubevirt/kubernetes-device-plugins" target="_blank" rel="noopener">Kubevirt</a></li><li><a href="https://github.com/vikaschoudhary16/sfc-device-plugin" target="_blank" rel="noopener">SFC</a></li></ul><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>本次安裝作業系統採用<code>Ubuntu 16.04 Server</code>,測試環境為實體機器:</p><table><thead><tr><th>IP Address</th><th>Role</th><th>vCPU</th><th>RAM</th><th>Extra Device</th></tr></thead><tbody><tr><td>172.22.132.51</td><td>gpu-node1</td><td>8</td><td>16G</td><td>GTX 1060 3G</td></tr><tr><td>172.22.132.52</td><td>gpu-node2</td><td>8</td><td>16G</td><td>GTX 1060 3G</td></tr><tr><td>172.22.132.53</td><td>master1</td><td>8</td><td>16G</td><td>無</td></tr></tbody></table><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>安裝 Device Plugin 前,需要確保以下條件達成:</p><ul><li>所有節點正確安裝指定版本的 NVIDIA driver、CUDA、Docker、NVIDIA Docker。請參考 <a href="https://kairen.github.io/2018/02/17/container/docker-nvidia-install/">安裝 Nvidia Docker 2</a>。</li><li>所有節點以 kubeadm 部署成 Kubernetes v1.9+ 叢集。請參考 <a href="https://kairen.github.io/2016/09/29/kubernetes/deploy/kubeadm/">用 kubeadm 部署 Kubernetes 叢集</a>。</li></ul><h2 id="安裝-NVIDIA-Device-Plugin"><a href="#安裝-NVIDIA-Device-Plugin" class="headerlink" title="安裝 NVIDIA Device Plugin"></a>安裝 NVIDIA Device Plugin</h2><p>若上述要求以符合,再開始前需要在<code>每台 GPU worker 節點</code>修改<code>/lib/systemd/system/docker.service</code>檔案,將 Docker default runtime 改成 nvidia,依照以下內容來修改:</p><pre><code class="sh">...ExecStart=/usr/bin/dockerd -H fd:// --default-runtime=nvidia...</code></pre><blockquote><p>這邊也可以修改<code>/etc/docker/daemon.json</code>檔案,請參考 <a href="https://docs.docker.com/config/daemon/" target="_blank" rel="noopener">Configure and troubleshoot the Docker daemon</a>。</p></blockquote><p>完成後儲存,並重新啟動 Docker:</p><pre><code class="sh">$ sudo systemctl daemon-reload && sudo systemctl restart docker</code></pre><p>接著由於 v1.9 版本的 Device Plugins 還是處於 Alpha 中,因此需要手動修改<code>每台 GPU worker 節點</code>的 kubelet drop-in <code>/etc/systemd/system/kubelet.service.d/10-kubeadm.conf</code>檔案,這邊在<code>KUBELET_CERTIFICATE_ARGS</code>加入一行 args:</p><pre><code class="sh">...Environment="KUBELET_EXTRA_ARGS=--feature-gates=DevicePlugins=true"...</code></pre><p>完成後儲存,並重新啟動 kubelet:</p><pre><code class="sh">$ sudo systemctl daemon-reload && sudo systemctl restart kubelet</code></pre><p>確認上述完成,接著在<code>Master</code>節點安裝 NVIDIA Device Plugins,透過以下方式來進行:</p><pre><code class="sh">$ kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v1.9/nvidia-device-plugin.ymldaemonset "nvidia-device-plugin-daemonset" created$ kubectl -n kube-system get po -o wideNAME READY STATUS RESTARTS AGE IP NODE...nvidia-device-plugin-daemonset-bncw2 1/1 Running 0 2m 10.244.41.135 kube-gpu-node1nvidia-device-plugin-daemonset-ddnhd 1/1 Running 0 2m 10.244.152.132 kube-gpu-node2</code></pre><h2 id="測試-GPU"><a href="#測試-GPU" class="headerlink" title="測試 GPU"></a>測試 GPU</h2><p>首先執行以下指令確認是否可被分配資源:</p><pre><code class="sh">$ kubectl get nodes "-o=custom-columns=NAME:.metadata.name,GPU:.status.allocatable.nvidia\.com/gpu"NAME GPUmaster1 <none>gpu-node1 1gpu-node2 1</code></pre><p>當 NVIDIA Device Plugins 部署完成後,即可建立一個簡單範例來進行測試:</p><pre><code class="sh">$ cat <<EOF | kubectl create -f -apiVersion: v1kind: Podmetadata: name: gpu-podspec: restartPolicy: Never containers: - image: nvidia/cuda name: cuda command: ["nvidia-smi"] resources: limits: nvidia.com/gpu: 1EOFpod "gpu-pod" created$ kubectl get po -a -o wideNAME READY STATUS RESTARTS AGE IP NODEgpu-pod 0/1 Completed 0 50s 10.244.41.136 kube-gpu-node1$ kubectl logs gpu-podThu Mar 15 07:28:45 2018+-----------------------------------------------------------------------------+| NVIDIA-SMI 390.30 Driver Version: 390.30 ||-------------------------------+----------------------+----------------------+| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC || Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. ||===============================+======================+======================|| 0 GeForce GTX 106... Off | 00000000:01:00.0 Off | N/A || 0% 41C P8 10W / 120W | 0MiB / 3019MiB | 1% Default |+-------------------------------+----------------------+----------------------++-----------------------------------------------------------------------------+| Processes: GPU Memory || GPU PID Type Process name Usage ||=============================================================================|| No running processes found |+-----------------------------------------------------------------------------+</code></pre><p>從上面結果可以看到 Kubernetes Pod 正確的使用到 NVIDIA GPU,這邊也可以利用 TensorFlow 來進行測試,新增一個檔案<code>tf-gpu-dp.yml</code>加入以下內容:</p><pre><code class="yaml">apiVersion: apps/v1kind: Deploymentmetadata: name: tf-gpuspec: replicas: 1 selector: matchLabels: app: tf-gpu template: metadata: labels: app: tf-gpu spec: containers: - name: tensorflow image: tensorflow/tensorflow:latest-gpu ports: - containerPort: 8888 resources: limits: nvidia.com/gpu: 1</code></pre><p>利用 kubectl 建立 Deployment,並曝露 Jupyter port:</p><pre><code class="sh">$ kubectl create -f tf-gpu-dp.ymldeployment "tf-gpu" created$ kubectl expose deploy tf-gpu --type LoadBalancer --external-ip=172.22.132.53 --port 8888 --target-port 8888service "tf-gpu" exposed$ kubectl get po,svc -o wideNAME READY STATUS RESTARTS AGE IP NODEpo/tf-gpu-6f9464f94b-pq8t9 1/1 Running 0 1m 10.244.152.133 kube-gpu-node2NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTORsvc/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 23h <none>svc/tf-gpu LoadBalancer 10.105.104.183 172.22.132.53 8888:30093/TCP 12s app=tf-gpu</code></pre><blockquote><p>確認無誤後,透過 logs 指令取得 token,並登入<code>Jupyter Notebook</code>,這邊 IP 為 <master1_ip>:8888。</master1_ip></p></blockquote><p>這邊執行一個簡單範例,並在用 logs 指令查看就能看到 Pod 透過 NVIDIA Device Plugins 使用 GPU:</p><pre><code class="sh">$ kubectl logs -f tf-gpu-6f9464f94b-pq8t9...2018-03-15 07:37:22.022052: I tensorflow/core/platform/cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA2018-03-15 07:37:22.155254: I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:898] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero2018-03-15 07:37:22.155565: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1212] Found device 0 with properties:name: GeForce GTX 1060 3GB major: 6 minor: 1 memoryClockRate(GHz): 1.7845pciBusID: 0000:01:00.0totalMemory: 2.95GiB freeMemory: 2.88GiB2018-03-15 07:37:22.155586: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1312] Adding visible gpu devices: 02018-03-15 07:37:22.346590: I tensorflow/core/common_runtime/gpu/gpu_device.cc:993] Creating TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 2598 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060 3GB, pci bus id: 0000:01:00.0, compute capability: 6.1)</code></pre><p>最後因為目前 Pod 會綁整張 GPU 來使用,因此當無多餘顯卡時就讓 Pod 處於 Pending:</p><pre><code class="sh">$ kubectl scale deploy tf-gpu --replicas=3$ kubectl get po -o wideNAME READY STATUS RESTARTS AGE IP NODEtf-gpu-6f9464f94b-42xcf 0/1 Pending 0 4s <none> <none>tf-gpu-6f9464f94b-nxdw5 1/1 Running 0 12s 10.244.41.138 kube-gpu-node1tf-gpu-6f9464f94b-pq8t9 1/1 Running 0 5m 10.244.152.133 kube-gpu-node2</code></pre>]]></content>
<summary type="html">
<p><a href="https://kubernetes.io/docs/concepts/cluster-administration/device-plugins/" target="_blank" rel="noopener">Device Plugins</a> 是 Kubernetes v1.8 版本開始加入的 Alpha 功能,目標是結合 Extended Resource 來支援 GPU、FPGA、高效能 NIC、InfiniBand 等硬體設備介接的插件,這樣好處在於硬體供應商不需要修改 Kubernetes 核心程式,只需要依據 <a href="https://github.com/kubernetes/community/blob/master/contributors/design-proposals/resource-management/device-plugin.md" target="_blank" rel="noopener">Device Plugins 介面</a>來實作特定硬體設備插件,就能夠提供給 Kubernetes Pod 使用。而本篇會稍微提及 Device Plugin 原理,並說明如何使用 NVIDIA device plugin。</p>
<p>P.S. 傳統的<code>alpha.kubernetes.io/nvidia-gpu</code>將於 1.11 版本移除,因此與 GPU 相關的排程與部署原始碼都將從 Kubernetes 核心移除。<br>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="NVIDIA GPU" scheme="https://kairen.github.io/tags/NVIDIA-GPU/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
</entry>
<entry>
<title>安裝 NVIDIA Docker 2 來讓容器使用 GPU</title>
<link href="https://kairen.github.io/2018/02/17/container/docker-nvidia-install/"/>
<id>https://kairen.github.io/2018/02/17/container/docker-nvidia-install/</id>
<published>2018-02-17T09:08:54.000Z</published>
<updated>2018-05-30T01:51:59.431Z</updated>
<content type="html"><![CDATA[<p>本篇主要介紹如何使用 <a href="https://github.com/NVIDIA/nvidia-docker" target="_blank" rel="noopener">NVIDIA Docker v2</a> 來讓容器使用 GPU,過去 NVIDIA Docker v1 需要使用 nvidia-docker 來取代 Docker 執行 GPU image,或是透過手動掛載 NVIDIA driver 與 CUDA 來使 Docker 能夠編譯與執行 GPU 應用程式 image,而新版本的 Docker 則可以透過 –runtime 來選擇使用 NVIDIA Docker v2 的 Runtime 來執行 GPU 應用。</p><a id="more"></a><p>安裝前需要確認滿足以下幾點:</p><ul><li>GNU/Linux x86_64 with kernel version > 3.10</li><li>Docker CE or EE == v18.03.1</li><li>NVIDIA GPU with Architecture > Fermi (2.1)</li><li>NVIDIA drivers ~= 361.93 (untested on older versions)</li></ul><p>首先透過 APT 安裝 Docker CE or EE v17.12 版本:</p><pre><code class="sh">$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -$ echo "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial edge" | sudo tee /etc/apt/sources.list.d/docker.list$ sudo apt-get update && sudo apt-get install -y docker-ce=18.03.1~ce-0~ubuntu</code></pre><p>接著透過 APT 安裝 NVIDIA Driver(v390.30) 與 CUDA 9.1:</p><pre><code class="sh">$ wget http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_9.1.85-1_amd64.deb$ sudo dpkg -i cuda-repo-ubuntu1604_9.1.85-1_amd64.deb$ sudo apt-key adv --fetch-keys http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/7fa2af80.pub$ sudo apt-get update && sudo apt-get install -y cuda</code></pre><p>測試 NVIDIA Dirver 與 CUDA 是否有安裝完成:</p><pre><code class="sh">$ cat /usr/local/cuda/version.txtCUDA Version 9.1.85$ sudo nvidia-smiTue Mar 13 06:10:39 2018+-----------------------------------------------------------------------------+| NVIDIA-SMI 390.30 Driver Version: 390.30 ||-------------------------------+----------------------+----------------------+| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC || Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. ||===============================+======================+======================|| 0 GeForce GTX 106... Off | 00000000:01:00.0 Off | N/A || 0% 33C P0 15W / 120W | 0MiB / 3019MiB | 2% Default |+-------------------------------+----------------------+----------------------++-----------------------------------------------------------------------------+| Processes: GPU Memory || GPU PID Type Process name Usage ||=============================================================================|| No running processes found |+-----------------------------------------------------------------------------+</code></pre><p>確認上述無誤後,接著安裝 NVIDIA Docker v2,這邊透過 APT 來進行安裝:</p><pre><code class="sh">$ 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 && sudo apt-get install -y nvidia-docker2=2.0.3+docker18.03.1-1$ sudo pkill -SIGHUP dockerd</code></pre><p>測試 NVIDIA runtime,這邊下載 NVIDIA image 來進行測試:</p><pre><code class="sh">$ docker run --runtime=nvidia --rm nvidia/cuda nvidia-smi...+-----------------------------------------------------------------------------+| NVIDIA-SMI 390.30 Driver Version: 390.30 ||-------------------------------+----------------------+----------------------+| GPU Name Persistence-M| Bus-Id Disp.A | Volatile Uncorr. ECC || Fan Temp Perf Pwr:Usage/Cap| Memory-Usage | GPU-Util Compute M. ||===============================+======================+======================|| 0 GeForce GTX 106... Off | 00000000:01:00.0 Off | N/A || 0% 35C P0 15W / 120W | 0MiB / 3019MiB | 2% Default |+-------------------------------+----------------------+----------------------++-----------------------------------------------------------------------------+| Processes: GPU Memory || GPU PID Type Process name Usage ||=============================================================================|| No running processes found |+-----------------------------------------------------------------------------+</code></pre><p>透過 TensorFlow GPU image 來進行測試,這邊執行後登入 IP:8888 執行簡單範例程式:</p><pre><code class="sh">$ docker run --runtime=nvidia -it -p 8888:8888 tensorflow/tensorflow:latest-gpu...2018-03-13 06:44:21.719705: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1212] Found device 0 with properties:name: GeForce GTX 1060 3GB major: 6 minor: 1 memoryClockRate(GHz): 1.7845pciBusID: 0000:01:00.0totalMemory: 2.95GiB freeMemory: 2.88GiB2018-03-13 06:44:21.719728: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1312] Adding visible gpu devices: 02018-03-13 06:44:21.919097: I tensorflow/core/common_runtime/gpu/gpu_device.cc:993] Creating TensorFlow device (/job:localhost/replica:0/task:0/device:GPU:0 with 2598 MB memory) -> physical GPU (device: 0, name: GeForce GTX 1060 3GB, pci bus id: 0000:01:00.0, compute capability: 6.1)</code></pre>]]></content>
<summary type="html">
<p>本篇主要介紹如何使用 <a href="https://github.com/NVIDIA/nvidia-docker" target="_blank" rel="noopener">NVIDIA Docker v2</a> 來讓容器使用 GPU,過去 NVIDIA Docker v1 需要使用 nvidia-docker 來取代 Docker 執行 GPU image,或是透過手動掛載 NVIDIA driver 與 CUDA 來使 Docker 能夠編譯與執行 GPU 應用程式 image,而新版本的 Docker 則可以透過 –runtime 來選擇使用 NVIDIA Docker v2 的 Runtime 來執行 GPU 應用。</p>
</summary>
<category term="Container" scheme="https://kairen.github.io/categories/Container/"/>
<category term="Docker" scheme="https://kairen.github.io/tags/Docker/"/>
<category term="Container" scheme="https://kairen.github.io/tags/Container/"/>
<category term="NVIDIA GPU" scheme="https://kairen.github.io/tags/NVIDIA-GPU/"/>
</entry>
<entry>
<title>Ceph Luminous CRUSH map 400000000000000 問題</title>
<link href="https://kairen.github.io/2018/02/11/ceph/luminous-crush-issue/"/>
<id>https://kairen.github.io/2018/02/11/ceph/luminous-crush-issue/</id>
<published>2018-02-11T09:08:54.000Z</published>
<updated>2018-02-27T05:53:45.454Z</updated>
<content type="html"><![CDATA[<p>在 Ceph Luminous(v12) 版本中,預設開啟了一些 Kernel 特性,其中首先遇到的一般是 400000000000000 問題,即<code>CEPH_FEATURE_NEW_OSDOPREPLY_ENCODING</code>特性(可以從對照表得知<a href="http://cephnotes.ksperis.com/blog/2014/01/21/feature-set-mismatch-error-on-ceph-kernel-client/" target="_blank" rel="noopener">CEPH_FEATURE Table and Kernel Version</a>),剛問題需要在 Kernel 4.5+ 才能夠被支援,但如果不想升級可以依據本篇方式解決。</p><a id="more"></a><p>在 L 版本中,當建立 RBD 並且想要 Map 時,會發生 timeout 問題,這時候可以透過 journalctl 來查看問題,如以下:</p><pre><code class="sh">$ journalctl -xeFeb 12 08:36:57 kube-server2 kernel: libceph: mon0 172.22.132.51:6789 feature set mismatch, my 106b84a842a42 < server's 40106b84a842a42, missing 400000000000000</code></pre><p>查詢發現是 400000000000000 問題,這時可以選擇兩個解決方式:</p><ul><li>將作業系統更新到 Linux kernel v4.5+ 的版本。</li><li>修改 CRUSH 中的 tunables 參數。</li></ul><p>若想修改 CRUSH tunnables 參數,可以先到任一 Monitor 或者 Admin 節點中,執行以下指令:</p><pre><code class="sh">$ ceph osd crush tunables jewel$ ceph osd crush reweight-all</code></pre><p>只要執行以上指令即可。</p>]]></content>
<summary type="html">
<p>在 Ceph Luminous(v12) 版本中,預設開啟了一些 Kernel 特性,其中首先遇到的一般是 400000000000000 問題,即<code>CEPH_FEATURE_NEW_OSDOPREPLY_ENCODING</code>特性(可以從對照表得知<a href="http://cephnotes.ksperis.com/blog/2014/01/21/feature-set-mismatch-error-on-ceph-kernel-client/" target="_blank" rel="noopener">CEPH_FEATURE Table and Kernel Version</a>),剛問題需要在 Kernel 4.5+ 才能夠被支援,但如果不想升級可以依據本篇方式解決。</p>
</summary>
<category term="Ceph" scheme="https://kairen.github.io/categories/Ceph/"/>
<category term="Ceph" scheme="https://kairen.github.io/tags/Ceph/"/>
<category term="Storage" scheme="https://kairen.github.io/tags/Storage/"/>
</entry>
<entry>
<title>利用 RBAC + SA 進行 Kubectl 權限控管</title>
<link href="https://kairen.github.io/2018/01/08/kubernetes/rbac-sa-kubectl/"/>
<id>https://kairen.github.io/2018/01/08/kubernetes/rbac-sa-kubectl/</id>
<published>2018-01-08T09:08:54.000Z</published>
<updated>2018-03-20T10:06:07.651Z</updated>
<content type="html"><![CDATA[<p>這邊說明如何建立不同 Service account user,以及 RBAC 來定義存取規則,並綁定於指定 Service account ,以對指定 Namespace 中資源進行存取權限控制。</p><a id="more"></a><h2 id="Service-account"><a href="#Service-account" class="headerlink" title="Service account"></a>Service account</h2><p>Service account 一般使用情境方便是 Pod 中的行程呼叫 Kubernetes API 或者其他服務設計而成,這可能會跟 Kubernetes user account 有所混肴,但是由於 Service account 有別於 User account 是可以針對 Namespace 進行建立,因此這邊嘗試拿 Service account 來提供資訊給 kubectl 使用,並利用 RBAC 來設定存取規則,以限制該 Account 存取 API 的資源。</p><h2 id="RBAC"><a href="#RBAC" class="headerlink" title="RBAC"></a>RBAC</h2><p>RBAC(Role-Based Access Control)是從 Kubernetes 1.6 開始支援的存取控制機制,叢集管理者能夠對 User 或 Service account 的角色設定指定資源存取權限,在 RBAC 中,權限與角色相互關聯,其透過成為適當的角色成員,以獲取這些角色的存取權限,這比起過去 ABAC 來的方便使用、更簡化等好處。</p><h2 id="簡單範例"><a href="#簡單範例" class="headerlink" title="簡單範例"></a>簡單範例</h2><p>首先建立一個 Namespace 與 Service account:</p><pre><code class="sh">$ kubectl create ns dev$ kubectl -n dev create sa dev# 取得 secret 資訊$ SECRET=$(kubectl -n dev get sa dev -o go-template='{{range .secrets}}{{.name}}{{end}}')</code></pre><p>建立一個 dev.conf 設定檔,添加以下內容:</p><pre><code class="sh">$ API_SERVER="https://172.22.132.51:6443"$ CA_CERT=$(kubectl -n dev get secret ${SECRET} -o yaml | awk '/ca.crt:/{print $2}')$ cat <<EOF > dev.confapiVersion: v1kind: Configclusters:- cluster: certificate-authority-data: $CA_CERT server: $API_SERVER name: clusterEOF$ TOKEN=$(kubectl -n dev get secret ${SECRET} -o go-template='{{.data.token}}')$ kubectl config set-credentials dev-user \ --token=`echo ${TOKEN} | base64 -d` \ --kubeconfig=dev.conf$ kubectl config set-context default \ --cluster=cluster \ --user=dev-user \ --kubeconfig=dev.conf$ kubectl config use-context default \ --kubeconfig=dev.conf</code></pre><blockquote><ul><li>在不同作業系統中,<code>base64</code> 的 decode 指令不一樣,有些是 -D(OS X)。</li></ul></blockquote><p>新增 RBAC role 來限制 dev-user 存取權限:</p><pre><code class="sh">$ cat <<EOF > dev-user-role.ymlkind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata: namespace: dev name: dev-user-podrules:- apiGroups: ["*"] resources: ["pods", "pods/log"] verbs: ["get", "watch", "list", "update", "create", "delete"]EOF$ kubectl create rolebinding dev-view-pod \ --role=dev-user-pod \ --serviceaccount=dev:dev \ --namespace=dev</code></pre><blockquote><ul><li>apiGroups 為不同 API 的群組,如 rbac.authorization.k8s.io,[“*”] 為允許存取全部。</li><li>resources 為 API 存取資源,如 pods、pods/log、pod/exec,[“*”] 為允許存取全部。</li><li>verbs 為 API 存取方法,如 get、list、watch、create、update、 delete、proxy,[“*”] 為允許存取全部。</li></ul></blockquote><p>透過 kubectl 確認權限設定沒問題:</p><pre><code class="shell=">$ kubectl --kubeconfig=dev.conf get poError from server (Forbidden): pods is forbidden: User "system:serviceaccount:dev:dev" cannot list pods in the namespace "default"$ kubectl -n dev --kubeconfig=dev.conf run nginx --image nginx --port 80 --restart=Never$ kubectl -n dev --kubeconfig=dev.conf get poNAME READY STATUS RESTARTS AGEnginx 1/1 Running 0 39s$ kubectl -n dev --kubeconfig=dev.conf logs -f nginx10.244.102.64 - - [04/Jan/2018:06:42:36 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.47.0" "-"$ kubectl -n dev --kubeconfig=dev.conf exec -ti nginx shError from server (Forbidden): pods "nginx" is forbidden: User "system:serviceaccount:dev:dev" cannot create pods/exec in the namespace "dev"</code></pre><blockquote><ul><li>也可以用<code>export KUBECONFIG=dev.conf</code>來設定使用的 config。</li></ul></blockquote>]]></content>
<summary type="html">
<p>這邊說明如何建立不同 Service account user,以及 RBAC 來定義存取規則,並綁定於指定 Service account ,以對指定 Namespace 中資源進行存取權限控制。</p>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Docker" scheme="https://kairen.github.io/tags/Docker/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="Kubernetes RBAC" scheme="https://kairen.github.io/tags/Kubernetes-RBAC/"/>
</entry>
<entry>
<title>多租戶 Kubernetes 部署方案 Stackube</title>
<link href="https://kairen.github.io/2017/12/20/openstack/stackube/"/>
<id>https://kairen.github.io/2017/12/20/openstack/stackube/</id>
<published>2017-12-20T08:23:01.000Z</published>
<updated>2018-03-29T08:46:19.875Z</updated>
<content type="html"><![CDATA[<p><a href="https://github.com/openstack/stackube" target="_blank" rel="noopener">Stackube</a>是一個 Kubernetes-centric 的 OpenStack 發行版本(架構如下圖所示),該專案結合 Kubernetes 與 OpenStack 的技術來達到真正的 Kubernetes 租戶隔離,如租戶實例採用 Frakti 來進行隔離、網路採用 Neutron OVS 達到每個 Namespace 擁有獨立的網路資源等。本篇會簡單介紹如何用 DevStack 建立測試用 Stackube。</p><a id="more"></a><p><img src="/images/openstack/stackube-arch.png" alt=""></p><blockquote><p>P.S. 目前 Stackube 已經不再維護,僅作為測試與研究程式碼使用。</p></blockquote><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>本次安裝作業系統採用<code>Ubuntu 16.04 Server</code>,測試環境為實體機器:</p><table><thead><tr><th>IP Address</th><th>Host</th><th>vCPU</th><th>RAM</th></tr></thead><tbody><tr><td>172.22.132.42</td><td>stackube1</td><td>8</td><td>32G</td></tr></tbody></table><h2 id="部署-Stackube"><a href="#部署-Stackube" class="headerlink" title="部署 Stackube"></a>部署 Stackube</h2><p>首先新增 Devstack 使用的 User:</p><pre><code class="sh">$ sudo useradd -s /bin/bash -d /opt/stack -m stack$ echo "stack ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/stack$ sudo su - stack</code></pre><p>透過 Git 取得 Ocata 版本的 Devstack:</p><pre><code class="sh">$ git clone https://git.openstack.org/openstack-dev/devstack -b stable/ocata$ cd devstack</code></pre><p>取得單節範例設定檔:</p><pre><code class="sh">$ curl -sSL https://raw.githubusercontent.com/kairen/stackube/master/devstack/local.conf.sample -o local.conf</code></pre><p>完成後即可進行安裝:</p><pre><code class="sh">$ ./stack.sh</code></pre><h2 id="測試基本功能"><a href="#測試基本功能" class="headerlink" title="測試基本功能"></a>測試基本功能</h2><p>完成後,就可以透過以下指令來引入 Kubernetes 與 OpenStack client 需要的環境變數:</p><pre><code class="sh">$ export KUBECONFIG=/opt/stack/admin.conf$ source /opt/stack/devstack/openrc admin admin</code></pre><p>Stackube 透過 CRD 新增了一個新抽象物件 Tenant,可以直接透過 Kubernetes API 來建立一個租戶,並將該租戶與 Kubernettes namespace 做綁定:</p><pre><code class="sh">$ cat <<EOF | kubectl create -f -apiVersion: "stackube.kubernetes.io/v1"kind: Tenantmetadata: name: testspec: username: "test" password: "password"EOF$ kubectl get namespace testNAME STATUS AGEtest Active 2h$ kubectl -n test get network test -o yamlapiVersion: stackube.kubernetes.io/v1kind: Networkmetadata: clusterName: "" creationTimestamp: 2017-12-20T06:03:33Z generation: 0 name: test namespace: test resourceVersion: "4631" selfLink: /apis/stackube.kubernetes.io/v1/namespaces/test/networks/test uid: e9aef6fa-3316-11e8-8b66-448a5bd481f0spec: cidr: 10.244.0.0/16 gateway: 10.244.0.1 networkID: ""status: state: Active</code></pre><p>檢查 Neutron 網路狀況:</p><pre><code class="sh">$ neutron net-list+--------------------------------------+----------------------+----------------------------------+----------------------------------------------------------+| id | name | tenant_id | subnets |+--------------------------------------+----------------------+----------------------------------+----------------------------------------------------------+| 2a8e3b54-d76f-48a9-8380-7c2a5513b1fe | kube-test-test | f2f25d24fd9a4616bff41b018e8725d2 | 625909a9-6abf-4661-b259-ffc625bdf681 10.244.0.0/16 |</code></pre><blockquote><p>P.S. 這邊個人只是研究 Stackube CNI,故不針對其於進行測試,可自行參考 <a href="https://stackube.readthedocs.io/en/latest/user_guide.html" target="_blank" rel="noopener">Stackube</a>。</p></blockquote>]]></content>
<summary type="html">
<p><a href="https://github.com/openstack/stackube" target="_blank" rel="noopener">Stackube</a>是一個 Kubernetes-centric 的 OpenStack 發行版本(架構如下圖所示),該專案結合 Kubernetes 與 OpenStack 的技術來達到真正的 Kubernetes 租戶隔離,如租戶實例採用 Frakti 來進行隔離、網路採用 Neutron OVS 達到每個 Namespace 擁有獨立的網路資源等。本篇會簡單介紹如何用 DevStack 建立測試用 Stackube。</p>
</summary>
<category term="OpenStack" scheme="https://kairen.github.io/categories/OpenStack/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="Openstack" scheme="https://kairen.github.io/tags/Openstack/"/>
</entry>
<entry>
<title>Deploy OpenStack on Kubernetes using OpenStack-helm</title>
<link href="https://kairen.github.io/2017/11/29/openstack/openstack-helm/"/>
<id>https://kairen.github.io/2017/11/29/openstack/openstack-helm/</id>
<published>2017-11-29T08:23:01.000Z</published>
<updated>2018-01-16T05:55:26.137Z</updated>
<content type="html"><![CDATA[<p><a href="https://github.com/openstack/openstack-helm" target="_blank" rel="noopener">OpenStack Helm</a> 是一個提供部署建置的專案,其目的是為了推動 OpenStack 生產環境的解決方案,而這種部署方式採用容器化方式,並執行於 Kubernetes 系統上來提供 OpenStack 服務的管理與排程等使用。</p><p><img src="https://i.imgur.com/8sMjowM.png" alt=""></p><a id="more"></a><p>而本篇文章將說明如何建置多節點的 OpenStack Helm 環境來進行功能驗證。</p><h2 id="節點與安裝版本"><a href="#節點與安裝版本" class="headerlink" title="節點與安裝版本"></a>節點與安裝版本</h2><p>以下為各節點的硬體資訊。</p><table><thead><tr><th>IP Address</th><th>Role</th><th>CPU</th><th>Memory</th></tr></thead><tbody><tr><td>172.22.132.10</td><td>vip</td><td>-</td><td>-</td></tr><tr><td>172.22.132.101</td><td>master1</td><td>4</td><td>16G</td></tr><tr><td>172.22.132.22</td><td>node1</td><td>4</td><td>16G</td></tr><tr><td>172.22.132.24</td><td>node2</td><td>4</td><td>16G</td></tr><tr><td>172.22.132.28</td><td>node3</td><td>4</td><td>16G</td></tr></tbody></table><p>使用 Kernel、作業系統與軟體版本:</p><table><thead><tr><th></th><th>資訊描述</th></tr></thead><tbody><tr><td>作業系統版本</td><td>16.04.3 LTS (Xenial Xerus)</td></tr><tr><td>Kernel 版本</td><td>4.4.0-101-generic</td></tr><tr><td>Kubernetes</td><td>v1.8.4</td></tr><tr><td>Docker</td><td>Docker 17.09.0-ce</td></tr><tr><td>Calico</td><td>v2.6.2</td></tr><tr><td>Etcd</td><td>v3.2.9</td></tr><tr><td>Ceph</td><td>v10.2.10</td></tr><tr><td>Helm</td><td>v2.7.0</td></tr></tbody></table><h2 id="Kubernetes-叢集"><a href="#Kubernetes-叢集" class="headerlink" title="Kubernetes 叢集"></a>Kubernetes 叢集</h2><p>本節說明如何建立 Kubernetes Cluster,這邊採用 <a href="https://github.com/kairen/kube-ansible" target="_blank" rel="noopener">kube-ansible</a> 工具來建立。</p><h3 id="初始化與設定基本需求"><a href="#初始化與設定基本需求" class="headerlink" title="初始化與設定基本需求"></a>初始化與設定基本需求</h3><p>安裝前需要確認以下幾個項目:</p><ul><li>所有節點的網路之間可以互相溝通。</li><li><code>部署節點</code>對其他節點不需要 SSH 密碼即可登入。</li><li>所有節點都擁有 Sudoer 權限,並且不需要輸入密碼。</li><li>所有節點需要安裝<code>Python</code>。</li><li>所有節點需要設定<code>/etc/host</code>解析到所有主機。</li><li><code>部署節點</code>需要安裝 <strong>Ansible >= 2.4.0</strong>。</li></ul><pre><code class="shell"># Ubuntu install$ sudo apt-get install -y software-properties-common$ sudo apt-add-repository -y ppa:ansible/ansible$ sudo apt-get update && sudo apt-get install -y ansible git make# CentOS install$ sudo yum install -y epel-release$ sudo yum -y install ansible cowsay</code></pre><h3 id="安裝與設定-Kube-ansible"><a href="#安裝與設定-Kube-ansible" class="headerlink" title="安裝與設定 Kube-ansible"></a>安裝與設定 Kube-ansible</h3><p>首先取得最新穩定版本的 Kubernetes Ansible:</p><pre><code class="shell">$ git clone https://github.com/kairen/kube-ansible.git$ cd kube-ansible</code></pre><p>然後新增<code>inventory</code>檔案來描述要部屬的主機角色:</p><pre><code>[etcds]172.22.132.101 ansible_user=ubuntu[masters]172.22.132.101 ansible_user=ubuntu[nodes]172.22.132.22 ansible_user=ubuntu172.22.132.24 ansible_user=ubuntu172.22.132.28 ansible_user=ubuntu[kube-cluster:children]mastersnodes[kube-addon:children]masters</code></pre><p>接著編輯<code>group_vars/all.yml</code>檔案來添加與修改以下內容:</p><pre><code class="yaml"># Kubenrtes version, only support 1.8.0+.kube_version: 1.8.4# CNI plugin# Support: flannel, calico, canal, weave or router.network: calicopod_network_cidr: 10.244.0.0/16# CNI opts: flannel(--iface=enp0s8), calico(interface=enp0s8), canal(enp0s8).cni_iface: ""# Kubernetes cluster network.cluster_subnet: 10.96.0kubernetes_service_ip: "{{ cluster_subnet }}.1"service_ip_range: "{{ cluster_subnet }}.0/12"service_node_port_range: 30000-32767api_secure_port: 5443# Highly Available configuration.haproxy: truekeepalived: true # set `lb_vip_address` as keepalived vip, if this enable.keepalived_vip_interface: "{{ ansible_default_ipv4.interface }}"lb_vip_address: 172.22.132.10lb_secure_port: 6443lb_api_url: "https://{{ lb_vip_address }}:{{ lb_secure_port }}"etcd_iface: ""insecure_registrys:- "172.22.132.253:5000" # 有需要的話ceph_cluster: true</code></pre><blockquote><ul><li>這邊<code>insecure_registrys</code>為 deploy 節點的 Docker registry ip 與 port。</li><li>Extra addons 部分針對需求開啟,預設不會開啟。</li><li>若想把 Etcd, VIP 與 Network plugin 綁定在指定網路的話,請修改<code>etcd_iface</code>, <code>keepalived_vip_interface</code> 與 <code>cni_iface</code>。其中<code>cni_iface</code>需要針對不同 Plugin 來改變。</li><li>若想要修改部署版本的 Packages 的話,請編輯<code>roles/commons/packages/defaults/main.yml</code>來修改版本。</li></ul></blockquote><p>接著由於 OpenStack-helm 使用的 Kubernetes Controller Manager 不同,因此要修改<code>roles/commons/container-images/defaults/main.yml</code>的 Image 來源如下:</p><pre><code class="yaml">... manager: name: kube-controller-manager repos: kairen/ tag: "v{{ kube_version }}"...</code></pre><p>完後成修改 storage roles 設定版本並進行安裝。</p><p>首先編輯<code>roles/storage/ceph/defaults/main.yml</code>修改版本為以下:</p><pre><code class="yaml">ceph_version: jewel</code></pre><p>接著編輯<code>roles/storage/ceph/tasks/main.yml</code>修改成以下內容:</p><pre><code class="yaml">---- name: Install Ceph dependency packages include_tasks: install-ceph.yml# - name: Create and copy generator config file# include_tasks: gen-config.yml# delegate_to: "{{ groups['masters'][0] }}"# run_once: true## - name: Deploy Ceph components on Kubernetes# include_tasks: ceph-on-k8s.yml# delegate_to: "{{ groups['masters'][0] }}"# run_once: true# - name: Label all storage nodes# shell: "kubectl label nodes node-type=storage"# delegate_to: "{{ groups['masters'][0] }}"# run_once: true# ignore_errors: true</code></pre><h3 id="部屬-Kubernetes-叢集"><a href="#部屬-Kubernetes-叢集" class="headerlink" title="部屬 Kubernetes 叢集"></a>部屬 Kubernetes 叢集</h3><p>確認<code>group_vars/all.yml</code>與其他設定都完成後,就透過 ansible ping 來檢查叢集狀態:</p><pre><code class="shell">$ ansible -i inventory all -m ping...172.22.132.101 | SUCCESS => { "changed": false, "failed": false, "ping": "pong"}...</code></pre><p>接著就可以透過以下指令進行部署叢集:</p><pre><code class="shell">$ ansible-playbook cluster.yml...TASK [cni : Apply calico network daemonset] *********************************************************************************************************************************changed: [172.22.132.101 -> 172.22.132.101]PLAY RECAP ******************************************************************************************************************************************************************172.22.132.101 : ok=155 changed=58 unreachable=0 failed=0172.22.132.22 : ok=117 changed=28 unreachable=0 failed=0172.22.132.24 : ok=50 changed=18 unreachable=0 failed=0172.22.132.28 : ok=51 changed=19 unreachable=0 failed=0</code></pre><p>完成後,進入<code>master</code>節點執行以下指令確認叢集:</p><pre><code class="shell">$ kubectl get nodeNAME STATUS ROLES AGE VERSIONkube-master1 Ready master 1h v1.8.4kube-node1 Ready <none> 1h v1.8.4kube-node2 Ready <none> 1h v1.8.4kube-node3 Ready <none> 1h v1.8.4$ kubectl -n kube-system get poNAME READY STATUS RESTARTS AGEcalico-node-js6qp 2/2 Running 2 1hcalico-node-kx9xn 2/2 Running 2 1hcalico-node-lxrjl 2/2 Running 2 1hcalico-node-vwn5f 2/2 Running 2 1hcalico-policy-controller-d549764f6-9kn9l 1/1 Running 1 1hhaproxy-kube-master1 1/1 Running 1 1hkeepalived-kube-master1 1/1 Running 1 1hkube-apiserver-kube-master1 1/1 Running 1 1hkube-controller-manager-kube-master1 1/1 Running 1 1hkube-dns-7bd4879dc9-kxmx6 3/3 Running 3 1hkube-proxy-7tqkm 1/1 Running 1 1hkube-proxy-glzmm 1/1 Running 1 1hkube-proxy-krqxs 1/1 Running 1 1hkube-proxy-x9zdb 1/1 Running 1 1hkube-scheduler-kube-master1 1/1 Running 1 1h</code></pre><p>檢查 kube-dns 是否連 host 都能夠解析:</p><pre><code class="shell">$ nslookup kubernetesServer: 10.96.0.10Address: 10.96.0.10#53Non-authoritative answer:Name: kubernetes.default.svc.cluster.localAddress: 10.96.0.1</code></pre><p>接著安裝 Ceph 套件:</p><pre><code class="sh">$ ansible-playbook storage.yml</code></pre><h2 id="OpenStack-helm-叢集"><a href="#OpenStack-helm-叢集" class="headerlink" title="OpenStack-helm 叢集"></a>OpenStack-helm 叢集</h2><p>本節說明如何建立 OpenStack on Kubernetes 使用 Helm,部署是使用 <a href="https://github.com/openstack/openstack-helm" target="_blank" rel="noopener">openstack-helm</a>。過程將透過 OpenStack-helm 來在 Kubernetes 建置 OpenStack 叢集。以下所有操作都在<code>kube-master1</code>上進行。</p><h3 id="Helm-init"><a href="#Helm-init" class="headerlink" title="Helm init"></a>Helm init</h3><p>在開始前需要先將 Helm 進行初始化,以提供後續使用,然而這邊由於使用到 RBAC 的關係,因此需建立一個 Service account 來提供給 Helm 使用:</p><pre><code class="shell">$ kubectl -n kube-system create sa tiller$ kubectl create clusterrolebinding tiller --clusterrole cluster-admin --serviceaccount=kube-system:tiller$ helm init --service-account tiller</code></pre><blockquote><p>由於 <code>kube-ansible</code> 本身包含 Helm 工具, 因此不需要自己安裝,只需要依據上面指令進行 init 即可。</p></blockquote><p>新增一個檔案<code>openrc</code>來提供環境變數:</p><pre><code class="shell">export HELM_HOST=$(kubectl describe svc/tiller-deploy -n kube-system | awk '/Endpoints/{print $2}')export OSD_CLUSTER_NETWORK=172.22.132.0/24export OSD_PUBLIC_NETWORK=172.22.132.0/24export WORK_DIR=localexport CEPH_RGW_KEYSTONE_ENABLED=true</code></pre><blockquote><ul><li><code>OSD_CLUSTER_NETWORK</code>與<code>OSD_PUBLIC_NETWORK</code>都是使用實體機器網路,這邊 daemonset 會使用 hostNetwork。</li><li><code>CEPH_RGW_KEYSTONE_ENABLED</code> 在 Kubernetes 版本有點不穩,可依需求關閉。</li></ul></blockquote><p>完成後,透過 source 指令引入:</p><pre><code class="shell">$ source openrc$ helm versionClient: &version.Version{SemVer:"v2.7.0", GitCommit:"08c1144f5eb3e3b636d9775617287cc26e53dba4", GitTreeState:"clean"}Server: &version.Version{SemVer:"v2.7.0", GitCommit:"08c1144f5eb3e3b636d9775617287cc26e53dba4", GitTreeState:"clean"}</code></pre><h3 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h3><p>首先透過 Kubernetes label 來標示每個節點的角色:</p><pre><code class="shell">kubectl label nodes openstack-control-plane=enabled --allkubectl label nodes ceph-mon=enabled --allkubectl label nodes ceph-osd=enabled --allkubectl label nodes ceph-mds=enabled --allkubectl label nodes ceph-rgw=enabled --allkubectl label nodes ceph-mgr=enabled --allkubectl label nodes openvswitch=enabled --allkubectl label nodes openstack-compute-node=enabled --all</code></pre><blockquote><p>這邊為了避免過度的節點污染,因此不讓 masters 充當任何角色:</p><pre><code class="shell">kubectl label nodes kube-master1 openstack-control-plane-kubectl label nodes kube-master1 ceph-mon-kubectl label nodes kube-master1 ceph-osd-kubectl label nodes kube-master1 ceph-mds-kubectl label nodes kube-master1 ceph-rgw-kubectl label nodes kube-master1 ceph-mgr-kubectl label nodes kube-master1 openvswitch-kubectl label nodes kube-master1 openstack-compute-node-</code></pre></blockquote><p>由於使用 Kubernetes RBAC,而目前 openstack-helm 有 bug,不會正確建立 Service account 的 ClusterRoleBindings,因此要手動建立(這邊偷懶一下直接使用 Admin roles):</p><pre><code class="shell">$ cat <<EOF | kubectl create -f -apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRoleBindingmetadata: name: ceph-sa-adminroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-adminsubjects: - apiGroup: rbac.authorization.k8s.io kind: User name: system:serviceaccount:ceph:defaultEOF$ cat <<EOF | kubectl create -f -apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRoleBindingmetadata: name: openstack-sa-adminroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-adminsubjects: - apiGroup: rbac.authorization.k8s.io kind: User name: system:serviceaccount:openstack:defaultEOF</code></pre><blockquote><p>若沒有建立的話,會有類似以下的錯誤資訊:</p><pre><code>Error from server (Forbidden): error when creating "STDIN": secrets is forbidden: User "system:serviceaccount:ceph:default" cannot create secrets in the namespace "ceph"</code></pre></blockquote><p>下載最新版本 openstack-helm 專案:</p><pre><code class="shell">$ git clone https://github.com/openstack/openstack-helm.git$ cd openstack-helm</code></pre><p>現在須建立 openstack-helm chart 來提供部署使用:</p><pre><code class="shell">$ helm serve &$ helm repo add local http://localhost:8879/charts$ make# output...1 chart(s) linted, no failuresif [ -d congress ]; then helm package congress; fiSuccessfully packaged chart and saved it to: /root/openstack-helm/congress-0.1.0.tgzmake[1]: Leaving directory '/root/openstack-helm'</code></pre><h3 id="Ceph-Chart"><a href="#Ceph-Chart" class="headerlink" title="Ceph Chart"></a>Ceph Chart</h3><p>在部署 OpenStack 前,需要先部署 Ceph 叢集,這邊透過以下指令建置:</p><pre><code class="shell">$ helm install --namespace=ceph ${WORK_DIR}/ceph --name=ceph \ --set endpoints.identity.namespace=openstack \ --set endpoints.object_store.namespace=ceph \ --set endpoints.ceph_mon.namespace=ceph \ --set ceph.rgw_keystone_auth=${CEPH_RGW_KEYSTONE_ENABLED} \ --set network.public=${OSD_PUBLIC_NETWORK} \ --set network.cluster=${OSD_CLUSTER_NETWORK} \ --set deployment.storage_secrets=true \ --set deployment.ceph=true \ --set deployment.rbd_provisioner=true \ --set deployment.client_secrets=false \ --set deployment.rgw_keystone_user_and_endpoints=false \ --set bootstrap.enabled=true</code></pre><blockquote><ul><li><code>CEPH_RGW_KEYSTONE_ENABLED</code>是否啟動 Ceph RGW Keystone。</li><li><code>OSD_PUBLIC_NETWORK</code>與<code>OSD_PUBLIC_NETWORK</code>為 Ceph 叢集網路。</li></ul></blockquote><p>成功安裝 Ceph chart 後,就可以透過 kubectl 來查看結果:</p><pre><code class="shell">$ kubectl -n ceph get poNAME READY STATUS RESTARTS AGEceph-mds-57798cc8f6-r898r 1/1 Running 2 10minceph-mon-96p9r 1/1 Running 0 10minceph-mon-check-bd8875f87-whvhd 1/1 Running 0 10minceph-mon-qkj95 1/1 Running 0 10minceph-mon-zx7tw 1/1 Running 0 10minceph-osd-5fvfl 1/1 Running 0 10minceph-osd-kvw9b 1/1 Running 0 10minceph-osd-wcf5j 1/1 Running 0 10minceph-rbd-provisioner-599ff9575-mdqnf 1/1 Running 0 10minceph-rbd-provisioner-599ff9575-vpcr6 1/1 Running 0 10minceph-rgw-7c8c5d4f6f-8fq9c 1/1 Running 3 10min</code></pre><p>確認 Ceph 叢集建立正確:</p><pre><code class="shell">$ MON_POD=$(kubectl get pods \ --namespace=ceph \ --selector="application=ceph" \ --selector="component=mon" \ --no-headers | awk '{ print $1; exit }')$ kubectl exec -n ceph ${MON_POD} -- ceph -s cluster 02ad8724-dee0-4f55-829f-3cc24e2c7571 health HEALTH_WARN too many PGs per OSD (856 > max 300) monmap e2: 3 mons at {kube-node1=172.22.132.22:6789/0,kube-node2=172.22.132.24:6789/0,kube-node3=172.22.132.28:6789/0} election epoch 8, quorum 0,1,2 kube-node1,kube-node2,kube-node3 fsmap e5: 1/1/1 up {0=mds-ceph-mds-57798cc8f6-r898r=up:active} osdmap e21: 3 osds: 3 up, 3 in flags sortbitwise,require_jewel_osds pgmap v6053: 856 pgs, 10 pools, 3656 bytes data, 191 objects 43091 MB used, 2133 GB / 2291 GB avail 856 active+clean</code></pre><blockquote><p>Warn 這邊忽略,OSD 機器太少….。</p></blockquote><p>接著為了讓 Ceph 可以在其他 Kubernetes namespace 中存取 PVC,這邊要產生 client secret key 於 openstack namespace 中來提供給 OpenStack 元件使用,這邊執行以下 Chart 來產生:</p><pre><code class="shell">$ helm install --namespace=openstack ${WORK_DIR}/ceph --name=ceph-openstack-config \ --set endpoints.identity.namespace=openstack \ --set endpoints.object_store.namespace=ceph \ --set endpoints.ceph_mon.namespace=ceph \ --set ceph.rgw_keystone_auth=${CEPH_RGW_KEYSTONE_ENABLED} \ --set network.public=${OSD_PUBLIC_NETWORK} \ --set network.cluster=${OSD_CLUSTER_NETWORK} \ --set deployment.storage_secrets=false \ --set deployment.ceph=false \ --set deployment.rbd_provisioner=false \ --set deployment.client_secrets=true \ --set deployment.rgw_keystone_user_and_endpoints=false</code></pre><p>檢查 pod 與 secret 是否建立成功:</p><pre><code class="shell">$ kubectl -n openstack get secret,po -aNAME TYPE DATA AGEsecrets/default-token-q2r87 kubernetes.io/service-account-token 3 2msecrets/pvc-ceph-client-key kubernetes.io/rbd 1 2mNAME READY STATUS RESTARTS AGEpo/ceph-namespace-client-key-generator-w84n4 0/1 Completed 0 2m</code></pre><h3 id="OpenStack-Chart"><a href="#OpenStack-Chart" class="headerlink" title="OpenStack Chart"></a>OpenStack Chart</h3><p>確認沒問題後,就可以開始部署 OpenStack chart 了。首先先安裝 Mariadb cluster:</p><pre><code class="shell">$ helm install --name=mariadb ./mariadb --namespace=openstack</code></pre><blockquote><p>這邊跑超久…34mins…,原因可能是 Storage 效能問題。</p></blockquote><p>這邊正確執行後,會依序依據 StatefulSet 建立起 Pod 組成 Cluster:</p><pre><code class="shell">$ kubectl -n openstack get poNAME READY STATUS RESTARTS AGEmariadb-0 1/1 Running 0 37mmariadb-1 1/1 Running 0 4mmariadb-2 1/1 Running 0 2m</code></pre><p>當 Mariadb cluster 完成後,就可以部署一些需要的服務,如 RabbitMQ, OVS 等:</p><pre><code class="shell">helm install --name=memcached ./memcached --namespace=openstackhelm install --name=etcd-rabbitmq ./etcd --namespace=openstackhelm install --name=rabbitmq ./rabbitmq --namespace=openstackhelm install --name=ingress ./ingress --namespace=openstackhelm install --name=libvirt ./libvirt --namespace=openstackhelm install --name=openvswitch ./openvswitch --namespace=openstack</code></pre><p>上述指令若正確執行的話,會分別建立起以下服務:</p><pre><code class="shell">$ kubectl -n openstack get poNAME READY STATUS RESTARTS AGEetcd-5c9bc8c97f-jpm2k 1/1 Running 0 4mingress-api-jhjjv 1/1 Running 0 4mingress-api-nx5qm 1/1 Running 0 4mingress-api-vr8xf 1/1 Running 0 4mingress-error-pages-86b9db69cc-mmq4p 1/1 Running 0 4mlibvirt-94xq5 1/1 Running 0 4mlibvirt-lzfzs 1/1 Running 0 4mlibvirt-vswxb 1/1 Running 0 4mmariadb-0 1/1 Running 0 42mmariadb-1 1/1 Running 0 9mmariadb-2 1/1 Running 0 7mmemcached-746fcc894-cwhpr 1/1 Running 0 4mopenvswitch-db-7fjr2 1/1 Running 0 4mopenvswitch-db-gtmcr 1/1 Running 0 4mopenvswitch-db-hqmbt 1/1 Running 0 4mopenvswitch-vswitchd-gptp9 1/1 Running 0 4mopenvswitch-vswitchd-s4cwd 1/1 Running 0 4mopenvswitch-vswitchd-tvxlg 1/1 Running 0 4mrabbitmq-6fdb8879df-6vmz8 1/1 Running 0 4mrabbitmq-6fdb8879df-875zz 1/1 Running 0 4mrabbitmq-6fdb8879df-h5wj6 1/1 Running 0 4m</code></pre><p>一旦所有基礎服務與元件都建立完成後,就可以開始部署 OpenStack 的專案 Chart,首先建立 Keystone 來提供身份認證服務:</p><pre><code class="shell">$ helm install --namespace=openstack --name=keystone ./keystone \ --set pod.replicas.api=1$ kubectl -n openstack get po -l application=keystoneNAME READY STATUS RESTARTS AGEkeystone-api-74c774d448-dkqmj 0/1 Init:0/1 0 4mkeystone-bootstrap-xpdtl 0/1 Init:0/1 0 4mkeystone-db-sync-2bxtp 1/1 Running 0 4m 0 29s</code></pre><blockquote><p>這邊由於叢集規模問題,副本數都為一份。</p></blockquote><p>這時候會先建立 Keystone database tables,完成後將啟動 API pod,如以下結果:</p><pre><code class="shell">$ kubectl -n openstack get po -l application=keystoneNAME READY STATUS RESTARTS AGEkeystone-api-74c774d448-dkqmj 1/1 Running 0 11m</code></pre><p>如果安裝支援 RGW 的 Keystone endpoint 的話,可以使用以下方式建立:</p><pre><code class="shell">$ helm install --namespace=openstack ${WORK_DIR}/ceph --name=radosgw-openstack \ --set endpoints.identity.namespace=openstack \ --set endpoints.object_store.namespace=ceph \ --set endpoints.ceph_mon.namespace=ceph \ --set ceph.rgw_keystone_auth=${CEPH_RGW_KEYSTONE_ENABLED} \ --set network.public=${OSD_PUBLIC_NETWORK} \ --set network.cluster=${OSD_CLUSTER_NETWORK} \ --set deployment.storage_secrets=false \ --set deployment.ceph=false \ --set deployment.rbd_provisioner=false \ --set deployment.client_secrets=false \ --set deployment.rgw_keystone_user_and_endpoints=true$ kubectl -n openstack get po -a -l application=cephNAME READY STATUS RESTARTS AGEceph-ks-endpoints-vfg4l 0/3 Completed 0 1mceph-ks-service-tr9xt 0/1 Completed 0 1mceph-ks-user-z5tlt 0/1 Completed 0 1m</code></pre><p>完成後,安裝 Horizon chart 來提供 OpenStack dashbaord:</p><pre><code class="shell">$ helm install --namespace=openstack --name=horizon ./horizon \ --set network.enable_node_port=true \ --set network.node_port=31000$ kubectl -n openstack get po -l application=horizonNAME READY STATUS RESTARTS AGEhorizon-7c54878549-45668 1/1 Running 0 3m</code></pre><p>接著安裝 Glance chart 來提供 OpenStack image service。目前 Glance 支援幾個 backend storage:</p><ul><li><strong>pvc</strong>: 一個簡單的 Kubernetes PVCs 檔案後端。</li><li><strong>rbd</strong>: 使用 Ceph RBD 來儲存 images。</li><li><strong>radosgw</strong>: 使用 Ceph RGW 來儲存 images。</li><li><strong>swift</strong>: 另用 OpenStack switf 所提供的物件儲存服務來儲存 images.</li></ul><p>這邊可以利用以下方式來部署不同的儲存後端:</p><pre><code class="shell">$ export GLANCE_BACKEND=radosgw$ helm install --namespace=openstack --name=glance ./glance \ --set pod.replicas.api=1 \ --set pod.replicas.registry=1 \ --set storage=${GLANCE_BACKEND}$ kubectl -n openstack get po -l application=glanceNAME READY STATUS RESTARTS AGEglance-api-6cd8b856d6-lhzfs 1/1 Running 0 14mglance-registry-599f8b857b-gt4c6 1/1 Running 0 14m</code></pre><p>接著安裝 Neutron chart 來提供 OpenStack 虛擬化網路服務:</p><pre><code class="shell">$ helm install --namespace=openstack --name=neutron ./neutron \ --set pod.replicas.server=1$ kubectl -n openstack get po -l application=neutronNAME READY STATUS RESTARTS AGEneutron-dhcp-agent-2z49d 1/1 Running 0 9hneutron-dhcp-agent-d2kn8 1/1 Running 0 9hneutron-dhcp-agent-mrstl 1/1 Running 0 9hneutron-l3-agent-9f9mw 1/1 Running 0 9hneutron-l3-agent-cshzw 1/1 Running 0 9hneutron-l3-agent-j5vb9 1/1 Running 0 9hneutron-metadata-agent-6bfb2 1/1 Running 0 9hneutron-metadata-agent-kxk9c 1/1 Running 0 9hneutron-metadata-agent-w8cnl 1/1 Running 0 9hneutron-ovs-agent-j2549 1/1 Running 0 9hneutron-ovs-agent-plj9t 1/1 Running 0 9hneutron-ovs-agent-xlx7z 1/1 Running 0 9hneutron-server-6f45d74b87-6wmck 1/1 Running 0 9h</code></pre><p>接著安裝 Nova chart 來提供 OpenStack 虛擬機運算服務:</p><pre><code class="shell">$ helm install --namespace=openstack --name=nova ./nova \ --set pod.replicas.api_metadata=1 \ --set pod.replicas.osapi=1 \ --set pod.replicas.conductor=1 \ --set pod.replicas.consoleauth=1 \ --set pod.replicas.scheduler=1 \ --set pod.replicas.novncproxy=1$ kubectl -n openstack get po -l application=novaNAME READY STATUS RESTARTS AGEnova-api-metadata-84fdc84fd7-ldzrh 1/1 Running 1 9hnova-api-osapi-57f599c6d6-pqrjv 1/1 Running 0 9hnova-compute-8rvm9 2/2 Running 0 9hnova-compute-cbk7h 2/2 Running 0 9hnova-compute-tf2jb 2/2 Running 0 9hnova-conductor-7f5bc76d79-bxwnb 1/1 Running 0 9hnova-consoleauth-6946b5884f-nss6n 1/1 Running 0 9hnova-novncproxy-d789dccff-7ft9q 1/1 Running 0 9hnova-placement-api-f7c79578f-hj2g9 1/1 Running 0 9hnova-scheduler-778866f555-mmksg 1/1 Running 0 9h</code></pre><p>接著安裝 Cinfer chart 來提供 OpenStack 區塊儲存服務:</p><pre><code class="shell">$ helm install --namespace=openstack --name=cinder ./cinder \ --set pod.replicas.api=1$ kubectl -n openstack get po -l application=cinderNAME READY STATUS RESTARTS AGEcinder-api-5cc89f5467-ssm8k 1/1 Running 0 32mcinder-backup-67c4d8dfdb-zfsq4 1/1 Running 0 32mcinder-scheduler-65f9dd49bf-6htwg 1/1 Running 0 32mcinder-volume-69bfb67b4-bmst2 1/1 Running 0 32m</code></pre><p>(option)都完成後,將 Horizon 服務透過 NodePort 方式曝露出來(如果上面 Horizon chart 沒反應的話),執行以下指令編輯:</p><pre><code class="shell">$ kubectl -n openstack edit svc horizon-int# 修改 type: type: NodePort</code></pre><p>最後連接 <a href="http://172.22.132.10:31000" target="_blank" rel="noopener">Horizon Dashboard</a>,預設使用者為<code>admin/password</code>。</p><p><img src="https://i.imgur.com/8yunUPy.png" alt=""></p><p>其他 Chart 可以利用以下方式來安裝,如 Heat chart:</p><pre><code class="shell">$ helm install --namespace=openstack --name=heat ./heat$ kubectl -n openstack get po -l application=heatNAME READY STATUS RESTARTS AGEheat-api-5cf45d9d44-qrt69 1/1 Running 0 13mheat-cfn-79dbf55789-bq4wh 1/1 Running 0 13mheat-cloudwatch-bcc4647f4-4c4ln 1/1 Running 0 13mheat-engine-55cfcc86f8-cct4m 1/1 Running 0 13m</code></pre><h2 id="測試-OpenStack-功能"><a href="#測試-OpenStack-功能" class="headerlink" title="測試 OpenStack 功能"></a>測試 OpenStack 功能</h2><p>在<code>kube-master1</code>安裝 openstack client:</p><pre><code class="shell">$ sudo pip install python-openstackclient</code></pre><p>建立<code>adminrc</code>來提供 client 環境變數:</p><pre><code class="shell">export OS_PROJECT_DOMAIN_NAME=defaultexport OS_USER_DOMAIN_NAME=defaultexport OS_PROJECT_NAME=adminexport OS_USERNAME=adminexport OS_PASSWORD=passwordexport OS_AUTH_URL=http://keystone.openstack.svc.cluster.local:80/v3export OS_IDENTITY_API_VERSION=3export OS_IMAGE_API_VERSION=2</code></pre><p>引入環境變數,並透過 openstack client 測試:</p><pre><code class="shell">$ source adminrc$ openstack user list+----------------------------------+-----------+| ID | Name |+----------------------------------+-----------+| 42f0d2e7823e413cb469f9cce731398a | glance || 556a2744811f450098f64b37d34192d4 | nova || a97ec73724aa4445b2d575be54f23240 | cinder || b28a5dcfd18948419e14acba7ecf6f63 | swift || d1f312b6bb7c460eb7d8d78c8bf350fc | admin || dc326aace22c4314a0100865fe4f57c2 | neutron || ec5d6d3c529847b29a1c9187599c8a6b | placement |+----------------------------------+-----------+</code></pre><p>接著需要設定對外網路來提供給 VM 存取,在有<code>neutron-l3-agent</code>節點上,新增一個腳本<code>setup-gateway.sh</code>:</p><pre><code class="shell">#!/bin/bashset -x# Assign IP address to br-exOSH_BR_EX_ADDR="172.24.4.1/24"OSH_EXT_SUBNET="172.24.4.0/24"sudo ip addr add ${OSH_BR_EX_ADDR} dev br-exsudo ip link set br-ex up# Setup masquerading on default route dev to public subnetDEFAULT_ROUTE_DEV="enp3s0"sudo iptables -t nat -A POSTROUTING -o ${DEFAULT_ROUTE_DEV} -s ${OSH_EXT_SUBNET} -j MASQUERADE</code></pre><blockquote><ul><li>網卡記得修改<code>DEFAULT_ROUTE_DEV</code>。</li><li>這邊因為沒有額外提供其他張網卡,所以先用 bridge 處理。</li></ul></blockquote><p>然後透過執行該腳本建立一個 bridge 網路:</p><pre><code class="shell">$ chmod u+x setup-gateway.sh$ ./setup-gateway.sh</code></pre><p>確認完成後,接著建立 Neutron ext net,透過以下指令進行建立:</p><pre><code class="shell">$ openstack network create \ --share --external \ --provider-physical-network external \ --provider-network-type flat ext-net$ openstack subnet create --network ext-net \ --allocation-pool start=172.24.4.10,end=172.24.4.100 \ --dns-nameserver 8.8.8.8 --gateway 172.24.4.1 \ --subnet-range 172.24.4.0/24 \ --no-dhcp ext-subnet$ openstack router create router1$ neutron router-gateway-set router1 ext-net</code></pre><p>直接進入 Dashboard 新增 Self-service Network:<br><img src="https://i.imgur.com/lqMrgqs.png" alt=""></p><p>加入到 router1:<br><img src="https://i.imgur.com/4aNnF3O.png" alt=""></p><p>完成後,就可以建立 instance,這邊都透過 Dashboard 來操作:<br><img src="https://i.imgur.com/fCYkxSC.png" alt=""></p><p>透過 SSH 進入 instance:<br><img src="https://i.imgur.com/Ijylo9X.png" alt=""></p><h2 id="Refers"><a href="#Refers" class="headerlink" title="Refers"></a>Refers</h2><ul><li><a href="https://github.com/portdirect/sydney-workshop" target="_blank" rel="noopener">sydney-workshop</a></li><li><a href="https://docs.openstack.org/openstack-helm/latest/install/multinode.html" target="_blank" rel="noopener">Multi Node</a></li></ul>]]></content>
<summary type="html">
<p><a href="https://github.com/openstack/openstack-helm" target="_blank" rel="noopener">OpenStack Helm</a> 是一個提供部署建置的專案,其目的是為了推動 OpenStack 生產環境的解決方案,而這種部署方式採用容器化方式,並執行於 Kubernetes 系統上來提供 OpenStack 服務的管理與排程等使用。</p>
<p><img src="https://i.imgur.com/8sMjowM.png" alt=""></p>
</summary>
<category term="OpenStack" scheme="https://kairen.github.io/categories/OpenStack/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="Helm" scheme="https://kairen.github.io/tags/Helm/"/>
<category term="Openstack" scheme="https://kairen.github.io/tags/Openstack/"/>
</entry>
<entry>
<title>Kubernetes v1.8.x 全手動苦工安裝教學(TL;DR)</title>
<link href="https://kairen.github.io/2017/10/27/kubernetes/deploy/manual-v1.8/"/>
<id>https://kairen.github.io/2017/10/27/kubernetes/deploy/manual-v1.8/</id>
<published>2017-10-27T09:08:54.000Z</published>
<updated>2018-04-03T07:22:27.413Z</updated>
<content type="html"><![CDATA[<p>Kubernetes 提供了許多雲端平台與作業系統的安裝方式,本章將以<code>全手動安裝方式</code>來部署 Kubernetes v1.8.x 版本,主要是學習與了解 Kubernetes 建置流程。若想要瞭解更多平台的部署可以參考 <a href="https://kubernetes.io/docs/getting-started-guides/" target="_blank" rel="noopener">Picking the Right Solution</a>來選擇自己最喜歡的方式。</p><p>本次安裝版本為:</p><ul><li>Kubernetes v1.8.6</li><li>CNI v0.6.0</li><li>Etcd v3.2.9</li><li>Calico v2.6.2</li><li>Docker v17.10.0-ce</li></ul><a id="more"></a><h2 id="預先準備資訊"><a href="#預先準備資訊" class="headerlink" title="預先準備資訊"></a>預先準備資訊</h2><p>本教學將以下列節點數與規格來進行部署 Kubernetes 叢集,作業系統可採用<code>Ubuntu 16.x</code>與<code>CentOS 7.x</code>:</p><table><thead><tr><th>IP Address</th><th>Role</th><th>CPU</th><th>Memory</th></tr></thead><tbody><tr><td>172.16.35.12</td><td>master1</td><td>1</td><td>2G</td></tr><tr><td>172.16.35.10</td><td>node1</td><td>1</td><td>2G</td></tr><tr><td>172.16.35.11</td><td>node2</td><td>1</td><td>2G</td></tr></tbody></table><blockquote><ul><li>這邊 master 為主要控制節點也是<code>部署節點</code>,node 為應用程式工作節點。</li><li>所有操作全部用<code>root</code>使用者進行(方便用),以 SRE 來說不推薦。</li><li>可以下載 <a href="https://kairen.github.io/files/manual-v1.8/Vagrantfile">Vagrantfile</a> 來建立 Virtual box 虛擬機叢集。</li></ul></blockquote><p>首先安裝前要確認以下幾項都已經準備完成:</p><ul><li>所有節點彼此網路互通,並且<code>master1</code> SSH 登入其他節點為 passwdless。</li><li>所有防火牆與 SELinux 已關閉。如 CentOS:</li></ul><pre><code class="sh">$ systemctl stop firewalld && systemctl disable firewalld$ setenforce 0$ vim /etc/selinux/configSELINUX=disabled</code></pre><ul><li>所有節點需要設定<code>/etc/host</code>解析到所有主機。</li></ul><pre><code>...172.16.35.10 node1172.16.35.11 node2172.16.35.12 master1</code></pre><ul><li>所有節點需要安裝<code>Docker</code>或<code>rtk</code>引擎。這邊採用<code>Docker</code>來當作容器引擎,安裝方式如下:</li></ul><pre><code class="sh">$ curl -fsSL "https://get.docker.com/" | sh</code></pre><blockquote><p>不管是在 <code>Ubuntu</code> 或 <code>CentOS</code> 都只需要執行該指令就會自動安裝最新版 Docker。<br>CentOS 安裝完成後,需要再執行以下指令:</p><pre><code class="sh">$ systemctl enable docker && systemctl start docker</code></pre></blockquote><p>編輯<code>/lib/systemd/system/docker.service</code>,在<code>ExecStart=..</code>上面加入:</p><pre><code>ExecStartPost=/sbin/iptables -A FORWARD -s 0.0.0.0/0 -j ACCEPT</code></pre><blockquote><p>完成後,重新啟動 docker 服務:</p><pre><code class="sh">$ systemctl daemon-reload && systemctl restart docker</code></pre></blockquote><ul><li>所有節點需要設定<code>/etc/sysctl.d/k8s.conf</code>的系統參數。</li></ul><pre><code class="sh">$ cat <<EOF > /etc/sysctl.d/k8s.confnet.ipv4.ip_forward = 1net.bridge.bridge-nf-call-ip6tables = 1net.bridge.bridge-nf-call-iptables = 1EOF$ sysctl -p /etc/sysctl.d/k8s.conf</code></pre><ul><li>在<code>master1</code>需要安裝<code>CFSSL</code>工具,這將會用來建立 TLS certificates。</li></ul><pre><code class="sh">$ export CFSSL_URL="https://pkg.cfssl.org/R1.2"$ wget "${CFSSL_URL}/cfssl_linux-amd64" -O /usr/local/bin/cfssl$ wget "${CFSSL_URL}/cfssljson_linux-amd64" -O /usr/local/bin/cfssljson$ chmod +x /usr/local/bin/cfssl /usr/local/bin/cfssljson</code></pre><h2 id="Etcd"><a href="#Etcd" class="headerlink" title="Etcd"></a>Etcd</h2><p>在開始安裝 Kubernetes 之前,需要先將一些必要系統建置完成,其中 Etcd 就是 Kubernetes 最重要的一環,Kubernetes 會將大部分資訊儲存於 Etcd 上,來提供給其他節點索取,以確保整個叢集運作與溝通正常。</p><h3 id="建立叢集-CA-與-Certificates"><a href="#建立叢集-CA-與-Certificates" class="headerlink" title="建立叢集 CA 與 Certificates"></a>建立叢集 CA 與 Certificates</h3><p>在這部分,將會需要產生 client 與 server 的各元件 certificates,並且替 Kubernetes admin user 產生 client 證書。</p><p>首先在<code>master1</code>建立<code>/etc/etcd/ssl</code>資料夾,然後進入目錄完成以下操作。</p><pre><code class="sh">$ mkdir -p /etc/etcd/ssl && cd /etc/etcd/ssl$ export PKI_URL="https://kairen.github.io/files/manual-v1.8/pki"</code></pre><p>下載<code>ca-config.json</code>與<code>etcd-ca-csr.json</code>檔案,並從 CSR json 產生 CA 金鑰與 Certificate:</p><pre><code class="sh">$ wget "${PKI_URL}/ca-config.json" "${PKI_URL}/etcd-ca-csr.json"$ cfssl gencert -initca etcd-ca-csr.json | cfssljson -bare etcd-ca$ ls etcd-ca*.pemetcd-ca-key.pem etcd-ca.pem</code></pre><p>下載<code>etcd-csr.json</code>檔案,並產生 Etcd certificate 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/etcd-csr.json"$ cfssl gencert \ -ca=etcd-ca.pem \ -ca-key=etcd-ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ etcd-csr.json | cfssljson -bare etcd$ ls etcd*.pemetcd-ca-key.pem etcd-ca.pem etcd-key.pem etcd.pem</code></pre><blockquote><p>若節點 IP 不同,需要修改<code>etcd-csr.json</code>的<code>hosts</code>。</p></blockquote><p>完成後刪除不必要檔案:</p><pre><code class="sh">$ rm -rf *.json</code></pre><p>確認<code>/etc/etcd/ssl</code>有以下檔案:</p><pre><code class="sh">$ ls /etc/etcd/ssletcd-ca.csr etcd-ca-key.pem etcd-ca.pem etcd.csr etcd-key.pem etcd.pem</code></pre><h3 id="Etcd-安裝與設定"><a href="#Etcd-安裝與設定" class="headerlink" title="Etcd 安裝與設定"></a>Etcd 安裝與設定</h3><p>首先在<code>master1</code>節點下載 Etcd,並解壓縮放到 /opt 底下與安裝:</p><pre><code class="sh">$ export ETCD_URL="https://github.com/coreos/etcd/releases/download"$ cd && wget -qO- --show-progress "${ETCD_URL}/v3.2.9/etcd-v3.2.9-linux-amd64.tar.gz" | tar -zx$ mv etcd-v3.2.9-linux-amd64/etcd* /usr/local/bin/ && rm -rf etcd-v3.2.9-linux-amd64</code></pre><p>完成後新建 Etcd Group 與 User,並建立 Etcd 設定檔目錄:</p><pre><code class="sh">$ groupadd etcd && useradd -c "Etcd user" -g etcd -s /sbin/nologin -r etcd</code></pre><p>下載<code>etcd</code>相關檔案,我們將來管理 Etcd:</p><pre><code class="sh">$ export ETCD_CONF_URL="https://kairen.github.io/files/manual-v1.8/master"$ wget "${ETCD_CONF_URL}/etcd.conf" -O /etc/etcd/etcd.conf$ wget "${ETCD_CONF_URL}/etcd.service" -O /lib/systemd/system/etcd.service</code></pre><blockquote><p>若與該教學 IP 不同的話,請用自己 IP 取代<code>172.16.35.12</code>。</p></blockquote><p>建立 var 存放資訊,然後啟動 Etcd 服務:</p><pre><code class="sh">$ mkdir -p /var/lib/etcd && chown etcd:etcd -R /var/lib/etcd /etc/etcd$ systemctl enable etcd.service && systemctl start etcd.service</code></pre><p>透過簡單指令驗證:</p><pre><code class="sh">$ export CA="/etc/etcd/ssl"$ ETCDCTL_API=3 etcdctl \ --cacert=${CA}/etcd-ca.pem \ --cert=${CA}/etcd.pem \ --key=${CA}/etcd-key.pem \ --endpoints="https://172.16.35.12:2379" \ endpoint health# outputhttps://172.16.35.12:2379 is healthy: successfully committed proposal: took = 641.36µs</code></pre><h2 id="Kubernetes-Master"><a href="#Kubernetes-Master" class="headerlink" title="Kubernetes Master"></a>Kubernetes Master</h2><p>Master 是 Kubernetes 的大總管,主要建置<code>apiserver</code>、<code>Controller manager</code>與<code>Scheduler</code>來元件管理所有 Node。本步驟將下載 Kubernetes 並安裝至 <code>master1</code>上,然後產生相關 TLS Cert 與 CA 金鑰,提供給叢集元件認證使用。</p><h3 id="下載-Kubernetes-元件"><a href="#下載-Kubernetes-元件" class="headerlink" title="下載 Kubernetes 元件"></a>下載 Kubernetes 元件</h3><p>首先透過網路取得所有需要的執行檔案:</p><pre><code class="sh"># Download Kubernetes$ export KUBE_URL="https://storage.googleapis.com/kubernetes-release/release/v1.8.6/bin/linux/amd64"$ wget "${KUBE_URL}/kubelet" -O /usr/local/bin/kubelet$ wget "${KUBE_URL}/kubectl" -O /usr/local/bin/kubectl$ chmod +x /usr/local/bin/kubelet /usr/local/bin/kubectl# Download CNI$ mkdir -p /opt/cni/bin && cd /opt/cni/bin$ export CNI_URL="https://github.com/containernetworking/plugins/releases/download"$ wget -qO- --show-progress "${CNI_URL}/v0.6.0/cni-plugins-amd64-v0.6.0.tgz" | tar -zx</code></pre><h3 id="建立叢集-CA-與-Certificates-1"><a href="#建立叢集-CA-與-Certificates-1" class="headerlink" title="建立叢集 CA 與 Certificates"></a>建立叢集 CA 與 Certificates</h3><p>在這部分,將會需要產生 client 與 server 的各元件 certificates,並且替 Kubernetes admin user 產生 client 證書。</p><p>一樣在<code>master1</code>建立<code>pki</code>資料夾,然後進入目錄完成以下操作。</p><pre><code class="sh">$ mkdir -p /etc/kubernetes/pki && cd /etc/kubernetes/pki$ export PKI_URL="https://kairen.github.io/files/manual-v1.8/pki"$ export KUBE_APISERVER="https://172.16.35.12:6443"</code></pre><p>下載<code>ca-config.json</code>與<code>ca-csr.json</code>檔案,並產生 CA 金鑰:</p><pre><code class="sh">$ wget "${PKI_URL}/ca-config.json" "${PKI_URL}/ca-csr.json"$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca$ ls ca*.pemca-key.pem ca.pem</code></pre><h4 id="API-server-certificate"><a href="#API-server-certificate" class="headerlink" title="API server certificate"></a>API server certificate</h4><p>下載<code>apiserver-csr.json</code>檔案,並產生 kube-apiserver certificate 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/apiserver-csr.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=10.96.0.1,172.16.35.12,127.0.0.1,kubernetes.default \ -profile=kubernetes \ apiserver-csr.json | cfssljson -bare apiserver$ ls apiserver*.pemapiserver-key.pem apiserver.pem</code></pre><blockquote><p>若節點 IP 不同,需要修改<code>-hostname</code>。</p></blockquote><h4 id="Front-proxy-certificate"><a href="#Front-proxy-certificate" class="headerlink" title="Front proxy certificate"></a>Front proxy certificate</h4><p>下載<code>front-proxy-ca-csr.json</code>檔案,並產生 Front proxy CA 金鑰,Front proxy 主要是用在 API aggregator 上:</p><pre><code class="sh">$ wget "${PKI_URL}/front-proxy-ca-csr.json"$ cfssl gencert \ -initca front-proxy-ca-csr.json | cfssljson -bare front-proxy-ca$ ls front-proxy-ca*.pemfront-proxy-ca-key.pem front-proxy-ca.pem</code></pre><p>下載<code>front-proxy-client-csr.json</code>檔案,並產生 front-proxy-client 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/front-proxy-client-csr.json"$ cfssl gencert \ -ca=front-proxy-ca.pem \ -ca-key=front-proxy-ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ front-proxy-client-csr.json | cfssljson -bare front-proxy-client$ ls front-proxy-client*.pemfront-proxy-client-key.pem front-proxy-client.pem</code></pre><h4 id="Bootstrap-Token"><a href="#Bootstrap-Token" class="headerlink" title="Bootstrap Token"></a>Bootstrap Token</h4><p>由於透過手動建立 CA 方式太過繁雜,只適合少量機器,因為每次簽證時都需要綁定 Node IP,隨機器增加會帶來很多困擾,因此這邊使用 TLS Bootstrapping 方式進行授權,由 apiserver 自動給符合條件的 Node 發送證書來授權加入叢集。</p><p>主要做法是 kubelet 啟動時,向 kube-apiserver 傳送 TLS Bootstrapping 請求,而 kube-apiserver 驗證 kubelet 請求的 token 是否與設定的一樣,若一樣就自動產生 kubelet 證書與金鑰。具體作法可以參考 <a href="https://kubernetes.io/docs/admin/kubelet-tls-bootstrapping/" target="_blank" rel="noopener">TLS bootstrapping</a>。</p><p>首先建立一個變數來產生<code>BOOTSTRAP_TOKEN</code>,並建立 <code>bootstrap.conf</code> 的 kubeconfig 檔:</p><pre><code class="sh">$ export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')$ cat <<EOF > /etc/kubernetes/token.csv${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"EOF# bootstrap set-cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../bootstrap.conf# bootstrap set-credentials$ kubectl config set-credentials kubelet-bootstrap \ --token=${BOOTSTRAP_TOKEN} \ --kubeconfig=../bootstrap.conf# bootstrap set-context$ kubectl config set-context default \ --cluster=kubernetes \ --user=kubelet-bootstrap \ --kubeconfig=../bootstrap.conf# bootstrap set default context$ kubectl config use-context default --kubeconfig=../bootstrap.conf</code></pre><blockquote><p>若想要用 CA 方式來認證,可以參考 <a href="https://gist.github.com/kairen/60ad8545b79e8e7aa9bdc8a2893df7a0" target="_blank" rel="noopener">Kubelet certificate</a>。</p></blockquote><h4 id="Admin-certificate"><a href="#Admin-certificate" class="headerlink" title="Admin certificate"></a>Admin certificate</h4><p>下載<code>admin-csr.json</code>檔案,並產生 admin certificate 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/admin-csr.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ admin-csr.json | cfssljson -bare admin$ ls admin*.pemadmin-key.pem admin.pem</code></pre><p>接著透過以下指令產生名稱為 <code>admin.conf</code> 的 kubeconfig 檔:</p><pre><code class="sh"># admin set-cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../admin.conf# admin set-credentials$ kubectl config set-credentials kubernetes-admin \ --client-certificate=admin.pem \ --client-key=admin-key.pem \ --embed-certs=true \ --kubeconfig=../admin.conf# admin set-context$ kubectl config set-context kubernetes-admin@kubernetes \ --cluster=kubernetes \ --user=kubernetes-admin \ --kubeconfig=../admin.conf# admin set default context$ kubectl config use-context kubernetes-admin@kubernetes \ --kubeconfig=../admin.conf</code></pre><h4 id="Controller-manager-certificate"><a href="#Controller-manager-certificate" class="headerlink" title="Controller manager certificate"></a>Controller manager certificate</h4><p>下載<code>manager-csr.json</code>檔案,並產生 kube-controller-manager certificate 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/manager-csr.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ manager-csr.json | cfssljson -bare controller-manager$ ls controller-manager*.pem</code></pre><blockquote><p>若節點 IP 不同,需要修改<code>manager-csr.json</code>的<code>hosts</code>。</p></blockquote><p>接著透過以下指令產生名稱為<code>controller-manager.conf</code>的 kubeconfig 檔:</p><pre><code class="sh"># controller-manager set-cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../controller-manager.conf# controller-manager set-credentials$ kubectl config set-credentials system:kube-controller-manager \ --client-certificate=controller-manager.pem \ --client-key=controller-manager-key.pem \ --embed-certs=true \ --kubeconfig=../controller-manager.conf# controller-manager set-context$ kubectl config set-context system:kube-controller-manager@kubernetes \ --cluster=kubernetes \ --user=system:kube-controller-manager \ --kubeconfig=../controller-manager.conf# controller-manager set default context$ kubectl config use-context system:kube-controller-manager@kubernetes \ --kubeconfig=../controller-manager.conf</code></pre><h4 id="Scheduler-certificate"><a href="#Scheduler-certificate" class="headerlink" title="Scheduler certificate"></a>Scheduler certificate</h4><p>下載<code>scheduler-csr.json</code>檔案,並產生 kube-scheduler certificate 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/scheduler-csr.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ scheduler-csr.json | cfssljson -bare scheduler$ ls scheduler*.pemscheduler-key.pem scheduler.pem</code></pre><blockquote><p>若節點 IP 不同,需要修改<code>scheduler-csr.json</code>的<code>hosts</code>。</p></blockquote><p>接著透過以下指令產生名稱為 <code>scheduler.conf</code> 的 kubeconfig 檔:</p><pre><code class="sh"># scheduler set-cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../scheduler.conf# scheduler set-credentials$ kubectl config set-credentials system:kube-scheduler \ --client-certificate=scheduler.pem \ --client-key=scheduler-key.pem \ --embed-certs=true \ --kubeconfig=../scheduler.conf# scheduler set-context$ kubectl config set-context system:kube-scheduler@kubernetes \ --cluster=kubernetes \ --user=system:kube-scheduler \ --kubeconfig=../scheduler.conf# scheduler set default context$ kubectl config use-context system:kube-scheduler@kubernetes \ --kubeconfig=../scheduler.conf</code></pre><h4 id="Kubelet-master-certificate"><a href="#Kubelet-master-certificate" class="headerlink" title="Kubelet master certificate"></a>Kubelet master certificate</h4><p>下載<code>kubelet-csr.json</code>檔案,並產生 master node certificate 證書:</p><pre><code class="sh">$ wget "${PKI_URL}/kubelet-csr.json"$ sed -i 's/$NODE/master1/g' kubelet-csr.json$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=master1,172.16.35.12 \ -profile=kubernetes \ kubelet-csr.json | cfssljson -bare kubelet$ ls kubelet*.pemkubelet-key.pem kubelet.pem</code></pre><blockquote><p>這邊<code>$NODE</code>需要隨節點名稱不同而改變。</p></blockquote><p>接著透過以下指令產生名稱為 <code>kubelet.conf</code> 的 kubeconfig 檔:</p><pre><code class="sh"># kubelet set-cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server=${KUBE_APISERVER} \ --kubeconfig=../kubelet.conf# kubelet set-credentials$ kubectl config set-credentials system:node:master1 \ --client-certificate=kubelet.pem \ --client-key=kubelet-key.pem \ --embed-certs=true \ --kubeconfig=../kubelet.conf# kubelet set-context$ kubectl config set-context system:node:master1@kubernetes \ --cluster=kubernetes \ --user=system:node:master1 \ --kubeconfig=../kubelet.conf# kubelet set default context$ kubectl config use-context system:node:master1@kubernetes \ --kubeconfig=../kubelet.conf</code></pre><h4 id="Service-account-key"><a href="#Service-account-key" class="headerlink" title="Service account key"></a>Service account key</h4><p>Service account 不是透過 CA 進行認證,因此不要透過 CA 來做 Service account key 的檢查,這邊建立一組 Private 與 Public 金鑰提供給 Service account key 使用:</p><pre><code class="sh">$ openssl genrsa -out sa.key 2048$ openssl rsa -in sa.key -pubout -out sa.pub$ ls sa.*sa.key sa.pub</code></pre><p>完成後刪除不必要檔案:</p><pre><code class="sh">$ rm -rf *.json *.csr</code></pre><p>確認<code>/etc/kubernetes</code>與<code>/etc/kubernetes/pki</code>有以下檔案:</p><pre><code class="sh">$ ls /etc/kubernetes/admin.conf bootstrap.conf controller-manager.conf kubelet.conf pki scheduler.conf token.csv$ ls /etc/kubernetes/pkiadmin-key.pem apiserver-key.pem ca-key.pem controller-manager-key.pem front-proxy-ca-key.pem front-proxy-client-key.pem kubelet-key.pem sa.key scheduler-key.pemadmin.pem apiserver.pem ca.pem controller-manager.pem front-proxy-ca.pem front-proxy-client.pem kubelet.pem sa.pub scheduler.pem</code></pre><h3 id="安裝-Kubernetes-核心元件"><a href="#安裝-Kubernetes-核心元件" class="headerlink" title="安裝 Kubernetes 核心元件"></a>安裝 Kubernetes 核心元件</h3><p>首先下載 Kubernetes 核心元件 YAML 檔案,這邊我們不透過 Binary 方案來建立 Master 核心元件,而是利用 Kubernetes Static Pod 來達成,因此需下載所有核心元件的<code>Static Pod</code>檔案到<code>/etc/kubernetes/manifests</code>目錄:</p><pre><code class="sh">$ export CORE_URL="https://kairen.github.io/files/manual-v1.8/master"$ mkdir -p /etc/kubernetes/manifests && cd /etc/kubernetes/manifests$ for FILE in apiserver manager scheduler; do wget "${CORE_URL}/${FILE}.yml.conf" -O ${FILE}.yml done</code></pre><blockquote><p>若<code>IP</code>與教學設定不同的話,請記得修改<code>apiserver.yml</code>、<code>manager.yml</code>、<code>scheduler.yml</code>。<br>apiserver 中的 <code>NodeRestriction</code> 請參考 <a href="https://kubernetes.io/docs/admin/authorization/node/" target="_blank" rel="noopener">Using Node Authorization</a>。</p></blockquote><p>產生一個用來加密 Etcd 的 Key:</p><pre><code class="sh">$ head -c 32 /dev/urandom | base64SUpbL4juUYyvxj3/gonV5xVEx8j769/99TSAf8YT/sQ=</code></pre><p>在<code>/etc/kubernetes/</code>目錄下,建立<code>encryption.yml</code>的加密 YAML 檔案:</p><pre><code class="sh">$ cat <<EOF > /etc/kubernetes/encryption.ymlkind: EncryptionConfigapiVersion: v1resources: - resources: - secrets providers: - aescbc: keys: - name: key1 secret: SUpbL4juUYyvxj3/gonV5xVEx8j769/99TSAf8YT/sQ= - identity: {}EOF</code></pre><blockquote><p>Etcd 資料加密可參考這篇 <a href="https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/" target="_blank" rel="noopener">Encrypting data at rest</a>。</p></blockquote><p>在<code>/etc/kubernetes/</code>目錄下,建立<code>audit-policy.yml</code>的進階稽核策略 YAML 檔:</p><pre><code class="sh">$ cat <<EOF > /etc/kubernetes/audit-policy.ymlapiVersion: audit.k8s.io/v1beta1kind: Policyrules:- level: MetadataEOF</code></pre><blockquote><p>Audit Policy 請參考這篇 <a href="https://kubernetes.io/docs/tasks/debug-application-cluster/audit/" target="_blank" rel="noopener">Auditing</a>。</p></blockquote><p>下載<code>kubelet.service</code>相關檔案來管理 kubelet:</p><pre><code class="sh">$ export KUBELET_URL="https://kairen.github.io/files/manual-v1.8/master"$ mkdir -p /etc/systemd/system/kubelet.service.d$ wget "${KUBELET_URL}/kubelet.service" -O /lib/systemd/system/kubelet.service$ wget "${KUBELET_URL}/10-kubelet.conf" -O /etc/systemd/system/kubelet.service.d/10-kubelet.conf</code></pre><blockquote><p>若<code>cluster-dns</code>或<code>cluster-domain</code>有改變的話,需要修改<code>10-kubelet.conf</code>。</p></blockquote><p>最後建立 var 存放資訊,然後啟動 kubelet 服務:</p><pre><code class="sh">$ mkdir -p /var/lib/kubelet /var/log/kubernetes$ systemctl enable kubelet.service && systemctl start kubelet.service</code></pre><p>完成後會需要一段時間來下載映像檔與啟動元件,可以利用該指令來監看:</p><pre><code class="sh">$ watch netstat -ntlptcp 0 0 127.0.0.1:10248 0.0.0.0:* LISTEN 23012/kubelettcp 0 0 127.0.0.1:10251 0.0.0.0:* LISTEN 22305/kube-scheduletcp 0 0 127.0.0.1:10252 0.0.0.0:* LISTEN 22529/kube-controlltcp6 0 0 :::6443 :::* LISTEN 22956/kube-apiserve</code></pre><blockquote><p>若看到以上資訊表示服務正常啟動,若發生問題可以用<code>docker cli</code>來查看。</p></blockquote><p>完成後,複製 admin kubeconfig 檔案,並透過簡單指令驗證:</p><pre><code class="sh">$ cp /etc/kubernetes/admin.conf ~/.kube/config$ kubectl get csNAME STATUS MESSAGE ERRORetcd-0 Healthy {"health": "true"}scheduler Healthy okcontroller-manager Healthy ok$ kubectl get nodeNAME STATUS ROLES AGE VERSIONmaster1 NotReady master 1m v1.8.6$ kubectl -n kube-system get poNAME READY STATUS RESTARTS AGEkube-apiserver-master1 1/1 Running 0 4mkube-controller-manager-master1 1/1 Running 0 4mkube-scheduler-master1 1/1 Running 0 4m</code></pre><p>確認服務能夠執行 logs 等指令:</p><pre><code class="sh">$ kubectl -n kube-system logs -f kube-scheduler-master1Error from server (Forbidden): Forbidden (user=kube-apiserver, verb=get, resource=nodes, subresource=proxy) ( pods/log kube-apiserver-master1)</code></pre><blockquote><p>這邊會發現出現 403 Forbidden 問題,這是因為 <code>kube-apiserver</code> user 並沒有 nodes 的資源權限,屬於正常。</p></blockquote><p>由於上述權限問題,我們必需建立一個 <code>apiserver-to-kubelet-rbac.yml</code> 來定義權限,以供我們執行 logs、exec 等指令:</p><pre><code class="sh">$ cd /etc/kubernetes/$ export URL="https://kairen.github.io/files/manual-v1.8/master"$ wget "${URL}/apiserver-to-kubelet-rbac.yml.conf" -O apiserver-to-kubelet-rbac.yml$ kubectl apply -f apiserver-to-kubelet-rbac.yml# 測試 logs$ kubectl -n kube-system logs -f kube-scheduler-master1...I1031 03:22:42.527697 1 leaderelection.go:184] successfully acquired lease kube-system/kube-scheduler</code></pre><h2 id="Kubernetes-Node"><a href="#Kubernetes-Node" class="headerlink" title="Kubernetes Node"></a>Kubernetes Node</h2><p>Node 是主要執行容器實例的節點,可視為工作節點。在這步驟我們會下載 Kubernetes binary 檔,並建立 node 的 certificate 來提供給節點註冊認證用。Kubernetes 使用<code>Node Authorizer</code>來提供<a href="https://kubernetes.io/docs/admin/authorization/node/" target="_blank" rel="noopener">Authorization mode</a>,這種授權模式會替 Kubelet 生成 API request。</p><p>在開始前,我們先在<code>master1</code>將需要的 ca 與 cert 複製到 Node 節點上:</p><pre><code class="sh">$ for NODE in node1 node2; do ssh ${NODE} "mkdir -p /etc/kubernetes/pki/" ssh ${NODE} "mkdir -p /etc/etcd/ssl" # Etcd ca and cert for FILE in etcd-ca.pem etcd.pem etcd-key.pem; do scp /etc/etcd/ssl/${FILE} ${NODE}:/etc/etcd/ssl/${FILE} done # Kubernetes ca and cert for FILE in pki/ca.pem pki/ca-key.pem bootstrap.conf; do scp /etc/kubernetes/${FILE} ${NODE}:/etc/kubernetes/${FILE} done done</code></pre><h3 id="下載-Kubernetes-元件-1"><a href="#下載-Kubernetes-元件-1" class="headerlink" title="下載 Kubernetes 元件"></a>下載 Kubernetes 元件</h3><p>首先透過網路取得所有需要的執行檔案:</p><pre><code class="sh"># Download Kubernetes$ export KUBE_URL="https://storage.googleapis.com/kubernetes-release/release/v1.8.6/bin/linux/amd64"$ wget "${KUBE_URL}/kubelet" -O /usr/local/bin/kubelet$ chmod +x /usr/local/bin/kubelet# Download CNI$ mkdir -p /opt/cni/bin && cd /opt/cni/bin$ export CNI_URL="https://github.com/containernetworking/plugins/releases/download"$ wget -qO- --show-progress "${CNI_URL}/v0.6.0/cni-plugins-amd64-v0.6.0.tgz" | tar -zx</code></pre><h3 id="設定-Kubernetes-node"><a href="#設定-Kubernetes-node" class="headerlink" title="設定 Kubernetes node"></a>設定 Kubernetes node</h3><p>接著下載 Kubernetes 相關檔案,包含 drop-in file、systemd service 檔案等:</p><pre><code class="sh">$ export KUBELET_URL="https://kairen.github.io/files/manual-v1.8/node"$ mkdir -p /etc/systemd/system/kubelet.service.d$ wget "${KUBELET_URL}/kubelet.service" -O /lib/systemd/system/kubelet.service$ wget "${KUBELET_URL}/10-kubelet.conf" -O /etc/systemd/system/kubelet.service.d/10-kubelet.conf</code></pre><blockquote><p>若<code>cluster-dns</code>或<code>cluster-domain</code>有改變的話,需要修改<code>10-kubelet.conf</code>。</p></blockquote><p>接著在所有<code>node</code>建立 var 存放資訊,然後啟動 kubelet 服務:</p><pre><code class="sh">$ mkdir -p /var/lib/kubelet /var/log/kubernetes /etc/kubernetes/manifests$ systemctl enable kubelet.service && systemctl start kubelet.service</code></pre><blockquote><p>P.S. 重複一樣動作來完成其他節點。</p></blockquote><h3 id="授權-Kubernetes-Node"><a href="#授權-Kubernetes-Node" class="headerlink" title="授權 Kubernetes Node"></a>授權 Kubernetes Node</h3><p>當所有節點都完成後,在<code>master1</code>節點,因為我們採用 TLS Bootstrapping,所需要建立一個 ClusterRoleBinding:</p><pre><code class="sh">$ kubectl create clusterrolebinding kubelet-bootstrap \ --clusterrole=system:node-bootstrapper \ --user=kubelet-bootstrap</code></pre><p>在<code>master</code>透過簡單指令驗證,會看到節點處於<code>pending</code>:</p><pre><code class="sh">$ kubectl get csrNAME AGE REQUESTOR CONDITIONnode-csr-YWf97ZrLCTlr2hmXsNLfjVLwaLfZRsu52FRKOYjpcBE 2s kubelet-bootstrap Pendingnode-csr-eq4q6ffOwT4yqYQNU6sT7mphPOQdFN6yulMVZeu6pkE 2s kubelet-bootstrap Pending</code></pre><p>透過 kubectl 來允許節點加入叢集:</p><pre><code class="sh">$ kubectl get csr | awk '/Pending/ {print $1}' | xargs kubectl certificate approvecertificatesigningrequest "node-csr-YWf97ZrLCTlr2hmXsNLfjVLwaLfZRsu52FRKOYjpcBE" approvedcertificatesigningrequest "node-csr-eq4q6ffOwT4yqYQNU6sT7mphPOQdFN6yulMVZeu6pkE" approved$ kubectl get csrNAME AGE REQUESTOR CONDITIONnode-csr-YWf97ZrLCTlr2hmXsNLfjVLwaLfZRsu52FRKOYjpcBE 30s kubelet-bootstrap Approved,Issuednode-csr-eq4q6ffOwT4yqYQNU6sT7mphPOQdFN6yulMVZeu6pkE 30s kubelet-bootstrap Approved,Issued$ kubectl get noNAME STATUS ROLES AGE VERSIONmaster1 NotReady master 21m v1.8.6node1 NotReady node 8s v1.8.6node2 NotReady node 8s v1.8.6</code></pre><h2 id="Kubernetes-Core-Addons-部署"><a href="#Kubernetes-Core-Addons-部署" class="headerlink" title="Kubernetes Core Addons 部署"></a>Kubernetes Core Addons 部署</h2><p>當完成上面所有步驟後,接著我們需要安裝一些插件,而這些有部分是非常重要跟好用的,如<code>Kube-dns</code>與<code>Kube-proxy</code>等。</p><h3 id="Kube-proxy-addon"><a href="#Kube-proxy-addon" class="headerlink" title="Kube-proxy addon"></a>Kube-proxy addon</h3><p><a href="https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/kube-proxy" target="_blank" rel="noopener">Kube-proxy</a> 是實現 Service 的關鍵元件,kube-proxy 會在每台節點上執行,然後監聽 API Server 的 Service 與 Endpoint 資源物件的改變,然後來依據變化執行 iptables 來實現網路的轉發。這邊我們會需要建議一個 DaemonSet 來執行,並且建立一些需要的 certificate。</p><p>首先在<code>master1</code>下載<code>kube-proxy-csr.json</code>檔案,並產生 kube-proxy certificate 證書:</p><pre><code class="sh">$ export PKI_URL="https://kairen.github.io/files/manual-v1.8/pki"$ cd /etc/kubernetes/pki$ wget "${PKI_URL}/kube-proxy-csr.json" "${PKI_URL}/ca-config.json"$ cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-proxy-csr.json | cfssljson -bare kube-proxy$ ls kube-proxy*.pemkube-proxy-key.pem kube-proxy.pem</code></pre><p>接著透過以下指令產生名稱為 <code>kube-proxy.conf</code> 的 kubeconfig 檔:</p><pre><code class="sh"># kube-proxy set-cluster$ kubectl config set-cluster kubernetes \ --certificate-authority=ca.pem \ --embed-certs=true \ --server="https://172.16.35.12:6443" \ --kubeconfig=../kube-proxy.conf# kube-proxy set-credentials$ kubectl config set-credentials system:kube-proxy \ --client-key=kube-proxy-key.pem \ --client-certificate=kube-proxy.pem \ --embed-certs=true \ --kubeconfig=../kube-proxy.conf# kube-proxy set-context$ kubectl config set-context system:kube-proxy@kubernetes \ --cluster=kubernetes \ --user=system:kube-proxy \ --kubeconfig=../kube-proxy.conf# kube-proxy set default context$ kubectl config use-context system:kube-proxy@kubernetes \ --kubeconfig=../kube-proxy.conf</code></pre><p>完成後刪除不必要檔案:</p><pre><code class="sh">$ rm -rf *.json</code></pre><p>確認<code>/etc/kubernetes</code>有以下檔案:</p><pre><code class="sh">$ ls /etc/kubernetes/admin.conf bootstrap.conf encryption.yml kube-proxy.conf pki token.csvaudit-policy.yml controller-manager.conf kubelet.conf manifests scheduler.conf</code></pre><p>在<code>master1</code>將<code>kube-proxy</code>相關檔案複製到 Node 節點上:</p><pre><code class="sh">$ for NODE in node1 node2; do echo "--- $NODE ---" for FILE in pki/kube-proxy.pem pki/kube-proxy-key.pem kube-proxy.conf; do scp /etc/kubernetes/${FILE} ${NODE}:/etc/kubernetes/${FILE} done done</code></pre><p>完成後,在<code>master1</code>透過 kubectl 來建立 kube-proxy daemon:</p><pre><code class="sh">$ export ADDON_URL="https://kairen.github.io/files/manual-v1.8/addon"$ mkdir -p /etc/kubernetes/addons && cd /etc/kubernetes/addons$ wget "${ADDON_URL}/kube-proxy.yml.conf" -O kube-proxy.yml$ kubectl apply -f kube-proxy.yml$ kubectl -n kube-system get po -l k8s-app=kube-proxyNAME READY STATUS RESTARTS AGEkube-proxy-bpp7q 1/1 Running 0 47skube-proxy-cztvh 1/1 Running 0 47skube-proxy-q7mm4 1/1 Running 0 47s</code></pre><h3 id="Kube-dns-addon"><a href="#Kube-dns-addon" class="headerlink" title="Kube-dns addon"></a>Kube-dns addon</h3><p><a href="https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns" target="_blank" rel="noopener">Kube DNS</a> 是 Kubernetes 叢集內部 Pod 之間互相溝通的重要 Addon,它允許 Pod 可以透過 Domain Name 方式來連接 Service,其主要由 Kube DNS 與 Sky DNS 組合而成,透過 Kube DNS 監聽 Service 與 Endpoint 變化,來提供給 Sky DNS 資訊,已更新解析位址。</p><p>安裝只需要在<code>master1</code>透過 kubectl 來建立 kube-dns deployment 即可:</p><pre><code class="sh">$ export ADDON_URL="https://kairen.github.io/files/manual-v1.8/addon"$ wget "${ADDON_URL}/kube-dns.yml.conf" -O kube-dns.yml$ kubectl apply -f kube-dns.yml$ kubectl -n kube-system get po -l k8s-app=kube-dnsNAME READY STATUS RESTARTS AGEkube-dns-6cb549f55f-h4zr5 0/3 Pending 0 40s</code></pre><h2 id="Calico-Network-安裝與設定"><a href="#Calico-Network-安裝與設定" class="headerlink" title="Calico Network 安裝與設定"></a>Calico Network 安裝與設定</h2><p>Calico 是一款純 Layer 3 的資料中心網路方案(不需要 Overlay 網路),Calico 好處是他已與各種雲原生平台有良好的整合,而 Calico 在每一個節點利用 Linux Kernel 實現高效的 vRouter 來負責資料的轉發,而當資料中心複雜度增加時,可以用 BGP route reflector 來達成。</p><p>首先在<code>master1</code>透過 kubectl 建立 Calico policy controller:</p><pre><code class="sh">$ export CALICO_CONF_URL="https://kairen.github.io/files/manual-v1.8/network"$ wget "${CALICO_CONF_URL}/calico-controller.yml.conf" -O calico-controller.yml$ kubectl apply -f calico-controller.yml$ kubectl -n kube-system get po -l k8s-app=calico-policyNAME READY STATUS RESTARTS AGEcalico-policy-controller-5ff8b4549d-tctmm 0/1 Pending 0 5s</code></pre><blockquote><p>若節點 IP 不同,需要修改<code>calico-controller.yml</code>的<code>ETCD_ENDPOINTS</code>。</p></blockquote><p>在<code>master1</code>下載 Calico CLI 工具:</p><pre><code class="sh">$ wget https://github.com/projectcalico/calicoctl/releases/download/v1.6.1/calicoctl$ chmod +x calicoctl && mv calicoctl /usr/local/bin/</code></pre><p>然後在<code>所有</code>節點下載 Calico,並執行以下步驟:</p><pre><code class="sh">$ export CALICO_URL="https://github.com/projectcalico/cni-plugin/releases/download/v1.11.0"$ wget -N -P /opt/cni/bin ${CALICO_URL}/calico$ wget -N -P /opt/cni/bin ${CALICO_URL}/calico-ipam$ chmod +x /opt/cni/bin/calico /opt/cni/bin/calico-ipam</code></pre><p>接著在<code>所有</code>節點下載 CNI plugins設定檔,以及 calico-node.service:</p><pre><code class="sh">$ mkdir -p /etc/cni/net.d$ export CALICO_CONF_URL="https://kairen.github.io/files/manual-v1.8/network"$ wget "${CALICO_CONF_URL}/10-calico.conf" -O /etc/cni/net.d/10-calico.conf$ wget "${CALICO_CONF_URL}/calico-node.service" -O /lib/systemd/system/calico-node.service</code></pre><blockquote><blockquote><p>若節點 IP 不同,需要修改<code>10-calico.conf</code>的<code>etcd_endpoints</code>。</p><ul><li>若部署的機器是使用虛擬機,如 Virtualbox 等的話,請修改<code>calico-node.service</code>檔案,並在<code>IP_AUTODETECTION_METHOD</code>(包含 IP6)部分指定綁定的網卡,以避免預設綁定到 NAT 網路上。</li></ul></blockquote></blockquote><p>之後在<code>所有</code>節點啟動 Calico-node:</p><pre><code class="sh">$ systemctl enable calico-node.service && systemctl start calico-node.service</code></pre><p>在<code>master1</code>查看 Calico nodes:</p><pre><code class="sh">$ cat <<EOF > ~/calico-rcexport ETCD_ENDPOINTS="https://172.16.35.12:2379"export ETCD_CA_CERT_FILE="/etc/etcd/ssl/etcd-ca.pem"export ETCD_CERT_FILE="/etc/etcd/ssl/etcd.pem"export ETCD_KEY_FILE="/etc/etcd/ssl/etcd-key.pem"EOF$ . ~/calico-rc$ calicoctl get node -o wideNAME ASN IPV4 IPV6master1 (64512) 172.16.35.12/24node1 (64512) 172.16.35.10/24node2 (64512) 172.16.35.11/24</code></pre><p>查看 pending 的 pod 是否已執行:</p><pre><code class="sh">$ kubectl -n kube-system get poNAME READY STATUS RESTARTS AGEcalico-policy-controller-5ff8b4549d-tctmm 1/1 Running 0 4mkube-apiserver-master1 1/1 Running 0 20mkube-controller-manager-master1 1/1 Running 0 20mkube-dns-6cb549f55f-h4zr5 3/3 Running 0 5mkube-proxy-fnrkb 1/1 Running 0 6mkube-proxy-l72bq 1/1 Running 0 6mkube-proxy-m6rfw 1/1 Running 0 6mkube-scheduler-master1 1/1 Running 0 20m</code></pre><p>最後若想省事,可以直接用 <a href="https://docs.projectcalico.org/v2.6/getting-started/kubernetes/installation/hosted/hosted" target="_blank" rel="noopener">Standard Hosted</a> 方式安裝。</p><h2 id="Kubernetes-Extra-Addons-部署"><a href="#Kubernetes-Extra-Addons-部署" class="headerlink" title="Kubernetes Extra Addons 部署"></a>Kubernetes Extra Addons 部署</h2><p>本節說明如何部署一些官方常用的 Addons,如 Dashboard、Heapster 等。</p><h3 id="Dashboard-addon"><a href="#Dashboard-addon" class="headerlink" title="Dashboard addon"></a>Dashboard addon</h3><p><a href="https://github.com/kubernetes/dashboard" target="_blank" rel="noopener">Dashboard</a> 是 Kubernetes 社區官方開發的儀表板,有了儀表板後管理者就能夠透過 Web-based 方式來管理 Kubernetes 叢集,除了提升管理方便,也讓資源視覺化,讓人更直覺看見系統資訊的呈現結果。</p><p>在<code>master1</code>透過 kubectl 來建立 kubernetes dashboard 即可:</p><pre><code class="sh">$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/recommended/kubernetes-dashboard.yaml$ kubectl -n kube-system get po,svc -l k8s-app=kubernetes-dashboardNAME READY STATUS RESTARTS AGEpo/kubernetes-dashboard-747c4f7cf-md5m8 1/1 Running 0 56sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEsvc/kubernetes-dashboard ClusterIP 10.98.120.209 <none> 443/TCP 56s</code></pre><p>這邊會額外建立一個名稱為<code>open-api</code> Cluster Role Binding,這僅作為方便測試時使用,在一般情況下不要開啟,不然就會直接被存取所有 API:</p><pre><code class="sh">$ cat <<EOF | kubectl create -f -apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRoleBindingmetadata: name: open-api namespace: ""roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-adminsubjects: - apiGroup: rbac.authorization.k8s.io kind: User name: system:anonymousEOF</code></pre><blockquote><p>P.S. 管理者可以針對特定使用者來開放 API 存取權限,但這邊方便使用直接綁在 cluster-admin cluster role。</p></blockquote><p>完成後,就可以透過瀏覽器存取 <a href="https://172.16.35.12:6443/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy/" target="_blank" rel="noopener">Dashboard</a>。</p><p>在 1.7 版本以後的 Dashboard 將不再提供所有權限,因此需要建立一個 service account 來綁定 cluster-admin role:</p><pre><code class="sh">$ kubectl -n kube-system create sa dashboard$ kubectl create clusterrolebinding dashboard --clusterrole cluster-admin --serviceaccount=kube-system:dashboard$ SECRET=$(kubectl -n kube-system get sa dashboard -o yaml | awk '/dashboard-token/ {print $3}')$ kubectl -n kube-system describe secrets ${SECRET} | awk '/token:/{print $2}'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlLXN5c3RlbSIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtdG9rZW4tdzVocmgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYWJmMTFjYzMtZjRlYi0xMWU3LTgzYWUtMDgwMDI3NjdkOWI5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmUtc3lzdGVtOmRhc2hib2FyZCJ9.Xuyq34ci7Mk8bI97o4IldDyKySOOqRXRsxVWIJkPNiVUxKT4wpQZtikNJe2mfUBBD-JvoXTzwqyeSSTsAy2CiKQhekW8QgPLYelkBPBibySjBhJpiCD38J1u7yru4P0Pww2ZQJDjIxY4vqT46ywBklReGVqY3ogtUQg-eXueBmz-o7lJYMjw8L14692OJuhBjzTRSaKW8U2MPluBVnD7M2SOekDff7KpSxgOwXHsLVQoMrVNbspUCvtIiEI1EiXkyCNRGwfnd2my3uzUABIHFhm0_RZSmGwExPbxflr8Fc6bxmuz-_jSdOtUidYkFIzvEWw2vRovPgs3MXTv59RwUw</code></pre><blockquote><p>複製<code>token</code>,然後貼到 Kubernetes dashboard。</p></blockquote><h3 id="Heapster-addon"><a href="#Heapster-addon" class="headerlink" title="Heapster addon"></a>Heapster addon</h3><p><a href="https://github.com/kubernetes/heapster" target="_blank" rel="noopener">Heapster</a> 是 Kubernetes 社區維護的容器叢集監控與效能分析工具。Heapster 會從 Kubernetes apiserver 取得所有 Node 資訊,然後再透過這些 Node 來取得 kubelet 上的資料,最後再將所有收集到資料送到 Heapster 的後台儲存 InfluxDB,最後利用 Grafana 來抓取 InfluxDB 的資料源來進行視覺化。</p><p>在<code>master1</code>透過 kubectl 來建立 kubernetes monitor 即可:</p><pre><code class="sh">$ export ADDON_URL="https://kairen.github.io/files/manual-v1.8/addon"$ wget ${ADDON_URL}/kube-monitor.yml.conf -O kube-monitor.yml$ kubectl apply -f kube-monitor.yml$ kubectl -n kube-system get po,svcNAME READY STATUS RESTARTS AGE...po/heapster-74fb5c8cdc-62xzc 4/4 Running 0 7mpo/influxdb-grafana-55bd7df44-nw4nc 2/2 Running 0 7mNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE...svc/heapster ClusterIP 10.100.242.225 <none> 80/TCP 7msvc/monitoring-grafana ClusterIP 10.101.106.180 <none> 80/TCP 7msvc/monitoring-influxdb ClusterIP 10.109.245.142 <none> 8083/TCP,8086/TCP 7m···</code></pre><p>完成後,就可以透過瀏覽器存取 <a href="https://172.16.35.12:6443/api/v1/proxy/namespaces/kube-system/services/monitoring-grafana" target="_blank" rel="noopener">Grafana Dashboard</a>。</p><h2 id="簡單部署-Nginx-服務"><a href="#簡單部署-Nginx-服務" class="headerlink" title="簡單部署 Nginx 服務"></a>簡單部署 Nginx 服務</h2><p>Kubernetes 可以選擇使用指令直接建立應用程式與服務,或者撰寫 YAML 與 JSON 檔案來描述部署應用程式的配置,以下將建立一個簡單的 Nginx 服務:</p><pre><code class="sh">$ kubectl run nginx --image=nginx --port=80$ kubectl expose deploy nginx --port=80 --type=LoadBalancer --external-ip=172.16.35.12$ kubectl get svc,poNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEsvc/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 1hsvc/nginx LoadBalancer 10.97.121.243 172.16.35.12 80:30344/TCP 22sNAME READY STATUS RESTARTS AGEpo/nginx-7cbc4b4d9c-7796l 1/1 Running 0 28s 192.160.57.181 ,172.16.35.12 80:32054/TCP 21s</code></pre><blockquote><p>這邊<code>type</code>可以選擇 NodePort 與 LoadBalancer,在本地裸機部署,兩者差異在於<code>NodePort</code>只映射 Host port 到 Container port,而<code>LoadBalancer</code>則繼承<code>NodePort</code>額外多出映射 Host target port 到 Container port。</p></blockquote><p>確認沒問題後即可在瀏覽器存取 <a href="http://172.16.35.12。" target="_blank" rel="noopener">http://172.16.35.12。</a></p><h3 id="擴展服務數量"><a href="#擴展服務數量" class="headerlink" title="擴展服務數量"></a>擴展服務數量</h3><p>若叢集<code>node</code>節點增加了,而想讓 Nginx 服務提供可靠性的話,可以透過以下方式來擴展服務的副本:</p><pre><code class="sh">$ kubectl scale deploy nginx --replicas=2$ kubectl get pods -o wideNAME READY STATUS RESTARTS AGE IP NODEnginx-158599303-0h9lr 1/1 Running 0 25s 10.244.100.5 node2nginx-158599303-k7cbt 1/1 Running 0 1m 10.244.24.3 node1</code></pre>]]></content>
<summary type="html">
<p>Kubernetes 提供了許多雲端平台與作業系統的安裝方式,本章將以<code>全手動安裝方式</code>來部署 Kubernetes v1.8.x 版本,主要是學習與了解 Kubernetes 建置流程。若想要瞭解更多平台的部署可以參考 <a href="https://kubernetes.io/docs/getting-started-guides/" target="_blank" rel="noopener">Picking the Right Solution</a>來選擇自己最喜歡的方式。</p>
<p>本次安裝版本為:</p>
<ul>
<li>Kubernetes v1.8.6</li>
<li>CNI v0.6.0</li>
<li>Etcd v3.2.9</li>
<li>Calico v2.6.2</li>
<li>Docker v17.10.0-ce</li>
</ul>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Docker" scheme="https://kairen.github.io/tags/Docker/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="Calico" scheme="https://kairen.github.io/tags/Calico/"/>
</entry>
<entry>
<title>利用 Kuryr 整合 OpenStack 與 Kubernetes 網路</title>
<link href="https://kairen.github.io/2017/08/29/openstack/kuryr-kubernetes/"/>
<id>https://kairen.github.io/2017/08/29/openstack/kuryr-kubernetes/</id>
<published>2017-08-29T08:23:01.000Z</published>
<updated>2017-10-26T14:15:10.000Z</updated>
<content type="html"><![CDATA[<p><a href="https://github.com/openstack/kuryr-kubernetes" target="_blank" rel="noopener">Kubernetes Kuryr</a> 是 OpenStack Neutron 的子專案,其主要目標是透過該專案來整合 OpenStack 與 Kubernetes 的網路。該專案在 Kubernetes 中實作了原生 Neutron-based 的網路,因此使用 Kuryr-Kubernetes 可以讓你的 OpenStack VM 與 Kubernetes Pods 能夠選擇在同一個子網路上運作,並且能夠使用 Neutron 的 L3 與 Security Group 來對網路進行路由,以及阻擋特定來源 Port。</p><p><img src="https://i.imgur.com/2XfP3vb.png" alt=""></p><a id="more"></a><p>Kuryr-Kubernetes 整合有兩個主要組成部分:</p><ol><li><strong>Kuryr Controller</strong>:<br>Controller 主要目的是監控 Kubernetes API 的來獲取 Kubernetes 資源的變化,然後依據 Kubernetes 資源的需求來執行子資源的分配和資源管理。</li><li><strong>Kuryr CNI</strong>:主要是依據 Kuryr Controller 分配的資源來綁定網路至 Pods 上。</li></ol><p>本篇我們將說明如何利用<code>DevStack</code>與<code>Kubespray</code>建立一個簡單的測試環境。</p><h2 id="環境資源與事前準備"><a href="#環境資源與事前準備" class="headerlink" title="環境資源與事前準備"></a>環境資源與事前準備</h2><p>準備兩台實體機器,這邊測試的作業系統為<code>CentOS 7.x</code>,該環境將在扁平的網路下進行。</p><table><thead><tr><th>IP Address 1</th><th>Role</th></tr></thead><tbody><tr><td>172.24.0.34</td><td>controller, k8s-master</td></tr><tr><td>172.24.0.80</td><td>compute, k8s-node1</td></tr></tbody></table><p>更新每台節點的 CentOS 7.x packages:</p><pre><code class="shell=">$ sudo yum --enablerepo=cr update -y</code></pre><p>然後關閉 firewalld 以及 SELinux 來避免實現發生問題:</p><pre><code class="shell=">$ sudo setenforce 0$ sudo systemctl disable firewalld && sudo systemctl stop firewalld</code></pre><h2 id="OpenStack-Controller-安裝"><a href="#OpenStack-Controller-安裝" class="headerlink" title="OpenStack Controller 安裝"></a>OpenStack Controller 安裝</h2><p>首先進入<code>172.24.0.34(controller)</code>,並且執行以下指令。</p><p>然後執行以下指令來建立 DevStack 專用使用者:</p><pre><code class="shell=">$ sudo useradd -s /bin/bash -d /opt/stack -m stack$ echo "stack ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/stack</code></pre><blockquote><p>選用 DevStack 是因為現在都是用 Systemd 來管理服務,不用再用 screen 了,雖然都很方便。</p></blockquote><p>接著切換至該使用者環境來建立 OpenStack:</p><pre><code class="shell=">$ sudo su - stack</code></pre><p>下載 DevStack 安裝套件:</p><pre><code class="shell=">$ git clone https://git.openstack.org/openstack-dev/devstack$ cd devstack</code></pre><p>新增<code>local.conf</code>檔案,來描述部署資訊:</p><pre><code>[[local|localrc]]HOST_IP=172.24.0.34GIT_BASE=https://github.comADMIN_PASSWORD=passwdDATABASE_PASSWORD=passwdRABBIT_PASSWORD=passwdSERVICE_PASSWORD=passwdSERVICE_TOKEN=passwdMULTI_HOST=1</code></pre><blockquote><p>[color=#fc9fca]Tips:<br>修改 HOST_IP 為自己的 IP 位置。</p></blockquote><p>完成後,執行以下指令開始部署:</p><pre><code class="shell=">$ ./stack.sh</code></pre><h2 id="Openstack-Compute-安裝"><a href="#Openstack-Compute-安裝" class="headerlink" title="Openstack Compute 安裝"></a>Openstack Compute 安裝</h2><p>進入到<code>172.24.0.80(compute)</code>,並且執行以下指令。</p><p>然後執行以下指令來建立 DevStack 專用使用者:</p><pre><code class="shell=">$ sudo useradd -s /bin/bash -d /opt/stack -m stack$ echo "stack ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/stack</code></pre><blockquote><p>選用 DevStack 是因為現在都是用 Systemd 來管理服務,不用再用 screen 了,雖然都很方便。</p></blockquote><p>接著切換至該使用者環境來建立 OpenStack:</p><pre><code class="shell=">$ sudo su - stack</code></pre><p>下載 DevStack 安裝套件:</p><pre><code class="shell=">$ git clone https://git.openstack.org/openstack-dev/devstack$ cd devstack</code></pre><p>新增<code>local.conf</code>檔案,來描述部署資訊:</p><pre><code>[[local|localrc]]HOST_IP=172.24.0.80GIT_BASE=https://github.comMULTI_HOST=1LOGFILE=/opt/stack/logs/stack.sh.logADMIN_PASSWORD=passwdDATABASE_PASSWORD=passwdRABBIT_PASSWORD=passwdSERVICE_PASSWORD=passwdDATABASE_TYPE=mysqlSERVICE_HOST=172.24.0.34MYSQL_HOST=$SERVICE_HOSTRABBIT_HOST=$SERVICE_HOSTGLANCE_HOSTPORT=$SERVICE_HOST:9292ENABLED_SERVICES=n-cpu,q-agt,n-api-meta,c-vol,placement-clientNOVA_VNC_ENABLED=TrueNOVNCPROXY_URL="http://$SERVICE_HOST:6080/vnc_auto.html"VNCSERVER_LISTEN=$HOST_IPVNCSERVER_PROXYCLIENT_ADDRESS=$VNCSERVER_LISTEN</code></pre><blockquote><p>Tips:<br>修改 HOST_IP 為自己的主機位置。<br>修改 SERVICE_HOST 為 Master 的IP位置。</p></blockquote><p>完成後,執行以下指令開始部署:</p><pre><code class="shell=">$ ./stack.sh</code></pre><h2 id="建立-Kubernetes-叢集環境"><a href="#建立-Kubernetes-叢集環境" class="headerlink" title="建立 Kubernetes 叢集環境"></a>建立 Kubernetes 叢集環境</h2><p>首先確認所有節點之間不需要 SSH 密碼即可登入,接著進入到<code>172.24.0.34(k8s-master)</code>並且執行以下指令。</p><p>接著安裝所需要的套件:</p><pre><code class="shell=">$ sudo yum -y install software-properties-common ansible git gcc python-pip python-devel libffi-devel openssl-devel$ sudo pip install -U kubespray</code></pre><p>完成後,新增 kubespray 設定檔:</p><pre><code class="shell=">$ cat <<EOF > ~/.kubespray.ymlkubespray_git_repo: "https://github.com/kubernetes-incubator/kubespray.git"# Logging optionsloglevel: "info"EOF</code></pre><p>然後利用 kubespray-cli 快速產生環境的<code>inventory</code>檔,並修改部分內容:</p><pre><code class="shell=">$ sudo -i$ kubespray prepare --masters master --etcds master --nodes node1</code></pre><p>編輯<code>/root/.kubespray/inventory/inventory.cfg</code>,修改以下內容:</p><pre><code>[all]master ansible_host=172.24.0.34 ansible_user=root ip=172.24.0.34node1 ansible_host=172.24.0.80 ansible_user=root ip=172.24.0.80[kube-master]master[kube-node]masternode1[etcd]master[k8s-cluster:children]kube-node1kube-master</code></pre><p>完成後,即可利用 kubespray-cli 指令來進行部署:</p><pre><code class="shell=">$ kubespray deploy --verbose -u root -k .ssh/id_rsa -n calico</code></pre><p>經過一段時間後就會部署完成,這時候檢查節點是否正常:</p><pre><code class="shell=">$ kubectl get noNAME STATUS AGE VERSIONmaster Ready,master 2m v1.7.4node1 Ready 2m v1.7.4</code></pre><p>接著為了方便讓 Kuryr Controller 簡單取得 K8s API Server,這邊修改<code>/etc/kubernetes/manifests/kube-apiserver.yml</code>檔案,加入以下內容:</p><pre><code>- "--insecure-bind-address=0.0.0.0"- "--insecure-port=8080"</code></pre><blockquote><p>Tips:<br>將 insecure 綁定到 0.0.0.0 之上,以及開啟 8080 Port。</p></blockquote><h2 id="安裝-Openstack-Kuryr"><a href="#安裝-Openstack-Kuryr" class="headerlink" title="安裝 Openstack Kuryr"></a>安裝 Openstack Kuryr</h2><p>進入到<code>172.24.0.34(controller)</code>,並且執行以下指令。</p><p>首先在節點安裝所需要的套件:</p><pre><code class="shell=">$ sudo yum -y install gcc libffi-devel python-devel openssl-devel install python-pip</code></pre><p>然後下載 kuryr-kubernetes 並進行安裝:</p><pre><code class="shell=">$ git clone http://git.openstack.org/openstack/kuryr-kubernetes$ pip install -e kuryr-kubernetes</code></pre><p>新增<code>kuryr.conf</code>至<code>/etc/kuryr</code>目錄:</p><pre><code class="shell=">$ cd kuryr-kubernetes$ ./tools/generate_config_file_samples.sh$ sudo mkdir -p /etc/kuryr/$ sudo cp etc/kuryr.conf.sample /etc/kuryr/kuryr.conf</code></pre><p>接著使用 OpenStack Dashboard 建立相關專案,在瀏覽器輸入<a href="http://172.24.0.34" target="_blank" rel="noopener">Dashboard</a>,並執行以下步驟。</p><ol><li>新增 K8s project。</li><li>修改 K8s project member 加入到 service project。</li><li>在該 Project 中新增 Security Groups,參考 <a href="https://docs.openstack.org/kuryr-kubernetes/latest/installation/manual.html" target="_blank" rel="noopener">kuryr-kubernetes manually</a>。</li><li>在該 Project 中新增 pod_subnet 子網路。</li><li>在該 Project 中新增 service_subnet 子網路。</li></ol><p>完成後,修改<code>/etc/kuryr/kuryr.conf</code>檔案,加入以下內容:</p><pre><code>[DEFAULT]use_stderr = truebindir = /usr/local/libexec/kuryr[kubernetes]api_root = http://172.24.0.34:8080[neutron]auth_url = http://172.24.0.34/identityusername = adminuser_domain_name = Defaultpassword = adminproject_name = serviceproject_domain_name = Defaultauth_type = password[neutron_defaults]ovs_bridge = br-intpod_security_groups = {id_of_secuirity_group_for_pods}pod_subnet = {id_of_subnet_for_pods}project = {id_of_project}service_subnet = {id_of_subnet_for_k8s_services}</code></pre><p>完成後執行 kuryr-k8s-controller:</p><pre><code class="shell=">$ kuryr-k8s-controller --config-file /etc/kuryr/kuryr.conf</code></pre><h2 id="安裝-Kuryr-CNI"><a href="#安裝-Kuryr-CNI" class="headerlink" title="安裝 Kuryr-CNI"></a>安裝 Kuryr-CNI</h2><p>進入到<code>172.24.0.80(node1)</code>並且執行以下指令。</p><p>首先在節點安裝所需要的套件:</p><pre><code class="shell=">$ sudo yum -y install gcc libffi-devel python-devel openssl-devel python-pip</code></pre><p>然後安裝 Kuryr-CNI 來提供給 kubelet 使用:</p><pre><code class="shell=">$ git clone http://git.openstack.org/openstack/kuryr-kubernetes$ sudo pip install -e kuryr-kubernetes</code></pre><p>新增<code>kuryr.conf</code>至<code>/etc/kuryr</code>目錄:</p><pre><code class="shell=">$ cd kuryr-kubernetes$ ./tools/generate_config_file_samples.sh$ sudo mkdir -p /etc/kuryr/$ sudo cp etc/kuryr.conf.sample /etc/kuryr/kuryr.conf</code></pre><p>修改<code>/etc/kuryr/kuryr.conf</code>檔案,加入以下內容:</p><pre><code>[DEFAULT]use_stderr = truebindir = /usr/local/libexec/kuryr[kubernetes]api_root = http://172.24.0.34:8080</code></pre><p>建立 CNI bin 與 Conf 目錄:</p><pre><code class="shell=">$ sudo mkdir -p /opt/cni/bin$ sudo ln -s $(which kuryr-cni) /opt/cni/bin/$ sudo mkdir -p /etc/cni/net.d/</code></pre><p>新增<code>/etc/cni/net.d/10-kuryr.conf</code> CNI 設定檔:</p><pre><code>{ "cniVersion": "0.3.0", "name": "kuryr", "type": "kuryr-cni", "kuryr_conf": "/etc/kuryr/kuryr.conf", "debug": true}</code></pre><p>完成後,更新 oslo 與 vif python 函式庫:</p><pre><code class="shell=">$ sudo pip install 'oslo.privsep>=1.20.0' 'os-vif>=1.5.0'</code></pre><p>最後重新啟動相關服務:</p><pre><code>sudo systemctl daemon-reload && systemctl restart kubelet.service</code></pre><h2 id="測試結果"><a href="#測試結果" class="headerlink" title="測試結果"></a>測試結果</h2><p>我們這邊開一個 Pod 與 OpenStack VM 來進行溝通:<br><img src="https://i.imgur.com/UYXdKud.png" alt=""></p><p><img src="https://i.imgur.com/dwoEytW.png" alt=""></p>]]></content>
<summary type="html">
<p><a href="https://github.com/openstack/kuryr-kubernetes" target="_blank" rel="noopener">Kubernetes Kuryr</a> 是 OpenStack Neutron 的子專案,其主要目標是透過該專案來整合 OpenStack 與 Kubernetes 的網路。該專案在 Kubernetes 中實作了原生 Neutron-based 的網路,因此使用 Kuryr-Kubernetes 可以讓你的 OpenStack VM 與 Kubernetes Pods 能夠選擇在同一個子網路上運作,並且能夠使用 Neutron 的 L3 與 Security Group 來對網路進行路由,以及阻擋特定來源 Port。</p>
<p><img src="https://i.imgur.com/2XfP3vb.png" alt=""></p>
</summary>
<category term="OpenStack" scheme="https://kairen.github.io/categories/OpenStack/"/>
<category term="Docker" scheme="https://kairen.github.io/tags/Docker/"/>
<category term="OpenStack" scheme="https://kairen.github.io/tags/OpenStack/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
</entry>
<entry>
<title>利用 OpenStack Ironic 提供裸機部署服務</title>
<link href="https://kairen.github.io/2017/08/16/openstack/ironic/"/>
<id>https://kairen.github.io/2017/08/16/openstack/ironic/</id>
<published>2017-08-16T08:23:01.000Z</published>
<updated>2017-08-24T16:42:08.000Z</updated>
<content type="html"><![CDATA[<p><a href="https://docs.openstack.org/ironic/latest/user/index.html" target="_blank" rel="noopener">Ironic</a> 是 OpenStack 專案之一,主要目的是提供裸機機器部署服務(Bare-metal service)。它能夠單獨或整合 OpenStack 其他服務被使用,而可整合服務包含 Keystone、Nova、Neutron、Glance 與 Swift 等核心服務。當使用 Compute 與 Network 服務對 Bare-metal 進行適當的配置時,OpenStack 可以透過 Compute API 同時部署虛擬機(Virtual machines)與裸機(Bare machines)。</p><p>本篇為了精簡安裝過程,故這邊不採用手動安裝教學(會在 Gitbook 書上更新),因此採用 <a href="https://docs.openstack.org/devstack/latest/" target="_blank" rel="noopener">DevStack</a> 來部署服務,再手動設定一些步驟。</p><p>本環境安裝資訊:</p><ul><li>OpenStack Pike</li><li>DevStack Pike</li><li>Pike Pike Pike ….</li></ul><a id="more"></a><p><img src="/images/openstack/openstack-ironic.png" alt=""></p><blockquote><p>P.S. 這邊因為我的 Manage net 已經有 MAAS 的服務,所以才用其他張網卡進行部署。</p></blockquote><h2 id="節點資訊"><a href="#節點資訊" class="headerlink" title="節點資訊"></a>節點資訊</h2><p>本次安裝作業系統採用<code>Ubuntu 16.04 Server</code>,測試環境為實體主機:</p><table><thead><tr><th>Role</th><th>CPU</th><th>Memory</th></tr></thead><tbody><tr><td>controller</td><td>4</td><td>16G</td></tr><tr><td>bare-node1</td><td>4</td><td>16G</td></tr></tbody></table><blockquote><p>這邊 controller 為主要控制節點,將安裝大部分 OpenStack 服務。而 bare-node 為被用來做裸機部署的機器。</p></blockquote><p>網卡若是實體主機,請設定為固定 IP,如以下:</p><pre><code>auto eth0iface eth0 inet static address 172.20.3.93/24 gateway 172.20.3.1 dns-nameservers 8.8.8.8</code></pre><blockquote><p>若想修改主機的網卡名稱,可以編輯<code>/etc/udev/rules.d/70-persistent-net.rules</code>。</p></blockquote><p>其中<code>controller</code>的<code>eth2</code>需設定為以下:</p><pre><code>auto <ethx>iface <ethx> inet manual up ip link set dev $IFACE up down ip link set dev $IFACE down</code></pre><h2 id="事前準備"><a href="#事前準備" class="headerlink" title="事前準備"></a>事前準備</h2><p>安裝前需要確認叢集滿足以下幾點:</p><ul><li>確認所有節點網路可以溝通。</li><li>Bare-node IPMI 設定完成。包含 Address、User 與 Password。</li><li>修改 Controller 的 <code>/etc/apt/sources.list</code>,使用<code>tw.archive.ubuntu.com</code>。</li></ul><h2 id="安裝-OpenStack-服務"><a href="#安裝-OpenStack-服務" class="headerlink" title="安裝 OpenStack 服務"></a>安裝 OpenStack 服務</h2><p>這邊採用 DevStack 來部署測試環境,首先透過以下指令取得 DevStack:</p><pre><code class="sh">$ sudo useradd -s /bin/bash -d /opt/stack -m stack$ echo "stack ALL=(ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/stack$ sudo su - stack$ git clone https://git.openstack.org/openstack-dev/devstack$ cd devstack</code></pre><p>接著撰寫 local.conf 來描述部署過程所需的服務:</p><pre><code class="sh">$ wget https://kairen.github.io/files/devstack/ironic-local.conf -O local.conf$ sed -i 's/HOST_IP=.*/HOST_IP=172.22.132.93/g' local.conf</code></pre><blockquote><p><code>HOST_IP</code>請更換為自己環境 IP。有其他 Driver 請記得加入。</p></blockquote><p>完成後執行部署腳本進行建置:</p><pre><code class="sh">$ ./stack.sh</code></pre><blockquote><p>大約經過 15 min 就可以完成整個環境安裝。</p></blockquote><p>測試 OpenStack 環境:</p><pre><code class="sh">$ source openrc admin$ openstack user list+----------------------------------+----------------+| ID | Name |+----------------------------------+----------------+| 3ba4e813270e4e98ad781f4103284e0d | demo || 40c6014bc18f407fbfbc22aadedb1ca0 | placement || 567156ad1c7b4ccdbcd4ea02e7c44ce3 | alt_demo || 7a22ce5036614993a707dd976c505ccd | swift || 8d392f051afe45008289abca4dadf3ca | swiftusertest1 || a6e616af3bf04611bc23625e71a22e64 | swiftusertest4 || a835f1674648427396a7c6ac7e5eef06 | neutron || b2bf73ef2eaa425c93e4f552e9266056 | swiftusertest2 || b7de1af8522b495c8a9fb743eb6e7f59 | nova || cada5913a03e4f2794066902144264d3 | admin || f03e39680b234474b139d00c3fbca989 | swiftusertest3 || f0a4033463f64c00858ff05525545b6d | glance-swift || f2a1b186e7e84b10ae7e8f810e5c2412 | glance || ff31787d136f4fba96c19af419b8559c | ironic |+----------------------------------+----------------+</code></pre><p>測試 ironic 是否正常運行:</p><pre><code class="sh">$ ironic driver-list+---------------------+----------------+| Supported driver(s) | Active host(s) |+---------------------+----------------+| agent_ipmitool | ironic-dev || fake | ironic-dev || ipmi | ironic-dev || pxe_ipmitool | ironic-dev |+---------------------+----------------+</code></pre><h3 id="建立-Bare-metal-網路"><a href="#建立-Bare-metal-網路" class="headerlink" title="建立 Bare metal 網路"></a>建立 Bare metal 網路</h3><p>首先我們需要設定一個網路來提供 DHCP, PXE 與其他需求使用,這部分會說明如何建立一個 Flat network 來提供裸機配置用。詳細可參考 <a href="https://docs.openstack.org/ironic/latest/install/configure-networking.html" target="_blank" rel="noopener">Configure the Networking service for bare metal provisioning</a>。</p><p>首先編輯<code>/etc/neutron/plugins/ml2/ml2_conf.ini</code>修改以下內容:</p><pre><code>[ml2_type_flat]flat_networks = public, physnet1[ovs]datapath_type = systembridge_mappings = public:br-ex, physnet1:br-eth2tunnel_bridge = br-tunlocal_ip = 172.22.132.93</code></pre><p>接著建立 bridge 來處理實體網路與 OpenStack 之間的溝通:</p><pre><code class="sh">$ sudo ovs-vsctl add-br br-eth2$ sudo ovs-vsctl add-port br-eth2 eth2</code></pre><p>完成後重新啟動 Neutron server 與 agent:</p><pre><code class="sh">$ sudo systemctl restart devstack@q-svc.service$ sudo systemctl restart devstack@q-agt.service</code></pre><p>建立完成後,OVS bridges 會類似如下:</p><pre><code class="sh">$ sudo ovs-vsctl show Bridge br-int fail_mode: secure Port "int-br-eth2" Interface "int-br-eth2" type: patch options: {peer="phy-br-eth2"} Port br-int Interface br-int type: internal Bridge "br-eth2" Port "phy-br-eth2" Interface "phy-br-eth2" type: patch options: {peer="int-br-eth2"} Port "eth2" Interface "eth2" Port "br-eth2" Interface "br-eth2" type: internal</code></pre><p>接著建立 Neutron flat 網路來提供使用:</p><pre><code class="sh">$ neutron net-create sharednet1 \ --shared \ --provider:network_type flat \ --provider:physical_network physnet1$ neutron subnet-create sharednet1 172.22.132.0/24 \ --name sharedsubnet1 \ --ip-version=4 --gateway=172.22.132.254 \ --allocation-pool start=172.22.132.180,end=172.22.132.200 \ --enable-dhcp</code></pre><blockquote><p>P.S. neutron-client 在未來會被移除,故請轉用 <a href="https://docs.openstack.org/install-guide/launch-instance-networks-provider.html" target="_blank" rel="noopener">Provider network</a>。</p></blockquote><h3 id="設定-Ironic-cleaning-network"><a href="#設定-Ironic-cleaning-network" class="headerlink" title="設定 Ironic cleaning network"></a>設定 Ironic cleaning network</h3><p>當使用到 <a href="http://docs.openstack.org/ironic/latest/admin/cleaning.html#node-cleaning" target="_blank" rel="noopener">Node cleaning</a> 時,我們必須設定<code>cleaning_network</code>選項來提供使用。首先取得 Network 資訊,透過以下指令:</p><pre><code class="sh">$ openstack network list+--------------------------------------+------------+----------------------------------------------------------------------------+| ID | Name | Subnets |+--------------------------------------+------------+----------------------------------------------------------------------------+| 03de10a0-d4d2-43ce-83db-806a5277dd29 | private | 2a651bfb-776d-47f4-a958-f8a418f7fcd5, 99bdbd78-7a20-41b7-afa3-7cf7bf25b95b || 349a6a5b-1e26-4e36-8444-f6a6bbbdd227 | public | 032a516e-3d55-4623-995d-06ee033eaee4, daf733a9-492e-4ea6-8a45-6364b88a8f6f || ade096bd-6a86-4d90-9cf4-bce9921f7257 | sharednet1 | 3f9f2a47-fdd9-472b-a6a2-ce6570e490ff |+--------------------------------------+------------+----------------------------------------------------------------------------+</code></pre><p>編輯<code>/etc/ironic/ironic.conf</code>修改一下內容:</p><pre><code>[neutron]cleaning_network = sharednet1</code></pre><p>完成後,重新啟動 Ironic 服務:</p><pre><code class="sh">$ sudo systemctl restart devstack@ir-api.service$ sudo systemctl restart devstack@ir-cond.service</code></pre><h3 id="建立-Deploy-與-User-映像檔"><a href="#建立-Deploy-與-User-映像檔" class="headerlink" title="建立 Deploy 與 User 映像檔"></a>建立 Deploy 與 User 映像檔</h3><p>裸機服務在配置時需要兩組映像檔,分別為 <code>Deploy</code> 與 <code>User</code> 映像檔,其功能如下:</p><ul><li><code>Deploy images</code>: 用來準備裸機服務機器以進行實際的作業系統部署,在 Cleaning 等階段會使用到。</li><li><code>User images</code>:最後安裝至裸機服務提供給使用者使用的作業系統映像檔。</li></ul><p>由於 DevStack 預設會建立一組 Deploy 映像檔,這邊只針對 User 映像檔做手動建構說明,若要建構 Deploy 映像檔可以參考 <a href="https://docs.openstack.org/ironic/latest/install/deploy-ramdisk.html#deploy-ramdisk" target="_blank" rel="noopener">Building or downloading a deploy ramdisk image</a>。</p><p>首先我們必須先安裝<code>disk-image-builder</code>工具來提供建構映像檔:</p><pre><code class="sh">$ virtualenv dib$ source dib/bin/activate(dib) $ pip install diskimage-builder</code></pre><p>接著執行以下指令來進行建構映像檔:</p><pre><code class="sh">$ cat <<EOF > k8s.repo[kubernetes]name=Kubernetesbaseurl=http://yum.kubernetes.io/repos/kubernetes-el7-x86_64enabled=1gpgcheck=0repo_gpgcheck=0gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpgEOF$ DIB_YUM_REPO_CONF=k8s.repo \ DIB_DEV_USER_USERNAME=kyle \ DIB_DEV_USER_PWDLESS_SUDO=yes \ DIB_DEV_USER_PASSWORD=r00tme \ disk-image-create \ centos7 \ dhcp-all-interfaces \ devuser \ yum \ epel \ baremetal \ -o k8s.qcow2 \ -p vim,docker,kubelet,kubeadm,kubectl,kubernetes-cni...Converting image using qemu-img convertImage file k8s.qcow2 created...</code></pre><p>完成後會看到以下檔案:</p><pre><code class="sh">$ lsdib k8s.d k8s.initrd k8s.qcow2 k8s.repo k8s.vmlinuz</code></pre><p>上傳至 Glance 以提供使用:</p><pre><code class="sh"># 上傳 Kernel$ openstack image create k8s.kernel \ --public \ --disk-format aki \ --container-format aki < k8s.vmlinuz# 上傳 Initrd$ openstack image create k8s.initrd \ --public \ --disk-format ari \ --container-format ari < k8s.initrd# 上傳 Qcow2$ export MY_VMLINUZ_UUID=$(openstack image list | awk '/k8s.kernel/ { print $2 }')$ export MY_INITRD_UUID=$(openstack image list | awk '/k8s.initrd/ { print $2 }')$ openstack image create k8s \ --public \ --disk-format qcow2 \ --container-format bare \ --property kernel_id=$MY_VMLINUZ_UUID \ --property ramdisk_id=$MY_INITRD_UUID < k8s.qcow2</code></pre><h2 id="建立-Ironic-節點"><a href="#建立-Ironic-節點" class="headerlink" title="建立 Ironic 節點"></a>建立 Ironic 節點</h2><p>在所有服務配置都完成後,這時候要註冊實體機器資訊,來提供給 Compute 服務部署時使用。首先確認 Ironic 的 Driver 是否有資源機器的 Power driver:</p><pre><code class="sh">$ ironic driver-list+---------------------+----------------+| Supported driver(s) | Active host(s) |+---------------------+----------------+| agent_ipmitool | ironic-dev || fake | ironic-dev || ipmi | ironic-dev || pxe_ipmitool | ironic-dev |+---------------------+----------------+</code></pre><blockquote><p>若有缺少的話,請參考 <a href="https://docs.openstack.org/ironic/latest/install/setup-drivers.html" target="_blank" rel="noopener">Set up the drivers for the Bare Metal service</a>。</p></blockquote><p>確認有支援後,透過以下指令來建立 Node,並進行註冊:</p><pre><code class="sh">$ export DEPLOY_VMLINUZ_UUID=$(openstack image list | awk '/ipmitool.kernel/ { print $2 }')$ export DEPLOY_INITRD_UUID=$(openstack image list | awk '/ipmitool.initramfs/ { print $2 }')$ ironic node-create -d agent_ipmitool \ -n bare-node-1 \ -i ipmi_address=172.20.3.194 \ -i ipmi_username=maas \ -i ipmi_password=passwd \ -i ipmi_port=623 \ -i deploy_kernel=$DEPLOY_VMLINUZ_UUID \ -i deploy_ramdisk=$DEPLOY_INITRD_UUID</code></pre><blockquote><p>若使用 Console 的話,要加入<code>-i ipmi_terminal_port=9000</code>,可參考 <a href="https://docs.openstack.org/ironic/latest/admin/console.html" target="_blank" rel="noopener">Configuring Web or Serial Console</a>。</p></blockquote><p>接著更新機器資訊,這邊透過手動方式來更新資訊:</p><pre><code class="sh">$ export NODE_UUID=$(ironic node-list | awk '/bare-node-1/ { print $2 }')$ ironic node-update $NODE_UUID add \ properties/cpus=4 \ properties/memory_mb=8192 \ properties/local_gb=100 \ properties/root_gb=100 \ properties/cpu_arch=x86_64</code></pre><p>(option)也可以使用 inspector 來識別裸機機器的硬體資訊,但需要修改<code>/etc/ironic-inspector/dnsmasq.conf</code>修改一下:</p><pre><code>no-daemonport=0interface=eth1bind-interfacesdhcp-range=172.22.132.200,172.22.132.210dhcp-match=ipxe,175dhcp-boot=tag:!ipxe,undionly.kpxedhcp-boot=tag:ipxe,http://172.22.132.93:3928/ironic-inspector.ipxedhcp-sequential-ip</code></pre><blockquote><p>完成後,透過 systemctl 重新啟動背景服務<code>devstack@ironic-inspector-dhcp.service</code>與<code>devstack@ironic-inspector.service</code>。</p></blockquote><p>透過 port create 來把 Node 的所有網路資訊進行註冊:</p><pre><code class="sh">$ ironic port-create -n $NODE_UUID -a NODE_MAC_ADDRESS</code></pre><blockquote><p>這邊<code>NODE_MAC_ADDRESS</code>是指<code>bare-node-1</code>節點的 PXE(eth1)網卡 Mac Address,如 54:a0:50:85:d5:fa。</p></blockquote><p>完成後透過 validate 指令來檢查:</p><pre><code class="sh">$ ironic node-validate $NODE_UUID+------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| Interface | Result | Reason |+------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+| boot | False | Cannot validate image information for node 8e6fd86a-8eed-4e24-a510-3f5ebb0a336a because one or more parameters are missing from its instance_info. Missing are: ['ramdisk', 'kernel', 'image_source'] || console | False | Missing 'ipmi_terminal_port' parameter in node\'s driver_info. || deploy | False | Cannot validate image information for node 8e6fd86a-8eed-4e24-a510-3f5ebb0a336a because one or more parameters are missing from its instance_info. Missing are: ['ramdisk', 'kernel', 'image_source'] || inspect | True | || management | True | || network | True | || power | True | || raid | True | || storage | True | |+------------+--------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+</code></pre><blockquote><p>P.S. 這邊<code>boot</code>與<code>deploy</code>的錯誤若是如上所示的話,可以直接忽略,這是因為使用 Nova 來管理 baremetal 會出現的問題。</p></blockquote><p>最後利用 provision 指令來測試節點是否能夠提供服務:</p><pre><code class="sh">$ ironic --ironic-api-version 1.34 node-set-provision-state $NODE_UUID manage$ ironic --ironic-api-version 1.34 node-set-provision-state $NODE_UUID provide$ ironic node-list+--------------------------------------+--------+---------------+-------------+--------------------+-------------+| UUID | Name | Instance UUID | Power State | Provisioning State | Maintenance |+--------------------------------------+--------+---------------+-------------+--------------------+-------------+| 0c20cf7d-0a36-46f4-ac38-721ff8bfb646 | bare-0 | None | power off | cleaning | False |+--------------------------------------+--------+---------------+-------------+--------------------+-------------+</code></pre><blockquote><p>這時候機器會進行 clean 過程,經過一點時間就會完成,若順利完成則該節點就可以進行部署了。若要了解細節狀態,可以參考 <a href="https://docs.openstack.org/ironic/latest/contributor/states.html" target="_blank" rel="noopener">Ironic’s State Machine</a>。</p></blockquote><p><img src="/images/openstack/ironic-clean.png" alt=""></p><h2 id="透過-Nova-部署-baremetal-機器"><a href="#透過-Nova-部署-baremetal-機器" class="headerlink" title="透過 Nova 部署 baremetal 機器"></a>透過 Nova 部署 baremetal 機器</h2><p>最後我們要透過 Nova API 來部署裸機,在開始前要建立一個 flavor 跟上傳 keypair 來提供使用:</p><pre><code class="sh">$ ssh-keygen -t rsa$ openstack keypair create --public-key ~/.ssh/id_rsa.pub default$ openstack flavor create --vcpus 4 --ram 8192 --disk 100 baremetal.large</code></pre><p>完成後,即可透過以下指令進行部署:</p><pre><code class="sh">$ NET_ID=$(openstack network list | awk '/sharednet1/ { print $2 }')$ openstack server create --flavor baremetal.large \ --nic net-id=$NET_ID \ --image k8s \ --key-name default k8s-01</code></pre><p>經過一段時間後,就會看到部署完成,這時候可以透過以下指令來確認部署結果:</p><pre><code class="sh">$ openstack server list+--------------------------------------+--------+--------+---------------------------+-------+-----------------+| ID | Name | Status | Networks | Image | Flavor |+--------------------------------------+--------+--------+---------------------------+-------+-----------------+| a40e5cb1-dfc6-44d5-b638-648e8c0975fb | k8s-01 | ACTIVE | sharednet1=172.22.132.187 | k8s | baremetal.large |+--------------------------------------+--------+--------+---------------------------+-------+-----------------+$ openstack baremetal list+--------------------------------------+--------+--------------------------------------+-------------+--------------------+-------------+| UUID | Name | Instance UUID | Power State | Provisioning State | Maintenance |+--------------------------------------+--------+--------------------------------------+-------------+--------------------+-------------+| 0c20cf7d-0a36-46f4-ac38-721ff8bfb646 | bare-0 | a40e5cb1-dfc6-44d5-b638-648e8c0975fb | power on | active | False |+--------------------------------------+--------+--------------------------------------+-------------+--------------------+-------------+</code></pre><p>最後透過 ssh 來進入部署機器來建立應用:</p><pre><code class="sh">$ ssh kyle@172.22.132.187[kyle@host-172-22-132-187 ~]$ sudo systemctl start kubelet.service[kyle@host-172-22-132-187 ~]$ sudo systemctl start docker.service[kyle@host-172-22-132-187 ~]$ sudo kubeadm init --service-cidr 10.96.0.0/12 \ --kubernetes-version v1.7.4 \ --pod-network-cidr 10.244.0.0/16 \ --apiserver-advertise-address 172.22.132.187 \ --token b0f7b8.8d1767876297d85c</code></pre><blockquote><p>整合<code>Magnum</code>有空再寫,先簡單玩玩吧。</p></blockquote><p>若是懶人可以用 Dashboard 來部署,另外本教學的 DevStack 有使用 Ironic UI,因此可以在以下頁面看到 node 資訊。<br><img src="/images/openstack/ironic-ui.png" alt=""></p>]]></content>
<summary type="html">
<p><a href="https://docs.openstack.org/ironic/latest/user/index.html" target="_blank" rel="noopener">Ironic</a> 是 OpenStack 專案之一,主要目的是提供裸機機器部署服務(Bare-metal service)。它能夠單獨或整合 OpenStack 其他服務被使用,而可整合服務包含 Keystone、Nova、Neutron、Glance 與 Swift 等核心服務。當使用 Compute 與 Network 服務對 Bare-metal 進行適當的配置時,OpenStack 可以透過 Compute API 同時部署虛擬機(Virtual machines)與裸機(Bare machines)。</p>
<p>本篇為了精簡安裝過程,故這邊不採用手動安裝教學(會在 Gitbook 書上更新),因此採用 <a href="https://docs.openstack.org/devstack/latest/" target="_blank" rel="noopener">DevStack</a> 來部署服務,再手動設定一些步驟。</p>
<p>本環境安裝資訊:</p>
<ul>
<li>OpenStack Pike</li>
<li>DevStack Pike</li>
<li>Pike Pike Pike ….</li>
</ul>
</summary>
<category term="OpenStack" scheme="https://kairen.github.io/categories/OpenStack/"/>
<category term="OpenStack" scheme="https://kairen.github.io/tags/OpenStack/"/>
<category term="DevStack" scheme="https://kairen.github.io/tags/DevStack/"/>
<category term="Bare-metal" scheme="https://kairen.github.io/tags/Bare-metal/"/>
</entry>
<entry>
<title>利用 LinuxKit 建立 Kubernetes 叢集</title>
<link href="https://kairen.github.io/2017/07/22/kubernetes/deploy/linuxkit-k8s/"/>
<id>https://kairen.github.io/2017/07/22/kubernetes/deploy/linuxkit-k8s/</id>
<published>2017-07-21T16:00:00.000Z</published>
<updated>2017-08-08T08:01:50.000Z</updated>
<content type="html"><![CDATA[<p>LinuxKit 是以 Container 來建立最小、不可變的 Linux 作業系統映像檔框架,先前有簡單介紹與操作過,可以參考<a href="https://kairen.github.io/2017/04/23/container/linuxkit/">LinuxKit</a>。本篇則將利用 LinuxKit 來建立 Kubernetes 的映像檔,並部署簡單的 Kubernetes 叢集。</p><p><img src="/images/kube/moby+kubernetes.png" alt=""></p><a id="more"></a><p>本次教學會在<code>Mac OS X</code>作業系統上進行,而部署的軟體資訊如下:</p><ul><li>Kubernetes v1.7.2(2017-08-07, Update)</li><li>Etcd v3</li><li>Weave</li><li>Docker v17.06.0-ce</li></ul><h2 id="預先準備資訊"><a href="#預先準備資訊" class="headerlink" title="預先準備資訊"></a>預先準備資訊</h2><ul><li>主機已安裝與啟動<code>Docker</code>工具。</li><li>主機已安裝<code>Git</code>工具。</li><li>主機以下載 LinuxKit 專案,並建構了 Moby 與 LinuxKit 工具。</li></ul><p>建構 Moby 與 LinuxKit 方法如以下操作:</p><pre><code class="sh">$ git clone https://github.com/linuxkit/linuxkit.git$ cd linuxkit$ make$ ./bin/moby versionmoby version 0.0commit: c2b081ed8a9f690820cc0c0568238e641848f58f$ ./bin/linuxkit versionlinuxkit version 0.0commit: 0e3ca695d07d1c9870eca71fb7dd9ede31a38380</code></pre><h2 id="建構-Kubernetes-系統映像檔"><a href="#建構-Kubernetes-系統映像檔" class="headerlink" title="建構 Kubernetes 系統映像檔"></a>建構 Kubernetes 系統映像檔</h2><p>首先我們要建立一個包好 Kubernetes 的 Linux 映像檔,而官方已經有做好範例,只要利用以下方式即可建構:</p><pre><code class="sh">$ cd linuxkit/projects/kubernetes/$ make build-vm-images...Create outputs: kube-node-kernel kube-node-initrd.img kube-node-cmdline</code></pre><h2 id="建置-Kubernetes-cluster"><a href="#建置-Kubernetes-cluster" class="headerlink" title="建置 Kubernetes cluster"></a>建置 Kubernetes cluster</h2><p>完成建構映像檔後,就可以透過以下指令來啟動 Master OS 映像檔,然後獲取節點 IP:</p><pre><code class="sh">$ ./boot.sh(ns: getty) linuxkit-025000000002:~\# ip addr show dev eth02: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000 link/ether 02:50:00:00:00:02 brd ff:ff:ff:ff:ff:ff inet 192.168.65.3/24 brd 192.168.65.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::abf0:9fa4:d0f4:8da2/64 scope link valid_lft forever preferred_lft forever</code></pre><p>啟動後,開啟新的 Console 來 SSH 進入 Master,來利用 kubeadm 初始化 Master:</p><pre><code class="sh">$ cd linuxkit/projects/kubernetes/$ ./ssh_into_kubelet.sh 192.168.65.3linuxkit-025000000002:/\# kubeadm-init.sh...kubeadm join --token 4236d3.29f61af661c49dbf 192.168.65.3:6443</code></pre><p>一旦 kubeadm 完成後,就會看到 Token,這時請記住 Token 資訊。接著開啟新 Console,然後執行以下指令來啟動 Node:</p><pre><code class="sh">console1>$ ./boot.sh 1 --token 4236d3.29f61af661c49dbf 192.168.65.3:6443</code></pre><blockquote><p>P.S. 開啟節點格式為<code>./boot.sh <n> [<join_args> ...]</code>。</p></blockquote><p>接著分別在開兩個 Console 來加入叢集:</p><pre><code class="sh">console2> $ ./boot.sh 2 --token 4236d3.29f61af661c49dbf 192.168.65.3:6443console3> $ ./boot.sh 3 --token 4236d3.29f61af661c49dbf 192.168.65.3:6443</code></pre><p>完成後回到 Master 節點上,執行以下指令來查看節點狀況:</p><pre><code class="sh">$ kubectl get noNAME STATUS AGE VERSIONlinuxkit-025000000002 Ready 16m v1.7.2linuxkit-025000000003 Ready 6m v1.7.2linuxkit-025000000004 Ready 1m v1.7.2linuxkit-025000000005 Ready 1m v1.7.2</code></pre><h2 id="簡單部署-Nginx-服務"><a href="#簡單部署-Nginx-服務" class="headerlink" title="簡單部署 Nginx 服務"></a>簡單部署 Nginx 服務</h2><p>Kubernetes 可以選擇使用指令直接建立應用程式與服務,或者撰寫 YAML 與 JSON 檔案來描述部署應用程式的配置,以下將建立一個簡單的 Nginx 服務:</p><pre><code class="sh">$ kubectl run nginx --image=nginx --replicas=1 --port=80$ kubectl get pods -o wideNAME READY STATUS RESTARTS AGE IP NODEnginx-1423793266-v0hpb 1/1 Running 0 38s 10.42.0.1 linuxkit-025000000004</code></pre><p>完成後要接著建立 svc(Service),來提供外部網路存取應用程式,使用以下指令建立:</p><pre><code class="sh">$ kubectl expose deploy nginx --port=80 --type=NodePort$ kubectl get svcNAME CLUSTER-IP EXTERNAL-IP PORT(S) AGEkubernetes 10.96.0.1 <none> 443/TCP 19mnginx 10.108.41.230 <nodes> 80:31773/TCP 5s</code></pre><p>由於這邊不是使用實體機器部署,因此網路使用 Docker namespace 網路,故這邊透過<code>ubuntu-desktop-lxde-vnc</code>來瀏覽 Nginx 應用:</p><pre><code class="sh">$ docker run -it --rm -p 6080:80 dorowu/ubuntu-desktop-lxde-vnc</code></pre><blockquote><p>完成後透過瀏覽器連接 <a href="localhost:6080" target="_blank" rel="noopener">HTLM VNC</a></p></blockquote><p><img src="/images/kube/docker-desktop.png" alt=""></p><p>最後關閉節點只需要執行以下即可:</p><pre><code class="sh">$ halt[ 1503.034689] reboot: Power down</code></pre>]]></content>
<summary type="html">
<p>LinuxKit 是以 Container 來建立最小、不可變的 Linux 作業系統映像檔框架,先前有簡單介紹與操作過,可以參考<a href="https://kairen.github.io/2017/04/23/container/linuxkit/">LinuxKit</a>。本篇則將利用 LinuxKit 來建立 Kubernetes 的映像檔,並部署簡單的 Kubernetes 叢集。</p>
<p><img src="/images/kube/moby+kubernetes.png" alt=""></p>
</summary>
<category term="Kubernetes" scheme="https://kairen.github.io/categories/Kubernetes/"/>
<category term="Docker" scheme="https://kairen.github.io/tags/Docker/"/>
<category term="Kubernetes" scheme="https://kairen.github.io/tags/Kubernetes/"/>
<category term="LinuxKit" scheme="https://kairen.github.io/tags/LinuxKit/"/>
</entry>
<entry>
<title>智能合約(Smart contracts)</title>
<link href="https://kairen.github.io/2017/05/28/blockchain/smart-contracts/"/>
<id>https://kairen.github.io/2017/05/28/blockchain/smart-contracts/</id>
<published>2017-05-28T09:08:54.000Z</published>
<updated>2017-08-02T02:33:37.000Z</updated>
<content type="html"><![CDATA[<p><code>智能合約(Smart Contracts)</code> 是在 Ethereum 區塊鏈中所屬的物件。它們包含程式碼函式以及能夠與其他合約進行互動、做出決策、儲存資料與傳送乙太幣給其他人。合約是由創建者所定義,但是它們的執行與他們所提供的服務,都是由 Ethereum 網路本身提供。它們將存在且可被執行,只要整個網路存在,並且只會因程式中有撰寫自我銷毀的功能才會消失。</p><p>我可以用合約做什麼呢?只要想像力夠豐富,要做什麼幾乎都沒問題,但以下指南只會是入門,讓我們去實現一些簡單的事情。</p><a id="more"></a><h3 id="Smart-Sponsor"><a href="#Smart-Sponsor" class="headerlink" title="Smart Sponsor"></a>Smart Sponsor</h3><p>本節將說明一智能合約範例,透過建構一個合約來允許以下賬戶持有人進行互動。</p><ul><li>一個慈善機構舉行籌款活動,我們稱之為 <strong>thebenefactor</strong>。</li><li>一個受贊助的 runner 想為慈善機構募款,我們稱之為 <strong>therunner</strong>。</li><li>其他的人想要贊助 runner,我們稱之為 <strong>thesponsor</strong>。</li><li>一個 Ethereum 節點,用來開採區塊鏈以驗證交易,我們稱之為 <strong>theminer</strong>。</li></ul><p>我們的合約(smartSponsor):</p><ul><li>是由一位 <strong>runner</strong> 透過贊助的執行來為慈善機構募款。</li><li>當建立合約時,<strong>runner</strong> 會任命為募集錢的捐助者。</li><li><strong>runner</strong> 則邀情其他人去進行贊助。用戶透過呼叫一個在智能合約上的函式,將乙太幣從 <strong>贊助商的帳戶</strong> 轉移到 <strong>合約</strong>,並保持乙太幣於合約,直到有進一步的通知。</li><li>在合約的時限期間的所有人都能看到誰是 <strong>捐助者</strong>,有多少的乙太幣被從誰捐(雖然贊助者可以匿名,當然:p)。</li></ul><p><img src="/images/blockchain/smartSponsor-1.png" alt="flow-1"></p><p>那麼有兩件事情可能發生:</p><ul><li>執行都按計劃進行,以及 <strong>runner</strong> 指示合約轉移到所有資金的捐助者。</li></ul><p><img src="/images/blockchain/smartSponsor-2.png" alt="flow-2"></p><ul><li>執行由於謀些原因無法承擔,而 runner 指示合約將退還贊助商的抵押。</li></ul><p><img src="/images/blockchain/smartSponsor-3.png" alt="flow-3"></p><p>Ethereum 允許智能合約由撰寫 Solidity 語言來定義。Solidity 的合約是類似於 Java 的類別定義。成員變數的儲存採用區塊鍊交易與合約的方法,來詢問合約或改變的其狀態。作為區塊鏈的副本會分散到網路中的所有節點,任何人都可以詢問合約,以從中搜尋公開的訊息。</p><p>合約有以下幾種方法:</p><ul><li><strong><code>smartSponsor</code></strong>:合約的建構子。它初始化合約的狀態。合約的建立者傳入賬戶的位址,有利於合約的 drawdown。</li><li><strong><code>pledge</code></strong>:任何人都可以呼叫捐贈乙太幣贊助基金。贊助商提供支援的選擇性訊息</li><li><strong><code>getPot</code></strong>:回傳當前儲存在合約的總乙太幣。</li><li><strong><code>refund</code></strong>:把贊助商的錢退回給贊助商。只有合約的擁有者才能呼叫這個函式。</li><li><strong><code>drawdown</code></strong>:傳送合約的總價值給捐助者賬戶。只有合約的擁有者才能呼叫這個函式。</li></ul><p>這個想法是使一個合約擁有約束力。他們不能拿回任何資金,除非整個合約被退還。在這種情況下,所有資料都是被公開存取的,這意味著任何人都有存取 Ethereum 區塊鏈,來查看誰建立了合約,誰是捐助者,以及誰透過存取合約程式碼本身保證了每一筆資金。</p><p>要注意很重要的一點,任何改變合約的狀態(建立、承若、退還或 drawing down)都需要在區塊鏈上建立交易,這意味著這些交易不會被儲存,要直到這些交易的區塊被開採。操作只能讀取到一個現有合約狀態(getPot 或讀取公有成員變數)都不需要進行挖礦。這是一個很重要且微妙的點:寫入操作是很慢的(因為我們要等到採礦完成)。由於這情況合約可能永遠不會被建立到區塊鍊中,因此呼叫方需要提供一些獎勵,來促進礦工去工作。這是被稱為 gas 的 Ethereum 術語,所有的寫入操作都是需要 gas 的支出來改變區塊鍊的狀態。</p><p>幸運的是我們不需要購買真正的乙太幣,以及參與 Ethereum 網路。我們可以使用相同的軟體,但要配置它運行一個本地測試區塊鏈,以及產生自己的假乙太幣。</p><p>以下為一個 Solidity 語言的智能合約範例:</p><pre><code class="js">contract smartSponsor { address public owner; address public benefactor; bool public refunded; bool public complete; uint public numPledges; struct Pledge { uint amount; address eth_address; bytes32 message; } mapping(uint => Pledge) public pledges; // constructor function smartSponsor(address _benefactor) { owner = msg.sender; numPledges = 0; refunded = false; complete = false; benefactor = _benefactor; } // add a new pledge function pledge(bytes32 _message) { if (msg.value == 0 || complete || refunded) throw; pledges[numPledges] = Pledge(msg.value, msg.sender, _message); numPledges++; } function getPot() constant returns (uint) { return this.balance; } // refund the backers function refund() { if (msg.sender != owner || complete || refunded) throw; for (uint i = 0; i < numPledges; ++i) { pledges[i].eth_address.send(pledges[i].amount); } refunded = true; complete = true; } // send funds to the contract benefactor function drawdown() { if (msg.sender != owner || complete || refunded) throw; benefactor.send(this.balance); complete = true; }}</code></pre><blockquote><ul><li><p>一個<code>Pledge</code>結構模型的捐贈,儲存著贊助商的帳戶 ID、承若押金,以及一些訊息字串。</p></li><li><p>這個<code>pledges</code>陣列儲存了一個承若方的列表。</p></li><li><p>合約中的所有成員變數都是公開的,所以<code>getters</code>將自動被建立。</p></li><li><p><code>throw</code>被稱為某些函式(functions),用以防止資料被寫入錯誤的資料到該區塊鏈中。</p></li></ul></blockquote><h2 id="參考連結"><a href="#參考連結" class="headerlink" title="參考連結"></a>參考連結</h2><ul><li><a href="https://medium.com/@kpcb_edge/our-thoughts-on-ethereum-31520b164e00#.2q1i88278" target="_blank" rel="noopener">Our thoughts on Ethereum</a></li><li><a href="https://www.ethereum.org/greeter" target="_blank" rel="noopener">Building a smart contract using the command line</a></li><li><a href="https://developer.ibm.com/clouddataservices/2016/05/19/block-chain-technology-smart-contracts-and-ethereum/" target="_blank" rel="noopener">Block chain technology, smart contracts and Ethereum</a></li></ul>]]></content>
<summary type="html">
<p><code>智能合約(Smart Contracts)</code> 是在 Ethereum 區塊鏈中所屬的物件。它們包含程式碼函式以及能夠與其他合約進行互動、做出決策、儲存資料與傳送乙太幣給其他人。合約是由創建者所定義,但是它們的執行與他們所提供的服務,都是由 Ethereum 網路本身提供。它們將存在且可被執行,只要整個網路存在,並且只會因程式中有撰寫自我銷毀的功能才會消失。</p>
<p>我可以用合約做什麼呢?只要想像力夠豐富,要做什麼幾乎都沒問題,但以下指南只會是入門,讓我們去實現一些簡單的事情。</p>
</summary>
<category term="Blockchain" scheme="https://kairen.github.io/categories/Blockchain/"/>
<category term="Ethereum" scheme="https://kairen.github.io/tags/Ethereum/"/>
<category term="Blockchain" scheme="https://kairen.github.io/tags/Blockchain/"/>
<category term="Solidity" scheme="https://kairen.github.io/tags/Solidity/"/>
<category term="Smart Contract" scheme="https://kairen.github.io/tags/Smart-Contract/"/>
</entry>
</feed>