仅仅是为了记录自己学习中的一些知识点,不喜勿喷,谢谢!


一、基本的知识储备
1 容器规范
为了保证容器生态的健康发展,使不同容器之间彼此兼容,成立了一个叫 Open Container Initiative(OCI) 的组织,
目前OCI 发布了两个规范:runtime spec 和 image format spec;

2 容器 runtime:
runtime 是容器真正运行的地方;
Java 程序就好比是容器,JVM 则好比是 runtime。JVM 为 Java 程序提供运行环境;
同样的道理,容器只有在 runtime 中才能运行

3 容器管理工具:
容器管理工具对内与 runtime 交互,对外为用户提供 interface,比如 CLI
4 容器定义工具:

dockerfile --->docker iamge;
docker iamge--->Registry仓库;
docker iamge+ runtime --->docker container;

5 Registry:仓库,用来存储docker image;
分私有和公有两种;
Docker Hub(https://hub.docker.com/) 是默认的 Registry;
出于对速度或安全的考虑,用户也可以创建自己的私有 Registry;
docker pull
docker run

6 容器 OS :专门运行容器的操作系统;
OS,CoreOS、atomic 和 ubuntu core 是其中的杰出代表;

7 容器编排引擎:
基于容器的应用一般会采用微服务架构;
所以容器编排的提前是应用的微服务化和容器化;

所谓编排(orchestration),通常包括容器管理、调度、集群定义和服务发现等;
docker swarm 是 Docker 开发的容器编排引擎;
kubernetes 是 Google 领导开发的开源容器编排引擎;

8 容器管理平台:
在容器编排引擎之上的一个更为通用的平台;
通常容器管理平台能够支持多种编排引擎,抽象了编排引擎的底层实现细节,为用户提供更方便的功;
Rancher 和 ContainerShip 是容器管理平台的典型代表。

9 容器支持技术:
容器网络
docker network 是 Docker 原生的网络解决方案。
除此之外,我们还可以采用第三方开源解决方案,例如 flannel、weave 和 calico;

服务发现
动态变化是微服务应用的一大特点;
当负载增加时,集群会自动创建新的容器;负载减小,多余的容器会被销毁;
etcd、consul 和 zookeeper 是服务发现的典型解决方案。

监控
docker ps/top/stats 是 Docker 原生的命令行监控工具。
除了命令行,Docker 也提供了 stats API,用户可以通过 HTTP 请求获取容器的状态信息。

10 docker 最基本的概念

通过docker version命令:docker典型的cs架构

Client: Docker Engine - Community
 Version:           20.10.6
 API version:       1.41
 Go version:        go1.13.15
 Git commit:        370c289
 Built:             Fri Apr  9 22:46:01 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.6
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.13.15
  Git commit:       8728dd2
  Built:            Fri Apr  9 22:44:13 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.4.4
  GitCommit:        05f951a3781f4f2c1911b05e61c160e9c30eaa8e
 runc:
  Version:          1.0.0-rc93
  GitCommit:        12644e614e25b05da6fd08a38ffa0cfe1903fdec
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

环境的选择

1 docker -v
Docker version 20.10.6, build 370c289
更多docker信息可以通过docker version查询

2 操作系统 - Ubuntu
uname -a
Linux yundong-VirtualBox 5.4.0-42-generic #46~18.04.1-Ubuntu SMP Fri Jul 10 07:21:24 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

3 docker安装

官方安装教程:
https://docs.docker.com/engine/install/ Install Docker Engine on Ubuntu:
https://docs.docker.com/engine/install/ubuntu/

第一种安装方式:安装步骤:
sudo apt install curl
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

第二种安装方式:
第一步:配置Docker 的 apt 源
sudo apt update && sudo apt install  apt-transport-https  ca-certificates  curl  gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
第二步:安装 Docker
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io

Manage Docker as a non-root user
https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker 
docker version/info

4 docker核心组件
Docker 客户端 - Client
Docker 服务器 - Docker daemon
Docker 镜像 - Image
Docker 仓库 - Registry
Docker 容器 - Container

Docker 客户端:
最常用的 Docker 客户端是 docker 命令

Docker daemon 
是服务器组件,以 Linux 后台服务的方式运行;
可以通过该命令查看状态:
systemctl status docker.service
也可以通过如下命令:
docker info

默认配置下,Docker daemon 只能响应来自本地 Host 的客户端请求。
如果要允许远程客户端请求,需要在配置文件中打开 TCP 监听;
配置文件目录:
/etc/systemd/system/multi-user.target.wants/docker.service
在环境变量 ExecStart 后面添加 -H tcp://0.0.0.0,允许来自任意 IP 的客户端连接;
然后通过命令systemctl daemon-reload重启 Docker daemon。

Docker镜像:
1 base 镜像:
两个原则:
不依赖其他镜像,从 scratch 构建。
其他镜像可以之为基础进行扩展。

base 镜像提供的是最小安装的 Linux 发行版

2 镜像的分层结构
Docker 支持通过扩展现有镜像,创建新的镜像。
实际上,Docker Hub 中 99% 的镜像都是通过在 base 镜像中安装和配置需要的软件构建出来的;

docker镜像分层结构的最大好处:资源共享

3 镜像可以被多个容器共享

容器 Copy-on-Write 特性
可写的容器层:
当容器启动的时候,一个新的可写层会被添加到容器的顶部;
这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”
所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。
只有容器层是可写的,容器层下面的所有镜像层都是只读的;

只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。
可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改

容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享;

4 构建镜像

Docker 提供了两种构建镜像的方法:
docker commit 命令
Dockerfile 构建文件
但是Docker 并不建议用户通过docker commit 命令这种方式构建镜像;

通过Dockerfile 构建镜像:
Dockerfile一般位于构建上下文的根目录下,也可以通过-f指定该文件的位置;
举个栗子:
docker build -t ubuntu-with-vi -f /home/yundong/dockerfiles/ubuntu-with-vi .
-t 指定image的名字
-f 指定dockerfile的路径

5 镜像的缓存特性

Docker 会缓存已有镜像的镜像层,构建新镜像时,如果某镜像层已经存在,就直接使用,无需重新创建。
如果我们希望在构建镜像时不使用缓存,可以在 docker build 命令中加上 --no-cache 参数;

Dockerfile 中每一个指令都会创建一个镜像层,上层是依赖于下层的。
无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失效;

换句话说,如果我们改变 Dockerfile 指令的执行顺序,或者修改或添加指令,都会使缓存失效。

6 调试 Dockerfile
docker run -it 启动镜像的一个容器

7 RUN vs CMD vs ENTRYPOINT

RUN 执行命令并创建新的镜像层,RUN 经常用于安装软件包;
CMD 设置容器启动后默认执行的命令及其参数;
ENTRYPOINT 配置容器启动时运行的命令;

Exec 格式
<instruction> ["executable", "param1", "param2", ...]
例如:
RUN ["apt-get", "install", "python3"]  
CMD ["/bin/echo", "Hello world"]  
ENTRYPOINT ["/bin/echo", "Hello world"]

注意:apt-get update 和 apt-get install 被放在一个 RUN 指令中执行,这样能够保证每次安装的是最新的包。
如果 apt-get install 在单独的 RUN 中执行,则会使用 apt-get update 创建的镜像层,而这一层可能是很久以前缓存的;

最佳实践
使用 RUN 指令安装应用和软件包,构建镜像。
如果想为容器设置默认的启动命令,可使用 CMD 指令。用户可在 docker run 命令行中替换此默认命令。
如果 Docker 镜像的用途是运行应用程序或服务,比如运行一个 MySQL,应该优先使用 Exec 格式的 ENTRYPOINT 指令。
CMD 可为 ENTRYPOINT 提供额外的默认参数,同时可利用 docker run 命令行替换默认参数;

8 Dockerfile 常用指令
FROM
指定 base 镜像

COPY
将文件从 build context 复制到镜像。
COPY 支持两种形式:
COPY src dest
COPY ["src", "dest"]

ADD
与 COPY 类似,从 build context 复制文件到镜像。
不同的是,如果 src 是归档文件(tar, zip, tgz, xz 等),文件会被自动解压到 dest

ENV
设置环境变量,环境变量可被后面的指令使用。例如:

EXPOSE
指定容器中的进程会监听某个端口,Docker 可以将该端口暴露出来;

VOLUME
将文件或目录声明为 volume

WORKDIR
为后面的 RUN, CMD, ENTRYPOINT, ADD 或 COPY 指令设置镜像中的当前工作目录;

RUN
在容器中运行指定的命令;

CMD
容器启动时运行指定的命令;
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效

ENTRYPOINT
设置容器启动时运行的命令。
Dockerfile 中可以有多个 ENTRYPOINT 指令,但只有最后一个生效

9 镜像命名的最佳实践
docker build -t repository:tag -f dockerfile_path;

实际上一个特定镜像的名字由两部分组成:repository 和 tag;
如果执行 docker build 时没有指定 tag,会使用默认值 latest;

我们在使用镜像时最好还是避免使用 latest,明确指定某个 tag;

10 搭建本地 Registry

官方文档:
https://docs.docker.com/registry/deploying/

11 docker镜像常用命令

build       Build an image from a Dockerfile
history     Show the history of an image
inspect     Display detailed information on one or more images
ls          List images
prune       Remove unused images
pull        Pull an image or a repository from a registry
push        Push an image or a repository to a registry
rm          Remove one or more images
save        Save one or more images to a tar archive (streamed to STDOUT by default)
tag         Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

Docker容器

1 查看容器状态
docker ps/docker ps -a

2 运行或启动容器
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
例子:
docker run -it -d --name "my-ubuntu" ubuntu 

-d   以后台方式启动 
-it  在容器启动后就直接进入
--name  容器名称

3 两种进入容器的方法
docker attach containId
docker exec -it <container> bash|sh

4 运行容器的最佳实践
服务类容器以 daemon 的形式运行,对外提供服务。比如 web server,数据库等。通过 -d 以后台方式启动这类容器是非常合适的。
如果要排查问题,可以通过docker exec -it <container> bash|sh进入容器;

工具类容器通常给能我们提供一个临时的工作环境,通常以 run -it 方式运行;

顺便说一句:
docker rm 是删除容器,
而 docker rmi 是删除镜像。

5 容器常用指令

Manage containers

Commands:
  attach      Attach local standard input, output, and error streams to a running container
  commit      Create a new image from a container's changes
  cp          Copy files/folders between a container and the local filesystem
  create      Create a new container
  diff        Inspect changes to files or directories on a container's filesystem
  exec        Run a command in a running container
  export      Export a container's filesystem as a tar archive
  inspect     Display detailed information on one or more containers
  kill        Kill one or more running containers
  logs        Fetch the logs of a container
  ls          List containers
  pause       Pause all processes within one or more containers
  port        List port mappings or a specific mapping for the container
  prune       Remove all stopped containers
  rename      Rename a container
  restart     Restart one or more containers
  rm          Remove one or more containers
  run         Run a command in a new container
  start       Start one or more stopped containers
  stats       Display a live stream of container(s) resource usage statistics
  stop        Stop one or more running containers
  top         Display the running processes of a container
  unpause     Unpause all processes within one or more containers
  update      Update configuration of one or more containers
  wait        Block until one or more containers stop, then print their exit codes

6 限制容器对资源的使用
限制容器对内存的使用

内存限额:与操作系统类似,容器可使用的内存包括两部分:物理内存和 swap
-m 或 --memory:设置内存的使用限额,例如 100M, 2G
--memory-swap:设置 内存+swap 的使用限额

如果在启动容器时只指定 -m 而不指定 --memory-swap,那么 --memory-swap 默认为 -m 的两倍;

限制容器对CPU的使用
默认设置下,所有容器可以平等地使用 host CPU 资源并且没有限制。
Docker 可以通过 -c 或 --cpu-shares 设置容器使用 CPU 的权重。如果不指定,默认值为 1024

限制容器的 Block IO
Block IO 指的是磁盘的读写,
docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽;
默认情况下,所有容器能平等地读写磁盘,可以通过设置 --blkio-weight 参数来改变容器 block IO 的优先级;
限制 bps 和 iops
bps 是 byte per second,每秒读写的数据量。
iops 是 io per second,每秒 IO 的次数。

可通过以下参数控制容器的 bps 和 iops:
--device-read-bps,限制读某个设备的 bps。
--device-write-bps,限制写某个设备的 bps。
--device-read-iops,限制读某个设备的 iops。
--device-write-iops,限制写某个设备的 iops。

7 实现容器的底层技术
cgroup 和 namespace 是最重要的两种技术。
cgroup 实现资源限额, namespace 实现资源隔离。

cgroup 全称 Control Group。
Linux 操作系统通过 cgroup 可以设置进程使用 CPU、内存 和 IO 资源的限额;
-cpu-shares、-m、--device-write-bps 实际上就是在配置 cgroup;
 
关于cgroup相关的可以在下面目录中找到
/sys/fs/cgroup
Linux 会为每个容器创建一个 cgroup 目录,以容器长ID 命名
/sys/fs/cgroup/cpu/docker/容器长ID
/sys/fs/cgroup/memory/docker/容器长ID
/sys/fs/cgroup/blkio/docker/容器长ID

namespace
namespace 管理着 host 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说,namespace 实现了容器间资源的隔离。
Linux 使用了六种 namespace,分别对应六种资源:Mount、UTS、IPC、PID、Network 和 User;

Mount namespace 
让容器看上去拥有整个文件系统,容器有自己的 / 目录,可以执行 mount 和 umount 命令。这些操作只在当前容器中生效,不会影响到 host 和其他容器;

UTS namespace
让容器有自己的 hostname
默认情况下,容器的 hostname 是它的短ID,可以通过 -h 或 --hostname 参数设置。

IPC namespace
让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信;

PID namespace
所有容器的进程都挂在 dockerd 进程下,同时也可以看到容器自己的子进程;

Network namespace
让容器拥有自己独立的网卡、IP、路由等资源;

User namespace
让容器能够管理自己的用户,host 不能看到容器中创建的用户

Docker 网络

Docker 安装时会自动在 host 上创建三个网络:bridge/host/none
我们可用 docker network ls 命令查看;
ifconfig
ip a

none 网络:
故名思议,none 网络就是什么都没有的网络;
容器创建时,可以通过 --network=none 指定使用 none 网络;

host 网络
连接到 host 网络的容器共享 Docker host 的网络栈,容器的网络配置与 host 完全一样。
容器创建时,可以通过 --network=host 指定使用 host 网络;
直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,则可以选择 host 网络;

bridge 网络
应用最广泛也是默认的 bridge 网络。
Docker 安装时会创建一个 命名为 docker0 的 linux bridge。
如果不指定--network,创建的容器默认都会挂到 docker0 上。

通过命令brctl show查看

docker network inspect bridge
ifconfig docker0
ip a
ip link show
ip route list

Docker容器中bash: ip: command not found
sudo apt update & apt install -y iproute2

容器间通信的三种方式:

参考教程:
https://mp.weixin.qq.com/s?__biz=MzIwMTM5MjUwMg==&mid=2653587693&idx=1&sn=0f0b0b7e41d8edb0ed5f8a0b4224a0db&chksm=8d3080f4ba4709e22f4baf33de67fedd4f351784c77d97a97a6477a6a522ef5faaa90e612550&scene=21#wechat_redirect

1 IP 通信
两个容器要能通信,必须要有属于同一个网络的网卡;
满足这个条件后,容器就可以通过 IP 交互了。
具体做法是在容器创建时通过 
--network 指定相应的网络,
或者通过 docker network connect 将现有容器加入到指定网络;

2 Docker DNS Server

通过 IP 访问容器虽然满足了通信的需求,但还是不够灵活。
因为我们在部署应用之前可能无法确定 IP,部署之后再指定要访问的 IP 会比较麻烦;

从 Docker 1.10 版本开始,docker daemon 实现了一个内嵌的 DNS server,使容器可以直接通过“容器名”通信。
方法很简单,只要在启动时用 --name 为容器命名就可以了。
下面启动两个容器 bbox1 和 bbox2:
docker run -it --network=my_net2 --name=bbox1 busybox
docker run -it --network=my_net2 --name=bbox2 busybox
bbox2 就可以直接 ping 到 bbox1 了
ping -c 3 bbox1;
使用 docker DNS 有个限制:只能在 user-defined 网络中使用;

3 joined 容器
joined 容器非常特别,它可以使两个或多个容器共享一个网络栈,共享网卡和配置信息,joined 容器之间可以通过 127.0.0.1 直接通信
先创建一个 httpd 容器,名字为 web1:
docker run -d -it --name=web1 httpd
然后创建 busybox 容器并通过 --network=container:web1 指定 jointed 容器为 web1
适合以下场景:
不同容器中的程序希望通过 loopback 高效快速地通信,比如 web server 与 app server。
希望监控其他容器的网络流量,比如运行在独立容器中的网络监控程序;

容器如何访问外部世界?
容器默认就可以访问外网
这里外网指的是容器网络以外的网络环境,并非特指 internet;
通过 NAT,docker 实现了容器对外网的访问

外部世界如何访问容器?
端口映射。
docker 可将容器对外提供服务的端口映射到 host 的某个端口,外网通过该端口访问容器。
容器启动时通过-p参数映射端口

Docker 存储
Docker 为容器提供了两种存放数据的资源:
由 storage driver 管理的镜像层和容器层。
Data Volume。

对于某些容器,直接将数据放在由 storage driver 维护的层中是很好的选择,
比如那些无状态的应用。无状态意味着容器没有需要持久化的数据,随时可以从镜像直接创建;

另一类应用这种方式有持久化数据的需求,容器启动时需要加载已有的数据,容器销毁时希望保留产生的新数据,
也就是说,这类容器是有状态的,就要用到 Docker 的另一种存储机制:Data Volume;

Docker 安装时会根据当前系统的配置选择默认的 driver。默认 driver 具有最好的稳定性;
可以通过该命令查询默认的Storage Driver
docker info |grep Storage

Docker Machine相关
对于multi-host 环境,docker 给出的解决方案是 Docker Machine;
Docker Machine 可以批量安装和配置 docker host,这个 host 可以是本地的虚拟机、物理机,也可以是公有云中的云主机;

1 Install Docker Machine官方教程:
https://docs.docker.com/machine/install-machine/

If you are running Linux:

base=https://github.com/docker/machine/releases/download/v0.16.0 \
&& curl -L $base/docker-machine-$(uname -s)-$(uname -m) >/tmp/docker-machine \
&& sudo mv /tmp/docker-machine /usr/local/bin/docker-machine \
&& chmod +x /usr/local/bin/docker-machine

Check the installation by displaying the Machine version:
docker-machine version
##docker-machine version 0.16.0, build 702c267f

2 Install bash completion scripts

Docker 最常用的监控方案
1 Docker 自带的监控子命令
docker container ls
docker container top [container]
docker container stats 用于显示每个容器各种资源的使用情况
docker container stats [container1] [container2]....
2 监控利器 sysdig
安装和运行 sysdig 的最简单方法是运行 Docker 容器:
特点:
监控信息全,包括 Linux 操作系统和容器。
界面交互性强。
sysdig 显示的是实时数据,看不到变化和趋势。
而且是命令行操作方式,需要 ssh 到 Host 上执行,会带来一些不便;
安装指令:
docker container run -it --rm --name=sysdig --privileged=true   --volume=/var/run/docker.sock:/host/var/run/docker.sock --volume=/dev:/host/dev --volume=/proc:/host/proc:ro  --volume=/boot:/host/boot:ro --volume=/lib/modules:/host/lib/modules:ro --volume=/usr:/host/usr:ro  sysdig/sysdig

Docker日志管理
1 Docker logs
docker logs [OPTIONS] CONTAINER
docker logs 能够打印出自容器启动以来完整的日志,并且 -f 参数可以继续打印出新产生的日志,效果上与 Linux 命令 tail -f 一样
docker logs CONTAINER -f 

2 多种日志方案
Docker 提供了多种日志机制帮助用户从运行的容器中提取日志信息。这些机制被称作 logging driver。
Docker 的默认 logging driver 是 json-file;
更多内容参考官方文档:
https://docs.docker.com/engine/admin/logging/overview/#supported-logging-drivers

docker info | grep "Logging Driver" //Logging Driver: json-file
json-file 会将容器的日志保存在 json 文件中,Docker 负责格式化其内容并输出到 STDOUT 和 STDERR;
日志路径:
/var/lib/docker/containers/<contariner ID>/<contariner ID>-json.log

3 关于ELK

在开源的日志管理方案中,最出名的莫过于 ELK 了。
ELK 是三个软件的合称:Elasticsearch、Logstash、Kibana;
Elasticsearch
一个近乎实时查询的全文搜索引擎。
设计目标就是要能够处理和搜索巨量的日志数据。
Logstash
读取原始日志,并对其进行分析和过滤,然后将其转发给其他组件(比如 Elasticsearch)进行索引或存储。
Logstash 支持丰富的 Input 和 Output 类型,能够处理各种应用的日志。
Kibana
一个基于 JavaScript 的 Web 图形界面程序,专门用于可视化 Elasticsearch 的数据;
Kibana 能够查询 Elasticsearch 并通过丰富的图表展示结果。用户可以创建 Dashboard 来监控系统的日志。

Docker 部署环境下典型的 ELK 日志处理流程:
logs--->Logstash--->Elasticsearch--->Kibana;
Logstash 负责从各个 Docker 容器中提取日志,
Logstash将日志转发到 Elasticsearch 进行索引和保存,
Kibana 分析和可视化数据

安装 ELK 套件
ELK 的部署方案可以非常灵活,在规模较大的生产系统中,ELK 有自己的集群,实现了高可用和负载均衡;
最短的时间内学习并实践 ELK,因此将采用最小部署方案:在容器中搭建 ELK;

Docker Swarm

1 创建 Swarm 集群?
docker官方教程:
https://docs.docker.com/engine/swarm/swarm-tutorial/create-swarm/

docker swarm init --advertise-addr  hostip_enp0s8
docker node ls
docker swarm join-token worker
docker swarm join --token SWMTKN-1-0396e8m6tx8yw5oo1apw39pb3apqhzngxo7ey7vxwfsmj4nru0-7fcd2gs873ii1xxlt6aixwdak hostip_enp0s8:2377
docker swarm leave --force

netstat -napt
netstat -nltp
ip a | grep "enp0s3"
ip a | grep "enp0s8"
 
查看防火墙状态
systemctl status firewalld.service
停止防火墙
systemctl stop firewalld.service

查看已经连接的服务端口
netstat -a
查看所有的服务端口
netstat -ap
查看指定端口,可以结合grep命令
netstat -ap | grep 8080

2 创建第一个servcie

常用命令:
Usage:  docker service COMMAND
Manage services
Commands:
  create      Create a new service
  inspect     Display detailed information on one or more services
  logs        Fetch the logs of a service or task
  ls          List services
  ps          List the tasks of one or more services
  rm          Remove one or more services
  rollback    Revert changes to a service's configuration
  scale       Scale one or multiple replicated services
  update      Update a service

docker service create --name serviceName imageName
docker service create --name serviceName --replicas=副本数 imageName
--name serviceName
--replicas=副本数

查看当前 swarm 中的 service
docker service ls
查看 service 每个副本的状态
docker service ps 

3 Service 伸缩?
swarm 要实现这个目标非常简单,增加或者减少 service 的副本数就可以了;

docker service scale serviceName=副本数
需要注意的是,默认配置下 manager node 也是 worker node
果不希望在 manager 上运行 service,那么:
docker node update --availability drain hostname_swarm-manager
Drain 表示 swarm-manager 已经不负责运行 service;

4 如何实现 Failover?
故障是在所难免的,容器可能崩溃,Docker Host 可能宕机;
关闭其中的一个work node:
shut down now
然后观察这个 failover 过程:
docker service ps serviceName
也可以在node上执行命令查看
docker ps

5 如何访问 Service?

要将 service 暴露到外部,方法其实很简单,执行下面的命令:
docker service update --publish-add 8080:80 serviceName
容器在 80 端口上监听 http 请求,--publish-add 8080:80 将容器的 80 映射到主机的 8080 端口;

当我们应用 --publish-add 8080:80 时,swarm 会重新配置 service
执行命令docker service ps serviceName
可以看到之前的所有副本都被 Shutdown,然后启动了新的副本;
容器的网络与 --publish-add 之前已经大不一样了,现在有两块网卡,每块网卡连接不同的 Docker 网络:

eth0 连接的是一个 overlay 类型的网络,名字为 ingress,其作用是让运行在不同主机上的容器可以相互通信
eth1 连接的是一个 bridge 类型的网络,名字为 docker_gwbridge,其作用是让容器能够访问到外网。
ingress 网络是 swarm 创建时 Docker 为自动我们创建的,swarm 中的每个 node 都能使用 ingress

通过 overlay 网络,主机与容器、容器与容器之间可以相互访问;
同时,routing mesh 将外部请求路由到不同主机的容器,从而实现了外部网络对 service 的访问

6 Service 之间如何通信
微服务架构的应用由若干 service 组成,
比如有web 前端,有提供缓存的redis,有存放数据的 mysql,
每一层都是 swarm 的一个 service,每个 service 运行了若干容器。
在这样的架构中,service 之间是必然要通信的;

服务发现;
通过swarm提供的服务发现,service 的使用者不需要知道 service 运行在哪里,IP 是多少,有多少个副本,就能与 service 通信;

第一步创建 overlay 网络

要使用服务发现,需要相互通信的 service 必须属于同一个 overlay 网络:
具体命令格式:
#docker network create --driver overlay myapp_net
docker network create --driver overlay overlay_network_name
创建完成使用docker network ls检查
更多参考docker network命令:
Usage:  docker network COMMAND
Manage networks
Commands:
  connect     Connect a container to a network
  create      Create a network
  disconnect  Disconnect a container from a network
  inspect     Display detailed information on one or more networks
  ls          List networks
  prune       Remove all unused networks
  rm          Remove one or more networks

第二步:部署 service 到 overlay
#docker service create --name myapp --replicas=3 --network myapp_net httpd
docker service create --name service_name --replicas=3  --network overlay_network_name image_name
--name  
--replicas
--network

第三步:测试
部署一个 util 服务用于测试,挂载到同一个 overlay 网络。

docker service create --name util --network overlay_network_name busybox sleep 10000000
确认 util 所在的节点
docker service ps util 
登录到util 所在的节点
在容器util中ping 服务 service_name

7 如何滚动更新 Service?
滚动更新降低了应用更新的风险,
如果某个副本更新失败,整个更新将暂停,其他副本则可以继续提供服务。
同时,在更新的过程中,总是有副本在运行的,因此也保证了业务的连续性。

#docker service create --name my_web --replicas=3 httpd:2.2.31
docker service create --name service_name --replicas=3 image_name:tag
#将 service 更新到 httpd:2.2.32:
#docker service update --image httpd:2.2.32 my_web
docker service update --image_name:tag service_name

Swarm 将按照如下步骤执行滚动更新:
停止第一个副本;
调度任务,选择 worker node。
在 worker 上用新的镜像启动副本。
如果副本(容器)运行成功,继续更新下一个副本;如果失败,暂停整个更新过程。

docker service ps service_name查看更新结果

默认配置下,Swarm 一次只更新一个副本,并且两个副本之间没有等待时间;
通过 --update-parallelism 设置并行更新的副本数目,
通过 --update-delay 指定滚动更新的间隔时间;
例子:
docker service update --replicas 6 --update-parallelism 2 --update-delay 1m30s service_name
service 增加到六个副本,每次更新两个副本,间隔时间一分半钟

查看 service 的当前配置
docker service inspect service_name

Swarm 还有个方便的功能是回滚,如果更新后效果不理想,可以通过 --rollback 快速恢复到更新之前的状态。
--rollback 只能回滚到上一次执行 docker service update 之前的状态,并不能无限制地回滚

8 如何管理数据?
目前最佳的方案:
利用 Docker 的 volume driver,由外部 storage provider 管理和提供 volume,所有 Docker 主机 volume 将挂载到各个副本;

volume 不依赖 Docker 主机和容器,
生命周期由 storage provider 管理,
volume 的高可用和数据有效性也全权由 provider 负责,
Docker 只管使用

Rex-Ray 是开源的容器存储管理解决方案

Kubernetes
Kubernetes 是 Google Omega 的开源版本;
官方教程:
https://kubernetes.io/docs/tutorials/kubernetes-basics/