Linux容器引擎-Docker
2014-09-09 15:50:40 阿炯

Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口,几乎没有性能开销,可以很容易地在机器和数据中心中运行。最重要的是它们不依赖于任何语言、框架或包装系统。采用Go语言开发并在ApacheV2.0协议下授权。


Build, Ship and Run Any App, Anywhere


Docker - An open platform for distributed applications for developers and sysadmins.

Docker是一个功能强大的自动化分布式系统:大规模的Web部署、数据库集群、持续部署系统、私有PaaS、面向服务的体系结构等。Docker是一种增加了高级API的LinuX Container(LXC)技术,提供了能够独立运行Unix进程的轻量级虚拟化解决方案。它提供了一种在安全、可重复的环境中自动部署软件的方式。

特点
文件系统隔离:每个进程容器运行在一个完全独立的根文件系统里。

资源隔离:系统资源,像CPU和内存等可以分配到不同的容器中,使用cgroup。

网络隔离:每个进程容器运行在自己的网络空间,虚拟接口和IP地址。

日志记录:Docker将会收集和记录每个进程容器的标准流(stdout/stderr/stdin),用于实时检索或批量检索。

变更管理:容器文件系统的变更可以提交到新的映像中,并可重复使用以创建更多的容器,无需使用模板或手动配置。

交互式shell:Docker可以分配一个虚拟终端并关联到任何容器的标准输入上,例如运行一个一次性交互shell。

也可概括如下:
标准 Linux 支持,提供对 Fedora/RHEL、Ubuntu、Gentoo、Arch 等发行版的开箱即用支持。

实现统一化存储驱动,包含对三种存储方式的支持:AUFS、VFS 和 DeviceMapper 的支持,正在开发包括 Btrfs 及 ZFS 在内的更多文件系统支持。

支持离线传输,可以将完整的运行时环境原封不动的进行迁移,方便企业级用户进行封包部署。

改善了端口映射,允许更复杂的端口映射逻辑,并时代调整了语法。

在明确允许的情况下可以在各个容器间搭建连接实现互通。

由于其基于LXC的轻量级虚拟化的特点,docker相比KVM之类最明显的特点就是启动快,资源占用小。因此对于构建隔离的标准化的运行环境,轻量级的PaaS, 构建自动化测试和持续集成环境,以及一切可以横向扩展的应用(尤其是需要快速启停来应对峰谷的web应用)。

构建标准化的运行环境,现有的方案大多是在一个baseOS上运行一套puppet/chef,或者一个image文件,其缺点是前者需要base OS许多前提条件,后者几乎不可以修改(因为copy on write 的文件格式在运行时rootfs是read only的)。并且后者文件体积大,环境管理和版本控制本身也是一个问题。

PaaS环境是不言而喻的,其设计之初和dotcloud的案例都是将其作为PaaS产品的环境基础。

因为其标准化构建方法(buildfile)和良好的REST API,自动测试和持续集成/部署能够很好的集成进来。

因为LXC轻量级的特点,其启动快,而且docker能够只加载每个container变化的部分,这样资源占用小,能够在单机环境下与KVM之类的虚拟化方案相比能够更加快速和占用更少资源。

Docker技术介绍需要主机上具备以下组件:overlayfs、iproute2、iptables、libcgroup-tools、util-linux >= 2.25.2、coreutils >= 7.5。大部分功能在 centos7 上都是满足的,overlayfs 可以通过 modprobe overlay 挂载。

Namespace

Linux Namespace 是 Linux 提供的一种内核级别环境隔离的方法。学习过 Linux 的基础知识应该对 chroot 命令比较熟悉(通过修改根目录把用户限制在一个特定目录下),chroot 提供了一种简单的隔离模式:chroot 内部的文件系统无法访问外部的内容。Linux Namespace 在此基础上,提供了对 UTS、IPC、mount、PID、network、User 等的隔离机制。Namespace 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。有如下种类:


Namespace 相关的系统调用有 3 个,分别是 clone(),setns(),unshare()。
1、clone: 创建一个新的进程并把这个新进程放到新的 namespace 中
2、setns: 将当前进程加入到已有的 namespace 中
3、unshare: 使当前进程退出指定类型的 namespace,并加入到新创建的 namespace 中

上面的概念都比较抽象,来看看在 Linux 系统中怎么样去查看 namespace。系统中的每个进程都有 /proc/[pid]/ns/ 这样一个目录,里面包含了这个进程所属 namespace 的信息,里面每个文件的描述符都可以用来作为 setns 函数 (2.1.2) 的 fd 参数。

# ls -l /proc/$$/ns
total 0
lrwxrwxrwx 1 root root 0 Jan 17 21:43 ipc -> ipc:[4026531839]
lrwxrwxrwx 1 root root 0 Jan 17 21:43 mnt -> mnt:[4026531840]
lrwxrwxrwx 1 root root 0 Jan 17 21:43 net -> net:[4026531956]
lrwxrwxrwx 1 root root 0 Jan 17 21:43 pid -> pid:[4026531836]
lrwxrwxrwx 1 root root 0 Jan 17 21:43 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Jan 17 21:43 uts -> uts:[4026531838]
 
#这些 namespace 文件都是链接文件。链接文件的内容的格式为 xxx:[inode number]。

其中的 xxx 为 namespace 的类型,inode number 则用来标识一个 namespace,我们也可以把它理解为 namespace 的 ID。如果两个进程的某个 namespace 文件指向同一个链接文件,说明其相关资源在同一个 namespace 中。以ipc:[4026531839]例,ipc是namespace的类型,4026531839是inode number,如果两个进程的ipc namespace的inode number一样,说明他们属于同一个namespace。这条规则对其他类型的namespace也同样适用。
 
#从上面的输出可以看出,对于每种类型的namespace,进程都会与一个namespace ID关联。
 
#当一个namespace中的所有进程都退出时,该namespace将会被销毁。在 /proc/[pid]/ns 里放置这些链接文件的作用就是,一旦这些链接文件被打开,只要打开的文件描述符(fd)存在,那么就算该 namespace 下的所有进程都结束了,但这个 namespace 也会一直存在,后续的进程还可以再加入进来。

IPC Namespace
用来隔离 System V IPC objects 和 POSIX message queues。其中 System V IPC objects 包含消息列表 Message queues、信号量 Semaphore sets 和共享内存 Shared memory segments。为了展现区分 IPC Namespace 会使用到 ipc 相关命令:

#nsenter: 加入指定进程的指定类型的namespace中,然后执行参数中指定的命令。
#命令格式:nsenter [options] [program [arguments]]
#示例:nsenter –t 27668 –u –I /bin/bash
#
#unshare: 离开当前指定类型的namespace,创建且加入新的namesapce,然后执行参数中执行的命令。
#命令格式:unshare [options] program [arguments]
#示例:unshare --fork --pid --mount-proc readlink /proc/self
#
#ipcmk:创建shared memory segments, message queues, 和semaphore arrays
#参数-Q:创建message queues
#ipcs:查看shared memory segments, message queues, 和semaphore arrays的相关信息
#参数-a:显示全部可显示的信息
#参数-q:显示活动的消息队列信息


Net Namespace
Network namespace 用来隔离网络设备,IP 地址,端口等。每个 namespace 将会有自己独立的网络栈,路由表,防火墙规则,socket 等。每个新的 network namespace 默认有一个本地环回接口,除了 lo 接口外,所有的其他网络设备(物理 / 虚拟网络接口,网桥等)只能属于一个 network namespace。每个 socket 也只能属于一个 network namespace。当新的 network namespace 被创建时,lo 接口默认是关闭的,需要自己手动启动起。标记为 "local devices" 的设备不能从一个 namespace 移动到另一个 namespace,比如 loopback, bridge, ppp 等,我们可以通过 ethtool -k 命令来查看设备的 netns-local 属性。

使用以下命令来创建 net namespace:
ip netns: 管理网络namespace
用法:
ip netns list
ip netns add NAME
ip netns set NAME NETNSID
ip [-all] netns delete [NAME]


Cgroup

Cgroup 和 namespace 类似,也是将进程进行分组,但它的目的和 namespace 不一样,namespace 是为了隔离进程组之间的资源,而 cgroup 是为了对一组进程进行统一的资源监控和限制。

作用:
1、资源限制 (Resource limiting): Cgroups 可以对进程组使用的资源总额进行限制。如对特定的进程进行内存使用上限限制,当超出上限时,会触发 OOM。
2、优先级分配 (Prioritization): 通过分配的 CPU 时间片数量及硬盘 IO 带宽大小,实际上就相当于控制了进程运行的优先级。
3、资源统计(Accounting): Cgroups 可以统计系统的资源使用量,如 CPU 使用时长、内存用量等等,这个功能非常适用于计费。
4、进程控制(Control):Cgroups 可以对进程组执行挂起、恢复等操作。

组成:
1、task: 在 Cgroups 中,task 就是系统的一个进程。

2、cgroup: Cgroups 中的资源控制都以 cgroup 为单位实现的。cgroup 表示按照某种资源控制标准划分而成的任务组,包含一个或多个子系统。一个任务可以加入某个 cgroup,也可以从某个 cgroup 迁移到另外一个 cgroup。

3、subsystem:  一个 subsystem 就是一个内核模块,被关联到一颗 cgroup 树之后,就会在树的每个节点(进程组)上做具体的操作。subsystem 经常被称作 "resource controller",因为它主要被用来调度或者限制每个进程组的资源,但是这个说法不完全准确,因为有时我们将进程分组只是为了做一些监控,观察一下他们的状态,比如 perf_event subsystem。到目前为止,Linux 支持 13 种 subsystem(Cgroup v1),比如限制 CPU 的使用时间,限制使用的内存,统计 CPU 的使用情况,冻结和恢复一组进程等。

4、hierarchy: 一个 hierarchy 可以理解为一棵 cgroup 树,树的每个节点就是一个进程组,每棵树都会与零到多个 subsystem 关联。在一颗树里面,会包含 Linux 系统中的所有进程,但每个进程只能属于一个节点(进程组)。系统中可以有很多颗 cgroup 树,每棵树都和不同的 subsystem 关联,一个进程可以属于多颗树,即一个进程可以属于多个进程组,只是这些进程组和不同的 subsystem 关联。如果不考虑不与任何 subsystem 关联的情况(systemd 就属于这种情况),Linux 里面最多可以建 13 颗 cgroup 树,每棵树关联一个 subsystem,当然也可以只建一棵树,然后让这棵树关联所有的 subsystem。当一颗 cgroup 树不和任何 subsystem 关联的时候,意味着这棵树只是将进程进行分组,至于要在分组的基础上做些什么,将由应用程序自己决定,systemd 就是一个这样的例子。

查看当前系统支持的 subsystem,通过/proc/cgroups查看当前系统支持哪些subsystem
# cat /proc/cgroups
#subsys_name    hierarchy       num_cgroups     enabled
cpuset              11              1           1
cpu                 4               67          1
cpuacct             4               67          1
memory              5               69          1
devices             7               62          1
freezer             8               1           1
net_cls             6               1           1
blkio               9               62          1
perf_event          3               1           1
hugetlb             2               1           1
pids                10              62          1
net_prio            6               1           1
 
#字段含义
#subsys_name: subsystem的名称
#hierarchy:subsystem所关联到的cgroup树的ID,如果多个subsystem关联到同一颗cgroup树,那么他们的这个字段将一样,比如这里的cpu和cpuacct就一样,表示其绑定到了同一颗树。如果出现下面的情况,这个字段将为0:
    当前subsystem没有和任何cgroup树绑定
    当前subsystem已经和cgroup v2的树绑定
    当前subsystem没有被内核开启
#num_cgroups: subsystem所关联的cgroup树中进程组的个数,也即树上节点的个数
#enabled: 1表示开启,0表示没有被开启(可以通过设置内核的启动参数“cgroup_disable”来控制subsystem的开启).

查看当前shell进程所属的cgroup
# cat /proc/$$/cgroup
11:cpuset:/
10:pids:/system.slice/sshd.service
9:blkio:/system.slice/sshd.service
8:freezer:/
7:devices:/system.slice/sshd.service
6:net_prio,net_cls:/
5:memory:/system.slice/sshd.service
4:cpuacct,cpu:/system.slice/sshd.service
3:perf_event:/
2:hugetlb:/
1:name=systemd:/system.slice/sshd.service
 
#字段含义(以冒号分为3列):
1、cgroup树ID,对应/proc/cgroups中的hierachy。
2、cgroup所绑定的subsystem,多个subsystem使用逗号分隔。name=systemd表示没有和任何subsystem绑定,只是给他起了个名字叫systemd。
3、进程在cgroup树中的路径,即进程所属的cgroup,这个路径是相对于挂载点的相对路径。

cgroup 相关的所有操作都是基于内核中的 cgroup virtual filesystem,使用 cgroup 很简单,挂载这个文件系统就可以了。一般情况下都是挂载到 /sys/fs/cgroup 目录下,当然也可以挂载到其它任何目录。

查看下当前系统 cgroup 挂载情况,过滤系统挂载可以查看cgroup。
# mount |grep cgroup

#如果系统中没有挂载cgroup,可以使用mount命令创建cgroup
#挂载根cgroup
# mkdir /sys/fs/cgroup
# mount -t tmpfs cgroup_root /sys/fs/cgroup
 
#将cpuset subsystem关联到/sys/fs/cgroup/cpu_memory
# mkdir /sys/fs/cgroup/cpuset
# sudo mount -t cgroup cpuset -o cgroup /sys/fs/cgroup/cpuset/
 
#将cpu和memory subsystem关联到/sys/fs/cgroup/cpu_memory
# mkdir /sys/fs/cgroup/cpu_memory
# sudo mount -n -t cgroup -o cpu,memory cgroup /sys/fs/cgroup/cpu_memory

除了 mount 命令之外我们还可以使用以下命令对 cgroup 进行创建、属性设置等操作,还可以使用命令来创建和管理 cgroup。Centos操作系统可以通过yum install cgroup-tools 来安装,安装完成后有如下指令可用。

cgcreate: 在层级中创建新cgroup。
用法: cgcreate [-h] [-t <tuid>:<tgid>] [-a <agid>:<auid>] [-f mode] [-d mode] [-s mode] -g <controllers>:<path> [-g ...]
示例: cgcreate -g *:student -g devices:teacher //在所有的挂载hierarchy中创建student cgroup,在devices hierarchy挂载点创建teacher cgroup

cgset: 设置指定cgroup(s)的参数
用法: cgset [-r <name=value>] <cgroup_path> ...
示例: cgset -r cpuset.cpus=0-1 student //将student cgroup的cpuset控制器中的cpus限制为0-1

cgexec: 在指定的cgroup中运行任务
用法: cgexec [-h] [-g <controllers>:<path>] [--sticky] command [arguments]
示例: cgexec -g cpu,memory:test1 ls -l //在cpu和memory控制器下的test1 cgroup中运行ls -l命令

Rootfs

Rootfs 是 Docker 容器在启动时内部进程可见的文件系统,即 Docker 容器的根目录。rootfs 通常包含一个操作系统运行所需的文件系统,例如可能包含典型的类 Unix 操作系统中的目录系统,如 /dev、/proc、/bin、/etc、/lib、/usr、/tmp 及运行 Docker 容器所需的配置文件、工具等。类似 Linux 启动会先用只读模式挂载 rootfs,运行完完整性检查之后,再切换成读写模式一样。Docker deamon 为 container 挂载 rootfs 时,也会先挂载为只读模式,但是与 Linux 做法不同的是,在挂载完只读的 rootfs 之后,Docker deamon 会利用联合挂载技术(Union Mount)在已有的 rootfs 上再挂一个读写层。container 在运行过程中文件系统发生的变化只会写到读写层,并通过 whiteout 技术隐藏只读层中的旧版本文件。

Docker 支持不同的存储驱动,包括 aufs、devicemapper、overlay2、zfs 和 vfs 等,目前在 Docker 中,overlay2 取代了 aufs 成为了推荐的存储驱动。

overlayFS
overlayfs是联合挂载技术的一种实现。除了 overlayFS 以外还有 aufs,VFS,Brtfs,device mapper 等技术。虽然实现细节不同,但是他们做的事情都是相同的。Linux 内核为 Docker 提供的 overalyFS 驱动有 2 种:overlay2 和 overlay,overlay2 是相对于 overlay 的一种改进,在 inode 利用率方面比 overlay 更有效。它通过三个目录来实现:lower 目录、upper 目录、以及 work 目录。三种目录合并出来的目录称为 merged 目录。
1、lower:可以是多个,是处于最底层的目录,作为只读层。
2、upper:只有一个,作为读写层。
3、work:为工作基础目录,挂载后内容会被清空,且在使用过程中其内容用户不可见。
4、merged:为最后联合挂载完成给用户呈现的统一视图,也就是说 merged 目录里面本身并没有任何实体文件,给我们展示的只是参与联合挂载的目录里面文件而已,真正的文件还是在 lower 和 upper 中。所以,在 merged 目录下编辑文件,或者直接编辑 lower 或 upper 目录里面的文件都会影响到 merged 里面的视图展示。


merged 层目录会显示离它最近层的文件。层级关系中 upperdir 比 lowerdir 更靠近 merged 层,而多个 lowerdir 的情况下,写的越靠前的目录离 merged 层目录越近。相同文件名的文件会依照层级规则进行 “覆盖”。

overlayFS 如何工作

读:
1、如果文件在容器层(upperdir),直接读取文件;
2、如果文件不在容器层(upperdir),则从镜像层(lowerdir)读取。

写:
1、首次写入: 如果在 upperdir 中不存在,overlay 执行 cow 操作,把文件从 lowdir 拷贝到 upperdir,由于 overlayfs 是文件级别的(即使文件只有很少的一点修改,也会产生的 cow 的行为),后续对同一文件的在此写入操作将对已经复制到容器的文件的副本进行操作。值得注意的是,cow 操作只发生在文件首次写入,以后都是只修改副本;
2、删除文件和目录: 当文件在容器被删除时,在容器层(upperdir)创建 whiteout 文件,镜像层 (lowerdir) 的文件是不会被删除的,因为他们是只读的,但 whiteout 文件会阻止他们显示。



Docker的常用命令简介

主要可分为操作、镜像、容器命令。

一、操作类命令

1、启动docker:systemctl start docker

2、停止docker:systemctl stop docker

3、重启docker:systemctl restart docker

4、查看docker状态:systemctl status docker

5、开机启动:systemctl enable docker

6、查看docker概要信息:docker info

7、查看docker总体帮助文档:docker --help

8、查看docker命令帮助文档:docker 具体命令 --help

二、镜像命令

1、docker images:列出本地主机上的镜像

OPTION字段说明:-a 列出所有本地镜像,-q 只显示镜像ID

2、docker search:某个镜像名字:查找某个镜像
加上 --limit 5 redis,可以限制查看redis镜像的数量为5
docker search --filter&=STARS=9000 mysql #搜索 STARS > 9000的 mysql 镜像

3、docker pull 某个镜像名字:下载镜像
也可以输入 docker pull 镜像名字:TAG,选择下载的版本

4、docker system df: 查看镜像/容器/数据卷所占的内存空间

5、docker rmi 某个镜像名字的ID:删除某个镜像

docker rmi -f 镜像ID    删除单个镜像

docker rmi -f 镜像名1:TAG 镜像名2:TAG 删除多个镜像

docker rmi -f $(docker images -qa)  删除全部镜像

docker images 显示的各个选项说明:
REPOSITORY:表示镜像的仓库源
TAG:镜像的标签版本号
lMAGE ID:镜像ID
CREATED:镜像创建时间
SIZE:镜像大小
同一仓库源可以有多个TAG版本,代表这个仓库源的不同个版本,使用REPOSITORY:TAG来定义不同的镜像。如果不指定一个镜像的版本标签,例如只使用ubuntu,docker 将默认使用ubuntu:latest镜像。

镜像标签:
有时需要对一个镜像进行分类或者版本迭代操作。比如:一个微服务已经打为docker镜像,但是想根据环境进行区分为develop环境与alpha环境;这时候就可以使用Tag,来进对镜像做一个标签添加,从而行进区分;版本迭代逻辑也是一样,根据不同的tag进行区分:
# 分离为开发环境
app:develop-1.0.0   
# 分离为alpha环境
app:alpha-1.0.0


虚悬镜像是什么
仓库名、标签都是<none>的镜像,叫做虚悬镜像 dangling image。

三、容器命令

有镜像才能创建容器

1、容器的启动和创建

docker run [OPTIONS] IMAGES [COMMAND] [ARG...]

docker  run -it --name=myos ubuntu bash //用bash交互式启动一个名为myos的ubuntu的容器,不指定名字则随机分配名字

启动交互式容器(前台命令行)

2、列出当前所有运行的容器

docker ps [OPTIONS]

OPTIONS说明(常用):
-a :列出当前所有正在运行的容器+历史上运行过的
-l:显示最近创建的容器。
-n:显示最近n个创建的容器。
-q:静默模式,只显示容器编号。

3、退出容器

两种退出方式:exit 和 ctrl+q+p

1.exit -- run 进去,exit退出后,容器回停止。直接退出 (如果没有添加-d 参数(持久化运行容器) 该容器会被关闭)

2.ctri+q+p -- run进去,ctrl+q+p停止后,容器不会停止。(优雅退出 (无论是否添加-d 参数 容器都不会被关闭))

4、启动已经停止运行的容器

docker start 容器ID或者容器名

5、重启容器

docker restart 容器ID或者容器名

6、停止容器

docker stop 容器ID或者容器名

7、强制停止容器
docker kill 容器ID或者容器名

8、删除已经停止的容器
docker rm 容器ID

#删除一个容器
docker rm -f 容器名/容器ID

#删除多个容器 空格隔开要删除的容器名或容器ID
docker rm -f 容器名/容器ID 容器名/容器ID 容器名/容器ID

#删除全部容器
docker rm -f $(docker ps -aq)

强制删除(不推荐)
docker rm -f
docker rm -f $(docker ps -a-q)    //删除多个
docker ps -a -q | xargs docker rm   //删除多个

#进入容器(方式一)
docker exec -it 容器名/容器ID /bin/bash

#进入容器(方式二) -- 不推荐使用
docker attach 容器名/容器ID

9、启动守护式容器

在大部分的场景下希望docker的服务是在后台运行的,可以过-d指定容器的后台运行模式。
命令:docker -d 容器名

如:使用redis来演示:
前台交互式:docker run -it redis //退出后,进程被杀死

后台守护式:docker run -d redis // 退出后,进程在后台继续运行

10、查看容器日志
docker logs 容器ID网址:yii666.com

11、查看容器内运行的进程
docker  top 容器ID

12、查看容器内部细节
docker inspect 容器ID

13、进入正在运行的容器,并用命令行交互
命令1:docker exec -it 容器ID bashShell
命令2:docker attach 容器ID

两个命令的区别:

attach直接进入容器启动命令的终端,不会启动新的进程用exit退出,会导致容器的停止。
exec是在容器中打开新的终端,并且可以启动新的进程用exit退出,不会导致容器的停止。
推介使用docker exec 来进入容器,因为退出容器终端后,不会导致容器停止。


# 运行一个容器
# -restart=always 该容器随docker服务启动而自动启动
docker run -it -d --name 要取的别名 镜像名:Tag /bin/bash

命令参数说明:
-d:后台运行容器
-p:端口映射,格式为主机端口:容器端口
-e:设置环境变量,这里设置的是root密码
--name:设置容器别名
-v 挂载文件,格式为:宿主机绝对路径目录:容器内目录,

比如使用:-v /usr/local/mysql/logs:/var/log/mysql
将mysql容器存放日志文件的目录:/var/log/mysql挂载在宿主机的/usr/local/mysql/logs下。

# 示例
docker run --name mysql \
-v /myapp/mysql:/var/lib/mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=12Az56 \
-d mysql:8.0.20



Docker的常用命令介绍

从容器内拷贝文件到主机:
docker cp 容器ID:容器内路径 目的主机路径

导入和导出容器:
export 导出容器的内容流,作为一个tar归档文件(对应 import 命令)

import 从tar 包中的内容创建 一个新的文件系统再导入为镜像[对应export]

案例:docker export  容器ID > 文件名.tat

cat 文件名.tar / docker import-镜像用户/镜像名:镜像版本号

开机如何不自启

这里是的docker服务,非其中的容器服务。在docker下对容器的自启动控制见文章尾部。

旨在提供一种灵活的方式,实现Docker在开机时不自动启动容器的功能。通过该方案,用户可以根据自身需求选择性地启动Docker容器,从而减少开机时的资源占用,提升系统性能。在 Docker 中,容器是基于镜像创建的,每个容器都是独立的、隔离的运行环境。

Linux系统下通过配置systemd服务来控制Docker容器的启动。具体步骤如下:

1. 创建systemd服务配置文件
在/etc/systemd/system/目录下创建一个名为docker-autostart.service的文件,内容如下所示:
[Unit]
Description=Start Docker Containers
Wants=docker.service
After=docker.service

[Service]
ExecStart=/usr/bin/docker start -a CONTAINER_NAME
ExecStop=/usr/bin/docker stop CONTAINER_NAME

[Install]
WantedBy=multi-user.target

其中,CONTAINER_NAME为需要启动的Docker容器名称,可以根据实际情况进行修改。

2. 设置systemd服务开机自启
执行以下命令,设置docker-autostart.service服务为开机自启动:
systemctl enable docker-autostart.service

3. 启动/停止Docker容器
通过以下命令,手动启动/停止Docker容器:
systemctl start docker-autostart.service  # 启动Docker容器
systemctl stop docker-autostart.service   # 停止Docker容器

以下是一个简单的示例,展示如何使用本方案来控制Docker容器的启动。
Dockerfile
FROM ubuntu:latest
RUN apt-get update && apt-get install -y nginx
CMD ["nginx", "-g", "daemon off;"]

构建镜像
执行以下命令,将Dockerfile构建为镜像:
docker build -t my-nginx .

创建容器
执行以下命令创建一个名为my-nginx-container的容器:
docker run -d --name my-nginx-container my-nginx

创建systemd服务配置文件
docker-autostart.service的内容如下:
[Unit]
Description=Start Docker Containers
Wants=docker.service
After=docker.service

[Service]
ExecStart=/usr/bin/docker start -a my-nginx-container
ExecStop=/usr/bin/docker stop my-nginx-container

[Install]
WantedBy=multi-user.target

设置systemd服务开机自启
执行以下命令,设置docker-autostart.service服务为开机自启动:
systemctl enable docker-autostart.service

启动/停止Docker容器
通过以下命令,手动启动/停止Docker容器:
systemctl start docker-autostart.service  # 启动Docker容器
systemctl stop docker-autostart.service   # 停止Docker容器

上面通过配置systemd服务来控制Docker容器的启动,实现了在开机时不自动启动容器的功能。用户可以根据实际需求选择性地启动/停止容器,从而优化系统性能。适用于各类Linux系统。


docker如何停止容器

可以使用docker stop命令来实现,下面是停止容器的操作步骤:
1.打开终端或命令提示符窗口,并确保已经安装并正确配置了Docker。

2.使用以下命令列出当前正在运行的容器:
docker ps

这将显示所有正在运行的容器的列表,包括容器ID、名称、状态等信息。

3.找到要停止的容器的容器ID或名称。

4.使用以下命令停止容器,其中是容器的ID或名称:
docker stop

如果要停止容器ID为abcd1234的容器,可以运行以下命令:
docker stop abcd1234

这将向容器发送停止信号,并使容器停止运行。

5. 等待一段时间,直到容器完全停止。可以使用以下命令来验证容器的状态:
docker ps -a

这将显示所有容器的列表,包括已停止的容器。确保目标容器的状态显示为"Exited"。

现在已经成功停止了Docker容器。如果需要重新启动容器,可以使用docker start命令。


保存镜像为tar包
docker save -o redis.tar redis:1.0.1

加载tar包为镜像
docker load -i redis.tar

docker将容器内的文件复制到容器外
例如将mysql镜像中的log文件夹复制出来

构建镜像
# 构建一个新的镜像
docker commit -m="提交信息" -a="作者信息" 容器名/容器ID 提交后的镜像名:Tag

1.查看并找到容器id
docker ps
docker exec -it ITRunID /bin/bash

2.找到log目录的路径为/var/log

3.使用exit退出容器

docker cp 容器id:容器内文件路径 服务器文件路径

docker-compose.yaml文件配置

volumes:前面为服务器文件目录,后面为容器内文件目录

查看容器日志
docker logs -f --tail=99 容器id
docker logs -f 容器id

列出机器上运行的所有容器可以使用 docker container ls 命令。在旧的 Docker 版本中对应的命令为 docker ps。
列出的结果是按列显示的。每一列的值分别为:
Container ID:一开始的几个字符对应你的容器的唯一 ID
Image:运行容器的镜像名
Command:容器启动后运行的命令
Created:创建时间
Status:容器当前状态
Ports:与宿主端口相连接的端口信息
Names:容器名(如果没有命名你的容器,那么会随机创建)

查看 Docker 容器的历史纪录
在第一步使用了 -d 参数来将容器,在它一开始运行的时候,就从当前的 shell 中脱离出来。在这种情况下,不知道容器里面发生了什么。所以为了查看容器的历史纪录,Docker 提供了 logs 命令。它采用容器名称或 ID 作为参数。
# docker container logs freeoa_corit

这里使用了容器名称作为参数。

容器是一个使用宿主资源来运行的进程,这样可以在宿主系统的进程表中定位容器的进程。在宿主系统上确定容器进程。Docker 使用著名的 top 命令作为子命令的名称,来查看容器产生的进程。它采用容器的名称或 ID 作为参数。在旧版本的 Docker 中,只可运行 docker top 命令;新版本中 docker top 和 docker container top 命令都可以生效。

# docker container top freeoa_corit
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                15702               15690               0                   18:35               ?                   00:00:00            httpd -DFOREGROUND
bin                 15729               15702               0                   18:35               ?                   00:00:00            httpd -DFOREGROUND
bin                 15730               15702               0                   18:35               ?                   00:00:00            httpd -DFOREGROUND
bin                 15731               15702               0                   18:35               ?                   00:00:00            httpd -DFOREGROUND

# ps -ef |grep -i 15702
root     15702 15690  0 18:35 ?        00:00:00 httpd -DFOREGROUND
bin      15729 15702  0 18:35 ?        00:00:00 httpd -DFOREGROUND
bin      15730 15702  0 18:35 ?        00:00:00 httpd -DFOREGROUND
bin      15731 15702  0 18:35 ?        00:00:00 httpd -DFOREGROUND
root     15993 15957  0 18:59 pts/0    00:00:00 grep --color=auto -i 15702

在第一个输出中,列出了容器产生的进程的列表。它包含了所有细节,包括用户号、进程号,父进程号、开始时间、命令等等。这里所有的进程号都可以在宿主的进程表里搜索到。这就是在第二个命令里做得。这证明了容器确实是宿主系统中的进程。

停止 Docker 容器
只需要 stop 命令!同样采用容器名称或 ID 作为参数。
# docker container stop freeoa_corit

如何列出停止的或不活动的 Docker 容器
现在停止了容器,这时如果使用 ls 命令列出时它将不会出现在列表中。
# docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

所以在这种情况下,如果想要查看停止的或不活动的容器,需要在 ls 命令里同时使用 -a 参数。
# docker container ls -a

有了 -a 参数就可以查看已停止的容器。注意这些容器的状态被标注为已退出。既然容器只是一个进程,那么用“退出”比“停止”更合适!

如何(重新)启动 Docker 容器
现在来启动这个已停止的容器。这和运行一个容器有所区别。当运行一个容器时将启动一个全新的容器。当启动一个容器时将开始一个已经停止并保存了当时运行状态的容器。它将以停止时的状态重新开始运行。
# docker container start 容器ID

# docker container ls -a

移除 Docker 容器
使用 rm 命令来移除容器,不可以移除在运行中的容器。因此移除之前需要先停止容器,可以使用 -f 参数搭配 rm 命令来强制移除容器,但并不推荐这么做。
# docker container rm freeoa_corit

exec 进入容器

在使用 Docker 运行容器时,有时需要在容器内执行一些命令或者进入容器的运行环境进行调试。这就是 docker exec 命令。

docker exec 命令用于在正在运行的容器内部执行命令。通过该命令可以进入容器的运行环境,并且在容器内部执行任意的命令。其语法如下:
docker exec [OPTIONS] CONTAINER COMMAND [ARG...]

其中,OPTIONS 是一些可选参数,CONTAINER 是容器的名称或者 ID,COMMAND 是要在容器内部执行的命令,ARG 是命令的参数。

使用示例
下面通过一个示例来演示如何使用 docker exec 进入容器并执行命令。假设已经在本地创建了一个名为 mycontainer 的容器,并且容器正在运行中。现在要进入这个容器并在容器内执行一个命令。首先可以使用 docker ps 命令查看正在运行的容器的列表:
$ docker ps
CONTAINER ID   IMAGE        COMMAND                  CREATED         STATUS         PORTS     NAMES
abc123   nginx        "nginx -g 'daemon of…"   10 minutes ago  Up 10 minutes  80/tcp    mycontainer

可以看到的容器 mycontainer 正在运行中。接下来使用 docker exec 命令进入容器并执行一个命令。比如可以在容器内部执行 ls 命令来列出容器内的文件和目录:
$ docker exec -it mycontainer ls
bin
boot
dev
etc
...

在上面的例子中使用 -it 参数来告诉 Docker 在交互模式下执行命令,并且指定了要进入的容器的名称 mycontainer。然后执行了 ls 命令来列出容器内的文件和目录。

进入容器的运行环境
除了在容器内执行命令,还可以通过 docker exec 命令进入容器的运行环境,在容器内进行调试或者执行多个命令。在进入容器的运行环境时需要使用 -it 参数,这样我们可以进入容器的交互模式。例如使用以下命令进入容器的 Bash 终端:
$ docker exec -it mycontainer bash
root@freeoa.net:/#

在上面的例子中使用了 -it 参数来进入容器的交互模式,并且指定了要进入的容器的名称 mycontainer。然后执行了 bash 命令来进入容器的 Bash 终端。一旦进入容器的运行环境,就可以执行任意的命令,就像在本地的终端中一样。例如可以列出容器内的文件和目录:
root@freeoa.net:/# ls
bin
boot
dev
etc
...


其他操作
# 查看docker磁盘占用总体情况
du -hs /var/lib/docker/

# 查看Docker的磁盘使用具体情况
docker system df

# ----------------------------删除 无用的容器和 镜像
# 删除异常停止的容器
docker rm `docker ps -a | grep Exited | awk '{print $1}'`

# 删除名称或标签为none的镜像
docker rmi -f  `docker images | grep '<none>' | awk '{print $3}'`

# 清除所有无容器使用的镜像 (只要是镜像无容器使用(容器正常运行)都会被删除,包括容器临时停止)
docker system prune -a


关闭容器自启动(docker自动启停容器,非docker进程本身)

对于使用Docker的开发者来说,遇到过容器在自动重启的问题肯定不是一回两回了。这是为什么呢?这是因为Docker默认的容器自动重启策略为“常规”,也就是说无论容器退出原因是什么,在Docker启动时都会尝试重启容器。

如果想要关闭容器自启,有两种操作方法:
方法一:使用Docker命令

docker update --restart=no 容器ID

方法二:通过Docker Compose文件实现关闭自启
version: '3'
services:
service1:
image: image1
restart: no

以上两种方法都可以成功关闭容器的自启动,开发者可以依据自身需求来选择使用哪种方法。提醒大家在关闭容器自启功能之前一定要三思而行,因为自动重启可以保持服务的高可用性,降低服务的停服时间。如果长期运行的容器出现了故障,并且容器配置了自动重启策略,那么容器在出现故障后可以迅速重启,降低故障对系统的影响。


Dockerfile

Dockerfile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。

构建三步骤:
1.编写Dockerfile文件
2.docker build命令构建镜像
3.docker run依镜像运行容器实例

# 构建镜像(需要在Dockerfile同级目录下构建)
docker build -t cat:1.0 .

# 说明(-t:设置 镜像的名字及tag)(最后的. 为当前目录)
FROM
基础镜像&#xff0c;当前新镜像是基于哪个镜像的&#xff0c;指定一个已经存在的镜像作为模板&#xff0c;第一条必须是from

MAINTAINER
镜像维护者的姓名和邮箱地址

RUN
容器构建时需要运行的命令
两种格式

shell格式(1)
RUN yum -y install vim

exec格式(2)
["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
 
注意:RUN 是在 docker build时运行

EXPOSE
当前容器对外暴露出的端口

WORKDIR
指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点

USER
指定该镜像以什么样的用户去执行,如果都不指定,默认是root

ENV
用来在构建镜像过程中设置环境变量
 
ENV MY_PATH /usr/mytest
这个环境变量可以在后续的任何RUN指令中使用,这就如同在命令前面指定了环境变量前缀一样;也可以在其它指令中直接使用这些环境变量。

比如,WORKDIR $MY_PATH

ADD
将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包

COPY
类似ADD,拷贝文件和目录到镜像中。

将从构建上下文目录中 <源路径> 的文件/目录复制到新的一层的镜像内的 <目标路径> 位置
COPY src dest
COPY ["src", "dest"]
<src源路径>,源文件或者源目录
<dest目标路径>,容器内的指定路径,该路径不用事先建好,路径不存在的话会自动创建。

VOLUME
容器数据卷,用于数据保存和持久化工作

CMD
指定容器启动后的要干的事情
 
注意:Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效,CMD 会被 docker run 之后的参数替换


docker-compose.yml

Compose 是 Docker 公司推出的一个工具软件,可以管理多个 Docker 容器组成一个应用。需要定义一个 YAML 格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后只要一个命令,就能同时启动/关闭这些容器。

简而言之:Docker-Compose是Docker官方的开源项目, 负责实现对Docker容器集群的快速编排。

docker-compose -h        # 查看帮助

docker-compose up        # 启动所有docker-compose服务
docker-compose up -d        # 启动所有docker-compose服务并后台运行

docker-compose down        # 停止并删除容器、网络、卷、镜像
docker-compose exec  yml里面的服务id        # 进入容器实例内部yml文件中写的服务id /bin/bash

docker-compose ps        # 展示当前docker-compose编排过的运行的所有容器
docker-compose top        # 展示当前docker-compose编排过的容器进程
 
docker-compose logs  yml里面的服务id    # 查看容器输出日志

docker-compose config    # 检查配置
docker-compose config -q    # 检查配置&#xff0c;有问题才有输出

docker-compose restart    # 重启服务
docker-compose start    # 启动服务
docker-compose stop    # 停止服务


最新版本:1.6
改进内容包括 Docker 引擎、Docker Hub 和文档。值得关注的新特性包括:

重启策略:docker 命令增加了 --restart 参数来指定容器的重启策略。

独立进程中的Docker代理
Docker 用户空间代理将外部的访问路由到你的目标容器中,目前该进程已经独立开来,每个连接有一个独立的进程在运行,此举大大降低守护进程的负载,提升了稳定性和效率。
Labels 可以让你将用户自定义元数据附加到容器和印象,并为其他工具所用
Windows 客户端预览版、Compose 支持开发环境外的应用
更多内容请看发行说明

最新版本:29
29.0.0 现已于2025年11月中旬发布,此版本包含多项破坏性变更和弃用项。升级前建议仔细阅读版本说明,主要更新内容如下:
现可以通过设置 Docker 守护进程的 firewall-backend 选项为 nftables 来启用对 nftables 的实验性支持。更多信息可参阅 Docker Engine docs。
containerd 镜像存储现在是全新安装的默认镜像存储。此变更不适用于配置了 userns-remap 的守护进程。

Go 模块 github.com/docker/docker 已被弃用,建议改用 github.com/moby/moby/client 和 github.com/moby/moby/api。github.com/moby/moby 模块被视为内部实现细节 —— 唯一受支持的公共模块是 client 和 api。从 v29 版本开始,发布版本将添加 docker- 前缀(例如 docker-v29.0.0。此变更仅影响 Go 模块用户和软件包维护者。
守护进程现在需要 API 版本 v1.44 或更高版本(Docker v25.0+)。
Debian armhf(32 位)软件包现在面向 ARMv7 CPU,将无法在 ARMv6 设备上运行。
官方的 Raspbian(32 位)软件包已不再提供。对于 64 位设备,建议使用 Debian arm64 软件包;对于 32 位 ARMv7 设备,建议使用 Debian armhf 软件包。
cgroup v1 已弃用。支持将持续到 2029 年 5 月,但官方建议尽快迁移到 cgroup v2。
Docker Content Trust 已从 Docker CLI 中移除。可作为独立插件构建。


官方主页:https://www.docker.com/

Docker中文指南:http://www.widuu.com/chinese_docker/