《XAPI项目》:GitHub仓库(勿打🚫小破站一个)
这篇文章,主要内容是记录使用Docker Compose 部署《XAPI项目》遇道的问题及解决方案
在启动 Docker 容器后,将本地的 MySQL 数据导入到容器内的 MySQL 数据库中。以下是一种方法:
mysql:
image: mysql:8.0
volumes:
- ./xapi/mysql8.0/mysql-data:/var/lib/mysql
- ./data.sql:/docker-entrypoint-initdb.d/data.sql
environment:
- MYSQL_ROOT_PASSWORD=root
- MYSQL_DATABASE=xapi
ports:
- "3306:3306"
docker-compose exec mysql mysql -uroot -p
以下是一些步骤:
environment:
- DB_HOST=mysql # 设置 MySQL 主机地址为容器名称
- DB_USER=root
- DB_PASSWORD=root
- DB_NAME=xapi
// 从环境变量中获取 MySQL 连接信息
dbHost := os.Getenv("DB_HOST")
dbUser := os.Getenv("DB_USER")
dbPassword := os.Getenv("DB_PASSWORD")
dbName := os.Getenv("DB_NAME")
// 构建 MySQL 连接字符串
dataSourceName := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8&parseTime=true", dbUser, dbPassword, dbHost, dbName)
db.MyDB = db.ConnectionPool(dataSourceName)
mysql -uroot -p
GRANT ALL PRIVILEGES ON *.* TO 'root'@'172.22.0.5' IDENTIFIED BY 'your_password' WITH GRANT OPTION;
将 ‘172.22.0.5’ 替换为你的 Docker 容器内部的主机 IP 地址,将 ‘your_password’ 替换为你的 MySQL root 用户的密码。
然后,刷新 MySQL 权限:
FLUSH PRIVILEGES;
version: '3'
services:
mysql:
image: mysql:8.0 # MySQL的Docker镜像名称,你可以根据需要选择版本
environment:
- MYSQL_ROOT_PASSWORD=root # 设置MySQL的root密码
- MYSQL_DATABASE=xapi # 创建一个MySQL数据库,并设置其名称
ports:
- "3306:3306" # 映射MySQL的端口
volumes:
- ./docker-mysql8.0/mysql-data:/var/lib/mysql # 将MySQL数据持久化到宿主机的目录
- ./init.sql:/docker-entrypoint-initdb.d/init.sql # 将 ./init.sql 映射到容器内的 /docker-entrypoint-initdb.d/init.sql,这是 MySQL 容器初始化时会自动运行的 SQL 初始化脚本目录。
将一个名为 init.sql 的初始化脚本映射到了 MySQL 容器内部的 /docker-entrypoint-initdb.d/init.sql 目录。这个脚本会在 MySQL 容器启动时自动执行。在 init.sql 文件中,你可以添加设置 MySQL 权限的 SQL 命令,init.sql文件内容如下:
-- 授予用户对数据库的访问权限(替换 database_name)
GRANT ALL PRIVILEGES ON database_name.* TO 'username'@'%';
-- 举例:
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%';
GRANT ALL PRIVILEGES ON xapi.* TO 'xapiuser'@'%';
-- 刷新权限
FLUSH PRIVILEGES;
替换 username 、database_name 为实际的用户名、数据库名称。然后将这个 init.sql 文件与 docker-compose.yml 放在同一目录下,启动 Docker Compose 时,它会自动运行初始化脚本以设置权限。
⚠️ 注意:该init.sql 只会在容器首次启动时执行,一旦执行过,就会生成/var/lib/mysql 中的数据,也就是只要 /var/lib/mysql 中数据不是空的,之后在重启这个容器,都不会触发init.sql的执行。
mysql:
# ...
volumes:
- ./docker-mysql8.0/mysql-data:/var/lib/mysql
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
- ./my.cnf:/etc/mysql/my.cnf # 将 my.cnf 映射到容器内的 MySQL 配置文件
[mysqld]
bind-address = 0.0.0.0
version: '3'
networks:
xapi_net:
services:
backend:
image: xapi-backend-image # 后端项目的Docker镜像名称
ports:
- "8090:8090" # 映射后端项目的端口
depends_on:
- mysql
- nacos
- gateway
volumes:
- ./docker-xapi-backend/conf:/app/conf # 将MySQL数据持久化到宿主机的目录
extra_hosts:
- "xapi-gateway.com:192.168.2.104"
custom_hosts
的文本文件,内容如下:192.168.2.104 xapi-gateway.com
docker run -d --name mycontainer -v /path/to/custom_hosts:/etc/hosts my_image
version: '3'
services:
myservice:
image: my_image
volumes:
- /path/to/custom_hosts:/etc/hosts
在该Docker Compose配置中,服务都连接到了一个自定义的网络 xapi_net。可以使用以下命令来查找某个服务容器的IP地址:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' container_name_or_id
例如,如果你想查找 backend 服务的IP地址,可以运行以下命令:
docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' backend
详细错误日志
docker-xapi-gateway-1 | 2023-09-09T09:30:53.982Z WARN proxy/proxy.go:210 [CallProxy] received rpc err: Failed to invoke the method GetInvokeUser. No provider available for the service tri://:@172.23.0.4:80/?interface=rpc_api.UserInfo&group=&version= from registry nacos://nacos:8848?registry=nacos®istry.group=®istry.label=true®istry.namespace=®istry.preferred=false®istry.role=0®istry.timeout=5s®istry.ttl=10s®istry.weight=0®istry.zone=&remote-client-name=dubbo.registries-nacos-nacos%3A8848&simplified=false on the consumer 172.23.0.4 using the dubbo version 3.0.0 .Please check if the providers have been started and registered.
错误解析:
Failed to invoke the method GetInvokeUser
:这是Dubbo服务调用中的一个方法名,表示在Dubbo消费者中尝试调用名为 GetInvokeUser 的方法时出现了问题。No provider available for the service
:这表示Dubbo消费者无法找到提供名为 tri://:@172.23.0.4:80/?interface=rpc_api.UserInfo&group=&version=
的服务的提供者。registry nacos://nacos:8848?registry=nacos®istry.group=®istry.label=true®istry.namespace=®istry.preferred=false®istry.role=0®istry.timeout=5s®istry.ttl=10s®istry.weight=0®istry.zone=&remote-client-name=dubbo.registries-nacos-nacos%3A8848&simplified=false
:这是从Nacos注册中心获取服务提供者信息的详细信息,它指定了使用Nacos进行服务发现的配置。Please check if the providers have been started and registered
:这是建议,意思是你应该检查服务提供者是否已经启动并在Nacos注册中心进行了注册。docker-xapi-backend-1 | 2023-09-09T10:06:57.033Z ERROR protocol/protocol.go:201 provider service tri://:@:20000/?interface=rpc_api.IntefaceInfo&group=&version= register registry nacos://:@nacos:8848/?interface=rpc_api.IntefaceInfo&group=&version= error, error message is retry3times request failed,err=Post "http://nacos:8848/nacos/v1/ns/instance": dial tcp 172.23.0.3:8848: connect: connection refused
docker-xapi-backend-1 | 2023-09-09T10:06:57.033Z ERROR config/provider_config.go:149 service IntefaceInfoServerImpl export failed! err: Registry protocol new exporter error, registry is {nacos://nacos:8848?nacos.access=&nacos.group=DEFAULT_GROUP&nacos.namespaceId=&nacos.password=&nacos.secret=&nacos.username=®istry=nacos®istry.group=®istry.label=true®istry.namespace=®istry.preferred=false®istry.role=3®istry.timeout=5s®istry.ttl=10s®istry.weight=0®istry.zone=&remote-client-name=dubbo.registries-nacos-nacos%3A8848&simplified=false&timeout=5s}, url is {tri://:20000/rpc_api.IntefaceInfo?accesslog=&app.version=3.0.0&application=dubbo.io&auth=&bean.name=IntefaceInfoServerImpl&cluster=failover&config.tracing=&environment=dev&execute.limit=&execute.limit.rejected.handler=&export=true&interface=rpc_api.IntefaceInfo&loadbalance=random&message_size=4&metadata-type=local&module=sample&name=dubbo.io&organization=dubbo-go&owner=dubbo-go¶m.sign=&pid=1®istry=nacos®istry.role=3&release=dubbo-golang-3.0.0&retries=&serialization=&service.filter=echo%2Cmetrics%2Ctoken%2Caccesslog%2Ctps%2Cgeneric_service%2Cexecute%2Cpshutdown&side=provider×tamp=1694254017&tps.limit.interval=&tps.limit.rate=&tps.limit.rejected.handler=&tps.limit.strategy=&tps.limiter=&warmup=}
docker-xapi-backend-1 | 2023-09-09T10:06:57.044Z ERROR protocol/protocol.go:201 provider service tri://:@:20000/?interface=rpc_api.UserInfo&group=&version= register registry nacos://:@nacos:8848/?interface=rpc_api.UserInfo&group=&version= error, error message is retry3times request failed,err=Post "http://nacos:8848/nacos/v1/ns/instance": dial tcp 172.23.0.3:8848: connect: connection refused
在docker-compose.yml中,我们写了
backend:
image: xiaoxiongmao5/xapi-backend:1.0.5
container_name: xapi-backend
ports:
- "8090:8090"
depends_on:
- mysql
- nacos #这里 depends_on nacos,只会等待容器启动
- gateway
这意味着虽然backend容器等待nacos容器启动,但它不能确保nacos内部的服务已经完全初始化并准备好接受连接。
要解决这个问题,可以在backend项目内添加一些逻辑来等待nacos服务准备就绪。一种常见的做法是使用一个轮询机制,不断地检查nacos服务是否已经可用。可用后再加载nacos相关的配置。
// 参数: Nacos 服务地址和端口
func RegisterServiceToNacos(nacosHost string, nacosPort int) {
// 最大尝试次数和当前尝试次数
maxAttempts := 30
attempt := 1
// 循环检查 Nacos 服务是否可用
for attempt <= maxAttempts {
url := fmt.Sprintf("http://%s:%d/nacos/health", nacosHost, nacosPort)
resp, err := http.Get(url)
if err == nil && resp != nil && resp.StatusCode == http.StatusOK {
glog.Log.Info("Nacos is up and running, starting backend service...")
//开始加载nacos相关的配置
LoadDubboConfig()
break
} else {
glog.Log.Infof("Attempt %d: Nacos is not ready yet, waiting...", attempt)
attempt++
time.Sleep(5 * time.Second)
}
}
if attempt > maxAttempts {
glog.Log.Info("Max attempts reached. Nacos may not be available.")
// 在这里可以添加适当的错误处理或退出逻辑
} else {
// Nacos 可用后执行启动后端服务的操作
// 示例:启动后端服务
// startBackendService()
}
}
上述函数将等待Nacos服务最多30次,每次等待5秒,以检查Nacos是否已准备就绪。一旦Nacos服务就绪,它将加载nacos相关的配置。
访问 http://localhost:8000/interface_info/2
显式如下:
Error response
Error code: 404
Message: File not found.
Error code explanation: HTTPStatus.NOT_FOUND - Nothing matches the given URI.
请求日志如下:
docker-xapi-frontend-1 | 172.22.0.1 - - [09/Sep/2023:16:40:19 +0000] "GET /interface_info/2 HTTP/1.1" 404 555 "http://localhost/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36" "-"
docker-xapi-frontend-1 | 2023/09/09 16:40:19 [error] 32#32: *18 open() "/usr/share/nginx/html/interface_info/2" failed (2: No such file or directory), client: 172.22.0.1, server: localhost, request: "GET /interface_info/2 HTTP/1.1", host: "localhost", referrer: "http://localhost/"
config.ts
export default defineConfig({
history: { type: 'browser' }, // 默认是 browser
nginx.conf
server {
listen 80;
root /usr/share/nginx/html;
location / {
# 用于配合 browserHistory使用
try_files $uri $uri/index.html /index.html;
}
}
dockerfile
# 使用一个基础的 Node.js 镜像
FROM node:14 as builder
# 设置工作目录
WORKDIR /app
# 将本地项目文件复制到容器中
COPY . .
# 安装依赖并构建前端应用程序
RUN npm install
RUN npm run build
# 第二阶段:使用一个基础的 Nginx 镜像来创建最终的容器
FROM nginx:alpine
# 复制自定义Nginx配置文件
COPY nginx.conf /etc/nginx/conf.d/default.conf
# 复制构建后的前端文件到 Nginx 的默认站点目录
COPY --from=builder /app/dist /usr/share/nginx/html
# 定义容器暴露的端口
EXPOSE 80
# 启动 Nginx
CMD ["nginx", "-g", "daemon off;"]
docker-compose.yml
version: '3'
services:
single-frontend:
build:
context: .
dockerfile: Dockerfile
ports:
- "80:80" # 映射容器的80端口到主机的80端口
restart: always # 总是重新启动容器