0%

1
kubectl [command] [TYPE] [NAME] [flags]

kubeadm 提供了kubeadm initkubeadm join ,可以快速创建k8s集群。

kubeadm 通过执行必要的操作来启动和运行最小可用集群。按照设计,它只关注启动引导,而非配置机器。

我们希望在 kubeadm 之上构建更高级别以及更加合规的工具,理想情况下,使用 kubeadm 作为所有部署工作的基准将会更加易于创建一致性集群。

安装各种 “锦上添花” 的扩展,例如 Kubernetes Dashboard、监控方案、以及特定云平台的扩展,都不在讨论范围内。

安装kubeadm

参考:https://developer.aliyun.com/mirror/kubernetes?spm=a2c6h.13651102.0.0.3e221b11Qo8aZk

kubeadm

1
kubeadm [command] [flags]

command:

1
2
3
4
5
6
7
8
9
10
11
alpha           # kubeadm 处于测试阶段的命令,尽量不要用,`kubeadm alpha --help`查看有哪些命令
certs # 处理kubernetes证书
completion # bash 命令补全,需要安装 bash-completion
config # 管理 kubeadm 集群的配置,该配置保留在集群的 ConfigMap 中
help # Help about any command
init # 设置一个Kubernetes控制平面
join # 将节点加入到k8s集群
reset # 还原使用`kubeadm init`或者`kubeadm join`对系统产生的环境变化
token # 管理 token
upgrade # 升级 k8s 版本
version # 查看版本信息

flags:

1
2
3
4
5
6
7
8
9
--add-dir-header	# 如果为true,则将文件目录添加到日志消息的头部
-h, --help
--log-file string # 如果非空,则使用此日志文件
--log-file-max-size uint # 定义日志文件可以增长到的最大大小。单位是字节。当该值为0时,表示不限制文件的最大大小。1800(默认)
--one-output # 如果为真,则只将日志写入其本机的严重级别(vs也写入每个较低的严重级别)
--rootfs string # 到“真实的”主机根文件系统的路径。
--skip-headers # 如果为true,则避免在日志消息中使用头前缀
--skip-log-headers # 如果为true,在打开日志文件时避免头文件
-v, --v Level # 日志级别详细程度的编号

kubeadm completion

让kubeadm命令支持自动补全,只支持bash和zsh两种环境。
kubeadm completion bash 打印的脚本加载到环境变量即可:

1
2
3
4
5
6
7
yum install bash-completion -y
touch /etc/profile.d/kubeadm_completion.sh
kubeadm completion bash > /etc/profile.d/kubeadm_completion.sh
exit # 重新登录终端

[root@master01 ~]$kubeadm # tab键就会出现命令提示
alpha certs completion config help init join reset token upgrade version

kubeadm init

此命令初始化一个 Kubernetes 控制平面节点

参考:https://kubernetes.io/zh/docs/reference/setup-tools/kubeadm/kubeadm-init

1
2
kubeadm init [command]
kubeadm init [flags]

command:

1
phase	# 使用此命令调用init工作流的单个阶段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
kubeadm init phase [command]
# command:
addon # Install required addons for passing Conformance tests
bootstrap-token # Generates bootstrap tokens used to join a node to a cluster
certs # Certificate generation
control-plane # 生成建立控制平面所需的所有静态pod清单文件
etcd # Generate static Pod manifest file for local etcd
kubeconfig # 生成建立控制平面所需的所有kubeconfig文件和管理kubeconfig文件
kubelet-finalize # Updates settings relevant to the kubelet after TLS bootstrap
kubelet-start # Write kubelet settings and (re)start the kubelet
mark-control-plane # Mark a node as a control-plane
preflight # Run pre-flight checks
upload-certs # Upload certificates to kubeadm-certs
upload-config # Upload the kubeadm and kubelet configuration to a ConfigMap

# 每个命令都可以使用--help查询帮助,例如:`kubeadm init phase addon --help`

flags:标星的是需要指定的,其他没有标星的一般情况下使用默认值即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
★ --apiserver-advertise-address string	# K8S API Server 将要监听的监听的 IP 地址,设置为master的IP,默认自动探测
★ --apiserver-bind-port int32 # API Server 绑定的端口,默认为 6443
--apiserver-cert-extra-sans strings # 可选的证书额外信息,用于指定 API Server 的服务器证书。可以是 IP 地址也可以是 DNS 名称
--cert-dir string # 证书的存储路径,缺省路径为 /etc/kubernetes/pki
--certificate-key string # 定义一个用于加密 kubeadm-certs Secret 中的控制平台证书的密钥
--config string # kubeadm 配置文件的路径
★ --control-plane-endpoint string # 为控制平面指定一个稳定的 IP 地址或 DNS 名称,即配置一个可以长期使用且是高可用的 VIP 或者域名,k8s 多 master 高可用基于此参数实现
--cri-socket string # 要连接的 CRI(容器运行时接口:Container Runtime Interface)套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值;仅当安装了多个 CRI 或具有非标准 CRI 插槽时,才使用此选项
--dry-run # 不要应用任何更改;只是输出将要执行的操作,其实就是测试运行
--experimental-patches string # 用于存储 kustomize 为静态 pod 清单所提供的补丁的路径
--feature-gates string # 一组用来描述各种功能特性的键值(key=value)对。选项是:IPv6DualStack=true|false (ALPHA - default=false)
--ignore-preflight-errors strings # 可以忽略检查过程 中出现的错误信息,比如忽略 swap,如果为 all 就忽略所有
★ --image-repository string # 设置用于拉取控制平面镜像的容器仓库,默认为 k8s.gcr.io
★ --kubernetes-version string # 为控制平面选择一个特定的 Kubernetes 版本,默认为 stable-1
--node-name string # 指定master节点的名称,默认使用hostname
★ --pod-network-cidr string # 设置 pod ip 地址范围, control plane 会自动将 网络发布到其他节点的node,让其上启动的容器使用此网络,flannel插件的默认值是 10.244.0.0/16
★ --service-cidr string # 指定service 的IP 范围. (default "10.96.0.0/12")
--service-dns-domain string # 指定 service 的 dns 后缀, e.g. "myorg.internal". (default "cluster.local")
--skip-certificate-key- # 不打印 control-plane 用于加密证书的key.
--skip-phases strings # 跳过指定的阶段(phase)
--skip-token-print # 不打印 kubeadm init 生成的 default bootstrap token
--token string # 指定 node 和control plane 之间,简历双向认证的token ,格式为 [a-z0-9]{6}\.[a-z0-9]{16} - e.g. abcdef.0123456789abcdef
--token-ttl duration # token 自动删除的时间间隔。 (e.g. 1s, 2m, 3h). 如果设置为 '0', token 永不过期 (default 24h0m0s)
--upload-certs # 上传 control-plane 证书到 kubeadm-certs Secret

k8s中有三个网段:node、service、pod,这三个网段千万不要冲突,否则通信会出现问题

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 单控制平面(一个master节点)
kubeadm init --apiserver-advertise-address=10.0.0.71 \
--apiserver-bind-port=6443 \
--kubernetes-version=v1.20.1 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.10.0.0/16 \
--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers

# 多控制平面(多个master节点)
# 多个master通过HAProxy配置的负载均衡,再通过keepalived配置HAProxy的高可用,假设VIP为 10.0.0.1/24
kubeadm init --apiserver-advertise-address=10.0.0.71 \
--control-plane-endpoint=10.0.0.1 \
--apiserver-bind-port=6443 \
--kubernetes-version=v1.20.1 \
--pod-network-cidr=10.244.0.0/16 \
--service-cidr=10.10.0.0/16 \
--image-repository=registry.cn-hangzhou.aliyuncs.com/google_containers

kubeadm join

此命令用来初始化 Kubernetes 节点并将其加入集群

在执行完 kubeadm init 后,会在终端打印以后加入此集群时需要执行的kubeadm join 命令

参考:https://kubernetes.io/zh/docs/reference/setup-tools/kubeadm/kubeadm-join/

当节点加入 kubeadm 初始化的集群时,我们需要建立双向信任。 这个过程可以分解为发现TLS 引导两部分。

通常两个部分会使用相同的令牌。 在这种情况下可以使用 –token 参数,而不是单独指定每个令牌。

1
2
kubeadm join [api-server-endpoint] [flags]
kubeadm join [command]

command:

1
phase	# 使用此命令可以调用连接工作流的单个阶段

api-server-endpoint:控制平面的IP,如果是单控制平面,就是master节点的IP,如果是多控制平面,就是VIP

flags:

1

kubeadm alpha

kubeadm 处于测试阶段的命令,尽量不要用,kubeadm alpha --help 查看有哪些命令,v1.20.1版本中只有一个:kubeconfig

kubeadm certs

1
kubeadm certs [command]

command:

1
2
3
4
certificate-key		# 生成证书密钥
check-expiration # 检查Kubernetes集群的证书过期情况
generate-csr # 生成密钥和证书签名请求
renew # 为Kubernetes集群更新证书
1
2
3
4
5
kubeadm certs check-expiration [flags]
# flags:
--cert-dir string
--config string
--kubeconfig string
1
2
3
4
5
kubeadm certs generate-csr [flags]
# flags:
--cert-dir string
--config string
--kubeconfig-dir string
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kubeadm certs renew [command]
# command:
admin.conf # 更新嵌入在kubeconfig文件中的证书,供管理员和kubeadm本身使用
all # 续期所有可用证书
apiserver # 更新服务Kubernetes API的证书
apiserver-etcd-client # 更新apiserver访问etcd时使用的证书
apiserver-kubelet-client # 更新证书以便API服务器连接到kubelet
controller-manager.conf # 更新嵌入在kubeconfig文件中的证书,以便控制器管理器使用
etcd-healthcheck-client # 将活动探测的证书更新到healthcheck etcd
etcd-peer # 为etcd节点之间的通信更新证书
etcd-server # 续证供etcd使用
front-proxy-client # 更新前端代理客户端证书
scheduler.conf # 更新kubeconfig文件中嵌入的证书,以便调度器管理器使用

# 每个命令都可以使用 --help 查询帮助,例如:`kubeadm certs renew admin.conf --help`

kubeadm config

kubeadm init 执行期间,kubeadm 将 ClusterConfiguration 对象上传到你的集群的 kube-system 名字空间下 名为 kubeadm-config 的 ConfigMap 对象中。 然后在 kubeadm joinkubeadm resetkubeadm upgrade 执行期间读取此配置。 要查看此 ConfigMap,请调用 kubectl get cm -o yaml -n kube-system kubeadm-config

你可以使用 kubeadm config print 命令打印默认配置, 并使用 kubeadm config migrate 命令将旧版本的配置转化成新版本。 kubeadm config images listkubeadm config images pull 命令可以用来列出并拉取 kubeadm 所需的镜像。

1
2
kubeadm config [command]
kubeadm config [flags]

command:

1
2
3
images	# 与kubeadm使用的容器镜像交互
migrate # 将本地旧版本的配置对象转换为最新支持的版本,而无需变更集群中的任何内容
print # 打印对象
1
2
3
4
kubeadm config images [command]	[flags]
# command:
list # 打印 kubeadm 要使用的镜像列表。配置文件用于自定义任何镜像或镜像存储库。
pull # 拉取 kubeadm 使用的镜像
1
2
3
4
5
6
7
8
kubeadm config images list [flags]
# flags
--allow-missing-template-keys
--config string
-o, --experimental-output string
--feature-gates string
--image-repository string
--kubernetes-version string
1
2
3
4
5
6
7
kubeadm config images pull [flags]
# flags
--config string # kubeadm 配置文件的路径
--cri-socket string
--feature-gates string
--image-repository string
--kubernetes-version string
1
2
3
4
kubeadm config migrate [flags]
# flags:
--new-config string # 使用新的 API 版本生成的 kubeadm 配置文件的路径。默认输出到stdout。
--old-config string # 使用旧 API 版本且应转换的 kubeadm 配置文件的路径。此参数是必需的
1
2
3
4
kubeadm config print [command]
# command:
init-defaults # 打印默认的初始化配置,可以用于`kubeadm init`
join-defaults # 打印默认连接配置,可以用于`kubeadm join`
1
2
3
4
kubeadm config print init-defaults [flags]
kubeadm config print join-defaults [flags]
# flags
--component-configs strings

flags:

1
--kubeconfig string		# 与集群通信时要使用的kubeconfig文件。(默认/etc/kubernetes/admin.conf)

kubeadm reset

kubeadm reset 负责从使用 kubeadm initkubeadm join 命令创建的文件中清除节点本地文件系统。对于控制平面节点,reset 还从 etcd 集群中删除该节点的本地 etcd 堆成员,还从 kubeadm ClusterStatus 对象中删除该节点的信息。 ClusterStatus 是一个 kubeadm 管理的 Kubernetes API 对象,该对象包含 kube-apiserver 端点列表。

kubeadm reset phase 可用于执行上述工作流程的各个阶段。 要跳过阶段列表,你可以使用 –skip-phases 参数,该参数的工作方式类似于 kubeadm joinkubeadm init 阶段运行器

“reset” 执行以下阶段:

1
2
3
4
preflight              Run reset pre-flight checks
update-cluster-status Remove this node from the ClusterStatus object.
remove-etcd-member Remove a local etcd member.
cleanup-node Run cleanup node.
1
2
kubeadm reset [command]
kubeadm reset [flags]

command:

1
phase	# 调用重置工作流的单个阶段
1
2
3
4
5
6
kubeadm reset phase [command]
# command
cleanup-node # Run cleanup node.
preflight # Run reset pre-flight checks
remove-etcd-member # Remove a local etcd member.
update-cluster-status # Remove this node from the ClusterStatus object.

flags:

1
2
3
4
5
6
--cert-dir string	# 存储证书的目录路径。需要清空此目录。默认"/etc/kubernetes/pki"
--cri-socket string # 要连接的 CRI 套接字的路径。如果为空,则 kubeadm 将尝试自动检测此值;仅当安装了多个CRI 或具有非标准 CRI 插槽时,才使用此选项
-f, --force # 在不提示确认的情况下重置节点
--ignore-preflight-errors strings # 忽略检查过程中出现的错误信息,比如swap,如果为all就忽略所有
--kubeconfig string # 与集群通信时使用的 kubeconfig 文件,默认 /etc/kubernetes/admin.conf
--skip-phases strings # 要跳过的阶段列表,四个阶段

注意:如果使用了外部 etcd,kubeadm reset 将不会删除任何 etcd 中的数据

kubeadm token

用于管理 kubeadm join 使用的令牌

使用 kubeadm init 默认生成的token有效期24小时

kubeadm upgrade

用于升级 Kubernetes 集群到新版本

kubeadm version

用于打印 kubeadm 的版本信息

kubeadm help

部署k8s集群

部署规划图

学习环境,简单部署,准备三台主机,都是centos7

角色 主机名 ip地址
master1 master01.ljk.cn 10.0.0.71/24
node1 node01.ljk.cn 10.0.0.72/24
node2 node02.ljk.cn 10.0.0.72/24

在三台主机上均执行以下步骤:

安装docker

1
2
3
4
5
6
7
8
9
10
11
12
13
14
yum install -y yum-utils device-mapper-persistent-data lvm2
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum makecache fast
yum -y install docker-ce
service docker start
# 加速
mkdir -p /etc/docker
tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://3417nt4m.mirror.aliyuncs.com"]
}
EOF
systemctl daemon-reload
systemctl restart docker

安装Kubernetes

1
2
3
4
5
6
7
8
9
10
11
12
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
setenforce 0
yum install -y kubelet kubeadm kubectl
systemctl enable kubelet && systemctl start kubelet

部署dns,或者直接修改/etc/hosts

1
2
3
4
5
# cat /etc/hosts
...
10.0.0.71 master01.ljk.cn
10.0.0.72 node01.ljk.cn
10.0.0.73 node02.ljk.cn

kubeadm init初始化

集群初始化,而且集群初始化只需要初始化一次

注意:必须是在master节点上运行,任意一master节点即可

1

以应用为中心

资源的分类:

  1. 工作负载型资源(workload):Pod
    stateful:有状态;stateless:无状态
       Deployment:最著名的无状态应用编排工具,支持应用的自动扩容、缩荣、滚动更新、金丝雀发布等
       DaemonSet:编排无状态的系统应用
       StatefulSet:编排有状态应用,遗憾的是,没什么意义
       Operator:StatefulSet的下一代
       Job、Cronjob:作业和周期性作业
  2. 服务发现和负载均衡:Service、Ingress
  3. 配置和存储:ConfigMap/Secret、PVC/PV、Downward API
  4. namespace:命名空间也是一种资源,集群级别
  5. endpoint

资源的作用域,两种级别:

  1. 全局级别,或者说集群级别:整个k8s
  2. 命名空间级别:各种namespace,和php一样,也是为了避免重名

租约

lease 是分布式系统中一个常见的概念,用于代表一个分布式租约。典型情况下,在分布式系统中需要去检测一个节点是否存活的时,就需要租约机制。

上图示例中的代码示例首先创建了一个 10s 的租约,如果创建租约后不做任何的操作,那么 10s 之后,这个租约就会自动过期。接着将 key1 和 key2 两个 key value 绑定到这个租约之上,这样当租约过期时 etcd 就会自动清理掉 key1 和 key2,使得节点 key1 和 key2 具备了超时自动删除的能力。

如果希望这个租约永不过期,需要周期性的调用 KeeyAlive 方法刷新租约。比如说需要检测分布式系统中一个进程是否存活,可以在进程中去创建一个租约,并在该进程中周期性的调用 KeepAlive 的方法。如果一切正常,该节点的租约会一致保持,如果这个进程挂掉了,最终这个租约就会自动过期。

在 etcd 中,允许将多个 key 关联在同一个 lease 之上,这个设计是非常巧妙的,可以大幅减少 lease 对象刷新带来的开销。试想一下,如果有大量的 key 都需要支持类似的租约机制,每一个 key 都需要独立的去刷新租约,这会给 etcd 带来非常大的压力。通过多个 key 绑定在同一个 lease 的模式,我们可以将超时间相似的 key 聚合在一起,从而大幅减小租约刷新的开销,在不失灵活性同时能够大幅提高 etcd 支持的使用规模。

Socket套接字

套接字Socket是进程间通信IPC的一种实现,允许位于不同主机(或同一主机)上不同进程之间进行通信和数据交换,SocketAPI出现于1983年 BSD 4.2实现

在建立通信连接的每一端,进程间的传输要有两个标志:IP地址和端口号,合称为套接字地址 socket address

客户机套接字地址定义了一个唯一的客户进程
服务器套接字地址定义了一个唯一的服务器进程

Socket API

封装了内核中所提供的socket通信相关的系统调用

Socket Domain:根据其所使用的地址

1
2
3
AF_INET		# Address Family,IPv4
AF_INET6 # IPv6
AF_UNIX # 同一主机上不同进程之间通信时使用

Socket Type:根据使用的传输层协议

1
2
3
SOCK_STREAM		# 流,tcp套接字,可靠地传递、面向连接
SOCK_DGRAM # 数据报,udp套接字,不可靠地传递、无连接
SOCK_RAW # 裸套接字,无须tcp或udp,APP直接通过IP包通信

客户/服务器程序的套接字函数

1
2
3
4
5
6
7
8
socket() 		# 创建一个套接字
bind() # 绑定IP和端口
listen() # 监听
accept() # 接收请求
connect() # 请求连接建立
write() # 发送
read() # 接收
close() # 关闭连接

http 协议及报文头部结构

Method 方法

请求方法,标明客户端希望服务器对资源执行的动作

  • GET: 从服务器获取一个资源
  • HEAD: 只从服务器获取文档的响应首部
  • POST: 向服务器输入数据,通常会再由网关程序继续处理
  • PUT: 将请求的主体部分存储在服务器中,如上传文件
  • DELETE: 请求删除服务器上指定的文档
  • TRACE: 追踪请求到达服务器中间经过的代理服务器
  • OPTIONS:请求服务器返回对指定资源支持使用的请求方法
  • CONNECT:建立一个到由目标资源标识的服务器的隧道
  • PATCH:用于对资源应用部分修改

status 状态码

参考资料:https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status

1
2
3
4
5
1xx:100-101 信息提示
2xx:200-206 成功
3xx:300-307 重定向
4xx:400-415 错误类信息,客户端错误
5xx:500-505 错误类信息,服务器端错误
1
2
3
4
5
6
7
8
9
10
11
12
200: 成功,请求数据通过响应报文的entity-body部分发送;OK
301: 永久重定向;Moved Permanently
302: 临时重定向 Moved Temporarily
304: 客户端发出了条件式请求,但服务器上的资源未曾发生改变,则通过响应此响应状态码通知客户端;Not Modified
307: 浏览器内部重定向
401: 需要输入账号和密码认证方能访问资源;Unauthorized
403: 请求被禁止;Forbidden
404: 服务器无法找到客户端请求的资源;Not Found
500: 服务器内部错误;Internal Server Error
502: 代理服务器从后端服务器收到了一条伪响应,如无法连接到网关;Bad Gateway
503: 服务不可用,临时服务器维护或过载,服务器无法处理请求
504: 网关超时

关于301和302:

用户输入了你不希望输入的url或者说是没用的url,用301重定向,例如http转到https、旧域名转到新域名;
因为业务逻辑临时跳转,用302重定向,例如用户直接访问管理后台就临时跳转到登录页面

web相关工具

wget

1
wget [OPTION]... [URL]...
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
#启动
-V,-version 显示wget的版本后退出
-h,-help 打印语法帮助
-b,-background 启动后转入后台执行
-e,-execute=COMMAND 执行`.wgetrc'格式的命令,wgetrc格式参见/etc/wgetrc或~/.wgetrc

#记录和输入文件
-o,-output-file=FILE 把记录写到FILE文件中
-a,-append-output=FILE 把记录追加到FILE文件中
-d,-debug 打印调试输出
-q,-quiet 安静模式(没有输出)
-v,-verbose 冗长模式(这是缺省设置)
-nv,-non-verbose 关掉冗长模式,但不是安静模式
-i,-input-file=FILE 下载在FILE文件中出现的URLs
-F,-force-html 把输入文件当作HTML格式文件对待
-B,-base=URL 将URL作为在-F -i参数指定的文件中出现的相对链接的前缀
-sslcertfile=FILE 可选客户端证书
-sslcertkey=KEYFILE 可选客户端证书的KEYFILE
-egd-file=FILE 指定EGD socket的文件名

#下载
-bind-address=ADDRESS 指定本地使用地址(主机名或IP,当本地有多个IP或名字时使用)
-t,-tries=NUMBER 设定最大尝试链接次数(0 表示无限制).
-O -output-document=FILE 把文档写到FILE文件中
-nc,-no-clobber 不要覆盖存在的文件或使用.#前缀
-c,-continue 接着下载没下载完的文件
-progress=TYPE 设定进程条标记
-N,-timestamping 不要重新下载文件除非比本地文件新
-S,-server-response 打印服务器的回应
-spider 不下载任何东西
-T,-timeout=SECONDS 设定响应超时的秒数
-w,-wait=SECONDS 两次尝试之间间隔SECONDS秒
-waitretry=SECONDS 在重新链接之间等待1…SECONDS秒
-random-wait 在下载之间等待0…2*WAIT秒
-Y,-proxy=on/off 打开或关闭代理
-Q,-quota=NUMBER 设置下载的容量限制
-limit-rate=RATE 限定下载输率
#目录
-nd -no-directories 不创建目录
-x,-force-directories 强制创建目录
-nH,-no-host-directories 不创建主机目录
-P,-directory-prefix=PREFIX 将文件保存到目录 PREFIX/…
-cut-dirs=NUMBER 忽略 NUMBER层远程目录
#HTTP 选项
-http-user=USER 设定HTTP用户名为 USER.
-http-passwd=PASS 设定http密码为 PASS.
-C,-cache=on/off 允许/不允许服务器端的数据缓存 (一般情况下允许).
-E,-html-extension 将所有text/html文档以.html扩展名保存
-ignore-length 忽略 'Content-Length'头域
-header=STRING 在headers中插入字符串 STRING
-proxy-user=USER 设定代理的用户名为 USER
-proxy-passwd=PASS 设定代理的密码为 PASS
-referer=URL 在HTTP请求中包含 `Referer: URL'
-s,-save-headers 保存HTTP头到文件
-U,-user-agent=AGENT 设定代理的名称为 AGENT而不是 Wget/VERSION.
-no-http-keep-alive 关闭 HTTP活动链接 (永远链接).
-cookies=off 不使用 cookies.
-load-cookies=FILE 在开始会话前从文件 FILE中加载cookie
-save-cookies=FILE 在会话结束后将 cookies保存到 FILE文件中

#FTP 选项
-nr,-dont-remove-listing 不移走 `.listing'文件
-g,-glob=on/off 打开或关闭文件名的 globbing机制
-passive-ftp 使用被动传输模式 (缺省值).
-active-ftp 使用主动传输模式
-retr-symlinks 在递归的时候,将链接指向文件(而不是目录)

#递归下载
-r,-recursive 递归下载--慎用!
-l,-level=NUMBER 最大递归深度 (inf 或 0 代表无穷).
-delete-after 在现在完毕后局部删除文件
-k,-convert-links 转换非相对链接为相对链接
-K,-backup-converted 在转换文件X之前,将之备份为 X.orig
-m,-mirror 等价于 -r -N -l inf -nr.
-p,-page-requisites 下载显示HTML文件的所有图片

#递归下载中的包含和不包含(accept/reject)
-A,-accept=LIST 分号分隔的被接受扩展名的列表
-R,-reject=LIST 分号分隔的不被接受的扩展名的列表
-D,-domains=LIST 分号分隔的被接受域的列表
-exclude-domains=LIST 分号分隔的不被接受的域的列表
-follow-ftp 跟踪HTML文档中的FTP链接
-follow-tags=LIST 分号分隔的被跟踪的HTML标签的列表
-G,-ignore-tags=LIST 分号分隔的被忽略的HTML标签的列表
-H,-span-hosts 当递归时转到外部主机
-L,-relative 仅仅跟踪相对链接
-I,-include-directories=LIST 允许目录的列表
-X,-exclude-directories=LIST 不被包含目录的列表
-np,-no-parent 不要追溯到父目录

常用选项:

1
2
3
4
5
-q 静默模式
-c 断点续传
-P /path 保存在指定目录
-O filename 保存为指定文件名,filename 为 - 时,发送至标准输出
--limit-rate= 指定传输速率,单位K,M等

curl

curl是基于URL语法在命令行方式下工作的文件传输工具,它支持FTP,FTPS,HTTP,HTTPS,GOPHER,TELNET,DICT,FILE及LDAP等协议。curl支持HTTPS认证,并且支持HTTP的POST、PUT等方法,FTP上传,kerberos认证,HTTP上传,代理服务器,cookies,用户名/密码认证,下载文件断点续传,上载文件断点续传,http代理服务器管道( proxy tunneling),还支持IPv6,socks5代理服务器,通过http代理服务器上传文件到FTP服务器等,功能十分强大

1
curl [options] [URL...]
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
-A/--user-agent <string> 设置用户代理发送给服务器
-e/--referer <URL> 来源网址
--cacert <file> CA证书 (SSL)
-k/--insecure 允许忽略证书进行 SSL 连接
--compressed 要求返回是压缩的格式
-H/--header "key:value” 自定义首部字段传递给服务器
-i 显示页面内容,包括报文首部信息
-I/--head 只显示响应报文首部信息
-D/--dump-header <file>将url的header信息存放在指定文件中
--basic 使用HTTP基本认证
-u/--user <user[:password]>设置服务器的用户和密码
-L 如果有3xx响应码,重新发请求到新位置
-O 使用URL中默认的文件名保存文件到本地
-o <file> 将网络文件保存为指定的文件中
--limit-rate <rate> 设置传输速度
-0/--http1.0 数字0,使用HTTP 1.0
-v/--verbose 更详细
-C 选项可对文件使用断点续传功能
-c/--cookie-jar <file name> 将url中cookie存放在指定文件中
-x/--proxy <proxyhost[:port]> 指定代理服务器地址
-X/--request <command> 向服务器发送指定请求方法
-U/--proxy-user <user:password> 代理服务器用户和密码
-T 选项可将指定的本地文件上传到FTP服务器上
--data/-d 方式指定使用POST方式传递数据
-s --silent Silent mode
-b name=data 从服务器响应set-cookie得到值,返回给服务器
-w <format> 显示相应的指定的报文信息,如:%{http_code},%{remote_ip}等
-m, --max-time <time> 允许最大传输时间

httpie

httpie 工具是现代的 HTTP 命令行客户端,它能通过命令行界面与 Web 服务进行交互。它提供一个简单的 http 命令,允许使用简单而自然的语法发送任意的 HTTP 请求,并会显示彩色的输出

httpie 能用于测试、调试及与 HTTP 服务器交互

  • 具表达力的和直观语法
  • 格式化的及彩色化的终端输出
  • 内置 JSON 支持
  • 表单和文件上传
  • HTTPS、代理和认证任意请求数据
  • 自定义头部
  • 持久化会话
  • 类似 wget 的下载
  • 支持 Python 2.7 和 3.x

官方网站:https://httpie.org
安装:基于EPEL(CentOS 8 目前还不支持)

1
[root@centos7 ~]#yum install httpie -y

范例:

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
# 显示信息(包含响应头200)
http www.magedu.com

# 显示详细的请求(包含请求和返回头200)
http -v www.magedu.com

# 只显示Header
http -h www.magedu.com
http --head www.magedu.com
http --header www.magedu.com
http --headers www.magedu.com

# 只显示Body
http -b www.magedu.com
http --body magedu.com

# 下载文件
http -d www.magedu.com

# 模拟提交表单
http -f POST www.magedu.com username='wang'

# 请求删除的方法
http DELETE www.magedu.com

# 传递JSON数据请求(默认就是JSON数据请求)
http PUT www.magedu.com username='wang' password='magedu'

# 如果JSON数据存在不是字符串则用:=分隔,例如
http PUT www.magedu.com username='wang' password='magedu' age:=30 a:=true streets:='["a", "b"]'

# 模拟Form的Post请求, Content-Type: application/x-www-form-urlencoded;
charset=utf-8
http --form POST www.magedu.com username='wang'

# 模拟Form的上传, Content-Type: multipart/form-data
http -f POST www.magedu.com/jobs username='wang' file@~/test.pdf

# 修改请求头, 使用:分隔
http www.magedu.com User-Agent:magedu-agent/1.0 'Cookie:a=b;b=c' Referer:http://www.google.com/

# 认证
http -a username:password www.magedu.com
http -A basic -a username:password www.magedu.com

# 使用http代理
http --proxy=http:http://172.16.0.100:8081 proxy.magedu.com
http --proxy=http:http://user:pass@172.16.0.100:8081 proxy.magedu.com
http --proxy=https:http://172.16.0.100:8118 proxy.magedu.com
http --proxy=https:http://user:pass@172.16.0.100:8118 proxy.magedu.com

范例:查看信息及相应头

1
2
3
4
5
6
7
8
9
10
11
12
[root@centos7 ~]#http 192.168.8.8/test.html
HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: Keep-Alive
Content-Length: 30
Content-Type: text/html; charset=UTF-8
Date: Thu, 07 Nov 2019 09:09:34 GMT
ETag: "1e-596be05bc9a34"
Keep-Alive: timeout=5, max=100
Last-Modified: Thu, 07 Nov 2019 09:09:27 GMT
Server: Apache/2.4.37 (centos)
<strong>马哥教育</strong>

范例:查看请示和响应头部及信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@centos7 ~]#http -v 192.168.8.8/test.html
GET /test.html HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: 192.168.8.8
User-Agent: HTTPie/0.9.4

HTTP/1.1 200 OK
Accept-Ranges: bytes
Connection: Keep-Alive
Content-Length: 30
Content-Type: text/html; charset=UTF-8
Date: Thu, 07 Nov 2019 09:09:39 GMT
ETag: "1e-596be05bc9a34"
Keep-Alive: timeout=5, max=100
Last-Modified: Thu, 07 Nov 2019 09:09:27 GMT
Server: Apache/2.4.37 (centos)
<strong>马哥教育</strong>

范例:查看响应报文头部

1
2
3
4
5
6
7
8
9
10
11
[root@centos7 ~]#http HEAD http://www.magedu.com
HTTP/1.1 200 OK
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Date: Thu, 07 Nov 2019 08:09:49 GMT
Link: <http://www.magedu.com/wp-json/>; rel="https://api.w.org/"
Link: <http://www.magedu.com/>; rel=shortlink
Server: Tengine
Vary: Accept-Encoding
Vary: Accept-Encoding, Cookie

范例: 查看请求和响应报文头部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@centos7 ~]#http -p Hh http://www.magedu.com
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: www.magedu.com
User-Agent: HTTPie/0.9.4

HTTP/1.1 200 OK
Cache-Control: max-age=3, must-revalidate
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html; charset=UTF-8
Date: Thu, 07 Nov 2019 03:44:14 GMT
Server: Tengine
Transfer-Encoding: chunked
Vary: Accept-Encoding
Vary: Accept-Encoding, Cookie

范例:指定请求头部的首部字段

1
2
3
4
5
6
7
8
9
[root@centos7 ~]#http -p H http://www.magedu.com User-Agent:wangtest
Referer:http://www.baidu.com
GET / HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: www.magedu.com
Referer: http://www.baidu.com
User-Agent: wangtest

范例:下载资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#方法1
[root@centos7 ~]#http http://www.magedu.com/wp-content/uploads/2018/12/2018122312035677.png > logo.png
#方法2
[root@centos7 ~]#file logo.png
logo.png: PNG image data, 411 x 127, 8-bit/color RGBA, non-interlaced
[root@centos7 ~]#http --download http://www.magedu.com/wp-content/uploads/2018/12/2018122312035677.png
HTTP/1.1 200 OK
Accept-Ranges: bytes
Cache-Control: max-age=604800
Connection: keep-alive
Content-Length: 8983
Content-Type: image/png
Date: Thu, 07 Nov 2019 08:20:44 GMT
ETag: "5c1f79ac-2317"
Expires: Thu, 14 Nov 2019 08:20:44 GMT
Last-Modified: Sun, 23 Dec 2018 12:03:56 GMT
Server: Tengine
Vary: Accept-Encoding

Downloading 8.77 kB to "2018122312035677.png"
Done. 8.77 kB in 0.00091s (9.46 MB/s)

范例:用POST方法提交json格式的数据

1
[root@centos7 ~]#http http://www.magedu.com user=wang password=magedu

范例:用POST方法,指交表单数据

1
[root@centos7 ~]#http -f POST http://www.magedu.com user=wang password=magedu

压力测试工具

ab 来自httpd-tools包

1
ab [OPTIONS] URL

option:

  • -n:总请求数
  • -c:模拟的并发数
  • -k:以持久连接模式测试

运维三大职能:故障管理、变更管理、资源管理

什么是k8s?
k8s是部署在一群服务器之上的组件,将所有服务器整合成一个资源池,然后向客户端提供各种接口, 客户端只需要调用相应接口就可以管理容器,至于底层容器具体跑在哪台服务器,不可见也不需要关心

针对各种特定的业务,尤其是比较依赖工程师经验的业务(例如数据库集群宕机后的数据恢复、各种集群的扩缩容),可以将一系列复杂操作代码化,这个代码在k8s中就叫operator,只要用好各种operator,就可以方便高效的解决各种问题,这样,运维工程师的主要工作就变成了维护k8s,确保k8s自身能够良好运行

k8s内置了各种operator,但是这些operator更重视通用性,很难完全匹配实际工作需要,所以就需要对k8s进行二次开发,编写各种operator,这也是SRE工程师的必备技能之一

operator能单独管理应用集群,实现复杂操作;controllor:控制器,只能实现简单的容器操作

开发人员为k8s开发应用程序的时候,通常不会完整的部署一个分布式k8s集群,而是使用一个叫做MiniKube,它可以在单机上模拟出一个完整意义上的k8s集群

kubernetes的组件

资源池中的各个服务器叫做节点,运行Pod的节点叫做Worker Node(后文称Node

k8s组件分为三种:控制平面组件Node组件Addons

控制平面组件

控制平面(control plane)管理Node和Node上的Pods

控制平面组件可以分别运行在任意节点上,但是为了简单,通常会将所有控制平面组件运行在同一个节点上,还可以以副本的形式运行在多个节点上,然后禁止在这种节点上运行pod,这种节点就叫做Master Node(后文简称Master),当集群中只有一个Master时,就是单控制平面,有多个Master时,就是多控制平面,生产中肯定是多控制平面,但是学习中一般只使用单控制平面

kube-apiserver

https服务器,监听在6443端口,它将k8s集群内的一切都抽象成资源,提供RESTful风格的API,对资源进行增删改查等管理操作。

资源类型:Pod、Deployment、Service等等,反应在Etcd中,就是一张张的表

对象:资源表中的每一条数据项就是一个对象,例如 Pod表中的数据项就是Pod对象。所以资源表通常不叫Pod表、Deployment表…,而是叫做PodList、DeploymentList…

apiserver是整个k8s系统的总线,所有组件之间,都是通过它进行协同,它是唯一可以存储k8s集群的状态信息的组件(储存在Etcd)

生产中,apiserver需要做冗余,因为无状态,所以最少部署两个apiserver,因为是https服务器,所以需要做四层负载,使用Nginx、HAProxy、LVS均可,然后搭配keepalived给负载均衡做高可用

关于健康监测,分为AH(主动监测)和PH(被动监测或者叫异常值探测)

kube-controller-manager

控制器管理器,负责管理控制器,真正意义上让k8s所有功能得以实现的控制中心,k8s可以把运维人员日常重复性的工作代码化,而代码化集成的位置就在controller-manager中实现,controller-manager中有很多controller(deployment等数十种),这些controller才是真正意义上的k8s控制中心,可以将多controller打包起来,单一运行。

controller loop:控制循环

kube-scheduler

调度器,调度pod,它的核心作用就是运行应用,scheduler时刻关注着每个节点的资源可用量,以及运行pod所需的资源量,让二者达到最佳匹配,让pod以最好的状态运行

etcd

不是k8s内置,而是第三方的,基于raft协议,使用go研发的轻量级、分布式、支持数据强一致的kv存储系统,为避免脑裂,通常部署3个或5个节点

cloud-controller-manager

略…

Node组件

Node组件运行在所有的节点上,包括Master

kubelet

工作在每个Node上的agent(代理),与api server建立联系,监视api server中与自己node相关的pod的变动信息,执行指令操作

kube-porxy

守护进程,管理当前节点的iptables或ipvs规则,而且管理的只是和service相关的规则

监控service,把集群上的每一个service的定义转换为本地的ipvs或iptables规则

kube-proxy是运行在集群中的每个节点上的网络代理,实现了Kubernetes服务概念的一部分。
kube-proxy维护节点上的网络规则。这些网络规则允许从集群内部或外部的网络会话到Pods进行网络通信。
kube-proxy使用操作系统包过滤层(如果有的话)并且它是可用的。否则,kube-proxy将自己转发流量

Container runtime

通常是docker,其他类型的容器也支持

Addons

附加组件扩展了Kubernetes的功能

插件使用Kubernetes资源(DaemonSet, Deployment,等等)来实现集群特性。因为它们提供了集群级的特性,所以插件的命名空间资源属于kube-system命名空间。

DNS

CoreDNS,k8s中,DNS是至关重要的,所有的访问都不会基于ip,而是基于name,name再通过DNS解析成ip

Web UI

集群监控系统

prometheus

集群日志系统

EFK、LG

Ingress Controller

入栈流量控制器,是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。

附件千千万,按需部署

kubectl

kubectl是kube-apiserver的命令行客户端,就像redis-cli是redis的命令行客户端

所以很显然,也可以使用python、go、php等编程语言操作kube-apiserver

创建对象的三种方式

  1. 命令式命令:命令,全部配置通过选项指定
  2. 命令式配置文件:命令,加载配置文件
  3. 声明式配置文件:声明式命令,加载配置清单

推荐使用第三种

配置清单:

1
2
3
4
5
6
7
8
9
10
11
12
# 配置清单的规范叫做资源规范,范例:
apiVersion: v1
kind: Pod
metadata:
name: myPod
labels:
app: mypod
release: canary
spec: # 期望状态
containers:
- name: demoapp
image: ikubernetes/demoapp:v1.0

资源清单是yml格式,api-server会自动转成json格式

如果实际状态和期望状态有出入,控制器的控制循环就会监控到差异,然后将需要做出的更改提交到apiserver,调度器scheduler监控到apiserver中有未执行的操作,就会去找适合执行操作的node,然后提交到apiserver,kubelet监控到apiserver中有关于自己节点的操作,就会执行操作,将执行结果返回给apiserver,apiserver再更新实际状态

部署(centos7)

三个节点:master01、node01、node02

  1. 安装docker

    1
    2
    3
    4
    5
    yum install -y yum-utils device-mapper-persistent-data lvm2
    yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    yum makecache fast
    yum -y install docker-ce
    service docker start
  2. 安装Kubernetes

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    cat <<EOF > /etc/yum.repos.d/kubernetes.repo
    [kubernetes]
    name=Kubernetes
    baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
    enabled=1
    gpgcheck=1
    repo_gpgcheck=1
    gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
    EOF
    setenforce 0
    yum install -y kubelet kubeadm kubectl
    systemctl enable kubelet && systemctl start kubelet
  3. 部署dns,或者直接修改/etc/hosts

    1
    2
    3
    4
    5
    # cat /etc/hosts
    ...
    10.0.0.71 master01.ljk.cn
    10.0.0.72 node01.ljk.cn
    10.0.0.73 node02.ljk.cn
  4. 此命令初始化一个 Kubernetes 控制平面节点,在主节点运行 kubeadm init

    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
    [root@node01 ~]$kubeadm init --help
    Run this command in order to set up the Kubernetes control plane

    The "init" command executes the following phases:
    ```
    preflight 预检测
    certs 生成证书
    /ca
    /apiserver
    /apiserver-kubelet-client
    /front-proxy-ca
    /front-proxy-client
    /etcd-ca
    /etcd-server
    /etcd-peer
    /etcd-healthcheck-client
    /apiserver-etcd-client
    /sa
    kubeconfig 生成配置文件
    /admin
    /kubelet
    /controller-manager
    /scheduler
    kubelet-start 启动kubelet
    control-plane 创建control-plane,就是控制平面
    /apiserver
    /controller-manager
    /scheduler
    etcd Generate static Pod manifest file for local etcd
    /local
    upload-config 上传配置,为高可用控制平面准备
    /kubeadm
    /kubelet
    upload-certs 上传证书,为高可用控制平面准备
    mark-control-plane Mark a node as a control-plane
    bootstrap-token 引导令牌,以方便其他节点加进来
    kubelet-finalize Updates settings relevant to the kubelet after TLS bootstrap
    /experimental-cert-rotation Enable kubelet client certificate rotation
    addon 安装组件
    /coredns 服务注册和服务发行的关键组件,k8s是基于dns做服务发现的
    /kube-proxy Install the kube-proxy addon to a Kubernetes cluster
    ```

    1. run preflight checks:预检
    2. kubelet start:启动
    3. generate certificate:生成证书
    4. generate kubeconfig files:生成kubeconfig文件,/etc/kubernetes/*.conf
    5. generate static pod manifests for the control plane:生成静态pod的清单,/etc/kubernetes/manifests/*.yaml
    6. wait for the control plane to be healthy:等待pod启动成功
    7. Upload kubeadm & kubelet config to a ConfigMap
    8. Taint and label the master:给master节点设置污点,效果就是将来pod不会部署到master上
    9. Generate a (by default random) Bootstrap Token:生成一个引导令牌,节点凭令牌加入集群
    10. Setup the RBAC Authorization System:设置基于角色的授权模型
    11. Install DNS and Proxy Addons:安装附件

pod

pop是容器集,是k8s的最小运行单元,一个pop中的所有容器必须运行在同一node上

Kubernetes 集群中的 Pod 主要有两种用法:

  • 运行单个容器的 Pod。”每个 Pod 一个容器”模型是最常见的 Kubernetes 用例; 在这种情况下,可以将 Pod 看作单个容器的包装器,并且 Kubernetes 直接管理 Pod,而不是容器。
  • 运行多个协同工作的容器的 Pod。 Pod 可能封装由多个紧密耦合且需要共享资源的共处容器组成的应用程序。 这些位于同一位置的容器可能形成单个内聚的服务单元 —— 一个容器将文件从共享卷提供给公众, 而另一个单独的“挂斗”(sidecar)容器则刷新或更新这些文件。 Pod 将这些容器和存储资源打包为一个可管理的实体。

一个pod就像是一个虚拟机

pause是pod的基础设施容器,这个容器的作用是给pod内部容器提供可被加入和共享的net、ipc、uts namespace,同时我们向一个pod附加volume时,也是附加在pause之上,从而可以被共享给一个pod内的多个容器

建立pod

p83,4.5.2 pod的创建过程

通信

pod内的容器通信

复习:命名空间,linux底层概念,在内核层实现,容器使用namespce技术实现容器间相互隔离

  • mnt namespace:隔离根,即chroot技术
  • ipc namespace:隔离进程间通信
  • uts namespace:隔离系统信息,主机名等信息
  • pid namespace:隔离进程号
  • net namespace:隔离网络
  • user namespce:隔离用户

运行在同一个pod内的容器,共享net、ipc、uts、一组存储卷,亲密关系,同进同退

node内pod之间通信

借助于网络插件实现,通过bridge通信

跨node的pod之间通信

借助于网络插件实现,两种实现网络模型:叠加网络 和 承载网络

叠加:搭建隧道

承载:设置路由表

service

pod之间通信,一般不会pod直接访问pod,而是pod先访问service,然后service再到pod

kube-proxy会把集群上的每一个service的定义转换为本地的ipvs或iptables规则,所以当访问service ip(cluster ip)时,在内核中就会自动转向service后的某个pod的ip地址

service的ip也不会直接拿来用,而是访问service的name,使用CoreDNS将name解析为ip

service有三种实现模型:userspace、iptables、ipvs,ipvs性能最好,p138,6.1.2

参考:https://www.cnblogs.com/fuyuteng/p/11598768.html

集群外部的客户端访问集群中的服务

两种方式:

  1. 集群外的流量先进入节点网络,再进入service网络,最后进入pod网络
  2. 物理桥接,让pod共享节点的网络名称空间

默认情况下,容器没有资源的使用限制,docker提供了控制容器使用资源的方法,可以限制容器使用多少内存或 CPU等, 在docker run 命令的运行时配置标志实现资源限制功能。

容器的内存限制

为了运行效率,建议关闭交换内存,如果内存不够了,打报告采购就好了,而且k8s也禁止使用交换内存

在容器中使用free命令,看到的是宿主机的内存情况

内存相关配置项 说明
-m | –mermory= 容器可以使用的最大内存,硬限制,最小4m,此项常用
–mermory-swap 交换内存相关的配置项,如果宿主机关闭了交换内存,建议忽略此项

范例:

1
2
3
# 单位:b、k、m、g
# 限制容器的内存不超过300m
root@u3:~# docker container run -it -m 300m harbor.ljk.org/library/alpine:v1

容器的CPU限制

docker 仓库分为公有云仓库 和 私有云仓库

公有云仓库分为 官方 和 阿里云等第三方仓库,推荐第三方

私有云仓库一般通过 docker registory 或者 docker harbor这两个软件搭建,推荐docker harbor

阿里云docker仓库

  1. 访问凭证
  2. 命名空间
  3. 镜像仓库

范例:

1
2
3
4
5
6
7
8
9
10
# 1. 浏览器的登录阿里云,创建命名空间107,镜像仓库可以创建也可以不创建,如果不创建,push的时候会自动创建
# 2. 登录阿里云,交互式,密码存储在~/.docker/config.json
docker login --username=1076956365@qq.com registry.cn-beijing.aliyuncs.com
# 3. 给镜像打标签,注意格式
docker tag nginx-alpine:v1 registry.cn-beijing.aliyuncs.com/107/nginx:1.18-v1
# 4. 上传到阿里云的docker镜像仓库
docker push registry.cn-beijing.aliyuncs.com/107/nginx:1.18-v1
# 5. 刷新网站页面,如果没有看到镜像,可能是区域设置不对
# 6. 其他机器下载镜像
docker pull registry.cn-beijing.aliyuncs.com/107/nginx:1.18-v1

docker之分布式仓库 harbor

harbor功能介绍:

  • 基于角色的访问控制
  • 镜像复制
  • 图形化用户界面
  • 审计管理
  • 国际化
  • resful api
  • 部署简单

harbor组成:

harbor是由很多容器组成实现完整功能

  • proxy:对应启动组件nginx。它是一个nginx反向代理,代理notary client(镜像认证)、docker client(镜像上传下载等)和浏览器的访问请求(core service)给后端的各服务
  • UI(Core Service):对应启动组件harbor-ui。底层数据存储使用mysql数据库,主要提供了四个子功能:
    • UI:一个web管理页面ui
    • API:Harbor暴露的API服务
    • Auth:用户认证服务,decode后的token中的用户信息在这里进行认证;auth后端可以接db、ldap、uaa三种认证实现
    • Token服务(上图中未体现):负责根据用户在每个project中的role来为每一个docker push/pull命令发布一个token,如果从docker client发送给registry的请求没有带token,registry会重定向请求到token服务创建token
  • registry:对应启动组件registry。负责存储镜像文件,和处理镜像的pull/push命令。Harbor对镜像进行强制的访问控制,Registry会将客户端的每个pull、push请求转发到token服务来获取有效的token
  • admin service:对应启动组件harbor-adminserver。是系统的配置管理中心附带检查存储用量,ui和jobserver启动时候需要加载adminserver的配置
  • job service:对应启动组件harbor-jobservice。负责镜像复制工作的,他和registry通信,从一个registry pull镜像然后push到另一个registry,并记录job_log
  • log collector:对应启动组件harbor-log。日志汇总组件,通过docker的log-driver把日志汇总到一起
  • db:对应启动组件harbor-db,负责存储project、 user、 role、replication、image_scan、access等的metadata数据

配置harbor仓库

两台harbor服务器 10.0.0.118 和 10.0.0.119 均按照以下步骤配置:

  1. 安装docker
  2. 安装docker compose,docker compose 必须先于harbor安装,否则会报错
  3. 下载Harbor安装包并解压缩
  4. 编辑配置文件 harbor.yml,旧版本是harbor.cfg
  5. 运行 harbor 安装脚本
  6. 实现开机自动启动 harbor
  7. 登录 harbor 主机网站
  8. 建立项目
  9. 修改harbor配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 安装docker
$ sudo apt-get update
$ sudo apt-get -y install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
$ curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository \
"deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
$(lsb_release -cs) \
stable"
$ sudo apt-get update
$ sudo apt -y install docker-ce

# 以上是直接安装最新版本,推荐安装时指定版本
$ apt-cache madison docker-ce
$ sudo apt -y install docker-ce=<VERSION_STRING>
1
2
3
4
5
# 2. 安装docker compose
root@u1:~# apt -y install python3-pip # 为了方便,直接安装pip
root@u1:~# pip3 install docker-compose
root@u1:~# docker-compose --version
docker-compose version 1.27.4, build unknown
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 3. 下载Harbor安装包并解压缩
# 可以下载在线安装包或者完整的离线安装包,推荐下载离线包
# 下载网址:https://github.com/goharbor/harbor/releases
# 推荐使用迅雷下载:harbor-offline-installer-v1.10.6.tgz
root@u1:/usr/local/src# tar zxvf harbor-offline-installer-v1.10.6.tgz
root@u1:/usr/local/src# cd harbor/
root@u1:/usr/local/src/harbor# ll
total 700264
drwxr-xr-x 2 root root 4096 Nov 24 13:29 ./
drwxr-xr-x 3 root root 4096 Nov 24 13:29 ../
-rw-r--r-- 1 root root 3398 Nov 17 03:58 common.sh
-rw-r--r-- 1 root root 717021676 Nov 17 03:59 harbor.v1.10.6.tar.gz
-rw-r--r-- 1 root root 5882 Nov 17 03:58 harbor.yml
-rwxr-xr-x 1 root root 2284 Nov 17 03:58 install.sh*
-rw-r--r-- 1 root root 11347 Nov 17 03:58 LICENSE
-rwxr-xr-x 1 root root 1749 Nov 17 03:58 prepare*
1
2
3
4
5
6
7
8
9
10
11
12
13
# 4. 编辑配置文件 harbor.yml
root@u1:/usr/local/src/harbor# vim harbor.yml
#只需要修改下面两行
hostname: 10.0.0.118 # 修改此行,指向当前主机IP 或 FQDN
harbor_admin_password: 123456 # 修改此行指定harbor登录用户admin的密码

# 不需要https,注释以下内容
# https:
# # https port for harbor, default is 443
# port: 443
# # The path of cert and key files for nginx
# certificate: /your/certificate/path
# private_key: /your/private/key/path
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
# 5. 运行 harbor 安装脚本
root@u1:/usr/local/src# mv harbor /usr/local/
root@u1:/usr/local/src# cd /usr/local/harbor/
root@u1:/usr/local/src# ./prepare
...
root@u1:/usr/local/src# ./install.sh
...
Creating network "harbor_harbor" with the default driver
Creating harbor-log ... done
Creating harbor-db ... done
Creating redis ... done
Creating registry ... done
Creating registryctl ... done
Creating harbor-portal ... done
Creating harbor-core ... done
Creating harbor-jobservice ... done
Creating nginx ... done
✔ ----Harbor has been installed and started successfully.----
root@u1:/usr/local/src# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
goharbor/chartmuseum-photon v1.10.6 01b70eccaf71 7 days ago 178MB
goharbor/harbor-migrator v1.10.6 a5d4a4ee44e4 7 days ago 356MB
goharbor/redis-photon v1.10.6 99e25b65195c 7 days ago 132MB
goharbor/clair-adapter-photon v1.10.6 aa72598ecc12 7 days ago 61.3MB
goharbor/clair-photon v1.10.6 da1b03030e34 7 days ago 171MB
goharbor/notary-server-photon v1.10.6 37c8bed3e255 7 days ago 142MB
goharbor/notary-signer-photon v1.10.6 c56d82220929 7 days ago 139MB
goharbor/harbor-registryctl v1.10.6 1d3986d90c65 7 days ago 101MB
goharbor/registry-photon v1.10.6 3e669c8204ed 7 days ago 83.7MB
goharbor/nginx-photon v1.10.6 a39d8dd46060 7 days ago 43.7MB
goharbor/harbor-log v1.10.6 1085d3865a57 7 days ago 106MB
goharbor/harbor-jobservice v1.10.6 aa05538acecf 7 days ago 143MB
goharbor/harbor-core v1.10.6 193e76e6be5d 7 days ago 129MB
goharbor/harbor-portal v1.10.6 942a9c448850 7 days ago 51.8MB
goharbor/harbor-db v1.10.6 37da2e5414ae 7 days ago 170MB
goharbor/prepare v1.10.6 35f073e33ec5 7 days ago 177MB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 6. 实现开机自动启动 harbor
root@u1:~# vim /lib/systemd/system/harbor.service
[Unit]
Description=Harbor
After=docker.service systemd-networkd.service systemd-resolved.service
Requires=docker.service
Documentation=http://github.com/vmware/harbor

[Service]
Type=simple
Restart=on-failure
RestartSec=5
ExecStart=/usr/local/bin/docker-compose -f usr/local/harbor/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f usr/local/harbor/docker-compose.yml down

[Install]
WantedBy=multi-user.target

root@u1:~# systemctl daemon-reload
root@u1:~# systemctl enable --now harbor.service
Created symlink /etc/systemd/system/multi-user.target.wants/harbor.service → /lib/systemd/system/harbor.service.
  1. 登录 harbor 主机网站 http://10.0.0.118

1
2
3
4
5
6
# 9. 修改harbor配置
# 后期如果需要修改harbor配置
1.修改 harbor.yml 文件
2.停止harbor: systemctl stop harbor.service
3.执行prepare
4.启动harbor:sysytemcrl start harbor.service

配置docker客户端

客户端 10.0.0.117

  1. 安装docker
  2. 登录harbor
  3. 给本地镜像大标签并上传到harbor
  4. 下载harbor的镜像
1
2
# 1. 安装docker
略...
  1. 建立项目

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 3. 登录harbor.两种方法,命令行或者配置文件
# 方法一:命令行
root@u3:~# vim /usr/lib/systemd/system/docker.service
...
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --insecure-registry 10.0.0.118 --insecure-registry 10.0.0.119
...
# 方法二:配置文件,推荐
root@u3:~# vim /etc/docker/daemon.json
{
"registry-mirrors": ["https://3417nt4m.mirror.aliyuncs.com"],
"insecure-registries": ["10.0.0.118", "10.0.0.119"]
}
root@u3:~# systemctl restart docker.service
root@u3:~# docker login 10.0.0.118
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store

Login Succeeded
1
2
3
4
5
# 3. 给本地镜像大标签并上传到harbor
# 修改 images 的名称,不修改成指定格式无法将镜像上传到 harbor 仓库
# 格式:Harbor主机IP/项目名/image名字:版本
root@u3:~# docker tag alpine:latest 10.0.0.118/library/alpine-base:3.12
root@u3:~# docker push 10.0.0.118/library/alpine-base:3.12
1
2
3
# 4. 下载harbor的镜像
# 另开一台机器,安装docker,daemon.json设置insecure-registries,就可以直接下载镜像
root@u4:~# docker image pull 10.0.0.118/library/alpine-base:3.12

harbor高可用

实现harbor有两种方式:共享存储 和 镜像复制,这里只学习后者,可以通过web界面直接配置镜像复制

前面配置了两台harbor仓库,10.0.0.118 和 10.0.0.119,但是只使用了一台,这里使用另一台组成双向复制

注意:不是主从,而是双向复制,通过负载均衡器将请求分流

h1:10.0.0.118
h2:10.0.0.119

  1. 确保 h1 和 h2 中有相同的项目

  2. h1 和 h2 均新建仓库,目标分别为对方

    复制模式选择 push-based

  3. h1 和 h2 均新建复制,目标分别为对方

  4. 测试,分别在h1和h2上传(注意上传前先登录harbor)然后删除,都能同步成功

harbor配置https

harbor默认使用http,可以配置https,不过我认为没有这个必要

在前面配置好harbor仓库的基础上,执行以下操作:

  1. 配置服务端

    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
    root@u1:~# mkdir -p /root/.rnd
    root@u1:~# mkdir -p /usr/local/harbor/certs
    root@u1:~# cd /usr/local/harbor/certs
    root@u1:/usr/local/harbor/certs# ls
    # 创建私有CA
    root@u1:/usr/local/harbor/certs# openssl req -newkey rsa:1024 -nodes -sha256 -keyout ca.key -x509 -subj "/CN=ca.ljk.org" -days 3650 -out ca.crt
    Generating a RSA private key
    ..............+++++
    ...................................+++++
    writing new private key to 'ca.key'
    -----
    root@u1:/usr/local/harbor/certs# ls
    ca.crt ca.key
    # 生成harbor主机的证书申请
    root@u1:/usr/local/harbor/certs# openssl req -newkey rsa:1024 -nodes -sha256 -subj "/CN=harbor.ljk.org" -keyout harbor.ljk.org.key -out harbor.ljk.org.csr
    Generating a RSA private key
    .....................................................................+++++
    ..........+++++
    writing new private key to 'harbor.ljk.org.key'
    -----
    root@u1:/usr/local/harbor/certs# ls
    ca.crt ca.key harbor.ljk.org.csr harbor.ljk.org.key
    # 给harbor主机颁发证书
    root@u1:/usr/local/harbor/certs# openssl x509 -req -in harbor.ljk.org.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out harbor.ljk.org.crt
    Signature ok
    subject=CN = harbor.ljk.org
    Getting CA Private Key
    root@u1:/usr/local/harbor/certs# tree
    .
    ├── ca.crt
    ├── ca.key
    ├── ca.srl
    ├── harbor.ljk.org.crt
    ├── harbor.ljk.org.csr
    └── harbor.ljk.org.key

    0 directories, 6 files
    root@u1:/usr/local/harbor/certs# vim ../harbor.yml
    hostname: harbor.ljk.org # 此行需改为域名
    https:
    # https port for harbor, default is 443
    port: 443
    # The path of cert and key files for nginx
    certificate: /usr/local/harbor/certs/harbor.ljk.org.crt
    private_key: /usr/local/harbor/certs/harbor.ljk.org.key
    ...
    root@u1:/usr/local/harbor/certs# cd ..
    root@u1:/usr/local/harbor# systemctl stop harbor.service # 这一步要等挺久
    root@u1:/usr/local/harbor# ./prepare
    root@u1:/usr/local/harbor# ./install.sh
    root@u1:/usr/local/harbor# systemctl start harbor.service

    访问:https://10.0.0.118

  2. 配置客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    root@u3:~# echo '10.0.0.118 harbor.ljk.org' >> /etc/hosts

    # 因为是自建证书,所以harbor规定,客户端也下载一份,位于/etc/docker/certs.d/harbor.ljk.org
    # 其实,只需要harbor.ljk.org.crt即可,这里为了方便,全部拷贝过来
    root@u3:~# scp lujinkai@harbor.ljk.org:/usr/local/harbor/certs/* /etc/docker/certs.d/harbor.ljk.org/

    # 登录成功
    root@u3:~# docker login harbor.ljk.org

    # 上传镜像
    root@u3:~# docker image push harbor.ljk.org/library/alpine:v1

docker 网络支持5种网络模式:none、bridge、host、container、network-net

创建新容器时,docker run命令可以指定网络模式:

1
2
3
4
5
6
7
8
docker run --network <mode>

# mode:默认是bridge
none
bridge
host
container:容器名或容器ID
自定义网络名称

bridge 模式

默认模式,容器连接到一个虚拟网桥与外界通信,通过SNAT访问外网,通过DNAT可以让容器被外部主机访问,所以此模式也称为NAT模式

此模式会启动宿主机的ip_forward功能

bridge模式特点:

  • 网络资源隔离:不同主机的容器之间无法直接通信
  • 因为NAT转换,所以性能较低
  • 端口管理繁琐,每个容器必须手动指定唯一的端口,容易产生端口冲突

修改默认的bridge模式网络配置:

方法一:修改docker.service

1
2
3
4
5
# docker0默认ip为172.17.0.1/16,修改为10.100.0.1/24
root@Z510:~# vim /usr/lib/systemd/system/docker.service
...
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --bip=10.100.0.1/24
...

方法二:修改daemon.json,推荐这种方法

1
2
3
4
5
6
7
8
9
10
root@Z510:~# vim /etc/docker/daemon.json
# hosts 指定docker守护进程侦听的地址,默认/var/run/docker.sock
"hosts": ["tcp://0.0.0.0:2375", "fd://"],
"bip": "192.168.100.100/24", # docker0网卡的IP
"fixed-cidr": "192.168.100.128/26", # 分配给容器的IP范围
"fixed-cidr-v6": "2001:db8::/64",
"mtu": 1500,
"default-gateway": "192.168.100.200", # 网关必须和bip在同一个网段
"default-gateway-v6": "2001:db8:abcd::89",
"dns": [ "1.1.1.1", "8.8.8.8"]

host 模式

容器和宿主机之间网络不隔离(其他资源隔离),容器不创建自己的虚拟网卡,而是使用宿主机的网卡和IP地址,因此在容器里面查看到的IP信息就是宿主机的信息,访问容器的时候直接使用宿主机IP+容器端口即可

此模式由于直接使用宿主机的网络无需转换,网络性能最高,但是各容器内使用的端口不能相同,适用于运行容器端口比较固定的业务

host 网络模式特点:

  • 共享宿主机网络
  • 网络性能无损耗
  • 网络故障排除相对简单
  • 各容器网络无隔离
  • 网络资源无法分别统计
  • 端口管理困难: 容易产生端口冲突
  • 不支持端口映射

范例:

1
docker run -d --network host --name web1 nginx-centos7-base:1.6.1

none 模式

容器不会进行任何网络配置,没有网卡、没有IP也没有路由,因此默认无法与外界通信,需要手动添加网卡配置IP等,所以极少使用

containter 模式

指定一个已经存在的容器(选择与其通信频繁的容器),共享其网络,因此这个容器的端口不能和被指定容器的端口冲突

这种模式也较少使用

自定义网络模式

1
2
3
4
5
6
7
8
9
10
11
# docker network --help
Usage: docker network COMMAND

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

自定义网络内的容器可以直接通过容器进行相互的访问

可以使用自定义网络模式,实现不同集群应用的独立网络管理,而互补影响,而且在一个网络内,可以直接利用容器名称相互访问,非常便利

创建自定义网络:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# docker network create --help
Usage: docker network create [OPTIONS] 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 copying 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
1
2
3
docker network create -d <mode> --subnet <CIDR> --gateway <网关> <自定义网络名称>

#注意mode不支持host和none

容器间通信

禁止同宿主机的不同容器间通信

docker.service

1
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --icc=false

daemon.json

1
"icc": false

容器名称互联

前文中,自定义网络模式,可以实现容器名称互联,如果是默认模式,想要实现这个功能,需要在创建容器时,使用–link选项

范例:实现a1和a2容器名称互联

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 1. 创建容器a1
root@Z510:~# docker container run -it --name a1 alpine:latest
/ # hostname -i
172.17.0.2
# 2. 创建容器a2
root@Z510:~# docker container run -it --name a2 --link a1 alpine:latest
/ # hostname -i
172.17.0.3
/ # cat /etc/hosts
...
172.17.0.2 a1 6079167b0ed1
172.17.0.3 b4e8abef6893
/ # ping a1 # a2可以ping通a1
PING a1 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.191 ms
...
# 3. 修改容器a1的hosts
/ # vi /etc/hosts
# 添加如下行
172.17.0.3 a2 b4e8abef6893
/ # ping a2 # a1可以ping通a2
PING a2 (172.17.0.3): 56 data bytes
64 bytes from 172.17.0.3: seq=0 ttl=64 time=0.135 ms
...

同宿主机之间不同网络的容器通信

同一个宿主机,创建两个容器,一个使用自定义网络,一个使用默认的bridge模式,默认这两个容器是无法通信的,如果想要实现通信,有两种方式:修改iptables规则 和 通过docker network connect实现

本笔记只记录第二种方式:

1
2
3
4
5
6
7
8
9
10
# docker network connect --help
Usage: docker network connect [OPTIONS] NETWORK CONTAINER

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

范例:默认docker0中的容器为test1,自定义网络test-net中的容器为test2

1
2
3
4
5
6
7
# 1. 创建网络和容器,过程略...
# 2. 让默认网络中容器test1可以连通自定义网络test-net的容器test2
docker network connect test-net test1
ip a # 可以看到新增一个网卡,ip和test-net同网段
# 3. 让自定义网络中容器test2可以连通默认网络的容器test1
docker network connect bridge test2
ip a # 可以看到新增一个网卡,ip和docker0同网段

docker容器创建后,必不可少的要和其它主机或容器进行网络通信

docker的网络模式和KVM很相似

docker 网络连接模式

跨宿主机的容器之间网络通信

四种方法:

  1. 桥接
  2. NAT
  3. Open vSwitch
  4. weave

生产中,以上四种都不用,而是通过k8s实现,如果真的用到了,再回来复习课件

案例:docker & LVS 实现网络架构高可用

整体规划图:

过程略..

docker镜像由多个只读层叠加而成,启动容器时,docker会加载只读镜像层并在镜像栈顶部添加一个读写层

所有的修改都被复制到读写层,保存在diff目录,原理就是“写时复制(COW)”

但是一旦删除容器,读写层也会被删除,所以为了数据持久化,需要将容器中的数据保存到宿主机的制定目录,方法就是使用数据卷(Data Volume):直接将宿主机目录挂载到容器的指定目录

数据卷

数据卷实际上就是宿主机上的目录或文件,可以直接mount到容器中使用

实际生产环境中,需要针对不同类型的服务,不同类型的数据存储要求做到相应的规划,最终保证服务的可扩展性、稳定性以及数据的安全性

使用场景:数据库、日志、静态web页面、应用配置文件、多容器间目录或文件共享

数据卷特点:

  • 可以多容器之间共享
  • 依赖宿主机目录,宿主机出问题,依赖的容器就会受影响,当宿主机较多时,不方便统一管理
  • 镜像中的挂载点中包含数据,则在容器初始化时会将数据拷贝到数据卷(匿名和命名)中

数据卷的使用

1
2
3
4
5
docker container run -v [host-src:]container-dest[:<options>]

options:
ro:容器内对此数据卷只读,代码等文件适合设置为只读
rw:容器内对此数据卷读写,默认
1
2
3
4
5
6
7
8
# 将宿主机目录挂载容器目录,两个目录都可自动创建
-v <宿主机绝对路径的目录或文件>:<容器目录或文件>[:ro]

# 匿名卷,宿主机自动生成/var/lib/docker/volumes/<卷ID>/_data目录,并挂载至容器指定路径
-v <容器内路径>

# 命名卷,宿主机自动生成/var/lib/docker/volumes/<卷名>/_data目录,并挂载至容器指定路径
-v <卷名>:<容器目录路径>

管理卷命令:

1
2
3
4
5
6
7
8
9
# docker volume --help
Usage: docker volume COMMAND

Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove all unused local volumes
rm Remove one or more volumes

关于匿名卷和命名卷:

命名卷,因为有名字可以指定,在用过一次后,以后挂载容器的时候还可以使用,所以一般需要保存的数据使用命名卷保存。

匿名卷没有名字,随容器建立而建立,当容器消亡,匿名卷即使还存在,但也失去了意义,因此匿名卷只存放无关紧要的临时数据。

Dockerfile中指定VOLUME为匿名数据卷,其目的只是为了将某个路径确定为卷。

数据卷默认可能会保存于 /var/lib/docker/volumes,不过一般不需要、也不应该访问这个位置。

按照最佳实践的要求,不应该在容器存储层内进行数据写入操作,所有写入应该使用卷。

1
2
3
4
# 查看数据卷的挂载关系
docker inspect --format="{{.Mounts}}" <容器ID>
# 删除所有数据卷
docker volume rm `docker volume ls -q`

案例

MySQL使用数据卷:

1
2
3
4
5
docker run -d \
--name mysql \
-p 3306:3306 \
-v /data/mysql/:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 mysql:5.7.30

文件数据卷:

1
2
3
4
5
docker run -d \
-v /data/bin/catalina.sh:/apps/tomcat/bin/catalina.sh:ro \
-v /data/testapp:/data/tomcat/webapps/testapp \
-v /data/logs:/apps/tomcat/logs \
-p 8080:8080 tomcat-web:app1

docker 镜像生命周期

制作镜像方法:

  • 手工制作(基于容器)
  • 自动制作(基于dockerfile),企业通常都是基于dockerfile制作镜像

手动构建镜像

将现有容器通过 docker commitdocker container commit 手动构建镜像

根据容器的更改创建新镜像:

1
2
3
4
5
6
7
8
# docker container commit --help
Usage: docker container commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

Options:
-a, --author string 作者 (e.g., "John Hannibal Smith <hannibal@a-team.com>")
-c, --change list 使用Dockerfile指令来创建镜像
-m, --message string 提交时的说明文字
-p, --pause 在commit时,将容器暂停 (default true)

说明:

  • 制作镜像和容器的状态无关,停止状态也可以制作
  • 如果没有指定[REPOSITORY[:TAG]],REPOSITORY和TAG都为<none>
  • 提交的时候标记TAG,后期可以根据TAG标记创建不同版本的镜像以及创建不同版本的容器

具体步骤

  1. 下载一个官方的基础镜像,例如:centos、ubuntu、alpine
  2. 基于基础镜像启动一个容器,并进入
  3. 在容器里面进行安装服务、修改配置等操作
  4. 提交一个新镜像 docker container commit
  5. 基于自己的镜像创建容器并访问、

案例:基于alpine基础镜像制作nginx镜像

  1. 下载最新版alpine基础镜像

    1
    root@Z510:~# docker pull alpine
  2. 启动alpine并进入

    1
    2
    root@Z510:~# docker container run -it alpine
    / #
  3. 另开一个终端,将shell脚本拷贝到alpine镜像

    1
    2
    3
    4
    5
    lujinkai@Z510:~/www/script$ sudo docker container ls
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    b8de1bdb6c88 alpine "/bin/sh" About a minute ago Up About a minute funny_chatelet

    lujinkai@Z510:~/www/script$ sudo docker container cp -a ./alpine-docker/ b8de:/root
  4. 回到容器,运行shell脚本

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    root@Z510:~# docker container run -it alpine
    / # cd
    ~ # ls
    alpine-docker
    ~ # cd alpine-docker/
    ~/alpine-docker # ./install.sh
    ....
    make[1]: Leaving directory '/root/alpine-docker/src/nginx-1.18.0'
    Nginx installed successfully!
    ~/alpine-docker # exit
  5. 提交镜像

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    lujinkai@Z510:~$ sudo docker container commit \
    -a 'lujinkai<root@lujinkai.com>' \
    -c 'EXPOSE 80' \
    -c 'CMD ["/usr/local/nginx/sbin/nginx"]' \
    b8de1bdb6c88 nginx-alpine:v1
    lujinkai@Z510:~$ sudo !!
    sudo docker image ls
    REPOSITORY TAG IMAGE ID CREATED SIZE
    nginx-alpine v1 de56d80c5c8c 14 seconds ago 340MB
    alpine latest d6e46aa2470d 4 weeks ago 5.57MB
  6. 启动基于新制作镜像的容器

    1
    2
    3
    4
    root@Z510:~# docker container run -d -p 80:80 nginx-alpine:v1 
    ebbe778a7fe303e6a5cb840203d95a7b5e2a07208365b71f784ddc8357a664d7
    root@Z510:~# curl 127.0.0.1:81
    hello nginx

  7. iptables

    1
    2
    3
    4
    5
    6
    root@Z510:~# iptables -t nat -nL
    ...
    Chain DOCKER (2 references)
    target prot opt source destination
    RETURN all -- anywhere anywhere
    DNAT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:81 to:172.17.0.2:80
  8. ss

    1
    2
    3
    4
    5
    root@Z510:~# ss -ntl
    State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
    ...
    LISTEN 0 4096 *:81 *:*
    ...
  9. 有需求的话,可以导出镜像:docker image save [OPTIONS] IMAGE

自动构建镜像

利用 DockerFile 文件执行 docker build 自动构建镜像

个人觉得还是手动的比较方便,然后在容器内无法完成的设置,docker container commit提交的时候设置-c参数即可

Dockerfile 相关指令

  • FROM
  • LABEL
  • RUN
  • ENV
  • COPY
  • ADD
  • CMD
  • ENTRYPOINT
  • ARG
  • VOLUME
  • EXPOSE
  • WORKDIR
  • ONBUILD
  • USER
  • HEALTHCHECK
  • STOPSIGNAL
  • SHELL

容器生命周期

容器管理docker container

启动容器 run

每次run,都会启动一个新的容器

1
2
# docker run [选项] [镜像名] [shell命令] [参数]
docker container run [OPTIONS] IMAGE [COMMAND] [ARG...]

COMMAND:一次性运行容器中命令

OPTIONS:

  • -i:Keep STDIN open even if not attached,通常和-t一起使用
  • -t:分配pseudo-TTY,通常和-i一起使用,注意对应的容器必须运行shell才支持进入
  • -d:后台运行容器,并返回容器ID,默认前台运行
  • –name string:启动容器时会自动随机字符作为容器名,–name指定容器名称
  • -h string:设置虚拟机内的hostname
  • –rm:退出后立即删除容器,一次性运行,用于测试
  • -p(小写) list:暴露指定端口映射,为避免宿主机端口冲突,要做好端口规划
  • -P(大写):暴露所有端口,随机,默认从32768开始
  • port:查看端口映
  • –dns list:Set custom DNS servers
  • –entrypoint string:Overwrite the default ENTRYPOINT of the image
  • –restart policy:
  • –privileged:赋予虚拟机内的root真正的root权限,有权限宿主机,没有特殊需求不要使用
  • -e=[]:传递环境变量
  • –env-file=[]:传递环境变量,文件
  • –restart string:容器的重启策略,面向生产环境的启动策略
    • no:默认策略,容器退出时不重启容器
    • on-failure:容器非正常退出时(退出状态非0),才会重启容器
    • on-failure:3:容器非正常退出时重启容器,最多重启3次
    • always:容器退出时总是重启容器,设置为always,则启动docker服务的时候会自动启动容器
    • unless-stopped:容器退出时总是重启容器,但是不考虑在docker守护进程启动时就已经停止了的容器

注意:容器启动后,如果容器内没有前台运行的进程,将自动退出停止

退出容器:exit 容器停止;ctrl+p+q 容器不停止

查看容器信息 ls、top、stats、inspect

  • 查看当前存在的容器
1
2
3
4
5
6
7
8
9
10
docker ps [OPTIONS]
docker container ls [OPTIONS]

选项:
-a, --all 查看所所有容器,包括退出状态的容器(默认只显示处于运行状态的容器)
-q, --quiet Only display numeric IDs
-s, --size 显示容器大小
-f, --filter 过滤,例如 `-f status=exited`
-l, --latest 显示最新创建的容器
-n, --last int Show n last created containers (includes all states)(default -1)
  • 查看容器内的进程
1
docker container top CONTAINER [ps OPTIONS]
  • 查看容器资源使用情况
1
2
3
4
5
6
7
8
# docker container stats --help
Usage: docker container stats [OPTIONS] [CONTAINER...]

Options:
-a, --all Show all containers (default shows just running)
--format string Pretty-print images using a Go template
--no-stream Disable streaming stats and only pull the first result
--no-trunc Do not truncate output
  • 查看容器的详细信息
1
2
3
4
5
6
# docker container inspect --help
Usage: docker container inspect [OPTIONS] CONTAINER [CONTAINER...]

Options:
-f, --format string Format the output using the given Go template
-s, --size Display total file sizes

范例:

1
2
3
docker inspect 9997
docker inspect -f "{{.Metadata}}" test:v1.0 # 选择性查看
docker inspect -f "{{.Created}}" c1

删除容器 rm

rm 删除容器,即使处于运行状态也被强制删除

prune 删除容器,只删除停止的容器

1
2
3
4
5
6
7
# docker container rm --help
Usage: docker container rm [OPTIONS] CONTAINER [CONTAINER...]

Options:
-f, --force Force the removal of a running container (uses SIGKILL)
-l, --link Remove the specified link
-v, --volumes 同时删除数据卷
1
2
3
4
5
6
# docker container prune --help
Usage: docker container prune [OPTIONS]

Options:
--filter filter Provide filter values (e.g. 'until=<timestamp>')
-f, --force 不提示确认信息

范例:删除指定状态的容器

1
docker rm `docker ps -qf status=exited`
1
alias rmc='docker container ls -aq | xargs -n1 docker container rm -f'

容器的启动和停止

1
docker start|stop|restart|pause|unpause 容器ID
  • start:启动容器,后台启动,并执行默认COMMAND
  • stop:关闭容器
  • restart:重启容器
  • pause:暂停容器中的所有进程
  • unpause:恢复容器中的进程

给正在运行的容器发信号 kill

默认信号 SIGKILL,即9信号

1
2
3
4
5
# docker container kill --help
Usage: docker container kill [OPTIONS] CONTAINER [CONTAINER...]

Options:
-s, --signal string Signal to send to the container (default "KILL")

进入正在运行的容器 exec

run -it 是新建容器,并进入,如果要进入正在运行的容器,推荐使用exec,此外 attach 也可以,但是不推荐

exec 的作用是在运行的容器中执行命令

1
2
3
4
5
6
7
8
9
10
11
12
# docker container exec --help
Usage: docker container exec [OPTIONS] CONTAINER COMMAND [ARG...]

Options:
-d, --detach Detached mode: run command in the background
--detach-keys string Override the key sequence for detaching a container
-e, --env list Set environment variables
-i, --interactive Keep STDIN open even if not attached
--privileged Give extended privileges to the command
-t, --tty Allocate a pseudo-TTY
-u, --user string Username or UID (format: <name|uid>[:<group|gid>])
-w, --workdir string Working directory inside the containe

常见用法:

1
2
#常见用法
docker exec -it 容器ID sh|bash # 进入容器,且exit退出但容器不停止

暴露端口

端口映射的本质就是利用NAT技术实现的

暴露所有端口,随机:大写P

1
docker container run -P

暴露指定端口:小写p

1
2
3
4
5
6
7
8
9
10
docker container run -p 

# 范例
docker run -p 80 --name nginx-test-port1 nginx # 容器80映射到宿主机随机端口
docker run -p 81:80 --name nginx-test-port2 nginx # 容器80端口映射到宿主机本地端口81
docker run -p 10.0.0.100:82:80 --name nginx-test-port3 docker.io/nginx # 指定宿主机ip
docker run -p 10.0.0.100::80 --name nginx-test-port4 docker.io/nginx
docker run -p 10.0.0.100:83:80/udp --name nginx-test-port5 docker.io/nginx #指定udp,默认tcp
# 一次性映射多个端口+协议
docker run -p 8080:80/tcp -p 8443:443/tcp -p 53:53/udp --name nginx-test-port6 nginx

查看容器日志 logs

查看容器中运行的进程在控制台输出的日志信息,如果容器中的进程不在控制台输出日志,那就没办法了,所以这种方法一般不用

1
2
3
4
5
6
7
8
9
10
# docker container logs --help
Usage: docker container logs [OPTIONS] CONTAINER

Options:
--details Show extra details provided to logs
-f, --follow Follow log output
--since string Show logs since timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)
--tail string Number of lines to show from the end of the logs (default "all")
-t, --timestamps Show timestamps
--until string Show logs before a timestamp (e.g. 2013-01-02T13:23:37) or relative (e.g. 42m for 42 minutes)

传递运行命令

docker run xxx启动容器,如果xxx容器中没有常驻前台的进程,xxx会认为没什么任务,在启动后马上退出,有的服务支持前台运行,有的不支持,如果服务不支持前台运行,我们可以启动一个常驻前台的进程,阻止容器推出,常用的方式是让容器运行tail -f /etc/hosts命令,注意不要监听频繁更改的文件,避免产生不必要的磁盘IO

范例:

1
2
3
4
5
6
# nginx前台运行,直接后台启动即可
docker container run -d nginx:1120
# alpine没有常驻前台的进程,以下两种方式均可
# 在容器内执行`/bin/sh`,然后把容器放到宿主机后台,sh是交互式命令,所以也有常驻前台的效果
docker container run -itd alpine
docker run -d alpine 'tail -f /etc/hosts'

指定容器DNS

默认采用宿主机的dns地址,可以在run启动容器时指定

范例:

1
2
3
4
5
root@Z510:~# docker container run -it --dns 1.1.1.1 --dns 8.8.8.8 alpine:1119 
/ # cat /etc/resolv.conf
search magedu.com
nameserver 1.1.1.1
nameserver 8.8.8.8

容器内和宿主机之间复制文件 cp

1
2
3
4
5
6
7
# docker container cp --help
Usage: docker container cp [OPTIONS] CONTAINER:SRC_PATH DEST_PATH
docker container cp [OPTIONS] SRC_PATH CONTAINER:DEST_PATH

Options:
-a, --archive Archive mode (copy all uid/gid information)
-L, --follow-link Always follow symbol link in SRC_PATH

使用systemd控制容器运行

范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# /lib/systemd/system/hello.service
[Unit]
Description=Hello World
After=docker.service
Requires=docker.service
[Service]
TimeoutStartSec=0
ExecStartPre=-/usr/bin/docker kill busybox-hello
ExecStartPre=-/usr/bin/docker rm busybox-hello
ExecStartPre=/usr/bin/docker pull busybox
ExecStart=/usr/bin/docker run --name busybox-hello busybox /bin/sh -c "while true; do echo Hello World; sleep 1; done"
ExecStop=/usr/bin/docker kill busybox-hello
[Install]
WantedBy=multi-user.target

传递环境变量

docker container -edocker container --env-file

范例:

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
docker run \
-d \
--name mysql-test1 \
-v /data/mysql:/var/lib/mysql \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=wordpress \
-e MYSQL_USER=wpuser \
-e MYSQL_PASSWORD=123456 \
mysql:5.7.30

# 或
docker run \
-d \
--name mysql-test2 \
-v /root/mysql/:/etc/mysql/conf.d \
-v /data/mysql2:/var/lib/mysql \
-p 3307:3306 \
--env-file=env.list \
mysql:5.7.30

cat env.list
MYSQL_ROOT_PASSWORD=123456
MYSQL_DATABASE=wordpress
MYSQL_USER=wpuser
MYSQL_PASSWORD=wppass

镜像结构和原理

镜像就是创建容器的模板,含有启动容器所需要的文件系统及所需要的内容,因此镜像主要用于方便和快速的创建并启动容器

镜像理由是一层一层的文件系统:Union FS(联合文件系统),可以将几层目录挂载到一起(类似俄罗斯套娃),形成一个虚拟文件系统,虚拟文件系统的目录结构就像普通linux目录结构一样,镜像通过这些文件,再加上宿主机的内核共同构成了一个linux的虚拟环境,每一层文件系统叫做一层layer,联合文件系统可以对每一层文件系统设置三种权限:readonly、readwrite、writeout-able。但是镜像中每一层文件系统都是只读的,构建镜像的时候,从一个最基本的操作系统开始,每个构建提交的操作都相当与做一层的修改,增加了一层文件系统,一层层往上叠加,上层的修改会覆盖底层该位置的可见性,当使用镜像的时候,我们只会看见一个整体,并不知道里面有几层,实际上也不需要知道

一个典型的Linux文件系统由bootfs和rootfs两部分组成

bootfs(boot file system)主要包含bootloader和kernel,bootloader主要用于引导加载kernel。系统刚启动时会加载bootfs文件系统,kernel加载到内存后,接管系统的控制权,bootfs就会被umount掉

rootfs(root file system)就是我们看到的/dev、/proc、/bin、/etc等目录和文件,不同的linux发行版本(如ubuntu和centos)主要在rootfs这一层会有所区别

一般的镜像通常都比较小,镜像直接调用宿主机的内核,镜像中只提供rootfs,也就是只需包括最基本的命令、配置文件和程序库等相关文件就可以了

容器、镜像和父镜像关系:

alpine 介绍

Alpine 操作系统是一个面向安全的轻型 Linux 发行版。它不同于通常 Linux 发行版,Alpine 采用了musl libc 和 busybox 以减小系统的体积和运行时资源消耗,但功能上比 busybox 又完善的多,因此得到开源社区越来越多的青睐。在保持瘦身的同时,Alpine 还提供了自己的包管理工具 apk,可以通过https://pkgs.alpinelinux.org/packages 网站上查询包信息,也可以直接通过 apk 命令直接查询和安装各种软件。

Alpine 由非商业组织维护的,支持广泛场景的 Linux发行版,它特别为资深/重度Linux用户而优化,关注安全,性能和资源效能。Alpine 镜像可以适用于更多常用场景,并且是一个优秀的可以适用于生产的基础系统/环境。

Alpine Docker 镜像也继承了 Alpine Linux 发行版的这些优势。相比于其他 Docker 镜像,它的容量非常小,仅仅只有 5 MB 左右(对比 Ubuntu 系列镜像接近 200 MB),且拥有非常友好的包管理机制。官方镜像来自 docker-alpine 项目。

目前 Docker 官方已开始推荐使用 Alpine 替代之前的 Ubuntu 做为基础镜像环境。这样会带来多个好处。包括镜像下载速度加快,镜像安全性提高,主机之间的切换更方便,占用更少磁盘空间等。

1
2
3
4
5
6
7
8
9
10
11
12
#修改源替换成阿里源:将/etc/apk/repositories中的 dl-cdn.alpinelinux.org 改成 mirrors.aliyun.com
sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/' /etc/apk/repositories
#更新源
apk update
# 查找可用软件包
apk search [vim]
# 列出软件信息
apk info [vim]
#安装软件
apk add vim
#删除软件
apk del openssh openntp vim

搜索镜像

如果联网下载,一般不会使用命令搜索,而是去官网搜索

docker镜像加速

登录阿里云 –> 容器镜像服务 –> 镜像中心 –> 镜像加速器

1
2
3
4
5
6
7
8
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://3417nt4m.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

docker image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# docker help image
Usage: docker image COMMAND

Commands:
build Build an image from a Dockerfile
history Show the history of an image
import Import the contents from a tarball to create a filesystem image
inspect Display detailed information on one or more images
load Load an image from a tar archive or STDIN
ls List images
prune Remove unused images
pull Pull an image or a repository from a registry
push Push an image or a repository to a registry
rm Remove one or more images
save Save one or more images to a tar archive (streamed to STDOUT by default)
tag Create a tag TARGET_IMAGE that refers to SOURCE_IMAGE

下载镜像 pull

1
2
3
4
5
6
7
8
# docker help pull
docker pull [OPTIONS] NAME[:TAG|@DIGEST]

Options:
-a, --all-tags Download all tagged images in the repository
--disable-content-trust Skip image verification (default true)
--platform string Set platform if server is multi-platform capable
-q, --quiet Suppress verbose output
  • NAME:镜像名,仓库服务器:端口/项目名称/镜像名称
  • :TAG:版本号,如果不指定:TAG,则下载最新版镜像
1
2
3
4
5
6
7
[root@ubuntu1804 ~]#docker pull hello-world
Using default tag: latest #默认下载最新版本
latest: Pulling from library/hello-world
1b930d010525: Pull complete #分层下载
Digest: sha256:9572f7cdcee8591948c2963463447a53466950b3fc15a247fcad1917ca215a2f #摘要
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest #下载的完整地址

下载后的镜像位于:

1
/var/lib/docker/overlay2

查看本地镜像 ls

1
2
3
4
5
6
7
8
9
docker image ls [OPTIONS] [REPOSITORY[:TAG]]

Options:
-a, --all Show all images (default hides intermediate images)
--digests Show digests
-f, --filter filter Filter output based on conditions provided
--format string Pretty-print images using a Go template
--no-trunc Don't truncate output
-q, --quiet Only show numeric IDs

镜像导出 save

将本地镜像到处为一个文件(tar格式),然后复制到其他服务器,导入使用

1
2
3
4
5
# docker help image save
Usage: docker image save [OPTIONS] IMAGE [IMAGE...]

Options:
-o, --output string Write to a file, instead of STDOUT

常见用法:以下两种用法等价

1
2
docker save -o /path/file.tar IMAGE1 IMAGE2 ...
docker save IMAGE1 IMAGE2 ... > /path/file.tar

镜像导入 load

docker image save 导出的镜像压缩文件再导入

1
2
3
4
5
6
7
# docker help image load

Usage: docker image load [OPTIONS]

Options:
-i, --input string Read from tar archive file, instead of STDIN
-q, --quiet Suppress the load output

常见用法:以下两种用法等价

1
2
docker load -i /data/myimages.tar
docker load < /data/myimages.tar

删除镜像 rm

删除本地镜像

1
2
3
4
5
6
7
8
9
# docker help image rm
Usage: docker image rm [OPTIONS] IMAGE [IMAGE...]

Aliases:
rm, rmi, remove

Options:
-f, --force Force removal of the image # 强制删除
--no-prune Do not delete untagged parents
1
alias rmi='docker image ls -aq | xargs -n1 docker image rm'

镜像打标签 tag

给镜像打标签,类似于起别名,但通常要遵守一定的命名规范,才可以上传到指定的仓库

1
docker image tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]

TARGET_IMAGE[:TAG]格式一般为:仓库主机FQDN或IP[:端口]/项目名(或用户名)/image名字:版本

tag 默认为 latest

先tag给镜像打标签,然后再save导出镜像,拷贝到其他机器,其他机器可以load导入

安装和删除方法

官方文档:https://docs.docker.com/engine/install/
ubuntu:https://docs.docker.com/engine/install/ubuntu/
centos:https://docs.docker.com/install/linux/docker-ce/centos/
阿里云文档:https://developer.aliyun.com/mirror/docker-ce

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# ubuntuu
$ sudo apt-get update
$ sudo apt-get -y install \
apt-transport-https \
ca-certificates \
curl \
gnupg-agent \
software-properties-common
$ curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository \
"deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
$(lsb_release -cs) \
stable"
$ sudo apt-get update
$ sudo apt -y install docker-ce

# 以上是直接安装最新版本,推荐安装时指定版本
$ apt-cache madison docker-ce
$ sudo apt -y install docker-ce=<VERSION_STRING>

二进制安装:https://docs.docker.com/install/linux/docker-ce/binaries/
https://mirrors.aliyun.com/docker-ce/linux/static/stable/x86_64/

配置文件

docker-ce 配置文件:

1
/etc/docker/daemon.json

docker registry 配置文件:

1
2
3
4
# ubuntu,来自containerd.io软件包
/etc/containerd/config.toml
# centos
/etc/containers/registries.conf

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
lujinkai@Z510:~$ sudo docker system info
Client:
Debug Mode: false #client 端是否开启 debug

Server:
Containers: 3 #当前主机运行的容器总数
Running: 0 #有几个容器是正在运行的
Paused: 0 #有几个容器是暂停的
Stopped: 3 #有几个容器是停止的
Images: 4 #当前服务器的镜像数
Server Version: 19.03.13 #服务端版本
Storage Driver: overlay2 #正在使用的存储引擎
Backing Filesystem: extfs #后端文件系统,即服务器的磁盘文件系统
Supports d_type: true #是否支持 d_type
Native Overlay Diff: true #是否支持差异数据存储
Logging Driver: json-file #日志类型
Cgroup Driver: cgroupfs #Cgroups 类型
Plugins: #插件
Volume: local #卷
Network: bridge host ipvlan macvlan null overlay # overlay 跨主机通信
#日志类型
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive #是否支持 swarm
Runtimes: runc #已安装的runtime
Default Runtime: runc #默认使用的容器runtime
Init Binary: docker-init #初始化容器的守护进程,即 pid 为 1 的进程
containerd version: 8fba4e9a7d01810a393d5d25a3621dc101981175 #版本
runc version: dc9208a3303feef5b3839f4323d9beb36df0a9dd #runc 版本
init version: fec3683 #init 版本
Security Options: #安全选项
apparmor #安全模块,https://docs.docker.com/engine/security/apparmor/
seccomp #安全计算模块,即制容器操作,https://docs.docker.com/engine/security/seccomp/
Profile: default #默认的配置文件
Kernel Version: 5.4.0-53-generic #宿主机内核版本
Operating System: Ubuntu 20.04.1 LTS #宿主机操作系统
OSType: linux #宿主机操作系统类型
Architecture: x86_64 #宿主机架构
CPUs: 4 #宿主机 CPU 数量
Total Memory: 15.56GiB #宿主机总内存
Name: Z510 #宿主机 hostname
ID: 7FJX:7VKN:2YGG:VKRB:5KKA:DB7C:KNLC:UW2N:XH3E:EVEG:OSQJ:EEKV #宿主机 ID
Docker Root Dir: /var/lib/docker #宿主机关于docker数据的保存目录
Debug Mode: false #server 端是否开启 debug
Registry: https://index.docker.io/v1/ #仓库路径
Labels:
Experimental: false #是否测试版
Insecure Registries:
127.0.0.0/8 #非安全的镜像仓库
Registry Mirrors:
https://3417nt4m.mirror.aliyuncs.com/ #镜像仓库
Live Restore Enabled: false #是否开启活动重启 (重启docker-daemon 不关闭容器 )

WARNING: No swap limit support #系统警告信息 (没有开启 swap 资源限制 )

解决上述SWAP报警提示:

1
2
3
4
5
6
7
# vim /etc/default/grub
...
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0 swapaccount=1" #修改此行
...

sudo update-grub
sudo reboot

docker0 网卡

在docker安装启动之后,默认会生成一个名称为docker0的网卡,默认IP地址为172.17.0.1的网卡

docker 存储引擎

docker官方推荐首选存储引擎为overlay2,需要磁盘分区支持d-type功能

docker 服务进程

docker相关的四个进程:

  • dockerd:服务端程序,被client直接访问,父进程为宿主机的systemd守护进程
  • docker-proxy:每个进程docker-proxy实现对应一个需要网络通信的容器,管理宿主机和容器的之间端口映射,其父进程为dockerd,如果容器不需要网络则无需启动
  • containerd:被dockerd进程调用以实现与runc交互
  • containerd-shim:真正运行容器的载体,每个容器对应一个containerd-shim进程,其父进程为containerd

containerd-shim 命令

容器的创建与管理过程

  1. dockerd通过grpc和containerd模块通信,由libcontainerd负责,通信的socket:/run/containerd/containerd.sock
  2. containerd 在 dockerd 启动时被启动,然后containerd启动grpc请求监听,containerd处理请求,根据请求作出相应动作
  3. run/start/exec容器,containerd拉起一个container-shim,并进行相应的操作
  4. container-shim被拉起后,start/exec/create拉起runc进程,通过exit、control文件和containerd通信,通过父子进程关系和sigchld监控容器中进程状态
  5. 在整个容器生命周期中,container通过epoll监控容器文件、事件

gRPC

gPRC是谷歌开发的一款高性能、开源和通用的RPC框架,支持众多语言客户端

docker 服务管理

docker 服务基于 C/S 架构,可以实现基于本地和远程方式进行管理

范例:docker服务添加标签

1
2
3
4
5
6
7
8
9
vim /lib/systemd/system/docker.service
# 修改如下行
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --label="name=docker1"

docker info
...
Labels:
name=docker1 #此处显示添加的标签
...

docker的主要目标:一次封装,到处运行

容器和虚拟机技术比较:

  • 容器内的应用直接运行在宿主机的内核之上,容器并没有自己的内核,也不需要虚拟硬件,相当轻量化
  • 容器间是互相隔离,每个容器内都有一个属于自己的独立文件系统、进程空间、网络空间、用户空间等,所以在同一个宿主机上的多个容器之间彼此不会相互影响

docker 的组成

docker 官网:http://www.docker.com
帮助文档:https://docs.docker.com/
docker 镜像:https://hub.docker.com/
docker 中文网站:http://www.docker.org.cn/

镜像和容器:

镜像可以理解为是一个模板,创建实例使用的模板,本质就是一些程序文件的合集
容器是从镜像生成对外提供服务的一个或一组服务,本质就是将镜像中的程序启动后生成的进程

namespace

命名空间,linux底层概念,在内核层实现,容器使用namespce技术实现容器间相互隔离

  • mnt namespace:隔离根,即chroot技术
  • ipc namespace:隔离进程间通信
  • uts namespace:隔离系统信息
  • pid namespace:隔离进程号
  • net namespace:隔离网络
  • user namespce:隔离用户

Cgroups

Cgroups:Control Groups

Cgroups 最主要的作用,就是限制一个进程组能够使用的资源上限,包括CPU、内存、磁盘、网络带宽等等。此外,还能够对进程进行优先级设置,资源的计量以及资源的控制(比如 将进程挂起和恢复等操作)

利用Cgroups,宿主机可以对容器进行资源分配限制,比如CPU、内存等

容器核心技术

容器规范

容器除了docker,还有rkt、Pouch等,所有容器都要遵守标准的容器规范,这个规范由一个叫OCI的组织定,目前OCI一共发布了两个规范,分别是 runtime specimage fortmat spec,只要遵守这两个规范,就可以保证容器的可移植性和相互可操作性。

容器 runtime

runtime是真正运行容器的地方,因此为了运行不同的容器,runtime需要和操作系统内核紧密合作、相互支持,以便为容器提供相应的运行环境

runtime类型:lxc、libcontainer、runc、rkt

docker早期的runtime类型是lxc,现在是runc

容器管理工具

管理工具连接runtime与用户,对用户提供图形或命令方式操作,然后管理工具将用户操作传递给runtime执行

  • lxc 的 管理工具是 lxc
  • rkt 的 管理工具是 rkt cli
  • runc 的管理工具是 docker engine,docker engine包含daemon和cli两部分额,大家经常提到的docker就是docker engine

容器定义工具

容器定义工具允许用户定义容器的属性和内容,以方便容器能够被保存、共享和重建

docker image:是docker容器的模板,runtime依据docker image创建容器

dockerfile:包含N个命令的文本文件,通过dockerfile创建出docker image

镜像仓库 Registry

  • docker hub:docker官方的公共仓库,已经保存了大量的常用镜像,可以方便大家直接使用
  • 阿里云、网易等第三方镜像的公共仓库
  • image registry:docker 官方提供的私有仓库部署工具,无web管理界面,目前使用较少
  • harbor:vmware 提供的自带web界面自带认证功能的镜像私有仓库,目前有很多公司使用

容器编排工具

  • docker compose:docker 官方实现单机的容器的编排工具
  • docker swarm:docker 官方开发的容器编排引擎
  • Mesos+Marathon:Mesos是Apache下的开源分布式资源管理框架,它被称为是分布式系统的内核。Mesos最初是由加州大学伯克利分校的AMPLab开发的,后在Twitter得到广泛使用。通用的集群组员调度平台,mesos(资源分配)与marathon(容器编排平台)一起提供容器编排引擎功能
  • Kubernetes:google主导开发的容器编排引擎,内部项目为Borg,且其同时支持 docker 和CoreOS,当前已成为容器编排工具事实上的标准

JumpServer简介

跳板机 和 堡垒机

跳板机:跳板机就是一台服务器,维护人员在维护过程中,首先要统一登录到这台服务器上,然后从这台服务器再登录到目标设备进行维护。但跳板机没有实现对运维人员操作行为的控制和审计,此外,跳板机存在严重的安全风险,一旦跳板机系统被攻入,则将后端资源风险完全暴露无遗。安装OpenVPN的机器就是跳板机

堡垒机:跳板机的升级版,提供了认证、授权、审计、自动化运维等功能

可以同时配置openvpn和jumpserver,运维自己通过openvpn连接,比较方便,开发测试等其他人员都通过jumpserver连接,便于管理

JumpServer 是全球首款完全开源的堡垒机,使用 Python / Django、Go 进行开发,符合4A 标准

官方地址: http://www.jumpserver.org/
github项目: https://github.com/jumpserver

特色优势:

  • 开源
  • 分布式
  • 无插件:仅需浏览器,极致的 Web Terminal 使用体验;
  • 多云系统:一套系统,同时管理不同云上面的资产;
  • 云端存储
  • 多租户:一套系统,多个子公司和部门同时使用。

功能列表

https://docs.jumpserver.org/zh/master/#_3,其中部分功能商业版才具有

  • 身份鉴别

    Authentication,防止身份冒用和复用

  • 账号管理

    Account,人员和资产管理

  • 授权控制

    Authorization,防止内部误操作和权限滥用

  • 安全审计

    Audit,追溯的保障和事故分析的依据

  • 数据库审计

    Database

JumpServer 组成

安装 JumpServer

文档只介绍了极速部署和负载均衡两种安装方式,其实以前的文档是很详细的,现在大大简化了,可能是为了让你买商业版吧

jumpserver依赖MySQL(5.7及以上)和Redis(5.0及以上),这里为了方便,均使用docker安装

安装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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
root@u1:~# docker pull mysql:5.7.32		# pull mysql镜像,指定版本号5.7.32
# 在宿主机准备相关目录
root@u1:~# mkdir -p /usr/local/jumpserver/etc/mysql/{conf.d,mysql.conf.d}
root@u1:~# mkdir -p /data/jumpserver/mysql
# 生成配置文件,指定字符集
root@u1:~# tee /usr/local/jumpserver/etc/mysql/conf.d/mysql.cnf <<EOF
[mysql]
default-character-set=utf8 # 指定字符集
EOF
root@u1:~# tee /usr/local/jumpserver/etc/mysql/mysql.conf.d/mysqld.cnf <<EOF
[mysqld]
pid-file= /var/run/mysqld/mysqld.pid
socket= /var/run/mysqld/mysqld.sock
datadir= /var/lib/mysql
symbolic-links=0
character-set-server=utf8 # 指定字符集
EOF
root@u1:~# tree /usr/local/jumpserver/etc/mysql/
/usr/local/jumpserver/etc/mysql/
├── conf.d
│   └── mysql.cnf
└── mysql.conf.d
└── mysqld.cnf

2 directories, 2 files
# 启动容器
root@u1:~# docker container run -d -it -p 3306:3306 --name mysql --restart always \
-e MYSQL_ROOT_PASSWORD=123456 \
-e MYSQL_DATABASE=jumpserver \
-e MYSQL_USER=jumpserver \
-e MYSQL_PASSWORD=123456 \
-v /data/jumpserver/mysql:/var/lib/mysql \
-v /usr/local/jumpserver/etc/mysql/mysql.conf.d/mysqld.cnf:/etc/mysql/mysql.conf.d/mysqld.cnf \
-v /usr/local/jumpserver/etc/mysql/conf.d/mysql.cnf:/etc/mysql/conf.d/mysql.cnf \
mysql:5.7.32

# 添加root'@'127.0.0.1用户
root@u1:~# docker exec -it mysql bash
ot@829aa7ccf010:/# mysql -uroot -p123456 -e "CREATE USER 'root'@'127.0.0.1' IDENTIFIED BY \"123456\";"
root@829aa7ccf010:/# mysql -uroot -p123456 -e "GRANT ALL ON *.* TO 'root'@'127.0.0.1';"

安装Redis

1
2
3
4
5
6
7
8
9
10
11
12
# 拉取镜像
root@u1:~# docker pull redis:5.0.10-alpine
# 启动容器
root@u1:~# docker run -d -p 6379:6379 --name redis --restart always redis:5.0.10-alpine

# 另开一台虚拟机,验证redis是否安装成功
[root@c71 ~]$yum install redis -y
[root@c71 ~]$redis-cli -h 10.0.0.118
10.0.0.118:6379> info server
# Server
redis_version:5.0.10
...

部署JumpServer

可以单独部署各个组件,为了方便,直接部署全部:jms_all

生成 key 和 token:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
root@u1:/data/wwwroot/test# cat key.sh 
#!/bin/bash
if [ ! "$SECRET_KEY" ]; then
SECRET_KEY=$(cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 50)
echo "SECRET_KEY=$SECRET_KEY" >>~/.bashrc
echo $SECRET_KEY
else
echo $SECRET_KEY
fi
if [ ! "$BOOTSTRAP_TOKEN" ]; then
BOOTSTRAP_TOKEN=$(cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 16)
echo "BOOTSTRAP_TOKEN=$BOOTSTRAP_TOKEN" >>~/.bashrc
echo $BOOTSTRAP_TOKEN
else
echo $BOOTSTRAP_TOKEN
fi
root@u1:/data/wwwroot/test# ./key.sh
4NrDPj3azajRYQ6B4MM9pi559G1l3JPOW4i8LkUblSKUM7jojL # SECRET_KEY
p4ecZLj7Q2QTLs1s # BOOTSTRAP_TOKEN
root@u1:/data/wwwroot/test# tail -n2 ~/.bashrc
SECRET_KEY=4NrDPj3azajRYQ6B4MM9pi559G1l3JPOW4i8LkUblSKUM7jojL
BOOTSTRAP_TOKEN=p4ecZLj7Q2QTLs1s
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
# 拉取镜像,官方给的docker镜像版本较低
root@u1:~# docker pull jumpserver/jms_all:v2.5.3
# 启动容器
root@u1:~# docker run -d --name jms_all -p 80:80 -p 2222:2222 --restart always \
-v /opt/jumpserver/data:/opt/jumpserver/data \
-e SECRET_KEY=4NrDPj3azajRYQ6B4MM9pi559G1l3JPOW4i8LkUblSKUM7jojL \
-e BOOTSTRAP_TOKEN=p4ecZLj7Q2QTLs1s \
-e DB_HOST=172.17.0.1 \
-e DB_PORT=3306 \
-e DB_USER=root \
-e DB_PASSWORD=123456 \
-e DB_NAME=jumpserver \
-e REDIS_HOST=172.17.0.1 \
-e REDIS_PORT=6379 \
-e REDIS_PASSWORD='' \
--privileged=true \
jumpserver/jms_all:v2.5.3

# 验证启动是否成功
root@u1:~# docker logs -f jms_all
2021-01-09 11:44:43 Sat Jan 9 11:44:43 2021
2021-01-09 11:44:43 JumpServer version v2.5.3, more see https://www.jumpserver.org
2021-01-09 11:44:43 Check database connection ...
...
2021-01-09 11:44:49 Database connect success
2021-01-09 11:44:49 Check database structure change ...
2021-01-09 11:44:49 Migrate model change to database ...
Operations to perform:
Apply all migrations: admin, applications, assets, audits, auth, authentication, captcha, common, contenttypes, django_cas_ng, django_celery_beat, jms_oidc_rp, ops, orgs, perms, sessions, settings, terminal, tickets, users
Running migrations:
...各种OK...
2021-01-09 11:45:16 Collect static files
2021-01-09 11:45:19 Collect static files done
guacd[88]: INFO: Guacamole proxy daemon (guacd) version 1.2.0 started
Starting guacd: SUCCESS
Tomcat started.
Jumpserver ALL v2.5.3
官网 http://www.jumpserver.org
文档 http://docs.jumpserver.org

进入容器命令 docker exec -it jms_all /bin/bash
root@u1:~# ll /data/jumpserver/mysql/jumpserver/
total 15648
... # 生成的各种表

通过80端口登录:浏览器输入 10.0.0.118

默认账号密码都是admin,第一次登录需要修改密码,修改密码为123456

通过2222端口登录:另开一台虚拟机

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@c71 ~]$ssh -p 2222 admin@10.0.0.118
admin@10.0.0.118's password: # 123456
Administrator, 欢迎使用JumpServer开源堡垒机系统

1) 输入 部分IP,主机名,备注 进行搜索登录(如果唯一).
2) 输入 / + IP,主机名,备注 进行搜索,如:/192.168.
3) 输入 p 进行显示您有权限的主机.
4) 输入 g 进行显示您有权限的节点.
5) 输入 d 进行显示您有权限的数据库.
6) 输入 k 进行显示您有权限的Kubernetes.
7) 输入 r 进行刷新最新的机器和节点信息.
8) 输入 h 进行显示帮助.
9) 输入 q 进行退出.
Opt>

使用 JumpServer

配置邮件

系统设置 -> 邮件设置:

用户管理

多因子认证

创建用户的时候,可以选择开启多因子认证功能,就是需要安装手机APP谷歌验证器,在登录的时候,打开谷歌验证器输入验证码,二次验证,加强安全性

注意:启用,未来用户可以自己关闭;强制启用,未来用户不可以强制关闭

注意:服务器的时间需要校准,否则谷歌验证器无法通过验证

1
timedatectl set-timezone Asia/Shanghai	# 校准服务器时间

统审计员

系统审计员即查看录像带的人,可以实时共享其他用户的终端,可以以录像的方式查看用户的操作历史

管理资产

jumpserver内置了ansible

注意:ubuntu默认不能使用root进行ssh连接,需要修改ssh配置

1
2
3
4
lujinkai@u1:~$ sudo vim /etc/ssh/sshd_config
PermitRootLogin yes # 在 #PermitRootLogin prohibit-password 下面添加此行
UseDNS no # 加速ssh连接速度
lujinkai@u1:~$ sudo systemctl restart sshd.service # 重启服务

管理用户

管理用户是资产(被控服务器)上的 root,或拥有 NOPASSWD: ALL sudo 权限的用户, JumpServer 使用该用户来 推送系统用户获取资产硬件信息 等。

系统用户

用户使用自己的用户名登录 JumpServer,JumpServer 使用系统用户登录资产和应用

命令过滤

只能是间接禁止,因为虽然禁止的命令不能使用,但是可以通过脚本执行

权限管理

资产授权

有了用户,有了资产,就需要将用户和资产关联起来

应用管理

数据库

以mysql为例:

  1. 应用管理 –> 数据库 –> 创建
  2. 权限管理 –> 系统用户 –> 创建,协议选mysql
  3. 权限管理 –> 应用授权 –> 创建

注意:jumpserver2.5.3版本有bug,web终端无法连接数据库,建议选择其他版本,v2.4.4没有此bug

会话管理

所有用户的所有行为都会被jumpserver以录像的形式记录下来,方便在将来出现问题的时候进行追责

web架构介绍

单机房架构

多机房架构

公有云架构

私有云架构

负载均衡

负载均衡:Load Balance,简称LB

负载均衡类型

  • 四层(传输层):LVS、Nginx(1.9之后)、HAProxy

    LVS是集成在内核中的功能,真四层;Nginx和HAProxy是用户空间的软件,伪四层

  • 七层(应用层):Nginx、HAProxy

  • 硬件:F5NetscalerArray深信服北京灵州

    大公司都用F5

应用场景

企业生产环境中,每天会有很多的需求变更,比如增加服务器、新业务上线、url路由修改、域名配置等等,对于前端负载均衡设备来说,容易维护,复杂度低,是首选指标

LVS性能最高,最稳定,但是只支持四层负载 ,百万级并发

HAProxy四层负载和七层代理都支持,如果只做反向代理,推荐HAProxy,功能丰富,方便管理,万级并发

Nginx的功能基于模块实现,HAProxy有的功能Nginx都有,小型系统可以使用Nginx

HAProxy

C语言开发,高性能的TCP和HTTP负载均衡器,支持基于cookie的持久性,自动故障切换,支持正则表达式及web状态统计,目前最新TLS版本为2.2

分为企业版和社区版

  • TCP 和 HTTP反向代理
  • SSL/TSL服务器
  • 可以针对HTTP请求添加cookie,进行路由后端服务器
  • 可平衡负载至后端服务器,并支持持久连接
  • 支持所有主服务器故障切换至备用服务器
  • 支持专用端口实现监控服务
  • 支持停止接受新连接请求,而不影响现有连接
  • 可以在双向添加,修改或删除HTTP报文首部
  • 响应报文压缩
  • 支持基于pattern实现连接请求的访问控制
  • 通过特定的URI为授权用户提供详细的状态信息

HAProxy安装

yum或者apt安装的版本较老,推荐编译安装

HAProxy支持基于Lua实现功能扩展

编译安装Lua

1
2
3
4
5
6
wget http://www.lua.org/ftp/lua-5.4.2.tar.gz
mkdir /usr/local/lua
tar zxvf lua-5.4.2.tar.gz -C /usr/local/lua --strip-components 1
cd /usr/local/lua
make all test
./src/lua -v # Lua 5.4.2 Copyright (C) 1994-2020 Lua.org, PUC-Rio

编译安装HAProxy

http://www.haproxy.org/ # 优先下载LTS版

http://www.haproxy.org/download/

1
2
3
4
5
6
7
8
9
10
11
12
13
wget http://www.haproxy.org/download/2.2/src/haproxy-2.2.6.tar.gz
yum -y install gcc openssl-devel pcre-devel systemd-devel
mkdir /usr/local/haproxy
tar zxvf haproxy-2.2.6.tar.gz
cd haproxy-2.2.6/
# 参考INSTALL文件进行编译安装
make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_LUA=1 LUA_INC=/usr/local/lua/src/ LUA_LIB=/usr/local/lua/src/
make install PREFIX=/usr/local/haproxy
vim /etc/profile # export PATH=/usr/local/haproxy/sbin:$PATH
. /etc/profile
haproxy -v
haproxy -V
haproxy -vv

配置文件

查看配置文件示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@c71 ~]$tree /usr/local/src/haproxy-2.2.6/examples/
/usr/local/src/haproxy-2.2.6/examples/
├── acl-content-sw.cfg
├── content-sw-sample.cfg
├── errorfiles
│   ├── 400.http
│   ├── 403.http
│   ├── 408.http
│   ├── 500.http
│   ├── 502.http
│   ├── 503.http
│   ├── 504.http
│   └── README
├── haproxy.init
├── option-http_proxy.cfg
├── socks4.cfg
├── transparent_proxy.cfg
└── wurfl-example.cfg

1 directory, 15 files

创建自定义的配置文件:

HAProxy的配置文件haproxy.cfg由两大部分组成,分别是global和proxies部分

  • global:全局配置段

    进程及安全配置相关的参数、性能调整相关参数、Debug参数

  • proxies:代理配置段

    • defaults [<name>]:默认配置项,针对以下的frontend、backend和listen生效,可以多个name也可以没有name
    • frontend <name>:前端,类似于Nginx的server和LVS的服务集群
    • backend <name>:后端,相当于nginx的upstream和LVS中的RS
    • listen <name>:同时拥有前端和后端配置,配置简单,通常只用于TCP协议的应用,适合简单的环境,生产推荐使用

以下列举的是部分配置,官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html

global

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[root@c71 ~]$mkdir /var/lib/haproxy
[root@c71 ~]$mkdir /etc/haproxy
[root@c71 ~]$vim /etc/haproxy/haproxy.cfg
global
chroot /usr/local/haproxy # 锁定运行目录
stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin # socket文件
#uid 99
#gid 99
user haproxy # 运行haproxy的用户
group haproxy # 运行haproxy的用户组
daemon
nbproc 1 # 启的haproxy work 进程数,默认进程数是一个
# nbthread 1 # 指定每个haproxy进程开启的线程数,默认为每个进程一个线程,和nbproc配置互斥
#cpu-map 1 0 # 绑定haproxy worker 进程至指定CPU,将第1个work进程绑定至0号CPU
#cpu-map 2 1 # 绑定haproxy worker 进程至指定CPU,将第2个work进程绑定至1号CPU
#cpu-map 3 2
#cpu-map 4 3
maxconn 100000 # 每个haproxy进程的最大并发连接数
# maxsslconn n # 每个haproxy进程ssl最大连接数,用于haproxy配置了证书的场景下
# maxconnrate n # 每个进程每秒创建的最大连接数量
# spread-checks n # 后端server状态check随机提前或延迟百分比时间,建议2-5(20%-50%)之间,默认0
pidfile /var/lib/haproxy/haproxy.pid # 指定pid文件路径
# log 127.0.0.1 local2 info # 全局的syslog服务器;日志服务器需要开启UDP协议,最多可以定义两个

HAproxy本身不记录客户端的访问日志,此外为减少服务器负载,一般生产中HAProxy不记录日志

defaults

1
2
3
4
5
6
7
8
9
10
11
12
defaults
option redispatch # 当server Id对应的服务器挂掉后,强制定向到其他健康的服务器,重新派发
option abortonclose # 服务器负载很高时,自动结束当前队列处理比较久的连接,针对业务情况选择开启
option http-keep-alive # 开启与客户端的会话保持
option forwardfor # 透传客户端真实IP至后端web服务器
maxconn 100000
mode http # http|tcp,设置默认工作类型,使用TCP服务器性能更好,减少压力
timeout connect 120s # 客户端请求从haproxy到后端server最长连接等待时间(TCP连接之前),默认单位ms
timeout server 120s # 客户端请求从haproxy到后端服务端的请求处理超时时长(TCP连接之后),默认单位ms,如果超时,会出现502错误,此值建议设置较大些,访止502错误
timeout client 120s # 设置haproxy与客户端的最长非活动时间,默认单位ms,建议和timeoutserver相同
timeout check 5s # 对后端服务器的默认检测超时时间
default-server inter 1000 weight 3 # 指定后端服务器的默认设置

default-server指定后端服务器的默认配置,以下是主要配置项:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 对指定real server进行健康状态检查,默认不开启检查,只有check后面没有其它配置也可以启用检查功能
# 默认对相应的后端服务器IP和端口,利用TCP连接进行周期性健康性检查,注意必须指定端口才能实现健康性检查
check
addr <IP> # 健康状态监测IP,可以是专门的数据网段,减少业务网络的流量
port <num> # 健康状态监测端口
inter <num> # 健康状态检查间隔时间,默认2000 ms
fall <num> # 后端服务器从线上转为线下的检查的连续失效次数,默认为3
rise <num> # 后端服务器从下线恢复上线的检查的连续有效次数,默认为2
# 动态修改权重,默认为1,最大值为256,0(状态为蓝色)表示不参与负载均衡,但仍接受持久连接
weight <weight>
backup # 将后端服务器标记为备份状态,只在所有非备份主机down机时提供服务,类似Sorry Server
disabled # 将后端服务器标记为不可用状态,即维护状态,除了持久模式,将不再接受连接,状态为深黄色
redirect prefix http://www.baidu.com/ # 302重定向
redir http://www.baidu.com # 同上,区别是最后不加"/"
maxconn <maxconn> # 当前后端server的最大并发连接数

注意:HAProxy server的weight和Nginx server的weight表示的意义是一样的,和Keepalived real_server的weight表示的意义不一样

frontend

1
2
3
4
5
6
7
8
9
10
frontend magedu_web_port	# 形式命名:业务-服务-端口号
# 指定HAProxy的监听地址,可以是IPV4或IPV6,可以同时监听多个IP或端口,可同时用于listen字段中
# bind [<address>]:<port_range> [, ...] [param*],address省略则等于0.0.0.0
# 注意:如果需要绑定在非本机的IP,需要开启内核参数:net.ipv4.ip_nonlocal_bind=1
bind :80,:8080
bind 10.0.0.7:10080,:8801-8810,10.0.0.17:9001-9010
mode http|tcp # 指定负载协议类型
use_backend <backend_name> # 调用的后端服务器组名称
# 针对所有server配置,当前端服务器的连接数达到上限后的后援队列长度,注意:不支持backend
backlog <backlog>

backend

1
2
3
4
5
backend magedu-test-http-nodes
mode tcp # 指定负载协议类型,和对应的frontend必须一致
default-server inter 1000 weight 6
server web1 10.0.0.27:80 check # 定义后端real server,必须指定IP和端口
server web1 10.0.0.17:80 weight 2 check addr 10.0.0.117 port 8080

listen

1
2
3
4
5
6
7
8
9
10
11
12
13
listen web_port
mode http
bind 10.0.0.71:80
log global # 开启日志功能,默认不记录日志
server web1 127.0.0.1:8080 check inter 3000 fall 2 rise 5

listen stats # stats相关:状态页配置项
mode http
bind 0.0.0.0:9999
log global
stats enable # 于默认的参数启用stats page
stats uri /haproxy-status # 自定义stats page uri,默认值:/haproxy?stats
stats auth admin:123456 # 认证时的账号和密码,可定义多个用户,每行指定一个用户

使用子配置文件保存配置

按业务分类,将配置信息拆分,放在不同的子配置文件中,从而达到方便维护的目的

注意:配置文件必须为cfg后缀非.开头

1
mkdir /etc/haproxy/conf.d/

因为没有类似include的指令引入子配置,所以要在启动命令上指定子配置文件

systemctl启动文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@c71 ~]$vim /usr/lib/systemd/system/haproxy.service
[Unit]
Description=HAProxy Load Balancer
After=syslog.target network.target

[Service]
ExecStartPre=/usr/local/haproxy/sbin/haproxy -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/ -c -q
ExecStart=/usr/local/haproxy/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -f /etc/haproxy/conf.d/ -p /var/lib/haproxy/haproxy.pid
ExecReload=/bin/kill -USR2 $MAINPID

[Install]
WantedBy=multi-user.target

[root@c71 ~]$useradd -r -s /sbin/nologin -d /var/lib/haproxy haproxy

HAProxy调度算法

官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#4-balance

HAProxy的调度算法分为静态和动态两种:

  • 静态算法:按照事先定义好的规则轮询公平调度,不关心后端服务器的当前负载、连接数和响应速度等,且无法实时修改权重(只能为0和1,不支持其它值),只能靠重启HAProxy生效
  • 动态算法:基于后端服务器状态进行调度适当调整,优先调度至当前负载较低的服务器,且权重可以haproxy运行时动态调整无需重启

静态算法

static-rr

基于权重的轮询调度,不支持运行时利用socat进行权重的动态调整(只支持0和1)及后端服务器慢启动,其后端主机数量没有限制,相当于LVS中的 wrr

1
2
3
4
5
6
7
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance static-rr
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 2 check inter 3000 fall 2 rise 5

first

根据服务器在列表中的位置,自上而下进行调度,但是其只会当第一台服务器的连接数达到上限,新请求才会分配给下一台服务,因此会忽略服务器的权重设置,此方式使用较少

不支持用socat进行动态修改权重,可以设置0和1

动态算法

socat 工具

Socat 是 Linux 下的一个多功能的网络工具,名字来由是 「Socket CAT」。其功能与有瑞士军刀之称的 Netcat 类似,可以看做是 Netcat 的加强版。

Socat 的主要特点就是在两个数据流之间建立通道,且支持众多协议和链接方式。如 IP、TCP、 UDP、IPv6、PIPE、EXEC、System、Open、Proxy、Openssl、Socket等。

这里利用 socat工具,对服务器动态权重和其它状态进行调整

socat stdio:对文件进行标准写入和标准读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 查看HAProxy帮助
echo "help" | socat stdio /var/lib/haproxy/haproxy.sock
# 查看HAProxy信息
echo "show info" | socat stdio /var/lib/haproxy/haproxy.sock
# 查看所有server的各种状态
echo "show servers state" | socat stdio /var/lib/haproxy/haproxy.sock
# 查看指定server的weight
echo "get weight magedu-test-80/web2" | socat stdio /var/lib/haproxy/haproxy.sock
# 修改指定server的weight,如果静态算法,如:static-rr,可以更改weight为0或1,但不支持动态更改为其它值
echo "set weight magedu-test-80/web2 2" | socat stdio /var/lib/haproxy/haproxy.sock
# 将指定后端服务器禁用
echo "disable server magedu-test-80/web2" | socat stdio /var/lib/haproxy/haproxy.sock
# 启用指定后端服务器
echo "enable server magedu-test-80/web2" | socat stdio /var/lib/haproxy/haproxy.sock
# 将后端服务器软下线,即weight设为0
echo "set weight magedu-test-80/web1 0" | socat stdio /var/lib/haproxy/haproxy.sock

一个进程就有一个haproxy.socket文件,以上的操作针对的都是单进程

roundrobin

基于权重的轮询动态调度算法,支持权重的运行时调整,不同于lvs中的rr轮训模式,HAProxy中的roundrobin支持慢启动(新加的服务器会逐渐增加转发数),其每个后端backend中最多支持4095个real server,支持对real server权重动态调整,roundrobin为默认调度算法,此算法使用广泛

支持动态调整权重:

1
2
3
4
5
echo "get weight web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock
1 (initial 1)
echo "set weight web_host/web1 3" | socat stdio /var/lib/haproxy/haproxy.sock
echo "get weight web_host/web1" | socat stdio /var/lib/haproxy/haproxy.sock
3 (initial 1)

leastconn

leastconn加权的最少连接的动态,支持权重的运行时调整和慢启动,即根据当前连接最少的后端服务器而非权重进行优先调度(新客户端连接),比较适合长连接的场景使用,比如:MySQL等场景

random

1.9版本增加,负载平衡算法,其基于随机数作为一致性hash的key,随机负载平衡对于大型服务器场或经常添加或删除服务器非常有用,支持weight的动态调整,weight较大的主机有更大概率获取新请求

其他算法

其它算法即可作为静态算法,又可以通过选项成为动态算法

source

源地址hash,基于用户源地址hash并将请求转发到后端服务器,后续同一个源地址请求将被转发至同一个后端web服务器。此方式当后端服务器数据量发生变化时,会导致很多用户的请求转发至新的后端服务器,默认为静态方式,但是可以通过hash-type支持的选项更改

这个算法一般是在不插入Cookie的TCP模式下使用,也可给拒绝会话cookie的客户提供最好的会话粘性,适用于session会话保持但不支持cookie和缓存的场景

源地址有两种转发客户端请求到后端服务器的服务器选取计算方式:取模法、一致性hash

map-base取模法

对source地址进行hash计算,再基于服务器总权重的取模,最终结果决定将此请求转发至对应的后端服务器。此方法是静态的,即不支持在线调整权重,不支持慢启动,可实现对后端服务器均衡调度。缺点是当服务器的总权重发生变化时,即有服务器上线或下线,都会因总权重发生变化而导致调度结果整体改变,hash-type 指定的默认值为此算法

1
2
3
4
5
6
7
8
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode tcp
log global
balance source
hash-type map-based # 取模法
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 3
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 3
一致性hash

当服务器的总权重发生变化时,对调度结果影响是局部的,不会引起大的变动,hash(o)mod n ,该hash算法是动态的,支持使用 socat等工具进行在线权重调整,支持慢启动

1
2
3
4
5
6
7
8
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode tcp
log global
balance source
hash-type consistent # 一致性hash
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5

uri

基于对用户请求的uri的左半部分或整个uri做hash,再将hash结果对总权重进行取模后,根据最终结果将请求转发到后端指定服务器,适用于后端是缓存服务器场景,默认是静态算法,也可以通过hash-type指定map-based和consistent,来定义使用取模法还是一致性hash

注意:此算法基于应用层,所以只支持 mode http ,不支持 mode tcp

1
2
3
4
5
6
7
8
9
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance uri
# hash-type map-based # 取模法,默认,可省略
hash-type consistent # 一致性hash
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5

url_param

对用户请求的url中的 params 部分中的一个参数key对应的value值作hash计算,并由服务器总权重相除以后派发至某挑出的服务器;通常用于追踪用户,以确保来自同一个用户的请求始终发往同一个real server,如果没有key,将按roundrobin算法

1
2
3
4
5
6
7
8
9
10
# 假设:http://10.0.0.7/index.html?userid=<NAME_ID>&typeid=<TYPE_ID>
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance url_param userid # 对userid的值取hash
# hash-type map-based # 取模法,默认,可省略
hash-type consistent # 一致性hash
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5

hdr

针对用户每个http头部(header)请求中的指定信息做hash,此处由 name 指定的http首部将会被取出并做hash计算,然后由服务器总权重取模以后派发至某挑出的服务器,如果无有效值,则会使用默认的轮询调度

1
2
3
4
5
6
7
8
9
listen web_host
bind 10.0.0.7:80,:8801-8810,10.0.0.7:9001-9010
mode http
log global
balance hdr(User-Agent)
# hash-type map-based # 取模法,默认,可省略
hash-type consistent # 一致性hash
server web1 10.0.0.17:80 weight 1 check inter 3000 fall 2 rise 5
server web2 10.0.0.27:80 weight 1 check inter 3000 fall 2 rise 5

测试访问:

1
2
[root@c71 ~]$curl -vA 'firefox' http://10.0.0.7/index.html
[root@c71 ~]$curl -vA 'chrome' http://10.0.0.7/index.html

各种算法使用场景

1
2
3
4
5
6
7
8
9
10
first 							# 使用较少
static-rr # 做了session共享的web集群
roundrobin
random
leastconn # 数据库
source # 基于客户端公网IP的会话保持
Uri--------------->http # 缓存服务器,CDN服务商,蓝汛、百度、阿里云、腾讯
url_param--------->http # 可以实现session保持
hdr # 基于客户端请求报文头部做下一步处理
rdp-cookie # 基于Windows主机,很少使用

高级功能及配置

基于cookie的会话保持

为当前server指定cookie值,实现基于cookie的会话黏性,相对于基于 source 地址hash 调度算法对客户端的粒度更精准,但同时也加大了haproxy负载,目前此模式使用较少, 已经被session共享服务器代替

1
2
3
4
5
6
7
8
9
# 配置示例:
listen web_port
bind 10.0.0.7:80
balance roundrobin
mode http # 注意:不支持 tcp
log global
cookie WEBSRV insert nocache indirect
server web1 10.0.0.17:80 check inter 3000 fall 2 rise 5 cookie web1
server web2 10.0.0.27:80 check inter 3000 fall 2 rise 5 cookie web2

HAProxy状态页

通过web界面,显示当前HAProxy的运行状态

状态页配置项:

1
2
3
4
5
6
7
8
stats enable 				# 基于默认的参数启用stats page
stats hide-version # 将状态页中haproxy版本隐藏
stats refresh <delay> # 设定自动刷新时间间隔,默认不自动刷新
stats uri <prefix> # 自定义stats page uri,默认值:/haproxy?stats
stats realm <realm> # 账户认证时的提示信息,示例:stats realm HAProxy\
Statistics
stats auth <user>:<passwd> # 认证时的账号和密码,可定义多个用户,每行指定一个用户
stats admin { if | unless } <cond> # 启用stats page中的管理功能
1
2
3
4
5
6
7
8
9
10
11
# 启用状态页
listen stats
bind :9999
stats enable
#stats hide-version
stats uri /haproxy-status # 自定义stats page uri
stats realm HAProxy\ Stats\ Page # 账户认证时的提示信息
stats auth haadmin:123456 # 两个用户
stats auth admin:123456
#stats refresh 30s
stats admin if TRUE # 安全原因,不建议打开

backend server信息:

利用状态页实现haproxy服务器的健康性检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@centos8 ~]#curl -I http://haadmin:123456@10.0.0.7:9999/haproxy-status
HTTP/1.1 200 OK
cache-control: no-cache
content-type: text/html

[root@centos8 ~]#curl -I -u haadmin:123456 http://10.0.0.7:9999/haproxy-status
HTTP/1.1 200 OK
cache-control: no-cache
content-type: text/html

[root@centos8 ~]#echo $?
0

[root@haproxy ~]#systemctl stop haproxy

[root@centos8 ~]#curl -I http://haadmin:123456@10.0.0.7:9999/haproxy-status
curl: (7) Failed to connect to 10.0.0.7 port 9999: Connection refused

[root@centos8 ~]#echo $?
7

IP透传

web服务器中需要记录客户端的真实IP地址,用于做访问统计、安全防护、行为分析、区域排行等场景

四层负载 和 七层代理

四层负载:

四层负载均衡设备不参与建立链接(实际上还是建立的,haproxy和lvs不同,haproxy是伪四层负载均衡)

七层代理:

七层负载均衡设备起到了代理服务器的作用,七层代理需要和Client以及后端服务器分别建立连接

四层IP透传

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# haproxy 配置示例
listen web_http_nodes
bind 172.16.0.100:80
mode tcp # 不支持http协议
balance roundrobin
server web1 to2b.cn:80 send-proxy check inter 3000 fall 3 rise 5 # 添加send-proxy

# nginx配置:在访问日志中通过变量$proxy_protocol_addr 记录透传过来的客户端IP
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" "$proxy_protocol_addr"'
server {
listen 80 proxy_protocol; #启用此项,将无法直接访问此网站,只能通过四层代理访问
server_name to2b.cn;
...

七层IP透传

当haproxy工作在七层的时候,也可以透传客户端真实IP至后端服务器

HAProxy配置:

在由haproxy发往后端主机的请求报文中添加“X-Forwarded-For”首部

1
2
3
4
5
option forwardfor [ except <network> ] [ header <name> ] [ if-none ]

[ except <network> ]:请求报请来自此处指定的网络时不予添加此首部,如haproxy自身所在网络
[ header <name> ]:使用自定义的首部名称,而非“X-Forwarded-For",示例:X-client
[ if-none ] 如果没有首部才添加首部,如果有使用默认值

示例:

1
2
3
defaults
option forwardfor # 此为默认值,首部字段默认为:X-Forwarded-For
# option forwardfor except 127.0.0.0/8 header X-client # 或者自定义首部,如:X-client

web服务器日志格式配置:

配置web服务器,记录负载均衡透传的客户端IP地址

示例:

1
2
3
4
5
6
7
8
9
# apache
LogFormat "%{X-Forwarded-For}i %a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

# nginx
$proxy_add_x_forwarded_for:包括客户端IP和中间经过的所有代理的IP
$http_x_forwarded_For:只有客户端IP

# tomcat
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%{X-Forwarded-For}i %h %l %u %t &quot;%r&quot; %s %b" />

报文修改

在http模式下,基于实际需求修改客户端的请求报文与响应报文

官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#4-http-request

1
2
3
4
5
6
7
8
9
10
# 请求头添加字段
http-request add-header <name> <fmt> [ { if | unless } <condition> ]
# 请求头修改字段
http-request set-header <name> <fmt> [ { if | unless } <condition> ]
# 请求头删除字段
http-request del-header <name> [ { if | unless } <condition> ]
# 响应头添加字段
http-response add-header <name> <fmt> [ { if | unless } <condition> ]
# 响应头删除字段
http-response del-header <name> [ { if | unless } <condition> ]

fmt:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#8.2.4

注意:以上指令适用于2.1版本以上,2.1版本以下的指令是reqadd、reqdel等

自定义日志格式

1
2
3
4
5
6
7
8
9
10
11
12
13
log global 			# 开启记录日志,默认不开启
option httplog # 开启记录httplog日志格式选项,一般不建议开启,加重HAProxy负载
capture cookie <name> len <length> # 捕获请求和响应报文中的 cookie及值的长度,将之记录到日志
capture request header <name> len <length> # 捕获请求报文中指定的首部内容和长度并记录日志
capture response header <name> len <length> # 捕获响应报文中指定的内容和长度首部并记录日志

# 示例
log global
option httplog
capture request header Host len 256
capture request header User-Agent len 512
capture request header Referer len 15
capture request header X-Forwarded-For len 15

压缩功能

对响应给客户端的报文进行压缩,以节省网络带宽,但是会占用部分CPU性能

建议在后端服务器开启压缩功能,而非在HAProxy上开启压缩

1
2
3
4
5
6
7
8
9
10
11
12
compression algo <algorithm> ...	# 启用http协议中的压缩机制
compression type <mime type> ... # 要压缩的文件类型

# 压缩算法<algorithm>支持下面类型
identity # debug调试使用的压缩方式
gzip # 常用的压缩方式,与各浏览器兼容较好
deflate # 有些浏览器不支持
raw-deflate # 新式的压缩方式

# 示例
compression algo gzip deflate
compression type text/html text/css text/plain

web服务器状态监测

  • 四层:传输端口做状态监测,此为默认方式

    不断的发送tcp握手报文

  • 七层:指定 URI 做状态监测

    指定请求方式(默认options),不断请求指定URL(默认根),会在后端服务器生成大量访问日志

四层监测节省系统资源,但是不够准确,可能发生监测通过,但是网站打不开的情况,工作中更推荐使用七层监测

1
2
3
4
5
6
7
8
9
10
11
12
option httpchk	# 启用七层健康性检测,对tcp和 http模式都支持
option httpchk <uri> # uri默认"/"
option httpchk <method> <uri> # method默认options,推荐设置为HEAD,节省网络流量
option httpchk <method> <uri> <version> # version默认HTTP/1.0,注意HTTP/1.1必须有Host字段

http-check expect [!] <match> <pattern> # 期望以上检查得到的响应码


# 示例
option httpchk HEAD /monitor/check.html HTTP/1.1\r\nHost:\ 10.0.0.7
http-check expect status 200
http-check expect ! rstatus ^5 # 支持正则表达式,响应码不以5开头

ACL ★★★

访问控制列表(ACL,Access Control Lists),特定过滤条件的集合

首先定义ACL,然后调用ACL

官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#7

ACL配置选项

1
2
acl  <aclname>  <criterion>  [flags]  [operator]  [<value>] ...
acl 名称 匹配规范 匹配模式 具体操作符 操作对象类型
aclname

ACL名称,严格区分大小写

criterion

定义ACL匹配规范,即:判断条件,以下列举的只是一部分:

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
hdr string  # 提取在一个HTTP请求报文的首部
hdr([<name> [,<occ>]]) # 完全匹配字符串,header的指定信息,<occ> 表示在多值中使用的值的出现次数
hdr_beg ([<name> [,<occ>]]) # 前缀匹配,header中指定匹配内容的begin
hdr_end ([<name> [,<occ>]]) # 后缀匹配,header中指定匹配内容end
hdr_dom ([<name> [,<occ>]]) # 域匹配,header中的domain name
hdr_dir ([<name> [,<occ>]]) # 路径匹配,header的uri路径
hdr_len ([<name> [,<occ>]]) # 长度匹配,header的长度匹配
hdr_reg ([<name> [,<occ>]]) # 正则表达式匹配,自定义表达式(regex)模糊匹配
hdr_sub ([<name> [,<occ>]]) # 子串匹配,header中的uri模糊匹配
base : string
base : exact string match
base_beg : prefix match
base_dir : subdir match
base_dom : domain match
base_end : suffix match
base_len : length match
base_reg : regex match
base_sub : substring match
path : string
path : exact string match
path_beg : prefix match # 请求的URL开头,如/static、/images、/img、/css
path_end : suffix match # 请求的URL中资源的结尾,如 .gif .png .css .js .jpg .jpeg
path_dom : domain match
path_dir : subdir match
path_len : length match
path_reg : regex match
path_sub : substring match
url : string
url :exact string match
url_beg : prefix match
url_dir : subdir match
url_dom : domain match
url_end : suffix match
url_len : length match
url_reg : regex match
url_sub : substring match
dst # 目标ip
dst_port # 目标port
src # 源IP
src_port # 源PORT
flags
1
2
3
4
5
6
7
-i : 不区分大小写
-f : 从文件中加载pattern匹配方法
-m : 使用指定的pattern匹配方法
-n : 不做DNS解析
-M : load the file pointed by -f like a map file.
-u : 禁止acl重名,否则多个同名ACL匹配或关系
-- : force end of flags. Useful when a string looks like one of the flags.

-f、-m可选的pattern匹配方法:

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
- "found" : only check if the requested sample could be found in the stream,
but do not compare it against any pattern. It is recommended not
to pass any pattern to avoid confusion. This matching method is
particularly useful to detect presence of certain contents such
as headers, cookies, etc... even if they are empty and without
comparing them to anything nor counting them.

- "bool" : check the value as a boolean. It can only be applied to fetches
which return a boolean or integer value, and takes no pattern.
Value zero or false does not match, all other values do match.

- "int" : match the value as an integer. It can be used with integer and
boolean samples. Boolean false is integer 0, true is integer 1.

- "ip" : match the value as an IPv4 or IPv6 address. It is compatible
with IP address samples only, so it is implied and never needed.

- "bin" : match the contents against a hexadecimal string representing a
binary sequence. This may be used with binary or string samples.

- "len" : match the sample's length as an integer. This may be used with
binary or string samples.

- "str" : exact match : match the contents against a string. This may be
used with binary or string samples.

- "sub" : substring match : check that the contents contain at least one of
the provided string patterns. This may be used with binary or
string samples.

- "reg" : regex match : match the contents against a list of regular
expressions. This may be used with binary or string samples.

- "beg" : prefix match : check that the contents begin like the provided
string patterns. This may be used with binary or string samples.

- "end" : suffix match : check that the contents end like the provided
string patterns. This may be used with binary or string samples.

- "dir" : subdir match : check that a slash-delimited portion of the
contents exactly matches one of the provided string patterns.
This may be used with binary or string samples.

- "dom" : domain match : check that a dot-delimited portion of the contents
exactly match one of the provided string patterns. This may be
used with binary or string samples.
operator

ACL操作符

整数比较:eq、ge、gt、le、lt

字符比较:

1
2
3
4
5
6
- exact match (-m str) 		# 字符串必须完全匹配模式
- substring match (-m sub) # 在提取的字符串中查找模式,如果其中任何一个被发现,ACL将匹配
- prefix match (-m beg) # 在提取的字符串首部中查找模式,如果其中任何一个被发现,ACL将匹配
- suffix match (-m end) # 将模式与提取字符串的尾部进行比较,如果其中任何一个匹配,则ACL进行匹配
- subdir match (-m dir) # 查看提取出来的用斜线分隔(“/")的字符串,如其中任一个匹配,则ACL进行匹配
- domain match (-m dom) # 查找提取的用点(“.")分隔字符串,如果其中任何一个匹配,则ACL进行匹配
value

value类型

1
2
3
4
5
6
7
8
9
10
11
12
- Boolean 						# 布尔值
- integer or integer range # 整数或整数范围,比如用于匹配端口范围
- IP address / network # IP地址或IP范围, 192.168.0.1 ,192.168.0.1/24
- string--> www.magedu.com
exact # 精确比较
substring # 子串
suffix # 后缀比较
prefix # 前缀比较
subdir # 路径, /wp-includes/js/jquery/jquery.js
domain # 域名,www.magedu.com
- regular expression # 正则表达式
- hex block # 16进制

多个ACL的组合调用方式

多个ACL的逻辑处理关系:

1
2
3
4
5
6
7
8
# 默认
# or 或 ||
否定 # !

# 示例
if valid_src valid_port # 与,ACL中A和B都要满足为true
if invalid_src || invalid_port # 或,ACL中A或者B满足一个为true
if ! invalid_src

ACL示例-域名匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
bind 10.0.0.7:80
mode http
balance roundrobin
log global
option httplog
###################### acl setting ###############################
acl pc_domain hdr_dom(host) -i www.magedu.org
acl mobile_domain hdr_dom(host) -i mobile.magedu.org
###################### acl hosts #################################
use_backend pc_hosts if pc_domain # use_backend 调用后端服务器组
use_backend mobile_hosts if mobile_domain
default_backend pc_hosts # 所有ACL都不匹配,则使用的默认backend

###################### backend hosts #############################
backend mobile_hosts
mode http
server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend pc_hosts
mode http
server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL示例-基于源IP或子网调度访问

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
# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
bind 10.0.0.7:80
mode http
balance roundrobin
log global
option httplog
###################### acl setting ###############################
acl pc_domain hdr_dom(host) -i www.magedu.org
acl mobile_domain hdr_dom(host) -i mobile.magedu.org
acl ip_range_test src 172.18.0.0/16 10.0.0.6 # 基于源地址的ACL,定义多个ACL的顺序无关
acl ip_range_test2 src 172.18.0.200
###################### acl hosts #################################
use_backend pc_hosts if ip_range_test #放在前面的ACL规则优先生效,引用ACL时,严格的ACL应放在前面
use_backend pc_hosts if pc_domain
use_backend mobile_hosts if mobile_domain
default_backend pc_hosts

###################### backend hosts #############################
backend mobile_hosts
mode http
server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend pc_hosts
mode http
server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL示例-基于源地址的访问控制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
listen web_host
bind 10.0.0.7:80
mode http
balance roundrobin
log global
option httplog
###################### acl setting ###############################
acl acl_deny_src src 10.0.0.6 192.168.0.0/24
###################### acl hosts #################################
http-request deny if acl_deny_src # http-request deny 拒绝请求,返回403
#block if acl_deny_src #2.1版本后,不再支持block
#http-request allow
default_backend default_web

###################### backend hosts #############################
backend magedu_host
mode http
server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend default_web
mode http
server web1 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL示例-匹配浏览器类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
bind 10.0.0.7:80
mode http
balance roundrobin
log global
option httplog
###################### acl setting ###############################
acl acl_user_agent hdr_sub(User-Agent) -i curl wget # 浏览器是curl和wget
acl acl_user_agent_ab hdr_sub(User-Agent) -i ApacheBench # 浏览器是ApacheBench
###################### acl hosts #################################
redirect prefix http://www.baidu.com if acl_user_agent # 302 临时重定向至新URL
http-request deny if acl_user_agent_ab # 拒绝ab
default_backend pc_hosts

###################### backend hosts #############################
backend mobile_hosts
mode http
server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend pc_hosts
mode http
server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL示例-基于文件后缀名实现动静分离

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
# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
bind 10.0.0.7:80
mode http
balance roundrobin
log global
option httplog
###################### acl setting ###############################
acl acl_static path_end -i .jpg .jpeg .png .gif .css .js .html # 静
acl acl_php path_end -i .php # 动
###################### acl hosts #################################
use_backend mobile_hosts if acl_static
use_backend app_hosts if acl_php
default_backend pc_hosts

###################### backend hosts #############################
backend mobile_hosts
mode http
server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend pc_hosts
mode http
server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5
backend app_hosts
mode http
server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL示例-匹配访问路径实现动静分离

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# cat /etc/haproxy/conf.d/test.cfg
frontend magedu_http_port
bind 10.0.0.7:80
mode http
balance roundrobin
log global
option httplog
###################### acl setting ###############################
acl acl_static path_beg -i /static /images /javascript # 路径起始
acl acl_static path_end -i .jpg .jpeg .png .gif .css .js .html .htm # 路径结尾
acl acl_app path_beg -i /api
###################### acl hosts #################################
use_backend static_hosts if acl_static
use_backend app_hosts if acl_app
default_backend app_hosts

###################### backend hosts #############################
backend static_hosts
mode http
server web1 10.0.0.17 check inter 2000 fall 3 rise 5
backend app_hosts
mode http
server web2 10.0.0.27:80 check inter 2000 fall 3 rise 5

ACL示例-预定义ACL使用

官方文档:http://cbonte.github.io/haproxy-dconv/2.2/configuration.html#7.4

ACL name Equivalent to Usage
FALSE always_false never match
HTTP req_proto_http match if protocol is valid HTTP
HTTP_1.0 req_ver 1.0 match HTTP version 1.0
HTTP_1.1 req_ver 1.1 match HTTP version 1.1
HTTP_CONTENT hdr_val(content-length) gt 0 match an existing content-length
HTTP_URL_ABS url_reg ^[^/:]*:// match absolute URL with scheme
HTTP_URL_SLASH url_beg / match URL beginning with “/“
HTTP_URL_STAR url * match URL equal to “*”
LOCALHOST src 127.0.0.1/8 match connection from local host
METH_CONNECT method CONNECT match HTTP CONNECT method
METH_DELETE method DELETE match HTTP DELETE method
METH_GET method GET HEAD match HTTP GET or HEAD method
METH_HEAD method HEAD match HTTP HEAD method
METH_OPTIONS method OPTIONS match HTTP OPTIONS method
METH_POST method POST match HTTP POST method
METH_PUT method PUT match HTTP PUT method
METH_TRACE method TRACE match HTTP TRACE method
RDP_COOKIE req_rdp_cookie_cnt gt 0 match presence of an RDP cookie
REQ_CONTENT req_len gt 0 match data in the request buffer
TRUE always_true always match
WAIT_END wait_end wait for end of content analysis

示例:

1
http-request deny if METH_TRACE HTTP_1.1 	# 引用预定义的ACL,多个ACL默认为与关系

自定义HAProxy 错误界面

1
2
3
4
5
errorfile <code> <file>		# 自定义错误页
<code> # HTTP status code.支持200, 400, 403, 405, 408, 425, 429, 500, 502,503,504
<file> # 包含完整HTTP响应头的错误页文件的绝对路径。 建议后缀".http",以和一般的html文件相区分

errorloc <code> <url> # 错误页面重定向

示例:

1
2
3
4
5
6
defaults
...
errorfile 400 /etc/haproxy/errorfiles/400badreq.http
errorfile 403 /etc/haproxy/errorfiles/403forbid.http
# errorfile 503 /etc/haproxy/errorfiles/503sorry.http
errorloc 503 http://www.magedu.com/error_pages/503.html # 重定向

HAProxy 四层负载

问题: 后端服务器和haproxy还是和客户端建立三次握手?

答:HAProxy,因为HAProxy是伪四层,LVS是真四层,不过,即使是伪四层,四层负载也比七层代理性能要高

四层负载示例:

注意:如果使用frontend和backend,一定在 frontend 和 backend 段中都指定mode tcp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 对MySQL服务实现四层负载
listen magedu_mysql
bind 10.0.0.7:3306
mode tcp
balance leastconn
server mysql1 10.0.0.17:3306 check
server mysql2 10.0.0.27:3306 check # 如果不写端口号,可以转发,但无法check状态

# 或者使用frontend和backend实现
frontend mysql
bind :3306
mode tcp # 必须指定tcp模式
default_backend mysqlsrvs
backend mysqlsrvs
mode tcp # 必须指定tcp模式
balance leastconn
server mysql1 10.0.0.17:3306
server mysql2 10.0.0.27:3306
1
2
3
# ACL示例-四层访问控制
acl invalid_src src 192.168.1.0/24 10.0.0.8 # 定义acl
tcp-request connection reject if invalid_src # tcp-request,走的是四层的拒绝

HAProxy https 实现

haproxy可以实现https的证书安全,但基于性能考虑,生产中证书都是在后端服务器比如nginx上实现,在HAProxy上采用四层负载,监听ip:443,然后直接转发,让后端服务器去处理https证书

本章重点总结

  • HAProxy调度算法
  • 动静分离与客户端源IP透传
  • ACL使用与报文修改
  • 服务器动态下线
  • 编写shell脚本,实现能够基于参数传递 Real Server 服务器IP,并实现将其从多个HAProxy进程下线与上线

基本配置

将Linux主机接入到网络,需要配置网络相关设置。一般包括如下内容:

  • 主机名
  • IP/netmask
  • 路由:默认网关
  • DNS服务器
    • 主DNS服务器
    • 次DNS服务器
    • 第三个DNS服务器

命令修改的配置一般都是临时的,重启机器就失效了,如果要让配置更改永久生效,需要将配置更改写入到相关的配置文件中

相关配置文件

配置当前主机的主机名

永久修改:

1
2
3
4
5
6
#centos6 之前版本
/etc/sysconfig/network
HOSTNAME=
#centos7 以后版 使用 hostnametcl set-hostname xxx 命令 或者修改文件
/etc/hostname
HOSTNAME

本地主机名数据库和IP地址的映射

优先级高于DNS

1
/etc/hosts

DNS域名解析

/etc/resolv.conf

1
2
3
4
nameserver DNS_SERVER_IP1
nameserver DNS_SERVER_IP2
nameserver DNS_SERVER_IP3
search DOMAIN

nmcli会将配置的DNS写入到这个文件,但是不管nmcli给connection有没有配置DNS或是配置的什么,最终解析域名还是以这个文件中的DNS为准,所以建议直接修改这个文件。

search DOMAIN是当域名无法DNS解析时,会将DOMAIN加到域名后面再尝试进行解析,例如:

1
2
3
4
5
6
7
8
[root@centos7 ~]#cat /etc/resolv.conf 
# Generated by NetworkManager
search lujinkai.cn
nameserver 180.76.76.76
[root@centos7 ~]#ping blog
PING blog.lujinkai.cn (47.105.171.233) 56(84) bytes of data.
64 bytes from 47.105.171.233 (47.105.171.233): icmp_seq=1 ttl=128 time=24.0 ms
64 bytes from 47.105.171.233 (47.105.171.233): icmp_seq=2 ttl=128 time=23.5 ms

修改/etc/host和DNS的优先级

/etc/nsswitch.conf

1
hosts:      files dns myhostname

路由相关的配置文件

新建文件 /etc/sysconfig/network-scripts/route-IFACE

1
2
3
4
5
6
7
8
两种风格:
(1) TARGET via GW
如:10.0.0.0/8 via 172.16.0.1

(2) 每三行定义一条路由
ADDRESS#=TARGET
NETMASK#=mask
GATEWAY#=GW

不推荐直接修改配置文件,推荐使用nmcli:

1
2
3
4
5
6
# 添加一条路由: 0.0.0.0 via 10.0.0.8
[root@centos8 ~]$nmcli connection modify eth0 ipv4.addresses 0.0.0.0 ipv4.gateway 10.0.0.8
[root@centos8 network-scripts]$route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.0.8 0.0.0.0 UG 100 0 0 eth0

网卡相关配置文件

修改网卡设备名

网卡组成格式:

1
2
3
4
5
6
7
en: Ethernet 有线局域网
wl: wlan 无线局域网
ww: wwan无线广域网
o<index>: 集成设备的设备索引号
s<slot>: 扩展槽的索引号
x<MAC>: 基于MAC地址的命名
p<bus>s<slot>: enp2s1

CentOS6之前网卡的默认命名方式 eth0、eth1、eth2。。。从CentOS/RHEL7起,可预见的命名规则变成了默认。这一规则,接口名称被自动基于固件,拓扑结构和位置信息来确定。现在,即使添加或移除网络设备,接口名称仍然保持固定,而无需重新枚举,和坏掉的硬件可以无缝替换。

修改CentOS7、8的网卡设备名为CentOS6形式:

  1. 编辑/etc/default/grub配置文件,给GRUB_CMDLINE_LINUX选项添加net.ifnames=0字段,如果是戴尔的机器,还需要再加biosdevname=0

  2. 为grub2生成其配置文件:grub2-mkconfig -o /etc/grub2.cfg

    如果是ubuntu:sudo grub-mkconfig -o /boot/grub/grub.cfg

静态指定IP

/etc/sysconfig/network-scripts下新建文件

1
2
3
4
5
6
7
8
9
NAME=eth0
DEVICE=eth0
BOOTPROTO=static
IPADDR=10.0.0.6
PREFIX=24 # 子网掩码
GATEWAY=10.0.0.2 # 网关,也就是路由器
DNS1=223.6.6.6
DNS2=180.76.76.76
ONBOOT=yes

动态分配IP

也是在/etc/sysconfig/network-scripts下新建文件

1
2
3
4
DEVICE=eth0
NAME=eth0
BOOTPROTO=dhcp
ONBOOT=yes

网卡别名

还是在/etc/sysconfig/network-scripts下新建文件,网卡别名必须使用静态地址

1
2
3
DEVICE=eth0:1
IPADDR=11.0.0.6
PREFIX=8

修改完后 service network restart重启服务即可

以上是CentOS6配置网卡的方式,CentOS7、8不推荐手动修改文件,推荐使用nmcli命令自动生成配置文件。

多网卡

将多块网卡绑定同一IP地址对外提供服务,可以实现高可用或者负载均衡。直接给两块网卡设置同一IP地址是不可以的。通过 Bonding或Teaming,虚拟一块网卡对外提供连接,物理网卡的被修改为相同的MAC地址。

Bonding

共7种模式,0-6mode:

0:轮询、1:活动-备份、3:广播

说明:active-backup、balance-tlb 和 balance-alb 模式不需要交换机的任何特殊配置。其他绑定模式需要配置交换机以便整合链接。如:Cisco 交换机需要在模式 0、2 和 3 中使用 EtherChannel,但在模式4中需要 LACP和 EtherChannel。

实现bonding模式1:

创建bonding设备的配置文件/etc/sysconfig/network-scripts/ifcfg-bond0:

1
2
3
4
5
6
7
NAME=bond0
TYPE=bond
DEVICE=bond0
BOOTPROTO=none
IPADDR=10.0.0.100
PREFIX=8
BONDING_OPTS="mode=1 miimon=100"

修改两个eth0和eth1两个网卡的配置

1
2
3
4
5
6
NAME=eth0
DEVICE=eth0
BOOTPROTO=none
MASTER=bond0
SLAVE=yes
ONBOOT=yes
1
2
3
4
5
6
NAME=eth1
DEVICE=eth1
BOOTPROTO=none
MASTER=bond0
SLAVE=yes
ONBOOT=yes

查看bond0状态:/proc/net/bonding/bond0

删除bond0:

1
2
ifconfig bond0 down
rmmod bonding

Teaming

从CentOS7开始,不推荐使用bonding,推荐使用网络组,网络组提供更好的性能和扩展性。网络组由内核驱动和teamd守护进程实现。

网络组也有多种模式:broadcast、roundrobin、activebackup、loadbalance、lacp (implements the 802.3ad Link Aggregation Control Protocol)

网络组特点

  • 启动网络组接口不会自动启动网络组中的port接口
  • 启动网络组接口中的port接口总会自动启动网络组接口
  • 禁用网络组接口会自动禁用网络组中的port接口
  • 没有port接口的网络组接口可以启动静态IP连接
  • 启用DHCP连接时,没有port接口的网络组会等待port接口的加入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建网络组接口,虽然使用connection创建,但好像使用device来创建更符合逻辑
nmcli con add type team con-name CNAME ifname INAME [config JSON]
CNAME 连接名
INAME 接口名,也随便写。其他连接ifname是指定关联的物理网卡接口,而网络组是软件,所以这里的ifname不是指定,是定义
JSON 指定runner方式,格式:'{"runner": {"name": "METHOD"}}'
METHOD 可以是broadcast, roundrobin, activebackup, loadbalance, lacp

# 创建port接口
nmcli con add type team-slave con-name CNAME ifname INAME master TEAM
CNAME 连接名,连接名若不指定,默认为team-slave-IFACE
INAME 网络接口名
TEAM 网络组接口名

# 断开和启动
nmcli dev dis INAME
nmcli con up CNAME
INAME 设备名 CNAME 网络组接口名或port接口

范例:

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
# 添加网络组接口
[root@centos8 ~]# nmcli con add type team con-name con-team0 ifname if-team0 config '{"runner":{"name": "loadbalance"}}' ipv4.addresses 10.0.0.88/24 ipv4.method manual
Connection 'con-team0' (6a08e7d5-473e-4bed-bc6d-2065efc296c4) successfully added.
[root@centos8 ~]# nmcli device
DEVICE TYPE STATE CONNECTION
eth0 ethernet connected eth0
eth1 ethernet connected eth1
eth2 ethernet connected eth2
eth3 ethernet connected eth3
if-team0 team connected con-team0
lo loopback unmanaged --

# 给eth0、eth1、eth2三个网卡接口添加连接,并加入到网络组
[root@centos8 ~]# nmcli con add con-name team0-eth1 type team-slave ifname eth1 master if-team0
Connection 'team0-eth1' (66aad47b-4d03-49ef-bcc7-d628f77f3256) successfully added.
[root@centos8 ~]# nmcli con add con-name team0-eth2 type team-slave ifname eth2 master if-team0
Connection 'team0-eth2' (175ea1cb-05a8-4f97-b756-eae6ec844a43) successfully added.
[root@centos8 ~]# nmcli con add con-name team0-eth3 type team-slave ifname eth3 master if-team0
Connection 'team0-eth3' (d021a567-5c2c-4528-acdb-e01f25d80988) successfully added.
# 看一下连接状态,新创建的连接还没有激活
[root@centos8 ~]# nmcli connection
NAME UUID TYPE DEVICE
eth0 d9c122c4-1861-45f3-826f-a1fff455543c ethernet eth0
eth1 8c913f0c-de22-4fa9-a153-9de53621de5a ethernet eth1
eth2 0ee56953-0b62-4181-9987-fb09da4dacd9 ethernet eth2
eth3 8028cae7-39b5-4656-8ad8-e82b1adf0025 ethernet eth3
con-team0 6a08e7d5-473e-4bed-bc6d-2065efc296c4 team if-team0
team0-eth1 66aad47b-4d03-49ef-bcc7-d628f77f3256 ethernet --
team0-eth2 175ea1cb-05a8-4f97-b756-eae6ec844a43 ethernet --
team0-eth3 d021a567-5c2c-4528-acdb-e01f25d80988 ethernet --
# 看一下网络组的状态,组内的连接都还没启动
[root@centos8 ~]# teamdctl if-team0 state
setup:
runner: loadbalance

# 一个网卡接口可以关联多个连接,但同时只能有一个活跃,切换加入了网络组的连接为活跃状态
[root@centos8 ~]# nmcli connection up team0-eth1
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/14)
[root@centos8 ~]# nmcli connection up team0-eth2
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/15)
[root@centos8 ~]# nmcli connection up team0-eth3
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/16)
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/16)

# 看一下网络组的状态,都启动了
[root@centos8 ~]# teamdctl if-team0 state
setup:
runner: loadbalance
ports:
eth1
link watches:
link summary: up
instance[link_watch_0]:
name: ethtool
link: up
down count: 0
eth2
link watches:
link summary: up
instance[link_watch_0]:
name: ethtool
link: up
down count: 0
eth3
link watches:
link summary: up
instance[link_watch_0]:
name: ethtool
link: up
down count: 0

网桥

网桥和交换机都有若干网络接口,交换机网口更多,但两者实现的功能是相同的,都是把这些若干网络接口“连接”起来,在数据链路层对报文进行互相转发,使得连接网口的设备之间能够通信。

“连接”网口并实现报文互相转发的技术就叫桥接,物理的网桥和交换机(以下简称网桥)的网口数量是固定的,而软件模拟的网桥的网口数量是不固定的,因为软件模拟的网桥的网口既可以指定物理网口,也可以指定虚拟网口,物理网口对外连接交换机,虚拟网口对内连接虚拟机。

例如:一台服务器有4个网口(可能是四张网卡,每张一个网口,也可能是一张网卡有四个网口)、系统中安装了5个虚拟机。使用软件网桥把2个物理网口和3个虚拟网口桥接在一起,每个虚拟网口连接一个虚拟机。这样,从这两个网口中过来的报文只能转发到这个三个虚拟机中,不会影响到其他虚拟机。

实验一:环境vmware,三台虚拟机A、B、C,A是仅主机模式,C是nat模式,所以AC之间不能通信,B配置两张网卡,一张仅主机,一张nat,所以B既能和A通信,也能和C通信,所以让B充当网桥,把A和C桥接在一起,从而实现AC通信。实现方式可以安装bridge-utils,也可以使用nmcli命令。

1

路由记录了整个网络通讯的路径,路由如果出错,网络肯定不通

路由表主要构成:

1
2
3
4
5
6
[root@centos7 network-scripts]#route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 10.0.0.2 0.0.0.0 UG 100 0 0 ens33
10.0.0.0 0.0.0.0 255.255.255.0 U 100 0 0 ens33
10.0.0.0 0.0.0.0 255.255.255.0 U 101 0 0 ens37
  • Destination:目标网络

    • 可以写具体某一个ip,但一般不这么写
    • 写一个网段,配合Genmask。例如上图最后一条路由10.0.0.0的子网掩码255.255.255.0,表示前24为是网络ID,那么这条路由的目标地址是10.0.0.1 - 10.0.0.254的所有ip
    • 0.0.0.0/0 表示所有未知网络,又称为默认路由,优先级最低。即一条请求,其它路由都没有指定它目标地址,那它就走Destination位0.0.0.0的这条路由。我称之为兜底路由。上面的VMware虚拟机的路由表中,10.0.0.2是NAT的网关IP

  • Genmask:目标网络对应的子网掩码

  • Iface:到达对应网络,应该从当前主机哪个网卡发送出来

  • Gateway: 网关

    • 目标网络非直达,gateway是相邻(下一跳)路由器的靠近本机的接口IP地址
      • 要和本机在同一网段,不同网段是不能通信的
      • 要先有一条能到达网关地址的路由,不然会报错SIOCADDRT: Network is unreachable
    • 目标网络直达,gateway是0.0.0.0,通过广播通信
  • Metric:路由开销,值越小,路由记录的优先级最高

总结:网络通讯过程

  1. 查路由
  2. 查对方和我是不是在一个网段
  3. arp广播
  4. 拿到对方mac
  5. 三次握手
  6. 通信

配置动态路由

route命令可以添加路由,可是靠手动一条一条的添加太麻烦了,大型的网络环境中,路由器中的路由不是手动添加的,而是通过路由协议自动生成的。

路由协议:
RIP:Routing Information Protocol,路由信息协议
OSPF:Open Shortest Path First,开放式最短路径优先
BGP:Border Gateway Protocol,边界网关协议

大概的过程是这样的,每个路由器向网络中广播,告诉其他路由器自己连接的网段,互通有无。例如pc1要和pc2通信,pc1只要知道gwA能连接pc1,gwA只知道gwB能连接pc1,而gwB只知道gwC能连接pc1,就这样,最后通过gwE才真正连接到pc2。

不同的协议决定了路由器选择路径的算法不同。RIP很少用,OSPF用于局域网,BGP用于互联网。