Kubernetes subPath | 容器原目錄下的文件全被覆蓋了,什麼鬼?

前言

docker 中的 volume 可以 mount 文件到特定目錄,同時保留原有目錄不變;同樣的 mount 放到 Kubernetes 卻變成了:只是把 mount 的幾個文件直接放到了根目錄,原有目錄中文件消失。在很多場景下,我們會希望只是掛載一個文件到容器內部某個目錄,而不影響原有目錄,常見使用場景有以下兩種:

  1. 我需要把一個特定文件放置到 linux 特定目錄下,且需要依賴 linux 底層 lib 庫,該目錄下已經存在其它文件,不能覆蓋。

  2. 項目在沒有容器化之前,證書和配置通常放到一個目錄中,現在容器化之後依然如此,這就有個問題,當把證書放到 Secret、配置放到 ConfigMap中,之後 mount 到容器特定目錄時互相覆蓋,影響服務正常運行,這些都是一些非常常見的場景。

當然解決方式也非常簡單,大致如下兩種:

  • 首先我可以通過曲線救國的方式把這些文件掛載到其它目錄,不影響原有文件夾。這樣一來,就解決了文件覆蓋問題,但是如果程序中已經配置只能從特定文件夾讀取該文件,或者該文件只能在特定文件夾下運行,如果非要修改目錄,那麼就需要修改代碼,這種方式不在過多解釋。

  • 其次我通過查找 Kubernetes 官網 https://kubernetes.io/docs/concepts/storage/volumes/#using-subpath Kubernetes 官方已經通過 subPath 解決同一個 Pod 多次使用同一個 volume 而引起的覆蓋問題,下面通過示例解釋,如何通過 subPath 解決同一個 Pod 共享數據卷問題。

subPath 使用

以下是使用單個共享卷的 LAMP 堆棧(Linux Apache Mysql PHP)的 pod 的示例。HTML 內容映射到其 html 文件夾,數據庫將存儲在 mysql 文件夾中,這樣就不需要爲 mysql 和 html 單獨創建 volume 了。

apiVersion: v1
kind: Pod
metadata:
  name: my-lamp-site
spec:
    containers:
    - name: mysql
      image: mysql
      volumeMounts:
      - mountPath: /var/lib/mysql
        name: site-data
        subPath: mysql
    - name: php
      image: php
      volumeMounts:
      - mountPath: /var/www/html
        name: site-data
        subPath: html
    volumes:
    - name: site-data
      persistentVolumeClaim:
        claimName: my-lamp-site-data

那麼如果 subPath 不是文件夾,而是一個文件,又該如何解決呢?同樣的道理,只需要通過 subPath 指定出該文件即可,注意 subPath 要使用相對目錄。具體如下所示:

containers:
- volumeMounts:
  - name: demo-config
    mountPath: /etc/ssl.key
    subPath: ssl.key
volumes:
- name: demo-ssl
  configMap:
    name: ssl-secret

Kubernetes 如何實現 subPath

代碼進行了精簡,重點 t.Mode()&os.ModeDir 即根據 volume 中的 subPath 指定的是目錄還是文件分別進行不同的操作。

func (mounter Interface, subpath Subpath, kubeletPid int) (hostPath string, err error) {
...
	// Create target of the bind mount. A directory for directories, empty file
	// for everything else.
	t, err := os.Lstat(subpath.Path)
	if err != nil {
		return "", fmt.Errorf("lstat %s failed: %s", subpath.Path, err)
	}
	if t.Mode()&os.ModeDir > 0 {
		if err = os.Mkdir(bindPathTarget, 0750); err != nil && !os.IsExist(err) {
			return "", fmt.Errorf("error creating directory %s: %s", bindPathTarget, err)
		}
	} else {
		// "/bin/touch <bindDir>".
		// A file is enough for all possible targets (symlink, device, pipe,
		// socket, ...), bind-mounting them into a file correctly changes type
		// of the target file.
		if err = ioutil.WriteFile(bindPathTarget, []byte{}, 0640); err != nil {
			return "", fmt.Errorf("error creating file %s: %s", bindPathTarget, err)
		}
	}

總結

本文主要介紹了通過使用 volume subPath 解決把文件掛載到容器已存在文件的目錄且不覆蓋原有目錄的方法。更多文檔請參考 [1]

參考資料

[1]

更多文檔: https://kubernetes.io/docs/concepts/storage/volumes/#using-subpath


推薦閱讀


如何使用 Ingress-nginx 進行前後端分離?

深入探究 K8S ConfigMap 和 Secret

Kubernetes入門培訓(內含PPT)


原創不易,隨手關注或者”在看“,誠摯感謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章