Docker 快速入门

2019/08/18 Tools
知识共享许可协议


Docker Logo


  调试你的应用,而不是你的环境!无论何地都能安全的构建、分享及运行任何的应用。

—— 摘自 Docker 官网   

Docker 概念 1

  Docker 为开发人员及系统管理员从事开发、部署及运行基于容器的应用提供一个通用平台。使用基于 Linux 容器来部署应用的操作被称为 容器化。容器本身虽不是新技术理念,但是通过容器化来简化部署应用的这种理念还是蛮新奇的理念。

  容器化之所以越来越流行,是因为容器具备如下特点:

  • 高灵活性:即便是很复杂的应用也能被容器化
  • 轻量级:容器之间能有效共享及利用宿主机内核
  • 高可替换性:你可以在运行时进行更新、升级
  • 高可移植性:你可以本地构建、云端部署、到处运行
  • 高可扩展性:你可以增加并自动分发容器的副本
  • 高可堆叠性:你可以在运行时垂直堆叠更多的服务

镜像及容器

  容器 container 通过运行一个 镜像 image 而被启动。而 镜像 是一个可执行的包,它包含要运行的应用所需的所有依赖,例如:代码、运行环境、库、环境变量及配置文件。

  容器 是 镜像 运行时的具体实例,即:当 镜像 被执行后载入内存(它是一个内存中带状态的镜像或用户进程)。你可以通过执行 docker ps 命令来查看目前正在运行的容器列表,类似于 Linux 上查看进程。

容器及虚拟机

  容器 container 运行在原生的 Linux 上,并与其他容器共享宿主机的内核资源。它运行在一个独立的进程中,与其他可执行的其他进程一样,也不需其他额外的内存资源,这也是它具备轻量级的原因所在。

  与容器相比,虚拟机 Virtual Machine 是通过 hypervisor 虚拟访问宿主机资源,来运行一个完整的寄生操作系统。总之,虚拟机提供了一个大而全的应用运行环境及资源,然而大多数应用可能使用不到这么多的环境资源,故而存在资源浪费。

 

Docker 安装

  • 注册 Docker Hub 账号

  • 下载 Docker Desktop for Mac
  • 安装后,启动 Docker 应用

    附带有个快速入门示例 Demo cheers2019

    1. 克隆项目

       git clone https://github.com/docker/doodle.git
      
    2. 构建 Docker 镜像

       cd doodle/cheers2019 && docker build -t docker/cheers2019 .
      

      具体 build 命令的用法,执行 docker build --help

    3. 运行 Docker 镜像

       docker run -it --rm docker/cheers2019
      

      具体 run 命令的用法,执行 docker run --help

      运行结果如下:

      Cheers2019

    4. 分享 Docker 镜像

       docker login && docker push docker/cheers2019
      

      如果感觉这个例子有趣,执行上面命令,将会在 Docker Hub 分享你的容器镜像。

Docker CLI

检查版本

# docker 版本
$ docker -v
Docker version 19.03.1, build 74b1e89
	
# docker 详细信息
$ docker info
Client:
Debug Mode: false
	
# docker-compose 版本
$ docker-compose -v
docker-compose version 1.24.1, build 4667896b
	
# docker-machine 版本
$ docker-machine -v
docker-machine version 0.16.1, build cce350d7

基本命令

## 列出 docker 的所有命令行命令列表
docker
## 获得子命令的帮助
docker container --help
	
## 显示 docker 的版本及信息
docker --version
docker version
docker info
	
## 执行 docker 镜像
docker run hello-world
	
## 显示目前系统中的所有 docker 镜像列表
docker image ls
	
## 显示目前系统中的所有 docker 容器列表 (running, all, all in quiet mode)
docker container ls
docker container ls --all
docker container ls -aq

Docker 实践

  在 Docker 安装中,我们通过一个小 Demo 大致感受到了采用 Docker 来编译部署及运行一个容器化的应用。在本小节我们将详细阐述 Docker 在各个阶段的主要任务的实现过程。

  在着手以 Docker 的方式构建一个应用时,一般是要经过应用开发、构建容器、发布服务及合成服务栈的几个重要步骤。创建容器应用是基础,容器应用就可以跑在 Docker 提供的应用环境上,提供具体服务。将不同的服务巧妙地组合就能构造出复杂的服务栈,来满足实际的业务需求。所以它们的层级关系应该如图所示:

Containers 容器:可以视作包含应用代码及其运行时所需的依赖及环境的统称
Services 服务:分布式应用中常常指能提供某项功能的单独模块
Stacks 服务栈:将不同的服务组合堆叠从而能够完成业务需求的结构

  我们这里通过编写一个 Python 应用来实践整个过程。首先创建容器 Container,然后将其发布成服务 Service,最后组合多个 Service 形成一个服务栈 Stack。

创建容器 Containers

  • 开发容器化应用

      在过去,如果要开发 Python 应用,首先得安装 Python 运行环境。Docker 方式就不同,只需获得一个可移植的 Python 运行环境的 Docker 镜像,而无需实际的安装。在应用构建时,应用所需的所有依赖都会和代码一同被打包到应用镜像中。像这种可移植镜像的声明被定义在一个叫做 Dockerfile 的文件中。

    • 创建文件 Dockerfile

        # 使用 Docker 官方的 Python 运行期镜像当做父镜像
        FROM python:2.7-slim
      		
        # 设置项目的工作目录设置为 /app
        WORKDIR /app
      		
        # 将当前目录的所有内容复制到容器中的 /app 目录
        COPY . /app
      		
        # 安装一些必要的依赖包,这些依赖包被指定在 requirements.txt 的文本文件中
        RUN pip install --trusted-host pypi.python.org -r requirements.txt
      		
        # 开放容器的 80 端口提供给外部访问
        EXPOSE 80
      		
        # 定义一个环境变量,变量名称 NAME,变量值 World
        ENV NAME World
      		
        # 当容器启动时,运行 app.py
        CMD ["python", "app.py"]
      
    • 编写依赖包声明文件 requirements.txt

        Flask
        Redis
      
    • 编写应用代码 app.py

        from flask import Flask
        from redis import Redis, RedisError
        import os
        import socket
      		
        # Connect to Redis
        redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
      		
        app = Flask(__name__)
      		
        @app.route("/")
        def hello():
            try:
                visits = redis.incr("counter")
            except RedisError:
                visits = "<i>cannot connect to Redis, counter disabled</i>"
      		
            html = "<h3>Hello {name}!</h3>" \
                   "<b>Hostname:</b> {hostname}<br/>" \
                   "<b>Visits:</b> {visits}"
            return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
      		
        if __name__ == "__main__":
            app.run(host='0.0.0.0', port=80)
      

      编写完毕后,当前目录的结构如下所示:

      .
      └── requirements.txt		// 依赖包文件
      └── Dockerfile		// 容器定义文件
      └── app.py			// Python 代码
    
  • 构建容器化应用镜像

      来到项目的根目录,执行如下命令构建应用:

      $ docker build --tag=friendlyhello .
    

      使用--tag 为当前镜像设置标签,也可以使用简写 -t,标签的整体格式为 REPOSITORY_NAME:TAG,其中 REPOSITORY_NAME 为库名称,TAG 为具体标签说明,可以是版本等说明信息,例如:friendlyhello:v0.0.1

      等待编译完成后,查看具体镜像:

      $ docker image ls
      REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
      friendlyhello        latest              550941976752        9 minutes ago       148MB
    
  • 运行容器化应用

      执行下面命令,将镜像文件运行起来:

      $ docker run -p 4001:80 friendlyhello4001
       * Serving Flask app "app" (lazy loading)
       * Environment: production
         WARNING: This is a development server. Do not use it in a production deployment.
         Use a production WSGI server instead.
       * Debug mode: off
       * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
      172.17.0.1 - - [18/Aug/2019 15:16:26] "GET / HTTP/1.1" 200 -
      172.17.0.1 - - [18/Aug/2019 15:16:27] "GET /favicon.ico HTTP/1.1" 404 -
    

      由于我们将4001端口映射为容器中的80端口,故而在浏览器中访问地址 http://localhost:4001 即可访问页面,如图所示:

    app-in-brower.png

      尝试执行如下命令,可以获取更多相关容器的一些信息:

      # 列出当前正在运行的容器列表
      $ docker container ls
      CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS                  NAMES
      6af27ed7e8d9        friendlyhello       "python app.py"     9 seconds ago       Up 8 seconds        0.0.0.0:4001->80/tcp   practical_grothendieck
    	
      # 停止容器 6af27ed7e8d9 为上面展示的 CONTAINER ID
      $ docker container stop 6af27ed7e8d9
    	
    
  • 发布容器化应用

      运行调试完毕,我们可以将镜像打上标签,并上传至 Docker Hub 进行镜像托管,当然你也可以选择不同的镜像托管服务器。具体操作步骤如下:

      # 登录到 Docker Hub
      $ docker login
      Authenticating with existing credentials...
      Login Succeeded
    	
      # 将本地镜像 friendlyhello 重新打上规范的标签
      # username 替换为你的 Docker Hub 账号用户名
      $ docker tag friendlyhello username/friendlyhello:v1.0
    	
      # 重新标签后,我们能看到一个新的镜像文件
      $ docker image ls
      REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
      friendlyhello           latest              550941976752        51 minutes ago      148MB
      username/friendlyhello   v1.0                550941976752        51 minutes ago      148MB
    	
      # 将新的镜像 push 到 Docker Hub
      $ docker push username/friendlyhello:v1.0
    	
      # 此后,就可以在任何时候执行如下命令拉取远程镜像文件执行
      $ docker run -p 4001:80 username/friendlyhello:v1.0
    	
    

发布服务 Services

  对于 Docker 而言,服务就是产品级的容器,服务指定了容器以什么方式运行,例如:容器运行在哪个端口、需要多少硬件资源、需要多少容器实例拷贝…… 所以符合 Docker 容器化的服务,天生就具备良好的可扩展性。Docker 通过 docker-compose.yml 文件来配置容器运行的参数,下面就上一节生成的镜像文件来编写这个文件。

  • 编写 docker-compose.yml 文件

      version: "3"
      services:
        web:
          # 根据自身的镜像信息替换 username/friendlyhello:v1.0
          image: username/friendlyhello:v1.0
          deploy:
            # 容器实例个数
            replicas: 5
            # 资源配置
            resources:
              limits:
                # 限制每个实例占用 cpu 时间为 10%
                cpus: "0.1"
                # 限制每个实例占用内存为 50M
                memory: 50M
            # 重启策略设置为失败自动重启
            restart_policy:
              condition: on-failure
          # 端口映射,将 4001 端口映射为容器的 80 端口
          ports:
            - "4001:80"
          # 将此 web 参与到负载均衡网络 webnet
          networks:
            - webnet
      # 采取默认方式定义一个负载均衡的网络
      networks:
        webnet:
    
  • 发布具备负载均衡应用服务

      由于要使用负载均衡的功能,在部署时涉及后续小节的内容,现在如果不理解,先照着命令执行即可。安装如下命令发布配置好的服务:

      # 将当前节点初始化为集群的管理节点,将在下节介绍。
      # 如果不初始化,下面的 docker stack 命令将会报错。
      $ docker swarm init
      Swarm initialized: current node (t6r6euslxnpwitw4yn0g92uaf) is now a manager.
      To add a worker to this swarm, run the following command:
          docker swarm join --token ... 192.168.65.X:2377
      To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
    	
      # 应用根目录执行,后面的 getstartedlab 是指定的当前服务栈的名称
      $ docker stack deploy -c docker-compose.yml getstartedlab
      Creating network getstartedlab_webnet
      Creating service getstartedlab_web
    	
      # 查看当前的服务信息
      $ docker service ls
      ID                  NAME                MODE                REPLICAS            IMAGE                        PORTS
      kqibyn0286pu        getstartedlab_web   replicated          5/5                 username/friendlyhello:v1.0   *:4001->80/tcp
    
      # 除了上面的命令外,下面的可以只显示指定服务栈名称的服务信息
      $ docker stack services getstartedlab
      ID                  NAME                MODE                REPLICAS            IMAGE                        PORTS
      kqibyn0286pu        getstartedlab_web   replicated          5/5                 username/friendlyhello:v1.0   *:4001->80/tcp
    	
      # 根据配置,当前服务运行了5个容器实例
      # 服务中的每个容器实例被称作任务 task,每个任务有一个唯一的自增长的 ID
      # 此 ID 编号一直增长到配置文件中指定的实例个数,类似下面的 getstartedlab_web.5
      # docker stack ps getstartedlab 也能显示相同任务内容
      $ docker service ps getstartedlab_web
      ID                  NAME                  IMAGE                        NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
      2j70ic6zxs55        getstartedlab_web.1   username/friendlyhello:v1.0   docker-desktop      Running             Running 4 minutes ago                       
      vretbeyraceh        getstartedlab_web.2   username/friendlyhello:v1.0   docker-desktop      Running             Running 4 minutes ago                       
      qig853sns67z        getstartedlab_web.3   username/friendlyhello:v1.0   docker-desktop      Running             Running 4 minutes ago                       
      chuipb76htpg        getstartedlab_web.4   username/friendlyhello:v1.0   docker-desktop      Running             Running 4 minutes ago                       
      mhcbn7dab9vt        getstartedlab_web.5   username/friendlyhello:v1.0   docker-desktop      Running             Running 4 minutes ago
    	
      # 下面的命令显示不同的容器实例,只不过没有根据服务栈名称过滤
      # 注意:这里显示的是容器ID,上面命令是任务ID
      $ docker container ls
      CONTAINER ID        IMAGE                        COMMAND             CREATED             STATUS              PORTS               NAMES
      decd34f76501        username/friendlyhello:v1.0   "python app.py"     6 minutes ago       Up 6 minutes        80/tcp              getstartedlab_web.3.qig853sns67zlmdtpz83tl439
      eb04fb1c8146        username/friendlyhello:v1.0   "python app.py"     6 minutes ago       Up 6 minutes        80/tcp              getstartedlab_web.4.chuipb76htpgsbbk0f9e7zqnr
      4250101e9006        username/friendlyhello:v1.0   "python app.py"     6 minutes ago       Up 6 minutes        80/tcp              getstartedlab_web.2.vretbeyraceh2o7dd22jr4n20
      5a5c3a5f525a        username/friendlyhello:v1.0   "python app.py"     6 minutes ago       Up 6 minutes        80/tcp              getstartedlab_web.5.mhcbn7dab9vt5krt3vkpe35em
      4194f16e84a5        username/friendlyhello:v1.0   "python app.py"     6 minutes ago       Up 6 minutes        80/tcp              getstartedlab_web.1.2j70ic6zxs55cpekpusq4tjmp
    

      执行完命令后,访问 URL 地址 http://localhost:4001。刷新几次页面,发现页面中 Hostname 是变化的,因为每次请求可能被负载均衡到不同的容器实例上。可以修改 docker-compose.yml 文件来改变应用的资源配置、实例数等参数,每次修改保存后,只要重复执行 docker stack deploy -c docker-compose.yml getstartedlab 命令就能更新实例。值得注意的是,Docker 平台采取一站式更新,因此你不必先停掉你的 stack 或者 kill 所有的容器,Awesome!

  • 停止应用及集群

      # 停止服务栈
      $ docker stack rm getstartedlab
    	
      # 关闭集群
      $ docker swarm leave --force
    

创建集群 Swarms

  在上一小节中,我们将之前的应用发布成服务,且使它以 5 个实例运行。这个小节将更进一步,把之前的应用部署运行到一个集群的不同机器上。在 Docker 平台中,像这种把应用分布式的部署到多容器、多机器的 “容器化” 集群上的行为,称作 Swarm。

  Swarm 就是运行着 Docker 环境,并且加入到同一个集群的一组机器的统称。在此环境下,你将可以继续运行之前的 Docker 命令,只不过这些命令必须运行在这个集群下的管理节点上,被称为 Swarm Manager。集群下的机器可以是真实的物理机也可以是虚拟机,在加入集群后它们被统称做节点 Nodes。

  Swarm Managers 可以使用不同的策略来运行容器,例如:emptiest node 模式下,将采取尽可能少的机器来运行容器;而 global 模式下,将确保每台机器运行特定容器的一个实例。这些都可以在 Compose 文件中进行配置。

  Swarm Managers 是集群里唯一能执行命令、批准其他机器加入 swarm 的机器。这些被批准进入的机器被称作 Workers。 Workers 只是单纯的增加节点数,从而提升应用性能,除此之外没有管理节点的任何权限。

  到目前为止,我们只是使用本地单主机运行 Docker,其实 Docker 可以切换成 Swarm 模式。一旦切换成 Swarm 模式,当前的机器自动变成 Swarm Manager,Docker 将把你指定的命令运行在你所管理的 Swarm 上,而非单单只运行在当前机器。好了,那让我们开始设置 Swarm 吧!

  • 创建集群

      由于没有多余的物理机,我们在当前物理机上设置两台运行 Docker 的虚拟机,并指定其中一台为 Swarm Manager。根据自身物理机的操作系统,选择合适的创建虚拟机的方式。目前 Mac、Linux 及 Windows 8 和 7 的操作系统,需要安装 Oracle VirtualBox,而 Windows 10 的机器由于自带了 Hyper-v 管理器,可以直接创建。我们以 Mac 系统为例进行说明。

      安装完 VirtualBox 后,使用 docker-machine 命令创建两台虚拟机,名字分别为 myvm1、myvm2:

      # 使用 virtualbox 驱动创建虚拟机 myvm1
      $ docker-machine create --driver virtualbox myvm1
    	
      # 使用 virtualbox 驱动创建虚拟机 myvm2
      $ docker-machine create --driver virtualbox myvm2
    	
      # 查看虚拟机信息
      $ docker-machine ls
      NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
      myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v19.03.1   
      myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v19.03.1   
    

      指定 myvm1 为 Swarm Manager,并将 myvm2 加入到 Swarm 集群:

      # 将 myvm1 设置为 Swarm Manager
      # <myvm1 ip> 替换成虚拟机 myvm1 的 IP 地址,根据上面信息,显示为 192.168.99.100
      # 如果使用 SSH 有异常,可以使用物理机的本地 SSH 服务,如下:
      # 	docker-machine --native-ssh ssh myvm1 "docker swarm init --advertise-addr <myvm1 ip>:2377"
      $ docker-machine ssh myvm1 "docker swarm init --advertise-addr <myvm1 ip>:2377"
      Swarm initialized: current node (bshep3rmn9nrt8wweaug1cz7h) is now a manager.
      To add a worker to this swarm, run the following command:
          docker swarm join --token <token> <ip>:2377
      To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.
    
      # 将 myvm2 添加到 Swarm,设置为 Worker
      # <token> 替换成上面命令显示的 token 值
      # <ip> 替换成 myvm1 的 IP
      $ docker-machine ssh myvm2 "docker swarm join --token <token> <ip>:2377"
      This node joined a swarm as a worker.
    	
      # 在 myvm1 上执行 `docker node ls` 可以查看节点信息
      # 注意观察:MANAGER STATUS,myvm1 为 Leader
      $ docker-machine ssh myvm1 "docker node ls"
      ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS      ENGINE VERSION
      bshep3rmn9nrt8wweaug1cz7h *   myvm1               Ready               Active              Leader              19.03.1
      dysqnbb80zlwcpn7bm15521xs     myvm2               Ready               Active                                  19.03.1
    
    注意:2377 为 Swarm 管理端口,2376 为 Docker 进程端口。
    应当避免占用这两个端口,而产生不必要的错误。  

      如果想要离开集群,在那个节点上执行 docker swarm leave 即可。如果节点为管理节点,会有警告提示。当最后一个管理节点离开时,整个 Swarm 将会全部删除。

  • 集群上部署应用

      在上面我们要在某个节点执行命令,都需要通过 docker-machine ssh myvmX ”COMMANDS“ 的形式来封装 COMMANDS 命令,以 ssh 的方式指定节点名称的方式来实现,稍显麻烦。可以通过 docker-machine env <MACHINE_NAME> 来指定默认的执行命令的节点,省去封装的麻烦:

      # 查看现在的虚拟机列表,注意 ACTIVE 状态。两台都是 "-" 表示未激活状态
      $ docker-machine ls
      NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
      myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v19.03.1   
      myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v19.03.1
    	
      # 指定默认节点为 myvm1,注意:此时只是给出设置默认节点的命令,并没有真实生效
      $ docker-machine env myvm1
      export DOCKER_TLS_VERIFY="1"
      export DOCKER_HOST="tcp://192.168.99.100:2376"
      export DOCKER_CERT_PATH="/Users/Reion/.docker/machine/machines/myvm1"
      export DOCKER_MACHINE_NAME="myvm1"
      # Run this command to configure your shell: 
      # eval $(docker-machine env myvm1)
    	
    	
      # 执行如下命令,即可将上面的设置默认节点的命令真实执行
      $ eval $(docker-machine env myvm1)
    	
      # 再次查看虚拟机列表,注意 myvm1 的 ACTIVE 状态变成 “*” 表示该节点处于激活状态
      $ docker-machine ls
      NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
      myvm1   *        virtualbox   Running   tcp://192.168.99.100:2376           v19.03.1   
      myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v19.03.1
    	  
    

      至此,我们已经将默认执行命令的节点设置为 myvm1,接下来我们就可以默认在此管理节点执行命令来将应用部署到 Swarm 集群中了。和上一小节一样,执行服务栈部署命令,不同的是此命令现在是交由 Swarm 集群中的管理节点 myvm1 来执行:

      # 拷贝物理机的 docker-compose.yml 文件到管理节点的 Docker 用户主目录
      $ docker-machine scp docker-compose.yml myvm1:~
    	
      # 在 myvm1 上部署应用
      $ docker stack deploy -c docker-compose.yml getstartedlab
      Creating network getstartedlab_webnet
      Creating service getstartedlab_web
    	
      # 在 myvm1 上查看目前运行的服务信息
      # 可以看到 5 个容器实例有的分配到 myvm1 有的分配到 myvm2
      $ docker service ps getstartedlab_web
      ID                  NAME                  IMAGE                        NODE                DESIRED STATE       CURRENT STATE           ERROR               PORTS
      ehj66cg86mne        getstartedlab_web.1   username/friendlyhello:v1.0   myvm2               Running             Running 2 minutes ago                       
      wt2k9m9blucm        getstartedlab_web.2   username/friendlyhello:v1.0   myvm2               Running             Running 2 minutes ago                       
      qrohhlrbwbjs        getstartedlab_web.3   username/friendlyhello:v1.0   myvm1               Running             Running 2 minutes ago                       
      se2dgrqzujev        getstartedlab_web.4   username/friendlyhello:v1.0   myvm2               Running             Running 2 minutes ago                       
      wpp8d9xjb2b5        getstartedlab_web.5   username/friendlyhello:v1.0   myvm1               Running             Running 2 minutes ago
    	
      # 查看网络配置信息,发现 getstartedlab_webnet 的 SCOPE 为 sawrm
      $ docker network ls
      NETWORK ID          NAME                   DRIVER              SCOPE
      0624l1hslohj        getstartedlab_webnet   overlay             swarm
    

      怎么样,通过如上配置,你的应用就已经跑在了 swarm 集群上了,在 Mac 物理机浏览器分别访问 http://192.168.99.100:4001 或 http://192.168.99.101:4001,都能访问到内容 2,是不是很酷!😎

    注意:Mac 主机浏览器如不能访问,请关闭任何代理或参考脚注 2。  

    疑问:
      在笔者的浏览器分别访问这两个网址,不管哪个网址,刷新 N 次主机名都是同一个,而不会像官网教程视频中的会变化,为什么呢?

    答:
      在完成后续的 Stacks 章节后,分别访问两个网址,刷新又都能得到不同的主机名,so weird!!

  • 清理及重启

      # 停止 stack 服务栈
      $ docker stack rm getstartedlab
      Removing service getstartedlab_web
      Removing network getstartedlab_webnet
    	
      # 删除 swarm 集群
      # 先将 worker 节点删除
      $ docker-machine ssh myvm2 "docker swarm leave"
      Node left the swarm.
    	
      # 后将 swarm manager 节点删除
      $ docker-machine ssh myvm1 "docker swarm leave --force"
      Node left the swarm.
    	
      # 清理默认激活的 Docker 主机
      $ eval $(docker-machine env -u)
    
      # 可以看到 myvm1 的 ACTIVE 状态又恢复为 “-” 未激活状态
      $ docker-machine ls
      NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
      myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v19.03.1   
      myvm2   -        virtualbox   Running   tcp://192.168.99.101:2376           v19.03.1
    	
      # 停止主机 myvm2
      $ docker-machine stop myvm2
      Stopping "myvm2"...
      Machine "myvm2" was stopped. 
    	
      # 检查主机状态,发现 myvm2 确实是停止状态
      $ docker-machine ls
      NAME    ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER     ERRORS
      myvm1   -        virtualbox   Running   tcp://192.168.99.100:2376           v19.03.1   
      myvm2   -        virtualbox   Stopped                                       Unknown
    	
      # 启动主机 myvm2
      $ docker-machine start myvm2
    

创建服务栈 Stacks

  通过上一节,我们已经能将应用容器部署到运行 Docker 的多机器的 Swarm 集群上,这个小节中我们将增加一个 visualizer 服务,使它和之前的 web 服务整合,共享依赖及资源。像这样整合多服务在一块,共享依赖及资源的模式,被称为 Stack。一个单独的 Stack 虽然可以定义和完成整个应用的所有功能,但是现实中的复杂应用基本都是组合多个 Stacks。现在开始调整我们的应用配置。

  • 追加 visualizer 服务,重新部署

    1. 修改 docker-compose.yml 文件

       version: "3"
       services:
         web:
           # replace username/repo:tag with your name and image details
           image: username/repo:tag
           deploy:
             replicas: 5
             restart_policy:
               condition: on-failure
             resources:
               limits:
                 cpus: "0.1"
                 memory: 50M
           ports:
             - "4001:80"
           networks:
             - webnet
         # 追加的 service
         visualizer:
           image: dockersamples/visualizer:stable
           ports:
             # 将 4002 端口映射到容器的 8080 端口
             - "4002:8080"
           volumes:
             - "/var/run/docker.sock:/var/run/docker.sock"
           deploy:
             placement:
               constraints: [node.role == manager]
           networks:
             - webnet
       networks:
         webnet:
      
    2. 指定默认连接节点为管理节点 myvm1

       $ eval $(docker-machine env myvm1)
      
    3. 执行如下命令创建或更新部署

       # 将物理机修改的 yml 文件拷贝到管理节点上
       # 不过好像此步骤不做也没啥影响,@_@!
       $ docker-machine scp docker-compose.yml myvm1:~
      		
       $ docker stack deploy -c docker-compose.yml getstartedlab
       Creating network getstartedlab_webnet
       Creating service getstartedlab_web
       Creating service getstartedlab_visualize
      		
       # 查看管理节点容器实例,发现多了一个 visualizer 的实例
       $ docker container ls
       CONTAINER ID        IMAGE                             COMMAND             CREATED             STATUS              PORTS               NAMES
        7cd84c4bf951        dockersamples/visualizer:stable   "npm start"         4 minutes ago       Up 4 minutes        8080/tcp            getstartedlab_visualizer.1.aav1sbe1tb7blibj35m2b3hdc
        1fb14d2d2855        username/friendlyhello:v1.0        "python app.py"     4 minutes ago       Up 4 minutes        80/tcp              getstartedlab_web.2.m940oxzv35ppd6j3vrfpfk2nt
        09d290b5e5c2        username/friendlyhello:v1.0        "python app.py"     4 minutes ago       Up 4 minutes        80/tcp              getstartedlab_web.5.p8iqhhpn28xdal16uqdo1elyu
      
    4. 访问 visualizer 服务

        访问地址 http://192.168.99.100:4002,可以显示如下图所示的页面:

        可以很清晰的看到,Visualizer 单实例服务运行在管理节点上,而 5 个实例的 web 服务被分散在整个 swarm 集群当中,其中两个在 myvm1 节点上,三个在 myvm2 节点上。当然,你也可以使用命令 docker stack ps getstartedlab 来显示文本形式的实例信息。

  • 实现数据持久化

    1. 修改 docker-compose.yml 文件

       version: "3"
       services:
         web:
           # replace username/repo:tag with your name and image details
           image: username/repo:tag
           deploy:
             replicas: 5
             restart_policy:
               condition: on-failure
             resources:
               limits:
                 cpus: "0.1"
                 memory: 50M
           ports:
             - "4001:80"
           networks:
             - webnet
         # 追加的 service
         visualizer:
           image: dockersamples/visualizer:stable
           ports:
             - "4002:8080"
           volumes:
             - "/var/run/docker.sock:/var/run/docker.sock"
           deploy:
             placement:
               constraints: [node.role == manager]
           networks:
             - webnet
         # 追加的 service
         redis:
           image: redis
           ports:
             - "6379:6379"
           volumes:
             # 映射 docker 用户下的 data 目录到 redis 容器的绝对路径 /data 
             - "/home/docker/data:/data"
           deploy:
             placement:
               constraints: [node.role == manager]
           command: redis-server --appendonly yes
           networks:
             - webnet
       networks:
         webnet:
      
    2. 在管理节点创建 ./data 目录

       # 其实 data 目录的绝对路径为: /home/docker/data
       $ docker-machine ssh myvm1 "mkdir ./data"
      
    3. 指定默认连接节点为管理节点 myvm1

       $ eval $(docker-machine env myvm1)
      
    4. 执行如下命令创建或更新部署

       # 将物理机修改的 yml 文件拷贝到管理节点上
       # 不过好像此步骤不做也没啥影响,@_@!
       $ docker-machine scp docker-compose.yml myvm1:~
      		
       $ docker stack deploy -c docker-compose.yml getstartedlab
      
    5. 查看服务列表,发现多了 Redis 服务

       $ docker service ls
       ID                  NAME                       MODE                REPLICAS            IMAGE                             PORTS
        flhckxh3oaff        getstartedlab_redis        replicated          1/1                 redis:latest                      *:6379->6379/tcp
        7sta8gyetz3i        getstartedlab_visualizer   replicated          1/1                 dockersamples/visualizer:stable   *:4002->8080/tcp
        aopvt7nhwytk        getstartedlab_web          replicated          5/5                 username/friendlyhello:v1.0        *:4001->80/tcp
      	
       $ docker stack ps getstartedlab
       ID                  NAME                         IMAGE                             NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
        xezow5of91cf        getstartedlab_redis.1        redis:latest                      myvm1               Running             Running 46 minutes ago                       
        aav1sbe1tb7b        getstartedlab_visualizer.1   dockersamples/visualizer:stable   myvm1               Running             Running 2 hours ago                          
        a5e26gatsbo7        getstartedlab_web.1          username/friendlyhello:v1.0        myvm2               Running             Running 2 hours ago                          
        m940oxzv35pp        getstartedlab_web.2          username/friendlyhello:v1.0        myvm1               Running             Running 2 hours ago                          
        ekedl3pqvu50        getstartedlab_web.3          username/friendlyhello:v1.0        myvm2               Running             Running 2 hours ago                          
        pw432si8mi6s        getstartedlab_web.4          username/friendlyhello:v1.0        myvm2               Running             Running 2 hours ago                          
        p8iqhhpn28xd        getstartedlab_web.5          username/friendlyhello:v1.0        myvm1               Running             Running 2 hours ago 
      
    6. 浏览并刷新 http://192.168.99.100:4001,能够发现 Visits 参数的变化

线上部署容器化应用

  在上小节,我们是把应用部署在本地的两台虚拟机组成的 Swarm 集群当中。其实你可以将你的容器化的应用部署到云端或服务器当中。目前有 Docker Enterprise、Docker Engine - Community 两种方案供选择。

  Docker Enterprise 是一个稳定的、受商业支持的 Docker Engine 版,它能提供优秀的管理软件、Docker 数据中心。你能通过便利的图形化的控制窗口来管理设置你的容器化的应用,但是它是需要收费的。点击查看所有可供选择的架设方案。

  Docker Engine - Community 是免费的社区版的,目前支持桌面级、服务器级的安装方式。桌面级的目前有 Docker Desktop for Mac、Docker Desktop for Windows(win 10),服务器级的支持主流的 Linux 发行版,点击查看详细支持列表。

  选择合适版本,安装完成后。按照与本地部署方式类似的步骤,即可将容器化的应用正式部署到线上服务器。

脚注

  1. 本文参考自 Docker 官方文档,阅读原文请移步 Get started。 ↩

  2. 不能从 MacOS 主机浏览器访问 Docker 容器 ↩


知识共享许可协议

Search

    mail github location contact

    Table of Contents