banner
Violet

Violet's Blog

A Web <Developer />. Code_ for Fun.
x
github
bilibili
email

Docker Deploying Applications

Deploy a Spring Boot + Vue application using Docker on CentOS 8, with containers for Spring Boot, MySQL, Redis, and Nginx. This includes building the Spring Boot image Dockerfile, bridging container interconnection, directory mapping, and more.

Basic Environment#

  • Enable port forwarding for the CentOS firewall
firewall-cmd --add-masquerade --permanent
  • Restart the firewall
firewall-cmd --reload
  • Create directories

    Create a /data folder in the root directory to uniformly save files mounted to the container

    • /data/redis/data: [directory] saves Redis persistent data
    • /data/redis/redis.conf: [file] Redis configuration file
    • /data/nginx/html: [directory] Nginx HTML file directory
    • /data/nginx/log: [directory] logs
    • /data/nginx/nginx/nginx.conf: [file] Nginx configuration file

Network#

  • Create a Docker subnet
docker network create --subnet 192.168.0.0/16 --gateway 192.168.0.1 docker-net

192.168.0.0/16: subnet range

192.168.0.1: gateway address

docker-net: subnet name

  • View after creation
docker network inspect docker-net
[
    {
        "Name": "docker-net",
        "Id": "1fc4884e63c593800fbbf263d10c09a8dadb52307096d57a5b2e80f84574ab90",
        "Created": "2023-01-11T19:18:32.691672677+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {},
        "Labels": {}
    }
]
  • Join the network

    • Add --net docker-net to the Docker startup parameters

    • After the container starts, use the command docker network connect docker-net <container ID/NAME>

Redis#

  • Download the Redis Docker image
docker pull redis
  • Create the Redis configuration file
vi redis.conf
  • Write to the redis.conf file
requirepass <your redis password>
  • Start Redis
docker run -d -p 6379:6379 --name docker-redis --restart always -v /data/redis:/etc/redis -v /data/redis/data:/data --net docker-net redis redis-server /etc/redis/redis.conf

-d: run in the background

-p: port mapping, host port 6379 mapped to container port 6379

--name: name of the container after startup

--restart always: container auto-restart

-v: file mount maps the directory inside the container to the host, the left side is the host address, the right side is the container address

--net docker-net: join the Docker subnet

  • After starting, use docker ps -a to check if the container started normally

MySQL#

  • Get the image
docker pull mysql:8.0.25

":" is used to specify the MySQL version

  • Start the MySQL container
docker run -d -p 3306:3306 --name d-mysql --net docker-net --restart always -e MYSQL_ROOT_PASSWORD=mysqlpwd -e MYSQL_USER=violet -e MYSQL_PASSWORD=Dd112211 mysql:8.0.25

-e MYSQL_ROOT_PASSWORD=mysqlpwd: -e indicates custom parameters, root user password

-e MYSQL_USER=violet: create a user when the container starts

-e MYSQL_PASSWORD=Dd112211: the password for that user

Other parameters can be viewed at docker hub mysql

  • Set user permissions

You can grant permissions to the user created when the container starts or allow the root user to log in remotely.

Enter the container

docker exec -it d-mysql /bin/bash

Enter MySQL client

mysql -uroot -p

Set root to allow remote login

grant all privileges on *.* to 'root'@'%';

Refresh permissions

FLUSH PRIVILEGES;
  • Exit MySQL client and exit the Docker container
exit
  • Import MySQL database data into Docker MySQL

Execute in MySQL client

mysqldump -uroot -p --databases arrogant javawebsql my_blog springcloud vote_sys >/data/mysql/data-backup.sql

Use mysqldump to download data and save it in an executable SQL file

/data/mysql/data-backup.sql: directory and final generated SQL file name

--databases and the directory in between are the names of the databases to be dumped

Copy the SQL file into the MySQL container

docker cp /data/mysql/data-backup.sql d-mysql:/home/

Enter the MySQL container, enter the MySQL client, execute the command to write the data, must enter from the SQL file directory

source data-backup.sql

Build Spring Boot Image#

  • Set application.yml

Set the database address to d-mysql, which is the name of the MySQL Docker container

url: jdbc:mysql://d-mysql:3306/my_blog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true

Set the Redis address to d-redis

host: d-redis
  • Create a Dockerfile in the src directory
FROM openjdk:8
LABEL maintainer=lnbiuc
COPY ./*.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
  • Upload files

Place the Dockerfile and JAR file in the same directory

  • Build the image
docker build -t api:8.8 .

-t: indicates the tag here, referring to the image version, api is the image name

.: the dot is very important, indicating to build in the current directory

  • After the build is complete, check the image
docker images

image

  • Start the container
docker run -d -p 8888:8888 -v /data/api:/log --name api --net docker-net --restart always api:8.8.3

Here the log folder is mounted out for easy viewing, the JAR file is in the root directory, so the log folder is also at the same level in the root directory.

Nginx#

  • Pull the image
docker pull nginx
  • Get the Spring Boot access address
docker network inspect docker-net

Output

[
    {
        "Name": "docker-net",
        "Id": "1fc4884e63c593800fbbf263d10c09a8dadb52307096d57a5b2e80f84574ab90",
        "Created": "2023-01-11T19:18:32.691672677+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "192.168.0.0/16",
                    "Gateway": "192.168.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "00729e5b41d6ddcec195722869e57d1baf7bd754793cac7f986d452032b6810d": {
                "Name": "d-redis",
                "EndpointID": "2ea0446903341146ccc741aac0431ae3f3e0e6acc8972763f024136a3499a0ae",
                "MacAddress": "02:42:c0:a8:00:02",
                "IPv4Address": "192.168.0.2/16",
                "IPv6Address": ""
            },
            "0682772aaec9f7d6bb435cf0d38e478c5298fa577783a226d086867d71bcf4b3": {
                "Name": "d-nginx",
                "EndpointID": "97dde4d3e0b8fff81722518b3b62094af5f0c1b1b484ef112c7db3490fb89ee7",
                "MacAddress": "02:42:c0:a8:00:03",
                "IPv4Address": "192.168.0.3/16",
                "IPv6Address": ""
            },
            "27325e2b869eb28534e5a3e65de5eb8a650fa8488ded994de46cbb271af40a17": {
                "Name": "d-gitea",
                "EndpointID": "1a9ab8e968041b4df12ebaacecb74a166f23438cbba9b5345878c6fa672d982d",
                "MacAddress": "02:42:c0:a8:00:04",
                "IPv4Address": "192.168.0.4/16",
                "IPv6Address": ""
            },
            "bf2d1cf717fbc3a39c06068bba9638ad6ce49d407b3a6e28c196803188597038": {
                "Name": "api",
                "EndpointID": "34a832756b3c9c051d67e096f27448a80165407579de0a25877fe4d4e9f6e5d3",
                "MacAddress": "02:42:c0:a8:00:05",
                "IPv4Address": "192.168.0.5/16",
                "IPv6Address": ""
            },
            "ebb72c81a769e9e8385cdbf910a5845ae3f53d8cd8e4d3d317046dd726b74947": {
                "Name": "d-mysql",
                "EndpointID": "c86a7becc085a4f47a35a8488b81bef771e0ae9f6eadd7e41d0db2beae3a8862",
                "MacAddress": "02:42:c0:a8:00:06",
                "IPv4Address": "192.168.0.6/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

The IPv4Address under the name api is the address that Nginx needs to reverse proxy.

  • Prepare the SSL certificate and nginx.conf file, place them in the /data/nginx/nginx directory

My nginx.conf

error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

include /usr/share/nginx/modules/*.conf;
# include enable-cors.conf

events {
    worker_connections 1024;
}
http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    include /etc/nginx/conf.d/*.conf;
    gzip on;
    gzip_static on;
	gzip_min_length 1k;
    gzip_comp_level 7;
    gzip_buffers 4 16k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    gzip_vary on;

	server {
    	listen *:80;
		listen [::]:80;
    	server_name beyondhorizon.top;

    	rewrite ^(.*)$ https://${server_name}$1 permanent;
	}

    server {
    	listen *:80;
		listen [::]:80;
    	server_name www.beyondhorizon.top;

    	rewrite ^(.*)$ https://${server_name}$1 permanent;
	}

    server {
		listen *:443 ssl http2;
    	listen [::]:443  ssl http2;
        server_name beyondhorizon.top www.beyondhorizon.top;
		ssl on;
        ssl_certificate beyondhorizon.top_bundle.crt;
        ssl_certificate_key beyondhorizon.top.key;
        ssl_session_timeout 5m;
        ssl_protocols TLSv1.2 TLSv1.3;
		ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:ECDHE-RSA-DES-CBC3-SHA:ECDHE-ECDSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:DES-CBC3-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA';

        ssl_prefer_server_ciphers on;

	    ssl_session_cache   shared:SSL:10m;
    	ssl_session_tickets on;
    	ssl_stapling        on;
    	ssl_stapling_verify on;
		add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
        client_max_body_size 10m;
        keepalive_timeout   70;
        include /etc/nginx/default.d/*.conf;

        location / {
                root /usr/share/nginx/html;
                index index.html;
                try_files $uri $uri/ /index.html;
		}

        location ~ /api/ {
                add_header Cache-Control max-age=3600;
                proxy_pass http://192.168.0.5:8888;
        }

       error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
    }
}
  • Upload HTML files to /data/nginx/html
  • Start Nginx
docker run -d -p 80:80 -p 443:443 -v /data/nginx/html:/usr/share/nginx/html -v /data/nginx/nginx:/etc/nginx -v /data/nginx/log:/var/log/nginx --name d-nginx --restart always --net docker-net nginx
  • If you modify the Nginx configuration file later, simply restart the container using the command
docker restart d-nginx

At this point, all containers should be started, and the website should be accessible normally.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.