Docker入门,Part3:服务

要求

  • Docker安装版本在1.13或以上
  • 安装Docker Compose。如果是安装的Docker for Mac和Docker for Windows,那么Docker Compose会一起安装。在Linux系统上需要直接进行安装。如果是在不支持Hyper-v的Windows10以前的操作系统,可以使用Docker Toolbox进行安装
  • 已完成Docker入门,Part1的阅读学习
  • 学会如何创建容器,完成Docker入门,Part2的学习
  • 确保已根据Docker入门,Part2的知识点,将提到的friendlyhello镜像上传推送到你的仓库注册地址。在本节我们将会用到这个镜像
  • 确保镜像能够已经能够在本机作为一个部署的容器运行 docker run -p 4000:80 username/repo:tag

说明介绍

在本节,我们将扩展我们的应用,并实现负载均衡。为了实现这个目的,我们必须在分布式应用程序的层次结构中上升到更高一级:服务
* Stack(堆栈)
* Services(服务,本节内容)
* Container(容器,在Part2中介绍的相关概念)

关于服务

在分布式应用中,应用的不同部分(组件)称为服务。例如,如果搭建一个视频分享网站的应用,我们这个应用就可能会包含一个存储应用数据的数据库服务,一个在后台负责处理用户上传的视频转码的服务以及一个负责处理前后端业务的服务等。
服务在生产环境中就是各类容器。一个服务只基于一个镜像运行,但是它能够控制对外暴露的端口信息,能够控制运行多少个容器的副本以确保该服务能够满足业务性能的要求等。通过扩展服务能够改变应用中这部分容器实例运行的数量,能够分配更多的计算资源给这个服务。
在Docker平台扩展服务是一件非常容易的事情,仅仅只需要编写docker-compose.yml文件。

第一个docker-compose.yml文件

docker-compose.yml采用YAML文件格式定义了在环境中容器的行为。
将下文提到的内容保存到docker-compose.yml并将该文件保存在合适的位置。这个地方需要确保我们在Part2中的镜像已经成功的上传推送到公共镜像仓库,并将文中的username/repo:tag替换为你自己的名称

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:
      - "4000:80"
    networks:
      - webnet
networks:
  webnet:

上文定义的docker-compose.yml将告诉Docker执行以下的动作
* 从公共仓库注册地拉取在Part2上传的镜像 username/repo:tag
* 运行这个镜像的5个实例作为名为web的服务,并限制每个实例的资源使用情况,最多10%的CPU使用已经50M的内存
* 在遇到错误停止时,立即重启容器
* 将主机的4000端口映射到web服务的80端口
* 指定web服务的容器通过一个名为webnet的负载均衡的网络共享80端口(在服务内部,容器会使用一个临时的端口对应到web服务的80端口)
* 使用默认的配置定义webnet网络(默认方式是负载均衡的网络)

运行新的负载均衡的应用

在使用docker stack deploy前,我们首先需要运行docker swarm init

注意: 在Part4我们将学习到这些命令。如果不运行docker swarm init那么会在执行时报错”this node is not a swarm manager”

至此,我们就可以运行我们的命令,我们需要给应用命名(这里使用getstartedlab):

docker stack deploy -c docker-compose.yml getstartedlab

这样由运行在镜像上的5个容器实例组成的单一服务的应用就启动起来了。通过docker service ls查看所有的服务,查找到以应用名称为前缀的web服务的信息。如果是使用的上文提到
的名称命名的话,那么服务名称就是getstartedlab_web。同时我们也能够看到服务的ID、副本的数量、镜像名称以及端口信息。

在服务中一个单独运行的容器就叫做task(任务)。每个任务都会有一个根据副本的数量自增的唯一ID,通过docker service ps getstartedlab_web可以查看到服务的任务信息。

在终端可以多次运行curl -4 http://localhost:4000,或者在浏览器中打开页面并每隔几秒钟进行刷新,仔细观察会发小页面的信息会变化,变化的值实际上是容器的ID。这说明每次提供服务的容器是不同的,实现了一个简单的负载均衡的效果。

扩展应用

我们可以通过修改docker-compose.yml里的replicas的值来扩展应用,保存修改,然后重新运行docker stack deploy命令
docker stack deploy -c docker-compose.yml getstartedlab
Docker实现了热更新,不需要首先停止服务或者关闭容器。运行完上面的命令后,接着运行docker container ls -q可以看到变化的情况。如果你是增加了副本的数量,那么更多的任务,更多的容器被启动了

停止应用和集群

  • 使用docker stack rm来停止应用,docker stack rm getstartedlab
  • 使用docker swarm leave --force关闭集群

使用Docker来启动和扩展应用是一件非常简单的事情。通过上面的介绍和前面几篇文章的学习,已经了解了很多在生产环境运行容器的知识。接下来,我们将继续学习如何在Docker集群上运行应用程序使之成为一个真正的应用集群

注意: Compose文件定义了如何在Docker中运行应用,并能够使用Docker Cloud上传到云服务提供方。

总结

本节使用到的部分命令如下:

docker stack ls #列出应用或stacks

docker stack deploy -c #运行指定的compose文件

docker service ls #使用指定的应用名称列出所有的服务信息

docker service ps #使用指定的应用名称列出任务信息

docker inspect #检查任务和容器的信息

docker container ls -q #列出容器的id信息

docker stack rm #停止应用

docker swarm leave –force #从管理端关闭一个单节点的集群

了解更多

Docker入门,Part 2:容器

要求

  • 已安装Dockers1.13或更高版本
  • 完成Part 1的阅读
  • 在机器环境上快速启动运行一个容器保证所有都配置正确docker run hello-world

介绍

是时候开始使用Docker方式来构建一个应用了。在本节我们将基于一个应用垂直结构的底层开始构建容器。在这之上是服务,我们将在Part3第三节介绍基于容器的服务是如何在生产环境运行的。最后,在最顶层是栈,定义了所有服务的交互行为,我们将在Part5第五节进行介绍说明。 * Stack栈 * Services服务 * Container容器(本节的介绍内容) *

新的开发环境

在以往,如果我们要开始着手写一个Python应用,要做的第一件事就是在机器上安装Python的运行环境。但是,这样会造成一个问题,就是我们搭建的环境要完美的适配应用程序的需求,同时也要匹配将来生产环境的要求。 使用Docker,我们仅仅需要做的就是构建一个便捷的Python运行环境镜像,不再需要而外安装其他东西。基于一个通用的镜像我们将代码和运行环境一起构建为一个专用镜像,保证所有的依赖、运行环境、代码全都在一起。 通过使用Dockerfile我们能够定义这些非常便捷的镜像。

使用Dockerfile定义容器

Dockerfile定义了容器是如何运转的。类似于网络接口和数据硬盘驱动等这些资源的使用在环境中都是虚拟化的,并且与系统其他程序是相互隔离的,因此我们需要将端口映射到外部,确定文件是如何来实现共通。我们期望通过Dockerfile定义配置的应用容器能够准确无误在任何地方运行。

Dockerfile

  • 创建一个空目录
  • 切换工作路径到这个新目录
  • 在目录下新建Dockerfile,将下文中的内容复制到Dockerfile中,然后保存文件(请详细查看每个语句的注释部分,解释了每个语句具体的含义)
# 使用官方的Python运行环境作为基础镜像
FROM python:2.7-slim

# 设置工作路径为/app
WORKDIR /app

# 复制当前目录下的内容到容器的/app下
ADD . /app

# 安装在文件requirements.txt指定的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt

# 将80端口进行对外开放,这样就能从容器外部通过映射访问容器内部的80端口
EXPOSE 80

# 定义环境变量
ENV NAME World

# 容器启动后运行 app.py程序
CMD ["python", "app.py"]

Dockerfile中提到了有几个我们还没有创建的文件,例如app.py和requirements.txt。我们接下来进行逐一的创建

应用程序

我们再创建两个文件,分别为requirements.txt和app.py,并将他们保存到跟Dockerfile一致的目录。这样我们就非常简单的完成了应用。当上文提到的Dockerfile构建为一个镜像后,app.py和requirements.txt也会在镜像中,因为我们使用了Dockerfile的ADD命令,并且因为使用的EXPOSE命令我们能够使用http协议访问到内部的服务

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)

现在我们可以看到pip install -r requirements.txt将按照Flask和Redis包,在应用程序中会将环境变量NAME进行打印输出,同时还会输出socket.gethostname()的内容。最后应为Redis并没有运行(因为我们并没有启用任何的Redis服务),因此在程序中尝试使用Redistribution会出错同时也会输出相关的错误信息。

注意:在容器中获取机器的名称会返回一个类似于程序进程ID一样的容器ID

如此简单,不需要在机器上安装Python和相关的依赖,也不需要在机器上构建安装这些镜像。看上去就像我们没有配置Python和Flask,一样,实际上我们已经配置好了。

构建应用程序

通过上面的配置,我们已经准备好构建需要的所有。确保你的工作目录是处于之前创建的新目录。通过ls命令我们可以看到

$ ls
Dockerfile     app.py    requirements.txt

运行构建命令。将会新建一个Docker镜像,我们可以通过使用-t参数为镜像取一个友好的名字 docker build -t friendlyhello . 我们构建的镜像在哪里呢?它位于机器上的本地Docker镜像仓库中,

docker image ls
REPOSITORY    TAG     IMAGE ID
friendlyhello latest   ******

Linux 用户可能遇到的问题 代理服务设置 代理服务在启动运行时能够阻止(修改)网络连接。如果你的机器上运行了一个代理服务器,需要将下面的相关指令添加到Dockerfile,通过使用ENV指令能够指定代理服务的主机和端口

# 配置代理服务,修改host:port为你自己机器上的对应值
EVN http_proxy host:port
EVN https_proxy host:port

DNS设置 错误的DNS设置会给pip的使用带来问题。为确保pip的正常使用,需要设置DNS服务地址。我们可以通过修改Docker守护进程里的DNS信息。通过在编辑(或者新建)配置文件(/etc/docker/daemon.json)添加dns信息

{
  "dns": ["你的DNS地址", "8.8.8.8"]
}
保存对daemon.json文件的修改,重启docker服务`sudo service docker restart`

问题修复后,重新使用build进行构建

运行应用

使用-p参数进行端口映射将主机的4000端口映射为容器对外发布的80端口,docker run -p 4000:80 friendlyhello 通过控制台我们可以看到容器启动的相关信息,可以看到Python监听了80端口启动了一个HTTP服务。但是这个信息是来自于容器内容,因此80端口也是指的容器内的端口,在运行启动时我们通过端口映射将主机的4000端口映射到容器内的80,因此我们可以在本机通过http://localhost:4000来访问服务。如果一起启动正常我们可以在浏览器看到相关的信息

注意 如果是在windows7上通过Docker Toolbox来运行容器复苏的话,需要将localhost替换为Docker Machine的IP。可以通过docker machine ip来查看Docker Machine的IP地址

在终端中使用CTRL+C,可以退出

在windows操作系统中,需要明确停止容器 在windows操作系统中,使用CTRL+C不会停止容器,只会退出命令终端。因此我们需要通过CTRL+C退出命令终端(此时通过docker container ls查看还有哪些容器在运行),然后使用docker container stop <Container NAME od ID>来停止容器

接下来,我们需要应用在后台运行,以静默的方式在后台运行 docker run -d -p 4000:80 friendlyhello 使用-d参数,可以让容器在后台运行,通过docker container ls查看正在运行的容器和相关信息,在后台运行的的容器可以使用docker container stop <Container NAME od ID>来停止容器

发布共享镜像

为演示我们刚刚运行的应用的可移植性,我们可以上传构建的镜像然后就可以在任意地方运行了。在部署容器到其他环境(生产环境、测试环境)前,需要知道如何将镜像推送上传到登记注册处。 一个登记注册处是仓库的集合,仓库是镜像的集合——就像GitHub的仓库一样。登记注册处的一个账号可以创建多个仓库。docker命令默认使用Dockers的公共注册处

注意 使用默认的注册处是因为它已经做好了默认的配置而且是免费的。当然还有很多其他的公共注册处提供选择,我们也可以自己创建私有的注册处

使用Docker账号登陆

如果还没有Docker账号,可以在http://hub.docker.com进行注册。在本机登陆公共的Docker登记注册处docker login,根据提示信息输入账号密码,完成登陆

为镜像打标签

仓库上的镜像与本地镜像的关联格式是username/repository:tag,tag是可选的,但是建议每次都附上tag,通过tag我们可以了解到docker镜像的版本。通过镜像功能和相关信息为repository:tag选择有意义的名称,例如get-started:part2 使用我们自己的username,repository和tag运行docker tag image,命令的语法格式为: docker tag image username/repository:tag 例如 docker tag friendlyhello gordon/get-started:part2 运行docker image ls查看刚刚打上tag的镜像

推送发布镜像

上传打上标签的镜像到仓库 docker push username/repository:tag,例如docker push gordon/get-started:part2 上传完成后,这个镜像就成为公共的,如果我们登陆Docker Hub,就可以看到刚刚上传的镜像

从远端拉取镜像并运行

至此,可以通过使用 docker run在任何其他支持docker的机器上运行应用 docker run -p 4000:80 username/repository:tag 如果这个镜像在本机没有检测到,Docker就会从远端仓库哦查找获取这个镜像。无论docker run在哪里运行,他都会获取到我们上传的镜像,在镜像中包含有Python、相关的依赖和运行的代码。它把所有需要的代码依赖组件等打包在一起,而在本地机器上我们不需要任何安装而外的东西就能够运行整个应用。

本节总结

本节通一个实际的案例讲解了Dockefile和Docker Hub的相关知识,在下一节,将会学习如何以服务的方式运行容器来扩展我们的应用

温故知新

下面的终端记录小视频记录了本节的所涉及的相关操作

下面列出了本节使用到的一些命令

docker build -t friendlyhello # 使用当前目录下的Dockerfile新建镜像

docker run -p 4000:80 friendlyhello # 运行“friendlyhello”,并将本机的4000端口映射到容器的80端口

docker run -d -p 4000:80 friendlyhello # 以后台静默方式运行容器,并将本机的4000端口映射到容器的80端口

docker container ls # 列出所有正在运行的容器

docker container ls -a # 列出所有容器,包括已停止的

docker container stop # 友好的停止指定的容器

docker contailer kill # 强制停止指定的容器

docker container rm # 从机器上删除指定的容器

docker container rm $(docker container ls -a -q) # 删除所有容器

docker image ls -a # 列出本机上所有的镜像

docker image rm # 从本机上删除指定的镜像

docker image rm $(docker image ls -a -q) # 删除本机上所有的镜像

docker login # 使用Docker账号进行登陆

docker tag username/repository:tag # 为待上传的镜像打标签

docker push username/repository:tag # 上传镜像到仓库

docker run username/repository:tag # 运行镜像(如果本机没有该镜像,就会从仓库下载镜像并允许)

了解更多

Docker入门,Part 1:方向

Docker入门,Part 1:方向

非常的开心,我们能够一起来学习Docker。通过Docker入门手册我们将一起学习了解以下几个方面的内容: 1. 配置Docker环境 2. 构建镜像并启动容器 3. 运行多个容器扩容你的应用 4. 添加后端数据库实现应用栈 5. 在生产环境上部署应用

Docker相关概念

Dockers为开发运维人员提供一个利用容器实现的开发,部署,运行的统一平台。利用Linux的容器技术实现应用的部署称之为容器化。容器并不是一门新的技术,但是通过容器来实现便捷的应用部署却是非常新颖的技术和方向。

容器化越来越流行是因为: * 灵活:即使是非常复杂的应用也能够实现容器化 * 轻量:容器与主机是共用同一个内核,我们可以这么理解容器实际上就是主机上的一个进程(当然这个描述不一定准确) * 可变:我们能够实现快捷的云部署和更新 * 便捷:我们能够在本地构建,在云端部署,在任何地方运行 * 可扩展:我们能够自动化的增加分布式容器的副本 * 堆栈:能够实现垂直的堆栈 Docker

镜像和容器

通过运行镜像来启动一个容器。镜像是一个可执行的包,包括运行应用所必需的所有东西(代码,运行环境,依赖库,环境变量和配置文件等) 一个容器就是运行镜像的实例。在Linux环境下我们可以通过命令docker ps来查看所有正在运行的容器

容器和虚拟机

容器运行于原生的主机系统中并与其他容器共同使用主机内核。每个容器都是独立运行的进程,不占用任何不必要的内存,所以非常的轻量 相比而言,虚拟机则是通过虚拟机管理程序虚拟访问主机资源运行了一个完整的操作系统。一般情况下,虚拟机提供的运行环境会比应用程序实际需要的资源更多。赞成资源的浪费和环境的笨重。 容器运行资源图 虚拟机运行资源图

准备我们的Doker环境

该章节会通过而外一篇文章来介绍,请在站内查找

测试Doker

  1. 运行docker --version 确保安装是可用的版本
  2. 运行docker info (或者 docker version,不添加--)查看更多的docker安装信息 为避免权限的问题,需要将用户添加到docker组

测试Docker

  1. 我们从运行一个非常简单的容器开始,运行万能的hello-world开始测试我们的安装是否成功docker run hello-world,如果一切OK,我们能够在终端看到运行成功的提示信息
  2. 查看本机上下面的镜像信息docker image ls
  3. 查看上文提到的hello-world容器信息,docker container ls --all(如果上文的hello-world容器一直在运行,可以不用添加--all选项)

回顾

## 列出Docker命令行命令
docker
docker container --help

## 查看Docker版本和相关信息
docker --version
docker version
docker info

## 运行Docker镜像
docker run hello-world

## 查看镜像
docker image ls

## 查看容器 (运行中, 所有,所有关闭模式的)
docker container ls
docker container ls --all
docker container ls -aq

总结

容器化是CI/CD无缝的集成,例如: * 应用不再有系统的依赖(一处构建,多处运行) * 能够对已发布的应用任何一部分进行更新 * 资源使用率得到优化 使用Docker,扩展应用程序的就仅仅在于启动新的容器,而不是运行笨重的VM主机

了解更多