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主机

了解更多

LNMP开发环境搭建系列简介

简介

LNMP(Linux,Nginx,Mysql,PHP)的开发环境搭建是每一个PHP程序员在成长道路上必须学会掌握的技能,本系列课程将围绕LNMP开发环境的搭建进行展开。作为一名PHP程序员特别是在中小型公司需要掌握的技能是要求比较多的。前端知识得了解能够独立进行前端页面的编写,PHP程序得熟悉能够进行后台程序的开发,服务器环境得知道配置能够随时进行开源工具的安装和服务器环境的搭建。

大部分PHPer在接触PHP时应该是使用Windows下的XAMPP或者WAMP进行环境搭建(我自己也是),傻瓜式安装,一路点击下一步即可轻松的完成本地开发环境的搭建。但是在实际工作中即使是使用的XAMPP这类工具也需要对配置文件进行适当的修改以配合团队的开发环境一致性要求。因此掌握环境的配置和安装是每一个PHPer必须掌握的技能。

任何环境搭建和软件的安装最全最完整的手册一定是软件的官方网站,因此一定去软件官网下载安装包,一定先阅读官网的安装手册。我在开发过程中接触到很多刚进入职场的PHPer他们,遇到问题找百度(这个还是可取),搜索安装包解决安装问题第一反应也是百度,因为一些原因用百度搜索安装包很多排在搜索结果第一和前列的并不是官方的地址,导致下载的安装包有可能有问题。安装包和安装手册一定是去官网原厂原汁原味。

LNMP开发环境搭建系列主要包括以下几方面的内容,每个内容块会陆续更新内容

  • 在Windows环境下搭建Linux虚拟机
  • 使用快捷方式安装Nginx和Mysql
  • 使用源码方式安装PHP7
  • 搭建PHP5和PHP7同时运行的环境
  • PHP扩展安装