[笔记]Docker 基础知识

Posted by jiangydev on May 25, 2017

[TOC]

1 Docker 基本概念和架构

1.1 简介

一种虚拟化方案,操作系统级别的虚拟化

决定其只能运行相同或相似内核的操作系统,依赖于Linux内核特性:Namespace和Cgroups(Control Group)。

将应用程序自动部署到容器。

鼓励使用面向服务的架构、快速高效的开发周期

1.2 Docker 的基本组成

  • Docker 客户端/守护进程

  • Docker Image 镜像:容器的基石、层叠的只读文件系统、联合加载(union mount)

  • Docker Container 容器:通过镜像启动,可写层,写时复制

  • Docker Registry 仓库:共有、私有

1.3 Docker 相关技术

1 Namespaces 命名空间

编程语言:封装 -> 代码隔离 操作系统:系统资源的隔离 PID (Process ID): 进程隔离 NET (Network): 管理网络接口 IPC (InterProcess Communication): 管理跨进程通信的访问 MNT (Mount): 管理挂载点 UTS (Unix Timesharing System): 隔离内核和版本标识

2 Control groups(cgroups) 控制组

用来分配资源;资源限制、优先级设定、资源计量、资源控制

3 Docker 容器的能力

文件系统隔离:每个容器都有自己的root文件系统 进程隔离:每个容器都运行在自己的进程中 网络隔离:容器间的虚拟网络接口和IP地址都是分开的 资源隔离和分组:使用cgroups将CPU和内存之类的资源独立分配给每个Docker容器

1.4 Docker 与 OpenStack

类别 Docker OpenStack
部署难度 非常简单 组件多,部署复杂
启动速度 秒级 分钟级
执行性能 和物理系统几乎一致 VM会占用一些资源
镜像体积 镜像是MB级别 虚拟机是GB级别
管理效率 管理简单 组件相互依赖,管理复杂
隔离性 隔离性高 彻底隔离
可管理性 单进程、不建议启动SSH 完整的系统管理
网络连接 比较弱 借助Neutron可以灵活组件各类网络架构

2 Docker 安装和部署

2.1 Ubuntu

1 安装前的检查:

1
2
3
4
# 查看内核版本,若低于3.8,需要升级内核
$ uname -a
# 检查Device Mapper
$ ls -l /sys/class/misc/device-mapper

2 安装Docker

安装Docker维护的版本:

  • 详细安装

    检查APT的HTTPS支持 查看/usr/lib/apt/methods/https文件是否存在,若不存在,运行安装命令

    1
    2
    
    $ apt-get update
    $ apt-get install -y apt-transport-https
    

    添加Docker的APT仓库

    1
    
    $ echo deb https://get.docker.com/ubuntu docker main > /etc/apt/sources.list.d/docker.list
    

    添加仓库的key

    1
    
    $ apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 36A1D7869245C8950F966E92D8576A8BA88D21E9
    

    安装

    1
    2
    
    $ apt-get update
    $ apt-get install -y lxc-docker
    
  • 简易安装

    1
    2
    
    $ sudo apt-get install -y curl
    $ curl -sSL https://get.docker.com/ | sudo sh
    

    安装Ubuntu维护的版本

    1
    2
    
    $ sudo apt-get install docker.io
    $ source /etc/bash_completion.d/docker.io
    

3 非root用户使用Docker

1
2
3
$ sudo groupadd docker
$ sudo gpasswd -a ${USER} docker
$ sudo service docker restart

2.2 Windows 安装Docker

Boot2Docker -> Virtual Box

2.3 OS X中安装

1
2
3
4
5
6
7
8
$ mkdir -p ~/.boot2docker
$ if [!-f ~/.boot2docker/boot2docker.iso];then cp /usr/local/share/boot2docker/boot2docker.iso ~/.boot2docker/ ;fi
$ boot2docker init
$ boot2docker up
$ boot2docker shellinit
$ docker version
# 进入Docker
$ boot2docker ssh

3 Docker 容器

3.1 容器的基本操作

  • 启动容器

    1
    2
    3
    
    $ docker run IMAGE [COMMAND] [ARG...]
    $ docker run -i -t --name name IMAGE /bin/bash	 # 该语句退出后生命周期结束
    $ docker run -d --name <name> nginx  			 # 后台运行
    
  • 重新启动停止的容器

    1
    
    $ docker start [-i] <CONTAINER ID|NAME>
    
  • 删除容器

    1
    
    $ docker rm <CONTAINER ID|NAME>
    
  • 查看容器

    1
    2
    3
    
    $ docker ps -a		# 查看所有容器
    $ docker ps -l  	# 查看最新创建的容器
    $ ip ad li    		# 在docker中查看网卡信息
    
  • 进入容器

    • docker exec

    • docker attach

    • nsenter

      如果没有,那么需要安装 util-linux 软件包

      1
      2
      3
      
      # 获取容器的Pid
      $ docker inspect --format "" <CONTAINER ID|name>
      $ nsenter --target <Pid> --mount --uts --ipc --net --pid  -> 进入容器
      

3.2 容器的导出和导入

容器导出为镜像命令:

1
$ docker export 容器名 > filename.tar

导入镜像:

1
$ cat filename.tar | docker import - 镜像名

3.3 守护式容器

(1) Ctrl + P Ctrl + Q -d

(2) 查看容器日志

1
2
3
4
$ docker logs [-f] [-t] [--tail] 容器名
    -f --follows=true | false 默认false
    -t --timestamps=true | false 默认false
    --tail ="all"

(3) 在运行中的容器内启动新进程

1
$ docker exec [-d] [-i] [-t] 容器名 [COMMAND] [ARG...]

(4) 容器的停止

1
2
$ docker stop <CONTAINER ID|NAME>   发送信号等待停止
$ docker kill <CONTAINER ID|NAME>   直接停止

3.4 限制容器资源

查看容器的状态:

1
$ docker stats 容器名

(1) 内存

测试工具:memload

参数:--memory 1024m

(2) CPU(亲和性)

CPU 亲和性(affinity)就是进程要在某个给定的 CPU 上尽量长时间地运行而不被迁移到其他处理器的倾向性。

1
2
3
4
5
6
# 查看 cpu 属性
$ lscpu
# 查看进程使用的 cpu
$ ps mo pid,comm,psr `pgrep cat`
# 指定进程使用的 cpu
$ taskset -c 0 ps

参数

  • 使用的cpu数量: --cpus 0.5;
  • 使用的cpu: --cpuset-cpu 0

3.5 容器监控工具(cAdvisor)

监控工具:cAdvisor 容器

通过挂载宿主机中的资源文件(如 /sys 等),读取资源的状态,以达到分析的目的。

1
2
3
4
5
6
$ docker pull google/cadvisor
$ docker run \
-v /var/run:/var/run \
-v /sys:/sys:ro \
-v /var/lib/docker:/var/lib/docker:ro \
-d -p 8080:8080 google/cadvisor

4 Docker 镜像与仓库

4.1 查看与删除镜像

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查看镜像
$ docker images [OPTIONS] [REPOSITORY]
    -a, --all=false
    -f, --filter=[]
    --no-trunc=false : 不截断镜像ID
    -q, --quiet=false

# 查看镜像的详细信息
$ docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
    -f, --format=""
# 删除镜像
$ docker rmi [OPTIONS] IMAGE [IMAGE...]
    -f, --force=false : Force removal of the image
    --no-prune=false : Do not delete untagged parents
# 删除所有ubuntu镜像
$ docker rmi $(docker images -q ubuntu)

4.2 获取和推送镜像

(1) 查找镜像

1
$ docker search [OPTIONS] TERM		# 最多返回25个结果

(2) 拉取镜像

1
2
3
4
5
6
$ docker pull [OPTIONS] NAME [:TAG]
    -a, --all-tags=false  Download all tagged images in the repository
# 镜像加速 (DAOCLOUNG | 阿里云)
$ sudo vim /etc/default/docker
# 添加:`DOCKER_OPTS= "--registry-mirror=http://MIRROR-ADDR"`
$ ps -ef | grep docker

(3) 推送镜像

1
$ docker push NAME[:TAG]

此时可能会报错:不安全的…,需要修改docker配置文件,加入–insecure-registry

1
2
3
$ vim /etc/sysconfig/docker
other_args="--insecure-registry 192.168.199.220:5001"
$ /etc/init.d/docker restart

(4) 标记镜像

1
$ docker tag NAME[:TAG] custom_name[:TAG]

TAG 本质为硬链接,镜像的 ID 都是相同的(类似于文件的硬链接,inode 相同)。

(5) 修改默认的镜像仓库地址

修改的文件:/etc/sysconfig/docker,增加的参数如下:

1
ADD_REGISTRY='--add-registry 192.168.1.1:5000'

4.3 镜像备份和导入

镜像备份命令:

1
$ docker save 镜像名 > filename.tar

备份镜像导入:

1
2
3
$ docker load -i filename.tar
# 或者
$ docker load < filename.tar

4.4 构建镜像

1 构建方式

(1) 通过容器构建

运行容器后,在容器内进行操作,如添加、修改或删除文件等,之后 commit 可写层即产生镜像。

1
2
3
4
$ docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
  -a, --author=""  Author
  -m, --message="" Commit message
  -p, --pause=true  Pause container during commit

(2) 通过Dockerfile构建

1
2
3
4
5
6
$ docker build [OPTIONS] PATH | URL | -
    --force-rm=false
    --no-cache=false
    --pull=false
    -q, --quiet=false
    -t, --tag=""

2 构建过程

构建完成后可以使用docker history <image>查看)

①从基础镜像运行一个容器; ②执行一条指令,对容器做出修改; ③再基于刚提交的镜像运行一个新容器; ④执行第②步,否则结束。 那么过程就允许中间层镜像的调试。 可以使用 --no-cache 不使用缓存,或者在Dockerfile中使用ENV REFRESH_DATE

4.5 Dockerfile 指令

编写 dockerfile 文件,如下:

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 已经存在的镜像、基础镜像、必须是第一条非注释指令
FROM <image>[:<tag>]

# 指定镜像的作者信息,包含镜像的所有者和联系信息
MAINTAINER <name>

# 指定构建镜像时运行的命令
RUN <command>  (shell 模式)
RUN ["executable", "param1", "param2"]  (exec 模式)

# 声明指定端口,实际不会打开端口,需要在运行容器时用-p
EXPOSE <PORT> [<PORT>...]

# 运行容器时的指令
CMD ["executable", "param1", "param2"]  (exec 模式)
CMD command param1 param2  (shell 模式)
CMD ["param1", "param2"]  (作为ENTRYPOINT指令的默认参数)
*若运行容器时,指定运行指令如`/bin/bash`,则CMD命令被覆盖*

# 与CMD不同,ENTRYPOINT不会被覆盖
ENTRYPOINT ["executable", "param1", "param2"]  (exec 模式)
ENTRYPOINT command param1 param2  (shell 模式)
*可以使用 `docker run --entrypoint` 覆盖*
ENTRYPOINT 和 CMD 组合使用,CMD 提供参数。

# ADD & COPY
ADD <src>...<dest>
ADD ["<src>"..."<dest>"]  (适用于文件路径中有空格的情况)
COPY <src>...<dest>
COPY ["<src>"..."<dest>"]  (适用于文件路径中有空格的情况)
*ADD 包含类似tar的解压功能,如果单纯复制文件,使用COPY*

# 添加卷
VOLUME ["/data"]

# 为后续的操作设置工作目录
WORKDIR /path
*通常使用绝对路径,如果使用相对路径,会传递*

# 环境变量
ENV <key><value>
ENV <key>=<value>

# 运行指令的用户,默认为root
USER nginx:group

# 镜像触发器,当一个镜像被其他镜像作为基础镜像时执行,会在构建过程中插入指令
ONBUILD [INSTRUCTION]

构建镜像

1
$ docker build -t my-image:v1 .

5 Docker 客户端与守护进程

5.1 Docker 的 C/S 模式

Docker CLI客户端 <–> Docker 守护进程 Remote API (RESTful风格API)

连接方式:

unix:///var/run/docker.sock (默认) tcp://host:port fd://socketfd

使用Linux的NetCat命令:

NetCat 介绍:

主要用于调试领域、传输领域甚至黑客攻击领域。利用该工具,可以将网络中一端的数据完整的发送至另一台主机终端显示或存储,常见的应用为文件传输、与好友即时通信、传输流媒体或者作为用来验证服务器的独立的客户端。

1
2
$ nc -U /var/run/docker.sock
"GET /info HTTP/1.1"

5.2 配置和操作

(1) 查看docker进程:

1
$ ps -ef / status docker

(2) 操作docker:

1
$ service docker stop|start|restart

(3) 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
28
29
30
31
32
$ docker -d [OPTIONS]
    -运行相关:
      -D, --debug=false
      -e, --exec-driver="native"
      -g, --graph="/var/lib/docker"
      --icc=true             # 默认true,允许inter-container来通信
      --label=[]
      -p, --pidfile="/var/run/docker.pid"
    -与服务器连接相关:
      -G, --group="docker"
      -H, --host=[]
      --tls=false
      --tlscacert="/home/sven/.docker/ca.pem"
      --tlscert="/home/sven/.docker/cert.pem"
      --tlskey="/home/sven/.docker/key.pem"
      --tlsverity=false
    -RemoteAPI相关:
      --api-enable-cors=false
    -存储相关:
      -s, --storage-driver="" # 默认是空,这是docker运行使用的一个存储驱动器
      --selinux-enabled=false
      --storage-opt=[]
    -Registry相关:
      --insecure-registry=[]
      --registry-mirror=[]
    -网络设置相关:
      --ip=0.0.0.0            # 默认"0.0.0.0":绑定容器端口的默认Ip地址
      --ip-forward=true
      --ip-masp=true
      --iptables=true
      --ipv6=false
      --mtu=0

(4) 配置文件

1
2
3
$ vi /etc/default/docker
  DOCKER_OPTS="--label name=docker_server_1"
$ service docker restart

5.3 远程访问

客户端与服务器端的访问。

(1) 需要第二台装有Docker的服务器

修改Docker守护进程启动选项,区别服务器 保证Client API与Server API版本一致

(2) 修改服务器端配置

守护进程启动选项:

1
2
3
-H  tcp://host:port
    unix:///path/to/socket
    fd://* or fd://socketfd

默认配置:

1
-H  unix:///var/run/docker.sock

(3) 修改方式

方式一 可以指定多个-H

文件 /etc/default/docker 生效的方式请看 #6.1

1
2
3
$ vi /etc/default/docker
# 添加
DOCKER_OPTS="... -H tcp://0.0.0.0:2375"

方式二

1
$ export DOCKER_HOST="tcp://10.10.10.10:2375"

6 Docker 容器的网络连接

6.1 容器的网络基础

1
2
3
4
5
6
# 查看路由信息
$ ip ro li
# 查看网卡信息,可以看到 docker0(虚拟网桥)
$ ifconfig
# 管理以太网桥
$ apt-get install bridge-utils

可以自定义虚拟网桥

(1) 添加虚拟网桥

1
2
$ brctl addbr br0
$ ifconfig br0 192.168.100.1 netmask 255.255.255.0

(2) 配置 /etc/default/docker 生效

1
2
3
4
5
6
7
8
9
10
# 修改配置文件
$ vi /usr/lib/systemd/system/docker.service
EnvironmentFile=-/etc/default/docker # -代表ignore error
# ExecStart 需要追加一个值 $DOCKER_OPTS
ExecStart=$DOCKER_OPTS

# 重载
$ systemctl daemon-reload
# 重启docker服务
$ service docker restart

(3) 更改启动配置

1
$ echo 'DOCKER_OPTS="-b=br0"' >> /etc/default/docker

6.2 容器的互联

(1) 默认情况下,允许所有容器互联。 icc=true

为了解决容器间的互联不会因为IP的变化收影响,使用link的方式:

1
$ docker run --link=[CONTAINER NAME]:[ALIAS] [IMAGE] [COMMAND]

(2) 拒绝所有容器互联

1
icc=false

(3) 允许特定容器的连接

1
2
icc=false --iptables=true
--link

6.3 容器与外部网络的连接

(1) ip-forward 流量转发

1
2
--ip-forward=true
$ sysctl net.ipv4.conf.all.forwarding  # 查看流量转发是否开启,1为开启

(2) iptables

iptables是与Linux内核集成的包过滤防火墙系统,几乎所有的linux发行版都会包含iptables的功能。 包含表、链、规则,ACCEPT, REJECT, DROP filter表中包含的链:INOUT, FORWARD, OUTPUT

1
2
3
4
# 默认为filter
$ iptables -t filter -L -n
# 在物理机设置过滤规则
$ iptables -I DOCKER -s 10.211.55.3 -d 172.17.0.7 -p TCP --dport 80 -j DROP

7 Docker 容器的数据管理

7.1 容器的数据卷 Data Volume

数据卷是经过特殊设计的目录,可以绕过联合文件系统(UFS),为一个或多个容器提供访问。

数据卷设计的目的

在于数据的永久化,它完全独立于容器的生存周期,因此,Docker不会在容器删除其挂载的数据卷,也不会存在类似的垃圾收集机制,对容器引用的数据卷进行处理。

特点 (1)数据卷可以是目录或文件。数据卷在容器启动时初始化,如果容器使用的镜像在挂载点包含了数据,这些数据会拷贝到新初始化的数据卷中。 (2)数据卷可以在容器之间共享和重用。 (3)可以对数据卷里的内容直接进行修改。 (4)数据卷的变化不会影响镜像的更新。 (5)卷会一直存在,即使挂载数据卷的容器已经被删除。

1
2
3
$ docker run -it -v ~/data:/data ubuntu /bin/bash
# 为数据卷添加访问权限
$ docker run -it -v ~/data:/data:ro ubuntu /bin/bash

Dockerfile 中:

初始化两个数据卷,此时不能实现挂载和共享 VOLUME [/data1, /data2]

7.2 数据卷容器

挂载数据卷容器,即使容器已经退出,仍然可用。

1
$ docker run --volumes-from [CONTAINER NAME]

7.3 数据卷的备份和还原

(1) 数据备份方法

1
$ docker run --volumes-from [CONTAINER NAME] -v $(pwd):/backup ubuntu tar cvf /backup/backup.tar [CONTAINER DATA VOLUME]

(2) 数据还原即解压缩

8 Docker 容器跨主机网络访问

8.1 使用网桥实现

(1) 修改 /etc/network/interfaces 文件:

1
2
3
4
5
6
auto br0
iface br0 inet static
address 10.211.55.3
netmask 255.255.255.0
gateway 10.211.55.1
bridge_ports eth0

(2) 修改 /etc/default/docker 文件

1
2
3
4
-b 指定使用自定义网桥 `-b=br0`
--fixed-cidr 限制ip地址分配范围
  HOST1: 10.211.55.64/26
  HOST2: 10.211.55.128/26

配置简单,不依赖第三方软件;与主机同网段,需要有网段控制权,不易实现和管理,兼容性不佳

8.2 Open vSwitch 实现

Open vSwitch:高质量的、多层虚拟交换机,开源;使得大规模网络自动化可以通过编程扩展,同时仍然支持标准的管理接口和协议。

GRE (通用路由协议封装)

隧道技术:是一种通过使用互联网络的基础设施在网络间传递数据的方式。使用隧道传递的数据(或负载)可以是不同协议的数据帧或包。隧道协议将其他协议的数据帧或包重新封装后通过隧道发送。新的帧头提供路由信息,以便通过互联网传递被封装的负载数据。

1
2
$ apt-get install openvswitch-switch
$ apt-get install bridge-utils

步骤

  • 建立ovs网桥

    1
    
      $ ovs-vsctl add-br obr0
    
  • 添加gre连接

    1
    2
    3
    4
    5
    6
    7
    
      # 在obr0网桥下添加两个Port
      $ ovs-vsctl add-port obr0 gre0
      $ ovs-vsctl set interface gre0 type=gre options:remote_ip=192.168.59.104
        
      $ brctl addbr br0
      $ ifconfig br0 192.168.1.1 netmask 255.255.255.0
      $ brctl addif br0 obr0
    
  • 配置docker容器虚拟网桥

  • 为虚拟网桥添加ovs接口

  • 添加不同docker容器网段路由

    1
    
      $ ip route add 192.168.2.0/24 via 192.168.59.104 dev eth0
    

8.3 weave 实现

weave: 编织,建立一个虚拟的网络,用于将运行在不同主机的Docker容器连接起来。

1
2
3
4
5
$ wget -O /usr/bin/weave https://raw.githubusercontent.com/zettio/weave/master/weave
$ chmod a+x /usr/bin/weave
$ weave launch (IP地址)

$ c2=$(weave run 192.168.1.2/24 -it ubuntu /bin/bash)  # 启动容器中包含一个ethwe

9 Docker 资源隔离和限制

9.1 资源隔离 LXC

Kernel namespace

pid: 进程隔离,可以嵌套 net: 网络隔离 ipc: 进程间交互隔离 mnt: 进程挂载在目录 uts: 每一个容器有hostname,domain user: 用户及组

9.2 资源限制 Cgroup

即使个别容器崩溃,不影响。

CPU 内存 (磁盘暂时不行)

9.3 压力测试

Docker 每个容器默认会有1024 shares(相对拥有物理100% CPU),当有多个容器,配额会按比例分配。

查看当前CPU信息

1
$ cat /proc/cpuinfo

压力测试工具:stress

1
2
3
4
# 可以使用 stress 镜像测试
$ docker pull progrium/stress
# 测试样例
$ docker run --rm -it progrium/stress --cpu 2 --io 1 --vm 2 --vm-bytes 128M --timeout 10s

10 Docker Dashboard

目前 Docker Web 页面管理的平台:

Shipyard 项目已经从 GitHub 中删除,这里不考虑。

10.1 Portainer

参考文献:

官方文档:Portainer Docs

一个开源的轻量级的 Docker 管理工具。

10.2 Rancher

参考文献:

官方文档:Rancher 1.6 Docs

官方文档有中文版,只建议不要用配置过低的主机做 rancher server,会无响应。