转载需经本人同意且标注本文原始地址: https://zhaomenghuan.github.i...
前言
第一次听到 Docker 这个词,是两年前找实习工作的时候,参与了 DaoCloud 的前端电话面试,了解到这家专注于做容器技术的公司,当时对于容器这些完全没有概念,只是觉得 Docker 是一个很高大上的技术,后来选择了自己相对擅长的移动开发,去了 DCloud。转眼间已经过了两年,现在人工智能和云计算已经不仅仅是风口,而是很多技术实现的基础设施。
今年打算学习一下人工智能领域一些最最基础的内容, 不至于几年后被市场淘汰吧。这段时间先学习一下谷歌的 TensorFlow 框架,正所谓"工欲善其事,必先利其器",第一步先必须搭建好开发环境,TensorFlow 目前可支持如下几种方式安装:
- Virtualenv
- Pip
- Docker
- Anaconda
- 从源代码安装
因为后续可能也会深入玩一下 Docker,所以对于 TensorFlow 的安装选用 Docker 的方式安装。
Docker 入门指南
Docker 是什么?
软件开发最大的麻烦事之一,就是环境配置。用户计算机的环境都不相同,你怎么知道自家的软件,能在那些机器跑起来?Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。
Docker 通过 Docker Engine 运行在操作系统 OS 上,虚拟机运行在硬件资源上。
通俗的讲 Docker 是一个“码头工人”,将我们需要的货物(应用)打包成具有某种标准规格的集装箱(镜像)内。docker 在部署过程中,将安装,配置等重复的部分自动化完成。只需要在第一次部署时,构建完可用的 docker 镜像(装好集装箱),在以后使用中,短短的几行命令就可以直接拉取镜像,根据这个镜像创建出一个容器,把服务跑起来了。所需要的仅仅是安装了 docker 的服务器,一个 Dockerfile 文件(装箱清单),以及比较流畅的网络而已,真可谓『一次构建,到处部署』
Docker 的主要用途,目前有三大类。
- 提供一次性的环境。比如,本地测试他人的软件、持续集成的时候提供单元测试和构建的环境。
- 提供弹性的云服务。因为 Docker 容器可以随开随关,很适合动态扩容和缩容。
- 组建微服务架构。通过多个容器,一台机器可以跑多个服务,因此在本机就可以模拟出微服务架构。
Docker 总架构图
docker 系统使用了 C/S 的架构,docker client 通过 REST API 请求 docker daemon 来管理 docker 的镜像和容器等。Server 端驻守在后台,称之为 docker daemon。Client 端是一个 CLI 程序,可以在命令行中通过 docker 这个二进制文件进行交互。
安装 Docker
Docker 是一个开源的商业产品,有两个版本:社区版(Community Edition,缩写为 CE)和企业版(Enterprise Edition,缩写为 EE)。
- 官方文档:https://docs.docker.com/
- MAC 环境下 Docker CE 的安装: https://docs.docker.com/docker-for-mac/install/
安装完成后,运行下面的命令,验证是否安装成功。
$ docker --version
Docker version 18.03.0-ce, build 0520e24
$ docker-compose --version
docker-compose version 1.20.1, build 5d8c71b
$ docker-machine --version
docker-machine version 0.14.0, build 89b8332
Docker Registry
Docker 远程镜像仓库:
- DockerHub:https://hub.docker.com/
- DaoCloud:https://hub.daocloud.io/
- Aliyun:https://dev.aliyun.com/search...
在安装环境的过程中,因为某个伟大的防火墙工程,我们需要的大部分资源都没法顺利的获取到,解决办法就是将下载的源换成国内某些厂商提供的镜像源。
Docker Image
Docker Image(镜像) 是用来创建 docker Container(容器) 的只读模版,其中包含了容器启动所需要的所有配置信息和运行程序,一次构建之后可以多次复用。
只有通过这个文件,才能生成 Docker Container(容器),Docker 根据 image 文件生成容器的实例,同一个 image 文件,可以生成多个同时运行的容器实例。实际场景下,一般我们自己创建的镜像都会依赖于某个 Linux 操作系统的镜像,例如 ubuntu,大多数情况下,我们可称其为基础镜像,但是我们也可以查看 ubuntu 镜像的 Dockerfile 会发现,它也是依赖于一个叫 scratch 的镜像,scratch 是 docker 的一个空镜像,里面只有 docker 加入的一些元数据,如果我们想要追求自己的镜像尽可能的轻量,也可以将 scratch 镜像作为我们的基础镜像来构建。
image 相关的命令:
# 将 image 文件从仓库抓取到本地
docker image pull hello-world
# 列出本机的所有 image 文件。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest ae513a47849c 3 weeks ago 109MB
hello-world latest e38bc07ac18e 6 weeks ago 1.85kB
# 删除 image 文件
$ docker image rm [imageName]
在 MacOS 下,Docker images 保存在哪个路径下?
如果你使用的是 Docker for Mac 版本,那么所有的 docker images 保存在下面这个文件里。
/Users/{YourUserName}/Library/Containers/com.docker.docker/Data/com.docker.driver.amd64-linux/Docker.qcow2
Docker Container
Docker Container(容器) 中包含了我们的应用代码和代码执行的环境,是用来打包分发代码的单元。image 文件生成的容器实例,本身也是一个文件,称为容器文件。
# 列出本机正在运行的容器,使用 docker container ls 或 docker ps
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b15f63d6e87a nginx "nginx -g 'daemon of…" About an hour ago Up About an hour 0.0.0.0:80->80/tcp webserver
# 列出本机所有容器,包括终止运行的容器,加上 --all 获取 -a
$ docker container ls -a
# 停止本机正在运行的容器
$ docker container stop webserver
# 可以删除某个容器
$ docker rm container_name/container_id
# 启动某个容器
$ docker start container_name/container_id
# 终止某个容器
$ docker stop container_name/container_id
# 在容器中执行 /bin/bash,执行该命令之后将可以以交互命令行的方式操作容器,另外 /bin/bash 可以替换成任意可执行命令
$ docker exec -it contaner_name /bin/bash
创建容器来执行应用代码,具体用到 run 命令:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
例如,我们要创建一个运行 nginx 的容器,可以执行:
docker run -d -p 80:80 --name webserver nginx
在 Web 浏览器中,转到 http://localhost/
查看 nginx 主页。因为我们指定了默认的 HTTP 端口,所以不需要在 URL 的末尾追加:80
。
参数的意义:
- -d 参数表示后台守护进程运行容器
- --name 参数表示容器的名称,可随意取
- -v 表示主机和容器共享文件的映射,容器的目录就是 Dockerfile 中用 VOLUME 命令定义的目录
- -p 表器主机和容器端口映射,容器的端口就是 Dockerfile 中用 EXPOSE 命令绑定的端口
Dockerfile
Dockerfile 是用来说明如何自动构建 docker image 的指令集文件,在 Dockerfile 中编写好指令集之后,我们就可以通过 docker build 命令构建镜像,Dockerfile 文件中命令的顺序就是构建过程中执行的顺序。
Dockerfile reference:https://docs.docker.com/engin...
以下为几个常用的指令:
FROM:依赖镜像,所有 Dockerfile 都必须以 FROM 命令开始,表示其依赖的镜像。
FROM image_name
RUN:在 shell 或者 exec 的环境下执行的命令
RUN
ADD:将主机文件复制到容器中
ADD /path/to/sourcefile/in/host /path/to/targetfile/in/container
CMD:指定容器启动默认执行的命令
CMD ["executable","param1","param2"]
EXPOSE:指定容器在运行时监听的端口
EXPOSE
WORKDIR:指定 RUN、CMD 与 ENTRYPOINT 命令的工作目录
WORKDIR /path/to/workdir/in/container
VOLUME:授权访问从容器内到主机上的目录
VOLUME ["/data"]
Hello World
我们这里也来跑一个 Hello World,打开命令行终端:
$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
ca4f61b1923c: Pull complete
Digest: sha256:ca0eeb6fb05351dfc8759c20733c91def84cb8007aa89a5bf606bc8b315b9fc7
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
这里在命令行终端输出了Hello from Docker!
,至此完成了我们的第一个 Docker 实例。这里我们是直接使用了 docker run
来创建容器来执行应用代码,如果本地没有 hello-world 镜像文件,则会先从服务上拉取镜像文件。Docker 官方提供的 image 文件,都放在 library 组里面,所以它的是默认组。我们也可以先使用 docker image pull hello-world
先下载镜像文件,然后再运行容器。这里我们也可以通过写 Dockerfile 文件完成上述功能。我们上述的 hello-world 镜像文件可以在这里查看:https://github.com/docker-lib...。
TensorFlow 入门指南
TensorFlow 简介
TensorFlow™ 是一个使用数据流图进行数值计算的开放源代码软件库。图中的节点代表数学运算,而图中的边则代表在这些节点之间传递的多维数组(张量)。借助这种灵活的架构,您可以通过一个 API 将计算工作部署到桌面设备、服务器或移动设备中的一个或多个 CPU 或 GPU。TensorFlow 最初是由 Google Brain 团队(隶属于 Google 机器智能研究部门)中的研究人员和工程师开发的,旨在用于进行机器学习和深度神经网络研究。但该系统具有很好的通用性,还可以应用于众多其他领域。
学习资料
- TensorFlow 官网
- TensorFlow github
- TensorFlow 中文社区
- TensorFlow 官方文档中文版-极客学院
什么是数据流图(Data Flow Graph)?
数据流图用“结点”(nodes)和“线”(edges)的有向图来描述数学计算。“节点” 一般用来表示施加的数学操作,但也可以表示数据输入(feed in)的起点/输出(push out)的终点,或者是读取/写入持久变量(persistent variable)的终点。“线”表示“节点”之间的输入/输出关系。这些数据“线”可以输运“size 可动态调整”的多维数据数组,即“张量”(tensor)。张量从图中流过的直观图像是这个工具取名为“Tensorflow”的原因。一旦输入端的所有张量准备好,节点将被分配到各种计算设备完成异步并行地执行运算。
TensorFlow 中的所有计算都会被转化为计算图上的节点。计算图中的每个节点可以有任意多个输入和任意多个输出,每个节点描述了一种运算操作(operation, op),节点可以算作运算操作的实例化(instance)。计算图描述了数据的计算流程,它也负责维护和更新状态,用户可以对计算图的分支进行条件控制或循环操作。用户可以使用 pyton、C++、Go、Java 等语言设计计算图。tensorflow 通过计算图将所有的运算操作全部运行在 python 外面,比如通过 c++运行在 cpu 或通过 cuda 运行在 gpu 上,所以实际上 python 只是一种接口,真正的核心计算过程还是在底层采用 c++或 cuda 在 cpu 或 gpu 上运行。
一个 TensorFlow图描述了计算的过程. 为了进行计算, 图必须在会话(session)里被启动. 会话将图的op分发到诸如CPU或GPU之的备上, 同时提供执行op的方法. 这些方法执行后, 将产生的tensor返回. 在Python语言中, 返回的tensor是numpy ndarray对象; 在C和C++语言中, 返回的tensor是tensorflow::Tensor实例。
从上面的描述中我们可以看到,tensorflow的几个比较重要的概念:tensor, computation graphy, node, session。正如前面所说,整个操作就好像数据(tensor)在计算图(computation graphy)中沿着边(edge)流过(flow)一个个节点(node),然后通过会话(session)启动计算。所以简单来说,要完成这整个过程,我们需要的东西是要定义数据、计算图和计算图上的节点,以及启动计算的会话。所以在实际使用中我们要做的大部分工作应该就是定义这些内容了。
使用 Docker 安装 TensorFlow
上述我们已经说明了 Docker 的基本概念,阅读这步前确保你已经理解了创建一个容器的基本流程。本节的内容主要介绍启动一个包含了 TensorFlow 二进制映像的 Docker 容器。
要启动一个包含 TensorFlow 二进制映像的 Docker 容器,请输入以下格式的命令:
$ docker run -it -p hostPort:containerPort TensorFlowImage
其中:
- “-p hostPort:containerPort”为可选项。如果您想从 shell 运行 TensorFlow 程序,请省略此选项。如果您想从 Jupyter Notebook 运行 TensorFlow 程序,请将“hostPort”和“containerPort”设置为 8888。 如果您想在容器内部运行 TensorBoard,请再添加一个 -p 标志,并将“hostPort”和“containerPort”设置为 6006。
-
“TensorFlowImage”是必填项。它指示 Docker 容器。 您必须指定下列某一个值:
- gcr.io/tensorflow/tensorflow:TensorFlow 二进制映像。
- gcr.io/tensorflow/tensorflow:latest-devel:TensorFlow 二进制映像以及源代码。
gcr.io 是 Google Container Registry。dockerhub 上也提供了一些 TensorFlow 镜像。Docker 将在您第一次启动 TensorFlow 二进制映像时下载该镜像。
例如,以下命令会在一个 Docker 容器中启动一个 TensorFlow CPU 二进制映像,您可以通过该容器在 shell 中运行 TensorFlow 程序:
$ docker run -it gcr.io/tensorflow/tensorflow bash
然后从 shell 中调用 Python,如下所示:
$ python
在 Python 交互式 shell 中输入以下几行简短的程序代码:
# Python
import tensorflow as tf
hello = tf.constant('Hello, TensorFlow!')
sess = tf.Session()
print(sess.run(hello))
如果系统输出以下内容,则说明您可以开始编写 TensorFlow 程序了:
Hello, TensorFlow!
实际效果图如下:
以下命令也可在 Docker 容器中启动一个 TensorFlow CPU 二进制映像。但是,在这个 Docker 容器里,您可以在 Jupyter Notebook 中运行 TensorFlow 程序:
$ docker run -it -p 8888:8888 gcr.io/tensorflow/tensorflow
这里我通过浏览器在 Jupyter Notebook 中正常运行了上面的代码。
实际效果图如下:
参考
- Docker 入门教程
- Docker 实践系列文章
- 在 macOS 上安装 TensorFlow
写文章不容易,也许写这些代码就几分钟的事,写一篇大家好接受的文章或许需要几天的酝酿,然后加上几天的码字,累并快乐着。如果文章对您有帮助请我喝杯咖啡吧!