docker compose启动springcloud微服务案例
gitee案例仓库入口
安装docker与docker compose
1.ubantu安装docker
# 更新 apt 包索引,输入以下命令:
sudo apt-get update
# 安装docker依赖,因为docker想在ubuntu系统运行起来还需要依赖一些别的环境
sudo apt-get install ca-certificates curl gnupg lsb-release
# 添加Docker官方GPG密钥
sudo curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# 添加Docker软件源
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# 安装docker
sudo apt-get install docker-ce docker-ce-cli containerd.io
# 启动docker
service docker start
# 配置docker服务开机自启
systemctl enable docker
# 配置docker加速器
# 执行下面命令
sudo mkdir -p /etc/docker
# 再接着执行
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://mu03bz48.mirror.aliyuncs.com"]
}
EOF
# 重启docker
sudo systemctl daemon-reload
sudo systemctl restart docker
如果无法拉取镜像用这个
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://do.nark.eu.org",
"https://dc.j8.work",
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.mirrors.ustc.edu.cn",
"https://docker.nju.edu.cn"
]
}
EOF
离线安装docker compose
github下载安装包,我下载的是v2.30.版本
https://github.com/docker/compose/releases
# 复制 Docker-Compose 到 /usr/local/bin
sudo cp -f ./docker-compose-linux-x86_64 /usr/local/bin/docker-compose
# 赋予 Docker-Compose 执行权限
sudo chmod +x /usr/local/bin/docker-compose
# 查看docker-compose版本
docker-compose -v
开始创建微服务项目
最终的项目结构:
首先创建一个父工程
删除多余文件,只需要pom.xml下载依赖
对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>
<groupId>com.family</groupId>
<artifactId>springclouddemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<!-- 子模块引入 product eureka gateway-->
<modules>
<module>product</module>
<module>eureka</module>
<module>gateway</module>
</modules>
<name>springclouddemo</name>
<packaging>pom</packaging>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring-boot.version>2.6.13</spring-boot.version>
<spring-cloud.version>2021.0.5</spring-cloud.version>
<eureka-server.version>3.1.4</eureka-server.version>
<eureka-client.version>3.1.4</eureka-client.version>
<mysql.version>8.0.33</mysql.version>
<!-- 注意点:这里用变量声明版本,标签必须有.version作为后缀:比如mysql.version不能定义为<mysql></mysql>-->
</properties>
<dependencies>
<!--公共依赖,在这里声明的依赖,子模块默认会继承-->
</dependencies>
<dependencyManagement>
<!--在 dependencyManagement里面声明的依赖,子类需要再显示声明一遍,才能下载下来,只是不用写版本号了,由这里统一控制-->
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
<version>${eureka-server.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>${eureka-client.version}</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!-- maven编译插件 -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
创建eureka模块
重写eureka模块的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">
<!-- 依赖父工程 parent -->
<parent>
<artifactId>springclouddemo</artifactId>
<groupId>com.family</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>eureka</artifactId>
<!--采用jdk8 -->
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<!-- 引入依赖eureka-server:不用指定版本,因为父工程限定了-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
<!-- 指定编译后的jar包名称 finalName-->
<build>
<finalName>eureka</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
启动类添加注解@EnableEurekaServer
编写application.yaml文件
spring:
application:
name: eureka-server
server:
port: 8700 #服务注册中心端口号
eureka:
instance:
hostname: eureka-server #服务注册中心IP地址
client:
registerWithEureka: false #是否向服务注册中心注册自己
fetchRegistry: false #是否检索服务
serviceUrl: #服务注册中心的配置内容,指定服务注册中心的位置
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/ # 这里defaultZone:http://eureka-server:8700/eureka/ 请求eureka注册,原本http后面eureka-server是ip的,
# 但因为是采用docker compose启动,所以ip可以用服务名代指
编写Dockerfile编写镜像
FROM openjdk:8
# 安装 ping 工具
RUN apt-get update && apt-get install -y iputils-ping
COPY ./eureka.jar /tmp/eureka.jar
ENTRYPOINT ["java","-jar","/tmp/eureka.jar"]
EXPOSE 8700
编写docker-compose.yaml文件启动
version: '3'
services:
eureka-server: # 注意此处的名称需要添加到eureka客户端的defaultZone中,相当于docker内的主机名
build:
context: ./eureka #编译的dockerfile位置,路径在下面图片有说
ports:
- "8700:8700" # 对外访问端口
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8700/eureka/" ] # 健康检查接口
interval: 30s
timeout: 10s
retries: 3
product-server:
build:
context: ./product
ports:
- "8701:8701"
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8701/actuator/health" ]
depends_on:
- eureka-server
gateway-server:
build:
context: ./gateway
ports:
- "8082:8082"
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8082/actuator/health" ]
depends_on: # depends_on依赖于其他服务,保证eureka-server先启动,再启动gateway-server
- eureka-server
我这里编写docker compose文件里面的build构建镜像地址都是相对的。
创建了myapp目录,里面有eureka目录,gateway目录,product目录,docker-compose.yaml文件
eureka目录放jar包和dockerfile文件
gateway目录放jar包和dockerfile文件和wait-for-it.sh脚本(被我改名为wait.sh)
product目录放jar包和dockerfile文件wait-for-it.sh脚本(被我改名为wait.sh)
创建product服务
注册进eureka服务中心
老样子,创建一个模块,更改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">
<parent>
<artifactId>springclouddemo</artifactId>
<groupId>com.family</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>product</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<finalName>product</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
启动类添加注解:
@EnableEurekaClient
@EnableDiscoveryClient
创建一个接口hello,后面测试用
product服务的application.yaml配置
spring:
application:
name: product-server
server:
port: 8701
eureka:
client:
service-url:
defaultZone: http://eureka-server:8700/eureka/ #此处的eurakaserver为docker-compose.yml中eureka-server工程的容器定义
instance:
prefer-ip-address: true # 使用IP注册,避免容器id:port调用报错
product服务的dockerfile文件
FROM openjdk:8
# 安装 Bash
RUN apt-get update && apt-get install -y bash
# 安装 curl 工具
RUN apt-get update && apt-get install -y curl
# 安装 ping 工具
RUN apt-get update && apt-get install -y iputils-ping
# 复制 JAR 文件
COPY ./product.jar /tmp/product.jar
# 复制 start.sh 脚本
COPY wait.sh /tmp/wait.sh
# 赋予 start.sh 脚本执行权限
RUN chmod +x /tmp/wait.sh
# 设置默认的环境变量
ENV EUREKA_URL=http://eureka-server:8700
# 启动命令
# 默认启动命令
CMD ["bash", "/tmp/wait.sh"]
# 暴露端口
EXPOSE 8701
需要product服务的wait.sh文件,
因为dockercompose的depends_on只能决定那个服务先启动,但是发生请求注册时接口还没准备好,此时刚启动项目就会报错。
比如报错:com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server
#!/bin/bash
# 获取环境变量,默认值为 eurekaserver:8701
EUREKA_URL=${EUREKA_URL:-http://eureka-server:8700}
echo "Starting wait script..."
# 检查网络连接:eureka-server:docker-compose.yaml配置的服务名及ip
if ! ping -c 1 eureka-server &> /dev/null; then
echo "eureka-server is not reachable"
exit 1
fi
while :
do
# 访问 eureka 注册中心,获取 HTTP 状态码
CODE=$(curl -I -m 10 -o /dev/null -s -w %{http_code} $EUREKA_URL)
# 判断状态码为 200
if [[ $CODE -eq 200 ]]; then
# 输出绿色文字,并跳出循环
echo -e "\033[42;34m $EUREKA_URL is ok \033[0m"
break
else
# 暂停 1 秒
sleep 1
echo "EUREKA_URL $EUREKA_URL is not available yet, status code: $CODE"
fi
done
echo "EUREKA_URL is available, starting product service..."
# 准备好后再启动product的jar包,保证不会刚启动报错
exec java -jar /tmp/product.jar
开始测试:
将jar包,dockerfile,docker-compose,wait.sh放到相应位置
eureka目录
cd 切换到docker-compose.yaml服务器的位置
docker compose up
有可能会启动失败,提供几条调试日志命令查看:
验证网络配置:
使用 docker network ls 命令检查网络列表,确保默认网络 myapp_default 已经创建。
使用 docker inspect myapp_default 命令检查网络配置,确保 eureka-server 和 product-service 都连接到了这个网络。
查看 eureka-server 的日志,确认是否有任何启动错误或异常信息
docker-compose logs eureka-server
docker-compose logs product-server
首先,确保 eureka-server 服务已经启动并运行正常。可以通过以下命令检查容器状态:
docker-compose ps
确保 product-service 可以通过 eureka-server 的主机名访问到 Eureka 服务。可以在 product-service 容器中运行以下命令:
docker exec -it product-service /bin/sh
再接着在容器内执行请求
curl http://eureka-server:8700/eureka/
下一步整合gateway服务
老样子:新建模块gateway,重写pom.xml
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.family</groupId>
<artifactId>springclouddemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>gateway</artifactId>
<name>gateway</name>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>3.1.9</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
<build>
<finalName>gateway</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
编写application文件–gateway
server:
port: 8082
eureka:
client:
service-url:
defaultZone: http://eureka-server:8700/eureka/ #此处的eurakaserver为docker-compose.yml中eureka-server工程的容器定义
instance:
prefer-ip-address: true # 使用IP注册,避免容器id:port调用报错
spring:
application:
name: gateway-server
cloud:
gateway:
discovery:
locator:
enabled: true
lowerCaseServiceId: true
routes:
- id: product-server
uri: lb://product-server
predicates:
- Path=/hello/**
dockerfile文件—gateway的
FROM openjdk:8
# 安装 Bash
RUN apt-get update && apt-get install -y bash
# 安装 curl 工具
RUN apt-get update && apt-get install -y curl
# 安装 ping 工具
RUN apt-get update && apt-get install -y iputils-ping
# 复制 JAR 文件
COPY ./gateway.jar /tmp/gateway.jar
# 复制 start.sh 脚本
COPY wait.sh /tmp/wait.sh
# 赋予 start.sh 脚本执行权限
RUN chmod +x /tmp/wait.sh
# 设置默认的环境变量
ENV EUREKA_URL=http://eureka-server:8700
# 启动命令
# 默认启动命令
CMD ["bash", "/tmp/wait.sh"]
# 暴露端口
EXPOSE 8082
编写wait,sh
#!/bin/bash
# 获取环境变量,默认值为 eurekaserver:8701
EUREKA_URL=${EUREKA_URL:-http://eureka-server:8700}
echo "Starting wait script..."
# 检查网络连接
if ! ping -c 1 eureka-server &> /dev/null; then
echo "eureka-server is not reachable"
exit 1
fi
while :
do
# 访问 eureka 注册中心,获取 HTTP 状态码
CODE=$(curl -I -m 10 -o /dev/null -s -w %{http_code} $EUREKA_URL)
# 判断状态码为 200
if [[ $CODE -eq 200 ]]; then
# 输出绿色文字,并跳出循环
echo -e "\033[42;34m $EUREKA_URL is ok \033[0m"
break
else
# 暂停 1 秒
sleep 1
echo "EUREKA_URL $EUREKA_URL is not available yet, status code: $CODE"
fi
done
echo "EUREKA_URL is available, starting product service..."
# 修改启动的jar包,应该要启动gateway.jar,不是product.jar了,上面不用动
exec java -jar /tmp/gateway.jar
最后的docker-compose.yaml文件就长这样了
version: '3'
services:
eureka-server: # 注意此处的名称需要添加到eureka客户端的defaultZone中,相当于docker内的主机名
build:
context: ./eureka
ports:
- "8700:8700"
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8700/eureka/" ]
interval: 30s
timeout: 10s
retries: 3
product-server:
build:
context: ./product
ports:
- "8701:8701"
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8701/actuator/health" ]
depends_on:
- eureka-server
gateway-server:
build:
context: ./gateway
ports:
- "8082:8082"
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8082/actuator/health" ]
depends_on:
- eureka-server
编译打包测试:
上面有说打成jar包步骤,放到对应目录,docker compose up (有坑,没有自动重新编译)
但有可能启动的是旧的镜像,所以先执行docker compose build
再接着执行docker compose up
或者提供另一种方案:
docker images
docker rmi 镜像名称(这里有可能不能删除,提示有容器,需要先删除容器docker rm 容器id,再执行删除镜像)
docker rm 容器id
再执行docker compose up 就不会有缓存,且编译完后自动启动容器,等待即可!!!
访问:http://虚拟机IP:8700/
无需加/eureka后缀
两个实例也注册到eureka中了,访问gateway的网关
完美收官!!!