本文旨在让读者能大体了解docker整个体系,而且对于docker运行机制有必定了解。如要深刻,请先深刻了解linux。html
Docker是开源的,基于Linux容器技术的引擎,统一了被隔离的应用程序访问系统核心的API。试图解决开发者的世纪难题在个人机器上能够跑
。前端
前端同窗能够视镜像为npm包,仓库为npm仓库。这样更方便理解。node
Docker是一种相似虚拟机技术的缩减版,因为虚拟机启动过程过于漫长与虚拟化以后的硬件在运行程序的时候,并不能很好的契合物理机,比较典型的例子就是移动端开发,启动虚拟系统的时候,过程十分的漫长。linux
咱们常常开启一个虚拟机仅仅是须要隔离一个应用,可是虚拟机建立占用了一套完整的系统资源(guest os),存在着大材小用的问题,成本也息息相关。nginx
而Docker随着Linux功能的更新出现了,Docker本质仅隔离应用程序,共享当前系统核心。git
下图为虚拟机与Docker架构对比:github
下图为容器虚拟机功能对比:web
这样的话,Docker就能够进行秒启动,由于Docker跳过了系统初始化(kernel init),直接使用了当前系统核心。可是这个也是有弊病的,好比 虚拟机热迁移 这个功能,Docker就作的不是很好。redis
使用Docker能够快速的搭建配置应用环境,简化操做,确保运行环境一致性“一次编译 处处运行”,应用级隔离,弹性伸缩,快速拓展。docker
镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。 镜像不包含任何动态数据,其内容在构建以后也不会被改变。
镜像利用(union file system)提供应用运行的只读模板,它能够只提供一个功能,也能够由多个镜像叠加建立多个功能服务。
镜像仅仅是定义隔离应用运行所须要的东西,容器则是运行这些镜像的进程。在容器内部,提供了完整的 文件系统、网络、进程空间等等。彻底隔离于外部环境,不会受到其余应用的侵扰。
容器的读写必须使用**Volume**
,或者宿主的存储环境,容器在重启或者关闭以后,存在于运行容器内部的数据将会丢失,每次启动容器,都是经过镜像建立一个新的容器。
Docker
仓库是集中存放镜像文件的场所。镜像构建完成后,能够很容易的在当前宿主上运行,可是, 若是须要在其它服务器上使用这个镜像,咱们就须要一个集中的存储、分发镜像的服务,Docker Registry
(仓库注册服务器)就是这样的服务。有时候会把仓库 (Repository)
和仓库注册服务器 (Registry)
混为一谈,并不严格区分。Docker
仓库的概念跟 Git
相似,注册服务器能够理解为 GitHub
这样的托管服务。实际上,一个 Docker Registry
中能够包含多个仓库 (Repository)
,每一个仓库能够包含多个标签 (Tag)
,每一个标签对应着一个镜像。因此说,镜像仓库是 Docker
用来集中存放镜像文件的地方相似于咱们以前经常使用的代码仓库。
一般,一个仓库会包含同一个软件不一样版本的镜像,而标签就经常使用于对应该软件的各个版本 。咱们能够经过<仓库名>:<标签>
的格式来指定具体是这个软件哪一个版本的镜像。若是不给出标签,将以 latest
做为默认标签.。
仓库又能够分为两种形式:
public
(公有仓库)private
(私有仓库)Docker client是一个泛称,用来向指定的Docker Engine发起请求,执行相应的容器管理操做.它既能够是Docker命令行工具,也能够是任何遵循了Docker API的客户端.目前, 社区中维护着的Docker client种类很是丰富,涵盖了包括C#(支持 Windows)、Java、Go、Ruby、JavaScript等经常使用语言,甚至还有使用Angular库编写的WebU格式的客户端,足以知足大多数用户的需求。
Docker Engine是Docker最核心的后台进程,它负责响应来自Docker client的请求,而后将这些请求翻译成系统调用完成容器管理操做。该进程会在后台启动一个API Server,负责接收由Docker client发送的请求;接收到的请求将经过Docker Engine内部的一个路由分发调度,再由具体的函数来执行请求。
##2.1 安装Docker
本文全部环境均运行在centos7
下。
首先,移除全部老版本的Docker。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
复制代码
若是,是一个全新环境,那能够跳过这一步。
为国内一些缘由,因此按照官网安装docker-ce大几率是装不上的,因此,咱们须要国内镜像来加速安装。下面咱们经过aliyun加速安装。
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加软件源信息
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 更新并安装 Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 开启Docker服务
sudo service docker start
复制代码
安装完成以后能够运行docker version
检查是否安装成功。
➜ ~ docker version
Client: Docker Engine - Community
Version: 19.03.3
API version: 1.40
Go version: go1.12.10
Git commit: a872fc2f86
Built: Tue Oct 8 00:58:10 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.3
API version: 1.40 (minimum version 1.12)
Go version: go1.12.10
Git commit: a872fc2f86
Built: Tue Oct 8 00:56:46 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.10
GitCommit: b34a5c8af56e510852c35414db4c1f4fa6172339
runc:
Version: 1.0.0-rc8+dev
GitCommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
docker-init:
Version: 0.18.0
GitCommit: fec3683
复制代码
##2.2 获取一个镜像
如今咱们须要拉取一个nginx
镜像,部署一个nginx
应用。
➜ ~ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
68ced04f60ab: Pull complete
28252775b295: Pull complete
a616aa3b0bf2: Pull complete
Digest: sha256:2539d4344dd18e1df02be842ffc435f8e1f699cfc55516e2cf2cb16b7a9aea0b
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
复制代码
拉取完成以后,使用docker image ls
查看当前docker本地镜像列表。
➜ ~ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 6678c7c2e56c 13 hours ago 127MB
复制代码
从新运行相同的命令docker pull nginx
会更新本地镜像。
##2.3 运行一个Docker容器
建立一个shell
脚本文件,写入如下内容:
docker run \
# 指定容器中止后的重启策略:
# no:容器退出时不重启
# on-failure:容器故障退出(返回值非零)时重启
# always:容器退出时老是重启
--restart=always \
# 指定docker运行在后台,若是不加-d,在执行完这条命令以后
# 你退出命令行也会将这个docker容器退掉
-d \
# 将宿主机端口号绑定至容器端口上
-p 8080:80 \
# 指定容器暴露的端口,即修改镜像的暴露端口
--expose=80 \
# 映射宿主目录至
-v /wwwroot:/usr/share/nginx/html \
# 指定容器名字,后续能够经过名字进行容器管理,links特性须要使用名字
--name=testdocker \
# 用哪一个镜像初始化这个容器
nginx:lastest
复制代码
咱们要明确一点docker的容器
网络是与宿主机隔离的,除非指定容器网络模式依托宿主机,不然是没法直接访问的。
如今咱们运行这个脚本,接着打开浏览器,输入http://ip:8080
就能够看到使用nginx
镜像运行的应用程序了。
更多命令,请参考:docs.docker.com/engine/refe…
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
02.
03. -d, --detach=false 指定容器运行于前台仍是后台,默认为false
04. -i, --interactive=false 打开STDIN,用于控制台交互
05. -t, --tty=false 分配tty设备,该能够支持终端登陆,默认为false
06. -u, --user="" 指定容器的用户
07. -a, --attach=[] 登陆容器(必须是以docker run -d启动的容器)
08. -w, --workdir="" 指定容器的工做目录
09. -c, --cpu-shares=0 设置容器CPU权重,在CPU共享场景使用
10. -e, --env=[] 指定环境变量,容器中可使用该环境变量
11. -m, --memory="" 指定容器的内存上限
12. -P, --publish-all=false 指定容器暴露的端口
13. -p, --publish=[] 指定容器暴露的端口
14. -h, --hostname="" 指定容器的主机名
15. -v, --volume=[] 给容器挂载存储卷,挂载到容器的某个目录
16. --volumes-from=[] 给容器挂载其余容器上的卷,挂载到容器的某个目录
17. --cap-add=[] 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities
18. --cap-drop=[] 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities
19. --cidfile="" 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法
20. --cpuset="" 设置容器可使用哪些CPU,此参数能够用来容器独占CPU
21. --device=[] 添加主机设备给容器,至关于设备直通
22. --dns=[] 指定容器的dns服务器
23. --dns-search=[] 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件
24. --entrypoint="" 覆盖image的入口点
25. --env-file=[] 指定环境变量文件,文件格式为每行一个环境变量
26. --expose=[] 指定容器暴露的端口,即修改镜像的暴露端口
27. --link=[] 指定容器间的关联,使用其余容器的IP、env等信息
28. --lxc-conf=[] 指定容器的配置文件,只有在指定--exec-driver=lxc时使用
29. --name="" 指定容器名字,后续能够经过名字进行容器管理,links特性须要使用名字
30. --net="bridge" 容器网络设置:
31. bridge 使用docker daemon指定的网桥
32. host //容器使用主机的网络
33. container:NAME_or_ID >//使用其余容器的网路,共享IP和PORT等网络资源
34. none 容器使用本身的网络(相似--net=bridge),可是不进行配置
35. --privileged=false 指定容器是否为特权容器,特权容器拥有全部的capabilities
36. --restart="no" 指定容器中止后的重启策略:
37. no:容器退出时不重启
38. on-failure:容器故障退出(返回值非零)时重启
39. always:容器退出时老是重启
40. --rm=false 指定容器中止后自动删除容器(不支持以docker run -d启动的容器)
41. --sig-proxy=true 设置由代理接受并处理信号,可是SIGCHLD、SIGSTOP和SIGKILL不能被代理
复制代码
咱们可使用docker exec -it [docker container id] /bin/bash
来进入正在运行的容器。
而要退出容器,有两种方式:
exit
就退出ctrl+P Q
也会退出以上两种方式都可从容器中退出,而且保持容器在后台运行。
##2.5 自定义一个镜像Dockerfile
Dockerfile 分为四部分:基础镜像信息、维护者信息、镜像操做指令和容器启动时执行指令。
这里我使用了一份简单的node启动开发环境的Dockerfile。
# 1. 设置来源的基础镜像
FROM node:12.0
# 指定后续 RUN、CMD、ENTRYPOINT 指令的工做目录
WORKDIR /workspace # 在上一次经过WORKDIR指定的目录运行 RUN 后续命令
RUN npm install --registry=https://registry.npm.taobao.org # 初始化暴露 8080 8001 8800端口号
# 如下端口号 也能够在docker run时暴露出去
EXPOSE 8080
EXPOSE 8001
EXPOSE 8800
# 默认执行的命令,若是在宿主机经过docker run -it /bin/bash进入时,如下命令不会被执行
# 彻底不被覆盖的指令为 ENTRYPOINT
CMD ["npm","run","dev-server"] 复制代码
保存退出编辑,执行docker build -t nodeapp:v1.0 .
注意 最后一个.
表示当前目录。
在运行完成以后,使用docker image ls
查看是否有已经编译完成的镜像便可。
这个时候,可能有些人有个提问,就是每次都须要npm install安装文件吗?
其实,若是你的node应用包不会变化,而你这个镜像又是专为此应用开发的,能够考虑使用ADD
指令,将node_modules
追加至docker镜像中。(现实基本不会如此处理,由于外部若是映射了数据卷进来会覆盖目录,此处只是为了演示像镜像追加文件。)
更多指令集,请查阅 :
中文版:yeasy.gitbooks.io/docker_prac…
官方英文版:docs.docker.com/engine/refe…
##2.6 多容器启动:Docker-compose
Docker-compose 须要单独安装。
咱们来假设一个场景,咱们启动了一个前端项目。须要启动nginx
运行前台项目,启动一个数据库来记录数据,保证整个应用的完整。那么这就是docker-compose的用武之地了。
首先要知道,docker-compose由如下两种类型组成:
service
):一个应用容器,实际上能够运行多个相同镜像的实例。project
):由一组关联的应用容器组成的一个完整业务单元。咱们回到以前建立Dockerfile
的目录中,编写一个docker-compose.yml
文件,配置多容器。
version: '1'
services:
web:
build: .
ports:
- "8080:80"
volumes:
- /wwwroot:/usr/share/nginx/html
redis:
image: "redis:alpine"
复制代码
运行命令docker-compose up
以后,咱们经过docker stats
能够看到两个(web、redis)docker已经启动了。
访问http://ip:8080
就能够看到跟以前同样的网页了。
更多可参考:docs.docker.com/compose/
因为应有隔离,你没法在外网直接访问到宿主机上的docker容器。因此咱们须要将宿主机上的端口绑定到容器上。
2.3已经介绍了如何绑定端口与导出容器端口。咱们这里了解一下容器互联。
# 运行命令建立一个docker网络
$ docker network create -d bridge my-net
# 建立两个容器 加入my-net网络
$ docker run -it --rm --name busybox1 --network my-net busybox sh
$ docker run -it --rm --name busybox2 --network my-net busybox sh
# 接着咱们进入busybox1
$ docker exec -it busybox1 /bin/bash
# ping 另一个容器,能够看到他的IP信息
$ root@busybox1:ping busybox2
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms
复制代码
Docker使用Go语言编写,而且使用了一系列Linux内核提供的特性来实现其功能。
一个能执行Docker的系统分为两大部分:
Docker使用的Linux核心模块功能包括下列各项:
使用虚拟机运行Linux,而后在Linux中运行Docker Engine。在本机运行Docker client。
前端同窗关注点
为何不能用CMD ['node','app.js']
做为默认启动,由于在 Node.js 的官方最佳实践里有写到 "Node.js was not designed to run as PID 1 which leads to unexpected behaviour when running inside of Docker."。下图来自 github.com/nodejs/dock… 。
这个问题涉及linux运行机制,简单的说,就是 linux pid为1的进程是系统守护的进程,将会接收全部孤儿进程。而且在适当的时候发送关闭信号给这些进程。
可是,docker中pid 1的进程为node,而node并无作回收孤儿进程的事情。因此,若是你的应用跑相似爬虫之类的应用,执行完毕以后将进程挂到pid 1 上,慢慢的容器就会BOOM。
解决方案:
1. 用`/bin/bash`启动。
2. 在`docker run`后面追加`--init`用于初始化一个docker的进程为pid 1。docker提供的进程能够回收全部孤儿进程。
复制代码
具体能够参考这个案例:juejin.im/post/5e0002…
hujb2000.gitbooks.io/docker-flow…
《docker从入门到实践》legacy.gitbook.com/book/yeasy/…
K8S部署方案:github.com/opsnull/fol…