Skip to content

Commit

Permalink
Add TM integration and separate docker-compose services for Varnish (#…
Browse files Browse the repository at this point in the history
…7746)

* Add TM integration and separate docker-compose services for Varnish

* Add vstats parser tests

* Get interface from TM and remove concurrency

* Remove . and / from interface names
  • Loading branch information
AbdelrahmanElawady committed Sep 8, 2023
1 parent 0410344 commit b31e003
Show file tree
Hide file tree
Showing 10 changed files with 317 additions and 5 deletions.
8 changes: 6 additions & 2 deletions cache-config/t3c-apply/torequest/torequest.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ type RestartData struct {
TrafficServerRestart bool // a trafficserver restart is required
RemapConfigReload bool // remap.config should be reloaded
HitchReload bool // hitch should be reloaded
VarnishReload bool // varnish should be reloaded
}

type ConfigFile struct {
Expand Down Expand Up @@ -522,6 +523,7 @@ func (r *TrafficOpsReq) replaceCfgFile(cfg *ConfigFile) (*FileRestartData, error
ntpdRestart := cfg.Name == "ntpd.conf"
sysCtlReload := cfg.Name == "sysctl.conf"
hitchReload := cfg.Name == "hitch.conf"
varnishReload := cfg.Name == "default.vcl"

log.Debugf("Reload state after %s: remap.config: %t reload: %t restart: %t ntpd: %t sysctl: %t", cfg.Name, remapConfigReload, trafficCtlReload, trafficServerRestart, ntpdRestart, sysCtlReload)

Expand All @@ -535,6 +537,7 @@ func (r *TrafficOpsReq) replaceCfgFile(cfg *ConfigFile) (*FileRestartData, error
TrafficServerRestart: trafficServerRestart,
RemapConfigReload: remapConfigReload,
HitchReload: hitchReload,
VarnishReload: varnishReload,
},
}, nil
}
Expand Down Expand Up @@ -814,6 +817,7 @@ func (r *TrafficOpsReq) CheckReloadRestart(data []FileRestartData) RestartData {
rd.TrafficServerRestart = rd.TrafficServerRestart || changedFile.TrafficServerRestart
rd.RemapConfigReload = rd.RemapConfigReload || changedFile.RemapConfigReload
rd.HitchReload = rd.HitchReload || changedFile.HitchReload
rd.VarnishReload = rd.VarnishReload || changedFile.VarnishReload
}
return rd
}
Expand Down Expand Up @@ -1153,7 +1157,7 @@ func (r *TrafficOpsReq) StartServices(syncdsUpdate *UpdateStatus, metaData *t3cu
// If check-reload does not know about these and we do, then we should initiate
// a reload as well
if serviceNeeds != t3cutil.ServiceNeedsRestart && serviceNeeds != t3cutil.ServiceNeedsReload {
if r.TrafficCtlReload || r.RemapConfigReload {
if r.TrafficCtlReload || r.RemapConfigReload || r.VarnishReload {
log.Infof("ATS config files unchanged, we updated files via t3c-apply, ATS needs reload")
serviceNeeds = t3cutil.ServiceNeedsReload
}
Expand Down Expand Up @@ -1215,7 +1219,7 @@ func (r *TrafficOpsReq) StartServices(syncdsUpdate *UpdateStatus, metaData *t3cu
reloadCommand := config.TSHome + config.TrafficCtl
reloadArgs := []string{"config", "reload"}
if cfg.CacheType == "varnish" {
reloadCommand = "varnishreload"
reloadCommand = "/usr/sbin/varnishreload"
reloadArgs = []string{}
}
if _, _, err := util.ExecCommand(reloadCommand, reloadArgs...); err != nil {
Expand Down
35 changes: 35 additions & 0 deletions infrastructure/cdn-in-a-box/docker-compose.varnish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# 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.
#
# This compose file will run cache servers as Varnish instead of ATS.
#
# docker-compose -f docker-compose.yml -f docker-compose.varnish.yml up
#

---
version: '3.8'

services:
edge:
build:
dockerfile: infrastructure/cdn-in-a-box/varnish/Dockerfile
mid-01:
build:
dockerfile: infrastructure/cdn-in-a-box/varnish/Dockerfile
mid-02:
build:
dockerfile: infrastructure/cdn-in-a-box/varnish/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,17 @@
"secure": false,
"value": "1000"
},
{
"configFile": "rascal.properties",
"name": "health.polling.format",
"secure": false,
"value": "vstats"
},
{
"configFile": "rascal.properties",
"name": "health.polling.url",
"secure": false,
"value": "http://${hostname}/_astats?application=&inf.name=${interface_name}"
"value": "http://${hostname}:2000/?inf.name=${interface_name}"
},
{
"configFile": "storage.config",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,11 +155,17 @@
"secure": false,
"value": "1000"
},
{
"configFile": "rascal.properties",
"name": "health.polling.format",
"secure": false,
"value": "vstats"
},
{
"configFile": "rascal.properties",
"name": "health.polling.url",
"secure": false,
"value": "http://${hostname}/_astats?application=&inf.name=${interface_name}"
"value": "http://${hostname}:2000/?inf.name=${interface_name}"
},
{
"configFile": "storage.config",
Expand Down
9 changes: 9 additions & 0 deletions infrastructure/cdn-in-a-box/varnish/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@

ARG BASE_IMAGE=rockylinux \
RHEL_VERSION=8

FROM golang:1.20-alpine AS vstats-builder

COPY infrastructure/cdn-in-a-box/varnish/vstats.go /

RUN go build -o /vstats /vstats.go

FROM ${BASE_IMAGE}:${RHEL_VERSION} AS common-varnish-cache-config-layers
ARG RHEL_VERSION=8
# Makes RHEL_VERSION available at runtime
Expand Down Expand Up @@ -51,6 +58,8 @@ RUN rpm -Uvh /$(basename $ORT_RPM) &&\

COPY infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab /etc/cron.d/traffic_ops_ort-cron-template

COPY --from=vstats-builder /vstats /usr/local/bin

ENV CACHE_SERVER_TYPE=VARNISH
CMD /run.sh

Expand Down
1 change: 1 addition & 0 deletions infrastructure/cdn-in-a-box/varnish/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ debug_binary="${!debug_variable_name}"
if ! type -p "$debug_binary"; then
t3c apply --cache=varnish --trafficserver-home=/opt/cache --run-mode=badass --traffic-ops-url="$TO_URL" --traffic-ops-user="$TO_USER" --traffic-ops-password="$TO_PASSWORD" --git=yes -vv || { echo "Failed"; }
fi
vstats -port 2000 >> /var/log/vstats.log 2>&1 &

envsubst < "/etc/cron.d/traffic_ops_ort-cron-template" > "/etc/cron.d/traffic_ops_ort-cron" && rm -f "/etc/cron.d/traffic_ops_ort-cron-template"
chmod "0644" "/etc/cron.d/traffic_ops_ort-cron" && crontab "/etc/cron.d/traffic_ops_ort-cron"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,4 @@
# specific language governing permissions and limitations
# under the License.

*/1 * * * * root t3c apply --cache=varnish --run-mode=syncds --traffic-ops-url=$TO_URL --traffic-ops-user=$TO_USER --traffic-ops-password=$TO_PASSWORD --git=yes -vv --cache-host-name=$(hostname -s) >> /var/log/ort.log 2>&1
*/1 * * * * root t3c apply --cache=varnish --run-mode=syncds --traffic-ops-url=$TO_URL --trafficserver-home=/opt/cache --traffic-ops-user=$TO_USER --traffic-ops-password=$TO_PASSWORD --git=yes -vv --cache-host-name=$(hostname -s) >> /var/log/ort.log 2>&1
112 changes: 112 additions & 0 deletions infrastructure/cdn-in-a-box/varnish/vstats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package main

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/

import (
"encoding/json"
"flag"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
)

type vstats struct {
ProcLoadavg string `json:"proc.loadavg"`
ProcNetDev string `json:"proc.net.dev"`
InfSpeed int64 `json:"inf_speed"`
NotAvailable bool `json:"not_available"`
// TODO: stats
}

func getSystemData(inf string) vstats {
var vstats vstats
loadavg, err := os.ReadFile("/proc/loadavg")
if err != nil {
log.Printf("failed to read /proc/loadavg: %s\n", err)
}
vstats.ProcLoadavg = strings.TrimSpace(string(loadavg))

procNetDev, err := os.ReadFile("/proc/net/dev")
if err != nil {
log.Printf("failed to read /proc/net/dev: %s\n", err)
}

parts := strings.Split(string(procNetDev), "\n")
for _, line := range parts {
if strings.HasPrefix(strings.TrimSpace(line), inf) {
vstats.ProcNetDev = strings.TrimSpace(line)
break
}
}

infSpeedFile := fmt.Sprintf("/sys/class/net/%s/speed", inf)
speedStr, err := os.ReadFile(infSpeedFile)
if err != nil {
log.Printf("failed to read %s: %s\n", infSpeedFile, err)
}
speed, err := strconv.ParseInt(strings.TrimSpace(string(speedStr)), 10, 64)
if err != nil {
log.Printf("failed to convert speed '%s' to int: %s\n", speedStr, err)
}
vstats.InfSpeed = speed

cmd := exec.Command("systemctl", "status", "varnish.service")
err = cmd.Run()
if err != nil {
log.Printf("failed to run systemctl: %s\n", err)
}
if cmd.ProcessState.ExitCode() != 0 {
vstats.NotAvailable = true
}
return vstats
}

func getStats(w http.ResponseWriter, r *http.Request) {
inf := r.URL.Query().Get("inf.name")
if inf == "" {
// assume default eth0?
inf = "eth0"
}
inf = strings.ReplaceAll(inf, ".", "")
inf = strings.ReplaceAll(inf, "/", "")
vstats := getSystemData(inf)
encoder := json.NewEncoder(w)
err := encoder.Encode(vstats)
if err != nil {
log.Printf("failed to write Varnish stats: %s", err)
}
}

func main() {
var port int
flag.IntVar(&port, "port", 2000, "port to run vstats on")

flag.Parse()
http.HandleFunc("/", getStats)

listenAddress := fmt.Sprintf(":%d", port)
if err := http.ListenAndServe(listenAddress, nil); err != nil {
log.Printf("server stopped %s", err)
}
}
79 changes: 79 additions & 0 deletions traffic_monitor/cache/vstats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package cache

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*/

import (
"errors"
"fmt"
"io"

"github.com/apache/trafficcontrol/lib/go-log"
"github.com/apache/trafficcontrol/traffic_monitor/todata"
jsoniter "github.com/json-iterator/go"
)

func init() {
registerDecoder("vstats", vstatsParse, vstatsPrecompute)
}

// Vstats holds Varnish cache statistics
type Vstats struct {
ProcNetDev string `json:"proc.net.dev"`
ProcLoadAvg string `json:"proc.loadavg"`
NotAvailable bool `json:"not_available"`
InfSpeed int64 `json:"inf_speed"`
Stats map[string]interface{} `json:"stats"`
}

func vstatsParse(cacheName string, r io.Reader, _ interface{}) (Statistics, map[string]interface{}, error) {
var stats Statistics

if r == nil {
log.Warnf("%s handler got nil reader", cacheName)
return stats, nil, errors.New("handler got nil reader")
}

var vstats Vstats
json := jsoniter.ConfigFastest

if err := json.NewDecoder(r).Decode(&vstats); err != nil {
return stats, nil, fmt.Errorf("failed to decode reader data: %w", err)
}
if err := stats.AddInterfaceFromRawLine(vstats.ProcNetDev); err != nil {
return stats, nil, fmt.Errorf("failed to add interface data %s: %w", vstats.ProcNetDev, err)
}

if loadAvg, err := LoadavgFromRawLine(vstats.ProcLoadAvg); err != nil {
return stats, nil, fmt.Errorf("failed to read average load data %s: %w", vstats.ProcLoadAvg, err)
} else {
stats.Loadavg = loadAvg
}

stats.NotAvailable = vstats.NotAvailable
inf := stats.Interfaces["eth0"]
inf.Speed = vstats.InfSpeed
stats.Interfaces["eth0"] = inf

return stats, vstats.Stats, nil
}

func vstatsPrecompute(cacheName string, data todata.TOData, stats Statistics, miscStats map[string]interface{}) PrecomputedData {
return PrecomputedData{DeliveryServiceStats: map[string]*DSStat{}}
}

0 comments on commit b31e003

Please sign in to comment.