cha0s32 / cha0s32.github.io

0 stars 0 forks source link

Docker 入门笔记 #3

Open cha0s32 opened 9 months ago

cha0s32 commented 9 months ago

容器生态系统

生态系统: 核心技术、平台技术和支持技术

容器核心技术:让Container在host运行起来的那些技术

组织: Open Container Initiative (OCI)

发布两个规范: runtime spec 和 image format spec

(1) runtime 为容器提供运行环境, docker 和 runtime 的关系类比 Java 和 JVM 的 关系

        lxc runc rkt 是目前主流的三种容器 runtime

(2) 容器管理工具

    对内与 runtime 交互, 对外提供用户接口 (CLI)

(3) 容器定义工具

    docker image 容器的模版
    dockerfile 用于创建docker image
    ACI 与image类似,不过是CoreOS开发的

(5) Registry

    镜像的仓库
  有Docker Hub 和 Quay.io

(6) 容器OS

    专门用于运行容器的操作系统

容器平台技术:使容器作为集群在分布式环境中运行

包括容器编排引擎、容器管理平台、基于容器的PaaS

docker 镜像

base 镜像

含义:

Linux 操作系统 由内核空间和用户空间 组成

对于base 镜像来说,底层直接用 Host 的 kernal,自己只需要提供 rootfs 就行了

base镜像用户空间和发行版一致,内核版本由Docker Host 决定,所有容器共用host的内核

镜像的分层结构

新镜像是从base镜像一层层叠加生成,每安装一个软件,就在现有镜像的基础上增加一层

如果有多个镜像由同一个base镜像构建而来,那docker host 只需要在内存中记载一份base镜像,就可以为所有容器提供服务

镜像的每一层都可以被共享

可写的容器层:当容器启动的时候,会有一个可写层加载到镜像顶部。

-----> 容器层
-----> Image 2
-----> Image 1
-----> Base Image
-----> Bootfs

所有改动,只会在容器层发生,镜像层只读

在容器层,用户看到的是一个叠加之后的文件系统。相同的文件上层会覆盖下层,用户只能访问到最上层的文件。(读文件的顺序从上到下)

修改和删除都是复制到容器层进行(在容器层记录修改和删除操作)

这种特性叫Copy-on-Write

构建镜像

docker commit

步骤:运行容器 ——> 修改 ———> 保存为新镜像

1. 运行容器 

docker run -it ubuntu

2. 修改 (安装程序)

apt-get install vim 

3. 保存为新镜像

ctrl + P + Q

docker ps
docker commit NAMES Image_name

不是推荐的方法,且用户不知道镜像如何创建的,存在安全隐患

dockerfile

  1. 创建dockerfile
  2. 进入dockerfile所在的文件夹
docker build -t ubuntu-with-vim .

-t 参数后面的是制作的镜像名,后面指定 build context ,docker 会默认在这里寻找dockerfile,同时为镜像构建提供所需要的文件或目录

创建过程其实也是一层层创建临时容器,然后再用 docker commit打包镜像

查看镜像分层结构

docker history image_name

docker 会进行镜像缓存,如果构建新镜像过程中,某镜像层已经存在,则不需要重新从 base 开始构建,直接用缓存构建。

dockerfile官方文档

FROM  指定base镜像
MAINTAINER 作者
COPY 从build context复制到镜像
COPY src dest 
COPY ["src","dest"]
ADD 跟COPY类似,若src文件为归档文件,会被自动解压到dest
ENV 设置环境变量
EXPOSE 指定容器中的进程会监听某个端口
VOLUME 将文件或目录声明为volume
WORKDIR 设置镜像中的当前工作目录
RUN 运行指定的指令
CMD 启动时运行指定的命令 可以有多个CMD指令,但只有最后一个生效,可以被docker run之后的参数替换
ENTRYPOINT 跟CMD类似

RUN CMD ENTRYPOINT 区别

run 经常用来安装软件包,CMD设置容器启动后执行的命令,ENTRYPOINT设置容器启动时运行的命令

两种传参方式:

RUN apt-get install python3
CMD echo "Hello World"

# 会被shell解析
RUN ["apt-get","install","python3"]
CMD ["/bin/echo","Hello World"]

# 直接运行命令,不会被解析

CMD 若docker run后面有参数,将会被忽略掉

docker run -it ubuntu /bin/bash

ENTRYPOINT 跟CMD很像,但是不会被忽略,即使有docker run参数,也一定会被执行

且CMD可以给ENTRYPOINT提供额外的参数,但CMD提供的参数可以在容器启动时被动态替换

ENTRYPOINT ["/bin/echo","Hello"]
CMD ["World"]

# 会输出Hello World

替换为 docker run -it ubuntu Cha0s32、

会输出为Hello Cha0s32

分发镜像

发布至官方Registry 的时候,要注意tag的使用

docker tag [镜像名] [tag]
docker login  -u xxx
docker tag httpd xxx/httpd:v1
docker push xxx/httpd:v1

搭建 私有Registry

docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:2

后面 -> 容器 映射 前面 -> 本机

# 拉取镜像,上传私有仓库
docker pull busybox
docker tag busybox:last ip:port/cha0s32/busybox:v1
docker push ip:port/cha0s32/busybox:v1

Docker 容器

删除镜像

docker rmi debian:latest

停止容器

docker stop 短ID

进入容器的方法

通过ctrl + p,ctrl + q 推出attach终端

docker exec -it xxxxx(端ID) bash

退出容器使用exit就可以了

区别:exec会在容器中打开新的终端,启动新的进程,如果想在终端中查看启动命令的输出,用attach,除此之外,用exec

停止容器

docker stop docker_name # 发送SIGTERM信号
docker kill docker_name # 发送SIGKILL信号
docker start

# 让容器发生错误时自动重启
docker run -d --restart=always httpd

# 只是希望容器暂停一段时间
docker pause # 不用占用CPU资源
docker unpause

删除容器

docker rm  ID # 可一次性删除多个

# 删除所有已经退出的容器
docker rm -v $(docker ps -aq -f status=exited)

资源限制

内存限制

-m 设置内存限额

--memory-swap 设置内存+swap限额

docker run -m 200M --memory-swap=300M ubuntu

CPU 限额

docker run it container_A -c 1024

-c 后面是比重,比如说两个容器,一个是1024,一个是512,前者使用的CPU资源将是后者的两倍

Block IO 限额

限制权重

docker run -it  ubuntu --name A --blkio-weight 600 
docker tun -it  ubuntu --name B --blkio-weight 300

限制bps和iops

--device-read-bps
--device-write-bps  # 限制读写数据量

--device-read-iops 
--device-read-iops # 限制IO次数

oflag=direct 可以指定用direct IO 方式写文件,只有加了这个,--device-read-bps才能生效

实现容器的底层技术

cgroup 实现资源限额,namespace 实现资源隔离

cgroup

host 中存在 /sys/fs/cgroup/cpu/docker 目录

以CPU限额为例,每个容器会创建一个cgroup目录,以容器长ID命名

内存、IO限额同理

namespace

Mount 让容器看上去拥有整个文件系统,可以执行mount 和 unmount 命令

UTS 让容器有自己的hostname,通过docker run -h myhost 这样指定

IPC 拥有自己的共享内存和信号量

PID 拥有自己独立的一套PID

Network 拥有独立的网卡、IP、路由

User 可以独立管理自己的用户

Docker网络

目前只讨论单个host的多docker网络

查看网络docker network ls

Bridge网络

docker安装时会创建一个docker0的Linux bridge,如果不指定--network,容器默认都会挂到这里

容器挂到docker0的拓扑:

新建一个容器,容器内部有一个网卡eth0

会新增一个veth接口(网卡),接到docker0

eth0和veth是一对veth pair,可理解为一根虚拟网线连接起来的一堆网卡,目的就是将eth0接到docker0上

查看bridge的配置

docker network inspect bridge

user-defined 网络

提供三种网络驱动:bridge、overlay、macvlan(最后两种用于创建跨主机的网络)

创建类似bridge的网络

docker network create --driver bridge my_net
brctl show # 查看网络结构变化
docker network inspect my_net # 查看my_net的配置

# 指定网段
docker network create --driver bridge -- subnet 172.22.16.0/24 --gateway 172.22.16.1 my_net2
# 创建容器时指定网络
docker run -it --network=my_net2  busybox

可通过 --ip 指定静态IP
docker run -it --network=my_net2 --ip 172.16.22.2 busybox

挂载在同一网络上的容器可以直接互相通信

不同网络上的容器可以通过host作为路由器通信,前提是操作系统上打开了ip forwarding

但是防火墙可能默认隔离不同的容器network

解决方法:给一个网络加上一块另一个网络的网卡

docker network connect my_net2 容器短ID

容器间通信

IP通信

详见上边说的,加入同一个网络或者添加一张一个网络的网卡,这里不加赘述

Docker DNS Server

上一种方法要知道对应的IP,如果无法确认IP,可以使用该方法,可通过容器名通信

如:在docker run的时候挂在同一个网络上,可以直接ping docker-name 的方法访问到

限制:只能在user-defined网络中能使用

join容器

可以使多个容器共享一个网络栈

# 创建一个web1的httpd容器
docker run -d -it --name=web1 httpd

# 创建一个busybox容器,指定joined容器为web1
docker run -it --network-container:web1 busybox

此时busybox的网卡mac地址和IP完全跟web1一致

可以直接使用127.0.0.1来访问web1的http服务

与外网交互

访问外网:

默认创建的容器通过nat访问外网

具体过程是:docker0收到容器172.16.0.2 的访问外网的流量,随后丢给NAT处理,NAT将源地址转化为docker host的网卡地址,再把流量转发给目的地址

外网访问docker:

通过端口映射实现

docker将容器对外提供服务的端口映射到host的某个端口

docker run -d -p host_port:docker_port httpd

每一个映射的端口,host都会启动一个docker-proxy进程来处理访问

Docker存储

storge driver

分层结构的基础

实现了多层数据的堆叠并为用户提供一个单一的合并之后的统一视图

有多种storge driver,目前解决方案是针对每一个Linux发行版,在安装docker的时候,会指定默认storge driver还有底层文件系统

应用场景:无状态的容器,数据不需要持久化存储,容器删除的时候数据可以一起删除

Volume

本质上是Docker Host 文件系统中的目录或文件,能够直接被mount到容器的文件系统中

数据可以永久保存,即使容器被销毁

volume的容量取决于文件系统当前未使用的空间,目前没有方法设置volume的容量

bind mount

将host上已存在的目录或文件mount到容器上

使用 -v , -v :

bind mount 可以让host 与 容器共享数据

可以指定数据的读写权限,默认可读可写

指定只读 ,容器无法对数据进行修改

-v ~/htdocs:/usr/local/apache2/htdocs:ro

缺点:当容器迁移到其他host,该host没有要mount的数据或数据不在相同的路径,操作会失败

docker managed volume

不需要指定mount源,知名mount point 就行

docker run -d -p 80:80 -v /usr/local/apache2/htdocs httpd

Data volume 的位置在配置文件中可以看到

docker inspect 容器ID

关注Mounts列表,Source就是该volume在host上的目录

步骤:

docker在 /var/lib/docker/volumes 中生成一个随机目录作为mount源

如果mount到的目录已经存在,则把数据复制到mount源

将volume mount到相关目录

docker和host数据共享

docker managed volume由于是容器启动时才生成,所以需要将共享数据复制到volume中

一种就是直接linux cp命令将数据复制到mount源文件夹

一种是通过docker cp在容器和host间进行数据复制

docker cp ~/htdocs/index.html 容器ID:/usr/local/apache/htdocs/

容器间数据共享

一种方法,一个目录mount到不同容器的文件系统

另一种方法是使用 volume container

volume就是专门给其他容器提供volume的容器

# 建立 volume container

docker create --name vc_data \
-v ~/htdocs:/usr/local/apache2/htdocs \
-v /other/useful/tools \
busybox

建立就好,本来不需要运行,只是提供数据

bind mount:存放Web Server的静态文件

docker managed vulume:存放一些实用工具

其他容器可以通过 --volumes-from 实用vc_data 这个volume container

docker run --name web1 -d -p 80 --volume-from vc_data httpd

Data-packed volume container

上述例子中,volume container 的数据是在host中,现在目的是将数据完全放到volume container中

原理:将数据打包到镜像中

FROM busybox:latest
ADD htdocs /usr/local/apache2/htdocs
VOLUME /usr/local/apache2/htdocs

打包镜像,使用该镜像创建容器

启动httpd容器,并使用--volume-from指定volume container

docker run -d -p 80:80 --volume-from vc_data httpd

该方法非常适合迁移

数据迁移

以docker私有仓库为例

创建命令

docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:2

迁移步骤(启用新版本)

  1. docker stop 掉当前容器
  2. 启用新版本容器并mount原有的volume
docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:latest

销毁

对于docker managed volume的,在docker rm容器的时候,带上-v 参数,会把volume一并删除

前提是没有其他容器mount该volume

没有容器mount到volume,但volume还存在,可以使用

docker volume ls # 查询
docker volume rm {volume_ID} #删除

# 批量删除
docker volume rm $(docker volume ls -q)