: O. Yuanying

KubernetesでConfigMapが変更されたらnginxをリロードしたい

今更だけど。shareProcessNamespace が導入されて簡単になったので。(今更だけど。)

⛔️ ちなみに、このテクニックは所謂バッドノウハウです。ConfigMapはできる限りimmutableに扱い、内容を変更したい場合は新しいConfigMapを作ってそれを参照するようにしてください。

承前

まず、以下のようななんの変哲もない nginx のマニフェストを用意したとする。

---
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: ClusterIP
  ports:
  - name: http
    port: 80
    targetPort: http
    protocol: TCP
  selector:
    app: web
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: config
data:
  default.conf: |
    server {
        listen       80;
        server_name  localhost;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }
        #error_page  404              /404.html;
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }
---
apiVersion: v1
kind: Pod
metadata:
  name: web
  labels:
    app: web
spec:
  containers:
  - name: web
    image: nginx
    ports:
    - name: http
      containerPort: 80
    volumeMounts:
    - name: config
      mountPath: /etc/nginx/conf.d
  volumes:
  - name: config
    configMap:
      name: config

以上のマニフェストにあるConfigMapの中身を変更した場合、nginxをリロードしたい際はどうするか。

答え

ConfigMap でマウントしたディレクトリの下にある ..data ディレクトリを inotifywait で監視する。

先ほどのマニフェストに以下を追加。

  • プロセス名前空間をコンテナ間で共有するための、shareProcessNamespace: true
  • pidファイルをコンテナ間で共有するための emptyDir
  • ConfigMap でマウントしたディレクトリを監視して、nginx をリロードする watcher コンテナ
➜ diff -u 0010.yaml 0020.yaml
--- 0010.yaml   2022-06-17 07:07:29.599464191 +0000
+++ 0020.yaml   2022-06-17 07:26:02.463611238 +0000
@@ -41,6 +41,7 @@
   labels:
     app: web
 spec:
+  shareProcessNamespace: true
   containers:
   - name: web
     image: nginx
@@ -48,9 +49,40 @@
     - name: http
       containerPort: 80
     volumeMounts:
+    - name: pid
+      mountPath: /var/run
+    - name: config
+      mountPath: /etc/nginx/conf.d
+  - name: watcher
+    image: ubuntu:22.04
+    command:
+    - /bin/bash
+    - -c
+    args:
+    - |
+      # Target directory to watch
+      TARGET=/etc/nginx/conf.d
+
+      # Install inotify-tools
+      apt-get update && apt-get install -y inotify-tools
+
+      # Watch inotify events
+      inotifywait -m --format '%w %f %e' -e modify,create,delete,move,attrib "${TARGET}" | \
+      while read DIR FILE EVENT; do
+          echo "${EVENT}: ${DIR}${FILE} is changed"
+          if [[ "${FILE}" == '..data' ]]; then
+              echo "--> trying reload nginx gracefully..."
+              kill -HUP $(cat /var/run/nginx.pid)
+          fi
+      done
+    volumeMounts:
+    - name: pid
+      mountPath: /var/run
     - name: config
       mountPath: /etc/nginx/conf.d
   volumes:
+  - name: pid
+    emptyDir: {}
   - name: config
     configMap:
       name: config

メモ

ConfigMapでマウントされたディレクトリ

ConfigMapでマウントされたディレクトリは以下のようにマウントされている。

  • 実際に利用する default.conf..data/default.conf へのシンボリックリンク。
  • ..data/default.conf は、さらに実体のファイルがある .2022_06_17_07_15_49.3234865442 へのシンボリックリンク。
root@web:/etc/nginx/conf.d# ls -la
total 12
drwxrwxrwx 3 root root 4096 Jun 17 07:15 .
drwxr-xr-x 3 root root 4096 May 28 05:40 ..
drwxr-xr-x 2 root root 4096 Jun 17 07:15 ..2022_06_17_07_15_49.3234865442
lrwxrwxrwx 1 root root   32 Jun 17 07:15 ..data -> ..2022_06_17_07_15_49.3234865442
lrwxrwxrwx 1 root root   19 Jun 17 07:10 default.conf -> ..data/default.conf

以上のような構成になっているのは、ConfigMapの変更によって引き起こされるファイル操作をアトミックに行うためと思われる。

ConfigMapの変更によって、inotifywait で観測されるイベント

以下のようなスクリプトで、

# Target directory to watch
TARGET=/etc/nginx/conf.d

# Install inotify-tools
apt-get update && apt-get install -y inotify-tools

# Watch inotify events
inotifywait -m --format '%w %f %e' -e modify,create,delete,move,attrib "${TARGET}" | \
while read DIR FILE EVENT; do
    echo "${EVENT}: ${DIR}${FILE} is changed"
done

ConfigMapでマウントしたボリュームを監視すると、このようなイベントが発行されていることがわかる。

CREATE,ISDIR: /etc/nginx/conf.d/..2022_06_17_07_11_51.493665868 is changed
ATTRIB,ISDIR: /etc/nginx/conf.d/..2022_06_17_07_11_51.493665868 is changed
CREATE: /etc/nginx/conf.d/..data_tmp is changed
MOVED_FROM: /etc/nginx/conf.d/..data_tmp is changed
MOVED_TO: /etc/nginx/conf.d/..data is changed
DELETE,ISDIR: /etc/nginx/conf.d/..2022_06_17_07_10_35.1071074202 is changed

実際にnginxが読み込んでいる /etc/nginx/conf.d/default.conf 自身は全く変更されず、以下の手順で中身が更新されている。

  1. ConfigMapの中身の実体である ..2022_06_17_07_11_51.493665868 が作成される。
  2. 操作をアトミックに行うために、..data_tmp のシンボリックリンクを作成し、
  3. ..data_tmp..data に移動する。
  4. 以前のConfigMapの中身が含まれている ..2022_06_17_07_10_35.1071074202 ディレクトリを削除する。

そのため、実体の /etc/nginx/conf.d/default.conf を inotifywait で監視しても、中身の変更がないためイベントが発生しない。また、/etc/nginx/conf.d/..data を直接監視したとしても inode が変更されてしまうため、一度しかイベントを拾うことができない。