This is an automated email from the ASF dual-hosted git repository.
friede pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficcontrol.git
The following commit(s) were added to refs/heads/master by this push:
new b31e00330d Add TM integration and separate docker-compose services for
Varnish (#7746)
b31e00330d is described below
commit b31e00330d6d3735d812cfeb737ff86c28c070c8
Author: AbdelrahmanElawady
<[email protected]>
AuthorDate: Sat Sep 9 01:11:11 2023 +0300
Add TM integration and separate docker-compose services for Varnish (#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
---
cache-config/t3c-apply/torequest/torequest.go | 8 +-
..._ops_ort.crontab => docker-compose.varnish.yml} | 19 +++-
.../profiles/011-VARNISH_EDGE_TIER_CACHE.json | 8 +-
.../profiles/021-VARNISH_MID_TIER_CACHE.json | 8 +-
infrastructure/cdn-in-a-box/varnish/Dockerfile | 9 ++
infrastructure/cdn-in-a-box/varnish/run.sh | 1 +
.../cdn-in-a-box/varnish/traffic_ops_ort.crontab | 2 +-
infrastructure/cdn-in-a-box/varnish/vstats.go | 112 +++++++++++++++++++++
traffic_monitor/cache/vstats.go | 79 +++++++++++++++
traffic_monitor/cache/vstats_test.go | 60 +++++++++++
10 files changed, 300 insertions(+), 6 deletions(-)
diff --git a/cache-config/t3c-apply/torequest/torequest.go
b/cache-config/t3c-apply/torequest/torequest.go
index ba9b443682..6da59c2657 100644
--- a/cache-config/t3c-apply/torequest/torequest.go
+++ b/cache-config/t3c-apply/torequest/torequest.go
@@ -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 {
@@ -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)
@@ -535,6 +537,7 @@ func (r *TrafficOpsReq) replaceCfgFile(cfg *ConfigFile)
(*FileRestartData, error
TrafficServerRestart: trafficServerRestart,
RemapConfigReload: remapConfigReload,
HitchReload: hitchReload,
+ VarnishReload: varnishReload,
},
}, nil
}
@@ -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
}
@@ -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
}
@@ -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 {
diff --git a/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
b/infrastructure/cdn-in-a-box/docker-compose.varnish.yml
similarity index 64%
copy from infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
copy to infrastructure/cdn-in-a-box/docker-compose.varnish.yml
index d830ed0062..f79ba0cff9 100644
--- a/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
+++ b/infrastructure/cdn-in-a-box/docker-compose.varnish.yml
@@ -14,5 +14,22 @@
# 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'
-*/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
+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
\ No newline at end of file
diff --git
a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json
b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json
index 16fb280843..e6abc723bd 100644
---
a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json
+++
b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/011-VARNISH_EDGE_TIER_CACHE.json
@@ -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",
diff --git
a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json
b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json
index 35d7051d82..b095b97f71 100644
---
a/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json
+++
b/infrastructure/cdn-in-a-box/traffic_ops_data/profiles/021-VARNISH_MID_TIER_CACHE.json
@@ -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",
diff --git a/infrastructure/cdn-in-a-box/varnish/Dockerfile
b/infrastructure/cdn-in-a-box/varnish/Dockerfile
index 9b5840d7b5..e6f89188e4 100644
--- a/infrastructure/cdn-in-a-box/varnish/Dockerfile
+++ b/infrastructure/cdn-in-a-box/varnish/Dockerfile
@@ -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
@@ -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
diff --git a/infrastructure/cdn-in-a-box/varnish/run.sh
b/infrastructure/cdn-in-a-box/varnish/run.sh
index eb9ccd4f62..4e68f1aafe 100755
--- a/infrastructure/cdn-in-a-box/varnish/run.sh
+++ b/infrastructure/cdn-in-a-box/varnish/run.sh
@@ -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"
diff --git a/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
b/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
index d830ed0062..c47d08376d 100644
--- a/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
+++ b/infrastructure/cdn-in-a-box/varnish/traffic_ops_ort.crontab
@@ -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
diff --git a/infrastructure/cdn-in-a-box/varnish/vstats.go
b/infrastructure/cdn-in-a-box/varnish/vstats.go
new file mode 100644
index 0000000000..10c871717f
--- /dev/null
+++ b/infrastructure/cdn-in-a-box/varnish/vstats.go
@@ -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)
+ }
+}
diff --git a/traffic_monitor/cache/vstats.go b/traffic_monitor/cache/vstats.go
new file mode 100644
index 0000000000..c977dedc9c
--- /dev/null
+++ b/traffic_monitor/cache/vstats.go
@@ -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{}}
+}
diff --git a/traffic_monitor/cache/vstats_test.go
b/traffic_monitor/cache/vstats_test.go
new file mode 100644
index 0000000000..d3f20aa6bc
--- /dev/null
+++ b/traffic_monitor/cache/vstats_test.go
@@ -0,0 +1,60 @@
+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 (
+ "strings"
+ "testing"
+)
+
+var vstatsData = `{
+ "proc.net.dev": "eth0:47907832129 14601260 0 0 0 0
0 790726 728207677726 10210700052 0 0 0 0 0 0",
+ "proc.loadavg": "0.30 0.12 0.21 803/863 1421",
+ "not_available": false,
+ "inf_speed": 70000,
+ "stats": {}
+}
+`
+
+func TestVstatsParse(t *testing.T) {
+ reader := strings.NewReader(vstatsData)
+ systemStats, statistics, err := vstatsParse("test", reader, nil)
+ if err != nil {
+ t.Errorf("got error %s", err)
+ }
+ // stats not implemented yet
+ if len(statistics) != 0 {
+ t.Errorf("expected statistics to be empty found %v", statistics)
+ }
+ load := Loadavg{One: 0.3, Five: 0.12, Fifteen: 0.21, CurrentProcesses:
803, TotalProcesses: 863, LatestPID: 1421}
+ inf := Interface{Speed: 70000, BytesOut: 728207677726, BytesIn:
47907832129}
+ if systemStats.Loadavg != load {
+ t.Errorf("got %v want %v", systemStats.Loadavg, load)
+ }
+ if len(systemStats.Interfaces) != 1 {
+ t.Errorf("expected 1 interface got %v", systemStats.Interfaces)
+ }
+ if systemStats.Interfaces["eth0"] != inf {
+ t.Errorf("got %v want %v", systemStats.Interfaces["eth0"], inf)
+ }
+ if systemStats.NotAvailable {
+ t.Errorf("expected NotAvailable to be false")
+ }
+}