The last blog introduced how to build a complete application, realize the front-end upload pictures to the background processing, the background will test results of the picture back to the front-end display. This article describes how to deploy an application in a Docker container, covering the following three aspects:

  • Packaging and Docker deployment of SpringBoot back-end applications
  • Docker deploys Nginx to access static resource files in the browser asIP: port/interface nameOpen the form ofhtmlFormat file
  • The connection between two containers is accessed. The HTML file in the Nginx container accesses the back-end application interface in the other container.

1. Deploy back-end applications

1.1 Application Packaging

SpringBoot applications are mostly used to develop back-end parts that are separated from the front and back ends, so application packages are usually packaged directly into JAR packages rather than WAR packages.

1. Use the spring-boot-Maven-plugin provided with SpringBoot to package SpringBoot. Add the following configuration to the pom.xml file:

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
Copy the code

2. In the Terminal window of IDEA, use MVN Clean Package to package the package. After the package is packaged, the JAR package of [project name] -0.0.1-snapshot. jar will be generated in the target directory of the project.

3. Verify the mirror. To run the file directly, open a command line window and use Java -jar [project name]-0.0.1 -snapshot.jar.

1.2 Image Creation

Image is the basis of Docker container. Container is the entity of image runtime. The relationship between image and container is similar to class and instance in object-oriented programming language.

DockerHub provides many images, such as the Java JDK, mysql, and so on. Docker provides a way to customize images in the Dockerfile configuration file.

To implement deploying our Web application in a container, we must customize an image and then run the customized image. In the previous section, the jar from which the SpringBoot application is packaged contains the libraries that the application depends on, but does not contain the JDK. Therefore, the image is customized based on the jar package and JDK. The steps are as follows:

1. Create a folder djlweb and create the image construction file Dockerfile in this folder

# create a java8 image based on an existing image
FROM java:8
# mount the local folder to the current container, specify/TMP and persist to the Docker data folder, because the embedded Tomcat container used by Spring Boot uses/TMP as the working directory by default
VOLUME /tmp
# Add your own project to app.jar. The app.jar name can be changed as long as the following lines match this name
COPYSpringboot - dl - CV - 0.0.1 - the SNAPSHOT. Jar app. The jar
Create an app.jar file during runtime
RUN bash -c 'touch /app.jar'
# open port 8009
EXPOSE 8009
# ENTRYPOINT specifies the command to execute by default after the container is run
ENTRYPOINT ["java"."-jar"."/app.jar"]
Copy the code

2. Create an image based on the image configuration file Dockerfile and run it in the CLI:

#The last. Reads the Dockerfile in the current path to customize the image
docker build -t djlweb .

#View the image information
docker iamges
Copy the code

1.3 Image Running

Run in a command line window:

#-d indicates that the image is running in the background
#-p 9090:8080 Indicates that port 9090 of the host is mapped to port 8080 of the container. The springboot application develops port 8080 by default
#-- Name Indicates the name of the container to be run by the image. This parameter is optional
docker run -d -p 9090:8080 --name townweb djlweb
Copy the code

Win10 docker Desktop can view image running logs:

  .   ____          _            __ _ _/ \ \ /___'_ __ _ _(_)_ __  __ _\ \ \ \ (()\___ | '_ | '_| | '_/ /_` | \ \ \ \\ \ /___) | |_) | | | | | | | -_| | |)))) '____| . __|_| |_|_| |_ \ __, | / / / / = = = = = = = = = |_| = = = = = = = = = = = = = = |___/ = /_/_/_/
 :: Spring Boot ::                (v24.1.)

2021-01-10 02:54:58062.  INFO 1 --- [           main] c.town.djl.cv.SpringbootDlCvApplication  : Starting SpringbootDlCvApplication v0. 01.-SNAPSHOT using Java 18.. 0 _111 on 89a57fb604ae with PID 1 (/app.jar started by root in /)
2021-01-10 02:54:58066.  INFO 1 --- [           main] c.town.djl.cv.SpringbootDlCvApplication  : No active profile set, falling back to default profiles: default
2021-01-10 02:54:59429.  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2021-01-10 02:54:59443.  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2021-01-10 02:54:59444.  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9. 0. 41]
2021-01-10 02:54:59513.  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2021-01-10 02:54:59514.  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1358 ms
2021-01-10 02:54:59783.  INFO 1 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2021-01-10 02:55:00241.  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
2021-01-10 02:55:00257.  INFO 1 --- [           main] c.town.djl.cv.SpringbootDlCvApplication  : Started SpringbootDlCvApplication in 265. seconds (JVM running for 322.)
Copy the code

To check the running status of the container, run docker PS from the command line

1.4 Application Deployment Verification

Access the API interface deployed in the container application. You need to access port 9090 on the host in order to map to port 8080 on the container. If port 8080 of the host is accessed, Error connect ECONNREFUSED 127.0.0.1:8080 is displayed

Note: You can view application logs in Docker Desktop. Since the application JAR package does not contain the C/C++ libraries that Deep Java Library relies on for target detection, the first call needs to be downloaded into the container.

The following error occurred during the download. The cause has not been found yet, but was accidentally resolved by restarting the host, re-creating the image and running it. We will continue to analyze the cause of this error and explore how to package dependent C/C++ libraries together when applying packaging.

o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Handler dispatch failed; nested exception is java.lang.StackOverflowError] with root cause

2. Nginx deployment & Static resource access

Resources: Docker deploys Nginx and mounts the default request path and configuration file

2.1 Image Creation

1. Configure the image construction file Dockerfile

DockerHub official nginx mirror in nginx. Conf configuration file path is/etc/nginx/nginx. Conf. Default. Conf configuration file path is/etc/nginx/conf. D/default. Conf, The default HTML path of the home page folder is /usr/share/nginx/html, and the log file path is /var/log/nginx.

# based on dockerhub official nginx image as a carrier to create
FROM nginx:latest
# overwrite the nginx configuration file in the original image
COPY ./conf.d/default.conf /etc/nginx/conf.d/default.conf
Nginx is responsible for local HTML files under HTML, which are the target files for static resource access
COPY ./html/objectdetection.html /usr/share/nginx/html/objectdetection.html
EXPOSE 8090
CMD ["nginx"."-g"."daemon off;"]
Copy the code

The content of the default.conf file is as follows:

Nginx configures multiple locations for a server

Server {listen 80; listen [::]:80; server_name localhost; #charset koi8-r; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; }} # add content server {listen 7070; server_name localhost; Alias /usr/share/nginx/ HTML; index objectdetection.html; }}Copy the code

Objectdetection. HTML realizes the uploading of local images to the back end for target detection, and the back end returns the detection results to the front end for display. The file content is as follows. For details, see SpringBoot+JS to build a simple target detection application

<! DOCTYPEhtml>
<html>
<head>
    <meta charset="utf-8">
    <title>Object Detection Demo</title>
    <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js">
    </script>
</head>

<body>
    <h4>Please chose an image to process object detection</h4>
    <input id="browse_file" type="file" accept="image/*" onchange="showImg(this)"/>
    <button id="detect_click" onclick="detectImg()">detect</button>
    <div id="main" style="width:100%;">
        <div id="left" style="width:50%; float:left;">
            <p>origal image</p>
            <img id="origal_img" src="" alt="" width="" height="">
        </div>
        <div id="right" style="width:50%; float:left;">
            <p>detected image</p>
            <img id="detected_img" src="" alt="" width="" height="">
        </div>
    </div>

    <script>
        function detectImg() {
            console.log("object detection start...");
            var file=$('#browse_file') [0].files[0];
            var formData=new FormData();    
            formData.append('file', file);  
            console.log("formData:", formData.get('file'));
            $.ajax({
                url : "http://localhost:8080/detectshow/formdata".type : 'POST'.data : formData,  
                processData : false.// It must be false to avoid jQuery's default handling of formData
                contentType : false.// The content-type must be false to automatically add the correct content-Type
                success : function(result) {  // Jquery requests return strings
                    console.log("reponse result:", result);
                    var src = 'data:image/png; base64,' + result;
                    $("#detected_img").attr('src', src);
                    $("#detected_img").css("width"."70%");
                    $("#detected_img").css("height"."70%");
                },  
                error : function(result) {  
                    console.log("reponse result:", result);
                    alert("Post Faile!"); }});console.log("object detection end!");
        }

        function showImg(obj) {           // Preview the image
            var file=$(obj)[0].files[0];  // Get file information
            var formData=new FormData();   
            formData.append('img',file);  // This is just a front view, the property name can be defined arbitrarily
            if(file) {
                var reader=new FileReader();   / / call FileReader
                reader.readAsDataURL(file);    // Read the file as DataURL(base64)
                reader.onload=function(evt){   // Triggered when the read operation is complete.
                    $("#origal_img").attr('src', evt.target.result); // bind the img tag SRC to the DataURL
                    $("#origal_img").css('width'."70%");
                    $("#origal_img").css('height'."70%");
                };
            }else {
                alert("Upload failed"); }}</script>
</body>
</html>
Copy the code

2. Create an image based on the image configuration file Dockerfile

Read the Dockerfile in the current path to customize the image
docker build -t nginxweb .

# View the image information
docker iamges
Copy the code

2.2 Image Running

Run from the command line:

#Map port 80 of the container to port 80 of the host
docker run -d -p 80:80 -p 7070:7070 --name townnginx nginxweb
docker ps
Copy the code

The /usr/share/nginx/html folder contains the objectDetection.html file that we copied

2.3 Static Resource Access

On the host browser at http://localhost:7070/detection can access nginx container under static resource file/usr/share/nginx/HTML/objectdetection. HTML, the results are as follows:

http://localhost:7070/detection
Copy the code

3. Connect containers

Resources: There are three ways for Docker containers to access each other

3.1 Containers -> Hosts -> Containers

Change the URL of the background service in the objectDetection.html file in Section 2.1 to:

$.ajax({
    url : "http://localhost:9090/detectshow/formdata". });Copy the code

It is found that the front end can be called to the back-end interface in the TownWeb container, and the effect of the back-end application log and the front end page can be confirmed. The results are as follows:

Cause: When townnginx is called localhost:9090, localhost is not the container itself, but the host. According to the running parameters of the back-end application container, the host is mapped to port 8080 of townWeb of the back-end application container to complete the service invocation. Further verification can be performed in the following two ways

  • Validation of 1Will:objectdetection.htmlThe URL of the background service in the file is changed to the IP address of the host directly:
$.ajax({
    url : "http://172.30.64.1:9090/detectshow/formdata". });Copy the code

The call still succeeds, indicating that the container can directly access the host network.

  • Verify the 2Will:objectdetection.htmlChange the url of the backend service in the file to use the IP address of the nginx container directly:
$.ajax({
    url : "http://172.17.0.3:9090/detectshow/formdata". });Copy the code

Docker inspect container id for Nginx container 172.17.0.3

The localhost in the container does not point to the container itself, but to the host.

3.2 Containers -> Containers

Directly connect to each other through the container IP address as follows:

Change the URL of the background service in the objectDetection. HTML file to the address of the back-end service. IP is the IP of the container where the back-end application resides:

$.ajax({
    url : "http://172.17.0.2:8080/detectshow/formdata".// The port is also changed to the service open port of the back-end application container. });Copy the code

But the page calls times connection timeout (failed) NET ::ERR_CONNECTION_TIMED_OUT

The townnginx command line can be called:

Check the application log in container TownWeb to confirm that the service has been invoked:

Further analysis is needed as to why a timeout error is reported when invoked on the front-end page, and the back-end application log is now not called to the service at all.

3.3 Customizing Docker Networks

Resources: Container Interconnection – Docker – Getting started to practice