面向前端的 Docker 入门介绍 😎
Docker 场景
开始介绍前,我们先来思考一个小 🌰
你一个人前后端全包,写了一个 Web 项目,俗称 全干工程师
(这里手动@Doma 的小蓝晶项目)
当你要部署项目到服务器的时候,大家可以思考下有哪些事情是需要做的?
- 下载并安装配置
nginx
- 后端服务、数据库服务、负载均衡服务等
- ...其他配置
当你在一台服务器上成功部署并运行后,
老板来了句,把这个项目再部署到其他几台服务器上?你该怎么做?
这时候 Docker 的作用就显而易见
接下来我们通过一个最小化的前端 + 后端 + 数据库的 Web 项目,来体验下 Docker 部署的便捷与高效。
$ git clone https://github.com/superman66/docker-demo
$ cd docker-demo
$ yarn
// 这里省去安装构建步骤...
$ docker-compose up -d
Docker-compose 配置文件
通过这么一个文件,我们就能搞定一个 Web 项目的部署,而无需去手动安装管理各个服务。
version: '3'
services:
web:
# 开机总是启动
restart: always
image: nginx
# 绑定端口
ports:
- '8084:80'
# 挂载本地文件夹到容器内部文件夹
volumes:
- ./app/build:/usr/share/nginx/html/web_app
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
server:
container_name: server
build: ./server
ports:
- '3200:3200'
links:
- mongo
depends_on:
- mongo
mongo:
container_name: mongo
image: mongo
expose:
- '27017'
ports:
- '27017:27017'
volumes:
- './db:/data/db'
Docker 介绍
Docker 是什么
Docker 最早是 dotCloud 公司的一个内部项目,并在 2013 年 3 月以 Apache 2.0 授权协议开源。后来还加入了 Linux 基金会,并成立推动开放容器联盟(OCI)。
Docker 使用 Go 开发,基于 Linux 内核的 cgroup,namespace,以及 AUFS 类的 Union FS 等技术,
对进行进行封装隔离,属于操作系统层面的虚拟化技术 。由于隔离的进程独立于宿主和其他的隔离的进程 ,因此也称其为容器。
Docker 架构图

Docker 与传统虚拟机技术
前面说到 Docker 是属于操作系统层面的虚拟化技术。那么它跟传统虚拟机技术有何不同?
传统虚拟机是虚拟出一套硬件后,在其上面运行一个完整的操作系统,然后在该系统上运行所需的应用进程。

容器内的应用进程则是直接运行于宿主的内核,容器自己本身没有内核,也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便。

Docker 的优势
更高效的利用系统资源
Docker 无需硬件模拟、运行完整操作系统,开销小,系统利用率高。相同配置的主机,可以运行更多数量的应用。
更快速的启动时间
传统虚拟机启动服务时间往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整操作系统。
因此可以做到秒级、甚至毫秒级的启动时间。
一致的运行环境
开放过程中一个常见的问题是环境一致性的问题。Docker 提供了除内核外完整的运行时环境,确保了应用运行环境一致性。
代码在我本地是好的 这句话将成历史。
持续交付和部署
Docker 可以通过 Dockerfile
定制应用镜像来实现持续集成、持续交付、部署。
更轻松的迁移
由于 Docker 确保了执行环境的一致性,使得应用的迁移更加容易。而且 Docker 可以在很多平台运行,物理机、虚拟机、公有云、私有云等,其运行结果都是一样的。
更轻松的维护和拓展
Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。
此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。
Docker 基本概念
Docker 三个基本的概念
- 镜像 image
- 容器 container
- Docker Registry
理解了这三个概念后,再学一些基本的操作命令,你就可以为项目写 Dockerfile 了。
镜像 Image
镜像是一个特殊的文件系统。除了提供容器程序运行时所需要的程序、库、资源、配置等文件外,
还包含了一些为运行时准备的一些配置参数(如数据卷、环境变量、用户名等)。
镜像不包含任何动态数据,镜像内容在构建后不会被改变。
容器 Container
镜像和容器的关系,就像是类和实例的关系。镜像是静态的定义,容器则是镜像运行时的实体。
容器是有生命周期的。可以被创建、启动、停止、删除、暂停。
容器的实质其实是进程。但与直接在宿主环境执行的进程不同,容器进程运行与属于自己的独立的命名空间。
因此容器可以拥有自己的 root 文件系统、自己的网络配置,自己的进程空间、甚至自己的用户 ID 空间。
前面说到镜像是不包含动态数据的。所有的数据都应该在容器上进行操作,利用数据卷、或者绑定宿主目录。
Registry
Docker Registry 就是类似 Github 的一个服务,你可以把你在本地生成的镜像推送上去。这样就可以在其他服务器直接该镜像进行使用,也可以把镜像公开,让别人也使用。
一个 Docker Registry 中可以包含多个 仓库 (Repository);每个仓库可以包含多个标签 Tag;每个标签对应一个镜像。
每个镜像可以用 tag 来指定具体版本的镜像。不打 tag
则默认 latest
同样的,Docker 官方提供了私有 Registry 服务,也可以搭建私有 Registry 服务。
镜像加速
跟 npm 一样,有时候也会存在国外 Docker 镜像服务比较慢,所以催生了一批过来镜像服务。
比如 DaoCloud 镜像市场、阿里云镜像库等。
Docker 基本操作
镜像基本操作
拉取镜像
docker pull nginx
也可以指定 tag
docker pull nginx:latest
列出镜像
docker image ls [option]
删除本地镜像
docker image rm [options]
容器基本操作
前面说过容器是有生命周期的,可以被
- 创建
- 启动
- 停止
- 删除
启动容器
启动容器有两种方式,一种是基于镜像新建一个容器并启动。另外一种是将处于终止状态下的容器重新启动。
由于 Docker 实在是太轻量级了,所以大部分情况都是随用随创建,不用就删掉。
docker run nginx-demo -P
run 有一些常用的参数:
-P
参数表示随机端口映射。用于映射容器内部端口到主机的端口-p
指定端口映射。格式为:主机端口:容器端口
。比如8083:80
。-d
后台运行容器。--name
指定容器名称。
当使用 docker run
命令去创建容器时,Docker 后台运行的操作是:
- 检查本地是否存在指定的镜像,不存在就从公有仓库下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个ip地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
查看容器
docker ps // 查看当前运行的容器
docker ps -a // 查看所有容器,包含已终止的容器
终止容器
docker contaier stop containerid
启动容器
可以对已经终止的容器进行重新启动
docker container start containerid
删除容器
docker container [name] rm [-f]
上面是删除单个容器,也可以删除全部处于终止状态的容器
docker container prune
又是一个例子
讲了上面的一些基本操作,我们就可以来看一个简单的 nginx 例子。
起一个 名为 mynginx 的 nginx 容器
docker run --name mynginx -d -P nginx
这就是最简单的一个例子了。
那问题来了?如果我们要更改 nginx 的配置呢?
这就要说到 Docker 数据管理了。
Docker 数据管理
- 数据卷(volumes)
- 挂载主机目录
数据卷
数据卷是一个可供一个或者多个容器使用的特殊目录。他具有以下的特征
- 在容器之间共享和重用
- 对数据卷的修改会立马生效
- 对数据卷的更新不会影响容器
- 数据卷默认一直存在,即使容器被删除。
数据卷操作
创建数据卷
docker volume create my-local-vol // 创建数据卷
docker volume ls // 查看所有数据卷
查看数据卷
docker volume inspect my-local-vol
创建完数据卷后,就可以在启动容器的时候使用 --mount
或者 --volume
将其挂载到容器里。
以前面 nginx 为例:
docker run --name mynginx -d -P --mount source=my-local-vol,target=/test nginx
挂载主机目录\文件
无需创建数据卷,直接将本地主机的目录或者文件挂载到容器里。
docker run --name mynginx -d -P --mount type=bind,source=/path/xxx/nginx.conf,target=/etc/nginx/conf.d/default.conf nginx
上面命令就会启动一个挂载了本地 nginx.conf 配置文件的 nginx 容器。
Dockerfile
Dockerfile 是什么
前面说到镜像可以从 Docker Hub 中拉取。还有一种方式也可以构建镜像:Dockerfile。
Dockerfile 就是一个文本文件,包含了一条条的指令,用于描述如何构建镜像。
Dockerfile 实例
下面是我之前写的一个 node 小服务的 Dockerfile: wakatime-sync Dockerfile
FROM node:carbon
COPY . /wakatime-sync
WORKDIR /wakatime-sync
RUN npm install \
&& npm install pm2 -g \
&& npm run build
CMD ["pm2-runtime", "start", "pm2.json"]
FROM
用于指定基础镜像COPY source target
复制文件。将当前目录所有文件复制到 容器内/wakatime-sync
这个目录。如果目标路径不存在,会自动创建WORKDIR
指定工作目录,指的就是当前目录。如果当前目录不存在,会自动创建。RUN
用于执行命令行命令。这里我们用于执行 npm 命令。CMD
用于启动容器的命令。前面说过,容器是进程。既然是进程,就需要指定所运行的程序及参数。这里是指定 pm2 作为容器的运行程序
通过这么一个 Dockerfile,我就可以构建该node服务的镜像,并上传到 Docker Hub。
以后需要部署该服务,只需要去Docker hub 拉取该镜像,直接启动容器即可,无需关心任何的环境配置问题。
Dockerfile 其他指令
ADD 高级的复制文件
ENV 设置环境变量
VOLUME 定义数据卷
EXPOSE 暴露端口
CMD 容器启动命令
WORKDIR 指定工作目录
Dockerfile 最佳实践
更多 Dockerfile 用法可以查阅官方提供的 Dockerfile最佳实践
Docker Compose
Docker Compose
Docker Compose 属于 Docker 三剑客中的一个。负责实现对 Docker 容器集群快速编排。
就像一开始说的最小化 Web 项目,用的就是 Docker Compose 进行编排的。
一个项目通常是需要多个容器相互配合。比如Web项目,除了需要 nginx 容器本身,还需要后端运行的容器、数据库容器、甚至是负载均衡容器。
如果不通过 Docker Compose 的话,你就需要手动去管理各个容器之间的关联,这样不高效也不方便。
Compose 恰好满足这样的需求,它允许用户通过一个单独的 docker-compose.yml
模板文件来定义一组相关联的应用容器为一个项目(project).
Docker Compose 最重要的两个概念
- 服务 service:一个应用的容器。比如一个 nginx 容器就是一个 service
- 项目 project:由一组关联的应用容器组成的一个完整业务单元。比如前面演示的那个项目,就是属于 Project,下面掌管着多个 service,nginx service,node service, mongodb servce。
The End
Enjoy 😄
- Docker-demo: https://github.com/superman66/docker-demo
- Slides: https://github.com/superman66/docker-slides