认识docker
docker(github 地址), 是一个开放源代码软件项目,让应用程序部署在“软件货柜下”的工作可以自动化进行,借此在Linux操作系统上,提供一个额外的软件抽象层,以及操作系统层虚拟化的自动管理机制。 Docker利用
Linux
核心中的资源分离机制,例如cgroups
,以及Linux
核心名字空间,来创建独立的容器。
虚拟机 VS docker
虚拟化技术大家比较熟悉的是虚拟机
所谓虚拟机,通常来说就是通过一个虚拟机监视器 ( Virtual Machine Monitor ) 的设施来隔离操作系统与硬件或者应用程序和操作系统,以此达到虚拟化的目的。
但是虚拟机有一个最大的弊端,就是性能低下。可以看以下这张图:
docker
的产生,为开发,测试,运维等带来了巨大额便利,不仅如此,由于当下是云计算时代,应用的开发也逐渐趋向服务化甚至微服务化,docker
应用更加广泛。
口说无凭,可以看一下docker 官方对 Docker 在工作上带来的提升做了调查研究,分别从工作效率的提升和技术设计投入的减少等方面数据化了 Docker 所做出的突出贡献:
docker 的核心技术
docker 的核心技术实现归结于此三大技术:
- NameSpace: 命名空间是 Linux 核心在 2.4 版本后逐渐引入的一项用于运行隔离的模块. 以进程为例,通过 PID Namespace,在这个空间中运行的进程,完全感知不到外界系统中的其他进程或是其他进程命名空间中运行的进程。于是,利用 PID Namespace,Docker 就实现了容器中隔离程序运行中进程隔离这一目标。
- Contrl Group: 资源控制组 ( 常缩写为 CGroups ) 是 Linux 内核在 2.6 版本后逐渐引入的一项对计算机资源控制的模块。虚拟化除了制造出虚拟的环境隔离同一物理平台运行的不同程序之外,另一大作用就是控制硬件资源的分配,CGroups 的使用正是为了这样的目的。
- Union File System: 是一种能够同时挂载不同实际文件或文件夹到同一目录,形成一种联合文件结构的文件系统。Docker 创新的将其引入到容器实现中,用它解决虚拟环境对文件系统占用过量,实现虚拟环境快速启停等问题。后面接触到docker volume(docker 数据卷)时,就会发现他们都存储在统一的目录如:
/var/lib/docker/volumes/8d47f68f0aad71a7d091cb0904c827/_data
(可以通过docker inspect containerid
查看目录地址),同时,文件的更新像git一样只是将修改的部分记录在案,再更新源文件,大大减少了存储空间的使用。
docker的核心组成
docker的四大核心概念的介绍
- 镜像:
所谓镜像,可以理解为一个只读的文件包,其中包含了虚拟环境运行最原始文件系统的内容。 - 容器:
如果把镜像理解为编程中的类,那么容器就可以理解为类的实例。在容器技术中,容器就是用来隔离虚拟环境的基础设施,而在 Docker 里,它也被引申为隔离出来的虚拟环境。 - 数据卷:
在以往的虚拟机中,我们通常直接采用虚拟机的文件系统作为应用数据等文件的存储位置。为了保证数据的独立性,我们通常会单独挂载一个文件系统来存放数据。而在 UnionFS 的加持下,除了能够从宿主操作系统中挂载目录外,还能够建立独立的目录持久存放数据,或者在容器间共享。在 Docker 中,通过这几种方式进行数据共享或持久化的文件或目录,我们都称为数据卷 ( Volume )。 - 网络:
在 Docker 中,实现了强大的网络功能,我们不但能够十分轻松的对每个容器的网络进行配置,还能在容器间建立虚拟网络,将数个容器包裹其中,同时与其他网络环境隔离。
目前 Docker 官方为我们提供了五种 Docker 网络驱动,分别是:Bridge Driver
、Host Driver
、Overlay Driver
、MacLan Driver
、None Driver
Bridge Driver
: 默认网络驱动程序. 如果未指定驱动程序,则这是您正在创建的网络类型. 当您的应用程序在需要通信的独立容器中运行时,通常会使用网桥网络。Host Driver
: 对于独立容器,取消容器与Docker主机之间的网络隔离,并直接使用主机的网络. host仅可用于Docker 17.06及更高版本上的集群服务.Overlay Driver
: 覆盖网络将多个Docker守护程序连接在一起,并使群集服务能够相互通信. 还可以使用覆盖网络来促进群集服务和独立容器之间或不同Docker守护程序上的两个独立容器之间的通信. 这种策略消除了在这些容器之间进行操作系统级路由的需要。MacLan Driver
: 为容器分配MAC地址,使其在网络上显示为物理设备。None Driver
: 对于此容器,禁用所有联网. 通常与自定义网络驱动程序一起使用. none不适用于群体服务。
docker 实践
首先先安装docker
, 安装的环境可以是 linux
, mac ox
, window
。 其中mac ox
, window
分别安装的是docker for mac
, doker for window
(具体 mac 和window 的版本还有要求可在官网查询)。其原理实际上只是在mac
或者 window
的环境上造了一个linux
的环境,如下图所示:
这里以 Mysql
+ NodeJs
的服务为例,构建镜像并用docker compose
构建成容器集群。
搭建并启动Mysql
数据库容器服务
-
寻找镜像源(镜像库地址)及确定版本。
-
执行以下操作(5.7为版本号):
1 | $ docker pull mysql:5.7 |
- 查看本地镜像,就会发现:
1 | $ docker images |
镜像列表里已经有了 mysql:5.7
的镜像。
- 使用:可以在宿主机器中直接登录(注意,前提是宿主机已经安装mysql及可执行mysql命令),宿主机器可以直接连接该数据库。即以下操作:
1 | $ docker run -d -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql:5.7 |
接着在宿主机登录容器中的 mysql
:
1 | $ mysql -h 127.0.0.1 -P 3306 -u root -p |
(然后输入密码,注意 -h和 -P需要写全,否则可能会报错)
构建Node服务的相关镜像文件
开始自己构建镜像,这里推荐直接使用VS Code 的插件docker,安装好之后接着 command + shift + p
使用docker add
后面就会自动创建docker 配置文件。
如下,为Dockfile配置文件:
关于Dockerfile的配置项,可以参考官方文档。
1 | FROM node:10 |
对该dockfile简单的说明如下:
FROM node:10
该服务所依赖的环境,如果你的docker已经有node:10的镜像,会直接拉取本地已有的镜像,若没有则会从dockerhub镜像库拉取,这里推荐可以使用极小版的node:10-alpine
ENV NODE_ENV docker
设定环境变量
WORKDIR /usr/app/myprojectname
设定你的项目在容器中的位置,一个容器可以看成是一个小的linux系统,所以我们可以在容器启动后通过命令:
1 | docker exec -it ecef8319d2c8 /bin/sh |
(ecef8319d2c8 是你的 container id)
直接进入到项目目录下,也可以查看整个容器的文件目录情况。会发现就是一个小的,与外界隔离的linux系统。
COPY ["package.json", "package-lock.json*", "npm-shrinkwrap.json*", "./"]
将本地目录下的指定的文件复制到上面设置的 WORKDIR的项目目录中
RUN npm install
运行你需要运行的命令
COPY . .
:.
代表当前目录,即将当前目录的文件复制到docker的项目根目录下。
EXPOSE 3001
将端口号暴露出来,后续可直接在宿主机器通过 127.0.0.1:3001暴露出来
CMD npm run dev
设置容器启动后运行的命令
配置好dockerfile文件后, 在项目根目录使用命令:
1 | docker build -t yourDockerImageName . |
(注意: 你的docker镜像的命名格式最好是 你想上传该镜像的镜像库的注册ID/镜像名, 否则可能会因为命名的不唯一而被拒绝上传)
build完之后可以运行你的镜像了。
1 | $ docker run -p 127.0.0.1:3000:3000 yourDockerImageName |
构建docker-compose
Compose 是用于定义和运行多容器 Docker 应用程序的工具。在 Docker Compose 里,我们通过一个配置文件,将所有与应用系统相关的软件及它们对应的容器进行配置,之后使用 Docker Compose 提供的命令进行启动,就能一次性启动并管理多个相关联的容器。
这里将上述的构建的mysql容器和node服务器的容器构建docker-compose.yml 配置文件:
1 | version: '2.1' |
关于docker-compose.yml的配置说明可以详细阅读官网
其中比较难理解的:
-
Volumes配置相关: 该选项是
${YOUR LOCAL PATH}:{DOCKER'S ABSOLUTE PATH}
, 也即可以将宿主机的本地文件和容器的某个绝对路径下的文件共享。什么时候使用?可以在项目有需要存放持久数据文件的时候将本地或者ftp服务器上专门的文件存放位置映射到容器的文件存放目录中。 -
容器间通信相关:
使用 Dcoker 部署项目常常会生成很多个容器,这些容器默认只能通过 ip 地址进行访问,但新建一个容器所产生的 ip 地址是不可控的,docker 默认额模式是桥接模式,网络地址一般为:
172.17.0.0/16
或172.18.0.0/16
, 比如一个主机中有两个容器,一个可能是172.18.0.2/16
,另一个是172.18.0.3/16
。(可以使用docker network inspect b9a9b34d772
(你的container id) 查看ip)
这就给容器之间通信带来了一定的麻烦。Docker 中使用 Network 来管理容器之间的通信,只要两个 Conteiner 处于同一个 Network 之中,就可以通过容器名去互相通信。
在上述 docker-compose up运行之后,会:
- 创建一个名为
myqpp_default
的网络。 - 使用web服务的配置创建容器,它以
web
这个名称加入myqpp_default
的网络。 - 使用db服务的配置容器,它以
db
这个名称加入myqpp_default
的网络。
因此总结一下容器间互相通信的方式:
- 方式一:可以通过使用容器的IP地址来通信。这种方式会导致IP地址的硬编码,不方便迁移,并且容器重启后IP地址可能会改变,除非使用固定的IP地址。
- 方式2: 可以通过宿主机的IP加上容器暴露出的端口号来通信。这种方式比较单一,只能依靠监听在暴露出的端口的进程来进行有限的通信。
- 方式3: 可以使用容器名,通过docker的link机制通信。这种方式通过docker的link机制可以通过一个name来和另一个容器通信,link机制方便了容器去发现其它的容器并且可以安全的传递一些连接信息给其它的容器。使用name给容器起一个别名,方便记忆和使用。即使容器重启了,地址发生了变化,不会影响两个容器之间的连接。例如,web这个服务可使用 webapp://db:3306 访问db容器。若有其他容器连接上述服务名为web的容器,可以使用 http://web:3001 访问。
当然,也可以使用别名:
1 | web |
- networks 配置相关:networks关键字用于指定自定义网络
例如:加入已经存在的网络
1 | networks: |
以下例子定义了front和back网络,实现了网络隔离。其中proxy和db之间只能通过app来实现通信。
1 | services: |
构建好之后只要在当前路径下使用命令就能启动docker-compose:
1 | docker-compose up |
总结:docker network相关
-
bridge模式: 默认使用
bridge
模式, 为容器提供一个独立的网络环境,因此,若要和其他容器连接,可以使用上述的link
和服务名的方法。1
$ docker network create my-net
1
2
3
4$ docker create --name my-nginx \
--network my-net \
--publish 8080:80 \
nginx:latest断开连接
1
$ docker network disconnect my-net my-nginx
-
host模式:该模式会破坏docker容器的网络隔离,使得所有的集群中的容器都使用宿主机的host。但是该模式在docker for mac 和docker for window 下不生效,因此如果你想在这两种环境中连接其他该环境中的容器,可以使用 docker 特有的域名
host.docker.internal
作为宿主网络的域名。(因宿主机ip可能会经常变化) -
overlay 模式:该模式适用于多个不同的docker进程间的容器通信。即有多个Docker host,希望能够通过Docker swarm连接起来。具体可以参考overlay network
-
macvlan 模式: 略
注意的点
Docker 容器启动时,默认会把容器内部第一个进程,也就是pid=1的程序,作为docker容器是否正在运行的依据,如果 docker 容器pid=1的进程挂了,那么docker容器便会直接退出。
Docker未执行自定义的CMD之前,nginx的pid是1,执行到CMD之后,nginx就在后台运行,bash或sh脚本的pid变成了1。
所以一旦执行完自定义CMD,nginx容器也就退出了。
因此,如果使用pm2
来部署node
服务, 或者使用ngnix
来部署前端服务时,需要注意进程被自动退出问题:
需要加上防止后台运行的设置如:
1 | pm2 start ./master.js --no-daemon # pm2 |
1 | daemon off; # 关闭后台运行 ngnix.conf |
常用docker命令
1 | docker images |
参考链接: