2023年10月30日 星期一

Dockerfile編寫準則及優化

基礎準則

  1. 儘量挑選官方鏡像
  2. 選擇合適的基礎鏡像
    • 能用小的就不要用大的
    • 能用輕量級的就不要用重量級的
    • 儘量選擇用runtime的
  3. 利用多階段構建
  4. 優化指令順序: 變動越小的放在越前面

選擇合適的基礎映像

選擇正確的基礎映像是優化的第一步。對於ASP.NET Core應用,推薦使用官方的ASP.NET Core運行時映像。這些映像已經為性能和安全性進行了優化,且體積相對較小。

下面以asp.net core 6.0的Web專案建立Dockerfile並分析其內容:

FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

多階段構建

多階段構建可以幫助您在一個Dockerfile中分別進行應用的構建和運行,這樣可以大大減少最終映像的大小。

# 構建階段 
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build 
WORKDIR /src 
COPY ["MyApp/MyApp.csproj", "MyApp/"] 
RUN dotnet restore "MyApp/MyApp.csproj"
COPY . . 
WORKDIR "/src/MyApp" 
RUN dotnet build "MyApp.csproj" -c Release -o /app/build
# 發布階段
FROM build AS publish
RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish
# 運行階段
FROM base AS final
WORKDIR /app 
COPY --from=publish /app/publish . 
ENTRYPOINT ["dotnet", "MyApp.dll"]

構建Image過程分析

使用Dockerfile建立Image過程中,每個可執行的指令基本上都會產生一個新的層(快照)。在上面的Dockerfile 中,指令及其對應的層如下:

在 Docker 中,每個可執行的指令基本上都會產生一個新的層(快照)。在您問的 Dockerfile 範例中,指令及其對應的層如下:

  1. FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base - 第一個基礎映像層
  2. WORKDIR /app - 變更工作目錄到/app
  3. EXPOSE 80 - 暴露Port
  4. EXPOSE 443 - 暴露Port
  5. FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build - 第二個基礎映像層(用於建置)
  6. WORKDIR /src - 變更工作目錄
  7. COPY ["MyApp/MyApp.csproj", "MyApp/"] - 複製專案檔案到image的/src的MyApp目錄下(不存在則建立)
  8. RUN dotnet restore "MyApp/MyApp.csproj" - Restore專案及相關套件
  9. COPY . . - 複製本地目錄所有檔案到到image的/src中
  10. WORKDIR "/src/MyApp" - 再次變更工作目錄
  11. RUN dotnet build "MyApp.csproj" -c Release -o /app/build - 建置應用程序
  12. FROM build AS publish - 從建置階段開始新的階段
  13. RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish - 發布應用程序
  14. FROM base AS final - 從基礎映像開始最終階段
  15. WORKDIR /app - 變更工作目錄到/app(可能不會產生新層,因為這個工作目錄已經在基礎映像階段被設定)
  16. COPY --from=publish /app/publish . - 從發布階段複製檔案到/app
  17. ENTRYPOINT ["dotnet", "MyApp.dll"] - 設置容器入口點

案例-變更Promgram.cs重新建𦋠Image的緩存使用

  1. FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base - 使用快取,因為基礎映像沒有改變。
  2. WORKDIR /app - 使用快取,這一步和 Program.cs 的改變無關。
  3. EXPOSE 80 - 使用快取,無相關改變。
  4. EXPOSE 443 - 使用快取,無相關改變。
  5. FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build - 使用快取,同樣因為基礎映像沒有改變。
  6. WORKDIR /src - 使用快取,這一步和 Program.cs 的改變無關。
  7. COPY ["MyApp/MyApp.csproj", "MyApp/"] - 使用快取,假設 .csproj 檔案沒有改變。
  8. RUN dotnet restore "MyApp/YourApp.csproj" - 使用快取,因為沒有 .csproj 檔案的改變。
  9. COPY . . - 重新建置新層,因為您修改了 Program.cs,而這一步涉及複製整個專案目錄。
  10. WORKDIR "/src/MyApp" - 重新建置新層,因為它跟在一個已經改變的層後面。
  11. RUN dotnet build "MyApp.csproj" -c Release -o /app/build - 重新建置新層,需要重新編譯因為源代碼已經改變。
  12. FROM build AS publish - 重新建置新層,這是一個新階段,而前面的步驟已經更改。
  13. RUN dotnet publish "MyApp.csproj" -c Release -o /app/publish - 重新建置新層,必須發布修改後的代碼。
  14. FROM base AS final - 使用快取,回到了最初的基礎映像,且此時階段與 Program.cs 的修改無關。
  15. WORKDIR /app - 使用快取,如果這個指令沒有在前面的基礎映像層中被改變。
  16. COPY --from=publish /app/publish . - 重新建置新層,需要從已更新的發布階段複製文件。
  17. ENTRYPOINT ["dotnet", "MyApp.dll"] - 重新建置新層,雖然這個指令本身未改變,但它跟隨在已更改的層後面。

總之,合理規劃 Dockerfile 中命令的順序對於優化建置過程、減少建置時間、利用 Docker 快取、降低映像大小,以及保持容易維護性都非常重要。

2023年10月29日 星期日

建立第一個K8S的Deployment Hello Kubernetes

設定Context

電腦中有2個K8S的環境, 建立Deployment時要注意切換到正確的Context:

  • Docker-Desktop
  • Azure K8S

檢查有效的context

kubectl config get-contexts

輸出: *表示目前使用的Context

CURRENT   NAME                    CLUSTER                 AUTHINFO                                                   NAMESPACE
*         aks-XXX-5ecjl5yhtk5i4   aks-XXX-5ecjl5yhtk5i4   clusterUser_AAA-BBBI-DEV-Service_aks-ccc-5ecjl5yhtk5i4   XXX-dev
          docker-desktop          docker-desktop          docker-desktop

切換到Docker-Desktop
kubectl config use-context <docker-desktop-context-name>

kubectl config use-context docker-desktop

檢查
kubectl config current-context

建立deployment要使用的namespace

kubectl create namespace hello-k8s

kubectl get ns

切換目前Context的namespace

kubectl config set-context --current --namespace=hello-k8s

建立nginx-deployment.yaml

建立cluster到docker-desktop k8

kubectl apply -f nginx-deployment.yaml

yaml內容

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: hello-k8s
spec:
  selector:
    matchLabels:
      app: nginx
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:alpine3.18
        ports:
        - containerPort: 80
        resources:
          limits:
            cpu: "1"
            memory: "512Mi"
          requests:
            cpu: "0.5"
            memory: "256Mi"

建立Services: nginx-service.yaml

建立NodePort service來Expose deployment建立cluster到docker-desktop k8

kubectl apply -f nginx-service.yaml

yaml內容

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  namespace: hello-k8s
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
    - protocol: TCP
      port: 80
      nodePort: 30080

驗證是否成功部署

curl http://localhost:30080

Check deployment,pods, service

kubectl get deployments -n hello-k8s
kubectl get pods -n hello-k8s
kubectl get service -n hello-k8s

使用ClusterIP+Ingress

前面範例使用NodePort即可直接訪問http://localhost:30080.
也可改用ClusterIP+Ingress:

nginx-service.yaml

spec:
  type: ClusterIP
  selector:
    app: nginx
...
...

建立Ingress: nginx-ingress.yaml
因為沒有dns, 需修改C:\Windows\System32\drivers\etc\hosts

127.0.0.1 nginx.local
kubectl apply -f nginx-ingress.yaml

yaml內容

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-ingress
  namespace: hello-k8s
spec:
  rules:
  - host: nginx.local
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: nginx-service
            port:
              number: 80

科普Service的type: NodePort

ClusterIP
功能: ClusterIP 是 Kubernetes 預設的服務類型。這種服務類型在集群內分配一個獨特的 IP 地址。它用於集群內部的通信,這意味著它只能從 Kubernetes 集群內部訪問。
使用案例: 當你想要暴露一個僅在 Kubernetes 集群內部訪問的服務時使用 ClusterIP,例如內部 API 或後端服務。
可訪問性: ClusterIP 服務無法直接從 Kubernetes 集群外部訪問。如果需要外部訪問,必須使用其他方法(如 NodePort、LoadBalancer 或 Ingress)。
範例情景: 一個數據庫或僅供同一 Kubernetes 集群內其他 pod 使用的內部 API,而不對外部網路暴露。

NodePort
功能: NodePort 服務是 ClusterIP 的擴展。它在每個節點的 IP 上以一個固定端口(NodePort)暴露服務。NodePort 服務會路由到自動創建的 ClusterIP 服務。這意味著 NodePort 服務使用 <節點IP>:<節點端口> 從集群外部使內部服務可達。
使用案例: 當你想要向外部流量暴露一個服務,且不使用 LoadBalancer 或 Ingress 時使用 NodePort。它是一種簡單的方法,可以從集群外部訪問服務,經常用於開發和測試環境。
可訪問性: 通過 <節點IP>:<節點端口> 在外部訪問。NodePort 是從 --service-node-port-range 指定的範圍(默認:30000-32767)中分配的端口。
範例情景: 暴露一個網絡應用或 API 以供測試,可從 Kubernetes 集群外部訪問。

關鍵差異
可訪問性:
ClusterIP 只能在集群內訪問。
NodePort 可以從集群外部訪問。

端口分配:
ClusterIP 服務不會對外部集群環境暴露端口。
NodePort 服務在集群的所有節點(虛擬機器/實體機器)上開放特定端口。
使用案例:

ClusterIP 非常適合於服務無需對集群外部暴露的情況。
NodePort 適合於開發/測試環境,需要對外部訪問服務時。

服務鏈:
NodePort 服務可以訪問 ClusterIP 服務,因為它們是其上層。
在您使用 Docker Desktop 的場景中,使用 NodePort 服務在開發和測試中很方便,因為它允許您從本機機器瀏覽器或使用像 curl 這樣的工具訪問 Nginx 服務。如果您使用 ClusterIP,則該服務只能從 Kubernetes 集群內部訪問。

kubectl常用命令

維護資源

建立資源

kubectl create -f mysql-deployment.yaml -n java-web-dev

更新資源

kubectl apply -f mysql-deployment.yaml -n java-web-dev

刪除資源

kubectl delete -f mysql-deployment.yaml -n java-web-dev

使用apply或create?
安全性:kubectl create在資源已存在的情況下會失敗,從而避免覆蓋,而kubectl apply可以安全地用於更新現有資源。
方便性:對於需要經常更新配置的環境,如動態的開發環境或 CI/CD,使用kubectl apply更為方便。
最佳實務:在實際應用中,kubectl apply廣泛用於持續部署,因為它可以方便地處理資源的變化,而kubectl create則用於需要明確建立新資源的情況。
kubectl apply在實際應用中較為方便使用.

2023年10月28日 星期六

Blogger使用上的技巧整理

使用StackEdit搭配highlight.js配置Blogger顯示

使用StackEdit用markdown寫文章, 搭配highlight.js對code block做高亮顯示.

StackEdit

  1. 連結Blogger帳號
  2. 寫完後直接publish
  3. PageId可不填

如何取得PageId?
在blogger管理頁面, 打開這篇文章, url=https://www.blogger.com/blog/post/edit/1111111111/22222222, 第1個參數是blogId(1111111111)最後一個參數是PageId(22222222)

highlight.js

  1. 直接在網頁上的Demo挑選喜愛的顏色.
  2. 在Blogger的主題/自訂/編輯HTML<head></head>中加入即可:
<link crossorigin='anonymous' href='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/base16/bright.min.css' integrity='sha512-lo/WxEAEu2DlBAe1TUXoq4at3+QFUbqebT5UyFl009q4Rs2PZgbAyzQ/pxCFbbrXvgQeYtzcv4VoTglsy5JbCQ==' referrerpolicy='no-referrer' rel='stylesheet'/>
<script crossorigin='anonymous' integrity='sha512-D9gUyxqja7hBtkWpPWGt9wfbfaMGVt9gnyCvYa+jojwwPHLCzUm5i8rpk7vD7wNee9bA35eYIjobYPaQuKS1MQ==' referrerpolicy='no-referrer' src='https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'/>
<script>hljs.highlightAll();</script>

Google drive圖片引入文章的方法

這是取得共享的連結

https://drive.google.com/file/d/1-5X8zIR6jp84qZfCd4y_6B55axJfvTHZ/view?usp=drive_link

取出id並引入markdown的image語法

![TypeScript圖標](https://drive.google.com/uc?id=1-5X8zIR6jp84qZfCd4y_6B55axJfvTHZ =100x100)

實際輸出
TypeScript圖標

HTML語法

<img src="https://drive.google.com/uc?id=1-5X8zIR6jp84qZfCd4y_6B55axJfvTHZ" alt="image" width="50%" height="auto">
<img src="https://drive.google.com/uc?id=1-5X8zIR6jp84qZfCd4y_6B55axJfvTHZ" alt="image" width="120" height="auto">

實際輸出
image

預設的顯示寬度太窄, 要怎麼變寬?

在網路上找到這一篇Blogger 彈性版面,隨著視窗大小調整寬度, 我按照裡面的方式變更後, 整個版面就變的比較好看了.

2015年9月30日 星期三

Git使用學習系列 - 開分支

分支 branch 在GIT的使用可是很重要卻又很複雜, 藉由學習一些教學影片後, 將其整理一番.

2015年9月29日 星期二

Git使用學習系列-入門

在安裝並設定好TortoiseGit+Github後, 在網路找到一個不錯的入門學習教學影片, 就將教學中一些重點整理起來。

TortoiseGit與Github

今天心血來潮, 想到要用TortoiseGit+Github來管理自己學習的程式範例.