Linux宿主机可以ping
通容器内部。
如上,每启动一个docker容器,docker就会给docker容器分配一个ip,我们只要安装了docker,就会有一个网卡
docker0
,它使用桥接模式,其使用的技术是veth-pair
。
每启动一个容器,在宿主机都会看到增加了一个vethxxxxx
的网卡,且与容器内的网卡是配对的:
veth-pair
就是一对虚拟网络设备接口,它们都是成对出现的。
veth-pair
往往充当一个桥梁,连接各种虚拟网络设备。
容器之间可以ping通。
结论:如上图,默认情况下,容器tomcat01和tomcat02是共用一个路由器,即
docker0
。
所有容器在不指定网络的情况下,都是docker0
作为路由器的。docker0
会给容器分配一个默认的可用IP。
Docker中所有的网络接口都是虚拟的。
容器移除后,对应的那对网卡也随之删除。
--link
因为默认情况下,容器启动时,IP是由docker0
动态分配的,所以当容器重启后,IP有可能会变。
举个例子,比如Springboot的项目里,会把数据库连接配置写在配置文件中,比如database url = jdbc://172.17.0.6:3306/xxx
,如果MySQL的容器重启了,很有可能IP改变了。那这样项目部署就有问题了。
因此我们希望可以用名字来访问容器。
通过--link
就能实现。【但这种方式已经不建议使用了!】
新启动一个名为tomcat02
的容器,然后指定--link
这样,tomcat02
容器就可以通过tomcat01
这个容器名与对应的容器进行网络通信了。但与此同时,反过来是不行的,即tomcat01
容器还是无法通过tomcat02
这个容器名与对应的容器进行网络通信。因此--link
的结果是单向的,这也是它的一个缺点。
docker inspect tomcat02
查看tomcat02
的配置,可以看到有Links
项:
本质探究:在容器tomcat02
的/etc/hosts
文件可以看到对应的IP-名称
映射配置。
可以看到--link
这种配置方式太笨了,所以现在已经不推荐使用了!
现在推荐使用的是自定义网络,不使用docker0
。因为docker0
有局限性, 比如不支持使用容器名去进行网络连接。
[root@VM-8-8-centos ~]# docker network --help
Usage: docker network COMMAND
Manage networks
Commands:
connect Connect a container to a network
create Create a network
disconnect Disconnect a container from a network
inspect Display detailed information on one or more networks
ls List networks
prune Remove all unused networks
rm Remove one or more networks
Run 'docker network COMMAND --help' for more information on a command.
其实在启动容器时,是默认带有--net bridge
这个参数的,这个参数其实就是指使用docker0
。
1、先清空现有的容器,可以看到之前的桥接网卡也被删除,只剩下原有的lo
、eth0
和docker0
。
2、通过docker create
创建自定义网络
[root@VM-8-8-centos ~]# docker network create --help
Usage: docker network create [OPTIONS] NETWORK
Create a network
Options:
--attachable Enable manual container attachment
--aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default
map[])
--config-from string The network from which to copy the configuration
--config-only Create a configuration only network
-d, --driver string Driver to manage the Network (default "bridge")
--gateway strings IPv4 or IPv6 Gateway for the master subnet
--ingress Create swarm routing-mesh network
--internal Restrict external access to the network
--ip-range strings Allocate container ip from a sub-range
--ipam-driver string IP Address Management Driver (default "default")
--ipam-opt map Set IPAM driver specific options (default map[])
--ipv6 Enable IPv6 networking
--label list Set metadata on a network
-o, --opt map Set driver specific options (default map[])
--scope string Control the network's scope
--subnet strings Subnet in CIDR format that represents a network segment
3、创建两个容器tomcat-net-01
、tomcat-net-02
,并使用--net
指定网络为自定义网络mynet01
。可以看到,使用自定义网络的容器间可以通过容器名来进行网络连接:
这样带来的好处就是:
(1) 在自定义网络中,Docker帮我们维护了IP<->容器名
之间的对应关系;
(2) 不同的集群可使用不同的网络(比如一个Redis集群和一个MySQL集群),可保证不同集群之间的网络隔离,安全性。
PS:当然,上面这种网络隔离情况也是可以打通的。下面来说说网络连通。
docker network connect
//一个容器连接另一个网络
[root@VM-8-8-centos ~]# docker network connect --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER
Connect a container to a network
Options:
--alias strings Add network-scoped alias for the container
--driver-opt strings driver options for the network
--ip string IPv4 address (e.g., 172.30.100.104)
--ip6 string IPv6 address (e.g., 2001:db8::33)
--link list Add link to another container
--link-local-ip strings Add a link-local address for the container
首先处于不同网段的容器之间是无法ping通的,测试如下:
要连通的话,只能通过容器去连接另一个网络,如下图:
docker network connect mynet01 tomcat01
打通前:
打通后,将tomcat01放到了mynet的网络下,可以看到tomcat01容器多了一张网卡eth1
:
打通后,tomcat01容器就可以与mynet01网络里的容器互相ping通了。
创建集群的脚本:create-redis-cluster.sh
#!/bin/bash
# 创建用于redis集群的自定义网络
docker network create redis --subnet 172.38.0.0/16
# 创建6个redis容器
for port in $(seq 1 6); \
do \
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat <<EOF > /mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /mydata/redis/node-${port}/data:/data \
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-itd --network redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf; \
done
# 创建集群
docker exec -it redis-1 redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 --cluster-replicas 1
执行脚本:
查看集群信息:
测试集群,可以看到集群正常工作,到此Redis集群部署完成。可以看到通过docker来部署redis集群非常简单。
构建并打包Springboot项目
编写Dockerfile
FROM java:8
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
构建镜像
发布运行
以前交付、启动容器的步骤:
(1) 定义Dockerfile;
(2) docker build;
(3) docker run;
只适用单个容器的情况。
现在的微服务项目一般都有多个容器,且存在依赖关系,假设有100个容器,那用上面的方法来管理容器就非常低效了。
所以就有了Docker Compose。它可以轻松高效的管理多个容器(批量编排)。
官方介绍如下:
Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration. To learn more about all the features of Compose, see the list of features.
Compose works in all environments: production, staging, development, testing, as well as CI workflows. You can learn more about each case in Common Use Cases.
Using Compose is basically a three-step process:
- Define your app’s environment with a Dockerfile so it can be reproduced anywhere.
2. Define the services that make up your app in docker-compose.yml so they can be run together in an isolated environment.
3. Run docker compose up and the Docker compose command starts and runs your entire app. You can alternatively run docker-compose up using the docker-compose binary.
Docker Compose 是Docker官方的一个开源项目,并不是在Docker项目里的。要另外安装才有:
docker-compose.yml
示例:
version: "3.9" # optional since v1.27.0
services:
web:
build: .
ports:
- "5000:5000"
volumes:
- .:/code
- logvolume01:/var/log
links:
- redis
redis:
image: redis
volumes:
logvolume01: {}
在Docker Compose的语境下,有以下几个概念:
(1) 服务Services:指的是单个的容器/应用。(web、redis、mysql…)
(2) 项目Projects:一组关联的容器(比如一个博客项目,它包含了web、mysql、redis、nginx等容器)。
参考官方文档:
https://docs.docker.com/compose/install/
如果是国内的服务器环境,建议替换成get.daocloud.io
这个源,下载速度会飞快。
//1、下载
curl -L "https://get.daocloud.io/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
//2、赋予执行权限
chmod u+x /usr/local/bin/docker-compose
//3、创建软链接
ls -l /usr/bin/docker-compose
按照官方文档的get started进行实践:https://docs.docker.com/compose/gettingstarted/
Define the application dependencies.
$ mkdir composetest
$ cd composetest
app.py
in your project directory and paste this in:import time
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
retries = 5
while True:
try:
return cache.incr('hits')
except redis.exceptions.ConnectionError as exc:
if retries == 0:
raise exc
retries -= 1
time.sleep(0.5)
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello World! I have been seen {} times.\n'.format(count)
flask
redis
目的:将应用打包成镜像
In this step, you write a Dockerfile that builds a Docker image. The image contains all the dependencies the Python application requires, including Python itself.
In your project directory, create a file named Dockerfile
and paste the following:
由于测试时用的是国内的服务器,所以在Dockerfile中加入了国内的源,包括alpine linux 和 pypi的,否则下载会很慢。
#syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
RUN apk update && apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip config set global.index-url https://mirrors.ustc.edu.cn/pypi/web/simple
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]
目的:定义整个项目服务需要的环境,该示例则是包括web、redis。
Create a file called docker-compose.yml
in your project directory and paste the following:
version: "3.9"
services:
web:
build: .
ports:
- "5000:5000"
redis:
image: "redis:alpine"
Web service
The web service uses an image that’s built from the Dockerfile in the current directory. It then binds the container and the host machine to the exposed port, 5000. This example service uses the default port for the Flask web server, 5000.
Redis service
The redis service uses a public Redis image pulled from the Docker Hub registry.
1、From your project directory, start up your application by running docker-compose up
.
会自动创建单独的自定义网络,意味着处于该网络的容器之间可以通过容器名来互相连接(比如ping):
一共3个镜像:
修改docker-compose.yml文件,针对web服务的容器添加数据卷挂载。
version: "3.9"
services:
web:
build: .
volumes:
- .:/code
environment:
FLASK_ENV: development
ports:
- "5000:5000"
redis:
image: "redis:alpine"
将主机上的项目目录(当前目录)挂载到容器内的/code
目录,这样就可以在本地修改代码而无需重建镜像。另外,FLASK_ENV
环境变量表示让Flask 在开发模式下运行,这样可以在代码变化时自动重新加载代码(这种模式只应在开发中使用)。
docker-compose up -d
重新构建,并启动容器。
修改app.py
中返回的字符串:
@app.route('/')
def hello():
count = get_hit_count()
return 'Hello from Docker! I have been seen {} times.\n'.format(count)
PS:当自己定义的Dockerfile有变化,该镜像需要重新生成再启动容器的话,用
docker-compose up --build
,--build
参数会先重新构建镜像。
如果镜像不用重新生成,则用docker-compose up
即可。
这是
docker-compose.yml
的核心。
需要的时候查阅官方文档即可:https://docs.docker.com/compose/compose-file/compose-file-v3/
官方的一些示例:https://docs.docker.com/samples/wordpress/
Docker Compose 属于单机部署,实际上企业生产环境多是用的集群部署的方式。
四台Centos7(CentOS Linux release 7.7.1908 (Core)
)虚拟机。
四台都安装docker环境。
PS:四台同时安装docker环境的小技巧,也是很多优秀的shell终端管理工具都有的功能,比如xshell、iTerm2。这里以iTerm2为例:
[root@centos7-docker-1 ~]# docker swarm --help
Usage: docker swarm COMMAND
Manage Swarm
Commands:
ca Display and rotate the root CA
init Initialize a swarm
join Join a swarm as a node and/or manager
join-token Manage join tokens
leave Leave the swarm
unlock Unlock swarm
unlock-key Manage the unlock key
update Update the swarm
Run 'docker swarm COMMAND --help' for more information on a command.
1、初始化集群
docker swarm init --advertise-addr //IP一般是私网IP
docker swarm join --token <管理节点的IP:PORT>
如果要添加管理节点,则需要先在已有的管理节点上执行docker swarm join-token manager
来获取管理节点的加入凭证(join-token),如下:
添加完毕后,可以在管理节点上使用docker node ls
查看所有节点:
3、提升/降级节点
//提升节点
docker node promote --help
Usage: docker node promote NODE [NODE...]
Promote one or more nodes to manager in the swarm
//降级节点
[root@centos7-docker-4 ~]# docker node demote --help
Usage: docker node demote NODE [NODE...]
Demote one or more nodes from manager in the swarm
将centos7-docker-4
提升为管理节点:
这样呢,就有了两个管理节点,两个工作节点。
当 Docker Engine 以 swarm 模式运行时,管理器节点实现 Raft 一致性算法来管理全局集群状态。
Docker swarm 模式之所以使用一致性算法,是为了确保集群中负责管理和调度任务的所有管理器节点都存储相同的一致状态。
在整个集群中具有相同的一致状态意味着在发生故障时,任何 Manager 节点都可以接收任务并将服务恢复到稳定状态。例如,如果集群中负责调度任务的 Leader Manager 意外死亡,任何其他 Manager 都可以接手调度的任务并重新平衡任务以匹配所需的状态。
Raft 最多可以容忍 (N-1)/2 次失败,并且需要多数或法定人数 (N/2)+1 名成员就提议给集群的值达成一致。这意味着在运行 Raft 的 5 个 Manager 的集群中,如果 3 个节点不可用,系统将无法处理更多请求以安排额外的任务。现有任务继续运行,但如果管理器集不健康,则调度程序无法重新平衡任务以应对故障。
官方文档:https://docs.docker.com/engine/swarm/raft/
测试
使centos7-docker-3
也成为管理节点管理,此时共有3个管理节点(1、3和4):
此时如果centos7-docker-1
挂了(使用systemctl stop docker.service
和systemctl stop docker.socket
停止docker进程来模拟):
centos7-docker-3
和centos7-docker-4
这两个管理节点依旧正常,故集群还是可以正常工作的。
此时,如果centos7-docker-3
也挂了,管理节点只剩下一个,此时管理节点就无法正常工作了,整个集群也就异常了:
以上测试也验证了Raft一致性的一些失败/故障次数<=(N-1)/2
原则(N
表示管理节点数)。
在集群里面:
容器 => 服务 => 副本
比如一个redis服务=>10个副本(同时开启10个redis容器)
docker run //容器启动:不具备扩缩容器的能力。
docker sevice //启动服务:局别扩缩容器、滚动更新的能力。
服务:在集群中的任意节点都可以访问。服务可以有多个副本,动态扩缩用。
# docker service --help
Usage: docker service COMMAND
Manage services
Commands:
create Create a new service
inspect Display detailed information on one or more services
logs Fetch the logs of a service or task
ls List services
ps List the tasks of one or more services
rm Remove one or more services
rollback Revert changes to a service's configuration
scale Scale one or multiple replicated services
update Update a service
Run 'docker service COMMAND --help' for more information on a command.
虽然只有centos7-docker-4
这个节点上运行了nginx容器,但可通过任意节点的8888端口访问nginx服务,可以看出其实集群就是一个整体。
如果把docker-4节点停止,很快其他的节点又会把容器运行起来。
在上面第一步,创建了集群nginx服务后,可以看到nginx服务只有一个副本:
当站点访问量增大后,服务器压力增大,就有了扩容的需求。
更新服务的副本数:
docker service update --replicas
docker service scale
命令进行动态扩缩容。docker service scale --help
Usage: docker service scale SERVICE=REPLICAS [SERVICE=REPLICAS...]
Scale one or multiple replicated services
Options:
-d, --detach Exit immediately instead of waiting for the service to converge
docker service update
和docker service scale
作用是一样的,都是用来动态扩缩容。
docker service rm
PS:docker swarm并不难,只要会搭建集群,启动服务、动态管理容器即可。docker swarm比k8s简单多了,k8s功能更加强大,但也比较复杂。
详见官方文档:https://docs.docker.com/engine/swarm/key-concepts/
负责集群的管理和编排。
docker可以初始化一个swarm集群,其他的docker节点可以加入。
即docker节点。多个节点组成一个网络集群。
docker swarm的节点分为manager节点和worker节点。
-Service
服务,可以在管理节点或工作节点运行。【核心】
在复制服务模型中,swarm管理器根据你设置的副本数在节点之间分配特定数量的副本任务。
对于全局服务,swarm 在集群中的每个可用节点上为该服务运行一项任务。
调整service以什么方式运行
--mode
--mode Service mode (replicated, global, replicated-job, or global-job) (default "replicated")
docker service create --mode replicated --name mytomcat tomcat:7
docker service create --mode global --name allnx alpine ping www.baidu.com
//场景的话,比如日志收集:
每个节点有自己的日志收集器,过滤。把所有日志最终再传给日志中心,服务监控,状态性能。
-Task
一个任务就是指一个容器和该容器内运行的命令(因为前面也学习过,docker容器就是分层的,每次执行的变更都是新的一层)。其实就是指服务的一个副本。它是swarm的原子调度单元。
管理节点根据服务设置的副本数将任务分配给工作节点。一旦任务被分配到一个节点,它就不能移动到另一个节点。
网络模式:
这部分的话,作为了解即可,因为用的很少了,基本都是用K8s了。
//单机部署
docker-compose up -d wordpress.yml
docker service create //一次只能部署一个服务
//集群部署
docker stack deploy wordpress.yml
以在 Swarm 集群中部署 WordPress 为例:
version: "3"
services:
wordpress:
image: wordpress
ports:
- 80:80
networks:
- overlay
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
deploy:
mode: replicated
replicas: 3
db:
image: mysql
networks:
- overlay
volumes:
- db-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
deploy:
placement:
constraints: [node.role == manager]
visualizer:
image: dockersamples/visualizer:stable
ports:
- "8080:8080"
stop_grace_period: 1m30s
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
deploy:
placement:
constraints: [node.role == manager]
volumes:
db-data:
networks:
overlay:
在 Swarm 集群管理节点新建该文件,其中的 visualizer 服务提供一个可视化页面,我们可以从浏览器中很直观的查看集群中各个服务的运行节点。
在 Swarm 集群中使用 docker-compose.yml 我们用 docker stack 命令:
docker stack deploy
部署服务,其中 -c
参数指定 compose 文件名:$ docker stack deploy -c docker-compose.yml wordpress
集群部署后,可通过浏览器访问wordpress、visualizer:
使用docker stack rm
删除服务:
参考:https://yeasy.gitbook.io/docker_practice/swarm_mode/secret
参考:https://yeasy.gitbook.io/docker_practice/swarm_mode/config
参考:https://yeasy.gitbook.io/docker_practice/swarm_mode/rolling_update