ConfigMap熱更新測試
ConfigMap是用來存儲配置檔案的kubernetes資源對象,所有的配置内容都存儲在etcd中,下文主要是探究 ConfigMap 的建立和更新流程,以及對 ConfigMap 更新後容器内挂載的内容是否同步更新的測試。
測試示例
假設我們在 default namespace 下有一個名為 nginx-config 的 ConfigMap,可以使用 kubectl指令來擷取:
$ kubectl get configmap nginx-config
NAME DATA AGE
nginx-config 1 99d
擷取該ConfigMap的内容。
kubectl get configmap nginx-config -o yaml
apiVersion: v1
data:
nginx.conf: |-
worker_processes 1;
events { worker_connections 1024; }
http {
sendfile on;
server {
listen 80; # a test endpoint that returns http 200s
location / {
proxy_pass http://httpstat.us/200;
proxy_set_header X-Real-IP $remote_addr; } }
server {
listen 80;
server_name api.hello.world;
location / {
proxy_pass http://l5d.default.svc.cluster.local;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_http_version 1.1;
more_clear_input_headers 'l5d-ctx-*' 'l5d-dtab' 'l5d-sample'; } }
server {
listen 80;
server_name www.hello.world;
location / { # allow 'employees' to perform dtab overrides if ($cookie_special_employee_cookie != "letmein") {
more_clear_input_headers 'l5d-ctx-*' 'l5d-dtab' 'l5d-sample'; } # add a dtab override to get people to our beta, world-v2 set $xheader ""; if ($cookie_special_employee_cookie ~* "dogfood") { set $xheader "/host/world => /srv/world-v2;"; }
proxy_set_header 'l5d-dtab' $xheader;
proxy_pass http://l5d.default.svc.cluster.local;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_http_version 1.1; } } }
kind: ConfigMap
metadata:
creationTimestamp: 2017-08-01T06:53:17Z
name: nginx-config
namespace: default
resourceVersion: "14925806"
selfLink: /api/v1/namespaces/default/configmaps/nginx-config
uid: 18d70527-7686-11e7-bfbd-8af1e3a7c5bd
ConfigMap中的内容是存儲到etcd中的,然後查詢etcd:
ETCDCTL_API=3 etcdctl get /registry/configmaps/default/nginx-config
/registry/configmaps/default/nginx-config
注意使用 v3 版本的 etcdctl API,下面是輸出結果:
k8s
v1 ConfigMap�
T
nginx-configdefault"*$18d70527-7686-11e7-bfbd-8af1e3a7c5bd28B
�ʀ����xz�
nginx.conf�
worker_processes 1;
events { worker_connections 1024; }
http {
sendfile on;
server {
listen 80;
# a test endpoint that returns http 200s
location / {
proxy_pass http://httpstat.us/200;
proxy_set_header X-Real-IP $remote_addr;
}
}
server {
listen 80;
server_name api.hello.world;
location / {
proxy_pass http://l5d.default.svc.cluster.local;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_http_version 1.1;
more_clear_input_headers 'l5d-ctx-*' 'l5d-dtab' 'l5d-sample';
}
}
server {
listen 80;
server_name www.hello.world;
location / {
# allow 'employees' to perform dtab overrides
if ($cookie_special_employee_cookie != "letmein") {
more_clear_input_headers 'l5d-ctx-*' 'l5d-dtab' 'l5d-sample';
}
# add a dtab override to get people to our beta, world-v2
set $xheader "";
if ($cookie_special_employee_cookie ~* "dogfood") {
set $xheader "/host/world => /srv/world-v2;";
}
proxy_set_header 'l5d-dtab' $xheader;
proxy_pass http://l5d.default.svc.cluster.local;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_http_version 1.1;
}
}
}"
輸出中在 nginx.conf 配置檔案的基礎中增加了檔案頭内容,是kubernetes增加的。
代碼
ConfigMap 結構體的定義:
// ConfigMap holds configuration data for pods to consume.
type ConfigMap struct {
metav1.TypeMeta `json:",inline"` // Standard object's metadata. // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata // +optional
metav1.ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"` // Data contains the configuration data. // Each key must be a valid DNS_SUBDOMAIN with an optional leading dot. // +optional Data map[string]string `json:"data,omitempty" protobuf:"bytes,2,rep,name=data"` }
在 staging/src/k8s.io/client-go/kubernetes/typed/core/v1/configmap.go 中ConfigMap 的接口定義:
// ConfigMapInterface has methods to work with ConfigMap resources.
type ConfigMapInterface interface { Create(*v1.ConfigMap) (*v1.ConfigMap, error) Update(*v1.ConfigMap) (*v1.ConfigMap, error) Delete(name string, options *meta_v1.DeleteOptions) error
DeleteCollection(options *meta_v1.DeleteOptions, listOptions meta_v1.ListOptions) error
Get(name string, options meta_v1.GetOptions) (*v1.ConfigMap, error) List(opts meta_v1.ListOptions) (*v1.ConfigMapList, error) Watch(opts meta_v1.ListOptions) (watch.Interface, error) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1.ConfigMap, err error) ConfigMapExpansion }
在 staging/src/k8s.io/client-go/kubernetes/typed/core/v1/configmap.go 中建立 ConfigMap 的方法如下:
// Create takes the representation of a configMap and creates it. Returns the server's representation of the configMap, and an error, if there is any.
func (c *configMaps) Create(configMap *v1.ConfigMap) (result *v1.ConfigMap, err error) {
result = &v1.ConfigMap{}
err = c.client.Post(). Namespace(c.ns). Resource("configmaps"). Body(configMap). Do(). Into(result) return }
通過 RESTful 請求在 etcd 中存儲 ConfigMap 的配置,該方法中設定了資源對象的 namespace 和 HTTP 請求中的 body,執行後将請求結果儲存到 result 中傳回給調用者。
注意 Body 的結構
// Body makes the request use obj as the body. Optional. // If obj is a string, try to read a file of that name. // If obj is a []byte, send it directly. // If obj is an io.Reader, use it directly. // If obj is a runtime.Object, marshal it correctly, and set Content-Type header. // If obj is a runtime.Object and nil, do nothing. // Otherwise, set an error.
建立 ConfigMap RESTful 請求中的的 Body 中包含 ObjectMeta 和 namespace。
HTTP 請求中的結構體:
// Request allows for building up a request to a server in a chained fashion. // Any errors are stored until the end of your call, so you only have to // check once.
type Request struct { // required
client HTTPClient
verb string
baseURL *url.URL
content ContentConfig
serializers Serializers // generic components accessible via method setters
pathPrefix string
subpath string params url.Values
headers http.Header // structural elements of the request that are part of the Kubernetes API conventions namespace string
namespaceSet bool
resource string
resourceName string
subresource string
timeout time.Duration // output
err error
body io.Reader // This is only used for per-request timeouts, deadlines, and cancellations.
ctx context.Context
backoffMgr BackoffManager
throttle flowcontrol.RateLimiter }
測試
分别測試使用 ConfigMap 挂載 Env 和 Volume 的情況。
更新使用ConfigMap挂載的Env
使用下面的配置建立 nginx 容器測試更新 ConfigMap 後容器内的環境變量是否也跟着更新。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 1 template:
metadata:
labels:
run: my-nginx
spec:
containers: - name: my-nginx
image: sz-pg-oam-docker-hub-001.tendcloud.com/library/nginx:1.9
ports: - containerPort: 80
envFrom: - configMapRef:
name: env-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: env-config
namespace: default
data:
log_level: INFO
擷取環境變量的值
$ kubectl exec `kubectl get pods -l run=my-nginx -o=name|cut -d "/" -f2` env|grep log_level
log_level=INFO
修改 ConfigMap
$ kubectl edit configmap env-config
修改 log_level 的值為 DEBUG。
再次檢視環境變量的值。
$ kubectl exec `kubectl get pods -l run=my-nginx -o=name|cut -d "/" -f2` env|grep log_level
log_level=INFO
實踐證明修改 ConfigMap 無法更新容器中已注入的環境變量資訊。
更新使用ConfigMap挂載的Volume
使用下面的配置建立 nginx 容器測試更新 ConfigMap 後容器内挂載的檔案是否也跟着更新。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-nginx
spec:
replicas: 1 template:
metadata:
labels:
run: my-nginx
spec:
containers: - name: my-nginx
image: sz-pg-oam-docker-hub-001.tendcloud.com/library/nginx:1.9
ports: - containerPort: 80
volumeMounts: - name: config-volume
mountPath: /etc/config
volumes: - name: config-volume
configMap:
name: special-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
log_level: INFO
$ kubectl exec `kubectl get pods -l run=my-nginx -o=name|cut -d "/" -f2` cat /tmp/log_level
INFO
$ kubectl edit configmap special-config
等待大概10秒鐘時間,再次檢視環境變量的值。
$ kubectl exec `kubectl get pods -l run=my-nginx -o=name|cut -d "/" -f2` cat /tmp/log_level
DEBUG
我們可以看到使用 ConfigMap 方式挂載的 Volume 的檔案中的内容已經變成了 DEBUG。
總結
更新 ConfigMap 後:
- 使用該 ConfigMap 挂載的 Env 不會同步更新
- 使用該 ConfigMap 挂載的 Volume 中的資料需要一段時間(實測大概10秒)才能同步更新
ENV 是在容器啟動的時候注入的,啟動之後 kubernetes 就不會再改變環境變量的值,且同一個 namespace 中的 pod 的環境變量是不斷累加的,參考
Kubernetes中的服務發現與docker容器間的環境變量傳遞源碼探究。為了更新容器中使用 ConfigMap 挂載的配置,可以通過滾動更新 pod 的方式來強制重新挂載 ConfigMap,也可以在更新了 ConfigMap 後,先将副本數設定為 0,然後再擴容。
本文轉自kubernetes中文社群-
Kubernetes ConfigMap熱更新測試 – 探究ConfigMap的建立和更新流程