Kubernetes 提供了強大的容器協調能力,簡化了應用程式的佈署和管理。然而,在容器化應用中,保護敏感資訊(如密碼、API 金鑰等)至關重要。本文將探討 Kubernetes 的秘密管理機制,確保應用程式安全地存取敏感資料。首先,我們會以一個簡單的 Go 應用程式為例,說明如何構建容器映像,並使用 Podman 和 Kubernetes 進行佈署。接著,我們將詳細介紹 Kubernetes 的核心概念,如 Pod、Deployment 和 Service,以及如何使用 YAML 清單定義和管理這些資源。最後,我們將重點介紹 Kubernetes 中的 Secrets,說明如何建立、使用和管理 Secrets,以保護應用程式的敏感資訊。透過本文的實務,讀者將能夠理解如何在 Kubernetes 環境中安全地管理和應用 Secrets,提升應用程式的安全性。

Kubernetes秘密管理

在瞭解 Kubernetes 秘密管理之前,先來瞭解一下容器技術及其益處。

容器技術的優勢

容器技術類別似於輕量級的虛擬機器,但不需要完整的作業系統,這大幅減少了軟體開發生命週期及安全管理的負擔。多個應用程式可以分享底層的物理主機,而不需要虛擬化層帶來的額外負擔,從而獲得接近原生機器的效能。

容器技術提供以下優勢:

  • 標準化:符合 OCI (Open Container Initiative) 規範,簡化了容器的建立、分發及佈署。
  • 效率高:高效、可預測且不可變,只包含應用程式所需的函式庫及執行時環境。
  • 可攜性:無需考慮基礎架構或平台,應用程式可以在任何環境中執行。
  • 分離關注點:開發者和平台工程師不需要存取物理或虛擬主機即可開發、測試及佈署應用程式。
  • 自動化:支援 DevOps 情境,簡化基礎架構、應用程式及安全管理。

然而,容器技術也帶來了一些挑戰:

  • 架構轉型困難:許多 IT 組織在架構及管理上難以接受新的正規化。
  • 潛在隔閡:開發者和平台工程師之間的分離可能導致隔閡。
  • 微服務誤解:過度強調微服務可能導致複雜度增加而沒有顯著效能提升。

以下圖示展示了不同佈署方式在物理伺服器上的應用密度:

  graph TD;
    A[物理伺服器] --> B[裸機];
    A --> C[虛擬機器];
    A --> D[容器];
    B --> E[單一應用];
    C --> F[多個應用];
    D --> G[更多應用];

圖示解說:

這圖示展示了物理伺服器上不同佈署方式(裸機、虛擬機器及容器)的應用密度。容器技術允許在同一物理伺服器上執行更多應用程式,因為它們更輕量且效率更高。

Kubernetes 的起源與設計原則

Kubernetes 提供了快速原型設計、快速佈署、簡單的功能測試等額外優勢:

  • 小型碼函式庫:每個微服務都有獨立的碼函式庫,簡化了版本控制。
  • 降級模式:單一微服務失敗不影響其他微服務。
  • 粒度控制:精確分配計算資源並進行擴充套件。
  • 快速還原:只需重新排程相關微服務即可還原。

然而,容器執行時(如 Docker 或 Podman)本身並不提供任何故障還原能力。Kubernetes 作為一個高用性容器協調平台,能夠管理數百個微服務,確保業務連續性並提供高自動化程度。

Kubernetes 的核心概念

Kubernetes 提供以下核心概念:

  • 服務發現與負載平衡
  • 儲存協調
  • 自動佈署與回復
  • 自動資源封裝
  • 自我修復
  • 秘密與組態管理

Kubernetes 的設計原則

Kubernetes 如何實作無縫操作呢?以下是玄貓根據曾經在 Red Hat 專業服務組織擔任雲端架構師經驗的分析:

工作負載層面

每個應用程式所需的基礎架構要求都以宣告式方式定義,無需專家知識。YAML 清單描述 Pod、Service 和 Deployment 物件的期望狀態,由 Kubernetes 作為服務代理處理。這意味著應用程式團隊可以寫出環境和 Kubernetes 分佈無關的清單來佈署工作負載。

基礎架構層面

每個堆積疊元件都對應於 Kubernetes API 物件。如果沒有對應物件,供應商可以使用標準 Kubernetes API 物件 CustomResourceDefinition (CRD) 引入自定義物件。這保證了一致的標準,即使與第三方軟體、硬體或雲端供應商互動。

當 Kubernetes 收到有效物件定義的請求時,協調器會執行相關的 CRUD 操作。這意味著 Kubernetes 提供了本地自動化和協調。相同原則適用於所有以容器形式執行的 Kubernetes 元件,使其受益於自我修復、可靠性和可擴充套件性,同時與底層軟體、硬體或雲端供應商無關。

在瞭解了這些基本概念後,我們將探討如何設計一個適合生產環境且具有安全性保障的秘密管理解決方案。

從本地容器到 Kubernetes Pod:實踐

在現代雲端運算與微服務架構中,Kubernetes 作為一個強大的容器協調工具,已經成為許多企業的首選。Kubernetes 的設計原則強調可攜性、自動化以及對應用程式的高度抽象化,這些特性使得 Kubernetes 能夠有效地降低技術專家參與度,簡化應用程式的佈署、維護以及擴充套件。

Kubernetes 的起源與設計原則

Kubernetes 的設計初衷是為了提供一個高效的容器協調平台,能夠自動化管理和擴充套件容器化應用程式。這種方式不僅支援容器化應用程式的可攜性,還能夠降低技術專家在佈署和維護過程中的參與度。Kubernetes 的核心理念是透過定義和管理應用程式的期望狀態來實作自動化。這種方式讓開發者可以專注於應用程式的開發,而不必關心底層的基礎設施。

Pod 的 YAML 定義

在 Kubernetes 中,Pod 是最小的佈署單位,通常包含一個或多個容器。Pod 可以透過 YAML 檔案來定義其期望狀態。以下是一個簡單的 Pod 定義範例:

apiVersion: v1
kind: Pod
metadata:
  name: hello-app
spec:
  containers:
  - name: hello-world
    image: hello-path:0.1
    ports:
    - containerPort: 8080

內容解密:

  • apiVersion: 指定 API 的版本,這裡使用 v1
  • kind: 指定資源型別,這裡是 Pod
  • metadata: 包含 Pod 的後設資料,如名稱 hello-app
  • spec: 指定 Pod 的規格,包括容器的組態。
    • containers: 包含一個名為 hello-world 的容器。
      • name: 容器的名稱。
      • image: 指定使用的 Docker 畫素影像 hello-path:0.1
      • ports: 指定容器暴露的埠,這裡是 8080

這個 Pod 定義提供了 Kubernetes 需要的所有資訊來建立並執行一個簡單的容器化應用程式。我們不需要關心底層基礎設施的細節,這使得佈署變得非常簡單。

Kubernetes 架構

Kubernetes 的強大功能背後是多個元件協同工作所產生的。以下是 Kubernetes 架構中的主要元件:

  graph TD;
    A[API Server] --> B[Controller Manager];
    A --> C[Scheduler];
    A --> D[etcd];
    B --> E[Node Controller];
    B --> F[Replication Controller];
    C --> G[Node];
    D --> H[Persistent Storage];

    subgraph Control Plane
        A;
        B;
        C;
        D;
    end

    subgraph Node
        G;
        E;
        F;
        H;
    end

此圖示展示了 Kubernetes 的控制平面和節點之間的關係。控制平面負責管理叢集狀態和協調各個節點上的資源分配。

控制平面元件

  • kube-apiserver: 提供 Kubernetes API,處理所有對叢集狀態的操作。
  • etcd: 一個分散式鍵值儲存系統,用於儲存叢集狀態。
  • kube-scheduler: 負責將 Pod 排程到適當的節點上執行。
  • kube-controller-manager: 處理叢集中各種控制迴圈,確保系統達到期望狀態。

節點元件

  • kubelet: 在每個節點上執行,負責與 API Server 通訊並確保 Pod 滿足期望狀態。
  • kube-proxy: 提供網路代理功能,管理叢集內外網路流量。
  • container runtime: 處理容器的啟動、執行和停止。

建立第一個 Kubernetes Pod

要從本地容器轉移到 Kubernetes Pod,首先需要確保我們有以下準備:

  • 滿足章節開始時提到的技術需求
  • 存取本文 GitHub 儲存函式庫(https://github.com/PacktPublishing/Kubernetes-Secrets-Handbook)
  • 準備好範例程式碼(可在 ch01/example01 資料夾中找到)

以下是從本地 Docker 搭建一個簡單 Hello World 應用程式並在 Kubernetes 上執行該應用程式的一些步驟:

# 建立 Dockerfile
FROM alpine:latest
CMD ["echo", "Hello, World!"]

# 建立 Docker Image
docker build -t hello-world-image .

# 推播到 Docker Hub 或私有 Registry(如果需要)
docker tag hello-world-image <your-dockerhub-username>/hello-world-image:latest
docker push <your-dockerhub-username>/hello-world-image:latest

# 建立 Kubernetes Deployment YAML 檔案 deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hello-world
  template:
    metadata:
      labels:
        app: hello-world
    spec:
      containers:
      - name: hello-world-container
        image: <your-dockerhub-username>/hello-world-image:latest
        ports:
        - containerPort: 8080

# 在 Kubernetes 叢集中應用 YAML 檔案
kubectl apply -f deployment.yaml

# 驗證 Pod 是否正常執行
kubectl get pods --watch

# 檢視 Deployment 情況
kubectl get deployments

內容解密:

  1. Dockerfile: 建立一個簡單的 Docker Image。
  2. Docker Build & Push: 建立並推播 Docker Image 至遠端倉函式庫。
  3. Deployment YAML: 建立 Kubernetes Deployment YAML 檔案來描述我們希望佈署的 Pod 樣子。
  4. Apply to Kubernetes: 用 kubectl apply 命令將 YAML 檔案應用到 Kubernetes 叢集中。
  5. Verify: 用 kubectl 命令驗證 Pod 是否正常執行以及檢視 Deployment 情況。

Kubernetes 秘密管理指引

在探討 Kubernetes 秘密管理之前,讓我們先了解一下基本的軟體供應鏈範例。這個範例將涵蓋從構建應用程式二進位檔到在 Kubernetes 上佈署容器化應用程式的整個過程。

軟體供應鏈示例

構建應用程式二進位檔

這個範例使用一個簡單的 Go 應用程式,展示了一個 HTTP 服務和控制檯記錄功能。

構建容器映象

應用程式將使用 Golang 工具集容器映象進行構建。第二個小型容器映象將用於承載應用程式二進位檔。

使用 Podman 執行容器化應用程式

首先,我們將利用 Podman Desktop 的圖形介面來說明執行容器的簡單過程。

使用 Kubernetes 佈署容器化應用程式

接著,我們將使用 kubectl 命令列工具來展示如何處理第一個 YAML 清單來建立 Kubernetes Pod 物件。

這個範例不依賴於特定的 CPU 架構,因此你可以在不同的 CPU 目標上安全地執行相同的操作,而無需重寫程式碼或更改組態檔案。

Dockerfile 與容器映象構建

容器執行時環境如 Docker 或 Podman 用於構建應用程式和包含我們應用程式二進位檔的容器映象。這是透過一個名為 Dockerfile 的文字檔案來定義的,該檔案定義了構建我們容器映象所需的所有步驟:

FROM registry.access.redhat.com/ubi8/go-toolset@sha256:168ac23af41e6c5a6fc75490ea2ff9ffde59702c6ee15d8c005b3e3a3634fcc2 AS build
COPY ./hello/* .
RUN go mod init hello
RUN go mod tidy
RUN go build .
FROM registry.access.redhat.com/ubi8/ubi-micro@sha256:6a56010de933f172b195a1a575855d37b70a4968be8edb35157f6ca193969ad2
LABEL org.opencontainers.image.title "Hello from Path"
LABEL org.opencontainers.image.description "Kubernetes Secrets Handbook - Chapter 01 - Container Build Example"
COPY --from=build ./opt/app-root/src/hello .
EXPOSE 8080
ENTRYPOINT ["./hello"]

內容解密:

  • FROM registry.access.redhat.com/ubi8/go-toolset@sha256:168ac23af41e6c5a6fc75490ea2ff9ffde59702c6ee15d8c005b3e3a3634fcc2 AS build:這行命令從指定的倉函式庫中提取 golang 工具集映象,並命名為 build。
  • COPY ./hello/ .*:將本地目錄 ./hello 中的所有檔案複製到容器內。
  • RUN go mod init hello:初始化 Go 模組。
  • RUN go mod tidy:清理並更新 Go 模組依賴。
  • RUN go build .:構建 Go 應用程式。
  • FROM registry.access.redhat.com/ubi8/ubi-micro@sha256:6a56010de933f172b195a1a575855d37b70a4968be8edb35157f6ca193969ad2:從指定的倉函式庫中提取 ubi-micro 映象,這將是最終執行應用的基礎映象。
  • LABEL org.opencontainers.image.title “Hello from Path”:為映象新增標籤,這裡是標題。
  • LABEL org.opencontainers.image.description “Kubernetes Secrets Handbook - Chapter 01 - Container Build Example”:為映象新增描述標籤。
  • COPY –from=build ./opt/app-root/src/hello .:從 build 階段複製構建好的二進位檔到最終映象中。
  • EXPOSE 8080:暴露應用程式所需的埠。
  • ENTRYPOINT ["./hello"]:設定容器啟動時執行的命令。

在 Kubernetes 上執行應用

在本地透過 Docker 或 Podman 啟動容器相對簡單。然而,在 Kubernetes 上執行這個容器需要透過 YAML 清單宣告方式來處理。以下是一個交易性概覽:

此圖示展示了 Kubernetes Pod 物件建立過程中的交易性概覽。

  graph TD;
    A[提交 YAML 清單] --> B[kube-apiserver];
    B --> C[etcd 資料儲存];
    C --> D[Pod 物件建立];
    D --> E[元件狀態更新];
    E --> F[狀態收斂];

內容解密:

  • 提交 YAML 清單:使用 kubectl 工具將 YAML 清單提交給 kube-apiserver。
  • kube-apiserver:接收請求並將其寫入 etcd 資料儲存。
  • etcd 資料儲存:儲存Pod物件的期望狀態和當前狀態,生成稽核跟蹤。
  • Pod 物件建立:根據期望狀態建立 Pod 物件。
  • 元件狀態更新:Kubernetes 各元件根據期望狀態進行調整和更新。
  • 狀態收斂:Kubernetes 各元件不斷努力使當前狀態與期望狀態保持一致。

Kubernetes 的起源與設計原則

Kubernetes 的設計原則之一是透過 etcd 資料儲存持續更新 Pod 物件的記錄。期望狀態和每個元件的當前狀態都會被儲存下來,從而生成一種稽核跟蹤。這種設計使得在未能達到預期結果時更易於除錯。

一旦 Pod 物件在 etcd 中註冊,所有 Kubernetes 元件都會努力使系統向期望狀態收斂,即使可能遇到網路分割槽、節點故障等問題。這正是執行單機本地容器與在 Kubernetes 上協調容器之間的區別。

在不同環境中的佈署比較

執行本地 Docker 或 Podman 時,我們僅關注單個機器上的容器執行情況。然而,在 Kubernetes 上協調容器時,我們需要考慮網路、儲存、Secrets 和其他資源的管理。這需要一個更復雜但更強大的系統來處理這些問題。

例如,Kubernetes 中有一個名為 Deployment 的物件,它處理釋出管理和擴充套件能力。詳細資訊可以參考官方檔案:https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

提高效率與靈活性

使用虛擬機器進行開發可能需要幾天甚至幾周時間。而使用容器開發應用程式則允許真正靈活的開發週期,支援持續整合、持續佈署和持續改進。此外,使用 YAML 清單佈署應用程式還會促進 GitOps 的實踐,將所有組態管理集中到 Git 倉函式庫中。

Kubernetes 中的 Secrets

Kubernetes 中的 Secrets 是一種安全地儲存和管理敏感資訊(如密碼、API 鍵等)的機制。Secrets 允許你將敏感資訊與你的應用程式碼分離,並且可以在不暴露這些資訊的情況下將它們傳遞給你的應用程式。

以下是一些關鍵點:

  • Secrets 可以透過 YAML 清單或 kubectl 命令列工具建立。
  • Secrets 在 Kubernetes 叢集中以加密形式儲存。
  • 應用程式可以透過環境變數或卷掛載來存取 Secrets。
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
type: Opaque
data:
  username: YWRtaW4=   # base64 encoded value of "admin"
  password: MWYyZDFlMmU2N2Rm   # base64 encoded value of "secret"

內容解密:

apiVersion: v1:指定 API 版本為 v1。
kind: Secret:指定資源型別為 Secret。
metadata:
name: my-secret:定義 Secret 的名稱為 my-secret。
type: Opaque:定義 Secret 的型別為 Opaque(不透明),表示它包含任意資料。
data:
username: YWRtaW4=:以 base64 編碼方式儲存「admin」字串作為「username」。
password: MWYyZDFlMmU2N2Rm:以 base64 編碼方式儲存「secret」字串作為「password」。

透過這些步驟及實際案例展示瞭如何在 Kubernetes 中管理敏感資訊及其重要性。希望這些內容能夠幫助你更好地理解及實踐 Kuberentes 中對於 Confidential data 的管理技術。