Docker File部分镜像制作实操

镜像制作例子

  • [一.Springboot 镜像制作](#一.Springboot 镜像制作)
    • [1.编写 demo](#1.编写 demo)
    • [2.编写 Dockerfile 并构建生成镜像](#2.编写 Dockerfile 并构建生成镜像)
    • 3.创建并运行容器
  • [二.正确使用 CMD 和 EntryPoint](#二.正确使用 CMD 和 EntryPoint)
  • [三.优秀实践一合理使用 dokerignore](#三.优秀实践一合理使用 dokerignore)
  • 四.优秀实践二多阶段构建
  • 五.优秀实践三合理使用缓存
  • [六.Dockerfile 结合 docker compose 搭建 mysql 主从同步](#六.Dockerfile 结合 docker compose 搭建 mysql 主从同步)
    • [1.Docker compose 构建参数](#1.Docker compose 构建参数)
    • 2.实操
  • [七.Dockerfile 结合 docker compose 搭建 redis 集群](#七.Dockerfile 结合 docker compose 搭建 redis 集群)
  • [八.Dockerfile 结合 docker compose 搭建 nginx、 mysql、springboot 微服务站](#八.Dockerfile 结合 docker compose 搭建 nginx、 mysql、springboot 微服务站)

一.Springboot 镜像制作

1.编写 demo

使用 Spring Boot 创建一个简单的 demo, 在浏览器输出 hello docker!。

  • 创建 maven 项目,选择 Spring Boot 2.x 的第一个发布版本 2.0.2.RELEASE 进行
    课程讲解, 如下在 pom.xml 中添加 spring-boot 依赖
c 复制代码
<?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>
<groupId>org.example</groupId>
<artifactId>springboot-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target>
</properties>
<!-- 项目的 pom 定义继承自 SpringBoot 的父 pom -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
<!-- Web 项目,添加 spring-boot-starter-web 依赖即可,版本号由父
pom 已经定义好了,此处省略 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- 添加 spring boot 项目构建插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
  • 编写主函数构建 Spring 容器
java 复制代码
package com.bittech.boot;
import org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class ExampleApplication {
public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
}
}
  • 编写控制器
java 复制代码
package com.bittech.boot;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value = "/hello")
public class ExampleController {
@RequestMapping(value = "")
public String index() {
return "Hello Spring Boot for Dokcer World!";
}
}
  • 使用 maven 打包生成 jar 包(生成的 jar 包位于项目根路径下的 target 目录)

  • 测试 jar 包是否可用

powerShell 复制代码
java -jar D:\code\java_relearn\springboot-demo\target\springbootdemo-1.0-SNAPSHOT.jar
  • 启动容器后, 访问 localhost:8080/hello 查看运行结果, 确认 jar 包可用。
  • 上传到 Linux 服务器备用

2.编写 Dockerfile 并构建生成镜像

  • 编写 dockerfile
go 复制代码
# 指定 java 环境基础镜像
FROM openjdk:8
# 指定作者, 在公司内编写的时候最好写上 方便维护
MAINTAINER bitejiuyeke
# 拷贝 jar 包到容器中
ADD ./springboot-demo-1.0-SNAPSHOT.jar /app.jar
# 运行 jar 包
CMD ["java", "-jar", "/app.jar"]
  • 构建生成镜像

3.创建并运行容器


访问 Linux 服务器的 8888 端口查看运行结果,可以看到通过浏览器已经可以访问到输

出的日志了

二.正确使用 CMD 和 EntryPoint

1.多次覆盖

我们创建一个 Dockerfile,指定多个 CMD,创建目录如下

创建 Dockerfile1

然后编译镜像,运行查看结果,可以看到第一个 CMD 被覆盖了

我们创建一个 Dockerfile2,指定多个 EntryPoint 然后运行查看结果

编译构建,运行查看结果

2.参数覆盖

我们通过指定后面的启动参数,可以覆盖 CMD 的指令,但是 EntryPoint 的无法覆

盖,执行效果如下

如果我们指定参数--entrypoint 就可以完成对 entrypoint 的替换

3.Shell VS Exec

我们编写下面的 Dockerfile4,执行 ping 命令

然后编译镜像,运行

进入镜像里面查看,可以看到 PID 为 1 的其实是/bin/sh

我们新建 Dockerfile5,这个里面我们采用 EXEC 模式

我们编译镜像,然后运行镜像

我们进入镜像查看,可以看到 PID 为 1 的进程是 ping 而不再是我们的/bin/sh

4.组合

我们新建 Dockerfile6,同时设置 ENTRYPOINT 和 CMD

此时我们编译镜像然后启动运行看下效果是什么,可以看到 CMD 的内容作为

ENTRYPOINT 的参数添加到了后面

因为 CMD 的内容可以被替换,如果我们运行的时候替换成另外的一个网站,我们

可以看到他会 ping 的是另外一个网站,也就是说 CMD 的参数被替换掉了。

5.总结

ENTRYPOINT 和 CMD 都是在 docker image 里执行一条命令, 但是他们有一些微妙的

区别.一般来说两个大部分功能是相似的都能满足。

比如执行运行一个没有调用 ENTRYPOINT 或者 CMD 的 docker 镜像, 返回错误,一般

的镜像最后都提供了 CMD 或者 EntryPoint 作为入口。

覆盖

在写 Dockerfile 时, ENTRYPOINT 或者 CMD 命令会自动覆盖之前的 ENTRYPOINT

或者 CMD 命令.

在 docker 镜像运行时, 用户也可以在命令指定具体命令, 覆盖在 Dockerfile 里的命令.

如果你希望你的 docker 镜像只执行一个具体程序, 不希望用户在执行 docker run 的时

候随意覆盖默认程序. 建议用 ENTRYPOINT.

Shell 和 Exec 模式

ENTRYPOINT 和 CMD 指令支持 2 种不同的写法: shell 表示法和 exec 表示法.

CMD 命令语法

shell 复制代码
# EXEC 语法
CMD ["executable","param1","param2"] (exec form, this is the
preferred form)
#用于给 ENTRYPOINT 传入参数,推荐使用
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
#shell 语法
CMD command param1 param2 (shell form)

ENTRYPOINT 语法

shell 复制代码
# EXEC 语法
ENTRYPOINT ["executable", "param1", "param2"] (exec form,
preferred)
# Shell 语法
ENTRYPOINT command param1 param2 (shell form)

当使用 shell 表示法时, 命令行程序作为 sh 程序的子程序运行, docker 用/bin/sh -c 的语

法调用. 如果我们用 docker ps 命令查看运行的 docker, 就可以看出实际运行的是/bin/sh -c 命令。这样运行的结果就是我们启动的程序的 PID 不是 1,如果从外部发送任何 POSIX 信号到 docker 容器, 由于/bin/sh 命令不会转发消息给实际运行的命令, 则不能安全得关闭 docker 容器。

EXEC 语法没有启动/bin/sh 命令, 而是直接运行提供的命令, 命令的 PID 是 1. 无论你用

的是 ENTRYPOINT 还是 CMD 命令, 都强烈建议采用 exec 表示法。

组合模式

组合使用 ENTRYPOINT 和 CMD, ENTRYPOINT 指定默认的运行命令, CMD 指定默认

的运行参数.ENTRYPOINT 和 CMD 同时存在时, docker 把 CMD 的命令拼接到ENTRYPOINT 命令之后, 拼接后的命令才是最终执行的命令.

三.优秀实践一合理使用 dokerignore

建立 Dockerfile,拷贝当前目录的所有文件到容器的根目录下

建立 .dockerignore 文件, 忽略以.txt 为后缀的文件

创建文件并查看文件目录

构建镜像并查看结果,可以发现 test2.txt 和 已经被忽略掉了,只有 test.data拷贝和 dockerfile 拷贝成功

四.优秀实践二多阶段构建

  • 将全部组件及其依赖库的编译、测试、打包等流程封装进一个 docker 镜像中。但
    是这种方式存在一些问题, 比如 Dockefile 特别长,可维护性降低;镜像的层次多,
    体积大,部署时间长等问题
  • 将每个阶段分散到多个 Dockerfile。一个 Dockerfile 负责将项目及其依赖库
    编译测试打包好后,然后将运行文件拷贝到运行环境中,这种方式需要我们编写多个
    Dockerfile 以及一些自动化脚本才能将其两个阶段自动整合起来
  • 为了解决以上的两个问题, Docker 17.05 版本开始支持多镜像阶段构建。只需
    要编写一个 Dockerfile 即可解决上述问题。

下面我们做个实验来验证一下:假如现在有一个 C 语言程序,我们想用 Docker 帮我

们编译成可执行文件,然后执行该可执行文件。

c 复制代码
#include <stdio.h>
int main()
{
printf("hello docker!\n");
return 0;
}

编写 Dockerfile,因为要有 C 语言的编译环境,我们需要安装 gcc,这次我们选用centos:7

构建这个 Docker 镜像, 并且创建容器测试结果(从结果可以看到生成镜像非常的大,达到 900MB+。)

实际上当我们把 test.c 编译完以后,并不需要一个大的 GCC 编译环境,一个小的运

行环境镜像即可。这时候我们就可以使用多阶段构建解决这个问题

构建镜像并创建容器测试结果(可以看到这个生成的镜像是 204MB)

我们进一步优化可以选取较小的基础镜像像 alpine, busybox, debian 都有很小的体

积,我们以 busybox 为例再次构建, Dockerfile 如下

构建镜像并创建容器测试结果,可以看到我们将镜像已经缩小到了不到 5MB,因为我们编译使用的软件,都没有打到我们的运行态的软件里面,所以可以变得更小

五.优秀实践三合理使用缓存

  • 在镜像的构建过程中, Docker 会根据 Dockerfile 指定的顺序执行每个指令。在执行每条指令之前, Docker 都会在缓存中查找是否已经存在可重用的镜像,如果有就使用现存的镜像,不会再重复创建。
  • 在上边提到 Dockerfile 中的每一条指令都会产生一层 image layer。当某一个layer 修改后,后面的所有 layer 我们都不能使用缓存, 这一点一定要注意。
  • 如果不想在构建过程中使用缓存,你可以在 docker build 命令中使用 --nocache=true 选项。但是我们建议最好合理使用缓存, 这样可以加快构建镜像的效率。

还是之前 C 语言的例子, 假如现在有一个 C 语言程序,我们想用 Docker 帮我们编译

成可执行文件,然后执行该可执行文件。

编写 C 语言源代码:

c 复制代码
#include <stdio.h>
int main()
{
printf("hello docker!\n");
return 0;
}

编写 Dockerfile

构建镜像,可以发现由于是我们第一次构建镜像, 并没有使用到缓存,构建了整整 80s

改变源代码文件重新创建镜像(可以调整 Dockerfile 命令执行的顺序,建议将不经常修改的内容放在 Dockerfile的前边, 经常修改的内容放在后边。)

调整 Dockerfile 的顺序

构建镜像(在重新构建镜像时发现此时已经可以使用缓存, 构建镜像的效率变得非常之高,我们只需要不到 3s 就可以完成构建了。)

六.Dockerfile 结合 docker compose 搭建 mysql 主从同步

1.Docker compose 构建参数

build

  • 功能
    在 docker-compose.yml 文件中使用 build 选项编译镜像。
  • 格式
powershell 复制代码
services:
# 格式一
frontend:
image: awesome/webapp
build: ./webapp
# 格式二
backend:
image: awesome/database
build:
#构建上下文目录
context: ./backend
dockerfile: ./backend.Dockerfile




2.实操

创建目录

powershell 复制代码
mkdir -p /data/maxhou/mysqlcluster/
mkdir -p /data/maxhou/mysqlcluster/master/
mkdir -p /data/maxhou/mysqlcluster/slave/

进入目录/data/maxhou/mysqlcluster/master,创建主库 Dockerfile 文件为

Dockerfile-master

创建主库配置脚本 master.sql

进入目录/data/maxhou/mysqlcluster/slave/,创建从库 Dockerfile,文件为

Dockerfile-slave

创建从库配置脚本 slave.sql

进入/data/maxhou/mysqlcluster 目录,创建 docker-compose.yml 配置文件

  • server-id:
  • log-bin:打开二进制日志功能,配置 binlog 文件名
  • binlog-ignore-db: 配置忽略的数据库
  • binlog_cache_size:在一个事务中 binlog 为了记录 SQL 状态所持有的 cache 大小,如果经常使用大事务,可以增加此值来获取更大的性能。
  • binlog_format:ROW/STATEMENT/MIXED
  • lower_case_table_names:表采用小写
  • character-set-server:配置字符集
  • collation-server:配置比较规则



构建镜像

启动服务进行测试

状态查看正常

连上主库,查看数据库可以看到运行正常

查看数据库角色,和同步状态,连接上从库

主库上创建数据库

连接上任意一个从库,查看从库上数据库自动创建

主库上创建表,插入数据

查看从库上数据自动写入(至此可以看到我们搭建的 mysql 集群已经能够正常进行工作)

七.Dockerfile 结合 docker compose 搭建 redis 集群

打开 redis 官网 https://redis.io/,找到下载 https://redis.io/download/,下载 7.0.x

对应的版本的源码文件,无法下载的话使用 http://download.redis.io/releases/redis-

7.0.11.tar.gz 替换对应版本进行下载

准备目录

找到里面的配置文件模板, redis.conf,修改以下内容,完成配置文件创建,创建好的文件放到目录/data/maxhou/rediscluster/redis

powershell 复制代码
#表示前台运行
daemonize no
#端口
port 6379
#持久化
dir /data/redis
#启用集群
cluster-enabled yes
#集群参数配置
cluster-config-file nodes.conf
#集群超时时间
cluster-node-timeout 5000
#密码配置
requirepass 123456
#主节点密码配置
masterauth 123456
#表示远端可以连接
bind * -::*

在目录/data/maxhou/rediscluster/redis,通过 vi Dockerfile 创建 Dockerfile,用于构

建自己的 redis 镜像我们可以基于 Ubuntu 和 Centos 搭建我们的镜像,建议 ubuntu

java 复制代码
FROM ubuntu:22.04 as buildstage
RUN sudo sed -i
's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g'
/etc/apt/sources.list & apt update
RUN apt install -y build-essential
#wget http://download.redis.io/releases/redis-7.0.11.tar.gz
ADD redis-7.0.11.tar.gz /
ADD redis.conf /redis/
WORKDIR /redis-7.0.11
RUN make
RUN mv /redis-7.0.11/src/redis-server /redis/ && mv /redis-
7.0.11/src/redis-cli /redis/
ENTRYPOINT ["/redis/redis-server", "/redis/redis.conf"]
FROM ubuntu:22.04
RUN mkdir -p /data/redis && mkdir -p /redis
COPY --from=buildstage /redis /redis
EXPOSE 6379
ENTRYPOINT ["/redis/redis-server", "/redis/redis.conf"]

通过 docker build -t 测试镜像构建

启动一个容器测试是服务能否正常运行,可以看到容器正常运行(要记得清理容器释放资源,因后续要启动的实例较多,要看下服务器资源是否足够)

编写 docker-compose.yml

yaml 复制代码
version: "3"
services:
redis01:
image: myredis:v1.0
build: ./redis
ports:
- 6379:6379
container_name: redis01
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis02:
image: myredis:v1.0
container_name: redis02
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis03:
image: myredis:v1.0
container_name: redis03
healthcheck:
test: /redis/redis-cli pinginterval: 10s
timeout: 5s
retries: 10
redis04:
image: myredis:v1.0
container_name: redis04
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis05:
image: myredis:v1.0
container_name: redis05
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis06:
image: myredis:v1.0
container_name: redis06
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis07:
image: myredis:v1.0
container_name: redis07
entrypoint: ["/redis/redis-cli","--
cluster","create","redis01:6379","redis02:6379","redis03:6379","re
dis04:6379","redis05:6379","redis06:6379","--clusterreplicas","1","-a","123456","--cluster-yes"]
depends_on:
redis01:
condition: service_healthy
redis02:
condition: service_healthy
redis03:
condition: service_healthy
redis04:
condition: service_healthy
redis05:condition: service_healthy
redis06:
condition: service_healthy

执行构建可以完成镜像构建,我们之前测试的时候已经构建过了,所以这一步非常快

启动服务

查看容器状态

查看 07 的日志是否创建成功

进入任意一个 01-06 的容器检查功能是否正常

释放资源

八.Dockerfile 结合 docker compose 搭建 nginx、 mysql、springboot 微服务站

1.方案设计

整体架构图

库表设计

对应脚本

sql 复制代码
drop database if exists test;
CREATE DATABASE `test` DEFAULT CHARACTER SET utf8mb4 ;
use `test`;
CREATE TABLE `users` (`sno` int(11) PRIMARY KEY,
`sname` varchar(50) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
INSERT INTO users (sno,sname) VALUES
(1,'pony'),
(2,'maxhou');

接口设计

用户管理一般有用户的增加,用户编辑,用户查询,用户删除等功能,我们实现其中一个用户查询功能。用户查询功能的逻辑如下:

2.实操

准备目录

搭建 Mysql 集群和 redis 集群, mysql 和 redis 的相关配置我们在之前已经讲解过

了,将对应内容拷贝过去

编写进入目录/data/maxhou/mymicroservice, vi docker-compose.yml,考虑服务

器资源,我们将 mysql 集群配置为 1 主 1 从,降低内存占用

yaml 复制代码
version: "3"
services:
mysql-master:
build:
context: ./mysql
dockerfile: ./master/Dockerfile-master
image: mysqlmaster:v2.0
restart: always
container_name: mysql-master
volumes:
- ./mastervarlib:/var/lib/mysql
ports:
- 9306:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=1',
'--log-bin=master-bin','--binlog-ignore-db=mysql',
'--binlog_cache_size=256M',
'--binlog_format=mixed',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
mysql-slave:
build:
context: ./mysql
dockerfile: ./slave/Dockerfile-slave
image: mysqlslave:v2.0
restart: always
container_name: mysql-slave
volumes:
- ./slavevarlib:/var/lib/mysql
ports:
- 9307:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=2',
'--relay_log=slave-relay',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
depends_on:
- mysql-master
redis01:
image: myredis:v1.0
build: ./redis
ports:
- 6379:6379
container_name: redis01
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis02:
image: myredis:v1.0
container_name: redis02
healthcheck:
test: /redis/redis-cli ping
interval: 10stimeout: 5s
retries: 10
redis03:
image: myredis:v1.0
container_name: redis03
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis04:
image: myredis:v1.0
container_name: redis04
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis05:
image: myredis:v1.0
container_name: redis05
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis06:
image: myredis:v1.0
container_name: redis06
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis07:
image: myredis:v1.0
container_name: redis07
entrypoint: ["/redis/redis-cli","--
cluster","create","redis01:6379","redis02:6379","redis03:6379","re
dis04:6379","redis05:6379","redis06:6379","--clusterreplicas","1","-a","123456","--cluster-yes"]
depends_on:
redis01:
condition: service_healthyredis02:
condition: service_healthy
redis03:
condition: service_healthy
redis04:
condition: service_healthy
redis05:
condition: service_healthy
redis06:
condition: service_healthy

构建镜像,启动 mysql 和 redis 集群。

我们使用本地客户端工具 workbench 或者 navicat 连接 mysql, redis 可以进入容

器连接上我们创建的 redis 集群,注意如果是云服务器需要通过安全组放通对应的端口

策略才能本地访问。(使用可视化工具执行脚本,初始化数据库.)

创建 springboot 的 maven 工程,可以直接复用之前 docker-compose 实战中的工

程,配置 pom.xml 添加 mysql 和 redis 对应的 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
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.11</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.bit</groupId>
<artifactId>testmymysql</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>testmymysql</name>
<description>testmymysql</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency><groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-dataredis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build><plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

添加一个简单的用户 bean

java 复制代码
package com.bit.testmymysql.bean;
import lombok.Data;
@Data
public class UserBean {
//用户 id
private Integer sno;
//用户名称
private String sname;
}

添加 redis 使用 json 来完成存储的配置

java 复制代码
package com.bit.testmymysql.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
@Configuration
public class CustomRedis {
@Bean
public RedisTemplate<String,Object>
redisSerTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> redisTemplate = new
RedisTemplate<>();
//设置工厂
redisTemplate.setConnectionFactory(redisConnectionFactory);
//设置序列化器
redisTemplate.setKeySerializer(RedisSerializer.string());
redisTemplate.setValueSerializer(RedisSerializer.string());
redisTemplate.setHashKeySerializer(RedisSerializer.string());
redisTemplate.setHashValueSerializer(RedisSerializer.string());
return redisTemplate;
}
}

创建简单控制器

java 复制代码
package com.bit.testmymysql;
import com.bit.testmymysql.bean.UserBean;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
UserService userService ;
@GetMapping("/userGet/{id}")
public Object getUsersById(@PathVariable(name = "id") Integer
sno){return userService.getUsersById(sno);
}
}

创建用户服务

java 复制代码
package com.bit.testmymysql;
import com.bit.testmymysql.bean.UserBean;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Service
public class UserService {
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
JdbcTemplate jdbcTemplate;
ObjectMapper objectMapper = new ObjectMapper();
@GetMapping("/userGet/{id}")
public Object getUsersById(@PathVariable(name = "id") Integer
sno){
String userKey = "u:"+sno;
//先查询缓存
if (stringRedisTemplate.hasKey(userKey)){
try {
return
objectMapper.readValue(stringRedisTemplate.opsForValue().get(userKey),UserBean.class) ;
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
//再查询数据库
String sql = "select * from users where sno= ?";
UserBean userBean= new UserBean();
try{
userBean= jdbcTemplate.queryForObject(sql, new
BeanPropertyRowMapper<>(UserBean.class),sno);
}
catch (Exception e){
return userBean;
}
//查询数据库说明没缓存,添加下缓存下次直接查询 redis
try {
if (userBean != null){
stringRedisTemplate.opsForValue().set(userKey,objectMapper.writeVa
lueAsString(userBean));
}
else {
return new UserBean();
}
} catch (Exception e) {
//缓存更新失败,可以再度重试,再度失败可以引入消息中间件记
录失败信息过段时间再重试
throw new RuntimeException(e);
}
return userBean;
}
}

配置默认的应用

java 复制代码
package com.bit.testmymysql;import org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TestmymysqlApplication {
public static void main(String[] args) {
SpringApplication.run(TestmymysqlApplication.class, args);
}
}

配置 application-docker.yml 来读取 mysql 和 redis

yaml 复制代码
spring:
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://mysql-master:3306/test
username: root
password: root
redis:
password: 123456
lettuce:
pool:
max-active: 50
max-idle: 5
min-idle: 0
max-wait: 5000
database: 0
cluster:
nodes:
redis01:6379,redis02:6379,redis03:6379,redis04:6379,redis05:6379,r
edis06:6379
#host: 127.0.0.1
connect-timeout: 5000
timeout: 5000

配置 application-local.yml 来测试,注意 ip 地址为我们集群所在服务器的 ip 地址。

yaml 复制代码
spring:datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://172.27.0.6:9306/test
username: root
password: root
redis:
password: 123456
lettuce:
pool:
max-active: 50
max-idle: 5
min-idle: 0
max-wait: 5000
database: 0
cluster:
nodes: 172.27.0.6:6379
#host: 127.0.0.1
connect-timeout: 5000
timeout: 5000

上传 jar 包到 java/user 目录下面

执行以下命令来测试我们的服务

powershell 复制代码
java -jar testmymysql-0.0.1-SNAPSHOT.jar --spring.profiles.active=local

通过浏览器访问,可以看到我们的功能正常,如果想本地调试,也可以在本地搭

建一个 redis 或者 mysql 连完成本地的调试。

下面开始制作我们的 java 镜像的 dockerfile

制作 nginx 的配置文件 bit.conf 放到 /data/maxhou/mymicroservice/nginx

制作 nginx 的 dockerfile,进入目录 /data/maxhou/mymicroservice/nginx, vi Dockerfile

进入目录/data/maxhou/mymicroservice,添加应用容器编排文件 dockercompose.yml

yaml 复制代码
version: "3"
services:
mysql-master:
build:
context: ./mysql
dockerfile: ./master/Dockerfile-master
image: mysqlmaster:v2.0
restart: always
container_name: mysql-master
volumes:
- ./mastervarlib:/var/lib/mysql
ports:
- 9306:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=1','--log-bin=master-bin',
'--binlog-ignore-db=mysql',
'--binlog_cache_size=256M',
'--binlog_format=mixed',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
mysql-slave:
build:
context: ./mysql
dockerfile: ./slave/Dockerfile-slave
image: mysqlslave:v2.0
restart: always
container_name: mysql-slave
volumes:
- ./slavevarlib:/var/lib/mysql
ports:
- 9307:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=2',
'--relay_log=slave-relay',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
depends_on:
- mysql-master
redis01:
image: myredis:v1.0
build: ./redis
ports:
- 6379:6379
container_name: redis01
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis02:
image: myredis:v1.0
container_name: redis02
healthcheck:
test: /redis/redis-cli pinginterval: 10s
timeout: 5s
retries: 10
redis03:
image: myredis:v1.0
container_name: redis03
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis04:
image: myredis:v1.0
container_name: redis04
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis05:
image: myredis:v1.0
container_name: redis05
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis06:
image: myredis:v1.0
container_name: redis06
healthcheck:
test: /redis/redis-cli ping
interval: 10s
timeout: 5s
retries: 10
redis07:
image: myredis:v1.0
container_name: redis07
entrypoint: ["/redis/redis-cli","--
cluster","create","redis01:6379","redis02:6379","redis03:6379","re
dis04:6379","redis05:6379","redis06:6379","--clusterreplicas","1","-a","123456","--cluster-yes"]
depends_on:
redis01:condition: service_healthy
redis02:
condition: service_healthy
redis03:
condition: service_healthy
redis04:
condition: service_healthy
redis05:
condition: service_healthy
redis06:
condition: service_healthy
web:
image: mynginx:v2.0
build:
context: ./nginx
ports:
- 80:80
depends_on:
myuser:
condition: service_started
myuser2:
condition: service_started
myuser:
image: myuser:v2.0
build:
context: ./app/user
depends_on:
redis01:
condition: service_healthy
redis02:
condition: service_healthy
redis03:
condition: service_healthy
redis04:
condition: service_healthy
redis05:
condition: service_healthy
redis06:
condition: service_healthy
mysql-master:
condition: service_started
myuser2:
image: myuser:v2.0
build:context: ./app/user
depends_on:
redis01:
condition: service_healthy
redis02:
condition: service_healthy
redis03:
condition: service_healthy
redis04:
condition: service_healthy
redis05:
condition: service_healthy
redis06:
condition: service_healthy
mysql-master:
condition: service_started

批量构建镜像

启动服务

测试服务,因为我们已经配置了 nginx 的反向代理,请求地址变为 http://服务器

ip/userGet/{用户 id}

查看两个用户服务的日志,可以看到 2 个服务会轮流请求,也就是发生了负载均


查看 redis 已经写入数据

至此我们完整的一个微服务拓扑就开发完了,实际工作中,我们微服务往往是多个,而且有对应的注册中心,配置中心,网关等一系列微服务,最后我们释放资源

相关推荐
螺旋小蜗3 小时前
docker-compose文件属性(3)顶部元素networks
运维·docker·容器
ICT董老师3 小时前
Kubernetes从私有镜像仓库拉取容器镜像时的身份验证
ubuntu·docker·云原生·容器·kubernetes
goodlook01234 小时前
open-java21镜像构建
java·运维·docker·容器
别多香了5 小时前
k8s管理
docker·容器·kubernetes
林_学5 小时前
Docker Desktop 全卸了,新项目上线从3天缩短到3分钟
运维·docker·容器
陈平安Java and C5 小时前
Docker镜像原理
运维·docker·容器
程序员在线炒粉8元1份顺丰包邮送可乐5 小时前
Docker 部署PaddleOCR 实战教程(含离线模型、接口调用、排障)
运维·docker·ai·容器
码猩6 小时前
自用centos9离线安装n8n非docker部署本版
运维·docker·容器
java_logo6 小时前
MySQL Docker 容器化部署全指南
数据库·mysql·docker·mysql部署·mysql部署文档·mysql部署方案·mysql部署手册