2019年6月11日 星期二

helm 的零碎小知識

記錄一下 helm 常使用的指令及一些小細節

安裝 helm 時,會先用 helm init 建立和 kubernetes cluster 的關係,也就是會 deploy 一個叫 tiller 的 deployment 到 kubernetes (預設在 namespace kube-system) 。

後續所有套件安裝的資訊以及將套件 deploy 到 kubernetes 都是透過 tiller 進行,因此 tiller 需要足夠的權限,而 tiller 的權限則是在 helm init 決定]。所以在 helm init 前,需要在 namespace kube-system 先建立好一個 service account,並且給予足夠的權限。然後在 helm init 時,用 --service-account 指定建立好的 service account。

helm 基本指令的使用都還蠻直覺的,不過在看指令前,先簡單了解一下 helm 的套件運作方式。

在 kubernetes 裡,部署、設定、管理之類的動作都是透過 yaml 進行,而一個軟體的部署可能會用到很多個 yaml 檔來設定,而且其中可能很多設定內容都是重複的。helm 做的事情就是把要讓使用者設定的項目提取出來,放在 values.yaml 裡,然後在安裝時,把 values.yaml 裡的設定內容轉化成真正的 yaml 檔案後,再部署到 kubernetes 裡。

所以用 helm 安裝套件時,大部分的情況都是會需要變更 values.yaml 的設定後再部署。也就是說大部分的安裝指令會類似如下:
helm install -f values.yaml -n pm --namespace monitoring stable/prometheus

會指定安裝後的名稱、要安裝到哪個 namespace、以及要讀取哪個 yaml 檔裡的設定。

但是一般來說,除非對要安裝的套件很熟了,否則不會知道 values.yaml 裡設定了什麼,所以要先透過 helm 指令,將套件抓下來後,取得裡面的 values.yaml 來修改。
helm fetch stable/prometheus

這樣就可以把 promethues 這個套件下載回來,下載的套件是一個 tarball ,解開來後,裡面就會有一個 values.yaml ,可以修改完後,用上述的指令進行安裝。

然而通常情況是,很多套件一開始使用時,values.yaml 設定的結果還需要調整。
有些小地方,可能直接用 kubectl 指令做一些調整就好,但是有些設定可能需要修改 values.yaml 再跑 upgrade 會方便一些。
helm upgrade -f values.yaml pm stable/prometheus

在刪除套件時,因為我大部分都是測試性質,不太會有 rollback 需求,所以通常都會完整的移除套件。有沒有加 --purge 參數的差別可以看這篇
helm delete pm --purge

另外較常用的指令大約就是
helm ls  #查看已安裝的套件
helm status pm # 查看已安裝套件名稱為 pm 的安裝資訊

最後是關於 private repo for helm
我是用 harbor 當 helm 的 repo,在啟動 harbor 時,加上 --with-chartmuseum 參數,harbor 就可以做為 helm 的 private repo。

要讓 helm 使用 private repo ,要先進行一些設定

* 新增 repo 的 url 到 helm,用 helm repo add 指令
* 安裝 helm plugin ,後續才能用 helm push 把套件推送到 repo 上。

關於這部分,這篇有蠻完整的介紹。
我個人覺得這部分的使用有點麻煩,因為我架設的 harbor 有使用 ssl ,因此要 helm repo add 或 helm push 時,都要指定一堆憑證路徑的參數 (--ca-file --cert-file --key-file)。
雖然這可以透過一些方式來簡化,但要自動化處理時,要把這些加入流程又要注意 security 會有點繁瑣。

2019年6月8日 星期六

CKA 小記

距離當初考 CKA 三個月了,簡單記錄一下考試的經驗。

題目共 24 題,其中 6~9 分的大約 4 題,其他 20 題則是 1~4 分。

在分數比較多的 4 題裡,大致上是如下題型:

1. 提供 ca 等憑證的路徑,建立起 master node ,也就是官網裡的 tls bootstrapping 的部分

2. secrets 的設定使用的情境

3. cluster 內的 DNS 查詢,也就是會先要求建立一個 pod 和 service ,然後用 nslookup 分別作正反查

4. 用 etcdctl 對 etcd 做備份

剩下的 20 題裡,大約有 2 題是要求找出 cluster 運作出問題的地方 (一題是針對 worker node ,另一題是針對 master node)。剩下的大部分都是比較基本的 kubectl 操作,從最簡單的 "找出 cpu 使用最高的 pod",到比較複雜一點的 "deployment 的 rollout history and rollback",不會有太刁鑽的題目。

雖然考試會用到 5~6 個 cluster ,但前 18 題左右都是在第一個 cluster 裡面考基本的題型,然後後面 3 個 cluster 則是建立或除錯 cluster,最後可能有一個是要設定 storage 相關的題目。

也就是說,即使放棄掉比較偏系統維運方面的題目,大致上也還有 80 分上下是可以透過 minikube 之類的環境就可以輕易做練習來準備的範圍。

所以如果單純以準備考試來說,基本操作的熟練度要夠,以及針對已知題型的練習也足夠,那要低空飛過只求及格是不會太難的。當然臨場的反應、細心還有緊張程度都會有些影響,這就只能靠個人去控制了。

如果去網路上查 CKA 如何準備,十篇大概會有七八篇會推薦 kubernetes the hard way ,主要是針對 tls bootstrapping 那一題。

不過我個人是覺得,如果是新手,為了準備這題,所花費準備的時間可能是其他所有題目的好幾倍,而且可能會相當挫折。把 kubernetes the hard way 從頭理解一遍當然是不錯,但是就準備考試本身來說,反而可能會是個不利的因素。除非已經是有一定熟悉度,目標往滿分邁進,否則我會建議掌握基本分和基本題型會輕鬆許多。

如果希望事先了解一下考試的介面,可以參考 arush 提供的測試環境
雖然和實際考試的有所差異,但可以作為參考和練習。

另外當初為了準備考試,有用 asciinema 錄製一些操作記錄,有興趣也可以參考。


就和所有的認證一樣,考過不代表高手,高手也不是一定就能考過。準備是必要的 (對大多數人而言),而考過只是代表一個起步而已。起步早晚不是重點,能一步步走下去才是考驗。



2019年6月6日 星期四

Prometheus-Operator 安裝設定

用 helm 安裝的 prometheus-operator 預設就有 mixin 的相關設定及 grafana dashboard ,會比用 helm 安裝的 prometheus 方便許多 (要自己安裝 grafana 及相關設定,如果要安裝 mixin 還要設定 prometheus rules 及 grafana dashboard 等)

用 helm 安裝的話,好像沒什麼好寫的,呃
helm install stable/prometheus-operator --name prometheus --namespace monitoring

搞定了!
先把 promthues 和 grafana 的 service type 改為 Loadbalancer ,來看看安裝結果。

咦?首先從 prometheus 的 UI -> Status -> Targets 看到 etcd 的 endpoints 狀態是 down
cluster 正常的情況下, etcd 的狀態不可能是 down 的,所以這應該是 prometheus 設定問題。

查了一下資料,這是因為 etcd 預設是需要憑證才能 access (如果是用 kubeadm 安裝的話)。就好像用 etcdctl 的話,要指定 ca crt key 等憑證的參數才能 access etcd 一樣。

從查到的資料來看,文章裡的解決方法,已經不適合目前安裝的版本 (prometheus-operator-5.7.0)。

目前的版本已經考慮到這個情況,在 values.html 已經提供可參考的設定方式。
helm fetch stable/prometheus-operator
tar zxvf prometheus-operator-5.7.0.tgz

然後修改 prometheus-operator/values.yaml 及相關設定,流程大致如下:

1. 尋找 etcd 相關的設定,可以看到有提供相關的設定,在 serviceMonitor 的後面調整為
schema: https
caFile: "/etc/prometheus/secrets/etcd-certs/ca.crt"
certFile: "/etc/prometheus/secrets/etcd-certs/healthcheck-client.crt"
keyFile: "/etc/prometheus/secrets/etcd-certs/healthcheck-client.key"

2. 尋找 secrets 的設定,調整為
secrets:
   - etcd-certs

3. 先還不要更新 helm package ,上面第二個設定的意思是,要把能夠 access etcd  的幾個憑證檔放到 etcd-certs 這個 secrets (這個 secrets 不存在,是要自行建立的)
kubectl -n monitoring create secret generic etcd-certs --from-file=/etc/kubernetes/pki/etcd/healthcheck-client.crt --from-file=/etc/kubernetes/pki/etcd/healthcheck-client.key --from-file=/etc/kubernetes/pki/etcd/ca.crt
   注意,這是要在 master node 上執行的,因為如果是用 kubeadm 安裝的話,這幾個憑證在 master node 才有。如果 etcd 是自行安裝,或是透過其他方式如 kubespray 安裝的話,就要自行調整產生 secrets 的方法了。

4. 可以更新 helm package 了
helm upgrade -f prometheus-operator/values.yaml prometheus stable/prometheus-operator

到這邊算是修改完成了,從 prometheus UI 上已經可以看到 etcd dashboard 裡有資料了。

遇到問題的話,可以檢查幾個地方

1. 檢查 servicemonitors 裡 etcd 相關的設定, servicemonitors 是 operator 建立的 CRD
kubectl -n monitoring edit servicemonitors prometheus-prometheus-oper-kube-etcd
   看裡面的設定是否有包含 values.yaml 裡有關憑證的設定

2. 檢查 pods 路徑下是否有確實把指定的 secrets 掛載起來
#kubectl -n monitoring exec -it prometheus-prometheus-prometheus-oper-prometheus-0
$ ls /etc/prometheus/secrets/etcd-certs/


2019年5月15日 星期三

將 windows server 加入 kubernetes cluster 成為 worker node

k8s 官方文件

ms 官方文件

kubernetes 原本環境的調整

  • CNI 限定 flannel (其實有三種,只是裡面最常見的是 flannel,官網範例也是用 flannel)
    flannel 的設定要照官網上的建議修改 net-conf.json 及 cni-conf.json 的內容
  • 修改 DaemonSet 的 yaml ,包含 kube-proxy、kube-flannel-ds-amd64、metallb 等
    spec.template.spec.nodeSelector 加上 beta.kubernetes.io/os=linux

    既有的 DaemonSet / Deployment / StatefulSet 等等,全都要做類似的修改。
    之後新增的也都要加上 nodeSelector。


windows server 2019 上的設定 (後面的操作全都是透過 PowerShell )

  • 安裝 docker

Install-Module -Name DockerMsftProvider -Repository PSGallery -Force
Install-Package -Name Docker -ProviderName DockerMsftProvider                            Restart-Computer -Force
Start-Service docker

  • 取得 kubernetes 的 certificate file

mkdir c:\k
cd c:\k
scp root@[master ip]:/root/.kube/config c:\k\config

  • 取得 windows node 上需要的 binary file,例如 kubelet、kube-proxy 等
    這邊兩個官網給的說明都有點不太清楚,不知道為什麼不直接提供連結
    找 node binaries 裡的 kubernetes-node-windows-amd64.tar.gz

wget -O c:\k\node.tar.gz https://dl.k8s.io/v1.13.6/kubernetes-node-windows-amd64.tar.gz
tar zxvf node.tar.gz
cd kubernetes\node\bin\
cp kube-proxy.exe c:\k\
cp kubelet.exe c:\k\
cp kubectl.exe c:\k\
      為什麼不一次 cp 三個檔案? 因為 powershell 裡就是沒辦法一次 cp 多個檔案,可能要加參數,不過我懶得查....


  • 這邊有一段是 kubernetes 官網上沒有但是 MS 官網有的環境變數設定

$env:Path += ";C:\k"
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\k",[EnvironmentVariableTarget]::Machine)
$env:KUBECONFIG="C:\k\config"                                                                     
[Environment]::SetEnvironmentVariable("KUBECONFIG", "C:\k\config", [EnvironmentVariableTarget]::User)

  • 下載 windows 上的 flannel binary file

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
wget https://raw.githubusercontent.com/Microsoft/SDN/master/Kubernetes/flannel/start.ps1 -o c:\k\start.ps1

  • 用 start.ps1 script 啟動 flannel 並帶起 kubelet 及 kube-proxy
  • 如果 flannel 設定都是預設值的話,指令大約如下
.\start.ps1 -ManagementIP -NetworkMode overlay -ClusterCIDR 10.244.0.0/16 -ServiceCIDR 10.96.0.0/12 -KubeDnsServiceIP 10.96.0.10 -LogDir c:\k

剩下是測試驗證了,照 MS 官網的文章,和一般 kubernetes deploy 的方式一樣
不過測試用的 image 感覺下載很慢,可以先用 docker pull 下來後,再跑測試會比較方便一些。
 

2019年5月8日 星期三

zero downtime in kubernetes

這是 原始文章,這篇不是翻譯,算是個人解讀後的摘要,如果有誤請指正~
原文有更詳細的解說,尤其第四篇文章內有關於 PDB 機制圖文並茂的解說。
要注意的是,這篇講的 zero downtime,指的是因某些需求而必須要主動停機的情況,如何在主動停機時不會中斷服務。


* 定義所遇到的挑戰為何
   假設目前有兩台 worker node,上面已經在跑一些服務。如果想要升級 worker node 的 kernel 時應該如何進行?
   一個方式是先裝新的 worker node join cluster,然後把原本 worker node 關機。           

   這會產生哪些考量?
  a.  假如這個 pods 需要在結束前做一些清理動作,但是關機程序沒有時間讓 pods 完成這些動作。
  b.  假如所有原本的 nodes  一起關機,會產生一段時間的服務中斷。

  首先要如何避免讓 pods reschedule 到原本的 pods 上?
  用 kubectl drain 設定原本的 node,確認所有的 pods 都移走後再關機。

  不過用 drain 的方式還是有兩個因素會造成服務中斷:
  a.  pods 裡的 applicaion 要能處理 TERM signal,因為 kubectl drain 是送 TERM signal 來 evicted pods。
       kubernetes 會在送出 TERM signal 後的一定時間內,去強制結束掉還在執行的 container。
  b.  如果 pods 在新的 node 起來前,舊的 node 上的 pods 就先都被終止,會產生服務中斷。

  Kubernetes 提供了三種機制來處理主動中斷 (例如 drain) 的情況
  a. Graceful termination
  b. Lifecycle hooks
  c. PodDisruptionBudgets


* Gracefully shutting down Pods

  kubectl drain 讓 kubelet 去 delete pod 的流程
  a. kubectl drain 發出通知給指定的 node,讓該 node 的 kubelet 開始準備做 delete pods 的動作
  b. kubelet 會執行 pod 定義裡的 preStop 設定
  c. preStop 執行完後,kubelet 會送出 TERM signal 給 pod
  d. kubelet 送出 TERM signal 後,會等待一個時間 (grace period, 預設是 30 秒) 後強制刪除 pod
 
 也就是在 kubelet 強制刪除 pod 前,有兩個時間點可以讓 pod 做清理的動作。
 例如說,可以設定應用程式收到 TERM signal 時停止接受新的工作,並且在完成所有已收到的工作後自行停止。

  如果沒辦法修改應用程式去處理 TERM signal ,可以設定 pod 的 preStop 去終止應用程式。

  不過在後面這種情況下,應用程式雖然可以透過 preStop 停止,但是 service 那邊在可能還不知道的情況下(見下節說明),還會繼續把 request 往 pods 送過去,使用者就會看到錯誤或服務被拒絕的結果。

* Delaying Shutdown to Wait for Pod Deletion Propagation

  回頭來看,當送出 delete pods 時,整個 kubernetes cluster 會進行哪些處理?
  a. kubelet 會開始進行 delete pod 的流程,如同前一節描述的
  b. 所有 node 上 kube-proxy 會移除該 pod ip 相關的 iptables rules
  c. endpoints controller 會移除和該 pod 相關的 endpoint,也就是會從 Service 中移除該 pod
 
  這就是為什麼透過 preStop 終止 container 後,流量 (request) 還可能會繼續往 pods 送的原因。

  一種應對方式是,確保 kube-proxy 和 endpoints controller 都處理完後,再終止 container。
  不過這方式是非常困難的,因為 node 可能有網路延遲,或者 node 數量非常非常多。

  實際上沒有辦法做到 100% 服務不中斷,只能盡可能做到 99%。
  方法是在 preStop 用 sleep 指令延後終止 container 的動作,在 "Kubernetes in Action" 一書中建議 sleep 的時間是 5~10 秒。

  也就說,如果在這 5~10 秒內,endpoints controller 可以移除相關的 endpoint,就可以在 container 終止前停止把流量送過去,也就不會有中斷服務的情況。

  此時 Deployment 會因為要保持 replicas 設定的數量在新的 node 建立新的 pods。         
  接下來的問題是,假如同時 drain 所有原本的 node,可能產生的問題是,在新的 pods 產生前,所有原本的 pods 都終止了,而造成了服務中斷。

  如果不要同時,而是逐步 drain node 會遇到什麼問題?
  第一台終止的 pod ,可能被安排到另一台原本也要停機的 node 上。也就是該 Deployment 的兩個 replicas 都在同一台準備要停機的 node 上。 當輪到這台 node 要 drain 時,就會造成這個 deployment 的 pods 全都準備終止的情況。下一節會來處理這個處理這個問題。


* Avoiding Outages in your Kubernetes Cluster using PodDisruptionBudgets

    使用 PDB (PodDistruptionBudgets) 這個機制來處理上節最後面提到的問題。
    PDB 是用來設定對於終止 pod 行為的容忍機制。

    如果在 PDB 的 yaml 設定 minAvailable: 1,表示至少要有一個 pod 存活。這可以防止所有 pods 全部一起被 delete 。在上述情境中,會等到新的 node 裡至少有一個 pod 起來後,才會讓原本最後一個 node 裡的 pod 被刪除。


綜合以上幾個設定方式,可以達到 zero downtime 的目標。

2019年5月7日 星期二

用 ansible 架 kubernetes cluster

用 ansible 架 kubernetes 不是新鮮事,成熟的專案有 kubespray,而 openshift 也是用 ansible 安裝。

會接觸 ansible 並用來裝 kubernetes cluster ,在於我想要整合用 ansible 去建立 vm (base on kvm),vm 是使用預先建立好的 CentOS 7 的 image。

因為是 vm 環境, kubespray 就顯得太龐大了,因此決定自己來寫一個 kvm 版本的安裝流程,從 virsh 建立 vm 開始,到設定 vm 網路環境,然後建立 kubernetes cluster。

這過程花了大約兩個禮拜的時間完成,只能說 ansible 真是一個很容易陷入的工具,因為太容易上手並取得成就感了。兩個禮拜裡從基本的 playbook 到建立 roles ,不斷地重構原本的程式並加入新的功能,以及嘗試讓設定更有彈性。雖然對 ansible 掌握度還不夠,但已經足夠完成目標想做到的 (基本) 程度了。

透過 inventory 裡的設定,可以建立符合需求數量的 vm ,安裝 haproxy + kubernetes cluster + harbar ,以及 kuberntes 一些基本的環境 (metallb + nginx-ingress + helm + metrics-server),harbar 也整合到 kuberentes cluster 。

對我來說,ansible 最難掌握的就是變數的傳遞,花了最多時間去查資料及適應 ansible 的邏輯。 在不同 playbook 間要傳遞變數,我目前知道的方式是 add_host,透過把變數放到虛構的 host 裡,因為 hostvars 相當於一個 global 的變數。另外就是不同方式設定變數的 priority,沒有弄清楚前也吃了一點苦頭。

因為 vm image 是可以自訂的,所以可以先把一些必要的東西先埋進去,這讓 ansible 在處理上也簡單不少,這算是用 vm 來處理的一個優勢。對於已經有 kvm 環境的人來說,只要硬體資源足夠,預先件好需要的 image 後,用 ansible 搭一個上述的 kuberentes 環境,大約只需要 3~5 分鐘左右的時間。

ansible 之旅暫時先告一段落,先回頭看 kubernetes 相關的工具與管理,等到對於 kubernetes 維護管理層面更完整後,再來修改 ansible,目標是可以在 kvm 環境裡快速搭建一個完整且實用的 kuberentes 。


kubernetes cluster 安裝

從 kubernetes 1.14 之後,越來越容易用 kubeadm 安裝 cluster 了,基本的環境準備好之後,熟練的話可能十分鐘就裝好一個 kuberentes cluster (master * 3 + worker * 3)

為了簡化設定環境,可以把六台的 selinux 和 firewall 都先關掉。
安裝環境假設準備如下:

  1. 六台機器, OS 為 CentOS 7
    假設其 ip 為 192.168.1.11 ~ 192.168.1.16
  2. 選定一台當第一台 master,設定 master 可以直接以 ssh key 登入其他五台
    假設第一台 master 為 192.168.1.11
    另外兩台 master 分別是 192.168.1.12 及 192.168.1.13
  3. 準備一台機器設定好 haproxy ,做為三台 master node 的前端 proxy
    假設 haproxy 的 ip 為 192.168.1.10
  4. 六台機器都準備以下文件 (檔案內容放在文後)
    * /etc/yum.repos.d/kubernetes.repo
    第一台 master 準備以下文件 (檔案內容放在文後)
    * /tmp/kube-config.yaml
  5. 六台先進行如下設定
    # yum update && yum install -y yum-utils
    # yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    # yum install -y docker-ce kubelet kubeadm kubectl --disableexcludes=kubernetes
    # systemctl enable --now docker
    # systemctl enable --now kubelet
    # swapoff -a
    * modprobe br_netfilter

到 192.168.1.11 (第一台 master) 開始安裝
# kubeadm init --config=/tmp/kube-config.yaml --experimental-upload-certs

一行指令就安裝完成了。

安裝完成後會提供一些訊息,其中有兩行分別是提供給其他 master 或 worker 加入 cluster,訊息內容類似如下:

kubeadm join 192.168.1.10:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866 --experimental-control-plane --certificate-key f8902e114ef118304e561c3ecd4d0b543adc226b7a07f675f56564185ffe0c07
將這個指令複製下來到 192.168.1.12 及 192.168.1.13 上面執行,就可以將這兩台以 master 身份加入到 cluster 
kubeadm join 192.168.1.10:6443 --token 9vr73a.a8uxyaju799qwdjv --discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866
將這個指令複製下來到另外三台上面執行,就可以將這三台以 worker 身份加入到 cluster

要注意的是,兩個指令裡的 token 及 key 是有使用期限的,時間到了 token 和 key 就會被移除。所以如果一段時間後要將新的機器加
入 cluster ,就需要透過 kubeadm 指令重新產生 token 和 key 來使用。

不過在將其他台機器加入 cluster 之前,還有一個動作要先做,要指定及安裝 kubernetes cluster 內部使用的網路模式 (CNI),這邊以 weave 為範例,在第一台 master 上執行以下指令

# kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"


最後附上需要的設定檔

/etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kube*

* /tmp/kube-config.yaml
apiVersion: kubeadm.k8s.io/v1beta1
kind: ClusterConfiguration
kubernetesVersion: stable
controlPlaneEndpoint: "192.168.1.10:6443"
networking:
  podSubnet: 10.244.0.0/16

Dockerfile Best Practice

來源是從這份 簡報

這份簡報主要提出改進的目標如下:
  • build time
  • image size
  • maintainability
  • security
  • consistency/repeatability
簡報開頭先建議使用 buildkit 功能
先摘錄一下官網路個人認為比較重要的部分:
  • docker build 新選項
    --secret 可以避免在 Dockerfile 中直接寫入密碼
    --ssh 可以讓 build 出來的 image 直接有 ssh daemon
  • 使用 buildkit
    從環境變數打開 : DOCKER_BUILDKIT=1 docker build .
    修改設定檔: /etc/docker/daemon.json : { "features": { "buildkit": true } }

然後簡報開始針對上面的目標提出改善方式
  1. 減少 build time
    a. 善用 cache ,把比較容易變動的指令放到後面
    b. 指定 copy 的範圍可以減少 cache 使用,例如 
        COPY . /app  改成 COPY target/app.jar /app
    c. 避免使用到過期的套件,例如將 apt-get update 和 install 合併為一行
        apt-get update && apt-get -y install ssh vim
  2. 減少 image size
    a. 移除非必要的套件, apt-get -y install --no-install-recommends
    b. 移除安裝套件的 cache , rm -fr /var/lib/apt/lists/*
  3. 維護性
    a. 盡量使用官方出的 image
    b. 盡量指定 image 的 tag ,例如 FROM openjdk:8
    c. 從官網尋找符合需求下 size 最小的 image tag
  4. 重複使用性
    a. 盡量維持 build 環境的一致性
    b. 透過 multi-stage build 來減少 build iamge 時的相依性

簡報提出 multi-stage 的適用情境
  • separate build from runtime envrionment ( 降低 image size )
  • 降低 image 的變動
  • Build /dev /test /lint /... specific envrionments
  • 將相依性變成非線性化 (concurrency)
  • Platform-specific stages

使用 multi-stage 情境的範例
  • 用 --target 來指定要 build 的 stage

    Dockerfile:
    From image_or_stage AS stage_name

    Build Command: 
    docker build --target stage_name
  • use global Arguments

    Dockerfile:
    ARG flavor=alpine
    FROM openjdk:8-jre-$flavor AS release

    Build Command:
    docker build --target release --build-arg flavor=jessie .
  • buildkit 可以在 build 時排除非必要的 stage,並且可以多個 stage 同時進行 build

Dockerfile 新的語法 (experimental) ,以下語法都是在 docker v18.09+ w/ BuildKit 使用
  • context mounts
    COPY . /app => RUN --mount=target=. mvn -e -B package -DoutputDirectory=/
  • cache dependencies
    RUN --mount=target=. --mount=type=cache,target=/root/.m2 && mvn package -DoutputDirectory=/
  • serects

    錯誤的方法:
    (1) ENV AWs-ACCESS_KEY_ID  (直接寫在 Dockerfile)
    (2) ARG AWs-ACCESS_KEY_ID  (會保存在 docker history)

    建議的方式:
    Dockerfile:
    RUN --mount=type=secret,id=aws,target=/root/.aws/credentials,required

    Build Command:
    docker build --secret id=aws,src=~/.aws/credentials .
  • 使用 private git repos

    錯誤的方法:
    COPY ./keys/private.pem /root/.ssh/private.pem

    建議的方式:
    Dockerfile:
    RUN --mount=type=ssh,required git clone git@xxx:/repo /work

    Build Command:
    docker build --ssh=default .

2018年6月11日 星期一

k8s 初探

架設環境是在 aws ec2 ubuntu 16.04 環境下,大致上照這篇進行
使用的範例則是這本書第一章的 hello world

依該篇文章進行時,初步遇到以下問題需排除:

1. 如果自行安裝 docker-ce 可能會遇到一些問題,使用 ubuntu 內建的 docker.io 會比較順利

2. kubeadm init 時,會使用預設的 cni 網路環境,可能會造成預設相關的 pod 會無法啟動 (Pending),可以依照這篇修改 10-kubeadm.conf ,將 KUBELET_NETWORK_ARGS 給 mark 掉,再執行 kubeadm init

3. 在 deploy pod network 時,該篇文章使用 flannel 網路環境,但是預設環境下產生的 flannel pod 會 Pending。或許可以照這篇做排除,不過還沒嘗試。我是依另一篇文章設定使用 weave 網路環境
sudo kubectl apply --filename https://git.io/weave-kube-1.6

4. 在 aws ec2 上使用的是免費方案等級的 instance,因此在產生範例的 pod 時,會因為 k8s 判斷硬體資源不足而讓 pod 變成 Pending。可以用以下指令讓 k8s 放行:
kubectl taint nodes --all node-role.kubernetes.io/master-


主要是遇到以上問題,就這樣也花了一天時間,才把書上的範例跑起來。
如果對 k8s 裡的元件定義有先弄清楚的話,例如 node rc pod service ,整個流程會比較清楚。我是邊做邊看,有些東西試了一陣子才發現是自己沒看清楚元件的定義或用途。

其他就是熟悉一下 k8s 指令,其實主要也就 kubectl 這個指令。
最常用的幾個 kubectl command 就是
kubectl get [nodes | pods | services ]
kubectl describe [resouce name]
kubectl create -f [filename]
kubectl delete [resource name]

禮拜四要去參加一場 k8s 研討會,希望在那之前能把 k8s 摸熟一點  QQ

2018年5月30日 星期三

karma - webpack unit test environment in laravel

因為對 webpack 不熟,因此拿別人做好的設定檔來用
https://github.com/alhoqbani/laravel-mix-test

npm 相關套件版本變化很快,因此沒有沿用這專案的 package.json,大致進行了如下設定後即可以 karma 來進行 unit test

1. 將該專案的 resources/assets/tests 目錄下的所有檔案複製一份到自己專案下

2. 依該專案設定的 karma 設定檔內的需求,安裝相關的 node package, 大致如下
    karma mocha sourcemap
    sinon chai sinon-chai karma-sinon-mocha
    karma-mocha karma-spec-reporter karma-coverage
    karma-webpack karma-chrome-launcher

3. 更改 karma.conf.js 設定

plugins: [
    'karma-webpack',    'karma-coverage',    'karma-sourcemap-loader',    'karma-mocha',    'karma-spec-reporter',    'karma-chrome-launcher',    require('karma-mocha'),    require('karma-sinon-chai')
],

4. 更改 package.json
"test": "cross-env NODE_ENV=test karma start --config= resources/assets/tests/unit/karma.conf.js --single-run",

然後跑 npm test 即可以看到用 karma 以 resources/assets/tests/unit/specs/ 裡面的檔案設定來跑 unit test 的結果,預設是測試 resouces/assets/js/componments/Example.vue

該專案有改過 Example.vue 的內容,所以如果測試失敗可以複製該專案的 Example.vue 來測試。


設定過程中遇到的問題,主要就是要安裝相關的 node package,以及 karma.conf.js 的設定,其中曾經遇到 karma 一直吐出 "webpack not registered" 之類的訊息,花了一點時間才知道是 karma.conf.js 設定黨的關係。

2018年1月28日 星期日

wordpress 網站放到 aws 上 , 若採用 aws load balance 及 http ssl 授權金鑰所產生的問題

這應該算是有點 tricky 的問題?

這次把公司網站放到 aws 上之後, 在設定 load balance 時發現 aws 可以直接設定 https ssl 所授權的網域, 這樣就不需要維護每三個月更新一次的 letsencrypt . 於是很愉快的使用下去了.

在設定時, 有注意到這種方式完全不用修改 apache 設定就可以直接 work , 但沒深究其原因.
直到發現公司官網在以 https 瀏覽時, 會因為出現有混合 http/https 資料而產生 ssl not secure 的情況, 造成網頁顯示問題時, 開始追究原因後才發現和這個有關.

比較簡單的說法是, aws load balance 的做法像是加一層 revserse proxy 擋在 web 前面, 所以對 web server 來說其實都是 http request , 而不是 https request, 所以 apache 不需要任何 ssl 相關的設定.

但 wordpress 會根據 request 來決定網頁內連結裡應該是 http or https , 所以丟出的連結都是 http 而造成了 http/https mixed content 的情況.
以上解讀若有錯誤請指正 QQ

解決方案 就如同這個網頁提供的, 因為 aws load balance 轉發 request 時會加入一個 header "X-Forwareded-Proto", 可以在 apache 裡設定當這個 header 存在時, 加入一個環境變數, 而 wordpress 會參考到這個環境變數而認為這是一個 https 的 request 產生 https 的連結.

對我來說這問題實在奇妙但真實, 再次驗證在設定系統時不能疏忽或小看任何變動的地方, 否則就是會踩坑給你看.

2017年12月7日 星期四

laravel + vue ( webpack , laravel-mix)

在 laravel 中,  如果要透過 webpack 來使用 vue ,  php 要傳資料給 vue 的方式和傳統方式上不太一樣.

可以參考這篇文章, 我一開始到處亂查資料都沒找對方向, 一直到看了這篇後, 才解決了問題.

vue 的檔案裡不像 blade 檔案會經過 laravel 處理, 所以不能直接放 php 的變數在 vue 檔案裡.  要將變數放在 blade 裡的 vue component 裡, 然後 vue 以 props 的方式取得變數內容.

假設 php 有個 route 要給 vue 以 ajax 使用

blade file
<sysconfigtab ajaxurl="{{ URL::to('/some_url') }}"></sysconfigtab>

vue file

export default{
  data(){
    return {}
  },
  props: ['ajaxurl'],
  mounted(){
    axios.get( this.ajaxurl ).then(function (response) {
    }
  },
}

解決這個問題後, 後續開始學 vue 才算慢慢有了進展.

2017年10月27日 星期五

A.I. 人工智慧 與 黎安娜之歌

A.I.這部電影刻劃了人類情感裡最深沉的部分,也帶出一些讓人思考的方向。

真的能透過 A.I. 學會什麼是愛嗎?

 機器人小男孩一開始的表現符合大部份人對小孩的期望,乖巧 貼心 等等正面的言語與行為,比較像是模擬人類對 "愛" 的想像來表達 "愛" 這件事。反而直到小男孩表達出吃醋的行為後,才讓人感覺到他是愛他母親的。

愛的本質是什麼?

這是有點弔詭的。如果小男孩自始至終都保持一樣正面的態度,會讓他像機器人。透過負面的行為反而讓他更像活生生的人。或者可以說,"愛" 的本質是不帶價值觀的,是在成長的過程中,學習把 "愛" 以正向的方式表達出來。

不過即使小男生開始比較像人類了,還是無法判斷說,這真的是愛嗎? 還是說這只是 A.I. 程式判斷而做出的行為?

如何判斷這是程式行為還是愛?

電影主題選擇用母子親情,而不是男女愛情,或許是在於親情更帶有一種天然的特性,尤其是在母親與孩子之間。小男孩的追尋之旅,某種程度上來說,也像是人類在成長中追尋與學習 "愛"。即使如此,我們還是無法判斷這是程式行為還是愛。

或者,說不定人類某天會需要承認,"愛" 也只是寫在染色體中的程式而已。

插曲,誰是獨一無二的?

在不認識時,我們靠著外表來認識其他人。隨著關係越來越緊密,開始互相馴服彼此而產生獨特性。對父母而言,自己的小孩永遠是獨一無二的,但在陌生人眼中,就像電影裡大量製造的機器人小男孩一樣。而在人的成長過程中,也是從認為自己是獨一無二的,慢慢發現自己越來越多平凡的地方。

如果機器人真的學會 "愛",那和人類的區別還剩下什麼?

這可能是許多科幻小說或電影喜歡探討的議題,不過我想這不是這部電影想要討論的。
在我看來,這部電影想探討 "愛" 這件事。
人類為什麼會有愛? 什麼是愛? 愛的力量可以到什麼程度?

愛是一種最深沉的渴望與記憶,可以深刻到即使過了上千年後依然期待的記憶。


剛翻到這篇文章,看著文章引用的電影對白,回想電影情節,竟然又有一種炫然欲泣的感受。這篇文章寫得很好,比我高明太多了,不過沒看過電影前先不要看比較好。

"如果我跟小木偶一樣變成小男孩,就能回家嗎?"  
看完電影再回頭看到這對白,這是多麼令人心碎的一句話。



黎安娜之歌

A.I. 給我的深沉感受,讓我聯想到這個短篇科幻小說。

這個短篇出自於王溢嘉先生翻譯的科幻小說選裡面的最後一篇。
這本科幻小說選是我最喜歡的科幻小說,印象中小時候家裡就有,從高中起開始已經看了幾十遍了。每個短篇都很有深度,不管是科幻性或故事性都很夠,譯者的文筆也相當好,每篇的風格也都相當不同,整本讀起來相當豐富有趣,也有很多感性的情節或讓人思考的地方。

然而年輕時,卻不太喜歡最後這一篇 "黎安娜之歌",覺得又長又缺乏劇情起伏,也沒什麼科幻的成份,看起來很悶。

一直到念研究所後,慢慢開始看出這篇有意思的地方。
工作後慢慢又多看了幾次,越來越感受到這篇深沉動人的地方。

人與人之間即使關係再親密, 畢竟還是無法心靈相通,因而還是會有所隔閡。
一種類似水蛭且缺乏生命意識的生物,卻能讓信奉者完全屏除隔閡感受到彼此的愛,因而形成一種看似慢性自殺行為的特殊宗教。

人與人之間的隔閡,把愛分隔開了,也讓人疏離了。

這篇深沉的地方在於,人在於面對這種先天限制的隔閡上,是多麼無能為力。你無法讓你愛的人知道你有多愛對方,也無法確實感受到愛你的人究竟有多愛你。人們只能透過外在的言語行為,來表達自己的愛,這是多麼間接又難以區隔真假的方式啊。