kubernetes二次开发(主要是开发满足自己业务的api)

kubernetes目前提供两种方式来创建所需要的pod,service,replicationcontroller等,一种是通过kubectl create -f ,一种通过http 的restful 接口,由于工作项目的原因,需要根据实际的业务需求来定制化的开发k8s的api,我所用到的库是官方给出的,代码库地址:https://github.com/kubernetes/client-go,以下是我在开发时的一些思路整理,由于水平有限,讲得不好,还请广大博友谅解。

  • 初始化连接
  • 代码解读
  • 创建namespace
  • 创建pod
  • 创建replicationController
  • 创建service

初始化连接

方式一:

直接上代码:

package main

import (
    "fmt"
    "time"

    "k8s.io/client-go/1.4/kubernetes"
    "k8s.io/client-go/1.4/pkg/api"
    "k8s.io/client-go/1.4/rest"
)

func main() {
    // creates the in-cluster config
    config, err := rest.InClusterConfig()
    if err != nil {
        panic(err.Error())
    }
    // creates the clientset
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

代码解读

以上代码就是获取一个kubernetes集群的client

config, err := rest.InClusterConfig()
  • 1
  • 2

本行代码是初始化一个默认的k8s的rest.Config,该config源码如下:

type Config struct {
// Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
// If a URL is given then the (optional) Path of that URL represents a prefix that must
// be appended to all request URIs used to access the apiserver. This allows a frontend
// proxy to easily relocate all of the apiserver endpoints.
Host string
// APIPath is a sub-path that points to an API root.
APIPath string
// Prefix is the sub path of the server. If not specified, the client will set
// a default value.  Use "/" to indicate the server root should be used
Prefix string

// ContentConfig contains settings that affect how objects are transformed when
// sent to the server.
ContentConfig

// Server requires Basic authentication
Username string
Password string

// Server requires Bearer authentication. This client will not attempt to use
// refresh tokens for an OAuth2 flow.
// TODO: demonstrate an OAuth2 compatible client.
BearerToken string

// Impersonate is the username that this RESTClient will impersonate
Impersonate string

// Server requires plugin-specified authentication.
AuthProvider *clientcmdapi.AuthProviderConfig

// Callback to persist config for AuthProvider.
AuthConfigPersister AuthProviderConfigPersister

// TLSClientConfig contains settings to enable transport layer security
TLSClientConfig

// Server should be accessed without verifying the TLS
// certificate. For testing only.
Insecure bool

// UserAgent is an optional field that specifies the caller of this request.
UserAgent string

// Transport may be used for custom HTTP behavior. This attribute may not
// be specified with the TLS client certificate options. Use WrapTransport
// for most client level operations.
Transport http.RoundTripper
// WrapTransport will be invoked for custom HTTP behavior after the underlying
// transport is initialized (either the transport created from TLSClientConfig,
// Transport, or http.DefaultTransport). The config may layer other RoundTrippers
// on top of the returned RoundTripper.
WrapTransport func(rt http.RoundTripper) http.RoundTripper

// QPS indicates the maximum QPS to the master from this client.
// If it's zero, the created RESTClient will use DefaultQPS: 5
QPS float32

// Maximum burst for throttle.
// If it's zero, the created RESTClient will use DefaultBurst: 10.
Burst int

// Rate limiter for limiting connections to the master from this client. If present overwrites QPS/Burst
RateLimiter flowcontrol.RateLimiter

// Version forces a specific version to be used (if registered)
// Do we need this?
// Version string
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

}

该config包含了连接apiserver需要的一些信息,我包括api的连接ip port(即hostname),用于认证用的一些信息,如:username,password等,以及api调用返回值的类型,序列化等。

回到InClusterConfig()这个方法中:

func InClusterConfig() (*Config, error) {
host, port := os.Getenv("KUBERNETES_SERVICE_HOST"), os.Getenv("KUBERNETES_SERVICE_PORT")
if len(host) == 0 || len(port) == 0 {
    return nil, fmt.Errorf("unable to load in-cluster configuration, KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT must be defined")
}

token, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountTokenKey)
if err != nil {
    return nil, err
}
tlsClientConfig := TLSClientConfig{}
rootCAFile := "/var/run/secrets/kubernetes.io/serviceaccount/" + api.ServiceAccountRootCAKey
if _, err := crypto.CertPoolFromFile(rootCAFile); err != nil {
    glog.Errorf("Expected to load root CA config from %s, but got err: %v", rootCAFile, err)
} else {
    tlsClientConfig.CAFile = rootCAFile
}

return &Config{
    // TODO: switch to using cluster DNS.
    Host:            "https://" + net.JoinHostPort(host, port),
    BearerToken:     string(token),
    TLSClientConfig: tlsClientConfig,
}, nil
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

首先就是获取KUBERNETES_SERVICE_HOST 和 KUBERNETES_SERVICE_PORT这连个环境变量,所以必须要设置这两个环境变量(就是apiserver的ip和port),然后是获取/var/run/secrets/kubernetes.io/serviceaccount/下的认证文件,进行config的初始化,从而为下一步的连接做准备。如果在相应的环境中没有相应的ca文件,则该方法会报错,初始化k8s的client不成功


方式二:
var (
    kubeconfig = flag.String("kubeconfig", "./config", "absolute path to the kubeconfig file")
)

func main() {
    flag.Parse()
    // uses the current context in kubeconfig
    config, err := clientcmd.BuildConfigFromFlags("183.131.19.231:8080", *kubeconfig)
    if err != nil {
        panic(err.Error())
    }
    // creates the clientset
    clientset, err := kubernetes.NewForConfig(config)
    if err != nil {
        panic(err.Error())
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

其中config配置文件信息如下:

apiVersion: v1
clusters:
- cluster:
    api-version: v1
    server: http://183.131.19.231:8080
  name: k8s-cluster
contexts:
- context:
    cluster: k8s-server
    user: myself
  name: default-context
current-context: default-context
kind: Config
preferences:
  colors: true
users:
- name: myself
  user:
    password: admin
    username: admin11232
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这种方式是通过配置文件的方式去链接kube-apiserver,从而获取clientset,配置文件的设置以及获取可以参考官方文档:http://kubernetes.io/docs/user-guide/kubeconfig-file/

创建namespace

首先讲创建namesapce的原因是因为后续的pod,replicationController,service等的创建,在通常的业务中都会和namesapce相关联,namespace其实就相当于租户的概念,或者就相当于group,起到资源隔离的作用。

首先来看看client.Core()这个方法中包含了那些接口以及接口的实现:

func (c *CoreClient) ComponentStatuses() ComponentStatusInterface {
    return newComponentStatuses(c)
}

func (c *CoreClient) ConfigMaps(namespace string) ConfigMapInterface {
    return newConfigMaps(c, namespace)
}

func (c *CoreClient) Endpoints(namespace string) EndpointsInterface {
    return newEndpoints(c, namespace)
}

func (c *CoreClient) Events(namespace string) EventInterface {
    return newEvents(c, namespace)
}

func (c *CoreClient) LimitRanges(namespace string) LimitRangeInterface {
    return newLimitRanges(c, namespace)
}

func (c *CoreClient) Namespaces() NamespaceInterface {
    return newNamespaces(c)
}

func (c *CoreClient) Nodes() NodeInterface {
    return newNodes(c)
}

func (c *CoreClient) PersistentVolumes() PersistentVolumeInterface {
    return newPersistentVolumes(c)
}

func (c *CoreClient) PersistentVolumeClaims(namespace string) PersistentVolumeClaimInterface {
    return newPersistentVolumeClaims(c, namespace)
}

func (c *CoreClient) Pods(namespace string) PodInterface {
    return newPods(c, namespace)
}

func (c *CoreClient) PodTemplates(namespace string) PodTemplateInterface {
    return newPodTemplates(c, namespace)
}

func (c *CoreClient) ReplicationControllers(namespace string) ReplicationControllerInterface {
    return newReplicationControllers(c, namespace)
}

func (c *CoreClient) ResourceQuotas(namespace string) ResourceQuotaInterface {
    return newResourceQuotas(c, namespace)
}

func (c *CoreClient) Secrets(namespace string) SecretInterface {
    return newSecrets(c, namespace)
}

func (c *CoreClient) Services(namespace string) ServiceInterface {
    return newServices(c, namespace)
}

func (c *CoreClient) ServiceAccounts(namespace string) ServiceAccountInterface {
    return newServiceAccounts(c, namespace)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

其中我们这里主要用到的是

func (c *CoreClient) Namespaces() NamespaceInterface {
    return newNamespaces(c)
}
  • 1
  • 2
  • 3

在看NamespaceInterface 这个接口中的方法:

type NamespaceInterface interface {
    Create(*v1.Namespace) (*v1.Namespace, error)
    Update(*v1.Namespace) (*v1.Namespace, error)
    UpdateStatus(*v1.Namespace) (*v1.Namespace, error)
    Delete(name string, options *api.DeleteOptions) error
    DeleteCollection(options *api.DeleteOptions, listOptions api.ListOptions) error
    Get(name string) (*v1.Namespace, error)
    List(opts api.ListOptions) (*v1.NamespaceList, error)
    Watch(opts api.ListOptions) (watch.Interface, error)
    Patch(name string, pt api.PatchType, data []byte, subresources ...string) (result *v1.Namespace, err error)
    NamespaceExpansion
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这里我们主要讲解namespace的创建: 
创建namespace需要传入v1.Namespace这个struct的指针,我们在看看这个strutc的结构:

type Namespace struct {
    unversioned.TypeMeta `json:",inline"`
    // Standard object's metadata.
    // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
    ObjectMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`

    // Spec defines the behavior of the Namespace.
    // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
    Spec NamespaceSpec `json:"spec,omitempty" protobuf:"bytes,2,opt,name=spec"`

    // Status describes the current status of a Namespace.
    // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
    Status NamespaceStatus `json:"status,omitempty" protobuf:"bytes,3,opt,name=status"`
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

其中的三个struct是于yaml中的类型,元数据,还有space是一一对应的。

    //create a namespace
    nc := new(v1.Namespace)
    ncTypeMeta := unversioned.TypeMeta{Kind: "NameSpace", APIVersion: "v1"}
    nc.TypeMeta = ncTypeMeta

    nc.ObjectMeta = v1.ObjectMeta{
        Name: "k8s-test",
    }

    nc.Spec = v1.NamespaceSpec{}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这个其实就相当于yaml文件中做如下定义:

apiVersion: v1
kind: NameSpace
metadata:
  name: "k8s-test"
spec:
  • 1
  • 2
  • 3
  • 4
  • 5

如果有lable,space之类的设置,查阅相关代码加上即可。

之后创建namespace:

resultnc, err := dao.Clientset.Core().Namespaces().Create(nc)
  • 1

穿件成功完成之后会返回创建的namespace,如果失败,会返回相应的错误信息。

之后在k8s集群中通过kubectl命令查看创建的namespace,创建成功。同理还有删除修改等操作,这里就不在一一演示了。

创建pod

创建pod以及rc,service的方式和namaspace的方式基本上是一致的,所以我就直接贴代码:

    pod:=new(v1.Pod)
    pod.TypeMeta=unversioned.TypeMeta{Kind: "Pod", APIVersion: "v1"}
    pod.ObjectMeta=v1.ObjectMeta{Name: app.Name, Namespace: app.UserName, Labels: map[string]string{"name": app.Name}}
    pod.Spec=v1.PodSpec{
                RestartPolicy: v1.RestartPolicyAlways,
                Containers: []v1.Container{
                    v1.Container{
                        Name:  app.Name,
                        Image: app.Image,
                        Ports: []v1.ContainerPort{
                            v1.ContainerPort{
                                ContainerPort: 9080,
                                Protocol:      v1.ProtocolTCP,
                            },
                        },
                        Resources: v1.ResourceRequirements{
                            Requests: v1.ResourceList{
                                v1.ResourceCPU:    resource.MustParse(app.Cpu),
                                v1.ResourceMemory: resource.MustParse(app.Memory),
                            },
                        },
                    },
                },
            }
    result, err := dao.Clientset.Core().Pods(NameSpace).Create(pod)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

创建replicationController

//create a replicationController
    rc := new(v1.ReplicationController)

    rcTypeMeta := unversioned.TypeMeta{Kind: "ReplicationController", APIVersion: "v1"}
    rc.TypeMeta = rcTypeMeta

    rcObjectMeta := v1.ObjectMeta{Name: app.Name, Namespace: app.UserName, Labels: map[string]string{"name": app.Name}}
    rc.ObjectMeta = rcObjectMeta

    rcSpec := v1.ReplicationControllerSpec{
        Replicas: &app.InstanceCount,
        Selector: map[string]string{
            "name": app.Name,
        },
        Template: &v1.PodTemplateSpec{
            v1.ObjectMeta{
                Name:      app.Name,
                Namespace: app.UserName,
                Labels: map[string]string{
                    "name": app.Name,
                },
            },
            v1.PodSpec{
                RestartPolicy: v1.RestartPolicyAlways,
                Containers: []v1.Container{
                    v1.Container{
                        Name:  app.Name,
                        Image: app.Image,
                        Ports: []v1.ContainerPort{
                            v1.ContainerPort{
                                ContainerPort: 9080,
                                Protocol:      v1.ProtocolTCP,
                            },
                        },
                        Resources: v1.ResourceRequirements{
                            Requests: v1.ResourceList{
                                v1.ResourceCPU:    resource.MustParse(app.Cpu),
                                v1.ResourceMemory: resource.MustParse(app.Memory),
                            },
                        },
                    },
                },
            },
        },
    }
    rc.Spec = rcSpec

    result, err := dao.Clientset.Core().ReplicationControllers(NameSpace).Create(rc)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

创建service

//create service
        service := new(v1.Service)

        svTypemeta := unversioned.TypeMeta{Kind: "Service", APIVersion: "v1"}
        service.TypeMeta = svTypemeta

        svObjectMeta := v1.ObjectMeta{Name: app.Name, Namespace: app.UserName, Labels: map[string]string{"name": app.Name}}
        service.ObjectMeta = svObjectMeta

        svServiceSpec := v1.ServiceSpec{
            Ports: []v1.ServicePort{
                v1.ServicePort{
                    Name:       app.Name,
                    Port:       9080,
                    TargetPort: intstr.FromInt(9080),
                    Protocol:   "TCP",
                    // NodePort:   32107,
                },
            },
            Selector: map[string]string{"name": app.Name},
            Type:     v1.ServiceTypeNodePort,
            // LoadBalancerIP: "172.17.11.2",
            // Status: v1.ServiceStatus{
            //  LoadBalancer: v1.LoadBalancerStatus{
            //      Ingress: []v1.LoadBalancerIngress{
            //          v1.LoadBalancerIngress{IP: "172.17.11.2"},
            //      },
            //  },
            // },
        }
        service.Spec = svServiceSpec

        _, err := dao.Clientset.Core().Services(NameSpace).Create(service)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值