Kubernetes使用

本文基本上翻译了 http://kubernetes.io/docs/user-guide/

yaml配置文件使用例子

  通常我们是通过yaml配置文件通过kubectl创建podservicereplication controller.在yaml文件中指定apiVersion,kind,name.

  一个yaml文件的例子:

apiVersion: v1
kind: Pod
metadata:
     name: hello-world
spec:  # specification of the pod's contents
    restartPolicy: Never
    containers:
    - name: hello
    image: "ubuntu:14.04"
    command: ["/bin/echo","hello'?,'?world"]

  - metadata.name是hello-world,这个name在一个集群内必须唯一

  - containers[0].name就是container的昵称

  - image是docker image,默认是从docker hub上拉。

  - restartPolicy: Never表示只运行容器一次之后就终止掉pod

  - command部分会覆盖掉docker container的Entrypoint,并可以结合args使用。

command: ["/bin/echo"]
args: ["hello","world"]

  然后就可以create了:

$ kubectl create -f ./hello-world.yaml
pods/hello-world

  当运行成功时,kubectl打印资源类型和资源名称。

  再看一下关于env的使用

apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:  # specification of the pod’s contents
  restartPolicy: Never
  containers:
  - name: hello
    image: "ubuntu:14.04"
    env:
    - name: MESSAGE
      value: "hello world"
    command: ["/bin/sh","-c"]
    args: ["/bin/echo \"${MESSAGE}\""]

  这里用env生命了环境变量MESSAGE,通过运行shell将之打印出来,当然更简单地,可以直接通过 kubernetes提供的$(ENVVAR)的语法。

command: ["/bin/echo"]
args: ["$(MESSAGE)"]

Deployment使用

  Deployment也是k8s中一个很重要的概念,用来创建一组replicated containers或者说是Pods,Deployment能保证在同一时刻一组指定数量的Pod在运行,如果数量多于指定数量,k8s会杀死多余的,如果太少,会自动启动新的,我们可以通过extension API调用:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: my-nginx
spec:
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

  注意 这里的kindDeploymentapiVersionextensions/v1beta1(使用centos通过yum安装k8s默认无extension API,这里是用ubuntu安装的)

  这里container name其实没必要显示指定,因为它们会继承自deployment name.与直接创建pods不同的是,deployment在某些情况下(比如节点down了)会替换那些被删除或者被终止的pods。因此,我们建议,如果你是想创建一个持续运行的应用可以使用deployment,即便你只需要一个pod.

Labels使用

  通过label查看刚刚创建的deployment,pod

$ kubectl get pods -L run
NAME                        READY     STATUS    RESTARTS   AGE       RUN
my-nginx-3800858182-1v53o   1/1       Running   0          46s       my-nginx
my-nginx-3800858182-2ds1q   1/1       Running   0          46s       my-nginx

$ kubectl get deployment/my-nginx -L run
NAME       DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE       RUN
my-nginx   2         2         2            2           2m        my-nginx

  注意,这里pod template里的label被复制到deployment的label里了。

与Pod通信

$ kubectl get pods -l run=my-nginx -o wide
NAME                        READY     STATUS    RESTARTS   AGE       NODE
my-nginx-3800858182-jr4a2   1/1       Running   0          13s       kubernetes-minion-905m
my-nginx-3800858182-kna2y   1/1       Running   0          13s       kubernetes-minion-ljyd

  查看podIP

$ kubectl get pods -l run=my-nginx -o yaml | grep podIP
podIP: 10.244.3.4
podIP: 10.244.2.5

  因为nginx服务是在80端口,理论上你现在在master上curl podIP就能显示nginx的首页了。
  跟docker一样,pod端口可以被映射到host端口,但是有了podIP的概念以后,这种做法是不建议的。

Service

  设想一个场景,如果一个deployment有两个pod,其中一个pod所在node down掉了,这里假设会有很多node,那么根据deployment的机制,会在其它的node上创建pod,那么podIP可能会发生变化。这样很不利于管理,Sevice能够解决这一问题。
  
  service是一组Pods的抽象,每个service都会分配一个独立IP,叫做clusterIP,这个IP在service活着的时候不会改变,Pods可以跟service进行通信,并且service会自动做load-balanced

  我们来查看一下service:

$ kubectl describe svc my-nginx
Name:            my-nginx
Namespace:        default
Labels:            run=my-nginx
Selector:        run=my-nginx
Type:            ClusterIP
IP:            10.0.162.149
Port:            <unset>    80/TCP
Endpoints:        10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
No events.

$ kubectl get ep my-nginx
NAME       ENDPOINTS                     AGE    
my-nginx   10.244.2.5:80,10.244.3.4:80   1m

  这里endposts就是service后端对应的pods,当有pod死掉了,会从endpoint中一处,满足service selector条件的新的pods会加入到endpoint里边。

  你可以在在任意一个node上,通过<CLUSTER-IP>:<PORT>访问service,记住clusterIP并不是真实存在的,具体原理可以看看 http://kubernetes.io/docs/user-guide/services/#virtual-ips-and-service-proxies

  • 访问Service

  k8s提供两种发现service的方式,通过环境变量或者DNS,前者是通过配置立即可用的,后者需要经过配置DNS才能实现。   

  先来看看通过环境变量方式,查看之前创建容器的Service.环境变量:

$ kubectl exec my-nginx-3800858182-jr4a2 -- printenv | grep SERVICE
KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443
KUBERNETES_SERVICE_PORT_HTTPS=443

  你会发现环境变量并没有你刚刚创建的Service,这是因为你先创建的pods,后创建的Service,pods的环境变量在它启动的时刻获取的,所以现在你可以手动杀掉刚刚通过Deployment创建两个pods,然后再用命令查看,就会看到你创建的Service。

$ kubectl exec my-nginx-3800858182-e9ihh -- printenv | grep SERVICE
KUBERNETES_SERVICE_PORT=443
MY_NGINX_SERVICE_HOST=10.0.162.149
KUBERNETES_SERVICE_HOST=10.0.0.1
MY_NGINX_SERVICE_PORT=80
KUBERNETES_SERVICE_PORT_HTTPS=443

  下面再看看DNS的方式,查看dns服务是否在运行,注意要指定namespace:

$ kubectl get services kube-dns --namespace=kube-system
NAME       CLUSTER-IP   EXTERNAL-IP   PORT(S)         AGE
kube-dns   10.0.0.10    <none>        53/UDP,53/TCP   8m

  关于dns使用说明的文档 https://github.com/kubernetes/kubernetes/tree/master/cluster/addons/dns

  默认情况下,DNS作为k8s的一个service运行,会给DNS分配一个静态虚拟IP,这个可以在config-default.sh里配置,配置了以后,在kubelet启动的时候就会添加这一flag。当然DNS还需要一个domain,默认是local domain。

  这里说一下两个易混的概念targetPortporttargetPort是指container暴露的端口,port是service的抽象暴露的端口.

  • 配置https证书部分先略过

  • 暴露Service:k8s提供了两种暴露service的方法NodePortLoadBalancer

  先创建一个type是NodePort的service:

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  type: NodePort
  ports:
  - port: 8080
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    run: my-nginx

  创建之后查看服务:

$ kubectl get svc my-nginx -o yaml
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2016-04-19T08:50:26Z
  labels:
    run: my-nginx
  name: my-nginx
  namespace: default
  resourceVersion: "162296"
  selfLink: /api/v1/namespaces/default/services/my-nginx
  uid: 07191fb3-f61a-11e5-8ae5-42010af00002
spec:
  clusterIP: 10.0.162.149
  ports:
  - name: http
    nodePort: 32210
    port: 8080
    protocol: TCP
    targetPort: 80
  selector:
    run: my-nginx
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

  可以看到创建了一个nodePort是32210的服务,这个端口是host的端口,也就是说通过访问host上这个端口就可以转发到这个service,这就完成了service的对外暴露。当然你可以在任意一台装有kube-proxy的机子上访问32210端口获取my-nginx这个service:

$ curl 192.168.xx.xx:32210
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

  如果你的host有公网IP的话,通过查看service可以看到external-ip:

$ kubectl get svc my-nginx
NAME       CLUSTER-IP     EXTERNAL-IP        PORT(S)               AGE
my-nginx   10.0.162.149   162.222.184.144    80/TCP,81/TCP,82/TCP  21s

  将上述的yaml文件中NodePort替换成LoadBalancer,再创建service,也能达到相同的效果。

  NodePort和LoadBalancer的区别

http://stackoverflow.com/questions/34443138/kubernetes-difference-between-nodeport-and-loadbalancer

  目前来讲,二者的区别就是loadBalancer可以帮你创建一个新的LB实例(当然是在cloud provider支持的情况下),如果是采用bare metal方式安装的话,我的理解是没有区别。