Kubernetes(k8s)总架构图及各组件介绍

一、Kubernetes的总架构图

未分类

二、Kubernetes各个组件介绍

(一)kube-master[控制节点]

master的工作流程图

未分类

  • Kubecfg将特定的请求,比如创建Pod,发送给Kubernetes Client。

  • Kubernetes Client将请求发送给API server。

  • API Server根据请求的类型,比如创建Pod时storage类型是pods,然后依此选择何种REST Storage API对请求作出处理。

  • REST Storage API对的请求作相应的处理。

  • 将处理的结果存入高可用键值存储系统Etcd中。

  • 在API Server响应Kubecfg的请求后,Scheduler会根据Kubernetes Client获取集群中运行Pod及Minion/Node信息。

  • 依据从Kubernetes Client获取的信息,Scheduler将未分发的Pod分发到可用的Minion/Node节点上。

1、API Server[资源操作入口]

  • 提供了资源对象的唯一操作入口,其他所有组件都必须通过它提供的API来操作资源数据,只有API Server与存储通信,其他模块通过API Server访问集群状态。

第一,是为了保证集群状态访问的安全。

第二,是为了隔离集群状态访问的方式和后端存储实现的方式:API Server是状态访问的方式,不会因为后端存储技术etcd的改变而改变。

  • 作为kubernetes系统的入口,封装了核心对象的增删改查操作,以RESTFul接口方式提供给外部客户和内部组件调用。对相关的资源数据“全量查询”+“变化监听”,实时完成相关的业务功能。

更多API Server信息请参考:Kubernetes核心原理(一)之API Server

2、Controller Manager[内部管理控制中心]

  • 实现集群故障检测和恢复的自动化工作,负责执行各种控制器,主要有:

endpoint-controller:定期关联service和pod(关联信息由endpoint对象维护),保证service到pod的映射总是最新的。

replication-controller:定期关联replicationController和pod,保证replicationController定义的复制数量与实际运行pod的数量总是一致的。

更多Controller Manager信息请参考:Kubernetes核心原理(二)之Controller Manager

3、Scheduler[集群分发调度器]

  • Scheduler收集和分析当前Kubernetes集群中所有Minion节点的资源(内存、CPU)负载情况,然后依此分发新建的Pod到Kubernetes集群中可用的节点。

  • 实时监测Kubernetes集群中未分发和已分发的所有运行的Pod。

  • Scheduler也监测Minion节点信息,由于会频繁查找Minion节点,Scheduler会缓存一份最新的信息在本地。

  • 最后,Scheduler在分发Pod到指定的Minion节点后,会把Pod相关的信息Binding写回API Server。

更多Scheduler信息请参考:Kubernetes核心原理(三)之Scheduler

(二)kube-node[服务节点]

kubelet结构图

未分类

1、Kubelet[节点上的Pod管家]

  • 负责Node节点上pod的创建、修改、监控、删除等全生命周期的管理

  • 定时上报本Node的状态信息给API Server。

  • kubelet是Master API Server和Minion之间的桥梁,接收Master API Server分配给它的commands和work,与持久性键值存储etcd、file、server和http进行交互,读取配置信息。

  • 具体的工作如下:

设置容器的环境变量、给容器绑定Volume、给容器绑定Port、根据指定的Pod运行一个单一容器、给指定的Pod创建network 容器。

同步Pod的状态、同步Pod的状态、从cAdvisor获取Container info、 pod info、 root info、 machine info。

在容器中运行命令、杀死容器、删除Pod的所有容器。

2、Proxy[负载均衡、路由转发]

  • Proxy是为了解决外部网络能够访问跨机器集群中容器提供的应用服务而设计的,运行在每个Node上。Proxy提供TCP/UDP sockets的proxy,每创建一种Service,Proxy主要从etcd获取Services和Endpoints的配置信息(也可以从file获取),然后根据配置信息在Minion上启动一个Proxy的进程并监听相应的服务端口,当外部请求发生时,Proxy会根据Load Balancer将请求分发到后端正确的容器处理。

  • Proxy不但解决了同一主宿机相同服务端口冲突的问题,还提供了Service转发服务端口对外提供服务的能力,Proxy后端使用了随机、轮循负载均衡算法。

3、kubectl(kubelet client)[集群管理命令行工具集]

通过客户端的kubectl命令集操作,API Server响应对应的命令结果,从而达到对kubernetes集群的管理。

kubernetes(k8s) 1.7.3 calico网络和Master ha安装说明

kubernetes 1.7.3

基于 二进制 文件部署 本地化 kube-apiserver, kube-controller-manager , kube-scheduler 我这边配置 既是 master 也是 nodes

环境说明

k8s-master-1: 10.6.0.140
k8s-master-2: 10.6.0.187
k8s-master-3: 10.6.0.188

初始化环境

hostnamectl --static set-hostname hostname

10.6.0.140 - k8s-master-1
10.6.0.187 - k8s-master-2
10.6.0.188 - k8s-master-3
#编辑 /etc/hosts 文件,配置hostname 通信

vi /etc/hosts

10.6.0.140 k8s-master-1
10.6.0.187 k8s-master-2
10.6.0.188 k8s-master-3

创建 验证

这里使用 CloudFlare 的 PKI 工具集 cfssl 来生成 Certificate Authority (CA) 证书和秘钥文件。

安装 cfssl

mkdir -p /opt/local/cfssl

cd /opt/local/cfssl

wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
mv cfssl_linux-amd64 cfssl

wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
mv cfssljson_linux-amd64 cfssljson

wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 cfssl-certinfo

chmod +x *

创建 CA 证书配置

mkdir /opt/ssl

cd /opt/ssl

/opt/local/cfssl/cfssl print-defaults config > config.json

/opt/local/cfssl/cfssl print-defaults csr > csr.json
# config.json 文件

{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
        "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ],
        "expiry": "87600h"
      }
    }
  }
}
# csr.json 文件

{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "ShenZhen",
      "L": "ShenZhen",
      "O": "k8s",
      "OU": "System"
    }
  ]
}

生成 CA 证书和私钥

cd /opt/ssl/

/opt/local/cfssl/cfssl gencert -initca csr.json | /opt/local/cfssl/cfssljson -bare ca


[root@k8s-master-1 ssl]# ls -lt
总用量 20
-rw-r--r-- 1 root root 1005 7月   3 17:26 ca.csr
-rw------- 1 root root 1675 7月   3 17:26 ca-key.pem
-rw-r--r-- 1 root root 1363 7月   3 17:26 ca.pem
-rw-r--r-- 1 root root  210 7月   3 17:24 csr.json
-rw-r--r-- 1 root root  292 7月   3 17:23 config.json

分发证书

# 创建证书目录
mkdir -p /etc/kubernetes/ssl

# 拷贝所有文件到目录下
cp * /etc/kubernetes/ssl

# 这里要将文件拷贝到所有的k8s 机器上

scp * 10.6.0.187:/etc/kubernetes/ssl/

scp * 10.6.0.188:/etc/kubernetes/ssl/

etcd 集群

etcd 是k8s集群的基础组件

安装 etcd

yum -y install etcd

创建 etcd 证书

cd /opt/ssl/

vi etcd-csr.json

{
  "CN": "etcd",
  "hosts": [
    "127.0.0.1",
    "10.6.0.140",
    "10.6.0.187",
    "10.6.0.188"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "ShenZhen",
      "L": "ShenZhen",
      "O": "k8s",
      "OU": "System"
    }
  ]
}
# 生成 etcd   密钥

/opt/local/cfssl/cfssl gencert -ca=/opt/ssl/ca.pem 
  -ca-key=/opt/ssl/ca-key.pem 
  -config=/opt/ssl/config.json 
  -profile=kubernetes etcd-csr.json | /opt/local/cfssl/cfssljson -bare etcd
# 查看生成

[root@k8s-master-1 ssl]# ls etcd*
etcd.csr  etcd-csr.json  etcd-key.pem  etcd.pem
# 拷贝到etcd服务器

# etcd-1 
cp etcd*.pem /etc/kubernetes/ssl/

# etcd-2
scp etcd* 10.6.0.187:/etc/kubernetes/ssl/

# etcd-3
scp etcd* 10.6.0.188:/etc/kubernetes/ssl/



# 如果 etcd 非 root 用户,读取证书会提示没权限

chmod 644 /etc/kubernetes/ssl/etcd-key.pem

修改 etcd 配置

修改 etcd 启动文件 /usr/lib/systemd/system/etcd.service

# etcd-1


vi /usr/lib/systemd/system/etcd.service


[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
User=etcd
# set GOMAXPROCS to number of processors
ExecStart=/usr/bin/etcd 
  --name=etcd1 
  --cert-file=/etc/kubernetes/ssl/etcd.pem 
  --key-file=/etc/kubernetes/ssl/etcd-key.pem 
  --peer-cert-file=/etc/kubernetes/ssl/etcd.pem 
  --peer-key-file=/etc/kubernetes/ssl/etcd-key.pem 
  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem 
  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem 
  --initial-advertise-peer-urls=https://10.6.0.140:2380 
  --listen-peer-urls=https://10.6.0.140:2380 
  --listen-client-urls=https://10.6.0.140:2379,http://127.0.0.1:2379 
  --advertise-client-urls=https://10.6.0.140:2379 
  --initial-cluster-token=k8s-etcd-cluster 
  --initial-cluster=etcd1=https://10.6.0.140:2380,etcd2=https://10.6.0.187:2380,etcd3=https://10.6.0.188:2380 
  --initial-cluster-state=new 
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
# etcd-2


vi /usr/lib/systemd/system/etcd.service


[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
User=etcd
# set GOMAXPROCS to number of processors
ExecStart=/usr/bin/etcd 
  --name=etcd2 
  --cert-file=/etc/kubernetes/ssl/etcd.pem 
  --key-file=/etc/kubernetes/ssl/etcd-key.pem 
  --peer-cert-file=/etc/kubernetes/ssl/etcd.pem 
  --peer-key-file=/etc/kubernetes/ssl/etcd-key.pem 
  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem 
  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem 
  --initial-advertise-peer-urls=https://10.6.0.187:2380 
  --listen-peer-urls=https://10.6.0.187:2380 
  --listen-client-urls=https://10.6.0.187:2379,http://127.0.0.1:2379 
  --advertise-client-urls=https://10.6.0.187:2379 
  --initial-cluster-token=k8s-etcd-cluster 
  --initial-cluster=etcd1=https://10.6.0.140:2380,etcd2=https://10.6.0.187:2380,etcd3=https://10.6.0.188:2380 
  --initial-cluster-state=new 
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
# etcd-3


vi /usr/lib/systemd/system/etcd.service


[Unit]
Description=Etcd Server
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=notify
WorkingDirectory=/var/lib/etcd/
User=etcd
# set GOMAXPROCS to number of processors
ExecStart=/usr/bin/etcd 
  --name=etcd3 
  --cert-file=/etc/kubernetes/ssl/etcd.pem 
  --key-file=/etc/kubernetes/ssl/etcd-key.pem 
  --peer-cert-file=/etc/kubernetes/ssl/etcd.pem 
  --peer-key-file=/etc/kubernetes/ssl/etcd-key.pem 
  --trusted-ca-file=/etc/kubernetes/ssl/ca.pem 
  --peer-trusted-ca-file=/etc/kubernetes/ssl/ca.pem 
  --initial-advertise-peer-urls=https://10.6.0.188:2380 
  --listen-peer-urls=https://10.6.0.188:2380 
  --listen-client-urls=https://10.6.0.188:2379,http://127.0.0.1:2379 
  --advertise-client-urls=https://10.6.0.188:2379 
  --initial-cluster-token=k8s-etcd-cluster 
  --initial-cluster=etcd1=https://10.6.0.140:2380,etcd2=https://10.6.0.187:2380,etcd3=https://10.6.0.188:2380 
  --initial-cluster-state=new 
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

启动 etcd

分别启动 所有节点的 etcd 服务

systemctl enable etcd

systemctl start etcd

systemctl status etcd
# 如果报错 请使用
journalctl -f -t etcd  和 journalctl -u etcd 来定位问题

验证 etcd 集群状态

查看 etcd 集群状态:

etcdctl --endpoints=https://10.6.0.140:2379 
        --cert-file=/etc/kubernetes/ssl/etcd.pem 
        --ca-file=/etc/kubernetes/ssl/ca.pem 
        --key-file=/etc/kubernetes/ssl/etcd-key.pem 
        cluster-health

member 29262d49176888f5 is healthy: got healthy result from https://10.6.0.188:2379
member d4ba1a2871bfa2b0 is healthy: got healthy result from https://10.6.0.140:2379
member eca58ebdf44f63b6 is healthy: got healthy result from https://10.6.0.187:2379
cluster is healthy

查看 etcd 集群成员:

etcdctl --endpoints=https://10.6.0.140:2379 
        --cert-file=/etc/kubernetes/ssl/etcd.pem 
        --ca-file=/etc/kubernetes/ssl/ca.pem 
        --key-file=/etc/kubernetes/ssl/etcd-key.pem 
        member list


29262d49176888f5: name=etcd3 peerURLs=https://10.6.0.188:2380 clientURLs=https://10.6.0.188:2379 isLeader=false
d4ba1a2871bfa2b0: name=etcd1 peerURLs=https://10.6.0.140:2380 clientURLs=https://10.6.0.140:2379 isLeader=true
eca58ebdf44f63b6: name=etcd2 peerURLs=https://10.6.0.187:2380 clientURLs=https://10.6.0.187:2379 isLeader=false

安装 kubectl 工具

Master 端

# 首先安装 kubectl

wget https://dl.k8s.io/v1.7.3/kubernetes-client-linux-amd64.tar.gz

tar -xzvf kubernetes-client-linux-amd64.tar.gz

cp kubernetes/client/bin/* /usr/local/bin/

chmod a+x /usr/local/bin/kube*


# 验证安装

kubectl version
Client Version: version.Info{Major:"1", Minor:"7", GitVersion:"v1.7.3", GitCommit:"2c2fe6e8278a5db2d15a013987b53968c743f2a1", GitTreeState:"clean", BuildDate:"2017-08-03T07:00:21Z", GoVersion:"go1.8.3", Compiler:"gc", Platform:"linux/amd64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?

创建 admin 证书

kubectl 与 kube-apiserver 的安全端口通信,需要为安全通信提供 TLS 证书和秘钥。

cd /opt/ssl/

vi admin-csr.json


{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "ShenZhen",
      "L": "ShenZhen",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
# 生成 admin 证书和私钥
cd /opt/ssl/

/opt/local/cfssl/cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem 
  -ca-key=/etc/kubernetes/ssl/ca-key.pem 
  -config=/etc/kubernetes/ssl/config.json 
  -profile=kubernetes admin-csr.json | /opt/local/cfssl/cfssljson -bare admin


# 查看生成

[root@k8s-master-1 ssl]# ls admin*
admin.csr  admin-csr.json  admin-key.pem  admin.pem

cp admin*.pem /etc/kubernetes/ssl/

scp admin*.pem 10.6.0.187:/etc/kubernetes/ssl/

scp admin*.pem 10.6.0.188:/etc/kubernetes/ssl/

配置 kubectl kubeconfig 文件

server 配置为 本机IP 各自连接本机的 Api

# 配置 kubernetes 集群

kubectl config set-cluster kubernetes 
  --certificate-authority=/etc/kubernetes/ssl/ca.pem 
  --embed-certs=true 
  --server=https://10.6.0.140:6443


# 配置 客户端认证

kubectl config set-credentials admin 
  --client-certificate=/etc/kubernetes/ssl/admin.pem 
  --embed-certs=true 
  --client-key=/etc/kubernetes/ssl/admin-key.pem



kubectl config set-context kubernetes 
  --cluster=kubernetes 
  --user=admin


kubectl config use-context kubernetes

kubectl config 文件

# kubeconfig 文件在 如下:

/root/.kube

部署 Kubernetes Master 节点

Master 需要部署 kube-apiserver , kube-scheduler , kube-controller-manager 这三个组件。

安装 组件

# 从github 上下载版本

cd /tmp

wget https://dl.k8s.io/v1.7.3/kubernetes-server-linux-amd64.tar.gz

tar -xzvf kubernetes-server-linux-amd64.tar.gz

cd kubernetes

cp -r server/bin/{kube-apiserver,kube-controller-manager,kube-scheduler,kubectl,kube-proxy,kubelet} /usr/local/bin/

创建 kubernetes 证书

cd /opt/ssl

vi kubernetes-csr.json

{
  "CN": "kubernetes",
  "hosts": [
    "127.0.0.1",
    "10.6.0.140",
    "10.6.0.187",
    "10.6.0.188",
    "10.254.0.1",
    "kubernetes",
    "kubernetes.default",
    "kubernetes.default.svc",
    "kubernetes.default.svc.cluster",
    "kubernetes.default.svc.cluster.local"
  ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "ShenZhen",
      "L": "ShenZhen",
      "O": "k8s",
      "OU": "System"
    }
  ]
}


## 这里 hosts 字段中 三个 IP 分别为 127.0.0.1 本机, 10.6.0.140 和 10.6.0.187 为 Master 的IP,多个Master需要写多个 10.254.0.1 为 kubernetes SVC 的 IP, 一般是 部署网络的第一个IP , 如: 10.254.0.1 , 在启动完成后,我们使用   kubectl get svc , 就可以查看到

生成 kubernetes 证书和私钥

/opt/local/cfssl/cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem 
  -ca-key=/etc/kubernetes/ssl/ca-key.pem 
  -config=/etc/kubernetes/ssl/config.json 
  -profile=kubernetes kubernetes-csr.json | /opt/local/cfssl/cfssljson -bare kubernetes

# 查看生成

[root@k8s-master-1 ssl]# ls -lt kubernetes*
-rw-r--r-- 1 root root 1245 7月   4 11:25 kubernetes.csr
-rw------- 1 root root 1679 7月   4 11:25 kubernetes-key.pem
-rw-r--r-- 1 root root 1619 7月   4 11:25 kubernetes.pem
-rw-r--r-- 1 root root  436 7月   4 11:23 kubernetes-csr.json


# 拷贝到目录
cp -r kubernetes* /etc/kubernetes/ssl/

scp -r kubernetes* 10.6.0.187:/etc/kubernetes/ssl/

scp -r kubernetes* 10.6.0.188:/etc/kubernetes/ssl/

配置 kube-apiserver

kubelet 首次启动时向 kube-apiserver 发送 TLS Bootstrapping 请求,kube-apiserver 验证 kubelet 请求中的 token 是否与它配置的 token 一致,如果一致则自动为 kubelet生成证书和秘钥。

# 生成 token

[root@k8s-master-1 ssl]# head -c 16 /dev/urandom | od -An -t x | tr -d ' '
10b459a82af1e16663f25061372fdab4


# 创建 token.csv 文件

cd /opt/ssl

vi token.csv

10b459a82af1e16663f25061372fdab4,kubelet-bootstrap,10001,"system:kubelet-bootstrap"


# 拷贝

cp token.csv /etc/kubernetes/

scp token.csv 10.6.0.187:/etc/kubernetes/

scp token.csv 10.6.0.188:/etc/kubernetes/

创建 kube-apiserver.service 文件

# 自定义 系统 service 文件一般存于 /etc/systemd/system/ 下
# 配置为 各自的本地 IP

vi /etc/systemd/system/kube-apiserver.service

[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target

[Service]
User=root
ExecStart=/usr/local/bin/kube-apiserver 
  --admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota 
  --advertise-address=10.6.0.140 
  --allow-privileged=true 
  --apiserver-count=3 
  --audit-log-maxage=30 
  --audit-log-maxbackup=3 
  --audit-log-maxsize=100 
  --audit-log-path=/var/lib/audit.log 
  --authorization-mode=RBAC 
  --bind-address=10.6.0.140 
  --client-ca-file=/etc/kubernetes/ssl/ca.pem 
  --enable-swagger-ui=true 
  --etcd-cafile=/etc/kubernetes/ssl/ca.pem 
  --etcd-certfile=/etc/kubernetes/ssl/etcd.pem 
  --etcd-keyfile=/etc/kubernetes/ssl/etcd-key.pem 
  --etcd-servers=https://10.6.0.140:2379,https://10.6.0.187:2379,https://10.6.0.188:2379 
  --event-ttl=1h 
  --kubelet-https=true 
  --insecure-bind-address=10.6.0.140 
  --runtime-config=rbac.authorization.k8s.io/v1alpha1 
  --service-account-key-file=/etc/kubernetes/ssl/ca-key.pem 
  --service-cluster-ip-range=10.254.0.0/16 
  --service-node-port-range=30000-32000 
  --tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem 
  --tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem 
  --experimental-bootstrap-token-auth 
  --token-auth-file=/etc/kubernetes/token.csv 
  --v=2
Restart=on-failure
RestartSec=5
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
# 这里面要注意的是 --service-node-port-range=30000-32000
# 这个地方是 映射外部端口时 的端口范围,随机映射也在这个范围内映射,指定映射端口必须也在这个范围内。

启动 kube-apiserver

systemctl daemon-reload
systemctl enable kube-apiserver
systemctl start kube-apiserver
systemctl status kube-apiserver

配置 kube-controller-manager

master 配置为 各自 本地 IP

# 创建 kube-controller-manager.service 文件

vi /etc/systemd/system/kube-controller-manager.service


[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager 
  --address=127.0.0.1 
  --master=http://10.6.0.140:8080 
  --allocate-node-cidrs=true 
  --service-cluster-ip-range=10.254.0.0/16 
  --cluster-cidr=10.233.0.0/16 
  --cluster-name=kubernetes 
  --cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem 
  --cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem 
  --service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem 
  --root-ca-file=/etc/kubernetes/ssl/ca.pem 
  --leader-elect=true 
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

启动 kube-controller-manager

systemctl daemon-reload
systemctl enable kube-controller-manager
systemctl start kube-controller-manager
systemctl status kube-controller-manager

配置 kube-scheduler

master 配置为 各自 本地 IP

# 创建 kube-cheduler.service 文件

vi /etc/systemd/system/kube-scheduler.service


[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/GoogleCloudPlatform/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-scheduler 
  --address=127.0.0.1 
  --master=http://10.6.0.140:8080 
  --leader-elect=true 
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

启动 kube-scheduler

systemctl daemon-reload
systemctl enable kube-scheduler
systemctl start kube-scheduler
systemctl status kube-scheduler

验证 Master 节点

[root@k8s-master-1 ~]# kubectl get componentstatuses
NAME                 STATUS    MESSAGE              ERROR
scheduler            Healthy   ok                   
controller-manager   Healthy   ok                   
etcd-0               Healthy   {"health": "true"}   
etcd-2               Healthy   {"health": "true"}   
etcd-1               Healthy   {"health": "true"} 



[root@k8s-master-2 ~]# kubectl get componentstatuses
NAME                 STATUS    MESSAGE              ERROR
controller-manager   Healthy   ok                   
scheduler            Healthy   ok                   
etcd-2               Healthy   {"health": "true"}   
etcd-0               Healthy   {"health": "true"}   
etcd-1               Healthy   {"health": "true"}  



[root@k8s-master-3 ~]# kubectl get componentstatuses
NAME                 STATUS    MESSAGE              ERROR
scheduler            Healthy   ok                   
controller-manager   Healthy   ok                   
etcd-0               Healthy   {"health": "true"}   
etcd-1               Healthy   {"health": "true"}   
etcd-2               Healthy   {"health": "true"}   

部署 Master Node 部分

Node 部分 需要部署的组件有 docker calico kubectl kubelet kube-proxy 这几个组件。

配置 kubelet

kubelet 启动时向 kube-apiserver 发送 TLS bootstrapping 请求,需要先将 bootstrap token 文件中的 kubelet-bootstrap 用户赋予 system:node-bootstrapper 角色,然后 kubelet 才有权限创建认证请求(certificatesigningrequests)。

# 先创建认证请求
# user 为 master 中 token.csv 文件里配置的用户
# 只需创建一次就可以

kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap

创建 kubelet kubeconfig 文件

server 配置为 master 本机 IP

# 配置集群

kubectl config set-cluster kubernetes 
  --certificate-authority=/etc/kubernetes/ssl/ca.pem 
  --embed-certs=true 
  --server=https://10.6.0.140:6443 
  --kubeconfig=bootstrap.kubeconfig

# 配置客户端认证

kubectl config set-credentials kubelet-bootstrap 
  --token=10b459a82af1e16663f25061372fdab4 
  --kubeconfig=bootstrap.kubeconfig


# 配置关联

kubectl config set-context default 
  --cluster=kubernetes 
  --user=kubelet-bootstrap 
  --kubeconfig=bootstrap.kubeconfig


# 配置默认关联
kubectl config use-context default --kubeconfig=bootstrap.kubeconfig

# 拷贝生成的 bootstrap.kubeconfig 文件

mv bootstrap.kubeconfig /etc/kubernetes/

创建 kubelet.service 文件

# 创建 kubelet 目录

> 配置为 node 本机 IP

mkdir /var/lib/kubelet

vi /etc/systemd/system/kubelet.service


[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/var/lib/kubelet
ExecStart=/usr/local/bin/kubelet 
  --address=10.6.0.140 
  --hostname-override=10.6.0.140 
  --pod-infra-container-image=jicki/pause-amd64:3.0 
  --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig 
  --kubeconfig=/etc/kubernetes/kubelet.kubeconfig 
  --require-kubeconfig 
  --cert-dir=/etc/kubernetes/ssl 
  --cluster_dns=10.254.0.2 
  --cluster_domain=cluster.local. 
  --hairpin-mode promiscuous-bridge 
  --allow-privileged=true 
  --serialize-image-pulls=false 
  --logtostderr=true 
  --v=2
ExecStopPost=/sbin/iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 4194 -j ACCEPT
ExecStopPost=/sbin/iptables -A INPUT -s 172.16.0.0/12 -p tcp --dport 4194 -j ACCEPT
ExecStopPost=/sbin/iptables -A INPUT -s 192.168.0.0/16 -p tcp --dport 4194 -j ACCEPT
ExecStopPost=/sbin/iptables -A INPUT -p tcp --dport 4194 -j DROP
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
# 如上配置:
10.6.0.140       为本机的IP
10.254.0.2       预分配的 dns 地址
cluster.local.   为 kubernetes 集群的 domain
jicki/pause-amd64:3.0  这个是 pod 的基础镜像,既 gcr 的 gcr.io/google_containers/pause-amd64:3.0 镜像, 下载下来修改为自己的仓库中的比较快。

启动 kubelet

systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet
systemctl status kubelet

配置 TLS 认证

# 查看 csr 的名称

[root@k8s-master-1 ~]# kubectl get csr
NAME                                                   AGE       REQUESTOR           CONDITION
node-csr-LkH2ZX9b2kACKmkNbp9PnK6BYAa5fMeh7nWtrGCipYc   58s       kubelet-bootstrap   Pending
node-csr-cxXvnzvukInZrSXT1EJTFaDzERFsuwsR2hCcgWyYZ2o   1m        kubelet-bootstrap   Pending
node-csr-jcQdD_haTRkPMTXwcHeyjQZUt2lb1S4rDeTgKUeQwgM   1m        kubelet-bootstrap   Pending


# 增加 认证

[root@k8s-master-1 ~]# kubectl certificate approve node-csr-LkH2ZX9b2kACKmkNbp9PnK6BYAa5fMeh7nWtrGCipYc
certificatesigningrequest "node-csr-LkH2ZX9b2kACKmkNbp9PnK6BYAa5fMeh7nWtrGCipYc" approved
[root@k8s-master-1 ~]# 
[root@k8s-master-1 ~]# kubectl certificate approve node-csr-cxXvnzvukInZrSXT1EJTFaDzERFsuwsR2hCcgWyYZ2o
certificatesigningrequest "node-csr-cxXvnzvukInZrSXT1EJTFaDzERFsuwsR2hCcgWyYZ2o" approved
[root@k8s-master-1 ~]# 
[root@k8s-master-1 ~]# kubectl certificate approve node-csr-jcQdD_haTRkPMTXwcHeyjQZUt2lb1S4rDeTgKUeQwgM
certificatesigningrequest "node-csr-jcQdD_haTRkPMTXwcHeyjQZUt2lb1S4rDeTgKUeQwgM" approved

验证 nodes

[root@k8s-master-1 ~]# kubectl get nodes
NAME         STATUS    AGE       VERSION
10.6.0.140   Ready     27s       v1.7.3
10.6.0.187   Ready     20s       v1.7.3
10.6.0.188   Ready     37s       v1.7.3


# 成功以后会自动生成配置文件与密钥

# 配置文件

ls /etc/kubernetes/kubelet.kubeconfig   
/etc/kubernetes/kubelet.kubeconfig


# 密钥文件

ls /etc/kubernetes/ssl/kubelet*
/etc/kubernetes/ssl/kubelet-client.crt  /etc/kubernetes/ssl/kubelet.crt
/etc/kubernetes/ssl/kubelet-client.key  /etc/kubernetes/ssl/kubelet.key

配置 kube-proxy

创建 kube-proxy 证书

# 证书方面由于我们node端没有装 cfssl
# 我们回到 master 端 机器 去配置证书,然后拷贝过来

[root@k8s-master-1 ~]# cd /opt/ssl


vi kube-proxy-csr.json

{
  "CN": "system:kube-proxy",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "ShenZhen",
      "L": "ShenZhen",
      "O": "k8s",
      "OU": "System"
    }
  ]
}

生成 kube-proxy 证书和私钥

/opt/local/cfssl/cfssl gencert -ca=/etc/kubernetes/ssl/ca.pem 
  -ca-key=/etc/kubernetes/ssl/ca-key.pem 
  -config=/etc/kubernetes/ssl/config.json 
  -profile=kubernetes  kube-proxy-csr.json | /opt/local/cfssl/cfssljson -bare kube-proxy

# 查看生成
ls kube-proxy*
kube-proxy.csr  kube-proxy-csr.json  kube-proxy-key.pem  kube-proxy.pem

# 拷贝到目录
cp kube-proxy*.pem /etc/kubernetes/ssl/

scp kube-proxy*.pem 10.6.0.187:/etc/kubernetes/ssl/

scp kube-proxy*.pem 10.6.0.188:/etc/kubernetes/ssl/

创建 kube-proxy kubeconfig 文件

server 配置为各自 本机IP

# 配置集群

kubectl config set-cluster kubernetes 
  --certificate-authority=/etc/kubernetes/ssl/ca.pem 
  --embed-certs=true 
  --server=https://10.6.0.140:6443 
  --kubeconfig=kube-proxy.kubeconfig


# 配置客户端认证

kubectl config set-credentials kube-proxy 
  --client-certificate=/etc/kubernetes/ssl/kube-proxy.pem 
  --client-key=/etc/kubernetes/ssl/kube-proxy-key.pem 
  --embed-certs=true 
  --kubeconfig=kube-proxy.kubeconfig


# 配置关联

kubectl config set-context default 
  --cluster=kubernetes 
  --user=kube-proxy 
  --kubeconfig=kube-proxy.kubeconfig



# 配置默认关联
kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

# 拷贝到目录
mv kube-proxy.kubeconfig /etc/kubernetes/

创建 kube-proxy.service 文件

配置为 各自的 IP

# 创建 kube-proxy 目录

mkdir -p /var/lib/kube-proxy


vi /etc/systemd/system/kube-proxy.service

[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=network.target

[Service]
WorkingDirectory=/var/lib/kube-proxy
ExecStart=/usr/local/bin/kube-proxy 
  --bind-address=10.6.0.140 
  --hostname-override=10.6.0.140 
  --cluster-cidr=10.254.0.0/16 
  --kubeconfig=/etc/kubernetes/kube-proxy.kubeconfig 
  --logtostderr=true 
  --v=2
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

启动 kube-proxy

systemctl daemon-reload
systemctl enable kube-proxy
systemctl start kube-proxy
systemctl status kube-proxy

部署 Node 节点

Node 节点 基于 Nginx 负载 API 做 Master HA

# master 之间除 api server 以外其他组件通过 etcd 选举,api server 默认不作处理;在每个 node 上启动一个 nginx,每个 nginx 反向代理所有 api server,node 上 kubelet、kube-proxy 连接本地的 nginx 代理端口,当 nginx 发现无法连接后端时会自动踢掉出问题的 api server,从而实现 api server 的 HA
cd /tmp

wget https://dl.k8s.io/v1.7.3/kubernetes-server-linux-amd64.tar.gz

tar -xzvf kubernetes-server-linux-amd64.tar.gz

cd kubernetes

cp -r server/bin/{kube-proxy,kubelet} /usr/local/bin/
# kubelet

# 首先 创建 kubelet kubeconfig 文件


kubectl config set-cluster kubernetes 
  --certificate-authority=/etc/kubernetes/ssl/ca.pem 
  --embed-certs=true 
  --server=https://127.0.0.1:6443 
  --kubeconfig=bootstrap.kubeconfig


# 配置客户端认证

kubectl config set-credentials kubelet-bootstrap 
  --token=10b459a82af1e16663f25061372fdab4 
  --kubeconfig=bootstrap.kubeconfig


# 配置关联

kubectl config set-context default 
  --cluster=kubernetes 
  --user=kubelet-bootstrap 
  --kubeconfig=bootstrap.kubeconfig


# 配置默认关联
kubectl config use-context default --kubeconfig=bootstrap.kubeconfig

# 拷贝生成的 bootstrap.kubeconfig 文件

mv bootstrap.kubeconfig /etc/kubernetes/
# 创建 kube-proxy kubeconfig 文件


kubectl config set-cluster kubernetes 
  --certificate-authority=/etc/kubernetes/ssl/ca.pem 
  --embed-certs=true 
  --server=https://127.0.0.1:6443 
  --kubeconfig=kube-proxy.kubeconfig


# 配置客户端认证

kubectl config set-credentials kube-proxy 
  --client-certificate=/etc/kubernetes/ssl/kube-proxy.pem 
  --client-key=/etc/kubernetes/ssl/kube-proxy-key.pem 
  --embed-certs=true 
  --kubeconfig=kube-proxy.kubeconfig


# 配置关联

kubectl config set-context default 
  --cluster=kubernetes 
  --user=kube-proxy 
  --kubeconfig=kube-proxy.kubeconfig



# 配置默认关联
kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

# 拷贝到目录
mv kube-proxy.kubeconfig /etc/kubernetes/

创建Nginx 代理

在每个 node 都必须创建一个 Nginx 代理, 这里特别注意, 当 Master 也做为 Node 的时候 不需要配置 Nginx-proxy

# 创建配置目录
mkdir -p /etc/nginx

# 写入代理配置

cat << EOF >> /etc/nginx/nginx.conf
error_log stderr notice;

worker_processes auto;
events {
  multi_accept on;
  use epoll;
  worker_connections 1024;
}

stream {
    upstream kube_apiserver {
        least_conn;
        server 10.6.0.140:6443;
        server 10.6.0.187:6443;
        server 10.6.0.188:6443;
    }

    server {
        listen        0.0.0.0:6443;
        proxy_pass    kube_apiserver;
        proxy_timeout 10m;
        proxy_connect_timeout 1s;
    }
}
EOF
# 配置 Nginx 基于 docker 进程,然后配置 systemd 来启动

cat << EOF >> /etc/systemd/system/nginx-proxy.service

[Unit]
Description=kubernetes apiserver docker wrapper
Wants=docker.socket
After=docker.service

[Service]
User=root
PermissionsStartOnly=true
ExecStart=/usr/bin/docker run -p 6443:6443 \
                              -v /etc/nginx:/etc/nginx \
                              --name nginx-proxy \
                              --net=host \
                              --restart=on-failure:5 \
                              --memory=512M \
                              nginx:1.13.3-alpine
ExecStartPre=-/usr/bin/docker rm -f nginx-proxy
ExecStop=/usr/bin/docker stop nginx-proxy
Restart=always
RestartSec=15s
TimeoutStartSec=30s

[Install]
WantedBy=multi-user.target
EOF
# 启动 Nginx

systemctl daemon-reload
systemctl start nginx-proxy
systemctl enable nginx-proxy


# 重启 Node 的 kubelet 与 kube-proxy

systemctl restart kubelet
systemctl status kubelet

systemctl restart kube-proxy
systemctl status kube-proxy

Master 配置 TLS 认证

# 查看 csr 的名称

[root@k8s-master-1 ~]# kubectl get csr
NAME                                                   AGE       REQUESTOR           CONDITION
node-csr-LkH2ZX9b2kACKmkNbp9PnK6BYAa5fMeh7nWtrGCipYc   58s       kubelet-bootstrap   Pending
node-csr-cxXvnzvukInZrSXT1EJTFaDzERFsuwsR2hCcgWyYZ2o   1m        kubelet-bootstrap   Pending
node-csr-jcQdD_haTRkPMTXwcHeyjQZUt2lb1S4rDeTgKUeQwgM   1m        kubelet-bootstrap   Pending


# 增加 认证

[root@k8s-master-1 ~]# kubectl certificate approve node-csr-LkH2ZX9b2kACKmkNbp9PnK6BYAa5fMeh7nWtrGCipYc
certificatesigningrequest "node-csr-LkH2ZX9b2kACKmkNbp9PnK6BYAa5fMeh7nWtrGCipYc" approved
[root@k8s-master-1 ~]# 
[root@k8s-master-1 ~]# kubectl certificate approve node-csr-cxXvnzvukInZrSXT1EJTFaDzERFsuwsR2hCcgWyYZ2o
certificatesigningrequest "node-csr-cxXvnzvukInZrSXT1EJTFaDzERFsuwsR2hCcgWyYZ2o" approved
[root@k8s-master-1 ~]# 
[root@k8s-master-1 ~]# kubectl certificate approve node-csr-jcQdD_haTRkPMTXwcHeyjQZUt2lb1S4rDeTgKUeQwgM
certificatesigningrequest "node-csr-jcQdD_haTRkPMTXwcHeyjQZUt2lb1S4rDeTgKUeQwgM" approved

安装 docker

# 导入 yum 源

# 安装 yum-config-manager

yum -y install yum-utils

# 导入
yum-config-manager 
    --add-repo 
    https://download.docker.com/linux/centos/docker-ce.repo


# 更新 repo
yum makecache

# 安装

yum install docker-ce -y

更改docker 配置

# 修改配置

vi /usr/lib/systemd/system/docker.service

[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target firewalld.service
Wants=network-online.target

[Service]
Type=notify
ExecStart=/usr/bin/dockerd $DOCKER_NETWORK_OPTIONS $DOCKER_OPTS $DOCKER_DNS_OPTIONS
ExecReload=/bin/kill -s HUP $MAINPID
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TimeoutStartSec=0
Delegate=yes
KillMode=process
Restart=on-failure
StartLimitBurst=3
StartLimitInterval=60s

[Install]
WantedBy=multi-user.target
# 修改其他配置

mkdir -p /usr/lib/systemd/system/docker.service.d/


cat >> /usr/lib/systemd/system/docker.service.d/docker-options.conf << EOF
[Service]
Environment="DOCKER_OPTS=--insecure-registry=10.254.0.0/16 --graph=/opt/docker --registry-mirror=http://b438f72b.m.daocloud.io --iptables=false"
EOF
# 重新读取配置,启动 docker 
systemctl daemon-reload
systemctl start docker
systemctl enable docker

Calico 网络

修改 kubelet.service

vi /etc/systemd/system/kubelet.service

# 增加 如下配置

  --network-plugin=cni 


# 重新加载配置
systemctl daemon-reload
systemctl restart kubelet.service
systemctl status kubelet.service

修改 kube-proxy.service

vi /etc/systemd/system/kube-proxy.service

# 增加如下配置:
    --masquerade-all 

# 重新加载配置
systemctl daemon-reload
systemctl restart kube-proxy.service
systemctl status kube-proxy.service

安装 Calico

官网地址 http://docs.projectcalico.org/v2.3/getting-started/kubernetes/installation/hosted/hosted

# 下载 yaml 文件

wget http://docs.projectcalico.org/v2.3/getting-started/kubernetes/installation/hosted/calico.yaml

wget http://docs.projectcalico.org/v2.3/getting-started/kubernetes/installation/rbac.yaml


# 下载 镜像

# 国外镜像 有墙
quay.io/calico/node:v1.3.0
quay.io/calico/cni:v1.9.1
quay.io/calico/kube-policy-controller:v0.6.0


# 国内镜像
jicki/node:v1.3.0
jicki/cni:v1.9.1
jicki/kube-policy-controller:v0.6.0

配置 calico

vi calico.yaml

# 注意修改如下选项:


  etcd_endpoints: "https://10.6.0.140:2379,https://10.6.0.187:2379,https://10.6.0.188:2379"

    etcd_ca: "/calico-secrets/etcd-ca"  
    etcd_cert: "/calico-secrets/etcd-cert"
    etcd_key: "/calico-secrets/etcd-key"  


# 这里面要写入 base64 的信息



data:
  etcd-key: (cat /etc/kubernetes/ssl/etcd-key.pem | base64 | tr -d 'n')
  etcd-cert: (cat /etc/kubernetes/ssl/etcd.pem | base64 | tr -d 'n')
  etcd-ca: (cat /etc/kubernetes/ssl/ca.pem | base64 | tr -d 'n')


    - name: CALICO_IPV4POOL_CIDR
      value: "10.233.0.0/16"

导入 yaml 文件

[root@k8s-master-1 ~]# kubectl apply -f calico.yaml 
configmap "calico-config" created
secret "calico-etcd-secrets" created
daemonset "calico-node" created
deployment "calico-policy-controller" created
serviceaccount "calico-policy-controller" created
serviceaccount "calico-node" created


[root@k8s-master-1 ~]# kubectl apply -f rbac.yaml
clusterrole "calico-policy-controller" created
clusterrolebinding "calico-policy-controller" created
clusterrole "calico-node" created
clusterrolebinding "calico-node" created

验证 Calico

[root@k8s-master-1 ~]# kubectl get pods -n kube-system 
NAME                                       READY     STATUS    RESTARTS   AGE
calico-node-2dsq4                          2/2       Running   0          6m
calico-node-9ktvk                          2/2       Running   0          6m
calico-node-gwmx5                          2/2       Running   0          6m
calico-policy-controller-458850194-pn65p   1/1       Running   0          6m

安装 Calicoctl

cd /usr/local/bin/

wget -c  https://github.com/projectcalico/calicoctl/releases/download/v1.3.0/calicoctl

chmod +x calicoctl



## 创建 calicoctl 配置文件

# 配置文件, 在 安装了 calico 网络的 机器下

mkdir /etc/calico

vi /etc/calico/calicoctl.cfg


apiVersion: v1
kind: calicoApiConfig
metadata:
spec:
  datastoreType: "etcdv2"
  etcdEndpoints: "https://10.6.0.140:2379,https://10.6.0.187:2379,https://10.6.0.188:2379"
  etcdKeyFile: "/etc/kubernetes/ssl/etcd-key.pem"
  etcdCertFile: "/etc/kubernetes/ssl/etcd.pem"
  etcdCACertFile: "/etc/kubernetes/ssl/ca.pem"




# 查看 calico 状态

[root@k8s-master-1 ~]# calicoctl node status
Calico process is running.

IPv4 BGP status
+--------------+-------------------+-------+----------+-------------+
| PEER ADDRESS |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+--------------+-------------------+-------+----------+-------------+
| 10.6.0.188   | node-to-node mesh | up    | 10:11:59 | Established |
| 10.6.0.187   | node-to-node mesh | up    | 10:16:32 | Established |
+--------------+-------------------+-------+----------+-------------+

IPv6 BGP status
No IPv6 peers found.

测试集群

# 创建一个 nginx deplyment

apiVersion: extensions/v1beta1 
kind: Deployment 
metadata: 
  name: nginx-dm
spec: 
  replicas: 2
  template: 
    metadata: 
      labels: 
        name: nginx 
    spec: 
      containers: 
        - name: nginx 
          image: nginx:alpine 
          imagePullPolicy: IfNotPresent
          ports: 
            - containerPort: 80

---

apiVersion: v1 
kind: Service
metadata: 
  name: nginx-svc 
spec: 
  ports: 
    - port: 80
      targetPort: 80
      protocol: TCP 
  selector: 
    name: nginx
[root@k8s-master-1 ~]# kubectl get pods
NAME                        READY     STATUS    RESTARTS   AGE
nginx-dm-2214564181-lxff5   1/1       Running   0          14m
nginx-dm-2214564181-qm1bp   1/1       Running   0          14m


[root@k8s-master-1 ~]# kubectl get deployment
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx-dm   2         2         2            2           14m


[root@k8s-master-1 ~]# kubectl get svc
NAME         CLUSTER-IP      EXTERNAL-IP   PORT(S)   AGE
kubernetes   10.254.0.1      <none>        443/TCP   4h
nginx-svc    10.254.129.54   <none>        80/TCP    15m
# 在 node 里 curl

[root@k8s-node-1 ~]# curl 10.254.129.54
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

配置 KubeDNS

官方 github yaml 相关 https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns

下载镜像

# 官方镜像
gcr.io/google_containers/k8s-dns-sidecar-amd64:1.14.4
gcr.io/google_containers/k8s-dns-kube-dns-amd64:1.14.4
gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64:1.14.4


# 我的镜像
jicki/k8s-dns-sidecar-amd64:1.14.4
jicki/k8s-dns-kube-dns-amd64:1.14.4
jicki/k8s-dns-dnsmasq-nanny-amd64:1.14.4

下载 yaml 文件

curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/kubedns-cm.yaml


curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/kubedns-sa.yaml


curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/kubedns-controller.yaml.base


curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/kubedns-svc.yaml.base


# 修改后缀

mv kubedns-controller.yaml.base kubedns-controller.yaml

mv kubedns-svc.yaml.base kubedns-svc.yaml

系统预定义的 RoleBinding

预定义的 RoleBinding system:kube-dns 将 kube-system 命名空间的 kube-dns ServiceAccount 与 system:kube-dns Role 绑定, 该 Role 具有访问 kube-apiserver DNS 相关 API 的权限;

[root@k8s-master-1 kubedns]# kubectl get clusterrolebindings system:kube-dns -o yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: 2017-07-04T04:15:13Z
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-dns
  resourceVersion: "106"
  selfLink: /apis/rbac.authorization.k8s.io/v1beta1/clusterrolebindings/system%3Akube-dns
  uid: 60c1e0e1-606f-11e7-b212-d4ae52d1f0c9
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-dns
subjects:
- kind: ServiceAccount
  name: kube-dns
  namespace: kube-system

修改 kubedns-svc.yaml

# kubedns-svc.yaml 中 clusterIP: __PILLAR__DNS__SERVER__ 修改为我们之前定义的 dns IP 10.254.0.2

cat kubedns-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: kube-dns
  namespace: kube-system
  labels:
    k8s-app: kube-dns
    kubernetes.io/cluster-service: "true"
    addonmanager.kubernetes.io/mode: Reconcile
    kubernetes.io/name: "KubeDNS"
spec:
  selector:
    k8s-app: kube-dns
  clusterIP: 10.254.0.2
  ports:
  - name: dns
    port: 53
    protocol: UDP
  - name: dns-tcp
    port: 53
    protocol: TCP

修改 kubedns-controller.yaml

1. # 修改 --domain=__PILLAR__DNS__DOMAIN__.   为 我们之前 预定的 domain 名称 --domain=cluster.local.

2. # 修改 --server=/__PILLAR__DNS__DOMAIN__/127.0.0.1#10053  中 domain 为我们之前预定的 --server=/cluster.local./127.0.0.1#10053

3. # 修改 --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.__PILLAR__DNS__DOMAIN__, 中的 domain 为我们之前预定的  --probe=kubedns,127.0.0.1:10053,kubernetes.default.svc.cluster.local.,

4. # 修改 --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.__PILLAR__DNS__DOMAIN__,  中的 domain 为我们之前预定的  --probe=dnsmasq,127.0.0.1:53,kubernetes.default.svc.cluster.local.,

导入 yaml 文件

# 替换所有的 images

sed -i 's/gcr.io/google_containers/jicki/g' *

# 导入

[root@k8s-master-1 kubedns]# kubectl create -f .
configmap "kube-dns" created
deployment "kube-dns" created
serviceaccount "kube-dns" created
service "kube-dns" created

查看 kubedns 服务

[root@k8s-master-1 kubedns]# kubectl get all --namespace=kube-system
NAME                                          READY     STATUS    RESTARTS   AGE
po/calico-node-2dsq4                          2/2       Running   0          15h
po/calico-node-9ktvk                          2/2       Running   0          15h
po/calico-node-gwmx5                          2/2       Running   0          15h
po/calico-policy-controller-458850194-pn65p   1/1       Running   0          15h
po/kube-dns-1511229508-jxkvs                  3/3       Running   0          4m

NAME           CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
svc/kube-dns   10.254.0.2   <none>        53/UDP,53/TCP   4m

NAME                              DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
deploy/calico-policy-controller   1         1         1            1           15h
deploy/kube-dns                   1         1         1            1           4m

NAME                                    DESIRED   CURRENT   READY     AGE
rs/calico-policy-controller-458850194   1         1         1         15h
rs/kube-dns-1511229508                  1         1         1         4m

验证 dns 服务

在验证 dns 之前,在 dns 未部署之前创建的 pod 与 deployment 等,都必须删除,重新部署,否则无法解析

# 导入之前的 nginx-dm yaml文件

[root@k8s-master-1 ~]# kubectl get pods -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP              NODE
nginx-dm-2214564181-0ctcx   1/1       Running   0          27s       10.233.168.1    10.6.0.188
nginx-dm-2214564181-brz79   1/1       Running   0          3m        10.233.196.2    10.6.0.140
nginx-dm-2214564181-z8whk   1/1       Running   0          3m        10.233.182.65   10.6.0.187

[root@k8s-master-1 ~]# kubectl get svc -o wide
NAME         CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE       SELECTOR
kubernetes   10.254.0.1     <none>        443/TCP   16h       <none>
nginx-svc    10.254.140.2   <none>        80/TCP    3m        name=nginx


# 创建一个 pods 来测试一下 nameserver

apiVersion: v1
kind: Pod
metadata:
  name: alpine
spec:
  containers:
  - name: alpine
    image: alpine
    command:
    - sh
    - -c
    - while true; do sleep 1; done



# 查看 pods
[root@k8s-master-1 ~]# kubectl get pods
NAME                        READY     STATUS    RESTARTS   AGE
alpine                      1/1       Running   0          5s
nginx-dm-2214564181-0ctcx   1/1       Running   0          7m
nginx-dm-2214564181-brz79   1/1       Running   0          10m
nginx-dm-2214564181-z8whk   1/1       Running   0          10m



# 测试

[root@k8s-master-1 ~]# kubectl exec -it alpine nslookup nginx-svc
nslookup: can't resolve '(null)': Name does not resolve

Name:      nginx-svc
Address 1: 10.254.140.2 nginx-svc.default.svc.cluster.local


[root@k8s-master-1 ~]# kubectl exec -it alpine ping nginx-svc    
PING nginx-svc (10.254.140.2): 56 data bytes

部署 Ingress 与 Dashboard

部署 dashboard && heapster

官方 dashboard 的github https://github.com/kubernetes/dashboard

下载 dashboard 镜像

# 官方镜像
gcr.io/google_containers/kubernetes-dashboard-amd64:v1.6.3

# 国内镜像
jicki/kubernetes-dashboard-amd64:v1.6.3

下载 yaml 文件

curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dashboard/dashboard-controller.yaml


curl -O https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dashboard/dashboard-service.yaml



# 因为开启了 RBAC 所以这里需要创建一个 RBAC 认证

vi dashboard-rbac.yaml


apiVersion: v1
kind: ServiceAccount
metadata:
  name: dashboard
  namespace: kube-system

---

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1alpha1
metadata:
  name: dashboard
subjects:
  - kind: ServiceAccount
    name: dashboard
    namespace: kube-system
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

下载 heapster 镜像

# 官方文件
gcr.io/google_containers/heapster-amd64:v1.3.0
gcr.io/google_containers/heapster-influxdb-amd64:v1.1.1


# 本地文件
jicki/heapster-amd64:v1.3.0
jicki/heapster-influxdb-amd64:v1.1.1

下载 heapster 文件

官方网站 https://github.com/kubernetes/heapster/blob/master/docs/influxdb.md

curl -O https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/influxdb/influxdb.yaml

curl -O https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/influxdb/heapster.yaml

curl -O https://raw.githubusercontent.com/kubernetes/heapster/master/deploy/kube-config/rbac/heapster-rbac.yaml

修改 influxdb 配置

# influxdb.yaml 记录存储data数据

# 如下:

        volumeMounts:
        - mountPath: /data
          name: influxdb-storage
      volumes:
      - name: influxdb-storage
        emptyDir: {}


# volumes 请自行修改为 共享存储 或者 本地目录

导入 yaml

# 替换所有的 images

sed -i 's/gcr.io/google_containers/jicki/g' *




# dashboard-controller.yaml 增加 rbac 授权


# 在第二个 spec 下面 增加

    spec:
      serviceAccountName: dashboard



# 导入文件

[root@k8s-master-1 dashboard]# kubectl apply -f .
deployment "kubernetes-dashboard" created
serviceaccount "dashboard" created
clusterrolebinding "dashboard" created
service "kubernetes-dashboard" created



[root@k8s-master-1 heapster]# kubectl apply -f .
clusterrolebinding "heapster" created
serviceaccount "heapster" created
deployment "heapster" created
service "heapster" created
deployment "monitoring-influxdb" created
service "monitoring-influxdb" created



# 查看 svc 与 pod

[root@k8s-master-1 ~]# kubectl get svc -n kube-system
NAME                  CLUSTER-IP       EXTERNAL-IP   PORT(S)         AGE
heapster              10.254.231.18    <none>        80/TCP          13s
kube-dns              10.254.0.2       <none>        53/UDP,53/TCP   1h
monitoring-influxdb   10.254.240.245   <none>        8086/TCP        13s

部署 Nginx Ingress

Kubernetes 暴露服务的方式目前只有三种:LoadBlancer Service、NodePort Service、Ingress; 什么是 Ingress ? Ingress 就是利用 Nginx Haproxy 等负载均衡工具来暴露 Kubernetes 服务。

官方 Nginx Ingress github https://github.com/kubernetes/ingress/tree/master/examples/deployment/nginx

配置 调度 node

# ingress 有多种方式 1.  deployment 自由调度 replicas
                     2.  daemonset 全局调度 分配到所有node里


#  deployment 自由调度过程中,由于我们需要 约束 controller 调度到指定的 node 中,所以需要对 node 进行 label 标签


# 默认如下:
[root@k8s-master-1 ingress]# kubectl get nodes
NAME         STATUS    AGE       VERSION
10.6.0.140   Ready     18h       v1.7.3
10.6.0.187   Ready     18h       v1.7.3
10.6.0.188   Ready     18h       v1.7.3


# 对 140 与 187 打上 label

[root@k8s-master-1 ingress]# kubectl label nodes 10.6.0.140 ingress=proxy
node "10.6.0.140" labeled
[root@k8s-master-1 ingress]# kubectl label nodes 10.6.0.187 ingress=proxy
node "10.6.0.187" labeled


# 打完标签以后

[root@k8s-master-1 ingress]# kubectl get nodes --show-labels
NAME         STATUS    AGE       VERSION   LABELS
10.6.0.140   Ready     18h       v1.7.3    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=proxy,kubernetes.io/hostname=10.6.0.140
10.6.0.187   Ready     18h       v1.7.3    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=proxy,kubernetes.io/hostname=10.6.0.187
10.6.0.188   Ready     18h       v1.7.3    beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/hostname=10.6.0.188
# 下载镜像

# 官方镜像
gcr.io/google_containers/defaultbackend:1.0
gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.11

# 国内镜像
jicki/defaultbackend:1.0
jicki/nginx-ingress-controller:0.9.0-beta.11
# 部署 Nginx  backend , Nginx backend 用于统一转发 没有的域名 到指定页面。

curl -O https://raw.githubusercontent.com/kubernetes/ingress/master/examples/deployment/nginx/default-backend.yaml


# 直接导入既可, 这里不需要修改

[root@k8s-master-1 ingress]# kubectl apply -f default-backend.yaml 
deployment "default-http-backend" created
service "default-http-backend" created



# 查看服务
[root@k8s-master-1 ingress]# kubectl get deployment -n kube-system default-http-backend
NAME                   DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
default-http-backend   1         1         1            1           36s
# 部署 Ingress RBAC 认证

curl -O https://raw.githubusercontent.com/kubernetes/ingress/master/examples/rbac/nginx/nginx-ingress-controller-rbac.yml


# 修改 namespace

sed -i 's/namespace: nginx-ingress/namespace: kube-system/g' nginx-ingress-controller-rbac.yml


# 导入 yaml 文件

[root@k8s-master-1 ingress]# kubectl apply -f nginx-ingress-controller-rbac.yml 
namespace "nginx-ingress" created
serviceaccount "nginx-ingress-serviceaccount" created
clusterrole "nginx-ingress-clusterrole" created
role "nginx-ingress-role" created
rolebinding "nginx-ingress-role-nisa-binding" created
clusterrolebinding "nginx-ingress-clusterrole-nisa-binding" created
# 部署 Ingress Controller 组件

# 下载 yaml 文件

curl -O https://raw.githubusercontent.com/kubernetes/ingress/master/examples/deployment/nginx/nginx-ingress-controller.yaml

# 上面 对 两个 node 打了 label 所以配置 replicas: 2
# 修改 yaml 文件 增加 rbac 认证 , hostNetwork  还有 nodeSelector, 第二个 spec 下 增加。

spec:
  replicas: 2
  ....
    spec:
      hostNetwork: true
      serviceAccountName: nginx-ingress-serviceaccount
      nodeSelector:
        ingress: proxy
    ....
# 导入 yaml 文件

[root@k8s-master-1 ingress]# kubectl apply -f nginx-ingress-controller.yaml
deployment "nginx-ingress-controller" created


# 查看服务,可以看到这两个 pods 被分别调度到 140 与 187 中
[root@k8s-master-1 ingress]# kubectl get pods -n kube-system -o wide
NAME                                       READY     STATUS    RESTARTS   AGE       IP             NODE
nginx-ingress-controller-190167013-9j5kd   1/1       Running   0          45s       10.6.0.140     10.6.0.140
nginx-ingress-controller-190167013-n66qd   1/1       Running   0          45s       10.6.0.187     10.6.0.187
# 查看我们原有的 svc

[root@k8s-master-1 dashboard]# kubectl get svc -n kube-system kubernetes-dashboard
NAME                   CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes-dashboard   10.254.243.198   <none>        80/TCP    21s


# 创建 yaml 文件

vi dashboard-ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: dashboard-ingress
  namespaces: kube-system
spec:
  rules:
  - host: dashboard.jicki.me
    http:
      paths:
      - backend:
          serviceName: kubernetes-dashboard
          servicePort: 80



# 导入 yaml

[root@k8s-master-1 dashboard]# kubectl apply -f dashboard-ingress.yaml 
ingress "dashboard-ingress" created



# 查看 ingress

[root@k8s-master-1 dashboard]# kubectl get ingress -n kube-system -o wide
NAME                HOSTS                ADDRESS                 PORTS     AGE
dashboard-ingress   dashboard.jicki.me   10.6.0.140,10.6.0.187   80        1h


# 测试访问

[root@k8s-master-1 dashboard]# curl -I dashboard.jicki.me
HTTP/1.1 200 OK
Server: nginx/1.13.2
Date: Thu, 06 Jul 2017 06:32:00 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 848
Connection: keep-alive
Accept-Ranges: bytes
Cache-Control: no-store
Last-Modified: Tue, 16 May 2017 12:53:01 GMT

未分类

破坏性测试

破坏性测试,手动重启服务器,查看各服务间的恢复以及问题

[root@k8s-master-1 ~]# kubectl get nodes
NAME         STATUS    AGE       VERSION
10.6.0.140   Ready     20h       v1.7.3
10.6.0.187   Ready     20h       v1.7.3
10.6.0.188   Ready     20h       v1.7.3



[root@k8s-master-1 nginx]# etcdctl --endpoints=https://10.6.0.140:2379 
>         --cert-file=/etc/kubernetes/ssl/etcd.pem 
>         --ca-file=/etc/kubernetes/ssl/ca.pem 
>         --key-file=/etc/kubernetes/ssl/etcd-key.pem 
>         member list
2017-08-08 02:36:38.187672 I | warning: ignoring ServerName for user-provided CA for backwards compatibility is deprecated
29262d49176888f5: name=etcd3 peerURLs=https://10.6.0.188:2380 clientURLs=https://10.6.0.188:2379 isLeader=false
d4ba1a2871bfa2b0: name=etcd1 peerURLs=https://10.6.0.140:2380 clientURLs=https://10.6.0.140:2379 isLeader=true
eca58ebdf44f63b6: name=etcd2 peerURLs=https://10.6.0.187:2380 clientURLs=https://10.6.0.187:2379 isLeader=false


[root@k8s-master-1 ~]# reboot
PolicyKit daemon disconnected from the bus.
We are no longer a registered authentication agent.
# 服务器重启

[root@k8s-master-2 ~]# kubectl get nodes
NAME         STATUS     AGE       VERSION
10.6.0.140   NotReady   20h       v1.7.3
10.6.0.187   Ready      20h       v1.7.3
10.6.0.188   Ready      20h       v1.7.3



[root@k8s-master-2 ~]# etcdctl --endpoints=https://10.6.0.187:2379 
>         --cert-file=/etc/kubernetes/ssl/etcd.pem 
>         --ca-file=/etc/kubernetes/ssl/ca.pem 
>         --key-file=/etc/kubernetes/ssl/etcd-key.pem 
>         member list
2017-08-08 02:34:00.132653 I | warning: ignoring ServerName for user-provided CA for backwards compatibility is deprecated
29262d49176888f5: name=etcd3 peerURLs=https://10.6.0.188:2380 clientURLs=https://10.6.0.188:2379 isLeader=false
d4ba1a2871bfa2b0: name=etcd1 peerURLs=https://10.6.0.140:2380 clientURLs=https://10.6.0.140:2379 isLeader=false
eca58ebdf44f63b6: name=etcd2 peerURLs=https://10.6.0.187:2380 clientURLs=https://10.6.0.187:2379 isLeader=true



# 等待服务器重启以后

[root@k8s-master-2 ~]# kubectl get nodes
NAME         STATUS    AGE       VERSION
10.6.0.140   Ready     20h       v1.7.3
10.6.0.187   Ready     20h       v1.7.3
10.6.0.188   Ready     20h       v1.7.3


[root@k8s-master-2 ~]# kubectl get pods -o wide
NAME                        READY     STATUS    RESTARTS   AGE       IP              NODE
alpine                      1/1       Running   1          4h        10.233.196.8    10.6.0.140
nginx-dm-2214564181-0ctcx   1/1       Running   0          4h        10.233.168.4    10.6.0.188
nginx-dm-2214564181-brz79   1/1       Running   1          4h        10.233.196.7    10.6.0.140
nginx-dm-2214564181-z8whk   1/1       Running   0          4h        10.233.182.65   10.6.0.187


[root@k8s-master-2 ~]# etcdctl --endpoints=https://10.6.0.140:2379 
>         --cert-file=/etc/kubernetes/ssl/etcd.pem 
>         --ca-file=/etc/kubernetes/ssl/ca.pem 
>         --key-file=/etc/kubernetes/ssl/etcd-key.pem 
>         member list
2017-08-08 02:39:52.830072 I | warning: ignoring ServerName for user-provided CA for backwards compatibility is deprecated
29262d49176888f5: name=etcd3 peerURLs=https://10.6.0.188:2380 clientURLs=https://10.6.0.188:2379 isLeader=true
d4ba1a2871bfa2b0: name=etcd1 peerURLs=https://10.6.0.140:2380 clientURLs=https://10.6.0.140:2379 isLeader=false
eca58ebdf44f63b6: name=etcd2 peerURLs=https://10.6.0.187:2380 clientURLs=https://10.6.0.187:2379 isLeader=false

Kubernetes(k8s) EmptyDir、HostPath、ConfigMap和Secret等几种存储类型介绍

一个运行中的容器,缺省情况下,对文件系统的写入,都是发生在其分层文件系统的可写层的,一旦容器运行结束,所有写入都会被丢弃。因此需要对持久化支持。

Kubernetes 中通过 Volume 的方式提供对存储的支持。下面对一些常见的存储概念进行一点简要的说明。

EmptyDir

顾名思义,EmptyDir是一个空目录,他的生命周期和所属的 Pod 是完全一致的,可能读者会奇怪,那还要他做什么?EmptyDir的用处是,可以在同一 Pod 内的不同容器之间共享工作过程中产生的文件。

缺省情况下,EmptyDir 是使用主机磁盘进行存储的,也可以设置emptyDir.medium 字段的值为Memory,来提高运行速度,但是这种设置,对该卷的占用会消耗容器的内存份额。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

HostPath

这种会把宿主机上的指定卷加载到容器之中,当然,如果 Pod 发生跨主机的重建,其内容就难保证了。

这种卷一般和DaemonSet搭配使用,用来操作主机文件,例如进行日志采集的 FLK 中的 FluentD 就采用这种方式,加载主机的容器日志目录,达到收集本主机所有日志的目的。

apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: gcr.io/google_containers/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data

NFS/GlusterFS/CephFS/AWS/GCE 等等

作为一个容器集群,支持网络存储自然是重中之重了,Kubernetes 支持为数众多的云提供商和网络存储方案。

各种支持的方式不尽相同,例如 GlusterFS 需要创建 Endpoint,Ceph/NFS 之流就没这么麻烦了。

各种个性配置可移步参考文档。

ConfigMap 和 Secret

镜像使用的过程中,经常需要利用配置文件、启动脚本等方式来影响容器的运行方式,如果仅有少量配置,我们可以使用环境变量的方式来进行配置。然而对于一些较为复杂的配置,例如 Apache 之类,就很难用这种方式进行控制了。另外一些敏感信息暴露在 YAML 中也是不合适的。

ConfigMap 和 Secret 除了使用文件方式进行应用之外,还有其他的应用方式;这里仅就文件方式做一点说明。

例如下面的 ConfigMap,将一个存储在 ConfigMap 中的配置目录加载到卷中。

apiVersion: v1
kind: Pod
metadata:
  name: dapi-test-pod
spec:
  containers:
    - name: test-container
      image: gcr.io/google_containers/busybox
      command: [ "/bin/sh", "-c", "ls /etc/config/" ]
      volumeMounts:
      - name: config-volume
        mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        # Provide the name of the ConfigMap containing the files you want
        # to add to the container
        name: special-config
  restartPolicy: Never

注意,这里的 ConfigMap 会映射为一个目录,ConfigMap 的 Key 就是文件名,每个 Value 就是文件内容,比如下面命令用一个目录创建一个 ConfigMap:

kubectl create configmap 
    game-config 
    --from-file=docs/user-guide/configmap/kubectl

创建一个 Secret:

kubectl create secret generic 
    db-user-pass --from-file=./username.txt 
    --from-file=./password.txt

使用 Volume 加载 Secret:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
  namespace: myns
spec:
  containers:
    - name: mypod
      image: redis
      volumeMounts:
        - name: foo
          mountPath: /etc/foo
          readOnly: true
  volumes:
    - name: foo
      secret:
        secretName: mysecret

可以看到 Secret 和 ConfigMap 的创建和使用是很相似的。在 RBAC 中,Secret 和 ConfigMap 可以进行分别赋权,以此限定操作人员的可见、可控权限。

PV & PVC

PersistentVolume 和 PersistentVolumeClaim 提供了对存储支持的抽象,也提供了基础设施和应用之间的分界,管理员创建一系列的 PV 提供存储,然后为应用提供 PVC,应用程序仅需要加载一个 PVC,就可以进行访问。

而 1.5 之后又提供了 PV 的动态供应。可以不经 PV 步骤直接创建 PVC。

k8s(kubernetes)kube-proxy转发模式及service端口类型介绍

配置方式

kubernetes版本大于或者等于1.2时,外部网络(即非K8S集群内的网络)访问cluster IP的办法是:

修改master的/etc/kubernetes/proxy,把KUBE_PROXY_ARGS=”“改为KUBE_PROXY_ARGS=”–proxy-mode=userspace”
启动kube-proxy服务
在核心路由设备或者源主机上添加一条路由,访问cluster IP段的路由指向到master上。

kubernetes版本小于1.2时,直接添加路由

未分类

kube-proxy转发的两种模式

kube-proxy在转发时主要有两种模式Userspace和Iptables。如下图,左侧是Userspace模式,也是kube-proxy默认的方式,所有的转发都是通过kube-proxy软件实现的;右侧是Iptables模式,所有转发都是通过Iptables内核模块实现,而kube-proxy只负责生成相应的Iptables规则。

未分类

使用Userspace模式(k8s版本为1.2之前默认模式),外部网络可以直接访问cluster IP。
使用Iptables模式(k8s版本为1.2之后默认模式),外部网络不能直接访问cluster IP。
从效率上看,Iptables会更高一些,但是需要Iptables version >=1.4.11,Iptables模式在k8s1.2版本放出。

service转发后端服务的四种类型

ClusterIP

此类型会提供一个集群内部的虚拟IP(与Pod不在同一网段),以供集群内部的pod之间通信使用。ClusterIP也是Kubernetes service的默认类型。

未分类

为了实现图上的功能主要需要以下几个组件的协同工作:

  • apiserver:在创建service时,apiserver接收到请求以后将数据存储到etcd中。
  • kube-proxy:k8s的每个节点中都有该进程,负责实现service功能,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables中。
  • iptables:使用NAT等技术将virtualIP的流量转至endpoint中。

NodePort

NodePort模式除了使用cluster ip外,也将service的port映射到每个node的一个指定内部port上,映射的每个node的内部port都一样。
为每个节点暴露一个端口,通过nodeip + nodeport可以访问这个服务,同时服务依然会有cluster类型的ip+port。内部通过clusterip方式访问,外部通过nodeport方式访问。

未分类

loadbalance

LoadBalancer在NodePort基础上,K8S可以请求底层云平台创建一个负载均衡器,将每个Node作为后端,进行服务分发。该模式需要底层云平台(例如GCE)支持。

Ingress

Ingress,是一种HTTP方式的路由转发机制,由Ingress Controller和HTTP代理服务器组合而成。Ingress Controller实时监控Kubernetes API,实时更新HTTP代理服务器的转发规则。HTTP代理服务器有GCE Load-Balancer、HaProxy、Nginx等开源方案。

详细说明请见http://blog.csdn.net/liyingke112/article/details/77066814

未分类

service的三种端口

port

service暴露在cluster ip上的端口,:port 是提供给集群内部客户访问service的入口。

nodePort

nodePort是k8s提供给集群外部客户访问service入口的一种方式,:nodePort 是提供给集群外部客户访问service的入口。

targetPort

targetPort是pod上的端口,从port和nodePort上到来的数据最终经过kube-proxy流入到后端pod的targetPort上进入容器。

port、nodePort总结

总的来说,port和nodePort都是service的端口,前者暴露给集群内客户访问服务,后者暴露给集群外客户访问服务。从这两个端口到来的数据都需要经过反向代理kube-proxy流入后端pod的targetPod,从而到达pod上的容器内。

docker compose出现ERROR: Version in “./docker-compose.yml” is unsupported错误

问题

以下是我的docker-compose.yml文件

version: “2”
services:
  web:
   build: .
   environment:
    MONGO_URI="mongodb://ravimongo:27017"
   ports:
    — “3000:3000”
   links:
    — ravimongo
   depends_on:
    — ravimongo
  ravimongo:
   image: mongo:3.2.6
   ports:
     — “27017:27017”

下面是错误信息:

ERROR: Version in "./docker-compose.yml" is unsupported. You might be seeing this error because you're using the wrong Compose file version. Either specify a supported version ("2.0", "2.1", "3.0") and place your service definitions under the `services` key, or omit the `version` key and place your service definitions at the root of the file to use version 1.
For more on the Compose file format versions, see https://docs.docker.com/compose/compose-file/

docker-compose的版本信息:

docker-compose version 1.11.2, build dfed245
docker-py version: 2.1.0
CPython version: 2.7.12
OpenSSL version: OpenSSL 1.0.2j  26 Sep 2016

docker版本信息:

Client:
 Version:      17.03.1-ce
 API version:  1.27
 Go version:   go1.7.5
 Git commit:   c6d412e
 Built:        Tue Mar 28 00:40:02 2017
 OS/Arch:      darwin/amd64

Server:
 Version:      17.03.1-ce
 API version:  1.27 (minimum version 1.12)
 Go version:   go1.7.5
 Git commit:   c6d412e
 Built:        Fri Mar 24 00:00:50 2017
 OS/Arch:      linux/amd64
 Experimental: true

我使用了 http://www.yamllint.com/ 和 https://codebeautify.org/yaml-validator验证这个yaml文件,没发现有什么问题。

最佳答案

YAML是有效的。不过你使用了一个左双引号,如:

version: “2”

基于错误显示,看来docker compose无法解析正确的版本。如果你使用了一个左双引号,而不是正常的双引号,docker compose将解析版本为“2”,而不是2。应该改为:

version: "2"

CentOS7启动出现grub>提示符修复方法

本文档适用于CentOS 7.X,其他系统未测试。

出现这个问题的原因是grub配置文件错误、损坏或丢失,执行以下步骤修复

1. 罗列所有的磁盘区块

grub>ls

我的是(hd0),(hd0,msdos1),(hd0,msdos2)

2. 确定boot分区

ls (hd0,X)/boot/grub

如果存在,则列出该文件夹里的所有文件,不存在则会报错。如果boot为独立分区则使用

ls(hd0,X)/grub2

3. 将boot分区设置为临时root分区

grub>set root=hd0,msdos1

hd0,msdos1为第二步中确定的boot分区

4. 命令后面的路径可以用tab键补全,xxxx代表你的系统内核版本号

/dev/mapper/centos-root为根分区,如果分区格式不是lvm的,根据你的本机情况填写,如/dev/sda1

grub>linux16 /vmlinuz-xxxx.e17.x86_64  root=/dev/mapper/centos-root

5. 命令后面的路径可以用tab键补全,xxxx代表你的系统内核版本号

grub>lintrad16 /initramfs-xxxx.e17.x86_64.img

6. 启动

grub>boot

7. 进入系统之后使用命令重新生成grub配置文件

grub2-mkconfig -o /boot/grub2/grub.cfg

使用路由和iptables配置不同主机上的docker容器互相通信

docker启动时,会在宿主主机上创建一个名为docker0的虚拟网络接口,默认选择172.17.42.1/16,一个16位的子网掩码给容器提供了65534个IP地址。docker0只是一个在绑定到这上面的其他网卡间自动转发数据包的虚拟以太网桥,它可以使容器和主机相互通信,容器与容器间通信。

问题是,如何让位于不同主机上的docker容器可以通信?

最简单的思路,修改一台主机docker默认的虚拟网段,然后在各自主机上分别把对方的docker网段加入到路由表中,即可实现docker容器夸主机通信。

现有两台虚拟机

  • v1:192.168.124.51
  • v2:192.168.124.52

更改虚拟机docker0网段,修改为

  • v1:172.17.1.1/24
  • v2:172.17.2.1/24

命令如下:

#v1
sudo ifconfig docker0 172.17.1.1 netmask 255.255.255.0
sudo service docker restart

#v2
sudo ifconfig docker0 172.17.2.1 netmask 255.255.255.0
sudo service docker restart

然后在v1,v2上把对方的docker0网段加入到自己的路由表中

#v1
sudo route add -net 172.17.2.0 netmask 255.255.255.0 gw 192.168.124.52
sudo iptables -t nat -F POSTROUTING
sudo iptables -t nat -A POSTROUTING -s 172.17.1.0/24 ! -d 172.17.0.0/16 -j MASQUERADE

#v2
sudo route add -net 172.17.1.0  netmask 255.255.255.0  gw 192.168.124.51
sudo iptables -t nat -F POSTROUTING
sudo iptables -t nat -A POSTROUTING -s 172.17.2.0/24 ! -d 172.17.0.0/16 -j MASQUERADE

测试,v1,v2创建容器test1,test2

#v1
docker run --rm --name test1 -i -t base:latest bin/bash
docker inspect --format '{{.NetworkSettings.IPAddress}}' test1
#172.17.1.1

v2
docker run --rm --name test2 -i -t base:latest bin/bash
docker inspect --format '{{.NetworkSettings.IPAddress}}' test2
#172.17.2.1

主机上可以ping通对方容器ip,至此也就ok了。

Docker Ubuntu容器cron定时任务不生效解决方法

最近在Docker的Ubuntu容器中设置了一个定时备份任务,发现没有生效,安装rsyslog记录cron日志,发现cron输出了报错信息:

CRON[253]: Cannot make/remove an entry for the specified session

经过一番折腾在stackoverflow找到了解决方法:

#You can use something similar to this in your Dockerfile:
RUN sed -i '/session    required     pam_loginuid.so/c#session    required   pam_loginuid.so' /etc/pam.d/cron

意思就是注释掉/etc/pam.d/cron文件中的下面这一行:

...
session    required     pam_loginuid.so
...

变为:

...
# session    required     pam_loginuid.so
...

这样cron就可以正常工作了。

搭建带ssl认证的docker registry私有仓库

1、首先Docker pull registry默认下载最新版的镜像,我这边是2.6.2版本

未分类

2、这边考虑私有仓库部署的服务器可能没有网络,可以使用docker save -o registry.tar registry:2.6.2保存一个镜像,然后把registry.tar打包到部署包里面,下次使用docker load -i registry.tar加载到本地镜像

3、保证ssl已安装,且/ect/ssl/openssl.cnf中关于生成密码定义的字段都有(网上可以查)

docker run --entrypoint htpasswd registry:2.6.2 -Bbn ${username} ${password} > ${dir}/auth/htpasswd openssl req -x509 -days 3650 -subj "/C=CN/ST=GuangDong/L=ShenZhen/CN=Registry/O=Company/CN=test.io/" -nodes -newkey rsa:2048 -keyout ${dir}/certs/registry.key -out ${dir}/certs/registry.crt

4、这里username password是你要设置ssl认证通过的用户名和密码,这里需要你自己记住的几个目录dir下的auth,certs,data。auth是存放你生成的用户名密码文件,certs是存放生成的证书文件,data后续准备用来挂载私有仓库的镜像存放目录的

docker run -d -p 5000:5000 --privileged=true --restart=always --name ${name} 
-v ${dir}/config.yml:/etc/docker/registry/config.yml 
-v ${dir}/auth:/auth 
-e "REGISTRY_AUTH=htpasswd" 
-e "REGISTRY_AUTH_HTPASSWD_REALM=registry on test.io" 
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd 
-v ${dir}/certs:/certs 
-v ${dir}/data:/var/lib/registry 
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt 
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.key registry:2.6.2

这里的name是你要创建的私有仓库容器的名字,从挂载的三个目录我们可以看到是为了一一对应私有仓库容器内的目录结构的。这里我们可以看到私有仓库容器内存放镜像的目录为/var/lib/registry。这里说一下config.yml的映射原因:

version: 0.1
log:
  fields:
    service: registry
storage:
    cache:
        blobdescriptor: inmemory
    filesystem:
        rootdirectory: /var/lib/registry
    delete:
        enabled: true
http:
  addr: :5000
  headers:
    X-Content-Type-Options: [nosniff]
health:
  storagedriver:
    enabled: true
    interval: 10s
    threshold: 3

相比默认配置文件增加了delete:enabled:true选项,这是为了后面使用remote api支持删除镜像的,后面再说,到这里私有仓库就建好了。

5、现在要想使用私有仓库连接私有仓库并下载镜像查询删除镜像,你的主机需要的条件

/etc/hosts需要有xx.xx.xx.xx test.io的对应关系
/etc/ssl目录下有certs目录
你要在你的/etc/docker/certs.d目录下新建一个文件夹,命名为test.io:5000,然后把私有仓库${dir}/certs目录下的registry.crt证书拷贝到test.io:5000文件夹下(我这里的crt证书名字是registry.crt看上面生成的shell),最后我们使用docker login -u username -p password test.io:5000登陆。就可以开始使用私有仓库了。

6、如果你要上传镜像的话可以

docker tag test:latest test.io:5000/test:1.0
docker push test.io:5000/test:1.0

7、如果你要下载镜像的话可以

docker pull test.io:5000/test:1.0

8.现在要重点说v2版本之后的remote api改变

查询有哪些镜像:

curl -s –insecure –user username:password https://test.io:5000/v2/_catalog

这里curl使用了-s选项,因为有些情况下会出现%Total %Received %Xferd这些状态信息

未分类

查询镜像有哪些tag:curl -s –insecure –user username:password

https://test.io:5000/v2/image_name/tags/list

删除镜像:curl –header “Accept:

application/vnd.docker.distribution.manifest.v2+json” -I -X HEAD –insecure –user username:password https://test.io:5000/v2/image_name/manifests/image_tag

未分类

这里先拿到Docker-Content-Digest的值,后面从sha256都要带上(注意带上sha256)

curl –insecure –user username:password -X DELETE https://test.io:5000/v2/image_name/manifests/sha256:1583251f6052c35180381fbf28e93db0b9a26c2971f45532a5263c5dc4d18b61

这样删除就完成了。

但是,我测过有一个问题,使用curl删除之后,你再来使用curl来查询所有的镜像它还是存在的,你使用curl查询这个镜像的tag你会看到tag变为了null,然后你pull也是会失败的。也就是说你删除镜像仅仅只是阻止了pull,然后查询那里只是让tag变为了null。我自己试过把私有仓库容器的/var/lib/registry/docker/registry/v2/repositories/下对应镜像名字的文件夹删除掉,再查询就查不到了。最后删除了镜像还要回收空间,使用docker exec name bin/registry garbage-collect /etc/docker/registry/config.yml,这里name是你的私有仓库容器名字,/var/lib/registry/docker/registry/v2/blob是存放镜像的地方,使用gc垃圾回收这里的占用硬盘大小可以看到质的变化,我们可以在这里看垃圾回收的效果。

Docker指定网桥和指定网桥IP

$ docker network ls
NETWORK ID          NAME                DRIVER
7fca4eb8c647        bridge              bridge
9f904ee27bf5        none                null
cf03ee007fb4        host                host
  • Bridge
    默认bridge网络,我们可以使用docker network inspect命令查看返回的网络信息,我们使用docker run 命令是将网络自动应用到新的容器

  • Host
    如果是hosts模式,启动容器时不会获得独立的网络namespace,而是和宿主机使用同一个,容器不会有网卡和ip,但是除了网络其他方面还是独立的

  • Container
    如果是container指定的新创建的会和已经存在的容器共享一个网络namespace,不和宿主机有共享网络,也不会有自己的网卡和ip,而是和指定的容器共享,除了网络之外其他都是独立的

  • None
    docker容器有自己的网络namespace,但是和docker容器的网络配置没有关系,这个none的容器是没有网卡,ip,路由等,我们要手动指定

一、指定网桥

1、建网桥

[root@linuxea ~]# docker network create linuxea.com

af4526e387772f33b053ff2ab47e601ddf9618bc2d444770775723d76d3a1010

[root@linuxea ~]# docker network ls

NETWORK ID          NAME                DRIVER              SCOPE
3ebf99e55db8        bridge              bridge              local          
7eb855581296        host                host                local          
af4526e38777        linuxea.com         bridge              local          
58d75a1a38bc        none                null                local  

[root@linuxea ~]# 

查看linuxea.com

[root@linuxea ~]# docker network inspect linuxea.com
[


    {
        "Name": "linuxea.com",
        "Id": "af4526e387772f33b053ff2ab47e601ddf9618bc2d444770775723d76d3a1010",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {},
        "Options": {},

下载镜像

[root@linuxea ~]# docker pull nginx

Using default tag: latest
latest: Pulling from library/nginx
6a5a5368e0c2: Pull complete 
4aceccff346f: Pull complete 
c8967f302193: Pull complete 
Digest: sha256:1ebfe348d131e9657872de9881fe736612b2e8e1630e0508c354acb0350a4566
Status: Downloaded newer image for nginx:latest

2、指定网桥

[root@linuxea ~]# docker run --network=linuxea.com -itd --name=mynginx nginx

b0ec2c7951fa5343d20218811005b16304f9ec5cb3107d06abbf60d5a94df248

[root@linuxea ~]# docker network inspect linuxea.com
[

    {
        "Name": "linuxea.com",
        "Id": "af4526e387772f33b053ff2ab47e601ddf9618bc2d444770775723d76d3a1010",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1/16"
                }
            ]
        },
        "Internal": false,
        "Containers": {
            "b0ec2c7951fa5343d20218811005b16304f9ec5cb3107d06abbf60d5a94df248": {
                "Name": "mynginx",
                "EndpointID": "adaec00497b42ada6f6b251bff18a26623cfe96890a47df8b5da3c3d75582482",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]
[root@linuxea ~]# linuxea

二、指定网桥ip地址

1、指定docker0网段内的ip

我们手动指定–net=none,可以发现,容器中并没有网卡

[root@linuxea ~]# docker run --net=none --name mynginx -d -p 80:80 nginx

09b9819234338e47a8df7d3eba8daf23bf919b9fa2ea114d60742c3318dc2d69

[root@linuxea ~]# docker ps -a

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
09b981923433        nginx               "nginx -g 'daemon off"   7 seconds ago       Up 5 seconds                            mynginx

[root@linuxea ~]# /root/in.sh mynginx

root@09b981923433:/# ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 


    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever

root@09b981923433:/# 

查看docker0地址从172.17.0.0网段

[root@LinuxEA ~]# ip addr show docker0

4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN 


    link/ether 02:42:af:55:9a:54 brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.1/16 scope global docker0
       valid_lft forever preferred_lft forever
    inet6 fe80::42:afff:fe55:9a54/64 scope link 
       valid_lft forever preferred_lft forever

2、获取pid

创建连接文件后创建端到端网卡,将veth_db84e747c3绑定到docker0,并且启动

[root@LinuxEA ~]#  docker inspect -f '{{.State.Pid}}' mynginx
28383
[root@LinuxEA ~]# mkdir -p /var/run/netns

[root@LinuxEA ~]#  ln -s /proc/28383/ns/net /var/run/netns/28383

[root@LinuxEA ~]# ip link add veth_db84e747c3 type veth peer name x

3、安装brctl-tools

yum install bridge-utils

[root@LinuxEA ~]# brctl addif docker0 veth_db84e747c3

[root@LinuxEA ~]# ip link set veth_db84e747c3 up

[root@LinuxEA ~]# ip link set x netns 28383

此时mynginx中已经有块网卡

[root@LinuxEA mysql]# /root/in.sh mynginx

root@e224723da286:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
47: x@if48: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
    link/ether 2a:bf:7a:75:58:5f brd ff:ff:ff:ff:ff:ff

root@e224723da286:/# 

4、给新加网卡配置ip

[root@LinuxEA ~]# ip netns exec 28383 ip link set dev x name eth0

[root@LinuxEA ~]# ip netns exec 28383 ip link set eth0 up

[root@LinuxEA ~]# ip netns exec 28383 ip addr add 172.17.0.100/24 dev eth0

[root@LinuxEA ~]# ip netns exec 28383 ip route add default via 172.17.0.1

回到mynginx查看ip已经固定设置

root@e224723da286:/# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 


    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
47: eth0@if48: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 2a:bf:7a:75:58:5f brd ff:ff:ff:ff:ff:ff
    inet 172.17.0.100/24 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::28bf:7aff:fe75:585f/64 scope link 
       valid_lft forever preferred_lft forever

root@e224723da286:/# ping -w 3 www.baidu.com

PING www.a.shifen.com (103.235.46.39): 56 data bytes
64 bytes from 103.235.46.39: icmp_seq=0 ttl=46 time=197.858 ms
64 bytes from 103.235.46.39: icmp_seq=1 ttl=46 time=209.700 ms
64 bytes from 103.235.46.39: icmp_seq=2 ttl=46 time=196.508 ms
--- www.a.shifen.com ping statistics ---
4 packets transmitted, 3 packets received, 25% packet loss
round-trip min/avg/max/stddev = 196.508/201.355/209.700/5.926 ms

root@e224723da286:/# 

5、添加ip脚本如下

[root@linuxea ~]# cat /root/ip.sh 

#!/bin/bash
# filename: bind_addr.sh

if [ `id -u` -ne 0 ];then
    echo '必须使用root权限'
    exit
fi

if [ $# != 2 ]; then
    echo "使用方法: $0 容器名字 IP"
    exit 1
fi

container_name=$1
bind_ip=$2

container_id=`docker inspect -f '{{.Id}}' $container_name 2> /dev/null`
if [ ! $container_id ];then
    echo "容器不存在"
    exit 2
fi
bind_ip=`echo $bind_ip | egrep '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]).){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'`
if [ ! $bind_ip ];then
    echo "IP地址格式不正确"
    exit 3
fi

container_minid=`echo $container_id | cut -c 1-10`
container_netmask=`ip addr show docker0 | grep "inetb" | awk '{print $2}' | cut -d / -f2`
container_gw=`ip addr show docker0 | grep "inetb" | awk '{print $2}' | cut -d / -f1`

bridge_name="veth_$container_minid"
container_ip=$bind_ip/$container_netmask
pid=`docker inspect -f '{{.State.Pid}}' $container_name 2> /dev/null`
if [ ! $pid ];then
    echo "获取容器$container_name的id失败"
    exit 4
fi

if [ ! -d /var/run/netns ];then
    mkdir -p /var/run/netns
fi

ln -sf /proc/$pid/ns/net /var/run/netns/$pid

ip link add $bridge_name type veth peer name X
brctl addif docker0 $bridge_name
ip link set $bridge_name up
ip link set X netns $pid
ip netns exec $pid ip link set dev X name eth0
ip netns exec $pid ip link set eth0 up
ip netns exec $pid ip addr add $container_ip dev eth0
ip netns exec $pid ip route add default via $container_gw

三、指定网桥并且指定网桥内固定IP

1、准备工作

停掉docker,并且删除掉docker0,创建新的网桥linuxea0

[root@linuxea ~]# service docker stop
Redirecting to /bin/systemctl stop  docker.service

[root@linuxea ~]# ip link set dev docker0 down

[root@linuxea ~]# brctl delbr docker0

[root@linuxea ~]# brctl addbr linuxea0

ip段为192.168.100.0/24

[root@linuxea ~]# ip addr add 192.168.100.1/24 dev linuxea0

[root@linuxea ~]# ip link set dev linuxea0 up

[root@linuxea ~]# ip addr show linuxea0

63: linuxea0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN 

    link/ether 1e:28:a7:71:19:46 brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.1/24 scope global linuxea0
       valid_lft forever preferred_lft forever

[root@linuxea ~]# 

2、下载pipwork

[root@linuxea docker]# git clone https://github.com/jpetazzo/pipework.git
Cloning into 'pipework'...
remote: Counting objects: 475, done.
remote: Total 475 (delta 0), reused 0 (delta 0), pack-reused 475
Receiving objects: 100% (475/475), 158.46 KiB | 98.00 KiB/s, done.
Resolving deltas: 100% (250/250), done.

[root@linuxea docker]# cp -rp pipework/pipework /usr/local/bin/

[root@linuxea docker]# pipework
Syntax:
pipework <hostinterface> [-i containerinterface] [-l localinterfacename] [-a addressfamily] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr][@vlan]
pipework <hostinterface> [-i containerinterface] [-l localinterfacename] <guest> dhcp [macaddr][@vlan]
pipework route <guest> <route_command>
pipework --wait [-i containerinterface]

[root@linuxea docker]# 

查看

[root@linuxea docker]# brctl show

bridge name     bridge id               STP enabled     interfaces
br-24418946eb12         8000.0242668f42e0       no       
linuxea0                8000.000000000000       no       

写入内容如下:

[root@linuxea docker]# cat /etc/sysconfig/docker | grep 'OPTIONS='
OPTIONS='
OPTIONS=--selinux-enabled -b=linuxea -H fd://

删除了docker0后将默认桥指定了linuxea0,则在创建容器时加上net=none

3、run一个服务后

[root@linuxea docker]# docker run --rm -ti --net=none nginx /bin/bash

root@b6d29d0accf0:/#

使用pipwork将linuxea0指定ip到run起的服务上

[root@linuxea ~]# docker ps -a

CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
b6d29d0accf0        nginx               "/bin/bash"         38 seconds ago      Up 35 seconds                           condescending_minsky

[root@linuxea ~]# pipework linuxea0 -i eth0 b6d29d0accf0 192.168.100.100/[email protected]

而后在查看

root@b6d29d0accf0:/# ip a

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default 

    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
       valid_lft forever preferred_lft forever
65: eth0@if66: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether 72:78:ef:7b:f2:9b brd ff:ff:ff:ff:ff:ff
    inet 192.168.100.100/24 brd 192.168.100.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::7078:efff:fe7b:f29b/64 scope link 
       valid_lft forever preferred_lft forever

root@b6d29d0accf0:/# 

# 默认不指定网卡设备名,则默认添加为 eth1

# 另外 pipework 不能添加静态路由,如果有需求则可以在 run 的时候加上 --privileged=true 权限在容器中手动添加,

# 但这种安全性有缺陷,可以通过 ip netns 操作

使用 ip netns 添加静态路由,避免创建容器使用 –privileged=true 选项造成一些不必要的安全问题

[root@linuxea ~]# docker inspect --format="{{ .State.Pid }}"  9f28a3f40737
15142
[root@linuxea ~]# ln -s /proc/15142/ns/net /var/run/netns/15142
[root@linuxea ~]# ip netns exec 15142 ip route add 192.168.100.0/24 dev eth0 via 192.168.100.1
[root@linuxea ~]# ip netns exec 15142 ip route
default via 192.168.100.1 dev eth0 

192.168.100.0/24 dev eth0  proto kernel  scope link  src 192.168.100.100 
[root@linuxea ~]# 

到此为止虽然,IP或者网桥指定了,事实上使用起来并不是很方便,且每次都需要指定nat,本次到此为止

  • 在容器中
route add default gw 10.0.0.1
  • 在docker宿主机上
route add -net 192.168.100.1 gw 10.0.0.1

除了默认的 docker0 网桥,用户也可以指定网桥来连接各个容器。在启动 Docker 服务的时候,使用 -b BRIDGE或–bridge=BRIDGE 来指定使用的网桥。

如果服务已经运行,那需要先停止服务,并删除旧的网桥。

$ sudo service docker stop

$ sudo ip link set dev docker0 down

$ sudo brctl delbr docker0

然后创建一个网桥 bridge0。

要使Linux可以工作在网桥模式,必须安装网桥工具bridge-utils,运行命令:

yum install bridge-utils


$ sudo brctl addbr bridge0


$ sudo ip addr add 192.168.5.1/24 dev bridge0


$ sudo ip link set dev bridge0 up

查看确认网桥创建并启动。

$ ip addr show bridge04: bridge0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state UP group default
    link/ether 66:38:d0:0d:76:18 brd ff:ff:ff:ff:ff:ff
    inet 192.168.5.1/24 scope global bridge0
       valid_lft forever preferred_lft forever

配置 Docker 服务,默认桥接到创建的网桥上。

$ echo 'DOCKER_OPTS="-b=bridge0"' >> /etc/default/docker

$ sudo service docker start

启动 Docker 服务。新建一个容器,可以看到它已经桥接到了 bridge0 上。

可以继续用 brctl show 命令查看桥接的信息。另外,在容器中可以使用 ip addr 和 ip route 命令来查看 IP 地址配置和路由信息。

brctl addbr bridge

brctl addif bridge eth0

brctl addif bridge ath0

ifconfig eth0 0.0.0.0

ifconfig bridge 10.0.0.1 netmask 255.255.255.0 up

添加iptables -t nat -A POSTROUTING -o eth1-j SNAT –to 192.168.2.173

将有线和无线都设置为10.0.0.*网段,即可通过网上邻居进行访问

当然了,要是Linux可以工作在网桥模式,必须安装网桥工具bridge-utils,运行命令:

yum install bridge-utils

或者下载bridge-utils-1.4.tar.gz进行安装,步骤如下:

编译安装bridge-utils

  • 进入到/usr/src目录下,下载bridge-utils-1.4.tar.gz :
#cd /usr/src
#wget http://launchpad.net/bridgeutils/
main/1.4/+download/bridge-utils-1.4.tar.gz
  • 解压缩:
#tar zxvf bridge-utils-1.4.tar.gz
进入bridge-utils-1.4目录:
#cd bridge-utils-1.4
  • 编译安装:
#autoconf
生成configure文件:
#./configure
#make
#make install

编译安装完成。最后将命令brctl复制到/sbin下:

# cp/usr/local/sbin/brctl    /sbin 

addbr bridge的名称 #添加bridge;

delbr bridge的名称 #删除bridge;

addif bridge的名称device的名称#添加接口到bridge;

delif bridge的名称device的名称#从bridge中删除接口

setageing bridge的名称时间 #设置老化时间,即生存周期

setbridgeprio bridge的名称 优先级#设置bridge的优先级

setfd bridge的名称时间 #设置bridge转发延迟时间

sethello bridge的名称时间 #设置hello时间

setmaxage bridge的名称时间 #设置消息的最大生命周期

setpathcost bridge的名称 端口 权重#设置路径的权值

setportprio bridge的名称 端口 优先级#设置端口的优先级

show #显示bridge列表

showmacs bridge的名称 #显示MAC地址

showstp bridge的名称 #显示bridge的stp信息

stp bridge的名称{on|off} #开/关stp

设置linux让网桥运行 配置网桥

  • 我们需要让linux知道网桥,首先告诉它,我们想要一个虚拟的以太网桥接口:(这将在主机bridge上执行,不清楚的看看测试场景)
root@bridge:~> brctl addbr br0
  • 其次,我们不需要STP(生成树协议)等。因为我们只有一个路由器,是绝对不可能形成一个环的。我们可以关闭这个功能。(这样也可以减少网络环境的数据包污染):
root@bridge:~> brctl stp br0 off
  • 经过这些准备工作后,我们终于可以做一些立竿见影的事了。我们添加两个(或更多)以太网物理接口,意思是:我们将他们附加到刚生成的逻辑(虚拟)网桥接口br0上。
root@bridge:~> brctl addif br0 eth0

root@bridge:~> brctl addif br0 eth1
  • 现在,原来我们的两个以太网物理接口变成了网桥上的两个逻辑端口。那两个物理接口过去存在,未来也不会消失。要不信的话,去看看好了。.现在他们成了逻辑网桥设备的一部分了,所以不再需要IP地址。下面我们将这些IP地址释放掉

  • 最后,启用网桥

root@bridge:~> ifconfig br0 up

可选: 我们给这个新的桥接口分配一个IP地址

root@bridge:~> ifconfig br0 10.0.3.129

或者把最后这两步合成一步:

root@bridge:~> ifconfig br0 10.0.3.129 up

就是多一个up!

这下我们做完了 。

关闭网桥命令

brctl delif ena eth1;

brctl delif ena eth0;

ifconfig ena down;

brctl delbr ena; 

Docker服务进程在启动的时候会生成一个名为docker0的网桥,容器默认都会挂载到该网桥下,但是我们可以通过添加docker启动参数-b Birdge 或更改docker配置文件来选择使用哪个网桥。

删除docker0网桥

service docker stop //关闭docker服务  


ip link set dev docker0 down //关闭docker0网桥  

ip link del dev docker0       //删除docker0网桥   

自定义网桥设置(/etc/sysconfig/network-scripts/ifcfg-br0文件)

DEVICE="br0"  
ONBOOT="yes"  
TYPE="Bridge"  
BOOTPROTO="static"  
IPADDR="10.10.10.20"  
NETMASK="255.255.255.0"  
GATEWAY="10.10.10.20"  
DEFROUTE="yes"  
NM_CONTROLLED="no"  

重启网络服务

service network restart  

查看网桥

[black@test opt]$ brctl show  
bridge name     bridge id               STP enabled     interfaces  
br0             8000.32e7297502be       no                
virbr0          8000.000000000000       yes

接下来我们需要重新启动docker,可以在启动docker服务进程时使用以下两种方式:

第一种:-b 参数指定网桥

[root@test opt]# docker -d -b br0  


INFO[0000] Listening for HTTP on unix (/var/run/docker.sock)   
INFO[0000] [graphdriver] using prior storage driver "devicemapper"   
WARN[0000] Running modprobe bridge nf_nat failed with message: , error: exit status 1   
INFO[0000] Loading containers: start.                     
......  
INFO[0000] Loading containers: done.                      
INFO[0000] Daemon has completed initialization            
INFO[0000] Docker daemon      commit=786b29d execdriver=native-0.2 graphdriver=devicemapper version=1.7.1  

不知道为什么这样启动docker 服务进程会阻塞当前终端(︶︿︶),只好重新开一个终端,然后运行一个容器

[root@test shell]# docker run -ti --rm centos:latest  
[root@3c6874559411 /]# ifconfig  
eth0      Link encap:Ethernet  HWaddr 02:42:0A:0A:0A:01    
          inet addr:10.10.10.1  Bcast:0.0.0.0  Mask:255.255.255.0  
          inet6 addr: fe80::42:aff:fe0a:a01/64 Scope:Link  
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1  
          RX packets:5 errors:0 dropped:0 overruns:0 frame:0  
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0  
          collisions:0 txqueuelen:0   
          RX bytes:418 (418.0 b)  TX bytes:508 (508.0 b)  

容器成功使用br0网桥。

第二种:修改/etc/sysconfig/docker文件

我在进行这种操作的时候遇到了一点问题,我修改了/etc/sysconfig/docker文件

[root@test opt]# vi /etc/sysconfig/docker   

# /etc/sysconfig/docker  
#  
# Other arguments to pass to the docker daemon process  
# These will be parsed by the sysv initscript and appended  
# to the arguments list passed to docker -d  

other_args="-b br0"  

接着使用service docker start启动docker服务,但是other_args并不生效,

在centos7下servicer docker start仍然会采用systemctl start docker.service命令来运行,于是我就打开/usr/lib/systemd/system/docker.service查看

[root@test opt]# vi /lib/systemd/system/docker.service   
[Unit]  
Description=Docker Application Container Engine  
Documentation=https://docs.docker.com  
After=network.target docker.socket  
Requires=docker.socket  
[Service]  
ExecStart=/usr/bin/docker -d  -H fd://  
MountFlags=slave  
LimitNOFILE=1048576  
LimitNPROC=1048576  
LimitCORE=infinity  

[Install]  
WantedBy=multi-user.target  

发现ExecStart一项并没有运行参数,于是将ExecStart改为/usr/bin/docker -d -b br0 -H fd://,运行docker服务,启动一个容器发现能够成功使用br0网桥。

在网上看到了一种更好的方法,将docker.service改为如下

[black@test ~]$ vi /usr/lib/systemd/system/docker.service   
[Unit]  
Description=Docker Application Container Engine  
Documentation=https://docs.docker.com  
After=network.target docker.socket  
Requires=docker.socket  
[Service]  
EnvironmentFile=-/etc/sysconfig/docker  
ExecStart=/usr/bin/docker -d $other_args  -H fd://  
MountFlags=slave  
LimitNOFILE=1048576  
LimitNPROC=1048576  
LimitCORE=infinity  

[Install]  
WantedBy=multi-user.target  

这个时候在other_args中添加的参数就有效了。