書き込みが必要なDockerコンテナをReadOnlyで動かせるようにする

コンテナを触っている人であれば当たり前っちゃ当たり前のテクニックな感じだと思いますが、最近k8s環境でDockerコンテナを読み取り専用で動かすことができたので、メモしておきます。

Dockerコンテナを安全に運用するための手段の一つとして、コンテナを読み取り専用(ReadOnly)にすることがあります。 しかし、ログやキャッシュディレクトリなどアプリケーションの仕組み上、どうしても書き込みが必要な場合があり、そのままでは読み取り専用で動作させることはできません。

例えば、nginxの公式イメージだと起動時にこうなります。

docker run --rm --read-only nginx:1.18.0
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: error: can not modify /etc/nginx/conf.d/default.conf (read-only file system?)
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2020/11/23 XX:XX:XX [emerg] 1#1: mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)
nginx: [emerg] mkdir() "/var/cache/nginx/client_temp" failed (30: Read-only file system)

--read-only をつけると読み取り専用で動作させることができます。

これを解決するために、書き込み可能なボリュームをマウントしてあげることで回避することができました。

以下、設定例です。

  • dockerコマンドの場合
docker run --rm \
  -v /var/cache/nginx \
  -v /var/run \
  -v /etc/nginx/conf.d \
  --read-only \
  -p 80:80 \
  nginx:1.18.0
  • docker-composeの場合

設定箇所のみピックアップして載せています

services:
  nginx:
    image: nginx:1.18.0
    read_only: true
    volumes:
      - /var/cache/nginx
      - /var/run
      - /etc/nginx/conf.d

設定箇所のみピックアップして載せています

template:
  spec:
    containers:
    - name: nginx
      image: nginx:1.18.0
      securityContext:
        readOnlyRootFilesystem: true
      volumeMounts:
      - name: nginx-cache
        mountPath: /var/cache/nginx
      - name: pid-dir
        mountPath: /var/run
      - name: nginx-conf
        mountPath: /etc/nginx/conf.d
    volumes:
    - name: nginx-cache
      emptyDir: {}
    - name: pid-dir
      emptyDir: {}
    - name: nginx-conf
      emptyDir: {}

今回の例では匿名ボリューム、emptyDirを設定しましたが必要に応じて、適切なボリュームを設定してあげてください。

もっと良い方法があれば教えてください。