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 只显示镜像ID
  • docker image ls --digests 显示长的镜像信息
  • 显示镜像信息
  1. # 只显示镜像ID和仓库名
  2. docker image ls --format "{{.ID}}: {{.Repository}}"
  3. # 显示镜像ID和仓库名、标签
  4. docker image ls --format "{{.ID}}\t{{.Repository}}\t{{.Tag}}"
  5. # 多显示一行开头
  6. docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}"

删除镜像

  • docker rmi
  • docker rmi $(docker images | grep "^<none>" | awk "{print $3}")

上传镜像

  1. # 登录docker.hub,输入你的账号和密码,出现login susses提示表示登录成功
  2. docker login
  3. # 退出登录
  4. docker logout
  5. # 查找镜像
  6. docker search centos
  7. #下载镜像
  8. docker pull centos
  9. # 用户名haoxincheng
  10. docker tag ubuntu:18.04 haoxincheng/ubuntu:18.04
  11. # 提交镜像
  12. docker push haoxincheng/ubuntu:18.04
  13. # 查找镜像
  14. docker search haoxincheng

私有仓库

官方registry、Harbor vmware公司的私有仓库
使用官方registry,

  1. docker run -d \
  2. -p 15000:5000 \
  3. -v /opt/data/registry:/var/lib/registry \
  4. 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 指定协议为udp
  • docker 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

  1. docker run -it --network=none busybox

Host共享 Docker host 的网络栈,容器的网络配置与 host 完全一样,进入容器使用IP命令查看

  1. docker run -it --network=host busybox

Bridge网络 自定义网络,默认创建容器是使用docker的Bridge

  1. docker network create -d bridge my_net
  2. # 宿主机操作,查看Bridge的网络结构
  3. yum -y install bridge-utils && brctl show
  4. # 创建时指定网段
  5. docker network inspect -d bridge --subnet 172.22.16.0/24 --gateway 172.22.16.1 my_test
  6. ## 查看网络
  7. docker network inspect my_test
  8. docker run -it --network=my_test busybox
  9. docker run -it --network=my_test --ip 172.22.16.88 busybox
  10. docker run -it --network=my_test --ip 172.22.16.89 busybox

DNS信息

mount #可以看到docker容器的挂载信息

进入容器,进行ping,你会发现2个容器是可以通信的
/etc/docker/daemon.json

  1. {
  2. "dns" : [
  3. "114.114.114.114",
  4. "8.8.8.8"
  5. ]
  6. }
  1. $ docker run -it --rm ubuntu:18.04 cat etc/resolv.conf
  2. nameserver 114.114.114.114
  3. 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标识还可以将宿主机的一个特定文件挂载为数据卷

常见问题

  1. docker run -d \
  2. --name gitlab \
  3. -p 80:80 \
  4. -p 8443:443 \
  5. -v /opt/docker/gitlab/etc:/etc/gitlab \
  6. -v /opt/docker/gitlab/var/log:/var/log/gitlab \
  7. -v /opt/docker/gitlab/var/opt:/var/opt/gitlab \
  8. twang2218/gitlab-ce-zh:latest

很多镜像用-v选项挂载会有问题,因为你挂载的范围有点大了,此时会报错出/var/lib路径有问题

  1. docker run -d \
  2. --name gitlab \
  3. -p 80:80 \
  4. -p 8443:443 \
  5. -v /opt/docker/gitlab/var:/var \
  6. -v /opt/docker/gitlab/etc:/etc/gitlab \
  7. twang2218/gitlab-ce-zh:latest

如果还是想这么挂载大范围,那么解决方法是先跑一遍容器,然后把容器数据复制到宿主机,然后再挂载

  1. docker run -d \
  2. --name gitlab \
  3. -p 80:80 \
  4. -p 8443:443 \
  5. -v /opt/docker/gitlab/var:/var \
  6. -v /opt/docker/gitlab/etc:/etc/gitlab \
  7. twang2218/gitlab-ce-zh:latest

如果不确定镜像挂载是什么样的,可以通过docker inspect命令查看镜像信息

  • docker diff webserver 查看容器文件的更改变化

  • docker logs -f 9123 查看容器日志

  • docker attach 243c 如果exit退出,那么容器也会停止(了解即可,不要使用)

  • docker exec -it 69d1 bash 如果exit推出,容器依然运行,所以不要用attach

  • docker 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 对已有容器打包为镜像

缺点:

  1. commit会让构建臃肿
  2. 不清楚对容器之前的操作过程,时间长了无法管理
  1. docker commit \
  2. --author "Tao Wang <twang2218@gmail.com>" \
  3. --message "修改了默认网页" \
  4. webserver \
  5. nginx:v2 #author作者信息、message记录修改的内容,这2个可以不用加,加上更加清楚
  6. docker image ls nginx #
  7. docker history nginx:v2 #查看镜像的历史记录,可以知道容器第一次启动的命令是什么
  8. docker run --name web2 -d -p 81:80 nginx:v2

docker export

  1. # 容器制作成镜像
  2. docker export 7691a814370e > ubuntu.tar
  3. # 导入本地镜像
  4. cat ubuntu.tar | docker import - test/ubuntu:v1.0
  5. # 也可以通过网络资源导入为镜像
  6. docker import http://example.com/exampleimage.tgz example/imagerepo
  7. # 查看镜像
  8. docker images

docker load

常见问题 Error response from daemon: No command specified

镜像没有容器第一次命令,所以创建容器时需要指定命令,后来发现是导入的方式不对

原来镜像是通过docker save方式保存的,所以必须要用docker load方式导入,不然会有问题

  1. [root@jq-test ~]# docker inspect jq/gitlab|grep -i cmd
  2. "Cmd": null,
  3. "Cmd": null,

容器访问外网

容器访问控制,宿主机打开Linux内核路由转发,通过iptables控制容器的通信

  1. sysctl net.ipv4.ip_forward #查看
  2. sysctl -w net.ipv4.ip_forward=1 #为1开启转发,为0不转发

iptables -nL
iptables -nL

iptables -t nat -nL #查看Chain DOCKER表的规则
如果希望永久绑定到某个固定的 IP 地址,可以在 Docker 配置文件 中添加如下内容。

  1. {
  2. "ip": "0.0.0.0"
  3. }

Docker 1.2.0 开始支持在运行中的容器里编辑 /etc/hosts, /etc/hostname 和 /etc/resolv.conf 文件。

但是这些修改是临时的,只在运行的容器中保留,容器终止或重启后并不会被保存下来,也不会被 docker commit 提交。

容器数据

  1. docker volume create my-vol #创建数据卷
  2. docker volume ls #查看所有的数据卷
  3. docker volume inspect my-vol #查看指定数据卷的信息
  4. docker run -d -P \
  5. --name web \
  6. # -v my-vol:/usr/share/nginx/html \
  7. --mount source=my-vol,target=/usr/share/nginx/html \
  8. nginx:alpine
  9. 不能含有注释,不然无法运行
  10. docker run -d -P \
  11. --name web \
  12. --mount source=my-vol,target=/usr/share/nginx/html \
  13. nginx:alpine
  14. docker volume rm my-vol #删除数据卷
  15. docker rm -v # 删除容器是不会删除数据卷的,-v可以删除数据卷
  16. docker run -d -P \
  17. --name web \
  18. -v /src/webapp:/usr/share/nginx/html \
  19. nginx:alpine
  20. docker run -d -P \
  21. --name web \
  22. --mount type=bind,source=/src/webapp,target=/usr/share/nginx/html \
  23. nginx:alpine # 挂载一个主机目录作为数据卷。以前使用 -v 参数时如果本地目录不存在 Docker 会自动为你创建一个文件夹,现在使用 --mount 参数时如果本地目录不存在,Docker 会报错。
  24. docker run -d -P \
  25. --name web \
  26. --mount type=bind,source=/src/webapp,target=/usr/share/nginx/html,readonly \
  27. nginx:alpine #只读挂载,容器是无法进行写操作的
  28. 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远程访问

  1. "hosts": ["tcp://0.0.0.0:2375","unix://var/run/docker.sock"]

example: docker -H Ip:Port Comman


瓦雀