在Jenkins Pipeline中使用systemd运行docker容器

137次阅读
没有评论

问题描述

在Jenkins Pipeline中尝试使用systemd运行docker容器,但是遇到了以下错误信息:

ERROR: The container started but didn't run the expected command. Please double check your ENTRYPOINT does execute the command passed as docker run argument, as required by official docker images (see https://github.com/docker-library/official-images#consistency for entrypoint consistency requirements).Alternatively you can force image entrypoint to be disabled by adding option `--entrypoint=''.

用户怀疑是Jenkins在处理CMD参数时出现了问题,并将其替换为了cat命令。用户认为这是不合理的,因为/usr/sbin/init需要作为第一个运行的命令,理想情况下应该作为CMD命令运行。

解决方案

请注意以下操作注意版本差异及修改前做好备份。

方案1

Jenkins的Docker支持机制导致了这个问题。Jenkins在启动容器时使用一个什么都不做的程序,以便可以通过docker exec机制在容器内部运行各种命令。这对于大多数情况来说是有好处的,因为即使命令本身失败,也不会导致整个容器崩溃。

对于您的systemd用户场景,您可以考虑使用Jenkins的sidecar容器支持。这种方式可以在Jenkins主容器旁边运行一个辅助容器,以便可以在其中运行systemd命令。以下是一些步骤来实现这个方案:

  1. 在Jenkins Pipeline中添加一个stage,用于启动sidecar容器。
  2. 在sidecar容器中运行systemd命令。

下面是一个示例的Jenkins Pipeline代码片段:

pipeline {
    agent {
        docker {
            image "jenkins/jenkins:latest"
            args "--privileged"
        }
    }
    stages {
        stage('Start Sidecar Container') {
            steps {
                script {
                    // 启动sidecar容器
                    docker.image('centos/systemd').inside('--privileged') {
                        // 在sidecar容器中运行systemd命令
                        sh 'systemctl'
                    }
                }
            }
        }
        // 其他阶段
    }
}

在上面的示例中,我们首先使用docker.image方法启动了一个Jenkins主容器,并指定了jenkins/jenkins:latest镜像。然后,在Start Sidecar Container阶段中,我们使用docker.image('centos/systemd').inside('--privileged')启动了一个sidecar容器,并在其中运行了systemctl命令。

请注意,--privileged参数是为了在sidecar容器中获得足够的权限来运行systemd命令。

方案2

使用脚本或工具来管理容器的启动顺序可能会增加复杂性,并且需要确保容器A和容器B之间的依赖关系正确设置。
另一种方法是编写脚本或使用工具来控制容器的运行顺序。你可以使用docker run命令来手动控制容器的启动顺序,或者使用一些第三方工具来管理容器的依赖关系。

示例:

以下是一个简单的bash脚本示例,可以在容器A启动后启动容器B:

#!/bin/bash
# 启动容器A
docker run -d --name container_a your_image_a
# 等待容器A完全启动
while ! docker exec container_a echo "Container A is ready"; do
  sleep 1
done
# 启动容器B
docker run -d --name container_b your_image_b

在这个示例中,我们首先使用docker run命令启动容器A,并将其命名为container_a。然后,使用一个循环来等待容器A完全启动(这里是通过在容器内运行echo命令来测试)。一旦容器A就绪,我们再使用docker run命令启动容器B,并将其命名为container_b

请注意,这种方法需要手动编写脚本,并确保容器A和容器B之间的依赖关系正确设置。

以上是两种解决方案,您可以根据自己的需求选择适合您的方法。希望对您有帮助!

正文完