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)