docker(一)介绍与基础命令
之前介绍过什么是docker,这里说点细节的
Docker 包括三个基本概念:
镜像(Image):Docker 镜像(Image),就相当于是一个 root 文件系统。比如官方镜像 ubuntu:16.04 就包含了完整的一套 Ubuntu16.04 最小系统的 root 文件系统。
容器(Container):镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
仓库(Repository):仓库可看成一个代码控制中心,用来保存镜像。
Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器。
Docker 容器通过 Docker 镜像来创建。
容器与镜像的关系类似于面向对象编程中的对象与类。
概念 | 说明 |
---|---|
Docker 镜像(Images) | Docker 镜像是用于创建 Docker 容器的模板,比如 centos系统。 |
Docker 容器(Container) | 容器是独立运行的一个或一组应用,是镜像运行时的实体。 |
Docker 客户端(Client) | Docker 客户端通过命令行或者其他工具使用 Docker SDK (https://docs.docker.com/develop/sdk/) 与 Docker 的守护进程通信。 |
Docker 主机(Host) | 一个物理或者虚拟的机器用于执行 Docker 守护进程和容器。 |
Docker Registry | 1.Docker 仓库用来保存镜像,可以理解为代码控制中的代码仓库。2.Docker Hub(https://hub.docker.com) 提供了庞大的镜像集合供使用。3.一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。4.通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签> 的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。 |
Docker Machine | Docker Machine是一个简化Docker安装的命令行工具,通过一个简单的命令行即可在相应的平台上安装Docker,比如VirtualBox、 Digital Ocean、Microsoft Azure。 |
安装
rpm包下载
# 阿里云开源镜像站:https://mirrors.aliyun.com/docker-ce/linux/centos/7/x86_64/stable/Packages/
# 官方站点:https://docs.docker.com/ https://docs.docker.com/install/linux/docker-ce/centos/
# 二者下载皆可 但是阿里云更快一些
自动安装命令如下:
curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
也可以使用国内 daocloud 一键安装命令:
curl -sSL https://get.daocloud.io/docker | sh
手动安装
1.卸载旧版本
yum remove docker docker-client docker-client-latest
docker-common docker-latest docker-latest-logrotate
docker-logrotate docker-engine
2.安装依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
#yum-utils 提供了 yum-config-manager ,并且 device mapper 存储驱动程序需要
#device-mapper-persistent-data 和 lvm2。
3.添加软件源
#采用的是阿里云的源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
4.安装
yum install docker-ce docker-ce-cli containerd.io
#containerd.io -守护进程与操作系统API(在本例中是LXC-Linux容器)接口,本质上是将Docker与操作系统分离,还为非Docker容器管理器提供容器服务
#docker-ce -docker守护进程,这是完成所有管理工作的部分,需要Linux上的另外两个
#docker ce cli -cli工具来控制守护进程,如果你想控制一个远程docker守护进程,你可以自己安装它们
#如果提示您接受 GPG 密钥,请选是。
4.1如果启用了多个 Docker 仓库,则在未在 yum install 或
yum update 命令中指定版本的情况下,进行的安装或更新将始终安装
最高版本,这可能不适合您的稳定性需求。
#列出并排序您存储库中可用的版本。此示例按版本号(从高到低)对结果进行排序。
yum list docker-ce --showduplicates | sort -r
#通过其完整的软件包名称安装特定版本,该软件包名称是软件包名称
#(docker-ce)加上版本字符串(第二列),从第一个冒号(:)
#一直到第一个连字符,并用连字符(-)分隔。
#例如:docker-ce-18.09.1。
yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io
5.启动 Docker
systemctl start docker
docker version
docker run hello-world
#通过运行 hello-world 映像来验证是否正确安装
加速
国内从 DockerHub 拉取镜像有时会遇到困难,此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:
网易:https://hub-mirror.c.163.com/
阿里云:https://<你的ID>.mirror.aliyuncs.com
七牛云加速器:https://reg-mirror.qiniu.com
当配置某一个加速器地址之后,若发现拉取不到镜像,请切换到另一个加速器地址。国内各大云服务商均提供了 Docker 镜像加速服务,建议根据运行 Docker 的云平台选择对应的镜像加速服务。
阿里云镜像获取地址:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors,登陆后,左侧菜单选中镜像加速器就可以看到你的专属地址了:
对于使用 systemd 的系统,请在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件):
{"registry-mirrors":["https://reg-mirror.qiniu.com/"]}
#"自己的加速地址"
systemctl daemon-reload
systemctl restart docker
"""
解决“警告”问题:
docker info
可以看到
WARNING: IPv4 forwarding is disabled
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
cd /etc/sysctl.d
# 过滤内核参数 将这些改成 1
sysctl -a | grep forwarding
sysctl -a | grep bridge
怎么改:
在当前目录下
vim docker.conf
inet.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.conf.all.forwarding = 1
net.ipv4.conf.all.mc_forwarding = 1
net.ipv4.conf.default.forwarding = 1
net.ipv4.conf.default.mc_forwarding = 1
net.ipv4.conf.docker0.forwarding = 1
net.ipv4.conf.docker0.mc_forwarding = 1
net.ipv4.conf.eth0.forwarding = 1
net.ipv4.conf.eth0.mc_forwarding = 1
net.ipv4.conf.lo.forwarding = 1
net.ipv4.conf.lo.mc_forwarding = 1
net.bridge.bridge-nf-call-ip6tables = 1
sysctl --system #使其立即生效
"""
文件目录与镜像结构
# docker的所有数据都存放在此
[root@docker docker]# ls
builder containers network plugins swarm trust
buildkit image overlay2 runtimes tmp volumes
[root@docker docker]# pwd
/var/lib/docker
docker镜像
镜像是docker容器的基石,容器是镜像的运行实例,有了镜像才能启动容器
[root@foundation0 ~]# docker images rhel7
REPOSITORY TAG IMAGE ID CREATED SIZE
rhel7 latest 0a3eb3fde7fd 4 years ago 140 MB
为什么一个rhel7只有140MB
"""
linux操作系统由内核空间和用户空间组成(rootfs bootfs)
内核空间是kernel,linux刚启动的时候会加载bootfs文件系统,之后bootfs会被卸载掉
用户空间的文件系统是rootfs,包括我们熟悉鹅/dev,/proc,/bin 等目录
对于base镜像来说,底层直接用host的kernel,自己只需要提供rootfs就行了
而对于一个精简版的os,rootfs可以很小,只需要包括最基本的命令,工具和程序就可以了
"""
# base镜像提供的是最小安装的Linux发行版本
# 支持运行多种Linux OS 不同的Linux发行版的区主要就是rootfs
#比如Ubuntu 使用upstat 管理服务 apt管理软件包 ,
#而centos 7 使用systemd和yum 这些都是用户空间上的区别
#linux kernel差别不大
##注意:容器的内核版本和宿主机的版本是一致的
##可通过:uname -r来看
##所有容器都共用host的kernel,在容器中没办法对kernel升级,
#如果容器对kernel有要求(比如某个应用只能在某个kernel版本下运行),
#则不建议用容器,这种场景虚拟机更适合
#镜像的分层结构
docker支持通过扩展现有镜像,创建新的镜像
实际上,docker hub中99%的镜像都是通过在base镜像中安装和配置需要的软件构建出来的。
新的镜像是从base镜像一层一层叠加生成的,每安装一个软件,就在现有的基础增加一层。
#为什么docker镜像要采用这种分层结构呢?
"""
最大的一个好处是:共享资源
比如:有多个镜像都从相同的base镜像构建而来,
那么docker host只需在磁盘上保存一份base镜像:同时内存中也只需加载一份base镜像,
就可以为所有容器服务了,而其镜像的每一层都可以被共享
那么一个疑问是:如果多个容器共享一份基础镜像,
当某个容器修改了基础镜像的内容,比如/etc下的文件,
这时其他容器的/etc是否也会被修改???
答案是不会
修改会被限制在单个容器内
这就是我们接下来要学习的容器copy-on-write特性
"""
可写的容器层
当容器启动时,一个新的可写层被加载到镜像的顶部
这一层通常被叫做“容器层”,“容器层”之下的都叫“镜像层”
所有对容器的改动,无论添加,删除,还是修改文件都只会发生在容器层,只有容器层是可写的,容器层下面的所有镜像层都是只读的
#注意:一个镜像层最多127层(镜像层最好不要太多)
#具体细节
"""
镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统,
如果不同层中有一个相同路径的文件,比如/a,上层的/a会覆盖下层的/a,
也就是说用户只能访问到山层中的文件/a,在容器层中,
用户看到的是一个叠加之后的文件系统
1.添加文件,在容器中创建文件时,新文件被添加到容器层中
2.读取文件,在容器中读取某个文件时,docker会从上往下依次在各个镜像层中查找到此文件,一旦找到,打开并读入内存
3.修改文件,在容器中修改已经存在的文件时,docker会从上往下依次在各个镜像层中查找到此文件,
一旦找到,立即将其复制到容器层,然后修改
4.删除文件,在容器中删除文件时,docker也是从上往下依次在镜像层中查找此文件,找到后,会在容器层中记录下此删除操作
"""
##只有当修改的时候才复制一份数据,这种特性被称作copy-on-write,可见,
#容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改
这样就解释了我们前面提出的问题:容器层记录对镜像的修改,
所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享
docker run -it --name ubuntu
#-it 以交互式模式开启一个终端
# 我们可以看到容器和虚拟机共享内核
# 到底怎么共享的呢?
[root@docker docker]# hostnamectl
Static hostname: docker
Icon name: computer-vm
Chassis: vm
Machine ID: e26d28698aed47fb9ec897d00ec96f27
Boot ID: 4f549a312dff439b80de72c1c74a0682
Virtualization: kvm
Operating System: Red Hat Enterprise Linux Server 7.5 (Maipo)
CPE OS Name: cpe:/o:redhat:enterprise_linux:7.5:GA:server
Kernel: Linux 3.10.0-862.el7.x86_64
Architecture: x86-64
[root@server3 sysctl.d]# docker run -it --name vm1 ubuntu
#run:创建并运行一个容器 -it:以交互式的形式 --name:给容器起个名字 ubuntu:镜像名称
Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
7413c47ba209: Pull complete
0fe7e7cbb2e8: Pull complete
1d425c982345: Pull complete
344da5c95cec: Pull complete
Digest: sha256:c303f19cfe9ee92badbbbd7567bc1ca47789f79303ddcef56f77687d4744cd7a
Status: Downloaded newer image for ubuntu:latest
Try 'uname --help' for more information.
root@b16f9eaab99e:/# uname -r
#可以见得 docker是对我们操作系统内核有一定的要求的
3.10.0-514.el7.x86_64
[root@docker docker]# docker run -it --name vm1 ubuntu
root@4154d58490f2:/# ls
bin dev home lib64 mnt proc run srv tmp var
boot etc lib media opt root sbin sys usr
root@4154d58490f2:/# touch file1 #改变的是容器层
root@4154d58490f2:/# touch file2
#但是,如果我们只是运行容器,然后退出不保存容器的话,容器中我们
#所做的工作是不会被保存和持久化的。
总结一下
"""
1.共享宿主机的kernel
2.base镜像提供的是最小的linux发行版
3.同一docker主机支持运行多种linux发行版
4.采用分层结构的最大好处是:共享资源
"""
"""
Copy-on-Write可写容器层
容器层意所有镜像层都是只读的
docker从上往下依次查找文件
容器层保存镜像变换的部分 并不会对镜像本身进行任何修改
一个镜像最多127层
"""
命令
docker command --help
容器使用
#获取镜像
如果我们本地没有 ubuntu 镜像,我们可以使用 docker pull 命令来载入 ubuntu 镜像:
docker pull ubuntu
运行容器
docker run 命令来在容器内运行一个应用程序。
docker run ubuntu:15.10 /bin/echo "Hello world"
各个参数解析:
docker: Docker 的二进制执行文件。
run: 与前面的 docker 组合来运行一个容器。
ubuntu:15.10 指定要运行的镜像,Docker 首先从本地主机上查找镜像是否存在,如果不存在,Docker 就会从镜像仓库 Docker Hub 下载公共镜像。
/bin/echo "Hello world": 在启动的容器里执行的命令
以上命令完整的意思可以解释为:Docker 以 ubuntu15.10 镜像创建一个新容器,然后在容器里执行 bin/echo "Hello world",然后输出结果。
#运行交互式的容器
docker 的两个参数 -i -t,让 docker 运行的容器实现"对话"的能力:
runoob@runoob:~$ docker run -i -t ubuntu:15.10 /bin/bash
root@0123ce188bd8:/#
各个参数解析:
-t: 在新容器内指定一个伪终端或终端。
-i: 允许你对容器内的标准输入 (STDIN) 进行交互。
注意第二行 root@0123ce188bd8:/
#,此时我们已进入一个 ubuntu15.10 系统的容器
我们尝试在容器中运行命令 cat /proc/version和ls分别查看当前系统的版本信息和当前目录下的文件列表
exit 命令或者使用 CTRL+D 来退出容器
#启动容器(后台模式)
docker run -itd --name ubuntu-test ubuntu /bin/bash
#加了 -d 参数默认不会进入容器,想要进入容器需要使用指令 docker exec(下面会介绍到)。
runoob@runoob:~$ docker run -d ubuntu:15.10 /bin/sh -c "while true; do echo hello world; sleep 1; done"
2b1b7a428627c51ab8810d541d759f072b4fc75487eed05812646b8534a2fe63
在输出中,我们没有看到期望的 "hello world",而是一串长字符
这个长字符串叫做容器 ID,对每个容器来说都是唯一的,我们可以通过容器 ID 来查看对应的容器发生了什么。
首先,我们需要确认容器有在运行,可以通过 docker ps 来查看:
runoob@runoob:~$ docker ps
CONTAINER ID IMAGE COMMAND ...
5917eac21c36 ubuntu:15.10 "/bin/sh -c 'while t…" ...
输出详情介绍:
CONTAINER ID: 容器 ID。
IMAGE: 使用的镜像。
COMMAND: 启动容器时运行的命令。
CREATED: 容器的创建时间。
STATUS: 容器状态。
'状态有7种:
created(已创建)
restarting(重启中)
running(运行中)
removing(迁移中)
paused(暂停)
exited(停止)
dead(死亡)'
PORTS: 容器的端口信息和使用的连接类型(tcp\udp)。
NAMES: 自动分配的容器名称。
在宿主主机内使用 docker logs 命令,查看容器内的标准输出:
docker logs 2b1b7a428627
docker logs amazing_cori
停止容器
我们使用 docker stop 命令来停止容器:
docker stop 2b1b7a428627
通过 docker ps 查看,容器已经停止工作:
runoob@runoob:~$ docker ps
可以看到容器已经不在了。
也可以用下面的命令来停止:
runoob@runoob:~$ docker stop amazing_cori
#启动已停止运行的容器
docker ps -a
docker start b750bbbcfd88
docker stop <容器 ID>
docker restart <容器 ID>
进入容器
在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:
docker attach
docker exec:推荐大家使用 docker exec 命令,因为此退出容器终端,不会导致容器的停止。
#attach 命令
docker attach 1e560fca3906
#如果从这个容器退出,会导致容器的停止。
#exec 命令
docker exec -it 243c32535da7 /bin/bash
#如果从这个容器退出,不会导致容器的停止
导出/入容器
#导出容器
如果要导出本地某个容器,可以使用 docker export 命令。
docker export 1e560fca3906 > ubuntu.tar
#导出容器 1e560fca3906 快照到本地文件 ubuntu.tar。
#导入容器快照
可以使用 docker import 从容器快照文件中再导入为镜像
cat docker/ubuntu.tar | docker import - test/ubuntu:v1
#将快照文件 ubuntu.tar 导入到镜像 test/ubuntu:v1:
也可以通过指定 URL 或者某个目录来导入,例如:
docker import http://example.com/exampleimage.tgz example/imagerepo
删除容器
删除容器使用 docker rm 命令:
docker rm -f 1e560fca3906
下面的命令可以清理掉所有处于终止状态的容器。
docker container prune
web容器的应用
#在docker容器中运行一个 Python Flask 应用来运行一个web应用。
docker pull training/webapp # 载入镜像
docker run -d -P training/webapp python app.py
参数说明:
-d:让容器在后台运行。
-P:将容器内部使用的网络端口随机映射到我们使用的主机上。
#查看 WEB 应用容器
使用 docker ps 来查看我们正在运行的容器:
docker ps
CONTAINER ID IMAGE COMMAND ... PORTS
d3d5e39ed9d3 training/webapp "python app.py" ... 0.0.0.0:32769->5000/tcp
Docker 开放了 5000 端口(默认 Python Flask 端口)映射到主机端口 32769 上。
这时我们可以通过浏览器访问WEB应用
ip:32769
我们也可以通过 -p 参数来设置不一样的端口:
docker run -d -p 5000:5000 training/webapp python app.py
docker ps
CONTAINER ID IMAGE PORTS NAMES
bf08b7f2cd89 training/webapp ... 0.0.0.0:5000->5000/tcp wizardly_chandrasekhar
d3d5e39ed9d3 training/webapp ... 0.0.0.0:32769->5000/tcp xenodochial_hoov
#容器内部的 5000 端口映射到我们本地主机的 5000 端口上。
网络端口的快捷方式
通过 docker ps 命令可以查看到容器的端口映射,docker 还提供了另一个快捷方式 docker port,使用 docker port 可以查看指定 (ID 或者名字)容器的某个确定端口映射到宿主机的端口号。
上面我们创建的 web 应用容器 ID 为 bf08b7f2cd89 名字为 wizardly_chandrasekhar。
我可以使用 docker port bf08b7f2cd89 或 docker port wizardly_chandrasekhar 来查看容器端口的映射情况。
docker port bf08b7f2cd89
5000/tcp -> 0.0.0.0:5000
docker port wizardly_chandrasekhar
5000/tcp -> 0.0.0.0:5000
查看日志
docker logs [ID或者名字] 可以查看容器内部的标准输出。
docker logs -f bf08b7f2cd89
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
192.168.239.1 - - [09/May/2016 16:30:37] "GET / HTTP/1.1" 200 -
192.168.239.1 - - [09/May/2016 16:30:37] "GET /favicon.ico HTTP/1.1" 404 -
-f: 让 docker logs 像使用 tail -f 一样来输出容器内部的标准输出。
从上面,我们可以看到应用程序使用的是 5000 端口并且能够查看到应用程序的访问日志。
查看进程与底层信息
我们还可以使用 docker top 来查看容器内部运行的进程
runoob@runoob:~$ docker top wizardly_chandrasekhar
UID PID PPID ... TIME CMD
root 23245 23228 ... 00:00:00 python app.py
使用 docker inspect 来查看 Docker 的底层信息。
它会返回一个 JSON 文件记录着 Docker 容器的配置和状态信息。
runoob@runoob:~$ docker inspect wizardly_chandrasekhar
[
{
"Id": "bf08b7f2cd897b5964943134aa6d373e355c286db9b9885b1f60b6e8f82b2b85",
"Created": "2018-09-17T01:41:26.174228707Z",
"Path": "python",
"Args": [
"app.py"
],
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 23245,
"ExitCode": 0,
"Error": "",
"StartedAt": "2018-09-17T01:41:26.494185806Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
......
停止与重启
#停止 WEB 应用容器
docker stop wizardly_chandrasekhar
wizardly_chandrasekhar
#重启WEB应用容器
已经停止的容器,我们可以使用命令 docker start 来启动。
docker start wizardly_chandrasekhar
wizardly_chandrasekhar
docker ps -l 查询最后一次创建的容器:
docker ps -l
CONTAINER ID IMAGE PORTS NAMES
bf08b7f2cd89 training/webapp ... 0.0.0.0:5000->5000/tcp wizardly_chandrasekhar
#正在运行的容器,我们可以使用 docker restart 命令来重启。
#我们可以使用 docker rm 命令来删除不需要的容器
runoob@runoob:~$ docker rm wizardly_chandrasekhar
wizardly_chandrasekhar
#删除容器时,容器必须是停止状态,否则会报如下错误
#或者rm -f
runoob@runoob:~$ docker rm wizardly_chandrasekhar
Error response from daemon: You cannot remove a running container bf08b7f2cd897b5964943134aa6d373e355c286db9b9885b1f60b6e8f82b2b85. Stop the container before attempting removal or force remove