在Docker中使用Jenkins并访问主机Docker

12

我有一个发布Web应用程序到我的开发服务器的工作流程,服务器只有一个Docker宿主机,并且我使用docker-compose来管理容器。

  1. 将我的应用程序更改推送到私有的GitLab(在Docker中运行)。该应用程序包含Dockerfile和docker-compose.yml文件。
  2. GitLab触发Jenkins构建(Jenkins也在Docker中运行),进行一些常规构建操作(例如运行测试)。
  3. Jenkins需要使用docker-compose构建新的Docker镜像并部署它。这是我遇到的问题所在:按照我设置的方式,Jenkins容器可以访问宿主机的Docker,因此在构建脚本中运行任何docker命令实际上与在宿主机上运行相同。这是使用以下DockerFile为Jenkins完成的:

    FROM jenkins
    USER root
    
    # Give jenkins access to docker
    RUN groupadd -g 997 docker
    RUN gpasswd -a jenkins docker
    
    # Install docker-compose
    RUN curl -L https://github.com/docker/compose/releases/download/1.2.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
    RUN chmod +x /usr/local/bin/docker-compose
    
    USER jenkins
    

    并将以下卷映射到Jenkins容器:

    -v /var/run/docker.sock:/var/run/docker.sock
    -v /usr/bin/docker:/usr/bin/docker
    
    一个典型的Jenkins构建脚本大致如下:

    docker-compose build
    docker-compose up
    

    这个方法可以正常运行,但有两个问题:

    • 它感觉像是一个hack。但我发现唯一的其他选择是使用Jenkins的Docker插件,将其发布到注册表,然后有一种方法让主机知道它需要重新启动。这样涉及到的部分更多,而且docker-jenkins插件要求Docker主机在开放端口上,这并不是我真正想要暴露的。

    • Jenkins DockerFile包含groupadd -g 997 docker,这是为了让Jenkins用户访问Docker所需的。但是,GID(997)是主机上的GID,因此不可移植。

    我真的不确定我在寻找什么解决方案。我看不出有任何实际的方法可以避免这种方法,但如果有一种方式可以允许在Jenkins容器内运行docker命令,而无需在DockerFile中硬编码GID,那将是很好的。有人对此有什么建议吗?


当我正在构建运行Android Studio的Docker镜像时,遇到了类似的问题。我需要对kvm和video组拥有相同的GID,以便Android Studio能够运行设备模拟器。我开发了一个Hack:1.在主机上创建一个简短的脚本,将环境变量设置为所需的GID值。2.在Dockerfile中,使用上述脚本源并使用环境变量执行addgroup并验证现有组的GID。问题是生成的Docker镜像不能移植到其他主机,这些主机可能具有不同的GID。 - Omer Zak
4个回答

5

我的上一个回答较为通用,介绍了如何在运行时修改容器内的GID。现在,巧合的是,我的一位密切同事请求提供一个可进行Docker开发的Jenkins实例,因此我创建了以下内容:

FROM bdruemen/jenkins-uid-from-volume
RUN apt-get -yqq update && apt-get -yqq install docker.io && usermod -g docker jenkins
VOLUME /var/run/docker.sock
ENTRYPOINT groupmod -g $(stat -c "%g" /var/run/docker.sock) docker && usermod -u $(stat -c "%u" /var/jenkins_home) jenkins && gosu jenkins /bin/tini -- /usr/local/bin/jenkins.sh

父Dockerfile与我在我的回答中描述的相同: 在预构建docker容器(jenkins)中更改用户UID

使用它时,挂载jenkins_home和docker.sock。

docker run -d /home/jenkins:/var/jenkins_home -v /var/run/docker.sock:/var/run/docker.sock <IMAGE>

容器中的Jenkins进程将具有与挂载的主机目录相同的UID。假设Docker套接字对主机上的Docker组可访问,则在容器中创建了一个名为docker的组,其GID也相同。


4

1
有用的文章,我认为在Jenkins中任何Docker命令都需要使用sudo docker ...,是吗? - aquavitae
我已更新链接。看起来当网站更新时,我们所有的博客链接都发生了改变 :( - Adrian Mouat

3
请看我刚发布的docker文件: https://github.com/bdruemen/jenkins-docker-uid-from-volume/blob/master/gid-from-volume/Dockerfile 这里从装载卷(主机目录)中提取GID。
stat -c '%g' <VOLUME-PATH>

然后,容器用户所在组的GID被更改为与之相同的值

groupmod -g <GID>

这个必须以 root 用户身份完成,但是随后 root 权限将被降低。

gosu <USERNAME> <COMMAND>

所有的操作都在ENTRYPOINT中完成,因此真正的GID只有在运行时才能确定。

docker run -d -v <HOST-DIRECTORY>:<VOLUME-PATH> ...

注意,在更改GID之后,容器中可能有其他文件对进程不再可访问,因此您可能需要进行适当的调整。
chgrp -R <GROUPNAME> <SOME-PATH>    

在执行gosu命令之前。

您还可以更改UID,请参见我的答案(在预构建docker容器中更改用户的UID(jenkins)),也许您想同时更改两者以增加安全性。


0

我用以下方法解决了类似的问题。主机上安装了Docker,Jenkins部署在主机的Docker容器中。Jenkins必须在主机上构建和运行包含Web应用程序的容器。

Jenkins主节点使用REST API连接到Docker主机。因此我们需要为我们的Docker主机启用远程API

登录主机并打开docker服务文件/lib/systemd/system/docker.service。搜索ExecStart并将该行替换为以下内容。

ExecStart=/usr/bin/dockerd -H tcp://0.0.0.0:4243 -H unix:///var/run/docker.sock

重新加载并重启Docker服务

sudo systemctl daemon-reload
sudo service docker restart

Docker文件用于Jenkins

FROM jenkins/jenkins:lts
USER root

# Install the latest Docker CE binaries and add user `jenkins` to the docker group
RUN apt-get update
RUN apt-get -y --no-install-recommends install apt-transport-https \
      apt-utils ca-certificates curl gnupg2 software-properties-common && \
    curl -fsSL https://download.docker.com/linux/$(. /etc/os-release; echo     "$ID")/gpg > /tmp/dkey; apt-key add /tmp/dkey && \
    add-apt-repository \
      "deb [arch=amd64] https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") \
      $(lsb_release -cs) \
      stable"

RUN apt-get update && apt-get install -y docker-ce-cli docker-ce && \
   apt-get clean && \
   usermod -aG docker jenkins

USER jenkins
RUN jenkins-plugin-cli --plugins "blueocean:1.25.6 docker-workflow:1.29 ansicolor"

构建 Jenkins Docker 镜像

  docker build -t you-jenkins-name .

运行Jenkins

  docker run --name you-jenkins-name --restart=on-failure --detach \
  --network jenkins \
  --env DOCKER_HOST=tcp://172.17.0.1:4243 \
  --publish 8080:8080 --publish 50000:50000 \
  --volume jenkins-data:/var/jenkins_home \
  --volume jenkins-docker-certs:/certs/client:ro \
  you-jenkins-name  

您的Web应用程序在根目录下有一个存储库,其中包含Jenkins和Docker文件。 Web应用程序的Jenkinsfile:

    pipeline {
        agent any
    
        environment {
            PRODUCT = 'web-app'
            HTTP_PORT = 8082
            DEVICE_CONF_HOST_PATH = '/var/web-app'
        }
    
        options {
            ansiColor('xterm')
            skipDefaultCheckout()
        }
    
        stages {
            stage('Checkout') {
                steps {
                    script {
                        //BRANCH_NAME = env.CHANGE_BRANCH ? env.CHANGE_BRANCH : env.BRANCH_NAME
                        deleteDir()
                        //git url: "git@<host>:<org>/${env.PRODUCT}.git", branch: BRANCH_NAME
                    }
                    checkout scm
                }
            }
            stage('Stop and remove old') {
                steps {
                    script {
                        try {
                            sh "docker stop ${env.PRODUCT}"
                        } catch (Exception e) {}
                        try {
                            sh "docker rm ${env.PRODUCT}"
                        } catch (Exception e) {}
                        try {
                            sh "docker image rm ${env.PRODUCT}"
                        } catch (Exception e) {}
                    }
                }
            }
    
            stage('Build') {
                steps {
                    sh "docker build . -t ${env.PRODUCT}"
                }
            }
    
            // ④ Run the test using the built docker image
            stage('Run new') {
                steps {
                    script {
                        sh """docker run
                         --detach
                         --name ${env.PRODUCT} \
                        --publish ${env.HTTP_PORT}:8080 \
                        --volume ${env.DEVICE_CONF_HOST_PATH}:/var/web-app \
                        ${env.PRODUCT} """
                    }
                }
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接