I have a Python websockets client script running in cloud run. I need to connect to a 3rd party websocket server, who have provided their URI and API key. When I launch the service a connection is established and data starts to flow, but Cloud Run never recognizes the container as ready.
To make sure it wasn't the websockets causing the problem, I wrote a quick websocket server script and ran it, and that worked fine. I've got a feeling the problem is that Cloud Run is expecting a script that "listens" on the exposed port, and since the original script is a client it isn't considered a listener?
Here is the client script:
import asyncio
from websockets.client import connect
async def hello():
headers = {
"Authorization": "Bearer " + "API_KEY",
"Sec-WebSocket-Version": "13",
"Connection": "Upgrade",
"Upgrade": "websocket",
"Host": "logstream.3rdparty.com:443"
}
async with connect(uri="wss://logstream.3rdparty.com:443/v1/stream?cid=mycompany_hosted&type=message", extra_headers=headers) as websocket:
#websocket.send("Hello world!")
while True:
message = await websocket.recv()
print(f"Received: {message[:20]}")
asyncio.run(hello())
Here is the docker file:
FROM python:3.10
ENV ENV=dev \
TOPIC_PROJECT_ID=project_id \
TOPIC_NAME=dev-topic_name \
API_KEY=API_KEY
\
PORT=8765 \
HOSTNAME=0.0.0.0
EXPOSE 8080
COPY . /workdir
WORKDIR /workdir
RUN pip install -r requirements.txt
ENTRYPOINT ["python", "main.py"]
There are some lines in the docker file that are not required for this version of the script, so feel free to ignore the TOPIC variables and EXPOSE 8080, I left them in for the sake of troubleshooting.
TLDR: How do I get Cloud Run to set my running process to the ready state if it's not a server?
One way to handle this situation is to create a small HTTP server within your container that Cloud Run can ping to check if the container is ready. You can use a lightweight web framework like Flask to do this. Here's how you can modify your code and Dockerfile to achieve this:
Modify your Python script (main.py) to include a simple HTTP server using Flask
import asyncio
from websockets.client import connect
from flask import Flask
app = Flask(__name__)
@app.route('/')
def health_check():
return "OK"
async def hello():
headers = {
"Authorization": "Bearer " + "API_KEY",
"Sec-WebSocket-Version": "13",
"Connection": "Upgrade",
"Upgrade": "websocket",
"Host": "logstream.3rdparty.com:443",
}
async with connect(uri="wss://logstream.3rdparty.com:443/v1/stream?cid=mycompany_hosted&type=message", extra_headers=headers) as websocket:
while True:
message = await websocket.recv()
print(f"Received: {message[:20]}")
if __name__ == "__main__":
asyncio.run(hello())
Modify your Dockerfile to install Flask and expose the port
FROM python:3.10
ENV ENV=dev \
TOPIC_PROJECT_ID=project_id \
TOPIC_NAME=dev-topic_name \
API_KEY=API_KEY \
PORT=8080 \
HOSTNAME=0.0.0.0
EXPOSE 8080
COPY . /workdir
WORKDIR /workdir
RUN pip install -r requirements.txt
ENTRYPOINT ["python", "main.py"]
Flask app listens on port 8080. You should make sure that your Cloud Run service is configured to use port 8080 when deploying it. The / route is used for the health check. When Cloud Run pings the root route, it should return "OK," indicating that the container is ready.
With these modifications, your Cloud Run service should recognize the container as ready, as it has an HTTP server listening on the specified port