文|Seraph
01 | 容器生态环境概览
- 安装(Ubuntu)
- 安装docker依赖包:
apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common
- 添加docker的官方GPG秘钥:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
- 将docker源(稳定版本)添加到
/etc/apt/sources.llist
:add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
- 更新apt包引用:
apt-get update
- 安装Docker Engine-Community 和 containerd :
apt-get install docker-ce docker-ce-cli containerd.io
-
由于Docker Hub在国外,下载镜像会比较慢,DaoCloud提供国内镜像服务。
注册账户并登陆后,点击右上角的小火箭打开加速器
拷贝下面的链接在相应的系统下执行即可。
然后使用systemctl restart docker.service
重启Docker deamon。 -
基本docker命令
命令 | 含义 |
---|---|
docker run | 运行容器 |
docker pull | 拉取镜像 |
docker images | 查看镜像 |
docker commit | 提交容器更新至新的镜像,添加--no-cache 参数表示不是用缓存。 |
docker build | 构建镜像 |
docker history | 查看镜像构建历史 |
docker tag | 给镜像打标签 |
docker login | 登录docker hub |
docker push | 上传image至docker hub |
docker search | 搜索Docker Hub中的镜像 |
docker rmi | 删除Docker Host上的镜像 |
docker rm | 删除容器 |
docker ps | 查询Docker Host上运行的容器,加上-a 查询所有的容器(包含停止的) |
docker stop | 停止容器 |
docker attch | 直接进入容器启动命令的终端,不会打开新的终端 |
docker exec | 在容器打开新的终端 |
docker logs | 查看容器输出 |
docker start | 启动容器 |
docker restart | 重启容器 |
docker kill | 终止容器 |
docker pause | 暂停容器 |
docker uppause | 恢复暂停的容器 |
docker create | 创建容器 |
docker info | 查看docker相关信息 |
docker inspect | 查看容器信息 |
docker volume | 查看容器挂载信息 |
docker cp | 容器与host数据拷贝 |
docker top | 查询容器内运行进程情况 |
docker stats | 监控容器资源使用情况 |
docker save/load | 导出/导入镜像 |
docker export/import | 导出/导入容器 |
02 | 容器技术
- 容器由共享主机的内核系统,这个是与虚拟机最本质的区别。
容器一般由两部分组成:应用程序本身、依赖(库等)。
所以容器可以看成一个个相互隔离的进程。占用的体积也小。 - 容器最关键的特性是:可移植性。解决了不同环境下部署难得问题。
- Docker核心组件
查询docker运行状态:systemctl status docker.service
- 修改远程登陆功能(应用场景应该很低)
- 修改
/etc/systemd/system/multi-user.target.wants/docker.service
文件,在ExecStart
后面添加-H tcp://0.0.0.0
。 - 重启Docker daemon
systemctl daemon-reload
systemctl restart docker.service
-
Docker镜像
以Dockerfile构建而来,Dockerfile构建过程中,又是利用docker commit进行构建。 -
docker run命令 = docker pull + 启动
03 | Docker镜像
一、 Dockerfile
- 指令
指令 | 含义 |
---|---|
FROM | 指定基础镜像 |
MAINTAINER | 设置镜像作者 |
CMD | 容器启动时运行指定的命令,只有最有一个CMD会生效,同事CMD会被docker run 之后的参数替换 |
RUN | 运行指定指令 |
COPY | 从build context复制文件到容器中 |
ADD | 与COPY类似,但是src文件为归档文件(tar\zip\tgz等),会被自动解压 |
ENV | 设置环境变量,环境变量可被后面的指令使用 |
EXPOSE | 指定容器中的进程监听某个端口 |
VOLUME | 将文件或目录声明为volume |
WORKDIR | 设置工作目录 |
ENTRYPOINT | 设置容器启动时运行的命令,只有最后一个会生效,且CMD或docker run值后的运行参数会被当作参数传递给ENTRYPOINT |
-
两种运行格式Shell和Exec格式:
Shell格式(默认调用/bin/sh -c):<instruction> <command>
Exec格式:<instruction> ["executable", "param1", "param2", ...]
-
由于每一层都会创建镜像层,当有些由于不同时间执行可能受影响的指令,需要和其他执行搭配在一样进行执行。
比如apt update
,不能单独占一行,否则会使用之前的镜像层。 -
当CMD与ENTRYPOINT配合使用时,CMD提供参数,ENTRYPOINT必须使用Exec格式。
二、原理
- 容器维护的是rootfs(用户空间),内核空间都是使用主机的Kernel,所以在容器内查询Kernel,都和主机一样。
所以当容器本身应用对内核版本有要求的时候,不建议使用容器,推荐使用虚拟机。
三、构建镜像
-
一般能用官方或者别人创建稳定的镜像,一定要先用。没有才考虑自己构建容器。
推荐使用Dockerfile方式构建镜像,不推荐使用docker commit
方式创建。
由于不能复用,再者使用者也不知道是创建出来的,无法对镜像进行审计。 -
每执行一条指令,对容器修改后,都会使用类似
docker commit
操作,新建镜像层。这样的话,如果其中某个指令执行失败,也能得到前一个指令执行后构建出来的镜像。 -
镜像是分层结构的,同时我们在容器中的修改只会保存在容器层。
Copy-on-Write
添加文件:在容器中创建文件,新文件被添加到容器层。
读取文件:从容器层往下,再到镜像层,查找文件。
修改文件:先如读取文件时一样查找文件,然后将文件复制到容器层进行修改。
删除文件:先如读取文件时一样查找文件,在容器层中记录此删除操作。(实际上不删除文件)
四、分发镜像
-
每个repository可以有多个tag,所以当发新版本的时候,可以使用tag管理版本关系。
我们可以把tag看成linux下的链接,为了我们用通用的名字使用特定的版本。
Image的ID是唯一,如果使用docker images查询ID相同,说明是同一份镜像,只不过tag不一样而已。 -
搭建本地Registry
docker run -d -p 5000:5000 -v /myregistry:/var/lib/registry registry:2
repository的完整格式是:[registry-host]:[post]/[username]
,只不过Docker Hub可以省略。
私人库其实就是在我们机器上启动一个Registry容器,使用上仅是要加上repostitory完整格式指明是哪个私人库,其它和访问Docker Hub一样。
04 | 容器
一、Run容器
- 当进程退出时(或OOM),Docker会根据
--restart
策略判断是否要重启容器。
- 当加
--restart=always
参数时,表示无论容器什么原因退出,都会重启容器。 - 当加
--restart==on-failure:3
参数时,表示容器退出非0时,则重启容器。
- 内存限制
-m
:内存限制--memory-swap
:设置内存+swap的使用限制-vm
:启动内存工作线程数-vm-bytes
:每个线程分配内存数
- CPU限制
-c
:CPU优先级,数字越大,占比越高。--cpu
:CPU数量
- block IO带宽限额
--blkio-weight
:block IO权重。--device-read-bps
:限制读某个设备的bps--device-write-bps
:限制写某个设备的bps--device-read-iops
:限制读某个设备的iops--device-write-iops
:限制写某个设备的iops
其中bps
表示每秒读取多少byte。
iops
表示每秒IO的次数。
测试例子:
<1> 创建限制写/dev/sda的速率30M/s:docker run -it --device-write-bps /dev/sda:30MB ubuntu
。
<2> 在容器中运行dd测试:time dd if=/dev/zero of=test.out bs=1M count=800 oflag=direct
。
progrium/stress
压力测试镜像。
二、删除容器
- 删除所有停止的容器
docker rm -v $(docker ps -qf status=exited)
。
三、实现容器的底层技术
-
cgroup实现资源限额。
cgroup的主目录为:/sys/fs/cgroup
。
按cpu、memory、blkio等区分的多个子目录:
每个容器的限额会在相应的子目录下的docker目录下新建一个以容器ID为名的目录。其中保存着该容器的限额信息。 -
namespace实现资源隔离。
隔离的资源有:Mount、UTS(hostname)、IPC(共享内存和信号量)、PID、Network、User。
04 | Docker网络
Docker容器安装时会自动在host上新建三个网络,用docker network ls
可以查询到。
docker网络run参数:--network
。
一、 none网络
none网络表示在这个网络下的容器除了lo外,没有其他任何网卡。适用于需要网络隔离的场景
二、host 网络
host网络表示共享Docker host的网络栈。
使用host网络的有点是网络性能最好。但因为是和host共享网络栈,所以需要避免端口冲突。
三、bridge网络
Docker安装时会创建一个命名为docker0的linux bridge。我们新建的容器默认网络模式为bridge,所以会挂载到docker0上。不过虚拟网卡是成对的,一个在host上,与docker0连接;一个在容器内。
- 通过
docker network inspect bridge
命令可以查看网桥设置。 brctl show
查看网桥信息。
四、user-defined网络
- 可以使用
docker network create
创建网络
--driver
:网络驱动,bridge、overlay和macvlan。--subnet
:指定子网。--gateway
:指定网关。--ip
:指定ip(前提已经使用–subnet指定了子网)
eg.docker network create --driver bridge --subnet 172.22.16.0/24 --gatway 172.22.16.1 my_net2
。
- 使用
docker network connect
可以给容器添加网络。
建立在同一个网络上的容器才能IP通信,所以当两个容器需要IP通信时,可以让其中一个容器添加另一个容器的网络即可。
五、容器间通信
容器间可通过IP、Docker DNS Server或joined容器三种方式通信。
- 建立在同一个网络上的容器即能IP通信。但是仅能通过静态IP。
- Docker DNS Server通信,可以给容器主机命名,通过对方的主机名与在一个user-defined网络(其它网络不行)上进行通信。
- joined容器,我们可以通过joined容器的形式与容器共享网络栈:
docker run -it --network=container:容器名
。
六、与外部网络连接
- 我们默认以bridge模式建立的容器是能与外部网络连接的。其主要是docker0通过NAT方式与外部连接。
- 外部网络访问容器,通常是根据容器映射到host上的端口。
每一个映射的端口,host都会启动一个docker-proxy进程来处理。
06 | Docker 存储
一、storage driver
- storage driver实现了多层数据的堆叠。docker支持多种storage driver实现,优先使用系统本身默认的storage driver,这个样稳定性更好。
二、Data Volume
对于需要持久化的数据(即使容器删除,数据也保留),需要使用Data Volume来处理。
有两种方式:bind mount、docker managed volume。
- bind mount
- 使用方式:在docker run加入
-v <host path>:<container path>[:读写属性]
即可。
其实就是目录映射。 - Data Volume默认是可读可写,可以指定添加
:ro
只读 - 缺点:需要指定目录,移植后如发现host没有指定目录会出错。
- docker managed volume
- 使用方式:在docker run加入
-v <container path>
即可。docker会为容器自动生成host共享目录。 - 当移植时,不会自动打包数据,只会生成相应的挂载目录。
- 另一种数据共享方式:volume container
volume container的意识是专门为其它容器提供volume,其容器只需被create就行,不用run。
- 新建volume容器示例:
docker create --name vc_data -v ~/htdocs:/usr/local/apache2/htdocs -v /other/useful/tools busybox
。 - 其它容器使用volume容器示例:
docker run --name web1 -d -p 80 --volumes-from vc_data httpd
。
- 在Dockerfile文件中使用VOLUME、ADD添加共享文件或volume。(自包含的)
- volume删除
- 删除容器是加上
-v
参数可以将容器使用到的volume也删除,但前提是没有其它容器也使用了。 - 可以使用
docker volume ls
查询volume的情况。如果删除容器时,没有加-v
参数,docker volume ls
还是能查找volume存在的。
如要继续删除,可以使用docker volume rm
。 - 删除所有孤儿volume指令:
docker volume rm $(docker volume ls -1)
。
07 | 多主机管理
docker machine,目前没有实际环境,后面再试。
99 | 问题解决
- 在ubuntu下使用
--memory-swap
会弹出Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.
解决:尝试在aliyun服务器改动过配置文件,然后服务器就起不来了。。。。