はじめに
Kubernetes でのボリュームパーミッションの管理は、開発者やシステム管理者にとって難しい場合があります。コンテナが永続ボリュームから読み書きする必要がある場合、コンテナのユーザー ID とボリュームの所有権の不一致により、パーミッションの問題が頻繁に発生します。これらの課題は、アプリケーションの障害やデータアクセス問題を引き起こす可能性があります。
この実験(Lab)では、Kubernetes でよくあるボリュームパーミッションの問題について説明し、それらを解決するための実践的なソリューションを提供します。セキュリティコンテキストを適切に設定し、init コンテナを使用し、Kubernetes デプロイメントにおけるボリュームパーミッション管理のベストプラクティスを実装する方法を学びます。
Kubernetes ボリュームの理解
このステップでは、Kubernetes ボリュームを探求し、その仕組みを理解します。Kubernetes ボリュームは、コンテナが再起動または再スケジュールされても、コンテナがデータを永続的に保存およびアクセスするための方法を提供します。
Kubernetes ボリュームの種類
Kubernetes は、いくつかのボリュームタイプをサポートしています。
- EmptyDir: Pod の存続期間中存在するシンプルな空のディレクトリ。
- HostPath: ホストノードのファイルシステムからファイルまたはディレクトリを Pod にマウントします。
- PersistentVolume: 管理者によってプロビジョニングされ、Pod とは独立したライフサイクルを持つストレージリソース。
- ConfigMap および Secret: 設定データと機密情報を注入する方法を提供します。
最初のボリュームの作成
EmptyDir ボリュームを持つシンプルな Pod を作成してみましょう。
- Pod 設定の YAML ファイルを作成します。
cd ~/project/k8s-volume-demo
nano emptydir-pod.yaml
- 次の内容をファイルにコピーします。
apiVersion: v1
kind: Pod
metadata:
name: emptydir-pod
spec:
containers:
- name: container-1
image: ubuntu:22.04
command:
[
"/bin/bash",
"-c",
"while true; do echo $(date) >> /data/log.txt; sleep 10; done"
]
volumeMounts:
- name: data-volume
mountPath: /data
volumes:
- name: data-volume
emptyDir: {}
ファイルを保存します (Ctrl+X、次に Y、次に Enter を押します)。
Kubernetes クラスターで Pod を作成します。
kubectl apply -f emptydir-pod.yaml
- Pod が準備できるまで待ちます。
kubectl get pods
出力には、Pod が実行中の状態で表示されます。
NAME READY STATUS RESTARTS AGE
emptydir-pod 1/1 Running 0 30s
- ボリュームの内容を確認してみましょう。
kubectl exec emptydir-pod -- cat /data/log.txt
タイムスタンプのリストが表示され、コンテナがボリュームに書き込んでいることがわかります。
Mon Jan 1 12:34:56 UTC 2023
Mon Jan 1 12:35:06 UTC 2023
Mon Jan 1 12:35:16 UTC 2023
ボリュームマウントの理解
上記の例では、次のようになっています。
emptyDirタイプのdata-volumeという名前のボリュームを定義しました。- このボリュームをパス
/dataのコンテナにマウントしました。 - コンテナはこのボリューム内のファイルにタイムスタンプを書き込みます。
これは、Kubernetes ボリュームの基本的な概念を示しています。つまり、Pod 内のコンテナがアクセスできるストレージを提供します。EmptyDir ボリュームは Pod の存続期間中存在するため、Pod が削除されるとデータは失われます。
クリーンアップ
作成した Pod を削除しましょう。
kubectl delete pod emptydir-pod
次のステップでは、ボリュームを使用する際にパーミッションの問題がどのように発生するか、そしてそれらを特定する方法を探求します。
ボリュームパーミッションの問題の特定
このステップでは、Kubernetes でよくあるボリュームパーミッションの問題を示すシナリオを作成します。これらの問題は、通常、HostPath ボリュームまたは永続ボリュームを使用し、ファイルシステムのパーミッションがコンテナ内で実行されているユーザー ID と一致しない場合に発生します。
問題の理解
コンテナが非 root ユーザーとして実行されているが、root (または別のユーザー) が所有するボリュームにアクセスしようとすると、パーミッション拒否エラーが発生する可能性があります。これは、コンテナを非 root ユーザーとして実行することがセキュリティのベストプラクティスである本番環境でよくある問題です。
パーミッションの問題がある HostPath ボリュームの作成
root 所有権を持つ HostPath ボリュームにアクセスしようとする Pod を作成してみましょう。
- Pod 設定の YAML ファイルを作成します。
cd ~/project/k8s-volume-demo
nano hostpath-pod.yaml
- 次の内容をファイルにコピーします。
apiVersion: v1
kind: Pod
metadata:
name: hostpath-pod
spec:
containers:
- name: container-1
image: ubuntu:22.04
command:
[
"/bin/bash",
"-c",
"while true; do echo 'Trying to write' >> /data/output.txt; sleep 10; done"
]
volumeMounts:
- name: host-data
mountPath: /data
securityContext:
runAsUser: 1000 ## Run as non-root user
volumes:
- name: host-data
hostPath:
path: /home/labex/project/k8s-volume-demo/data
type: Directory
ファイルを保存します (Ctrl+X、次に Y、次に Enter を押します)。
Kubernetes クラスターで Pod を作成します。
kubectl apply -f hostpath-pod.yaml
- しばらく待ってから、Pod のステータスを確認します。
kubectl get pods
- Pod が実行されていることがわかりますが、ログを確認すると、エラーが表示される場合があります。
kubectl logs hostpath-pod
次のようなパーミッション拒否エラーが表示される場合があります。
bash: /data/output.txt: Permission denied
- ホストディレクトリのパーミッションを確認して、問題を確認しましょう。
ls -la ~/project/k8s-volume-demo/data
次のような出力が表示されるはずです。
total 12
drwxr-xr-x 2 root root 4096 Jan 1 12:00 .
drwxr-xr-x 3 labex labex 4096 Jan 1 12:00 ..
-rw-r--r-- 1 root root 19 Jan 1 12:00 test.txt
ディレクトリとファイルは root によって所有されていますが、コンテナはユーザー ID 1000 として実行されています。この不一致が、パーミッション拒否エラーの原因となります。
コンテナ内のユーザー ID とグループ ID の理解
Kubernetes では、securityContext 設定を使用して、コンテナを特定のユーザー ID として実行できます。この例では、次のようになります。
securityContext:
runAsUser: 1000 ## Run as non-root user
これは、Kubernetes にコンテナプロセスをユーザー ID 1000 として実行するように指示します。このユーザー ID には、root が所有するファイルへの書き込み権限がありません。
クリーンアップ
次のステップに進む前に、Pod を削除しましょう。
kubectl delete pod hostpath-pod
次のステップでは、これらのパーミッション問題に対するソリューションを探求します。
Security Context を使用したパーミッション問題の修正
このステップでは、Kubernetes の SecurityContext を使用してボリュームパーミッションの問題を解決する方法を探求します。SecurityContext は、Pod およびコンテナの特権とアクセス制御設定を定義します。
fsGroup を使用したパーミッションの修正
SecurityContext の fsGroup 設定は、パーミッションの問題の解決に役立ちます。指定すると、Kubernetes はボリュームのグループ所有権を指定されたグループ ID に変更し、そのグループがボリュームを読み書きできるようにパーミッションを設定します。
適切な SecurityContext を持つ Pod を作成してみましょう。
- Pod 設定の YAML ファイルを作成します。
cd ~/project/k8s-volume-demo
nano fixed-pod.yaml
- 次の内容をファイルにコピーします。
apiVersion: v1
kind: Pod
metadata:
name: fixed-pod
spec:
securityContext:
fsGroup: 2000 ## Set group ID for all containers in the pod
containers:
- name: container-1
image: ubuntu:22.04
command:
[
"/bin/bash",
"-c",
"while true; do echo $(date) >> /data/output.txt; cat /data/test.txt; sleep 10; done"
]
volumeMounts:
- name: host-data
mountPath: /data
securityContext:
runAsUser: 1000 ## Run as non-root user
volumes:
- name: host-data
hostPath:
path: /home/labex/project/k8s-volume-demo/data
type: Directory
ファイルを保存します (Ctrl+X、次に Y、次に Enter を押します)。
Kubernetes クラスターで Pod を作成します。
kubectl apply -f fixed-pod.yaml
- Pod が準備できるまで待ちます。
kubectl get pods
- さて、ログを確認して、パーミッションの問題が解決されたかどうかを確認しましょう。
kubectl logs fixed-pod
タイムスタンプが正常に書き込まれていることがわかるはずです。これは、コンテナがボリュームに書き込めるようになったことを示しています。
This is a test file
Mon Jan 1 12:45:06 UTC 2023
This is a test file
Mon Jan 1 12:45:16 UTC 2023
- ボリュームのパーミッションがどうなったか調べてみましょう。
## Get into the container
kubectl exec -it fixed-pod -- bash
## Inside the container, check the permissions
ls -la /data
## Exit the container
exit
Kubernetes が fsGroup 設定を適用したため、ボリューム内のファイルにコンテナからアクセスできるようになったことがわかるはずです。
Security Context 設定の理解
- runAsUser: コンテナプロセスが実行されるユーザー ID を指定します。
- fsGroup: ボリュームアクセスに使用されるグループ ID を制御します。Pod 内のすべてのプロセスはこの補足グループの一部になります。
- runAsGroup: コンテナ内のすべてのプロセスのプライマリグループ ID を指定します (オプション)。
これらの設定は、コンテナが root として実行せずに、セキュリティのベストプラクティスを維持しながら、ボリュームデータに適切にアクセスできるようにするのに役立ちます。
クリーンアップ
作成した Pod を削除しましょう。
kubectl delete pod fixed-pod
次のステップでは、init コンテナを使用してパーミッションの問題を修正する別の方法を探求します。
Init コンテナを使用したパーミッションの修正
状況によっては、fsGroup が十分でない、または使用できない場合があります。たとえば、特定のボリュームタイプを使用する場合や、古いバージョンの Kubernetes で実行する場合などです。このような場合、メインコンテナが開始する前に、init コンテナを使用して適切なパーミッションを設定できます。
Init コンテナとは?
Init コンテナは、Pod 内のメインコンテナの前に実行されます。パーミッションの設定、コンテンツのダウンロード、依存関係の待機など、初期化タスクを実行できます。Init コンテナは、ボリュームファイルの所有権とパーミッションを変更するために、高い権限で実行できるため、パーミッション管理に特に役立ちます。
Init コンテナを使用した Pod の作成
init コンテナを使用してパーミッションを修正する Pod を作成してみましょう。
- Pod 設定の YAML ファイルを作成します。
cd ~/project/k8s-volume-demo
nano init-pod.yaml
- 次の内容をファイルにコピーします。
apiVersion: v1
kind: Pod
metadata:
name: init-pod
spec:
initContainers:
- name: permission-fixer
image: ubuntu:22.04
command:
["/bin/bash", "-c", "chown -R 1000:1000 /data && chmod -R 755 /data"]
volumeMounts:
- name: host-data
mountPath: /data
securityContext:
runAsUser: 0 ## Run as root to change permissions
containers:
- name: main-container
image: ubuntu:22.04
command:
[
"/bin/bash",
"-c",
"while true; do echo $(date) >> /data/init-output.txt; cat /data/test.txt; sleep 10; done"
]
volumeMounts:
- name: host-data
mountPath: /data
securityContext:
runAsUser: 1000 ## Run as non-root user
volumes:
- name: host-data
hostPath:
path: /home/labex/project/k8s-volume-demo/data
type: Directory
ファイルを保存します (Ctrl+X、次に Y、次に Enter を押します)。
Kubernetes クラスターで Pod を作成します。
kubectl apply -f init-pod.yaml
- Pod が準備できるまで待ちます。init コンテナが完了するまで、Pod は「Init」状態のままになります。
kubectl get pods
- Pod が実行状態になったら、メインコンテナのログを確認します。
kubectl logs init-pod
タイムスタンプが正常に書き込まれていることがわかるはずです。これは、コンテナがボリュームに書き込めるようになったことを示しています。
This is a test file
Mon Jan 1 13:05:06 UTC 2023
This is a test file
Mon Jan 1 13:05:16 UTC 2023
- ボリュームのパーミッションがどうなったか調べてみましょう。
## Check the content of the data directory
ls -la ~/project/k8s-volume-demo/data
ファイルの所有権が、init コンテナのコマンドで指定されたユーザー ID を反映するように変更されていることに気付くでしょう。
Init コンテナのアプローチの理解
この例では、次のようになります。
- init コンテナは root 権限 (
runAsUser: 0) で実行されます。 - ボリュームの内容の所有権とパーミッションを変更します。
- init コンテナが完了すると、メインコンテナが開始されます。
- メインコンテナは非 root ユーザー (
runAsUser: 1000) として実行されます。 - メインコンテナは、ボリュームの読み書きができるようになります。
この手法は、特に次のような場合に役立ちます。
- 特定の所有権パターンでボリュームを準備する必要がある場合
fsGroupをサポートしていないボリュームを操作している場合- 複雑なパーミッション設定ロジックを実行する必要がある場合
クリーンアップ
作成した Pod を削除しましょう。
kubectl delete pod init-pod
次のステップでは、ベストプラクティスを探求し、これらのアプローチを組み合わせて、堅牢なボリュームパーミッション管理を行う方法を学びます。
ベストプラクティスと組み合わせたアプローチ
実際のシナリオでは、ボリュームパーミッションを効果的に処理するために、複数のアプローチを組み合わせる必要がある場合があります。この最終ステップでは、ベストプラクティスを探求し、堅牢なソリューションを実装する包括的な例を作成します。
ボリュームパーミッションのベストプラクティス
Kubernetes でボリュームパーミッションを管理するための主なベストプラクティスを以下に示します。
- 可能であれば、コンテナを非 root ユーザーとして実行します。
- アプリケーションが機能するために必要な最小限の権限を持つユーザーを使用します。
- Pod とコンテナの両方のレベルでセキュリティコンテキストを活用します。
- 組織全体でUID/GID の値を標準化します。
- 複雑なセットアップシナリオには init コンテナを使用します。
- パーミッションの問題に対して適切なエラー処理を実装します。
包括的なソリューションの作成
学習内容を組み合わせて、堅牢なソリューションを作成しましょう。
- デプロイメントの YAML ファイルを作成します。
cd ~/project/k8s-volume-demo
nano best-practice-deployment.yaml
- 次の内容をファイルにコピーします。
apiVersion: apps/v1
kind: Deployment
metadata:
name: volume-best-practices
labels:
app: volume-demo
spec:
replicas: 1
selector:
matchLabels:
app: volume-demo
template:
metadata:
labels:
app: volume-demo
spec:
## Pod-level security context
securityContext:
fsGroup: 2000
## Init container for advanced preparation
initContainers:
- name: volume-permissions
image: ubuntu:22.04
command:
- /bin/bash
- -c
- |
## Create directory structure if it doesn't exist
mkdir -p /data/app /data/logs /data/config
## Set appropriate permissions
chmod 755 /data
chmod 775 /data/app /data/logs
chmod 755 /data/config
## Set ownership (belt and suspenders approach)
chown -R 1000:2000 /data
echo "Volume prepared successfully"
volumeMounts:
- name: data-volume
mountPath: /data
securityContext:
runAsUser: 0 ## Run as root for setup
## Application containers
containers:
- name: app-container
image: ubuntu:22.04
command:
- /bin/bash
- -c
- |
echo "Starting application with user $(id)"
while true; do
echo "$(date) - Application running" >> /data/logs/app.log
echo "Writing to app directory" >> /data/app/status.txt
echo "Reading from config"
cat /data/config/test.txt || echo "No config found"
sleep 10
done
volumeMounts:
- name: data-volume
mountPath: /data
securityContext:
runAsUser: 1000 ## Run as non-root user
runAsGroup: 2000 ## Use the same group as fsGroup
allowPrivilegeEscalation: false ## Prevent privilege escalation
volumes:
- name: data-volume
hostPath:
path: /home/labex/project/k8s-volume-demo/data
type: Directory
ファイルを保存します (Ctrl+X、次に Y、次に Enter を押します)。
Kubernetes クラスターでデプロイメントを作成します。
kubectl apply -f best-practice-deployment.yaml
- デプロイメントが準備できるまで待ちます。
kubectl get pods -l app=volume-demo
- init コンテナのログを確認しましょう。
## Get the pod name
POD_NAME=$(kubectl get pods -l app=volume-demo -o jsonpath='{.items[0].metadata.name}')
## Check the init container logs
kubectl logs $POD_NAME -c volume-permissions
ボリュームが正常に準備されたというメッセージが表示されるはずです。
- 次に、アプリケーションコンテナのログを確認します。
kubectl logs $POD_NAME -c app-container
アプリケーションが実行されており、ボリュームの読み書きができることがわかるはずです。
- デプロイメントによって作成されたファイルを調べてみましょう。
ls -la ~/project/k8s-volume-demo/data
init コンテナによって作成されたディレクトリ構造が、適切なパーミッションと所有権で表示されるはずです。
包括的なソリューションの理解
このソリューションは、複数のベストプラクティスを組み合わせています。
- 基本的なパーミッションを設定するための
fsGroupを使用したPod レベルのセキュリティコンテキスト - 複雑なディレクトリ構造のセットアップのためのinit コンテナ
- 非 root として実行するためのコンテナレベルのセキュリティコンテキスト
fsGroupとrunAsGroup間の適切なグループアライメントallowPrivilegeEscalation: falseによるセキュリティの強化
このアプローチにより、以下が保証されます。
- アプリケーションは、機能するために必要なパーミッションを持っています。
- 最小権限の原則が守られています。
- ソリューションは、さまざまな環境で堅牢です。
クリーンアップ
このラボで作成されたすべてのリソースをクリーンアップしましょう。
kubectl delete deployment volume-best-practices
rm -rf ~/project/k8s-volume-demo/data/app ~/project/k8s-volume-demo/data/logs ~/project/k8s-volume-demo/data/config
これで、Kubernetes でボリュームパーミッションの問題を解決するための複数のアプローチを学び、ベストプラクティスに従った包括的なソリューションを実装しました。
まとめ
この実験では、Kubernetes のボリュームパーミッションの問題を、いくつかの方法で特定し、解決する方法を学びました。
- まず、Kubernetes のボリュームの仕組みを理解し、シンプルな EmptyDir ボリュームの例を作成しました。
- 次に、ユーザーパーミッションが一致しない HostPath ボリュームを作成することにより、一般的なボリュームパーミッションの問題を特定しました。
- 適切なボリュームパーミッションを設定するために、
fsGroupを使用した Kubernetes の SecurityContext を使用してソリューションを実装しました。 - メインコンテナが開始する前に、init コンテナを使用して明示的にパーミッションを設定する代替アプローチを検討しました。
- 最後に、これらのテクニックを包括的なベストプラクティスソリューションに組み合わせ、Kubernetes でボリュームパーミッションを管理するための堅牢な方法を提供しました。
これらのスキルは、コンテナ化されたアプリケーションが、セキュリティのベストプラクティスを維持しながら、永続的なストレージに適切にアクセスできるようにするのに役立ちます。適切な SecurityContext、init コンテナ、およびパーミッション管理テクニックを組み合わせることで、Kubernetes デプロイメントで一般的なパーミッションの問題を回避できます。


