nekop's blog

OpenShift / JBoss / WildFly / Infinispanの中の人 http://twitter.com/nekop

Kubernetes CLI pluginを使ってOpenShiftのアプリケーション情報をダンプするpluginを作る

Kubernetes / OpenShift もくもく会 No. 1でした。

ここにあるOpenShiftで特定のプロジェクト(ネームスペース)をダンプするスクリプトをてきとーに作って使ったりしていたんですが、他の人にも使われるようになって汎用化とかOpenShiftの製品の一部にするとかいろいろしなきゃなー、って感じになってきたので、同僚のRobertが手を付けていたKubernetes CLI pluginとしてマージする作業をはじめました。以下のリポジトリです。

https://github.com/nekop/openshift-sos-plugin

とりあえず最低限のダンプがとれるようにアップデートして、一旦Robertに目を通してもらうためにpull reqしました。

https://github.com/bostrt/openshift-sos-plugin/pull/1

以下のように実行するとダンプファイルが生成されます。トラブルシューティングに必要な大体のオブジェクトのダンプと、イベントログ、podのログを全て保存して固めたものです。

$ oc plugin sos -n logging
Data capture complete and archived in /tmp/oc-sos-logging-20180206-210447.tar.xz
$ tar tf /tmp/oc-sos-logging-20180206-210447.tar.xz
logging/
logging/pods-logging-kibana-3-bwlw2_kibana-proxy.previous.log
logging/pods-logging-kibana-3-bwlw2_kibana-proxy.log
logging/pods-logging-kibana-3-bwlw2_kibana.previous.log
logging/pods-logging-kibana-3-bwlw2_kibana.log
logging/pods-logging-fluentd-rwgjv_fluentd-elasticsearch.previous.log
logging/pods-logging-fluentd-rwgjv_fluentd-elasticsearch.log
logging/pods-logging-fluentd-6fbm9_fluentd-elasticsearch.previous.log
logging/pods-logging-fluentd-6fbm9_fluentd-elasticsearch.log
logging/pods-logging-fluentd-4b5n5_fluentd-elasticsearch.previous.log
logging/pods-logging-fluentd-4b5n5_fluentd-elasticsearch.log
logging/pods-logging-fluentd-2fxhc_fluentd-elasticsearch.previous.log
logging/pods-logging-fluentd-2fxhc_fluentd-elasticsearch.log
logging/pods-logging-es-data-master-ye3u4nvd-3-q6md6_elasticsearch.previous.log
logging/pods-logging-es-data-master-ye3u4nvd-3-q6md6_elasticsearch.log
logging/pods-logging-es-data-master-ye3u4nvd-3-q6md6_proxy.previous.log
logging/pods-logging-es-data-master-ye3u4nvd-3-q6md6_proxy.log
logging/oc-get-all.txt
logging/oc-get-all.yaml
logging/oc-get-project.yaml
logging/oc-get-event.txt
logging/oc-status.txt
logging/oc-version.txt

Kubernetes CLI pluginについてはここに記述されています。

https://kubernetes.io/docs/tasks/extend-kubectl/kubectl-plugins/

気付いたこととして、plugin.yamlの記述をミスっても静かにプラグインとして認識されなくなります。実行しようとしてもそんなプラグインないよ、という以外に特にエラー報告とかはされないのでがんばって直しましょう。

あとはflagsには必ずValueを要求するオプションしか定義できないようです。-v / --debugオプションを定義したかったのですが、この制限のため微妙な感じになったので後回し。

このあとのTODOとしてはこんな感じ。

  • sos.sh単体でも利用できるよう、plugin特有のENVと直接コマンドパラメータの両方を入力として受けられるようにする、かな。
  • アプリケーションではなくoc get node,hostsubnetなどノードレベルのトラブルのための情報を取得するcluster-admin向けのオプション追加
  • debugオプション追加
  • secretやconfigmapを取得されたくないときもあるので、取得切り替えフラグ追加

ThinkPad T470sにFedora 27インストール

会社からクリスマスプレゼントでThinkPad T470sをもらったので翌日出社して受け取ってFedora 27をインストールしてメインマシンのスイッチ。スペックはIntel Core i7-7600U 2.8GHz 4 cores, 16GB mem, NVMe SSD 256GB。

前回の記録はFedora 24インストール

いつも通りHow to create and use Live USBCUIlivecd-toolsを使ってUSB起動したけどなぜかgrub/images/pxeboot/vmlinuzという存在していないパスを参照していて起動しなかった。いい機会だったのでごちゃまぜUSBメモリの運用をやめて、新しいUSBメモリGUImediawriterを使ってインストール専用USBメモリを作成してふつうにインストールした。

セットアップの流れはあまり変わってない。今回のセットアップでの変更点がいくつか。

  • メインブラウザとしてFirefoxではなくChrome(実験)
    • Firefoxのセットアップやspice-xpiインストールなどはスキップ
  • Emacsのsemiとwanderlustをpackage-installするようにした
  • Bluejeansはchrome extensionになったのでrpmパッケージはもういらない
  • Emacsのフォントがなぜか明朝になったのでとりあえず以下の設定を入れた。あとでフォント変えるかも。
(cond
 (window-system (set-default-font "DejaVu Sans Mono-8")
                (set-fontset-font (frame-parameter nil 'font)
                                  'japanese-jisx0208
                                  '("VL ゴシック" . "unicode-bmp"))))

Chromechrome://settings/でOn Startup -> Continue where you left offに設定してセッションを復元するように。あとはcp /usr/share/applications/google-chrome.desktop ~/.local/share/applications/してExecに--auth-server-whitelistを指定してKerberos認証の有効化。.desktopファイルをまるっとコピーしているのが若干微妙なんだけど差分だけ書けるようなもっとスマートな方法あるのかな。

OpenShift Container Platform 3.7をAWS上にシングルノードでセットアップする

OpenShift 全部俺 Advent Calendar 2017

OpenShiftをAWSでセットアップするリファレンスアーキテクチャやCloudFormationやデプロイスクリプトなどはいろいろ用意されていますが、本番構成向けのもったりした構成です。AWS上でのテスト用にシングルノードセットアップを使いたい場合があるので、手順を書いておきます。

VPC, Internet Gateway, Subnetなどは事前に作成する前提です。EC2 Instanceは以下のものを利用しました。

  • AMI: RHEL-7.4_HVM_GA-20170724-x86_64-1-Access2-GP2
  • Instance Type: m4.xlarge (4 vCPU, 16G mem)
  • Disk: 80GB
  • Tag: kubernetes.io/cluster/foo=owned
  • Elastic IP付与

Elastic IPの生IPやながーいホスト名は扱いづらいので、Route 53でmasterのホスト名とワイルドカードアプリケーションドメインをCNAMEもしくはAレコードで上記Elastic IPに設定します。

AWS上でKubernetesを利用する場合はkubernetes.io/cluster/<clusterid>タグが必要になります。設定しないと同一アカウント内の複数のKubernetesクラスタがEBSの奪い合いなどリソースの処理で競合して面白いことが起きます。

Security Groupは適切に設定してください。Allow All設定でAllowAllPasswordIdentityProviderの設定であれば当然ですがビットコイン掘られます。

起動したら前回と同じように以下のようなスクリプトを用意してsudoで流し込みます。

RHSM_USERNAME=
RHSM_PASSWORD=
RHSM_POOLID=
OPENSHIFT_VERSION=3.7

subscription-manager register --username=$RHSM_USERNAME --password=$RHSM_PASSWORD
subscription-manager attach --pool $RHSM_POOLID
subscription-manager repos --disable=*
subscription-manager repos \
     --enable=rhel-7-server-rpms \
     --enable=rhel-7-server-extras-rpms \
     --enable=rhel-7-server-optional-rpms \
     --enable=rhel-7-server-rh-common-rpms \
     --enable=rhel-7-fast-datapath-rpms \
     --enable=rhel-7-server-ose-$OPENSHIFT_VERSION-rpms

yum install chrony wget git net-tools bind-utils iptables-services bridge-utils nfs-utils sos sysstat bash-completion lsof tcpdump yum-utils yum-cron docker atomic-openshift-utils -y
sed -i 's/DOCKER_STORAGE_OPTIONS=/DOCKER_STORAGE_OPTIONS="--storage-driver overlay2"/' /etc/sysconfig/docker-storage
systemctl enable chronyd docker
systemctl start chronyd docker
reboot

再度接続したらansibleの準備です。

$ ssh-keygen
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
$ sudo mkdir -p /etc/aws
$ sudo cat <<EOF > /etc/aws/aws.conf
[Global]
Zone = ap-northeast-1c
EOF
$ sudo vi /etc/ansible/hosts

Ansibleのhostsファイルはこんな感じです。AWSやOpenStackなどのクラウド環境上ではホスト定義にopenshift_hostnameパラメータを定義してはいけません。Kubernetesのクラウドインテグレーションは内部ホスト名を要求するので、オーバーライドするといろいろエラーになります。

[OSEv3:children]
masters
etcd
nodes

[OSEv3:vars]
ansible_ssh_user=ec2-user
ansible_become=true
deployment_type=openshift-enterprise
openshift_master_identity_providers=[{'name': 'htpasswd', 'login': 'true', 'challenge': 'true', 'kind': 'HTPasswdPasswordIdentityProvider', 'filename': '/etc/origin/master/htpasswd'}]
os_sdn_network_plugin_name=redhat/openshift-ovs-multitenant
openshift_node_kubelet_args={'kube-reserved': ['cpu=100m,memory=100Mi'], 'system-reserved':['cpu=100m,memory=100Mi'], 'eviction-hard': [ 'memory.available<4%', 'nodefs.available<4%', 'nodefs.inodesFree<4%', 'imagefs.available<4%', 'imagefs.inodesFree<4%' ], 'eviction-soft': [ 'memory.available<8%', 'nodefs.available<8%', 'nodefs.inodesFree<8%', 'imagefs.available<8%', 'imagefs.inodesFree<8%' ], 'eviction-soft-grace-period': [ 'memory.available=1m30s', 'nodefs.available=1m30s', 'nodefs.inodesFree=1m30s', 'imagefs.available=1m30s', 'imagefs.inodesFree=1m30s' ]}
openshift_disable_check=memory_availability,disk_availability

openshift_master_cluster_public_hostname=master.example.com
openshift_master_default_subdomain=apps.example.com

openshift_cloudprovider_kind=aws
openshift_cloudprovider_aws_access_key="{{ lookup('env','AWS_ACCESS_KEY_ID') }}"
openshift_cloudprovider_aws_secret_key="{{ lookup('env','AWS_SECRET_ACCESS_KEY') }}"
openshift_clusterid=foo

[masters]
master.example.com

[etcd]
master.example.com

[nodes]
master.example.com openshift_node_labels="{'region': 'infra'}" openshift_schedulable=true

インストールを行なえばEBS PVのダイナミックプロビジョニングもセットアップされている状態になっているはずです。

export AWS_ACCESS_KEY_ID=...
export AWS_SECRET_ACCESS_KEY=...
ansible-playbook -vvvv /usr/share/ansible/openshift-ansible/playbooks/byo/config.yml | tee -a openshift-install-$(date +%Y%m%d%H%M%S).log
reboot

OpenShiftでメモリでオートスケールをしてみる

OpenShift 全部俺 Advent Calendar 2017

まだAlpha機能ですが、CPUではなくメモリ利用量でオートスケールを設定することができます。

https://docs.openshift.org/3.11/dev_guide/pod_autoscaling.html#pod-autoscaling-memory

先に重要な点を書いておきますが、このメモリ利用量とはcgroupsのmemory.usage_in_bytesであり、これはbuff/cacheを含みます。実際には解放できるディスクキャッシュなども使用中としてレポートされ、キャッシュが活用されている限りメモリ利用は常に高い状態をキープし、オートスケールであまり意味もなくPodが増えることになるので、メモリベースのオートスケールは現状あまり使い物にならないと思います。

追記: 上記記述は古いものであり、OpenShift 3.11以降など新しいバージョンはメモリ量としてmemory.usage_in_bytesではなくより一般的な利用量と呼ぶのにふさわしい値であるWorking set (memory.stat.rss + memory.stat.cache - memory.stat.inactive_file) を返却するのでメモリオートスケールも普通に利用できます。

まずはドキュメントの通り以下の設定をmaster-config.yamlへ反映してmasterを再起動します。

apiServerArguments:
  runtime-config:
  - apis/autoscaling/v2alpha1=true

いつものRubyのサンプルを作成してrequests.memoryに64Miを指定します。

$ oc new-project test-memhpa
$ oc new-app https://github.com/nekop/hello-sinatra
$ oc set resources dc/hello-sinatra --requests=memory=64Mi
$ cat <<EOF | oc create -f -
apiVersion: autoscaling/v2alpha1
kind: HorizontalPodAutoscaler
metadata:
  name: hpa-memory 
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: DeploymentConfig
    name: hello-sinatra
  minReplicas: 1 
  maxReplicas: 10 
  metrics:
  - type: Resource
    resource:
      name: memory
      targetAverageUtilization: 50 
EOF

作成したHPAを再度取得するとAlpha機能であるため内容のほとんどは以下のようにアノテーション内に格納されていることがわかります。

$ oc get hpa hpa-memory -o yaml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  annotations:
    autoscaling.alpha.kubernetes.io/conditions: '[{"type":"AbleToScale","status":"True","lastTransitionTime":"2017-12-25T02:36:07Z","reason":"ReadyForNewScale","message":"the
      last scale time was sufficiently old as to warrant a new scale"},{"type":"ScalingActive","status":"True","lastTransitionTime":"2017-12-25T02:36:07Z","reason":"ValidMetricFound","message":"the
      HPA was able to succesfully calculate a replica count from memory resource utilization
      (percentage of request)"},{"type":"ScalingLimited","status":"False","lastTransitionTime":"2017-12-25T02:36:07Z","reason":"DesiredWithinRange","message":"the
      desired replica count is within the acceptible range"}]'
    autoscaling.alpha.kubernetes.io/current-metrics: '[{"type":"Resource","resource":{"name":"memory","currentAverageUtilization":24,"currentAverageValue":"16576512"}}]'
    autoscaling.alpha.kubernetes.io/metrics: '[{"type":"Resource","resource":{"name":"memory","targetAverageUtilization":50}}]'
  creationTimestamp: 2017-12-25T02:35:37Z
  name: hpa-memory
  namespace: test-memhpa
  resourceVersion: "4241124"
  selfLink: /apis/autoscaling/v1/namespaces/test-memhpa/horizontalpodautoscalers/hpa-memory
  uid: 4a2e8f4e-e91c-11e7-b3cf-001a4a40dc83
spec:
  maxReplicas: 10
  minReplicas: 1
  scaleTargetRef:
    apiVersion: apps/v1
    kind: DeploymentConfig
    name: hello-sinatra
status:
  currentReplicas: 1
  desiredReplicas: 1

初期状態はメモリ16Mi利用しています。

$ oc adm top pod
NAME                    CPU(cores)   MEMORY(bytes)   
hello-sinatra-2-d9ft8   0m           16Mi

32MBのファイルを作ってcatしてディスクキャッシュに載せてみます。

$ oc rsh dc/hello-sinatra sh -c "dd if=/dev/zero of=/tmp/32mb bs=32M count=1; cat /tmp/32mb"

少し経過すると、メトリクスに反映されました。

$ oc adm top pod
NAME                    CPU(cores)   MEMORY(bytes)   
hello-sinatra-2-d9ft8   5m           48Mi            

HPAによるスケールアウトがトリガーされ、Podが2つに増えました。

$ oc get pod
NAME                    READY     STATUS    RESTARTS   AGE
hello-sinatra-2-b9hxt   1/1       Running   0          16s
hello-sinatra-2-d9ft8   1/1       Running   0          5m
$ oc adm top pod
NAME                    CPU(cores)   MEMORY(bytes)   
hello-sinatra-2-d9ft8   0m           48Mi            
hello-sinatra-2-b9hxt   0m           15Mi   

動作に必要なメモリが足りなくなりそうな場合にスケールアウト、というのが欲しいところですが、今のところはこのように動作に不可欠ではない解放されるメモリもカウントされてオートスケールされてしまいますので、使い道はかなり限定されそうです。

OpenShiftのmaster-config.yamlやnode-config.yamlの書式

OpenShift 全部俺 Advent Calendar 2017

たとえばmaster-config.yamlをカスタマイズしたい場合にどうやって記述すればいいのかなどを調べたい場合があります。

以下に項目がさらっと書かれたドキュメントがあり、さらっと把握するのには役立ちますが、やはりyaml形式の場合にどのように書いたら良いというようなガイドはありません。

https://docs.openshift.org/3.11/install_config/master_node_configuration.html

どこを見れば良いかというとソースコードです。OpenShiftはGo言語で記述されており、yaml形式の設定ファイルはこの定義とダイレクトにマップしています。

https://github.com/openshift/origin/blob/release-3.7/pkg/cmd/server/api/v1/types.go

たとえばkubernetesMasterConfig.controllerArgumentsは以下のように定義されています。

ControllerArguments ExtendedArguments `json:"controllerArguments"`

ExtendedArgumentsはkeyがstring、valueが[]stringのmapです。

type ExtendedArguments map[string][]string

つまり記述はこんな感じになります。

kubernetesMasterConfig:
  controllerArguments:
    foo:
    - bar
    - baz
    hoge:
    - "10"

string型であるので、数値などはクオートしないといけません。

Go言語の型とyaml形式のちょっとした知識は必要となりますが、難しいものではないので、このapi/v1/types.goを参照すればよい、ということさえわかっていればすぐ調べられます。

ちなみに各コンポーネントのデフォルト値を調べたい場合はソースツリーからoptions.goというファイルをリストするとたぶんわかります。

OpenShiftのディスク利用傾向

OpenShift 全部俺 Advent Calendar 2017

OpenShiftやKubernetesではどのようにディスクが使われ、どのくらいのディスクを必要とするのでしょうか。PVを割り当てて利用する分は自明ですが、それ以外はどのようになっているでしょうか。

コンテナイメージにVolumeが定義されている場合、oc new-appはEmptyDirを割り当てるようになっています。

EmptyDirは実際にはコンテナホストのファイルシステムの以下の位置にマップされています。

/var/lib/origin/openshift.local.volumes/pods/${POD_ID}/volumes/kubernetes.io~empty-dir/${MOUNT_PATH}

実際にEmptyDirへの書き込みをやってみましょう。

$ oc new-project test-volume
$ cat <<EOF | oc new-build --dockerfile=- --to=sleep
FROM registry.access.redhat.com/rhel
RUN mkdir /testvol && chmod 777 /testvol
VOLUME /testvol
CMD tail -f /dev/null
EOF
$ oc new-app sleep

初期状態のディスクはこのような状態です。Dockerのストレージドライバはoverlay2なので、コンテナのストレージは/var/lib/docker以下となります。

$ df -H
Filesystem                             Size  Used Avail Use% Mounted on
/dev/mapper/rhel_unused--221--68-root   54G   43G   12G  80% /
$ sudo du -hs /var/lib/docker /var/lib/origin
8.6G    /var/lib/docker
8.8G    /var/lib/origin

EmptyDirに1GBのsushiを書き込んでみます。/var/lib/originにファイルが作成されました。

$ oc volume dc/sleep
deploymentconfigs/sleep
  empty directory as sleep-volume-1
    mounted at /testvol
$ oc rsh dc/sleep ls -la /testvol
total 0
drwxrwsrwx. 2 root 1000200000  6 Dec 22 04:39 .
drwxr-xr-x. 1 root root       21 Dec 22 04:39 ..
$ oc rsh dc/sleep dd if=/dev/zero of=/testvol/sushi bs=1M count=1K
1024+0 records in
1024+0 records out
1073741824 bytes (1.1 GB) copied, 4.07493 s, 263 MB/s
$ oc rsh dc/sleep ls -l /testvol
-rw-r--r--. 1 1000200000 1000200000 1073741824 Dec 22 04:40 sushi
$ sudo find /var/lib/origin -name sushi
/var/lib/origin/openshift.local.volumes/pods/efe5f789-e6d4-11e7-9665-001a4a40dc83/volumes/kubernetes.io~empty-dir/sleep-volume-1/sushi
$ sudo du -hs /var/lib/docker /var/lib/origin
8.6G    /var/lib/docker
9.8G    /var/lib/origin

EmptyDirの定義を消してみます。EmptyDirではなくコンテナ内のディスクに書いているので、今度は/var/lib/dockerに1GBのsushiが置かれました。

$ oc volume dc/sleep --remove --confirm
deploymentconfig "sleep" updated
$ oc volume dc/sleep
deploymentconfigs/sleep
$ oc rsh dc/sleep dd if=/dev/zero of=/testvol/sushi bs=1M count=1K
$ oc rsh dc/sleep ls -l /testvol
total 1048576
-rw-r--r--. 1 1000200000 1000200000 1073741824 Dec 22 04:52 sushi
$ sudo find /var/lib/docker -name sushi
/var/lib/docker/volumes/46ddab197dfdb73aeea844898d903b6ecfe487e8abbda4724639f77e57f20836/_data/sushi
$ sudo du -hs /var/lib/docker /var/lib/origin
9.6G    /var/lib/docker
8.8G    /var/lib/origin

また、コンテナのログもログドライバがjson-fileにしろjournaldにしろ、コンテナホスト /var のディスク領域を消費します。数十のコンテナアプリケーションがログを出力することになるので、ログの量も大量です。

コンテナはこのようにコンテナホストの/var以下のディスク領域をとにかく大量に消費するため、ディスクフル障害となりやすいので注意してください。コンテナのこれらのディスク利用量を制限する簡単な方法は今のところありません1し、コンテナがどのくらいのデータやログを出力するの事前予測も簡単ではありません。ノードリソースにもよりますが、/varには200GB+程度のディスクを割りあてるなど、余裕を持ったディスク割り当てにしておくというのが今のところ最も有効な障害の予防手段となります。


  1. OpenShiftにはvolumeConfig.localQuota.perFsGroupxfs_quotaで制限をかける仕組みがあります

OpenShiftでUID指定されているコンテナをデプロイする

OpenShift 全部俺 Advent Calendar 2017

OpenShift上で動作するコンテナにはOpenShift側で採番されたUIDが割り当てられて、そのUIDで動作します。そのため、rootや特定のUIDでファイルにアクセスしたりすることを期待しているコンテナは動作しません。

とはいえ、DockerfileでUSER指定されているふつうのコンテナを動かしたいときもあります。そんなときにはSCCのnonrootを利用します。

https://docs.openshift.org/3.11/admin_guide/manage_scc.html

このOpenShiftのSCC (Security Context Constraints)はKubernetesPSP (Pod Security Policies)のベースとなっています。何度見てもプレイステーションポータブルって読んでしまいます。

https://kubernetes.io/docs/concepts/policy/pod-security-policy/

sccのデフォルトは以下のようになっています。

$ oc get scc
NAME               PRIV      CAPS      SELINUX     RUNASUSER          FSGROUP     SUPGROUP    PRIORITY   READONLYROOTFS   VOLUMES
anyuid             false     []        MustRunAs   RunAsAny           RunAsAny    RunAsAny    10         false            [configMap downwardAPI emptyDir persistentVolumeClaim projected secret]
hostaccess         false     []        MustRunAs   MustRunAsRange     MustRunAs   RunAsAny    <none>     false            [configMap downwardAPI emptyDir hostPath persistentVolumeClaim projected secret]
hostmount-anyuid   false     []        MustRunAs   RunAsAny           RunAsAny    RunAsAny    <none>     false            [configMap downwardAPI emptyDir hostPath nfs persistentVolumeClaim projected secret]
hostnetwork        false     []        MustRunAs   MustRunAsRange     MustRunAs   MustRunAs   <none>     false            [configMap downwardAPI emptyDir persistentVolumeClaim projected secret]
nonroot            false     []        MustRunAs   MustRunAsNonRoot   RunAsAny    RunAsAny    <none>     false            [configMap downwardAPI emptyDir persistentVolumeClaim projected secret]
privileged         true      [*]       RunAsAny    RunAsAny           RunAsAny    RunAsAny    <none>     false            [*]
restricted         false     []        MustRunAs   MustRunAsRange     MustRunAs   RunAsAny    <none>     false            [configMap downwardAPI emptyDir persistentVolumeClaim projected secret]

sccには適用順があります。nonrootにはpriorityがセットされていないので、まずpriorityをセットする必要があります。

https://docs.openshift.org/3.11/architecture/additional_concepts/authorization.html#scc-prioritization

$ oc patch scc nonroot -p "priority: 2"

これで準備はOKです。まずはUSERを指定したコンテナをデプロイして、割り当てられたUIDで動作していることを確認します。

$ cat <<EOF | oc new-build --dockerfile=- --to=sleep
FROM registry.access.redhat.com/rhel
RUN useradd -u 1001 -g 0 -d / -s /sbin/nologin default
USER 1001
CMD tail -f /dev/null
EOF
$ oc new-app sleep

scc, UIDの割り当てとランタイムのUIDを確認してみます。

$ oc describe project test-nonroot | grep scc
            openshift.io/sa.scc.mcs=s0:c14,c4
            openshift.io/sa.scc.supplemental-groups=1000190000/10000
            openshift.io/sa.scc.uid-range=1000190000/10000
$ oc describe pod sleep-1-5hgvn | grep scc
        openshift.io/scc=restricted
$ oc rsh dc/sleep id
uid=1000190000 gid=0(root) groups=0(root),1000190000

USER指定の1001ではないUIDとなっています。

scc nonrootをdefaultサービスアカウントに与えて再デプロイしてみます。

$ oc adm policy add-scc-to-user nonroot -z default
$ oc rollout latest sleep
$ oc describe pod sleep-2-hhzfd | grep scc
        openshift.io/scc=nonroot
$ oc rsh dc/sleep id
uid=1001(default) gid=0(root) groups=0(root)

きちんとUIDが変更されていますね。

UIDの変更方法を紹介しましたが、基本的にはコンテナイメージガイドラインに沿って、割り当てられたUIDで動作するようコンテナを作成することが推奨されます。

https://docs.openshift.org/3.11/creating_images/guidelines.html

sccの適用は基本的にはセキュリティリスクとなりますので、利用は最小限となるようにしましょう。nonrootは比較的安全なsccですが、NFS PVへのアクセスなど、UIDがキーとなるセキュリティコントロールがあるのでやはりデフォルトのrestrictedより劣ります。また、rootが許可されるprivilegedやanyuidはセキュリティ的には最低ですので、これらが適用されるコンテナのイメージは必ず信頼されるもののみとしてください。自社できちんとメンテナンスしているもの、Red Hatなどのベンダーから正式に提供されているもの、などです。