docker-and-kubernetes
Docker and Kubernetes
為何使用 Docker
什麼是 Docker
Docker Client and Docker Server
使用 Docker Client
什麼是 Container
Namespacing and Control Groups 並不屬於 mac os 或 windows 的,而是屬於 linux 的
基本指令
Override Default Command
Command 能不能使用取決於該 image 的 system 有沒有該程式能夠執行
查看 containers
docker ps 列出正在執行的 container
docker ps --all 列出包含曾經創建過的 container
Container 的生命週期
docker run = docker create + docker start -a
-a 表示顯示 logs
可以重啟之前的 Container,但不能替換原本的 Command
移除停止的 Containers
查看 Container 的 logs
停止 Container
SIGTERM = terminate signal
stop 會通知程式停止,讓程式能夠根據自己寫的程式碼在自己停止之前執行備份之類的事,而 kill 則是直接強制停止程式,若執行 stop 後 10 秒程式仍未自己停止則會執行 kill 強制停止。
在 Container 執行多個 Command
在執行中的 Container 執行 Command
-it = -i -t
i 表示連接 STDIN t 表示格式化輸出文字
在 Container 呼叫命令提示字元
離開為 CTRL+D 或輸入 exit
啟動 Container 時開啟命令提示字元
創建 image
創建 Dockerfile
docker build .
產生 image ID
docker run ${imageID}
啟動 container
每次執行一個 instruction 會產生暫存的 image 並刪除上一次暫存的 image,然後每次執行下一個 instruction 會先用上次暫存的 image 啟動 container 再執行
Rebuild with Cache
因為 build 會根據是否有 cache 的關係,instruction 的順序會影響找不到之前的 cache 而重新 fetch 建立新的 image 和 container
Tagging an Image
傳統命名規範
技術上來說 tag 是指後面的 version,前面 dockerID/repo 比較像專案名稱
若沒指定 version 預設為:latest
啟動時若不指定版本則會拿最新或 latest 的
Docker Commit for Windows
用 Docker Commit 手動產生 image
可以在執行中的 docker 中產生 image
實務上不推薦使用,用 Dockerfile 就好,方便重啟
image ID 可以不用貼全部
Docker with Node.js
Port Mapping
只能在 runtime 指定,不能在 Dockerfile 指定
設定 Working Directory
最小化 Rebuild 成本
若沒修改 package.json 在安裝套件階段(RUN npm install)就應該繼續使用原本的 Cache 來產生 image
多個 container
若 Node 和 Redis 用同一個 Container 在以後擴展時會不方便
因此將 Redis 分出來有自己的 Container
Docker Compose
用 Docker CLI 設定網路那些太麻煩,指令太多,所以通常都用 Docker Compose 設定
為了避免每次啟動都要輸入一堆 Docker CLI,所以才用 Docker Compose,另外也方便一次啟動多個 Container 和自動設定他們之間的連線
version 為 docker-compose 的格式版本,build 表示使用 Dockerfile 建立 image,-在 yml 中表示陣列
Difference between service and container in docker compose
一個 service 可以由一個或多個 container 執行
Docker 會將 request 出去的 host 檢查是否為 Docker service name,若符合則 request 到該 service container,Redis 預設 port 為 6379
Docker Compose Command
第一次若直接使用 docker-compose run 也會自動 build
停止 docker-compose
-d 為在背景執行
-d=false: Detached mode: Run container in the background, print new container id
處理 container crash
模擬 crash
Status codes
除了 0 以外都是 error code
Restart Policies
unless-stopped 為總是重啟除非在終端機手動輸入停止
no 必須加雙引號或單引號告訴 yaml 為字串,因為 no 在 yaml 中為 boolean 的 false
docker-compose ps
類似 docker ps,但必須所在目錄要有 docker-compose.yml 檔案,會根據該檔案列出其中正在執行的 container
開發工作流程
Docker 的目的
用 CRA 建立 react 專案並新增 Dockfile.dev 用於 dev 環境,之後的 Dockfile 用於 prod 環境
指定 Dockfile
docker build -f ${fileName} .
將不必要的 node_modules 刪除避免 COPY,節省 build 的時間
Docker Volumes
將本地的 source code 改為 mapping reference 的方式,避免每次檔案更動都要手動 docker build 和 ducker run
pwd = present working directory
pwd 在 Windows 的 PowerShell 沒有該指定,可以用 git bash,但仍會有問題,所以改使用${PWD}
在 PowerShell 上,並在 package.json 的 scripts 上新增 WATCHPACK_POLLING=true
可以將 WATCHPACK_POLLING=true 移至 docker-compose.yml(推薦),不影響 local 的指令
CHOKIDAR_USEPOLLING=true 為舊版 webpack 變量,新版為 WATCHPACK_POLLING=true
https://github.com/facebook/create-react-app/issues/10253#issuecomment-1127340307
-v /app/node_modules 不加:path 代表不 mapping 到本地的 node_modules,因為本地沒有 node_modules
用 docker-compose 簡化指令,且不再因為 OS 和 CMD 的關係遇到建立 volumes 的問題($(pwd) / ${PWD}
那些)
因為 build: .會去找 Dockfille 而不是 Dockfile.dev
context 為 image 的目標位址,dockerfile 為指定的 Dockerfile 檔案
那 Dockfile 還需要 COPY . .嗎?
如果用 docker-compose 的確是不必要的,但還是會建議留著,因為有可能之後不再用 docker-compose 改用原本的 docker cli 但忘記補上 volume mounting,或留著當作提醒或參考
test 環境也能 hot reload
不推薦的方法,要記指令和 container ID
docker-compose 也能使用 ps 拿到 service name 再 exec,exec 預設會自動帶-it
推薦使用 docker-compose
目前遇到 Windows 無法自動 reload App.test.js 的問題,所以必須透過 docker-compose exec web 或 tests npm run test 的方式進去,每次 App.test.js 檔案有更動再按一次 Enter 更新,即使有加 WATCHPACK_POLLING=true 也沒用
docker attach 的是 npm process(primary process)而不是 start.js
也可以只建立 web service 就好,測試時也用 web 環境
建立 prod service
Multi-Step Docker Build Process
as 命名 phase/stage name,每個 FROM 開頭都是一個 phase/stage block,--from=phase 或 stage
nginx 預設 port 是 80,-d 為在背景執行
docker build .
docker run -d -p 8080:80 ${imageId}
RUN、CMD、ENTRYPOINT 的差別
為 container 命名
docker run --name my-name busybox
Travis CI
因為現在 Travis CI 要收費,可以改用 GitHub Actions
-t 為給 image 命名(docker build),--name 為給 container 命名(docker create/docker run)
新增.travis.yml
sudo: required 是為了能夠執行 docker build
script 執行指令若回傳 0 以外的 status code 代表有錯
npm run test -- --test 產生測試覆蓋率
為了能夠在 npm run test 後 exit,所以不能使用預設的 dev 環境,否則將永遠不會 exit 並回傳 status code,因此要指定為 CI 環境
-e 為設定環境變數
建立 AWS Elastic Beanstalk 並新增 docker-compose-dev.yml 和修改 docker-compose.yml
Elastic Beanstalk 有偵測流量變大自動幫我們 scale 擴展的優點
建立 AWS Elastic Beanstalk 也會自動建立 AWS S3
.travis.yml 新增 deploy config
region 去 Elastic Beanstalk 查看 URL
bucket_name 為 Elastic Beanstalk 的 S3 名稱
bucket_path 為 Elastic Beanstalk 的 S3 專案資料夾位置(第一次 deploy 才會建立)
on 指定 master 這個 branch 才觸發
GitHub Actions
目前不需要上傳至 Docker Hub,所以其實不需要 run: docker login 這行
name: Deploy Frontend
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
- run: docker build -t cygnetops/react-test -f Dockerfile.dev .
- run: docker run -e CI=true cygnetops/react-test npm test
- name: Generate deployment package
run: zip -r deploy.zip . -x '*.git*'
- name: Deploy to EB
uses: einaregilsson/beanstalk-deploy@v18
with:
aws_access_key: ${{ secrets.AWS_ACCESS_KEY }}
aws_secret_key: ${{ secrets.AWS_SECRET_KEY }}
application_name: docker-gh
environment_name: Dockergh-env
existing_bucket_name: elasticbeanstalk-us-east-1-923445559289
region: us-east-1
version_label: ${{ github.sha }}
deployment_package: deploy.zip
設定 AWS KEY
去 IAM 新增 user
設定 Travis CI 環境變數
.travis.yml 新增 AWS KEY
Dockerfile 補上 EXPOSE port
停止使用 Elastic Beanstalk
單個 Container 部屬問題
用 Fib 計算機專案模擬多個 Container 情景
設定 runtime 環境變數
runtime 環境變數在 build image 還不會設定,要啟動 container 時才會
Nginx Path Routing
新增 nginx default.conf
/etc/nginx/nginx.conf 會 include /etc/nginx/conf.d/*.conf
server 屬於關鍵字,所以要將 docker-compose 的 server 也改名
設定 nginx 監聽 port、proxy_pass 反向代理設定、rewrite path 移除/api
新增 nginx Dockerfile
docker-compose 新增 nginx
如果第一次 docker-compose up --build 啟動可能會有錯誤,某些 container 可能還在 install 所以連線失敗,重新 docker-compose up 就好
dev websocket 問題
prod CI
目的:減少每次部屬時在 EB 上重新 build image 的時間,將提前 build 好的 image 存到 Docker Hub,以後改用其他除了 EB 以外的服務時也方便直接下載使用
prod 的 client nginx 預設的 80 port 改為 3000
其實可以只需要一個 nginx 就好,但有可能你的 react files 不是使用 nginx 存放,而是其他簡單的 file system data store,
新增 client 的 nginx config
當有聲明監聽其他 port 時,會自動停用預設的 80 port
index 指定 root 的網站初始頁
try_files 為抓取資源的優先順序,如果請求是/aaa/bbb,那會先去 aaa 資料夾找有沒有 bbb 這個檔案,沒有就找 aaa/bbb 資料夾有沒有 index.html 或.htm 檔案,沒有就找/index.html
記得補上 try_files $uri $uri/ /index.html
location、root、alias 的差別
新增 client 的 Dockerfile,EXPOSE 不再是 80 而是 3000
先移除 test,因為在測試時我們的後端不會啟動,實務上會用 mockup 資料替代
Travis prod config
新增.travis.yml
docker run lovebuizel/react-test npm test -- --cover 改為 docker run -e CI=true lovebuizel/react-test npm test
sudo: required
services:
- docker
before_install:
- docker build -t lovebuizel/react-test -f ./client/Dockerfile.dev ./client
script:
- docker run lovebuizel/react-test npm test -- --cover
after_success:
-docker build -t lovebuizel/multi-client ./client
-docker build -t lovebuizel/multi-nginx ./nginx
-docker build -t lovebuizel/multi-server ./server
-docker build -t lovebuizel/multi-worker ./worker
其實也可以在.travis.yml 使用 docker-compose
透過 Docker CLI 登入並將 build 完的 image 上傳至 Docker Hub
sudo: required
services:
- docker
before_install:
- docker build -t lovebuizel/react-test -f ./client/Dockerfile.dev ./client
script:
- docker run lovebuizel/react-test npm test -- --cover
after_success: -docker build -t lovebuizel/multi-client ./client
-docker build -t lovebuizel/multi-nginx ./nginx
-docker build -t lovebuizel/multi-server ./server
-docker build -t lovebuizel/multi-worker ./worker
## Log in to the docker CLI
- echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_ID" --password-stdin
## Take those images and push them to docker hub
- docker push lovebuizel/multi-client
- docker push lovebuizel/multi-nginx
- docker push lovebuizel/multi-server
- docker push lovebuizel/multi-worker
設定 Travis 環境變數
使用 GitHub Actions 在 prod 環境
prod deploy in AWS
之前因為只有一個 Dockerfile,所以 EB 會自動幫我們 build image 和 run image
但這次有多個資料夾,每個資料夾裡面有各自的 Dockfile,EB 不知道該怎麼處理
Dockerrun.aws.json 告訴 EB 要去哪裡 pull image 和設定 port mapping 等相關資訊
docker-compose.yml 和 Dockerrun.aws.json 類似,主要差別為前者如何 build image,後者如何使用 image
EB 其實不知道如何處理 container,尤其是 multi-container,實際上他會委派 AWS 的另一個服務 Elastic Container Service(ECS)去處理,而 ECS 中的 task definition,用於告訴 ECS 怎麼 run single container,其格式又跟 Dockerrun.aws.json 幾乎一樣,所以可以當作文件參考
新增 Dockerrun.aws.json
name 名稱無所謂
hostname 就跟 docker-compose 的 services 一樣,用於 container 間彼此的連接,所以 server 的 hostname 必須為 api,但如果沒用到其實可以不用指定名稱,例如這裡的 worker 和 nginx
essential 表示如果這個 container 掛掉是否要將其他 container 也一起關掉,必須至少要有一個為 true,這裡選擇 nginx container,因為網站如果掛掉那其他服務也沒用了
portMappings 就跟 docker-compose 的 ports 一樣,映射本地與 container 的 port
Dockerrun.aws.json 不像 docker-compose 可以直接使用 services 連接 container,必須額外聲明,且是單向的,links 填入的 container 名稱是前面指定的 name
新版 EB 平台不再使用 Dockerrun.aws.json,而是 docker-compose.yml
prod 的 redis 和 postgres 建議使用其他服務,而不是自己建立 container
選擇其他服務的原因
以後遷移至 EB 以外的服務方便
自動備份很重要
預設 AWS 的服務不能彼此溝通,必須透過 Virtual Private Cloud(VPC)的 Security Group 設定
每個 region 都有預設的 VPC
建立 EB 同時也會建立一個預設的 Security Group 套用上去
Security Group 也可以設定 inbound、outbound 等規則
建立一個在相同 Security Group 內 AWS service 可以彼此溝通的規則並套用上去
建立 AWS RDS
建立 AWS ElastiCache Redis
建立 Security Group
套用 Security Group
設定 EB 環境變數
DB 使用者、DB 密碼、DB 名稱來自建立 RDS 時設定的
Redis Endpoint
RDS Endpoint
新增 AWS IAM user 並設定 Travis 環境變數
.travis.yml 新增 deploy script
分配 Container 記憶體容量
實際要分配多久記憶體沒有一定標準,要自己研究,這裡全部指定為 128MB
docker-compose.yml 的參數則為 mem_limit: 128m
version: "3"
services:
client:
image: "lovebuizel/multi-client-10-14"
mem_limit: 128m
hostname: client
server:
image: "lovebuizel/multi-server-10-14"
mem_limit: 128m
hostname: api
environment:
- REDIS_HOST=$REDIS_HOST
- REDIS_PORT=$REDIS_PORT
- PGUSER=$PGUSER
- PGHOST=$PGHOST
- PGDATABASE=$PGDATABASE
- PGPASSWORD=$PGPASSWORD
- PGPORT=$PGPORT
worker:
image: "lovebuizel/multi-worker-10-14"
mem_limit: 128m
hostname: worker
environment:
- REDIS_HOST=$REDIS_HOST
- REDIS_PORT=$REDIS_PORT
nginx:
image: "lovebuizel/multi-nginx-10-14"
mem_limit: 128m
hostname: nginx
ports:
- "80:80"
若有錯誤可以去 Log 查看
什麼是 Kubernetes 和為何使用
方便擴展或是你的應用程式需要 run 不同的 Container
EB 預設的擴展策略,我們無法控制彼此的 container group,且無法有效地將資源放在需要的地方
希望變成
Cluster 為 Master(負責控制每個 Node)+一個或多個 Nodes
Node 可以是 VM 或實體電腦,每個 Node 可以有一個或多個 Container,且 image 可以不同
minikube 只有在 dev 環境會用到,初學者在 prod 建議使用其他服務方便管理也比較安全
kubectl 負責管理 Node 和其裡面 Container,minikube 目的就只有在本地建立和執行 Kubernetes Cluster
安裝 Kubernetes 不像安裝 Docker 一樣會自動幫你安裝其他相關的軟體,所以你還要安裝其他的東西
macOS 安裝 Docker Desktop 內建的 Kubernetes
Windows 安裝 Docker Desktop 內建的 Kubernetes
macOS 安裝 Minikube
Windows 應該使用 Docker Desktop 內建的 Kubernetes 而不是 Minikube,因為 VM 驅動需要的 virtualization 會和 WSL2 衝突
macOS 安裝 Minikube
將之前的 multi-client 專案用 container 跑在本地的 Kubernetes Cluster 上
錯誤提醒
新開專案並建立兩個 config file
k8s 是 Kubernetes 的縮寫,意旨 k 和 s 之間有 8 個字母
client-pod.yaml
client-node-port.yaml
會將 config file 透過 kubectl 轉成 k8s cluster 裡的 object
object 有各種不同的 type(config file 裡的 kind)負責不同的工作,例如 pod 負責 run container、servicce 負責網路設定
不同 apiVersion 能使用的 object types 也不同
Minikube 會在你電腦建立 VM(node)
必須在 pod 裡面才能執行 container,pod 為一個 container group,可以有一個或多個 container,目的是將高度相關或必須一起執行才有意義的 container 放在一起,例如以下的 support containers
每個 Node 可以有多個 Pod
metadata 的 name 主要用於查看 log 時方便辨認的,labels 則為 selector 尋找指定的 service 用的,component: web 也可改叫 tier: frontend,只要 labels 和 selector 一樣就好
Service 又有四種 subtype,在 prod 不太會用 NodePort,因為不希望 port 為 30000~32767,除非遇到少數情況
Node 內建 kube-proxy 程式,是 Node 對外唯一的接口,負責決定 request 要傳到哪個 Service,NodePort 負責 expose container 的 port
port 為其他 pod 之間溝通用(目前可以忽略),targetPort 為 mapping 指定的 pod 與外界的 nodePort(port 只能在 3000032767 之間),若沒指定 nodePort 則會隨機指派 3000032767 之間的 port
套用 config file
查看所有 pod 的狀態,預設為 default namespace 的 pod,除非加--all-namespaces,另外查看 namespace 為 kebuctl get ns
READY 左邊為正在 runnung 的 pod 數量,右邊為總共 pod 需要的數量
查看所有 service 的狀態
PORT(S)的第一個為 NodePort Service 的 port,第二個為 nodePort,targetPort 單純不重要所以不顯示
minikube 建立的 VM 不能使用 localhost 訪問,要用 minikube ip 查詢的 ip,若使用 Docker Desktop 則可以直接使用 localhost
kube-apiserver 有 4 個程式,目的是負責確保 node 運作正常
config file 只會傳到 master,master 會自動分配哪些 node 要執行哪些 container,那些 node 的 docker 會去 docker hub 抓 image 存到自己的環境並建立 container
master 有 responsibility list 可以確認監控到的狀態,若刪掉 container 會自己重啟該 container
k8s 有兩種開發方法,Imperative 和 Declarative
Imperative 比較麻煩,一般開發和正式環境都不推薦
Declarative 只要將目標狀態寫在 config file 重新送給 master 就好
只要知道有些部落格或文件的教學是用 Imperative,建議改成 Declarative 使用
更新 pod 使用的 image
Master 透過 Name 和 Kind 知道要更新原本 Pod 的 image,而不是新建一個 Pod
kubectl describe 查看詳細狀態
object name 可以不指定
更改 port 後重新 apply 卻報錯
pod config 有些屬性是不能更新的,必須砍掉 pod 重建
Object Type 由 Pods 改用 Deployment
Deployment 會監測和自動更新 pod,所以適合 dev 和 prod 環境
Pod 執行一組 container(container 可以一個或多個),而 Deployment 執行一組相同的 pod(pod 可以一個或多個)
Pod 因為更新不方便,所以只適合一次性的開發用,但仍然不適合正式環境使用
透過 Deployment 裡的 Pod Template 建立及更新 Pod
建立 Deployment config file
replicas 代表 pod 數量,template 就跟先前的 Pod config 類似,Deployment 並不會直接建立 Pod,而是拿 config 叫 Master 建立,所以還需要 selector matchLabels 跟 Master 說要 handle 那些 Pod,template 的 pod labels 可以有多個,所以可能有 selector matchLabels 不需要 handle 全部 pod 的情況
kubectl delete 會根據 config file 裡的 kind 和 name 去刪除 pod,會需要可能 10 秒的時間,如同之前刪除 container 一樣
DESIRED 為需要的 pod 數量、CURRENT 為目前的 pod 數量、當 apply config file 更新時會將全部存在的 pod 設為 out of date,等到 pod 更新或重建才會改成 UP-TO-DATE,AVAILABLE 代表成功啟動 container 或已準備好的 pod 數量
新版改成 READY
-o wide 等於--output=wide,用來查看更多的資訊
Service 讓你不用管因為更新或新建 pod 而改變的 ip 是什麼,自動幫你 mapping
如何確保 k8s 使用的 image 是最新的
本地重新 build image 並 push 上 docker hub
要確保 k8s 使用的 image 是最新的是很難的事情,目前沒有好方法,只能選比較不差的方法(新版有 kubectl rollout restart 可以使用)
因為 config file 沒改動,apply 會被拒絕,第一個因為有可能刪錯 pod 所以最不推薦,這裡選第三個方法
新版可以用 kubectl rollout restart -f deployment.yml,會用直接新增 pod 的方式重啟 container,期間舊的繼續運行,待新增完後再刪掉舊的 pod,達到服務不中斷的效果
kubectl set
用 Minikube 的每次終端機要使用 docker-client 使令時都要將 docker-server 環境指定為 k8s 的 Node,而不是預設的 local docker for Mac/Windows
因為 eval $(minikube docker-env)是暫時的,每個終端機視窗都是獨立的
可以只記 minikube docker-env 就好,會有提示你 eval $(minikube docker-env)
將 multi-container 專案應用在 k8s 上
CTRL+C 不等於 docker-compose down,資料仍會保存
刪除 nginx 並新增 k8s 資料夾和 config file
什麼是 ClusterIP?和 NodePort 的區別差在 NodePort expose pod 到外網,而 ClusterIP expose pod 到內網,讓 cluster 內其他 pod 彼此可以溝通
新增 ClusterIP config file,屬性跟 NodePort 類似,但因為不用 expose 外網所以沒有 nodePort 屬性,port 和 targetPort 可以不一樣,但目前沒有必要
kubectl delete deployment
kubectl delete service
kubectl apply -f 資料夾名稱 可以 apply 該資料夾的全部 config file
新增 multi-server 的 deployment config file
還缺少 db 和 redis 的連線設定
新增 multi-server 的 ClusterIP config file
將多個 config file 合併成一個 config file,但不推薦,多個 config file 可以立馬知道有幾個 object,也能更快找到該 object 的 config
新增 multi-worker 的 Deployment config file
replicas 設 1 就好之後會擴展出去,這裡不用 expose container 的 port,因為沒有其他 object 會連進來 worker,所以也不需要 ClusterIP
還缺少 redis 連線設定
新增 redis 的 Deployment 和 ClusterIP config file
新增 postgres 的 Deployment 和 ClusterIP config file
replicas 為 1 是防止 DB 同時寫入造成資料不一致的衝突
Persistent Volume Claim(PVC)
防止 DB pod 掛掉重建後遺失資料,所以將資料存在 Volume(Persistent Volume,PV),透過 PVC 去設定 config
Volume 在 Container 和 Kubernetes 為不一樣的東西
Volume 在 k8s 中為 Pod 層級,如果 pod 內 container 掛掉重啟,volume 資料還會存在,但如果 pod 掛掉的話資料仍會遺失
所以我們需要的是 Persistent Volume Claim(PVC)和 Persistent Volume(PV),而不是 Volume
Volume 和 Persistent Volume(PV)的差別,Volume 只有 container 掛掉資料不會遺失,而 PV 不管是 container 還是 pod 掛掉資料都不會遺失
Persistent Volume Claim(PVC)和 Persistent Volume(PV)的關係
PVC 就像是一個廣告牌,讓 Pod config 知道能有什麼儲存資源能用,若 k8s 當下有提前建好的資源則稱為靜態供應 PV,需要再建立該資源則稱為動態供應 PV
比喻
換成 PVC
新增 PVC config file
accessModes 的種類
PV 存在哪裡
預設 PV 位置為 local,但如果 k8s 在其他雲端平台上,則該預設 PV 位置可能為該平台的其他 Service,不過一般來說用預設也沒差,除非有想特別指定的位置
可以通過設定 PVC config file 的 storageClassName 指定 PV 的位置
查看 PV 位置
Pod template 選擇創建時要使用哪個 PVC 作為 volume,並指定 container 要使用哪個 volume
mountPath 和 subPatj 為選擇該 volume 要存在哪裡,這裡作為 DB back up 用
kubectl get pv 或 pvc,STATUS Bound 代表正在使用
設定環境變數
紅色 host 為該 service 的 metadata name,黃色為普通的變數,密碼因為安全原因不能寫在 config file 裡,要另外設定
沒有"http://",只是要表示 host 為 url
Secrets object
因為安全關係我們不用 config file 的方式去建立 Secrets,而是用 imperative 指令的方式去建,在 dev 和 prod 環境都一樣
secret 的種類大多是 generic,其他可能會用到的有 docker-registry、tls 等等
<secret_name>類似於 object 的 metadata name
--from-literal 表示要儲存的密碼在指令的後面,而不是來自其他檔案
因為設置了 postgres 的密碼而不再使用預設的,所以 postgres deployment config 要額外 override default password
postgres deployment config 中 image 為 postgres 時若環境變數有 POSTGRES_PASSWORD(舊版為 PGPASSWORD),postgres 會自動將該變數當成預設密碼
valueFrom name 為前面的<secret_name>
env 的 port 號要加單引號或雙引號改成字串,不然會報錯
NodePort 比較適合 dev,所以 prod 改用 Ingress
LoadBalancer 是舊的方法,現在建議改用 Ingress
LoadBalancer 只能導入一組特定的 pod,另外 k8s 也會在背後聯繫 cloud provider 需要建立一個 cluster 外部他們自己設定的 load balancer,並自動設定將流量導入 cluster 裡的 load balancer service
這裡使用的是 ingress-nginx,而不是 kubernetes-ingress
不同環境安裝 ingress 的方式可能不同
Ingress 和 Deployment 類似,都是負責將 current state 轉成 desired state
Ingress 和 ingress-nginx 有一點點不同
Ingress-Nginx 在 GC 上其實也會建 Load Balancer,Ingress config 會經由 kubectl 建立一個 Deployment,裡面有 nginx-controller 建立的 nginx pod,並建立一個 Load Balancer 依附在上,另外也會預設建立一個額外的 deployment(裡面有 default-backend pod),用來檢測健康狀態,但在現實中會用 express api server 取代,讓要檢查健康狀態的 request 跑到 multi-server
為何不用自己客製的 nginx 而是 ingress-nginx
ingress-nginx 有很多額外好用的功能,例如 Sticky Sessions,他會將訪問不經過 ClusterIP(ClustrtIP 仍存在)直接連到 multi-client pod,且相同用戶的 request 都會連到相同的 server,達到不因為 server 不同而無法辨識 session 的功能
ingress-nginx 補充資料
https://www.joyfulbikeshedding.com/blog/2018-03-26-studying-the-kubernetes-ingress-system.html
安裝 ingress-nginx
https://kubernetes.github.io/ingress-nginx/deploy/#quick-start
新增 Ingress config file
預設 minikube ip 或 docker desktop localhost 的預設 port 為 80 或 443
apiVersion: networking.k8s.io/v1
## UPDATE API
kind: Ingress
metadata:
name: ingress-service
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
# ADD ANNOTATION
nginx.ingress.kubernetes.io/rewrite-target: /$1
# UPDATE ANNOTATION
spec:
rules:
- http:
paths:
- path: /?(.*)
# UPDATE PATH
pathType: Prefix
# ADD PATHTYPE
backend:
service:
# UPDATE SERVICE FIELDS
name: client-cluster-ip-service
port:
number: 3000
- path: /api/?(.*)
# UPDATE PATH
pathType: Prefix
# ADD PATHTYPE
backend:
service:
# UPDATE SERVICE FIELDS
name: server-cluster-ip-service
port:
number: 5000
Minikube Dashboard
在 Dashboard 上更新設定不會儲存
Docker Desktop 的 Kubernetes Dashboard 需要額外設定
錯誤修正,將 server-deployment 的 image stephengrider/multi-server 改為 cygnetops/multi-server-pgfix-5-11
將 k8s cluster 上到 prod 正式環境
為何選擇 Google Cloud 而不是 AWS
Google Kubernetes Engine(GKE)
要注意建立 Cluster 後就開始計算付費了
Travis config file
去 IAM 建立 Service Account 並下載 private key json
Ruby Version Fix
需要 Travis CLI 將 service account file 加密再上傳,但 Travis CLI 需要 ruby 的環境(只有 mac 內建),所以使用 Ruby image 的 Docker 來達成,就不用再 local 處理環境的問題
--no-rdoc --no-ri 為可選,代表不要安裝 document,讓安裝速度更快,ruby 2.4 版後就不再需要了
因為需要 build native extensions,所以不能使用 ruby 的 alpine 之類的版本,否則會 build 失敗
travis login github 改用 Personal Token
travis login
travis encrypt-file service-account.json(之前下載的 service account file) -r ${使用者名稱/專案名稱}
添加加密後提示的指令
加密完要上傳前記得刪除原始下載的 service account file
更多 Google Cloud CLI config,project 後面為專案 ID
之前有提到的錯誤修正
登入 Docker 並 build image 跑測試
Custom Deployment Providers
因為沒有 Travis 內建的 k8s provider,只能用 script 叫他執行自己另外寫的 deploy.sh
會遇到之前沒有強制更新 latest image 的問題,可以一樣用 kubectl rollout restart deployments/server-deployment 解決,另外覺得將 build image 和 push image 像之前一樣放在 after_success 比較好
之前的寫法
同之前不會抓最新 image 的問題,可以一樣用 kubectl rollout restart deployments/server-deployment 解決
有加 tag latest(應該也可不加,因為預設是 latest),tag 和 push 兩個版本同時將 latest 和該 GIT_SHA 更新,好讓之後用 kubectl apply -f k8s 時不用指定 GIT_SHA 也能抓到最新的版本
k8s pull policy
https://kubernetes.io/docs/concepts/containers/images/#updating-images
添加 Travis env
CLOUDSDK_CORE_DISABLE_PROMPTS=1 表示不要提示詢問
deploy.sh file
docker build -t cygnetops/multi-client-k8s:latest -t cygnetops/multi-client-k8s:$SHA -f ./client/Dockerfile ./client
docker build -t cygnetops/multi-server-k8s-pgfix:latest -t cygnetops/multi-server-k8s-pgfix:$SHA -f ./server/Dockerfile ./server
docker build -t cygnetops/multi-worker-k8s:latest -t cygnetops/multi-worker-k8s:$SHA -f ./worker/Dockerfile ./worker
docker push cygnetops/multi-client-k8s:latest
docker push cygnetops/multi-server-k8s-pgfix:latest
docker push cygnetops/multi-worker-k8s:latest
docker push cygnetops/multi-client-k8s:$SHA
docker push cygnetops/multi-server-k8s-pgfix:$SHA
docker push cygnetops/multi-worker-k8s:$SHA
kubectl apply -f k8s
kubectl set image deployments/server-deployment server=cygnetops/multi-server-k8s-pgfix:$SHA
kubectl set image deployments/client-deployment client=cygnetops/multi-client-k8s:$SHA
kubectl set image deployments/worker-deployment worker=cygnetops/multi-worker-k8s:$SHA
在 Google Console 設定 Google Cloud CLI
為何需要再設定一次 Google Cloud CLI
在 Google Console 設定 Secret
Helm(用來管理 k8s 中的套件)
安裝 Helm 會安裝 Helm CLI + Tiller Server(用來改變 cluster config)
Role Based Access Control(RBAC),用於規範誰有權限存取修改 cluster 的 object,防止隨便一個 pod 都有權限修改刪除 cluster
預設 local 環境不會啟用 RBAC,但 Google Cloud 預設會啟用
因為 Tiller 要修改 cluster,所以需要額外設定 Tiller 的權限,建立有著 ClusterRoleBinding 的 Service Accounts 並指派給 Tiller,要最保險也可以用 RoleBinding
Google Cloud 安裝 Helm 之前先設定 RBAC
用 Helm 安裝 ingress-nginx
Google Cloud 內建的 Load Balancer
成功部屬
使用 HTTPS
購買 domain 和設定
安裝 Cert Manager
https://cert-manager.io/docs/installation/helm/#steps
Cert Manager 就像 pod,而 Certificate 和 Issuer 為 object,Issuer 可以有多個
Issuer config file
https://cert-manager.io/docs/configuration/acme/#creating-a-basic-acme-issuer
Certificate config file
部屬後會看到 certificates
若看到這邊失敗沒關係
Events 才是重點
Ingress config for HTTPS
annotations 新增 cert manager 和強制跳轉使用 HTTPS
spec 新增 tls
rules 新增 host 並複製一份 for www host
清理 local 環境
local k8s 開發無法像 docker volume 一樣自動即時更新,要重新 rebuild image
Skaffold 有兩種模式可以達到自動即時更新的效果,第一種 rebuild 比較費時,第二種替換更新的檔案比較快(但要專案本來有 hot reload 的功能,例如這裡的 CRA dev 模式和 nodemon)
新增 Skaffold config file
push false 表示不要上傳 image 到 docker hub 或其他 docker repository
artifacts 為要管理的 image,sync 就是方法二,指定哪些檔案變動時要更新,除了這些檔案以外變動就會用方法一重新 rebuild