基于 Docker 搭建 TensorFlow 开发环境

转载需经本人同意且标注本文原始地址: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 总架构图

docker 系统使用了 C/S 的架构,docker client 通过 REST API 请求 docker daemon 来管理 docker 的镜像和容器等。Server 端驻守在后台,称之为 docker daemon。Client 端是一个 CLI 程序,可以在命令行中通过 docker 这个二进制文件进行交互。

安装 Docker

Docker 是一个开源的商业产品,有两个版本:社区版(Community Edition,缩写为 CE)和企业版(Enterprise Edition,缩写为 EE)。

安装完成后,运行下面的命令,验证是否安装成功。

$ 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 远程镜像仓库:

在安装环境的过程中,因为某个伟大的防火墙工程,我们需要的大部分资源都没法顺利的获取到,解决办法就是将下载的源换成国内某些厂商提供的镜像源。

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 <command>

ADD:将主机文件复制到容器中

ADD /path/to/sourcefile/in/host /path/to/targetfile/in/container

CMD:指定容器启动默认执行的命令

CMD ["executable","param1","param2"]

EXPOSE:指定容器在运行时监听的端口

EXPOSE <port>

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 机器智能研究部门)中的研究人员和工程师开发的,旨在用于进行机器学习和深度神经网络研究。但该系统具有很好的通用性,还可以应用于众多其他领域。

学习资料

什么是数据流图(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 中正常运行了上面的代码。

实际效果图如下:

参考


写文章不容易,也许写这些代码就几分钟的事,写一篇大家好接受的文章或许需要几天的酝酿,然后加上几天的码字,累并快乐着。如果文章对您有帮助请我喝杯咖啡吧!


匠心博客
看似寻常最奇崛,成如容易却艰辛。始终保持一颗匠心去铸造去创造。

看似寻常最奇崛,成如容易却艰辛。

4.6k 声望
1.5k 粉丝
0 条评论
推荐阅读
基于沙盒技术的企业移动应用安全平台设计
移动互联网的飞速发展, 改变了企业传统的业务模式, 提高了工作效率. 但同时也给企业的数据安全带来了巨大的挑战, 我们面对各种攻击的可能性会大 大增加, 面临潜在的风险:

匠心8阅读 5.4k

Docker学习:Image的本地存储结构
在使用Docker时候,针对镜像的操作一般就是docker pull,docker build,docker commit(刚开始接触Docker的时候,还不会Dockerfile,经常使用这个命令,但是经历了一次血的教训,已经放弃这个命令很久)这些操作...

backbp4阅读 9.7k评论 3

利用Docker部署管理LDAP及其初次使用
前言:本周主要写了gitlabWebhook转github的项目,总体上没有遇到什么大问题,这周接触到了LDAP,于是就花时间实际操作了解了一下。

李明5阅读 829

BI系统打包Docker镜像及部署的技术难度和实现
随着容器化技术盛行,Docker在前端领域也有着越来越广泛的应用;传统的前端部署方式需要我们将项目打包生成一系列的静态文件,然后上传到服务器,配置nginx文件;如果我们使用容器化部署,将部署操作都命令化,集...

葡萄城技术团队2阅读 8.3k

深入剖析容器网络和 iptables
Docker 能为我们提供很强大和灵活的网络能力,很大程度上要归功于与 iptables 的结合。在使用时,你可能没有太关注到 iptables 的作用,这是因为 Docker 已经帮我们自动完成了相关的配置。

张晋涛3阅读 1.2k

封面图
Kubernetes v1.26 新特性一览
我每期的 「k8s生态周报」都有一个叫上游进展的部分,所以很多值得关注的内容在之前的文章中已经发过了。这篇中我会再额外介绍一些之前未涵盖的,和之前介绍过的值得关注的内容。

张晋涛2阅读 573评论 1

封面图
Kubernetes 证书管理系列(一)
大家好,我是张晋涛。这是一个系列文章,将会通过七篇内容和大家一起聊聊 Kubernetes 中的证书管理。以下是内容概览:如上所示,在第一篇中,我们将从原理出发,来理解 Kubernetes 中的证书及其相关的作用,然后...

张晋涛2阅读 803

封面图

看似寻常最奇崛,成如容易却艰辛。

4.6k 声望
1.5k 粉丝
宣传栏