diff --git a/bcs-services/bcs-logbeat-sidecar/app/app.go b/bcs-services/bcs-logbeat-sidecar/app/app.go index 7fde0dd874..f44d319dc2 100644 --- a/bcs-services/bcs-logbeat-sidecar/app/app.go +++ b/bcs-services/bcs-logbeat-sidecar/app/app.go @@ -54,4 +54,6 @@ func setConfig(conf *config.Config, op *options.SidecarOption) { conf.TemplateFile = op.TemplateFile conf.LogbeatDir = op.LogbeatDir conf.Kubeconfig = op.Kubeconfig + conf.EvalSymlink = op.EvalSymlink + conf.LogbeatPIDFilePath = op.LogbeatPIDFilePath } diff --git a/bcs-services/bcs-logbeat-sidecar/app/options/options.go b/bcs-services/bcs-logbeat-sidecar/app/options/options.go index c7e6187794..a0b796f21d 100644 --- a/bcs-services/bcs-logbeat-sidecar/app/options/options.go +++ b/bcs-services/bcs-logbeat-sidecar/app/options/options.go @@ -28,11 +28,13 @@ type SidecarOption struct { conf.LogConfig conf.ProcessConfig - DockerSock string `json:"docker_sock" value:"unix:///var/run/docker.sock" usage:"docker socket file"` - LogbeatDir string `json:"logbeat_dir" value:"" usage:"logbeat config directory"` - TemplateFile string `json:"template_file" value:"./unifytlogc-template.conf" usage:"logbeat tempalte config file"` - PrefixFile string `json:"prefix_file" value:"" usage:"logbeat config file prefix name"` - Kubeconfig string `json:"kubeconfig" value:"" usage:"kubeconfig"` + DockerSock string `json:"docker_sock" value:"unix:///var/run/docker.sock" usage:"docker socket file"` + LogbeatDir string `json:"logbeat_dir" value:"" usage:"logbeat config directory"` + TemplateFile string `json:"template_file" value:"./unifytlogc-template.conf" usage:"logbeat tempalte config file"` + PrefixFile string `json:"prefix_file" value:"" usage:"logbeat config file prefix name"` + Kubeconfig string `json:"kubeconfig" value:"" usage:"kubeconfig"` + EvalSymlink bool `json:"eval_symlink" value:"false" usage:"whether to enable remove symbol link in the log path"` + LogbeatPIDFilePath string `json:"logbeat_pid_file_path" value:"" usage:"logbeat PID file path, which is used to reload logbeat"` } //NewSidecarOption create SidecarOption object diff --git a/bcs-services/bcs-logbeat-sidecar/config/config.go b/bcs-services/bcs-logbeat-sidecar/config/config.go index 6e2d34550c..5cbdeaf9d0 100644 --- a/bcs-services/bcs-logbeat-sidecar/config/config.go +++ b/bcs-services/bcs-logbeat-sidecar/config/config.go @@ -20,6 +20,11 @@ type Config struct { PrefixFile string //kube-apiserver config file path Kubeconfig string + // whether to enable remove symbol link in the log path + // this should be false if deployed as in-cluster mode + EvalSymlink bool + // logbeat PID file path + LogbeatPIDFilePath string } //NewConfig create a config object diff --git a/bcs-services/bcs-logbeat-sidecar/go.mod b/bcs-services/bcs-logbeat-sidecar/go.mod index 101205f749..27bc82fe7b 100644 --- a/bcs-services/bcs-logbeat-sidecar/go.mod +++ b/bcs-services/bcs-logbeat-sidecar/go.mod @@ -16,10 +16,15 @@ replace ( require ( github.com/Tencent/bk-bcs v0.0.0-20200805130634-8a6c639f4a4c + github.com/containerd/containerd v1.4.3 // indirect + github.com/docker/docker v20.10.0-rc1+incompatible // indirect github.com/fsouza/go-dockerclient v1.6.5 + github.com/moby/sys/mount v0.2.0 // indirect github.com/prometheus/client_golang v1.7.1 github.com/prometheus/client_model v0.2.0 + gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/yaml.v2 v2.3.0 + gotest.tools/v3 v3.0.3 // indirect k8s.io/api v0.18.6 k8s.io/apiextensions-apiserver v0.18.6 k8s.io/apimachinery v0.18.6 diff --git a/bcs-services/bcs-logbeat-sidecar/metric/logfileinfo_guage.go b/bcs-services/bcs-logbeat-sidecar/metric/logfileinfo_guage.go index 2bccfb5344..eaea29f217 100644 --- a/bcs-services/bcs-logbeat-sidecar/metric/logfileinfo_guage.go +++ b/bcs-services/bcs-logbeat-sidecar/metric/logfileinfo_guage.go @@ -23,7 +23,8 @@ import ( ) var ( - logFileInfoGaugeLabelNames = []string{"clusterID", "crdName", "crdNamespace", "hostIP", "containerID", "podName", "podNamespace", "workloadType", "workloadName", "workloadNamespace"} + logFileInfoGaugeLabelNames = []string{"clusterID", "crdName", "crdNamespace", "hostIP", "containerID", + "podName", "podNamespace", "workloadType", "workloadName", "workloadNamespace"} // LogFileInfoGauge is container log collection task Gauge metric LogFileInfoGauge *prometheus.GaugeVec ) @@ -47,7 +48,8 @@ func init() { LogFileInfoGauge = prometheus.NewGaugeVec( prometheus.GaugeOpts{ Name: "logbeat_sidecar_logfile_gen_info", - Help: "logbeat_sidecar_logfile_gen_info has labels describing the infomation of logfile and gauge value 0/1 corresponds to (logfile generated/logfile does not generated)", + Help: "logbeat_sidecar_logfile_gen_info has labels describing the information" + + " of logfile and gauge value 0/1 corresponds to (logfile generated/logfile does not generated)", }, logFileInfoGaugeLabelNames, ) diff --git a/bcs-services/bcs-logbeat-sidecar/sidecar/controller.go b/bcs-services/bcs-logbeat-sidecar/sidecar/controller.go index fd446a4284..8005baa97e 100644 --- a/bcs-services/bcs-logbeat-sidecar/sidecar/controller.go +++ b/bcs-services/bcs-logbeat-sidecar/sidecar/controller.go @@ -32,6 +32,7 @@ import ( docker "github.com/fsouza/go-dockerclient" "gopkg.in/yaml.v2" apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + "k8s.io/apimachinery/pkg/labels" corev1 "k8s.io/client-go/listers/core/v1" "k8s.io/client-go/tools/cache" ) @@ -171,13 +172,14 @@ func (s *SidecarController) listenerDockerEvent() { } func (s *SidecarController) syncLogConfs() { + var hostIP string //list all running containers apiContainers, err := s.client.ListContainers(docker.ListContainersOptions{All: true}) if err != nil { blog.Errorf("docker ListContainers failed: %s", err.Error()) return } - + // generate container log config for _, apiC := range apiContainers { c, err := s.client.InspectContainer(apiC.ID) if err != nil { @@ -186,10 +188,43 @@ func (s *SidecarController) syncLogConfs() { } s.produceContainerLogConf(c) + //Get host IP + if hostIP != "" { + continue + } + name := c.Config.Labels[ContainerLabelK8sContainerName] + if name == "POD" || name == "" { + continue + } + podName := c.Config.Labels[ContainerLabelK8sPodName] + podNameSpace := c.Config.Labels[ContainerLabelK8sPodNameSpace] + pod, err := s.podLister.Pods(podNameSpace).Get(podName) + if err != nil { + blog.Errorf("list pod(%s/%s) failed: %s", podNameSpace, podName, err.Error()) + continue + } + hostIP = pod.Status.HostIP + } + + // generate host log config + bcsLogConfigs, err := s.bcsLogConfigLister.List(labels.Everything()) + if err != nil { + blog.Errorf("list bcslogconfig error %s", err.Error()) + return + } + for _, conf := range bcsLogConfigs { + if conf.Spec.ConfigType == bcsv1.HostConfigType { + s.produceHostLogConf(conf, hostIP) + } } //remove invalid logconfig file s.removeInvalidLogConfigFile() + err = s.reloadLogbeat() + if err != nil { + blog.Errorf("reload logbeat failed: %s", err.Error()) + } + blog.V(3).Infof("reload logbeat succ") } func (s *SidecarController) removeInvalidLogConfigFile() { @@ -202,9 +237,9 @@ func (s *SidecarController) removeInvalidLogConfigFile() { for _, o := range files { confKey := fmt.Sprintf("%s/%s", s.conf.LogbeatDir, o.Name()) s.RLock() - _, ok := s.logConfs[confKey] + config, ok := s.logConfs[confKey] s.RUnlock() - if ok { + if ok && config.yamlData != nil { continue } err := os.Remove(confKey) @@ -243,10 +278,18 @@ func (s *SidecarController) getContainerLogConfKey(containerID string) string { return fmt.Sprintf("%s/%s-%s.yaml", s.conf.LogbeatDir, s.prefixFile, []byte(containerID)[:12]) } +func (s *SidecarController) getHostLogConfKey(logConf *bcsv1.BcsLogConfig) string { + return fmt.Sprintf("%s/%s-%s-%s.yaml", s.conf.LogbeatDir, s.prefixFile, logConf.GetNamespace(), logConf.GetName()) +} + +func (s *SidecarController) getBCSLogConfigKey(logConf *bcsv1.BcsLogConfig) string { + return fmt.Sprintf("%s/%s", logConf.Namespace, logConf.Name) +} + func (s *SidecarController) produceContainerLogConf(c *docker.Container) { key := s.getContainerLogConfKey(c.ID) y, ok := s.produceLogConfParameterV2(c) - by, _ := yaml.Marshal(y) + //the container don't match any BcsLogConfig if !ok { s.Lock() @@ -259,13 +302,71 @@ func (s *SidecarController) produceContainerLogConf(c *docker.Container) { } return } + s.writeLogConfFile(key, y) +} + +func (s *SidecarController) produceHostLogConf(logConf *bcsv1.BcsLogConfig, hostIP string) { + if logConf.Spec.NonStdDataId == "" || len(logConf.Spec.LogPaths) == 0 { + blog.Errorf("host logconfig(%+v) didn't set NonStdDataId or LogPaths", logConf) + return + } + y := &types.Yaml{ + Local: make([]types.Local, 0), + BCSLogConfigKey: s.getBCSLogConfigKey(logConf), + } + para := types.Local{ + ToJSON: true, + ExtMeta: make(map[string]string), + Paths: make([]string, 0), + } + para.ExtMeta["io_tencent_bcs_cluster"] = logConf.Spec.ClusterId + para.ExtMeta["io_tencent_bcs_appid"] = logConf.Spec.AppId + //custom log tags + for k, v := range logConf.Spec.LogTags { + para.ExtMeta[k] = v + } + dataid, err := strconv.Atoi(logConf.Spec.NonStdDataId) + if err != nil { + blog.Warnf("logconfig(%+v) has wrong type of NonStdDataID(%s): %s", logConf, logConf.Spec.NonStdDataId, err.Error()) + return + } + para.DataID = dataid + for _, f := range logConf.Spec.LogPaths { + if !filepath.IsAbs(f) { + blog.Errorf("host logconf path specified as \"%s\" is not an absolute path", f) + continue + } + para.Paths = append(para.Paths, s.getCleanPath(f)) + } + y.Local = append(y.Local, para) + // construct log file metric info + y.Metric = &metric.LogFileInfoType{ + ClusterID: strings.ToLower(logConf.Spec.ClusterId), + CRDName: logConf.GetName(), + CRDNamespace: logConf.GetNamespace(), + HostIP: hostIP, + } + s.writeLogConfFile(s.getHostLogConfKey(logConf), y) +} + +func (s *SidecarController) writeLogConfFile(key string, y *types.Yaml) { + by, _ := yaml.Marshal(y) + // get container id + var cid string + if y.Metric != nil { + cid = y.Metric.ContainerID + } //if log config exist, and not changed s.RLock() logConf, _ := s.logConfs[key] s.RUnlock() if logConf != nil { + if logConf.yamlData != nil && logConf.yamlData.BCSLogConfigKey != "" && logConf.yamlData.BCSLogConfigKey != y.BCSLogConfigKey { + blog.Errorf("Unexpected conflict config detected: BcsLogConfig %s and %s define log config for the same container(%s)", + logConf.yamlData.BCSLogConfigKey, y.BCSLogConfigKey, cid) + } if string(by) == string(logConf.data) { - blog.Infof("container %s log config %s not changed", c.ID, logConf.confPath) + blog.Infof("container %s or host log config %s not changed", cid, logConf.confPath) if logConf.yamlData == nil { logConf.yamlData = y if err := y.Metric.Renew(); err != nil { @@ -273,12 +374,10 @@ func (s *SidecarController) produceContainerLogConf(c *docker.Container) { } } return - } else { - blog.Infof("container %s log config %s changed, from(%s)->to(%s)", c.ID, logConf.confPath, string(logConf.data), string(by)) } - blog.Infof("container %s log config %s changed, from(%s)->to(%s)", c.ID, logConf.confPath, string(logConf.data), string(by)) + blog.Infof("container %s or host log config %s changed, from(%s)->to(%s)", cid, logConf.confPath, string(logConf.data), string(by)) } else { - blog.Infof("container %s log config %s will created, and LogConfig(%s)", c.ID, key, string(by)) + blog.Infof("container %s or host log config %s will created, and LogConfig(%s)", cid, key, string(by)) } newlogConf := &ContainerLogConf{ @@ -288,17 +387,17 @@ func (s *SidecarController) produceContainerLogConf(c *docker.Container) { } f, err := os.Create(newlogConf.confPath) if err != nil { - blog.Errorf("container %s open file %s failed: %s", c.ID, newlogConf.confPath, err.Error()) + blog.Errorf("container %s or host open file %s failed: %s", cid, newlogConf.confPath, err.Error()) return } defer f.Close() _, err = f.Write(by) if err != nil { - blog.Errorf("container %s tempalte execute failed: %s", c.ID, err.Error()) + blog.Errorf("container %s or host tempalte execute failed: %s", cid, err.Error()) return } - blog.Infof("produce container %s log config %s success", c.ID, newlogConf.confPath) + blog.Infof("produce container %s or host log config %s success", cid, newlogConf.confPath) // Set/Update metric if logConf == nil || logConf.yamlData == nil { err := y.Metric.Set(1) @@ -314,7 +413,6 @@ func (s *SidecarController) produceContainerLogConf(c *docker.Container) { s.Lock() s.logConfs[key] = newlogConf s.Unlock() - return } func (s *SidecarController) deleteContainerLogConf(containerID string) { @@ -359,81 +457,93 @@ func (s *SidecarController) produceLogConfParameterV2(container *docker.Containe return nil, false } - para := types.Local{ - ExtMeta: make(map[string]string), - NonstandardPaths: make([]string, 0), - } - para.ExtMeta["io_tencent_bcs_cluster"] = logConf.Spec.ClusterId - para.ExtMeta["io_tencent_bcs_pod"] = pod.Name - para.ExtMeta["io_tencent_bcs_namespace"] = pod.Namespace - para.ExtMeta["io_tencent_bcs_server_name"] = pod.OwnerReferences[0].Name - para.ExtMeta["io_tencent_bcs_type"] = pod.OwnerReferences[0].Kind - para.ExtMeta["io_tencent_bcs_appid"] = logConf.Spec.AppId - para.ExtMeta["io_tencent_bcs_projectid"] = pod.Labels["io.tencent.paas.projectid"] - para.ExtMeta["container_id"] = container.ID - para.ExtMeta["container_hostname"] = container.Config.Hostname - para.ToJSON = true - containerRootPath := s.getContainerRootPath(container) - var matchedLogConfig bcsv1.ContainerConf + var matchedLogConfigs = make([]*bcsv1.ContainerConf, 0) if len(logConf.Spec.ContainerConfs) > 0 { for _, conf := range logConf.Spec.ContainerConfs { if conf.ContainerName == name { - conf.DeepCopyInto(&matchedLogConfig) - break + matchedConf := conf.DeepCopy() + matchedLogConfigs = append(matchedLogConfigs, matchedConf) } } } else { + var matchedLogConfig bcsv1.ContainerConf matchedLogConfig.StdDataId = logConf.Spec.StdDataId matchedLogConfig.NonStdDataId = logConf.Spec.NonStdDataId matchedLogConfig.LogPaths = logConf.Spec.LogPaths matchedLogConfig.HostPaths = logConf.Spec.HostPaths matchedLogConfig.LogTags = logConf.Spec.LogTags - } - // generate intermediate config - para.StdoutDataid = matchedLogConfig.StdDataId - para.NonstandardDataid = matchedLogConfig.NonStdDataId - for _, f := range matchedLogConfig.LogPaths { - if !filepath.IsAbs(f) { - blog.Errorf("log path specified as \"%s\" is not an absolute path", f) + matchedLogConfigs = append(matchedLogConfigs, &matchedLogConfig) + } + + y := &types.Yaml{ + Local: make([]types.Local, 0), + BCSLogConfigKey: s.getBCSLogConfigKey(logConf), + } + var stdoutDataid = "" + for _, conf := range matchedLogConfigs { + var para = types.Local{ + ExtMeta: make(map[string]string), + NonstandardPaths: make([]string, 0), + Paths: make([]string, 0), + ToJSON: true, + StdoutDataid: conf.StdDataId, + NonstandardDataid: conf.NonStdDataId, + } + para.ExtMeta["io_tencent_bcs_cluster"] = logConf.Spec.ClusterId + para.ExtMeta["io_tencent_bcs_pod"] = pod.Name + para.ExtMeta["io_tencent_bcs_namespace"] = pod.Namespace + para.ExtMeta["io_tencent_bcs_server_name"] = pod.OwnerReferences[0].Name + para.ExtMeta["io_tencent_bcs_type"] = pod.OwnerReferences[0].Kind + para.ExtMeta["io_tencent_bcs_appid"] = logConf.Spec.AppId + para.ExtMeta["io_tencent_bcs_projectid"] = pod.Labels["io.tencent.paas.projectid"] + para.ExtMeta["container_id"] = container.ID + para.ExtMeta["container_hostname"] = container.Config.Hostname + //whether report pod labels to log tags + if logConf.Spec.PodLabels { + for k, v := range pod.Labels { + para.ExtMeta[k] = v + } + } + //custom log tags + for k, v := range conf.LogTags { + para.ExtMeta[k] = v + } + // generate std output log collection config + if stdoutDataid == "" && conf.StdDataId != "" { + stdPara := para + id, err := strconv.Atoi(conf.StdDataId) + if err != nil { + blog.Errorf("Convert dataid from string(%s) to int failed: %s, BcsLogConfig(%+v)", conf.StdDataId, err.Error(), logConf) + continue + } else { + stdPara.DataID = id + stdPara.Paths = []string{container.LogPath} + y.Local = append(y.Local, stdPara) + stdoutDataid = conf.StdDataId + } + } + if conf.NonStdDataId == "" { continue } - para.NonstandardPaths = append(para.NonstandardPaths, fmt.Sprintf("%s%s", containerRootPath, f)) - } - for _, f := range matchedLogConfig.HostPaths { - if !filepath.IsAbs(f) { - blog.Errorf("host path specified as \"%s\" is not an absolute path", f) + // generate non std output log collection config + id, err := strconv.Atoi(conf.NonStdDataId) + if err != nil { + blog.Errorf("Convert dataid from string(%s) to int failed: %s, BcsLogConfig(%+v)", conf.NonStdDataId, err.Error(), logConf) continue } - para.NonstandardPaths = append(para.NonstandardPaths, f) - } - para.LogTags = matchedLogConfig.LogTags - //whether report pod labels to log tags - if logConf.Spec.PodLabels { - for k, v := range pod.Labels { - para.ExtMeta[k] = v + para.DataID = id + for _, f := range conf.LogPaths { + actualPath, err := s.getActualPath(f, container) + if err != nil { + blog.Errorf("get actual path of %s with container (%+v) failed: %s", f, container, err.Error()) + continue + } + para.Paths = append(para.Paths, actualPath) } - } - //custom log tags - for k, v := range para.LogTags { - para.ExtMeta[k] = v - } - - y := &types.Yaml{Local: make([]types.Local, 0)} - //if stdout container log - if para.StdoutDataid != "" { - inLocal := para - inLocal.Paths = []string{container.LogPath} - i, _ := strconv.Atoi(para.StdoutDataid) - inLocal.DataID = i - y.Local = append(y.Local, inLocal) - } - //if nonstandard Log - if para.NonstandardDataid != "" && len(para.NonstandardPaths) > 0 { - inLocal := para - inLocal.Paths = para.NonstandardPaths - i, _ := strconv.Atoi(para.NonstandardDataid) - inLocal.DataID = i - y.Local = append(y.Local, inLocal) + if len(para.Paths) == 0 { + continue + } + y.Local = append(y.Local, para) } // construct log file metric info @@ -453,13 +563,46 @@ func (s *SidecarController) produceLogConfParameterV2(container *docker.Containe return y, true } +func (s *SidecarController) getCleanPath(path string) string { + if !s.conf.EvalSymlink { + return path + } + runes := []rune(path) + wildcardPos := s.getFirstWildcardPos(path) + slashPos := strings.LastIndex(string(runes[:wildcardPos]), string(os.PathSeparator)) + cleanPath, err := filepath.EvalSymlinks(string(runes[:(slashPos + 1)])) + if err != nil { + blog.Warnf("EvalSymlinks of path %s failed: %s", string(runes[:(slashPos+1)]), err.Error()) + } else { + path = cleanPath + string(runes[slashPos:]) + } + return path +} + +func (s *SidecarController) getFirstWildcardPos(str string) int { + var pos = len(str) + ind := strings.Index(str, "*") + if ind != -1 && ind < pos { + pos = ind + } + ind = strings.Index(str, "[") + if ind != -1 && ind < pos { + pos = ind + } + ind = strings.Index(str, "?") + if ind != -1 && ind < pos { + pos = ind + } + return pos +} + // getContainerRootPath return the root path of the container // Usually it begins with /data/bcs/lib/docker/overlay2/{hashid}/merged // If the container does not use OverlayFS, it will return /proc/{procid}/root func (s *SidecarController) getContainerRootPath(container *docker.Container) string { switch container.Driver { - // case "overlay2": - // return container.GraphDriver.Data["MergedDir"] + case "overlay2": + return container.GraphDriver.Data["MergedDir"] default: // blog.Warnf("Container %s has driver %s not overlay2", container.ID, container.Driver) return fmt.Sprintf("/proc/%d/root", container.State.Pid) diff --git a/bcs-services/bcs-logbeat-sidecar/sidecar/logconfig.go b/bcs-services/bcs-logbeat-sidecar/sidecar/logconfig.go index 03d490ab24..7df2603c00 100644 --- a/bcs-services/bcs-logbeat-sidecar/sidecar/logconfig.go +++ b/bcs-services/bcs-logbeat-sidecar/sidecar/logconfig.go @@ -89,9 +89,9 @@ func (s *SidecarController) initKubeconfig() error { //add k8s resources event handler functions s.bcsLogConfigInformer.AddEventHandler( cache.ResourceEventHandlerFuncs{ - AddFunc: s.handleChangedBcsLogConfig, + AddFunc: s.handleAddedBcsLogConfig, UpdateFunc: s.handleUpdatedBcsLogConfig, - DeleteFunc: s.handleChangedBcsLogConfig, + DeleteFunc: s.handleDeletedBcsLogConfig, }, ) blog.Infof("build internalClientset for config %s success", s.conf.Kubeconfig) @@ -172,6 +172,10 @@ func (s *SidecarController) getPodLogConfigCrd(container *docker.Container, pod //BcsLogConfig parameter ContainerName matched, increased 10 score //finally, the above scores will be accumulated to be the BcsLogConfig final score func scoreBcsLogConfig(container *docker.Container, pod *corev1.Pod, bcsLogConf *bcsv1.BcsLogConfig) int { + // do not select ConfigType == host + if bcsLogConf.Spec.ConfigType == bcsv1.HostConfigType { + return 0 + } // selector match podLabelSet := apilabels.Set(pod.GetLabels()) podSelector, err := buildSelector(bcsLogConf.Spec.Selector) @@ -275,14 +279,28 @@ func scoreBcsLogConfig(container *docker.Container, pod *corev1.Pod, bcsLogConf return score } -func (s *SidecarController) handleChangedBcsLogConfig(obj interface{}) { +func (s *SidecarController) handleAddedBcsLogConfig(obj interface{}) { + conf, ok := obj.(*bcsv1.BcsLogConfig) + if !ok { + blog.Errorf("cannot convert to *bcsv1.BcsLogConfig: %v", obj) + return + } + by, _ := json.Marshal(conf) + blog.Infof("handle kubernetes Add event BcsLogConfig(%s:%s) data(%s)", conf.Namespace, conf.Name, string(by)) + s.syncLogConfs() +} + +func (s *SidecarController) handleDeletedBcsLogConfig(obj interface{}) { conf, ok := obj.(*bcsv1.BcsLogConfig) if !ok { blog.Errorf("cannot convert to *bcsv1.BcsLogConfig: %v", obj) return } + if conf.Spec.ConfigType == bcsv1.HostConfigType { + delete(s.logConfs, s.getHostLogConfKey(conf)) + } by, _ := json.Marshal(conf) - blog.Infof("handle kubernetes AddOrDelete event BcsLogConfig(%s:%s) data(%s)", conf.Namespace, conf.Name, string(by)) + blog.Infof("handle kubernetes Delete event BcsLogConfig(%s:%s) data(%s)", conf.Namespace, conf.Name, string(by)) s.syncLogConfs() } diff --git a/bcs-services/bcs-logbeat-sidecar/sidecar/logconfop_linux.go b/bcs-services/bcs-logbeat-sidecar/sidecar/logconfop_linux.go new file mode 100644 index 0000000000..41ae2fd211 --- /dev/null +++ b/bcs-services/bcs-logbeat-sidecar/sidecar/logconfop_linux.go @@ -0,0 +1,51 @@ +// +build linux +/* +* Tencent is pleased to support the open source community by making Blueking Container Service available. +* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. +* Licensed under the MIT License (the "License"); you may not use this file except +* in compliance with the License. You may obtain a copy of the License at +* http://opensource.org/licenses/MIT +* Unless required by applicable law or agreed to in writing, software distributed under +* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +* either express or implied. See the License for the specific language governing permissions and +* limitations under the License. +* + */ + +package sidecar + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/Tencent/bk-bcs/bcs-common/common/blog" + docker "github.com/fsouza/go-dockerclient" +) + +func (s *SidecarController) reloadLogbeat() error { + return nil +} + +func (s *SidecarController) getActualPath(logPath string, container *docker.Container) (string, error) { + if !filepath.IsAbs(logPath) { + blog.Errorf("log path specified as \"%s\" is not an absolute path", logPath) + return "", fmt.Errorf("log path specified as \"%s\" is not an absolute path", logPath) + } + var mounted = false + var retpath string + for _, mountinfo := range container.Mounts { + if strings.HasPrefix(logPath, mountinfo.Destination+string(filepath.Separator)) { + mounted = true + retpath = mountinfo.Source + strings.TrimPrefix(logPath, mountinfo.Destination) + break + } + } + if !mounted { + retpath = s.getContainerRootPath(container) + logPath + } + blog.V(3).Infof("origin path: %s, mounted path: %s", logPath, retpath) + retpath = s.getCleanPath(retpath) + blog.V(3).Infof("origin path: %s, clean path: %s", logPath, retpath) + return retpath, nil +} diff --git a/bcs-services/bcs-logbeat-sidecar/sidecar/logconfop_windows.go b/bcs-services/bcs-logbeat-sidecar/sidecar/logconfop_windows.go new file mode 100644 index 0000000000..187e4440d7 --- /dev/null +++ b/bcs-services/bcs-logbeat-sidecar/sidecar/logconfop_windows.go @@ -0,0 +1,71 @@ +// +build windows +/* +* Tencent is pleased to support the open source community by making Blueking Container Service available. +* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved. +* Licensed under the MIT License (the "License"); you may not use this file except +* in compliance with the License. You may obtain a copy of the License at +* http://opensource.org/licenses/MIT +* Unless required by applicable law or agreed to in writing, software distributed under +* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, +* either express or implied. See the License for the specific language governing permissions and +* limitations under the License. +* + */ + +package sidecar + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/Tencent/bk-bcs/bcs-common/common/blog" + docker "github.com/fsouza/go-dockerclient" + "gopkg.in/natefinch/npipe.v2" +) + +const namedPipe = "_win_ipc_pipe" +const logbeatName = "bkunifylogbeat" +const logbeatPidPath = "" +const reloadMsg = "bkreload" + +func (s *SidecarController) reloadLogbeat() error { + // Caution: this is not normall path + // conn, err := npipe.Dial(`\\.\pipe\` + name + namedPipe) + conn, err := npipe.Dial(`\\.\pipe\` + logbeatName + namedPipe + s.conf.LogbeatPIDFilePath) + if err != nil { + return err + } + defer conn.Close() + // send msg + if _, err := fmt.Fprintln(conn, reloadMsg); err != nil { + return err + } + return nil +} + +func (s *SidecarController) getActualPath(logPath string, container *docker.Container) (string, error) { + if !filepath.IsAbs(logPath) { + blog.Errorf("log path specified as \"%s\" is not an absolute path", logPath) + return "", fmt.Errorf("log path specified as \"%s\" is not an absolute path", logPath) + } + var mounted = false + var retpath string + logPath = strings.ToLower(logPath) + for _, mountinfo := range container.Mounts { + destination := strings.ToLower(mountinfo.Destination) + if strings.HasPrefix(logPath, destination+string(filepath.Separator)) { + mounted = true + retpath = mountinfo.Source + strings.TrimPrefix(logPath, destination) + break + } + } + if !mounted { + blog.Errorf("ONLY support MOUNTED container log path in Windows") + return "", fmt.Errorf("ONLY support MOUNTED container log path in Windows") + } + blog.V(3).Infof("origin path: %s, mounted path: %s", logPath, retpath) + retpath = s.getCleanPath(retpath) + blog.V(3).Infof("origin path: %s, clean path: %s", logPath, retpath) + return retpath, nil +} diff --git a/bcs-services/bcs-logbeat-sidecar/types/types.go b/bcs-services/bcs-logbeat-sidecar/types/types.go index 78a6db3e49..6de3f0b25d 100644 --- a/bcs-services/bcs-logbeat-sidecar/types/types.go +++ b/bcs-services/bcs-logbeat-sidecar/types/types.go @@ -19,8 +19,9 @@ import ( // Yaml is the structure viewed of log config file, contains metric info type Yaml struct { - Local []Local `yaml:"local"` - Metric *metric.LogFileInfoType `yaml:"-"` + Local []Local `yaml:"local"` + Metric *metric.LogFileInfoType `yaml:"-"` + BCSLogConfigKey string `yaml:"-"` } // Local is a single log collection task with single dataid diff --git a/bcs-services/bcs-webhook-server/pkg/apis/bk-bcs/v1/bcslogconfig_types.go b/bcs-services/bcs-webhook-server/pkg/apis/bk-bcs/v1/bcslogconfig_types.go index 2f457b0b50..81abac7508 100644 --- a/bcs-services/bcs-webhook-server/pkg/apis/bk-bcs/v1/bcslogconfig_types.go +++ b/bcs-services/bcs-webhook-server/pkg/apis/bk-bcs/v1/bcslogconfig_types.go @@ -28,6 +28,8 @@ const ( BcsSystemConfigType = "bcs-system" // CustomConfigType custom log config type CustomConfigType = "custom" + // HostConfigType is config for host path collection + HostConfigType = "host" ) // BcsLogConfig defines BcsLogConfig CRD format