├── integration-tests-sample ├── docker │ ├── data │ │ └── mongodb │ │ │ └── .gitignore │ └── docker-compose.yml ├── src │ ├── test │ │ ├── resources │ │ │ ├── settings.xml │ │ │ ├── docker-compose.yml │ │ │ └── run_test.sh │ │ └── java │ │ │ └── com │ │ │ └── mycompany │ │ │ └── integrationtests │ │ │ └── sample │ │ │ ├── AppTest.java │ │ │ └── AppIT.java │ └── main │ │ └── java │ │ └── com │ │ └── mycompany │ │ └── integrationtests │ │ └── sample │ │ └── App.java ├── README.md └── pom.xml ├── debugging-sample ├── images │ ├── debug1.png │ ├── debug2.png │ └── debug3.png ├── src │ └── main │ │ └── java │ │ └── com │ │ └── mycompany │ │ └── debugging │ │ └── sample │ │ └── App.java ├── README.md └── pom.xml ├── docker-images ├── openjdk-nonblocking │ ├── Dockerfile │ └── README.md └── openjdk-numcpus-lib │ ├── lib │ └── numcpus.c │ ├── Dockerfile │ └── README.md ├── .gitignore ├── README.md ├── memory-sample ├── src │ └── main │ │ └── java │ │ └── com │ │ └── mycompany │ │ └── memory │ │ └── sample │ │ └── App.java ├── README.md └── pom.xml ├── README.md.orig ├── entropy-sample ├── src │ └── main │ │ └── java │ │ └── com │ │ └── mycompany │ │ └── entropy │ │ └── sample │ │ └── App.java ├── README.md └── pom.xml └── cpu-sample ├── src └── main │ └── java │ └── com │ └── mycompany │ └── cpu │ └── sample │ ├── App.java │ └── MonteCarloPI.java ├── README.md └── pom.xml /integration-tests-sample/docker/data/mongodb/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /debugging-sample/images/debug1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabianenardon/docker-java-issues-demo/HEAD/debugging-sample/images/debug1.png -------------------------------------------------------------------------------- /debugging-sample/images/debug2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabianenardon/docker-java-issues-demo/HEAD/debugging-sample/images/debug2.png -------------------------------------------------------------------------------- /debugging-sample/images/debug3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fabianenardon/docker-java-issues-demo/HEAD/debugging-sample/images/debug3.png -------------------------------------------------------------------------------- /docker-images/openjdk-nonblocking/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:latest 2 | 3 | RUN sed -i 's/^\(securerandom.strongAlgorithms\)=.*$/\1=NativePRNGNonBlocking:SUN/' $JAVA_HOME/jre/lib/security/java.security 4 | -------------------------------------------------------------------------------- /docker-images/openjdk-numcpus-lib/lib/numcpus.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int JVM_ActiveProcessorCount(void) { 5 | char* val = getenv("_NUM_CPUS"); 6 | return val != NULL ? atoi(val) : sysconf(_SC_NPROCESSORS_ONLN); 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /eclipse 3 | /tmp 4 | /modules 5 | /lib 6 | /test-result 7 | /logs 8 | /precompiled 9 | .classpath 10 | .project 11 | .settings/ 12 | .metadata/ 13 | dist/* 14 | nbproject/ 15 | target 16 | nbactions.xml 17 | .idea/ 18 | *.iml 19 | -------------------------------------------------------------------------------- /docker-images/openjdk-nonblocking/README.md: -------------------------------------------------------------------------------- 1 | # openjdk-nonblocking 2 | This project is a demo for commons issues when running java applications on docker 3 | This is a docker demo image with changed securerandom.strongAlgorithms, using a non-blocking implementation 4 | 5 | To build this image: 6 | ``` 7 | > docker build . -t openjdk-nonblocking 8 | ``` -------------------------------------------------------------------------------- /docker-images/openjdk-numcpus-lib/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:latest 2 | 3 | COPY lib/numcpus.c /var/tmp/ 4 | 5 | RUN apt-get update && \ 6 | DEBIAN_FRONTEND=noninteractive apt-get install -y gcc && \ 7 | gcc -O3 -fPIC -shared -Wl,-soname,libnumcpus.so -o /var/lib/libnumcpus.so /var/tmp/numcpus.c 8 | 9 | ENV LD_PRELOAD /var/lib/libnumcpus.so -------------------------------------------------------------------------------- /docker-images/openjdk-numcpus-lib/README.md: -------------------------------------------------------------------------------- 1 | # openjdk-numcpus-lib 2 | This project is a demo for commons issues when running java applications on docker 3 | This is a docker demo image with a custom LD_PRELOAD library written in C, and a LD_PRELOAD environment variable already set. 4 | 5 | To build this image: 6 | ``` 7 | > docker build . -t openjdk-numcpus-lib 8 | ``` -------------------------------------------------------------------------------- /integration-tests-sample/src/test/resources/settings.xml: -------------------------------------------------------------------------------- 1 | 5 | /m2/repository 6 | 7 | -------------------------------------------------------------------------------- /integration-tests-sample/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | mongo: 4 | image: library/mongo:3.2.12 5 | hostname: mongo 6 | container_name: mongo 7 | ports: 8 | - 27017:27017 9 | volumes: 10 | - ./data/mongodb:/data/db 11 | integration-tests-sample: 12 | image: integration-tests-sample:latest 13 | container_name: integration-tests-sample 14 | links: 15 | - mongo 16 | -------------------------------------------------------------------------------- /integration-tests-sample/src/test/resources/docker-compose.yml: -------------------------------------------------------------------------------- 1 | 2 | version: '3' 3 | services: 4 | mongo: 5 | image: library/mongo:3.2.12 6 | hostname: mongo 7 | ports: 8 | - "27017" 9 | integration-tests-sample: 10 | image: integration-tests-sample:latest 11 | links: 12 | - mongo 13 | integration-tests-sample-tests: 14 | image: integration-tests-sample-tests:latest 15 | volumes: 16 | - ~/.m2/repository:/m2/repository 17 | links: 18 | - mongo 19 | -------------------------------------------------------------------------------- /debugging-sample/src/main/java/com/mycompany/debugging/sample/App.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.debugging.sample; 2 | 3 | /** 4 | * 5 | * Just a simple app to show how to debug applications inside docker. 6 | * 7 | * @author fabiane 8 | */ 9 | public class App { 10 | 11 | public static void main(String[] args) { 12 | double number = Math.random(); 13 | double anotherNumber = Math.random(); 14 | double sum = number + anotherNumber; 15 | System.out.println("The sum is "+sum); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # docker-java-issues-demo 2 | This project is a demo for commons issues when running java applications on docker 3 | 4 | These topics are covered: 5 | 6 | 1. [Memory allocation](./memory-sample) 7 | 2. [CPU and Threads](./cpu-sample) 8 | 3. [Random numbers and security](./entropy-sample) 9 | 4. [Debugging applications inside containers](./debugging-sample) 10 | 5. [Integration tests](./integration-tests-sample) 11 | 12 | ## References: 13 | 14 | Several topics covered on these demos were derived from: 15 | 16 | - Conversations with Elijah Zupancic (@shitsukoisaru) 17 | - [Java inside docker: What you must know to not FAIL, by Rafael Benevides](https://developers.redhat.com/blog/2017/03/14/java-inside-docker/) 18 | - [LD_PRELOAD Hack](https://stackoverflow.com/questions/22741859/deceive-the-jvm-about-the-number-of-available-cores-on-linux/22762558#22762558) 19 | 20 | -------------------------------------------------------------------------------- /integration-tests-sample/src/test/java/com/mycompany/integrationtests/sample/AppTest.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.integrationtests.sample; 2 | 3 | import junit.framework.Test; 4 | import junit.framework.TestCase; 5 | import junit.framework.TestSuite; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | extends TestCase 12 | { 13 | /** 14 | * Create the test case 15 | * 16 | * @param testName name of the test case 17 | */ 18 | public AppTest( String testName ) 19 | { 20 | super( testName ); 21 | } 22 | 23 | /** 24 | * @return the suite of tests being tested 25 | */ 26 | public static Test suite() 27 | { 28 | return new TestSuite( AppTest.class ); 29 | } 30 | 31 | /** 32 | * Rigourous Test :-) 33 | */ 34 | public void testApp() 35 | { 36 | assertTrue( true ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /integration-tests-sample/src/test/resources/run_test.sh: -------------------------------------------------------------------------------- 1 | 2 | # Generates the images 3 | mvn clean install -Papp-docker-image 4 | 5 | # Starts mongo service 6 | docker-compose --file src/test/resources/docker-compose.yml up -d mongo 7 | 8 | # Waits for services do start 9 | sleep 30 10 | 11 | # Run our application 12 | docker-compose --file src/test/resources/docker-compose.yml run integration-tests-sample java -jar /maven/jar/integration-tests-sample-1.0-SNAPSHOT-jar-with-dependencies.jar mongo 13 | 14 | # Run our integration tests 15 | docker-compose --file src/test/resources/docker-compose.yml run integration-tests-sample mvn -f /maven/code/pom.xml -Dmaven.repo.local=/m2/repository -Pintegration-test verify 16 | # If you want to remote debug tests, run instead 17 | # docker run -v ~/.m2/repository:/m2/repository -p 5005:5005 --link mongo:mongo --net resources_default integration-tests-sample mvn -f /maven/code/pom.xml -Dmaven.repo.local=/m2/repository -Pintegration-test verify -Dmaven.failsafe.debug 18 | 19 | 20 | # Stop all the services 21 | docker-compose --file src/test/resources/docker-compose.yml down 22 | -------------------------------------------------------------------------------- /memory-sample/src/main/java/com/mycompany/memory/sample/App.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.memory.sample; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 8 | * Just something to use memory and display how much is used. 9 | * 10 | * @author fabiane 11 | */ 12 | public class App { 13 | 14 | public static void main(String[] args) { 15 | Runtime rt = Runtime.getRuntime(); 16 | long prevTotal = 0; 17 | long prevFree = rt.freeMemory(); 18 | 19 | Map map = new HashMap(); 20 | for (int i = 0; i < 4_000_000; i++) { 21 | long total = rt.totalMemory(); 22 | long free = rt.freeMemory(); 23 | if (total != prevTotal || free != prevFree) { 24 | long used = total - free; 25 | System.out.println( 26 | "#" + i 27 | + ", Total: " + total 28 | + ", Used: " + used 29 | + ", Free: " + free); 30 | prevTotal = total; 31 | prevFree = free; 32 | } 33 | map.put(i, "some string, just to use memory"); 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /README.md.orig: -------------------------------------------------------------------------------- 1 | # docker-java-issues-demo 2 | This project is a demo for commons issues when running java applications on docker 3 | 4 | These topics are covered: 5 | 6 | <<<<<<< HEAD 7 | 1. [Memory allocation](https://github.com/fabianenardon/docker-java-issues-demo/tree/master/memory-sample) 8 | 2. CPU and Threads 9 | 3. Random numbers and security 10 | 4. [Debugging applications inside containers](https://github.com/fabianenardon/docker-java-issues-demo/tree/master/debugging-sample) 11 | 5. [Integration tests](https://github.com/fabianenardon/docker-java-issues-demo/tree/master/integration-tests-sample) 12 | 13 | 14 | References: 15 | Several topics covered on these demos were derived from: 16 | - Conversations with Elijah Zupancic (@shitsukoisaru) 17 | - [Java inside docker: What you must know to not FAIL, by Rafael Benevides] 18 | (https://developers.redhat.com/blog/2017/03/14/java-inside-docker/) 19 | - [LD_PRELOAD Hack](https://stackoverflow.com/questions/22741859/deceive-the-jvm-about-the-number-of-available-cores-on-linux/22762558#22762558) 20 | 21 | ======= 22 | 1. [Memory allocation](./memory-sample) 23 | 2. [CPU and Threads](./cpu-sample) 24 | 3. [Random numbers and security](./entropy-sample) 25 | 4. [Debugging applications inside containers](./debugging-sample) 26 | 5. [Integration tests](./integration-tests-sample) 27 | >>>>>>> 8bd27701908a2631e58e7b262d3c20ad7e50743b 28 | -------------------------------------------------------------------------------- /entropy-sample/src/main/java/com/mycompany/entropy/sample/App.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.entropy.sample; 2 | 3 | import java.security.NoSuchAlgorithmException; 4 | import java.security.SecureRandom; 5 | import java.util.Arrays; 6 | import java.util.Date; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | /** 11 | * 12 | * @author babadopulos 13 | */ 14 | public class App { 15 | 16 | private final static int BYTES = 2; 17 | 18 | public static void main(String[] args) throws NoSuchAlgorithmException { 19 | Date now = new Date(); 20 | 21 | /* 22 | use a strong random implementations, defined by: 23 | $JAVA_HOME/jre/lib/security/java.security : 24 | securerandom.strongAlgorithms= 25 | */ 26 | SecureRandom sr = SecureRandom.getInstanceStrong(); 27 | 28 | Logger.getGlobal().log(Level.INFO, "\nRequesting {0} random bytes", BYTES); 29 | 30 | byte[] b = new byte[BYTES]; 31 | 32 | /* 33 | request N random bytes. 34 | depending on the SecureRandom implementation, this can potentialy be a blocking request. 35 | this method only returns when the system has enough entropy. 36 | */ 37 | sr.nextBytes(b); 38 | 39 | System.out.println("\n" + Arrays.toString(b) + "\n"); 40 | 41 | Logger.getGlobal().log(Level.INFO, "Took {0} ms\n", (System.currentTimeMillis() - now.getTime())); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /integration-tests-sample/src/main/java/com/mycompany/integrationtests/sample/App.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.integrationtests.sample; 2 | 3 | import com.mongodb.MongoClient; 4 | import org.bson.Document; 5 | import org.mongodb.morphia.Datastore; 6 | import org.mongodb.morphia.Morphia; 7 | 8 | /** 9 | * 10 | * Simple Mongo example. Saves a text to MongoDB. 11 | * 12 | * @author fabianenardon 13 | */ 14 | public class App { 15 | 16 | public static final String DATABASE = "testdb"; 17 | public static final String COLLECTION = "hello"; 18 | 19 | public App() { 20 | } 21 | 22 | 23 | /** 24 | * 25 | * @param args 26 | * 27 | */ 28 | public static void main(String[] args) throws Exception { 29 | if (args == null || args.length == 0) { 30 | System.err.println("MongoDB server not informed."); 31 | System.exit(1); 32 | } 33 | 34 | String mongoHost = args[0]; 35 | 36 | MongoClient mongo = new MongoClient(mongoHost); 37 | Morphia morphia = new Morphia(); 38 | Datastore ds = morphia.createDatastore(mongo, App.DATABASE); 39 | try { 40 | 41 | Document document = new Document(); 42 | document.append("text", "Hello World"); 43 | 44 | ds.getMongo() 45 | .getDatabase(App.DATABASE) 46 | .getCollection(App.COLLECTION).insertOne(document); 47 | 48 | } finally { 49 | ds.getMongo().close(); 50 | } 51 | 52 | 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /debugging-sample/README.md: -------------------------------------------------------------------------------- 1 | # debugging-sample 2 | This project shows how to debug Java applications inside Docker containers. 3 | 4 | ## How to run the demo 5 | 6 | Build the project: 7 | 8 | ``` 9 | mvn clean install 10 | ``` 11 | 12 | A new image will be generated. See: 13 | 14 | ``` 15 | > docker images 16 | 17 | REPOSITORY TAG IMAGE ID CREATED SIZE 18 | debugging-sample latest f13295b35871 2 minutes ago 610 MB 19 | ``` 20 | 21 | Open the project in your IDE and set a breakpoint. For example: 22 | 23 | ![Netbeans with breakpoint](https://raw.githubusercontent.com/fabianenardon/docker-java-issues-demo/master/debugging-sample/images/debug1.png) 24 | 25 | Then, run the application inside Docker with the debugging instruction: 26 | 27 | ``` 28 | > docker run -p5005:5005 -e JAVA_OPTIONS=\ 29 | '-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005' \ 30 | debugging-sample 31 | ``` 32 | 33 | The JAVA_OPTIONS variable is defined in our `pom.xml` in the image definition. This instruction will make the application stop and wait until the IDE connects to the debugging port. 34 | 35 | Go to the IDE and attach the debug to the application, using port 5005 in this example: 36 | 37 | ![Netbeans attaching debug](https://raw.githubusercontent.com/fabianenardon/docker-java-issues-demo/master/debugging-sample/images/debug2.png) 38 | 39 | ![Netbeans attaching debug with port](https://raw.githubusercontent.com/fabianenardon/docker-java-issues-demo/master/debugging-sample/images/debug3.png) 40 | 41 | The application will run inside Docker and stop at your breakpoint. -------------------------------------------------------------------------------- /integration-tests-sample/src/test/java/com/mycompany/integrationtests/sample/AppIT.java: -------------------------------------------------------------------------------- 1 | 2 | package com.mycompany.integrationtests.sample; 3 | 4 | import com.mongodb.MongoClient; 5 | import com.mongodb.client.MongoCursor; 6 | import static junit.framework.Assert.assertEquals; 7 | import static junit.framework.Assert.assertTrue; 8 | import org.bson.Document; 9 | import org.junit.Before; 10 | import org.junit.Test; 11 | import org.mongodb.morphia.Datastore; 12 | import org.mongodb.morphia.Morphia; 13 | 14 | /** 15 | * 16 | * Integration test. This test should run inside docker 17 | * with docker exec. 18 | * 19 | * @author fabiane 20 | */ 21 | public class AppIT { 22 | 23 | private static final String DOCKER_MONGO_HOST = "mongo"; 24 | private static final int DOCKER_MONGO_PORT = 27017; 25 | 26 | @Before 27 | public void setUp() throws Exception { 28 | } 29 | 30 | @Test 31 | public void testSave() throws Exception { 32 | MongoClient mongo = new MongoClient(DOCKER_MONGO_HOST, DOCKER_MONGO_PORT); 33 | Morphia morphia = new Morphia(); 34 | Datastore ds = morphia.createDatastore(mongo, App.DATABASE); 35 | try { 36 | 37 | MongoCursor result = ds.getMongo() 38 | .getDatabase(App.DATABASE) 39 | .getCollection(App.COLLECTION).find().limit(1).iterator(); 40 | assertTrue(result.hasNext()); 41 | assertEquals("Hello World", result.next().get("text")); 42 | System.out.println("ALL TESTS PASSED."); 43 | } finally { 44 | ds.getMongo().close(); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /cpu-sample/src/main/java/com/mycompany/cpu/sample/App.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.cpu.sample; 2 | 3 | import java.math.BigDecimal; 4 | import java.security.NoSuchAlgorithmException; 5 | import java.util.Date; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | /** 10 | * 11 | * @author babadopulos 12 | */ 13 | public class App { 14 | 15 | private final static BigDecimal POINTS = new BigDecimal("200000000"); 16 | 17 | public static void main(String[] args) throws NoSuchAlgorithmException { 18 | int number_of_executions = 1; //from LOOP env variable 19 | if (args.length > 0) { 20 | try { 21 | number_of_executions = Integer.parseInt(args[0]); 22 | } catch (NumberFormatException e) { 23 | number_of_executions = 1; 24 | } 25 | } 26 | 27 | int cores = Runtime.getRuntime().availableProcessors(); 28 | 29 | Logger.getGlobal().log(Level.INFO, "cores {0}", cores); 30 | BigDecimal points_per_thread = POINTS.divide(BigDecimal.valueOf(cores)); 31 | 32 | System.out.println("points: " + POINTS); 33 | System.out.println("points_per_thread: " + points_per_thread); 34 | System.out.println("number_of_executions: " + number_of_executions); 35 | 36 | for (int i = 0; i < number_of_executions; i++) { 37 | Date now = new Date(); 38 | 39 | //high CPU usage simulation 40 | MonteCarloPI monteCarloPI = new MonteCarloPI(cores, points_per_thread); 41 | 42 | BigDecimal result = monteCarloPI.calculate(); 43 | 44 | BigDecimal pi = BigDecimal.valueOf(Math.PI); 45 | 46 | double error = 100 * Math.abs(result.doubleValue() - Math.PI) / Math.PI; 47 | 48 | System.out.printf("pi = %s\terror = %.2g%% \tin: %d ms\n", result.toPlainString(), error, (System.currentTimeMillis() - now.getTime())); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /entropy-sample/README.md: -------------------------------------------------------------------------------- 1 | # entropy-sample 2 | This project is a demo for common entropy issues faced when running Java applications inside Docker containers. 3 | 4 | ## How to run the demo 5 | 6 | Build custom openjdk-nonblocking: 7 | ``` 8 | docker build ../docker-images/openjdk-nonblocking/ -t openjdk-nonblocking:latest 9 | ``` 10 | 11 | Build the project: 12 | 13 | ``` 14 | mvn clean install 15 | ``` 16 | 17 | A new image will be generated. See: 18 | 19 | ``` 20 | > docker images 21 | 22 | REPOSITORY TAG IMAGE ID CREATED SIZE 23 | entropy-sample latest xxxxxxxxxxxx 5 seconds ago 610 MB 24 | ``` 25 | 26 | If you run a container using a Docker inside a host with low entropy, the application will block until the host system has enough entropy: 27 | 28 | ``` 29 | > docker run entropy-sample 30 | Jun 17, 2017 7:02:56 PM com.mycompany.entropy.sample.App main 31 | INFO: 32 | Requesting 2 random bytes 33 | ``` 34 | 35 | 36 | To fix this blocking issue, you have a few options: 37 | 38 | 1. Edit $JAVA_HOME/jre/lib/security/java.security and change the default blocking securerandom.strongAlgorithms to NativePRNGNonBlocking:SUN 39 | 40 | ``` 41 | > sed -i 's/^\(securerandom.strongAlgorithms\)=.*$/\1=NativePRNGNonBlocking:SUN/' $JAVA_HOME/jre/lib/security/java.security 42 | ``` 43 | 44 | or 45 | 2. Use a non blocking instance, specifying the algorithm and provider when calling SecureRandom 46 | 47 | ``` 48 | SecureRandom.getInstance("NativePRNGNonBlocking", "SUN"); 49 | ``` 50 | 51 | or 52 | 3. Install in the Host system (outside docker container) an entropy daemon like haveged to improve the entropy pool. 53 | ``` 54 | Debian/Ubuntu: apt-get install haveged 55 | RHEL/CentOS/Fedora: yum install haveged 56 | ``` 57 | 58 | After making one of above changes, you should not have a blocking issue: 59 | 60 | ``` 61 | $ docker run entropy-sample 62 | Jun 17, 2017 7:41:25 PM com.mycompany.entropy.sample.App main 63 | INFO: 64 | Requesting 2 random bytes 65 | 66 | [89, -55] 67 | 68 | Jun 17, 2017 7:41:25 PM com.mycompany.entropy.sample.App main 69 | INFO: Took 56 ms 70 | ``` 71 | -------------------------------------------------------------------------------- /cpu-sample/README.md: -------------------------------------------------------------------------------- 1 | # cpu-sample 2 | This project is a demo for common cpu issues faced when running Java applications inside Docker containers. 3 | 4 | ## How to run the demo 5 | 6 | Build the custom openjdk-numcpus-lib image: 7 | ``` 8 | docker build ../docker-images/openjdk-numcpus-lib/ -t openjdk-numcpus-lib:latest 9 | ``` 10 | 11 | Build the project: 12 | 13 | ``` 14 | mvn clean install 15 | ``` 16 | 17 | Two new images will be generated. See: 18 | 19 | ``` 20 | > docker images 21 | 22 | REPOSITORY TAG IMAGE ID CREATED SIZE 23 | cpu-sample latest xxxxxxxxxxxx 5 seconds ago 610 MB 24 | cpu-sample-numcpus-lib latest xxxxxxxxxxxx 5 seconds ago 610 MB 25 | ``` 26 | 27 | If you run a container inside Docker, it will use the number of CPUs from the HOST machine to calculate how many threads it can use in parallel, even if you limit the CPUs with --cpus, because the application relies on Runtime.getRuntime().availableProcessors() and the JVM is not aware of the --cpus switch. 28 | 29 | ``` 30 | > docker run -e LOOP=2 --cpus=2 cpu-sample 31 | Jun 20, 2017 9:42:41 PM com.mycompany.cpu.sample.App main 32 | INFO: cores 4 33 | points: 200000000 34 | points_per_thread: 50000000 35 | number_of_executions: 2 36 | pi = 3.14163464 error = 0.0013% in: 6663 ms 37 | pi = 3.1417028 error = 0.0035% in: 6293 ms 38 | ``` 39 | 40 | 41 | To fix this cpu issue, you have two options: 42 | 43 | 1. Change your application to not rely on Runtime.getRuntime().availableProcessors() to calculate available CPUs 44 | 45 | or 46 | 2. Use a custom LD_PRELOAD library to override the JVM default behavior and change the number of available CPUs. This is useful if you can not change the application. 47 | 48 | [cpu-sample-numcpus-lib](../docker-images/openjdk-numcpus-lib/) 49 | Image has a custom LD_PRELOAD already compiled and an environment variable exported. 50 | 51 | ``` 52 | > docker run -e _NUM_CPUS=2 -e LOOP=2 --cpus=2 cpu-sample-numcpus-lib 53 | Jun 20, 2017 9:49:23 PM com.mycompany.cpu.sample.App main 54 | INFO: cores 2 55 | points: 200000000 56 | points_per_thread: 100000000 57 | number_of_executions: 2 58 | pi = 3.14149862 error = 0.0030% in: 2107 ms 59 | pi = 3.14148676 error = 0.0034% in: 3310 ms 60 | ``` 61 | 62 | 63 | -------------------------------------------------------------------------------- /memory-sample/README.md: -------------------------------------------------------------------------------- 1 | # memory-sample 2 | This project is a demo for common memory issues faced when running Java applications inside Docker containers. 3 | 4 | ## How to run the demo 5 | 6 | Build the project: 7 | 8 | ``` 9 | mvn clean install 10 | ``` 11 | 12 | A new image will be generated. See: 13 | 14 | ``` 15 | > docker images 16 | 17 | REPOSITORY TAG IMAGE ID CREATED SIZE 18 | memory-sample latest 9a4703216893 2 days ago 610 MB 19 | ``` 20 | 21 | Give more than 100M memory to your Docker machine and then run a container, giving 100M to it: 22 | 23 | ``` 24 | > docker run --memory 100M memory-sample 25 | ``` 26 | 27 | The application will print how much memory it is using. You will notice that Docker will kill the application when it exceeds 100M of memory. There will be no OutOfMemoryException. You will also notice that the application will print the total memory available as something above 100M. This is the VM default when no max memory is specified. This shows that the java vm is not aware of the `--memory` switch used by Docker. This switch just means "kill the application if it allocates more than 100M", and this is just what Docker does. 28 | 29 | To fix this, run the application like this: 30 | 31 | ``` 32 | > docker run --memory 100M -e JAVA_OPTIONS='-Xmx64m' memory-sample 33 | ``` 34 | 35 | 36 | As of Java 8u131 there is now an experimental VM option so the JVM is aware of the --memory switch used by Docker: 37 | 38 | ``` 39 | > docker run --memory 100M -e JAVA_OPTIONS='-XX:+UnlockExperimentalVMOptions -XX:+UseCGroupMemoryLimitForHeap -XX:MaxRAMFraction=1' memory-sample 40 | ``` 41 | 42 | The JAVA_OPTION variable is defined in our pom.xml, when the image is created. 43 | 44 | You'll notice that when running with JAVA_OPTIONS switch, the total memory reported by the application will be 100M and you'll get an OutOfMemoryException, as expected, when the memory allocated goes over 100M. 45 | 46 | There is an excellent discussion about this on this article, by Rafael Benevides: https://developers.redhat.com/blog/2017/03/14/java-inside-docker/ 47 | 48 | I had also a lot of interesting insights about this subject from Elijah Zupancic (https://twitter.com/shitsukoisaru), from [Joyent](https://www.joyent.com/). 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /integration-tests-sample/README.md: -------------------------------------------------------------------------------- 1 | # integration-tests-sample 2 | 3 | This sample application shows how to package a simple Java/MongoDB application with Docker. It highlights also how to do integration tests. 4 | 5 | ## Running MongoDB and the application: 6 | 7 | 8 | Compile the application and generate the docker images 9 | 10 | ``` 11 | mvn clean install -Papp-docker-image 12 | ``` 13 | 14 | 15 | Start all the services 16 | 17 | ``` 18 | docker-compose --file docker/docker-compose.yml up -d 19 | ``` 20 | 21 | Run the application 22 | ``` 23 | docker-compose --file docker/docker-compose.yml \ 24 | run integration-tests-sample \ 25 | java -jar \ 26 | /maven/jar/integration-tests-sample-1.0-SNAPSHOT-jar-with-dependencies.jar mongo 27 | ``` 28 | 29 | Stop all the services 30 | ``` 31 | docker-compose --file docker/docker-compose.yml down 32 | ``` 33 | 34 | ## Running integration tests: 35 | 36 | Start mongo service with a special `docker-compose-yml` file created for testing. This file does not map any ports or volumes: 37 | 38 | ``` 39 | docker-compose --file src/test/resources/docker-compose.yml up -d mongo 40 | ``` 41 | 42 | Run the application 43 | 44 | ``` 45 | docker-compose --file src/test/resources/docker-compose.yml \ 46 | run integration-tests-sample \ 47 | java -jar /maven/jar/integration-tests-sample-1.0-SNAPSHOT-jar-with-dependencies.jar mongo 48 | ``` 49 | 50 | Run the integration tests 51 | 52 | ``` 53 | docker-compose --file src/test/resources/docker-compose.yml \ 54 | run integration-tests-sample-tests mvn -f /maven/code/pom.xml \ 55 | -Dmaven.repo.local=/m2/repository -Pintegration-test verify 56 | ``` 57 | 58 | If you want to remote debug tests, run instead 59 | ``` 60 | docker run -v ~/.m2/repository:/m2/repository \ 61 | -p 5005:5005 --link mongo:mongo \ 62 | --net resources_default \ 63 | integration-tests-sample-tests mvn -f /maven/code/pom.xml \ 64 | -Dmaven.repo.local=/m2/repository -Pintegration-test\ 65 | verify -Dmaven.failsafe.debug 66 | ``` 67 | 68 | Stop all the services 69 | 70 | ``` 71 | docker-compose --file src/test/resources/docker-compose.yml down 72 | ``` 73 | 74 | No state will be saved, so next time you run the tests, your database will be empty. 75 | 76 | If you are running integration tests inside a tool like Jenkins, for example, you may want to be able to run simultaneous tests. In this case, you need to start the application in a separate network. To do that, use the `-p` switch with docker: 77 | 78 | ``` 79 | docker-compose -p app-$BUILD_NUMBER --file docker/docker-compose.yml up -d 80 | ``` 81 | In this example, the variable `BUILD_NUMBER` is a Jenkins variable that contains the current build number. Running this way, the services started by a build will run in a network used only by that build. 82 | 83 | -------------------------------------------------------------------------------- /cpu-sample/src/main/java/com/mycompany/cpu/sample/MonteCarloPI.java: -------------------------------------------------------------------------------- 1 | package com.mycompany.cpu.sample; 2 | 3 | import java.math.BigDecimal; 4 | import java.util.ArrayList; 5 | import java.util.concurrent.ExecutionException; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.Future; 9 | import java.util.concurrent.ThreadLocalRandom; 10 | import java.util.logging.Level; 11 | import java.util.logging.Logger; 12 | 13 | /** 14 | * 15 | * @author babadopulos 16 | */ 17 | public class MonteCarloPI { 18 | 19 | private final int number_of_threads; 20 | private final ExecutorService executor; 21 | private final BigDecimal pointsPerThread; 22 | 23 | public MonteCarloPI(int number_of_threads, BigDecimal pointsPerThread) { 24 | this.number_of_threads = number_of_threads; 25 | this.pointsPerThread = pointsPerThread; 26 | this.executor = Executors.newFixedThreadPool(number_of_threads); 27 | 28 | } 29 | 30 | public BigDecimal calculate() { 31 | ArrayList> threads = new ArrayList<>(); 32 | 33 | for (int i = 0; i < this.number_of_threads; i++) { 34 | Future future = executor.submit(() -> { 35 | return new Calculate(pointsPerThread).calculate(); 36 | }); 37 | threads.add(future); 38 | } 39 | 40 | executor.shutdown(); 41 | 42 | while (!executor.isTerminated()) { 43 | try { 44 | Thread.sleep(10); 45 | } catch (InterruptedException ex) { 46 | Logger.getLogger(MonteCarloPI.class.getName()).log(Level.SEVERE, null, ex); 47 | } 48 | } 49 | 50 | BigDecimal totalCount = BigDecimal.ZERO; 51 | 52 | for (Future thread : threads) { 53 | try { 54 | totalCount = totalCount.add(thread.get()); 55 | } catch (InterruptedException | ExecutionException ex) { 56 | Logger.getLogger(MonteCarloPI.class.getName()).log(Level.SEVERE, null, ex); 57 | } 58 | } 59 | 60 | BigDecimal result = totalCount.multiply(BigDecimal.valueOf(4l)).divide(pointsPerThread.multiply(BigDecimal.valueOf(number_of_threads))); 61 | 62 | return result; 63 | } 64 | 65 | private class Calculate extends Thread { 66 | 67 | private BigDecimal points; 68 | 69 | public Calculate(BigDecimal points) { 70 | this.points = points; 71 | } 72 | 73 | public BigDecimal calculate() { 74 | 75 | ThreadLocalRandom rnd = java.util.concurrent.ThreadLocalRandom.current(); 76 | 77 | BigDecimal count = BigDecimal.ZERO; 78 | 79 | while (points.compareTo(BigDecimal.ZERO) > 0) { 80 | double x = rnd.nextDouble(); 81 | double y = rnd.nextDouble(); 82 | 83 | if (((Math.pow(x, 2) + Math.pow(y, 2)) <= 1)) { 84 | count = count.add(BigDecimal.ONE); 85 | } 86 | points = points.add(BigDecimal.valueOf(-1l)); 87 | } 88 | 89 | return count; 90 | } 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /memory-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.mycompany 5 | memory-sample 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | UTF-8 10 | 1.8 11 | 1.8 12 | 13 | 14 | 15 | 16 | 17 | maven-assembly-plugin 18 | 19 | 20 | 21 | com.mycompany.memory.sample.App 22 | 23 | 24 | 25 | jar-with-dependencies 26 | 27 | 28 | 29 | 30 | make-assembly 31 | package 32 | 33 | single 34 | 35 | 36 | 37 | 38 | 39 | io.fabric8 40 | docker-maven-plugin 41 | 0.20.0 42 | 43 | 44 | 45 | memory-sample 46 | 47 | openjdk:latest 48 | 49 | artifact 50 | 51 | 52 | 53 | ${basedir}/target 54 | 55 | ${project.name}-${project.version}-jar-with-dependencies.jar 56 | 57 | / 58 | 59 | 60 | 61 | 62 | java -jar $JAVA_OPTIONS maven/${project.name}-${project.version}-jar-with-dependencies.jar 63 | 64 | 65 | 66 | 67 | 68 | 69 | docker-build 70 | package 71 | 72 | build 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /debugging-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.mycompany 5 | debugging-sample 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | UTF-8 10 | 1.8 11 | 1.8 12 | 13 | 14 | 15 | 16 | 17 | maven-assembly-plugin 18 | 19 | 20 | 21 | com.mycompany.debugging.sample.App 22 | 23 | 24 | 25 | jar-with-dependencies 26 | 27 | 28 | 29 | 30 | make-assembly 31 | package 32 | 33 | single 34 | 35 | 36 | 37 | 38 | 39 | io.fabric8 40 | docker-maven-plugin 41 | 0.20.0 42 | 43 | 44 | 45 | debugging-sample 46 | 47 | openjdk:latest 48 | 49 | artifact 50 | 51 | 52 | 53 | ${basedir}/target 54 | 55 | ${project.name}-${project.version}-jar-with-dependencies.jar 56 | 57 | / 58 | 59 | 60 | 61 | 62 | java -jar $JAVA_OPTIONS maven/${project.name}-${project.version}-jar-with-dependencies.jar 63 | 64 | 65 | 66 | 67 | 68 | 69 | docker-build 70 | package 71 | 72 | build 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /entropy-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.mycompany 5 | entropy-sample 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | UTF-8 10 | 1.8 11 | 1.8 12 | 13 | 14 | 15 | 16 | 17 | maven-assembly-plugin 18 | 19 | 20 | 21 | com.mycompany.entropy.sample.App 22 | 23 | 24 | 25 | jar-with-dependencies 26 | 27 | 28 | 29 | 30 | make-assembly 31 | package 32 | 33 | single 34 | 35 | 36 | 37 | 38 | 39 | io.fabric8 40 | docker-maven-plugin 41 | 0.20.0 42 | 43 | 44 | 45 | entropy-sample 46 | 47 | openjdk:latest 48 | 49 | artifact 50 | 51 | 52 | 53 | ${basedir}/target 54 | 55 | ${project.name}-${project.version}-jar-with-dependencies.jar 56 | 57 | / 58 | 59 | 60 | 61 | 62 | java -jar $JAVA_OPTIONS maven/${project.name}-${project.version}-jar-with-dependencies.jar 63 | 64 | 65 | 66 | entropy-sample-nonblocking 67 | 68 | openjdk-nonblocking:latest 69 | 70 | artifact 71 | 72 | 73 | 74 | ${basedir}/target 75 | 76 | ${project.name}-${project.version}-jar-with-dependencies.jar 77 | 78 | / 79 | 80 | 81 | 82 | 83 | java -jar $JAVA_OPTIONS maven/${project.name}-${project.version}-jar-with-dependencies.jar 84 | 85 | 86 | 87 | 88 | 89 | 90 | docker-build 91 | package 92 | 93 | build 94 | 95 | 96 | 97 | 98 | 99 | 100 | entropy-sample 101 | -------------------------------------------------------------------------------- /cpu-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.mycompany 5 | cpu-sample 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | UTF-8 10 | 1.8 11 | 1.8 12 | 13 | 14 | 15 | 16 | 17 | maven-assembly-plugin 18 | 19 | 20 | 21 | com.mycompany.cpu.sample.App 22 | 23 | 24 | 25 | jar-with-dependencies 26 | 27 | 28 | 29 | 30 | make-assembly 31 | package 32 | 33 | single 34 | 35 | 36 | 37 | 38 | 39 | io.fabric8 40 | docker-maven-plugin 41 | 0.20.0 42 | 43 | 44 | 45 | cpu-sample 46 | 47 | openjdk:latest 48 | 49 | artifact 50 | 51 | 52 | 53 | ${basedir}/target 54 | 55 | ${project.name}-${project.version}-jar-with-dependencies.jar 56 | 57 | / 58 | 59 | 60 | 61 | 62 | java -jar $JAVA_OPTIONS maven/${project.name}-${project.version}-jar-with-dependencies.jar $LOOP 63 | 64 | 65 | 66 | cpu-sample-numcpus-lib 67 | 68 | openjdk-numcpus-lib 69 | 70 | artifact 71 | 72 | 73 | 74 | ${basedir}/target 75 | 76 | ${project.name}-${project.version}-jar-with-dependencies.jar 77 | 78 | / 79 | 80 | 81 | 82 | 83 | 84 | java -jar $JAVA_OPTIONS maven/${project.name}-${project.version}-jar-with-dependencies.jar $LOOP 85 | 86 | 87 | 88 | 89 | 90 | 91 | docker-build 92 | package 93 | 94 | build 95 | 96 | 97 | 98 | 99 | 100 | 101 | cpu-sample 102 | -------------------------------------------------------------------------------- /integration-tests-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.mycompany 6 | integration-tests-sample 7 | 1.0-SNAPSHOT 8 | jar 9 | 10 | integration-tests-sample 11 | http://maven.apache.org 12 | 13 | 14 | UTF-8 15 | 16 | 17 | 18 | 19 | 20 | org.mongodb.morphia 21 | morphia 22 | 1.3.2 23 | 24 | 25 | 26 | org.mongodb 27 | mongo-java-driver 28 | 3.4.2 29 | 30 | 31 | 32 | junit 33 | junit 34 | 4.10 35 | test 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | org.apache.maven.plugins 44 | maven-compiler-plugin 45 | 2.5.1 46 | 47 | 1.7 48 | 1.7 49 | 50 | 51 | 52 | maven-assembly-plugin 53 | 54 | 55 | 56 | com.mycompany.integrationtests.sample.App 57 | 58 | 59 | 60 | jar-with-dependencies 61 | 62 | 63 | 64 | 65 | make-assembly 66 | package 67 | 68 | single 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | app-docker-image 81 | 82 | 83 | 84 | io.fabric8 85 | docker-maven-plugin 86 | 0.20.0 87 | 88 | 89 | 90 | integration-tests-sample 91 | 92 | openjdk:latest 93 | 94 | project 95 | 96 | 97 | 98 | ${basedir}/target 99 | 100 | integration-tests-sample-1.0-SNAPSHOT-jar-with-dependencies.jar 101 | 102 | /jar 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | integration-tests-sample-tests 113 | 114 | maven:latest 115 | 116 | 117 | 5005 118 | 119 | 120 | project 121 | 122 | 123 | 124 | ${basedir}/src 125 | 126 | **/** 127 | 128 | code/src 129 | 130 | 131 | ${basedir} 132 | 133 | pom.xml 134 | 135 | code 136 | 137 | 138 | ${basedir}/src/test/resources 139 | 140 | settings.xml 141 | 142 | /m2 143 | 144 | 145 | 146 | 147 | 148 | /m2 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | docker-build 157 | package 158 | 159 | build 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | integration-test 169 | 170 | 171 | 172 | org.apache.maven.plugins 173 | maven-failsafe-plugin 174 | 2.19.1 175 | 176 | 177 | integration-test 178 | integration-test 179 | 180 | integration-test 181 | 182 | 183 | 184 | verify 185 | verify 186 | 187 | verify 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | --------------------------------------------------------------------------------