Docker系统性入门(四)

swarm

  • 为了解决compose在生产环境的缺点,swarm容器编排技术出现了
  • 基本架构
    1
  • 虽然k8s在容器编排领域处于绝对领先的地位,但和swarm在概念和使用上有类似之处,都是分布式管理,这里作为入门学习

单节点

  • 单节点没法分布式,但是可以先试个水看看样子
  • 初始化单节点,做了以下事情:docker info可以查看swarm状态
    1
    • 创建swarm集群的根证书
    • manager节点的证书
    • 其它节点加入集群需要的tokens
    • 创建Raft数据库用于存储证书,配置,密码等数据
  • 因为是单节点,离开了就swarm就崩了,只能强制
    2
  • 初始化swarm后可以创建service(容器),这里为什么是service的概念呢?
    3
  • 执行:docker service create nginx:latest,service、service里面的进程(其实就是container)、container是不一样的
    4
    • compose中的scale,扩展出来的其实也是container,service是大概念
  • 扩展service,删掉某个容器,立马重新启一个
    5
    6
  • 可以直接删除service,或者离开swarm;开始玩多节点

集群

  • 创建集群的几个方法
    • 在这个网站玩:但是环境只能用四小时
    • 在本地用VMware创建三个虚拟机(或者Vagrant+VirualBox)
    • 在云上使用云主机, 亚马逊,Google,微软Azure,阿里云,腾讯云等;注意要设置安全组
  • 我三台虚机的IP
    node01: 192.168.109.131
    node02: 192.168.109.129
    node03: 192.168.109.132
    
  • 初始化swarm:docker swarm init --advertise-addr=192.168.109.131
    • 会出现这么个提示:docker swarm join --token SWMTKN-1-6ddq0dydkopalzkubp5u60tc67eo3ul7eype0swvf7ccqaut89-4eb4c8nqf17lwdozyc7pfyqdv 192.168.109.131:2377
    • 在其他两个节点执行上述命令:This node joined a swarm as a worker.
      7
    • 这里有个问题:三个节点的hostname和没设置,补充一首
      # hostname用来设置主机名,就是terminal中:用户@hostname,一般用来服务发现
      # 可以在/etc/hostname中修改,例如写为node01;;可能设置网需要重启terminal
      hostname -F /etc/hostname	# root用户下
      # hosts文件用于设置 IP hostname alias,相当于设置域名,网络互连(这个和hostname没啥关系,但是一般设置成hostname,方便记)
      
      # 编辑/etc/hosts,其他几个node都这么写
      192.168.109.133 node01
      192.168.109.129 node02
      192.168.109.132 node03
      # 此时ping其他机器就通了
      ping node02
      
    • 如果是改了hostname,其他节点会变为DOWN,需要执行:docker swarm leavedocker swarm join --token SWMTKN-1-6ddq0dydkopalzkubp5u60tc67eo3ul7eype0swvf7ccqaut89-4eb4c8nqf17lwdozyc7pfyqdv node01:2377重新加入
      8
    • 这就有了包含三个node的集群,接下来需要搞明白node——service——container的关系了
  • 创建service:docker service create --name swarm-web nginx,会在某个 节点上创建一个容器支持服务
    9
    • 注意看replicas,其实就是指container的状态;此时其他节点还没使用
  • 扩展service,其实就是多启几个容器:docker service update swarm-web --replicas 3
    • 之前有两个node起不来,因为hostname设置吗?重启虚拟机重新join解决
      11
    • 类似的指令:docker service scale swarm-web=4,这会在某个node上再启一个容器
      10
      12
    • 此时如果在某个node上rm掉支持服务的容器,会快速自动创建新的(在原节点)
      13
    • 总结:service只有一个,对应的容器可以多个,分布在不同的node,swarm监控管理服务和容器;服务的接口怎么暴露呢?每个node都提供接口,数据一致性呢?怎么同步?
    • 分布式的解决方案有很多需要了解,但需求+方案,不能空想方案
  • 还有两个常用的命令:docker service inspect swarm-webdocker service logs swarm-web

网络

  • 虚拟机全部关机之后service就没了,查看node还在,但连个节点一直处于DOWN,inspect发现是心跳失败
    # 有说法是删掉冲突的文件
    systemctl stop docker
    rm -rf /var/lib/docker/swarm/worker/tasks.db
    systemctl start docker
    # 建议先关闭manager防火墙试试
    systemctl stop firewalld.service 
    systemctl disable firewalld.service 
    # 最后还是swarm leave再join
    
  • 这里网络的driver是overlay类型
    1
    • 同时我们发现在每个node上还有一个docker_gwbridge网络
  • 因为之前的基础镜像不方便查看ip,自己搭建网络试一下;这里service的创建是基于swarm的(overlay网络也只能swarm使用)
    • docker network create -d overlay myoverlay,指定driver
    • docker run -idt --network myoverlay --name box1 busybox直接用这个网络创建容器是不行的
    • docker service create --network myoverlay --name box-overlay --replicas 2 busybox ping 8.8.8.8只能建立service
    • docker service ps box-overlay,这里manager(leader)还是node01,和swarm init有关
      2
    • 情况是这样的:
      docker network inspect docker_gwbridge
      # node01
      "Subnet": "172.20.0.0/16",
      "Gateway": "172.20.0.1"
      # node02
      "Subnet": "172.19.0.0/16",
      "Gateway": "172.19.0.1"
      docker network inspect myoverlay
      # node01 node02
      "Subnet": "10.0.1.0/24",
      "Gateway": "10.0.1.1"
      
      3
      4
    • 也不必多说特点了,docker_gwbridge这套网络用来和外部通信,overlay这套用来容器之间通信(东西通信/横向通信)
      5
      6
  • service网络示意图
    7
    8
    • overlay使用了VxLAN技术,在容器之间通信
    • 如图所示:主要原理是引入一个UDP格式的外层隧道作为数据链路层,而原有数据报文内容作为隧道净荷加以传输
    • node02/03又Down了,心跳检测失败
  • 前面介绍了两个基本模型,用于容器对外和互相通信,还有一种比较复杂的ingress网络Ingress Routing Mesh,实现把service的服务端口对外发布出去,让其能够被外部网络访问到;主要解决入方向流量的问题
    • iptables的 Destination NAT流量转发
    • Linux bridge, network namespace
    • 使用IPVS技术做负载均衡
    • 包括容器间的通信(overlay)和入方向流量的端口转发
  • ingress网络测试
    # 创建服务
    docker network create -d overlay mynet
    docker service create --name web --network mynet -p 8080:80 --replicas 2 containous/whoami	# 这个镜像会返回容器通信的信息
    # 这里做了端口映射,访问这swarm节点的8080,都能访问
    curl 192.168.109.129:8080
    # 在任意节点查看转发规则
    sudo iptables -nvL -t nat
    
    • 可以看到,将外部的8080都映射到了docker_gwbridge网络
      9
    • 查看这个网络,发现有个容器ingress-sbox连在上面,实则这只是个network namespace
      10
    • 除了我们自己创建的mynet网络(没有ingress-sbox),还有一个名为ingress的overlay型网络,也有这个命名空间ingress-sbox
  • OK,都连了这个ingress-sbox
    • 如何查看这个namespace呢?docker run -it --rm -v /var/run/docker/netns:/netns --privileged=true nicolaka/netshoot nsenter --net=/netns/ingress_sbox sh,进入
      11
    • 查看它的转发规则,这里给数据包做了个标记:0x101,即257
      12
    • 使用ipvs做负载均衡:ipvsadm,按道理讲,这里应该是数据包的转发列表
      13
      • 这里是三层转发,外部可能访问任意一个node,在路由层进行均衡转发 ;如果我们想像NGINX那样在四层,直接均衡可访问的机器,需要停掉这个ipvs
    • 下面是ingress网络示意图:也就是说,这个namespace解决了负载转发的问题(iptables+ipvs)
      14
    • 小结:外部流量通过docker_gwbridge将数据包转发给ingress,在namespace中打标记做负载均衡;因为ingress也是overlay类型,所以可直接转发给各容器
    • 这里使用的mynet网络用于容器间通信(默认使用ingress overlay)
  • 总结
    • 从网络流量来理解上述三类网络的分工,分三个流向
    • 容器内部访问外网,使用bridge转发
    • 外部访问service,使用ingress负载均衡,让外面看起来就像是一个服务器在处理请求
    • 集群内部需要管理,设置各种角色,搞各种策略,这需要通信,overlay解决这个问题
  • 这里搭建的这个例子也能看到负载均衡现象,在任意节点curl访问会随机返回server hostname,就像一个节点在提供服务(透明)
  • 可以看出,了解了网络的原理基本就明白了node——service——container直接的关系
    • 集群的一个service可以基于多个不同node的container
    • 每个node,对一个service可以有多个container(三个node,scale=4时就可以)

VIP

  • VIP网络用于swarm内部负载均衡,即内部多个service之间通信时
  • OK,先搞个service,三个replicas;再搞一个client,访问前面的service
    docker service create --name web --network mynet --replicas 3 containous/whoami
    docker service create --name client --network mynet xiaopeng163/net-box:latest ping 8.8.8.8
    
    15
  • 去client连我们的web服务,这里有个IP
    16
    • inspect这三个容器,属于mynet的IP是10.0.2.11 / 10.0.2.16 / 10.0.2.12 (ingress肯定是要接的,均衡外部请求)
      18
    • docker service inspect web 这就是那个VIP
      17
    • 这次研究的问题是用于内部通信的mynet网络,inspect看一下
      20
    • 下面列出的lb_pp...就是lb-mynet的network namespace,进去看看怎么搞的
      19
    • sudo nsenter --net=/var/run/docker/netns/lb_ppjtzo226 sh这里的2.14网络是?
      21
    • 如图所示,会将数据包给VIP打上标记后负载均衡,使用ipvsadm能看到转发表
      20
  • 小结:内部service之间通过此网络的VIP访问,实际上是将网络下的容器IP通过iptables+ipvs做了负载均衡

多service应用

  • swarm可以和compose一样,创建多个service,整起来
    • 但swarm在生产环境,不能build镜像,所以要提前准备好image
    • 按照之前的代码,分别手动创建redis和flask的service就属于多service应用(集群环境),就不用配置文件了:
      # 清理一下系统
      docker system prune -a -f
      # 切换到docker-compose目录,build好镜像
      docker-compose build
      docker image ls
      # 登录docker hub,推上去
      docker login
      docker-compose push	# 肯定是推image,根据什么呢?看yml文件猜一猜吧,roykun的前缀?
      # 之前使用过 docker image push roykun/hello:1.0,image名称前加上username,方便管理
      # 手动创建service
      docker service create --network mynet --name redis redis:latest redis-server --requirepass ABC123
      docker service create --network mynet --name flask --env REDIS_HOST=redis --env REDIS_PASS=ABC123 -p 8080:5000 roykun/flask-redis:latest
      
      version: "3.8"
      
      services:
        flask:
          build:
            context: ./
            dockerfile: Dockerfile
          image: roykun/flask-redis:latest
          ports:
            - "8080:5000"
          environment:
            - REDIS_HOST=redis-server
            - REDIS_PASS=${REDIS_PASSWORD}
      
      
        redis-server:
          image: redis:latest
          command: redis-server --requirepass ${REDIS_PASSWORD}
      
    • 测试使用:curl http://127.0.0.1:8080
  • 通过docker stack部署多service(stack基于swarm)
    • 需要先build镜像,可以推到自己的dockerhub
    • stack仍然使用docker-compose.yml,但针对的是多机环境(改了改hosts,重新init了一下docker swarm)
       # 使用stack构建,Ignoring unsupported options: build
       env REDIS_PASSWORD=ABC123 docker stack deploy --compose-file docker-compose.yml flask-demo
       # 当然,刚创建replicas都是1
      
      21
      22
    • 查看:docker stack lsdocker stack ps flask-demo
    • stack自动创建了一个overlay网络,和compose类似(bridge)
  • 因为使用的还是compose的文件,里面如果指定了networks也只适用于单机环境
    • 所以,一般情况下建议区分用于开发环境和生产环境的yml文件
    • 比如带上dev/prod后缀
  • 小结
    • 使用docker run在单节点创建container
    • 使用docker compose在单节点创建多个container/service(可以NGINX做均衡)
    • 使用docker stack在集群创建多个container/service(属于swarm,一个service可能多个container)

secret

  • secret可以用来保存密码,在我们设置env时指定密码的路径即可,更安全
  • 创建的两种方式
    # 1 直接用echo的输出作为mysql的秘钥
    echo abc123 | docker secret create mysql_pass -
    docker secret rm mysql_pass
    # 2 将密码存在文件里
    docker secret create mysql_pass mysql_pass.txt
    docker secret ls
    docker secret inspect mysql_pass
    
    • 这两种方式都有缺点,可以通过history看到密码,也可以通过文件看到;需要及时清理
    • 密码最终是存在swarm的raft数据库
  • 使用
    docker service create --name mysql-demo --secret mysql_pass --env MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_pass mysql:5.7
    
    • 指定secret后,会将密码存raft拿出来放到/run/secrets/mysql_pass文件
    • 这里用MySQL举例,有对应的env可以指定mysql需要的ROOT_PASSWORD,所以不同的镜像还需要查看docker官网的定义
  • 当然,也可以自定义compose文件,在compose中设置好ENV和secret,deploy时会全部创建;下面是官网的例子
    version: "3.9"
    
    services:
       db:
         image: mysql:latest
         volumes:
           - db_data:/var/lib/mysql
         environment:
           MYSQL_ROOT_PASSWORD_FILE: /run/secrets/db_root_password
           MYSQL_DATABASE: wordpress
           MYSQL_USER: wordpress
           MYSQL_PASSWORD_FILE: /run/secrets/db_password
         secrets:
           - db_root_password
           - db_password
    
       wordpress:
         depends_on:
           - db
         image: wordpress:latest
         ports:
           - "8000:80"
         environment:
           WORDPRESS_DB_HOST: db:3306
           WORDPRESS_DB_USER: wordpress
           WORDPRESS_DB_PASSWORD_FILE: /run/secrets/db_password
         secrets:
           - db_password
    
    
    secrets:
       db_password:
         file: db_password.txt
       db_root_password:
         file: db_root_password.txt
    
    volumes:
        db_data:
    

volume

  • swarm中使用local volume,各node把数据持久化到本地
    # 还是使用compose文件
    version: "3.8"
    
    services:
      db:
        image: mysql:5.7
        environment:
          - MYSQL_ROOT_PASSWORD_FILE=/run/secrets/mysql_pass
        secrets:
          - mysql_pass
        volumes:
          - data:/var/lib/mysql
    
    volumes:
      data:
    
    secrets:
      mysql_pass:
        file: mysql_pass.txt
    # 记得新建mysql的密码文件
    
  • deploy:docker stack deploy --compose-file docker-compose.yml db-demo,你就能看到secret,也能看到volume:docker volume ls

小结

  • 到目前为止,docker的主要操作基本OK(主要是概念),可以分这么几部分
    • image
      • 镜像可以从远处pull,也可以自己写Dockerfile构建docker image build,这是应用的基础
    • deploy
      • 部署这部分本质是拿着镜像创建容器,容器是核心,但又有三种方式,就是上一个小结里说的
      • 使用时可以分这几步考虑:手动部署还是通过compose文件?单节点还是集群?
      • 基本的容器命令是:docker containerdocker service
      • 一键部署的命令是:docker-compose up -ddocker stack
  • 集群用的比较广泛,可以从网络入手docker network,就可以理清关系

练习

  • 投票APP的stack部署配置文件(集群环境)
    version: "3"
    services:
      redis:
        image: redis:alpine
        networks:
          - frontend
        deploy:
          replicas: 1
          update_config:
            parallelism: 2
            delay: 10s
          restart_policy:
            condition: on-failure
      db:
        image: postgres:9.4
        environment:
          POSTGRES_USER: "postgres"
          POSTGRES_PASSWORD: "postgres"
        volumes:
          - db-data:/var/lib/postgresql/data
        networks:
          - backend
        deploy:
          placement:
            constraints: [node.role == manager]
      vote:
        image: dockersamples/examplevotingapp_vote:before
        ports:
          - 5000:80
        networks:
          - frontend
        depends_on:
          - redis
        deploy:
          replicas: 2
          update_config:
            parallelism: 2
          restart_policy:
            condition: on-failure
      result:
        image: dockersamples/examplevotingapp_result:before
        ports:
          - 5001:80
        networks:
          - backend
        depends_on:
          - db
        deploy:
          replicas: 1
          update_config:
            parallelism: 2
            delay: 10s
          restart_policy:
            condition: on-failure
    
      worker:
        image: dockersamples/examplevotingapp_worker
        networks:
          - frontend
          - backend
        depends_on:
          - db
          - redis
        deploy:
          mode: replicated
          replicas: 1
          labels: [APP=VOTING]
          restart_policy:
            condition: on-failure
            delay: 10s
            max_attempts: 3
            window: 120s
          placement:
            constraints: [node.role == manager]
    
      visualizer:
        image: dockersamples/visualizer:stable
        ports:
          - "8080:8080"
        stop_grace_period: 1m30s
        volumes:
          - "/var/run/docker.sock:/var/run/docker.sock"
        deploy:
          placement:
            constraints: [node.role == manager]
    
    networks:
      frontend:
      backend:
    
    volumes:
      db-data:
    
  • 部署:docker stack deploy --compose-file docker-compose.yml vote-demo
  • 这个compose文件有一些字段还没用过,可以学习;如果有问题可以docker service logs查看
    • 可能需要在db service中加一句POSTGRES_HOST_AUTH_METHOD: "trust"
      2
    • 再次执行docker stack即可update
      1
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

瑞士_R

修行不易...

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值