在 azure 上创建 ejbca docker 容器时出现 containerd 错误 "failed to find user by uid"

containerd error "failed to find user by uid" when creating ejbca docker container on azure

当我尝试为 EJBCA-ce 创建 Azure 容器实例时出现错误并且看不到任何日志。

我希望得到以下结果:

但是我收到以下错误:

Failed to start container my-azure-container-resource-name, Error response: to create containerd task: failed to create container e9e48a_________ffba97: guest RPC failure: failed to find user by uid: 10001: expected exactly 1 user matched '0': unknown

一些上下文:

我运行Azure云容器实例上的容器

我试过了

在本地使用相同的环境变量(数据库配置)运行没问题。 几周前它曾经 运行 使用相同的配置。

这是我从 az cli 附加容器组时获得的一些日志。

(count: 1) (last timestamp: 2020-11-03 16:04:32+00:00) pulling image "primekey/ejbca-ce:6.15.2.3"
(count: 1) (last timestamp: 2020-11-03 16:04:37+00:00) Successfully pulled image "primekey/ejbca-ce:6.15.2.3"
(count: 28) (last timestamp: 2020-11-03 16:27:52+00:00) Error: Failed to start container aci-pulsy-ccm-ejbca-snd, Error response: to create containerd task: failed to create container e9e48a06807fba124dc29633dab10f6229fdc5583a95eb2b79467fe7cdffba97: guest RPC failure: failed to find user by uid: 10001: expected exactly 1 user matched '0': unknown

the dockerfile from dockerhub

的摘录

我怀疑这个问题可能与我们在 dockerfile 中多次发现的命令 USER 0USER 10001 有关。

COPY dir:89ead00b20d79e0110fefa4ac30a827722309baa7d7d74bf99910b35c665d200 in /
/bin/sh -c rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
CMD ["/bin/bash"]
USER 0
COPY dir:893e424bc63d1872ee580dfed4125a0bef1fa452b8ae89aa267d83063ce36025 in /opt/primekey
COPY dir:756f0fe274b13cf418a2e3222e3f6c2e676b174f747ac059a95711db0097f283 in /licenses
USER 10001
CMD ["/opt/primekey/wildfly-14.0.1.Final/bin/standalone.sh" "-b" "0.0.0.0"
MAINTAINER PrimeKey Solutions AB
ARG releaseTag
ARG releaseEdition

ARM 模板

{
      "type": "Microsoft.ContainerInstance/containerGroups",
      "apiVersion": "2019-12-01",
      "name": "[variables('ejbcaContainerGroupName')]",
      "location": "[parameters('location')]",
      "tags": "[variables('tags')]",
      "dependsOn": [
        "[resourceId('Microsoft.DBforMariaDB/servers', variables('ejbcaMariadbServerName'))]",
        "[resourceId('Microsoft.DBforMariaDB/servers/databases', variables('ejbcaMariadbServerName'), variables('ejbcaMariadbDatabaseName'))]"
      ],
      "properties": {
        "sku": "Standard",
        "containers": [
          {
            "name": "[variables('ejbcaContainerName')]",
            "properties": {
              "image": "primekey/ejbca-ce:6.15.2.3",
              "ports": [
                {
                  "protocol": "TCP",
                  "port": 443
                },
                {
                  "protocol": "TCP",
                  "port": 8443
                }
              ],
              "environmentVariables": [

                {
                  "name": "DATABASE_USER",
                  "value": "[concat(parameters('mariadbUser'),'@', variables('ejbcaMariadbServerName'))]"
                },
                {
                  "name": "DATABASE_JDBC_URL",
                  "value": "[variables('ejbcaEnvVariableJdbcUrl')]"
                },
                {
                  "name": "DATABASE_PASSWORD",
                  "secureValue": "[parameters('mariadbAdminPassword')]"
                }
              ],
              "resources": {
                "requests": {
                  "memoryInGB": 1.5,
                  "cpu": 2
                }
              }
              ,
               "volumeMounts": [
                 {
                   "name": "certificates",
                   "mountPath": "/mnt/external/secrets"
                 }
               ]
            }
          }
        ],
        "initContainers": [],
        "restartPolicy": "OnFailure",
        "ipAddress": {
          "ports": [
                {
                  "protocol": "TCP",
                  "port": 443
                },
                {
                  "protocol": "TCP",
                  "port": 8443
                }
          ],
          "type": "Public",
          "dnsNameLabel": "[parameters('ejbcaContainerGroupDNSLabel')]"
        },
        "osType": "Linux",
         "volumes": [
           {
             "name": "certificates",
             "azureFile": {
               "shareName": "[parameters('ejbcaCertsFileShareName')]",
               "storageAccountName": "[parameters('ejbcaStorageAccountName')]",
               "storageAccountKey": "[parameters('ejbcaStorageAccountKey')]"
             }
           }
         ]
      }
    }

在 linux (ubuntu 20.04)

我的本地机器上 运行 没问题
docker run -it --rm -p 8080:8080 -p 8443:8443 -h localhost -e DATABASE_USER="mymaridbuser@my-db" -e DATABASE_JDBC_URL="jdbc:mariadb://my-azure-domain.mariadb.database.azure.com:3306/ejbca?useSSL=true" -e DATABASE_PASSWORD="my-pwd" primekey/ejbca-ce:6.15.2.3

UID 为 10001 的用户在您的映像中不存在。这不会阻止 Dockerfile 中的 USER 命令正常工作或图像本身无效,但它似乎会导致 Azure 容器出现问题。

我找不到关于为什么它在 Azure 上不起作用的文档或任何参考(如果是的话会更新),但在图像中添加用户应该可以解决问题。尝试在您的 Dockerfile 中添加类似这样的内容以创建具有 UID 10001 的用户(这必须以 root 身份完成,即使用用户 0):

useradd -u 10001 myuser

补充说明看用户10001不存在:

# When running container, not recognized by system
$ docker run docker.io/primekey/ejbca-ce:6.15.2.3 whoami
whoami: cannot find name for user ID 10001

# Not present in /etc/passwd
$ docker run docker.io/primekey/ejbca-ce:6.15.2.3 cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin

在 EJBCA-ce 容器映像中,我认为他们试图向 运行 EJBCA 服务器提供不同于 root 的用户。根据 Docker documentation:

The USER instruction sets the user name (or UID) and optionally the user group (or GID) to use when running the image and for any RUN, CMD and ENTRYPOINT instructions that follow it in the Dockerfile

Dockerfile中他们引用了两个用户,root,对应UID 0,另一个用户,UID 10001.

通常,在 Linux 和 UNIX 系统中,UID 可以组织在不同的 运行ges 中:这在很大程度上取决于具体的操作系统和用户管理实践,但很可能是在 linux 系统中创建的第一个用户帐户将被分配给 UID 100110001,就像在本例中一样。请参见 UID entry in wikipedia or this article.

AFAIK,指示的 USER 不需要存在于您的容器中才能 运行 它正确:事实上,如果您在本地 运行 它,它将启动而不会出现进一步的问题.

具有 UID 10001 的用户将通过在 Dockerfile、[=31] 中定义的 CMD 中的 运行 脚本实际设置在您的容器中=], 通过这个代码片段:

if ! whoami &> /dev/null; then
  if [ -w /etc/passwd ]; then
    echo "${APPLICATION_NAME}:x:$(id -u):0:${APPLICATION_NAME} user:/opt:/sbin/nologin" >> /etc/passwd
  fi
fi

请注意,在此上下文中 APPLICATION_NAME 取值 ejbca 并且 运行 使用此脚本的用户,如 Dockerfile 中所示,是 10001。这将是此代码中命令 id -u 提供的值。

如果你运行你的容器在本地,你可以验证它:

docker run -it -p 8080:8080 -p 8443:8443 -h localhost primekey/ejbca-ce:6.15.2.3

并启动bash进去:

 docker exec -it container_name /bin/bash

如果你运行whoami,它会告诉你ejbca

如果你 运行 id 它会给你以下输出:

uid=10001(ejbca) gid=0(root) groups=0(root)

您也可以在 /etc/passwd 中验证用户是否存在:

bash-4.2$ cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
games:x:12:100:games:/usr/games:/sbin/nologin
ftp:x:14:50:FTP User:/var/ftp:/sbin/nologin
nobody:x:99:99:Nobody:/:/sbin/nologin
systemd-network:x:192:192:systemd Network Management:/:/sbin/nologin
dbus:x:81:81:System message bus:/:/sbin/nologin
ejbca:x:10001:0:ejbca user:/opt:/sbin/nologin

Pierre 没有得到这个输出的原因是因为他 运行 容器覆盖了提供的 CMD 并且因此没有执行负责的 start.sh 脚本用户创建,如上所述。

出于任何原因,这就是我的知识让我失望的地方,当 Azure 尝试 运行 你的容器时,它失败了,因为 USER 10001Dockerfile不存在。

我认为这可能与使用 containerd 而不是 docker 有关。

Azure 报告的错误似乎与 Microsoft 项目有关 opengcs

他们对项目的评价:

Open Guest Compute Service is a Linux open source project to further the development of a production quality implementation of Linux Hyper-V container on Windows (LCOW). It's designed to run inside a custom Linux OS for supporting Linux container payload.

并且:

The focus of LCOW v2 as a replacement of LCOW v1 is through the coordination and work that has gone into containerd/containerd and its Runtime V2 interface. To see our containerd hostside shim please look here Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1.

您在控制台中看到的错误是 raisedspec.go 文件引起的,您可以在他们的代码库中找到该文件,当他们试图代表容器进程建立用户时应该是 运行:

func setUserID(spec *oci.Spec, uid int) error {
    u, err := getUser(spec, func(u user.User) bool {
        return u.Uid == uid
    })
    if err != nil {
        return errors.Wrapf(err, "failed to find user by uid: %d", uid)
    }
    spec.Process.User.UID, spec.Process.User.GID = uint32(u.Uid), uint32(u.Gid)
    return nil
}

此代码由其他代码片段执行 - 您可以看到 full function code here:

parts := strings.Split(userstr, ":")
switch len(parts) {
case 1:
    v, err := strconv.Atoi(parts[0])
    if err != nil {
        // evaluate username to uid/gid
        return setUsername(spec, userstr)
    }
    return setUserID(spec, int(v))

getUser 函数:

func getUser(spec *oci.Spec, filter func(user.User) bool) (user.User, error) {
    users, err := user.ParsePasswdFileFilter(filepath.Join(spec.Root.Path, "/etc/passwd"), filter)
    if err != nil {
        return user.User{}, err
    }
    if len(users) != 1 {
        return user.User{}, errors.Errorf("expected exactly 1 user matched '%d'", len(users))
    }
    return users[0], nil
}

如您所见,这些正是 Azure 向您报告的错误。

总而言之,我认为他们正在提供符合 OCI Image Format Specification 的 Windows LCOW 解决方案,适用于具有 containerd.[=68= 的 运行 容器]

正如您所说,如果 几周前它曾运行使用相同的配置我最好的客人是,也许他们将您的容器从纯Linux containerd 运行基于 Windows 和上述软件的时间实现,这就是容器现在失败的原因。

一种可能的解决方法是根据 PrimeKey 提供的官方图像创建自定义图像并创建用户 10001,正如 Pierre 指出的那样。

要完成此任务,首先,创建一个新的自定义 Dockerfile。你可以试试,比如:

FROM primekey/ejbca-ce:6.15.2.3

USER 0

RUN echo "ejbca:x:10001:0:ejbca user:/opt:/sbin/nologin" >> /etc/passwd

USER 10001

请注意,您可能需要从官方 EJBCA 映像定义一些环境变量。

有了这个 Dockerfile,您可以使用 docker 或 docker 与适当的 docker-compose.yaml 文件构建图像,例如:

version: "3"

services:
  ejbca:
    image: <your repository>/ejbca
    build: .
    ports:
      - "8080:8080"
      - "8443:8443"

请按您认为合适的方式进行定制。

使用此设置,新容器仍将在本地环境中以与原始容器相同的方式正确运行:我希望在 Azure 中也是如此。