地址
Docker依赖的 Linux内核特性
Namespaces 命名空间
Linux内核提供的一种对
系统资源隔离
的机制,例如进程、网络、挂载点等资源。Control groups(cgroups) 控制组
Linux内核提供的一种限制进程资源的机制;例如CPU、内存等资源。
Union file systems(联合文件系统)
Container和Image的分层
Namespaces五种命名空间
- PID(Process iD)进程隔离
- NET(Network)管理网络接口
- IPC(InterProcess Communication)管理跨进程通信的访问
- MNT(Mount)管理挂载点
- UTS(Unix Timesharing System)隔离內核和版本标识
Control groups的功能
- 资源限制
- 优先级设定
- 资源计量
- 资源控制
Docker容器的能力
Namespaces和Control groups使得Docker具有以下功能:
- 文件系统隔离:每个容器都有自己的root文件系统
- 进程隔离:每个容器都运行在自己的进程环境中
- 网络隔离:容器间的虚拟网络接口和|P地址都是分开的
- 资源隔离和分组:使用 groups将CPU和内存之类的资源独立分配给每个Docker容器
在运行中的容器内启动新进程
:
1 | docker exec [-d][-i][-t] 容器名 [COMMAND] [ARG...] |
Docker的CS模式
- 守护进程会在Docker启动后一直运行 , 负责实现Docker的各种功能
- 用户则是通过与Docker的客户端交互 , 进而与Docker的守护进程信息通讯
现在我们学得 , 客户端都是以命令行的形式与用户进行交互的 .
然而Docker还有API形式 , 可以集成到代码中 . 这个API被称为
remote API
Docker Machine
docker-machine就是帮助你快速去创建安装docker环境的工具
- 当人们说“Docker”时,他们通常是指 Docker Engine,它是一个客户端 - 服务器应用程序,由 Docker 守护进程、一个REST API指定与守护进程交互的接口、和一个命令行接口(CLI)与守护进程通信(通过封装REST API)。Docker Engine 从 CLI 中接受docker 命令,例如
docker run <image>
、docker ps
来列出正在运行的容器。 - Docker Machine 是一个用于配置和管理你的宿主机(上面具有 Docker Engine 的主机)的工具。通常,你在你的本地系统上安装 Docker Machine。Docker Machine有自己的命令行客户端 docker-machine 和 Docker Engine 客户端 docker。你可以使用 Machine 在一个或多个虚拟系统上安装 Docker Engine。
- 使用docker-machine命令,可以启动、审查、停止、重启托管的docker 也可以升级Docker客户端和守护程序并配置docker客户端和宿主机通信。
Docker machine命令
命令 | 说明 |
---|---|
active | 查看当前激活状态的Docker主机 |
config | 查看当前激活状态Docker主机的连接信息 |
creat | 创建Docker主机 |
env | 显示连接到某个主机需要的环境变量 |
inspect | 以json格式输出指定Docker的详细信息 |
ip | 获取指定Docker主机的地址 |
kill | 直接杀死指定的Docker主机 |
ls | 列出所有的管理主机 |
provision | 重新配置指定主机 |
regenerate-certs | 为某个主机重新生成TLS信息 |
restart | 重启指定的主机 |
rm | 删除某台Docker主机,对应的虚拟机也会被删除 |
ssh | 通过SSH连接到主机上,执行命令 |
scp | 在Docker主机之间以及Docker主机和本地主机之间通过scp远程复制数据 |
mount | 使用SSHFS从计算机装载或卸载目录 |
start | 启动一个指定的Docker主机,如果对象是个虚拟机,该虚拟机将被启动 |
status | 获取指定Docker主机的状态(包括:Running、Paused、Saved、Stopped、Stopping、Starting、Error)等 |
stop | 停止一个指定的Docker主机 |
upgrade | 将一个指定主机的Docker版本更新为最新 |
url | 获取指定Docker主机的监听URL |
version | 显示Docker Machine的版本或者主机Docker版本 |
help | 显示帮助信息 |
容器的本质
- 容器其实本质就是在镜像层上面添加了一个
container layer
, 这个container layer
是可读可写的 - image负责app的存储和分发,Container负责运行app
查找退出状态的容器
1 | docker container ls -f "status=exited" |
DockerFile注意
为了美观,复杂的RUN请用反斜线换行!避免无用分层,合并多条命令成一行!
1
2yum update && yum install-y vim \
python-dev #反斜线换行用 WORKDIR,不要用 RUN CD!
尽量使用绝对目录!
1
2WORKDIR /root
ADD hello test/ # /root/test/hello能使用copy就不要使用add
尽量使用ENV , 增加可维护性
RUN vs CMD vs ENTRYPOINT
RUN:执行命令并创建新的 Image Layer
CMD:设置容器启动后默认执行的命令和参数
ENTRYPOINT:设置容器启动时运行的命令和参数
Shell格式和Exec格式
shell格式
1
2
3RUN apt-get install -y vim
CMD echo "hello docker"
ENTRYPOINT echo "hello docker"exec格式:
1
2
3RUN ["apt-get" , "install" ,"-y" ,"vim"]
CMD ["/bin/echo" , "hello docker"]
ENTRYPOINT ["/bin/echo" , "hello docker"]
DockerFile的Debug
当我们build出现问题的时候就会有
1 | Step 1/7 : FROM python |
DockerFile支持我们进入这些出现Bug的image layer
.
也就是说 , 12wdx2asd2
, asdw24dasx
, 2dfg44dsad
都是可以通过docker run -it xxxx
进入的
Docker关于命令的典型写法
1 | FROM ... |
执行docker run -it my_container --vm 1
解释 :
ENTRYPOINT
的作用是追加docker run -it my_container --vm 1
后面的--vm 1
会被CMD捕获- 所以上面的Dockerfile就有这种效果
- 默认执行
/usr/bin/stress --verbose
- 如果docker run命令带有参数,就像
docker run -it my_container --vm 1
那么命令就会变成/usr/bin/stress --vm 1
使用ENTRYPOINT 加载sh文件
1 | FROM python |
1 | export APP_CONFIG="test.server.config" |
Docker网络
单机
- Bridge Network
- Host Network
- None Network
多机
- Overlay Network
网络分层复习
Ping和 telnet
- ping检测IP的可达性
- telnet验证服务的可用性
(比如远端服务器的3306端口开了个SQL服务 , 我们就可以使用Telnet检测是否可以使用这个服务)
容器之间的网络连接
- 容器指甲你的网络连接其实是内网 , 外部其实是无法访问的
- 两个容器就像下图的Namespace test1和Namespace test2
docker network
docker network ls
当前宿主机上有哪些网络
1 | docker network ls |
docker network inspect [networkID ]
查看当前网络的信息
其中有一个Containers
, 查看使用当前网络的容器
1 | "Containers": { |
bridge网络下容器之间是怎么通讯的
- 不同的容器通过不同的pair连接到docker0
- docker0就是一个网桥 , 以此沟通了两个容器
- docker0 再通过NAT连接到宿主机的eth0网卡 去访问外网 , 这就解释了docker容器为什么能访问外网
bridge网络下容器是怎么访问外网的 (网络地址转换NAT)
NAT其实就是一个映射翻译表
- 内部网络有私有地址 , 但是只有少数几个网络出口
- 因此就需要使用NAT来对内部网络地址进行翻译 , 确定哪条信息是属于哪个网络地址的
说人话 :
- NAT用于映射内部网络到宿主机的网卡
- 比如说宿主机从外网获取数据 , 就是通过NAT知道到底这份数据是属于哪个容器的
容器之间的link
默认 bridge 网络上的容器只能通过 IP 地址互相访问,除非使用在 docker run 时添加 —link
参数。
- link用于容器之间的连接 , 常用于后台容器访问数据库容器
- 一般而言 , 后台容器需要知道数据库容器的ip地址才能连接,但是ip地址是不确定的 .
- 因此我们可以数据库容器起一个名字 , 然后通过名字来访问这个容器
1 | docker run -d --name test2 --link test1 |
创建test2 , 并且把test2连接到test1上(这个test1一般就是数据库容器)
此时我们使用docker exec -it test2 /bin/sh
进入test2 , 然后执行ping test1
发现可以ping通
docker run -d --name test2 --link test1
有点类似于该test2添加一个DNS记录(实际上就是加入到hosts文件)- 原理 : 因为test1容器本身就是存在的 , 所以可以获取test1的IP,将其映射到test1上
- 使用 : 如果tests1就是mysql , 那么我们就可以使用
test1:3306
来访问MySQL了
注意 :
- LINK有指向性
docker run -d --name test2 --link test1
只能是test2指向test1 , 不能反过来- 也就是说 , 在test1容器里 , 是不能ping test2的
- 本质 : 只会往一个容器的hosts文件加入对方的IP
自定义network
1 | docker network create -d bridge my-bridge |
-d
: drive
之后使用docker network ls
查看
1 | $ docker network ls |
创建容器时指定network
这样 , 我们就可以在创建容器的时候指定network
1 | docker run -d --network my-bridge busybox |
将存在的容器连接到另一个network
1 | docker network connect my-bridge busybox |
将busybox容器 连接到 my-bridge 这个网络上
注意 : 如果两个容器连接到自定义的网络 , 那么这两个容器就是自动link好的
如果是连接到默认的
bridge
则不会
注意 : docker-compose中的networks是这个容器待加入的网络 , 如果网络不存在就会报错 .
这时就需要在底下创建network
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 version: "2"
services:
mongodb:
image: mongo:4
container_name: devops-mongo # 容器名
ports:
- "27017:27017"
volumes:
- "/data/docker_local/mongo/configdb:/data/configdb"
- "/data/docker_local/mongo/data/db:/data/db"
command: --auth # 开启授权验证
networks:
- mongo_net
# 创建network
networks:
mongo_net:
name: mongo_net如果你想要你的容器加入到一个已存在的网络中,使用
external
选项:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 version: "3.7"
services:
proxy:
build: ./proxy
networks:
- outside
- default
app:
build: ./app
networks:
- default
networks:
outside:
external: true这时候不会去创建一个
[projectname]_outside
的网络, Compose 会去查找一个已存在的叫做outside
的网络,并且将它和proxy
服务相连。
window或Mac中 run命令-p参数的本质
- -p的功能就是映射了
eth1
和docker0
none网络
创建一个network为none的容器 , 我们通过 docker network inspect [networkID]
, 查看Containers信息
1 | "Containers": { |
发现MacAddress
,IPv4Address
,IPv6Address
都是空的
所以 , 该容器只能通过docker exec -it
的方式连接这个容器
host网络
创建一个network为host的容器 , 我们通过 docker network inspect [networkID]
, 查看Containers信息
1 | "Containers": { |
host网络 : 没有独立的network Namespace , 共用宿主机的网络
network总结
- none : 只能通过
docker exec -it
的方式连接这个容器 - bridge : 在不使用
--link
的时候 , 使用默认的bridge的容器之间只是IP互通 , 无法通过link方式连接 , 而使用自定义容器却可以以link方式连接 - host : 没有独立的network Namespace , 共用宿主机的网络
Docker持久化
持久化数据的方案
- 基于
本地文件系统
的 Volume : 可以在执行 Docker create或 Docker run时,通过-v
参数将主机的目录作为容器的数据卷。这部分功能便是基于本地文件系统的 volume管理。 - 基于plugin的 Volume : 支持第三方的存储方案,比如NAS, aws
数据持久化 : Data Volume
docker volume ls
1 | docker volume ls |
docker volume inspect
1 | docker volume inspect f4d1af1ff85d46d3d8faabca45f7e898da2074413a9ec1607d05290527806367 |
可见 ,
- Data Volume其实并不是将数据存储到容器的
layer
中 - 而是将数据存储到
Volume
中
1 | docker run -v msyql:/dev/mysql/data |
注意 , 这里并不是连接宿主机和容器的数据卷 , 而是给
/dev/mysql/data
这个数据卷起一个名字mysql
而已我们可以通过
docker volume create mysql
来创建这个数据卷eg:
1 docker network create -d bridge test_volume
数据持久化:Bind Mouting
1 | docker run -v /home/aaa:/root/aaa |
联通宿主机和容器的两个目录
Docker-compose
为什么使用Docker-compose
多容器的APP太恶心
- 要从 Dockerfile build image或者Dockerhub拉取image
- 要创建多个 container
- 要管理这些 container(启动停止删除)
所以我们就想要使用文件来批量处理多个容器
什么是Docker-compose
- Docker Compose是一个工具
- 这个工具可以通过一个yml文件定义多容器的 docker 应用
- 通过一条命令就可以根据yml文件的定义去创建或者管理多个容器
就像dockerFile的默认名为dockerfile
一样 , 这个yml文件的默认名为docker-compose.yml
Docker-compose三大概念
- Serices
- Networks
- Volumes
- 使用
docker-compose up
就可以将serices启动了- 默认寻找
docker-compose
文件- 如果文件名改了 , 就可以使用
docker -compose -f my_docker-compose.yml up
启动
Serices
- 一个 service代表一个 container,这个 container 可以从dockerhub的 image 来创建,或者从本地的 Dockerfile buld出来的image来创建
- Service的启动类似 docker run,我们可以给其指定network和 volume,所以可以给 service指定 network 和Volume的引用
docker-compose.yml示例一
1 | version: '3' |
docker-compose.yml示例二
1 | version: '3' |
指定自定义网络
1 | version: '3' |
配置默认网络
1 | version: '2' |
使用已经存在的网络
1 | networks: |
docker-compose注意
同时指定 image 和 build 两个标签
如果同时指定了 image 和 build 两个标签,那么 Compose 会构建镜像并且把镜像命名为 image 后面的那个名字。
1 | build: ./dir |
environment和arg的区别
- environment可以保存变量到镜像里面,也就是说启动的容器也会包含这些变量设置,
- arg 不能保存变量到镜像里面。
extends标签
- extends标签可以扩展另一个服务,
- 扩展内容可以是来自在当前文件,也可以是来自其他文件,
- 相同服务的情况下,后来者会有选择地覆盖原有配置。
docker-compose命令
就像
docker ps
一样 ,有docker-compose ps
命令同理有 :
docker-compose rm
docker-compose stop
docker-compose start
docker-compose down
: 退出并删除容器 (不会删除image)docker-compose up -d
: 后台运行容器docker-compose images
: 列出docker-compose.yml文件中定义的imagedocker-compose exec my_container XXX
: 执行某个容器的exec的XXX命令eg :
docker-compose exec my_container bash
docker
Docker-compose常用命令
docker能用的命令docker-compose基本都能用
- docker-compose ps
- docker-compose exec
- docker-compose images
- docker-compose build
- …
命令 | 描述 |
---|---|
docker-compose up -d nginx | 构建建启动nignx容器 |
docker-compose exec nginx bash | 登录到nginx容器中 |
docker-compose down | 删除所有nginx容器,镜像 |
docker-compose ps | 显示所有容器 |
docker-compose restart nginx | 重新启动nginx容器 |
docker-compose run –no-deps –rm php-fpm php -v | 在php-fpm中不启动关联容器,并容器执行php -v 执行完成后删除容器 |
docker-compose build nginx | 构建镜像 。 |
docker-compose build –no-cache nginx | 不带缓存的构建。 |
docker-compose logs nginx | 查看nginx的日志 |
docker-compose logs -f nginx | 查看nginx的实时日志 |
docker-compose config -q | 验证(docker-compose.yml)文件配置,当配置正确时,不输出任何内容,当文件配置错误,输出错误信息。 |
docker-compose events –json nginx | 以json的形式输出nginx的docker日志 |
docker-compose pause nginx | 暂停nignx容器 |
docker-compose unpause nginx | 恢复ningx容器 |
docker-compose rm nginx | 删除容器(删除前必须关闭容器) |
docker-compose stop nginx | 停止nignx容器 |
docker-compose start nginx | 启动nignx容器 |
docker-comose scale 实现负载均衡
- scale : 规模
- 就是说 , scale命令能创建多个一模一样的容器
示例 :
现在docker-compose.yml
文件如下
1 | version: "3" |
1 | docker-compose up --scale web=3 |
上面的代码其实就执行错误
1
2
3 Starting flaskredis_web_1 ... done
Starting flaskredis_web_2 ... error
Starting flaskredis_web_3 ... error
- 可见第一个启动成功 , 接下来的都是启动失败
- 原因 :
端口占用
,- flask需要使用端口 , 而
--scale
开启了一模一样的容器 , 自然端口也一样- 这时只需要将
docker-compose.yml
中的posts标签删掉即可
因为开启了3个web容器 , 但只有一个redis容器 , 这么这3个web容器都会去访问redis容器
如果在前面添加一个负载均衡器
(HAProxy) , 能把外界的访问平均负载到多个web容器 , 这样就实现了负载均衡