Docker基础

Docker是一个开源的容器引擎,有助于更快地交付应用。 Docker可将应用程序和基础设施层隔离,且能将基础设施当作程序一样进行管理。使用Docker可更快地打包测试以及部署应用程序,且可缩短从编写到部署运行代码的周期。Docker有如下优点:

  • 简化程序:让开发者可打包应用以及依赖包到一个可移植的容器中,然后发布到任何流行Linux机器上,便可实现虚拟化。方便快捷是Docker最大优势,过去需要用数天乃至数周的任务,在Docker容器处理下只需要数秒就能完成。Docker镜像中包含了运行环境配置,所以Docker可简化部署多种应用实例工作
  • 节省开支:云计算时代到来,使开发者不必为了追求效果而配置高额的硬件,Docker改变了高性能必然高价格的思维定势。Docker与云的结合,让云空间得到更充分的利用。不仅解决了硬件管理问题,也改变了虚拟化的方式。

Docker架构

Docker daemon

Docker daemonDocker守护进程是一个运行在宿主机DOCKER-HOST的后台进程,可通过Docker客户端与之通信。

Client

Docker客户端是Docker的用户界面,可接受用户命令配置标识,并Docker daemon通信。图中docker build等都是Docker的相关命令。

Images

Docker镜像是一个只读模板,它包含创建Docker容器的说明和系统安装光盘有点像,使用系统安装光盘可安装系统,同理使用Docker镜像可运行Docker镜像中的程序。

Container

容器镜像的可运行实例镜像和容器的关系有点类似于面向对象中,类和对象的关系,可通过Docker API或CLI命令来启停移动删除容器

Registry

Docker Registry是一个集中存储与分发镜像的服务,构建完Docker镜像后,可在当前宿主机上运行。若想要在其他机器上运行该镜像,需要手动复制。此时可借助Docker Registry来避免镜像的手动复制。

一个Docker Registry可包含多个Docker仓库,每个仓库可包含多个镜像标签,每个标签对应一个Docker镜像。这跟Maven的仓库有点类似,若把Docker Registry比作Maven仓库的话,则Docker仓库就可理解为某jar包的路径,而镜像标签则可理解为jar包的版本号。

Docker Registry可分为公有Docker Registry和私有Docker Registry,最常用的Docker Registry莫过于官方的默认的Docker Hub,Docker Hub上存放着⼤量优秀的镜像,可使用Docker命令下载并使用。

Docker虚拟化原理

传统虚拟化技术是在硬件层面实现虚拟化,增加了系统调用链路的环节有性能损耗容器虚拟化技术共享宿主机Kernel的方式实现几乎没有性能损耗

Docker利用的是宿主机的内核,而不需要Guest OS,当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核,避免了寻址加载操作系统内核这些比较费时费资源的过程。当新建一个虚拟机时,虚拟机软件需要加载Guest OS,该新建过程是分钟级别的。而Docker由于直接利用宿主机的操作系统,则省略了该过程,因此新建一个Docker容器只需要几秒钟。

Docker是使用联合文件系统将机器的资源进行隔离的,常见的联合文件系统AUFSOverlaydevicemapperBTRFSZFS等。以Overlay2的架构图为例:

Overlayfs在Linux主机上只有两层,一个目录在下层,用来保存镜像(docker),另外一个目录在上层,用来存储容器信息。在Overlayfs中,底层的目录叫做lowerdir,顶层的目录称之为upperdir,对外提供统一的文件系统为merged。当需要修改一个文件时,使用COW(Copy-on-write)写时复制将文件从只读的Lower复制到可写的Upper进行修改结果也保存在Upper。在Docker底下的只读层就是image可写层就是Container

写时复制技术

所有驱动都用到写时复制技术COW全称copy-on-write,表示只是在需要写时才去复制,这是针对已有文件的修改场景。如基于一个Image启动多个Container,若每个Container都去分配一个Image一样的文件系统,将会占用大量磁盘空间。而COW技术可让所有容器共享Image的文件系统,所有数据都从Image中读取,只有当要对文件进行写操作时才从Image里把要写的文件复制到自己的文件系统进行修改

无论有多少个容器共享一个Image,所做的写操作都是对从Image中复制到自己的文件系统的副本上进行并不会修改Image源文件,且多个容器操作同一个文件,会在每个容器的文件系统里生成一个副本每个容器修改的都是自己的副本互相隔离互不影响。使用COW可有效的提高磁盘的利用率,故容器占用空间很少

用时分配

用时分配针对原本没有该文件的场景,只有在要新写入一个文件时才分配空间,这样可提高存储资源的利用率。如启动一个容器,并不会因为该容器分配一些磁盘空间,而是当有新文件写入时,才按需分配新空间

查看容器占用磁盘大小指令
1
2
3
4
# 查看所有容器的大小
cd /var/lib/docker/containers # 进入docker容器存储目录
du -sh * # 查看所有容器的大小
du -sh <容器完整id> #查看某一个容器的大小

镜像分层原理

Docker使用共享技术减少镜像存储空间,所有镜像层容器层都保存在宿主机的文件系统/var/lib/docker/由存储驱动进行管理,尽管存储方式不尽相同,但在所有版本的Docker中都可共享镜像层。在下载镜像时,Docker Daemon会检查镜像中的镜像层与宿主机文件系统中的镜像层进行对比,若存在则不下载,只下载不存在的镜像层,这样可非常节约存储空间

Docker安装卸载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 查看当前内核版本,Docker要求CentOS系统的内核版本高于3.10
uname -r
# 使用root权限登录Centos确保yum包更新到最新
yum -y update
# 若安装过Docker,卸载旧版本
sudo yum remove -y docker*
# 安装需要的软件包,yum-util提供yum-config-manager功能,另外两个是devicemapper驱动依赖的
yum install -y yum-utils
# 设置yum源,并更新yum包索引
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache fast
# 查看所有仓库中所有docker版本,并选择特定版本安装
yum list docker-ce --showduplicates | sort -r
# 安装docker,且指定安装版本
yum install -y docker-ce-3:19.03.9-3.el7.x86_64
# 启动Docker
systemctl start docker
# 将Docker启动加入开机启动
systemctl enable docker
# 验证安装是否成功:有client和service两部分表示docker安装启动都成功了
docker version

# Docker卸载
yum remove -y docker*
rm -rf /etc/systemd/system/docker.service.d
rm -rf /var/lib/docker
rm -rf /var/run/docker

镜像加速

可借助阿里云的镜像加速器,登录阿里云,在配置daemon.json文件时,若JSON格式有问题会导致Docker启动失败。

1
2
3
cd /etc/docker
# 查看有没有daemon.json,docker默认的配置文件,若没有则新建,若有则修改
vim daemon.json
1
2
3
{
"registry-mirrors": ["https://x0y2v4jf.mirror.aliyuncs.com"]
}
1
2
3
# 重启docker服务
systemctl daemon-reload
systemctl restart docker

Docker常用命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# 使用docker search命令搜索存放在Docker Hub中的镜像,搜索含有java关键词的镜像仓库
# NAME:镜像仓库名称
# DESCRIPTION:镜像仓库描述
# STARS:镜像仓库收藏数,表示该镜像仓库的受欢迎程度,类似于GitHub的stars0
# OFFICAL:表示是否为官方仓库,该列标记为[0K]的镜像均由各软件的官方项目组创建和维护
# AUTOMATED:表示是否是自动构建的镜像仓库
docker search java
# docker pull命令可从Docker Registry上下载镜像,执行该命令后,Docker会从Docker Hub中的nginx仓库下载最新版本的nginx镜像
docker pull nginx
# 若要下载指定版本则在java后面加冒号指定版本
docker pull java:8
# 列出已下载的镜像:REPOSITORY镜像所属仓库名称,TAG镜像标签默认是latest最新,IMAGE ID镜像ID镜像唯一标识,CREATED镜像创建时间,SIZE镜像大小
docker images
删除本地镜像

通过docker rmi命令删除镜像时,若不是latest版本删除时需要加上版本号

1
2
3
4
5
6
7
# 删除指定镜像
docker rmi java
# 删除指定版本即Tag的镜像
docker rmi java:8
docker rmi -f nginx
# 删除所有镜像
docker rmi $(docker images -q)
新建并启动容器

使用docker run命令即可新建并启动一个容器,该命令会先检查本地是否存在指定镜像,若本地不存在该名称的镜像,Docker就会自动从Docker Hub下载镜像并启动一个 Docker容器,该命令常用选项:

  • -d选项:表示后台运行
  • -P选项:随机端口映射
  • -p选项:指定端口映射,有以下四种格式
    • –ip:hostPort:containerPort
    • –ip::containerPort
    • –hostPort:containerPort
    • –containerPort
  • --net选项:指定网络模式,该选项有以下可选参数
    • –net=bridge默认选项,表示连接到默认的网桥
    • –net=host:容器使用宿主机的网络
    • –net=container:NAME-or-ID:告诉Docker让新建的容器使用已有容器的网络配置
    • –net=none:不配置该容器的网络,用户可自定义网络配置
1
2
3
4
5
6
docker run -d -p 91:80 nginx
# 通过--name指定容器名称为nginx_eleven
docker run -d --name nginx_eleven -p 91:80 nginx
# 启动已停止的容器,若容器已近被创建,但是被停止后不能创建相同名称的容器
docker start nginx_eleven
docker start 31dc1cff48b7
停止镜像
1
2
3
4
5
6
7
# 31dc1cff48b7是容器ID
docker stop 31dc1cff48b7
# 使用docker stop容器名称来停止指定容器
docker stop nginx_eleven
# 发送SIGKILL信号来强制停止容器
docker kill 31dc1cff48b7
docker kill nginx_eleven
列出容器
1
2
3
4
# 列出运行中的容器
docker ps
# 列出包括已停止的容器
docker ps -a

CONTAINER_ID表示容器IDIMAGE表示镜像名称COMMAND表示启动容器时运行的命令CREATED表示容器创建时间STATUS表示容器运行状态UP表示运行中Exited表示已停止PORTS表示容器对外端口号NAMES表示容器名称,该名称默认由Docker自动生成,也可使用docker run命令的--name选项自行指定。

查看容器信息
1
2
3
4
5
6
7
# 查看容器所有信息
docker inspect 31dc1cff48b7
docker inspect nginx_eleven
# 查看容器日志
docekr container logs nginx_eleven
# 查看容器里的进程
docker top nginx_eleven

进入容器

使用docker exec命令用于进入一个正在运行的docker容器,若docker run命令运行容器时未使用-it参数,就要用该命令进入容器。一旦进入容器就可在容器的Shell执行命令

1
2
3
# 使用exit命令退出容器
docker exec -it 31dc1cff48b7 /bin/bash
docker exec -it nginx_eleven /bin/bash

容器与宿主机相互复制文件
1
2
3
4
5
# 从容器里面拷文件到宿主机
# docker cp 容器id或容器名称:要拷贝的文件在容器里面的路径 宿主机的相应路径
docker cp nginx_eleven:/etc/nginx/nginx.conf /mydata/nginx
# docker cp 要拷贝的宿主机文件路径 容器id:要拷贝到容器里面对应的路径
docker cp /data/nginx/test.txt nginx_eleven:/etc/nginx/

在容器中安装软件
1
2
3
4
5
6
7
# 进入容器
docker exec -it nginx_eleven /bin/bash
# 在容器中执行以下命令
apt-get update
apt-get install vim # 安装vim
apt-get install iputils-ping # 安装ping
apt-get install net-tools # 安装ifconfig
删除容器
1
2
3
4
5
# 删除指定容器,只能删除已停止的容器,若需删除正在运行的容器,可使用-f参数
docker rm 31dc1cff48b7
docker rm nginx_eleven
# 强制删除所有容器
docker rm -f $(docker ps -a -q)
查看容器资源使用情况
1
2
docker stats  # 返回容器资源的实时使用情况,1秒刷新一次
docker stats --no-stream # 返回容器当时的资源使用情况

CONTAINER ID表示容器ID,CPU %表示CPU使用情况,MEM USAGE / LIMIT当前使用内存和最大可使用内存,MEM %以百分比的形式显示内存使用情况,NET I/O网络I/O数据,BLOCK I/O磁盘I/O数据,PIDS表示PID号。

Dockerfile

可以使用Dockerfile构建Docker镜像,从而将微服务运行在docker上,Dockerfile是一个文本文件,其中包含了若干条指令,指令描述了构建镜像的细节。在/data/docker/nginx_eleven目录下创建一个名为Dockerfile的文件,在里面增加如下内容:

1
2
FROM nginx
RUN echo '<h1>This is Tuling Nginx!!!</h1>' > /usr/share/nginx/html/index.html

其中的FROMRUN都是Dockerfile的指令,FROM指令用于指定基础镜像RUN指令用于执行命令,创建好Dockerfile后通过 docker build命令构建镜像

1
2
# -t指定镜像名字,eleven表示打包的版本号,/data/docker/nginx_eleven为Dockerfile文件路径
docker build -t nginx:eleven /data/docker/nginx_eleven

Dockerfile常用指令
命令 用途
FROM 基础镜像文件
RUN 构建镜像阶段执行命令,执行结果会打包进入image文件,一个Dockerfile可包含多个RUN命令
ADD 添加文件,从src目录复制文件到容器的dest,其中src可Dockerfile所在目录相对路径,也可以是一个URL,还可是一个压缩包
COPY 拷贝文件,和ADD命令类似,但不支持URL和压缩包
CMD 容器启动后执行命令,一个Dockerfile只能包含一个CMD命令,指定了CMD命令以后,docker container run命令就不能附加命令了,如前面的/bin/bash,否则会覆盖CMD命令
EXPOSE 声明容器在运行时对外提供的服务端口
WORKDIR 指定容器工作路径
ENV 指定环境变量
ENTRYPINT 容器入口, ENTRYPOINT和CMD指令目的一样,都是指定Docker容器启动时执行的命令,可多次设置但只有最后一个有效
USER 该指令用于设置启动镜像时的用户或UID,写在该指令后的RUNCMDENTRYPOINT指令都将使用该用户执行命令
VOLUME 指定挂载点,该指令使容器中的一个目录具有持久化存储的功能,该目录可被容器本身使用,也可共享给其他容器,当容器中的应用有持久化数据的需求时可在Dockerfile中使用该指令,格式为VOLUME["/data"]
使用Dockerfile构建微服务镜像

以eureka-server为例,将该微服务的可运行jar包构建成docker镜像,首先将该jar包上传到/data/docker/eureka目录,并在jar包所在目录创建名为Dockerfile的文件,文件内容如下:

1
2
3
4
5
6
7
8
# 基于哪个镜像
From java:8
# 复制文件到容器
ADD eureka-server-0.0.1-SNAPSHOT.jar /app.jar
# 声明需要暴露的端口
EXPOSE 8761
# 配置容器启动后执行的命令
ENTRYPOINT java ${JAVA_OPTS} -jar /app.jar
1
2
3
4
5
6
7
8
9
# 使用docker build命令构建镜像
docker build -t eleven-eureka-server:0.0.1 /data/docker/eureka
# 启动镜像,加-d可在后台启动
docker run -d -p 8761:8761 eleven-eureka-server:0.0.1
# 使用-v可挂载一个主机上的目录到容器的目录
docker run -d -p 8761:8761 -v /log:/container-log eleven-eureka-server:0.0.1
# 加上JVM参数
# 参数--cap-add=SYS_PTRACE是让docker能支持在容器里能执行jdk自带类似jinfo,jmap等命令,若不需要在容器里执行这些命令可不加
docker run -e JAVA_OPTS='-Xms1028M -Xmx1028M -Xmn512M -Xss512K -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M' --cap-add=SYS_PTRACE -d -p 8761:8761 eleven-eureka-server:0.0.1
将微服务镜像发布到远程镜像仓库

制作好微服务镜像,一般需要发布到镜像仓库供别人使用,可选择自建镜像仓库,也可直接使用docker官方镜像仓库,首先需要在docke官方镜像仓库里注册一个账号,然后在Linux服务器上用docker login命令登录镜像仓库,要把镜像推送到镜像仓库,需要将镜像前面加个分组名,一般为docker hub的账户名:

1
2
3
4
5
docker login
# 修改镜像名字
docker tag eleven-eureka-server:0.0.1 eleven/eleven-eureka-server:0.0.1
# 将镜像推送到远程仓库
docker push eleven/eleven-eureka-server:0.0.1