地址

Docker技术教程

Docker Compose 方式下的容器网络基础知识点

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模式

1573281178333

  • 守护进程会在Docker启动后一直运行 , 负责实现Docker的各种功能
  • 用户则是通过与Docker的客户端交互 , 进而与Docker的守护进程信息通讯

现在我们学得 , 客户端都是以命令行的形式与用户进行交互的 .

然而Docker还有API形式 , 可以集成到代码中 . 这个API被称为remote API

1573281455767

1573363261690

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

1573364789756

查找退出状态的容器

1
docker container ls -f "status=exited"

DockerFile注意

  • 为了美观,复杂的RUN请用反斜线换行!避免无用分层,合并多条命令成一行!

    1
    2
    yum update && yum install-y vim \
    python-dev #反斜线换行
  • 用 WORKDIR,不要用 RUN CD!尽量使用绝对目录!

    1
    2
    WORKDIR /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
    3
    RUN apt-get install -y vim
    CMD echo "hello docker"
    ENTRYPOINT echo "hello docker"

    exec格式:

    1
    2
    3
    RUN ["apt-get" , "install" ,"-y" ,"vim"]
    CMD ["/bin/echo" , "hello docker"]
    ENTRYPOINT ["/bin/echo" , "hello docker"]

DockerFile的Debug

当我们build出现问题的时候就会有

1
2
3
4
5
6
Step 1/7 : FROM python
---> 12wdx2asd2
Step 2/7 : RUN pip install flask
---> asdw24dasx
Step 3/7 : COPY app.py /app/
---> 2dfg44dsad

DockerFile支持我们进入这些出现Bug的image layer .

也就是说 , 12wdx2asd2 , asdw24dasx , 2dfg44dsad 都是可以通过docker run -it xxxx进入的

Docker关于命令的典型写法

1
2
3
4
FROM ...
...
ENTRYPOINT["/usr/bin/stress"]
CMD [--verbose]

执行docker run -it my_container --vm 1

解释 :

  • ENTRYPOINT的作用是追加
  • docker run -it my_container --vm 1 后面的--vm 1会被CMD捕获
  • 所以上面的Dockerfile就有这种效果
    1. 默认执行/usr/bin/stress --verbose
    2. 如果docker run命令带有参数,就像docker run -it my_container --vm 1 那么命令就会变成 /usr/bin/stress --vm 1

使用ENTRYPOINT 加载sh文件

1
2
3
4
5
6
FROM python
COPY . /test
WORKDIR /test
RUN pip install -r requirements.txt
EXPOSE 5000
ENTRYPOINT ["scripts/dev.sh"]
1
2
3
4
5
export APP_CONFIG="test.server.config"
python manage.py create_db
python manage.py create_admin
python manage.py create_data
python manage.py runserver -h 0.0.0.0

Docker网络

单机

  • Bridge Network
  • Host Network
  • None Network

多机

  • Overlay Network

网络分层复习

1573369538056

Ping和 telnet

  • ping检测IP的可达性
  • telnet验证服务的可用性
    (比如远端服务器的3306端口开了个SQL服务 , 我们就可以使用Telnet检测是否可以使用这个服务)

容器之间的网络连接

  • 容器指甲你的网络连接其实是内网 , 外部其实是无法访问的
  • 两个容器就像下图的Namespace test1和Namespace test2

1574261087516

docker network

docker network ls

当前宿主机上有哪些网络

1
2
3
4
5
docker network ls
NETWORK ID NAME DRIVER SCOPE
b6c8a4ac1b80 bridge bridge local
2683635c0cda host host local
528b0dc5abe2 none null local

docker network inspect [networkID ]

查看当前网络的信息

其中有一个Containers , 查看使用当前网络的容器

1
2
3
4
5
6
7
8
9
"Containers": {
"81bd6a69a884a5b5848b5ce7b5ed697b0376020d4beb5785262f2e703abb4ca0": {
"Name": "epic_napier",
"EndpointID": "7952063ceabc83147def3b9a709800c5ebd9606ef7df76891623e015c2d169e9",
"MacAddress": "02:42:ac:11:00:02",
"IPv4Address": "172.17.0.2/16",
"IPv6Address": ""
}
},

bridge网络下容器之间是怎么通讯的

1574261890525

  • 不同的容器通过不同的pair连接到docker0
  • docker0就是一个网桥 , 以此沟通了两个容器
  • docker0 再通过NAT连接到宿主机的eth0网卡 去访问外网 , 这就解释了docker容器为什么能访问外网

bridge网络下容器是怎么访问外网的 (网络地址转换NAT)

1573369728786

NAT其实就是一个映射翻译表

  • 内部网络有私有地址 , 但是只有少数几个网络出口
  • 因此就需要使用NAT来对内部网络地址进行翻译 , 确定哪条信息是属于哪个网络地址的

说人话 :

  • NAT用于映射内部网络到宿主机的网卡
  • 比如说宿主机从外网获取数据 , 就是通过NAT知道到底这份数据是属于哪个容器的

默认 bridge 网络上的容器只能通过 IP 地址互相访问,除非使用在 docker run 时添加 —link 参数。

  • link用于容器之间的连接 , 常用于后台容器访问数据库容器
  • 一般而言 , 后台容器需要知道数据库容器的ip地址才能连接,但是ip地址是不确定的 .
  • 因此我们可以数据库容器起一个名字 , 然后通过名字来访问这个容器

img

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
2
3
4
5
6
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
000836550c9f bridge bridge local
2683635c0cda host host local
d5de830f3890 my-bridge bridge local
528b0dc5abe2 none null local

创建容器时指定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参数的本质

1574263682922

  • -p的功能就是映射了eth1docker0

none网络

创建一个network为none的容器 , 我们通过 docker network inspect [networkID] , 查看Containers信息

1
2
3
4
5
6
7
8
9
"Containers": {
"85735c6a7a54d4f8ed8b603485fe973d60c4ab7240556b853cbacb822537870a": {
"Name": "pensive_borg",
"EndpointID": "793d5c30f9e715151544b5c6eb9009cce59d5ba761d20fd3b66525aac37ec032",
"MacAddress": "",
"IPv4Address": "",
"IPv6Address": ""
}
},

发现MacAddress,IPv4Address,IPv6Address都是空的

所以 , 该容器只能通过docker exec -it 的方式连接这个容器

host网络

创建一个network为host的容器 , 我们通过 docker network inspect [networkID] , 查看Containers信息

1
2
3
4
5
6
7
8
9
"Containers": {
"df4d59714558f78fc9ece107fc42a28e6a4dd9e9ddefc47b5833ecd41f3f1227": {
"Name": "hardcore_leakey",
"EndpointID": "ca5e05d54e5a7fc7eeb8db5687bb6272ab5983c11800c21bc24c15dbd35efd42",
"MacAddress": "",
"IPv4Address": "",
"IPv6Address": ""
}
},

host网络 : 没有独立的network Namespace , 共用宿主机的网络

network总结

  1. none : 只能通过docker exec -it 的方式连接这个容器
  2. bridge : 在不使用--link的时候 , 使用默认的bridge的容器之间只是IP互通 , 无法通过link方式连接 , 而使用自定义容器却可以以link方式连接
  3. host : 没有独立的network Namespace , 共用宿主机的网络

Docker持久化

持久化数据的方案

  • 基于本地文件系统的 Volume : 可以在执行 Docker create或 Docker run时,通过-v参数将主机的目录作为容器的数据卷。这部分功能便是基于本地文件系统的 volume管理。
  • 基于plugin的 Volume : 支持第三方的存储方案,比如NAS, aws

数据持久化 : Data Volume

docker volume ls

1
2
3
docker volume ls
# DRIVER VOLUME NAME
# local f4d1af1ff85d46d3d8faabca45f7e898da2074413a9ec1607d05290527806367

docker volume inspect

1
2
3
4
5
6
7
8
9
10
11
12
docker volume inspect f4d1af1ff85d46d3d8faabca45f7e898da2074413a9ec1607d05290527806367
# [
# {
# "CreatedAt": "2019-11-20T03:06:20Z",
# "Driver": "local",
# "Labels": null,
# "Mountpoint": "/mnt/sda1/var/lib/docker/volumes/f4d1af1ff85d46d3d8faabca45f7e898da2074413a9ec1607d05290527806367/_data",
# "Name": "f4d1af1ff85d46d3d8faabca45f7e898da2074413a9ec1607d05290527806367",
# "Options": null,
# "Scope": "local"
# }
]

1574341291438

可见 ,

  • 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
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
version: '3'
services:
wordpress:
image: wordpress
ports:
- 8080:80
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: root
networks:
- my-bridge

mysql:
image: mysql
encironment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress
volumes:
- mysql-data:/var/lib/mysql
networks:
- my-bridge

volumes:
mysql-data:

networks:
my-bridge:
driver: brige

docker-compose.yml示例二

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
version: '3'
services:
op-tasks:
build: .
command:
- /bin/bash
- -c
- |
gunicorn -c gunicorn.py app:app &
python run_celery.py worker -l info --beat -c 1
container_name: op-tasks
env_file:
- optasks-variables.env
ports:
- '50020:5000'
volumes:
- .:/var/www/op-tasks
depends_on:
- op-tasks-redis
links:
- op-tasks-redis
network_mode: bridge

op-tasks-redis:
image: redis:latest
container_name: op-tasks-redis
network_mode: bridge

指定自定义网络

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
version: '3'

services:
proxy:
build: ./proxy
networks:
- front
app:
build: ./app
networks:
- front
- back
db:
image: postgres
networks:
- back

networks:
front:
# Use a custom driver
driver: custom-driver-1
back:
# Use a custom driver which takes special options
driver: custom-driver-2
driver_opts:
foo: "1"
bar: "2"

配置默认网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
version: '2'

services:
web:
build: .
ports:
- "8000:8000"
db:
image: postgres

networks:
default:
# Use a custom driver
driver: custom-driver-1

使用已经存在的网络

1
2
3
4
networks:
default:
external:
name: my-pre-existing-network

docker-compose注意

同时指定 image 和 build 两个标签

如果同时指定了 image 和 build 两个标签,那么 Compose 会构建镜像并且把镜像命名为 image 后面的那个名字。

1
2
build: ./dir
image: webapp:tag

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文件中定义的image

  • docker-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
2
3
4
5
6
7
8
9
10
11
version: "3"
services:
redis:
image: redis

web:
build: .
posts:
- 8080:5000
environement:
REDIS_HOST:redis
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容器 , 这样就实现了负载均衡

1574346624394