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
自身は全く変更されず、以下の手順で中身が更新されている。
- ConfigMapの中身の実体である
..2022_06_17_07_11_51.493665868
が作成される。 - 操作をアトミックに行うために、
..data_tmp
のシンボリックリンクを作成し、 ..data_tmp
を..data
に移動する。- 以前のConfigMapの中身が含まれている
..2022_06_17_07_10_35.1071074202
ディレクトリを削除する。
そのため、実体の /etc/nginx/conf.d/default.conf
を inotifywait で監視しても、中身の変更がないためイベントが発生しない。また、/etc/nginx/conf.d/..data
を直接監視したとしても inode が変更されてしまうため、一度しかイベントを拾うことができない。