k8s1.11.0安裝、一個master、一個node、查看node名稱是ip、node是擴容進來的、帶cadvisor監控服務


一個master、一個node、查看node節點是ip


# 安裝順序:先在test1 上安裝完必要組件后,就開始在 test2 上單獨安裝node組件,實現node功能,再返回來配置test1加入集群,實現node功能

# 本實驗 test1 節點不做安裝kubelet組件。只有安裝啟動了kubelet才會生成csr,kube-apiserver通過csr請求后才會成為一個node。所以是node節點單獨安裝

# 注意:准備了三台,這三台都做了環境准備,但是k8s集群只是使用了 test1、test2這兩個節點,如果把test3擴容進來,隨時都可以,

# 注意:本實驗 test2 沒有安裝etcd,之前安裝的etcd給剔除了

# 本實驗 kubelet組件的參數中 --hostname-override= 寫的ip地址,通過 kubectl get nodes 查看得到的name就是ip,如果填寫主機名,得到的name就是主機名

實驗架構:

# 注意:下面列出來的組件順序就是本實驗的組件安裝順序

test1: 192.168.0.91    etcd、kubectl工具、kube-apiserver、kube-controller-manager、kube-scheduler、kubelet組件、cni、kube-proxy

test2: 192.168.0.92    docker、kubectl工具、kubelet組件、cni、kube-proxy、flannel、coredns

test3:192.168.0.931、環境配置

如下操作在所有節點操作

配置hosts解析
[root@bogon ~]# hostnamectl set-hostname test1
[root@bogon~]# hostnamectl set-hostname test2
[root@bogon ~]# hostnamectl set-hostname test3

cat >>/etc/hosts<<EOF
192.168.0.91 test1
192.168.0.92 test2
192.168.0.93 test3
EOF


禁用selinux

sed -i 's/SELINUX=permissive/SELINUX=disabled/' /etc/sysconfig/selinux

sed -i 's/enforcing/disabled/g' /etc/selinux/config


關閉swap

# 注釋/etc/fstab文件里swap相關的行

sed -i 's/\/dev\/mapper\/centos-swap/#\/dev\/mapper\/centos-swap/g' /etc/fstab


#關掉防火牆

systemctl stop firewalld && systemctl disable firewalld


配置免密登錄


退出xshell重新登錄,查主機名已改變


開啟forward
iptables -P FORWARD ACCEPT


配置轉發相關參數
cat >> /etc/sysctl.d/k8s.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
vm.swappiness=0
EOF
sysctl --system


加載ipvs相關內核模塊
如果重新開機,需要重新加載
modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
modprobe nf_conntrack_ipv4
lsmod | grep ip_vs


2、安裝etcd
etcd安裝請參照: https://www.cnblogs.com/effortsing/p/10295261.html

下面開始安裝k8s組件,序號從6開始


6、分發二進制組件

# 只在 test1 上操作

# 提前分發k8s所有組件二進制文件、順便安裝kubectl工具

# 安裝包解壓后包括 kubectl 工具,所以不需要單獨使用kubernetes-server-client-amd64.tar.gz 安裝包分發 kubectl 工具


下載、解壓安裝包

cd /server/software/k8s

下載鏈接:https://pan.baidu.com/s/1DXahqP8nXWP1aw5pIunJrw 
提取碼:9xfp

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



分發所有組件二進制文件,

# 后面配置 kube-apiserver 等各個組件啟動文件里面需要帶上各自二進制文件路徑

mkdir -p /usr/local/kubernetes/bin
cd /server/software/k8s/kubernetes/server/bin
cp kube-apiserver kube-controller-manager kube-scheduler kube-proxy kubectl /usr/local/kubernetes/bin    #這一步很關鍵



安裝kubectl工具

# 后面創建 admin kubeconfig等配置文件時候需要用到;

# admin.conf = ~/.kube/config,因為是復制過來的 ;

# kubectl作用:當kubelet組件通過bootstrap token 認證后,kubectl默認從 ~/.kube/config 文件讀取 kube-apiserver 地址、證書、用戶名等信息;

cp /usr/local/kubernetes/bin/kubectl /usr/local/bin/kubectl



查看 kubectl 版本,

# 出現下面的情況就是正確的。did you specify the right host or port? 這個報錯忽略,因為還沒有安裝kubelet服務

kubectl version

[root@test1 bin]# kubectl version
Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.0", GitCommit:"91e7b4fd31fcd3d5f436da26c980becec37ceefe", GitTreeState:"clean", BuildDate:"2018-06-27T20:17:28Z", GoVersion:"go1.10.2", Compiler:"gc", Platform:"linux/amd64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?
 
cd $HOME




7、生成admin的ca證書和私鑰 

# 只在 test1 上操作  

# kubectl 作為集群的管理工具,需要被授予最高權限。這里創建具有最高權限的 admin 證書、admin kubeconfig

# 注意:后面只有apiserver和kubelet這兩個服務啟動參數會用到admin的ca證書;kubectl工具和kubelet服務不是一回事

cd $HOME/ssl

cat >admin-csr.json<<EOF
{
    "CN": "admin",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "BeiJing",
            "L": "BeiJing",
            "O": "system:masters",
            "OU": "System"
        }
    ]
}
EOF


生成證書

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
-profile=kubernetes admin-csr.json | cfssljson -bare admin


查看生成的admin ca

ls admin*.pem




8、配置 kube-apiserver ca

# 只在 test1 上操作

# 10.96.0.1 是 kube-apiserver 指定的 service-cluster-ip-range 網段的第一個IP

cd $HOME/ssl

cat >kube-apiserver-csr.json<<EOF
{
    "CN": "kube-apiserver",
    "hosts": [
      "127.0.0.1",
      "192.168.0.91",
      "192.168.0.92",
      "192.168.0.93",
      "10.96.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": "BeiJing",
            "L": "BeiJing",
            "O": "k8s",
            "OU": "System"
        }
    ]
}
EOF

生成 kube-apiserver ca

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
-profile=kubernetes kube-apiserver-csr.json | cfssljson -bare kube-apiserver

查看生成的kube-apiserver ca

ls kube-apiserver*.pem




9、配置 kube-controller-manager ca

# 只在 test1 上操作

cd $HOME/ssl

cat >kube-controller-manager-csr.json<<EOF
{
    "CN": "system:kube-controller-manager",
    "hosts": [
      "127.0.0.1",
      "192.168.0.91"
    ],  
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "BeiJing",
            "L": "BeiJing",
            "O": "system:kube-controller-manager",
            "OU": "System"
        }
    ]
}
EOF

生成 kube-controller-manager ca

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
-profile=kubernetes kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager

查看生成的kube-controller-manager ca

ls kube-controller-manager*.pem






10、配置 kube-scheduler ca

# 只在 test1 上操作

cd $HOME/ssl

cat >kube-scheduler-csr.json<<EOF
{
    "CN": "system:kube-scheduler",
    "hosts": [
      "127.0.0.1",
      "192.168.0.91"
    ],
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "BeiJing",
            "L": "BeiJing",
            "O": "system:kube-scheduler",
            "OU": "System"
        }
    ]
}
EOF

生成 kube-scheduler ca

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
-profile=kubernetes kube-scheduler-csr.json | cfssljson -bare kube-scheduler

查看生成的kube-scheduler ca

ls kube-scheduler*.pem




11、配置 kube-proxy ca

# 只在 test1 上操作

# 注意:只是node節點需要用到kube-proxy ca

cd $HOME/ssl

cat >kube-proxy-csr.json<<EOF
{
    "CN": "system:kube-proxy",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "ST": "BeiJing",
            "L": "BeiJing",
            "O": "system:kube-proxy",
            "OU": "System"
        }
    ]
}
EOF


生成 kube-proxy ca

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json \
-profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy

查看生成的kube-proxy ca

ls kube-proxy*.pem



12、復制所有的ca 證書到一個目錄里面,方便管理

# 只在 test1 上操作

cd $HOME/ssl
mkdir -p /etc/kubernetes/pki
cp ca*.pem admin*.pem kube-proxy*.pem kube-scheduler*.pem kube-controller-manager*.pem kube-apiserver*.pem /etc/kubernetes/pki



13、開啟 bootstrap token 認證 ,kubelet TLS Boostrap機制 

# 只在 test1 上操作

# kube-apiserver、kubelet啟動文件需要用到token,

# token中包含kubelet-bootstrap用戶

# 后面kubelet組件啟動參數中需要使用 kubelet-bootstrap.conf 文件向 kube-apiserver 發送 CSR 請求,

# 請求通過后,kubectl才會從 ~/.kube/config 文件讀取 kube-apiserver 地址、證書、用戶名等信息



靜態獲取token

# 還有一種是用 kubeadm 動態獲取token,kubeadm token create,這樣可以使用 TLS bootstrap 機制自動生成 client 和 server 證書,過期后自動輪轉。

# 本實驗采用靜態獲取,一天后過期

export BOOTSTRAP_TOKEN=$(head -c 16 /dev/urandom | od -An -t x | tr -d ' ')



創建token.csv文件

# 只有kube-apiserver 啟動文件中需要用到

cat > /etc/kubernetes/token.csv <<EOF
${BOOTSTRAP_TOKEN},kubelet-bootstrap,10001,"system:kubelet-bootstrap"
EOF



創建kubelet-bootstrap.conf

# 只有kubelet啟動文件需要用到

cd /etc/kubernetes

export KUBE_APISERVER="https://192.168.0.91:6443"

kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/pki/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kubelet-bootstrap.conf

kubectl config set-credentials kubelet-bootstrap \
  --token=${BOOTSTRAP_TOKEN} \
  --kubeconfig=kubelet-bootstrap.conf

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

kubectl config use-context default --kubeconfig=kubelet-bootstrap.conf



給kubelet-bootstrap用戶授權

# 創建一個 clusterrolebinding,將 bootstrap token 文件中的 kubelet-bootstrap 用戶賦予 system:node-bootstrapper cluster 角色

# 默認情況下,bootstrap這個 user 和 group 沒有創建 CSR 的權限,kubelet 會啟動失敗,所以要給kubelet-bootstrap角色授權

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

# 如果沒有授權會出現下面錯誤:

[root@test2 kubernetes]# journalctl -u kubelet |tail
failed to run Kubelet: cannot create certificate signing request: certificatesigningrequests.certificates.k8s.io is forbidden: User "kubelet-bootstrap" cannot create certificatesigningrequests.certificates.k8s.io at the cluster scope



14、創建 admin kubeconfig 

只在 test1 上操作

# 只有kubelet服務啟動參數需要用到admin kubeconfig,
# admin.conf = ~/.kube/config,因為復制過來的 ;kubectl 默認從 ~/.kube/config 文件讀取 kube-apiserver 地址、證書、用戶名等信息,

cd /etc/kubernetes

export KUBE_APISERVER="https://192.168.0.91:6443"

設置集群參數

kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/pki/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=admin.conf

設置客戶端認證參數

kubectl config set-credentials admin \
  --client-certificate=/etc/kubernetes/pki/admin.pem \
  --client-key=/etc/kubernetes/pki/admin-key.pem \
  --embed-certs=true \
  --kubeconfig=admin.conf

設置上下文參數

kubectl config set-context default \
  --cluster=kubernetes \
  --user=admin \
  --kubeconfig=admin.conf

設置默認上下文

kubectl config use-context default --kubeconfig=admin.conf



15、創建 kube-controller-manager kubeconfig

只在 test1 上操作

cd /etc/kubernetes

export KUBE_APISERVER="https://192.168.0.91:6443"

kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/pki/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kube-controller-manager.conf

kubectl config set-credentials kube-controller-manager \
  --client-certificate=/etc/kubernetes/pki/kube-controller-manager.pem \
  --client-key=/etc/kubernetes/pki/kube-controller-manager-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-controller-manager.conf

kubectl config set-context default \
  --cluster=kubernetes \
  --user=kube-controller-manager \
  --kubeconfig=kube-controller-manager.conf

kubectl config use-context default --kubeconfig=kube-controller-manager.conf




16、創建 kube-scheduler kubeconfig

# 只在 test1 上操作

cd /etc/kubernetes

export KUBE_APISERVER="https://192.168.0.91:6443"

kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/pki/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kube-scheduler.conf

kubectl config set-credentials kube-scheduler \
  --client-certificate=/etc/kubernetes/pki/kube-scheduler.pem \
  --client-key=/etc/kubernetes/pki/kube-scheduler-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-scheduler.conf

kubectl config set-context default \
  --cluster=kubernetes \
  --user=kube-scheduler \
  --kubeconfig=kube-scheduler.conf

kubectl config use-context default --kubeconfig=kube-scheduler.conf




17、創建 kube-proxy kubeconfig

# 只在 test1 上操作

# 注意:只是node節點需要用到kube-proxy kubeconfig

cd /etc/kubernetes

export KUBE_APISERVER="https://192.168.0.91:6443"

kubectl config set-cluster kubernetes \
  --certificate-authority=/etc/kubernetes/pki/ca.pem \
  --embed-certs=true \
  --server=${KUBE_APISERVER} \
  --kubeconfig=kube-proxy.conf

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

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

kubectl config use-context default --kubeconfig=kube-proxy.conf

cd $HOME




18、配置啟動kube-apiserver

# 只在 test1 上操作

復制 etcd ca

mkdir -pv /etc/kubernetes/pki/etcd
cd $HOME/ssl
cp etcd.pem etcd-key.pem ca-key.pem ca.pem /etc/kubernetes/pki/etcd



生成 service account key

cd /etc/kubernetes/pki/
openssl genrsa -out /etc/kubernetes/pki/sa.key 2048
openssl rsa -in /etc/kubernetes/pki/sa.key -pubout -out /etc/kubernetes/pki/sa.pub
ls /etc/kubernetes/pki/sa.*
cd $HOME



配置啟動文件

cat >/etc/systemd/system/kube-apiserver.service<<EOF
[Unit]
Description=Kubernetes API Service
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/apiserver
ExecStart=/usr/local/kubernetes/bin/kube-apiserver \\
        \$KUBE_LOGTOSTDERR \\
        \$KUBE_LOG_LEVEL \\
        \$KUBE_ETCD_ARGS \\
        \$KUBE_API_ADDRESS \\
        \$KUBE_SERVICE_ADDRESSES \\
        \$KUBE_ADMISSION_CONTROL \\
        \$KUBE_APISERVER_ARGS
Restart=on-failure
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF



配置參數變量文件

# 下面 kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy這些服務 都需要用到,這里只配置一次,以后重復利用,后面的也寫了變量文件,只是為了知道怎么回事

cat >/etc/kubernetes/config<<EOF
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=2"
EOF



配置apiserver

# 注意參數--token-auth-file=/etc/kubernetes/token.csv 表示在 apiserver 中靜態配置bootstrap token,和后面開啟 bootstrap token 認證步驟相呼應,不是動態的,所以有過期時間,

# 后面kubelet組件啟動參數中需要使用 kubelet-bootstrap.conf 文件向 kube-apiserver 發送 CSR 請求,--bootstrap-kubeconfig 文件里面包含token和apiserver里面的token是一樣的,

cat >/etc/kubernetes/apiserver<<EOF
KUBE_API_ADDRESS="--advertise-address=192.168.0.91"
KUBE_ETCD_ARGS="--etcd-servers=https://192.168.0.91:2379 --etcd-cafile=/etc/kubernetes/pki/ca.pem --etcd-certfile=/etc/kubernetes/pki/etcd/etcd.pem --etcd-keyfile=/etc/kubernetes/pki/etcd/etcd-key.pem"
KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=10.96.0.0/12"
KUBE_ADMISSION_CONTROL="--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota"
KUBE_APISERVER_ARGS="--allow-privileged=true --authorization-mode=Node,RBAC --enable-bootstrap-token-auth=true --token-auth-file=/etc/kubernetes/token.csv --service-node-port-range=0-32767 --tls-cert-file=/etc/kubernetes/pki/kube-apiserver.pem --tls-private-key-file=/etc/kubernetes/pki/kube-apiserver-key.pem --client-ca-file=/etc/kubernetes/pki/ca.pem --service-account-key-file=/etc/kubernetes/pki/sa.pub --enable-swagger-ui=true --secure-port=6443 --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname --anonymous-auth=false --kubelet-client-certificate=/etc/kubernetes/pki/admin.pem --kubelet-client-key=/etc/kubernetes/pki/admin-key.pem"
EOF



啟動

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



通過瀏覽器訪問測試

# 401 報錯正常,是因為沒有權限,不影響,以后解決

curl https://192.168.0.91:6443/swaggerapi
[root@test1 ~]# curl https://192.168.0.91:6443/swaggerapi
curl: (60) Peer's Certificate issuer is not recognized.
More details here: http://curl.haxx.se/docs/sslcerts.html

curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.


19、配置啟動kube-controller-manager

# 只在 tes1 上操作

配置啟動文件

cat >/etc/systemd/system/kube-controller-manager.service<<EOF
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/controller-manager
ExecStart=/usr/local/kubernetes/bin/kube-controller-manager \\
        \$KUBE_LOGTOSTDERR \\
        \$KUBE_LOG_LEVEL \\
        \$KUBECONFIG \\
        \$KUBE_CONTROLLER_MANAGER_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF



配置參數變量文件

# 配置kube-apiserver啟動文件時已經配置過參數變量文件,這里就不需要再做,寫在這里只是為了知道下面配置文件里的參數怎么回事

cat >/etc/kubernetes/config<<EOF
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=2"
EOF



配置controller-manager文件

# 特別注意:這里的cluster-cidr地址要和 Kube-proxy里面的cluster-cidr、flannel 里面的Network 地址保持一致,這有這三個組件里面有 10.244.0.0

cat >/etc/kubernetes/controller-manager<<EOF
KUBECONFIG="--kubeconfig=/etc/kubernetes/kube-controller-manager.conf"
KUBE_CONTROLLER_MANAGER_ARGS="--address=127.0.0.1 --cluster-cidr=10.244.0.0/16 --cluster-name=kubernetes --cluster-signing-cert-file=/etc/kubernetes/pki/ca.pem --cluster-signing-key-file=/etc/kubernetes/pki/ca-key.pem --service-account-private-key-file=/etc/kubernetes/pki/sa.key --root-ca-file=/etc/kubernetes/pki/ca.pem --leader-elect=true --use-service-account-credentials=true --node-monitor-grace-period=10s --pod-eviction-timeout=10s --allocate-node-cidrs=true --controllers=*,bootstrapsigner,tokencleaner"
EOF



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




20、配置啟動kube-scheduler

# 只在 test1 上操作

配置啟動文件

cat >/etc/systemd/system/kube-scheduler.service<<EOF
[Unit]
Description=Kubernetes Scheduler Plugin
Documentation=https://github.com/kubernetes/kubernetes

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/scheduler
ExecStart=/usr/local/kubernetes/bin/kube-scheduler \\
            \$KUBE_LOGTOSTDERR \\
            \$KUBE_LOG_LEVEL \\
            \$KUBECONFIG \\
            \$KUBE_SCHEDULER_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF



配置參數變量文件

# 配置kube-apiserver啟動文件時已經配置過參數變量文件,這里就不需要再做,寫在這里只是為了知道下面配置文件里的參數怎么回事

cat >/etc/kubernetes/config<<EOF
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=2"
EOF



配置scheduler文件

cat >/etc/kubernetes/scheduler<<EOF
KUBECONFIG="--kubeconfig=/etc/kubernetes/kube-scheduler.conf"
KUBE_SCHEDULER_ARGS="--leader-elect=true --address=127.0.0.1"
EOF



啟動

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


給kubelet-bootstrap用戶授權
# 創建一個 clusterrolebinding,將 bootstrap token 文件中的 kubelet-bootstrap 用戶賦予 system:node-bootstrapper cluster 角色

# 默認情況下,bootstrap這個 user 和 group 沒有創建 CSR 的權限,kubelet 會啟動失敗,所以要給kubelet-bootstrap角色授權

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

# 如果沒有授權會出現下面錯誤:

[root@test2 kubernetes]# journalctl -u kubelet |tail
failed to run Kubelet: cannot create certificate signing request: certificatesigningrequests.certificates.k8s.io is forbidden: User "kubelet-bootstrap" cannot create certificatesigningrequests.certificates.k8s.io at the cluster scope
 
查看組件狀態

kubectl get componentstatuses

[root@test2 ~]# kubectl get componentstatuses
Unable to connect to the server: x509: certificate signed by unknown authority
# 報錯原因:經過排查后發現前面操作的幾個步驟中 ${KUBE_APISERVER} 這個變量寫成了 ${KUBE_  又重做了一遍,正確的結果是下面

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

 # test1 節點安裝到此結束,下面開始單獨安裝node節點

21、單獨配置node 相關組件


test2上操作


環境配置

# 如下操作在所有節點操作


修改主機名

# 注意修改 各自節點對應的 主機名

sed -i '$a\hostname=test2' /etc/hostname
sed -i '$a\hostname=test2' /etc/sysconfig/network && hostnamectl set-hostname test2



配置hosts解析

cat >>/etc/hosts<<EOF
192.168.0.91 test1
192.168.0.92 test2
192.168.0.93 test3
EOF


禁用selinux

sed -i 's/SELINUX=permissive/SELINUX=disabled/' /etc/sysconfig/selinux

sed -i 's/enforcing/disabled/g' /etc/selinux/config


關閉swap

# 注釋/etc/fstab文件里swap相關的行

sed -i 's/\/dev\/mapper\/centos-swap/#\/dev\/mapper\/centos-swap/g' /etc/fstab


關掉防火牆

systemctl stop firewalld && systemctl disable firewalld


重啟

reboot


開啟forward

iptables -P FORWARD ACCEPT



配置轉發相關參數

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



加載系統參數

sysctl --system



加載ipvs相關內核模塊

# 如果重新開機,需要重新加載

modprobe ip_vs
modprobe ip_vs_rr
modprobe ip_vs_wrr
modprobe ip_vs_sh
modprobe nf_conntrack_ipv4
lsmod | grep ip_vs


21.1、安裝docker

# 只在 test2 上操作

# 注意:docker和flannel是一體的,哪個節點上需要安裝flannel,哪個節點上就需要安裝docker

# docker 和kubelet組件、node狀態有關系, 之前停掉docker后,kubelet組件會自動停掉;node節點會變成 NotReady 狀態

# 但是停掉docker后,flanel、coredns 的pod 讓然是 running 狀態

# v1.11.0版本推薦使用docker v17.03, v1.11,v1.12,v1.13, 也可以使用,再高版本的docker可能無法正常使用。測試發現17.09無法正常使用,不能使用資源限制(內存CPU)



卸載自帶docker

yum remove -y docker-ce docker-ce-selinux container-selinux



下載Docker鏡像

下載鏈接:https://pan.baidu.com/s/1whfkq5wDODIew5_eqP63gA 
提取碼:xmqq 

下載鏈接:https://pan.baidu.com/s/1zZy_tbvuApZW2fsMPGv3Bg 
提取碼:u3t3 




添加至鏡像倉庫

rpm -ivh docker-ce-17.03.2.ce-1.el7.centos.x86_64.rpm
rpm -ivh docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm



用安裝 Docker 和依賴包
 
yum install -y docker-ce-*.rpm 



開機啟動 

systemctl enable docker 



啟動 docker 服務 

systemctl start docker




21.2、部署 kubelet組件

# 只在 test2 上操作

# kubelet的作用:向 test1 發起csr請求。

# 初次安裝完 kubelet,只要第一次啟動kubelet,通過kubelet get csr 就會得到這串字符串:node-csr-Yiiv675wUCvQl3HH11jDr0cC9p3kbrXWrxvG3EjWGoE

下載、解壓安裝包

mkdir -p /server/software/k8s
cd /server/software/k8s
下載鏈接:https://pan.baidu.com/s/1DXahqP8nXWP1aw5pIunJrw 
提取碼:9xfp
tar -xf kubernetes-server-linux-amd64.tar.gz



分發kubelet二進制文件

# 后面kubelet啟動文件需要用到kubelet二進制文件路徑

mkdir -p /usr/local/kubernetes/bin
cp /server/software/k8s/kubernetes/server/bin/kubelet /usr/local/kubernetes/bin    #這一步很關鍵



安裝kubectl工具

# kubecctl工具和kubelet服務不是一回事。安裝包解壓后包括 kubectl 工具,所以不需要單獨使用kubernetes-server-client-amd64.tar.gz 安裝包分發 kubectl 工具

# kubectl作用:當kubelet組件通過bootstrap token 認證后,kubectl 默認會從 ~/.kube/config 文件讀取 kube-apiserver 地址、證書、用戶名等信息;

# admin.conf = ~/.kube/config,因為復制過來的 ;

cp /server/software/k8s/kubernetes/server/bin/kubectl /usr/local/bin/kubectl



查看 kubectl 版本,

# 出現下面的情況就是正確的。did you specify the right host or port? 這個報錯忽略,因為還沒有安裝kubelet服務

kubectl version

[root@test1 bin]# kubectl version
Client Version: version.Info{Major:"1", Minor:"11", GitVersion:"v1.11.0", GitCommit:"91e7b4fd31fcd3d5f436da26c980becec37ceefe", GitTreeState:"clean", BuildDate:"2018-06-27T20:17:28Z", GoVersion:"go1.10.2", Compiler:"gc", Platform:"linux/amd64"}
The connection to the server localhost:8080 was refused - did you specify the right host or port?
 
cd $HOME



拷貝admin.conf

# 從test1上復制admin.conf到test2節點

test2上創建目錄

mkdir -p /etc/kubernetes

test2上復制

scp /etc/kubernetes/admin.conf root@192.168.0.92:/etc/kubernetes/



配置 /.kube/config 並且授權

# kubectl 默認從 ~/.kube/config 文件讀取 kube-apiserver 地址、證書、用戶名等信息,如果沒有配置,執行 kubectl 命令時可能會出錯:
# /.kube/config 和 admin.conf 內容是一樣的,/.kube/config 是 admin.conf 復制過來的,改了下名字而已

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



拷貝bootstrap.conf

#從test1 上復制 bootstrap.conf 到test2 節點

# 后面kubelet服務啟動參數中需要使用 kubelet-bootstrap.conf 向 kube-apiserver 發送 CSR 請求,

# 當kubelet服務通過bootstrap token 認證后,kubectl 默認會從 ~/.kube/config 文件讀取 kube-apiserver 地址、證書、用戶名等信息;

scp /etc/kubernetes/kubelet-bootstrap.conf root@192.168.0.92:/etc/kubernetes/



安裝cni

# kubelet 的啟動參數需要用到

cd /server/software/k8s

下載鏈接:https://pan.baidu.com/s/1DL1v4cH2SHUfobICjKXm3Q 
提取碼:puel
mkdir -p /opt/cni/bin
mkdir -p /etc/cni/net.d/
tar -xf cni-plugins-amd64-v0.7.1.tgz -C /opt/cni/bin
ls -l /opt/cni/bin

cd $HOME




配置啟動kubelet

創建數據目錄

mkdir -p /data/kubelet



配置kubelet啟動文件

cat >/etc/systemd/system/kubelet.service<<EOF
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/kubernetes/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/data/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/local/kubernetes/bin/kubelet \\
            \$KUBE_LOGTOSTDERR \\
            \$KUBE_LOG_LEVEL \\
            \$KUBELET_CONFIG \\
            \$KUBELET_HOSTNAME \\
            \$KUBELET_POD_INFRA_CONTAINER \\
            \$KUBELET_ARGS \\
            \$CADVISOR
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF



配置參數變量文件

cat >/etc/kubernetes/config<<EOF
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=2"
EOF



配置kubelet文件

# 注意修改kubelet文件里面的 --hostname-override= 要填寫test1、test2、test3 這三個節點改成對應各自 ip ,或者各自的主機名。本實驗寫的ip

# 如果--hostname-override= 填寫的是ip,那么kubectl get nodes 得到的name就顯示ip,如果填寫的是主機名,得到的name就顯示主機名

# image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1 這個鏡像跟 kubectl get componentstatuses 獲取不到資源沒有關系,跟docker也沒有關系

# 但是網上搜這個鏡像,說這個鏡像是基礎鏡像,目前被牆,最好下載放到本地 registry 上

# 注意 kubelet配置文件里面的參數kubelet.conf 是kubelet啟動后生成的,不用管,就是這樣寫的

# 注意 /etc/kubernetes/pki 目錄里面只放一個ca.pem 證書,

# 啟動kubelet后/etc/kubernetes/pki 目錄里面會自動生成四個文件 kubelet.crt、kubelet.key、kubelet-client-current.pem、kubelet-client-2019-01-22-10-07-06.pem

# CADVISOR 表明開啟cadvisor監控,然后在啟動文件中加上 ExecStart=$CADVISOR 就可以通過瀏覽器訪問cadvisor監控,否則是無法訪問的,

cat >/etc/kubernetes/kubelet<<EOF
KUBELET_HOSTNAME="--hostname-override=test2"
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1"
KUBELET_CONFIG="--config=/etc/kubernetes/kubelet-config.yml"
CADVISOR="--cadvisor-port=4194 --storage-driver-db='cadvisor' --storage-driver-host='localhost:8086'"
KUBELET_ARGS="--bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.conf --kubeconfig=/etc/kubernetes/kubelet.conf --cert-dir=/etc/kubernetes/pki --network-plugin=cni --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d"
EOF


拷貝ca.pem 證書

# 從test1 節點復制復制ca.pem 證書到test2 節點,下面kubelet-config.yml 文件參數需要用到 ca.pem

tes2上創建目錄

mkdir --p /etc/kubernetes/pki

test1上復制文件

scp $HOME/ssl/ca.pem 192.168.0.92:/etc/kubernetes/pki/


配置kubelet-config.yml文件

# 注意修改kubelet-config.yml相關ip,test1 test2 test3 使用各自ip

# 注意下面kubelet-config.yml里面的clusterDNS 地址要和coredns.yaml 里面的 clusterIP 地址保持一致。只有kubelet、coredns里面有 10.96.0.10,其他的組件都沒有

cat >/etc/kubernetes/kubelet-config.yml<<EOF
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: 192.168.0.92
port: 10250
cgroupDriver: cgroupfs
clusterDNS:
  - 10.96.0.10
clusterDomain: cluster.local.
hairpinMode: promiscuous-bridge
serializeImagePulls: false
authentication:
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.pem
EOF



啟動
systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet            # 啟動后就會生成 csr
systemctl status kubelet



查看日志

# 啟動 kubelet后查看日志會有報錯

# 報錯原因是因為kubelet配置了network-plugin=cni,但是還沒安裝網絡插件 flannel,所以狀態會是NotReady,會報上面的錯誤,不想看這個報錯或者不需要網絡,就修改kubelet配置文件,去掉network-plugin=cni 就可以了

[root@test2 ~]# journalctl -u kubelet |tail
network plugin is not ready: cni config uninitialized



21.3、通過csr請求

# 在 test2 上操作,也可以在test1 上操作

# 通過csr請求的目的就是要生成一個nodes 節點

# 注意:node-csr-Yiiv675wUCvQl3HH11jDr0cC9p3kbrXWrxvG3EjWGoE  這個長字符串是kubelet 初次啟動后生成的,

# 在test2 節點上操作就可以

# 查看csr

# 如果獲取不到csr看日志會出現下面錯誤:

[root@test2 kubernetes]# journalctl -u kubelet |tail
failed to run Kubelet: cannot create certificate signing request: certificatesigningrequests.certificates.k8s.io is forbidden: User "kubelet-bootstrap" cannot create certificatesigningrequests.certificates.k8s.io at the cluster scope

# 原因是因為沒有給kubelet-bootstrap授權,授權即可

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

kubectl get csr


執行結果

[root@test2 ~]# kubectl get csr
NAME                                                   AGE       REQUESTOR           CONDITION
node-csr-Yiiv675wUCvQl3HH11jDr0cC9p3kbrXWrxvG3EjWGoE   18s       kubelet-bootstrap   Pending
You have new mail in /var/spool/mail/root



通過csr請求,下面的長字符串填寫上一步的結果,

kubectl certificate approve node-csr-Yiiv675wUCvQl3HH11jDr0cC9p3kbrXWrxvG3EjWGoE


執行結果:

[root@test2 ~]# kubectl certificate approve node-csr-Yiiv675wUCvQl3HH11jDr0cC9p3kbrXWrxvG3EjWGoE
certificatesigningrequest.certificates.k8s.io/node-csr-Yiiv675wUCvQl3HH11jDr0cC9p3kbrXWrxvG3EjWGoE approved


查看kubelet啟動后生成的文件

說明:kubelet 啟動后使用 --bootstrap-kubeconfig 向 kube-apiserver 發送 CSR 請求,當這個 CSR 被 approve 后,kube-controller-manager 為 kubelet 創建 TLS 客戶端證書、私鑰和 --kubeletconfig 文件。

kubelet.conf文件決定了csr的存在,如果要想重新獲取csr,可以停掉kubelet,刪除kubelet.conf文件,重啟kubelet就可以獲得csr

ls -l /etc/kubernetes/kubelet.conf
ls -l /etc/kubernetes/pki/kubelet*

查看結果

[root@test2 ~]# ls -l /etc/kubernetes/kubelet.conf
-rw------- 1 root root 2295 Jan 22 10:07 /etc/kubernetes/kubelet.conf

[root@test2 ~]# ls -l /etc/kubernetes/pki/kubelet*
-rw------- 1 root root 1273 Jan 22 10:07 /etc/kubernetes/pki/kubelet-client-2019-01-22-10-07-06.pem
lrwxrwxrwx 1 root root   58 Jan 22 10:07 /etc/kubernetes/pki/kubelet-client-current.pem -> /etc/kubernetes/pki/kubelet-client-2019-01-22-10-07-06.pem
-rw-r--r-- 1 root root 2181 Jan 22 02:10 /etc/kubernetes/pki/kubelet.crt
-rw------- 1 root root 1675 Jan 22 02:10 /etc/kubernetes/pki/kubelet.key


查看節點

# 此時節點狀態為 NotReady,需要安裝flannel網絡后才可以ready;

# 但是此時就可以設置集群角色

# 此時查看只有一個node節點是因為test1 上並沒有安裝 kubelet 組件,只要 test1 安裝上kubelet並且啟動成功,就會生成csr,通過csr后,就會成為node節點

# 如果 test1 上也安裝了kubelet,就可以把 test1 節點設置為master,因為 test1 節點上面安裝了 kube-apiserver、 kube-controller-manager、kube-scheduler

# tes2 節點必須是node 節點,因為它沒有安裝  kube-apiserver、 kube-controller-manager、kube-scheduler 這幾個組件

kubectl get nodes

[root@test2 ~]# kubectl get nodes
NAME           STATUS     ROLES     AGE       VERSION
192.168.0.92   NotReady   <none>    9h        v1.11.0



設置集群角色

# 在test2 節點上操作就可以

# 設置 test2 為 node 角色

kubectl label nodes 192.168.0.92 node-role.kubernetes.io/node=

[root@test2 ~]# kubectl label nodes 192.168.0.92 node-role.kubernetes.io/node=
node/test2 labeled


再次查看節點

[root@test2 ~]# kubectl get nodes
NAME  STATUS    ROLES  AGE  VERSION
test2 NotReady  node   2m   v1.11.0


 21.4、配置啟動kube-proxy 


# 只在 test2 上操作

# 注意:關掉 kube-proxy 后仍然可以訪問 svc curl "10.103.38.143:80" ,kube-proxy 的作用目前還沒有發現

下載、解壓安裝包(省略)

cd /server/software/k8s

下載鏈接:https://pan.baidu.com/s/1DXahqP8nXWP1aw5pIunJrw 
提取碼:9xfp

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



分發kube-proxy二進制文件

# 后面kube-proxy.service啟動文件需要用到kube-proxy二進制文件路徑

mkdir -p /usr/local/kubernetes/bin
cp /server/software/k8s/kubernetes/server/bin/kube-proxy /usr/local/kubernetes/bin    #這一步很關鍵



安裝依賴包

yum install -y conntrack-tools



拷貝kube-proxy.conf文件

把 test1 節點上的kube-proxy.conf復制到/etc/kubernetes/ 目錄下,kube-proxy.service啟動參數中proxy文件需要用到kube-proxy.conf

scp /etc/kubernetes/kube-proxy.conf 192.168.0.92:/etc/kubernetes/



配置啟動文件
cat >/etc/systemd/system/kube-proxy.service<<EOF
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/proxy
ExecStart=/usr/local/kubernetes/bin/kube-proxy \\
        \$KUBE_LOGTOSTDERR \\
        \$KUBE_LOG_LEVEL \\
        \$KUBECONFIG \\
        \$KUBE_PROXY_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF



配置參數變量文件

# 前面配置kubelet組件時已經配置過參數變量文件,這里就不需要配置,寫在這里這是知道是怎么回事

cat >/etc/kubernetes/config<<EOF
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=2"
EOF



配置proxy文件

# 注意修改相關ip,test1 test2 test3 使用各自ip

# 看下面proxy文件'--proxy-mode=iptables',由於采用iptables模式,因為 ipvs 模式在centos7上有bug無法正常使用,1.11.0 以后的版本就可使用 ipvs 模式了,

# 本實驗采用centos7.5、kubernetes 1.11.0 版本,所以使用iptables模式,



# 特別注意:下面的cluster-cidr地址要和controller-manager里面的cluster-cidr、flannel 里面的Network 地址保持一致,這有這三個組件里面有 10.244.0.0

cat >/etc/kubernetes/proxy<<EOF
KUBECONFIG="--kubeconfig=/etc/kubernetes/kube-proxy.conf"
KUBE_PROXY_ARGS="--bind-address=192.168.0.92 --proxy-mode=iptables --hostname-override=192.168.0.92 --cluster-cidr=10.244.0.0/16"
EOF



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





21.5、安裝flannel網絡

# 只在 test2 上操作

# flannel 作用之一: 讓 node節點從 NotReady狀態變為ready狀態

# 注意: flanel 只需要安裝一次,不需要再往 test1 節點或者test2 節點上安裝 flanel

# 只有在安裝了docker的節點上才可以安裝flannel

# 注意下面的網卡名稱要填寫對應的網卡名稱



下載配置文件

mkdir flannel && cd flannel

下載鏈接:https://pan.baidu.com/s/1kdrYzXYzURTzO_tXtv_R1A 
提取碼:3kgd 




查看下載的文件

[root@test2 flannel]# ls
kube-flannel.yml



修改ip地址

# flannel網絡 分三種網絡 node network、service network、flannel network

# node network:承載kubernetes集群中各個“物理”Node(master和node)通信的網絡

# 下面Network 就是 node network,因為當前還沒有安裝 flannel,test2 節點為 NotReady 狀態;安裝完 flannel后就是ready狀態,

# 特別注意:下面的Network 地址要和controller-manager里面的cluster-cidr、kube-proxy里面的 cluster-cidr地址保持一致,這有這三個組件里面有 10.244.0.0


[root@test2 flannel]# vi kube-flannel.yml 

  net-conf.json: |
    {
      "Network": "10.244.0.0/16",
      "Backend": {
        "Type": "vxlan"
      }
    }



修改鏡像

# 由於自帶的鏡像被牆,無法下載,所以修改鏡像為國內源 image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64

# 注意 kube-flannel.yml 文件里面有兩個 鏡像都需要改


[root@test2 flannel]# vi kube-flannel.yml 
    containers:
      - name: kube-flannel
        image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        - --iface=ens33



添加網卡參數

# 注意 - --iface=ens33 這個ens33 是 192.168.0.92 這台虛擬機的網卡信息

# 如果Node有多個網卡的話,參考flannel issues 39701,https://github.com/kubernetes/kubernetes/issues/39701

# 目前需要在kube-flannel.yml中使用--iface參數指定集群主機內網網卡的名稱,否則可能會出現dns無法解析。容器無法通信的情況,

# flanneld啟動參數加上--iface=<iface-name>


[root@test2 flannel]# vi kube-flannel.yml 
    containers:
      - name: kube-flannel
        image: registry.cn-shanghai.aliyuncs.com/gcr-k8s/flannel:v0.10.0-amd64
        command:
        - /opt/bin/flanneld
        args:
        - --ip-masq
        - --kube-subnet-mgr
        - --iface=ens33


啟動flanel

kubectl apply -f kube-flannel.yml



查看pod

# 如果flanel啟動失敗,很有可能鏡像拉取失敗,所以最好還是自建一個鏡像倉庫。有點慢,等一會

kubectl get pods -n kube-system
kubectl get svc

[root@test2 flannel]# kubectl get pods -n kube-system
NAME                    READY     STATUS    RESTARTS   AGE
kube-flannel-ds-zb2r2   1/1       Running   0          18s

[root@test2 flannel]# kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   22h



查看節點狀態

# 當 flannel pod 全部啟動之后,節點狀態才為 Ready

kubectl get no

[root@test2 flannel]# kubectl get no
NAME           STATUS    ROLES     AGE       VERSION
test2          Ready     node      11h       v1.11.0




21.6、安裝coredns

# 只在 test2 上操作

# coredns的作用:是實現pod里面的DNS解析,就是可以通過svc(service)來訪問服務,例如:curl nginx-service ,curl + svc

# 注意:coredns 只安裝一次就可以,不需要再往master角色或 其他node角色上安裝

# 注意下面用的 coredns 1.2.0 版本 



安裝jq工具

# 之前安裝coredns報錯找不到jq, 所以配置jq 源,然后用yum安裝,安裝jq 參照:https://www.cnblogs.com/effortsing/p/10307424.html

yum install jq -y



下載coredns安裝文件

cd $HOME && mkdir coredns && cd coredns

下載鏈接:https://pan.baidu.com/s/1z7x4z2k2w3rdhOt89FDPww 
提取碼:7vkf 




查看下載的文件

[root@test2 coredns]# ls
coredns.yaml



修改配置文件

# 把文件里面的ip地址修改為10.96.0.10, 

# 注意:下面coredns.yaml里面的clusterIP 地址要和 kubelet組件中kubelet-config.yml 配置文件里面的 clusterDNS 地址保持一致。只有kubelet、coredns里面有 10.96.0.10
 
[root@test2 coredns]# vi coredns.yaml 
spec:
  selector:
    k8s-app: kube-dns
  clusterIP: 10.96.0.10
  ports:



啟動coredns

kubectl apply -f coredns.yaml



查看coredns

# 過幾分鍾才會running ,有點慢

kubectl get pods -n kube-system
kubectl get svc -n kube-system

[root@test2 coredns]# kubectl get pods -n kube-system
NAME                       READY     STATUS    RESTARTS   AGE
coredns-6c65fc5cbb-8ntpv   1/1       Running   0          34m
coredns-6c65fc5cbb-sj65c   1/1       Running   0          34m
kube-flannel-ds-zb2r2      1/1       Running   0          2h

[root@test2 coredns]# kubectl get svc -n kube-system
NAME       TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP   34m




測試coredns功能

# 啟動一個nginx容器,然后進行訪問nginx服務進行測試看是否能訪問

下載nginx.yaml文件

下載鏈接:https://pan.baidu.com/s/1PV8jyDhb8yBy6uyaeMMpSQ 
提取碼:xt5f 



查看下載的文件

[root@test2 ~]# ls
nginx.yaml



配置nginx鏡像

# 用docker 找一個簡單的nginx鏡像,替換 nginx.yaml 里面的鏡像,因為nginx.yaml里面的鏡像都已經過時,無法下載

docker search nginx

[root@test2 ~]# docker search nginx
nginxdemos/hello         NGINX webserver that serves a simple page ...   9                    [OK]

[root@test2 ~]# vi nginx.yaml 
    spec:
      containers:
      - name: http-test-con
        image: nginxdemos/hello
        ports:
        - containerPort: 80



創建nginx實例

# 需要過幾分鍾,拉取鏡像很慢的,

kubectl create -f nginx.yaml 



查看pod狀態

# 注意:使用kubectl工具養成帶上命名空間的習慣,雖然不帶default命名空間默認帶着的,但是這是個習慣,之前總是因為沒有帶命名空間出錯,排查半天,單耽誤時間,

kubectl get deploy -o wide -n default
kubectl get pods -o wide -n default
kubectl get svc -o wide -n default
kubectl describe svc example-service -n default

[root@test2 ~]# kubectl get deploy -o wide -n default
NAME            DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       CONTAINERS      IMAGES             SELECTOR
http-test-dm2   1         1         1            1           52m       http-test-con   nginxdemos/hello   name=http-test-dm2


[root@test2 ~]# kubectl get pods -o wide -n default
NAME                             READY     STATUS    RESTARTS   AGE       IP            NODE
http-test-dm2-76d4b58b47-f4pqm   1/1       Running   0          52m       10.244.0.12   192.168.0.92


[root@test2 ~]# kubectl get svc -o wide -n default
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE       SELECTOR
http-nginx-ser   NodePort    10.103.38.143   <none>        80:31000/TCP   5m        name=http-test-dm2
kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP        1d        <none>


[root@test2 ~]# kubectl describe pod http-test-dm2-76d4b58b47-f4pqm -n default



容器內測試coredns解析 

# 啟動一個具有解析功能的 dns 實例,然后進到 pod 里面用 nslookup、curl 工具進行訪問測試,下面這個實例啟動慢,等幾分鍾,

# 如果啟動失敗,通過看日志得知無法拉取鏡像 infoblox/dnstools,很可能這個鏡像不存在了,那就通過 docker search dns 找一個dns鏡像代替infoblox/dnstools鏡像

kubectl run -it --rm --image=infoblox/dnstools dns-client
nslookup kubernetes
nslookup nginx-service
curl nginx-service

執行結果:

[root@test2 ~]# kubectl run -it --rm --image=infoblox/dnstools dns-client
If you don't see a command prompt, try pressing enter.


dnstools# nslookup kubernetes
Server:        10.96.0.10
Address:    10.96.0.10#53

Name:    kubernetes.default.svc.cluster.local
Address: 10.96.0.1


dnstools# nslookup http-nginx-ser
Server:        10.96.0.10
Address:    10.96.0.10#53

Name:    http-nginx-ser.default.svc.cluster.local
Address: 10.103.38.143


dnstools# curl http-nginx-ser
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>



容器外測試coredns解析

# 注意:是在 test2 節點進行訪問訪測試的,

# 如果此時 去 test1 節點上訪問,是無法訪問的,因為test1 節點上還沒有安裝kubelet,什么角色也不是,只要成為node或master角色 就可以訪問,測試過

# 如果test1節點安裝了 kubelet,並且是ready狀態,就一定能通過 curl "10.103.38.143:80"  訪問到,已經測試過,

# coredns 可能是節點內部域名解析,Traefik-ingress是節點外部域名解析

# 10.103.38.143 是查看svc時獲取到的clusterip,svc是service的縮寫

curl "10.103.38.143:80"

執行結果:

[root@test2 ~]# curl "10.103.38.143:80" -n default
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>



通過瀏覽器訪問測試

# 31000 是查看svc時獲取到的 nodeport

http://192.168.0.92:31000/




22、關機重啟驗證

# test1、test2 節點都關機重啟,重新執行上面的過程驗證是否完好。本實驗重啟這兩個節點后一切是正常的



清理

kubectl delete -f nginx.yaml




23、回頭配置 test1 節點加入集群

# 只在 test1 上操作

# 目的是 在 test1 節點上安裝node組件,設置test1節點為master節點。

# 注意:不管要設置master角色 還是設置為node角色,前提必須先安裝node組件,主要是kubelet 組件。就是說先把節點加入集群,才能設置角色,

# 加入集群就是安裝啟動 kubelet 組件,通過csr請求后,就可以加入集群,然后才可以設置集群角色


23.1、安裝docker

# 只在 test1 上操作

# 注意:docker和flannel是一體的,哪個節點上需要安裝flannel,哪個節點上就需要安裝docker

# docker 和kubelet組件、node狀態有關系, 之前停掉docker后,kubelet組件會自動停掉;node節點會變成 NotReady 狀態

# 但是停掉docker后,flanel、coredns 的pod 讓然是 running 狀態

# v1.11.0版本推薦使用docker v17.03, v1.11,v1.12,v1.13, 也可以使用,再高版本的docker可能無法正常使用。測試發現17.09無法正常使用,不能使用資源限制(內存CPU)



卸載自帶docker

yum remove -y docker-ce docker-ce-selinux container-selinux



下載Docker鏡像

下載鏈接:https://pan.baidu.com/s/1whfkq5wDODIew5_eqP63gA 
提取碼:xmqq 

下載鏈接:https://pan.baidu.com/s/1zZy_tbvuApZW2fsMPGv3Bg 
提取碼:u3t3 




添加至鏡像倉庫

rpm -ivh docker-ce-17.03.2.ce-1.el7.centos.x86_64.rpm
rpm -ivh docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm



用安裝 Docker 和依賴包
 
yum install -y docker-ce-*.rpm 



開機啟動 

systemctl enable docker 



啟動 docker 服務 

systemctl start docker




23.2、部署 kubelet組件

# 只在 test1 上操作

# kubelet的作用:向 test1 發起csr請求。

# 初次安裝完 kubelet,只要第一次啟動kubelet,通過kubelet get csr 就會得到這串字符串:node-csr-Yiiv675wUCvQl3HH11jDr0cC9p3kbrXWrxvG3EjWGoE

下載、解壓安裝包

mkdir -p /server/software/k8s
cd /server/software/k8s
下載鏈接:https://pan.baidu.com/s/1DXahqP8nXWP1aw5pIunJrw 
提取碼:9xfp
tar -xf kubernetes-server-linux-amd64.tar.gz



分發kubelet二進制文件

# 后面kubelet啟動文件需要用到kubelet二進制文件路徑

mkdir -p /usr/local/kubernetes/bin
cp /server/software/k8s/kubernetes/server/bin/kubelet /usr/local/kubernetes/bin    #這一步很關鍵


查看組件狀態

kubectl get componentstatuses

[root@test1 ~]# kubectl get componentstatuses
Unable to connect to the server: x509: certificate signed by unknown authority
# 報錯原因:經過排查后發現前面操作的幾個步驟中 ${KUBE_APISERVER} 這個變量寫成了 ${KUBE_  又重做了一遍,正確的結果是下面

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




配置 /.kube/config 並且授權

# kubectl 默認從 ~/.kube/config 文件讀取 kube-apiserver 地址、證書、用戶名等信息,如果沒有配置,執行 kubectl 命令時可能會出錯:
# /.kube/config 和 admin.conf 內容是一樣的,/.kube/config 是 admin.conf 復制過來的,改了下名字而已

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


安裝cni

# kubelet 的啟動參數需要用到

cd /server/software/k8s

下載鏈接:https://pan.baidu.com/s/1DL1v4cH2SHUfobICjKXm3Q 
提取碼:puel
mkdir -p /opt/cni/bin
tar -xf cni-plugins-amd64-v0.7.1.tgz -C /opt/cni/bin
ls -l /opt/cni/bin

cd $HOME



配置啟動kubelet

創建數據目錄

mkdir -p /data/kubelet


配置kubelet啟動文件

cat >/etc/systemd/system/kubelet.service<<EOF
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/kubernetes/kubernetes
After=docker.service
Requires=docker.service

[Service]
WorkingDirectory=/data/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/local/kubernetes/bin/kubelet \\
            \$KUBE_LOGTOSTDERR \\
            \$KUBE_LOG_LEVEL \\
            \$KUBELET_CONFIG \\
            \$KUBELET_HOSTNAME \\
            \$KUBELET_POD_INFRA_CONTAINER \\
            \$KUBELET_ARGS \\
            \$CADVISOR
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF



配置參數變量文件

cat >/etc/kubernetes/config<<EOF
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=2"
EOF



配置kubelet文件

# 注意修改kubelet文件里面的 --hostname-override= 要填寫test1、test2、test3 這三個節點改成對應各自 ip ,或者各自的主機名。本實驗寫的是ip

# 如果--hostname-override= 填寫的是ip,那么kubectl get nodes 得到的name就顯示ip,如果填寫的是主機名,得到的name就顯示主機名

# image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1 這個鏡像跟 kubectl get componentstatuses 獲取不到資源沒有關系,跟docker也沒有關系

# 但是網上搜這個鏡像,說這個鏡像是基礎鏡像,目前被牆,最好下載放到本地 registry 上

# 注意 kubelet配置文件里面的參數kubelet.conf 是kubelet啟動后生成的,不用管,就是這樣寫的

# 注意 /etc/kubernetes/pki 目錄里面只放一個ca.pem 證書,

# 啟動kubelet后/etc/kubernetes/pki 目錄里面會自動生成四個文件 kubelet.crt、kubelet.key、kubelet-client-current.pem、kubelet-client-2219-01-22-10-07-06.pem

# CADVISOR 表明開啟cadvisor監控,然后在啟動文件中加上 ExecStart=$CADVISOR 就可以通過瀏覽器訪問cadvisor監控,否則是無法訪問的,

cat >/etc/kubernetes/kubelet<<EOF
KUBELET_HOSTNAME="--hostname-override=test2"
KUBELET_POD_INFRA_CONTAINER="--pod-infra-container-image=registry.cn-hangzhou.aliyuncs.com/google_containers/pause-amd64:3.1"
KUBELET_CONFIG="--config=/etc/kubernetes/kubelet-config.yml"
CADVISOR="--cadvisor-port=4194 --storage-driver-db='cadvisor' --storage-driver-host='localhost:8086'"
KUBELET_ARGS="--bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.conf --kubeconfig=/etc/kubernetes/kubelet.conf --cert-dir=/etc/kubernetes/pki --network-plugin=cni --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d"
EOF


配置kubelet-config.yml文件

# 注意修改kubelet-config.yml相關ip,test1 test2 test3 使用各自ip

# 注意下面kubelet-config.yml里面的clusterDNS 地址要和coredns.yaml 里面的 clusterIP 地址保持一致。只有kubelet、coredns里面有 10.96.0.10,其他的組件都沒有

cat >/etc/kubernetes/kubelet-config.yml<<EOF
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
address: 192.168.0.91
port: 10250
cgroupDriver: cgroupfs
clusterDNS:
  - 10.96.0.10
clusterDomain: cluster.local.
hairpinMode: promiscuous-bridge
serializeImagePulls: false
authentication:
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.pem
EOF



啟動
systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet            # 啟動后就會生成 csr
systemctl status kubelet



查看日志

# 啟動 kubelet后查看日志會有報錯

# 報錯原因是因為kubelet配置了network-plugin=cni,但是還沒安裝網絡插件 flannel,所以狀態會是NotReady,會報上面的錯誤,不想看這個報錯或者不需要網絡,就修改kubelet配置文件,去掉network-plugin=cni 就可以了

[root@test2 ~]# journalctl -u kubelet |tail
network plugin is not ready: cni config uninitialized




23.3、通過csr請求

# 在test1 上操作,也可以在 test2 上操作

# 通過csr請求的目的就是要加入集群

# 注意:node-csr-Yiiv675wUCvQl3HH11jDr0cC9p3kbrXWrxvG3EjWGoE  這個長字符串是kubelet 初次啟動后生成的,

# 在test2 節點上操作就可以

# 查看csr

# 如果獲取不到csr看日志會出現下面錯誤:

[root@test2 kubernetes]# journalctl -u kubelet |tail
failed to run Kubelet: cannot create certificate signing request: certificatesigningrequests.certificates.k8s.io is forbidden: User "kubelet-bootstrap" cannot create certificatesigningrequests.certificates.k8s.io at the cluster scope

# 原因是因為沒有給kubelet-bootstrap授權,授權即可

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


kubectl get csr

執行結果

[root@test1 ~]# kubectl get csr
NAME                                                   AGE       REQUESTOR           CONDITION
node-csr-0BVhjayOeu96EHndLS4vC4bijfh9zr2gH6iJCsmfOvQ   18s       kubelet-bootstrap   Pending



通過csr請求,下面的長字符串填寫上一步的結果,

kubectl certificate approve node-csr-0BVhjayOeu96EHndLS4vC4bijfh9zr2gH6iJCsmfOvQ

[root@test1 ~]# kubectl certificate approve node-csr-0BVhjayOeu96EHndLS4vC4bijfh9zr2gH6iJCsmfOvQ
certificatesigningrequest.certificates.k8s.io/node-csr-0BVhjayOeu96EHndLS4vC4bijfh9zr2gH6iJCsmfOvQ approved


查看kubelet啟動后生成的文件

說明:kubelet 啟動后使用 --bootstrap-kubeconfig 向 kube-apiserver 發送 CSR 請求,當這個 CSR 被 approve 后,kube-controller-manager 為 kubelet 創建 TLS 客戶端證書、私鑰和 --kubeletconfig 文件。

kubelet.conf文件決定了csr的存在,如果要想重新獲取csr,可以停掉kubelet,刪除kubelet.conf文件,重啟kubelet就可以獲得csr

ls -l /etc/kubernetes/kubelet.conf
ls -l /etc/kubernetes/pki/kubelet*

查看結果

[root@test2 ~]# ls -l /etc/kubernetes/kubelet.conf
-rw------- 1 root root 2295 Jan 22 10:07 /etc/kubernetes/kubelet.conf

[root@test2 ~]# ls -l /etc/kubernetes/pki/kubelet*
-rw------- 1 root root 1273 Jan 22 10:07 /etc/kubernetes/pki/kubelet-client-2019-01-22-10-07-06.pem
lrwxrwxrwx 1 root root   58 Jan 22 10:07 /etc/kubernetes/pki/kubelet-client-current.pem -> /etc/kubernetes/pki/kubelet-client-2019-01-22-10-07-06.pem
-rw-r--r-- 1 root root 2181 Jan 22 02:10 /etc/kubernetes/pki/kubelet.crt
-rw------- 1 root root 1675 Jan 22 02:10 /etc/kubernetes/pki/kubelet.key
 



查看節點狀態

# 之前做完上一步,就去吃飯了,飯后回來正想安裝 flanel,突然一看 192.168.0.91是Ready狀態,所以等一會狀態就會發生改變

# 解釋:此時節點狀態為Ready,因為test2 節點已經安裝過 flannel,這里就不需要再安裝。所以只要通過csr就是ready狀態,


[root@test1 ~]# kubectl get nodes
NAME           STATUS    ROLES         AGE       VERSION
192.168.0.91   Ready     <none>        2h        v1.11.0
192.168.0.92   Ready     node          21h       v1.11.0




設置集群角色

# 在test1 節點上操作

# 設置 test1 為 master 角色

kubectl label nodes 192.168.0.91 node-role.kubernetes.io/master=

# 設置 master 一般情況下不接受負載
kubectl taint nodes 192.168.0.91 node-role.kubernetes.io/master=true:NoSchedule

master運行pod

kubectl taint nodes master.k8s node-role.kubernetes.io/master-

master不運行pod

kubectl taint nodes master.k8s node-role.kubernetes.io/master=:NoSchedule


執行結果:

[root@test1 ~]# kubectl label nodes 192.168.0.91 node-role.kubernetes.io/master=
node/192.168.0.91 labeled

[root@master ~]# kubectl get nodes
NAME           STATUS    ROLES         AGE       VERSION
192.168.0.91   Ready     master        2h        v1.11.0
192.168.0.92   Ready     node           21h       v1.11.0

[root@test1 k8s]# kubectl taint nodes 192.168.0.91 node-role.kubernetes.io/master=true:NoSchedule
node/test1 tainted

23.4、配置啟動kube-proxy 

# 只在 tes1 上操作

# 注意:關掉 kube-proxy 后仍然可以訪問 svc curl "10.103.38.143:80" ,kube-proxy 的作用目前還沒有發現

下載、解壓安裝包

mkdir -p /server/software/k8s

cd /server/software/k8s

下載鏈接:https://pan.baidu.com/s/1DXahqP8nXWP1aw5pIunJrw 
提取碼:9xfp

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



分發kube-proxy二進制文件

# 后面kube-proxy.service啟動文件需要用到kube-proxy二進制文件路徑

mkdir -p /usr/local/kubernetes/bin
cp /server/software/k8s/kubernetes/server/bin/kube-proxy /usr/local/kubernetes/bin    #這一步很關鍵



安裝依賴包

yum install -y conntrack-tools



拷貝kube-proxy.conf文件

把 test1 節點上的kube-proxy.conf復制到/etc/kubernetes/ 目錄下,kube-proxy.service啟動參數中proxy文件需要用到kube-proxy.conf

cp /etc/kubernetes/kube-proxy.conf /etc/kubernetes/



配置啟動文件
cat >/etc/systemd/system/kube-proxy.service<<EOF
[Unit]
Description=Kubernetes Kube-Proxy Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target

[Service]
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/proxy
ExecStart=/usr/local/kubernetes/bin/kube-proxy \\
        \$KUBE_LOGTOSTDERR \\
        \$KUBE_LOG_LEVEL \\
        \$KUBECONFIG \\
        \$KUBE_PROXY_ARGS
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF



配置參數變量文件

# 前面配置kubelet組件時已經配置過參數變量文件,這里就不需要配置,寫在這里這是知道是怎么回事

cat >/etc/kubernetes/config<<EOF
KUBE_LOGTOSTDERR="--logtostderr=true"
KUBE_LOG_LEVEL="--v=2"
EOF



配置proxy文件

# 注意修改相關ip,test1 test2 test3 使用各自ip

# 看下面proxy文件'--proxy-mode=iptables',由於采用iptables模式,因為 ipvs 模式在centos7上有bug無法正常使用,1.11.0 以后的版本就可使用 ipvs 模式了,

# 本實驗采用centos7.5、kubernetes 1.11.0 版本,所以使用iptables模式,



# 特別注意:下面的cluster-cidr地址要和controller-manager里面的cluster-cidr、flannel 里面的Network 地址保持一致,這有這三個組件里面有 10.244.0.0

cat >/etc/kubernetes/proxy<<EOF
KUBECONFIG="--kubeconfig=/etc/kubernetes/kube-proxy.conf"
KUBE_PROXY_ARGS="--bind-address=192.168.0.91 --proxy-mode=iptables --hostname-override=192.168.0.91 --cluster-cidr=10.244.0.0/16"
EOF



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




23.5、容器外測試coredns解析

# 只在 test1 上操作

# 由於之前 test2節點已經安裝過coredns,所以 test1節點就不需要安裝了,直接測試

# 這里的 10.103.38.143:80 是之前在 test2 節點上查詢svc 得到的結果

# 訪問成功,

[root@master k8s]# curl "10.103.38.143:80"
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>


23.6、瀏覽器訪問 cadvisor監控服務

瀏覽器訪問http://192.168.0.91:4194/

 

參照文檔:
http://www.maogx.win/posts/35/
http://www.maogx.win/
https://juejin.im/user/59ffa2836fb9a0451c39c64f/posts
http://blog.itpub.net/10995764/viewspace-2124022/
 
 
 
 
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM