Docker组件:
- Docker客户端:Docker命令,例如
docker pull centos:7
- Docker服务端:管理Docker服务,例如
systemctl status docker
- Image镜像:通过镜像才能运行容器
- Container容器:容器是一个轻量级的虚拟机
- Registry仓库:镜像存储在仓库中,可以存放在公共仓库,也可以搭建私有仓库
Docker底层:
- Cgroup:cgroup 实现资源限额,控制容器使用CPU、MEM、IO
- Namespace:实现资源隔离,每个容器都是独立运行,拥有自己的网络、资源,互不影响
Group
全称 Control Group。Linux 操作系统通过 cgroup 可以设置进程使用 CPU、内存 和 IO 资源的限额
可以在 /sys/fs/cgroup
中找到它。还是用例子来说明,启动一个容器,设置 --cpu-shares=512
在 /sys/fs/cgroup/cpu/docker
目录中,Linux 会为每个容器创建一个 cgroup 目录,以容器长ID 命名
目录中包含所有与 cpu 相关的 cgroup 配置,文件 cpu.shares 保存的就是 --cpu-shares
的配置,值为 512。
同样的,/sys/fs/cgroup/memory/docker
和 /sys/fs/cgroup/blkio/docker
中保存的是内存以及 Block IO 的 cgroup 配置。
Namespace
在每个容器中,我们都可以看到文件系统,网卡等资源,每个容器都会认为自己有一块独立的网卡,即使 host 上只有一块物理网卡。
这种方式非常好,它使得容器更像一个独立的计算机。Linux 实现这种方式的技术是 namespace。
namespace 管理着 host 中全局唯一的资源,并可以让每个容器都觉得只有自己在使用它。换句话说,namespace 实现了容器间资源的隔离。
Linux 使用了六种 Namespace,分别对应六种资源:Mount、UTS、IPC、PID、Network 和 User
Mount
:文件系统 容器有自己的 / 目录,可以执行 mount 和 umount 命令。当然我们知道这些操作只在当前容器中生效,不会影响到 host 和其他容器。UTS
:让容器有自己的 hostname。 默认情况下,容器的 hostname 是它的短ID,可以通过 -h 或 —hostname 参数设置。IPC
:让容器拥有自己的共享内存和信号量(semaphore)来实现进程间通信,而不会与 host 和其他容器的 IPC 混在一起。PID
:所有容器的进程都挂在 dockerd 进程下,同时也可以看到容器自己的子进程。 如果我们进入到某个容器,ps 就只能看到自己的进程了Network
:让容器拥有自己独立的网卡、IP、路由等资源。我们会在后面网络章节详细讨论。User
:让容器能够管理自己的用户,host 不能看到容器中创建的用户
镜像
下载镜像
docker pull ubuntu
docker pull ubuntu:18.04
列出镜像
docker images
列出镜像docker image ls -f dangling=true
查看虚悬镜像- 虚悬镜像 镜像和标签都为none,悬空镜像没有意义,浪费磁盘空间可以删除
docker image prune
清空无用的悬空镜像docker images -a
查看中间层镜像- 中间层镜像名称和标签都是none,有被别的镜像依赖是不能被删除
docker system df
查看镜像、容器、数据卷所占用的空间docker images ls ubuntu
查看镜像docker images ls ubuntu:18.04
查看镜像docker image ls -f since=mongo:3.2
-f过滤器参数 镜像有创建时间,since表示查看在这镜像之后的镜像docker image ls -f before=mongo:3.2
镜像建立之后的镜像docker image ls -f label=com.example.version=0.1
只查看含有该标签的镜像docker image ls -q
只显示镜像IDdocker image ls --digests
显示长的镜像信息- 显示镜像信息
# 只显示镜像ID和仓库名
docker image ls --format "{{.ID}}: {{.Repository}}"
# 显示镜像ID和仓库名、标签
docker image ls --format "{{.ID}}\t{{.Repository}}\t{{.Tag}}"
# 多显示一行开头
docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"
删除镜像
docker rmi
docker rmi $(docker images | grep "^<none>" | awk "{print $3}")
上传镜像
# 登录docker.hub,输入你的账号和密码,出现login susses提示表示登录成功
docker login
# 退出登录
docker logout
# 查找镜像
docker search centos
#下载镜像
docker pull centos
# 用户名haoxincheng
docker tag ubuntu:18.04 haoxincheng/ubuntu:18.04
# 提交镜像
docker push haoxincheng/ubuntu:18.04
# 查找镜像
docker search haoxincheng
私有仓库
官方registry、Harbor vmware公司的私有仓库
使用官方registry,
docker run -d \
-p 15000:5000 \
-v /opt/data/registry:/var/lib/registry \
registry
容器
基础使用
docker run ubuntu:18.04 /bin/echo 'Hello world'
容器运行命令docker run -it ubuntu:18.04 /bin/bash
-t分配为tty伪终端,-i容器的标准输入保持打开,-it2个结合使用docker run -it --rm ubuntu:18.04 bash
—rm容器退出后,删除该容器,避免浪费磁盘资源docker run -dit ubuntu
后台运行容器docker run ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
前台运行,日志打印在终端上docker run -d ubuntu:18.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
后台运行,通过docker logs查看日志docker run -dit --restart=always ubuntu
无论容器因何种原因退出(包括正常退出),就立即重启docker run -dit --restart=on-failure:3 ubuntu
启动进程退出代码非0,则重启容器,最多重启3次docker start
启动容器docker stop
停止容器docker restart
重启容器
删除容器
docker image rm centos
删除镜像docker image rm 501
删除镜像,可以用短ID,最前面3个就可以docker image rm 501 centos
删除多个镜像docker image rm $(docker image ls -q redis)
调用命令docker image rm $(docker image ls -q -f before=mongo:3.2)
删除该镜像之后的容器
端口映射
docker run --name webserver -d -P nginx
—name自定义容器名,大P随机指定映射到宿主机的端口docker run --name webserver -d -p 80:80 nginx
宿主机:容器端口docker run -d -p 127.0.0.1:80:80 nginx:alpine
指定IP、宿主机端口、容器端口docker run -d -p 127.0.0.1::80 nginx:alpine
指定IP、容器端口docker run -d -p 127.0.0.1:80:80/udp nginx:alpine
指定协议为udpdocker run -d -p 80:80 -p 443:443 nginx:alpine
映射多个端口docker port web
查看容器端口映射 ,docker port web 443 查看容器端口映射指定端口
资源限制
MEM 限制
docker run -m 200M ubuntu
分配200M内存docker run -m 200M --memory-swap=300M ubuntu
允许该容器最多使用 200M 的内存和 100M 的 swap。默认情况下,没有限制docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 280M
启动 1 个内存工作线程,每个线程分配 280M 内存,vm-bytes是压力测试docker run -it -m 200M --memory-swap=300M progrium/stress --vm 1 --vm-bytes 310M
压力测试超过310M就会报错,分配的内存超过限额,stress 线程报错,容器退出docker run -it -m 200M ubuntu
在启动容器时只指定 -m 而不指定 —memory-swap,那么 —memory-swap 默认为 -m 的两倍。容器最多使用 200M 物理内存和 200M swap
CPU 限制
docker run --name "container_A" -c 1024 ubuntu progrium/stress --cpu 1
- -c 配置优先级,越大越优先,容器A使用的CPU资源是比容器B更加优先的
- —cpu是使用cpu的核数,当宿主机有多核cpu可以用该参数限制使用
- 通过宿主机top命令查看占用,如果将容器A停止,那么CPU资源都是给容器B使用
docker run --name "container_B" -c 512 ubuntu progrium/stress --cpu 1
IO 限制
docker run -it --device-write-bps /dev/sda:30MB ubuntu
- 默认磁盘不限制iops,此时进入容器time dd if=/dev/zero of=test.dbf bs=8k count=300000,会看到速度被限制了
- —device-read-bps,限制读某个设备的 bps。
- —device-write-bps,限制写某个设备的 bps。
- —device-read-iops,限制读某个设备的 iops。
- —device-write-iops,限制写某个设备的 iops。
docker run -it --name container_A --blkio-weight 600 ubuntu
—blkio-weight 与 —cpu-shares 类似,设置的是相对权重值,默认500,container_A 读写磁盘的带宽是 container_B 的两倍docker run -it --name container_B --blkio-weight 300 ubuntu
容器网络
None网络 就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。封闭网络存放安全的数据
--network
等同于 --net
docker run -it --network=none busybox
Host共享 Docker host 的网络栈,容器的网络配置与 host 完全一样,进入容器使用IP命令查看
docker run -it --network=host busybox
Bridge网络 自定义网络,默认创建容器是使用docker的Bridge
docker network create -d bridge my_net
# 宿主机操作,查看Bridge的网络结构
yum -y install bridge-utils && brctl show
# 创建时指定网段
docker network inspect -d bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_test
## 查看网络
docker network inspect my_test
docker run -it --network=my_test busybox
docker run -it --network=my_test --ip 172.22.16.88 busybox
docker run -it --network=my_test --ip 172.22.16.89 busybox
DNS信息
mount #可以看到docker容器的挂载信息
进入容器,进行ping,你会发现2个容器是可以通信的
/etc/docker/daemon.json
{
"dns" : [
"114.114.114.114",
"8.8.8.8"
]
}
$ docker run -it --rm ubuntu:18.04 cat etc/resolv.conf
nameserver 114.114.114.114
nameserver 8.8.8.8
数据挂载
将宿主机的目录、文件挂载到容器的目录、文件
注意事项:
- 容器目录可以是空目录,最好提前创建该目录,挂载参数为相对路径
- 某些容器可能不支持,例如busybox挂载后始终无法同步主机的数据
docker run -it -v /opt/docker/busybox:/home busybox
- 多个容器也可以挂载同一个宿主机目录,但是不建议该操作,可能造成数据混乱
- 不能挂载容器的范围很大,比分说镜像inspect规定了范围,指定超出的路径范围就会出问题
- 挂载文件的坏处: 挂载文件不挂载目录,可能造成文件修改不同步的问题,通过vim的方式编辑并不会同步问题,因为备份原来的文件,当新文件编辑完成后,再将新文件替换文原件,这会导致文件的inode变化,所以docker内外的文件并不会同步,
而用echo等重定向操作修改文件时,文件的inode保持不变,所以不会发生类似现象
常用挂载命令:
docker run -d -P --name web -v /webapp training/webapp python app.py
docker run -d -P --name web -v /src/webapp:/opt/webapp:ro training/webapp python app.py
挂载目录,只读权限docker run --rm -it -v ~/.bash_history:/.bash_history ubuntu /bin/bash
-v标识还可以将宿主机的一个特定文件挂载为数据卷
常见问题
docker run -d \
--name gitlab \
-p 80:80 \
-p 8443:443 \
-v /opt/docker/gitlab/etc:/etc/gitlab \
-v /opt/docker/gitlab/var/log:/var/log/gitlab \
-v /opt/docker/gitlab/var/opt:/var/opt/gitlab \
twang2218/gitlab-ce-zh:latest
很多镜像用-v选项挂载会有问题,因为你挂载的范围有点大了,此时会报错出/var/lib路径有问题
docker run -d \
--name gitlab \
-p 80:80 \
-p 8443:443 \
-v /opt/docker/gitlab/var:/var \
-v /opt/docker/gitlab/etc:/etc/gitlab \
twang2218/gitlab-ce-zh:latest
如果还是想这么挂载大范围,那么解决方法是先跑一遍容器,然后把容器数据复制到宿主机,然后再挂载
docker run -d \
--name gitlab \
-p 80:80 \
-p 8443:443 \
-v /opt/docker/gitlab/var:/var \
-v /opt/docker/gitlab/etc:/etc/gitlab \
twang2218/gitlab-ce-zh:latest
如果不确定镜像挂载是什么样的,可以通过docker inspect
命令查看镜像信息
docker diff webserver
查看容器文件的更改变化docker logs -f 9123
查看容器日志docker attach 243c
如果exit退出,那么容器也会停止(了解即可,不要使用)docker exec -it 69d1 bash
如果exit推出,容器依然运行,所以不要用attachdocker pause my
有时我们只是希望暂时让容器暂停工作一段时间,比如要对容器的文件系统打个快照,或者 dcoker host 需要使用 CPU。处于暂停状态的容器不会占用 CPU 资源,直到通过 docker unpause 恢复运行。docker unpause my
恢复容器运行
删除容器
docker rm -f
-f参数强制停止正在运行的容器docker rm -v $(docker ps -aq -f status=exited)
批量删除已经停止的容器docker container prune
清理无用的、已经停止的容器
打包镜像
Dockerfile
docker commit
当容器做出了更改,可以通过 docker commit
对已有容器打包为镜像
缺点:
- commit会让构建臃肿
- 不清楚对容器之前的操作过程,时间长了无法管理
docker commit \
--author "Tao Wang <twang2218@gmail.com>" \
--message "修改了默认网页" \
webserver \
nginx:v2 #author作者信息、message记录修改的内容,这2个可以不用加,加上更加清楚
docker image ls nginx #
docker history nginx:v2 #查看镜像的历史记录,可以知道容器第一次启动的命令是什么
docker run --name web2 -d -p 81:80 nginx:v2
docker export
# 容器制作成镜像
docker export 7691a814370e > ubuntu.tar
# 导入本地镜像
cat ubuntu.tar | docker import - test/ubuntu:v1.0
# 也可以通过网络资源导入为镜像
docker import http://example.com/exampleimage.tgz example/imagerepo
# 查看镜像
docker images
docker load
常见问题 Error response from daemon: No command specified
镜像没有容器第一次命令,所以创建容器时需要指定命令,后来发现是导入的方式不对
原来镜像是通过docker save
方式保存的,所以必须要用docker load
方式导入,不然会有问题
[root@jq-test ~]# docker inspect jq/gitlab|grep -i cmd
"Cmd": null,
"Cmd": null,
容器访问外网
容器访问控制,宿主机打开Linux内核路由转发,通过iptables控制容器的通信
sysctl net.ipv4.ip_forward #查看
sysctl -w net.ipv4.ip_forward=1 #为1开启转发,为0不转发
iptables -nL
iptables -nL
iptables -t nat -nL #查看Chain DOCKER表的规则
如果希望永久绑定到某个固定的 IP 地址,可以在 Docker 配置文件 中添加如下内容。
{
"ip": "0.0.0.0"
}
Docker 1.2.0 开始支持在运行中的容器里编辑 /etc/hosts, /etc/hostname 和 /etc/resolv.conf 文件。
但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被 docker commit 提交。
容器数据
docker volume create my-vol #创建数据卷
docker volume ls #查看所有的数据卷
docker volume inspect my-vol #查看指定数据卷的信息
docker run -d -P \
--name web \
# -v my-vol:/usr/share/nginx/html \
--mount source=my-vol,target=/usr/share/nginx/html \
nginx:alpine
不能含有注释,不然无法运行
docker run -d -P \
--name web \
--mount source=my-vol,target=/usr/share/nginx/html \
nginx:alpine
docker volume rm my-vol #删除数据卷
docker rm -v # 删除容器是不会删除数据卷的,-v可以删除数据卷
docker run -d -P \
--name web \
-v /src/webapp:/usr/share/nginx/html \
nginx:alpine
docker run -d -P \
--name web \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html \
nginx:alpine # 挂载一个主机目录作为数据卷。以前使用 -v 参数时如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数时如果本地目录不存在,Docker 会报错。
docker run -d -P \
--name web \
--mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly \
nginx:alpine #只读挂载,容器是无法进行写操作的
docker inspect web 挂载主机目录 的配置信息在 "Mounts" Key 下面
docker top sysdig 查看容器运行的进程使用的资源
docker stats 0所有容器各个资源的使用情况
docker stats sysdig 容器
docker run -d -p 9100:9100
-v “/proc:/host/proc”
-v “/sys:/host/sys”
-v “/:/rootfs”
—net=host
prom/node-exporter
—path.procfs /host/proc
—path.sysfs /host/sys
—collector.filesystem.ignored-mount-points “^/(sys|proc|dev|host|etc)($|/)”
配置Docker远程访问
"hosts": ["tcp://0.0.0.0:2375","unix://var/run/docker.sock"]
example: docker -H Ip:Port Comman