[refactor] optimize Docker image size and build context

This commit is contained in:
dijunkun
2025-12-08 16:43:19 +08:00
parent 799d1a31d5
commit 44b469ece3
6 changed files with 203 additions and 57 deletions

36
.dockerignore Normal file
View File

@@ -0,0 +1,36 @@
# Git
.git
.gitignore
.github
# Build artifacts
build/
.xmake/
*.o
*.a
*.so
*.exe
# IDE
.vs/
.vscode/
.idea/
*.swp
*.swo
*~
# Documentation
*.md
!README.md
!README_EN.md
# Workflows (not needed in container)
workflows/
# Docker files
# docker/
# Temporary files
*.log
*.tmp
*.cache

View File

@@ -1,22 +1,42 @@
FROM ubuntu:22.04 AS builder
# Set non-interactive mode
ENV DEBIAN_FRONTEND=noninteractive
# Install build dependencies
RUN apt-get update && \
apt-get install -y software-properties-common git curl unzip build-essential && \
add-apt-repository -y ppa:xmake-io/xmake && \
apt-get install -y --no-install-recommends \
ca-certificates \
gnupg \
software-properties-common \
git \
curl \
unzip \
build-essential
# Add xmake repository and install xmake
RUN add-apt-repository -y ppa:xmake-io/xmake && \
apt-get update && \
apt-get install -y xmake && \
apt-get install -y --no-install-recommends xmake && \
xmake --version --root
# Clean up to reduce image size
RUN apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
WORKDIR /src
COPY . .
# Build and copy only the binary, clean up build artifacts
RUN xmake b -vy --root crossdesk_server && \
mkdir -p /output && \
cp build/linux/x86_64/release/crossdesk_server /output/
cp build/linux/x86_64/release/crossdesk_server /output/ && \
rm -rf build .xmake
FROM crossdesk/crossdesk-server-base:latest
# Generate coturn certificates and set permissions in one layer
RUN mkdir -p /opt/turnserver && \
cd /opt/turnserver && \
openssl genrsa -out turn_server_pkey.pem 2048 && \
@@ -27,11 +47,10 @@ RUN mkdir -p /opt/turnserver && \
-out turn_server_cert.pem && \
chmod 600 /opt/turnserver/turn_server_pkey.pem
# Copy files and set permissions in one layer
COPY docker/start.sh /start.sh
COPY docker/generate_certs.sh /docker/generate_certs.sh
COPY --from=builder /output/crossdesk_server /crossdesk-server/crossdesk_server
RUN chmod +x /start.sh /docker/generate_certs.sh /crossdesk-server/crossdesk_server
ENTRYPOINT ["/start.sh"]

View File

@@ -70,8 +70,10 @@ echo "Signing server certificate with root certificate..."
openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \
-out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext
# 6. 生成完整链证书
# 6. 生成完整链证书并更新 bundle.crt包含服务器证书和根证书
cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT"
# 将完整证书链写入 bundle.crt这样服务器可以使用完整的证书链
cp "$FULLCHAIN_CERT" "$SERVER_CERT"
# 7. 清理中间文件
rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "$FULLCHAIN_CERT"

View File

@@ -80,8 +80,8 @@ exec turnserver -c "$CONF_FILE" &
# start crossdesk-server as main foreground process
echo "Starting crossdesk-server..."
# 程序现在使用固定目录:
# - 数据库和配置文件:/var/lib/crossdesk
# - 日志文件:/var/log/crossdesk
# 只需传递端口参数
./crossdesk-server/crossdesk_server ${CROSSDESK_SERVER_PORT}
echo "Certificate directory: $CERT_DIR"
echo "Certificate files:"
ls -la "$CERT_DIR" || echo "Warning: Cannot list certificate directory"
exec ./crossdesk-server/crossdesk_server ${CROSSDESK_SERVER_PORT}

View File

@@ -24,8 +24,16 @@ int main(int argc, char* argv[]) {
InitLogger(log_dir);
SignalServer s(std::stoi(port), certs_dir, db_path);
s.Run();
try {
SignalServer s(std::stoi(port), certs_dir, db_path);
s.Run();
} catch (std::exception& e) {
LOG_ERROR("Fatal error: {}", e.what());
return 1;
} catch (...) {
LOG_ERROR("Unknown fatal error occurred");
return 1;
}
return 0;
}

View File

@@ -1,5 +1,6 @@
#include "signal_server.h"
#include <filesystem>
#include <fstream>
#include "common.h"
@@ -113,6 +114,17 @@ context_ptr SignalServer::OnTlsInit(websocketpp::connection_hdl hdl) {
std::string cert_file = certs_dir_ + "/crossdesk.cn_bundle.crt";
std::string key_file = certs_dir_ + "/crossdesk.cn.key";
// Check if certificate files exist
if (!std::filesystem::exists(cert_file)) {
LOG_ERROR("Certificate file not found: {}", cert_file);
throw std::runtime_error("Certificate file not found: " + cert_file);
}
if (!std::filesystem::exists(key_file)) {
LOG_ERROR("Private key file not found: {}", key_file);
throw std::runtime_error("Private key file not found: " + key_file);
}
ctx->use_certificate_chain_file(cert_file);
ctx->use_private_key_file(key_file, asio::ssl::context::pem);
@@ -121,8 +133,11 @@ context_ptr SignalServer::OnTlsInit(websocketpp::connection_hdl hdl) {
"ECDHE-RSA-AES256-GCM-SHA384:"
"ECDHE-ECDSA-AES128-GCM-SHA256:"
"ECDHE-RSA-AES128-GCM-SHA256");
LOG_INFO("TLS context initialized successfully");
} catch (std::exception& e) {
std::cout << "Exception: " << e.what() << std::endl;
LOG_ERROR("Failed to initialize TLS context: {}", e.what());
throw; // Re-throw to prevent invalid context from being used
}
return ctx;
}
@@ -142,12 +157,58 @@ void SignalServer::Run() {
return;
}
server_.set_reuse_addr(true);
LOG_INFO("Signal server runs on port [{}]", port_);
// Verify certificate files exist
std::string cert_file = certs_dir_ + "/crossdesk.cn_bundle.crt";
std::string key_file = certs_dir_ + "/crossdesk.cn.key";
if (!std::filesystem::exists(cert_file)) {
LOG_ERROR("Certificate file not found: {}", cert_file);
return;
}
if (!std::filesystem::exists(key_file)) {
LOG_ERROR("Private key file not found: {}", key_file);
return;
}
server_.listen(port_);
server_.start_accept();
server_.run();
server_.set_reuse_addr(true);
LOG_INFO("Signal server starting on port [{}]", port_);
LOG_INFO("Certificate directory: [{}]", certs_dir_);
LOG_INFO("Database path: [{}]", db_path_);
try {
// Listen on all interfaces (0.0.0.0)
namespace asio = websocketpp::lib::asio;
asio::error_code ec;
server_.listen(asio::ip::tcp::v4(), port_, ec);
if (ec) {
LOG_ERROR("Failed to listen on port {}: {}", port_, ec.message());
return;
}
LOG_INFO("Successfully bound to port [{}]", port_);
server_.start_accept(ec);
if (ec) {
LOG_ERROR("Failed to start accepting connections: {}", ec.message());
return;
}
LOG_INFO("Signal server listening on port [{}], waiting for connections...",
port_);
server_.run();
LOG_INFO("Server run() returned");
} catch (std::exception& e) {
LOG_ERROR("Server error: {}, attempting to restart...", e.what());
// Try to restart the server
try {
server_.stop();
server_.listen(port_);
server_.start_accept();
server_.run();
} catch (std::exception& e2) {
LOG_ERROR("Failed to restart server: {}", e2.what());
}
} catch (...) {
LOG_ERROR("Unknown error occurred in server");
}
}
void SignalServer::SendMsg(websocketpp::connection_hdl hdl, json message) {
@@ -164,43 +225,63 @@ void SignalServer::OnMessage(websocketpp::connection_hdl hdl,
return;
}
std::string payload = msg->get_payload();
auto j = json::parse(payload);
std::string type = j["type"].get<std::string>();
switch (HASH_STRING_PIECE(type.c_str())) {
case "ping"_H: {
if (transmission_manager_) {
transmission_manager_->UpdateWsHandleLastActiveTime(hdl);
json message = {{"type", "pong"}};
server_.send(hdl, message.dump(), websocketpp::frame::opcode::text);
}
break;
try {
std::string payload = msg->get_payload();
json j;
try {
j = json::parse(payload);
} catch (json::parse_error& e) {
LOG_ERROR("Failed to parse JSON message: {}", e.what());
return;
}
case "login"_H:
signal_negotiation_->login_user(hdl, j);
break;
case "user_leave_transmission"_H:
signal_negotiation_->leave_transmission(hdl, j);
break;
case "query_user_id_list"_H:
signal_negotiation_->query_user_id_list(hdl, j);
break;
case "join_transmission"_H:
signal_negotiation_->join_transmission(hdl, j);
break;
case "offer"_H:
signal_negotiation_->offer(hdl, j);
break;
case "answer"_H:
signal_negotiation_->answer(hdl, j);
break;
case "new_candidate"_H:
signal_negotiation_->new_candidate(hdl, j);
break;
case "new_candidate_mid"_H:
signal_negotiation_->new_candidate_mid(hdl, j);
default:
break;
if (!j.contains("type")) {
LOG_ERROR("Message missing 'type' field");
return;
}
std::string type = j["type"].get<std::string>();
switch (HASH_STRING_PIECE(type.c_str())) {
case "ping"_H: {
if (transmission_manager_) {
transmission_manager_->UpdateWsHandleLastActiveTime(hdl);
json message = {{"type", "pong"}};
server_.send(hdl, message.dump(), websocketpp::frame::opcode::text);
}
break;
}
case "login"_H:
signal_negotiation_->login_user(hdl, j);
break;
case "user_leave_transmission"_H:
signal_negotiation_->leave_transmission(hdl, j);
break;
case "query_user_id_list"_H:
signal_negotiation_->query_user_id_list(hdl, j);
break;
case "join_transmission"_H:
signal_negotiation_->join_transmission(hdl, j);
break;
case "offer"_H:
signal_negotiation_->offer(hdl, j);
break;
case "answer"_H:
signal_negotiation_->answer(hdl, j);
break;
case "new_candidate"_H:
signal_negotiation_->new_candidate(hdl, j);
break;
case "new_candidate_mid"_H:
signal_negotiation_->new_candidate_mid(hdl, j);
break;
default:
LOG_WARN("Unknown message type: {}", type);
break;
}
} catch (std::exception& e) {
LOG_ERROR("Error processing message: {}", e.what());
} catch (...) {
LOG_ERROR("Unknown error processing message");
}
}