|
2 | 2 |
|
3 | 3 | = CI/CD using Docker
|
4 | 4 |
|
| 5 | +*PURPOSE*: This chapter explains how to use Jenkins and Docker to run continuous integration and continuous delivery. |
| 6 | + |
| 7 | +There are several possible approaches to run Docker builds with Jenkins: |
| 8 | + |
| 9 | +. Install Jenkins on your host machine, where Docker is also installed, and run Docker commands from your build, either using one of the several Jenkins Docker plugins, or by running Docker commands from a build step |
| 10 | +. Install Jenkins on your host machine and have a Jenkins slave machine with Docker installed to run your Docker builds |
| 11 | +. Run Jenkins on Docker and use the underlying Docker installed on the host to run Docker commands. |
| 12 | + |
| 13 | +NOTE: Another option is running Jenkins on Docker and do a complete Docker installation inside the Jenkins Docker container. This technique is called Docker in Docker and it is usually a bad idea. There are several discussions about the problems with this approach, like this one: http://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/ . A better approach is using Docker outside of Docker, as explained here: http://container-solutions.com/running-docker-in-jenkins-in-docker/ |
| 14 | + |
| 15 | +== Run Jenkins on docker |
| 16 | + |
| 17 | +In this example, we will run Jenkins on Docker and use the underlying Docker installed on the host to run Docker commands. This technique is known as Docker outside of Docker. |
| 18 | + |
| 19 | +First, clone the project at https://github.com/fabianenardon/jenkins-docker-demo |
| 20 | + |
| 21 | +Then, in the project folder, run: |
| 22 | + |
| 23 | +[source, text] |
| 24 | +---- |
| 25 | +docker-compose up |
| 26 | +---- |
| 27 | + |
| 28 | +Wait for jenkins to start and then go to the browser and open `http://localhost:8081`. Jenkins should be running. |
| 29 | + |
| 30 | +The Jenkins installation on this lab comes pre-configured. To login use the username `jenkins` and the password `jenkins`. |
| 31 | + |
| 32 | +== Running Integration Tests with Docker and Jenkins |
| 33 | + |
| 34 | +For this Continuous Integration demo, we will run the application described on the link:chapters/ch11-bigdata.adoc[Big Data Processing with Docker and Hadoop] chapter. |
| 35 | + |
| 36 | +[NOTE] |
| 37 | +==== |
| 38 | +When running integration tests, you want to test your application in an environment as close to production as possible, so you can test interactions between the several components, services, databases, network communication, etc. Fortunately, docker can help you a lot with integration tests. There are several strategies to run integration tests, but in this application we are going to use the following: |
| 39 | +
|
| 40 | +. Start the services with a `docker-compose.yml` file created for testing purposes. This file won't have any volumes mapped, so when the test is over, no state will be saved. The test `docker-compose.yml` file won't publish any port on the host machine, so we can run simultaneous tests. |
| 41 | +. Run the application, using the services started with the `docker-compose.yml` test file. |
| 42 | +. Run Maven integration tests to check if the application execution produced the expected results. This will be done by checking what was saved on the MongoDB database. |
| 43 | +. Stop the services. No state will be stored, so next time you run the integration tests, you will have a clean environment. |
| 44 | +==== |
| 45 | + |
| 46 | +Create a new job on jenkins: |
| 47 | + |
| 48 | +1 . Select Freestyle project |
| 49 | + |
| 50 | +image::docker-ci-cd-01.png[] |
| 51 | + |
| 52 | +2 . In Source Code Management, select Git and add the repository URL: `https://github.com/fabianenardon/hadoop-docker-demo` |
| 53 | + |
| 54 | +image::docker-ci-cd-02.png[] |
| 55 | + |
| 56 | +3 . In Build, select Add build step and select Execute shell |
| 57 | + |
| 58 | +image::docker-ci-cd-03.png[] |
| 59 | + |
| 60 | +4 . In the shell Command, add these instructions: |
| 61 | + |
| 62 | +[source, text] |
| 63 | +---- |
| 64 | +cd sample |
| 65 | +
|
| 66 | +# Generates the images |
| 67 | +sudo /var/jenkins_home/tools/hudson.tasks.Maven_MavenInstallation/maven/bin/mvn clean install -Papp-docker-image |
| 68 | +
|
| 69 | +
|
| 70 | +# Starts all the services. The -p option allows multiple builds to run at the same time, |
| 71 | +# since we can start multiple instances of the containers |
| 72 | +sudo docker-compose -p app-$BUILD_NUMBER --file src/test/resources/docker-compose.yml up -d |
| 73 | +
|
| 74 | +# Waits for containers to start |
| 75 | +sleep 30 |
| 76 | +
|
| 77 | +# Creates the folder we need on hdfs to test |
| 78 | +sudo docker-compose -p app-$BUILD_NUMBER --file src/test/resources/docker-compose.yml exec -T yarn hdfs dfs -mkdir /files/ |
| 79 | +
|
| 80 | +# Put the file we are going to process on hdfs |
| 81 | +sudo docker-compose -p app-$BUILD_NUMBER --file src/test/resources/docker-compose.yml run docker-hadoop-example hdfs dfs -put /maven/test-data/text_for_word_count.txt /files/ |
| 82 | +
|
| 83 | +# Run our application |
| 84 | +sudo docker-compose -p app-$BUILD_NUMBER --file src/test/resources/docker-compose.yml run docker-hadoop-example hadoop jar /maven/jar/docker-hadoop-example-1.0-SNAPSHOT-mr.jar hdfs://namenode:9000 /files mongo yarn:8050 |
| 85 | +
|
| 86 | +# Run our integration tests |
| 87 | +sudo docker-compose -p app-$BUILD_NUMBER --file src/test/resources/docker-compose.yml run docker-hadoop-example-tests mvn -f /maven/code/pom.xml -Dmaven.repo.local=/m2/repository -Pintegration-test verify |
| 88 | +---- |
| 89 | + |
| 90 | +5 . Click on Add post-build action and select Execute a set of scripts |
| 91 | + |
| 92 | +image::docker-ci-cd-04.png[] |
| 93 | + |
| 94 | +6 . In Post-build Actions, select Execute shell |
| 95 | + |
| 96 | +image::docker-ci-cd-05.png[] |
| 97 | + |
| 98 | +7 . In the Command box, add: |
| 99 | + |
| 100 | +[source, text] |
| 101 | +---- |
| 102 | +cd sample |
| 103 | +sudo docker-compose -p app-$BUILD_NUMBER --file src/test/resources/docker-compose.yml down |
| 104 | +---- |
| 105 | + |
| 106 | +8 . Uncheck the `Execute script only if build succeeds` and `Execute script only if build fails` options, so this script will run always when the build ends. This way, we make sure to always stop the services. |
| 107 | + |
| 108 | + |
| 109 | +[NOTE] |
| 110 | +==== |
| 111 | +. The `-p app-$BUILD_NUMBER` option allows multiple builds to run at the same time, since we can start multiple instances of the containers. We are using Jenkins $BUILD_NUMBER variable to isolate the containers. This way, each set of services will run on its own network. |
| 112 | +. We are running the commands with sudo because we are actually running the Docker socket on the host. Jenkins runs with the jenkins user and we added the jenkins user to the sudoers list in our image. Obviously, this can have security consequences in a production environment, since one could create a build that would access root level services on the host. You can avoid this by configuring the jenkins user on the host, so it will have access to the Docker socket and then run the commands without sudo. |
| 113 | +==== |
| 114 | + |
| 115 | +== Continuous Delivery with Docker and Jenkins |
| 116 | + |
| 117 | +Continuous Delivery strategies depend greatly on the application architecture. With a dockerized application like the one in our demo, the continuous delivery strategy could be to publish a new version of the application image if the tests passed. This way, next time the application runs on production, the new image will be downloaded and automatically deployed. You can publish images with Jenkins just like you invoked all the other docker commands in the build. |
| 118 | + |
| 119 | + |
| 120 | + |
0 commit comments