首页 > 科技 > 全栈程序员之路:前端工程师如何从零开始了解Docker

全栈程序员之路:前端工程师如何从零开始了解Docker

Docker 自开源以来受到了各大公司的广泛关注,或许现在互联网公司的运维体系不承载在 Docker(或 Pouch 等)之上都不好意思说自己的互联网公司。本文会简单介绍下 Docker 的基础概念,入门级使用方式和一些使用 Docker 能大大提升效率的场景。

环境配置的难题

软件开发最大的麻烦事之一,就是环境配置。用户计算机的环境都不相同,你怎么知道自家的软件,能在那些机器跑起来?

用户必须保证两件事:操作系统的设置,各种库和组件的安装。只有它们都正确,软件才能运行。举例来说,安装一个 Python 应用,计算机必须有 Python 引擎,还必须有各种依赖,可能还要配置环境变量。

如果某些老旧的模块与当前环境不兼容,那就麻烦了。开发者常常会说:"它在我的机器可以跑了"(It works on my machine),言下之意就是,其他机器很可能跑不了。

环境配置如此麻烦,换一台机器,就要重来一次,旷日费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样地复制过来。

虚拟机

虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。

虽然用户可以通过虚拟机还原软件的原始环境。但是,这个方案有几个缺点。

(1)资源占用多

虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。

(2)冗余步骤多

虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。

(3)启动慢

启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。

Linux 容器

由于虚拟机存在这些缺点,Linux 发展出了另一种虚拟化技术:Linux 容器(Linux Containers,缩写为 LXC)。

Linux 容器不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。

由于容器是进程级别的,相比虚拟机有很多优势。

(1)启动快

容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。

(2)资源占用少

容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。

(3)体积小

容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。

总之,容器有点像轻量级的虚拟机,能够提供虚拟化的环境,但是成本开销小得多。

四、Docker 是什么?

Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口。它是目前最流行的 Linux 容器解决方案。

Docker 将应用程序与该程序的依赖,打包在一个文件里面。运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。有了 Docker,就不用担心环境问题。

总体来说,Docker 的接口相当简单,用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。

DOCKER原理

对 Docker 最简单并且带有一定错误的认知就是 “Docker 是一种性能非常好的虚拟机”。

正如上面所说,这是有一定错误的说法。Docker 相比于传统虚拟机的技术来说先进了不少,具体表现在 Docker 不是在宿主机上虚拟出一套硬件后再虚拟出一个操作系统,而是让 Docker 容器里面的进程直接运行在宿主机上(Docker 会做文件、网络等的隔离),这样一来 Docker 会 “体积更轻、跑的更快、同宿主机下可创建的个数更多”。

Docker 中有三个核心概念:Image、Container、Repository

  • Image: 有领“好人卡”倾向的广大程序猿一定对 镜像 的概念不会陌生。但和 windows 的那种 iso 镜像相比,Docker 中的镜像是分层的,可复用的,而非简单的一堆文件迭在一起(类似于一个压缩包的源码和一个 git 仓库的区别)。

Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。

image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成。举例来说,你可以在 Ubuntu 的 image 基础上,往里面加入 Apache 服务器,形成你的 image。

image 文件是通用的,一台机器的 image 文件拷贝到另一台机器,照样可以使用。一般来说,为了节省时间,我们应该尽量使用别人制作好的 image 文件,而不是自己制作。即使要定制,也应该基于别人的 image 文件进行加工,而不是从零开始制作。

为了方便共享,image 文件制作完成后,可以上传到网上的仓库。Docker 的官方仓库 Docker Hub 是最重要、最常用的 image 仓库。此外,出售自己制作的 image 文件也是可以的。

  • Container: 容器的存在离不开镜像的支持,他是镜像运行时的一个载体(类似于实例和类的关系)。依托 Docker 的虚拟化技术,给容器创建了独立的端口、进程、文件等“空间”,Container 就是一个与宿机隔离 “容器”。容器可宿主机之间可以进行 port、volumes、network 等的通信。

image 文件生成的容器实例,本身也是一个文件,称为容器文件。也就是说,一旦容器生成,就会同时存在两个文件: image 文件和容器文件。而且关闭容器并不会删除容器文件,只是容器停止运行而已。

  • Repository: Docker 的仓库和 git 的仓库比较相似,拥有仓库名、tag。在本地构建完镜像之后,即可通过仓库进行镜像的分发。常用的 Docker hub 有 https://hub.docker.com/ 、 https://cr.console.aliyun.com/ 等。

相关命令

1. 安装

Docker 的安装是非常便捷的,在 macOS、ubuntu 等下面都有一键式安装工具或者脚本。更多可以参考 Docker 官方教程(http://www.docker.org.cn/)。

安装后 Terminal 中敲下 docker,有使用说明出来的话大多情况下说明已经安装成功了。

2. 寻找基础镜像

DockerHub 等网站都提供了众多镜像,一般情况下我们都会从它那找个镜像作为基础镜像,然后再进行我们的后续操作。

这里我们以 ubuntu 基础镜像为例,配置一个 node 环境。

因为 “链路太长” 的原因,国内访问 Docker Hub 可能会比较慢,可以使用国内众多厂商提供的镜像加速器

3. 拉取基础镜像

利用 docker pull 命令即可从相关 hub 网站上拉取镜像到本地。同时在拉的过程中就能看到是按照多个 “层” 去拉镜像的。

> docker pull ubuntu:18.04
18.04: Pulling from library/ubuntu
c448d9b1e62f: Pull complete
0277fe36251d: Pull complete
6591defe1cd9: Pull complete
2c321da2a3ae: Pull complete
08d8a7c0ac3c: Pull complete
Digest: sha256:2152a8e6c0d13634c14aef08b6cc74cbc0ad10e4293e53d2118550a52f3064d1
Status: Downloaded newer image for ubuntu:18.04

执行 docker images 即可看到本地所有的镜像

> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 18.04 58c12a55082a 44 hours ago 79MB

4. 创建一个 Docker 容器

docker create 命令通过镜像去创建一个容器,同时吐出容器 id。

> docker create --name ubuntuContainer ubuntu:18.04
0da83bc6515ea1df100c32cccaddc070199b72263663437b8fe424aadccf4778

用 docker start 即可运行改容器。

> docker start ubuntuContainer

用 docker ps 即可查看运行中的 container

> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9298a27262da ubuntu:18.04 "/bin/bash" 4 minutes ago Up About a minute ubuntuContainer

用 docker exec 即可进入该 container。

> docker exec -it 9298
root@9298a27262da:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@9298a27262da:/# exit

用 docker run 可以一步到位创建并运行一个容器,然后进入该容器。

> docker run -it --name runUbuntuContainer ubuntu:18.04 /bin/bash
root@57cdd61d4383:/# ls
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
root@57cdd61d4383:/#
# docker ps 可以查到已经成功运行了 runUbuntuContainer
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
57cdd61d4383 ubuntu:18.04 "/bin/bash" 9 seconds ago Up 8 seconds runUbuntuContainer
9298a27262da ubuntu:18.04 "/bin/bash" 9 minutes ago Up 6 minutes ubuntuContainer

5. 在容器里安装 Node 环境

进入容器之后一切操作和普通环境一致,我们安装个简单的 node 环境

> apt-get update
> apt-get install wget
> wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash
# 安装完之后可能当前 session 读不到 nvm 命令,可以 exit 之后再进入中终端环境
> nvm install 8.0.0
> node -v

6. commit 容器,创建新镜像

和 Ghost 装 windows 一样,很多时候,我们期望能定制自己的镜像,在里面安装一些基础环境(比如上文中的 node),然后制作出自己要的基础镜像。这个时候 docker commit 就派上用场了。

> docker commit --author "rccoder" --message "curl+node" 9298 rccoder/myworkspace:v1
sha256:68e83119eefa0bfdc8e523ab4d16c8cf76770dbb08bad1e32af1c872735e6f71
# 通过 docker images 就能看到新制作的 rccoder/myworkspace 就躺在这里了
>docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
rccoder/myworkspace v1 e0d73563fae8 20 seconds ago 196MB

接着,试一下我们新创建的镜像?

> docker run -it --name newWorkSpace rccoder/myworkspace:v1 /bin/bash
root@9109f6985735:/# node -v
8.0.0

看起来没问题。

7. push 镜像到 docker hub

镜像制作好了,怎么共享出去让别人使用呢?这里以 push 到 docker hub 为例。

第一步是先去 docker hub 注册一个账号,然后在终端上登录账号,进行 push。

> docker login
> docker push rccoder/myworkspace:v1
The push refers to repository [docker.io/rccoder/myworkspace]
c0913fec0e19: Pushing [=> ] 2.783MB/116.7MB
bb1eed35aacf: Mounted from library/ubuntu
5fc1dce434ba: Mounted from library/ubuntu
c4f90a44515b: Mounted from library/ubuntu
a792400561d8: Mounted from library/ubuntu
6a4e481d02df: Waiting
复制代码

8. 是时候使用 Dockerfile 了

用 Docker 进行持续集成?相比在了解 Docker 之前肯定听过这个事情,那就意外着需要从某个地方拷贝代码,然后执行(对,听上去有点 travis-ci 的那种感觉)。

是时候该 Dockerfile 出场了!

Dockerfile 是一个由一堆命令+参数构成的脚本,使用 docker build 即可执行脚本构建镜像,自动的去做一些事(同类似于travis-ci 中的 .travis.yml)。

Dockerfile 的格式统统为:

# Comment
INSTRUCTION arguments

必须以 FROM BASE_IMAGE 开头指定基础镜像。

更详细的规范与说明请参考 Dockerfile reference。这里我们以基于上面的 rccoder/myworkspace:v1 作为基础镜像,然后在根目录创建 a 目录为例

Dockerfile 如下:

FROM rccoder/myworkspace:v1
RUN mkdir a

然后执行:

> docker build -t newfiledocker:v1 .
Sending build context to Docker daemon 3.584kB
Step 1/2 : FROM rccoder/myworkspace:v1
---> 68e83119eefa
Step 2/2 : RUN mkdir a
---> Running in 1127aff5fbd3
Removing intermediate container 1127aff5fbd3
---> 25a8a5418af0
Successfully built 25a8a5418af0
Successfully tagged newfiledocker:v1
# 新建基于 newfiledocker 的容器并在终端中打开,发现里面已经有 a 文件夹了。
> docker docker run -it newfiledocker:v1 /bin/bash
root@e3bd8ca19ffc:/# ls
a bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var

借助 Dockerfile 的能力,Docker 留下了无限的可能。

能做什么

说了这么一堆,那实际生产环境中 Docker 能做什么呢?常用的可能有下面这些(欢迎在评论中补充)

1. 多环境的部署切换

业务开发中往往需要区分开发环境与线上环境,利用 Docker 能原封不动的将开发环境中的 代码与环境原封不动无污染的 迁移到线上环境,配合一定的自动化流程即可实现自动的发布。

2. 前端云构建

因为 node_modules 的蛋疼问题,同一个仓库下不同人开发往往会遇到不同的人使用不同的 包版本 且自己根本不知道与别人不一样,最终导致发布之后产生线上问题。利用 Docker 可以在云端新建容器,远程 无污染、低成本 构建代码,实现 不同人用的一定是同一个版本

题外:为什么我不使用 shrinkwrap(lock)

3. 复杂环境一键配置

某些场景下可能会配一些超级复杂的环境(比如:大一同学配 Java 环境),这个时候可以利用 Docker 对环境配置做封装,直接生成镜像,让大家低成本使用。

4. 持续集成单元测试

类似于 travis-ci 这种

5. 同应用多版本隔离、文件隔离

比如这个项目依赖 node6,那个项目依赖 node 8(只是举例子,硬盘够大的话还是建议通过 nodeinstall 解决);同一台服务器上跑了 100 个 wordpress 程序(可以用 Docker 建立隔离开,防止互相污染)。

4. 省钱

嗯,低成本安全超售(大雾)

本文来自投稿,不代表本人立场,如若转载,请注明出处:http://www.souzhinan.com/kj/229467.html