You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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。

解决步骤

  1. 修改秘密创建命令:使用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="..."会输出不带换行符的字符串,确保秘密内容完全匹配预期值。

  1. 彻底清理环境后重新部署:

    • 执行原脚本中的清理步骤(移除栈、秘密、卷、网络)
    • 替换创建秘密的命令后重新运行脚本
  2. 验证密码正确性:

    • 进入MySQL容器查看秘密文件内容:
      docker exec -it <db-container-id> cat /run/secrets/db_user_password
      
      确保输出没有多余换行符,就是你设置的p
  3. 额外检查点:

    • 确认MySQL容器启动时没有报错,日志中显示数据库初始化完成
    • 主容器中输出的密码日志(Database password : {dbPassword})也应该没有换行符

这样修改后,密码就能正确匹配,root和myuser用户都能正常登录,主容器也能建立数据库连接。


内容的提问来源于stack exchange,提问作者UnSure

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 09:15:55