Last chance! 50% off DataCamp Premium
Sale ends in
Saat bekerja dengan container Docker, salah satu aspek paling krusial yang saya temui adalah mengaktifkan komunikasi jaringan antara container dan dunia luar. Tanpa konfigurasi port yang tepat, aplikasi yang dikontainerisasi akan tetap terisolasi dan tidak dapat diakses. Dalam artikel ini, saya akan memandu Anda melalui proses mengekspos port Docker, sebuah keterampilan fundamental bagi siapa pun yang bekerja dengan aplikasi terkontainerisasi.
Panduan ini dirancang khusus untuk pengembang perangkat lunak, engineer DevOps, dan profesional TI yang perlu membuat aplikasi terkontainerisasi mereka dapat diakses oleh pengguna, layanan lain, atau sistem eksternal. Baik Anda menerapkan server web, API, atau database di dalam container, memahami eksposur port sangat penting untuk membangun sistem berbasis Docker yang fungsional.
Di akhir artikel ini, Anda akan memiliki pemahaman yang jelas tentang cara kerja jaringan Docker, berbagai metode untuk mengekspos port, dan teknik praktis untuk menerapkan konsep-konsep ini dalam proyek Anda sendiri.
Jika Anda baru mengenal Docker, pertimbangkan untuk mengikuti salah satu kursus kami, seperti Introduction to Docker, Containerization and Virtualization with Docker and Kubernetes, Intermediate Docker, atau Containerization and Virtualization Concepts.
Sebelum masuk ke detail teknis, penting untuk memahami apa arti eksposur port dalam ekosistem Docker.
Container Docker dirancang dengan isolasi sebagai prinsip inti. Isolasi ini dicapai melalui fitur kernel Linux seperti namespace dan cgroups. Namespace menyediakan isolasi proses, memastikan bahwa proses dalam sebuah container tidak dapat melihat atau berinteraksi dengan proses di container lain atau sistem host. Di sisi lain, cgroups mengendalikan alokasi sumber daya, membatasi seberapa banyak CPU, memori, dan sumber daya lainnya yang dapat dikonsumsi oleh container.
Dari perspektif jaringan, setiap container menerima namespace jaringannya sendiri, lengkap dengan antarmuka Ethernet virtual (pasangan veth). Antarmuka ini secara default terhubung ke jaringan bridge Docker, memungkinkan komunikasi antar-container sambil mempertahankan isolasi dari jaringan host. Anggaplah ini seperti setiap container memiliki alamat jaringan privatnya sendiri, yang tidak terlihat oleh dunia luar.
Dalam konfigurasi defaultnya, sebuah container Docker sepenuhnya terisolasi. Layanan yang berjalan di dalamnya dapat saling berkomunikasi, tetapi tidak dapat dijangkau dari luar container, bahkan dari mesin host. Di sinilah eksposur port menjadi diperlukan.
Saat bekerja dengan konfigurasi port Docker, Anda akan menemukan dua konsep yang terkait namun berbeda: expose dan publish.
Expose port pada dasarnya adalah fitur dokumentasi. Ketika Anda mengekspos port di Docker, Anda menambahkan metadata ke image container Anda, yang menunjukkan bahwa aplikasi dalam container mendengarkan pada port-port tertentu.
Namun, mengekspos port tidak benar-benar membuatnya dapat diakses dari luar container. Ini berfungsi sebagai dokumentasi bagi pengguna image Anda.
Publish port adalah yang benar-benar membuat layanan dalam container Anda tersedia bagi dunia luar. Publish membuat pemetaan antara port pada mesin host dan port di dalam container. Saat Anda memublikasikan port, Docker mengonfigurasi jaringan host untuk meneruskan trafik dari port host yang ditentukan ke port container yang sesuai.
Berikut kapan menggunakan masing-masing pendekatan:
Sekarang kita memahami konsepnya, mari lihat aspek praktis mengekspos port Docker dalam berbagai konteks.
Cara paling dasar untuk mengekspos port adalah dengan menggunakan instruksi EXPOSE dalam Dockerfile Anda. Ini menyatakan port yang dirancang untuk digunakan oleh container.
FROM my-image:latest
EXPOSE 80
EXPOSE 443
Pada contoh ini, saya telah menentukan bahwa container akan menggunakan port 80 dan 443, yang merupakan standar untuk trafik HTTP (Hypertext Transfer Protocol) dan HTTPS (Hypertext Transfer Protocol Secure). Anda juga dapat menggabungkannya menjadi satu instruksi:
EXPOSE 80 443
Saat menentukan port, praktik yang baik adalah menyertakan protokolnya, TCP (Transmission Control Protocol) atau UDP (User Datagram Protocol) jika aplikasi Anda menggunakan protokol tertentu:
EXPOSE 53/udp
EXPOSE 80/tcp
Jika tidak ada protokol yang ditentukan, secara default dianggap TCP. Ini sesuai untuk sebagian besar aplikasi web, tetapi layanan seperti DNS (Domain Name System) atau server gim sering memerlukan UDP.
Dari perspektif keamanan, saya menyarankan hanya mengekspos port yang benar-benar dibutuhkan aplikasi Anda. Setiap port yang diekspos merepresentasikan potensi vektor serangan, sehingga meminimalkan jumlah port yang diekspos mengikuti prinsip least privilege.
Walaupun instruksi EXPOSE mendokumentasikan port yang digunakan container, agar port tersebut dapat diakses dari host atau mesin lain, Anda perlu memublikasikannya saat menjalankan container.
Berikut cara melakukan binding secara eksplisit antara port container dan port host tertentu:
docker run -p 8081:80 my-image
Flag -p (atau --publish) memungkinkan Anda melakukan binding satu port atau rentang port di container ke host.
Perintah ini memetakan port 80 di dalam container ke port 8081 di host. Setelah menjalankan perintah ini, Anda dapat mengakses server web dengan membuka http://localhost:8081 di browser Anda.
Untuk kemudahan selama pengembangan, Docker menyediakan flag -P (P kapital) atau --publish-all, yang secara otomatis memublikasikan semua port yang diekspos ke port acak bernomor tinggi di host:
docker run -P my-image
Untuk mengetahui port host mana yang ditetapkan, saya dapat menggunakan perintah docker ps:
docker ps
Ini akan menampilkan keluaran seperti:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a7c53d9413bf image "/docker-.…" 10 s ago Up 9 s 0.0.0.0:49153->80/tcp, :::49153->80/tcp wizardl
Di sini, port 80 di dalam container dipetakan ke port 49153 di host.
Saat bekerja dengan layanan yang menggunakan TCP dan UDP pada port yang sama (seperti server DNS), Anda perlu menentukan protokol saat memublikasikan:
docker run -p 53:53/tcp -p 53:53/udp dns-server
Untuk aplikasi multi-container, Docker Compose menyediakan cara yang lebih mudah untuk mengonfigurasi jaringan dan port.
Dalam berkas YAML Docker Compose, Anda dapat menentukan pemetaan port di bawah kunci ports untuk setiap layanan:
services:
web:
image: my-image
ports:
- "8081:80"
- "443:443"
api:
build: ./api
ports:
- "3000:3000"
Docker Compose membuat pembedaan penting antara ports dan expose. Bagian ports membuat pemetaan port yang dipublikasikan dan dapat diakses dari luar, sedangkan expose hanya membuat port tersedia bagi layanan yang tertaut dalam jaringan Compose yang sama:
services:
web:
image: my-image
ports:
- "8081:80"
database:
image: postgres
expose:
- "5432"
Pada contoh ini, layanan web dapat diakses dari host pada port 8081, tetapi database PostgreSQL hanya dapat diakses oleh layanan lain dalam berkas Compose, tidak langsung dari host.
Untuk konfigurasi yang fleksibel, terutama lintas lingkungan yang berbeda, saya dapat menggunakan variabel lingkungan dalam pemetaan port:
services:
web:
image: my-image
ports:
- "${WEB_PORT:-8081}:80"
Sintaks ini memungkinkan saya menentukan port host melalui variabel lingkungan (WEB_PORT), dengan default 8081 jika tidak diatur.
Mari kita lalui contoh lengkap mengekspos port Docker untuk aplikasi web dengan backend database.
Bayangkan kita membangun aplikasi web sederhana yang terdiri dari layanan API Python Flask dan database PostgreSQL. Layanan API mendengarkan pada port 5000, dan PostgreSQL menggunakan port defaultnya 5432.
Berikut tampilan aplikasi Flask sederhana kita (app.py). Aplikasi ini akan membuat database myapp dan akan mengambil data dari tabel items, setelah tabel ini dibuat di dalam database.
from flask import Flask, jsonify
import os
import psycopg2
app = Flask(__name__)
# Database connection parameters from environment variables
DB_HOST = os.environ.get('DB_HOST', 'db')
DB_NAME = os.environ.get('DB_NAME', 'myapp')
DB_USER = os.environ.get('DB_USER', 'postgres')
DB_PASS = os.environ.get('DB_PASSWORD', 'postgres')
@app.route('/')
def index():
return jsonify({'message': 'API is running'})
@app.route('/items')
def get_items():
# Connect to the PostgreSQL database
conn = psycopg2.connect(
host=DB_HOST,
database=DB_NAME,
user=DB_USER,
password=DB_PASS
)
# Create a cursor and execute a query
cur = conn.cursor()
cur.execute('SELECT id, name FROM items')
# Fetch results and format as list of dictionaries
items = [{'id': row[0], 'name': row[1]} for row in cur.fetchall()]
# Close connections
cur.close()
conn.close()
return jsonify(items)
if __name__ == '__main__':
# Run the Flask app, binding to all interfaces (important for Docker)
app.run(host='0.0.0.0', port=5000)
Selanjutnya, saya akan membuat berkas requirements.txt kita:
flask
psycopg2-binary
Sekarang, saya perlu membuat Dockerfile untuk layanan API Python kita:
FROM python:3.9-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Expose the port Flask runs on
EXPOSE 5000
# Command to run the application
CMD ["python", "app.py"]
Terakhir, saya akan membuat berkas Docker Compose untuk mengorkestrasi kedua layanan:
services:
api:
build:
context: .
dockerfile: Dockerfile
tags:
- "my-custom-image:latest"
container_name: my-custom-api
ports:
- "5000:5000"
environment:
- DB_HOST=db
- DB_NAME=myapp
- DB_USER=postgres
- DB_PASSWORD=postgres
depends_on:
- db
db:
container_name: my-custom-db
image: postgres:13
expose:
- "5432"
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
postgres_data:
Dalam konfigurasi ini:
5000, membuatnya dapat diakses dari mesin host.5432 tetapi tidak memublikasikannya, sehingga hanya dapat diakses oleh layanan lain dalam jaringan Compose.5432.Untuk menjalankan aplikasi ini, gunakan:
docker-compose up
Sekarang, saya dapat mengakses API di http://localhost:5000, tetapi instance PostgreSQL tidak dapat diakses langsung dari luar jaringan Docker, yang merupakan praktik keamanan yang baik untuk layanan database. Namun Anda dapat mengakses PostgreSQL dari container yang berjalan:
docker compose exec db psql -U postgres -d myapp
Perintah ini akan mengakses CLI PostgreSQL, tempat saya dapat melihat database dan membuat tabel items. Setelah dibuat, saya dapat melihat datanya di http://localhost:5000/items
Jika aplikasi Python saya perlu mengekspos beberapa port, saya dapat menentukannya semua di Dockerfile:
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 5000 8081 9090
CMD ["python", "app.py"]
Dockerfile ini mengekspos:
5000 untuk aplikasi Flask utama (saat ini aktif)8081 untuk dasbor pemantauan potensial 9090 untuk antarmuka debugging di masa mendatangPenting untuk dicatat bahwa meskipun saya telah mengekspos port 8081 dan 9090 di Dockerfile sebagai dokumentasi, aplikasi Flask saya saat ini hanya dikonfigurasi untuk mendengarkan pada port 5000. Port tambahan yang diekspos menunjukkan di mana fungsionalitas di masa depan mungkin akan diimplementasikan.
Untuk memublikasikan port yang diekspos dalam Dockerfile, saya menggunakan flag -p atau -P saat menjalankan container:
docker run -p 8081:5000 my-custom-image
Ini memetakan port 5000 di container (yang diekspos dalam Dockerfile dan tempat Flask mendengarkan) ke port 8081 di host. Setelah menjalankan perintah ini, saya dapat mengakses aplikasi Flask saya dengan membuka http://localhost:8081 di browser.
Saya juga dapat menggunakan flag -P untuk secara otomatis memublikasikan semua port yang diekspos ke port acak di host:
docker run -P my-custom-image
Untuk memeriksa port mana yang dipublikasikan, saya dapat menggunakan:
docker ps
Untuk informasi yang lebih detail, saya dapat menggunakan:
docker port [container_id]
5000/tcp -> 0.0.0.0:32768
8081/tcp -> 0.0.0.0:32769
9090/tcp -> 0.0.0.0:32770
Perintah ini menampilkan semua pemetaan port untuk container tertentu. Misalnya, jika aplikasi Flask saya hanya mendengarkan pada port 5000 tetapi saya telah mengekspos beberapa port di Dockerfile, docker port akan menunjukkan dengan tepat port host mana yang dipetakan ke port container mana. Jadi, jika port 5000 telah dipetakan ke 32768, saya dapat mengakses aplikasi Flask saya melalui http://localhost:32768
Bahkan dengan konfigurasi yang cermat, masalah dapat terjadi pada pemetaan port Docker. Berikut beberapa teknik yang saya gunakan untuk mendiagnosis dan menyelesaikan masalah umum.
Langkah pertama dalam mendiagnosis masalah terkait port adalah memverifikasi bahwa pemetaan telah disetel dengan benar. Saya menggunakan perintah berikut:
Daftar semua container yang berjalan beserta pemetaan portnya:
docker ps
Dapatkan info detail tentang container tertentu:
docker inspect [container_id]
Periksa pemetaan port untuk container tertentu:
docker port [container_id]
Perintah docker inspect memberikan informasi terperinci, termasuk pengaturan jaringan. Untuk berfokus pada detail terkait jaringan, gunakan:
docker inspect --format='{{json .NetworkSettings.Ports}}' [container_id] | jq
Jika Anda perlu melakukan analisis trafik yang lebih dalam, alat seperti tcpdump dan netstat dapat sangat berguna. Pertama, pastikan alat tersebut terpasang di dalam container. Jika container belum menyertakan alat ini, Anda dapat memasangnya dengan menjalankan perintah berikut:
docker exec -u root -it f5c6cac71492 bash -c "apt-get update && apt-get install -y net-tools tcpdump"
Untuk melihat port mana yang didengarkan aplikasi di dalam container:
docker exec -it [container_id] netstat -tuln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.11:42265 0.0.0.0:* LISTEN
udp 0 0 127.0.0.11:34267 0.0.0.0:*
Ini menunjukkan, misalnya, bahwa aplikasi Flask mendengarkan pada semua antarmuka di port 5000.
Untuk memantau trafik masuk dan keluar pada antarmuka jaringan utama container (biasanya eth0):
docker exec -it [container_id] tcpdump -i eth0
listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
Perintah ini memungkinkan Anda menelusuri trafik dan mengidentifikasi apakah permintaan mencapai container atau jika paket dijatuhkan atau salah diarahkan.
Docker memanfaatkan iptables untuk mengelola Network Address Translation (NAT) dan mengendalikan aliran trafik antara host dan container. Docker secara otomatis memasukkan aturan untuk meneruskan port host ke IP dan port container yang sesuai, sehingga akses ke layanan dalam container menjadi mulus.
Pada host Linux native, Anda dapat memeriksa aturan ini menggunakan perintah seperti:
sudo iptables -t nat -L DOCKER -n -v
Ini menunjukkan bagaimana Docker memetakan trafik masuk pada port host tertentu ke endpoint container. Berdasarkan berkas Docker Compose sebelumnya, Anda akan melihat port yang diteruskan:
Chain DOCKER (2 references)
pkts bytes target prot opt in out source destination
50 3000 RETURN all -- docker0 * 0.0.0.0/0 0.0.0.0/0
100 6000 RETURN all -- br-xxxxxxx * 0.0.0.0/0 0.0.0.0/0
500 30000 DNAT tcp -- !br-xxxxxxx * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5000 to:172.18.0.2:5000
400 24000 DNAT tcp -- !br-xxxxxxx * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8081 to:172.18.0.2:5000
300 18000 DNAT tcp -- !br-xxxxxxx * 0.0.0.0/0 0.0.0.0/0 tcp dpt:9090 to:172.18.0.2:5000
Berikut penjelasannya:
DNAT mengarahkan ulang trafik dari host ke container.RETURN memastikan routing yang tepat untuk trafik jaringan internal Docker.Namun, bergantung pada lingkungan Anda (misalnya, Docker Desktop di Windows atau WSL2), Docker mungkin menangani penerusan port secara berbeda, dan aturan iptables mungkin tidak terlihat atau tidak dapat diubah dari dalam sistem. Dalam kasus ini, Anda dapat memeriksa penerusan port dengan perintah berikut:
sudo ss -tulnp
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port
tcp LISTEN 0 4096 *:5000 *:*
tcp LISTEN 0 4096 *:8081 *:*
tcp LISTEN 0 4096 *:9090 *:*
Tips pemecahan masalah:
docker ps dan docker port [container].netstat dan tcpdump untuk memverifikasi perilaku jaringan aplikasi Anda.Mengekspos port Docker adalah keterampilan fundamental yang menjembatani kesenjangan antara isolasi container dan kegunaan praktis. Sepanjang artikel ini, saya telah menjelaskan cara kerja model jaringan Docker, perbedaan antara mengekspos dan memublikasikan port, serta memberikan contoh praktis untuk berbagai skenario.
Ingat poin-poin kunci ini:
EXPOSE dalam Dockerfile untuk mendokumentasikan port yang digunakan aplikasi Anda-p atau -P saat menjalankan container untuk membuat port tersebut dapat diakses dari luarMenguasai konsep dan teknik ini akan memungkinkan Anda merancang aplikasi terkontainerisasi yang lebih tangguh, yang berkomunikasi secara efektif baik secara internal maupun dengan dunia luar. Baik Anda mengembangkan aplikasi web sederhana atau arsitektur microservices yang kompleks, pengelolaan port yang tepat sangat krusial untuk keberhasilan.
Seiring container terus mendominasi strategi penerapan aplikasi modern, kemampuan untuk dengan percaya diri mengelola jaringan dan port Docker akan tetap menjadi keterampilan esensial dalam perangkat kerja setiap pengembang.
Untuk terus belajar, pastikan untuk melihat sumber daya berikut:
Mengekspos port di Docker adalah cara untuk mendokumentasikan port mana yang didengarkan oleh aplikasi terkontainerisasi Anda, tetapi ini tidak membuat port tersebut dapat diakses di luar container.
Anda memublikasikan port menggunakan flag -p dengan docker run atau bagian ports di Docker Compose, yang memetakan port host ke port container.
EXPOSE digunakan dalam Dockerfile untuk mendeklarasikan port sebagai dokumentasi, sedangkan -p memublikasikan dan memetakan port tersebut untuk akses eksternal.
Ya, Anda dapat mengekspos beberapa port menggunakan beberapa instruksi EXPOSE dalam Dockerfile atau dengan menentukan beberapa port di bagian ports Docker Compose.
Gunakan perintah seperti docker ps, docker port, docker inspect, dan alat jaringan di dalam container (misalnya, netstat, tcpdump) untuk memverifikasi pemetaan port dan aliran trafik.
Sebagai Founder Martin Data Solutions serta Data Scientist, ML, dan AI Engineer lepas, saya memiliki portofolio beragam dalam Regression, Classification, NLP, LLM, RAG, Neural Networks, Ensemble Methods, dan Computer Vision.
Dalam setiap proyek yang saya tangani, saya memastikan menerapkan praktik terkini dalam rekayasa perangkat lunak dan DevOps, seperti CI/CD, code linting, formatting, pemantauan model, pelacakan eksperimen, dan penanganan error yang tangguh. Saya berkomitmen menyajikan solusi menyeluruh, mengubah wawasan data menjadi strategi praktis yang membantu bisnis bertumbuh dan memaksimalkan pemanfaatan data science, machine learning, dan AI.
Top Docker Courses
Program
Kursus
Program
blogs
David Woods
13 mnt
blogs
Dario Radečić
15 mnt
blogs
Hugo Bowne-Anderson
13 mnt

blogs
Javier Canales Luna
14 mnt