docker實現了更便捷的單機容器虛擬化的管理, docker的位置處于操作系統層與應用層之間;
-
相對傳統虛擬化(KVM,XEN):
docker可以更加靈活的去實現一些應用層功能, 同時對資源的利用率也更高
-
相對應用:
docker可以把應用更操作系統(鏡像)做更好的結合, 降低部署與維護的的成本
處于這樣一個位置在單機使用docker進行業務部署是可以感覺到質的提升; 但是針對跨機器, 大規模, 需要對業務質量進行保證的時候, docker本身又有些不足, 而傳統的運維自動化工具無論是在docker內部部署還是用來管理docker都顯得有些不倫不類.
Kubernetes則實現了大規模,分布式, 并且確保高可用的docker集群的管理.
1: 理解Kubernets
理念:
可以把kuberntes理解為容器級別的自動化運維工具, 之前的針對操作系統(linux, windows)的自動化運維工具比如puppet, saltstack, chef所做的工作是確保代碼狀態的正確, 配置文件狀態的正確, 進程狀態的正確, 本質是狀態本身的維護; 而kubernetes實際上也是狀態的維護, 只不過是容器級別的狀態維護; 不過kubernetes在容器級別要做到不僅僅狀態的維護, 還需要docker跨機器之間通信的問題.
相關概念
1: pod
pod是容器的集合, 每個pod可以包含一個或者多個容器; 為了便于管理一般情況下同一個pod里運行相同業務的容器
同一個pod的容器共享相同的系統棧(網絡,存儲)
同一個pod只能運行在同一個機器上
2: Replicateion controller
由于這個名字實在是太長了, 以下均用rc代替(kubernetes也知道這個名字比較長, 也是用rc代替)
rc是管理pod的, rc負責集群中在任何時候都有一定數量的pod在運行, 多了自動殺, 少了自動加;
rc會用預先定義好的pod模版來創建pod; 創建成功后正在運行的pod實例不會隨著模版的改變而改變;
rc通過SELECTOR(一種系統label)與pod對應起來
當rc中定義的pod數量改變是, rc會自動是運行中的pod數量與定義的數量一致
-
rc還有一種神奇的機制:
- rolling updates; 比如現在某個服務有5個正在運行的pod, 現在pod本身的業務要更新了, 可以以逐個替換的機制來實現整個rc的更新
3: service
services即服務, 真正提供服務的接口,將pod提供的服務暴力到外網, 每個服務后端可以有一個或者多個pod
4: lable
- label就是標簽, kubernetes在pod, service, rc上打了很多個標簽(K/V形式的鍵值對); lable的存儲在etcd(一個分布式的高性能,持久化緩存)中; kubernetes用etcd一下子解決了傳統服務中的服務之間通信(消息服務)與數據存儲(數據庫)的問題
架構實現
整個架構大體分為控制節點和計算節點; 控制節點發命令, 計算節點干活.
首先試圖從圖本身試圖對架構做一些理解
- 1: 真正提供服務的是node(計算節點), 計算節點的服務通過proxy,在通過防火墻后出去
- 2: 控制節點和計算節點通過REST的API通信
- 3: 用戶的命令需要授權后調用服務端的API發送到系統
- 4: 計算節點主要進程為kubelet與proxy
- 5: 控制節點負責調度, 狀態維護
2: Kubernetes部署
主機環境
- 192.168.56.110
- etcd
- kubernetes master
- 192.168.56.111
- etcd
- kubernetes master
- 192.168.56.112
- kubernetes master
操作系統: centos7
- kubernetes master
110和111部署etcd, 110作為kubenetes的控制節點, 111和112作為計算節點
環境準備:
- 安裝epel源:
<pre>
yum install epel-release
</pre> - 關閉防火墻
<pre>
systemctl stop firewalld
systemctl disable firewalld
</pre>
1: etcd
etcd是一個分布式, 高性能, 高可用的鍵值存儲系統,由CoreOS開發并維護的,靈感來自于 ZooKeeper 和 Doozer,它使用Go語言編寫,并通過Raft一致性算法處理日志復制以保證強一致性。
簡單: curl可訪問的用戶的API(HTTP+JSON)
安全: 可選的SSL客戶端證書認證
快速: 單實例每秒 1000 次寫操作
可靠: 使用Raft保證一致性
1: 安裝包:
<pre>
yum install etcd -y
</pre>-
2: 編輯配置: /etc/etcd/etcd.conf
<pre>
# [member]
ETCD_NAME=192.168.56.110 #member節點名字 要與后面的ETCD_INITIAL_CLUSTER對應
ETCD_DATA_DIR="/var/lib/etcd/default.etcd" #數據存儲目錄
#ETCD_SNAPSHOT_COUNTER="10000"
#ETCD_HEARTBEAT_INTERVAL="100"
#ETCD_ELECTION_TIMEOUT="1000"
ETCD_LISTEN_PEER_URLS="http://192.168.56.110:2380" #集群同步地址與端口
ETCD_LISTEN_CLIENT_URLS="http://192.168.56.110:4001" #client通信端口
#ETCD_MAX_SNAPSHOTS="5"
#ETCD_MAX_WALS="5"
#ETCD_CORS=""
#
#[cluster]
ETCD_INITIAL_ADVERTISE_PEER_URLS="http://192.168.56.110:2380" #peer初始化廣播端口
ETCD_INITIAL_CLUSTER="192.168.56.110=http://192.168.56.110:2380,192.168.56.111=http:// 192.168.56.111:2380" #集群成員, 格式: $節點名字=$節點同步端口 節點之前用","隔開
ETCD_INITIAL_CLUSTER_STATE="new" #初始化狀態, 初始化之后會變為existing
ETCD_INITIAL_CLUSTER_TOKEN="etcd-cluster" #集群名字
ETCD_ADVERTISE_CLIENT_URLS="http://192.168.56.110:4001" #client廣播端口
#ETCD_DISCOVERY=""
#ETCD_DISCOVERY_SRV=""
#ETCD_DISCOVERY_FALLBACK="proxy"
#ETCD_DISCOVERY_PROXY=""
#
#[proxy]
#ETCD_PROXY="off"
#
#[security]
#ETCD_CA_FILE=""
#ETCD_CERT_FILE=""
#ETCD_KEY_FILE=""
#ETCD_PEER_CA_FILE=""
#ETCD_PEER_CERT_FILE=""
#ETCD_PEER_KEY_FILE=""
</pre>
除ETCD_INITIAL_CLUSTER項目所有節點保持一致外, 其他配置中的IP均為本機IP
etcd的配置文件不支持每行后面加注釋 哈哈, 所以在實際配置過程中需要把每行#后面的注釋刪掉 3: 啟動服務
<pre>
systemctl enable etcd
systemctl start etcd
</pre>-
4: 驗證
<pre>etcdctl member list
dial tcp 127.0.0.1:2379: connection refused
etcd默認連接127.0.0.1的2379端口, 而咱們配置的是192.168.56.110的4001端口
etcdctl -C 192.168.56.110:4001 member list
no endpoints available
如果依然出現了上面的問題, 查看服務是否啟動
netstat -lnp | grep etcd
tcp 0 0 192.168.56.110:4001 0.0.0.0:* LISTEN 18869/etcd
tcp 0 0 192.168.56.110:2380 0.0.0.0:* LISTEN 18869/etcd #然后查看端口是否暢通
telnet 192.168.56.111 4001
Trying 192.168.56.111...
Connected to 192.168.56.111.
Escape character is '^]'.
^Cetcdctl -C 192.168.56.110:4001 member list
10f1c239a15ba875: name=192.168.56.110 peerURLs=http://192.168.56.110:2380 clientURLs=http://192.168.56.110:4001
f7132cc88f7a39fa: name=192.168.56.111 peerURLs=http://192.168.56.111:2380 clientURLs=http://192.168.56.111:4001 </pre> 5: 準備
<pre>
#etcdctl -C 192.168.56.110:4001 mk /coreos.com/network/config '{"Network":"10.0.0.0/16"}'
{"Network":"10.0.0.0/16"}
# etcdctl -C 192.168.56.110:4001 get /coreos.com/network/config
{"Network":"10.0.0.0/16"} </pre>
該配置后面的kubenetes會用到
2: kubenetes
-
1: 控制節點安裝
1: 包安裝
<pre>
yum -y install kubernetes
</pre>-
2: 配置文件: /etc/kubernetes/apiserver
<pre>
###
# kubernetes system config
#
# The following values are used to configure the kube-apiserver
## The address on the local server to listen to.
KUBE_API_ADDRESS="--address=0.0.0.0"# The port on the local server to listen on.
KUBE_API_PORT="--port=8080"# Port minions listen on
KUBELET_PORT="--kubelet_port=10250"# Comma separated list of nodes in the etcd cluster
#KUBE_ETCD_SERVERS="--etcd_servers=http://127.0.0.1:4001"
KUBE_ETCD_SERVERS="--etcd_servers=http://192.168.56.110:4001,http://192.168.56.111:4001"
# 修改為咱們配置的etcd服務# Address range to use for services
KUBE_SERVICE_ADDRESSES="--portal_net=192.168.56.150/28"
# 外網網段, kubenetes通過改網絡把服務暴露出去# default admission control policies
KUBE_ADMISSION_CONTROL="--admission_control=NamespaceAutoProvision,LimitRanger,ResourceQuota"
# Add your own!
KUBE_API_ARGS=""
</pre>
kubenetse的配置文件不支持每行后面加注釋, 實際生產中需要將每行后面的解釋刪掉 -
3: 啟動服務
API的啟動腳本有問題
/usr/lib/systemd/system/kube-apiserver.service
<pre>
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes[Service]
PermissionsStartOnly=true
ExecStartPre=-/usr/bin/mkdir /var/run/kubernetes
ExecStartPre=-/usr/bin/chown -R kube:kube /var/run/kubernetes/
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/apiserver
User=kube
ExecStart=/usr/bin/kube-apiserver
$KUBE_LOGTOSTDERR
$KUBE_LOG_LEVEL
$KUBE_ETCD_SERVERS
$KUBE_API_ADDRESS
$KUBE_API_PORT
$KUBELET_PORT
$KUBE_ALLOW_PRIV
$KUBE_SERVICE_ADDRESSES
$KUBE_ADMISSION_CONTROL
$KUBE_API_ARGS
Restart=on-failure
LimitNOFILE=65536[Install]
WantedBy=multi-user.target
</pre>
啟動服務
<pre>
systemctl enable kube-apiserver kube-controller-manager kube-scheduler
systemctl restart kube-apiserver kube-controller-manager kube-scheduler </pre> -
4: 驗證
<pre>ps aux | grep kube
kube 20505 5.4 1.6 45812 30808 ? Ssl 22:05 0:07 /usr/bin/kube-apiserver --logtostderr=true --v=0 --etcd_servers=http://192.168.56.110:2380,http://192.168.56.110:2380 --address=0.0.0.0 --allow_privileged=false --portal_net=192.168.56.0/24 --admission_control=NamespaceAutoProvision,LimitRanger,ResourceQuota
kube 20522 1.8 0.6 24036 12064 ? Ssl 22:05 0:02 /usr/bin/kube-controller-manager --logtostderr=true --v=0 --machines=127.0.0.1 --master=http://127.0.0.1:8080
kube 20539 1.3 0.4 17420 8760 ? Ssl 22:05 0:01 /usr/bin/kube-scheduler --logtostderr=true --v=0 --master=http://127.0.0.1:8080kubectl cluster-info
Kubernetes master is running at http://localhost:8080
</pre>
-
2: 計算節點安裝
1: 包安裝
<pre>
yum -y install kubernetes docker flannel bridge-utils net-tools
</pre>-
2: 配置文件
- /etc/kubernetes/config
<pre>
###
# kubernetes system config
#
# The following values are used to configure various aspects of all
# kubernetes services, including
#
# kube-apiserver.service
# kube-controller-manager.service
# kube-scheduler.service
# kubelet.service
# kube-proxy.service
# logging to stderr means we get it in the systemd journal
KUBE_LOGTOSTDERR="--logtostderr=true"
# journal message level, 0 is debug
KUBE_LOG_LEVEL="--v=0"
# Should this cluster be allowed to run privileged docker containers
KUBE_ALLOW_PRIV="--allow_privileged=false"
# How the controller-manager, scheduler, and proxy find the apiserver
KUBE_MASTER="--master=http://192.168.56.110:8080" #將改IP改為控制節點IP
</pre> - /etc/kubernetes/kubelet
<pre>
###
# kubernetes kubelet (minion) config
# The address for the info server to serve on (set to 0.0.0.0 or "" for all interfaces)
KUBELET_ADDRESS="--address=192.168.56.111" #本機地址
# The port for the info server to serve on
KUBELET_PORT="--port=10250"
# You may leave this blank to use the actual hostname
KUBELET_HOSTNAME="--hostname_override=192.168.56.111" #本機地址
# location of the api-server
KUBELET_API_SERVER="--api_servers=http://192.168.56.110:8080" #控制節點地址
# Add your own!
KUBELET_ARGS="--pod-infra-container-image=docker.io/kubernetes/pause:latest"
#kubenet服務的啟動需要依賴以pause這個鏡像, 默認kubenet會從google鏡像服務下載, 而由于***原因, 下載不成功, 這里我們指定為的docker的鏡像
#鏡像下載: docker pull docker.io/kubernetes/pause
</pre> - /etc/sysconfig/flanneld
<pre>
# Flanneld configuration options
# etcd url location. Point this to the server where etcd runs
FLANNEL_ETCD="http://192.168.56.110:4001,http://192.168.56.111:4001" #修改為etcd服務地址
# etcd config key. This is the configuration key that flannel queries
# For address range assignment
FLANNEL_ETCD_KEY="/coreos.com/network"
# Any additional options that you want to pass
#FLANNEL_OPTIONS=""
</pre>
- /etc/kubernetes/config
-
3: 服務修改
kubernetes的默認服務啟動有問題, 需要做寫調整
cat /usr/lib/systemd/system/kubelet.service
<pre>
[Unit]
Description=Kubernetes Kubelet Server
Documentation=https://github.com/GoogleCloudPlatform/kubernetes
After=docker.service
Requires=docker.service[Service]
WorkingDirectory=/var/lib/kubelet
EnvironmentFile=-/etc/kubernetes/config
EnvironmentFile=-/etc/kubernetes/kubelet
ExecStart=/usr/bin/kubelet
$KUBE_LOGTOSTDERR
$KUBE_LOG_LEVEL
$KUBELET_API_SERVER
$KUBELET_ADDRESS
$KUBELET_PORT
$KUBELET_HOSTNAME
$KUBE_ALLOW_PRIV
$KUBELET_ARGS
LimitNOFILE=65535
LimitNPROC=10240
Restart=on-failure[Install]
WantedBy=multi-user.target
</pre>
調整docker網絡
<pre>
systemctl start docker
systemctl stop docker
ifconfig docker0 down
brctl delbr docker0
</pre>啟動服務
<pre>
systemctl enable kube-proxy kubelet flanneld docker
systemctl restart kube-proxy kubelet flanneld docker
</pre> 驗證
<pre>
# kubectl get nodes
NAME LABELS STATUS
192.168.56.111 kubernetes.io/hostname=192.168.56.111 Ready
192.168.56.112 kubernetes.io/hostname=192.168.56.112 Ready
</pre>
3: Kubernetes使用
3.1 基本應用
kubenetes的管理實際上就是針對pod, rc, services的管理, 命令行針對kubenetes的管理建議基于配置文件進行, 這樣更便于管理, 也更規范
<pre>
kubectl create -h
Create a resource by filename or stdin.
JSON and YAML formats are accepted.
Usage:
kubectl create -f FILENAME [flags]
Examples:
// Create a pod using the data in pod.json.
$ kubectl create -f pod.json
// Create a pod based on the JSON passed into stdin.
$ cat pod.json | kubectl create -f -
</pre>
-
格式規范:
<pre>
apiVersion: v1beta3 #API版本, 要在 kubectl api-versions
kind: ReplicationController #Pod, ReplicationController, Service
metadata: #元數據, 主要是name與label
name: test
spec: #配置, 根據不同的kind, 具體配置項會有所不同
***
</pre>
kubenetes支持yaml或者json的文件輸入, json的用API來處理的時候比較方便, yaml對人更友好一些, 以下用yaml格式.一個典型的業務大概架構類似這樣:
<pre>
+-----------+
| |
| logic | #邏輯處理服務
| |
+---+--+----+
| |
+----+ +----+
| |
| |
+----v-----+ +----v----+
| | | |
| DB | | redis | #調用其他服務
| | | |
+----------+ +---------+
</pre>
思路: 每個pod內提供一組完整的服務
-
1: 準備鏡像
- postgres: 數據庫鏡像
- redis: 緩存服務鏡像
- wechat: 微信服務鏡像
2: rc配置wechat-rc.yaml:
<pre>
apiVersion: v1beta3
kind: ReplicationController
metadata:
name: wechatv4
labels:
name: wechatv4
spec:
replicas: 1
selector:
name: wechatv4
template:
metadata:
labels:
name: wechatv4
spec:
containers:
- name: redis
image: redis
ports:
- containerPort: 6379
- name: postgres
image: opslib/wechat_db
ports:
- containerPort: 5432
- name: wechat
image: opslib/wechat1
ports:
- containerPort: 80
</pre>
導入rc
<pre>
# kubectl create -f wechat-rc.yaml
replicationcontrollers/wechat
</pre>
確認
<img src="./getpods.png" width=800>
附:
在docker中可以利用link功能將容器之間連接起來, 而在kubenetes中是沒有這樣的系統的, 但是由于同一個pod內是共享網絡存儲相關空間的,在wechat的鏡像中的配置文件中, 連接數據庫和redis的配置項中的IP可以直接寫'127.0.0.1', 類似這樣:
<pre>
sql_connection='postgresql://wechat:wechatpassword@127.0.0.1/wechat'
cached_backend='redis://127.0.0.1:6379/0'
</pre>3: 服務配置wechat-service.yaml
<pre>
apiVersion: v1beta3
kind: Service
metadata:
name: wechat
labels:
name: wechat
spec:
ports:
- port: 80
selector:
name: wechatv4
</pre>
導入
<pre>
# kubectl create -f wechat-service.yaml
services/wechat
</pre>
查看
<pre>
kubectl get service wechat
NAME LABELS SELECTOR IP(S) PORT(S)
wechat name=wechat name=wechatv4 192.168.56.156 80/TCP
</pre>
確認
<pre>
# curl -i http://192.168.56.156
HTTP/1.1 200 OK
Content-Length: 0
Access-Control-Allow-Headers: X-Auth-Token, Content-type
Server: TornadoServer/4.2
Etag: "da39a3ee5e6b4b0d3255bfef95601890afd80709"
Date: Mon, 06 Jul 2015 09:04:49 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Content-Type: application/json
</pre>
3.2 業務更新
基本的業務部署完成后, 在服務要更新的時候, kubenetes可以利用滾動更新,基本上實現了業務的熱更新.
<pre>
kubectl rolling-update wechatv3 -f wechatv3.yaml
Creating wechatv4
At beginning of loop: wechatv3 replicas: 0, wechatv4 replicas: 1
Updating wechatv3 replicas: 0, wechatv4 replicas: 1
At end of loop: wechatv3 replicas: 0, wechatv4 replicas: 1
Update succeeded. Deleting wechatv3
wechatv4
</pre>
3.3 應用管理
當需要同一服務需要啟動多個實例, 服務本身一樣, 但是啟動服務的配置不一樣時候
一般我們可能會有3種需求:
- 1: 不同的container設置不同的資源權限
- 2: 不同的container掛載不同的目錄
- 3: 不同的container執行不同的啟動命令
可以在配置文件中針對不同的container設置不同的設置.
<pre>
apiVersion: v1beta3
kind: ReplicationController
metadata:
name: new
labels:
name: new
spec:
replicas: 1
selector:
name: new
template:
metadata:
labels:
name: new
spec:
containers:
- name: redis
image: redis
ports:
- containerPort: 6379
- name: postgres
image: opslib/wechat_db
ports:
- containerPort: 5432
- name: wechat
image: opslib/wechat1
command: #container的啟動命令有外部定義
- '/bin/bash'
- '-c'
- '/usr/bin/wechat_api'
- '--config=/etc/wechat/wechat.conf'
resources: #限制container的資源
request: #請求的資源
cpu: "0.5"
memory: "512Mi"
limits: #最大可以使用的資源
cpu: "1"
memory: "1024Mi"
ports:
- containerPort: 80
volumeMounts: #掛載目錄
- name: data
mountPath: /data
volumes:
- name: data
</pre>
參考文章:
- Kubernetes系統架構簡介: http://www.infoq.com/cn/articles/Kubernetes-system-architecture-introduction
- etcd:用于服務發現的鍵值存儲系統: http://www.infoq.com/cn/news/2014/07/etcd-cluster-discovery
- kubenetes部署: http://blog.opskumu.com/k8s-cluster-centos7.html