配置管理
兩種特殊類型存儲卷,其多數情況下目的不是給pod提供存儲空間來用的,而是給管理員或用戶提供了從 集羣外部向 pod內部的應用注入配置信息的方式, 理論來源
配置容器應用方法
- 自定義命令行兩種參數:
command, args:['xx','xx']
- 事先將配置文件直接寫死鏡像
- 環境變量: 應用程序應當支持環境變量,entrypoint(預處理腳本) 方式提供
- 如果程序鏡像是雲原生,或是entrypoint腳本通過傳遞環境變量接受參數完成配置,這是一種輕量化的配置方式,在容器啓動時傳遞N個環境變量,分別就能代表N個重要的配置參數,從而完成應用程序配置;
- 當以nginx爲例,當啓動nginx時需要加載自定義的配置文件模塊,或使用存儲卷的方式進行配置文件的傳遞,將外部配置文件加載至容器內部;
- 但上述的配置方式是有缺陷的,當使用環境變量配置,env是在容器啓動時被裝入的,意味着如果想要修改某個變量,只能重新啓動容器,如果使用存儲卷,存儲卷的內容也是在進程啓動時被裝入,後續就算是修改了存儲卷的配置,也得需要手動觸發進程 加載新的配置
- 存儲卷
- dockerConfig (不是常用的方式)
使用場景
當我們在一個Pod控制器下,有多個Pod時,這些Pod的配置信息應該都是一樣的,意味着它們會共享一個存儲卷,當存儲卷內容被修改,這些Pod都能加載到新的內容,但這些Pod都會被重載,尤其是當我們使用Hpa控制器時,它會根據用戶訪問量來自定義伸縮Pod副本,那此時就會不知道重載哪些pod,所以在這種環境下最好能提供一種機制,當我們修改配置信息後能自動關連相關的Pod重載,最好只需要改一次,所有加載該配置文件的Pod都能被自動重載;
k8s中用於實現配置自動重載功能的組件,它們分別是 ConfigMap、Secret,它們都是用於將配置文件配置爲k8s資源
-
ConfigMap用於提供非敏感配置
-
Secre用於提供敏感配置信息(密碼、密鑰等)進行編碼存放
它們在內部都是對鍵值對的方式存在,鍵做爲配置參數,值可以是整個文件,比如說 可以將配置文件文件名做爲鍵,整個文件的配置做爲值
configMap
特殊類型的存儲卷,configMap | secret也是存儲卷,但它們目的不是爲給pod提供存儲空間,而是給管理員或用戶提供從集羣外部向pod內部的應用注入配置信息的方式,集羣配置管理資源, 信息明文存放
configMap也屬於名稱空間級別的資源, 字段查看
kubectl explain cm
ConfigMap做爲一個鍵值存儲系統,我們的配置文件幾乎都可以被實例爲ConfigMap的鍵值對象,在k8s中pod的ConfigMap調用有兩種方式:
- 在k8s的容器中,將每個key的value映射爲這個容器內部環境變量的值,比如在Pod中某個環境變量名是MYSQL_USER它的值引用於ConfigMap的值,我們可以向ConfigMap賦值,而不是直接給容器的MYSQL_USER賦值,從而由ConfigMap替換,然後由我們的應用程序所引用;
- k8s的ConfigMap和Pod作爲標準的k8s資源,當我們定義一個ConfigMap,其第一個參數鍵叫redis_host,值爲10.1.1.100,第二個鍵爲redis_port,值爲6379,隨後在配置一個filebeat的Pod,這個Pod通過環境變量引用redis主機和端口的配置,對應環境變量名是host和port,那麼我就可以這樣定義Pod,“host:redis-cfg.redis_host,port:redis-cfg.redis_port”,後續修改配置也只需要配置ConfigMap即可,但這種引用也有一種缺陷,當Pod啓動之後就不會再獲取值了,這也就意味着後續如果修改了ConfigMap裏的數據也不會被Pod加載,這時就需要重建來重新加載新配置了,這也是環境變量的缺陷
配置文件引入方式
- 啓動pod時可以將configMap資源關連至當前pod資源, 從中讀一個配置以變量注入的方式傳遞配置
- 將configMap當成一個存儲卷掛載至容器的某一個目錄,該目錄爲項目的配置目錄
核心作用
爲了將配置文件從鏡像中解耦,從而增強應用的可移植性以及可複用性,一個configMap就是一系列配置數據的集合,而這些數據將來可以注入到pod對象容器中所使用,而注入方式有兩種
- 直接將configMap當存儲卷;
- 使用env的valueFrom方式來引用configMap, 在configMap中所有的配置都保存爲鍵值對的格式(kv),鍵值對中的值長度無限制
configMap臨時使用
Aliases:
configmap, cm
Examples:
# --from-file=path/to/bar 文件名當鍵, 文件當值
kubectl create configmap my-config --from-file=path/to/bar
# --from-file=key1=/path/to/bar/file1.txt 指定鍵是key, 值是某個文件
kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
# Create a new configmap named my-config with key1=config1 and key2=config2
kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2
獲取環境變量
-
在pod容器上使用env或volueFrom來獲取
~]# kubectl explain pods.spec.containers env name: 變量名 value: 數據是什麼 valueFrom: 數據引用另一個變量 configMapKeyRef: configMap資源 fieldRef: 引用pod資源的字段 resourceFieldRef: 資源需求 | 資源限制 secretKeyRef: 引用secretKey配置資源
以鍵值對方式創建
-
創建
# 創建一個臨時的鍵值對 ~]# kubectl create cm nginx-test --from-literal=NGINX_NAME=t1.xiong.com --from-literal=NGINX_PORT=80
-
查看
~]# kubectl get cm NAME DATA AGE nginx-test 2 6s # 查看詳細內容 ~]# kubectl describe cm nginx-test Name: nginx-test Namespace: default Labels: <none> Annotations: <none> Data ==== NGINX_NAME: ---- t1.xiong.com NGINX_PORT: ---- 80 Events: <none>
-
創建pod
]# cat nginx_cm.yaml apiVersion: v1 kind: Pod metadata: annotations: configMap/test: "nginx" name: nginx-cm-test labels: app: nginx-cm release: qa spec: containers: - name: nginx-cm image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 env: - name: NGINX_PORT valueFrom: configMapKeyRef: name: nginx-test key: NGINX_PORT - name: NGINX_NAME valueFrom: configMapKeyRef: # 來源 name: nginx-test key: NGINX_NAME ]# kubectl apply -f nginx_cm.yaml ]# kubectl exec -it nginx-cm-test -- /bin/sh / # env NGINX_PORT=80 NGINX_NAME=t1.xiong.com
-
編輯configMap
]# kubectl edit cm nginx-test # 將port修改爲8080 # 在查看pod, env NGINX_PORT=80 # 當使用環境變量注入時,只在系統啓動時有效
以存儲卷創建
-
創建
]# cat t2.xiong.conf server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; } } ]# kubectl create cm nginx-test --from-file=t2.xiong.conf
-
查看
]# kubectl describe cm nginx-test Name: t2-xiong Namespace: default Data ==== t2.xiong.conf: # key就是文件名 ---- # 值是文本中的全部內容 server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.html index.htm; } }
-
以存儲卷方式掛載
]# cat t3_cm.yaml apiVersion: v1 kind: Pod metadata: annotations: configMap/test: "nginx" name: t3-xiong labels: app: t3-xiong release: qa spec: containers: - name: nginx-cm image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 volumeMounts: - name: nginx mountPath: /etc/nginx/conf.d/ readOnly: true volumes: - name: nginx configMap: name: nginx-test # 或掛載多個 volumes: - name: conf # 該volumes的名字 configMap: name: nginx-conf # 要使用configMap的名字 items: - key: server1 # 要使用指定configMap裏面的鍵名 path: s1.conf # 掛載之後的文件名 - key: server2 # 要使用指定configMap裏面的鍵名 path: s2.conf # 掛載之後的文件名
-
測試
~]# kubectl exec -it t3-xiong -- /bin/sh /etc/nginx/conf.d # ls -lh lrwxrwxrwx 1 root root 20 May 7 06:02 t2.xiong.conf -> ..data/t2.xiong.conf # root目錄下創建xx.html 內容 hello world ]# kubectl get pods -o wide NAME READY STATUS RESTARTS AGE IP NODE t3-xiong 1/1 Running 0 69s 10.244.1.2 slave1 ]# curl 10.244.1.2/xx.html hello world
- 使用該 ConfigMap 掛載的 Env 不會同步更新
- 使用該 ConfigMap 掛載的 Volume 中的數據需要一段時間(實測大概10秒)才能同步更新
ENV 是在容器啓動的時候注入的,啓動之後 kubernetes 就不會再改變環境變量的值,且同一個 namespace 中的 pod 的環境變量是不斷累加的,爲了更新容器中使用 ConfigMap 掛載的配置,可以通過滾動更新 pod 的方式來強制重新掛載 ConfigMap,也可以在更新了 ConfigMap 後,先將副本數設置爲 0,然後再擴容。
配置中心
Kubernetes用來配置分佈式或者負載均衡集羣化的配置中心,比如如果在nginx後面維護有20個tomcat,需要改修tomcat配置文件的一些配置信息,比如在修改tomcat連接redis的賬號密碼,那如何讓20個tomcat都應用到新配置,此前的做法就是使用ansible,然後分批推,推的時候還需要以灰度的方式進行,如果一下全部推送那在某一時刻服務都無法訪問了,所以即使藉助了ansible來編排,也得一個一個來,但這種才20個,但是如果有30個或者更多這就比較麻煩了,所以很多規模非常大的網站都不會通過這種方式來配置應用程序,而是讓應用程序所有配置都不通過本地配置文件加載,而是通過聯繫一個配置中心的服務去加載配置,所以稱之爲配置中心;
啓動多個應用程序時,拋棄了傳統從本地文件加載配置的方法,而是通過一箇中心服務器來加載配置,這個服務叫做配置中心,隨後我們在這個配置中心修改了配置以後,會通知給每一個進程,讓進程自動進行重載,就能完成所有的進程進行重載,還可以以灰度的方式通知,那麼他們就可以以灰度的方式去更新自己的配置信息;
國內有兩個項目,爲非容器化應用程序提供配置中心,協程的Apollo、百度的Distconf,所以在非容器化模式中,也可以使用配置中心,要使用配置中心還需要我們的程序支持從配置中心加載配置纔行;
secret
功能與configMap一樣,信息以 base64編碼存放 (相當於加密)
ConfigMap和Secret都作爲k8s的標準資源,用於實現爲Pod中容器提供配置信息,而這個配置信息它還扮演着K8s系統之上一組類似應用的配置中心,當配置發生變量時,我們只需要修改配置中心的配置,而後配置中心應用程序會自動更新配置信息並讓其重載應用;
配置中心都需要存儲大量的配置數據,那麼這些配置數據可以存儲在各式各樣的存儲系統當中,KV存儲最爲常見,比如zookepper、etcd這些配置工具,ConfigMap提供的配置信息,一般而言可以被我們的Pod容器以兩種方式進行加載,所以Pod中的容器加載ConfigMap資源中的信息
-
第一種爲環境變量爲容器提供環境變量實現;
-
第二種方式就是通過volumes存儲卷的方式配置;
而因爲ConfigMap和Pod都是名稱空間級別的資源,因此而者必須在同一名稱空間下面引用;
ConfigMap允許我們在必要時進行修改,修改存儲卷中內容在過一段隨機時間之後會同步到Pod的容器中,但如果容器本身不支持自動重載,我們可以在外部發送重載命令,讓其進行重載,也可以以灰度的方式進行,從這個角度上來說它的發佈可能會有一定的問題,有一部分建構在k8s之上的高級工具也能夠幫助我們完成k8s所不支持的各式各樣的功能;
構建ConfigMap資源有如下幾種方式
- 命令行: 但使用命令行去維護的資源是沒辦法被k8s跟蹤的,所以最好使用配置清單進行創建,使用命令行創建也有幾種方式
- 第一種: --from-literal=key=value 可以定義多個, 同時寫一行即可
- 第二種: --from-file=key=file_name 可以不指定key, 一般就是直接以文件名做爲鍵名
- 配置清單: 如果嵌套的是一個文件內容,稍微複雜一點,如下示例
secret資源類型
kubectl create secret -h
- docker-registry: 創建一個給docker register使用的私有倉庫帳號密碼, 在docker-register創建pod時,在spec當中有一個imagePullSecrets,對象列表,可以給出多個Secret,一個Secret就是一組帳號密碼,如果當第一個認證成功,那就繼續往下,自上而下依次進行檢索
- generic: 專用於存儲非tls類型的敏感信息,比如MySQL密碼
- tls: 專用於ssl或者tls的x509格式的證書和私鑰打包進Secret,證書和私鑰本身就是base64編碼,因此需要特有的邏輯來組織,不管文件名叫做什麼,在tls這種格式來組織的證書通通統一爲tls.cert,私鑰名爲tls.key;
generic
generic的方式主要是存儲一些用戶自定義的密碼,非tls類型的,比如MySQL密碼等,示例如下
通過環境變量
-
可選參數
]# kubectl create secret generic -h Examples: # 可以指定文件,key爲文件名 kubectl create secret generic my-secret --from-file=path/to/bar # 指定key名,value爲文件內容 kubectl create secret generic my-secret --from-file=ssh-privatekey=path/to/id_rsa --from-file=ssh-publickey=path/to/id_rsa.pub # 手動指定kv kubectl create secret generic my-secret --from-literal=key1=supersecret --from-literal=key2=topsecret
-
創建一個 generic 資源
]# kubectl create secret generic mysql-pass --from-literal=password=xiong@123 secret/mysql-pass created
-
查看
]# kubectl get secret NAME TYPE DATA AGE mysql-pass Opaque 1 7s ]# kubectl describe secrets mysql-pass Data ==== password: 9 bytes # key爲password, 其值就是一個隱藏結果只顯示字符串長度 # 查看更詳細結果 ]# kubectl get secrets mysql-pass -o yaml apiVersion: v1 data: password: eGlvbmdAMTIz # 通過-o yaml可以查看到其結果爲base64編碼 kind: Secret metadata: creationTimestamp: "2020-05-07T06:34:29Z" managedFields: - apiVersion: v1 fieldsType: FieldsV1 fieldsV1: f:data: .: {} f:password: {} f:type: {} manager: kubectl operation: Update time: "2020-05-07T06:34:29Z" name: mysql-pass namespace: default resourceVersion: "25605" selfLink: /api/v1/namespaces/default/secrets/mysql-pass uid: 4075a4d2-f81d-4e26-8d98-61df0f8eab51 type: Opaque # 解碼 base64 ]# echo eGlvbmdAMTIz | base64 -d # 相對的來說如果有心加密的值也能被顯示出來 xiong@123
-
通過環境變量方式注入
]# cat s1_secret.yaml apiVersion: v1 kind: Pod metadata: name: s1-secret namespace: default labels: app: s1-secret release: qa spec: containers: - name: s1-secret image: ikubernetes/myapp:v1 imagePullPolicy: IfNotPresent ports: - name: http containerPort: 80 env: - name: mysql_pass valueFrom: secretKeyRef: name: mysql-pass # 指定secret定義的名稱 key: password # 指定secret定義的key,注意這個key需要存在對應的資源中
-
驗證
~]# kubectl exec s1-secret -- env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=s1-secret mysql_pass=xiong@123
通過volume
volume是通過文件的方式載入配置,比如tomcat的默認配置有指定一個管理的user,那麼這個時候我們就可以把這個文件提取出來,載入Kubernetes作爲Secret,而後通過volume的方式掛在到指定的目錄下(可以掛載單個文件),覆蓋原有文件;