安裝 Windows 11 虛擬機過程與問題

很久沒安裝 Windows 11 VM 了,原本想說應該很順利,結果裝了一整個晚上,沒想到會踩了一堆雷,所以這篇來紀錄一下安裝 Windows 11 VM 的過程:

讀取 ISO

在 Windows 11 開機的時候,若出現以下提示文字:

Press any key to boot from CD or DVD…

vm-windows-11-installation-1.png

此時,一定要按下鍵盤上的任意鍵,如果只用滑鼠輸入會失敗。

繞過 TPM 檢查

在安裝 Windows 11 時,系統會檢查硬體是否符合規格。如果是在虛擬機上安裝,得需要手動關閉 TPM 的檢查:

  1. 啟動命令提示字元:在安裝畫面按下 Shift + F10,這將開啟命令提示字元 (cmd)。
  2. 開啟登錄編輯器:在命令提示字元中輸入 regedit 並按下 Enter 鍵。
  3. 新增 LabConfig 機碼
  • 找到 HKEY_LOCAL_MACHINE\SYSTEM\Setup
  • 在此路徑下新增一個名為 LabConfig 的資料夾(機碼)。
  1. 繞過檢查的設定:在 HKEY_LOCAL_MACHINE\SYSTEM\Setup\LabConfig 機碼內新增以下三個 DWORD 32-bit 項目
  • BypassTPMCheck,值設為 1。
  • BypassRAMCheck,值設為 1。
  • BypassSecureBootCheck,值設為 1。

vm-windows-11-installation-2.png

完成這些設定後,關閉登錄編輯器,繼續安裝,即可成功繞過檢查。

帳號登入

在安裝過程的最後階段,Windows 可能會要求輸入帳號密碼,目前還不知道要略過此步驟。

建議

安裝完成後,建議馬上建立一個快照 (snapshot),這樣如果日後需要重置或修復系統,就不必重新安裝。

Reference

何時該使用 React Redux

今天這章會討論什麼時候要把資料放在 Redux 裡面,什麼時候要將資料放在 React 的 components 裡。

React 將 components 分成 Presentational Components (展示組件) 與 Container Components (容器組件)。

Presentational Components

Presentational Components 主要負責 UI 的部分,通常不會有複雜 application 狀態管理的資料,通過會由 props 傳入無狀態 (stateless) 的資料,像是:

  • 跟 UI 相關的資料 e.g. 顏色的使用
  • 只有在必要時才擁有自己的狀態 e.g. 下拉式選單的狀態
  • 需要手動建立新的東西 e.g. new post 的資料儲存

Container Components

Container Components 主要負責 application 的狀態,通常會使用 Redux 來管理,然後在 render 的時候會資料傳給 Presentational Components,通常會儲存有狀態的資料:

  • application data flow
  • 使用者相關的資訊 e.g. 最喜歡的顏色

Compare

特性 Presentational Container
主要用途 UI render application state
狀態 無狀態或是透過 props 獲得狀態 通常有狀態,透過 Redux 管理
類別 smart dumb
儲存類型 儲存複雜的東西 儲存簡單的東西

Reference

React Redux Example App 範例程式碼

前一篇介紹 Redux 是參考 Flux 的架構而設計的,這篇要用 Redux 來寫一個簡單的 React Counter App。

在 Ubuntu 18.04 上安裝 Docker CE

再提醒一下,React 跟 Redux 的差別是:React 是一個 front-end library;Redux 是一個架構,可以不用跟 React 一起使用,也可以跟 Vue 或 Angular 搭配。

Counter App

Install

建立一個 React App 然後安裝 Redux:

1
2
3
npx create-react-app counter-app
cd counter-app
npm install --save redux react-redux

Actions

建立 Redux 的 Action,Action 一定要回傳 type 讓 Reducer 去做對應的資料更新,Middleware 會在送到 Reducer 之前執行。

1
2
3
4
5
6
7
8
// actions.js
export const increment = () => ({
type: 'INCREMENT'
});

export const decrement = () => ({
type: 'DECREMENT'
});

Reducer

建立 Redux 的 Reducer,Reducer 不能修改原本的 state,必須複製一份,並且回傳最後 state 應該要的資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// reducer.js
const initialState = {
count: 0
};

const counterReducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREMENT':
return {
...state,
count: state.count + 1
};
case 'DECREMENT':
return {
...state,
count: state.count - 1
};
default:
return state;
}
};

export default counterReducer;

Store

建立 Redux 的 Store,用來儲存 state 的資料

1
2
3
4
5
6
7
// store.js
import { createStore } from 'redux';
import counterReducer from './reducer';

const store = createStore(counterReducer);

export default store;

Counter

建立 React Counter Element,而且注意這邊需要將 Action 傳入 Dispatcher 來讓後續的 Reducer 來更新狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Counter.js
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './actions';

const Counter = () => {
const count = useSelector(state => state.count);
const dispatch = useDispatch();

return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
</div>
);
};

export default Counter;

App

最後 react-redux library 透過 Provider Component 來跟 React 串接,Provider 會提供 Store 的資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
// App.js
import React from 'react';
import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';

const App = () => (
<Provider store={store}>
<Counter />
</Provider>
);

export default App;

Reference

Flux 跟 Redux 之間的關係

Flux 和 Redux 都是用來管理 application 狀態的架構,差異是不同的設計理念和實做方式:

Flux

Flux 是 Facebook 提出的架構模式,用於解決複雜的 data flow 問題,Flux 的架構如下:

  1. Action: 描述 application 的事件或行為。
  2. Dispatcher: 分發 Action 給 Store。它是 Flux 架構中的中央樞紐。
  3. Store: 儲存 application 狀態和邏輯。每個 Store 負責 application 一部分狀態。
  4. View: 展示 application UI,並且可以根據 Store 的變化來更新。

Flux 的 data flow 是單向的,從 Action 到 Dispatcher,再到 Store,最後到 view。

Redux

Redux 參考並簡化 Flux 管理狀態概念,Redux 的架構如下:

  1. Action: 與 Flux 中的 Action 類似,用於描述 application 的事件或行為。
  2. Reducer: 是一個 function,負責根據 Action 來更新 application 的狀態。Redux 中沒有 Sispatcher,取而代之的是 Reducer。
  3. Store: 儲存 application 的狀態。Redux 中只有一個單一的 Store,與 Flux 有多個 Store 不同。而且 Redux 只能透過 Action 可以修改 Store 的資料。
  4. Middleware: 用於處理異步操作或其他事件 e.g. Error Handling。

Redux 的 data flow 也是單向的,並且強調使用 function 來更新狀態。

Redux 使用單一集中狀態的物件,並以特定的方式進行更新。當你想要更新狀態時(e.g click event),會創建一個 Action 並由某個 Reducer 處理。Reducer 會複製當前狀態,且使用 Action 中的資料進行修改,然後返回新的狀態。當 Store 更新時,可以監聽事件並更新

Redux 跟 React 的差別是:React 是一個 front-end library;Redux 是一個架構,可以不用跟 React 一起使用,也可以跟 Vue 或 Angular 搭配。這篇是一個 React Redux 的範例:

React Redux Example App 範例程式碼

Compare

  • Redux 使用單一的 store: 與 Flux 在中多個 Store 中定位狀態信息不同,Redux 將所有內容保存在一個地方。在 Flux 中,可以有許多不同的 store。Redux 打破了這一點,強制使用單一 global Store。
  • Redux 使用 reducers: Reducers 是以不更動原本資料的方式來更新資料。在 Redux 中,行為是以可預測的,因為所有的改動都要經過 Reducer,且每一次的改動只會更新一次 global Store。
  • Redux 使用 middleware: 由於 Action 和資料以單向方式流動,我們可以透過 Redux 增加 middleware,並在資料更新時加上客製化的行為 e.g. Log or Catch Error。
  • Redux decouple Action 與 Store: 建立 Action 時不會向通知 Store 任何東西,反而是回傳 Action 物件;Flux 的 Action 則會直接修改 Store。

Reference

修改 Kubernetes Kube Proxy IPVS scheduler mode

由於 kube-proxy 在 ipvs scheduler 的模式是 rr,這篇會教學如何在 kubernetes cluster 裡面改變 ipvs scheduler 的模式。

環境

目前有三台 node 所組成的 kubernetes cluster

1
2
3
4
5
# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
node1 Ready master 5d7h v1.16.6 192.168.0.121 <none> Ubuntu 16.04.5 LTS 4.15.0-88-generic docker://18.9.7
node2 Ready master 5d7h v1.16.6 192.168.0.146 <none> Ubuntu 16.04.5 LTS 4.15.0-88-generic docker://18.9.7
node3 Ready <none> 5d7h v1.16.6 192.168.0.250 <none> Ubuntu 16.04.5 LTS 4.15.0-88-generic docker://18.9.7

Kubernetes 裡面有一個 hello world deployment,並使用 NodePort 暴露 8080 port 的位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/hello-868dc85486-8lvps 1/1 Running 0 3d2h
pod/hello-868dc85486-j2q7x 1/1 Running 0 3d2h
pod/hello-868dc85486-n2lvt 1/1 Running 0 3d2h
pod/hello-868dc85486-njcfn 1/1 Running 0 3d2h

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.233.0.1 <none> 443/TCP 5d8h
service/node-port NodePort 10.233.2.107 <none> 8080:31191/TCP 35h

NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/hello 4/4 4 4 3d2h

NAME DESIRED CURRENT READY AGE
replicaset.apps/hello-868dc85486 4 4 4 3d2h

使用 ipvsadm 查看 ipvs 的 scheduler 模式,目前的模式是 rr。

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
# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 169.254.25.10:31191 rr
-> 10.233.90.16:8080 Masq 1 0 0
-> 10.233.92.16:8080 Masq 1 0 0
-> 10.233.92.17:8080 Masq 1 0 0
-> 10.233.96.12:8080 Masq 1 0 0
TCP 172.17.0.1:31191 rr
-> 10.233.90.16:8080 Masq 1 0 0
-> 10.233.92.16:8080 Masq 1 0 0
-> 10.233.92.17:8080 Masq 1 0 0
-> 10.233.96.12:8080 Masq 1 0 0
TCP 192.168.0.121:31191 rr
-> 10.233.90.16:8080 Masq 1 0 0
-> 10.233.92.16:8080 Masq 1 0 0
-> 10.233.92.17:8080 Masq 1 0 0
-> 10.233.96.12:8080 Masq 1 0 0
TCP 10.233.90.0:31191 rr
-> 10.233.90.16:8080 Masq 1 0 0
-> 10.233.92.16:8080 Masq 1 0 0
-> 10.233.92.17:8080 Masq 1 0 0
-> 10.233.96.12:8080 Masq 1 0 0
...

下一節將說明如何修改 ipvs scheduler 的方法。

方法

首先,IPVS scheduler 的設定是在 kube-proxy 的 ConfigMap 裡面。

1
kubectl edit cm kube-proxy -n kube-system

data.config.conf.ipvs.scheduler 裡的參數修改成其他的 scheduler 模式:

1
2
3
4
data:
config.conf:
ipvs:
scheduler: sh

Service: IPVS proxy mode 裡面有提供以下的 scheduler 的模式:

  • rr: round-robin
  • lc: least connection (smallest number of open connections)
  • dh: destination hashing
  • sh: source hashing
  • sed: shortest expected delay
  • nq: never queue

或參考 IPVS wiki

刪除原有的 kube-proxy 的 pod,daemonset.apps/kube-proxy 會自動重新建立新的 kube-proxy Pod。

1
kubectl delete -n kube-system $(kubectl get all -n kube-system | grep pod/kube-proxy | cut -d ' ' -f 1)

再次使用 ipvsadm 查看 ipvs 的 scheduler 就會是修改後的狀態。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ipvsadm -ln
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
TCP 169.254.25.10:31191 sh
-> 10.233.90.16:8080 Masq 1 0 0
-> 10.233.92.16:8080 Masq 1 0 0
-> 10.233.92.17:8080 Masq 1 0 0
-> 10.233.96.12:8080 Masq 1 0 0
TCP 172.17.0.1:31191 sh
-> 10.233.90.16:8080 Masq 1 0 0
-> 10.233.92.16:8080 Masq 1 0 0
-> 10.233.92.17:8080 Masq 1 0 0
-> 10.233.96.12:8080 Masq 1 0 0
TCP 192.168.0.121:31191 sh
-> 10.233.90.16:8080 Masq 1 0 0
-> 10.233.92.16:8080 Masq 1 0 0
-> 10.233.92.17:8080 Masq 1 0 0
-> 10.233.96.12:8080 Masq 1 0 0
TCP 10.233.90.0:31191 sh
-> 10.233.90.16:8080 Masq 1 0 0
-> 10.233.92.16:8080 Masq 1 0 0
-> 10.233.92.17:8080 Masq 1 0 0
-> 10.233.96.12:8080 Masq 1 0 0

Reference

設定 Google Cloud Platform 第三方服務金鑰

這次是將現有的 Google Cloud Functions (GCF) 專案透過 GitHub Actions 自動化部屬至 Google Cloud Platform (GCP)。GitHub Actions 是第三方的服務,GCP 須建立金鑰給第三方服務使用,這篇將會記錄要如何設定 GCP 的金鑰。

環境

  • Google Cloud Platform: 需先建立專案,並開啟帳單功能,ProjectID 為 short-url-256304。

方法

建立憑證金鑰

  1. 點選「 IAM 與管理員」
  2. 點選「服務帳戶」
  3. 點選「建立服務帳戶」

GCP IAM STEP 1

  1. 輸入服務帳戶名稱
  2. 點選「建立」

GCP IAM STEP 2

  1. 點選「角色」-> 選擇要部署要應用程式「XXX開發人員」(下圖以 Gloud Functions 為例) -> 點選「建立」

GCP IAM STEP 3

  1. 點選「建立金鑰」

GCP IAM STEP 4

  1. 選擇金鑰類型「JSON」
  2. 點選「建立」,此時會下載一份金鑰檔,將這份檔案保存好
  3. 點選「完成」

GCP IAM STEP 5

  1. 建立完成畫面

GCP IAM STEP 6

賦予服務帳戶權限

這時候根據建立完還不能透過第三方部署應用程式,會出現以下錯誤訊息:

1
2
3
ERROR: (gcloud.functions.deploy) ResponseError: status=[403], code=[Forbidden], message=[Missing necessary permission iam.serviceAccounts.actAs for ***@appspot.gserviceaccount.com on project ***. 
Please grant ***@appspot.gserviceaccount.com the roles/iam.serviceAccountUser role.
You can do that by running 'gcloud projects add-iam-policy-binding *** --member=***@appspot.gserviceaccount.com --role=roles/iam.serviceAccountUser'

錯誤訊息有提示你要如何解決這個問題,所以照提示訊息輸入以下指令,需將 PROJECT_IDDEPLOY_NAME 改成你現在使用的專案:

1
2
3
4
export PROJECT_ID=short-url-256304
export DEPLOY_NAME=deploy-to-gcp

gcloud iam service-accounts add-iam-policy-binding $PROJECT_ID@appspot.gserviceaccount.com --member=serviceAccount:$DEPLOY_NAME@$PROJECT_ID.iam.gserviceaccount.com --role=roles/iam.serviceAccountUser --project=$PROJECT_ID

操作步驟:

  1. 點選右上角圖示「啟用 Cloud Shell」
  2. 在終端機上輸入賦予帳戶權限的指令

GCP IAM STEP 7

測試

使用的 Repo 是 akiicat/short-url,透過 GitHub Actions 自動部署至 Google Cloud Functions 的設定檔,注意 jobs.deploy.steps[] 裡的前兩個步驟:

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
# .github/workflows/google-cloud-functions.yml
name: GoogleCloudFuncitons
on: [push]
jobs:
deploy:
name: GCP Authenticate
runs-on: ubuntu-latest
steps:
- name: Setup Google Cloud
uses: actions/gcloud/auth@master
env:
# https://github.com/actions/gcloud/issues/13#issuecomment-511596628
GCLOUD_AUTH: ${{ secrets.GCLOUD_AUTH }}

- name: GCP Setup Project ID
uses: actions/gcloud/cli@master
with:
args: "config set project ${{ secrets.ProjectID }}"

- name: GCP Functions List
uses: actions/gcloud/cli@master
with:
args: "functions list"

- name: Deploy to GCP
uses: actions/gcloud/cli@master
with:
args: "functions deploy link --entry-point=Link --runtime=go111 --trigger-http"

第一個步驟(Setup Google Cloud),是使用先前下載 JSON的金鑰向 Google Cloud Platform 認證權限,這裡的 GCLOUD_AUTH 需先將 JSON 檔轉成 base64 的格式,可以參考這篇 issue

1
2
3
$ base64 short-url-256304-287.json
ewogICJ0eXBlIjogInNlcnZpY2VfYWNjb3VudCIsCiAgInByb2plY3RfaWQiOiAic2hvcnQtdXJsLTI1NjMwNCIsCiAgI...
lcGxveS10by1nY2YlNDBzaG9ydC11cmwtMjU2MzA0LmlhbS5nc2VydmljZWFjY291bnQuY29tIgp9Cg==

第二個步驟(GCP Setup Project ID),是設定 Google Cloud Platform 的 Project ID

secrets.GCLOUD_AUTHsecrets.ProjectID 的設定請參考 Virtual environments for GitHub Actions

Reference

RBAC Kubernetes 安裝 helm

環境

  • Google Kubernetes Engine
  • Kubernetes version 1.12.8
  • Helm version 2.14.1

安裝 Helm without RBAC

Helm 是 Kubernetes 的管理工具,Tiller 則是 API server,至於之間的差別,Helm 是 Client 端,Tiller 是 Server 端。輸入以下指令會安裝 HelmTiller

1
helm init

官方文件建議加上 –history-max 參數,限制最大歷史指令的數量,如果沒有設定最大歷史記錄,則永遠保留歷史記錄。

1
helm init --history-max 200

##設定 RBAC

在雲端平台上,由於有安全性的問題,如果不設定 RBAC,之後可能會無法正常使用,所以需要提供足夠的權限給 tiller,輸入以下指令建立權限,ServiceAccount 的名稱為 tiller

1
2
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller

套用 tiller 的 ServiceAccount 到已經現有的上

1
kubectl patch deployment --namespace kube-system tiller-deploy -p '{"spec":{"template":{"spec":{"serviceAccount":"tiller"}}}}'

安裝 Helm with RBAC

所有設定一次到位,在 helm init 的時候,加了 –service-account 參數,直接套用 ServiceAccount:

1
2
3
kubectl create serviceaccount --namespace kube-system tiller
kubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller
helm init --service-account tiller --history-max 200

測試

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
$ helm install --name my-db stable/influxdb
NAME: my-db
LAST DEPLOYED: Wed Jul 3 23:13:08 2019
NAMESPACE: default
STATUS: DEPLOYED

RESOURCES:
==> v1/ConfigMap
NAME DATA AGE
my-db-influxdb 1 1s

==> v1/PersistentVolumeClaim
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
my-db-influxdb Pending standard 1s

==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
my-db-influxdb-689d74646c-6fwc4 0/1 Pending 0 1s

==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-db-influxdb ClusterIP 10.11.249.197 <none> 8086/TCP,8088/TCP 1s

==> v1beta1/Deployment
NAME READY UP-TO-DATE AVAILABLE AGE
my-db-influxdb 0/1 1 0 1s

...

查看套件清單

1
2
3
$ helm ls
NAME REVISION UPDATED STATUS CHART APP VERSION NAMESPACE
my-db 1 Wed Jul 3 23:13:08 2019 DEPLOYED influxdb-1.1.9 1.7.6 default

刪除測試檔案,–purge 參數可以徹底刪除,不留下任何記錄,名稱可以透過上面的指令查詢:

1
helm delete --purge my-db

解除安裝 Helm

1
2
3
kubectl -n kube-system delete deployment tiller-deploy
kubectl delete clusterrolebinding tiller
kubectl -n kube-system delete serviceaccount tiller

Reference

在 kubernetes 上建立 influxdb 1.7.7

環境

  • Google Kubernetes Engine
  • Kubernetes 版本為 1.12.8

建立 Secret

Kubernetes secret 可以存放重要訊息,像是使用者密碼之類,不適合暴露在外。

設定以下參數,稍後提供給 influxdb 使用:

  1. INFLUXDB_DATABASE:資料庫名稱
  2. INFLUXDB_HOST:主機名稱
  3. INFLUXDB_USERNAME:使用者名稱
  4. INFLUXDB_PASSWORD:使用者密碼
1
2
3
4
5
kubectl create secret generic influxdb-creds \
--from-literal=INFLUXDB_DATABASE=db \
--from-literal=INFLUXDB_USERNAME=root \
--from-literal=INFLUXDB_PASSWORD=root \
--from-literal=INFLUXDB_HOST=influxdb

輸入以下指令檢查參數是否設定完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$ kubectl get secrets
NAME TYPE DATA AGE
influxdb-creds Opaque 4 46m

$ kubectl describe secrets influxdb-creds
Name: influxdb-creds
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
INFLUXDB_DATABASE: 12 bytes
INFLUXDB_HOST: 8 bytes
INFLUXDB_PASSWORD: 4 bytes
INFLUXDB_USERNAME: 4 bytes

密文型態為 Opaque,裡面有四筆資料

建立儲存空間 Persistent Volume Claim (PVC)

使用 Kubernetes PVC 宣告儲存空間,將儲存空間命名為 influxdb,容量大小 2GB:

1
2
3
4
5
6
7
8
9
10
11
12
13
# pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: influxdb
name: influxdb
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi

套用設定:

1
kubectl apply -f pvc.yaml

建立 Deployment

直接使用 Docker Hub 上的 influxdb,版本為 1.7.7

1
kubectl run influx-deployment --image=influxdb:1.7.7

套用 Secret

套用先前設定的 influxdb-creds Secret,輸入以下指令進入修改模式:

1
kubectl edit deployment influxdb

spec.template.spec.containers 位置加入 kubernetes secret:

1
2
3
4
5
6
7
8
spec:
template:
spec:
containers:
- name: influxdb
envFrom:
- secretRef:
name: influxdb-creds

套用 Persistent Volume Claim (PVC)

套用先前設定的 influxdb PVC,輸入以下指令進入修改模式:

1
kubectl edit deployment influxdb

spec.template.spec.volumes 位置加入 Persistent Volume Claim (PVC):

1
2
3
4
5
6
7
spec:
template:
spec:
volumes:
- name: var-lib-influxdb
persistentVolumeClaim:
claimName: influxdb

並且在 spec.template.spec.containers.volumeMounts 位置加入 Persistent Volume (PV):

1
2
3
4
5
6
7
spec:
template:
spec:
containers:
volumeMounts:
- mountPath: /var/lib/influxdb
name: var-lib-influxdb

最後整份文件的結果會長得如下:

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
apiVersion: apps/v1
kind: Deployment
metadata:
annotations:
deployment.kubernetes.io/revision: "4"
creationTimestamp: 2019-07-02T15:59:33Z
generation: 4
labels:
run: influxdb
name: influxdb
namespace: default
resourceVersion: "15136"
selfLink: /apis/apps/v1/namespaces/default/deployments/influxdb
uid: 62295880-9ce2-11e9-8f7f-42010a8000e8
spec:
progressDeadlineSeconds: 600
replicas: 1
revisionHistoryLimit: 2
selector:
matchLabels:
run: influxdb
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
creationTimestamp: null
labels:
run: influxdb
spec:
containers:
- envFrom:
- secretRef:
name: influxdb-creds
image: docker.io/influxdb:1.7.7
imagePullPolicy: IfNotPresent
name: influxdb
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/lib/influxdb
name: var-lib-influxdb
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext: {}
terminationGracePeriodSeconds: 30
volumes:
- name: var-lib-influxdb
persistentVolumeClaim:
claimName: influxdb
status:
...

Expose Service

這裡使用 LoadBalancer 來 expose 我們的服務,需要有雲端平台的支援:

1
kubectl expose deployment influx-deployment --name=influx-service --type=LoadBalancer --port=8086 --target-port=8086

EXTERNAL-IP 顯示為 pending,則再過一陣再重輸入一次。

1
2
3
$ kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
influxdb LoadBalancer 10.11.255.248 34.68.51.223 8086:31365/TCP 69m

看到 IP address 就代表完成了,接下來就測試看看是否能連進 server。

測試

從是否能從外部連進來:

1
2
3
4
5
6
7
$ influx --version
InfluxDB shell version: 1.7.7

$ influx -host '34.68.51.223' -port '8086' -username 'root' -password 'root'
Connected to http://34.68.51.223:8086 version 1.7.7
InfluxDB shell version: 1.7.7
>

Reference

物件導向程式設計 object-oriented-programming (OOP)

物件導向程式設計 object-oriented-programming (OOP) 是一種程式語言模型 (programming language model),程式需要圍繞著物件而不是圍繞著 function 和邏輯。

Object-oriented programming (OOP) is a programming language model in which programs are organized around data, or objects, rather than functions and logic. An object can be defined as a data field that has unique attributes and behavior.

當你看到 class 跟 object 可以把它們看成是相同的東西,因為英文通常會寫成一類物件 (a class of objects)

物件裡面可以包含兩個東西:

  • 唯一的屬性 unique attributes
  • 操作屬性的行為 behavior that manipulate it

物件導向裡面,行為又可以稱作為方法 method

定義

物件導向有四個基本的原則,分別是:

封裝 Encapsulation

封裝是一個物件導向的概念,它將屬性 (data) 和操作屬性的行為 (function) 綁在一起,同時避免外部的干擾和誤用。data hiding 能夠把不需要透露給外部的資料隱藏起來,讓外部無法存取。

Encapsulation is an Object Oriented Programming concept that binds together the data and functions that manipulate the data, and that keeps both safe from outside interference and misuse.

抽象 Abstraction

抽象是一種設計 programming 的技術,需要將介面 (interface)實作 (implementation) 的分離。簡單來說就是設計 API 的介面。在 C++ 需要把設計好的介面放在 public 底下。

Abstraction is a programming (and design) technique that relies on the separation of interface and implementation.

繼承 Inheritance

在定義物件時,子類 (subclass) 可以繼承一個或多個物件 classes。

Inheritance is the concept that when a class of objects is defined, any subclass that is defined can inherit the definitions of one or more general classes

多型 Polymorphism

在物件被繼承的時候,允許變數、函數或物件具有多於一種形式。相關的概念有 dynamic binding,在 C++ 裡面需要用到 virtual

Polymorphism is the characteristic of being able to assign a different meaning or usage to something in different contexts - specitically, to allow an entity such as a variable, a function or, an object to have more than one form.

比較 Encapsulation vs Abstraction

看完定義以後,會不會覺得封裝 (Encapsulation) 跟抽象 (Abstraction) 的定義有點類似:

  • 封裝強調的是綑綁 data 的機制,和使用這些 data 的 function

Data encapsulation is a mechanism of bundling the data, and the functions that use them

  • 抽象強調的是暴露介面和隱藏實作細節的機制

Data abstraction is a mechanism of exposing only the interfaces and hiding the implementation details from the user.

Reference

C++ Friend Class and Function

Friend

將另一個 class 設成 firend,可以讓該 class 存取自己 private data,參考底下的範例:

A 將 B 設成 firend,則物件 B 裡 private、protected、public function 都可以存取該物件 A 裡的 private、protected、public 的資料。

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
#include <iostream> 
class A {
private:
int a;
public:
A() { a=0; }
friend class B; // Friend Class
};

class B {
private:
int b;
public:
void showA(A& x) {
// In B, you can access private member of A by adding friend
std::cout << "A::a = " << x.a;
}
};

int main() {
A a;
B b;
b.showA(a);
return 0;
}

上面的程式碼代表著 A 的 private data 可以給 B 使用。

A 將某個 function 設成 firend,則該 function 都可以存取該物件 A 裡的 private、protected、public 的資料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
class A {
int a;
public:
A() {a = 0;}
friend void showA(A&); // global friend function
};

void showA(A& x) {
// In this function, you can access private member of A by adding friend
std::cout << "A::a=" << x.a;
}

int main() {
A a;
showA(a);
return 0;
}

上面的程式碼代表著 A 的 private data 可以給 showA function 使用。

friend function 可以放在 class 的任何地方,不受關鍵字 private、protected、public 的限制。

Reference