Setup Kubernetes cluster trên GCE với Kubespray

Kubespray là một tool dùng để provision k8s cluster trên các cloud provider, ngoài ra cũng có thể dùng Kubespray để setup k8s cluster trên các VPS và baremetal server. Bản chất kubespray chỉ là các ansible playbook nên rất linh hoạt và độc lập với các cloud-provider cũng như dễ dàng tích hợp với nhiều tool devops khác.
Bài này sẽ hướng dẫn cách dùng Kubespray để provisioning một k8s-cluster trên GCE, rồi kết nối tới k8s-cluster này từ máy cá nhân.

Để bắt đầu ta cần download GCE credential về máy cá nhân. Vào GCE console -> IAM & Admin -> Service accounts -> Create service account nếu chưa có; nếu có rồi thì chọn "Create key" -> P12 format.
Download P12-format key về rồi convert sang PEM key:

openssl pkcs12 -in /home/minhcd/Downloads/MinhCD\ Google-abcdxyz.p12  -passin pass:notasecret -nodes -nocerts | openssl rsa -out gce-key.pem  

Convert ssh pubkey sang format hỗ trợ bởi Google Cloud rồi push lên GCE:

$ gcloud compute project-info add-metadata --metadata-from-file sshKeys=/home/minhcd/.ssh/id_rsa.gcloud-specific.pub

Kubespray không cần dùng dynamic inventory nhưng ở đây ta vẫn sẽ cấu hình dynamic inventory với Ansible để dễ dàng thực hiện các lệnh check cũng nhưng để sau này dùng ansible trực tiếp với GCE nếu muốn:

# cd /etc/ansible
# wget https://github.com/ansible/ansible/raw/stable-2.3/contrib/inventory/gce.ini
# wget https://github.com/ansible/ansible/raw/stable-2.3/contrib/inventory/gce.py

Sửa nội dung file /etc/ansible/gce.ini

[gce]
gce_service_account_email_address = <...@project.gserviceaccount.com>  
gce_service_account_pem_file_path = </path/to/gce-key.pem>  
gce_project_id = <my-project-name>  

Tiếp theo ta sẽ install kubespray-cli - là một cli được build on-top-of kubespray và bổ sung một số tính năng như hỗ trợ khởi tạo instance trên AWS/GCE/Openstack, auto-assign roles cho các instances, giúp việc chạy kubespray's playbook, v.v.. dễ dàng hơn:
$ sudo pip2 install kubespray

Tạo file ~/.kubespray.yml có nội dung:

---                                                                                                                     
# Path where the kubespray ansible playbooks will be installed                                                          
# Defaults to current user's home directory if not set                                                                  
kubespray_path: "/home/minhcd/repo/kubespray"                                                                           

# Default inventory path                                                                                                
kubespray_git_repo: "https://github.com/kubernetes-incubator/kubespray"                                                 

# Logging options                                                                                                       
loglevel: "info"      

## Google Compute Engine options                                                                                        
## ---                                                                                                                  
image: ubuntu-1604-xenial-v20170619a  
masters_machine_type: g1-small  
nodes_machine_type: g1-small  
etcds_machine_type: g1-small  
service_account_email: ...@project.gserviceaccount.com  
pem_file: </path/to/gce-key.pem>  
project_id: <your-project-id>  
zone: asia-east1-b  
tags:  
  - k8s           

Dùng kubespray để tạo 3 nodes trên GCE:

$ kubespray gce --nodes 3 #[--noclone]
[...]
WRITTING INVENTORY ***********************************************************************************************************  
Inventory generated : /home/minhcd/repo/kubespray/inventory/inventory.cfg  

Nếu muốn customize roles/services cho các nodes thì có thể sửa file inventory.cfg

Re-check:

$ ansible -i /etc/ansible/gce.py asia-east1-b  -m shell -a "/bin/uname -a" -u ubuntu
k8s-james-590lpc | SUCCESS | rc=0 >>  
Linux k8s-james-590lpc 4.8.0-56-generic #61~16.04.1-Ubuntu SMP Wed Jun 14 11:58:22 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

k8s-james-qhda91 | SUCCESS | rc=0 >>  
Linux k8s-james-qhda91 4.8.0-56-generic #61~16.04.1-Ubuntu SMP Wed Jun 14 11:58:22 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

k8s-james-qqioja | SUCCESS | rc=0 >>  
Linux k8s-james-qqioja 4.8.0-56-generic #61~16.04.1-Ubuntu SMP Wed Jun 14 11:58:22 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

$ gcloud compute instances list 
NAME              ZONE          MACHINE_TYPE  PREEMPTIBLE  INTERNAL_IP  EXTERNAL_IP      STATUS  
k8s-james-590lpc  asia-east1-b  g1-small                   10.140.0.2   35.x.y.z   RUNNING  
k8s-james-qhda91  asia-east1-b  g1-small                   10.140.0.4   104.x.y.z  RUNNING  
k8s-james-qqioja  asia-east1-b  g1-small                   10.140.0.3   35.x.y.z   RUNNING  

Ok, giờ bắt đầu cài đặt k8s trên các instances vừa được provision:

$ kubespray deploy --gce -u ubuntu -n calico
[...]
/usr/local/bin/ansible-playbook --ssh-extra-args -o StrictHostKeyChecking=no -u ubuntu -b --become-user=root -i /home/minhcd/repo/kubespray/inventory/inventory.cfg /home/minhcd/repo/kubespray/cluster.yml -e kube_network_plugin=calico -e cloud_provider=gce
[...]
PLAY RECAP *******************************************************************************************************************  
k8s-james-590lpc           : ok=431  changed=112  unreachable=0    failed=0  
k8s-james-qhda91           : ok=366  changed=93   unreachable=0    failed=0  
k8s-james-qqioja           : ok=400  changed=104  unreachable=0    failed=0  
localhost                  : ok=3    changed=1    unreachable=0    failed=0

Kubernetes deployed successfuly  

Để kiểm tra cụm, trên k8s master-nodes ta download kubectl:

$ curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl \
&& sudo mv ./kubectl /usr/local/bin/kubectl \
&& sudo chmod a+x /usr/local/bin/kubectl

$ kubectl --namespace=kube-system get po,no,ep,cs
NAME                                          READY     STATUS    RESTARTS   AGE  
po/kube-apiserver-k8s-james-590lpc            1/1       Running   0          27m  
po/kube-apiserver-k8s-james-qqioja            1/1       Running   0          27m  
po/kube-controller-manager-k8s-james-590lpc   1/1       Running   0          28m  
po/kube-controller-manager-k8s-james-qqioja   1/1       Running   0          28m  
po/kube-dns-3841192733-9lk8h                  3/3       Running   0          27m  
po/kube-dns-3841192733-z459t                  3/3       Running   0          27m  
po/kube-proxy-k8s-james-590lpc                1/1       Running   0          27m  
po/kube-proxy-k8s-james-qhda91                1/1       Running   0          27m  
po/kube-proxy-k8s-james-qqioja                1/1       Running   0          27m  
po/kube-scheduler-k8s-james-590lpc            1/1       Running   0          27m  
po/kube-scheduler-k8s-james-qqioja            1/1       Running   0          27m  
po/kubedns-autoscaler-1833630871-kht9r        1/1       Running   0          27m  
po/nginx-proxy-k8s-james-qhda91               1/1       Running   0          28m

NAME                  STATUS    AGE       VERSION  
no/k8s-james-590lpc   Ready     28m       v1.6.4+coreos.0  
no/k8s-james-qhda91   Ready     28m       v1.6.4+coreos.0  
no/k8s-james-qqioja   Ready     28m       v1.6.4+coreos.0

NAME                         ENDPOINTS                                                        AGE  
ep/kube-controller-manager   <none>                                                           22m  
ep/kube-dns                  10.233.64.129:53,10.233.77.129:53,10.233.64.129:53 + 1 more...   27m  
ep/kube-scheduler            <none>                                                           22m

NAME                    STATUS    MESSAGE              ERROR  
cs/controller-manager   Healthy   ok  
cs/scheduler            Healthy   ok  
cs/etcd-0               Healthy   {"health": "true"}  
cs/etcd-2               Healthy   {"health": "true"}  
cs/etcd-1               Healthy   {"health": "true"}  

Để tương tác với k8s cluster từ laptop của mình thì cần config thêm cho kubectl:

$ kubectl config set-cluster gce-cluster1 --server=https://35.x.y.z:6443 --insecure-skip-tls-verify=true
Cluster "gce-cluster1" set.  
### (nếu muốn dùng TLS chuẩn thì phải download CA file về + config thêm IP or domain cho TLS cert of apiserver. Còn ko sẽ bị lỗi kiểu:
`Unable to connect to the server: x509: certificate is valid for 10.140.0.2, 10.140.0.2, 10.140.0.3, 10.140.0.3, 10.233.0.1, 127.0.0.1, not 35.x.y.z`)

$ kubectl config set-context gce-cluster1 --cluster=gce-cluster1 --user=gce-cluster1
Context "gce-cluster1" created.

$ kubectl config set-credentials gce-cluster1 --username=kube --password=changeme
User "gce-cluster1" set.

$ kubectl config use-context gce-cluster1                   
Switched to context "gce-cluster1".  

Bản chất các lệnh này sẽ add/update config của kubectl @ ~/.kube/config với nội dung như:

apiVersion: v1  
clusters:  
- cluster:
    insecure-skip-tls-verify: true
    server: https://35.x.y.z:6443
  name: gce-cluster1
contexts:  
- context:
    cluster: gce-cluster1
    user: gce-cluster1
  name: gce-cluster1
current-context: gce-cluster1  
kind: Config  
preferences: {}  
users:  
- name: gce-cluster1
  user:
    password: changeme
    username: kube

Kiểm tra lại nếu thực hiện được là ok:

$ kubectl get node                                                                                              
NAME               STATUS    AGE       VERSION  
k8s-james-590lpc   Ready     3h        v1.6.4+coreos.0  
k8s-james-qhda91   Ready     3h        v1.6.4+coreos.0  
k8s-james-qqioja   Ready     3h        v1.6.4+coreos.0  

PS: một vài option có thể set thêm trong ~/repo/kubespray/inventory/group_vars/*.yml như kube_api_pwd: "changeme", loadbalancer_apiserver_localhost: true, deploy_netchecker: true,
apiserver_loadbalancer_domain_name: "elb.some.domain", loadbalancer_apiserver: address: 1.2.3.4 port: 1234.

Nếu có deploy netchecker thì có thể check connectivity giữa các pod/svc có ngon không bằng lệnh:

$ curl http://35.x.y.z:31081/api/v1/connectivity_check
{"Message":"All 6 pods successfully reported back to the server","Absent":null,"Outdated":null}%                                                 

Note: với Kubespray các thành phần của Kubernetes (api, controller, proxy,...) đều đã được running theo mô hình self-hosted , chỉ còn kubelet và etcd là vẫn chạy như các system services (managed by OS). Tuy nhiên, kubespray hiện đã setup cho etcd chạy trong container, điều này có nghĩa là trong tương lai etcd cũng có thể được run và manage bởi K8s - nếu nó có thể codify các "operation knowledge" cho etcd-cluster cũng như xử lý được bài toán persistent data storage cho etcd.