1.Docker入门

1.1 Docker概述

  • 什么是Docker

    Docker是一个基于容器化技术,针对开发、运维进行应用程序的开发、运行和部署的平台

    使用Linux容器进行应用程序的部署,就称作容器化

    容器化并不是一种新的理念,它主要是用于降低应用程序在不同环境上的部署难度

    避免了由于开发人员与运维人员在软件环境上的差异性,出现各种软件部署的问题

  • 虚拟化 vs 容器化

    虚拟化

    容器化

    特性 容器 虚拟机
    启动 秒级 分钟级
    性能 接近原生 弱于
    空间使用 一般为MB 一般为GB
    系统支持量 单机支持上千个
  • Docker的优势

    • 更快速的交付和部署
    • 更高效的虚拟化
    • 更轻松的迁移和扩展
    • 更简单的管理
  • Docker的劣势

    • 不支持异构系统的虚拟化,原生Docker只能运行在linux系统上
  • Docker能干什么

    • 简化配置
    • 代码流水线管理
    • 提高开发效率
    • 隔离应用
    • 整合服务器
    • 调试能力
    • 多租户
    • 快速部署

1.2 Docker架构

image-20190619212013450

Docker组成

  • docker server/daemon
  • docker client
  • docker registry/仓库

1.3 Docker与Kubernetes

2.Docker安装

Docker目前推出的版本分为两种:

  • 社区版/Community Edition (CE)
  • 企业版/Enterprise Edition (EE)

支持平台

2.1 linux环境下的安装

  • 移除老版本Docker

    1
    sudo yum remove docker  docker-common docker-selinux docker-engine
  • 安装Docker依赖包

    1
    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
  • 配置Docker仓库源

    1
    2
    sudo yum-config-manager --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo
  • 安装Docker CE

    1
    sudo yum install -y docker-ce
  • 启动Docker

  • ```shell
    sudo systemctl start docker

    1
    2
    3
    4
    5

    查看Docker版本

    ```shell
    docker version
  • 测试

    1
    sudo docker run hello-world

2.2 Docker核心概念

Docker是一个提供了开发、打包、运行app的平台

它把app和底层的基础设施隔离开来

Docker Engine

Docker Engine的组成

  • 后台进程(dockerd)
  • Rest API Server
  • CLI接口

2.3 Docker底层技术实现

  • Namespaces

    用于隔离pid、net、ipc、mnt、uts

  • Control groups

    进行资源的限制

  • Union file systems

    container与image的分层

2.4 Docker Image

什么是Image

img

从基本的看起,一个典型的 Linux 文件系统由 bootfs 和 rootfs 两部分组成,bootfs(boot file system) 主要包含bootloader 和 kernel,bootloader 主要用于引导加载 kernel,当 kernel 被加载到内存中后 bootfs 会被 umount 掉。rootfs (root file system) 包含的就是典型 Linux 系统中的/dev,/proc,/bin,/etc 等标准目录和文件。

传统的 Linux 加载 bootfs 时会先将 rootfs 设为 read-only,然后在系统自检之后将 rootfs 从 read-only 改为 read-write,然后我们就可以在 rootfs 上进行读写操作了。但 Docker 在 bootfs 自检完毕之后并不会把 rootfs 的 read-only 改为 read-write,而是利用 union mount(UnionFS 的一种挂载机制)将 image 中的其他的 layer 加载到之前的 read-only 的 rootfs层之上,每一层 layer 都是 rootfs 的结构,并且是read-only 的。

查看本地的image

1
2
3
docker image ls
# 简写
docker images

image-20190620110800955

image获取

  • Dockerfile构建

    创建名称为Dockerfile的文件

    1
    touch Dockerfile

    添加如下内容

    1
    2
    3
    4
    5
    FROM centos:centos7.6.1810
    LABEL matintainer='maxpw'
    RUN yum install -y epel-release && yum install -y redis
    EXPOSE 6379
    ENTRYPOINT ["/usr/bin/redis-server"]

    构建image

    1
    docker bulid -t maxpw/centos-redis:latest .

    启动image实例

    1
    docker run maxpw/centos-redis
  • Pull from Registry

    1
    docker pull centos:centos7.6.1810

2.5 Docker Container

Docker中的Image与Container就属于面向对象中的类与对象

Image就是根据业务需求定义的一个类,而Container就是基于Image创建的一个对象实例

启动container

1
docker run maxpw/centos-redis

交互式运行container

1
docker run -it maxpw/centos-redis

查看启动中container列表

1
2
3
docker container ls
# 简写
docker ps

查看所有的container列表

1
docker container ls -a

删除容器

1
2
3
docker container rm containerId
# 简写
docker rm containerId

批量删除容器

1
docker rm $(docker container ls -aq)

删除已退出的容器

1
docker rm $(docker container ls -f "status=exited" -q)

3.Docker自定义镜像

3.1 Docker commit构建

交互式运行container

1
docker run -it centos

安装vim

1
yum install -y vim

完成后退出

1
exit

将container构建为image

1
ocker commit containerId maxpw/centos-vim

3.2 Dockerfile构建

创建目录

1
mkdir centos-vim

创建Dockerfile文件

1
touch Dockerfile

Dockerfile

1
2
FROM centos
RUN yum install -y vim

构建指令

1
docker build -t maxpw/centos-vim-new:latest .

3.3 Dockerfile语法

FROM

FROM指令通常是Dockerfile文件开头的指令,选择构建自定义image的base image

1
2
3
FROM scratch # no base image
FROM centos # base on centos image
FROM ubuntu:14.04 # base on ubuntu tag and 14.04 image

尽量使用官方的image作为base image

LABEL

为image添加元数据信息

指令格式

1
LABEL <key>=<value> <key>=<value> <key>=<value> ...

example

1
2
3
4
5
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."

RUN

run指令将在当前image上执行命名,并提交结果作为Dockerfile的下一步

两种语法格式

  • RUN shell方式,命令将运行在shell上,默认使用/bin/sh -c
  • RUN [“executable”, “param1”, “param2”] exec方式

shell方式

1
2
RUN yum update && yum install -y vim \ # 反斜线换行
python-dev

为了避免无用分层,请使用反斜线换行,合并多条命令

exec方式

1
RUN ["yum", "install", "-y", "vim"]

WORKDIR

语法格式

1
WORKDIR /path/to/workdir

workdir指令为任何runcmdentrypointcopyadd指令设置工作目录

1
2
3
WORKDIR /test # 如果目录不存在,则自动创建
WORKDIR demo # 基于/test目录下的demo目录
RUN pwd # 输出结果为/test/demo

用WORKDIR,不用用RUN cd,尽量使用绝对路径目录

ADD

add指令从复制新文件、目录或远程文件URL,并将它们添加到路径处的映像文件系统中。

两种语法格式

  • ADD [–chown=:]
  • ADD [–chown=:] [““,… ““]
1
ADD hom* /mydir/        # adds all files starting with "hom"

如果URL文件使用身份验证进行保护,则需要使用run wget、run curl或使用容器中的其他工具,因为add指令不支持身份验证

如果是一个可识别压缩格式(identity、gzip、bzip2或xz)的本地tar归档文件,则将其解包为一个目录。来自远程URL的资源不会被解压缩。

COPY

copy指令从复制新文件或目录,并将它们添加到位于路径的容器的文件系统中。

两种语法格式

  • COPY [–chown=:]
  • COPY [–chown=:] [““,… ““]
1
COPY hom* /mydir/        # adds all files starting with "hom"

ENV

env指令将环境变量设置为值。对于构建阶段的所有后续指令,该值将在环境中出现,并且可以在许多情况下以内联方式替换。

两种语法格式

  • ENV
  • ENV =
1
2
3
4
ENV myName="John Doe"
ENV myName John Doe
ENV MYSQL_VERSION 5.6
RUN yum install -y mysql-server="${MYSQL_VERSION}" && rm -rf /var/lib/lists/*

尽量使用ENV增加可维护性

CMD

cmd给出的是一个容器的默认的可执行体。也就是容器启动以后,默认的执行的命令。

意味着,如果docker run没有指定任何的执行命令或者dockerfile里面也没有entrypoint,那么,就会使用cmd指定的默认的执行命令执行。

三种语法格式

  • CMD ["executable","param1","param2"] (exec方式)
  • CMD ["param1","param2"] (ENTRYPOINT默认参数)
  • CMD command param1 param2 (shell方式)
1
2
FROM centos
CMD echo "hello cmd!"

一个dockerfile至多只能有一个cmd,如果有多个,只有最后一个生效一个dockerfile至多只能有一个cmd,如果有多个,只有最后一个生效

ENTRYPOINT

entrypoint用于定义容器启动以后的执行指令

两种语法格式

  • ENTRYPOINT ["executable", "param1", "param2"] (exec方式)
  • ENTRYPOINT command param1 param2 (shell方式)
1
2
FROM ubuntu
ENTRYPOINT exec top -b

4.Container操作

4.1 容器常用操作

以后台进程的方式启动容器

1
2
docker run -it centos
# 使用ctrl+P+Q退出容器且容器不关闭

进入运行中的容器

1
docker exec -it containerId /bin/bash

启动停止的容器

1
docker start containerId

停止启动的容器

1
docker stop containerId

查看容器详细信息

1
docker inspect containerId

4.2 容器资源限制

内存限制

1
docker run -it --name=test1 --memory=200M maxpw/centos-vim

CPU限制

1
docker run -it --name=test2 --cpu-shares=10 --memory=200M maxpw/centos-vim

5.Docker网络

5.1 linux命名空间

Linux的命名空间机制提供了一种资源隔离的解决方案。PID,IPC,Network等系统资源不再是全局性的,而是属于特定的Namespace。Linux Namespace机制为实现基于容器的虚拟化技术提供了很好的基础,LXC(Linux containers)就是利用这一特性实现了资源的隔离。不同Container内的进程属于不同的Namespace,彼此透明,互不干扰

Namespace是对全局系统资源的一种封装隔离,使得处于不同namespace的进程拥有独立的全局系统资源,改变一个namespace中的系统资源只会影响当前namespace里的进程,对其他namespace中的进程没有影响

Linux namespace 实现了 6 项资源隔离

namespace 系统调用参数 隔离内容
UTS CLONE_NEWUTS 主机和域名
IPC CLONE_NEWIPC 信号量、消息队列和共享内存
PID CLONE_NEWPID 进程编号
Network CLONE_NEWNET 网络设备、网络栈、端口等
Mount CLONE_NEWNS 挂载点
User CLONE_NEWUSER 用于和用户组

5.2 ip netns

netns是在linux中提供网络虚拟化的一个项目,使用netns网络空间虚拟化可以在本地虚拟化出多个网络环境,目前netns在lxc容器中被用来为容器提供网络

查看已存在虚拟网络空间

1
ip netns list

创建虚拟网络空间

1
ip netns add NAME

进入虚拟网络空间

1
ip [-all] netns exec [NAME] cmd

例如,进入test1网络空间

1
ip netns exec test1 bash

查看test网络配置信息

1
ip a

输出结果

1
2
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN 
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

lo其实是一个系统虚拟的环回接口,它的IP地址是127.0.0.1,利用这个接口可以实现系统内部发送和接收数据,所以一般情况下我们使用下面指令:ping 127.0.0.1

查看链路层状态

1
ip link

查看网络空间链路状态

1
ip netns exec test1 ip link

5.3 veth pair

veth pair不是一个设备,而是一对设备,以连接两个虚拟以太网端口。操作veth pair,需要跟namespace一起配合,不然就没有意义

创建veth pair

1
2
ip link add veth-test1 type veth peer name veth-test2

绑定veth到网络空间

1
2
3
ip link set veth-test1 netns test1
ip link set veth-test2 netns test2

绑定IP到veth

1
2
3
ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1
ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2

启动veth

1
2
3
ip netns exec test1 ip link set dev veth-test1 up
ip netns exec test2 ip link set dev veth-test2 up

ping测试

1
2
3
ip netns exec test1 ping 192.168.1.2
ip netns exec test2 ping 192.168.1.1

5.4 docker bridge

我们会发现,默认创建的两个docker容器可以直接相互ping通,并且可以ping通外网,例如:ping www.baidu.com

查看当前主机docker的网络配置列表

1
2
docker network ls

查看容器使用的网络模式

1
2
docker network inspect networkId

查看如下信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
"Containers": {
"75cc086dc00470bf2aad7d91ab0146eebb7589eb8449760f6ecb5a072db92907": {
"Name": "test2",
"EndpointID": "be3d21106fe1460b633ae6a6c0c4c047982fabc6d88fe215af2abe7569e058ec",
"MacAddress": "02:42:ac:11:00:03",
"IPv4Address": "172.17.0.3/16",
"IPv6Address": ""
},
"a9f8c4e1ad23a80547f678213a78bbee9b8baead5b7652e0a8ae780fbd7136a9": {
"Name": "test1",
"EndpointID": "14a3cf0037eff4e7ea60fedce8af04f99b55b4f04c96885e2e5d609049e00e5f",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
}

查看安装docker主机的网卡信息

1
2
ip a

查看如下信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP 
link/ether 02:42:16:1e:ad:91 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:16ff:fe1e:ad91/64 scope link
valid_lft forever preferred_lft forever
7: veth610c9e0@if6: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
link/ether a2:a9:86:8b:a2:fb brd ff:ff:ff:ff:ff:ff link-netnsid 1
inet6 fe80::a0a9:86ff:fe8b:a2fb/64 scope link
valid_lft forever preferred_lft forever
9: veth0593f81@if8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP
link/ether a6:91:b7:df:f5:26 brd ff:ff:ff:ff:ff:ff link-netnsid 2
inet6 fe80::a491:b7ff:fedf:f526/64 scope link
valid_lft forever preferred_lft forever

实际上,docker0是docker在安装时做配置的一个虚拟网络,用于与多个veth进行连接,每创建一个container,都在docker所在的主机创建一个veth,与其container的虚拟网络设备进行配对

安装网桥查看工具

1
2
yum install -y bridge-utils

查看网桥相关信息

1
2
brctl show

查看如下信息

1
2
3
4
bridge name	  bridge id		      STP enabled	  interfaces
docker0 8000.0242161ead91 no veth0593f81
veth610c9e0

在docker容器启动时,可以指定要链接的容器名称,就可以直接通过类似主机名的方式访问到另一台container

1
2
docker run -it --name test3 --link test1 centos

5.6 docker端口映射

1
2
docker run --name web -p 8888:80 nginx

6.Docker持久化

6.1 Data Volume

拉取mysql image

1
2
docker pull mysql:5.7

查看mysql Dockerfile

1
2
3
4
5
6
7
8
9
VOLUME /var/lib/mysql

COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s usr/local/bin/docker-entrypoint.sh /entrypoint.sh # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"]

EXPOSE 3306 33060
CMD ["mysqld"]

启动mysql容器

1
2
docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

查看data volume

1
2
docker volume ls

查看data volume详细信息

1
2
docker volume inspect volumeName

volume name重命名

1
2
docker run -d -v mysql:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

使用已存在的data volume

1
2
docker run -d -v mysql:/var/lib/mysql --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

6.2 Bind Mouting

Bind Mouting与Data Volume不同的是,Data Volume需要在Dockerfile中指定volume参数,而Bind Mouting可以直接指定本地目录与容器目录的对应关系

1
2
docker run -it -d -v $(pwd):/opt/share --name centos-share centos