简介

Docker是一个开源的应用容器引擎。基于Namspaces、Control Groups和UnionFS,保证容器的轻巧、隔离和易移植。

build once, configure once and run anywhere。

特性:

  • 速度飞快;
  • 优雅的隔离架构;
  • cpu/内存消耗低;
  • 快速启动/关闭/销毁;
  • 开源;

满足 DevOps 的需求:

  • 简化了大规模的集群部署步骤
  • 方便快捷的持续集成和部署 CI/CD
  • 微服务架构
  • 一次性的/定时的任务
  • 一致的开发测试环境
  • 演示、使用环境
  • 解决设备成本,充分利用资源
  • 技术方案快速验证

版本

2017/3/3,Docker版本从1.13.*直接跳入17.03,即17年3月。Docker以后会以CE(Community Edition)和EE(Enterprise Edition)的形式发布。CE版本每个月发布一次,EE三个月发布一次,对应17.03、17.06等。对于发布的每个EE版本,Docker官网都会提供一年的技术支持。docker EE支持各大主流系统和云服务器,但是CE则不是,

docker v.s 虚拟机

传统虚拟化技术的体系架构:

Docker入门知识_unix


Docker 技术的体系架构:

Docker入门知识_服务器_02


从上面两张图一目了然看到docker相对于虚拟机的优势所在:资源占用少,启动速度快,……。

安全性

容器相比虚拟机更为安全

概念

三个基本概念:镜像Image,容器Container,仓库Registry;
Docker镜像
一个只读的模板。镜像可以用来创建 Docker 容器。Docker 提供一个很简单的机制来创建镜像或者更新现有的镜像,用户可以直接从仓库下载一个已经做好的镜像直接使用。利用 Dockerfile 以及 docker build 命令来创建Docker Images镜像。镜像是一种文件结构,Dockerfile中的命令都会在文件系统中创建一个新的层次结构,镜像则构建与这些文件系统之上。

Docker容器
容器是从镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。可以把容器看作是一个简易版Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。
镜像是只读的,容器在启动的时候创建一层可写层作为最上层。

Docker仓库
仓库是集中存放镜像文件的场所。Repository镜像名和Registry是两回事。Registry 里存放着多个Repository,每个仓库中又包含多个镜像,每个镜像有不同的标签tag。
仓库分为公开仓库和私有仓库两种形式。最大的公开仓库是 Docker Hub,存放数量庞大的镜像供用户下载。 国内的公开仓库包括 Docker Pool等,可以提供大陆用户更稳定快速的访问。企业或者用户可以借助于 Registry (或者Harbor、Portus)在本地网络内创建一个私有仓库。
当用户创建自己的镜像之后就可以使用 push 命令将它上传到公有或者私有仓库,这样下次在另外一台机器上使用这个镜像时候,只需要从仓库上 pull 下来就可以。
不严谨的类比:Docker 仓库的概念跟 Git 类似,注册服务器可以理解为 GitHub 这样的托管服务。

常用命令行

# 前置知识:镜像的 ID 唯一标识镜像,如果两个镜像有相同的镜像ID,即下面的<image_id>,说明是同一镜像,TAG 信息用来标记来自同一个仓库的不同镜像,不指定tag信息,默认就是LATEST。
<image_id> == <rigestry_name>/<org_name>/<image_name>:<tag_name>
# 下载镜像images
docker pull <rigestry_name>/<org_name>/<image_name>:<tag_name>
docker pull ubuntu:12.04
# 等价于
docker pull registry.hub.docker.com/ubuntu:12.04 命令,即从注册服务器registry.hub.docker.com 中的 ubuntu 仓库来下载标记为 12.04 的镜像。官方仓库在国外,速度慢,从其它仓库(如阿里镜像库)下载时需要指定完整的仓库注册服务器地址,或者在配置文件里面修改。
# 利用 tag,新打一个image
docker tag <image_id> localhost:5000/mng-portal:1.5

# 如果pull下载失败,依据提示可能需要登录
docker login <rigestry_name>
# 列出本地的镜像
docker images
# 上传镜像images,不仅仅需要登录,还需要push的admin权限
docker push <rigestry_name>/<org_name>/<image_name>:<tag_name>
# build 镜像images, 需要在dockerfile所在目录下面执行,注意后面的点
docker build --build-arg http_proxy=http://<host>:<port>--build-arg https_proxy=https://<host>:<port> -t hello-world:1.0 .
# 删除空悬的images,即 <none>:<none>
docker rmi --force $(docker images -f dangling=true -q)

# 启动容器有两种方式,1. 基于镜像新建一个容器并启动,2. 将终止状态(stopped)的容器重新启动。
# 将镜像run起来,变成container。不带 -it 选项时,启动的容器并不会保留后台进程;-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开。在交互模式下,用户可以通过所创建的终端来输入命令。
docker run -it -p 15432:5432 -v /root:/mount hello-world:1.0 /bin/bash

原理:当利用 docker run 来创建容器时,Docker 在后台运行的标准操作包括:
1、检查本地是否存在指定的镜像,不存在就从公有仓库下载;利用镜像创建并启动一个容器
2、分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
3、从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
4、从地址池配置一个 ip 地址给容器
5、执行用户指定的应用程序
6、执行完毕后容器被终止

利用 docker start 命令,直接将一个已经终止的容器启动运行。容器的核心为所执行的应用程序,所需要的资源都是应用程序运行所必需的。除此之外,并没有其它的资源。可以在伪终端中利用 ps 或 top 来查看进程信息。容器中仅运行指定的 bash 应用,这使得 Docker 对资源的利用率极高。

# 保存镜像images为tar包:
docker save -o /<save_path>/*.tar <image_id>
# 从导出的本地 tar 文件中再导入到本地镜像库
docker load -i *.tar
# 导入镜像以及其相关的元数据信息(包括标签等)
docker load < ubuntu_14.04.tar
# docker images 列出本地所有镜像,其中很可能会包含有很多中间状态的未打过标签的镜像,大量占据着磁盘空间。
# 删除容器containers
docker rm -f <container_id>
# 清理所有未打过标签的本地镜像
docker rmi $(docker images -q/--quiet -f/--filter "dangling=true")
# 在删除镜像之前要先用 docker rm 删掉依赖于这个镜像的所有容器,-f 参数强制删除images,
docker rmi -f <images_id>

# kill_running_containers
docker kill `docker ps -q`
# 从容器拷贝文件到主机host上去
docker cp <containerId>:<containerPath> <hostPath>
//示例
docker cp Users.json /root
# 从主机上拷贝文件到容器内
docker cp ./xxxx.json 4755137f8c52:/
# list all containers:
docker ps -a
# 进入容器:
docker exec -it <container_id> bash

# 查看容器日志
docker logs

# 搜索镜像,按照star排名
docker search nginx

# 用于查看容器/镜像的配置信息,包含容器名、环境变量、运行命令、主机配置、网络配置和数据卷配置等。
docker inspect

# 其他不常用
# 退出登录Docker registry
docker logout

使用docker启动数据库

docker 形式安装本地的 postgres 数据库,命令:

docker run -d \
--name postgres-order \
-e POSTGRES_DB=order \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=1Qaz \
-p 5432:5432 \
postgres

上面这个命令会检查本地有没有一个 postgres 镜像,没有的话,则会执行 docker pull postgres从官方仓库拉取一个镜像。
以后比如想要新建一个本地的数据库 bank,则命令为:

docker run -d \
--name postgres-bank \
-e POSTGRES_DB=bank \
-e POSTGRES_USER=postgres \
-e POSTGRES_PASSWORD=1Qaz \
-p 5432:5432 \
postgres

即统一使用本地的 postgres image,container 的名字是 ​​postgres-<database>​​​,数据库名字使用 -e POSTGRES_DB 参数指定,用户名和密码保持不变。container 名字必须变,否则命令执行报错。
然后就可以使用客户端连接工具,如 dbeaver连接。

高级命令行

Docker System

使用过docker的人都知道,磁盘空间特别容易占用100%,虽然docker已经引入层的概念来优化存储卷空间。此时我们需要一个工具或者命令行来检测可以删除或者清理的容器或者镜像等占用的资源;上面讲到一条基本命令:​​# 删除空悬的images,即 <none>:<none>:docker rmi --force $(docker images -f dangling=true -q)​​。

如果开发或者生产环境一直使用这个命令行,会显得效率低下,并且并不能完全清理其他占用的空间。自Docker 17.03版本以来,docker自带命令​​docker system​​​用来清理空间。命令​​df -h​​​可以用来检测磁盘的使用情况;​​docker system df​​​命令,用于查看Docker的磁盘使用情况,基于Linux自带的命令​​df​​​。​​docker system prune​​可以用于删除所有停止的容器以及dangling镜像,执行此命令会给出warning:

WARNING! This will remove:
- all stopped containers
- all volumes not used by at least one container
- all networks not used by at least one container
- all dangling images

使用​​-a​​​选项可以做深度清理,​​docker system prune -a​​:

WARNING! This will remove:
- all stopped containers
- all volumes not used by at least one container
- all networks not used by at least one container
- all images without at least one container associated to them

这个命令将清理整个系统,并且只会保留真正在使用的镜像,容器,数据卷以及网络,因此需要格外谨慎。比如,不能在生产环境中运行prune -a命令,因为一些备用镜像(用于备份,回滚等)有时候需要用到,如果这些镜像被删除,则运行容器时需要重新下载。
此时,所有未绑定容器的镜像将会被删除。由于第一次prune命令删除所有容器,因此所有镜像(它们没有绑定任何容器)都会被删除。

dockerfile

参考另一篇博客​​Dockerfile编写总结​​

搭建镜像服务器

为什么需要搭建镜像服务器:

  1. 服务器部署在国外的 docker hub 对于国内企业/个人用户而言,访问速度慢得一逼;
  2. 主要是:企业用户为了安全和私有化考虑,需要搭建自己内部的私有仓库,类比于nexus,GitLab;

主要有docker registry,Harbor,以及Portus。

docker registry

官网:
旧版:​​​docker-registry​​​ 新版:​​distribution​​ Docker Registry是一个Docker仓库,用来存储和分享Docker镜像,一般用于局域网或者内网。
创建方式
最简单的方法:
​docker run -d -p 5000:5000 --restart=always --privileged=true --name registry -v /root/data:/var/lib/registry registry:2​​ 其他方法,无外乎直接使用代码库的dockerfile构建registry镜像,或者二进制安装。

Harbor
Portus

Docker API

参考​​sdk​​​ 对应于​​docker ps​​命令,http方式如下:

$ curl --unix-socket /var/run/docker.sock http:/v1.24/containers/json
[{
"Id":"ae63e8b89a26f01f6b4b2c9a7817c31a1b6196acf560f66586fbc8809ffcd772",
"Names":["/tender_wing"],
"Image":"bfirsh/reticulate-splines",
...
}]

那么使用Java呢?引入dependency:

<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-unixsocket</artifactId>
<version>0.20</version>
</dependency>

使用 UnixSocket 调用Docker API的demo程序:

public static void main(String[] args) {
// 建立 Unix Socket 连接
File sockFile = new File("/var/run/docker.sock");
UnixSocketAddress address = new UnixSocketAddress(sockFile);
UnixSocketChannel channel = UnixSocketChannel.open(address);
UnixSocket unixSocket = new UnixSocket(channel);

// 调用 Docker API
PrintWriter w = new PrintWriter(unixSocket.getOutputStream());
w.println("GET /v1.24/containers/json HTTP/1.1");
w.println("Host: http");
w.println("Accept: */*");
w.println("");
w.flush();
// 关闭 Output,否则会导致下面的 read 操作一直阻塞
unixSocket.shutdownOutput();

// 获取返回结果
BufferedReader br = new BufferedReader(new InputStreamReader(unixSocket.getInputStream()));
String line;
while ((line = br.readLine()) != null){
System.out.println(line);
}
unixSocket.close();
}

Docker Compose

Docker Compose,用来定义和运行多个容器的工具。需要编写 docker-compose.yml 来配置容器,docker compose 解析这个文件,分析其语法,如有错误肯定不能启动成功。推荐一个在线检查 yaml 文件格式的网址​​yamllint​​​。
一个简单示例:

services:
jetty:
images: jetty
container_name: jetty
ports:
- "8881:8080"
- "22"
jenkins:
images: jenkins
container_name: jenkins
ports:
- "8882:8080"
links:
jetty

命令​​docker-compose up -d​​,docker compose 解析文件之后,会先启动 jetty,然后启动 jenkins;

docker compose安装

对于Win/Mac系统,安装docker的时候附带安装;
Linux安装:
​​​curl https://github.com/docker/compose​​​ 下载文件到(>)/usr/local/bin/docker-compose,在下载地址中使用Docker入门知识_服务器_03(uname -s)-$(uname -m) > /usr/local/bin/docker-compose`

docker容器集群管理

系统如果是分布式或者微服务的,docker 化之后,容器肯定也不止一个,此时就需要集群编排管理工具,主流就三种 docker swarm、Kubernetes、Mesos。

docker swarm

Swarm,可以把多个主机变成一个虚拟的Docker主机来管理,Go语言开发,源码在[swarm](https://github.com/docker/swarm)上。Swarm使用标准的Docker API,给Docker用户带来无缝的集群使用体验。2016年7月, Swarm已经被整合进入Docker Engine。

Docker Swarm提供API 和CLI来在管理运行Docker的集群,它的功能和使用本地的Docker并没有本质的区别。但是可以通过增加Node带来和好的扩展性。理论上,你可以通过增加节点Node的方式拥有一个无限大的Docker主机。Swarm并不提供UI,需要UI的话,可以安装收费的UCP。

架构图:

Docker入门知识_unix_04

kubernetes

简介参考另一篇博客​​kubernetes基础入门总结​​

对比

Docker入门知识_服务器_05


网络

参考​​Docker网络​​

可视化工具

参考​​Docker可视化工具​​

参考

​Docker终极指南​