104 lines
5.6 KiB
HTML
104 lines
5.6 KiB
HTML
|
|
<h2>The Infrastructure Problem Every Trading Platform Faces</h2>
|
||
|
|
|
||
|
|
<p>If you've ever built a trading platform that needs to serve users across multiple countries, you already know the problem: China Mobile blocks direct SSH connections to overseas servers. No warning, no error message — just silent connection timeouts that make you think your server is down when it's actually fine.</p>
|
||
|
|
|
||
|
|
<p>Our setup: the main GFIL Terminal runs on a RackNerd server (107.174.186.162) in the US, but all development happens from China. Direct SSH? Blocked. VPN-based proxy? Unreliable for automated deployment scripts. The solution we landed on after weeks of failed attempts is a simple but effective SSH chain.</p>
|
||
|
|
|
||
|
|
<h2>The JD Cloud Jumphost Architecture</h2>
|
||
|
|
|
||
|
|
<p>The key insight: China Mobile blocks overseas SSH, but domestic cloud servers can connect to overseas servers freely. So we use a JD Cloud (京东云) Windows server (111.228.37.165) as a jumphost:</p>
|
||
|
|
|
||
|
|
<pre><code>Local PC (China)
|
||
|
|
→ JD Cloud jumphost (domestic IP, 111.228.37.165)
|
||
|
|
→ RackNerd target server (overseas IP, 107.174.186.162)
|
||
|
|
</code></pre>
|
||
|
|
|
||
|
|
<p>In Python with Paramiko, this looks like:</p>
|
||
|
|
|
||
|
|
<pre><code>import paramiko
|
||
|
|
|
||
|
|
# Step 1: Connect to JD Cloud
|
||
|
|
ssh_jd = paramiko.SSHClient()
|
||
|
|
ssh_jd.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||
|
|
ssh_jd.connect('111.228.37.165', port=22, username='root', password='***')
|
||
|
|
|
||
|
|
# Step 2: Open a direct TCP channel through JD Cloud to RackNerd
|
||
|
|
channel = ssh_jd.get_transport().open_channel(
|
||
|
|
'direct-tcpip',
|
||
|
|
('107.174.186.162', 22), # Target
|
||
|
|
('127.0.0.1', 22) # Source
|
||
|
|
)
|
||
|
|
|
||
|
|
# Step 3: Connect to RackNerd through the channel
|
||
|
|
ssh_rn = paramiko.SSHClient()
|
||
|
|
ssh_rn.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||
|
|
ssh_rn.connect('107.174.186.162', port=22, username='root', password='***', sock=channel)
|
||
|
|
|
||
|
|
# Now ssh_rn is connected — use sftp, exec_command, etc.
|
||
|
|
sftp = ssh_rn.open_sftp()
|
||
|
|
sftp.put('local_file.html', '/var/www/blog/tools/file.html')
|
||
|
|
</code></pre>
|
||
|
|
|
||
|
|
<p>The <code>open_channel('direct-tcpip', ...)</code> call is the magic — it tells the JD Cloud server to forward a TCP connection to the target, effectively creating an SSH tunnel without needing to configure port forwarding on the jumphost.</p>
|
||
|
|
|
||
|
|
<h2>The Deployment Speed Problem</h2>
|
||
|
|
|
||
|
|
<p>Once the SSH chain works, the next problem is speed. With 200+ HTML files to deploy, uploading them one by one through the SSH tunnel takes forever. Our first attempt uploaded files individually — it timed out at 300 seconds after processing only about half the files.</p>
|
||
|
|
|
||
|
|
<p>The fix: pack everything into a single tar.gz, upload once, extract on the server:</p>
|
||
|
|
|
||
|
|
<pre><code>import tarfile, os
|
||
|
|
|
||
|
|
# Pack locally
|
||
|
|
with tarfile.open('upload.tar.gz', 'w:gz') as tar:
|
||
|
|
for f in os.listdir('tools/'):
|
||
|
|
if f.endswith(('.html', '.json')):
|
||
|
|
tar.add(f'tools/{f}')
|
||
|
|
|
||
|
|
# Upload single file
|
||
|
|
sftp.put('upload.tar.gz', '/tmp/upload.tar.gz')
|
||
|
|
|
||
|
|
# Verify size match (critical — we've seen truncated uploads)
|
||
|
|
assert sftp.stat('/tmp/upload.tar.gz').st_size == os.path.getsize('upload.tar.gz')
|
||
|
|
|
||
|
|
# Extract on server
|
||
|
|
ssh_rn.exec_command('cd /var/www/blog && tar xzf /tmp/upload.tar.gz')
|
||
|
|
</code></pre>
|
||
|
|
|
||
|
|
<p>This reduced a 5-minute deployment to under 30 seconds.</p>
|
||
|
|
|
||
|
|
<h2>The Cloudflare CDN Cache Trap</h2>
|
||
|
|
|
||
|
|
<p>After deploying, we'd verify by fetching the live URL — and sometimes the old content would still be showing. The culprit: Cloudflare CDN cache, even when Nginx was configured with <code>Cache-Control: no-cache</code>.</p>
|
||
|
|
|
||
|
|
<p>The tricky part: our <code>blog.quant-view.xyz</code> DNS was set to "DNS-only" (grey cloud), not "Proxied" (orange cloud). This means Cloudflare shouldn't be caching anything — requests go directly to our Nginx server. But some ISPs and corporate proxies still cache responses. The fix:</p>
|
||
|
|
|
||
|
|
<pre><code># Add cache-busting headers to Nginx config
|
||
|
|
location ~* \.(html|xml|txt|md)$ {
|
||
|
|
add_header Cache-Control "no-cache, must-revalidate" always;
|
||
|
|
}
|
||
|
|
|
||
|
|
# When you need to force-refresh, add a query parameter
|
||
|
|
# https://blog.quant-view.xyz/tools/entity.html?v=2
|
||
|
|
</code></pre>
|
||
|
|
|
||
|
|
<p>But the real lesson: when your local file is correct but the live site shows old content, don't assume the deployment failed. Check the server directly first (<code>curl http://localhost/tools/entity.html</code> from the server itself) before spending hours debugging a deployment that actually succeeded.</p>
|
||
|
|
|
||
|
|
<h2>Lesson: Always Verify Server-Side First</h2>
|
||
|
|
|
||
|
|
<p>We wasted an entire audit cycle thinking our deployment had failed. The Claude reviewer checked the live URL and found old content. We re-deployed. Same result. It turned out the server had the correct files all along — the stale content was coming from an intermediate cache layer.</p>
|
||
|
|
|
||
|
|
<p>Our verification checklist now:</p>
|
||
|
|
|
||
|
|
<ol>
|
||
|
|
<li><strong>Server-side check</strong>: <code>curl http://localhost/path</code> from the server — bypasses all caches</li>
|
||
|
|
<li><strong>External check</strong>: <code>curl https://domain/path</code> from outside — tests what users see</li>
|
||
|
|
<li><strong>Content hash</strong>: Compare specific strings (e.g., <code>grep -c "liudapao880807-arch" /var/www/blog/tools/entity.html</code>) rather than full file comparison</li>
|
||
|
|
</ol>
|
||
|
|
|
||
|
|
<p>This three-step verification has saved us from false "deployment failed" alarms multiple times since.</p>
|
||
|
|
|
||
|
|
<h2>Try Our Free Tools</h2>
|
||
|
|
|
||
|
|
<p>This infrastructure powers <a href="https://blog.quant-view.xyz/tools/">22 free trading calculators</a> across 4 languages. Try the <a href="https://blog.quant-view.xyz/tools/position-size-calculator.html">Position Size Calculator</a> — it handles Forex, Gold, Crypto, and Indices with correct pip values for 30+ instruments.</p>
|