Replies: 1 comment
|
We did this! |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
LSProxy uses a single release image containing all LSP’s and language tooling for 10 supported languages. The resulting release image size is 9.4GB.
Brainstorming possible solutions that minimize image size on the host, as little configuration as possible, and offers flexibility for security conscious environments.
I see a spectrum of options:
Below I propose 3 options that map to this spectrum.
Least secure, most flexible: Dynamic orchestration on container start
Least secure: Requires Docker daemon socket to be mounted to LSProxy container process.
Most flexible: No configuration or additional steps required by user. It "just works" and uses the minimal set of LSP server images in addition to the slim base image.
This approach users a slim base image for the Rust service. On process start, the mounted workspace files are scanned to determine required LSP servers. The Rust service orchestrates the spawn of container processes in which there is 1 LSP server per container.
Each LSP server and its dependencies is extracted into a single image and removed from the existing LSProxy release image. This will significantly reduce the LSProxy release image size. Because LSP server images are dynamically pulled to the host based on the detected languages in the mounted workspace, the host's image cache contains only the images required by that host, reducing overall image disk utilization.
The tradeoff is LSProxy cold start with a mounted workspace will incur image pull and container start overhead. Subsequent starts in which cached LSP server images can be reused reduces the overhead to container start only.
The LSProxy Rust service functions more like a traditional "proxy" service between user requests and LSP servers in the sense it forwards the network requests to LSP server containers, and forwards their responses back to the user.
For endpoints using
ast-grep, the Rust code continues to own that behavior.By separating LSProxy workspace initialization from LSP server initialization, there are some key benefits:
ast-grep).sequenceDiagram participant User as User (CLI: lsproxy up) participant CLI as TypeScript CLI participant Rust as Rust Service (Orchestrator) participant Docker as Docker Engine participant LSP as LSP Containers User->>CLI: Run `lsproxy up --workspace ./myproj` CLI->>Rust: Start service with mounted workspace Rust->>Rust: Detect languages in workspace<br/>(package.json, go.mod, *.py, etc.) alt Go detected Rust->>Docker: docker run lsproxy-go (mount workspace) Docker-->>Rust: Container running with gopls end alt TypeScript detected Rust->>Docker: docker run lsproxy-ts (mount workspace) Docker-->>Rust: Container running with tsserver end alt Python detected Rust->>Docker: docker run lsproxy-py (mount workspace) Docker-->>Rust: Container running with jedi-language-server end Rust->>Rust: Register active LSP endpoints CLI-->>User: "lsproxy running, serving languages: Go, TS, Python" User->>CLI: Request "find references" CLI->>Rust: JSON-RPC request (symbol X) Rust->>LSP: Forward request to correct LSP container LSP-->>Rust: JSON-RPC response Rust-->>CLI: Forward result CLI-->>User: Show referencesSomething in the middle: Build single image on host
Moderately secure: CLI builds a custom image layered with the slim base layer and additional LSP server image layers based on the project structure.
Moderately flexible: Adds an additional build step for the user before running
up. Requires using the custom built image as a parameter toup. Small increase to operation and config burden.This sidesteps requiring the Docker daemon mounted to a container process making this option more secure, and instead uses the host’s Docker daemon directly. The resulting image is then used on the host or published to an image registry, where it can be pulled and used in restrictive or sensitive environments, or on the host directly.
sequenceDiagram participant User as User (CLI) participant CLI as lsproxy CLI/Builder participant Host as Host Docker CLI participant Docker as Docker Daemon participant Runner as Runner Image User->>CLI: lsproxy build-image ./myproj --go --python CLI->>CLI: Detect langs (Go, Python) CLI->>Host: Run `docker build --build-arg ENABLE_GO=1 --build-arg ENABLE_PY=1` Host->>Docker: Invoke build (multi-stage Dockerfile) Docker-->>Host: Produce lsproxy-runner:go-python Host-->>CLI: Build complete CLI-->>User: Image ready: lsproxy-runner:go-python User->>Host: docker run lsproxy-runner:go-python Runner-->>User: Service readyMost secure, least flexible: One giant image (today's solution)
Most secure: A single large image tens of GB's in size would continue to "just work". This never touches the host's Docker daemon.
Lease flexible: Users cannot easily configure their LSProxy runtime environment for language versions / LSP servers, and users accept the large image footprint including LSP and language tooling layers for languages the user does not need.
All reactions