{"id":717,"date":"2017-10-20T06:19:36","date_gmt":"2017-10-20T06:19:36","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=717"},"modified":"2017-10-20T06:19:36","modified_gmt":"2017-10-20T06:19:36","slug":"jenkins-master-build-slaves-docker-container","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=717","title":{"rendered":"Setup Jenkins master and Build Slaves as Docker Container"},"content":{"rendered":"<p>Do you want dockerized Jenkins which includes the configuration for build slaves also as Docker containers? So that you can run this image using docker command, then bang, everything is ready to run your Jenkins job on docker slave. Now let&#8217;s make this happen.<\/p>\n<h3 id=\"install-jenkins-as-docker-container\">Install Jenkins as docker container<\/h3>\n<p>Go to jenkins website (<a href=\"https:\/\/jenkins.io\/doc\/book\/getting-started\/installing\/?ref=devopscube.com\" rel=\"noopener\">https:\/\/jenkins.io\/doc\/book\/getting-started\/installing\/<\/a>) and find &#8220;Docker&#8221; section, before executing the command <code>docker run -d -p 49001:8080 -v $PWD\/jenkins:\/var\/jenkins_home -t jenkins\/jenkins<\/code>, we need to let &#8220;jenkins&#8221; be the owner of the jenkins directory on host using its UUID: <\/p>\n<pre><code>sudo chown -R 1000:1000 \/opt\/jenkins_home<\/code><\/pre>\n<p>Otherwise, you will find the jenkins docker container cannot be started, just exit after the container created, that&#8217;s because the jenkins container user by default is &#8220;jenkins&#8221;,<\/p>\n<p>Now you can access Jenkins from your browser, for example: http:\/\/[your Jenkins ip]:49001\/, create job and build your project from your Jenkins master container<\/p>\n<h2 id=\"setup-build-slave-as-docker-container\">Setup Build Slave as docker container<\/h2>\n<p>Although you can add VM as build slave in Jenkins, it&#8217;s more flexible and convenient to make build slave as docker container, because you don&#8217;t need to maintain each slave VM, you can just give Jenkins a slave host&#8217;s IP and slave&#8217;s docker image template, then Jenkins can create slave as docker container on that host. Jenkins make this happen by a plugin called DockerPlugin.<\/p>\n<p>Let&#8217;s see how to install and configure this plugin.<\/p>\n<h4 id=\"install-the-plugin\">Install the plugin<\/h4>\n<p>Navigate to the Manage Jenkins &gt; Manage Plugins page in the web UI. Find DockerPlugin and install it. See <a href=\"https:\/\/jenkins.io\/doc\/book\/managing\/plugins\/?ref=devopscube.com\" rel=\"noopener\">https:\/\/jenkins.io\/doc\/book\/managing\/plugins\/<\/a> for reference.<\/p>\n<h4 id=\"plugin-configuration\">Plugin Configuration<\/h4>\n<h3 id=\"create-your-slave-image\">Create your slave image.<\/h3>\n<p><strong>Step 1:<\/strong> Find steps in official site https:\/\/wiki.jenkins.io\/display\/JENKINS\/Docker+Plugin, see the section &#8220;Creating a docker image&#8221;, commands include: <\/p>\n<pre><code>docker pull ubuntu\ndocker run -i -t ubuntu \/bin\/bash\napt-get update\napt-get install openssh-server\nmkdir \/var\/run\/sshd\napt-get install openjdk-6-jdk\nadduser jenkins\n\/usr\/sbin\/sshd\nexit<\/code><\/pre>\n<p>However, the steps are insufficient if your build slave needs to build your project&#8217;s docker image, create docker container and build your project because you can not run docker command in the ubuntu container after these steps. The aim is not docker-in-docker, i.e., docker container running in another docker container, which is another more complicated case. We just need to install the docker binary in docker container and mount the docker socket (by volume &#8220;\/var\/run\/docker.sock&#8221;) so as docker command can be executed in the container. So add the below steps<\/p>\n<ul>\n<li>To let docker daemon can be found in docker container, install docker binary inside the ubuntu container (mount docker socket step will leave to DockerPlugin because DockerPlugin will create slave container automatically when running a job):<\/li>\n<\/ul>\n<pre><code>curl -fsSL https:\/\/get.docker.com\/ | sh<\/code><\/pre>\n<ul>\n<li>Add Jenkins user in docker group in the ubuntu container so as DockerPlugin can access docker binary to execute docker command by &#8220;jenkins&#8221; user: user mod -a -G docker jenkins.<\/li>\n<li>Commit the ubuntu container as docker image, for example, make the image name as jenkins-slave:<\/li>\n<\/ul>\n<pre><code>docker commit [container ID] jenkins-slave<\/code><\/pre>\n<p><strong>Step 2:<\/strong> On docker host, to expose docker&#8217;s TCP port so DockerPlugin can access docker host and create build slave container, edit \/etc\/default\/docker to modify the value of DOCKER_OPTS as (note that 4243 is the default TCP port for docker): <\/p>\n<pre><code>DOCKER_OPTS='-H tcp:\/\/0.0.0.0:4243 -H unix:\/\/\/var\/run\/docker.sock\u2019.<\/code><\/pre>\n<p>3. Now we can return back to Jenkins Web UI and configure Jenkins: Manage Jenkins &gt; Configure system &gt; Cloud &gt; docker:<\/p>\n<ol>\n<li>Input docker URL: tcp:\/\/[your host ip]:4243<\/li>\n<\/ol>\n<p>If click &#8220;Test Connection&#8221;, you can see docker API version shown on UI.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-10-55-29-am-1.jpg\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"1293\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/screen-shot-2017-10-20-at-10-55-29-am-1.jpg 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/screen-shot-2017-10-20-at-10-55-29-am-1.jpg 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/screen-shot-2017-10-20-at-10-55-29-am-1.jpg 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-10-55-29-am-1.jpg 2014w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<ol start=\"2\">\n<li>In \u201cDocker Template\u201d section, click \u201cContainer settings\u201d, input Volumes:<\/li>\n<\/ol>\n<pre><code>\/var\/run\/docker.sock:\/var\/run\/docker.sock \n\/home\/jenkins\/workspace<\/code><\/pre>\n<p>The volumes are volume mappings that DockerPlugin will use to create slave containers. The first volume mapping mounts docker socket into the container to enable docker command can be listened by docker daemon on the host, the second line let Jenkins find her workspace to execute the job.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-01-49-am-1.jpg\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1762\" height=\"800\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/screen-shot-2017-10-20-at-11-01-49-am-1.jpg 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/screen-shot-2017-10-20-at-11-01-49-am-1.jpg 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/screen-shot-2017-10-20-at-11-01-49-am-1.jpg 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-01-49-am-1.jpg 1762w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<ol start=\"3\">\n<li>DockerPlugin by default will destroy build slave container after each build, you can save your slave container for debugging purpose or deploy purpose. On \u201cContainer settings\u201d, from \u201cAvailability\u201d label, click \u201cExperimental options\u201d, choose \u201cExperimental: Keep this agent online as much as possible\u201d.<\/li>\n<\/ol>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-02-43-am-1.jpg\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1662\" height=\"680\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/screen-shot-2017-10-20-at-11-02-43-am-1.jpg 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/screen-shot-2017-10-20-at-11-02-43-am-1.jpg 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/screen-shot-2017-10-20-at-11-02-43-am-1.jpg 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-02-43-am-1.jpg 1662w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Now the docker slave configuration is complete. The whole configuration can be saved by committing the Jenkins image. To verify, you can create a job to run. Your Job configuration can be as below screenshots if you want a pipeline job in order to build from a repository (note your repository should contain a Jenkinsfile).<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-04-06-am-1.jpg\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"1361\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/screen-shot-2017-10-20-at-11-04-06-am-1.jpg 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/screen-shot-2017-10-20-at-11-04-06-am-1.jpg 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/screen-shot-2017-10-20-at-11-04-06-am-1.jpg 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-04-06-am-1.jpg 2008w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-05-04-am-1.jpg\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1474\" height=\"1304\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/screen-shot-2017-10-20-at-11-05-04-am-1.jpg 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/screen-shot-2017-10-20-at-11-05-04-am-1.jpg 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-05-04-am-1.jpg 1474w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>When the job is running, you can see the created slave containers from &#8220;Build Executor Status&#8221;, docker-xxx is a slave container&#8217;s name. After the job finished building, the container status will<br \/>be &#8220;Idle&#8221;, if you destroy the container on your slave host manually, the status will be &#8220;offline&#8221;.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-05-42-am-1.jpg\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"1650\" height=\"1126\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/03\/screen-shot-2017-10-20-at-11-05-42-am-1.jpg 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/03\/screen-shot-2017-10-20-at-11-05-42-am-1.jpg 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/03\/screen-shot-2017-10-20-at-11-05-42-am-1.jpg 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/screen-shot-2017-10-20-at-11-05-42-am-1.jpg 1650w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<hr>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/03\/logo-2.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"134\" height=\"111\"><\/figure>\n<p><strong>ONLINE COURSE:<\/strong>&nbsp;&nbsp;Docker Technologies for DevOps and Developers<\/p>\n<p>Learn how to develop and deploy web applications with Docker technologies. Take your DevOps skills to the next level.<\/p>\n<p><a href=\"https:\/\/devopscube.com\/recommends\/docker-devops-course\/\" rel=\"noopener\">Docker Technologies for DevOps and Developers<\/a><\/p>\n<ul>\n<li>Learn to containerize applications with microservices approach<\/li>\n<li>Best practices for making Dockerfiles<\/li>\n<li>Learn to build multi-node swarm cluster and learn to orchestrate applications<\/li>\n<li>In depth understanding for Docker and its workflows for complex applications<\/li>\n<\/ul>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/jenkins-master-build-slaves-docker-container\/\" target=\"_blank\" rel=\"noopener noreferrer\">Setup Jenkins master and Build Slaves as Docker Container \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/jenkins-master-build-slaves-docker-container\/<\/p>\n","protected":false},"author":1,"featured_media":718,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-717","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/717","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=717"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/717\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/718"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=717"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=717"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=717"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}