部署说明

此篇文章介绍的是Kubernets的1.10.2版本使用kubeadm 工具自动化部署一套简单的k8s集群,不涉及具体原理的说明。在后续的更新中会逐步加入一些常见的生产应用案例。

环境准备

Master: 10.0.0.1 node-1

node: 10.0.0.2 node-2

所有节点初始化

1、所有节点安装docker,官方推荐docker 1.12的版本,使用 v1.11, v1.13 和v17.03的也可以,不要使用最新的版本。

yum install -y docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm 
yum install -y docker-ce-17.03.2.ce-1.el7.centos.x86_64.rpm 

2、配置国内镜像源:

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

3、安装所需组件

setenforce 0
yum install -y kubelet kubeadm kubectl
systemctl enable docker && systemctl start docker
systemctl enable kubelet && systemctl start kubelet

4、查看docker 使用的Cgroup driver 和 k8s的是否一致,如果不一致需要修改:

docker info | grep -i cgroup
cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf|grep "cgroup-driver"
 #不一致的情况下需要修改:
sed -i "s/cgroup-driver=systemd/cgroup-driver=cgroupfs/g" /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

5、修改系统配置,防止iptable报错:

cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

6、重启kubelet:

systemctl daemon-reload
systemctl restart kubelet

7、在docker 的启动文件中,加入国内的镜像加速,重启docker:

vim /usr/lib/systemd/system/docker.service
...
ExecStart=/usr/bin/dockerd --registry-mirror https://qxx96o44.mirror.aliyuncs.com
...
systemctl daemon-reload
systemctl restart docker

8、设置主机名并解析,同步系统时间。

ntpdate times.aliyun.com

安装Master

初始化Master

在此处下载docker镜像: https://hub.docker.com/r/anjia0532/

1、使用如下脚本,下载镜像,并修改为google的源,否则容器无法启动:

#!/bin/bash
images=(kube-proxy-amd64:v1.10.2 kube-scheduler-amd64:v1.10.2 kube-controller-manager-amd64:v1.10.2 kube-apiserver-amd64:v1.10.2
etcd-amd64:3.1.12 pause-amd64:3.1 kubernetes-dashboard-amd64:v1.8.3 k8s-dns-sidecar-amd64:1.14.8 k8s-dns-kube-dns-amd64:1.14.8
k8s-dns-dnsmasq-nanny-amd64:1.14.8)
for imageName in ${images[@]} ; do
  docker pull anjia0532/$imageName
  docker tag anjia0532/$imageName k8s.gcr.io/$imageName
  docker rmi anjia0532/$imageName
done

执行此脚本,完成镜像的下载。

2、使用kubeadm init自动安装 Master 节点,需要指定版本:

kubeadm init  --kubernetes-version=v1.10.2    # 使用weave 等网络以这种方式初始化

kubeadm init --kubernetes-version=v1.10.2  --pod-network-cidr=10.244.0.0/16    # 使用flannel 网络需要加 --pod-network-cidr 参数

提示:在执行此命令的过程中,可以实时查看/var/log/message中的日志,确认服务是否启动,要特别注意镜像的版本,名称一致性问题,输出成功的记录需要保留,后面还会用到。

3、服务启动后需要根据输出提示,进行配置,如果是非root 用户需要执行:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 如果是root 用户执行的初始化,则已经创建了此文件, 只需执行:
export KUBECONFIG=/etc/kubernetes/admin.conf

配置网络

提示:我们需要安装网络插件才能使pod之间进行通信,网络插件的安装必须在应用部署之前。同时,在启动kube-dns和内部辅助服务之前必须先配置好网络。

K8S支持多种网络插件,下面是对常见几种插件的简单介绍:

  • ACI通过思科ACI提供集成的集装箱网络和网络安全。
  • Calico是一个安全的L3网络和网络策略提供者。
  • Canal 结合Flannel和Calico,提供网络和网络策略。
  • Cilium是L3网络和网络策略插件,可以透明地实施HTTP / API / L7策略。支持路由和覆盖/封装模式
  • CNI-Genie使Kubernetes能够无缝地连接到一系列CNI插件,例如Calico,Canal,Flannel,Romana或Weave。
  • Contiv为各种用例和丰富的策略框架提供了可配置的网络(使用BGP的本地L3,使用vxlan的覆盖,传统L2和Cisco-SDN / ACI)。 Contiv项目完全开源。安装程序提供基于kubeadm和非kubeadm的安装选项。
  • Flannel 提供一种覆盖网络,可以与Kubernetes一起使用。
  • Multus 提供除Kubernetes中的SRIOV,DPDK,OVS-DPDK和VPP工作负载之外,还是一个多插件,用于支持Kubernetes中的多个网络,以支持所有CNI插件(例如Calico,Cilium,Contiv,Flannel)。
  • NSX-T容器插件(NCP)提供VMware NSX-T与Kubernetes等容器编排器之间的集成,以及NSX-T与基于容器的CaaS / PaaS平台(如Pivotal Container Service(PKS)和Openshift 。
  • Nuage是一个SDN平台,可以在Kubernetes Pod和非Kubernetes环境之间提供基于策略的网络,并提供可视性和安全监控。
  • Romana是一种用于pod网络的第3层网络解决方案,也支持NetworkPolicy API。 Kubeadm附加安装细节可在此处获得。
  • Weave提供网络和网络策略,将在网络分区的两侧进行工作,并且不需要外部数据库

这里我们选择使用 flannel网络。

1、在Master上执行如下命令,完成网络安装:

sysctl net.bridge.bridge-nf-call-iptables=1
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.10.0/Documentation/kube-flannel.yml

## weave 网络使用如下命令,有兴趣的同学可以尝试☺
sysctl net.bridge.bridge-nf-call-iptables=1
kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"

2、验证是否正常启动。

[root@node-1 ~]# kubectl get pods --all-namespaces
NAMESPACE     NAME                             READY     STATUS    RESTARTS   AGE
kube-system   etcd-node-1                      1/1       Running   0          9m
kube-system   kube-apiserver-node-1            1/1       Running   0          9m
kube-system   kube-controller-manager-node-1   1/1       Running   0          9m
kube-system   kube-dns-86f4d74b45-jjc42        3/3       Running   0          10m
kube-system   kube-flannel-ds-hfv57            1/1       Running   0          9m
kube-system   kube-proxy-wsmxl                 1/1       Running   0          10m
kube-system   kube-scheduler-node-1            1/1       Running   0          9m

端口信息:

[root@node-1 ~]# netstat -lntpu
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:10248         0.0.0.0:*               LISTEN      9321/kubelet        
tcp        0      0 127.0.0.1:10249         0.0.0.0:*               LISTEN      9751/kube-proxy     
tcp        0      0 127.0.0.1:10251         0.0.0.0:*               LISTEN      9575/kube-scheduler 
tcp        0      0 127.0.0.1:2379          0.0.0.0:*               LISTEN      9521/etcd           
tcp        0      0 127.0.0.1:10252         0.0.0.0:*               LISTEN      9583/kube-controlle 
tcp        0      0 127.0.0.1:2380          0.0.0.0:*               LISTEN      9521/etcd           
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      963/sshd            
tcp6       0      0 :::10250                :::*                    LISTEN      9321/kubelet        
tcp6       0      0 :::6443                 :::*                    LISTEN      9590/kube-apiserver 
tcp6       0      0 :::10255                :::*                    LISTEN      9321/kubelet        
tcp6       0      0 :::10256                :::*                    LISTEN      9751/kube-proxy     
tcp6       0      0 :::22                   :::*                    LISTEN      963/sshd            
udp        0      0 0.0.0.0:8472            0.0.0.0:*                           -                 

提示:如果执行此命令出现x509的认证失败错误,请确认当前的命令行窗口已经export admin.conf的环境变量。

故障排查思路:

  • 确认端口和容器是否正常启动,查看 /var/log/message日志信息
  • 通过docker logs ID 查看容器的启动日志,特别是频繁创建的容器
  • 使用kubectl --namespace=kube-system describe pod POD-NAME 查看错误状态的pod日志。
  • 使用kubectl -n ${NAMESPACE} logs ${POD_NAME} -c ${CONTAINER_NAME} 查看具体错误。
  • Calico - Canal - Flannel已经被官方验证过,其他的网络插件有可能有坑,能不能爬出来就看个人能力了。
  • 一般常见的错误是镜像名称版本不对或者镜像无法下载。

添加节点

1、在需要添加的节点上执行:

docker pull mirrorgooglecontainers/pause-amd64:3.1
docker pull mirrorgooglecontainers/kube-proxy-amd64:v1.10.2
docker tag mirrorgooglecontainers/pause-amd64:3.1 k8s.gcr.io/pause-amd64:3.1
docker tag mirrorgooglecontainers/kube-proxy-amd64:v1.10.2 k8s.gcr.io/kube-proxy-amd64:v1.10.2

sysctl net.bridge.bridge-nf-call-iptables=1
# 此行命令来源于初始化Master成功后的输出
kubeadm join 10.0.0.1:6443 --token h00k39.t6l79i7cbm79n4gy --discovery-token-ca-cert-hash sha256:205922c8412e5a3ea1616c060c79c9b6b38d098833f46d261fdf5ed1ca7e3027

提示:如果执行join命令时提示token过期,按照提示在Master 上执行kubeadm token create 生成一个新的token。

2、执行添加命令后,在Master上查看节点信息:

[root@node-1 ~]# kubectl get nodes
NAME      STATUS    ROLES     AGE       VERSION
node-1    Ready     master    1h        v1.10.2
node-2    Ready     <none>    23m       v1.10.2

3、查看端口信息:

[root@node-2 ~]# netstat -lntpu
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
tcp        0      0 127.0.0.1:10248         0.0.0.0:*               LISTEN      24246/kubelet       
tcp        0      0 127.0.0.1:10249         0.0.0.0:*               LISTEN      25289/kube-proxy    
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      966/sshd            
tcp6       0      0 :::10250                :::*                    LISTEN      24246/kubelet       
tcp6       0      0 :::10255                :::*                    LISTEN      24246/kubelet       
tcp6       0      0 :::10256                :::*                    LISTEN      25289/kube-proxy    
tcp6       0      0 :::22                   :::*                    LISTEN      966/sshd            
udp        0      0 0.0.0.0:8472            0.0.0.0:*                           -                

4、配置node节点,查看kubete信息:

[root@node-1 ~]# scp /etc/kubernetes/admin.conf  10.0.0.2:/etc/kubernetes/

[root@node-2 ~]# kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes
NAME      STATUS    ROLES     AGE       VERSION
node-1    Ready     master    1h        v1.10.2
node-2    Ready     <none>    40m       v1.10.2

5、安照这种方式,我们可以横向添加多个节点。

创建应用

这里使用mysql 和tomcat 作为示例。

创建mysql的应用

1、创建一个mysql.yaml的文件:

apiVersion: v1
kind: ReplicationController         # 指定kind类型为RC
metadata:
  name: mysql                            # RC的名称,全局唯一 
spec:
  replicas: 1                                # pod 副本数量
  selector:
    app: mysql                             # 符合目标的Pod拥有此标签
  template:                                 # 根据此模板,创建Pod的副本(实例)
    metadata:
      labels:
        app: mysql                        # Pod 副本拥有的标签,对应RC的Selector
    spec:
      containers:                          # Pod 内 容器定义的部分
      - name: mysql                    #  容器名称
        image: mysql                   # 容器镜像
        ports:
        - containerPort: 3306       # 容器应用监听的端口
        env:                                  # 注入容器的环境变量
        - name: MYSQL_ROOT_PASSWORD
          value: "123456"

2、使用kubectl 命令发布到k8s集群,在Master执行:

 kubectl create -f mysql.yaml

3、查看容器,RC和pod状态:

# node-2节点上创建了两个容器,一个mysql 和 pause
[root@node-2 ~]# docker ps | grep mysql
f951c5e1043f        mysql@sha256:d60c13a2bfdbbeb9cf1c84fd3cb0a1577b2bbaeec11e44bf345f4da90586e9e1   "docker-entrypoint..."   19 minutes ago           Up 19 minutes                               k8s_mysql_mysql-kfkdd_default_6b18de55-5b14-11e8-9cc0-000c29c83e1f_0
dd17c26dad8c        k8s.gcr.io/pause-amd64:3.1                                                      "/pause"                 20 minutes ago           Up 20 minutes                               k8s_POD_mysql-kfkdd_default_6b18de55-5b14-11e8-9cc0-000c29c83e1f_0

# 查看 RC,Master上执行
[root@node-1 ~]# kubectl get rc
NAME      DESIRED   CURRENT   READY     AGE
mysql     1         1         1         22m

# 查看pod
[root@node-1 ~]# kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
mysql-kfkdd   1/1       Running   0          22m

4、创建service, 定义个mysql-service.yaml的文件:

apiVersion: v1
kind: Service                       # 说明这是一个K8S Service
metadata: 
  name: mysql                     # Service 的全局唯一名称
spec:
  ports:
    - port: 3306                     # service 提供的服务端口号
  selector:                            # 定义有哪些pod 对应到此服务 
    app: mysql

5、创建service:

[root@node-1 ~]# kubectl create -f mysql-service.yaml 
service "mysql" created

6、查看service,这里自动分配了一个ip和pod 中的虚端口。由于Kubernetes是使用kubeadm 以容器的方式启动,所以有一个kubernetes的服务。

[root@node-1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP    18h
mysql        ClusterIP   10.110.213.104   <none>        3306/TCP   17s

创建tomcat 应用

1、创建rc 文件tomcat.yaml:

apiVersion: v1
kind: ReplicationController
metadata:
  name: myweb
spec:
  replicas: 2                        # 指定两个副本
  selector:
    app: myweb
  template:
    metadata:
      labels:
        app: myweb
    spec:
      containers:
        - name: myweb
          image: kubeguide/tomcat-app:v1   
          ports:
          - containerPort: 8080

2、创建RC,并验证:

[root@node-1 ~]# kubectl create -f tomcat.yaml 
replicationcontroller "myweb" created

[root@node-1 ~]# kubectl get rc
NAME      DESIRED   CURRENT   READY     AGE
mysql     1         1         1         1h
myweb     2         2         2         8m

[root@node-1 ~]# kubectl get pods
NAME          READY     STATUS    RESTARTS   AGE
mysql-kfkdd   1/1       Running   0          1h
myweb-qqmp8   1/1       Running   0          7m
myweb-sz64h   1/1       Running   0          7m

[root@node-2 ~]# docker ps|grep myweb
c98ce89ce2c2        kubeguide/tomcat-app@sha256:7a9193c2e5c6c74b4ad49a8abbf75373d4ab76c8f8db87672dc526b96ac69ac4   "catalina.sh run"        15 minutes ago           Up 15 minutes                               k8s_myweb_myweb-qqmp8_default_3dc7da86-5b20-11e8-9cc0-000c29c83e1f_0
cbdc283633ce        kubeguide/tomcat-app@sha256:7a9193c2e5c6c74b4ad49a8abbf75373d4ab76c8f8db87672dc526b96ac69ac4   "catalina.sh run"        16 minutes ago           Up 16 minutes                               k8s_myweb_myweb-sz64h_default_3dc89ceb-5b20-11e8-9cc0-000c29c83e1f_0
f8416f5e72e9        k8s.gcr.io/pause-amd64:3.1                                                                     "/pause"                 17 minutes ago           Up 17 minutes                               k8s_POD_myweb-qqmp8_default_3dc7da86-5b20-11e8-9cc0-000c29c83e1f_0
70b6cd00594a        k8s.gcr.io/pause-amd64:3.1                                                                     "/pause"                 17 minutes ago           Up 17 minutes                               k8s_POD_myweb-sz64h_default_3dc89ceb-5b20-11e8-9cc0-000c29c83e1f_0

3、 创建Service,定义 tomcat-service.yaml:

apiVersion: v1
kind: Service
metadata: 
  name: myweb
spec:
  type: NodePort      # 定义外网访问模式
  ports:
    - port: 8080
      nodePort: 30001   # 外网访问的端口,映射的本地宿主机端口
  selector:
    app: myweb

4、验证:

[root@node-1 ~]# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP          19h
mysql        ClusterIP   10.110.213.104   <none>        3306/TCP         1h
myweb        NodePort    10.102.243.124   <none>        8080:30001/TCP   5m

[root@node-1 ~]# netstat -lntp|grep 30001
tcp6       0      0 :::30001                :::*                    LISTEN      2763/kube-proxy 

[root@node-2 ~]# netstat -lntp|grep 30001
tcp6       0      0 :::30001                :::*                    LISTEN      2362/kube-proxy    

通过访问Master或node节点的30001端口可以访问到tomcat默认页面。

5、如果要删除一个service 执行:

kubectl delete service  mysql

提示:删除pod 后会自动创建一个新的pod,删除node上运行的容器也会自动创建一个新的容器。