'рд╣реИрд▓реЛ рд╡рд░реНрд▓реНрдб' рдореЗрдШ рдореЗрдВ рдЖрдкрдХреЛ

рджреБрдирд┐рдпрд╛ 2 рдкрд╛рдЧрд▓ рд╣реЛ рд░рд╣реА рд╣реИ, 2 + 2 рдХреИрд▓рдХреБрд▓реЗрдЯрд░ рдХреЛ рдмрд╛рджрд▓реЛрдВ рдореЗрдВ рдзрдХреЗрд▓ рд░рд╣реА рд╣реИред рд╣рдо рдХреНрдпреЛрдВ рдмрджрддрд░ рд╣реИрдВ? рдЖрдЗрдП рд╣реИрд▓реЛ рд╡рд░реНрд▓реНрдб рдХреЛ рддреАрди рдорд╛рдЗрдХреНрд░реЛрд╕рд░реНрд╡рд┐рд╕реЗрдЬ рдореЗрдВ рдзрдХреЗрд▓реЗрдВ, рдПрдХ рджреЛ рдЯреЗрд╕реНрдЯ рд▓рд┐рдЦреЗрдВ, рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдбреЙрдХреНрдпреВрдореЗрдВрдЯреЗрд╢рди рдкреНрд░рджрд╛рди рдХрд░реЗрдВ, рдПрдХ рд╕реБрдВрджрд░ рдЕрд╕реЗрдВрдмрд▓реА рдкрд╛рдЗрдк рд▓рд╛рдЗрди рдЦреАрдВрдЪреЗрдВ рдФрд░ рдПрдХ рд╕рд╢рд░реНрдд рдХреНрд▓рд╛рдЙрдб рдмрд┐рдХреНрд░реА рдкрд░ рдПрдХ рддреИрдирд╛рддреА рдкреНрд░рджрд╛рди рдХрд░реЗрдВ рдпрджрд┐ рдкрд░реАрдХреНрд╖рдг рд╕рдлрд▓рддрд╛рдкреВрд░реНрд╡рдХ рдкрд╛рд╕ рд╣реЛ рдЬрд╛рддреЗ рд╣реИрдВред рдЗрд╕рд▓рд┐рдП, рдпрд╣ рд▓реЗрдЦ рдПрдХ рдЙрджрд╛рд╣рд░рдг рджрд┐рдЦрд╛рдПрдЧрд╛ рдХрд┐ рдЙрддреНрдкрд╛рдж рд╡рд┐рдХрд╛рд╕ рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЛ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рд╕реЗ рддреИрдирд╛рддреА рддрдХ рдХреЗ рдЙрддреНрдкрд╛рджреЛрдВ рдХреЗ рдирд┐рд░реНрдорд╛рдг рдХреЗ рд▓рд┐рдП рдХреИрд╕реЗ рдмрдирд╛рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИред Ineteresno? рддрдм рдореИрдВ рдХрд╛рдд рдХреЗ рдиреАрдЪреЗ рдкреВрдЫрддрд╛ рд╣реВрдВ


рд░реВрдУ рдХрд╣рд╛рдБ рд╕реЗ рд╢реБрд░реВ рд╣реЛрддреА рд╣реИ ...?


рдПрдХ рдорд╛рддреГрднреВрдорд┐ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдПрдХ рдЙрддреНрдкрд╛рдж рд╣реИред рдпрд╣ рд╕рд╣реА рд╣реИ, рдЙрддреНрдкрд╛рдж рдПрдХ рд╡рд┐рдЪрд╛рд░ рд╕реЗ рд╢реБрд░реВ рд╣реЛрддрд╛ рд╣реИред рддреЛ рд╡рд┐рдЪрд╛рд░ рдпрд╣ рд╣реИ:


  • рдРрд╕реА рд╕реЗрд╡рд╛ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рдЬреЛ REST API рдХреЗ рдорд╛рдзреНрдпрдо рд╕реЗ 'рд╣реИрд▓реЛ рд╡рд░реНрд▓реНрдб' рд▓реМрдЯрд╛рдП
  • 'рд╣реИрд▓реЛ' рд╢рдмреНрдж рдПрдХ microservice рджреЗрддрд╛ рд╣реИ, рдЬрд┐рд╕реЗ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ рдФрд░ рдХрдорд╛рдВрдб 1 рджреНрд╡рд╛рд░рд╛ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ
  • рд╢рдмреНрдж 'рд╡рд░реНрд▓реНрдб' рджреВрд╕рд░рд╛ рджреЗрддрд╛ рд╣реИ, рдЬреЛ рдЯреАрдо рджреНрд╡рд╛рд░рд╛ рдЪрд▓рд╛рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ ред2
  • рдЯреАрдо_3 'рд╣реИрд▓реЛ' рдФрд░ 'рд╡рд░реНрд▓реНрдб' рдХреЗ рд▓рд┐рдП рдПрдХ рдПрдХреАрдХрд░рдг рд╕реЗрд╡рд╛ рд▓рд┐рдЦрддреА рд╣реИ

рдЯреВрд▓рд╕реЗрдЯ


  • рдУрдПрд╕ (рдбреЗрд╕реНрдХрдЯреЙрдк) - рдбреЗрдмрд┐рдпрди 9 рдЦрд┐рдВрдЪрд╛рд╡
  • IDE - IntelliJ IDEA 2019.1
  • рдЧрд┐рдЯ рд░реЗрдкреЛ - рдЧрд┐рдЯрд╣рдм
  • CI - рд╕рдореНтАНрдорд┐рд▓рди 5.4.0
  • рдорд╛рд╡реЗрди рд░реЗрдкреЛ - рдиреЗрдХреНрд╕рд╕
  • Openjdk 11
  • рдорд╛рд╡реЗрди 3.6.0
  • рдХреБрдмреЗрд░рдиреЗрдЯреНрд╕ 1.14 (1 рдорд╛рд╕реНрдЯрд░ + 1 рдХрд╛рд░реНрдпрдХрд░реНрддрд╛): рдХреИрд▓рд┐рдХреЛ рдиреЗрдЯрд╡рд░реНрдХ, рдиреЗрдЧреНрдиреЗрдХреНрд╕-рдЗрдВрдЧреНрд░реЗрд╕-рдирд┐рдпрдВрддреНрд░рдХ

рдорд╣рддреНрд╡рдкреВрд░реНрдг рдиреЛрдЯ: рд▓реЗрдЦ рд╕реБрдВрджрд░ рдХреЛрдб (рдХреЛрдбрд╕реНрдЯрд╛рдЗрд▓, рдЪреЗрдХрд╕реНрдЯрд╛рдЗрд▓, рдЬреЗрд╡рд╛рдбреЙрдХреНрд╕, рдПрд╕рдУрдПрд▓рдЖрдИрдбреА рдФрд░ рдЕрдиреНрдп buzzwords) рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдирд╣реАрдВ рд╣реИ рдФрд░ рдкреВрд░реНрдгрддрд╛ рдХреЗ рд▓рд┐рдП рдкрд╛рд▓реЗ рдЧрдП рд╕рдорд╛рдзрд╛рди (рдЖрдк рдЕрдВрддрд╣реАрди рд╣реИрд▓реЛ рд╡рд░реНрд▓реНрдб рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдВрддрд╣реАрди рдмрд╛рдд рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ)ред рдпрд╣ рдПрдХ рд╕рд╛рде рдХреЛрдб, рд╡рд┐рдирд┐рд░реНрджреЗрд╢реЛрдВ, рдкрд╛рдЗрдкрд▓рд╛рдЗрди рдЕрд╕реЗрдВрдмрд▓реА рдФрд░ рд╕рдмрдХреБрдЫ рдХреА рдбрд┐рд▓реАрд╡рд░реА рдХреЛ рдПрдХ рд╕рд╛рде рдХрд░рдиреЗ рдХреЗ рддрд░реАрдХреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИред

рд╕реЗрд╡рд╛ рд╕реЗ рдХреНрдпрд╛ рдмрдирддрд╛ рд╣реИ?


рдЕрдВрддрд┐рдо рдЙрддреНрдкрд╛рдж рдХреЗ рд░реВрдк рдореЗрдВ рд╕реЗрд╡рд╛ рдореЗрдВ рд╢рд╛рдорд┐рд▓ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП:


  • OpenAPI рдорд╛рдирдХ рдХреЗ рдпрд╛рдореНрд▓-рджрд╕реНрддрд╛рд╡реЗрдЬрд╝ рдХреЗ рд░реВрдк рдореЗрдВ рд╡рд┐рдирд┐рд░реНрджреЗрд╢рди рдФрд░ рдЕрдиреБрд░реЛрдз рдкрд░ рджреЗрдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рд╣реЛ (GET / dL)
  • рдкрд╣рд▓реЗ рдкреИрд░рд╛рдЧреНрд░рд╛рдл рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдПрдкреАрдЖрдИ рддрд░реАрдХреЗ
  • рд╕реЗрд╡рд╛ рд╢реБрд░реВ рдХрд░рдиреЗ рдФрд░ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХрд░рдиреЗ рдХреЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рдХреЗ рд╕рд╛рде README.md

рд╣рдо рдХреНрд░рдо рдореЗрдВ рд╕реЗрд╡рд╛рдУрдВ рдХрд╛ рд╡рд┐рд╢реНрд▓реЗрд╖рдг рдХрд░реЗрдВрдЧреЗред рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВ!


'рд╣реИрд▓реЛ' рдорд╛рдЗрдХреНрд░реЛрд╕рд┐рд╕реНрдЯ рд╕реЗрд╡рд╛


рд╡рд┐рд╢рд┐рд╖реНрдЯрддрд╛


рд╣рдо рд╕реНрд╡реИрдЧрд░ рд╕рдВрдкрд╛рджрдХ рдореЗрдВ рдРрдирдХ рд▓рд┐рдЦрддреЗ рд╣реИрдВ рдФрд░ рдЗрд╕реЗ рдУрдкрдирдПрдкреАрдЖрдИ рдпреБрдХреНрддрд┐ рдореЗрдВ рд░реВрдкрд╛рдВрддрд░рд┐рдд рдХрд░рддреЗ рд╣реИрдВред рд╕реНрд╡реИрдЧрд░ рдПрдбрд┐рдЯрд░ рдХреЛ рдПрдХ рдХрдорд╛рдВрдб рдХреЗ рд╕рд╛рде рдбреЙрдХрдЯрд░ рдореЗрдВ рд▓реЙрдиреНрдЪ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рд╕реНрд╡реИрдЧрд░ рдбреЙрдХреНрд╕ рдХрд╛ рдУрдкрдиреИрдкреА-рдбреЙрдХ рдореЗрдВ рд░реВрдкрд╛рдВрддрд░рдг рдПрдбрд┐рдЯрд░ рдХреЗ рдпреВрдЖрдИ рдореЗрдВ рдПрдХ рдмрдЯрди рджрдмрд╛рдХрд░ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЬреЛ рдХрд┐ POST / api / рдХрдиреНрд╡рд░реНрдЯ рд░рд┐рдХреНрд╡реЗрд╕реНрдЯ рдХреЛ http://lexverter.swagger.io рдкрд░ рднреЗрдЬрддрд╛ рд╣реИред рд╣реИрд▓реЛ рд╕реЗрд╡рд╛ рдХреА рдЕрдВрддрд┐рдо рд╡рд┐рд╢рд┐рд╖реНрдЯрддрд╛:


openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 

рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди


рдХреЛрдб рдХреЛ рд▓рд┐рдЦреЗ рдЬрд╛рдиреЗ рдХреЗ рджреГрд╖реНрдЯрд┐рдХреЛрдг рд╕реЗ рд╕реЗрд╡рд╛ рдореЗрдВ 3 рд╡рд░реНрдЧ рд╢рд╛рдорд┐рд▓ рд╣реИрдВ:


  • рд╕реЗрд╡рд╛ рд╡рд┐рдзрд┐рдпреЛрдВ рдХреЗ рд╕рд╛рде рдЗрдВрдЯрд░рдлреЗрд╕ (рд╡рд┐рдзрд┐ рдирд╛рдо рдСрдкрд░реЗрд╢рди рдХреЗ рд░реВрдк рдореЗрдВ рдХрд▓реНрдкрдирд╛ рдореЗрдВ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рд╣реИрдВ)
  • рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди
  • рдпреБрдХреНрддрд┐ рдХреЛ рд╕реЗрд╡рд╛ рдХреЗ рд╕рд╛рде рдЬреЛрдбрд╝рдиреЗ рдХреЗ рд▓рд┐рдП vertx verticle (рдкрд╣рд▓реЗ рдкреИрд░рд╛рдЧреНрд░рд╛рдл рд╕реЗ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рд╡рд┐рдзрд┐) - рдФрд░ http рд╕рд░реНрд╡рд░ рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП

Src рдореЗрдВ рдлрд╝рд╛рдЗрд▓ рд╕рдВрд░рдЪрдирд╛ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреА рд╣реИ:


pom.xml
 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <properties> <main.verticle>io.bihero.hello.HelloVerticle</main.verticle> <vertx.version>3.8.1</vertx.version> <logback.version>1.2.3</logback.version> <junit-jupiter.version>5.3.1</junit-jupiter.version> <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version> <junit-platform-surefire-provider.version>1.1.0</junit-platform-surefire-provider.version> <assertj-core.version>3.8.0</assertj-core.version> <allure.version>2.8.1</allure.version> <allure-maven.version>2.10.0</allure-maven.version> <aspectj.version>1.9.2</aspectj.version> <mockito.version>2.21.0</mockito.version> <rest-assured.version>3.0.0</rest-assured.version> </properties> <groupId>io.bihero</groupId> <artifactId>hello-microservice</artifactId> <version>1.0.0-SNAPSHOT</version> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> ```1.8</source> <target>1.8</target> </configuration> <executions> <execution> <id>default-compile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedSourcesDirectory>src/main/generated</generatedSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/main</arg> </compilerArgs> </configuration> </execution> <execution> <id>default-testCompile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedTestSourcesDirectory>src/test/generated</generatedTestSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/test</arg> </compilerArgs> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <configuration> <properties> <property> <name>listener</name> <value>io.qameta.allure.junit5.AllureJunit5</value> </property> </properties> <includes> <include>**/*Test*.java</include> </includes> <argLine> -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Djdk.net.URLClassPath.disableClassPathURLCheck=true </argLine> <systemProperties> <property> <name>allure.results.directory</name> <value>${project.basedir}/target/allure-results</value> </property> <property> <name>junit.jupiter.extensions.autodetection.enabled</name> <value>true</value> </property> </systemProperties> <reportFormat>plain</reportFormat> </configuration> <dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit-platform-surefire-provider.version}</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> </dependency> </dependencies> </plugin> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <version>${allure-maven.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> <dependencies> <dependency> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-webdav-jackrabbit</artifactId> <version>2.8</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <Main-Class>io.vertx.core.Launcher</Main-Class> <Main-Verticle>${main.verticle}</Main-Verticle> </manifestEntries> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource> </transformer> </transformers> <artifactSet> </artifactSet> <outputFile>${project.build.directory}/${project.artifactId}-fat.jar</outputFile> </configuration> </execution> </executions> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/version.txt</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <excludes> <exclude>**/version.txt</exclude> </excludes> </resource> </resources> </build> <distributionManagement> <site> <id>reports</id> <url>dav:https://nexus.dev.techedge.pro:8443/repository/reports/${project.artifactId}/</url> </site> </distributionManagement> <reporting> <excludeDefaults>true</excludeDefaults> <plugins> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <configuration> <resultsDirectory>${project.build.directory}/allure-results</resultsDirectory> <reportDirectory>${project.reporting.outputDirectory}/${project.version}/allure</reportDirectory> </configuration> </plugin> </plugins> </reporting> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-api-service</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-codegen</artifactId> <version>${vertx.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <!-- test &ndash;&gt;--> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-unit</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-junit5</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>${assertj-core.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.qameta.allure</groupId> <artifactId>allure-junit5</artifactId> <version>${allure.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> </dependencies> </project> 

HelloService.java
 package io.bihero.hello; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; import io.vertx.ext.web.api.generator.WebApiServiceGen; @WebApiServiceGen public interface HelloService { static HelloService create(Vertx vertx) { return new DefaultHelloService(vertx); } void getHelloWord(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); } 

DefaultHelloService.java
 package io.bihero.hello; import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; public class DefaultHelloService implements HelloService { private final Vertx vertx; public DefaultHelloService(Vertx vertx) { this.vertx = vertx; } @Override public void getHelloWord(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { resultHandler.handle(Future.succeededFuture(OperationResponse.completedWithPlainText(Buffer.buffer("Hello")))); } @Override public void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { vertx.fileSystem().readFile("doc.yaml", buffResult -> resultHandler.handle(Future.succeededFuture( OperationResponse.completedWithPlainText(buffResult.result())) )); } } 

HelloVerticle.java
 package io.bihero.hello; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.api.contract.openapi3.OpenAPI3RouterFactory; import io.vertx.serviceproxy.ServiceBinder; public class HelloVerticle extends AbstractVerticle { private HttpServer server; private MessageConsumer<JsonObject> consumer; @Override public void start(Promise<Void> promise) { startHelloService(); startHttpServer().future().setHandler(promise); } /** * This method closes the http server and unregister all services loaded to Event Bus */ @Override public void stop(){ this.server.close(); consumer.unregister(); } private void startHelloService() { consumer = new ServiceBinder(vertx).setAddress("service.hello") .register(HelloService.class, HelloService.create(getVertx())); } /** * This method constructs the router factory, mounts services and handlers and starts the http server * with built router * @return */ private Promise<Void> startHttpServer() { Promise<Void> promise = Promise.promise(); OpenAPI3RouterFactory.create(this.vertx, "/doc.yaml", openAPI3RouterFactoryAsyncResult -> { if (openAPI3RouterFactoryAsyncResult.succeeded()) { OpenAPI3RouterFactory routerFactory = openAPI3RouterFactoryAsyncResult.result(); // Mount services on event bus based on extensions routerFactory.mountServicesFromExtensions(); // Generate the router Router router = routerFactory.getRouter(); int port = config().getInteger("serverPort", 8080); String host = config().getString("serverHost", "localhost"); server = vertx.createHttpServer(new HttpServerOptions().setPort(port).setHost(host)); server.requestHandler(router).listen(ar -> { // Error starting the HttpServer if (ar.succeeded()) promise.complete(); else promise.fail(ar.cause()); }); } else { // Something went wrong during router factory initialization promise.fail(openAPI3RouterFactoryAsyncResult.cause()); } }); return promise; } } 

рд╕реЗрд╡рд╛ рдХреЗ рдЗрдВрдЯрд░рдлрд╝реЗрд╕ рдФрд░ рдЙрд╕рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди (@WebApiServiceGen рдПрдиреЛрдЯреЗрд╢рди рдХреЗ рдЕрдкрд╡рд╛рдж рдХреЗ рд╕рд╛рде) рдореЗрдВ рдХреБрдЫ рднреА рдЕрд╕рд╛рдорд╛рдиреНрдп рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдЖрдк рдЗрд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдкреНрд░рд▓реЗрдЦрди рдореЗрдВ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ), рд▓реЗрдХрд┐рди рдЖрдЗрдП рдЕрдзрд┐рдХ рд╡рд┐рд╕реНрддрд╛рд░ рд╕реЗ рд╡рд░реНрдЯрд┐рдХрд▓ рдХреНрд▓рд╛рд╕ рдХреЛрдб рдХреЛ рджреЗрдЦреЗрдВред


рдКрд░реНрдзреНрд╡рд╛рдзрд░ рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рдХрд╣реЗ рдЬрд╛рдиреЗ рд╡рд╛рд▓реЗ рджреЛ рддрд░реАрдХреЗ рджрд┐рд▓рдЪрд╕реНрдк рд╣реИрдВ:


  • startHelloService рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛ рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд╕рд╛рде рдПрдХ рд╡рд╕реНрддреБ рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдШрдЯрдирд╛ рдмрд╕ рдореЗрдВ рдкрддреЗ рдкрд░ рдмрд╛рдВрдзрддрд╛ рд╣реИ (рдКрдкрд░ рдХреЗ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рд╕реЗ рдкреИрд░рд╛рдореАрдЯрд░ x-vertx-event-bus -address рдпрд╛рдж рдХрд░реЗрдВ)
  • startHttpServer рд╕реЗрд╡рд╛ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдПрдХ рд░рд╛рдЙрдЯрд░ рдлреИрдХреНрдЯреНрд░реА рдмрдирд╛рддрд╛ рд╣реИ, рдПрдХ http рд╕рд░реНрд╡рд░ рдмрдирд╛рддрд╛ рд╣реИ рдФрд░ рд╕рднреА рдЖрдиреЗ рд╡рд╛рд▓реЗ http рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рд╣реИрдВрдбрд▓ рдХреЗ рд▓рд┐рдП рдмрдирд╛рдП рдЧрдП рд░рд╛рдЙрдЯрд░ рдХреЛ рд╣реБрдХ рдХрд░рддрд╛ рд╣реИ (рдпрджрд┐ рдпрд╣ gurbo рд╣реИ, рддреЛ GET / рдЕрдиреБрд░реЛрдз рд╡рд░реНрдЬрди рд╕рд░реНрд╡рд┐рд╕ рдмрд╕ рдореЗрдВ рдкрддрд╛ service.hello рдХреЗ рд╕рд╛рде рдЧрд┐рд░ рдЬрд╛рдПрдЧрд╛) рдФрд░ (рд╣рдо рд╡рд╣реАрдВ рдмрд╛рдБрдз рджреЗрддреЗ рд╣реИрдВ io.bihero.hello.HelloService рд╕реЗрд╡рд╛ рдХрд╛ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди) рдФрд░ рд╕реЗрд╡рд╛ рд╡рд┐рдзрд┐ рдирд╛рдо getHelloWord рдХреЗ рд╕рд╛рде )

рдпрд╣ dzharnik рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдФрд░ рдЪрд▓рд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ:


 mvn clean package #   java -Dlogback.configurationFile=./src/conf/logback-console.xml -jar target/hello-microservice-fat.jar -conf ./src/conf/config.json #   

рд▓реЙрдиреНрдЪ рд▓рд╛рдЗрди рдореЗрдВ рджреЛ рдкреИрд░рд╛рдореАрдЯрд░ рджрд┐рд▓рдЪрд╕реНрдк рд╣реИрдВ:


  • -Dlogback.configurationFile = / Src / conf / logback -sole.xml - logback рдХреЗ рд▓рд┐рдП рдХреЙрдиреНрдлрд╝рд┐рдЧ рдлрд╝рд╛рдЗрд▓ рдХрд╛ рдкрде (рдкреНрд░реЛрдЬреЗрдХреНрдЯ рдирд┐рд░реНрднрд░рддрд╛ рдореЗрдВ slf4j рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдФрд░ slf4j-api рдХреЗ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд░реВрдк рдореЗрдВ рд▓реЙрдЧрдмреИрдХ рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП)
  • -conf ./src/conf/config.json - рд╕реЗрд╡рд╛ рдХреЙрдиреНрдлрд┐рдЧ, рд╡рд╣ рдкреЛрд░реНрдЯ рдЬрд╣рд╛рдВ http REST API рдЦреЛрд▓рд╛ рдЬрд╛рдПрдЧрд╛, рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ:
     { "type": "file", "format": "json", "scanPeriod": 5000, "config": { "path": "/home/slava/JavaProjects/hello-world-to-cloud/hellomicroservice/src/conf/config.json" }, "serverPort": 8081, "serverHost": "0.0.0.0" } 

рдорд╛рд╡реЗрди рдХрд╛ рдЖрдЙрдЯрдкреБрдЯ рд╣рдорд╛рд░реЗ рд▓рд┐рдП рд╡рд┐рд╢реЗрд╖ рд░реВрдк рд╕реЗ рджрд┐рд▓рдЪрд╕реНрдк рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рд╕реЗрд╡рд╛ рдХреИрд╕реЗ рд╢реБрд░реВ рд╣реБрдИ, ( io.netty рдкреИрдХреЗрдЬ рдХреЗ рд▓рд┐рдП рд▓рдХрдбрд╝рд╣рд╛рд░рд╛ рдХреА рд╕реЗрдЯрд┐рдВрдЧ рдореЗрдВ, рд╕реНрддрд░ = "INFO" рд╕реЗрдЯ рд╣реИ )


рд╕реЗрд╡рд╛ рдХреИрд╕реЗ рд╢реБрд░реВ рдХрд░реЗрдВ
 2019-10-03 20:52:45,159 [vert.x-worker-thread-0] DEBUG isvpOpenAPIV3Parser: Loaded raw data: openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 2019-10-03 20:52:45,195 [vert.x-worker-thread-0] DEBUG isvpOpenAPIV3Parser: Parsed rootNode: {"openapi":"3.0.1","info":{"title":"Hello ;)","description":"Hello microservice","version":"1.0.0"},"servers":[{"url":"https://demo1.bihero.io/api/hello"}],"tags":[{"name":"hello","description":"Everything about saying 'Hello'"}],"paths":{"/":{"x-vertx-event-bus":{"address":"service.hello","timeout":"1000c"},"get":{"tags":["hello"],"summary":"Get 'Hello' word","operationId":"getHelloWord","responses":{"200":{"description":"OK"}}}},"/doc":{"x-vertx-event-bus":{"address":"service.hello","timeout":"1000c"},"get":{"tags":["hello_doc"],"summary":"Get 'Hello' microservice documentation","operationId":"getDoc","responses":{"200":{"description":"OK"}}}}},"components":{}} Oct 03, 2019 8:52:45 PM io.vertx.core.impl.launcher.commands.VertxIsolatedDeployer INFO: Succeeded in deploying verticle 

рд╣реБрд░реНрд░реЗ! рд╕реЗрд╡рд╛ рдХрд╛рд░реНрдп рдХрд░ рд░рд╣реА рд╣реИ, рдЖрдк рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ:


 curl http://127.0.0.1:8081/ Hello curl -v http://127.0.0.1:8081/doc openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 

рд╕реЗрд╡рд╛ GET / рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП рд╣реИрд▓реЛ рд╢рдмреНрдж рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░рддреА рд╣реИ, рдЬреЛ рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рд╕реЗ рдореЗрд▓ рдЦрд╛рддреА рд╣реИ, рдФрд░ рдпрд╣ рдмрд╛рдд рдХрд░ рд╕рдХрддреА рд╣реИ рдХрд┐ рдпрд╣ рдХреНрдпрд╛ рдХрд░ рд╕рдХрддрд╛ рд╣реИ, GET / doc рдЕрдиреБрд░реЛрдз рдХреЗ рд▓рд┐рдП рд╡рд┐рдирд┐рд░реНрджреЗрд╢ рджреЗ рд░рд╣рд╛ рд╣реИред рдХреВрд▓, рдЪрд▓реЛ рдЙрддреНрдкрд╛рджреЛрдВ рдкрд░ рдЬрд╛рдПрдВ!


рдпрд╣рд╛рдБ рдХреБрдЫ рдЧрдбрд╝рдмрдбрд╝ рд╣реИ ...


рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ, рдореИрдВрдиреЗ рд▓рд┐рдЦрд╛ рд╣реИ рдХрд┐ рдЕрд╕реЗрдВрдмрд▓реА рдХреЗ рджреМрд░рд╛рди рдорд╛рд╡реЗрди рдХрд╛ рдЙрддреНрдкрд╛рджрди рд╣рдорд╛рд░реЗ рд▓рд┐рдП рдмрд╣реБрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рдирд╣реАрдВ рд╣реИред рдореИрдВрдиреЗ рдЭреВрда рдмреЛрд▓рд╛, рдирд┐рд╖реНрдХрд░реНрд╖ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдФрд░ рдмрд╣реБрдд рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИред рд╣рдореЗрдВ рдкрд░реАрдХреНрд╖рдг рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП рдорд╛рд╡реЗрди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ рдФрд░ рдЬрдм рдкрд░реАрдХреНрд╖рдг рдЧрд┐рд░ рдЬрд╛рддреЗ рд╣реИрдВ, рддреЛ рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдЧрд┐рд░ рдЬрд╛рддреА рд╣реИред рд╡рд┐рдзрд╛рдирд╕рднрд╛ рдКрдкрд░ рдкрд╛рд░рд┐рдд рдХрд░ рджрд┐рдпрд╛, рдФрд░ рдпрд╣ рдЗрдВрдЧрд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдпрд╛ рддреЛ рдкрд░реАрдХреНрд╖рдг рдкрд╛рд░рд┐рдд рдХрд░ рджрд┐рдпрд╛, рдпрд╛ рд╡реЗ рдирд╣реАрдВ рд╣реИрдВред рдмреЗрд╢рдХ, рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдХреЛрдИ рднреА рдкрд░реАрдХреНрд╖рдг рдирд╣реАрдВ рд╣реИ, рдпрд╣ рдЙрдиреНрд╣реЗрдВ рд▓рд┐рдЦрдиреЗ рдХрд╛ рд╕рдордп рд╣реИ (рдпрд╣рд╛рдВ рдЖрдк рдХрд╛рд░реНрдпрдкреНрд░рдгрд╛рд▓реА рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рддрд░реНрдХ рджреЗ рд╕рдХрддреЗ рд╣реИрдВ рдХрд┐ рдХрдм, рдХреИрд╕реЗ рдФрд░ рдХреИрд╕реЗ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦрдиреЗ рд╣реИрдВ, рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рд╕реЗ рдкрд╣рд▓реЗ рдпрд╛ рдмрд╛рдж рдореЗрдВ, рд▓реЗрдХрд┐рди рд╣рдо рд▓реЗрдЦ рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рдПрдХ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдиреЛрдЯ рдпрд╛рдж рд░рдЦреЗрдВрдЧреЗ рдФрд░ рдЖрдЧреЗ рдмрдврд╝реЗрдВрдЧреЗ - рд╣рдо рдХреБрдЫ рдкрд░реАрдХреНрд╖рдг рд▓рд┐рдЦреЗрдВрдЧреЗ)ред
рдкрд╣рд▓рд╛ рдкрд░реАрдХреНрд╖рдг рд╡рд░реНрдЧ рд╣реИ, рдЗрд╕рдХреА рдкреНрд░рдХреГрддрд┐ рд╕реЗ, рдПрдХ рдЗрдХрд╛рдИ рдкрд░реАрдХреНрд╖рдг рдЬреЛ рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛ рдХреЗ рджреЛ рд╡рд┐рд╢рд┐рд╖реНрдЯ рддрд░реАрдХреЛрдВ рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рддрд╛ рд╣реИ:


HelloServiceTest.java
 package io.bihero.hello; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.api.OperationRequest; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(VertxExtension.class) public class HelloServiceTest { private HelloService helloService = HelloService.create(Vertx.vertx()); @Test @DisplayName("Test 'getHelloWord' method returns 'Hello' word") public void testHelloMethod(VertxTestContext testContext) { helloService.getHelloWord(new OperationRequest(new JsonObject()), testContext.succeeding(it -> { assertThat(it.getStatusCode()).isEqualTo(200); assertThat(it.getPayload().toString()).isEqualTo("Hello"); testContext.completeNow(); })); } @Test @DisplayName("Test 'getDoc' method returns service documentation in OpenAPI format") public void testDocMethod(VertxTestContext testContext) { helloService.getDoc(new OperationRequest(new JsonObject()), testContext.succeeding(it -> { try { assertThat(it.getStatusCode()).isEqualTo(200); assertThat(it.getPayload().toString()).isEqualTo(IOUtils.toString(this.getClass() .getResourceAsStream("../../../doc.yaml"), "UTF-8")); testContext.completeNow(); } catch (IOException e) { testContext.failNow(e); } })); } } 

рджреВрд╕рд░рд╛ рдкрд░реАрдХреНрд╖рдг рдПрдХ рдЧреИрд░-рдПрдХреАрдХрд░рдг рдкрд░реАрдХреНрд╖рдг рд╣реИ, рдЬреЛ рдпрд╣ рдЬрд╛рдБрдЪрддрд╛ рд╣реИ рдХрд┐ рдКрд░реНрдзреНрд╡рд╛рдзрд░ рдЙрдЧрддрд╛ рд╣реИ рдФрд░ рдЕрдкреЗрдХреНрд╖рд┐рдд рд╕реНрдерд┐рддрд┐ рдФрд░ рдкрд╛рда рдХреЗ рд╕рд╛рде рд╕рдВрдмрдВрдзрд┐рдд http рдЕрдиреБрд░реЛрдзреЛрдВ рдХрд╛ рдЬрд╡рд╛рдм рджреЗрддрд╛ рд╣реИ:


HelloVerticleTest.java
 package io.bihero.hello; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.codec.BodyCodec; import io.vertx.junit5.Checkpoint; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @ExtendWith(VertxExtension.class) public class HelloVerticleTest { @Test @DisplayName("Test that verticle is up and respond me by 'Hello' word and doc in OpenAPI format") public void testHelloVerticle(Vertx vertx, VertxTestContext testContext) { WebClient webClient = WebClient.create(vertx); Checkpoint deploymentCheckpoint = testContext.checkpoint(); Checkpoint requestCheckpoint = testContext.checkpoint(2); HelloVerticle verticle = spy(new HelloVerticle()); JsonObject config = new JsonObject().put("serverPort", 8081).put("serverHost", "0.0.0.0"); doReturn(config).when(verticle).config(); vertx.deployVerticle(verticle, testContext.succeeding(id -> { deploymentCheckpoint.flag(); // test GET / webClient.get(8081, "localhost", "/") .as(BodyCodec.string()) .send(testContext.succeeding(resp -> { assertThat(resp.body()).isEqualTo("Hello"); assertThat(resp.statusCode()).isEqualTo(200); requestCheckpoint.flag(); })); // test GET /doc webClient.get(8081, "localhost", "/doc") .as(BodyCodec.string()) .send(testContext.succeeding(resp -> { try { assertThat(resp.body()).isEqualTo(IOUtils.toString(this.getClass() .getResourceAsStream("../../../doc.yaml"), "UTF-8")); assertThat(resp.statusCode()).isEqualTo(200); requestCheckpoint.flag(); } catch (Exception e) { requestCheckpoint.flag(); testContext.failNow(e); } })); })); } } 

рдпрд╣ рдкрд░реАрдХреНрд╖рдг рдХреЗ рд╕рд╛рде рдПрдХ рд╕реЗрд╡рд╛ рдмрдирд╛рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ:


 mvn clean package 

рд╣рдо рдЕрдЪреВрдХ рдкреНрд▓рдЧрдЗрди рд▓реЙрдЧ рдореЗрдВ рдмрд╣реБрдд рд░реБрдЪрд┐ рд░рдЦрддреЗ рд╣реИрдВ, рдпрд╣ рдХреБрдЫ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛ (рдЪрд┐рддреНрд░ рдХреНрд▓рд┐рдХ рдХрд░рдиреЗ рдпреЛрдЧреНрдп рд╣реИ):



рд╡рд╛рд╣! рд╕реЗрд╡рд╛ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдкрд░реАрдХреНрд╖рдг рдЪрд▓рддреЗ рд╣реИрдВ рдФрд░ рдЕрд╕рдлрд▓ рдирд╣реАрдВ рд╣реЛрддреЗ рд╣реИрдВ (рдереЛрдбрд╝реА рджреЗрд░ рдмрд╛рдж рд╣рдо рд╕реБрдВрджрд░рддрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрд╛рдд рдХрд░реЗрдВрдЧреЗ рдХрд┐ рдкрд░реАрдХреНрд╖рдг рдХреЗ рдкрд░рд┐рдгрд╛рдо рдЕрдзрд┐рдХрд╛рд░рд┐рдпреЛрдВ рдХреЛ рдХреИрд╕реЗ рджрд┐рдЦрд╛рдП рдЬрд╛рддреЗ рд╣реИрдВ), рдпрд╣ рд╕реЛрдЪрдиреЗ рдХрд╛ рд╕рдордп рд╣реИ рдХрд┐ рд╣рдо рдЗрд╕реЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рдХреИрд╕реЗ рд╡рд┐рддрд░рд┐рдд рдХрд░реЗрдВрдЧреЗ (рдЕрд░реНрдерд╛рдд, рд╕рд░реНрд╡рд░ рдкрд░)ред рдпрд╛рд░реНрдб рдореЗрдВ, 2019 рдХреЗ рдЕрдВрдд рдореЗрдВ, рдФрд░, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рд╣рдо рдПрдХ рдбреЙрдХрдЯрд░ рдЫрд╡рд┐ рдХреЗ рд░реВрдк рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдмрдВрдбрд▓ рдХрд░реЗрдВрдЧреЗред рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВ!


рдбреЙрдХрд░ рдФрд░ рд╕рдм рд╕рдм


рд╣рдо рдЕрдкрдиреА рдкрд╣рд▓реА рд╕реЗрд╡рд╛ рдХреЗ рд▓рд┐рдП рдПрдХ adoptopenjdk/openjdk11 рдЫрд╡рд┐ рдХрд╛ рдирд┐рд░реНрдорд╛рдг рдХрд░реЗрдВрдЧреЗ рдЬреЛ рдХрд┐ adoptopenjdk/openjdk11 рдЖрдзрд╛рд░ рдкрд░ adoptopenjdk/openjdk11 ред рд╕рднреА рдЖрд╡рд╢реНрдпрдХ рдХреЙрдиреНрдлрд╝рд┐рдЧрд░ рдХреЗ рд╕рд╛рде рд╣рдорд╛рд░реЗ рдЗрдХрдЯреНрдареЗ dzharnik рдХреА рдЫрд╡рд┐ рдореЗрдВ рдЬреЛрдбрд╝реЗрдВ рдФрд░ рдбреЙрдХрдЯрд░ рдореЗрдВ рд▓рд┐рдЦреЗрдВ рдХрдВрдЯреЗрдирд░ рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╢реБрд░реВ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХрдорд╛рдВрдб рджрд░реНрдЬ рдХрд░реЗрдВред рдкрд░рд┐рдгрд╛рдореА Dockerfile рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрд╛рдИ рджреЗрдЧреА:


 FROM adoptopenjdk/openjdk11:alpine-jre COPY target/hello-microservice-fat.jar app.jar COPY src/conf/config.json . COPY src/conf/logback-console.xml . COPY run.sh . RUN chmod +x run.sh CMD ["./run.sh"] 

Run.sh рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреА рд╣реИ:


 #!/bin/sh java ${JVM_OPTS} -Dlogback.configurationFile=./logback-console.xml -jar app.jar -conf config.json 

рд╣рдореЗрдВ рд╡рд╛рд╕реНрддрд╡ рдореЗрдВ рдЕрднреА рддрдХ рдЗрд╕ рд╕реНрддрд░ рдкрд░ JVM_OPTS рдкрд░реНрдпрд╛рд╡рд░рдг рдЪрд░ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реИ, рд▓реЗрдХрд┐рди рдереЛрдбрд╝реА рджреЗрд░ рдмрд╛рдж рд╣рдо рдЗрд╕реЗ рд╕рдХреНрд░рд┐рдп рд░реВрдк рд╕реЗ рдмрджрд▓ рджреЗрдВрдЧреЗ рдФрд░ рд╡рд░реНрдЪреБрдЕрд▓ рдорд╢реАрди рдФрд░ рд╣рдорд╛рд░реА рд╕реЗрд╡рд╛рдУрдВ рдХреЗ рдорд╛рдкрджрдВрдбреЛрдВ рдХреЛ рдЯреНрдпреВрди рдХрд░реЗрдВрдЧреЗред рдпрд╣ рдЫрд╡рд┐ рдХреЛ рдЗрдХрдЯреНрдард╛ рдХрд░рдиреЗ рдФрд░ рдХрдВрдЯреЗрдирд░ рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЪрд▓рд╛рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ:


 docker build -t="hellomicroservice" . docker run -dit --name helloms hellomicroservice #    ,      docker logs -f helloms #  docker logs 2019-10-05 14:55:46,059 [vert.x-worker-thread-0] DEBUG isvpOpenAPIV3Parser: Loaded raw data: openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 2019-10-05 14:55:46,098 [vert.x-worker-thread-0] DEBUG isvpOpenAPIV3Parser: Parsed rootNode: {"openapi":"3.0.1","info":{"title":"Hello ;)","description":"Hello microservice","version":"1.0.0"},"servers":[{"url":"https://demo1.bihero.io/api/hello"}],"tags":[{"name":"hello","description":"Everything about saying 'Hello'"}],"paths":{"/":{"x-vertx-event-bus":{"address":"service.hello","timeout":"1000c"},"get":{"tags":["hello"],"summary":"Get 'Hello' word","operationId":"getHelloWord","responses":{"200":{"description":"OK"}}}},"/doc":{"x-vertx-event-bus":{"address":"service.hello","timeout":"1000c"},"get":{"tags":["hello_doc"],"summary":"Get 'Hello' microservice documentation","operationId":"getDoc","responses":{"200":{"description":"OK"}}}}},"components":{}} Oct 05, 2019 2:55:46 PM io.vertx.core.impl.launcher.commands.VertxIsolatedDeployer INFO: Succeeded in deploying verticle 

рд╣рдо рдХрдВрдЯреЗрдирд░ рдХрд╛ рдЖрдИрдкреА-рдкрддрд╛ рдкреНрд░рд╛рдкреНрдд рдХрд░рддреЗ рд╣реИрдВ рдФрд░ рдХрдВрдЯреЗрдирд░ рдХреЗ рдЕрдВрджрд░ рд╕реЗрд╡рд╛ рдХреЗ рд╕рдВрдЪрд╛рд▓рди рдХреА рдЬрд╛рдВрдЪ рдХрд░рддреЗ рд╣реИрдВ:


 docker inspect helloms | grep IPAddress "SecondaryIPAddresses": null, "IPAddress": "172.17.0.2", "IPAddress": "172.17.0.2", 

 curl http://172.17.0.2:8081/ #     'Hello'   curl http://172.17.0.2:8081/doc #       OpenAPI 

рддреЛ, рдХрдВрдЯреЗрдирд░ рдореЗрдВ рд╕реЗрд╡рд╛ рд╢реБрд░реВ рд╣реЛрддреА рд╣реИред рд▓реЗрдХрд┐рди рд╣рдордиреЗ рдЗрд╕реЗ рдЕрдкрдиреЗ рд╣рд╛рдереЛрдВ рд╕реЗ рдЗрд╕ рддрд░рд╣ (docker run) рдкреНрд░реЛрдбрдХреНрд╢рди рдХреЗ рдорд╛рд╣реМрд▓ рдореЗрдВ рдирд╣реАрдВ рдЪрд▓рд╛рдпрд╛, рдЗрд╕рдХреЗ рд▓рд┐рдП рд╣рдорд╛рд░реЗ рдкрд╛рд╕ рдЕрджреНрднреБрдд рдХреБрдмреЗрд░рдиреЗрдЯ рд╣реИрдВред рдХреБрдмреЗрд░рдиреЗрдЯреНрд╕ рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдЪрд▓рд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рд╣рдореЗрдВ рдПрдХ рдЯреЗрдореНрдкреНрд▓реЗрдЯ, рдПрдХ yml рдлрд╝рд╛рдЗрд▓ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИ, рдЬрд┐рд╕рдХреЗ рд╡рд┐рд╡рд░рдг рдХреЗ рд╕рд╛рде рдХрд┐ рд╣рдо рдХреМрди рд╕реЗ рд╕рдВрд╕рд╛рдзрдиреЛрдВ (рддреИрдирд╛рддреА, рд╕реЗрд╡рд╛, рдкреНрд░рд╡реЗрд╢, рдЖрджрд┐) рдХреЛ рдЪрд▓рд╛рдПрдВрдЧреЗ рдФрд░ рдХрд┐рд╕ рдХрдВрдЯреЗрдирд░ рдкрд░ рдЖрдзрд╛рд░рд┐рдд рд╣реИрдВред рд▓реЗрдХрд┐рди, рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рдХрд┐ рд╣рдо k8s рдореЗрдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЯреЗрдореНрдкрд▓реЗрдЯ рдХрд╛ рд╡рд░реНрдгрди рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░реЗрдВ, рдкрд╣рд▓реЗ рд╕реЗ рдЗрдХрдЯреНрдареЗ рдХреА рдЧрдИ рдЫрд╡рд┐ рдХреЛ рдбреЙрдХрд╣реИрдм рдкрд░ рдзрдХреЗрд▓ рджреЗрдВ:


 docker tag hello bihero/hello docker push bihero/hello 

рд╣рдо рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдХреБрдмреЗрд░рдиреЗрдЯреНрд╕ рдореЗрдВ рд▓реЙрдиреНрдЪ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рдЯреЗрдореНрдкрд▓реЗрдЯ рд▓рд┐рдЦрддреЗ рд╣реИрдВ (рд▓реЗрдЦ рдХреЗ рдврд╛рдВрдЪреЗ рдХреЗ рднреАрддрд░, рд╣рдо рд╡рд╛рд╕реНрддрд╡рд┐рдХ рд╡реЗрд▓реНрдбрд░ рдирд╣реАрдВ рд╣реИрдВ рдФрд░ "рдХреЛрд╖реЗрд░" рдЯреЗрдореНрдкрд▓реЗрдЯ рд╣реЛрдиреЗ рдХрд╛ рджрд┐рдЦрд╛рд╡рд╛ рдирд╣реАрдВ рдХрд░рддреЗ рд╣реИрдВ):


 apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: io.bihero.hello.service: bihero-hello name: bihero-hello spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 template: metadata: labels: io.bihero.hello.service: bihero-hello spec: containers: - image: bihero/hello:${HELLO_SERVICE_IMAGE_VERSION} name: bihero-hello ports: - containerPort: 8081 imagePullPolicy: Always resources: {} restartPolicy: Always --- apiVersion: v1 kind: Service metadata: labels: io.bihero.hello.service: bihero-hello name: bihero-hello spec: ports: - name: "8081" port: 8081 targetPort: 8081 selector: io.bihero.hello.service: bihero-hello status: loadBalancer: {} --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: bihero-hello annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/secure-backends: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" nginx.ingress.kubernetes.io/rewrite-target: /$2 kubernetes.io/tls-acme: "true" namespace: default spec: tls: - hosts: - ${ID_DOMAIN} secretName: bihero rules: - host: ${ID_DOMAIN} http: paths: - path: /api/hello(/|$)(.*) backend: serviceName: bihero-hello servicePort: 8081 

рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ рд╣рдо рдЯреЗрдореНрдкрд▓реЗрдЯ рдореЗрдВ рдХреНрдпрд╛ рджреЗрдЦрддреЗ рд╣реИрдВ:


  • рдкрд░рд┐рдирд┐рдпреЛрдЬрди : рдпрд╣рд╛рдВ рд╣рдо рд╡рд░реНрдгрди рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рд╣рдо рдХрд┐рд╕ рдЫрд╡рд┐ рд╕реЗ рдФрд░ рдХрд┐рддрдиреЗ рдЙрджрд╛рд╣рд░рдгреЛрдВ рд╕реЗ рдЕрдкрдиреА рд╕реЗрд╡рд╛ рдХреЗ рд▓рд┐рдП рдкреНрд░рддрд┐рдХреГрддрд┐ рд╕реЗрдЯ рдмрдирд╛рддреЗ рд╣реИрдВред рдореЗрдЯрд╛рдбреЗрдЯрд╛.рд▓реИрдмрд▓реНрд╕ рдкрд░ рдзреНрдпрд╛рди рджреЗрдирд╛ рднреА рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ - рдЙрди рдкрд░ рд╣рдо рд╕реЗрд╡рд╛ рдХреЛ рддреИрдирд╛рддреА рдХреЗ рд▓рд┐рдП рдмрд╛рдзреНрдп рдХрд░реЗрдВрдЧреЗ
  • рд╕реЗрд╡рд╛ : рд╣рдо рд╕реЗрд╡рд╛ рдХреЛ рдкрд░рд┐рдирд┐рдпреЛрдЬрди / рдкреНрд░рддрд┐рдХреГрддрд┐рдпреЛрдВ рд╕реЗ рдмрд╛рдБрдзрддреЗ рд╣реИрдВред рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, k8s рдореЗрдВ рдПрдХ рд╕реЗрд╡рд╛ рдРрд╕реА рдЪреАрдЬ рд╣реИ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдХреНрд▓рд╕реНрдЯрд░ рдХреЗ рдЕрдВрджрд░ http рдЕрдиреБрд░реЛрдз рднреЗрдЬрдирд╛ рдкрд╣рд▓реЗ рд╕реЗ рд╕рдВрднрд╡ рд╣реИ (рдФрд░ рд╣рд╛рдВ - рдЪрдпрдирдХрд░реНрддрд╛ рдкрд░ рдзреНрдпрд╛рди рджреЗрдВ)
  • рдкреНрд░рдЧрддрд┐ : рдмрд╛рд╣рд░реА рджреБрдирд┐рдпрд╛ рдХреЗ рд▓рд┐рдП рд╕реЗрд╡рд╛ рдХреЛ рдЙрдЬрд╛рдЧрд░ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдкреНрд░рд╡реЗрд╢ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реЛрддреА рд╣реИред рд╣рдо рдЕрдкрдиреА рд╣реИрд▓реЛ рд╕реЗрд╡рд╛ ( https://domain.com/api/hello -> http: //bihero-hello.service.internal.domain.local: 8081 / ) рдкрд░ / api / hello рд╕реЗ рд╢реБрд░реВ рд╣реЛрдиреЗ рд╡рд╛рд▓реЗ рд╕рднреА рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЛ рд▓рдкреЗрдЯреЗрдВрдЧреЗред

рдЯреЗрдореНрдкрд▓реЗрдЯ рдореЗрдВ рджреЛ рдкрд░реНрдпрд╛рд╡рд░рдг рдЪрд░ рднреА рд╢рд╛рдорд┐рд▓ рд╣реИрдВ:


  • $ {HELLO_SERVICE_IMAGE_VERSION} - рд╕реЗрд╡рд╛ рдХреЗ рд╕рд╛рде docker рдЫрд╡рд┐ рдХрд╛ рдЯреИрдЧ рдЬрд┐рд╕рд╕реЗ рд╣рдо рдЕрдкрдиреА рдкрд╣рд▓реА рддреИрдирд╛рддреА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВрдЧреЗ
  • $ {ID_DOMAIN} - рд╡рд╣ рдбреЛрдореЗрди рдЬрд┐рд╕ рдкрд░ рд╣рдо рдЕрдкрдиреА рд╕реЗрд╡рд╛рдПрдБ рддреИрдирд╛рдд рдХрд░реЗрдВрдЧреЗ

Https рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдмрд╛рдд
рдЯреЗрд╕реНрдЯ рдХреНрд▓рд╕реНрдЯрд░ рдореЗрдВ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдПрдХ рдЧреБрдкреНрдд рдирд╛рдо рдмрд╛рдпрд╣рд░реЛ рд╣реИ , рдЬрд┐рд╕реЗ LetsEncrypt рд╕реЗ рд╡рд╛рдЗрд▓реНрдбрдХрд╛рд░реНрдб рдкреНрд░рдорд╛рдгрдкрддреНрд░ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдмрдирд╛рдпрд╛ рдЧрдпрд╛ рд╣реИред рд╕рдВрдХреНрд╖реЗрдк рдореЗрдВ, рдЯреАрдо рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрддреА рд╣реИ
 kubectl create secret tls bihero --key keys/privkey.pem --cert keys/fullchain.pem 


рдЬрд╣рд╛рдВ privkey.pem рдФрд░ fullchain.pem Letencrypt рджреНрд╡рд╛рд░рд╛ рдЙрддреНрдкрдиреНрди рдлрд╛рдЗрд▓реЗрдВ рд╣реИрдВ
рдЖрдк рд▓рд┐рдВрдХ рдХрд╛ рдЕрдиреБрд╕рд░рдг рдХрд░рдХреЗ k8 рдореЗрдВ tls рдХреЗ рд▓рд┐рдП рдПрдХ рд░рд╣рд╕реНрдп рдмрдирд╛рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдЕрдзрд┐рдХ рдкрдврд╝ рд╕рдХрддреЗ рд╣реИрдВ

рдпрд╣ k8s рдореЗрдВ рддреИрдирд╛рддреА рдХрд╛ рдкреНрд░рдпрд╛рд╕ рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ :) рдЪрд▓реЛ рдЪрд▓реЗрдВ!


 export HELLO_SERVICE_IMAGE_VERSION=latest export ID_DOMAIN=demo1.bihero.io cat k8s.yaml | envsubst | kubectl apply -f - 

рд╕реНрдЯрдбрдЖрдЙрдЯ рдореЗрдВ рдЖрдкрдХреЛ рдпрд╣ рджреЗрдЦрдирд╛ рдЪрд╛рд╣рд┐рдП:


 deployment.extensions/bihero-hello created service/bihero-hello created ingress.extensions/bihero-hello created 

рдЦреИрд░, рдЖрдЗрдП рджреЗрдЦреЗрдВ рдХрд┐ рдХреБрдмреЗрд░рдиреЗрдЯ рд╣рдореЗрдВ рд╡рд╣рд╛рдВ рдорд┐рд▓рд╛:


 kubectl get po # ,  pod   po, k8s   


рдЬреИрд╕рд╛ рдХрд┐ рдЕрдкреЗрдХреНрд╖рд┐рдд рдерд╛ - 3 рдЪреВрд▓реНрд╣рд╛


рдЖрдЗрдП рдПрдХ рдЪреВрд▓реНрд╣рд╛ рдХрд╛ рд╡рд┐рд╡рд░рдг рджреЗрдЦреЗрдВ


 kubectl describe po bihero-hello-5b4759d55b-bf4qc 


рд╕реЗрд╡рд╛ рдХреИрд╕реЗ рдХрд░ рд░рд╣рд╛ рд╣реИ?


 kubectl describe service bihero-hello 


рдкреНрд░рд╡реЗрд╢ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдХреНрдпрд╛?


 kubectl describe ing bihero-hello 


рд╡рд╛рд╣! рд╕реЗрд╡рд╛ k8s рдореЗрдВ рдЪрд▓рддреА рд╣реИ рдФрд░ рдХрд▓реНрдкрдирд╛ рдХреЗ рдЕрдиреБрд╕рд╛рд░, рдХреБрдЫ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреЗ рд╕рд╛рде рдЬрд╛рдВрдЪ рдХреА рдЬрд╛рддреА рд╣реИред


 curl https://demo1.bihero.io/api/hello Hello 

 curl https://demo1.bihero.io/api/hello/doc openapi: 3.0.1 info: title: Hello ;) description: Hello microservice version: 1.0.0 servers: - url: https://demo1.bihero.io/api/hello tags: - name: hello description: Everything about saying 'Hello' paths: /: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello summary: Get 'Hello' word operationId: getHelloWord responses: 200: description: OK /doc: x-vertx-event-bus: address: service.hello timeout: 1000c get: tags: - hello_doc summary: Get 'Hello' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 

A - рд╕реНрд╡рдЪрд╛рд▓рди


рдкреЗ ... рд╣рдо рд╕рдмрд╕реЗ рд╕реНрд╡рд╛рджрд┐рд╖реНрдЯ рдФрд░ рд░реЛрдорд╛рдВрдЪрдХ рддрдХ рдкрд╣реБрдБрдЪ рдЧрдПред рдмрд╣реБрдд рд╕реЗ рдХрд╛рдо рдХрд┐рдП рдЧрдП рдереЗ рдФрд░ рдкреНрд░рддреНрдпреЗрдХ рдЪрд░рдг рдореЗрдВ рдХреБрдЫ рдЙрдкрдХрд░рдгреЛрдВ рдХреЗ рдореИрдиреБрдЕрд▓ рд▓реЙрдиреНрдЪ рдХреЗ рд╕рд╛рде, рдкреНрд░рддреНрдпреЗрдХ рдЪрд░рдг рдореЗрдВ рд╕реНрд╡рдпрдВ рдХрд╛ рдерд╛ред , , k8s . , !


, , CI-.


?


  1. , , ( ), git-
  2. (mvn), (surefire, allure) тАФ fat-jar
  3. docker- (docker build)
  4. Push docker- ( docker registry) (docker push)
  5. k8s (kubectl apply)

CI- ?


, ( ), . :

:


  1. , master ( master , , , merge' merge request' )
  2. , dev- (telegram-bot)
  3. ,
  4. тАФ maven repository ( nexus blob store )
  5. fat-jar (mvn package, , тАФ )
  6. docker image . , , , ( ). registry k8s
  7. k8s
  8. , k8s .
  9. 4- , , maven repository
  10. ,

Concourse CI

CI- Concourse . Concourse CI:


  • UI ( yaml-, , fly ): тАФ , (mvn, docker, fly, kubectl), , ( tg- )
  • docker container', ( worker- , - environment- ) тАФ , , .

, , :


pipeline.yaml
 resource_types: - name: telegram type: docker-image source: repository: vtutrinov/concourse-telegram-resource tag: latest - name: kubernetes type: docker-image source: repository: zlabjp/kubernetes-resource tag: 1.16 - name: metadata type: docker-image source: repository: olhtbr/metadata-resource tag: 2.0.1 resources: - name: metadata type: metadata - name: sources type: git source: branch: master uri: git@github.com:bihero-io/hello-microservice.git private_key: ((deployer-private-key)) - name: docker-image type: docker-image source: repository: bihero/hello username: ((docker-registry-user)) password: ((docker-registry-password)) - name: telegram type: telegram source: bot_token: ((telegram-ci-bot-token)) chat_id: ((telegram-group-to-report-build)) ci_url: ((ci_url)) command: "/build_hello_ms" - name: kubernetes-demo type: kubernetes source: server: https://178.63.194.241:6443 namespace: default kubeconfig: ((kubeconfig-demo)) jobs: - name: build-hello-microservice serial: true public: true plan: - in_parallel: - get: sources trigger: true - get: telegram trigger: true - put: metadata - put: telegram params: status: Build In Progress - task: unit-tests config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: sources outputs: - name: tested-workspace run: path: /bin/sh args: - -c - | output_dir=tested-workspace cp -R ./sources/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" clean test caches: - path: ~/.m2/ on_failure: do: - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message run: path: /bin/sh args: - -c - | output_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${output_dir}/pom.xml" site-deploy version=$(cat $output_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - put: telegram params: status: Build Failed (unit-tests) message_file: message/msg - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message - name: tested-workspace run: path: /bin/sh args: - -c - | work_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${work_dir}/pom.xml" site-deploy version=$(cat $work_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - task: package config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace - name: metadata outputs: - name: app-packaged-workspace - name: metadata run: path: /bin/sh args: - -c - | output_dir=app-packaged-workspace cp -R ./tested-workspace/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" package -Dmaven.main.skip -DskipTests env tag="-"$(cat metadata/build_name) echo $tag >> ${output_dir}/target/classes/version.txt cat ${output_dir}/target/classes/version.txt > metadata/version caches: - path: ~/.m2/ on_failure: do: - put: telegram params: status: Build Failed (package) - put: docker-image params: build: app-packaged-workspace tag_file: app-packaged-workspace/target/classes/version.txt tag_as_latest: true get_params: skip_download: true - task: make-k8s-app-template config: platform: linux image_resource: type: docker-image source: repository: bhgedigital/envsubst inputs: - name: sources - name: metadata outputs: - name: k8s run: path: /bin/sh args: - -c - | export DOMAIN=demo1.bihero.io export HELLO_SERVICE_IMAGE_VERSION=$(cat metadata/version) cat sources/k8s.yaml | envsubst > k8s/hello_app_template.yaml cat k8s/hello_app_template.yaml - put: kubernetes-demo params: kubectl: apply -f k8s/hello_app_template.yaml - put: telegram params: status: Build Success message_file: message/msg 

:


  1. resource_types , , . ( , docker-, ): telegram tg- , kubernetes k8s- metadata ( , ..)
  2. resources , . , , docker-registry docker- , . input- ,
  3. jobs , . put- tg-. тАФ get (, git-), тАФ put тАФ (docker image) , (metadata). task тАФ docker- docker-image', image_resource
  4. ((parameter-name)) тАФ , , , ( docker-registry).

:


 fly -t bih sp -p hello-microservice -c pipeline.yaml -l credentials.yaml # -t - target name # sp - alias to set-pipeline # -p - pipeline name # -c - pipeline config file # -l - file with parameters and credentials 

credentials.yaml :


 docker-registry-user: <dockerhub-user> docker-registry-password: <dockerhub-password> docker-registry-uri: <private-docker-registry-url> docker-private-registry-user: <private-docker-registry-user> docker-private-registry-password: <private-docker-registry-passwordl> telegram-ci-bot-token: <telegram-bot-token> telegram-group-to-report-build: <telegram-group-id> ci_url: <ci-server-url> deployer-private-key: | -----BEGIN OPENSSH PRIVATE KEY----- github-deploy-key -----END OPENSSH PRIVATE KEY----- kubeconfig-demo: | apiVersion: v1 clusters: - cluster: certificate-authority-data: <kube-cert-data> server: <kube-api-server-url> name: kubernetes contexts: - context: cluster: kubernetes user: kubernetes-admin name: kubernetes-admin@kubernetes current-context: kubernetes-admin@kubernetes kind: Config preferences: {} users: - name: kubernetes-admin user: client-certificate-data: <kube-client-cert-data> client-key-data: <kube-client-key-data> 

. :


  1. CI-, :
  2. ( 1), c fly , CI-:
     fly -t bih tj -j hello-microservice/build-hello-microservice -w # tj - alias for 'trigger-job' # -j - job (<piprlinr-name>/<job-name-in-pipeline>) # -w - watch 
  3. /build_hello_ms -, telegram-group-to-report-build credentials.yaml
  4. master- (, , : master тАФ , тАФ ;) )

( ) -:


  1. :
  2. :

    , UI CI-:

рд╣реБрд░реНрд░реЗ! , - , , k8s . :


  1. docker-hub'

  2. , 2-
  3. :

curl https://demo1.bihero.io/api/hello -v
 curl https://demo1.bihero.io/api/hello -v юВ▓ яАМ юВ▓ 5350 юВ▓ 14:59:04 яАЧ * Trying 178.63.194.243... * TCP_NODELAY set * Connected to demo1.bihero.io (178.63.194.243) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs * TLSv1.2 (OUT), TLS header, Certificate Status (22): * TLSv1.2 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Client hello (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS change cipher, Client hello (1): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * ALPN, server accepted to use h2 * Server certificate: * subject: CN=*.bihero.io * start date: Nov 7 13:59:46 2019 GMT * expire date: Feb 5 13:59:46 2020 GMT * subjectAltName: host "demo1.bihero.io" matched cert's "*.bihero.io" * issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3 * SSL certificate verify ok. * Using HTTP2, server supports multi-use * Connection state changed (HTTP/2 confirmed) * Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 * Using Stream ID: 1 (easy handle 0x55778f779520) > GET /api/hello HTTP/1.1 > Host: demo1.bihero.io > User-Agent: curl/7.52.1 > Accept: */* > * Connection state changed (MAX_CONCURRENT_STREAMS updated)! < HTTP/2 200 < server: nginx/1.15.8 < date: Sun, 01 Dec 2019 11:59:06 GMT < content-type: text/plain < content-length: 5 < strict-transport-security: max-age=15724800; includeSubDomains < * Curl_http_done: called premature == 0 * Connection #0 to host demo1.bihero.io left intact Hello 

, , . , . , ( testcontainers). TODO- ( ). рдЪрд▓реЛ рдЪрд▓рддреЗ рд╣реИрдВ!


'World' microservice


Service specification
 openapi: 3.0.1 info: title: World ;) description: "'World' word microservice" version: 1.0.0 servers: - url: https://demo1.bihero.io/api/world tags: - name: world description: Everything about 'World' word paths: /: x-vertx-event-bus: address: service.world timeout: 1000 get: tags: - world summary: Get 'World' word operationId: getWorldWord responses: 200: description: OK content: {} /doc: x-vertx-event-bus: address: service.world timeout: 1000c get: tags: - world summary: Get 'World' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 

pom.xml
 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <properties> <main.verticle>io.bihero.world.WorldVerticle</main.verticle> <vertx.version>3.8.1</vertx.version> <logback.version>1.2.3</logback.version> <junit-jupiter.version>5.3.1</junit-jupiter.version> <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version> <junit-platform-surefire-provider.version>1.1.0</junit-platform-surefire-provider.version> <assertj-core.version>3.8.0</assertj-core.version> <allure.version>2.8.1</allure.version> <allure-maven.version>2.10.0</allure-maven.version> <aspectj.version>1.9.2</aspectj.version> <mockito.version>2.21.0</mockito.version> <rest-assured.version>3.0.0</rest-assured.version> </properties> <groupId>io.bihero</groupId> <artifactId>world-microservice</artifactId> <version>1.0.0-SNAPSHOT</version> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> ```11</source> <target>11</target> </configuration> <executions> <execution> <id>default-compile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedSourcesDirectory>src/main/generated</generatedSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/main</arg> </compilerArgs> </configuration> </execution> <execution> <id>default-testCompile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedTestSourcesDirectory>src/test/generated</generatedTestSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/test</arg> </compilerArgs> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <configuration> <properties> <property> <name>listener</name> <value>io.qameta.allure.junit5.AllureJunit5</value> </property> </properties> <includes> <include>**/*Test*.java</include> </includes> <argLine> -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Djdk.net.URLClassPath.disableClassPathURLCheck=true </argLine> <systemProperties> <property> <name>allure.results.directory</name> <value>${project.basedir}/target/allure-results</value> </property> <property> <name>junit.jupiter.extensions.autodetection.enabled</name> <value>true</value> </property> </systemProperties> <reportFormat>plain</reportFormat> </configuration> <dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit-platform-surefire-provider.version}</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> </dependency> </dependencies> </plugin> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <version>${allure-maven.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> <dependencies> <dependency> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-webdav-jackrabbit</artifactId> <version>2.8</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <Main-Class>io.vertx.core.Launcher</Main-Class> <Main-Verticle>${main.verticle}</Main-Verticle> </manifestEntries> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource> </transformer> </transformers> <artifactSet> </artifactSet> <outputFile>${project.build.directory}/${project.artifactId}-fat.jar</outputFile> </configuration> </execution> </executions> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/version.txt</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <excludes> <exclude>**/version.txt</exclude> </excludes> </resource> </resources> </build> <distributionManagement> <site> <id>reports</id> <url>dav:https://nexus.dev.techedge.pro:8443/repository/reports/${project.artifactId}/</url> </site> </distributionManagement> <reporting> <excludeDefaults>true</excludeDefaults> <plugins> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <configuration> <resultsDirectory>${project.build.directory}/allure-results</resultsDirectory> <reportDirectory>${project.reporting.outputDirectory}/${project.version}/allure</reportDirectory> </configuration> </plugin> </plugins> </reporting> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-api-service</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-codegen</artifactId> <version>${vertx.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <!-- test &ndash;&gt;--> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-unit</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-junit5</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>${assertj-core.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.qameta.allure</groupId> <artifactId>allure-junit5</artifactId> <version>${allure.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> </dependencies> </project> 

WorldService.java
 package io.bihero.world; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; import io.vertx.ext.web.api.generator.WebApiServiceGen; @WebApiServiceGen public interface WorldService { static WorldService create(Vertx vertx) { return new DefaultWorldService(vertx); } void getWorldWord(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); } 

DefaultWorldService.java
 package io.bihero.world; import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.buffer.Buffer; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; public class DefaultWorldService implements WorldService { private final Vertx vertx; public DefaultWorldService(Vertx vertx) { this.vertx = vertx; } public void getWorldWord(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { resultHandler.handle(Future.succeededFuture(OperationResponse.completedWithPlainText(Buffer.buffer("World")))); } @Override public void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { vertx.fileSystem().readFile("doc.yaml", buffResult -> resultHandler.handle(Future.succeededFuture( OperationResponse.completedWithPlainText(buffResult.result())) )); } } 

WorldVerticle.java
 package io.bihero.world; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.api.contract.openapi3.OpenAPI3RouterFactory; import io.vertx.serviceproxy.ServiceBinder; public class WorldVerticle extends AbstractVerticle { HttpServer server; MessageConsumer<JsonObject> consumer; public void startWorldService() { consumer = new ServiceBinder(vertx).setAddress("service.world") .register(WorldService.class, WorldService.create(getVertx())); } /** * This method constructs the router factory, mounts services and handlers and starts the http server * with built router * @return */ private Promise<Void> startHttpServer() { Promise<Void> promise = Promise.promise(); OpenAPI3RouterFactory.create(this.vertx, "/doc.yaml", openAPI3RouterFactoryAsyncResult -> { if (openAPI3RouterFactoryAsyncResult.succeeded()) { OpenAPI3RouterFactory routerFactory = openAPI3RouterFactoryAsyncResult.result(); // Mount services on event bus based on extensions routerFactory.mountServicesFromExtensions(); // Generate the router Router router = routerFactory.getRouter(); int port = config().getInteger("serverPort", 8080); String host = config().getString("serverHost", "localhost"); server = vertx.createHttpServer(new HttpServerOptions().setPort(port).setHost(host)); server.requestHandler(router).listen(ar -> { // Error starting the HttpServer if (ar.succeeded()) promise.complete(); else promise.fail(ar.cause()); }); } else { // Something went wrong during router factory initialization promise.fail(openAPI3RouterFactoryAsyncResult.cause()); } }); return promise; } @Override public void start(Promise<Void> promise) { startWorldService(); startHttpServer().future().setHandler(promise); } /** * This method closes the http server and unregister all services loaded to Event Bus */ @Override public void stop(){ this.server.close(); consumer.unregister(); } } 

WorldServiceTest.java
 package io.bihero.world; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.api.OperationRequest; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import java.io.IOException; import static org.assertj.core.api.Assertions.assertThat; @ExtendWith(VertxExtension.class) public class WorldServiceTest { private WorldService worldService = WorldService.create(Vertx.vertx()); @Test @DisplayName("Test 'getWorldWord' method returns 'World' word") public void testHelloMethod(VertxTestContext testContext) { worldService.getWorldWord(new OperationRequest(new JsonObject()), testContext.succeeding(it -> { assertThat(it.getStatusCode()).isEqualTo(200); assertThat(it.getPayload().toString()).isEqualTo("World"); testContext.completeNow(); })); } @Test @DisplayName("Test 'getDoc' method returns service documentation in OpenAPI format") public void testDocMethod(VertxTestContext testContext) { worldService.getDoc(new OperationRequest(new JsonObject()), testContext.succeeding(it -> { try { assertThat(it.getStatusCode()).isEqualTo(200); assertThat(it.getPayload().toString()).isEqualTo(IOUtils.toString(this.getClass() .getResourceAsStream("../../../doc.yaml"), "UTF-8")); testContext.completeNow(); } catch (IOException e) { testContext.failNow(e); } })); } } 

WorldVerticleTest.java
 package io.bihero.world; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.codec.BodyCodec; import io.vertx.junit5.Checkpoint; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; @ExtendWith(VertxExtension.class) public class WorldVerticleTest { @Test @DisplayName("Test that verticle is up and respond me by 'World' word and doc in OpenAPI format") public void testHelloVerticle(Vertx vertx, VertxTestContext testContext) { WebClient webClient = WebClient.create(vertx); Checkpoint deploymentCheckpoint = testContext.checkpoint(); Checkpoint requestCheckpoint = testContext.checkpoint(2); WorldVerticle verticle = spy(new WorldVerticle()); JsonObject config = new JsonObject().put("serverPort", 8082).put("serverHost", "0.0.0.0"); doReturn(config).when(verticle).config(); vertx.deployVerticle(verticle, testContext.succeeding(id -> { deploymentCheckpoint.flag(); // test GET / webClient.get(8082, "localhost", "/") .as(BodyCodec.string()) .send(testContext.succeeding(resp -> { assertThat(resp.body()).isEqualTo("World"); assertThat(resp.statusCode()).isEqualTo(200); requestCheckpoint.flag(); })); // test GET /doc webClient.get(8082, "localhost", "/doc") .as(BodyCodec.string()) .send(testContext.succeeding(resp -> { try { assertThat(resp.body()).isEqualTo(IOUtils.toString(this.getClass() .getResourceAsStream("../../../doc.yaml"), "UTF-8")); assertThat(resp.statusCode()).isEqualTo(200); requestCheckpoint.flag(); } catch (Exception e) { requestCheckpoint.flag(); testContext.failNow(e); } })); })); } } 

Dockerfile
 FROM adoptopenjdk/openjdk11:alpine-jre COPY target/world-microservice-fat.jar app.jar COPY src/conf/config.json . COPY src/conf/logback-console.xml . COPY run.sh . RUN chmod +x run.sh CMD ["./run.sh"] 

run.sh
 #!/bin/sh java ${JVM_OPTS} -Dlogback.configurationFile=./logback-console.xml -jar app.jar -conf config.json 

pipeline.yaml
 resource_types: - name: telegram type: docker-image source: repository: vtutrinov/concourse-telegram-resource tag: latest - name: kubernetes type: docker-image source: repository: zlabjp/kubernetes-resource tag: 1.16 - name: metadata type: docker-image source: repository: olhtbr/metadata-resource tag: 2.0.1 resources: - name: metadata type: metadata - name: sources type: git source: branch: master uri: git@github.com:bihero-io/worldmicroservice.git private_key: ((deployer-private-key)) - name: docker-image type: docker-image source: repository: bihero/world username: ((docker-registry-user)) password: ((docker-registry-password)) - name: telegram type: telegram source: bot_token: ((telegram-ci-bot-token)) chat_id: ((telegram-group-to-report-build)) ci_url: ((ci_url)) command: "/build_world_ms" - name: kubernetes-demo type: kubernetes source: server: ((k8s-api-server)) namespace: default kubeconfig: ((kubeconfig-demo)) jobs: - name: build-world-microservice serial: true public: true plan: - in_parallel: - get: sources trigger: true - get: telegram trigger: true - put: metadata - put: telegram params: status: Build In Progress - task: unit-tests config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: sources outputs: - name: tested-workspace run: path: /bin/sh args: - -c - | output_dir=tested-workspace cp -R ./sources/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" clean test caches: - path: ~/.m2/ on_failure: do: - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message run: path: /bin/sh args: - -c - | output_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${output_dir}/pom.xml" site-deploy version=$(cat $output_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - put: telegram params: status: Build Failed (unit-tests) message_file: message/msg - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message - name: tested-workspace run: path: /bin/sh args: - -c - | work_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${work_dir}/pom.xml" site-deploy version=$(cat $work_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/world-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - task: package config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace - name: metadata outputs: - name: app-packaged-workspace - name: metadata run: path: /bin/sh args: - -c - | output_dir=app-packaged-workspace cp -R ./tested-workspace/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" package -Dmaven.main.skip -DskipTests tag="-"$(cat metadata/build_name) echo $tag >> ${output_dir}/target/classes/version.txt cat ${output_dir}/target/classes/version.txt > metadata/version caches: - path: ~/.m2/ on_failure: do: - put: telegram params: status: Build Failed (package) - put: docker-image params: build: app-packaged-workspace tag_file: app-packaged-workspace/target/classes/version.txt tag_as_latest: true get_params: skip_download: true - task: make-k8s-app-template config: platform: linux image_resource: type: docker-image source: repository: bhgedigital/envsubst inputs: - name: sources - name: metadata outputs: - name: k8s run: path: /bin/sh args: - -c - | export DOMAIN=demo1.bihero.io export WORLD_SERVICE_IMAGE_VERSION=$(cat metadata/version) cat sources/k8s.yaml | envsubst > k8s/world_app_template.yaml cat k8s/world_app_template.yaml - put: kubernetes-demo params: kubectl: apply -f k8s/world_app_template.yaml - put: telegram params: status: Build Success message_file: message/msg 

k8s app template
 apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: io.bihero.hello.service: bihero-world name: bihero-world spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 template: metadata: labels: io.bihero.hello.service: bihero-world spec: containers: - image: bihero/world:${WORLD_SERVICE_IMAGE_VERSION} name: bihero-world ports: - containerPort: 8082 imagePullPolicy: Always resources: {} restartPolicy: Always --- apiVersion: v1 kind: Service metadata: labels: io.bihero.hello.service: bihero-world name: bihero-world spec: ports: - name: "8082" port: 8082 targetPort: 8082 selector: io.bihero.hello.service: bihero-world status: loadBalancer: {} --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: bihero-world annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/secure-backends: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" nginx.ingress.kubernetes.io/rewrite-target: /$2 kubernetes.io/tls-acme: "true" namespace: default spec: tls: - hosts: - ${DOMAIN} secretName: bihero rules: - host: ${DOMAIN} http: paths: - path: /api/world(/|$)(.*) backend: serviceName: bihero-world servicePort: 8082 

'HelloWorld' microservice


, . , , . testcontainers, .


Service specification
 openapi: 3.0.1 info: title: Hello World ;) description: "Hello World microservice. Aggregate 'Hello World' by hellomicroservice and worldmicroservice" version: 1.0.0 servers: - url: https://demo1.bihero.io/api/helloworld tags: - name: helloworld description: Everything about 'Hello World' paths: /: x-vertx-event-bus: address: service.helloworld timeout: 1000 get: tags: - helloworld summary: Aggregate 'Hello World' operationId: getHelloWorld responses: 200: description: OK content: {} /doc: x-vertx-event-bus: address: service.helloworld timeout: 1000c get: tags: - world summary: Get 'Hello World' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 

pom.xml
 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <properties> <main.verticle>io.bihero.helloworld.HelloWorldVerticle</main.verticle> <vertx.version>3.8.1</vertx.version> <logback.version>1.2.3</logback.version> <junit-jupiter.version>5.3.1</junit-jupiter.version> <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version> <junit-platform-surefire-provider.version>1.1.0</junit-platform-surefire-provider.version> <assertj-core.version>3.8.0</assertj-core.version> <allure.version>2.8.1</allure.version> <allure-maven.version>2.10.0</allure-maven.version> <aspectj.version>1.9.2</aspectj.version> <mockito.version>2.21.0</mockito.version> <rest-assured.version>3.0.0</rest-assured.version> <testcontainers.version>1.12.3</testcontainers.version> </properties> <groupId>io.bihero</groupId> <artifactId>hello-world-microservice</artifactId> <version>1.0.0-SNAPSHOT</version> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <configuration> ```11</source> <target>11</target> </configuration> <executions> <execution> <id>default-compile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedSourcesDirectory>src/main/generated</generatedSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/main</arg> </compilerArgs> </configuration> </execution> <execution> <id>default-testCompile</id> <configuration> <annotationProcessors> <annotationProcessor>io.vertx.codegen.CodeGenProcessor</annotationProcessor> </annotationProcessors> <generatedTestSourcesDirectory>src/test/generated</generatedTestSourcesDirectory> <compilerArgs> <arg>-Acodegen.output=${project.basedir}/src/test</arg> </compilerArgs> </configuration> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>${maven-surefire-plugin.version}</version> <configuration> <properties> <property> <name>listener</name> <value>io.qameta.allure.junit5.AllureJunit5</value> </property> </properties> <includes> <include>**/*Test.java</include> </includes> <argLine> -javaagent:"${settings.localRepository}/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar" -Djdk.net.URLClassPath.disableClassPathURLCheck=true </argLine> <systemProperties> <property> <name>allure.results.directory</name> <value>${project.basedir}/target/allure-results</value> </property> <property> <name>junit.jupiter.extensions.autodetection.enabled</name> <value>true</value> </property> </systemProperties> <reportFormat>plain</reportFormat> </configuration> <dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>${aspectj.version}</version> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit-platform-surefire-provider.version}</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> </dependency> </dependencies> </plugin> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <version>${allure-maven.version}</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-site-plugin</artifactId> <version>3.7.1</version> <dependencies> <dependency> <groupId>org.apache.maven.wagon</groupId> <artifactId>wagon-webdav-jackrabbit</artifactId> <version>2.8</version> </dependency> </dependencies> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>2.3</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <manifestEntries> <Main-Class>io.vertx.core.Launcher</Main-Class> <Main-Verticle>${main.verticle}</Main-Verticle> </manifestEntries> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/services/io.vertx.core.spi.VerticleFactory</resource> </transformer> </transformers> <artifactSet> </artifactSet> <outputFile>${project.build.directory}/${project.artifactId}-fat.jar</outputFile> </configuration> </execution> </executions> </plugin> </plugins> <resources> <resource> <directory>src/main/resources</directory> <filtering>true</filtering> <includes> <include>**/version.txt</include> </includes> </resource> <resource> <directory>src/main/resources</directory> <filtering>false</filtering> <excludes> <exclude>**/version.txt</exclude> </excludes> </resource> </resources> </build> <distributionManagement> <site> <id>reports</id> <url>dav:https://nexus.dev.techedge.pro:8443/repository/reports/${project.artifactId}/</url> </site> </distributionManagement> <reporting> <excludeDefaults>true</excludeDefaults> <plugins> <plugin> <groupId>io.qameta.allure</groupId> <artifactId>allure-maven</artifactId> <configuration> <resultsDirectory>${project.build.directory}/allure-results</resultsDirectory> <reportDirectory>${project.reporting.outputDirectory}/${project.version}/allure</reportDirectory> </configuration> </plugin> </plugins> </reporting> <dependencies> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-api-service</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-web-client</artifactId> <version>${vertx.version}</version> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-codegen</artifactId> <version>${vertx.version}</version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>${logback.version}</version> </dependency> <!-- test &ndash;&gt;--> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-unit</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.vertx</groupId> <artifactId>vertx-junit5</artifactId> <version>${vertx.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit-jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.assertj</groupId> <artifactId>assertj-core</artifactId> <version>${assertj-core.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>${mockito.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>io.qameta.allure</groupId> <artifactId>allure-junit5</artifactId> <version>${allure.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>junit-jupiter</artifactId> <version>${testcontainers.version}</version> </dependency> </dependencies> </project> 

HelloWorldService.java
 package io.bihero.helloworld; import io.vertx.core.AsyncResult; import io.vertx.core.Handler; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; import io.vertx.ext.web.api.generator.WebApiServiceGen; @WebApiServiceGen public interface HelloWorldService { static HelloWorldService create(Vertx vertx, JsonObject config) { return new DefaultHelloWorldService(vertx, config); } void getHelloWorld(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler); } 

DefaultHelloWorldService.java
 package io.bihero.helloworld; import io.vertx.core.*; import io.vertx.core.buffer.Buffer; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.api.OperationRequest; import io.vertx.ext.web.api.OperationResponse; import io.vertx.ext.web.client.WebClient; public class DefaultHelloWorldService implements HelloWorldService { private final Vertx vertx; private final JsonObject config; private final WebClient webClient; public DefaultHelloWorldService(Vertx vertx, JsonObject config) { this.vertx = vertx; this.config = config; this.webClient = WebClient.create(this.vertx); } @Override public void getHelloWorld(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { getHelloWord().compose(this::getHelloWorld).setHandler(v -> resultHandler.handle( Future.succeededFuture(OperationResponse.completedWithPlainText(Buffer.buffer(v.result()))) )); } @Override public void getDoc(OperationRequest context, Handler<AsyncResult<OperationResponse>> resultHandler) { vertx.fileSystem().readFile("doc.yaml", buffResult -> resultHandler.handle(Future.succeededFuture( OperationResponse.completedWithPlainText(buffResult.result())) )); } private Future<String> getHelloWord() { Future<String> future = Future.future(); webClient.get(config.getInteger("hello-service-port"), config.getString("hello-service-host"), "/").send(ar -> future.handle(Future.succeededFuture(ar.result().bodyAsString()))); return future; } private Future<String> getHelloWorld(String helloWord) { Future<String> future = Future.future(); webClient.get(config.getInteger("world-service-port"), config.getString("world-service-host"), "/").send(ar -> future.handle(Future.succeededFuture(helloWord + " " + ar.result().bodyAsString()))); return future; } } 

HelloWorldVerticle.java
 package io.bihero.helloworld; import io.vertx.core.AbstractVerticle; import io.vertx.core.Promise; import io.vertx.core.eventbus.MessageConsumer; import io.vertx.core.http.HttpServer; import io.vertx.core.http.HttpServerOptions; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.Router; import io.vertx.ext.web.api.contract.openapi3.OpenAPI3RouterFactory; import io.vertx.serviceproxy.ServiceBinder; public class HelloWorldVerticle extends AbstractVerticle { HttpServer server; MessageConsumer<JsonObject> consumer; public void startWorldService() { consumer = new ServiceBinder(vertx).setAddress("service.helloworld") .register(HelloWorldService.class, HelloWorldService.create(vertx, config())); } /** * This method constructs the router factory, mounts services and handlers and starts the http server * with built router * @return */ private Promise<Void> startHttpServer() { Promise<Void> promise = Promise.promise(); OpenAPI3RouterFactory.create(this.vertx, "/doc.yaml", openAPI3RouterFactoryAsyncResult -> { if (openAPI3RouterFactoryAsyncResult.succeeded()) { OpenAPI3RouterFactory routerFactory = openAPI3RouterFactoryAsyncResult.result(); // Mount services on event bus based on extensions routerFactory.mountServicesFromExtensions(); // Generate the router Router router = routerFactory.getRouter(); int port = config().getInteger("serverPort", 8080); String host = config().getString("serverHost", "localhost"); server = vertx.createHttpServer(new HttpServerOptions().setPort(port).setHost(host)); server.requestHandler(router).listen(ar -> { // Error starting the HttpServer if (ar.succeeded()) promise.complete(); else promise.fail(ar.cause()); }); } else { // Something went wrong during router factory initialization promise.fail(openAPI3RouterFactoryAsyncResult.cause()); } }); return promise; } @Override public void start(Promise<Void> promise) { startWorldService(); startHttpServer().future().setHandler(promise); } /** * This method closes the http server and unregister all services loaded to Event Bus */ @Override public void stop(){ this.server.close(); consumer.unregister(); } } 

HelloWorldServiceTest.java
 package io.bihero.helloworld; import io.vertx.core.DeploymentOptions; import io.vertx.core.Vertx; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.client.WebClient; import io.vertx.ext.web.codec.BodyCodec; import io.vertx.junit5.Checkpoint; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.spy; @Testcontainers @ExtendWith(VertxExtension.class) public class HelloWorldServiceTest { @Container private static final GenericContainer helloServiceContainer = new GenericContainer("bihero/hello") .withExposedPorts(8081); @Container private static final GenericContainer worldServiceContainer = new GenericContainer("bihero/world") .withExposedPorts(8082); @Test @DisplayName("Test 'helloworld' microservice respond by 'Hello World' string and doc in OpenAPI format") public void testHelloWorld(Vertx vertx, VertxTestContext testContext) { WebClient webClient = WebClient.create(vertx); Checkpoint deploymentCheckpoint = testContext.checkpoint(); Checkpoint requestCheckpoint = testContext.checkpoint(2); HelloWorldVerticle verticle = spy(new HelloWorldVerticle()); JsonObject config = new JsonObject().put("serverPort", 8083) .put("serverHost", "0.0.0.0") .put("hello-service-host", helloServiceContainer.getContainerIpAddress()) .put("world-service-host", worldServiceContainer.getContainerIpAddress()) .put("hello-service-port", helloServiceContainer.getMappedPort(8081)) .put("world-service-port", worldServiceContainer.getMappedPort(8082)); DeploymentOptions deploymentOptions = new DeploymentOptions().setConfig(config); vertx.deployVerticle(verticle, deploymentOptions, testContext.succeeding(id -> { deploymentCheckpoint.flag(); // test GET / webClient.get(8083, "localhost", "/") .as(BodyCodec.string()) .send(testContext.succeeding(resp -> { assertThat(resp.body()).isEqualTo("Hello World"); assertThat(resp.statusCode()).isEqualTo(200); requestCheckpoint.flag(); })); // test GET /doc webClient.get(8083, "localhost", "/doc") .as(BodyCodec.string()) .send(testContext.succeeding(resp -> { try { assertThat(resp.body()).isEqualTo(IOUtils.toString(this.getClass() .getResourceAsStream("../../../doc.yaml"), "UTF-8")); assertThat(resp.statusCode()).isEqualTo(200); requestCheckpoint.flag(); } catch (Exception e) { requestCheckpoint.flag(); testContext.failNow(e); } })); })); } } 

Dockerfile тАФ / , /usr/local , ConfigMap' k8s


Dockerfile
 FROM adoptopenjdk/openjdk11:alpine-jre COPY target/hello-world-microservice-fat.jar app.jar COPY src/conf/config.json /usr/local/config.json COPY src/conf/logback-console.xml . COPY run.sh . RUN chmod +x run.sh CMD ["./run.sh"] 

, , CI . Concourse , , worker- docker-compose' ( ui- postgresql). тАФ docker-, docker docker'. testcontainers ( hello world ). ? : ! docker', maven' 11- . , Dockerfile:


 FROM alpine:3.7 ENV DOCKER_CHANNEL=stable \ DOCKER_VERSION=17.12.1-ce \ DOCKER_COMPOSE_VERSION=1.19.0 \ DOCKER_SQUASH=0.2.0 # Install Docker, Docker Compose, Docker Squash RUN apk --update --no-cache add \ bash \ curl \ device-mapper \ py-pip \ iptables \ util-linux \ ca-certificates \ maven \ openjdk11 --repository=http://dl-cdn.alpinelinux.org/alpine/edge/community \ && \ apk upgrade && \ curl -fL "https://download.docker.com/linux/static/${DOCKER_CHANNEL}/x86_64/docker-${DOCKER_VERSION}.tgz" | tar zx && \ mv /docker/* /bin/ && chmod +x /bin/docker* && \ pip install docker-compose==${DOCKER_COMPOSE_VERSION} && \ curl -fL "https://github.com/jwilder/docker-squash/releases/download/v${DOCKER_SQUASH}/docker-squash-linux-amd64-v${DOCKER_SQUASH}.tar.gz" | tar zx && \ mv /docker-squash* /bin/ && chmod +x /bin/docker-squash* && \ rm -rf /var/cache/apk/* && \ rm -rf /root/.cache COPY repository /root/.m2/repository #      -,          COPY settings.xml /root/.m2/settings.xml #   maven'      COPY entrypoint.sh /bin/entrypoint.sh #  -,         ENV JAVA_HOME=/usr/lib/jvm/java-11-openjdk/ ENTRYPOINT ["entrypoint.sh"] 

entrypoint.sh , :


entrypoint.sh
 #!/usr/bin/env bash # Inspired by concourse/docker-image-resource: # https://github.com/concourse/docker-image-resource/blob/master/assets/common.sh set -o errexit -o pipefail -o nounset # Waits DOCKERD_TIMEOUT seconds for startup (default: 60) DOCKERD_TIMEOUT="${DOCKERD_TIMEOUT:-60}" # Accepts optional DOCKER_OPTS (default: --data-root /scratch/docker) DOCKER_OPTS="${DOCKER_OPTS:-}" # Constants DOCKERD_PID_FILE="/tmp/docker.pid" DOCKERD_LOG_FILE="/tmp/docker.log" sanitize_cgroups() { local cgroup="/sys/fs/cgroup" mkdir -p "${cgroup}" if ! mountpoint -q "${cgroup}"; then if ! mount -t tmpfs -o uid=0,gid=0,mode=0755 cgroup "${cgroup}"; then echo >&2 "Could not make a tmpfs mount. Did you use --privileged?" exit 1 fi fi mount -o remount,rw "${cgroup}" # Skip AppArmor # See: https://github.com/moby/moby/commit/de191e86321f7d3136ff42ff75826b8107399497 export container=docker # Mount /sys/kernel/security if [[ -d /sys/kernel/security ]] && ! mountpoint -q /sys/kernel/security; then if ! mount -t securityfs none /sys/kernel/security; then echo >&2 "Could not mount /sys/kernel/security." echo >&2 "AppArmor detection and --privileged mode might break." fi fi sed -e 1d /proc/cgroups | while read sys hierarchy num enabled; do if [[ "${enabled}" != "1" ]]; then # subsystem disabled; skip continue fi grouping="$(cat /proc/self/cgroup | cut -d: -f2 | grep "\\<${sys}\\>")" if [[ -z "${grouping}" ]]; then # subsystem not mounted anywhere; mount it on its own grouping="${sys}" fi mountpoint="${cgroup}/${grouping}" mkdir -p "${mountpoint}" # clear out existing mount to make sure new one is read-write if mountpoint -q "${mountpoint}"; then umount "${mountpoint}" fi mount -n -t cgroup -o "${grouping}" cgroup "${mountpoint}" if [[ "${grouping}" != "${sys}" ]]; then if [[ -L "${cgroup}/${sys}" ]]; then rm "${cgroup}/${sys}" fi ln -s "${mountpoint}" "${cgroup}/${sys}" fi done # Initialize systemd cgroup if host isn't using systemd. # Workaround for https://github.com/docker/for-linux/issues/219 if ! [[ -d /sys/fs/cgroup/systemd ]]; then mkdir "${cgroup}/systemd" mount -t cgroup -o none,name=systemd cgroup "${cgroup}/systemd" fi } # Setup container environment and start docker daemon in the background. start_docker() { echo >&2 "Setting up Docker environment..." mkdir -p /var/log mkdir -p /var/run sanitize_cgroups # check for /proc/sys being mounted readonly, as systemd does if grep '/proc/sys\s\+\w\+\s\+ro,' /proc/mounts >/dev/null; then mount -o remount,rw /proc/sys fi local docker_opts="${DOCKER_OPTS:-}" # Pass through `--garden-mtu` from gardian container if [[ "${docker_opts}" != *'--mtu'* ]]; then local mtu="$(cat /sys/class/net/$(ip route get 8.8.8.8|awk '{ print $5 }')/mtu)" docker_opts+=" --mtu ${mtu}" fi # Use Concourse's scratch volume to bypass the graph filesystem by default if [[ "${docker_opts}" != *'--data-root'* ]] && [[ "${docker_opts}" != *'--graph'* ]]; then docker_opts+=' --data-root /scratch/docker' fi rm -f "${DOCKERD_PID_FILE}" touch "${DOCKERD_LOG_FILE}" echo >&2 "Starting Docker..." dockerd ${docker_opts} &>"${DOCKERD_LOG_FILE}" & echo "$!" > "${DOCKERD_PID_FILE}" } # Wait for docker daemon to be healthy # Timeout after DOCKERD_TIMEOUT seconds await_docker() { local timeout="${DOCKERD_TIMEOUT}" echo >&2 "Waiting ${timeout} seconds for Docker to be available..." local start=${SECONDS} timeout=$(( timeout + start )) until docker info &>/dev/null; do if (( SECONDS >= timeout )); then echo >&2 'Timed out trying to connect to docker daemon.' if [[ -f "${DOCKERD_LOG_FILE}" ]]; then echo >&2 '---DOCKERD LOGS---' cat >&2 "${DOCKERD_LOG_FILE}" fi exit 1 fi if [[ -f "${DOCKERD_PID_FILE}" ]] && ! kill -0 $(cat "${DOCKERD_PID_FILE}"); then echo >&2 'Docker daemon failed to start.' if [[ -f "${DOCKERD_LOG_FILE}" ]]; then echo >&2 '---DOCKERD LOGS---' cat >&2 "${DOCKERD_LOG_FILE}" fi exit 1 fi sleep 1 done local duration=$(( SECONDS - start )) echo >&2 "Docker available after ${duration} seconds." } # Gracefully stop Docker daemon. stop_docker() { if ! [[ -f "${DOCKERD_PID_FILE}" ]]; then return 0 fi local docker_pid="$(cat ${DOCKERD_PID_FILE})" if [[ -z "${docker_pid}" ]]; then return 0 fi echo >&2 "Terminating Docker daemon." kill -TERM ${docker_pid} local start=${SECONDS} echo >&2 "Waiting for Docker daemon to exit..." wait ${docker_pid} local duration=$(( SECONDS - start )) echo >&2 "Docker exited after ${duration} seconds." } start_docker trap stop_docker EXIT await_docker # do not exec, because exec disables traps if [[ "$#" != "0" ]]; then "$@" else bash --login fi 

pipeline.yaml
 resource_types: - name: telegram type: docker-image source: repository: vtutrinov/concourse-telegram-resource tag: latest - name: kubernetes type: docker-image source: repository: zlabjp/kubernetes-resource tag: 1.16 - name: metadata type: docker-image source: repository: olhtbr/metadata-resource tag: 2.0.1 resources: - name: metadata type: metadata - name: sources type: git source: branch: master uri: git@github.com:bihero-io/helloworldmicroservice.git private_key: ((deployer-private-key)) - name: docker-image type: docker-image source: repository: bihero/helloworld username: ((docker-registry-user)) password: ((docker-registry-password)) - name: telegram type: telegram source: bot_token: ((telegram-ci-bot-token)) chat_id: ((telegram-group-to-report-build)) ci_url: ((ci_url)) command: "/build_helloworld_ms" - name: kubernetes-demo type: kubernetes source: server: ((k8s-api-server)) namespace: default kubeconfig: ((kubeconfig-demo)) jobs: - name: build-helloworld-microservice serial: true public: true plan: - in_parallel: - get: sources trigger: true - get: telegram trigger: true - put: metadata - put: telegram params: status: Build In Progress - task: tests privileged: true config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/dind #    ,    ,    , maven  11-  tag: latest username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: sources outputs: - name: tested-workspace run: path: entrypoint.sh args: - bash - -ceux - | #               testcontainers output_dir=tested-workspace cp -R ./sources/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" clean test caches: - path: ~/.m2/ on_failure: do: - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message run: path: /bin/sh args: - -c - | output_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${output_dir}/pom.xml" site-deploy version=$(cat $output_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-world-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - put: telegram params: status: Build Failed (unit-tests) message_file: message/msg - task: tests-report config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace outputs: - name: message - name: tested-workspace run: path: /bin/sh args: - -c - | work_dir=tested-workspace mvn -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -f "${work_dir}/pom.xml" site-deploy version=$(cat $work_dir/target/classes/version.txt) cat >message/msg <<EOL <a href="https://nexus.dev.techedge.pro:8443/repository/reports/hello-world-microservice/${version}/allure/">Allure report</a> EOL caches: - path: ~/.m2/ - task: package config: platform: linux image_resource: type: docker-image source: repository: ((docker-registry-uri))/bih/maven-dind tag: 3-jdk-11 username: ((docker-private-registry-user)) password: ((docker-private-registry-password)) inputs: - name: tested-workspace - name: metadata outputs: - name: app-packaged-workspace - name: metadata run: path: /bin/sh args: - -c - | output_dir=app-packaged-workspace cp -R ./tested-workspace/* "${output_dir}/" mvn -f "${output_dir}/pom.xml" package -Dmaven.main.skip -DskipTests tag="-"$(cat metadata/build_name) echo $tag >> ${output_dir}/target/classes/version.txt cat ${output_dir}/target/classes/version.txt > metadata/version caches: - path: ~/.m2/ on_failure: do: - put: telegram params: status: Build Failed (package) - put: docker-image params: build: app-packaged-workspace tag_file: app-packaged-workspace/target/classes/version.txt tag_as_latest: true get_params: skip_download: true - task: make-k8s-app-template config: platform: linux image_resource: type: docker-image source: repository: bhgedigital/envsubst inputs: - name: sources - name: metadata outputs: - name: k8s run: path: /bin/sh args: - -c - | export DOMAIN=demo1.bihero.io export HELLO_WORLD_SERVICE_IMAGE_VERSION=$(cat metadata/version) cat sources/k8s.yaml | envsubst > k8s/helloworld_app_template.yaml cat k8s/helloworld_app_template.yaml - put: kubernetes-demo params: kubectl: apply -f k8s/helloworld_app_template.yaml - put: telegram params: status: Build Success message_file: message/msg 

k8s. hello world k8s-. k8s <service-name>..default.svc.cluster.local , , , API. , :


k8s
 apiVersion: v1 kind: ConfigMap metadata: name: hello-world-config data: config.json: | { "type": "file", "format": "json", "scanPeriod": 5000, "config": { "path": "/config.json" }, "serverPort": 8083, "serverHost": "0.0.0.0", "hello-service-host": "bihero-hello.default.svc.cluster.local", "hello-service-port": 8081, "world-service-host": "bihero-world.default.svc.cluster.local", "world-service-port": 8082 } --- apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: io.bihero.hello.service: bihero-helloworld name: bihero-helloworld spec: replicas: 3 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 1 template: metadata: labels: io.bihero.hello.service: bihero-helloworld spec: containers: - image: bihero/helloworld:${HELLO_WORLD_SERVICE_IMAGE_VERSION} name: bihero-helloworld ports: - containerPort: 8083 imagePullPolicy: Always resources: {} volumeMounts: #  /usr/local       ConfigMap'  - mountPath: /usr/local/ name: hello-world-config restartPolicy: Always volumes: - name: hello-world-config configMap: name: hello-world-config --- apiVersion: v1 kind: Service metadata: labels: io.bihero.hello.service: bihero-helloworld name: bihero-helloworld spec: ports: - name: "8083" port: 8083 targetPort: 8083 selector: io.bihero.hello.service: bihero-helloworld status: loadBalancer: {} --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: bihero-helloworld annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/ssl-redirect: "true" nginx.ingress.kubernetes.io/secure-backends: "false" nginx.ingress.kubernetes.io/ssl-passthrough: "false" nginx.ingress.kubernetes.io/rewrite-target: /$2 kubernetes.io/tls-acme: "true" namespace: default spec: tls: - hosts: - ${DOMAIN} secretName: bihero rules: - host: ${DOMAIN} http: paths: - path: /api/helloworld(/|$)(.*) backend: serviceName: bihero-helloworld servicePort: 8083 

, тАФ , , , , :


 curl https://demo1.bihero.io/api/helloworld Hello World 

 curl https://demo1.bihero.io/api/helloworld/doc openapi: 3.0.1 info: title: Hello World ;) description: "Hello World microservice. Aggregate 'Hello World' by hellomicroservice and worldmicroservice" version: 1.0.0 servers: - url: https://demo1.bihero.io/api/helloworld tags: - name: helloworld description: Everything about 'Hello World' paths: /: x-vertx-event-bus: address: service.helloworld timeout: 1000 get: tags: - helloworld summary: Aggregate 'Hello World' operationId: getHelloWorld responses: 200: description: OK content: {} /doc: x-vertx-event-bus: address: service.helloworld timeout: 1000c get: tags: - world summary: Get 'Hello World' microservice documentation operationId: getDoc responses: 200: description: OK components: {} 

рд╣реБрд░реНрд░реЗ! ! , тАж


TODO' (backlog)


  1. тАФ parent pom .
  2. docker- docker-hub, тАФ , , private registry.
  3. "" (maven-release-plugin? concourse semver-resource ?), , , .
  4. API - ( HelloWorld, , , ). - , тАФ :)

, , , , :)



https://github.com/bihero-io/hello-microservice
https://github.com/bihero-io/worldmicroservice
https://github.com/bihero-io/helloworldmicroservice


[UPD] TODO'


  1. helm-, k8s , on-prem ,

Source: https://habr.com/ru/post/hi465149/


All Articles