Docker入门系列(四):让你的服务跨越多台机器

准备工作

介绍

第三节中,我们使用了第二节写的应用,并定义了它在线上的运行方式,然后启动了5个实例。

在这一节中,我们将这个应用部署到多机集群中,正式步入swarm模式,多机、多容器的应用。

理解 Swarm clusters

Swarm是什么?Swarm就是一组运行docker的机器,并联合成为一个集群。当启动Swarm集群之后,docker命令会通过Swarm manager执行在整个集群之上。Swarm集群的机器可以是物理机,也可以是虚拟机,当加入Swarm集群之后,被称为nodes

Swarm managers有两种不同的方式运行container:第一种emptiest node,尽量使用少的机器部署容器;第二种global,确保每一台机器上都会运行一个容器的实例。可以在docker-compose.yml中指定运行的模式。

Swarm managers是集群的核心控制节点,它负责执行命令,授权新机器加入集群。Worker节点只负责提供资源。

到目前为止,你已经学会在单机使用docker容器。docker可以很方便的切换为 swarm mode,切换的命令是docker swarm init,一旦切换为 swarm mode后,当前的机器角色就变为Swarm managers。

设置你的swarm集群

创建cluster

在本机上使用VM创建集群,我使用的Mac操作系统,接下来演示在Mac上创建swarm集群。

首先,需要安装虚拟机,在Mac上需要下载 Oracle VirtualBox

使用docker-machine创建多个虚拟机

docker-machine create –driver virtualbox myvm1
docker-machine create –driver virtualbox myvm2

使用docker-machine ls列出所有的虚拟机

$ docker-machine ls

NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS

myvm1 - virtualbox Running tcp://192.168.99.100:2376 v17.09.0-ce

myvm2 - virtualbox Running tcp://192.168.99.101:2376 v17.09.0-ce

接下来,开始初始化swarm,将myvm1设置为manager,并向集群中加入节点。

1
2
3
4
5
6
7
8
$ docker-machine ssh myvm1 "docker swarm init --advertise-addr 192.168.99.100:2376"
Swarm initialized: current node (gxhg37symzvlve65jgg9ya984) is now a manager.
To add a worker to this swarm, run the following command:
docker swarm join --token SWMTKN-1-07zo0vcw3uch6r47b3b8rpcqcqz00sa9679s3jil660cimyb72-8t8nfbynnphl2zw8str5efm47 192.168.99.100:2376
To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

添加worker节点

1
2
3
$ docker-machine ssh myvm2 "docker swarm join --token SWMTKN-1-07zo0vcw3uch6r47b3b8rpcqcqz00sa9679
s3jil660cimyb72-8t8nfbynnphl2zw8str5efm47 192.168.99.100:2377"`

2377 vs 2376

注意docker swarm initdocker swarm join运行的端口号是2377,或者不指定端口,使用默认端口。docker-machine ls返回的端口是2376,这个端口是docker deamon的端口。

swarm manager上执行docker node ls检查集群运行情况

1
2
3
4
$ docker-machine ssh myvm1 "docker node ls"
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS
gxhg37symzvlve65jgg9ya984 * myvm1 Ready Active Leader
qcbxu2hop7nf13ktzxfg36ein myvm2 Ready Active

如果看到以上输出信息,恭喜你,你已经启动成功。

使用docker swarm leave推出swarm集群

1
2
$ docker-machine ssh myvm2 "docker swarm leave"
Node left the swarm.

部署service到swarm cluster

当此最复杂的部分已经学习完,接下来重复在第三节的操纵,把service部署到集群中。记住,只有swarm manager也就是myvm1可以执行docker命令。每一次连上swarm manager都需要执行docker-machine ssh比较麻烦,我们一个使用另外一种替代方案。使用docker-machine env <machine>配置当前shell连接到在虚拟机上的Docker daemon

1
2
3
4
5
6
7
$ docker-machine env myvm1
export DOCKER_TLS_VERIFY="1"
export DOCKER_HOST="tcp://192.168.99.100:2376"
export DOCKER_CERT_PATH="/Users/zhangbing/.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)配置当前的shell连接myvm1

1
eval $(docker-machine env myvm1)

执行docker-machine ls验证myvm1生效。

1
2
3
4
$ docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
myvm1 * virtualbox Running tcp://192.168.99.100:2376 v17.09.0-ce
myvm2 - virtualbox Running tcp://192.168.99.101:2376 v17.09.0-ce

可以看到myvm1是active。

接下来,通过swarm manager将服务部署到集群上,

1
2
3
$ docker stack deploy -c docker-compose.yml getstartedlab
Creating network getstartedlab_webnet
Creating service getstartedlab_web

检查服务的启动情况

1
2
3
4
5
6
7
$ docker stack ps getstartedlab
ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS
j18ii2ij6daf getstartedlab_web.1 username/repo:tag myvm2 Running Preparing 30 seconds ago
oss51cyvy2n0 getstartedlab_web.2 username/repo:tag myvm2 Running Preparing 30 seconds ago
rfvqjkhwxtif getstartedlab_web.3 username/repo:tag myvm1 Running Preparing 30 seconds ago
y0xapfhxopum getstartedlab_web.4 username/repo:tag myvm2 Running Preparing 30 seconds ago
8a4q8rq4jcxs getstartedlab_web.5 username/repo:tag myvm1 Running Preparing 30 seconds ago

清理工作

停止service的命令

docker stack rm getstartedlab

停止swarm manager

docker swarm leave –force

Ok,你已经在生产环境上操作swarm进行服务的上线,扩容,下线的整个过程。

清理宿主机的shell环境

1
eval $(docker-machine env -u)
Comentarios

Docker入门系列(三):让你的应用变为可扩展的服务

准备工作

介绍

在这一节中,我们将应用扩容,并支持负载均衡,这一节着重介绍service

  • Stack
  • Services (<-you are)
  • Container (part 2)

Services

在分布式环境中,services有很多不同的应用实例构成。想象一下,你拥有一个大型的视频分享网站,这个网站比如包含一个服务用来处理数据存储,另一个服务在后台做视频编解码,还有一个服务作为API接入层等等。

一个服务只运行一种镜像,但是它定义了镜像运行的方式,比如,使用什么端口,运行多少容器的副本,在Docker平台上,使用docker-compose.yml 可以非常方便的对服务进行定义、运行、扩容。

第一个docker-compose.yml

docker-compose.yml是yaml格式的,它定义了Docker容器在生产环境的运行方式。

docker-compose.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: "3"
services:
web:
# replace username/repo:tag with your name and image details
image: username/repo:tag
deploy:
replicas: 5
resources:
limits:
cpus: "0.1"
memory: 50M
restart_policy:
condition: on-failure
ports:
- "80:80"
networks:
- webnet
networks:
webnet:

这个docker-compose.yml 定义了如下行为:

  • 从registry上Pull在第二节上传的镜像
  • 运行5个实例作为一个取名为web的service,限制每一个实例最多使用10%的cpu,50MB内存
  • 容器失败后立即重启
  • 将web的80端口映射到宿主机的80端口上
  • 通过webnet在容器间共享80端口,达到负载均衡的目的(在内部, 容器将映射80端口到一个临时端口上)
  • webnet 的网络进行默认设置

启动负载均衡的应用

启动swarm manager

docker swarm init

启动service,并命名为getstartedlab

docker stack deploy -c docker-compose.yml getstartedlab

运行以上的命令后,在一个宿主机上启动5个容器实例。

查看service的Id

docker service ls

ID NAME MODE REPLICAS IMAGE PORTS

pxfbw2lyo4bo getstartedlab_web replicated 5/5 ruccsbingo/get-started:part2 *:80->80/tcp

Docker swarms启动5个tasks来运行containers,可以使用ps命令查看这些tasks。

docker service ps

ID NAME IMAGE NODE DESIRED STATE CURRENT STATE ERROR PORTS

ly162fal310t getstartedlab_web.1 ruccsbingo/get-started:part2 moby Running Running 27 seconds ago

ghrcp7fzjaub _ getstartedlab_web.1 ruccsbingo/get-started:part2 moby Shutdown Shutdown 30 seconds ago

t1jh0d1jsbj8 getstartedlab_web.2 ruccsbingo/get-started:part2 moby Running Running about a minute ago

qdl4t4izqsza getstartedlab_web.3 ruccsbingo/get-started:part2 moby Ready Ready 10 seconds ago

nttsh0krytde _ getstartedlab_web.3 ruccsbingo/get-started:part2 moby Shutdown Running 10 seconds ago

wc9xqpqehwvx getstartedlab_web.4 ruccsbingo/get-started:part2 moby Running Running 10 seconds ago

m8xjlizio550 _ getstartedlab_web.4 ruccsbingo/get-started:part2 moby Shutdown Shutdown 13 seconds ago

t37arxf436d5 getstartedlab_web.5 ruccsbingo/get-started:part2 moby Running Running about a minute ago

使用如下命令列出containers

docker container ls -q

使用curl验证

Hello World!

Hostname: a8ee4747d8d7
Visits: cannot connect to Redis, counter disabled%

服务扩容

更改docker-compose.yml文件中replicas的数量,重启整个服务。

docker stack deploy -c docker-compose.yml getstartedlab

Docker将会做in-place替换,不用先停服务,或者kill容器。

停止service和swarm

停止service的命令

docker stack rm getstartedlab

停止swarm manager

docker swarm leave –force

到目前为止,你已经学会了如何使用swarm进行服务的上线,扩容,下线操作。在一下节中,会介绍在集群上执行上线,扩容,下线操作。

Comentarios

Docker入门系列(二):构建第一个docker应用

准备工作

  • 安装docker,版本最低1.13
  • 了解前一部分的内容目标和安排
  • 验证docker环境可用

    docker run hello-world

介绍

现在,可以使用docker的方式构建一个应用。首先,我们从最基础、最底层的部分开始。在这篇文章中,将介绍容器的使用。在下一篇文章中,我们将介绍service。最后,会介绍整个容器栈的顶层,service在容器中如何交互。
Stack
Services
Container (<– you are)

你的新开发环境

在过去,你想开发python应用,你首先需要在本机上安装python运行环境。经常会遇到你本机的运行环境和服务器的运行环境并不一致。环境不一致,会带来各种奇奇怪怪的问题,影响开发效率。
使用docker,你只需要获取一份可移植的python运行镜像,并不需要安装。然后,将code、运行环境、程序库文件、系统配置都安装到镜像中,确保程序可以run anywhere。

使用Dockerfile定义容器

Dockerfile是容器最重要的组成部分,它定义了容器的总体框架。在容器内部,访问系统资源,比如网络接口、磁盘驱动,都是虚拟化的,和系统的其他部分都是隔离的。因此,你不得不向外部世界映射出口,明确哪些文件需要复制到容器内部。做完这些,你的应用程序的表现行为将和Dockerfile中定义的行为完全一致。

Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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
ADD . /app
# Install any needed packages specified in requirements.txt
RUN pip install -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"]

准备应用程序

创建requirements.txtapp.py。和Dockerfile放在同一个目录。
requirements.txt

1
2
Flask
Redis

app.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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)

构建应用程序

进入Dockerfile存放的目录,执行build命令

docker build -t friendlyhello .

构建成功之后,执行images命令验证效果。

$ docker images
REPOSITORY TAG IMAGE ID
friendlyhello latest 326387cea398

运行应用程序

将应用程序的80端口映射到宿主机的4000端口,使用如下命令映射并启动。

docker run -p 4000:80 friendlyhello

在浏览器中输入http://localhost:4000验证。

恭喜你,运行成功。
当前的容器运行在terminal中,可以使用ctrl+c终止容器的运行。
也可以让容器运行在后台,使用如下命令启动

docker run -d -p 4000:80 friendlyhello

-d参数指示容器在后台运行,可以使用如下命令查看所有运行的容器。

$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED
1fa4ab2cf395 friendlyhello “python app.py” 28 seconds ago

CONTAINER ID是容器的身份标识,可以使用stop命令配合CONTAINER ID停止在后台运行的容器。

docker container stop 1fa4ab2cf395

Comentarios

Docker入门系列(一):目标和安排

这个系列的教程来源于docker的官方文档,此文档的目的在于一步一步学习docker的使用方法。
这一系列的教程有如下几篇文档:

  1. docker安装启动
  2. 构建第一个docker应用
  3. 让你的应用变为可扩展的服务
  4. 让你的服务跨越多台机器
  5. 增加一个可持久化的访问计数
  6. 将swarm部署到生产环境

Docker的价值在于,如何构建,传输以及运行你的应用程序。这是作为使用者最需要关注的方向。

准备工作

在正式开始之前,最好先了解Docker是什么我们为什么需要Docker
最好准备以下计算机基础知识:

  • IP地址和端口
  • 虚拟机
  • 系统配置文件
  • 代码依赖以及构建
  • 系统资源,比如cpu使用率,内存大小等

    容器的简单阐述

    镜像(image)是一个轻量的、独立的、可执行的软件包。这个软件包包含了程序代码、运行环境、库文件、环境变量、以及配置文件等程序执行所需要的所有部分。
    容器(container)是镜像的一个运行实例。在默认情况下,容器和宿主机是完全隔离的,也可以配置访问宿主机的文件系统和网络端口。
    容器在本地运行应用程序基于宿主机的内核(kernel)。和虚拟机相比,容器拥有更好的性能。容器能够直接访问机器资源,容器运行在独立的进程中,并不会比可执行程序消耗更多的内存。
    容器 VS 虚拟机
    虚拟机
    虚拟机运行在访客系统之上,这是一种资源竞争型的架构,会造成磁盘状态和应用在OS设置、系统安装依赖、系统安全层面相互干扰,还会有其他的easy-to-lose, hard-to-replicate的问题。
    容器
    容器可以共享内核,容器内部需要的配置,都会安装在容器的内部,各个容器之间是相互隔离的。因此,容器包含它运行所需要的所有环境,能真正的实现runs anywhere

    安装docker

    install

安装成功之后,运行hello-world

$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
…(snipped)…

检查docker版本

$ docker –version
Docker version 17.06.2-ce, build cec0b72

看到如下输出信息,说明已经安装成功,可以享受docker之旅了。

总结

服务伸缩的最小单元是独立的、可移植的意义重大,它意味着CI/CD能够更新一个分布式应用的任何一部分,系统依赖不在是问题,并且资源使用率也大大提升。服务编排将紧紧围绕可执行程序,而非VM主机。
这将是一个巨大的进步,首先让我们学会如何快速行走吧。

Comentarios