Justin-刘清政的博客

linux/Docker/11-docker网络扩展

2020-06-28

docker run参数

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
29
30
31
32
33
34
35
36
37
38
39
40
41
常用选项说明

-d, --detach=false, 指定容器运行于前台还是后台,默认为false
-i, --interactive=false, 打开STDIN,用于控制台交互
-t, --tty=false, 分配tty设备,该可以支持终端登录,默认为false
-u, --user="", 指定容器的用户
-a, --attach=[], 登录容器(必须是以docker run -d启动的容器)
-w, --workdir="", 指定容器的工作目录
-c, --cpu-shares=0, 设置容器CPU权重,在CPU共享场景使用
-e, --env=[], 指定环境变量,容器中可以使用该环境变量
-m, --memory="", 指定容器的内存上限
-P, --publish-all=false, 指定容器暴露的端口
-p, --publish=[], 指定容器暴露的端口
-h, --hostname="", 指定容器的主机名
-v, --volume=[], 给容器挂载存储卷,挂载到容器的某个目录
--volumes-from=[], 给容器挂载其他容器上的卷,挂载到容器的某个目录
--cap-add=[], 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities
--cap-drop=[], 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities
--cidfile="", 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法
--cpuset="", 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU
--device=[], 添加主机设备给容器,相当于设备直通
--dns=[], 指定容器的dns服务器
--dns-search=[], 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件
--entrypoint="", 覆盖image的入口点
--env-file=[], 指定环境变量文件,文件格式为每行一个环境变量
--expose=[], 指定容器暴露的端口,即修改镜像的暴露端口
--link=[], 指定容器间的关联,使用其他容器的IP、env等信息
--lxc-conf=[], 指定容器的配置文件,只有在指定--exec-driver=lxc时使用
--name="", 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字
--net="bridge", 容器网络设置:
bridge 使用docker daemon指定的网桥
host //容器使用主机的网络
container:NAME_or_ID >//使用其他容器的网路,共享IP和PORT等网络资源
none 容器使用自己的网络(类似--net=bridge),但是不进行配置
--privileged=false, 指定容器是否为特权容器,特权容器拥有所有的capabilities
--restart="no", 指定容器停止后的重启策略:
no:容器退出时不重启
on-failure:容器故障退出(返回值非零)时重启
always:容器退出时总是重启
--rm=false, 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)
--sig-proxy=true, 设置由代理接受并处理信号,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理

docker 网络

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 创建网络名称空间
ip netns list 查看
ip netns delete test1 删除
ip netns add test1 新增
ip netns add test2 新增
ip netns exec text1 ip a 查看网络名称空间test1的ip

ip link 查看linux网卡的二层信息

ip netns exec text1 ip link 查看名称空间text1网卡的二层信息

ip netns exec test1 ip link set dev lo up (ip link set dev lo up)把lo网卡启动

ip link add veth-test1 type veth peer name veth-test2 添加两个veth,分别是veth-test1和veth-test2


ip link set veth-test1 netns test1 把veth-test1添加到test1的名称空间中

ip netns exec text1 ip link 可以看到已经被添加到test1中了

ip link set veth-test2 netns test2
ip netns exec text2 ip link

ip netns exec test1 ip addr add 192.168.1.1/24 dev veth-test1 给设备新增ip地址veth-test1
ip netns exec test2 ip addr add 192.168.1.2/24 dev veth-test2 给设备新增ip地址veth-test2

ip netns exec test1 ip link set dev veth-test1 up 启动veth-test1
ip netns exec test2 ip link set dev veth-test3 up 启动veth-

ip netns exec test1 ip a 可以看到ip地址有了

ip netns exec test1 ping 192.168.1.2 发现能ping 通


bridge网络
docker network ls
docker network inspect 网络id号
ip a 本地有docker0和veth
docker0是docker的
veth是某个容器的,他们两个名称空间相连接
docker exec 容器id ip a
查看容器网卡可以看到有个eth..,它跟veth是一对parr,连到了主机的docker0上

brctl命令
yum install bridge-utils
brctl show 可以看到docker0 有个interface--》 veth,就是通过ip a看到的那个veth

再创建一个容器
docker network inspect 网络id号 containers又多了个veth
ip a 又多个一个veth
bractl show 发现docker0上有两个veth了

容器访问外网通过nat地址转换

image-20200628103705929

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#容器直接link
# 后台访问数据库,两个容器,需要知道ip地址去连接
创建test1和test2两个容器
创建test2的时候,加上link
docker run -d --name test2 --link test1 busybox
进入到test2中
ping test1名字也可以ping通(不需要直接知道test1的ip地址)
进入到test1容器中ping test2无法ping 通,是有方向的

# 新建bridge
docker network create -d bridge 名字
docker network ls
brctl show 可以看到多了一个bridge:br-id号,通商贸docker network ls的id号

# 启动容器,指定使用的网络
docker run -d --name test3 --network my-bridge busybox
brctl show 可以看到br-id号有接口了

# 两个网络连接到一起
docker network connect my-bridge test2 两个参数,一个是网络,一个是容器
docker network inspect bridge
docker network inspect my-bridge 发现test2 即连接到了bridge又连接到了my-bridge上

现在test3内直接 ping test2可以ping通:因为如果两个容器连接到了用户创建的网络上,默认就link好了,相互通过名字ping通


# 端口映射
-p指定映射
-P随机映射

# host和none网络
docker run -d --name test3 --network none busybox
docker network inspect none 发现test3在none中
进入到容器
ip a 发现该容器只有一个lo回环地址
孤立的,除了exec进去,其他都不能访问它,只需要本地使用,产生数据的服务器

docker run -d --name test3 --network host busybox
docker network inspect host 发现test3在none中,没有ip地址
进入到容器
ip a 发现该容器跟主机共享一套namespace,没有自己的一套
这样如果宿主机80端口被占用,容器就不能再起在80端口了


# 多容器部署
# 一个flask项目,一个redis
# app.py
import redis
from flask import Flask
import os
app = Flask(__name__)
redis_env=os.environ.get('redis')
cache = redis.Redis(host=redis_env, port=6379)
@app.route('/')
def hello():
count=cache.incr('hits')
return '你好,被访问了{} 次.\n'.format(count)

if __name__ == '__main__':
app.run(port=5000,host='0.0.0.0')
# Dockerfile
FROM python:3.6
WORKDIR /home
RUN pip install flask
RUN pip install redis
COPY ./app.py .
EXPOSE 5000
CMD ["python", "app.py"]

# 构建镜像
docker build -t liuqingzheng/flask_redis:v1 .
# 启动redis(不需要映射6379端口了)
docker run -d --name redis redis
# 启动一个flask-redis容器(设置一个环境变量)
docker run -d --link redis --name flask-redsi -e REDIS_HOST=redis liuqingzheng/flask_redis:v1
进入到容器
env一下可以看到设置的环境变量


## 多机通信(不通机器的redis和flask如何通信)
docker用的是VXLAN(一层一层包起来)
# overlay方式通过etcd来实现,多主机通信
# 保证不通机器上的容器ip地址不冲突
# 每台机器上分别启动etcd的进程
# 下载etcd
wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz
# 解压etcd
tar zxvf etcd-v3.0.12-linux-amd64.tar.gz
cd etcd-v3.0.12-linux-amd64
nohup ./etcd --name docker-node1 --initial-advertise-peer-urls http://192.168.205.10:2380 \
--listen-peer-urls http://192.168.205.10:2380 \
--listen-client-urls http://192.168.205.10:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.205.10:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://192.168.205.10:2380,docker-node2=http://192.168.205.11:2380 \
--initial-cluster-state new&

# 另一台机器
wget https://github.com/coreos/etcd/releases/download/v3.0.12/etcd-v3.0.12-linux-amd64.tar.gz
tar zxvf etcd-v3.0.12-linux-amd64.tar.gz
cd etcd-v3.0.12-linux-amd64/
nohup ./etcd --name docker-node2 --initial-advertise-peer-urls http://192.168.205.11:2380 \
--listen-peer-urls http://192.168.205.11:2380 \
--listen-client-urls http://192.168.205.11:2379,http://127.0.0.1:2379 \
--advertise-client-urls http://192.168.205.11:2379 \
--initial-cluster-token etcd-cluster \
--initial-cluster docker-node1=http://192.168.205.10:2380,docker-node2=http://192.168.205.11:2380 \
--initial-cluster-state new&

# 检查cluster状态
./etcdctl cluster-health
'''
member 21eca106efe4caee is healthy: got healthy result from http://192.168.205.10:2379
member 8614974c83d1cc6d is healthy: got healthy result from http://192.168.205.11:2379
cluster is healthy
'''

# 重启docker
/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.205.10:2379 --cluster-advertise=192.168.205.10:2375&
# 另一台
sudo /usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock --cluster-store=etcd://192.168.205.11:2379 --cluster-advertise=192.168.205.11:2375&

# 创建overlay网络
docker network create -d overlay demo
docker network ls
docker network inspect demo
# 没有在nod2上做,但是被etcd同步过去了
#通过查看etcd的key-value, 我们获取到,这个demo的network是通过etcd从node1同步到node2的
# 创建容器,连接到demo上,在node2上创建
docker run -d --name test1 --net demo busybox sh -c "while true; do sleep 3600; done"

# 在node2上创建,就会提示test1已经被用过了
docker run -d --name test2 --net demo busybox sh -c "while true; do sleep 3600; done"

# 两个地址不一样
docker network inspect demo 有两台机器上的两个地址
# 两个容器可以ping通
docker exec test1 ping 10.0.0.3
docker exec test1 ping test2 也可以

image-20200628111052466

image-20200628112711692

2 持久化存储和数据共享

image-20200628112827557

image-20200628112907879

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# 基于本地的Volume:在容器创建或者run的时候,通过 -v指定
# 基于plugin的Volume:插件方式,第三方方案,如NAS,aws

# ## volume的类型
#1 受管理的data Volume,由docker后台自动创建(Data volume)
#2 绑定挂载的Volume,具体挂载可以由用户指定 (Bind Mouting)

#################Data volume
## 启动一个mysql的容器
docker run -d --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql

docker volume ls 可以看到刚刚创建容器的voluem
docker volume id号 删除
docker inspect id号 可以看到mount到的本地某个位置
# 再创建,也会创建一个volume
# 删除容器,volume是不会被删除的(数据不会丢)


# 两个容器可以共用一个volume
docker run -d -v mysql:/var/lib/mysql --name mysql1 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
docker run -d --name=mysql -v mysql:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 mysql:5.7
docker run -di --name=mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -v mysql:/var/lib/mysql mysql:5.7
# 进入到容器,创建一个数据库
# 停掉容器,删除容器
sudo docker volume ls 文件还在
# 重新一个容器,使用该volume
docker run -d -v mysql:/var/lib/mysql --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
# 进入到容器,看到数据库还在



###############Bind Mouting

# index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">

<title>hello</title>

</head>

<body>
<h1>Hello Docker! </h1>
</body>
</html>

# Dockerfile

FROM nginx:latest
WORKDIR /usr/share/nginx/html
COPY index.html index.html

# 构建
docker build -t liuqingzheng/nginx_demo:v1 .
# 启动容器
docker run -d -p 80:80 --name web iuqingzheng/nginx_demo:v1
# 把当前路径映射到容器目录
docker run -d -v $(pwd):/usr/share/nginx/html -p 80:80 --name web iuqingzheng/nginx_demo:v1
# 进入到容器,操作容器目录,宿主机跟着改动



#####实战演练
# Dockerfile
FROM python:2.7
COPY . /skeleton
WORKDIR /skeleton
RUN pip install -r requirements.txt
EXPOSE 5000
ENTRYPOINT ["scripts/dev.sh"]
# 构建
docker build -t liuqingzheng/flask_demo:v1 .
# 启动容器
docker run -d -p 80:5000 --name flask liuqingzheng/flask_demo:v1
# 删除容器

# 重新操作
docker run -d -v $(pwd):/skeleton -p 80:5000 --name flask liuqingzheng/flask_demo:v1
# 这样修改了代码,直接访问,页面也改变了

3 docker-compose

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# docker-compose是一个工具
# 通过yml文件定义多个容器
# 通过一条命令根据yml去创建,管理这些容器

# 默认名字 docker-compose.yml
# 三个概念,Service Networks ,Volumes

version:有123版本,目前都用"3"
# 一个service代表一个container,这个container可以从dockerhub的image来创建,或者从本地dockerfilebuild的image来创建
# service的启动类似docker run,可以指定network和volume,所有可以给servier指定network和volume


####例如1 ::
services:
db:
image: mysql:5.7
volumes:
- "db-data:/var/lib/mysql"
networks:
- my-bridge
# 等同于
# docker run -d --network my-bridge -v db-data"/var/lib/mysql mysql:5.7

#### 例如2:
services:
worker:
build: ./worker
links:
- db
- redis
networks:
- my-bridge




Volumes

#### 例如:
services:
db:
image: mysql:5.7
volumes:
- "db-data:/var/lib/mysql"
networks:
- my-bridge
# db-data"/var/lib/mysql 等同于docker volume create db-data

Networks
### 例如
services:
worker:
build: ./worker
links:
- db
- redis
networks:
- my-bridge
# my-bridge 等同于docker network create -d bridge my-bridge
networks:
- my-bridge:
driver:bridge
-my-bridge2:
driver:bridge


### 使用docker-compose部署一个wordpress
version: '3'

services:

wordpress:
image: wordpress
ports:
- 8080:80
depends_on:
- mysql
environment:
WORDPRESS_DB_HOST: mysql
WORDPRESS_DB_PASSWORD: root
networks:
- my-bridge

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

volumes:
mysql-data:

networks:
my-bridge:
driver: bridge

docker-compose

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# mac和window安装上docker,docker-compose自带了
# linux需要单独安装
curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

# 启动管理容器
docker-compose up # 会自动搜索当前路径下的 docker-compose.yml文件
docker-compose -f 指定文件 up
docker-compose up -d # 后台执行,一般我们看日志输出,不用这个

docker-compose stop # 停止,不会删除容器和镜像
docker-compose down # 停止,并删除关联的容器
docker-compose start # 启动yml文件管理的容器
docker-compose ps # 正在运行的容器
docker-compose images # docker-compose管理的容器

docker-compose exec yml文件中写的service /bin/bash # 进入到容器内

示例:docker-compose部署flask-redis

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# Dockerfile
FROM python:3.6
COPY . /app
WORKDIR /app
RUN pip install flask redis
EXPOSE 5000
CMD [ "python", "app.py" ]

# docker-compose.yml
version: "3"

services:

redis:
image: redis

web:
build:
context: .
dockerfile: Dockerfile
ports:
- 8080:5000
environment:
REDIS_HOST: redis
# app.py
from flask import Flask
from redis import Redis
import os
import socket

app = Flask(__name__)
redis = Redis(host=os.environ.get('REDIS_HOST', '127.0.0.1'), port=6379)


@app.route('/')
def hello():
redis.incr('hits')
return '你好! 查看 %s 次, hostname 是 %s.\n' % (redis.get('hits'),socket.gethostname())


if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000, debug=True)
# 运行
docker-compose up

docker-compose水平扩展

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# 把flask_redis项目扩展成三个
docker-compose up --help
# scale SERVICE=NUM设置成三个
docker-compose up --scale web=3 # 执行有问题,因为8080端口已经被映射了
# 删除docker-compose.yml中的port
# 在启动
docker-compose up --scale web=3
docker-compose start
docker-compose ps # 可以看到三个web启动了

# 前面加一个负载均衡器HAProxy
# HAProxy和Nginx的区别?万能的百度

# app.py 改成监听的端口为80
# Dockerfile不需要改
# docker-compose.yml
version: "3"

services:

redis:
image: redis

web:
build:
context: .
dockerfile: Dockerfile
environment:
REDIS_HOST: redis

lb:
image: dockercloud/haproxy
links:
- web
ports:
- 8080:80
volumes:
- /var/run/docker.sock:/var/run/docker.sock
#启动,从浏览器查看
docker-compose up
# 把web扩展为3个从浏览器查看
docker-compose up --scale web=3 -d
# 减掉为1个
docker-compose up --scale web=1 -d

docker-compose部署一个复杂的应用

image-20200628153906975
1
2
3
4
5
# 部署
docker-compose up # 时间很久

# 补充:
docker-compose build # 如果yml文件中有需要先build的image,会先构建,然后再up,节约时间(并不会节约)

docker-swarm

1
2


其他

mysql配置文件位置

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
# 容器内部mysql配置文件位置
mysql --help | grep my.cnf
'''
/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
'''

ls /etc/mysql
# conf.d my.cnf my.cnf.fallback mysql.cnf mysql.conf.d
# my.cnf文件最后一行
!includedir /etc/mysql/conf.d/
!includedir /etc/mysql/mysql.conf.d/
# 除加载 my.cnf 配置文件外,还会到/etc/mysql/conf.d/目录下加载其他配置文件。
#当然可以配置多个!includedir这样做的好处是可以把不同目的或功能的配置放在不同的多个文件下,然后由 my.cnf 去统一加载这些配置。避免所有配置在一个文件中

## 演示
# 在当前目录下创建文件夹conf.d,内部vim my.cnf
# 加入:设置id号
[mysqld]
server-id=109
# 创建并启动容器
docker run -d -v ./conf.d/:/etc/mysql/conf.d -v mysql:/var/lib/mysql --name mysql2 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql:5.7

# 进入容器
# 进入mysql
#执行:可以看到id为109
show variables like 'server_id';
使用支付宝打赏
使用微信打赏

点击上方按钮,请我喝杯咖啡!

扫描二维码,分享此文章