diff --git a/README.md b/README.md index e757182..5c9cdba 100644 --- a/README.md +++ b/README.md @@ -191,6 +191,21 @@ This will fire up multiple containers running Spring Boot Apps inside Docker Win ![spring-cloud-example-running-docker-windows-containers](https://github.com/jonashackt/ansible-windows-docker-springboot/blob/master/spring-cloud-example-running-docker-windows-containers.png) +## How to scale multiple Spring Boot Apps inside a Docker Windows Containers with Ansible, docker-compose and Spring Cloud Netflix ([step3-multiple-spring-boot-apps](https://github.com/jonashackt/ansible-windows-docker-springboot/tree/master/step4-multiple-spring-boot-apps-docker-compose)) + + +Everything needed here is inside [step4-multiple-spring-boot-apps-docker-compose](https://github.com/jonashackt/ansible-windows-docker-springboot/tree/master/step4-multiple-spring-boot-apps-docker-compose). Be sure to have cloned and (Maven-) build the complete Spring Cloud example apps [spring-cloud-netflix-docker](https://github.com/jonashackt/spring-cloud-netflix-docker). Let´s cd into `step4-multiple-spring-boot-apps-docker-compose` and run the playbook: + +``` +ansible-playbook -i hostsfile ansible-windows-docker-springboot.yml --extra-vars "host=ansible-windows-docker-springboot-dev" +``` + +This will fire up multiple containers running Spring Boot Apps inside Docker Windows Containers on your Windows box. They will leverage the power of Spring Cloud Netflix with Zuul as a Proxy and Eureka as Service Registry (which dynamically tells Zuul, which Apps to route). + +![spring-cloud-example-running-docker-windows-containers-docker-compose](https://github.com/jonashackt/ansible-windows-docker-springboot/blob/master/spring-cloud-example-running-docker-windows-containers-docker-compose.png) + + + ## Best practices ##### Using Powershell on Host to Connect to Container diff --git a/spring-cloud-example-running-docker-windows-containers-docker-compose.png b/spring-cloud-example-running-docker-windows-containers-docker-compose.png new file mode 100644 index 0000000..d4e61ab Binary files /dev/null and b/spring-cloud-example-running-docker-windows-containers-docker-compose.png differ diff --git a/step0-packer-windows-vagrantbox/automated_windows_installation.png b/step0-packer-windows-vagrantbox/automated_windows_installation.png new file mode 100644 index 0000000..78df396 Binary files /dev/null and b/step0-packer-windows-vagrantbox/automated_windows_installation.png differ diff --git a/step3-multiple-spring-boot-apps/ansible-windows-docker-springboot.yml b/step3-multiple-spring-boot-apps/ansible-windows-docker-springboot.yml index d0d97e3..b05b49d 100644 --- a/step3-multiple-spring-boot-apps/ansible-windows-docker-springboot.yml +++ b/step3-multiple-spring-boot-apps/ansible-windows-docker-springboot.yml @@ -40,7 +40,18 @@ port: 8091 jar_input_path: "../../spring-cloud-netflix-docker/weatherbackend/target/weatherbackend-0.0.1-SNAPSHOT.jar" - - name: Copy docker-compose.yml to directory C:\spring-boot (this is only additionally, compose isn´t necessary for the apps to run) - win_template: - src: "../../spring-cloud-netflix-docker/docker-compose.yml" - dest: "{{base_path}}\\docker-compose.yml" + - name: Run Weather Backend Service 3 (Spring Boot app) inside Windows-Docker-Container + include: spring-boot-app-windows-docker.yml + vars: + spring_boot_app: + name: weatherbackend-third + port: 8092 + jar_input_path: "../../spring-cloud-netflix-docker/weatherbackend/target/weatherbackend-0.0.1-SNAPSHOT.jar" + + - name: Run Weather Backend Service 4 (Spring Boot app) inside Windows-Docker-Container + include: spring-boot-app-windows-docker.yml + vars: + spring_boot_app: + name: weatherbackend-forth + port: 8093 + jar_input_path: "../../spring-cloud-netflix-docker/weatherbackend/target/weatherbackend-0.0.1-SNAPSHOT.jar" \ No newline at end of file diff --git a/step4-multiple-spring-boot-apps-docker-compose/ansible-windows-docker-springboot.yml b/step4-multiple-spring-boot-apps-docker-compose/ansible-windows-docker-springboot.yml new file mode 100644 index 0000000..f4456a6 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/ansible-windows-docker-springboot.yml @@ -0,0 +1,35 @@ +--- +- hosts: "{{host}}" + vars: + base_path: "C:\\spring-boot" + service_registry_name: eureka-serviceregistry + services: + - name: eureka-serviceregistry + path_to_jar: "../../spring-cloud-netflix-docker/eureka-serviceregistry/target/eureka-serviceregistry-0.0.1-SNAPSHOT.jar" + port: 8761 + - name: zuul-edgeservice + path_to_jar: "../../spring-cloud-netflix-docker/zuul-edgeservice/target/zuul-edgeservice-0.0.1-SNAPSHOT.jar" + port: 8080 + - name: weatherbackend + path_to_jar: "../../spring-cloud-netflix-docker/weatherbackend/target/weatherbackend-0.0.1-SNAPSHOT.jar" + port: 8090 + - name: weatherbackend-second + path_to_jar: "../../spring-cloud-netflix-docker/weatherbackend/target/weatherbackend-0.0.1-SNAPSHOT.jar" + port: 8090 + + tasks: + - name: Create base directory C:\spring-boot, if not there + win_file: path={{base_path}} state=directory + + - name: Preparing the Spring Boot App´s Files for later docker-compose run + include: spring-boot-app-prepare.yml + with_items: "{{ vars.services }}" + + - name: Template docker-compose.yml to directory C:\spring-boot + win_template: + src: "templates/docker-compose.j2" + dest: "{{base_path}}\\docker-compose.yml" + + # Run docker-compose + + # Do Healthchecks \ No newline at end of file diff --git a/step4-multiple-spring-boot-apps-docker-compose/group_vars/all.yml b/step4-multiple-spring-boot-apps-docker-compose/group_vars/all.yml new file mode 100644 index 0000000..ef6e29d --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/group_vars/all.yml @@ -0,0 +1,7 @@ +# http://docs.ansible.com/ansible/intro_windows.html#windows-how-does-it-work + +ansible_port: 55986 # not 5986, as we would use for non-virtualized environments +ansible_connection: winrm + +# The following is necessary for Python 2.7.9+ when using default WinRM self-signed certificates: +ansible_winrm_server_cert_validation: ignore \ No newline at end of file diff --git a/step4-multiple-spring-boot-apps-docker-compose/group_vars/ansible-windows-docker-springboot-dev.yml b/step4-multiple-spring-boot-apps-docker-compose/group_vars/ansible-windows-docker-springboot-dev.yml new file mode 100644 index 0000000..394a490 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/group_vars/ansible-windows-docker-springboot-dev.yml @@ -0,0 +1,4 @@ +# http://docs.ansible.com/ansible/intro_windows.html#windows-how-does-it-work + +ansible_user: vagrant +ansible_password: vagrant \ No newline at end of file diff --git a/step4-multiple-spring-boot-apps-docker-compose/group_vars/ansible-windows-docker-springboot-qa.yml b/step4-multiple-spring-boot-apps-docker-compose/group_vars/ansible-windows-docker-springboot-qa.yml new file mode 100644 index 0000000..394a490 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/group_vars/ansible-windows-docker-springboot-qa.yml @@ -0,0 +1,4 @@ +# http://docs.ansible.com/ansible/intro_windows.html#windows-how-does-it-work + +ansible_user: vagrant +ansible_password: vagrant \ No newline at end of file diff --git a/step4-multiple-spring-boot-apps-docker-compose/hostsfile b/step4-multiple-spring-boot-apps-docker-compose/hostsfile new file mode 100644 index 0000000..ad0ac10 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/hostsfile @@ -0,0 +1,5 @@ +[ansible-windows-docker-springboot-dev] +127.0.0.1 + +[ansible-windows-docker-springboot-qa] +127.0.0.1 \ No newline at end of file diff --git a/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-health-check.yml b/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-health-check.yml new file mode 100644 index 0000000..ad84677 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-health-check.yml @@ -0,0 +1,39 @@ +--- + - name: Obtain the Docker Container´s internal IP address (because localhost doesn´t work for now https://github.com/docker/for-win/issues/458) + win_shell: "docker inspect -f {% raw %}'{{ .NetworkSettings.Networks.nat.IPAddress }}' {% endraw %} {{spring_boot_app.name}} {{ '>' }} container_ip.txt" + + - name: Get the Docker Container´s internal IP address from the temporary txt-file (we have to do this because of templating problems, see http://stackoverflow.com/a/32279729/4964553) + win_shell: cat container_ip.txt + register: win_shell_txt_return + + - name: Define the IP as variable + set_fact: + docker_container_ip: "{{ win_shell_txt_return.stdout.splitlines()[0] }}" + + - debug: + msg: "Your Docker Container has the internal IP {{ docker_container_ip }} --> Let´s do a health-check against this URI: 'http://{{ docker_container_ip }}:{{spring_boot_app.port}}/health'" + + ###### Smoke test, if app has booted up correctly + - name: Wait until our Spring Boot app is up & running + win_uri: + url: "http://{{ docker_container_ip }}:{{spring_boot_app.port}}/health" + method: GET + register: health_result + until: health_result.status_code == 200 + retries: 10 + delay: 5 + ignore_errors: yes + + - name: Show some more info, only when health check went wrong + win_uri: + url: "http://{{ docker_container_ip }}:{{spring_boot_app.port}}/health" + method: GET + register: health_bad_result + ignore_errors: yes + when: health_result|failed + + - name: Show some more info, only when health check went wrong + debug: + msg: "The app reacted with: {{health_bad_result}}" + when: health_result|failed + diff --git a/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-prepare.yml b/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-prepare.yml new file mode 100644 index 0000000..4896879 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-prepare.yml @@ -0,0 +1,24 @@ +--- + - name: Defining needed variables + set_fact: + spring_boot_app: + name: "{{ item.name }}" + port: "{{ item.port }}" + jar: "{{ item.path_to_jar }}" + + - name: Preparing the following Spring Boot App´s Files for docker-compose run + debug: + msg: "Processing '{{spring_boot_app.name}}' with port '{{ spring_boot_app.port }}'" + + - name: Create directory C:\spring-boot\spring_boot_app.name, if not there + win_file: path={{base_path}}\\{{spring_boot_app.name}} state=directory + + - name: Template and copy Spring Boot app´s Dockerfile to directory C:\spring-boot\spring_boot_app.name + win_template: + src: "templates/Dockerfile-SpringBoot-App.j2" + dest: "{{base_path}}\\{{spring_boot_app.name}}\\Dockerfile" + + - name: Copy Spring Boot app´s jar-File to directory C:\spring-boot\spring_boot_app.name + win_copy: + src: "{{spring_boot_app.jar}}" + dest: "{{base_path}}\\{{spring_boot_app.name}}\\{{spring_boot_app.name}}.jar" diff --git a/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-windows-docker.yml b/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-windows-docker.yml new file mode 100644 index 0000000..36392d3 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/spring-boot-app-windows-docker.yml @@ -0,0 +1,44 @@ +--- + - name: Preparing to run Spring Boot App in Docker Windows Container + debug: + msg: "Processing '{{spring_boot_app.name}}'" + + - name: Define some needed variables + set_fact: + spring_boot_app_path: "{{base_path}}\\{{spring_boot_app.name}}" + + - name: Create directory C:\spring-boot\spring_boot_app.name, if not there + win_file: path={{spring_boot_app_path}} state=directory + + - name: Template and copy Spring Boot app´s Dockerfile to directory C:\spring-boot\spring_boot_app.name + win_template: + src: "templates/Dockerfile-SpringBoot-App.j2" + dest: "{{spring_boot_app_path}}\\Dockerfile" + + - name: Copy Spring Boot app´s jar-File to directory C:\spring-boot\spring_boot_app.name + win_copy: + src: "{{jar_input_path}}" + dest: "{{spring_boot_app_path}}\\{{spring_boot_app.name}}.jar" + + - name: Stop the Service Docker container + win_shell: docker stop {{spring_boot_app.name}} + ignore_errors: yes + + - name: Remove the Service Docker container + win_shell: docker rm {{spring_boot_app.name}} --force + ignore_errors: yes + + - name: Remove the Service Docker image + win_shell: docker rmi {{spring_boot_app.name}}:latest --force + ignore_errors: yes + + - name: Build the Service Docker image + win_shell: docker build . --tag {{spring_boot_app.name}}:latest + args: + chdir: "{{spring_boot_app_path}}" + + - name: Run the Service Docker container + win_shell: "docker run -d --publish {{spring_boot_app.port}}:{{spring_boot_app.port}} --name={{spring_boot_app.name}} --restart=unless-stopped {{spring_boot_app.name}}:latest" + + - name: Healthchecking the Spring Boot app + include: spring-boot-app-health-check.yml diff --git a/step4-multiple-spring-boot-apps-docker-compose/templates/Dockerfile-SpringBoot-App.j2 b/step4-multiple-spring-boot-apps-docker-compose/templates/Dockerfile-SpringBoot-App.j2 new file mode 100644 index 0000000..01f0da3 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/templates/Dockerfile-SpringBoot-App.j2 @@ -0,0 +1,17 @@ +#jinja2: newline_sequence:'\r\n' +FROM springboot-oraclejre-nanoserver:latest + +MAINTAINER Jonas Hecht + +ENV REGISTRY_HOST {{service_registry_name}} +ENV SPRINGBOOT_APP_NAME {{spring_boot_app.name}} + +# Expose the apps Port +EXPOSE {{spring_boot_app.port}} + +# Add Spring Boot app.jar to Container +ADD {{spring_boot_app.name}}.jar app.jar + +# Fire up our Spring Boot app by default +CMD ["java.exe", "-jar app.jar --server.port={{spring_boot_app.port}}"] + diff --git a/step4-multiple-spring-boot-apps-docker-compose/templates/docker-compose.j2 b/step4-multiple-spring-boot-apps-docker-compose/templates/docker-compose.j2 new file mode 100644 index 0000000..c9a5c62 --- /dev/null +++ b/step4-multiple-spring-boot-apps-docker-compose/templates/docker-compose.j2 @@ -0,0 +1,35 @@ +#jinja2: newline_sequence:'\r\n' +version: '3.1' + +services: + + eureka-serviceregistry: + build: ./eureka-serviceregistry + ports: + - "8761:8761" + tty: + true + + # no portbinding here - the actual services should be accessible through Zuul proxy + weatherbackend: + build: ./weatherbackend + tty: + true + + # no portbinding here - the actual services should be accessible through Zuul proxy + weatherbackend-second: + build: ./weatherbackend-second + tty: + true + + zuul-edgeservice: + build: ./zuul-edgeservice + ports: + - "8080:8080" + tty: + true + +networks: + default: + external: + name: "nat" \ No newline at end of file