Docker Swarm中.NET容器无法连接MySQL数据库的问题求助
Docker Swarm环境下.NET容器无法连接MySQL的权限问题
在Windows 10上搭建Docker Swarm环境,包含.NET主容器和MySQL 5.7容器,使用Docker Secrets存储密码,但无论是root还是自定义用户myuser都无法通过密码验证,提示权限拒绝,主容器也无法和数据库通信。
Startup类代码
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddCors(options => { options.AddPolicy("AllowSpecificOrigin", builder => builder.WithOrigins("http://myurl.com", "http://client.myurl.com") .AllowAnyMethod().AllowAnyHeader()); }); services.Configure<FormOptions>(x => { x.ValueLengthLimit = int.MaxValue; x.MultipartBodyLengthLimit = int.MaxValue; // This is in size in bytes (100L * 1024 * 1024; // 100 MB) }); services.AddDataProtection().PersistKeysToFileSystem(new DirectoryInfo("/app/data")); // Used to store cookie data and such services.AddSingleton<IVersionInfoService, VersionInfoService>(); // This is my custom service that allows all Controllers to access the global VersionInfo var dbPassword = File.ReadAllText("/run/secrets/db_user_password"); Console.WriteLine($"Database password : {dbPassword}"); var connectionString = $"Server=db; Port=3306; Database=mydatabase; Uid=myuser; Pwd={dbPassword};"; services.AddDbContext<EmulatorDbContext>(options =>options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString))); services.AddControllers(); services.AddRazorPages(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) //, EmulatorDbContext context { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Error"); // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts. app.UseHsts(); } //app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.UseCors("AllowSpecificOrigin"); // I added app.UseEndpoints(endpoints => { // Specific route for updater subdomain endpoints.MapControllers(); }); //SeedDatabase(context); } }
EmulatorDbContext类代码
public class EmulatorDbContext : DbContext { public DbSet<User> Users { get; set; } public EmulatorDbContext(DbContextOptions<EmulatorDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>().ToTable("Users"); // Configure serialization for the PasswordHash and PasswordSalt modelBuilder.Entity<User>() .Property(e => e.PasswordHash) .HasConversion( v => Convert.ToBase64String(v), v => Convert.FromBase64String(v)) .Metadata.SetValueComparer(new ValueComparer<byte[]>( (c1, c2) => c1.SequenceEqual(c2), c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v)), c => c.ToArray())); // Configure serialization and comparison for the PasswordSalt modelBuilder.Entity<User>() .Property(e => e.PasswordSalt) .HasConversion( v => Convert.ToBase64String(v), v => Convert.FromBase64String(v)) .Metadata.SetValueComparer(new ValueComparer<byte[]>( (c1, c2) => c1.SequenceEqual(c2), c => c.Aggregate(0, (a, v) => HashCode.Combine(a, v)), c => c.ToArray())); } }
docker-compose.yml配置
version: '3.8' services: app: image: examplerserver build: context: . ports: - "80:80" # Listen on port 80 for HTTP, change to "443:443" for HTTPS with SSL setup depends_on: - db environment: DB_HOST: db DB_NAME: mydatabase DB_USER: myuser DB_PASSWORD_FILE: /run/secrets/db_user_password # Path to the encrypted Docker Secrets file JWT_KEY_FILE: /run/secrets/jwt_key volumes: - app-data:/app/data # Additional volume for storing files secrets: - db_user_password - jwt_key db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password MYSQL_PASSWORD_FILE: /run/secrets/db_user_password MYSQL_DATABASE: mydatabase MYSQL_USER: myuser volumes: - db-data:/var/lib/mysql secrets: - db_root_password # Name of the Secrets file that is encrypted and stores database password details - db_user_password secrets: db_root_password: external: true # true means we need to manually create the secret outside of compose db_user_password: external: true jwt_key: external: true volumes: db-data: app-data:
启动脚本
@echo off REM Stop and remove all Docker services and stacks docker stack rm exampleserver REM Waiting for Docker services to shut down completely timeout /t 10 REM Remove Docker secrets docker secret rm db_root_password docker secret rm db_user_password docker secret rm jwt_key REM Remove Docker volumes docker volume rm exampleserver_db-data docker volume rm exampleserver_app-data REM Clean up Docker networks docker network prune -f REM Recreate Docker secrets echo p | docker secret create db_root_password - echo p | docker secret create db_user_password - echo key | docker secret create jwt_key - REM Deploy Docker stack docker-compose build --no-cache docker stack deploy -c docker-compose.yml exampleserver REM Display Docker status docker ps docker service ls docker volume ls docker network ls REM Follow logs of the main app service docker service logs exampleserver_app -f pause
错误日志及操作信息
执行登录命令时的错误:
C:\Users\me\source\repos\ExampleServer\ExampleServer>docker exec -it exampleserver_db.1.rbyj8fono3cbpkh8qhgob91h9 mysql -h db -u myuser -p Enter password: ERROR 1045 (28000): Access denied for user 'myuser'@'10.0.5.4' (using password: YES) C:\Users\me\source\repos\ExampleServer\ExampleServer>docker exec -it exampleserver_db.1.rbyj8fono3cbpkh8qhgob91h9 mysql -u root -p Enter password: ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
MySQL服务日志关键内容:
exampleserver_db.1.rbyj8fono3cb@docker-desktop | 2024-04-20T01:18:28.990655Z 2 [Note] Access denied for user 'myuser'@'10.0.5.4' (using password: YES) exampleserver_db.1.rbyj8fono3cb@docker-desktop | 2024-04-20T01:28:01.106363Z 3 [Note] Access denied for user 'root'@'localhost' (using password: YES)
问题排查与解决
核心问题:Docker Secrets创建时引入了多余换行符
在Windows的CMD脚本中,echo p | docker secret create ...命令会自动在输出末尾添加换行符,导致存储到Docker Secrets中的密码实际是p\n而非预期的p,MySQL容器读取到的密码包含换行符,自然无法匹配你输入的p。
解决步骤
- 修改秘密创建命令:使用
set /p避免添加换行符,替换启动脚本中创建秘密的部分:
REM 原命令 REM echo p | docker secret create db_root_password - REM echo p | docker secret create db_user_password - REM echo key | docker secret create jwt_key - REM 修改后的命令 set "pass=p" echo|set /p="%pass%" | docker secret create db_root_password - echo|set /p="%pass%" | docker secret create db_user_password - set "jwt=key" echo|set /p="%jwt%" | docker secret create jwt_key -
echo|set /p="..."会输出不带换行符的字符串,确保秘密内容完全匹配预期值。
彻底清理环境后重新部署:
- 执行原脚本中的清理步骤(移除栈、秘密、卷、网络)
- 替换创建秘密的命令后重新运行脚本
验证密码正确性:
- 进入MySQL容器查看秘密文件内容:
确保输出没有多余换行符,就是你设置的docker exec -it <db-container-id> cat /run/secrets/db_user_passwordp
- 进入MySQL容器查看秘密文件内容:
额外检查点:
- 确认MySQL容器启动时没有报错,日志中显示数据库初始化完成
- 主容器中输出的密码日志(
Database password : {dbPassword})也应该没有换行符
这样修改后,密码就能正确匹配,root和myuser用户都能正常登录,主容器也能建立数据库连接。
内容的提问来源于stack exchange,提问作者UnSure
相关产品推荐
相关产品推荐

