Kubernetes 純手作部署在 Ubuntu 16.04

大約半年前,花了不少時間正式將 swarm 導入我們家的 production,而過了整整六個月後,因為要將原本的 swarm cluster 遷移到新的資料中心,但由於在重建(順便升級)的過程中實在中了太多的 Docker 1.12 後才推出的 swarm mode(Swarmkit) 的招,加上半年前導入 swarm 時踩到太多的坑,太多的悲情記憶和不眠的夜,讓我毅然決然的跳槽到 Kubernetes 的擁抱。至於 swarm,就先讓他的子彈再飛一會兒吧~

下面是部署 Google Kubernetes(k8s) 的筆記,適用在 Ubuntu 16.04 LTSsystemd,由於我是部署在 bare metal 的 server 上,而 k8s 的官方文件在 bare metal 的部署部分並沒有提及整個部署的步驟和細節,再加上目前很少有 Ubuntu 16.04 + systemd 的部署方式(官方文件只有支援 Ubuntu 14.04,也沒有 systemd 的設定),希望這篇文章可以幫助到很多最近嘗試才踏入 k8s 領域的朋友。

由於原廟東急著趕人出去,這次部署(順便測試)的步驟是以純手動為主,有興趣的人可以依照內容用自己喜愛的工具 Chef、Ansible … 等等,方便更快速不手殘的部署!

細節設定和內容有依照自己的習慣和喜好修改過,需要用到的服務和 component(ex: etcd, flannel) 基本都是從官網來的,應該會更好上手。

環境準備

server 資訊

server# Master Worker IP Address
NODE1 Yes Yes 10.55.66.10
NODE2 x Yes 10.55.66.20
NODE3 x Yes 10.55.66.30

環境變數

export MY_ETH0_IP=`/sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk '{ print $1}'`
export MASTER_IP="10.55.66.10"
export SERVICE_CLUSTER_IP_RANGE=10.99.0.0/16
export FLANNEL_NET=10.66.0.0/16
export DNS_SERVER_IP="10.99.8.8" //kubedns 會用到,IP 可從 $SERVICE_CLUSTER_IP_RANGE 自行指定

開始安裝

安裝 etcd

P.S.每一台 node 上都需安裝

下載 etcd binary

官方的文件、Binary 和 Release Note https://github.com/coreos/etcd/releases,我們這邊選用 v3.0.7

curl -L https://github.com/coreos/etcd/releases/download/v3.0.7/etcd-v3.0.7-linux-amd64.tar.gz -o etcd-v3.0.7-linux-amd64.tar.gz
tar xzvf etcd-v3.0.7-linux-amd64.tar.gz && cd etcd-v3.0.7-linux-amd64
mkdir -p /opt/etcd/bin
cp etcd* /opt/etcd/bin/

設定 etcd config

建立目錄準備

mkdir -p /var/lib/etcd/
mkdir -p /opt/etcd/config/

etcd config

cat <<EOF | sudo tee /opt/etcd/config/etcd.conf
ETCD_DATA_DIR=/var/lib/etcd
ETCD_NAME=$(hostname -s)
ETCD_INITIAL_CLUSTER=node1=http://10.55.66.10:2380,node2=http://10.55.66.20:2380,node3=http://10.55.66.30:2380
ETCD_INITIAL_CLUSTER_STATE=new
ETCD_LISTEN_PEER_URLS=http://${MY_ETH0_IP}:2380
ETCD_INITIAL_ADVERTISE_PEER_URLS=http://${MY_ETH0_IP}:2380
ETCD_ADVERTISE_CLIENT_URLS=http://${MY_ETH0_IP}:2379
ETCD_LISTEN_CLIENT_URLS=http://${MY_ETH0_IP}:2379,http://127.0.0.1:2379
GOMAXPROCS=$(nproc)
EOF

etcd 的 systemd config

cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=Etcd Server
Documentation=https://github.com/coreos/etcd
After=network.target

[Service]
User=root
Type=simple
EnvironmentFile=-/opt/etcd/config/etcd.conf
ExecStart=/opt/etcd/bin/etcd
Restart=on-failure
RestartSec=10s
LimitNOFILE=40000

[Install]
WantedBy=multi-user.target
EOF

啟動 etcd

systemctl daemon-reload && systemctl enable etcd && systemctl start etcd

安裝 flannel

P.S.每一台 node 上都需安裝

下載 flannel binary & 安裝

curl -L https://github.com/coreos/flannel/releases/download/v0.6.1/flannel-v0.6.1-linux-amd64.tar.gz -o flannel.tar.gz
mkdir -p /opt/flannel
tar xzf flannel.tar.gz -C /opt/flannel

安裝 Kubernetes

K8s Master

官方文件 Release Note 在這邊,https://github.com/kubernetes/kubernetes/releases
這邊以v1.3.6為例。

下載 k8s binary

此步驟全部 node 都要做。(也可以在每個 node 直接下載,或是下載到 server 上再自己 scp 過去到每個 node)

curl -L 'https://github.com/kubernetes/kubernetes/releases/download/v1.3.6/kubernetes.tar.gz' -o kubernetes.tar.gz
tar xf kubernetes.tar.gz
### 在這邊有一個 server/ 目錄,下面有不同 CPU 指令集的 binary,在這邊我們以 amd64 來做示範,您可以替換成自己適合的 binary。
tar xf ./server/kubernetes-server-linux-amd64.tar.gz -C /opt/

準備 Credentials

產生 certificate [方法一]

Google 提供生成 certificate 的參考 http://kubernetes.io/docs/admin/authentication/#creating-certificates

mkdir -p /srv/kubernetes/ && cd /srv/kubernetes/

openssl genrsa -out ca.key 2048
openssl req -x509 -new -nodes -key ca.key -subj "/CN=${MASTER_IP}" -days 10000 -out ca.crt

openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj "/CN=${MASTER_IP}" -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 10000
openssl x509 -noout -text -in ./server.crt

產生 certificate [方法二]

第二種方法是 CoreOS 提供的方法,https://coreos.com/kubernetes/docs/latest/openssl.html。第二種方法其實跟方法一大同小異,主要差別只是在於如果你未來會自訂cluster domain,可能會有一些 certificate 上的問題((像是這個)[https://github.com/pires/kubernetes-elasticsearch-cluster/issues/27]),而 CoreOS 這篇文章有提到在產生 certificate 時要怎麼指定 alternate names 的部分。

方法二的產生方法,我們就先不在這邊多琢磨了,各位朋友如果未來有需要再依照這個方法替換 k8s cluster 的憑證。

設定 k8s 相關 services

k8s API Server [MASTER]

PS.此步驟只在 Master 進行。

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

[Service]
User=root
ExecStart=/opt/kubernetes/server/bin/kube-apiserver \
--insecure-bind-address=${MY_ETH0_IP} \
--insecure-port=8080 \
--etcd-servers=http://10.55.66.10:2379, http://10.55.66.20:2379, http://10.55.66.30:2379 \
--logtostderr=true \
--allow-privileged=false \
--service-cluster-ip-range=${SERVICE_CLUSTER_IP_RANGE} \
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,SecurityContextDeny,ResourceQuota \
--service-node-port-range=30000-32767 \
--advertise-address=${MY_ETH0_IP} \
--client-ca-file=/srv/kubernetes/ca.crt \
--tls-cert-file=/srv/kubernetes/server.crt \
--tls-private-key-file=/srv/kubernetes/server.key
Restart=on-failure
Type=notify
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

k8s kube-controller-manager [MASTER]

PS.此步驟只在 Master 進行。

cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
User=root
ExecStart=/opt/kubernetes/server/bin/kube-controller-manager \
--master=${MASTER_IP}:8080 \
--root-ca-file=/srv/kubernetes/ca.crt \
--service-account-private-key-file=/srv/kubernetes/server.key \
--logtostderr=true
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

kube-scheduler [MASTER]

PS.此步驟只在 Master 進行。

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

[Service]
User=root
ExecStart=/opt/kubernetes/server/bin/kube-scheduler \
--logtostderr=true \
--master=${MASTER_IP}:8080
Restart=on-failure
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target
EOF

flanneld

PS.每一台 node 上都需安裝

cat <<EOF | sudo tee /etc/systemd/system/flanneld.service
[Unit]
Description=Flanneld
Documentation=https://github.com/coreos/flannel
After=network.target
Before=docker.service

[Service]
User=root
ExecStart=/opt/flannel/flanneld \
--etcd-endpoints="http://10.55.66.10:2379, http://10.55.66.20:2379, http://10.55.66.30:2379" \
--iface=${MY_ETH0_IP} \
--ip-masq
Restart=on-failure
Type=notify
LimitNOFILE=65536
EOF

設定 Flannel 和初始化

官方文件https://coreos.com/flannel/docs/latest/flannel-config.html

因為 flannel 預設的 backend type 預設是 udp,這邊我們如果想要使用 vxlan 作為 backend,就要自己塞 config 到 etcd 去給 flannel 用。

//僅在 Master 上操作
/opt/etcd/bin/etcdctl set /coreos.com/network/config '{"Network":"'${FLANNEL_NET}'", "Backend": {"Type": "vxlan"}}'

補充:感謝網友 @Fann 問到這邊兩個問題:

  • flannel backend 選用 vxlan 的原因
    • 是因為比起預設的 udp 來說,vxlan 效能相對好一些
  • 介紹中沒選用 Calico (與 flannel 對比,另一套 Network Solution)
    • 一方面是因為 k8s 官方文件預設是使用 flannel
    • 另一個原因是為了讓剛接觸的朋友儘量用最快最簡易的方式使用 k8s,也希望能與官方文件可以對照,所以希望用最基本的方式部署。

啟動 service

PS.每一台 node 上都需安裝

由於剛剛增加了很多 systemd 的設定,所以要 systemd 掃描一下剛剛新增的部分

systemctl daemon-reload

預設啟動 service

systemctl enable kube-apiserver
systemctl enable kube-controller-manager
systemctl enable kube-scheduler
systemctl enable flanneld //每個 node 都要

啟動 service

systemctl start kube-apiserver
systemctl start kube-controller-manager
systemctl start kube-scheduler
systemctl start flanneld //每個 node 都要

PS.systemd 看 error 可透過

journalctl -xe 或是 systemctl status [service name]

設定 docker 支援 flannel

取得 flannel 針對 host 分配的 subnet

source /run/flannel/subnet.env

更新 docker 啟動設定

sed -i "s|^ExecStart=/usr/bin/dockerd -H fd://$|ExecStart=/usr/bin/dockerd -H tcp://127.0.0.1:4243 -H unix:///var/run/docker.sock --bip=${FLANNEL_SUBNET} --mtu=${FLANNEL_MTU}|g" /etc/systemd/system/docker.service
rc=0
ip link show docker0 >/dev/null 2>&1 || rc="$?"
if [[ "$rc" -eq "0" ]]; then
ip link set dev docker0 down
ip link delete docker0
fi

PS. sed command 的部分,可以自行補上啟動 docker 時需要的參數,如:–insecure-registry=10.55.66.123:5000

重啟 docker

systemctl daemon-reload
systemctl enable docker
systemctl restart docker

部署 k8s node

設定 k8s kubelet

cat <<EOF | sudo tee /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
After=docker.service
Requires=docker.service

[Service]
ExecStart=/opt/kubernetes/server/bin/kubelet \
--hostname-override=${MY_ETH0_IP} \
--api-servers=http://${MASTER_IP}:8080 \
--logtostderr=true \
--cluster-dns=$DNS_SERVER_IP \
--cluster-domain=$DNS_DOMAIN
Restart=on-failure
KillMode=process

[Install]
WantedBy=multi-user.target
EOF

啟動 kubelet

systemctl daemon-reload
systemctl enable kubelet
systemctl start kubelet

設定 k8s kube-proxy

cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Proxy
After=network.target

[Service]
ExecStart=/opt/kubernetes/server/bin/kube-proxy \
--hostname-override=${MY_ETH0_IP} \
--master=http://${MASTER_IP}:8080 \
--logtostderr=true
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

啟動

systemctl daemon-reload
systemctl enable kube-proxy

開始測試

部署完成

準備 kubectl 需要的 config

準備參數

export KUBE_USER=admin
export KUBE_PASSWORD="ohya_comeonBaby"

export DEFAULT_KUBECONFIG="${HOME}/.kube/config"
export KUBECONFIG=${KUBECONFIG:-$DEFAULT_KUBECONFIG}

設定 ~/.kube/config

mkdir -p $(dirname "${KUBECONFIG}")
touch "${KUBECONFIG}"
export CONTEXT=ubuntu

KUBECONFIG="${KUBECONFIG}" /opt/kubernetes/server/bin/kubectl config set-cluster "${CONTEXT}" --server=http://${MASTER_IP}:8080 --insecure-skip-tls-verify=true
KUBECONFIG="${KUBECONFIG}" /opt/kubernetes/server/bin/kubectl config set-credentials "${CONTEXT}" --username=${KUBE_USER} --password=${KUBE_PASSWORD}
KUBECONFIG="${KUBECONFIG}" /opt/kubernetes/server/bin/kubectl config set-context "${CONTEXT}" --cluster="${CONTEXT}" --user="${CONTEXT}"
KUBECONFIG="${KUBECONFIG}" /opt/kubernetes/server/bin/kubectl config use-context "${CONTEXT}" --cluster="${CONTEXT}"

安裝 Add-ons

kubedns (又名skydns)

export DNS_DOMAIN="k8s.kidscrape"
export DNS_REPLICAS=1
export KUBE_APISERVER_URL=http://$MASTER_IP:8080

從官方下載 skydns 最新的 yaml

curl -OL 'https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/skydns-rc.yaml.base'

帶入設定
sed -i "s/__PILLAR__DNS__REPLICAS__/${DNS_REPLICAS}/g" skydns-rc.yaml.base
sed -i "s/__PILLAR__DNS__DOMAIN__/${DNS_DOMAIN}/g" skydns-rc.yaml.base
sed -i "s~__PILLAR__FEDERATIONS__DOMAIN__MAP__~- --kube-master-url=${KUBE_APISERVER_URL}~g" skydns-rc.yaml.base

啟動 skydns

/opt/kubernetes/server/bin/kubectl create -f skydns-rc.yaml.base
=> replicationcontroller "kube-dns-v19" created

skydns service 的 yaml

curl -OL 'https://raw.githubusercontent.com/kubernetes/kubernetes/master/cluster/addons/dns/skydns-svc.yaml.base'
sed -i "s/__PILLAR__DNS__SERVER__/${DNS_SERVER_IP}/g" skydns-svc.yaml.base

建立 skydns service

/opt/kubernetes/server/bin/kubectl create -f skydns-svc.yaml.base

安裝 kubernetes-dashboard

從官方下載 skydns 最新的 yaml

curl -OL 'https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml'

帶入設定

sed -i "s~# - --apiserver-host=http://my-address:port~- --apiserver-host=http://${MASTER_IP}:8080~g" kubernetes-dashboard.yaml

啟動 kubernetes-dashboard

/opt/kubernetes/server/bin/kubectl create -f kubernetes-dashboard.yaml

kubernetes 測試

/opt/kubernetes/server/bin/kubectl get nodes
NAME STATUS AGE
10.55.66.10 Ready 11d
10.55.66.20 Ready 9d
10.55.66.30 Ready 9d

OK!大功告成!現在你已經擁有一組完整的 kubernetes cluster 囉!

廣告

4 thoughts on “Kubernetes 純手作部署在 Ubuntu 16.04

  1. rw 說道:

    curl -L ‘https://github.com/kubernetes/kubernetes/releases/download/v1.3.6/kubernetes.tar.gz’ -o kubernetes.tar.gz
    tar xf ./server/kubernetes-server-linux-amd64.tar.gz -C /opt/

    Q1) 下載檔名和解壓檔名不同, 是要rename, 還是其中之一名稱錯誤?
    Q2) 下載的ubernetes.tar.gz解壓後沒有產生bin子夾, service檔案中ExecStart=/opt/kubernetes/server/bin/xxx.xxx就產生file can not be found錯誤, 還是下載檔案錯誤?

    按讚數

    • Zz 說道:

      Hi rw,
      感謝您的回覆,我們從 github 下載回來的整包 build,裡面是有包含不同平台的 binary,
      所以這邊有漏寫一個步驟,是要先將 kubernetes.tar.gz 解壓縮。

      此時就能看到 server/ 這個目錄下有一個我們要的 binary “kubernetes-server-linux-amd64.tar.gz" (您也可以依照自己的 CPU 架構選擇自己要的指令集)。

      感謝您的回覆,我已經把新的步驟更新在文章中了,再次感謝您!

      按讚數

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com Logo

您的留言將使用 WordPress.com 帳號。 登出 / 變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 / 變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 / 變更 )

Google+ photo

您的留言將使用 Google+ 帳號。 登出 / 變更 )

連結到 %s