From c5f943fef69c1df44735db4b182df8140c38e0cc Mon Sep 17 00:00:00 2001 From: Oliver Gould Date: Sat, 11 Nov 2023 18:31:28 +0000 Subject: [PATCH] admin: Add optional /debug/pprof/profile endpoint This change introduces a new, optional /debug/pprof/profile endpoint to the proxy's admin server. This endpoint is feature-gated and, initially, it will not be included in release builds. When the feature is enabled, profiling requests are only permitted over the loopback interface. The development Dockerfile is updated to prevent stripping debug symbols so the pprof data is hydrated with debug symbols. --- .github/workflows/check-all.yml | 1 + Cargo.lock | 387 ++++++++++++++++++++++++++++---- Dockerfile | 29 +-- deny.toml | 9 + justfile | 7 +- linkerd/app/Cargo.toml | 1 + linkerd/app/admin/Cargo.toml | 2 + linkerd/app/admin/src/server.rs | 69 +++++- linkerd/app/admin/src/stack.rs | 8 + linkerd/app/src/env.rs | 6 + linkerd2-proxy/Cargo.toml | 1 + 11 files changed, 464 insertions(+), 56 deletions(-) diff --git a/.github/workflows/check-all.yml b/.github/workflows/check-all.yml index 3ab742361b..5051aa1bfb 100644 --- a/.github/workflows/check-all.yml +++ b/.github/workflows/check-all.yml @@ -28,3 +28,4 @@ jobs: - run: git config --global --add safe.directory "$PWD" # actions/runner#2033 - run: just fetch - run: just check --exclude=linkerd-meshtls-boring + - run: just check --exclude=linkerd-meshtls-boring --features=pprof diff --git a/Cargo.lock b/Cargo.lock index b4b26c4d1f..fe1ebf8641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "addr2line" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] + [[package]] name = "adler" version = "1.0.2" @@ -29,9 +38,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.20" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] @@ -81,7 +90,7 @@ checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.12", + "syn 2.0.39", ] [[package]] @@ -135,6 +144,21 @@ dependencies = [ "tower-service", ] +[[package]] +name = "backtrace" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide 0.7.1", + "object", + "rustc-demangle", +] + [[package]] name = "base64" version = "0.13.1" @@ -164,7 +188,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.12", + "syn 2.0.39", ] [[package]] @@ -179,6 +203,15 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "boring" version = "3.0.4" @@ -266,6 +299,24 @@ dependencies = [ "cc", ] +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "cpufeatures" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.3.2" @@ -275,12 +326,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "data-encoding" version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + [[package]] name = "deflate" version = "1.0.0" @@ -311,6 +381,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "drain" version = "0.1.1" @@ -369,6 +449,18 @@ dependencies = [ "instant", ] +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "fixedbitset" version = "0.4.2" @@ -382,7 +474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.6.2", ] [[package]] @@ -499,7 +591,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.12", + "syn 2.0.39", ] [[package]] @@ -532,6 +624,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -543,6 +645,12 @@ dependencies = [ "wasi", ] +[[package]] +name = "gimli" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" + [[package]] name = "glob" version = "0.3.1" @@ -846,9 +954,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.140" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "libfuzzer-sys" @@ -919,6 +1027,7 @@ dependencies = [ "linkerd-app-core", "linkerd-app-inbound", "linkerd-tracing", + "pprof", "serde", "serde_json", "thiserror", @@ -1493,7 +1602,7 @@ dependencies = [ "linkerd-tls", "linkerd2-proxy-api", "pin-project", - "prost", + "prost 0.11.8", "tonic", "tower", "tracing", @@ -1535,7 +1644,7 @@ dependencies = [ "linkerd2-proxy-api", "maplit", "once_cell", - "prost-types", + "prost-types 0.11.8", "quickcheck", "thiserror", "tonic", @@ -1642,7 +1751,7 @@ dependencies = [ "linkerd-http-route", "linkerd2-proxy-api", "maplit", - "prost-types", + "prost-types 0.11.8", "quickcheck", "thiserror", ] @@ -1665,7 +1774,7 @@ dependencies = [ "linkerd2-proxy-api", "parking_lot", "pin-project", - "prost-types", + "prost-types 0.11.8", "quickcheck", "rand", "thiserror", @@ -1764,7 +1873,7 @@ dependencies = [ "linkerd-tonic-watch", "linkerd2-proxy-api", "once_cell", - "prost-types", + "prost-types 0.11.8", "quickcheck", "regex", "thiserror", @@ -1916,8 +2025,8 @@ dependencies = [ "linkerd-error", "linkerd-io", "linkerd-stack", - "prost", - "prost-build", + "prost 0.11.8", + "prost-build 0.11.8", "tokio", "tokio-test", "tracing", @@ -1961,8 +2070,8 @@ dependencies = [ "h2", "http", "ipnet", - "prost", - "prost-types", + "prost 0.11.8", + "prost-types 0.11.8", "quickcheck", "thiserror", "tonic", @@ -2026,7 +2135,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" dependencies = [ - "regex-automata", + "regex-automata 0.1.10", ] [[package]] @@ -2047,6 +2156,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memmap2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a5a03cefb0d953ec0be133036f14e109412fa594edc2f77227249db66cc3ed" +dependencies = [ + "libc", +] + [[package]] name = "mime" version = "0.3.16" @@ -2068,6 +2186,15 @@ dependencies = [ "adler", ] +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + [[package]] name = "mio" version = "0.8.6" @@ -2086,6 +2213,17 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "7.1.0" @@ -2126,6 +2264,15 @@ dependencies = [ "libc", ] +[[package]] +name = "object" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.17.1" @@ -2137,8 +2284,8 @@ name = "opencensus-proto" version = "0.1.0" dependencies = [ "bytes", - "prost", - "prost-types", + "prost 0.11.8", + "prost-types 0.11.8", "tonic", "tonic-build", ] @@ -2242,6 +2389,30 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "pprof" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5c97c51bd34c7e742402e216abdeb44d415fbe6ae41d56b114723e953711cb" +dependencies = [ + "backtrace", + "cfg-if", + "findshlibs", + "libc", + "log", + "nix", + "once_cell", + "parking_lot", + "prost 0.12.1", + "prost-build 0.12.1", + "prost-derive 0.12.1", + "sha2", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -2258,6 +2429,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prettyplease" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae005bd773ab59b4725093fd7df83fd7892f7d8eafb48dbd7de6e024e4215f9d" +dependencies = [ + "proc-macro2", + "syn 2.0.39", +] + [[package]] name = "proc-macro2" version = "1.0.69" @@ -2287,7 +2468,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48e50df39172a3e7eb17e14642445da64996989bc212b583015435d39a58537" dependencies = [ "bytes", - "prost-derive", + "prost-derive 0.11.8", +] + +[[package]] +name = "prost" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4fdd22f3b9c31b53c060df4a0613a1c7f062d4115a2b984dd15b1858f7e340d" +dependencies = [ + "bytes", + "prost-derive 0.12.1", ] [[package]] @@ -2303,15 +2494,37 @@ dependencies = [ "log", "multimap", "petgraph", - "prettyplease", - "prost", - "prost-types", + "prettyplease 0.1.25", + "prost 0.11.8", + "prost-types 0.11.8", "regex", "syn 1.0.109", "tempfile", "which", ] +[[package]] +name = "prost-build" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac" +dependencies = [ + "bytes", + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease 0.2.15", + "prost 0.12.1", + "prost-types 0.12.1", + "regex", + "syn 2.0.39", + "tempfile", + "which", +] + [[package]] name = "prost-derive" version = "0.11.8" @@ -2325,13 +2538,35 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "prost-derive" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.39", +] + [[package]] name = "prost-types" version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "379119666929a1afd7a043aa6cf96fa67a6dce9af60c88095a4686dbce4c9c88" dependencies = [ - "prost", + "prost 0.11.8", +] + +[[package]] +name = "prost-types" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e081b29f63d83a4bc75cfc9f3fe424f9156cf92d8a4f0c9407cce9a1b67327cf" +dependencies = [ + "prost 0.12.1", ] [[package]] @@ -2351,9 +2586,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.26" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -2426,13 +2661,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.7.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" +checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-automata 0.3.7", + "regex-syntax 0.7.5", ] [[package]] @@ -2441,7 +2677,18 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" dependencies = [ - "regex-syntax", + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.7.5", ] [[package]] @@ -2450,6 +2697,12 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + [[package]] name = "resolv-conf" version = "0.7.0" @@ -2475,6 +2728,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + [[package]] name = "rustc-hash" version = "1.1.0" @@ -2591,6 +2850,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.4" @@ -2646,6 +2916,35 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "symbolic-common" +version = "12.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "405af7bd5edd866cef462e22ef73f11cf9bf506c9d62824fef8364eb69d4d4ad" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bcd041ccfb77d9c70639efcd5b804b508ac7a273e9224d227379e225625daf9" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + [[package]] name = "syn" version = "1.0.109" @@ -2659,9 +2958,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.12" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -2881,8 +3180,8 @@ dependencies = [ "hyper-timeout", "percent-encoding", "pin-project", - "prost", - "prost-derive", + "prost 0.11.8", + "prost-derive 0.11.8", "tokio", "tokio-stream", "tokio-util", @@ -2899,9 +3198,9 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf5e9b9c0f7e0a7c027dcfaba7b2c60816c7049171f679d99ee2ff65d0de8c4" dependencies = [ - "prettyplease", + "prettyplease 0.1.25", "proc-macro2", - "prost-build", + "prost-build 0.11.8", "quote", "syn 1.0.109", ] @@ -3097,6 +3396,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-bidi" version = "0.3.11" @@ -3135,6 +3440,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "uuid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ad59a7560b41a70d191093a945f0b87bc1deeda46fb237479708a1d6b6cdfc" + [[package]] name = "valuable" version = "0.1.0" @@ -3448,5 +3759,5 @@ checksum = "9731702e2f0617ad526794ae28fbc6f6ca8849b5ba729666c2a5bc4b6ddee2cd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.12", + "syn 2.0.39", ] diff --git a/Dockerfile b/Dockerfile index e5f7d2a038..b03313d99a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,13 +7,13 @@ ARG RUST_IMAGE=ghcr.io/linkerd/dev:v42-rust # Use an arbitrary ~recent edge release image to get the proxy # identity-initializing and linkerd-await wrappers. -# Currently pinned to a build off of edge-23.11.1 + dev:v42 -ARG LINKERD2_IMAGE=ghcr.io/olix0r/l2-proxy:git-04283611 +ARG LINKERD2_IMAGE=ghcr.io/linkerd/proxy:edge-23.11.2 -# Build the proxy. -FROM --platform=$BUILDPLATFORM $RUST_IMAGE as build +FROM $LINKERD2_IMAGE as linkerd2 -ARG PROXY_FEATURES="" +FROM --platform=$BUILDPLATFORM $RUST_IMAGE as fetch + +ARG PROXY_FEATURES="multicore,meshtls-rustls,pprof" RUN apt-get update && \ apt-get install -y time && \ if [[ "$PROXY_FEATURES" =~ .*meshtls-boring.* ]] ; then \ @@ -28,6 +28,9 @@ WORKDIR /src COPY . . RUN --mount=type=cache,id=cargo,target=/usr/local/cargo/registry \ just fetch + +# Build the proxy. +FROM fetch as build ENV CARGO_INCREMENTAL=0 ENV RUSTFLAGS="-D warnings -A deprecated" ARG TARGETARCH="amd64" @@ -35,17 +38,15 @@ ARG PROFILE="release" ARG LINKERD2_PROXY_VERSION="" ARG LINKERD2_PROXY_VENDOR="" RUN --mount=type=cache,id=cargo,target=/usr/local/cargo/registry \ - /usr/bin/time -v just arch="$TARGETARCH" features="$PROXY_FEATURES" profile="$PROFILE" build && \ - bin=$(just --evaluate profile="$PROFILE" _target_bin) ; \ - du -sh "$bin" "$bin".dbg && \ - mkdir -p /out && mv "$bin" /out/linkerd2-proxy - -FROM $LINKERD2_IMAGE as linkerd2 + /usr/bin/time -v just arch="$TARGETARCH" features="$PROXY_FEATURES" profile="$PROFILE" build-debug && \ + ( mkdir -p /out ; \ + mv $(just --evaluate profile="$PROFILE" _target_bin) /out/ ; \ + du -sh /out/* ) -# Install the proxy binary into a base image that we can at least get a shell to -# debug on. +# Install the proxy binary into a base image that we can at least get a shell +# for debugging. FROM docker.io/library/debian:bookworm-slim as runtime WORKDIR /linkerd COPY --from=linkerd2 /usr/lib/linkerd/* /usr/lib/linkerd/ -COPY --from=build /out/linkerd2-proxy /usr/lib/linkerd/linkerd2-proxy +COPY --from=build /out/* /usr/lib/linkerd/ ENTRYPOINT ["/usr/lib/linkerd/linkerd2-proxy-identity"] diff --git a/deny.toml b/deny.toml index 441e22fbf2..f059ea1a66 100644 --- a/deny.toml +++ b/deny.toml @@ -76,6 +76,15 @@ skip = [ # `linkerd-trace-context`, `rustls-pemfile` and `tonic` depend on `base64` # v0.13.1 while `rcgen` depends on v0.21.5 { name = "base64" }, + # https://github.com/hawkw/matchers/pull/4 + { name = "regex-automata", version = "0.1" }, + { name = "regex-syntax", version = "0.6" }, + # linkerd2-proxy-api needs to upgrade tonic to upgrade prost... + { name = "prost", version = "0.11" }, + { name = "prost-build", version = "0.11" }, + { name = "prost-derive", version = "0.11" }, + { name = "prost-types", version = "0.11" }, + { name = "prettyplease", version = "0.1" }, ] skip-tree = [ # right now we have a mix of versions of this crate in the ecosystem diff --git a/justfile b/justfile index f8c985d3a1..592bb5a531 100644 --- a/justfile +++ b/justfile @@ -116,7 +116,12 @@ test-dir dir *flags: cd {{ dir }} && {{ _cargo }} test --frozen {{ _features }} {{ flags }} # Build the proxy -build: && checksec _strip +build: _build checksec _strip + +# Build the proxy without stripping debug symbols +build-debug: _build + +_build: @rm -f {{ _target_bin }} {{ _target_bin }}.dbg @{{ _cargo }} build --frozen --package=linkerd2-proxy {{ _features }} diff --git a/linkerd/app/Cargo.toml b/linkerd/app/Cargo.toml index eb19e1d92f..2221e5a1ca 100644 --- a/linkerd/app/Cargo.toml +++ b/linkerd/app/Cargo.toml @@ -14,6 +14,7 @@ This is used by tests and the executable. [features] allow-loopback = ["linkerd-app-outbound/allow-loopback"] log-streaming = ["linkerd-app-admin/log-streaming"] +pprof = ["linkerd-app-admin/pprof"] [dependencies] futures = { version = "0.3", default-features = false } diff --git a/linkerd/app/admin/Cargo.toml b/linkerd/app/admin/Cargo.toml index 7f22083298..f62ca587da 100644 --- a/linkerd/app/admin/Cargo.toml +++ b/linkerd/app/admin/Cargo.toml @@ -10,6 +10,7 @@ The linkerd proxy's admin server. """ [features] +default = [] log-streaming = ["linkerd-tracing/stream"] [dependencies] @@ -19,6 +20,7 @@ futures = { version = "0.3", default-features = false } linkerd-app-core = { path = "../core" } linkerd-app-inbound = { path = "../inbound" } linkerd-tracing = { path = "../../tracing" } +pprof = { version = "0.13", optional = true, features = ["prost-codec"] } serde = "1" serde_json = "1" thiserror = "1" diff --git a/linkerd/app/admin/src/server.rs b/linkerd/app/admin/src/server.rs index d70b53c5a8..9df2175495 100644 --- a/linkerd/app/admin/src/server.rs +++ b/linkerd/app/admin/src/server.rs @@ -19,7 +19,7 @@ use hyper::{ use linkerd_app_core::{ metrics::{self as metrics, FmtMetrics}, proxy::http::ClientHandle, - trace, Error, + trace, Error, Result, }; use std::{ future::Future, @@ -40,10 +40,11 @@ pub struct Admin { tracing: trace::Handle, ready: Readiness, shutdown_tx: mpsc::UnboundedSender<()>, + #[cfg(feature = "pprof")] + enable_profiling: bool, } -pub type ResponseFuture = - Pin, Error>> + Send + 'static>>; +pub type ResponseFuture = Pin>> + Send + 'static>>; impl Admin { pub fn new( @@ -57,9 +58,18 @@ impl Admin { ready, shutdown_tx, tracing, + + #[cfg(feature = "pprof")] + enable_profiling: false, } } + #[cfg(feature = "pprof")] + pub fn with_profiling(mut self, enabled: bool) -> Self { + self.enable_profiling = enabled; + self + } + fn ready_rsp(&self) -> Response { if self.ready.is_ready() { Response::builder() @@ -181,6 +191,46 @@ impl Admin { .map(|a| a.addr.ip().is_loopback()) .unwrap_or(false) } + + #[cfg(feature = "pprof")] + async fn pprof_profile(req: Request) -> Result> { + use pprof::protos::Message; + + fn query_param<'r, B>(req: &'r Request, name: &'static str) -> Option<&'r str> { + let params = req.uri().path_and_query()?.query()?.split('&'); + params + .filter_map(|p| { + if p.starts_with(name) && p[name.len()..].starts_with('=') { + Some(&p[name.len() + 1..]) + } else { + None + } + }) + .next() + } + let duration = std::time::Duration::from_secs_f64( + query_param(&req, "seconds") + .map(|s| s.parse::()) + .transpose()? + .unwrap_or(30.0), + ); + + let profile = { + const FREQUENCY_HZ: i32 = 100; + let guard = pprof::ProfilerGuard::new(FREQUENCY_HZ)?; + tokio::time::sleep(duration).await; + guard.report().build()?.pprof()? + }; + + let mut buf = Vec::new(); + profile.encode(&mut buf)?; + let rsp = Response::builder() + .header("Content-Type", "application/protobuf") + .body(Body::from(buf)) + .expect("Response must be valid"); + + Ok(rsp) + } } impl tower::Service> for Admin @@ -254,6 +304,19 @@ where } } + #[cfg(feature = "pprof")] + "/debug/pprof/profile" if self.enable_profiling => { + if !Self::client_is_localhost(&req) { + return Box::pin(future::ok(Self::forbidden_not_localhost())); + } + + Box::pin(async move { + Ok(Self::pprof_profile(req) + .await + .unwrap_or_else(Self::internal_error_rsp)) + }) + } + _ => Box::pin(future::ok(Self::not_found())), } } diff --git a/linkerd/app/admin/src/stack.rs b/linkerd/app/admin/src/stack.rs index 016c46711a..97514ae409 100644 --- a/linkerd/app/admin/src/stack.rs +++ b/linkerd/app/admin/src/stack.rs @@ -22,6 +22,8 @@ use tracing::debug; pub struct Config { pub server: ServerConfig, pub metrics_retain_idle: Duration, + #[cfg(feature = "pprof")] + pub enable_profiling: bool, } pub struct Task { @@ -96,7 +98,13 @@ impl Config { let policy = policy.get_policy(OrigDstAddr(listen_addr.into())); let (ready, latch) = crate::server::Readiness::new(); + + #[cfg_attr(not(feature = "pprof"), allow(unused_mut))] let admin = crate::server::Admin::new(report, ready, shutdown, trace); + + #[cfg(feature = "pprof")] + let admin = admin.with_profiling(self.enable_profiling); + let http = svc::stack(move |_| admin.clone()) .push( metrics diff --git a/linkerd/app/src/env.rs b/linkerd/app/src/env.rs index 969d3c656b..fd8eeec9e6 100644 --- a/linkerd/app/src/env.rs +++ b/linkerd/app/src/env.rs @@ -717,6 +717,12 @@ pub fn parse_config(strings: &S) -> Result keepalive: inbound.proxy.server.keepalive, h2_settings, }, + + // TODO(ver) Currently we always enable profiling when the pprof feature + // is enabled. In the future, this should be driven by runtime + // configuration. + #[cfg(feature = "pprof")] + enable_profiling: true, }; let dns = dns::Config { diff --git a/linkerd2-proxy/Cargo.toml b/linkerd2-proxy/Cargo.toml index 3197f20ae4..8b3af75ab7 100644 --- a/linkerd2-proxy/Cargo.toml +++ b/linkerd2-proxy/Cargo.toml @@ -14,6 +14,7 @@ meshtls-boring = ["linkerd-meshtls/boring"] meshtls-boring-fips = ["linkerd-meshtls/boring-fips"] meshtls-rustls = ["linkerd-meshtls/rustls"] log-streaming = ["linkerd-app/log-streaming"] +pprof = ["linkerd-app/pprof"] [dependencies] futures = { version = "0.3", default-features = false }