在看了无数篇的博客之后,小编终于决定啃一啃docker的官方文档,这里不是说,其他博主的博客毫无作用,而是,在有一定的docker基础之后,才有能力去挑战一下纯英文的官方文档。 通过阅读官方文档,确实多docker的了解更加深入,这里小编主要为大家介绍一下“get-started”,具体说了些什么,可以实现什么,让大家对docker有更深刻的了解。 最后,小编还是要夸一下官方文档,确实读了官档之后,简直对docker的了解焕然一新,虽然知道了这么多docker的命令和基础知识,但是就好像现在的“快船”,没有保罗这个线,果然只是各自绽放光芒的珍珠,整体实力不行啊!小编相信有了官方文档这个线,一定能把docker的知识点都串起来。 当然上一篇文章是整体的给大家介绍一下docker的使用和基本配置,仅仅处于单机版,现在这个科技爆棚、大数据吹捧的时代,没有分布式,一切都毫无意义,小编通过官档的part1-part6的研读,向大家介绍一下关于docker的分布式的部署和优点,让大家对docker有更更深入的了解,当然还是为k8s打基础,好了废话不说了,撸文档。 官方文档地址:https://docs.docker.com/get-started/ 这里详细的告诉我们,通过阅读这个基础入门,我们大体上可以学习到: - Part1:如何设置自己的docker环境 - Part2:构建一个image并作为容器运行 - Part3:扩展应用程序以运行多个容器(这里的扩展表示的是一个容器的多个副本) - Part4:跨集群的分布式应用程序(docker的分布式) - Part5:通过后端数据库来堆栈服务 - Part6:将应用程序上线到生产
1.Part1:Orientation and SetUp
Part1主要是对docker的概念做了解,并列出了一些docker的基础命令,告诉我们docker是什么、docker的优势以及docker的下载和安装。 这里小编就不在介绍docker如何安装,介绍一些docker的常见命令:
#查看docker的版本
docker --version
#查看docker的详细信息
docker info
#使用docker运行一个容器
docker run hello-world
#查看docker已下载的images
docker image ls
#查看所有的容器(运行的和已退出的)
docker container ls --all
2.Part2:Containers
经过part1的介绍和对docker的理解,在part2中我们也可以构建自己的应用程序,但是这里构建的应用程序只是最底层的使用一个container去构建,之后会介绍service级别的和堆栈级别的(用于生产)。
这里在构建一个应用程序时,体现了docker的一个优势—隔离,也就是说,如果你的应用在之后的升级和更新之后会变得很大,底层依赖的环境会越来越多,当这个应用程序大到可以左右你的系统时,那么如果需要对这个应用程序进行迁移或者在这个系统上部署其他的服务,对运维来说简直就是一个灾难。这时候docker的强大之处就体现出来了,docker可以保证每一个container是完全隔离的,不同的应用放置在不同的container中,如果想要迁移这个服务,只需要将这个container构建成一个image,并上传到自己的registry中(企业级的使用harbor),就可以在任意地方,pull这个image,之后运行即可。 官网给出了这样的一个例子:过去,如果要开始编写Python应用程序,首先要做的就是在机器上安装Python运行时。但是,这就造成了这样一种情况:您的机器上的环境需要根据您的应用程序去部署,以便您的应用程序能够按预期运行,而且还需要与您的生产环境相匹配。使用Docker,您只需要获取一个可移植的Python运行时作为映像,不需要安装。然后,只需要在这个有python环境基础的images上部署你的代码和依赖,他就可以正确的运行你的应用程序。最终这些可移植的image由dockerFile构建。 接下来通过一个实战案例去解释上面这段话的含义:
#Use an official Python runtime as a parent image
FROM python:2.7-slim
#Set the working directory to /app
WORKDIR /app
#Copy the current directory contents into the container at /app
COPY . /app
#Install any needed packages specified in requirements.txt
RUN pip install --trusted-host pypi.python.org -r requirements.txt
#Make port 80 available to the world outside this container
EXPOSE 80
#Define environment variable
ENV NAME World
#Run app.py when the container launches
CMD ["python", "app.py"]
这里这个dockerfile出现了两个文件需要自己编写:app.py and requirements.txt
#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
Flask
Redis
注意:dockerfile、app.py 、 requirements.txt在同一个目录下。
#构建并运行容器: #构建images docker build -t --tag=friendlyhello . #查看构建的image $ docker image ls #运行image的容器 docker run -p 4000:80 friendlyhello 如果顺利的话,就可以在http://localhost:4000中看见如下界面: 到这里,一个基于dockerfile的容器就这样构建好,并运行起来了,是不是非常的简单。
3.Part3:Services
在part2中我们只是使用dockerfile构建一个image,并且通过image运行了container,在part3中我们将实现分布式应用服务的一个特性--load-balancing,这就不得不将刚刚的container上升一个高度到:service。 在分布式应用程序中,应用程序的不同部分称为“服务”。例如,如果您设想一个视频共享站点,它可能包括一个用于在数据库中存储应用程序数据的服务、一个用于用户上传内容后在后台进行视频代码转换的服务、一个用于前端的服务,等等。 服务实际上只是“生产中的容器”。“一个服务只运行一个映像,但是它将映像运行的方式进行了编码——应该使用什么端口,应该运行多少个容器副本,以便服务具有所需的容量,等等。扩展服务可以改变运行该软件的容器实例的数量,从而为流程中的服务分配更多的计算资源。 这里我们就着手去构建一个自己的services: #编写一个docker-compose.yml文件: #Yml是一个yaml文件,它定义了Docker容器在生产环境中的行为。
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: nginx
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "4000:80"
networks:
- webnet
networks:
webnet:
这个文件告诉了docker需要做的事情:
-
从registry中pull一个nginx镜像
-
运行5个名字叫做web的镜像实例。(其中也定义了这五个实例的资源配额)
-
设置了失败重启策略
-
设置了端口映射
-
通过一个称为webnet的负载均衡网络,指示web的容器共享端口80(有点像nginx的负载均衡)
-
使用默认设置定义webnet网络(这是一个负载平衡的覆盖网络)
#设置节点为集群管理器 docker swarm init #运行自己的负载均衡app docker stack deploy -c docker-compose.yml getstartedlab 注意:这里“getstartedlab”是给自己的service起一个名字 #查看所有的service docker service ls #查看具体的service docker stack services getstartedlab 可以看到这个service中运行了5个副本,而其名称为“getstartedlab_web”
#我们在查看getstartedlab_web下的task docker service ps getstartedlab_web docker stack ps getstartedlab #这种方式也可以获取service的容器的运行状态 注意:这里的一个单一的container在service中称为task,每一个container在service有一个唯一的ID,以数字的形式顺序递增。
#同理我们查看docker的容器,同样可以看见这个五个task [root@zy dockerfile]# docker ps 此时在浏览器中输入http://localhost:4000,就能看见如下界面: 补充:
#删除服务
docker stack rm getstartedlab
#取消这个节点为集群的管理节点
docker swarm leave --force
4.Part4:Swarms
这一部分介绍的是,将应用程序部署到集群中去,并在多台机器上运行。多容器、多机器的应用程序可以通过将多台机器连接到一个叫“Dockerized”集群中来实现。 Swarms 是一组运行docker并加入到集群的机器。在Swarms中运行的docker命令,都将由集群管理器执行,集群中的机器可以是物理机也可以是虚拟机。一旦加入到了Swarms中它们就被称为一个节点。 集群的管理器可以使用不同的策略去运行容器。如“emptiest node” 表示:用容器填充利用率最低的容器;或者“global”表示:确保每台机器只获得指定容器的一个实例。指示集群管理器在编排文件中使用这些策略,就像已经使用的策略一样。 Swarms的集群管理器是唯一能够执行命令的机器。他可以授权其他的机器作为worker加入集群,worker只提供生产能力,没有权利指示其他的机器该做什么和不该做什么。
(1)建立自己的Swarms
集群是由多个节点组成的,可以是物理机也可以是虚拟节点。其基本概念比较简单:运行docker swarm init 以启用集群模式,并使当前正在使用的机器成为一个集群管理器,然后在其他机器上运行docker swarm join让他们作为worker加入到集群中。这里我们使用的是vm快速创建一个双机集群。
#创建两个虚拟节点
#这里使用docker-machine,以VirtualBox 驱动创建两个虚拟节点:
$docker-machine create --driver virtualbox myvm1
$docker-machine create --driver virtualbox myvm2
注意:Docker-machine是 Docker 官方提供的一个工具,它可以帮助我们在远程的机器上安装 Docker,或者在虚拟机 host 上直接安装虚拟机并在虚拟机中安装 Docker。 https://docs.docker.com/machine/install-machine/ 官网的安装docker-machine。
#查看这两个虚拟机的IP
docker-machine ls
注意:这里的默认端口是2376,如果系统中有进程占用了2376,会报错。
#初始化集群并添加节点
#以myvm1为管理节点,执行管理命令并验证myvm2是否加入集群,myvm2是一个从节点。
$ docker-machine ssh myvm1 "docker swarm init --advertise-addr <myvm1 ip>"
注意:如果这里出现了问题,使用--native-ssh 使用系统的ssh
docker-machine --native-ssh ssh myvm1 ...
#添加myvm2作为工作节点
$ docker-machine ssh myvm2 "docker swarm join \
--token <token> \
<ip>:2377"
到这里,就创建好了属于自己swarm的集群。
#查看集群的节点
$ docker-machine ssh myvm1 "docker node ls"
(2)部署自己的app在swarm集群上
#运行docker-machine env myvm1获得命令,将shell配置为与myvm1对话:
$ docker-machine env myvm1
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/sam/.docker/machine/machines/myvm1"
export DOCKER_MACHINE_NAME="myvm1"
# Run this command to configure your shell:
# eval $(docker-machine env myvm1)
注意:这句话表示当前的shell连接myvm1。(也就是在这个shell的操作实际上实在myvm1中的)
#运行给定的命令,将shell配置为与myvm1对话
eval $(docker-machine env myvm1)
#查看myvm1是否active,*号表示成功
$ docker-machine ls
在swarm管理节点上部署app
docker stack deploy -c docker-compose.yml getstartedlab
**注意:**虽然说这个shell是在myvm1中的,但是只要我们的所在位置是docker-compose.yml的文件所在位置,那么仍然可以使用docker-compose.yml文件。
#如果镜像不是在docker Hub,而是在自己的私服上,可以使用docker login 登录
$docker login registry.example.com
$docker stack deploy --with-registry-auth -c docker-compose.yml getstartedlab
#查看stack中运行的container
$ docker stack ps getstartedlab
注意:注意这里在集群的各个节点上都有运行,并且在访问时时随机的指定哪一个container提供服务,实现了负载均衡。 当然使用虚拟机的IP,去查看:
(3)实现负载均衡的原理
这两个IP地址都能工作的原因是群中的节点参与了一个入口路由网格。这可以确保部署在集群中某个端口的服务始终保留该端口,不管实际运行容器的是哪个节点。上面是一个图,展示了一个名为my-web的服务的路由网格是如何在一个三节点群上的8080端口发布的。
(4)应用程序的迭代和扩展
情况一:通过改变docker-compose.yml 文件去扩展应用程序。 情况二:改变应用程序的行为和代码之后,构建出一个新的image,并且push的仓库中。 以上的两种情况只需要docker-compose.yml file 重新构建service即可。 当然如果,swarm集群中加入了新的节点,此时,只需要在这个新的节点上执行docker swarm join 将这个节点加入到swarm集群中,之后的 docker stack deploy 操作会自动的利用新的资源(新加入的节点)。
(5)清除和重启
#拆除stack
docker stack rm getstartedlab
#移除整个swarm集群
连接每一个worker节点执行:
docker-machine ssh 节点名 "docker swarm leave"
#在每个节点上取消之前的环境变量:
eval $(docker-machine env -u)
#重启docker machines
docker-machine start <machine-name>
5.Part5:stack
在part4部分中,学习了如何设置一个运行docker容器的集群,并将一个应用程序部署到其中,容器在多台机器上同时运行。 在part5将介绍分布式应用程序层次中的顶层:堆栈。堆栈是一组互相关联的服务,他们共享依赖关系,并且可以在一起编排和伸缩,单个堆栈能够定义和协调整个应用程序的功能(非常复杂的应用程序需要使用多个堆栈)。 其实在part3中就使用了堆栈,当在撰写一个Compose 文件时就是使用的堆栈deploy,但是在单个主机上运行的单个服务堆栈,这个一般在生产环境中不会出现,在part5中可以使多个服务彼此关联,并在多台机器上运行。
(1) 添加新服务并重新部署
添加新服务很简单,使用Compose.yml文件即可。这里我们添加一个免费的visualizer服务,他允许我们查看集群如何调度容器、服务额部署。
#docker- composition.yml:
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: nginx
deploy:
replicas: 5
restart_policy:
condition: on-failure
resources:
limits:
cpus: "0.1"
memory: 50M
ports:
- "80:80"
networks:
- webnet
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
networks:
webnet:
这里我们创建了两个服务,nginx和visualizer。并且使用了volumes,提供了本机映射docker的目录文件。并且添加了一个放置键,保证了服务只能在docker的管理节点上运行,而不是在worker上运行。
#确保shell配置为与myvm1对话
$eval $(docker-machine env myvm1)
#在管理节点上重新运行docker stack deploy命令,任何需要更新的服务都会更新:
$ docker stack deploy -c docker-compose.yml getstartedlab
通过docker-machine ls获得其中一个节点的IP地址。去任何一个IP地址的8080端口都可以看见: 此时可以看见visualizer单一副本的服务在管理节点上运行,并且web的5个实例分散在集群中。可以通过docker stack ps <stack>,来验证:
$docker stack ps getstartedlab
(2)Persist the data
visualizer是一个独立的服务,可以在任何包含在堆栈中的应用程序中运行。它不依赖于其他任何东西。现在让我们创建一个具有依赖性的服务:提供访问者计数器的Redis服务。
#docker-compose.yml:
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: nginx
deploy:
replicas: 5
restart_policy:
condition: on-failure
resources:
limits:
cpus: "0.1"
memory: 50M
ports:
- "80:80"
networks:
- webnet
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
networks:
- webnet
redis:
image: redis
ports:
- "6379:6379"
volumes:
- "/home/docker/data:/data"
deploy:
placement:
constraints: [node.role == manager]
command: redis-server --appendonly yes
networks:
- webnet
networks:
webnet:
redis在镜像库中有一个官方的镜像,命名就是Redis,最重要的是,在Redis规范中有两件事可以让数据在这个堆栈的部署之间持久:
- Redis总是在管理节点上运行,所有它使用的相同的文件系统
- Redis容器映射出来一个/data目录,在主机的/home/docker/data,存储容器数据。 总之:这是在主机的物理文件系统中为Redis数据创建一个“真实源”。如果没有这个,Redis将把它的数据存储在容器的文件系统中,如果容器被重新部署,文件系统将被删除。 部署:
#在管理机器上创建一个/data目录
$docker-machine ssh myvm1 "mkdir ./data"
#保证当前shell在myvm1:
$eval $(docker-machine env myvm1)
#更新堆栈:
$ docker stack deploy -c docker-compose.yml getstartedlab
#查看服务
$ docker service ls
#查看IP
$docker-machine ls
#验证: 验证nginx在任意节点上访问nginx: #查看visualizer 服务: 检查一个节点的web页面,例如http://192.168.130.101,并查看访问者计数器的结果,该计数器现在是活动的,并在Redis上存储信息。 总结:栈是相互关联的服务,并且所有的服务是同步运行的。使用位置约束和数据卷,可以将容器中的数据持久化到本地主机中,因此当容器被重新部署的时候,应用程序的数据不会丢失。
6.Part6
最终part6总结了dockerized应用程序的一些选项:
#创建自己的swarm在node上
docker swarm init
#部署自己的app
docker stack deploy -c docker-compose.yml getstartedlab
#查看自己swarm的node
$ docker node ls
#查看服务列表
$ docker service ls
#查看具体服务下的容器
$ docker service ps vy7n2piyqrtr
#删除一个堆栈
docker stack rm getstartedlab