使用 runC 容器啟動應用程式

安全性警告

重要

此功能不穩定。由於此功能持續演進,API 可能無法維護,功能也可能變更或移除。

啟用此功能並在叢集中執行 runC 容器會產生安全性影響。由於 runC 與許多強大的核心功能整合,因此管理員在啟用此功能前,必須了解 runC 安全性。

概觀

runC 是一個 CLI 工具,用於根據開放容器計畫 (OCI) 規範產生和執行容器。runC 最初是從原始 Docker 基礎架構中衍生出來的。runC 搭配透過 squashFS 映像建立的 rootfs 掛載點,讓使用者可以將應用程式與其偏好的執行環境打包在一起,並在目標機器上執行。如需 OCI 的更多資訊,請參閱其網站

Linux 容器執行器 (LCE) 允許 YARN NodeManager 啟動 YARN 容器,以便直接在主機上、Docker 容器內,以及現在在 runC 容器內執行。要求資源的應用程式可以針對每個容器指定執行方式。LCE 也提供增強的安全性,而且在部署安全叢集時是必要的。當 LCE 啟動 YARN 容器在 runC 容器中執行時,應用程式可以指定要使用的 runC 映像。這些 runC 映像可以從 Docker 映像建置。

runC 容器提供一個自訂執行環境,應用程式的程式碼在其中執行,與 NodeManager 和其他應用程式的執行環境隔離。這些容器可以包含應用程式需要的特殊函式庫,而且它們可以有不同版本的原生工具和函式庫,包括 Perl、Python 和 Java。runC 容器甚至可以執行與 NodeManager 上執行的不同的 Linux 版本。

YARN 的 runC 提供一致性(所有 YARN 容器都將有相同的軟體環境)和隔離性(不會干擾實體機器上安裝的任何內容)。

LCE 中的 runC 支援仍在演進中。若要追蹤進度並查看 runC 設計文件,請查看 YARN-9014,這是 runC 支援改善的總括性 JIRA。

叢集設定

LCE 要求 container-executor 二進位檔由 root:hadoop 擁有,並具有 6050 權限。為了啟動 runC 容器,必須在將啟動 runC 容器的所有 NodeManager 主機上安裝 runC。

以下屬性應設定在 yarn-site.xml 中

<configuration>
  <property>
    <name>yarn.nodemanager.container-executor.class</name>
    <value>org.apache.hadoop.yarn.server.nodemanager.LinuxContainerExecutor</value>
    <description>
      This is the container executor setting that ensures that all applications
      are started with the LinuxContainerExecutor.
    </description>
  </property>

  <property>
    <name>yarn.nodemanager.linux-container-executor.group</name>
    <value>hadoop</value>
    <description>
      The POSIX group of the NodeManager. It should match the setting in
      "container-executor.cfg". This configuration is required for validating
      the secure access of the container-executor binary.
    </description>
  </property>

  <property>
    <name>yarn.nodemanager.linux-container-executor.nonsecure-mode.limit-users</name>
    <value>false</value>
    <description>
      Whether all applications should be run as the NodeManager process' owner.
      When false, applications are launched instead as the application owner.
    </description>
  </property>

  <property>
    <name>yarn.nodemanager.runtime.linux.allowed-runtimes</name>
    <value>default,runc</value>
    <description>
      Comma separated list of runtimes that are allowed when using
      LinuxContainerExecutor. The allowed values are default, docker, runc, and
      javasandbox.
    </description>
  </property>

  <property>
    <name>yarn.nodemanager.runtime.linux.type</name>
    <value></value>
    <description>
      Optional. Sets the default container runtime to use.
    </description>
  </property>

  <property>
    <description>The runC image tag to manifest plugin
      class to be used.</description>
    <name>yarn.nodemanager.runtime.linux.runc.image-tag-to-manifest-plugin</name>
    <value>org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.runc.ImageTagToManifestPlugin</value>
  </property>

  <property>
    <description>The runC manifest to resources plugin class to
      be used.</description>
    <name>yarn.nodemanager.runtime.linux.runc.manifest-to-resources-plugin</name>
    <value>org.apache.hadoop.yarn.server.nodemanager.containermanager.linux.runtime.runc.HdfsManifestToResourcesPlugin</value>
  </property>

  <property>
    <description>The HDFS location under which the oci image manifests, layers,
      and configs directories exist.</description>
    <name>yarn.nodemanager.runtime.linux.runc.image-toplevel-dir</name>
    <value>/runc-root</value>
  </property>

  <property>
    <description>Target count of layer mounts that we should keep on disk
      at one time.</description>
    <name>yarn.nodemanager.runtime.linux.runc.layer-mounts-to-keep</name>
    <value>100</value>
  </property>

  <property>
    <description>The interval in seconds between executions of
      reaping layer mounts.</description>
    <name>yarn.nodemanager.runtime.linux.runc.layer-mounts-interval-secs</name>
    <value>600</value>
  </property>

  <property>
    <description>Image to be used if no other image is specified.</description>
    <name>yarn.nodemanager.runtime.linux.runc.image-name</name>
    <value></value>
  </property>

  <property>
    <description>Allow or disallow privileged containers.</description>
    <name>yarn.nodemanager.runtime.linux.runc.privileged-containers.allowed</name>
    <value>false</value>
  </property>

  <property>
    <description>The set of networks allowed when launching containers
      using the RuncContainerRuntime.</description>
    <name>yarn.nodemanager.runtime.linux.runc.allowed-container-networks</name>
    <value>host,none,bridge</value>
  </property>

  <property>
    <description>The set of runtimes allowed when launching containers
      using the RuncContainerRuntime.</description>
    <name>yarn.nodemanager.runtime.linux.runc.allowed-container-runtimes</name>
    <value>runc</value>
  </property>

  <property>
    <description>ACL list for users allowed to run privileged
      containers.</description>
    <name>yarn.nodemanager.runtime.linux.runc.privileged-containers.acl</name>
    <value></value>
  </property>

  <property>
    <description>Allow host pid namespace for runC containers.
      Use with care.</description>
    <name>yarn.nodemanager.runtime.linux.runc.host-pid-namespace.allowed</name>
    <value>false</value>
  </property>

  <property>
    <description>The default list of read-only mounts to be bind-mounted
      into all runC containers that use RuncContainerRuntime.</description>
    <name>yarn.nodemanager.runtime.linux.runc.default-ro-mounts</name>
    <value></value>
  </property>

  <property>
    <description>The default list of read-write mounts to be bind-mounted
      into all runC containers that use RuncContainerRuntime.</description>
    <name>yarn.nodemanager.runtime.linux.runc.default-rw-mounts</name>
    <value></value>
  </property>

  <property>
    <description>Path to the seccomp profile to use with runC
      containers</description>
    <name>yarn.nodemanager.runtime.linux.runc.seccomp-profile</name>
    <value></value>
  </property>

  <property>
    <description>The HDFS location where the runC image tag to hash
      file exists.</description>
    <name>yarn.nodemanager.runtime.linux.runc.image-tag-to-manifest-plugin.hdfs-hash-file</name>
    <value>/runc-root/image-tag-to-hash</value>
  </property>

  <property>
    <description>The local file system location where the runC image tag
      to hash file exists.</description>
    <name>yarn.nodemanager.runtime.linux.runc.image-tag-to-manifest-plugin.local-hash-file</name>
    <value></value>
  </property>

  <property>
    <description>The interval in seconds between refreshing the hdfs image tag
      to hash cache.</description>
    <name>yarn.nodemanager.runtime.linux.runc.image-tag-to-manifest-plugin.cache-refresh-interval-secs</name>
    <value>60</value>
  </property>

  <property>
    <description>The number of manifests to cache in the image tag
      to hash cache.</description>
    <name>yarn.nodemanager.runtime.linux.runc.image-tag-to-manifest-plugin.num-manifests-to-cache</name>
    <value>10</value>
  </property>

  <property>
    <description>The timeout value in seconds for the values in
      the stat cache.</description>
    <name>yarn.nodemanager.runtime.linux.runc.hdfs-manifest-to-resources-plugin.stat-cache-timeout-interval-secs</name>
    <value>360</value>
  </property>

  <property>
    <description>The size of the stat cache which stores stats of the
      layers and config.</description>
    <name>yarn.nodemanager.runtime.linux.runc.hdfs-manifest-to-resources-plugin.stat-cache-size</name>
    <value>500</value>
  </property>
</configuration>

此外,必須存在 container-executor.cfg 檔案,並包含容器執行器的設定。該檔案必須由 root 擁有,權限為 0400。檔案格式是標準 Java 屬性檔格式,例如

`key=value`

需要以下屬性來啟用 runC 支援

設定名稱 說明
yarn.nodemanager.linux-container-executor.group NodeManager 的 Unix 群組。它應與 yarn-site.xml 檔案中的 yarn.nodemanager.linux-container-executor.group 相符。

container-executor.cfg 必須包含一個區段來確定允許的容器功能。它包含以下屬性

設定名稱 說明
module.enabled 必須為「true」或「false」才能分別啟用或停用啟動 runC 容器。預設值為 0。
runc.binary 用於啟動 runC 容器的二進位檔。預設為 /usr/bin/runc。
runc.run-root 將放置所有執行時期掛載和覆疊掛載的目錄。
runc.allowed.ro-mounts 允許容器以唯讀模式掛載的目錄,以逗號分隔。預設不允許掛載任何目錄。
runc.allowed.rw-mounts 允許容器以讀寫模式掛載的目錄,以逗號分隔。預設不允許掛載任何目錄。

請注意,如果您希望執行需要存取 YARN 本機目錄的 runC 容器,您必須將它們新增到 runc.allowed.rw-mounts 清單中。

此外,不允許容器以讀寫模式掛載 container-executor.cfg 目錄的任何父目錄。

下列屬性為選用

設定名稱 說明
min.user.id 允許啟動應用程式的最小 UID。預設為沒有最小值
banned.users 不應允許啟動應用程式的使用者名稱清單,以逗號分隔。預設設定為:yarn、mapred、hdfs 和 bin。
allowed.system.users 即使其 UID 低於設定的最小值,也應允許啟動應用程式的使用者名稱清單,以逗號分隔。如果使用者出現在 allowed.system.users 和 banned.users 中,將視為已禁止該使用者。
feature.tc.enabled 必須為「true」或「false」。「false」表示停用流量控制命令。「true」表示允許流量控制命令。
feature.yarn.sysfs.enabled 必須為「true」或「false」。詳細資訊請參閱 YARN sysfs 支援。預設設定為停用。

允許啟動 runC 容器的 container-executor.cfg 的一部分如下

yarn.nodemanager.linux-container-executor.group=yarn
[runc]
  module.enabled=true
  runc.binary=/usr/bin/runc
  runc.run-root=/run/yarn-container-executor
  runc.allowed.ro-mounts=/sys/fs/cgroup
  runc.allowed.rw-mounts=/var/hadoop/yarn/local-dir,/var/hadoop/yarn/log-dir

影像需求

runC 容器在衍生自 Docker 影像的影像內執行。Docker 影像會轉換成一組 squashFS 檔案影像,並上傳到 HDFS。為了與 YARN 搭配使用,這些 Docker 影像有幾個需求。

  1. runC 容器將明確以應用程式擁有者身分啟動,作為容器使用者。如果應用程式擁有者不是 Docker 影像中的有效使用者,應用程式將會失敗。容器使用者由使用者的 UID 指定。如果使用者的 UID 在 NodeManager 主機和 Docker 影像之間不同,容器可能會以錯誤的使用者身分啟動,或可能因為 UID 不存在而無法啟動。有關更多詳細資訊,請參閱runC 容器中的使用者管理部分。

  2. Docker 影像必須具備應用程式執行所需的所有內容。對於 Hadoop(MapReduce 或 Spark),Docker 影像必須包含 JRE 和 Hadoop 函式庫,並設定必要的環境變數:JAVA_HOME、HADOOP_COMMON_PATH、HADOOP_HDFS_HOME、HADOOP_MAPRED_HOME、HADOOP_YARN_HOME 和 HADOOP_CONF_DIR。請注意,Docker 影像中可用的 Java 和 Hadoop 元件版本必須與叢集和用於相同工作其他工作任務的其他 Docker 影像中安裝的版本相容。否則,在 runC 容器中啟動的 Hadoop 元件可能無法與外部 Hadoop 元件通訊。

  3. /bin/bash 必須在影像中可用。這通常是正確的,但微小的 Docker 影像(例如使用 busybox 進行 shell 命令的影像)可能未安裝 bash。在此情況下,會顯示下列錯誤

    Container id: container_1561638268473_0015_01_000002
    Exit code: 7
    Exception message: Launch container failed
    Shell error output: /usr/bin/docker-current: Error response from daemon: oci runtime error: container_linux.go:235: starting container process caused "exec: \"bash\": executable file not found in $PATH".
    Shell output: main : command provided 4
    
  4. find 命令也必須在影像中可用。沒有 find 會導致此錯誤

    Container exited with a non-zero exit code 127. Error file: prelaunch.err.
    Last 4096 bytes of prelaunch.err :
    /tmp/hadoop-systest/nm-local-dir/usercache/hadoopuser/appcache/application_1561638268473_0017/container_1561638268473_0017_01_000002/launch_container.sh: line 44: find: command not found
    

如果 Docker 影像已設定進入點,則會以容器的啟動命令作為其引數執行進入點。

從 Docker 影像衍生的 runC 影像會本機化到 runC 容器將執行的主機上,就像任何其他本機化資源一樣。MapReduce 和 Spark 都假設需要花費超過 10 分鐘來回報進度的任務已經停止,因此如果本機化花費太長的時間,指定大型影像可能會導致應用程式失敗。

將 Docker 影像轉換為 runC 影像

每個 Docker 影像都包含 3 個部分:- 建立檔案系統的一組層。- 包含與影像環境相關資訊的設定檔。- 描述該影像需要哪些層和設定檔的清單。

這 3 個部分結合在一起,建立相容於開放容器計畫 (OCI) 的影像。runC 在相容於 OCI 的容器上執行,但略有不同。runC 執行時期使用的每個層都會壓縮成 squashFS 檔案系統。squashFS 層會連同設定檔和清單上傳到 HDFS,以及描述影像標籤和與該影像關聯的清單之間對應關係的 image-tag-to-hash 對應檔。設定所有這些是一個複雜且繁瑣的過程。在 YARN-9564 上有一個修補程式,其中包含一個非官方的 Python 腳本,稱為 docker-to-squash.py,以協助轉換過程。此工具會輸入 Docker 影像,將其所有層轉換為 squashFS 檔案系統,並將 squashFS 層、設定檔和清單上傳到 runc-root 下方的 HDFS。它還會建立或更新 image-tag-to-hash 對應檔。以下是使用腳本上傳名為 centos:latest 的影像到 HDFS 的範例呼叫,runC 影像名稱為 centos

[user@foobar sbin]$ pwd
/home/user/hadoop/hadoop-dist/target/hadoop-3.3.0-SNAPSHOT/sbin
[user@foobar sbin]$ ls
distribute-exclude.sh  hadoop-daemons.sh        refresh-namenodes.sh  start-dfs.cmd        start-yarn.sh     stop-dfs.cmd        stop-yarn.sh
docker_to_squash.py    httpfs.sh                start-all.cmd         start-dfs.sh         stop-all.cmd      stop-dfs.sh         workers.sh
FederationStateStore   kms.sh                   start-all.sh          start-secure-dns.sh  stop-all.sh       stop-secure-dns.sh  yarn-daemon.sh
hadoop-daemon.sh       mr-jobhistory-daemon.sh  start-balancer.sh     start-yarn.cmd       stop-balancer.sh  stop-yarn.cmd       yarn-daemons.sh
[user@foobar sbin]$ hadoop fs -ls /
Found 3 items
drwxrwx---   - user supergroup          0 2019-08-07 19:35 /home
drwx------   - user supergroup          0 2019-08-07 19:35 /tmp
drwx------   - user supergroup          0 2019-08-07 19:35 /user
[user@foobar sbin]$ ./docker_to_squash.py --working-dir /tmp --log=DEBUG pull-build-push-update centos:latest,centos
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'version']
DEBUG: command: ['skopeo', '-v']
DEBUG: command: ['mksquashfs', '-version']
DEBUG: args: Namespace(LOG_LEVEL='DEBUG', check_magic_file=False, force=False, func=<function pull_build_push_update at 0x7fe6974cd9b0>, hadoop_prefix='/hadoop-2.8.6-SNAPSHOT', hdfs_root='/runc-root', image_tag_to_hash='image-tag-to-hash', images_and_tags=['centos:latest,centos'], magic_file='etc/dockerfile-version', pull_format='docker', replication=1, skopeo_format='dir', sub_command='pull-build-push-update', working_dir='/tmp')
DEBUG: extra: []
DEBUG: image-tag-to-hash: image-tag-to-hash
DEBUG: LOG_LEVEL: DEBUG
DEBUG: HADOOP_BIN_DIR: /hadoop-2.8.6-SNAPSHOT/bin
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root']
ls: `/runc-root': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-mkdir', '/runc-root']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-chmod', '755', '/runc-root']
DEBUG: Setting up squashfs dirs: ['/runc-root/layers', '/runc-root/config', '/runc-root/manifests']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/layers']
ls: `/runc-root/layers': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-mkdir', '/runc-root/layers']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/layers']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-chmod', '755', '/runc-root/layers']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/config']
ls: `/runc-root/config': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-mkdir', '/runc-root/config']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/config']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-chmod', '755', '/runc-root/config']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/manifests']
ls: `/runc-root/manifests': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-mkdir', '/runc-root/manifests']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/manifests']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-chmod', '755', '/runc-root/manifests']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/image-tag-to-hash']
ls: `/runc-root/image-tag-to-hash': No such file or directory
INFO: Working on image centos:latest with tags ['centos']
DEBUG: command: ['skopeo', 'inspect', '--raw', 'docker://centos:latest']
DEBUG: skopeo inspect --raw returned a list of manifests
DEBUG: amd64 manifest sha is: sha256:ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66
DEBUG: command: ['skopeo', 'inspect', '--raw', u'docker://centos@sha256:ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66']
INFO: manifest: {u'layers': [{u'mediaType': u'application/vnd.docker.image.rootfs.diff.tar.gzip', u'digest': u'sha256:8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df', u'size': 75403831}], u'schemaVersion': 2, u'config': {u'mediaType': u'application/vnd.docker.container.image.v1+json', u'digest': u'sha256:9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1', u'size': 2182}, u'mediaType': u'application/vnd.docker.distribution.manifest.v2+json'}
INFO: manifest: {u'layers': [{u'mediaType': u'application/vnd.docker.image.rootfs.diff.tar.gzip', u'digest': u'sha256:8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df', u'size': 75403831}], u'schemaVersion': 2, u'config': {u'mediaType': u'application/vnd.docker.container.image.v1+json', u'digest': u'sha256:9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1', u'size': 2182}, u'mediaType': u'application/vnd.docker.distribution.manifest.v2+json'}
DEBUG: Layers: [u'8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df']
DEBUG: Config: 9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1
DEBUG: hash_to_tags is null. Not removing tag centos
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66']
ls: `/runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', u'/runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1']
ls: `/runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', u'/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh']
ls: `/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh': No such file or directory
DEBUG: skopeo_dir: /tmp/docker-to-squash/centos:latest
INFO: Pulling image: centos:latest
DEBUG: command: ['skopeo', 'copy', 'docker://centos:latest', 'dir:/tmp/docker-to-squash/centos:latest']
INFO: Squashifying and uploading layer: 8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', u'/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh']
ls: `/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh': No such file or directory
DEBUG: command: ['sudo', 'tar', '-C', u'/tmp/docker-to-squash/expand_archive_8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df', '--xattrs', "--xattrs-include='*'", '-xzf', u'/tmp/docker-to-squash/centos:latest/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df']
DEBUG: command: ['sudo', 'find', u'/tmp/docker-to-squash/expand_archive_8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df', '-name', '.wh.*']
DEBUG: command: ['sudo', 'mksquashfs', u'/tmp/docker-to-squash/expand_archive_8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df', u'/tmp/docker-to-squash/centos:latest/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh']
DEBUG: command: ['sudo', 'rm', '-rf', u'/tmp/docker-to-squash/expand_archive_8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', u'/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh']
ls: `/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-put', u'/tmp/docker-to-squash/centos:latest/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh', u'/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-setrep', '1', u'/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-chmod', '444', u'/runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh']
INFO: Uploaded file /runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh with replication 1 and permissions 444
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', u'/runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1']
ls: `/runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-put', u'/tmp/docker-to-squash/centos:latest/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1', u'/runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-setrep', '1', u'/runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-chmod', '444', u'/runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1']
INFO: Uploaded file /runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1 with replication 1 and permissions 444
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-ls', '/runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66']
ls: `/runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66': No such file or directory
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-put', '/tmp/docker-to-squash/centos:latest/manifest.json', '/runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-setrep', '1', '/runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-chmod', '444', '/runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66']
INFO: Uploaded file /runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66 with replication 1 and permissions 444
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-put', '-f', '/tmp/docker-to-squash/image-tag-to-hash', '/runc-root/image-tag-to-hash']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-setrep', '1', '/runc-root/image-tag-to-hash']
DEBUG: command: ['/hadoop-2.8.6-SNAPSHOT/bin/hadoop', 'fs', '-chmod', '444', '/runc-root/image-tag-to-hash']
DEBUG: command: ['sudo', 'rm', '-rf', '/tmp/docker-to-squash']
[user@foobar sbin]$ hadoop fs -ls /
Found 4 items
drwxrwx---   - user supergroup          0 2019-08-07 19:35 /home
drwxr-xr-x   - user supergroup          0 2019-08-08 22:38 /runc-root
drwx------   - user supergroup          0 2019-08-07 19:35 /tmp
drwx------   - user supergroup          0 2019-08-07 19:35 /user
[user@foobar sbin]$ hadoop fs -ls /runc-root/*
Found 1 items
-r--r--r--   1 user supergroup       2182 2019-08-08 22:38 /runc-root/config/9f38484d220fa527b1fb19747638497179500a1bed8bf0498eb788229229e6e1
-r--r--r--   1 user supergroup         86 2019-08-08 22:38 /runc-root/image-tag-to-hash
Found 1 items
-r--r--r--   1 user supergroup   73625600 2019-08-08 22:38 /runc-root/layers/8ba884070f611d31cb2c42eddb691319dc9facf5e0ec67672fcfa135181ab3df.sqsh
Found 1 items
-r--r--r--   1 user supergroup        529 2019-08-08 22:38 /runc-root/manifests/ca58fe458b8d94bc6e3072f1cfbd334855858e05e1fd633aa07cf7f82b048e66

應用程式提交

在嘗試啟動 runC 容器之前,請確定 LCE 設定適用於要求一般 YARN 容器的應用程式。如果在啟用 LCE 之後,一個或多個 NodeManager 無法啟動,最可能的原因是容器執行檔的所有權和/或權限不正確。查看記錄以確認。

若要在 runC 容器中執行應用程式,請在應用程式的環境中設定下列環境變數

環境變數名稱 說明
YARN_CONTAINER_RUNTIME_TYPE 決定應用程式是否會在 runC 容器中啟動。如果值為「runc」,應用程式會在 runC 容器中啟動。否則,將使用一般程序樹容器。
YARN_CONTAINER_RUNTIME_RUNC_IMAGE 命名將用於啟動 runC 容器的映像。
YARN_CONTAINER_RUNTIME_RUNC_CONTAINER_HOSTNAME 設定 runC 容器要使用的主機名稱。
YARN_CONTAINER_RUNTIME_RUNC_MOUNTS 將額外的磁碟區掛載點新增至 runC 容器。環境變數的值應為掛載點的逗號分隔清單。所有此類掛載點都必須以「來源:目標:模式」提供,而模式必須為「ro」(唯讀)或「rw」(讀寫),以指定所要求的存取類型。如果未指定任一模式,將假設為讀寫。請求的掛載點將根據 container-executor.cfg 中針對 runc.allowed.ro-mounts 和 runc.allowed.rw-mounts 設定的值,由 container-executor 驗證。

前兩個是必要的。其餘部分可視需要設定。雖然透過環境變數控制容器類型並非理想,但它允許不了解 YARN 的 runC 支援的應用程式(例如 MapReduce 和 Spark)透過支援設定應用程式環境來利用它。

注意 如果您將任何內容掛載至容器中的 /tmp 或 /var/tmp,執行時期將無法運作。

一旦應用程式已提交至在 runC 容器中啟動,應用程式將與任何其他 YARN 應用程式完全相同。記錄將彙總並儲存在相關的歷程伺服器中。應用程式生命週期將與非 runC 應用程式相同。

使用 runC 繫結掛載的磁碟區

警告 啟用此功能時應小心。不建議存取目錄(包括但不限於 /、/etc、/run 或 /home),否則可能會導致容器對主機產生負面影響或洩漏敏感資訊。警告

通常需要 runC 容器中的主機檔案和目錄,而 runC 會透過掛載點將其提供給容器。範例包括在地化資源、Apache Hadoop 二進位檔和 socket。

若要將任何內容掛載至容器,必須設定下列事項。

  • 管理員必須透過將 runc.allowed.ro-mountsrunc.allowed.rw-mounts 設定為允許掛載的父目錄清單,在 container-executor.cfg 中定義磁碟區白名單。

管理員提供的白名單定義為允許掛載至容器的目錄的逗號分隔清單。使用者提供的來源目錄必須與指定的目錄相符或為其子目錄。

使用者提供的掛載清單定義為逗號分隔清單,格式為 來源:目標來源:目標:模式。來源是主機上的檔案或目錄。目標是容器中來源將繫結掛載到的路徑。模式定義使用者預期的掛載模式,可以是 ro(唯讀)或 rw(讀寫)。如果未指定,將假設為 rw。模式也可以包含繫結傳播選項(shared、rshared、slave、rslave、private 或 rprivate)。在這種情況下,模式應為 選項、rw+選項 或 ro+選項 的格式。

下列範例說明如何使用此功能將通常需要的 /sys/fs/cgroup 目錄掛載至在 YARN 上執行的容器中。

管理員在 container-executor.cfg 中將 runc.allowed.ro-mounts 設定為「/sys/fs/cgroup」。應用程式現在可以要求將「/sys/fs/cgroup」從主機以唯讀模式掛載到容器中。

Nodemanager 有選項可以設定預設的唯讀或唯寫掛載清單,透過 yarn-site.xml 中的 yarn.nodemanager.runtime.linux.runc.default-ro-mountyarn.nodemanager.runtime.linux.runc.default-rw-mounts 將其新增到容器中。在此範例中,yarn.nodemanager.runtime.linux.runc.default-ro-mounts 會設定為 /sys/fs/cgroup:/sys/fs/cgroup

runC 容器中的使用者管理

YARN 的 runC 容器支援使用 NodeManager 主機上定義的使用者 uid:gid 身分啟動容器程序。NodeManager 主機和容器中的使用者和群組名稱不符會導致權限問題、容器啟動失敗,甚至安全漏洞。集中管理主機和容器的使用者和群組可以大幅降低這些風險。在 YARN 上執行容器化應用程式時,有必要了解哪個 uid:gid 配對會用於啟動容器的程序。

以下為 uid:gid 配對含義的範例。預設情況下,在非安全模式中,YARN 會以使用者 nobody 身分啟動程序(請參閱 在 YARN 中使用 CGroup 底部的表格,了解在非安全模式中如何決定以使用者身分執行)。在 CentOS 系統上,nobody 使用者的 uid 為 99nobody 群組為 99。因此,YARN 會以 uid 99 和 gid 99 呼叫 runC。如果 nobody 使用者在容器中沒有 uid 99,啟動可能會失敗或產生意外結果。

有許多方法可以解決使用者和群組管理問題。預設情況下,runC 會針對容器中的 /etc/passwd(和 /etc/shadow)對使用者進行驗證。使用 runC 映像中提供的預設 /etc/passwd 不太可能包含適當的使用者項目,而且會導致啟動失敗。強烈建議集中管理使用者和群組。以下概述了幾種使用者和群組管理方法。

靜態使用者管理

管理使用者和群組最基本的方法是在 runC 映像中修改使用者和群組。此方法僅適用於非安全模式,其中所有容器程序都將以單一已知使用者(例如 nobody)啟動。在這種情況下,唯一的需求是 nobody 使用者和群組的 uid:gid 配對必須在主機和容器之間相符。在 CentOS 系統上,這表示容器中的 nobody 使用者需要 UID 99,容器中的 nobody 群組需要 GID 99

變更 UID 和 GID 的一種方法是利用 usermodgroupmod。以下設定 nobody 使用者/群組的正確 UID 和 GID。

usermod -u 99 nobody
groupmod -g 99 nobody

考量到無法新增使用者,此方法不建議用於測試以外的情況。

繫結掛載

當組織已在每個系統上實施自動化機制來建立本機使用者時,繫結掛載 /etc/passwd 和 /etc/group 到容器中可能是適當的替代方法,而非直接修改容器映像。若要啟用繫結掛載 /etc/passwd 和 /etc/group 的功能,請更新 container-executor.cfg 中的 runc.allowed.ro-mounts 以包含這些路徑。要在 runC 上執行此操作,「yarn.nodemanager.runtime.linux.runc.default-ro-mounts」需要包含 /etc/passwd:/etc/passwd:ro/etc/group:/etc/group:ro

此繫結掛載方法有幾個需要考量的挑戰。

  1. 映像中定義的任何使用者和群組都將被主機的使用者和群組覆寫
  2. 容器啟動後無法新增使用者和群組,因為 /etc/passwd 和 /etc/group 在容器中是不可變的。請勿將這些內容掛載為可讀寫,因為這可能會使主機無法運作。

考量到無法修改正在執行的容器,此方法不建議用於測試以外的情況。

SSSD

允許集中管理使用者和群組的另一種方法是 SSSD。系統安全服務守護程式 (SSSD) 提供對不同身分和驗證提供者的存取,例如 LDAP 或 Active Directory。

Linux 驗證的傳統架構如下

application -> libpam -> pam_authenticate -> pam_unix.so -> /etc/passwd

如果我們使用 SSSD 進行使用者查詢,則會變成

application -> libpam -> pam_authenticate -> pam_sss.so -> SSSD -> pam_unix.so -> /etc/passwd

我們可以將 SSSD 通訊的 UNIX socket 繫結掛載到容器中。這將允許 SSSD 客户端程式庫對執行於主機上的 SSSD 進行驗證。因此,使用者資訊不需要存在於 docker 映像的 /etc/passwd 中,而將由 SSSD 提供服務。

主機和容器的分步設定

  1. 主機設定
  • 安裝套件
    # yum -y install sssd-common sssd-proxy
    
  • 為容器建立 PAM 服務。
    # cat /etc/pam.d/sss_proxy
    auth required pam_unix.so
    account required pam_unix.so
    password required pam_unix.so
    session required pam_unix.so
    
  • 建立 SSSD 設定檔 /etc/sssd/sssd.conf 請注意,權限必須為 0600,且檔案必須由 root:root 擁有。
    # cat /etc/sssd/sssd/conf
    [sssd]
    services = nss,pam
    config_file_version = 2
    domains = proxy
    [nss]
    [pam]
    [domain/proxy]
    id_provider = proxy
    proxy_lib_name = files
    proxy_pam_target = sss_proxy
    
  • 啟動 sssd
    # systemctl start sssd
    
  • 驗證是否可以使用 sssd 擷取使用者
    # getent passwd -s sss localuser
    
  1. 容器設定

由於 SSSD UNIX socket 位於其中,因此將 /var/lib/sss/pipes 目錄從主機繫結掛載到容器中非常重要。

-v /var/lib/sss/pipes:/var/lib/sss/pipes:rw
  1. 容器設定

以下所有步驟都應在容器本身上執行。

  • 僅安裝 sss 客户端程式庫

    # yum -y install sssd-client
    
  • 確保在中為 passwd 和群組資料庫設定 sss

    /etc/nsswitch.conf
    
  • 設定應用程式用於呼叫 SSSD 的 PAM 服務

    # cat /etc/pam.d/system-auth
    #%PAM-1.0
    # This file is auto-generated.
    # User changes will be destroyed the next time authconfig is run.
    auth        required      pam_env.so
    auth        sufficient    pam_unix.so try_first_pass nullok
    auth        sufficient    pam_sss.so forward_pass
    auth        required      pam_deny.so
    
    account     required      pam_unix.so
    account     [default=bad success=ok user_unknown=ignore] pam_sss.so
    account     required      pam_permit.so
    
    password    requisite     pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
    password    sufficient    pam_unix.so try_first_pass use_authtok nullok sha512 shadow
    password    sufficient    pam_sss.so use_authtok
    password    required      pam_deny.so
    
    session     optional      pam_keyinit.so revoke
    session     required      pam_limits.so
    -session     optional      pam_systemd.so
    session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
    session     required      pam_unix.so
    session     optional      pam_sss.so
    
  • 儲存 docker 映像,並將 docker 映像用作應用程式的基礎映像。

  • 測試在 YARN 環境中啟動的 Docker 映像。

    $ id
    uid=5000(localuser) gid=5000(localuser) groups=5000(localuser),1337(hadoop)
    

範例:MapReduce

此範例假設 Hadoop 已安裝到 /usr/local/hadoop

在使用該映像執行之前,您還需要壓縮 Docker 映像並將其上傳到 HDFS。請參閱 將 Docker 映像轉換為 runC 映像,瞭解如何將 Docker 映像轉換為 runC 可使用的映像。在此範例中,我們假設您已完成一個名為 hadoop-image 的映像。

此外,container-executor.cfg 中的 runc.allowed.ro-mounts 已更新,包含目錄:/usr/local/hadoop,/etc/passwd,/etc/group

若要提交 pi 作業在 runC 容器中執行,請執行下列指令

  HADOOP_HOME=/usr/local/hadoop
  YARN_EXAMPLES_JAR=$HADOOP_HOME/share/hadoop/mapreduce/hadoop-mapreduce-examples-*.jar
  MOUNTS="$HADOOP_HOME:$HADOOP_HOME:ro,/etc/passwd:/etc/passwd:ro,/etc/group:/etc/group:ro"
  IMAGE_ID="hadoop-image"

  export YARN_CONTAINER_RUNTIME_TYPE=runc
  export YARN_CONTAINER_RUNTIME_RUNC_IMAGE=$IMAGE_ID
  export YARN_CONTAINER_RUNTIME_RUNC_MOUNTS=$MOUNTS

  yarn jar $YARN_EXAMPLES_JAR pi \
    -Dmapreduce.map.env.YARN_CONTAINER_RUNTIME_TYPE=runc \
    -Dmapreduce.map.env.YARN_CONTAINER_RUNTIME_RUNC_MOUNTS=$MOUNTS \
    -Dmapreduce.map.env.YARN_CONTAINER_RUNTIME_RUNC_IMAGE=$IMAGE_ID \
    -Dmapreduce.reduce.env.YARN_CONTAINER_RUNTIME_TYPE=runc \
    -Dmapreduce.reduce.env.YARN_CONTAINER_RUNTIME_RUNC_MOUNTS=$MOUNTS \
    -Dmapreduce.reduce.env.YARN_CONTAINER_RUNTIME_RUNC_IMAGE=$IMAGE_ID \
    1 40000

請注意,應用程式主控程式、對應工作和縮減工作是獨立設定的。在此範例中,我們對這三者都使用 hadoop-image 映像。

範例:Spark

此範例假設 Hadoop 已安裝到 /usr/local/hadoop,而 Spark 已安裝到 /usr/local/spark

在使用該映像執行之前,您還需要壓縮 Docker 映像並將其上傳到 HDFS。請參閱 將 Docker 映像轉換為 runC 映像,瞭解如何將 Docker 映像轉換為 runC 可使用的映像。在此範例中,我們假設您已完成一個名為 hadoop-image 的映像。

此外,container-executor.cfg 中的 runc.allowed.ro-mounts 已更新,包含目錄:/usr/local/hadoop,/etc/passwd,/etc/group

若要在 runC 容器中執行 Spark shell,請執行下列指令

  HADOOP_HOME=/usr/local/hadoop
  SPARK_HOME=/usr/local/spark
  MOUNTS="$HADOOP_HOME:$HADOOP_HOME:ro,/etc/passwd:/etc/passwd:ro,/etc/group:/etc/group:ro"
  IMAGE_ID="hadoop-image"

  $SPARK_HOME/bin/spark-shell --master yarn \
    --conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_TYPE=runc \
    --conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_RUNC_IMAGE=$IMAGE_ID \
    --conf spark.yarn.appMasterEnv.YARN_CONTAINER_RUNTIME_RUNC_MOUNTS=$MOUNTS \
    --conf spark.executorEnv.YARN_CONTAINER_RUNTIME_TYPE=runc \
    --conf spark.executorEnv.YARN_CONTAINER_RUNTIME_RUNC_IMAGE=$IMAGE_ID \
    --conf spark.executorEnv.YARN_CONTAINER_RUNTIME_RUNC_MOUNTS=$MOUNTS

請注意,應用程式主控程式和執行器是獨立設定的。在此範例中,我們對這兩者都使用 hadoop-image 映像。