在前面一篇文章中,演示了一个ASP.NET Core MVC应用程序,这个程序使用了MySQL,负载均衡,涉及到要创建多个容器,Volume卷以及自定义网络。这种纯手工的方式很容易出错,不仅每一个命令必须输入正确,并且每个步骤还要按照先后顺序来输入,比如再创建MySQL容器的时候,必须事先把其依赖的卷创建好。如果漏了某个步骤,或者某个步骤顺序发送错误,则程序就运行不起来。如果应用程序架构比较简单,这种问题不大,但当应用程序设计模块比较多时,可能会想到自己编写一些脚本来实现自动化,而Docker为我们提供了docker-compose功能,利用这以功能可以实现对复杂应用的管理,包括容器,Volume,自定义网络中。

准备工作


    在开始之前,我们将之前创建的容器,网络和Volume都删掉,这些我们在后续都可以通过docker-compose来创建。

docker rm -f $(docker ps -aq)
docker network rm $(docker network ls -q)
docker volume rm $(docker volume ls -q)

    这里还是在上一篇的代码基础上修改,先执行如下命令,创建名为DockerCompose的项目:

dotnet new mvc --no-https --output DockerCompose --framework net5.0  

    内容从上一节里的VolumeAndNetwork复制过来,然后修改HomeController下面的信息:

using Microsoft.AspNetCore.Mvc;
using DockerCompose.Models;
using Microsoft.Extensions.Configuration;

namespace DockerCompose.Controllers
{
    public class HomeController : Controller
    {
        private IRepository repository;
        private string message;
        public HomeController(IRepository repo, IConfiguration config)
        {
            repository = repo;
            message = $"Essential Docker ({config["HOSTNAME"]})";
        }

        public IActionResult Index()
        {
            ViewBag.Message = message;
            return View(repository.Products);
        }

    }
}

安装docker-compose

    如果是在Windows上安装的Docker Desktop,则会预先安装有docker-compose,检查是否安装可以运行 docker-compose --version命令查看:

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose --version
docker-compose version 1.29.0, build 07737305

    如果没有安装,可以执行如下命令(最新版本的docker-compose可以从这里https://github.com/docker/compose/releases获知):

sudo curl -L "https://github.com/docker/compose/releases/download/1.29.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

创建docker-compose文件


    在DockerCompose文件夹下,新建docker-compose.yml文件,内容如下:

version: "3"

volumes: 
    productdata:

networks: 
    frontend:
    backend:

services: 

    mysql:
        image:"mysql:8.0.23"
        volumes: 
            - productdata:/var/lib/mysql
        networks: 
            - backend
        environment: 
            - MYSQL_ROOT_PASSWORD=mysecret
            - bind-address=0.0.0.0
  • version,定义yml的版本号
  • volumes,自定义卷,用于在Docker容器之外存放数据
  • networks,自定义网络
  • services,定义需要创建的服务,用来创建容器。这里只定义了mysql,下面的参数跟前一篇创建mysql容器时一样。

    然后执行 docker-compose -f docker-compose.yml build

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose -f docker-compose.yml build
WARNING: Some networks were defined but are not used by any service: frontend
mysql uses an image, skipping

     也可以直接使用docker-compose build命令,他会寻找当前文件夹下的默认docker-compose.yml文件。这里的警告信息意思是我们定义的一个frontend网络但还没被任何服务组件使用。

     运行了这个命令只是定义,并没有真正的创建volume、network或者容器。

     接下来,运行docker-compose.yml定义的应用,才会真正创建内容,输入一下命令:

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose -f docker-compose.yml up
WARNING: Some networks were defined but are not used by any service: frontend
Docker Compose is now in the Docker CLI, try `docker compose up`

Creating network "dockercompose_backend" with the default driver
Creating volume "dockercompose_productdata" with default driver
Pulling mysql (mysql:8.0.23)...
8.0.23: Pulling from library/mysql
f7ec5a41d630: Already exists
9444bb562699: Pull complete
6a4207b96940: Pull complete
181cefd361ce: Pull complete
8a2090759d8a: Pull complete
15f235e0d7ee: Pull complete
d870539cd9db: Pull complete
5726073179b6: Pull complete
eadfac8b2520: Pull complete
f5936a8c3f2b: Pull complete
cca8ee89e625: Pull complete
6c79df02586a: Pull complete
Digest: sha256:6e0014cdd88092545557dee5e9eb7e1a3c84c9a14ad2418d5f2231e930967a38
Status: Downloaded newer image for mysql:8.0.23
Creating dockercompose_mysql_1 ... done
Attaching to dockercompose_mysql_1
mysql_1  | 2021-04-22 06:05:09+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started.
mysql_1  | 2021-04-22 06:05:09+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
mysql_1  | 2021-04-22 06:05:09+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.23-1debian10 started.
mysql_1  | 2021-04-22 06:05:09+00:00 [Note] [Entrypoint]: Initializing database files
mysql_1  | 2021-04-22T06:05:09.521249Z 0 [System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.23) initializing of server in progress as process 43
mysql_1  | 2021-04-22T06:05:09.530123Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
mysql_1  | 2021-04-22T06:05:21.108452Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
mysql_1  | 2021-04-22T06:06:03.565943Z 6 [Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
mysql_1  | 2021-04-22 06:07:13+00:00 [Note] [Entrypoint]: Database files initialized
mysql_1  | 2021-04-22 06:07:13+00:00 [Note] [Entrypoint]: Starting temporary server
mysql_1  | 2021-04-22T06:07:13.538744Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.23) starting as process 88
mysql_1  | 2021-04-22T06:07:13.599098Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
mysql_1  | 2021-04-22T06:07:14.576855Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
mysql_1  | 2021-04-22T06:07:14.899118Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Socket: /var/run/mysqld/mysqlx.sock
mysql_1  | 2021-04-22T06:07:15.789867Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
mysql_1  | 2021-04-22T06:07:15.790154Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
mysql_1  | 2021-04-22T06:07:15.963796Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
mysql_1  | 2021-04-22T06:07:15.983715Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.23'  socket: '/var/run/mysqld/mysqld.sock'  port: 0  MySQL Community Server - GPL.
mysql_1  | 2021-04-22 06:07:15+00:00 [Note] [Entrypoint]: Temporary server started.
mysql_1  | Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
mysql_1  | Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it.
mysql_1  | Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.
mysql_1  | Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it.
mysql_1  | 
mysql_1  | 2021-04-22 06:07:28+00:00 [Note] [Entrypoint]: Stopping temporary server
mysql_1  | 2021-04-22T06:07:28.394283Z 10 [System] [MY-013172] [Server] Received SHUTDOWN from user root. Shutting down mysqld (Version: 8.0.23).
mysql_1  | 2021-04-22T06:07:48.442075Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.23)  MySQL Community Server - GPL.
mysql_1  | 2021-04-22 06:07:49+00:00 [Note] [Entrypoint]: Temporary server stopped
mysql_1  |
mysql_1  | 2021-04-22 06:07:49+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up.
mysql_1  |
mysql_1  | 2021-04-22T06:07:49.641926Z 0 [System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.23) starting as process 1
mysql_1  | 2021-04-22T06:07:49.694335Z 1 [System] [MY-013576] [InnoDB] InnoDB initialization has started.
mysql_1  | 2021-04-22T06:07:50.524989Z 1 [System] [MY-013577] [InnoDB] InnoDB initialization has ended.
mysql_1  | 2021-04-22T06:07:50.768158Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
mysql_1  | 2021-04-22T06:07:51.306519Z 0 [Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
mysql_1  | 2021-04-22T06:07:51.306889Z 0 [System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
mysql_1  | 2021-04-22T06:07:51.363234Z 0 [Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
mysql_1  | 2021-04-22T06:07:51.385233Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.23'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server - GPL.

      从上面打印的输出可以看到,mysql已经创建成功。可以看到,docker-compose在依次创建network,volume和容器实例,并且每个实力前面都带了一个dockercompose前缀,这个前缀取自docker-compose.yml这个文件所在的目录名称,也可以使用-p参数来修改这个前缀,添加前缀是为了避免了冲突。另外,比如mysql容器的实例名字为dockercompose_mysql_1,带有_1后缀,这个是为了横向扩展做集群时用到。

     创建完成之后,可以使用前面两篇文章介绍的命令来查看相关信息:

PS D:\Study\DockStudyInVSCode> docker volume ls 
DRIVER    VOLUME NAME
local     dockercompose_productdata
PS D:\Study\DockStudyInVSCode> docker network ls
NETWORK ID     NAME                    DRIVER    SCOPE
d5fbb3a5a2b3   bridge                  bridge    local
1d2ed3540454   dockercompose_backend   bridge    local
49ce19152f68   host                    host      local
819bf5184145   none                    null      local
PS D:\Study\DockStudyInVSCode> docker ps -a
CONTAINER ID   IMAGE          COMMAND                  CREATED          STATUS          PORTS                 NAMES
933f312e1e8e   mysql:8.0.23   "docker-entrypoint.s…"   50 seconds ago   Up 48 seconds   3306/tcp, 33060/tcp   dockercompose_mysql_1

    在Docker Desktop里也能看到创建的docker-compose。

      按CTRL+C,就可以停止docker-compose,并停止容器。也可以使用docker-compose down -v 来删除docker-compose创建的网络和容器 (容器里的数据也会删除,如果保留volume,则把-v参数去掉):

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose down -v
WARNING: Some networks were defined but are not used by any service: frontend
Removing dockercompose_mysql_1 ... done
Removing network dockercompose_backend
Removing volume dockercompose_productdata

    如果不带-v参数,则不会删除volume。

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose down
WARNING: Some networks were defined but are not used by any service: frontend
Removing dockercompose_mysql_1 ... done
Removing network dockercompose_backend

准备数据库及应用程序


    在前面的一篇文章中,是通过在Startup的时候,在SeedData方法里使用 context.Database.Migrate();方法来实现数据库迁移的。这能保证模型定义跟数据库定义保持同步。

    这种方式在开发阶段比较实用,但是在实际生产环境中,自动应用迁移很容易导致数据丢失。EntityFramework Core的迁移实际是一系列作用于数据库的SQL语句,这些语句根据模型定义来修改数据库的表结构。比如,在定义的模型中删除某个字段,那么执行Migration迁移后,数据库中对应表的字段就会删掉。对实际生产数据库执行这种迁移操作风险很大。所以只有可控更新中才使用这种方式,而不是在每次应用程序启动时就执行迁移。在接下来我们创建两个不同的容器,一个容器来执行数据库初始化和数据库迁移,一个来运行ASP.NET Core MVC应用程序。

    首先要修改一下ProductDbContext类,修改内容如下:

using Microsoft.EntityFrameworkCore;
using System;

namespace DockerCompose.Models
{
    public class ProductDbContext : DbContext
    {
        public ProductDbContext() { }
        public ProductDbContext(DbContextOptions<ProductDbContext> options) : base(options) { }

        protected override void OnConfiguring(DbContextOptionBuilder options)
        {
            var envs = Environment.GetEnvironmentVariables();
            var host = envs["DBHOST"] ?? "localhost";
            var port = envs["DBPORT"] ?? "3306";
            var password = envs["DBPASSWORD"] ?? "mysecret";
            options.UseMySql($"server={host};userid=root;pwd={password};port={port};database=products", ServerVersion.FromString("8.0.23"));
        }
        public DbSet<Product> Products { get; set; }
    }
}
    Program.cs也需要修改如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace DockerCompose
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = CreateHostBuilder(args);
            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var configuration = services.GetService<IConfiguration>();
                    var initDb = configuration.GetValue("INITDB", "false") == "true";
                    if (initDb)
                    {
                        System.Console.WriteLine("Preparing Database...");
                        SeedData.EnsurePopulated(services);
                        System.Console.WriteLine("Database Preparation Complete");
                    }
                    else
                    {
                        host.Run();
                    }
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred seeding the DB.");
                }
            }
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    // webBuilder.UseDefaultServiceProvider(i => i.ValidateScopes = false);
                    webBuilder.UseStartup<Startup>();
                });
    }
}

     在配置文件中,设置了INITDB,如果该值为TRUE,则表示初始化数据库,如果设置为false,则运行ASP.NET Core MVC应用程序。这个配置文件从命令行读取或者从环境变量读取。从命令行读取需要额外的NuGet包,安装如下:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net5.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.EntityFrameworkCore" Version="5.0.5"/>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.5"/>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="2.0.3"/>
    <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.5"/>
    <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="5.0.0-alpha.2"/>
    <PackageReference Include="Microsoft.Extensions.Configuration.CommandLine" Version="5.0.0"/>
  </ItemGroup>
</Project>

    最后,在Startup里,删除数据库配置,以及SeedData的调用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using DockerCompose.Models;
using Microsoft.EntityFrameworkCore;

namespace DockerCompose
{
    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.AddDbContext<ProductDbContext>();
            services.AddSingleton<IConfiguration>(Configuration);
            services.AddTransient<IRepository, ProductRepository>();
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
            app.UseRouting();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}

 

定义数据库初始化以及MVC服务


    在前面一篇中,我们是按照顺序一个一个启动服务的,这是因为有时候后面的服务需要依赖前面的服务,比如MySQL容器初次启动时比较耗时,而MVC应用程序需要等到MySQL完全初始化等待连接时,才能正常运行。

    但Docker在使用Docker Compose文件时,不支持这种,他不知道MySQL服务什么时候会启动完成。他只会逐个执行操作。为了保证MVC的查询在MySQL容器初始化成功之后执行,我们需要安装一个名为wait-for-it的包,他会等到一个TCP端口接收连接。我么可以使用下面的命令来安装这个npm包:

PS D:\Study\DockStudyInVSCode\DockerCompose> npm install wait-for-it.sh@1.0.0

    安装完成之后,在DockerCompose文件夹下,会创建node_modules文件夹,下面就有wait-for-it.sh。接下来,我们新建Dockfile,然后编写如下脚本:

FROM mcr.microsoft.com/dotnet/aspnet:5.0 AS base
COPY dist /app
COPY node_modules/wait-for-it.sh/bin/wait-for-it /app/wait-for-it.sh
RUN chmod +x /app/wait-for-it.sh
WORKDIR /app
EXPOSE 80/tcp

ENV WAITHOST=mysql WAITPORT=3306
ENTRYPOINT ./wait-for-it.sh $WAITHOST:$WAITPORT --timeout=0 && exec dotnet DockerCompose.dll

    这里面,首先先将wait-for-it脚本拷贝到容器内,然后给予执行权限。然后在入口设置了,等待MySQL启动之后(这里通过监听3306端口是否能接受连接来实现),再执行ASP.NET Core MVC程序。

     接着,打开docker-compose.yml文件继续编辑。

version: "3"

volumes: 
    productdata:

networks: 
    frontend:
    backend:

services: 

    mysql:
        image: "mysql:8.0.23"
        volumes: 
            - productdata:/var/lib/mysql
        networks: 
            - backend
        environment: 
            - MYSQL_ROOT_PASSWORD=mysecret
            - bind-address=0.0.0.0
    
    dbinit:
        build:
            context: .
            dockerfile: Dockerfile
        networks: 
            - backend
        environment: 
            - INITDB=true
            - DBHOST=mysql
        depends_on: 
            - mysql
        
    mvc:
        build: 
            context: .
            dockerfile: Dockerfile
        networks: 
            - backend
            - frontend
        environment: 
            - DBHOST=mysql
        depends_on: 
            - mysql

    loadbalance:
        image: "dockercloud/haproxy"
        ports: 
            - 3000:80
        links: 
            - mvc
        volumes: 
            - /var/run/docker.sock:/var/run/docker.sock
        networks: 
            - frontend

     添加MVC和loadbalance依赖,这里需要注意的是,在loadbalance中的image,现在用的是“dockercloud/haproxy:1.6.7”,跟前一篇文章里的用法不一样,前一篇文章里的用法需要提供haproxy.cfg配置文件,这里不用,只需要配置links即可。

    另外一点可以看到,服务要么直接配置image,要么配置build和Dockerfile,用来生成image。

    接下来,先把本地的应用程序发布。使用以下命令:

PS D:\Study\DockStudyInVSCode\DockerCompose> dotnet restore
PS D:\Study\DockStudyInVSCode\DockerCompose> dotnet publish --framework net5.0 --configuration Release --output dist   

    然后,再编译docker-compose文件:

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose build

     接着执行db初始化:

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose up dbinit

    随后,启动mvc和loadbalance服务:

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose up mvc loadbalance

    执行成功之后,可以看到,在Docker Desktop里的容器运行情况:

    现在,在浏览器里输入localhost:3000 即可看到网页内容:

    目前自由1个mvc站点,可以通过以下命令,对mvc站点进行扩容,比如以下命令可以扩容5个站点。

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose scale mvc=5

     再次刷新上述网页,可以看到不同的HOSTNAME:

    这里我遇到了一个问题,就是当scale mvc=2n偶数个时,实际上刷新时,只会出现n个不同的HOSTNAME,scale mvc=奇数个时,则正常😂。

    如果要降级,只需要减少scale后面的数字即可:

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose scale mvc=3

    系统会自动删除三个mvc容器。

    关闭所有服务的命令是:

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose stop
Stopping dockercompose_mvc_5         ... done
Stopping dockercompose_mvc_4         ... done
Stopping dockercompose_mvc_3         ... done
Stopping dockercompose_mvc_2         ... done
Stopping dockercompose_loadbalance_1 ... done
Stopping dockercompose_mvc_1         ... done
Stopping dockercompose_mysql_1       ... done

     启动所有服务命令:

PS D:\Study\DockStudyInVSCode\DockerCompose> docker-compose start
Starting mysql       ... done
Starting dbinit      ... done
Starting mvc         ... done
Starting loadbalance ... done

总结


    这篇文章讲解了docker-compose文件如何用来描述一个复杂的系统,他能定义容器,自定义卷,一个自定义网络。然后演示了docker-compose相关命令,最后展示了如何使用scale参数来进行秒级扩容和降级。下面列出了本文用到的所有命令:

//各种删除
docker rm -f $(docker ps -aq)
docker network rm $(docker network ls -q)
docker volume rm $(docker volume ls -q)

//查看docker-compose版本
docker-compose --version

//执行docker-compose.yml构建
docker-compose -f docker-compose.yml build

//运行docker-compose.yml定义的应用创建容器
docker-compose -f docker-compose.yml up

//删除所有docker-compose.yml中定义的应用,如果要保留volume,则去掉-v参数
docker-compose –f docker-compose.yml down -v

//安装wait-for-it包,主要用来判断tcp某个端口是否能够开始连接,否则等待
npm install wait-for-it.sh@1.0.0

//运行docker-compose.yml中定义的某个服务
docker-compose up dbinit
docker-compose up mvc loadbalance

//扩展某个应用程序
docker-compose scale mvc=5
//比之前个数少的话,表示降级
docker-compose scale mvc=5

//关闭所有服务
docker-compose stop
//启动所有服务
docker-compose start