善假于物之 Docker 与 前端 ( 二 ) 四大件与基础操作

560 阅读5分钟

0. 造冰箱

工欲善其事必先利其器 , 在把大象装进冰箱 , 我们得先有个冰箱

相信大家都是用的 Windows 或 Mac 进行开发的 , 直接使用官方提供工具即可

Mac

  • 官网下载安装 stable edge

  • homebrew

    $ brew cask install docker
    

Windows

官网地址

stable edge

1. 基本概念

我们先从一个日常中会使用到的命令开始

首先我们在任意一个目录下新建一个文件 。

$ echo "Hello, Docker~" > index.html

启动容器

$ docker run --rm -p 8080:80 -v $(PWD):/usr/share/nginx/html nginx:latest

打开浏览器访问 localhost:8080 , 我们可以访问到我们刚才新建的文件 。 返回终端 , 我们可以看到已经输出了一些日志 。

Safari

至此 , 我们已经完成了最简单的 nginx 服务的启动 。

我们来拆解一下这个命令

$ docker run 
			--rm
			-p 8080:80
			-v $(PWD):/usr/share/nginx/html
			nginx:latest
  1. --rm

    没啥好讲的 , 如其意思 , 在容器停止后删除该容器 , 由于容器轻量级的设计 , 我们一般随用随开 , 随停随删 。 原因上一篇有讲 , Docker 启动一个新容器的速度是极快的 , 我们每次都从新跑一个新容器并不会造成太大的时间成本 , 也能保证每次的容器都是全新的一致的 。

  2. -p 8080:80

    这里涉及 Docker 中网络的概念 。 在这里是将宿主机 8080 端口映射到容器中 80 端口 。 因此 , 我们可以通过宿主机的 8080 端口 访问到容器 80 端口 。

  3. -v $(PWD):/usr/share/nginx/html

    这里涉及 Docker 中挂载的概念 。 PWD 命令是将当前目录输出 。 在这里就是将当前目录挂载到镜像中的 /usr/share/nginx/html 目录 。 该目录是 nginx 镜像指定的静态资源目录 。因此 , 我们可以通过 nginx 容器提供的服务访问到当前目录下的文件 , 如我们刚才新建的 index.html

  4. nginx:latest

    所使用的镜像名称 。

在上面使用的命令中 , 我们就已经接触到了 Docker 四大件 : 镜像 、 容器 、 数据卷 、 网络 。

接下来一一简介。

镜像

镜像是容器的基础 , 描述了运行环境文件系统的内容

命名规范

Group 3

  • username: 用于识别上传镜像的用户身份 。 Docker 官方维护项目中没有 username。
  • repository:用于识别项目名 。
  • tag:表示镜像的版本 , 方便进行版本或内容的区分 。 latest 为缺省tag 。

组成

镜像为一个只读的文件包,其中包含了虚拟环境运行最原始文件系统的内容。 Docker 中的镜像与虚拟机中的镜像是不一样的。由于 Docker 使用了 UnionFS , Docker 的进行是一种增量式的结构。

Group 3

基于该结构 , 每次对镜像内容的修改 , Docker都会将这些修改形成一个新的镜像层 , 而一个镜像就是由其下层所有镜像层所组成 。 这种结构允许了不同镜像之间共享镜像层 。 由此 , 我们可以在一台机器中存放许多镜像 。

Docker File Layer

网络

在 Docker 中,实现了强大的网络功能。我们可以对单个容器进行网络配置,还可以在容器间建立虚拟网络。与其他网络环境隔离。我们甚至能够在不同的物理服务器间实现,让处在两台物理服务器上的两个 Docker 所提供的容器,加入到同一个虚拟网络中,形成完全屏蔽硬件的效果。

容器网络模型

我们使用 Docker 时最常用的是其提供的端口映射功能 。 可将容器中 , 容器中指定端口映射到宿主机中 。

-p <ip>:<host-port>:<container-port>
$ docker run --rm -p 8080:80 -v $(PWD):/usr/share/nginx/html nginx:latest

如最顶部简单所示 , 将容器中端口映射到宿主机以提供服务 。 详细网络部分见后续技术详解部分 。

数据卷

由于容器的特性 , 在容器中操作的文件无法被持久化 。 且容器与宿主机环境隔离 , 无法直接操作容器外的文件 。 同理 , 宿主机也无法操作容器中的文件 。

在我们需要保持一些数据的时候 , 我们通常会单独挂载一个文件系统来存放数据 。 由于 Docker 采用了 UnionFS 文件系统 , 可以将不同物理位置的目录合并 Mount 到同一目录中 , 因此轻松的实现了容器的挂载功能 , 在 UnionFS 的加持下 , 除了能够从宿主操作系统中挂载目录外 , 还能够建立独立的数据卷持久存放数据且在容器间共享 。

一些云计算商也会提供花样众多的定制存储驱动 , 如阿里云可将 OSS 挂载到容器中 。

挂载方式

如上图所示 , Docker 中提供了以下三种挂载方式 。

  • BindMount

    直接将外部路径挂载到容器中 。 容器内外读写互相可见

  • Volume

    与 BindMount 相同 , 区别在于挂载目录交由 Docker 管理

  • TmpfsMount

    挂载内存中的一部分到容器中 。 存储不是持久的 , 内容随着容器停止而消失

实际使用中 , 我们常用的为 BindMount 、 Volume 。

BindMount

通过 BindMount 的方式 , 我们可以直接将外部路径挂载到容器中 。

在创建运行容器时 , 加入 -v--volume 选项 。

-v <host-path>:<container-path>
--volume <host-path>:<container-path>
# host-path 为宿主机路径
# container-path 为容器路径

如下所示 , 我们就将宿主机 /opt/nginx/html 目录挂载到容器 /usr/share/nginx/html 目录中了

$ docker run -d --name frontend -v /opt/nginx/html:/usr/share/nginx/html nginx

此时在容器中对该目录的操作都会反应在宿主机中

如果需要控制容器对文件只读 , 我们可以在后面加上 :ro 可以只读方式挂载

-v <host-path>:<container-path>:ro
--volume <host-path>:<container-path>:ro

如下所示

$ docker run -d --name frontend -v /opt/nginx/html:/usr/share/nginx/html:ro nginx

需要注意的是 , 在挂载时我们必须使用绝对路径

我们也可以不挂载目录 , 只挂载单个文件到容器中 , 如下所示

$ docker run -d --name frontend -v /opt/nginx/html/index.html:/usr/share/nginx/html/index.html:ro nginx

场景

部署时 , 共享静态 HTML 文件 、 一些必要的配置文件 。

借助 Docker 开发时 , 我们会将源码放置在外部 , 通过挂载的方式供容器使用 , 这样我们只需要在容器外修改代码即可

Volume

该挂载方式与 BindMount 相同 , 区别在于挂载目录交由 Docker 管理了 。

在创建运行容器时 , 加入 -v--volume 选项 。

-v [<volume-name>:]<container-path>
--volume [<volume-name>:]<container-path>
# volume-name 为数据卷别名
# container-path 为容器路径

不带别名创建

$ docker run -d --name frontend -v /usr/share/nginx/html nginx

带别名创建

$ docker run -d --name frontend -v html:/usr/share/nginx/html nginx

需要注意的是 , 该别名在 Docker 中是唯一的 , 如果之前已经存在 , 则会直接使用该数据卷 , 不存在则创建

之所以此前 BindMount 方式不允许使用相对路径 , 就是因为会出现与数据卷使用方式冲突 。

场景

多容器共享数据卷

对容器内容进行管理时

可以对存储介质的使用隐藏更多细节 , 如阿里云 OSS 挂载

###TmpfsMount

挂载内存中的一部分到容器中 。

在创建运行容器时 , 加入 --tmpfs 选项 。

--tmpfs <container-path>
# container-path 为容器路径

如下所示 , 我们就将内存中的一部分挂载到容器 /usr/share/nginx/html 目录中了

$ docker run -d --name frontend --tmpfs /usr/share/nginx/html nginx

由于是在内容中挂载 , 所以我们不需要指定具体位置 , 只传递挂载在容器内部的路径即可 。

需要注意的是 , 由于容器及内存的特性 , 该挂载的临时文件路径并不能持久化存储 。

场景

读写速度要求高 、 数据变化量大 、 不需要持久保存的数据

--mount 选项

Docker 还提供了一个较丰富且明确的挂载选项 , --mount 选项

--mount 'type=volume,src=<VOLUME-NAME>,dst=<CONTAINER-PATH>,volume-driver=local,volume-opt=type=nfs,volume-opt=device=<nfs-server>:<nfs-path>,"volume-opt=o=addr=<nfs-address>,vers=4,soft,timeo=180,bg,tcp,rw"'

--mount 相对复杂 , 一般来说 -v--volume``` 选项已经足够满足我们的需求 。

Docker --mount 选项文档

容器

在容器技术中,容器就是用来隔离虚拟环境的基础设施,而在 Docker 里,它也被引申为隔离出来的虚拟环境。

主进程

在 Docker 中 , 容器会存在一个 PID 为 1 的主进程 。 容器的启动可以理解为该进程的启动 。

当我们控制容器结束时 , Docker 会向主进程发送结束信号 , 通知程序退出后关闭容器 。

当容器中主进程主动关闭时 , 容器也会随之停止 。

生命周期

容器生命周期

2. 基础操作

了解完一些基本概念后 , 我们来了解一下基本操作 。

  • docker images 查看镜像列表

    $ docker images
    REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
    node                latest              a9c1445cbd52        6 months ago        904MB
    
  • docker pull 拉取镜像

    $ docker pull nginx
    Using default tag: latest
    latest: Pulling from library/nginx
    b8f262c62ec6: Pull complete
    e9218e8f93b1: Pull complete
    7acba7289aa3: Pull complete
    Digest: sha256:aeded0f2a861747f43a01cf1018cf9efe2bdd02afd57d2b11fcc7fcadc16ccd1
    Status: Downloaded newer image for nginx:latest
    
  • docker search 搜索镜像

    $ docker search nginx
    NAME                 DESCRIPTION                                 STARS   OFFICIAL   AUTOMATED
    nginx                Official build of Nginx.                    12055   [OK]
    jwilder/nginx-proxy  Automated Nginx reverse proxy for docker…   1672               [OK]
    
  • docker inspect 查看镜像详情

    $ docker inspect nginx
    
  • docker rmi 移除镜像

    $ docker rmi nginx
    Untagged: nginx:latest
    Untagged: nginx@sha256:aeded0f2a861747f43a01cf1018cf9efe2bdd02afd57d2b11fcc7fcadc16ccd1
    Deleted: sha256:f949e7d76d63befffc8eec2cbf8a6f509780f96fb3bacbdc24068d594a77f043
    Deleted: sha256:301c5d89cad94a6a99703841b021cf7df4326d2f14715c52b4b27893b13e02c0
    Deleted: sha256:c3172409dcf95530cce7aad6a4c16a476fec9c43ac38426e9487b43efd246357
    Deleted: sha256:2db44bce66cde56fca25aeeb7d09dc924b748e3adfe58c9cc3eb2bd2f68a1b68
    
    # 支持删除多个, 空格分割
    $ docker rmi nginx mysql
    
  • docker create 创建容器

    $ docker create --name nginx8080 nginx
    d57619691f65a593eb4610d134f7fb4780907733d91947322bbc022c2320d98e
    
  • docker start 启动容器

    $ docker start nginx8080
    nginx8080
    
  • docker run 创建并启动容器

    $ docker run --name nginx8080 nginx
    

    默认情况下通过 docker run 会采用前台运行的方式 , 可以加入 -d--detach 选项使其在启动后后台运行 。

    $ docker run --name nginx8080 -d nginx
    09bb5968bccd2f1c955cafef9abd275a721296228150d72c52d69b4d68e4b8f1
    
  • docker ps 罗列容器

    $ docker ps
    CONTAINER ID   IMAGE   COMMAND        CREATED          STATUS          PORTS     NAMES
    09bb5968bccd   nginx   "nginx -g '…"  15 seconds ago   Up 14 seconds   80/tcp    nginx8080
    

    默认情况下通过 docker ps 只会显示运行中的容器 , 可以加入 -a--all 选项使其列出所有状态的容器 。

    $ docker ps -a
    CONTAINER ID IMAGE        COMMAND     CREATED       STATUS          PORTS    NAMES
    09bb5968bccd nginx        "nginx -g…" 4 minutes ago Up 4 minutes    80/tcp   nginx8080
    0200ce4aa200 fe82f8db1d8b "yarn run…" 6 months ago  Exited (1) 6 months ago  goofy_dubinsky
    
  • docker stop 暂停容器

    $ docker stop nginx8080
    nginx8080
    
  • docker rm 删除容器

    $ docker rm nginx8080
    nginx8080
    

    默认情况下通过 docker rm 无法删除运行中的容器 。 可以加入 -f--force 选项将其强制停止并删除 。

  • docker exec 执行命令

    $ docker exec nginx8080 cat /etc/hosts
    127.0.0.1	localhost
    ::1	localhost ip6-localhost ip6-loopback
    fe00::0	ip6-localnet
    ff00::0	ip6-mcastprefix
    ff02::1	ip6-allnodes
    ff02::2	ip6-allrouters
    172.17.0.2	58a33449f761
    

    一般我们会通过直接执行容器中的控制台来不间断输入命令

    $ docker exec -it nginx8080 bash
    root@58a33449f761:/# ls
    bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var
    
    $ docker exec -it nginx8080 sh
    # ls
    bin  boot  dev	etc  home  lib	lib64  media  mnt  opt	proc  root  run  sbin  srv  sys  tmp  usr  var
    

    -i--interactive ) 表示保持输入流 。

    -t--tty ) 表示启用一个伪终端 。 使用他形成与 bash 的交互 。

  • docker attach 连接到容器

    $ docker attach nginx8080
    

    该命令将当前控制台连接到容器的主程序上 。

通过上面的介绍 , 我们会对 Docker 有一些基本的了解 , 我们不用全部记住 。 这篇文章只是将容器中的基本操作 、 技术及其部分原理概括 。 在后续实战文章中 , 我们会一一介绍回顾 。

下一文 , 我们来了解一下容器网络部分内容 , 回见 。

君子生非异也,善假于物也。

​ 《荀子·劝学》