Network Komutları: ip, ss, ping, curl, dig, nc ile "Port Açık mı, Kim Dinliyor, Neden Ulaşamıyorum?"
Bir servise ulaşılamıyorsa katman katman ilerlersin: getent ile isim, ping ve nc -zv ile yol ve firewall (refused dinleyen yok, timeout paket düşüyor demek), ss -tlnp ile kim hangi adreste dinliyor, sonra curl ve error log. ip, ss, lsof, curl, traceroute, dig ve resolvectl'i Bookstore sunucusunda gösterip eski hosts kaydı, ufw ve yanlış port'un üst üste bindiği gerçek bir vakayı çözüyor, proxy ve IPv6 tuzaklarına bakıyoruz.
Sürüm notu: Bu yazıdaki komutlar Ubuntu Server 24.04.5 LTS (iproute2 6.1, systemd-resolved 255, ufw 0.36, Nginx 1.24, curl 8.5, OpenBSD netcat) çalıştıran bir test VM'inde ve aynı ağdaki bir Ubuntu 24.04 istemcide; Bookstore.Api .NET SDK 10.0.402 ile derlenip ASP.NET Core 10.0.15 runtime'ıyla çalıştırılmıştır.
Önce Tahmin Et
Sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) API'si sunucuda çalışıyor ve sunucunun içinden sorunca cevap veriyor. Firewall kapalı. Kendi makinenden aynı port'a gidiyorsun:
$ curl -sS -m 5 http://10.20.30.10:5000/health
curl: (7) Failed to connect to 10.20.30.10 port 5000 after 3 ms: Couldn't connect to server
$ nc -zv -w 3 10.20.30.10 5000
nc: connect to 10.20.30.10 port 5000 (tcp) failed: Connection refused
Process çalışıyor, port doğru, firewall yok. Bağlantı neden 3 milisaniyede reddediliyor? İlk yazıda aynı hatayı görüp cevabı buraya bırakmıştık; ikinci bölümde.
Bu yazıda "port açık mı, kim dinliyor, neden ulaşamıyorum" sorularının komutlarını görüyoruz: ip, ss, lsof, ping, nc, curl, traceroute, dig ve resolvectl. Sonra bunları birleştirip üç arızanın üst üste bindiği bir vakayı katman katman çözüyoruz. IP, port ve NAT'ın (Network Address Translation; özel adresleri dış adrese çeviren mekanizma) teorisi Networking ve HTTP Nasıl Çalışır serisinin ikinci yazısında, DNS'in teorisi aynı serinin dördüncü yazısında; burada sunucuda elini klavyeye koyunca ne yazacağın var.
Kendi Adresini ve Yolunu Bilmek: ip
$ ip -br a
lo UNKNOWN 127.0.0.1/8 ::1/128
enp0s2 UP 10.20.30.10/24 fe80::5054:ff:fe3a:1c0a/64
$ ip r
default via 10.20.30.1 dev enp0s2 proto static
10.20.30.0/24 dev enp0s2 proto kernel scope link src 10.20.30.10
$ ip route get 1.1.1.1
1.1.1.1 via 10.20.30.1 dev enp0s2 src 10.20.30.10 uid 1000
cache
$ ip neigh
10.20.30.5 dev enp0s2 lladdr da:14:ec:1d:33:d8 REACHABLE
10.20.30.1 dev enp0s2 lladdr d2:3c:2c:25:a1:33 STALE
ip -br a (brief address) her interface'i tek satırda adresiyle verir: lo loopback, enp0s2 gerçek ağ kartı ve 10.20.30.10/24 adresi; fe80:: ile başlayan IPv6 adresi yalnızca bu ağ parçasında geçerli olan link-local adres. ip r route tablosu: 10.20.30.0/24 doğrudan bu kartta, gerisi (default) 10.20.30.1 gateway'ine gidiyor. "Bu adrese giden paket hangi karttan, hangi kaynak IP ile çıkar" sorusunun tek komutluk cevabı ip route get; birden çok kartı ya da VPN'i (şifreli özel ağ bağlantısı) olan sunucularda çok işe yarar. ip neigh ARP (Address Resolution Protocol; aynı ağda bir IP'nin donanım adresini bulan protokol) tablosudur, yani IP'den MAC (ağ kartının donanım adresi) adresine eşleme; aynı ağdaki makinelerle gerçekten konuşulup konuşulmadığını gösterir; FAILED görüyorsan karşı taraf bu ağda cevap vermiyordur.
Eski kaynaklardaki ifconfig, netstat ve route net-tools paketindedir ve Ubuntu 24.04 sunucu image'ında kurulu gelmez:
$ ifconfig
Command 'ifconfig' not found, but can be installed with:
sudo apt install net-tools
Karşılıkları: ifconfig yerine ip a, route -n yerine ip r, arp -n yerine ip neigh, netstat -tlnp yerine ss -tlnp. Eski sunucuları okumak on üçüncü yazının konusu.
Kim Dinliyor: ss ve lsof
$ sudo ss -tlnp
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 512 127.0.0.1:5000 0.0.0.0:* users:(("dotnet",pid=4181,fd=176))
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=3601,fd=7),("nginx",pid=3600,fd=7),("nginx",pid=711,fd=7))
LISTEN 0 4096 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=813,fd=3),("systemd",pid=1,fd=188))
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=491,fd=15))
LISTEN 0 4096 127.0.0.54:53 0.0.0.0:* users:(("systemd-resolve",pid=491,fd=17))
LISTEN 0 4096 [::]:22 [::]:* users:(("sshd",pid=813,fd=4),("systemd",pid=1,fd=193))
$ sudo lsof -nP -i :5000
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
dotnet 4181 bookstore 176u IPv4 45731 0t0 TCP 127.0.0.1:5000 (LISTEN)
Bayraklar: -t TCP, -l yalnızca dinleyenler (LISTEN), -n isim çözme (port'u http yerine 80 yaz), -p process. -p başka kullanıcıların process'lerini yalnızca sudo ile gösterir; sudo'suz çalıştırırsan Process sütunu boş kalır ve port'u kimin tuttuğunu göremezsin. lsof -i :5000 aynı bilgiyi process tarafından verir ve kullanıcı adını da gösterir: port'u bookstore kullanıcısının dotnet process'i tutuyor.
Tahminin cevabı Local Address sütununda. Nginx 0.0.0.0:80'de dinliyor: "bu makinenin bütün IPv4 adreslerinde". Bookstore.Api ise 127.0.0.1:5000'de: yalnızca loopback'te. Paket 10.20.30.10:5000'e geldiğinde o adres ve port çiftinde dinleyen bir socket yok ve kernel bağlantıyı hemen reddediyor: TCP RST (bağlantıyı reddeden sıfırlama paketi) gönderiyor. Bu bir hata değil, beşinci yazıdaki unit'te ASPNETCORE_URLS=http://127.0.0.1:5000 yazarak bilerek yaptığımız şey: uygulamaya yalnızca aynı makinedeki Nginx ulaşsın. [::]:22 IPv6'nın bütün adresleri; 127.0.0.53%lo:53 ise systemd-resolved'ın yerel DNS stub'ı (aşağıda). 22. port'ta sshd'nin yanında systemd'nin (PID 1) görünmesi, Ubuntu 22.10'dan beri SSH'ın (uzak bağlantı protokolü) socket activation ile gelmesinden: port'u önce systemd açıyor.
Dinleyenler değil, kurulmuş bağlantılar için -l'yi kaldır. Nginx'e 200 istek gönderip TIME_WAIT'teki bağlantıları saydım:
$ for i in $(seq 1 200); do curl -s -o /dev/null http://127.0.0.1/health; done; ss -tan state time-wait | wc -l
403
$ ss -tan state time-wait | head -n 4
Recv-Q Send-Q Local Address:Port Peer Address:Port Process
0 0 127.0.0.1:5000 127.0.0.1:59640
0 0 127.0.0.1:5000 127.0.0.1:52560
0 0 127.0.0.1:37360 127.0.0.1:80
Başlık satırı hariç 402: her istek iki bağlantı açıp kapattı, biri curl'den Nginx'e, biri Nginx'ten Bookstore.Api'ye. TIME_WAIT, bağlantıyı ilk kapatan tarafta bir süre bekleyen ve gecikmiş paketlerin yeni bir bağlantıya karışmasını önleyen normal bir durum. Binlercesini görüyorsan bir şey her istekte yeni bağlantı açıyor demektir: burada Nginx'in upstream'e varsayılan olarak keepalive'sız bağlanması. Bunun .NET'teki karşılığı HttpClient'ı her istekte yeniden oluşturmak ve socket'leri tüketmek; Networking ve HTTP Nasıl Çalışır serisinin TCP yazısı bunu anlatıyor. ss -s durumların özetini, ss -tanp state established açık bağlantıları process'leriyle verir.
Bağlantıyı Test Etmek: ping, nc, curl ve traceroute
ping ICMP (ağ kontrol mesajları protokolü) ile "host ayakta mı ve gecikme ne" sorusunu cevaplar. Port'un açık olup olmadığını söylemez. Port için nc -zv (-z veri gönderme, yalnızca bağlan; -v sonucu yaz) ve sonucun üç hâli var:
| nc sonucu | Anlamı | Sonraki adım |
|---|---|---|
succeeded | TCP bağlantısı kuruldu, biri dinliyor | Uygulama katmanına geç (curl) |
Connection refused | Paket ulaştı, o adres ve port'ta dinleyen yok | Sunucuda ss -tlnp, bind adresi |
timed out | Cevap gelmedi; arada paketi düşüren bir şey var | Firewall, security group, route |
Bu ayrım teşhisin yarısı: refused hızlı gelir ve sunucuya ulaştığını kanıtlar; timeout yavaş gelir ve çoğu zaman bir firewall'u gösterir. Windows'tan aynı testi PowerShell'de Test-NetConnection 10.20.30.10 -Port 80 ile ya da WSL (Windows Subsystem for Linux; Windows içinde Linux çalıştıran katman) 2 içindeki nc ile yapabilirsin.
Port açıksa sıra HTTP'de. curl -v bağlantının her adımını, -w ise süreleri yazar:
$ curl -sS -o /dev/null -w "dns=%{time_namelookup} connect=%{time_connect} ttfb=%{time_starttransfer} total=%{time_total}\n" http://bookstore.test/api/orders/1042
dns=0.000123 connect=0.001588 ttfb=0.230408 total=0.230443
$ curl -sI http://bookstore.test/api/books
HTTP/1.1 405 Method Not Allowed
Server: nginx/1.24.0 (Ubuntu)
Date: Sat, 14 Nov 2026 16:13:27 GMT
Connection: keep-alive
Allow: GET
İsim çözümü ve bağlantı 2 ms'nin altında, ilk byte 230 ms'de geliyor: zaman uygulamada geçiyor, ağda değil. -w ile süreleri okumayı Networking ve HTTP Nasıl Çalışır serisinin yavaş istek yazısı ayrıntılı anlatıyor. İkinci komut bir tuzak: curl -I GET değil HEAD gönderir ve minimal API'deki MapGet HEAD'e 405 döner. "Site çalışıyor mu" kontrolünde -I yerine curl -s -o /dev/null -w "%{http_code}" kullan.
Ağ yolu için traceroute ve mtr. Aradaki her router'ı ve gecikmesini gösterirler; lab'da istemci ile sunucu aynı ağda olduğu için yol tek adım. Yine de bir şey öğretiyor: sunucuda firewall açıkken (aşağıdaki vaka) klasik traceroute 30 satır boyunca * * * bastı, çünkü yüksek numaralı UDP (bağlantı kurmadan paket gönderen taşıma protokolü) port'larına paket gönderiyor ve firewall onları sessizce düşürüyordu. Aynı anda mtr (ICMP) ve TCP modundaki traceroute hedefe ulaştı:
$ sudo traceroute -T -p 80 -n 10.20.30.10
traceroute to 10.20.30.10 (10.20.30.10), 30 hops max, 60 byte packets
1 10.20.30.10 2.459 ms 2.590 ms 2.822 ms
Yıldızlar "yol kopuk" demek değil, "bu tür pakete cevap gelmedi" demek. İnternet üzerindeki router'ların çoğu da bu paketlere cevap vermez. Gerçek trafiğin kullandığı port'la denemek (-T -p 443) daha anlamlı sonuç verir.
İsim Çözümü: resolv.conf, resolvectl, dig ve getent
$ grep -v "^#" /etc/resolv.conf | grep -v "^$"
nameserver 127.0.0.53
options edns0 trust-ad
search .
$ resolvectl status enp0s2 | grep -E "DNS|Scopes"
Current Scopes: DNS
Protocols: +DefaultRoute -LLMNR -mDNS -DNSOverTLS DNSSEC=no/unsupported
Current DNS Server: 1.1.1.1
DNS Servers: 1.1.1.1 9.9.9.9
$ dig example.com +noall +answer
example.com. 232 IN A 104.20.23.154
example.com. 232 IN A 172.66.147.243
Ubuntu'da /etc/resolv.conf gerçek DNS sunucusunu değil, systemd-resolved'ın yerel stub'ını (127.0.0.53) gösterir; asıl sunucular resolvectl status'ta. resolv.conf'u elle düzenlemek işe yaramaz, dosya yeniden yazılır; DNS ayarı netplan'de (bu sunucuda /etc/netplan/50-cloud-init.yaml) yapılır. dig doğrudan DNS'e sorar ve TTL'i (cevabın cache'te tutulabileceği süre) de gösterir, burada 232 saniye; dig @9.9.9.9 ... belirli bir sunucuya sorar.
Önemli ayrım: uygulamalar ve curl ismi dig gibi çözmez. glibc önce /etc/nsswitch.conf'a bakar ve orada hosts: files dns yazdığı için önce /etc/hosts'u okur. Uygulamanın gördüğü sonucu görmek için getent hosts kullan. Aşağıdaki vakada bu fark doğrudan sorunun kendisi.
Katmanlı Teşhis: "Bookstore'a Dışarıdan Ulaşılamıyor"
Lab'da üç arızayı bilerek üst üste koydum: istemcinin /etc/hosts'unda Bookstore'un eski IP'si, sunucuda yalnızca SSH'a izin veren bir ufw (Ubuntu'nun firewall aracı), ve unit'e eklenmiş, uygulamayı 5001'e taşıyan bir drop-in. Kullanıcının gördüğü tek şey "site açılmıyor". Sırayla, istemciden uygulamaya doğru:

1. İsim çözümü (istemci my-pc'de):
$ curl -sS -m 5 http://bookstore.test/health
curl: (7) Failed to connect to bookstore.test port 80 after 3112 ms: Couldn't connect to server
$ getent hosts bookstore.test
10.20.30.11 bookstore.test
$ dig +short bookstore.test
$ curl -sS -m 5 --resolve bookstore.test:80:10.20.30.10 http://bookstore.test/health
curl: (28) Connection timed out after 5007 milliseconds
getent 10.20.30.11 diyor, yani hosts dosyasında taşınmadan önceki sunucunun adresi kalmış. dig hiçbir şey döndürmüyor çünkü .test gerçek DNS'te yok ve dig hosts dosyasına bakmıyor; yalnızca dig'e güvenseydin bu satırı hiç bulamazdın. curl --resolve DNS'i atlayıp ismi doğru IP'ye sabitler, böylece bir sonraki katmanı ismi düzeltmeden test edebilirsin. Doğru IP'yle de bağlanamıyoruz ama bu sefer hata farklı: timeout.
2 ve 3. Ağ yolu ve firewall:
$ ping -c 2 10.20.30.10
PING 10.20.30.10 (10.20.30.10) 56(84) bytes of data.
64 bytes from 10.20.30.10: icmp_seq=1 ttl=64 time=4.46 ms
64 bytes from 10.20.30.10: icmp_seq=2 ttl=64 time=1.22 ms
--- 10.20.30.10 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 1.219/2.837/4.455/1.618 ms
$ nc -zv -w 3 10.20.30.10 22
Connection to 10.20.30.10 22 port [tcp/*] succeeded!
$ nc -zv -w 3 10.20.30.10 80
nc: connect to 10.20.30.10 port 80 (tcp) timed out: Operation now in progress
Host ayakta, 22 açık, 80 timeout. Tablodaki üçüncü satır: aradaki bir şey paketi düşürüyor. Sunucuya SSH ile girip kontrol ettim:
$ sudo ss -tlnp "sport = :80"
State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=3601,fd=7),("nginx",pid=3600,fd=7),("nginx",pid=711,fd=7))
$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1/health
502
$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip
To Action From
-- ------ ----
22/tcp ALLOW IN Anywhere
22/tcp (v6) ALLOW IN Anywhere (v6)
$ journalctl -k --grep "UFW BLOCK" -n 1 --no-pager | grep -oE "IN=[^ ]+|SRC=[^ ]+|DST=[^ ]+|PROTO=[^ ]+|DPT=[^ ]+" | paste -sd" "
IN=enp0s2 SRC=10.20.30.5 DST=10.20.30.10 PROTO=TCP DPT=5000
$ sudo ufw allow 80/tcp
Rule added
Rule added (v6)
Nginx dinliyor ve sunucunun içinden ulaşılıyor; ufw ise yalnızca 22'ye izin veriyor. ufw engellediği paketleri kernel log'una [UFW BLOCK] diye yazar ve önceki yazıdaki journalctl -k bunu gösterir (son engellenen paket benim 5000'e denememdi). Ama aynı komut ikinci bir sorunu da gösterdi: sunucunun içinden bile 502. Firewall'u açınca kullanıcı artık timeout yerine 502 görecek.
4 ve 5. Nginx ve uygulama:
$ tail -n 1 /var/log/nginx/bookstore.error.log | cut -c1-150
2026/11/14 16:12:49 [error] 3601#3601: *12270 connect() failed (111: Connection refused) while connecting to upstream, client: 10.20.30.5, server: _,
$ curl -sS http://127.0.0.1:5000/health
curl: (7) Failed to connect to 127.0.0.1 port 5000 after 8 ms: Couldn't connect to server
$ sudo ss -tlnp | grep dotnet
LISTEN 0 512 127.0.0.1:5001 0.0.0.0:* users:(("dotnet",pid=5670,fd=176))
$ systemctl cat bookstore-api --no-pager | tail -n 3
# /etc/systemd/system/bookstore-api.service.d/override.conf
[Service]
Environment=ASPNETCORE_URLS=http://127.0.0.1:5001
$ sudo systemctl revert bookstore-api
Removed "/etc/systemd/system/bookstore-api.service.d/override.conf".
Removed "/etc/systemd/system/bookstore-api.service.d".
$ sudo systemctl restart bookstore-api
$ sudo ss -tlnp | grep dotnet
LISTEN 0 512 127.0.0.1:5000 0.0.0.0:* users:(("dotnet",pid=6062,fd=176))
Nginx 5000'e gidiyor, uygulama 5001'de dinliyor. systemctl cat sebebi gösterdi: birinin unutulmuş bir drop-in'i (beşinci yazıda neden systemctl cat'e bakmak gerektiğini konuşmuştuk). Drop-in'i geri alıp istemcinin hosts satırını düzeltince:
$ getent hosts bookstore.test
10.20.30.10 bookstore.test
$ curl -sS http://bookstore.test/health; echo
{"status":"Healthy"}
Üç arıza, üç farklı hata mesajı: "Couldn't connect" (yanlış IP), timeout (firewall), 502 (yanlış port). Her katmanda tek bir soruya cevap aradık ve bir katmanı doğrulamadan diğerine geçmedik. Lab'ı bir sonraki yazılar için temiz bırakmak adına ufw'yi sudo ufw --force reset ile kapattım; doğru kurulumu on birinci yazıda yapacağız.
Sunucudan Dışarı: Outbound ve Proxy
Sorun bazen ters yönde: uygulama dışarıdaki bir servise (ödeme sağlayıcısı, NuGet, bir API) ulaşamıyor. Kurumsal ağlarda dışarı çıkış çoğu zaman bir proxy üzerinden olur ve proxy environment variable'larla verilir:
$ curl -sS -m 5 -o /dev/null https://api.nuget.org/v3/index.json; echo "exit=$?"
exit=0
$ export HTTPS_PROXY=http://10.20.30.99:3128
$ curl -sS -m 5 -o /dev/null https://api.nuget.org/v3/index.json; echo "exit=$?"
curl: (7) Failed to connect to 10.20.30.99 port 3128 after 3122 ms: Couldn't connect to server
exit=7
$ curl -v -m 5 -o /dev/null https://api.nuget.org/v3/index.json 2>&1 | grep -i proxy | head -n 3
* Uses proxy env variable HTTPS_PROXY == 'http://10.20.30.99:3128'
$ NO_PROXY=api.nuget.org curl -sS -m 5 -o /dev/null -w "%{http_code}\n" https://api.nuget.org/v3/index.json
200
Aynı komut, shell'de unutulmuş bir değişken yüzünden çalışmaz hâle geldi; curl -v proxy kullandığını açıkça yazıyor. .NET'te HttpClient de Linux'ta HTTP_PROXY, HTTPS_PROXY, ALL_PROXY ve NO_PROXY'yi okur (küçük harfli adları önce). Ama systemd altında çalışan service senin shell'inin değişkenlerini görmez; beşinci yazıdaki cron tuzağının aynısı. Service'in gerçek environment'ına process'in kendisinden bak:
$ sudo cat /proc/$(systemctl show -p MainPID --value bookstore-api)/environ | tr "\0" "\n" | grep -i proxy; echo "matches=$?"
matches=1
Bookstore.Api'de proxy tanımlı değil. Gerekiyorsa unit'e bir drop-in ile Environment=HTTPS_PROXY=... ve Environment=NO_PROXY=localhost,127.0.0.1 eklenir. NO_PROXY'yi unutmak, uygulamanın kendi sunucusundaki bir servise bile proxy üzerinden gitmeye çalışmasına yol açar.
Üretimde Dikkat
Bind adresi bir güvenlik kararıdır. Kestrel'i 0.0.0.0'a ya da http://+:5000'e bağlamak, Nginx'teki rate limiting, TLS (HTTPS'in şifreleme katmanı) ve header ayarlarını atlayan ikinci bir kapı açar. Sunucuya yeni bir service kurduğunda sudo ss -tlnp çıktısında 0.0.0.0 ve [::] ile başlayan her satırı tek tek açıkla; açıklayamadığın bir port envanter eksiğidir. Bulut sağlayıcısının security group'u ve sunucudaki firewall iki ayrı katmandır; birini açıp diğerini unutmak en sık timeout sebebi.
Teşhis komutlarını sunucuya önceden kur. dig, nc, lsof, tcpdump olmayan bir sunucuda olay sırasında paket kurmak hem zaman kaybı hem de dışarı çıkış kapalıysa imkânsız.
Sık Düşülen Tuzaklar
ICMP kapalı diye sunucuyu "down" sanmak. Sunucuda ping'e cevabı kapatan bir kural ekledim:
$ sudo iptables -I ufw-before-input -p icmp --icmp-type echo-request -j DROP
$ ping -c 3 -W 1 10.20.30.10
PING 10.20.30.10 (10.20.30.10) 56(84) bytes of data.
--- 10.20.30.10 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2082ms
$ curl -sS -m 5 http://bookstore.test/health; echo
{"status":"Healthy"}
%100 kayıp, ama site çalışıyor. Birçok bulut ortamı ve kurumsal firewall ICMP'yi kapatır. Ping'in cevap vermemesi bir ipucudur, karar değil; kararı nc -zv ve curl verir.
localhost'un IPv4 olduğunu varsaymak:
$ curl -sv http://localhost:5000/health 2>&1 | grep -E "Trying|connect to|Connected|HTTP/1.1 200"
* Trying [::1]:5000...
* connect to ::1 port 5000 from ::1 port 41328 failed: Connection refused
* Trying 127.0.0.1:5000...
* Connected to localhost (127.0.0.1) port 5000
< HTTP/1.1 200 OK
localhost hem ::1'e hem 127.0.0.1'e çözülür ve curl önce IPv6'yı denedi. Uygulama yalnızca IPv4'te dinlediği için ilk deneme reddedildi, curl ikinci adrese geçti. curl bunu sessizce telafi etti, ama her istemci etmez: bazı library'ler ilk adreste durur ya da her istekte önce IPv6'yı deneyip bekler. Config'lerde localhost yerine dinlenen adresi (127.0.0.1) açıkça yaz; Nginx'teki proxy_pass http://127.0.0.1:5000 bu yüzden IP ile.
dig'in cevabını uygulamanın gördüğü cevap sanmak. Vakadaki gibi /etc/hosts'taki bir satır her şeyi değiştirir; getent hosts ile kontrol et. Container ve WSL 2 ortamlarında da kendi hosts dosyaları ve DNS ayarları vardır.
Refused ile timeout'u aynı şey sanmak. Biri "dinleyen yok", diğeri "paket düşüyor"; birinde sunucuya, diğerinde firewall'a bakarsın.
Küçük Bir Deneme
★☆☆ Kendi sunucunda sudo ss -tlnp çıktısındaki her satır için hangi process'in, hangi adreste dinlediğini ve bunun dışarıdan erişilebilir olması gerekip gerekmediğini yaz.
★★☆ Bookstore.Api'yi geçici olarak http://0.0.0.0:5000'e bağla ve başka bir makineden nc -zv ile 5000'in artık açık olduğunu gör. Sonra ufw ile yalnızca 22 ve 80'i açıp aynı testi tekrarla: refused mı, timeout mu?
★★★ İstemcide /etc/hosts'a yanlış bir satır, sunucuda bir ufw kuralı ve unit'e yanlış bir port koyması için bir arkadaşından yardım iste; figürdeki sırayla yalnızca bu yazıdaki komutları kullanarak üçünü de bul ve her birinin hata mesajını not et.
Network tarafında araçlar hazır. Bir sonraki yazıda sunucuya yazılım kuruyoruz: apt, repository'ler, deb822 formatı, signed-by key'ler ve sunucuya .NET ile Node kurarken feed'leri karıştırmamak.