{"id":221,"date":"2026-09-04T12:00:00","date_gmt":"2026-09-04T12:00:00","guid":{"rendered":"https:\/\/konteynerium.com\/?p=221"},"modified":"2026-10-06T11:34:00","modified_gmt":"2026-10-06T11:34:00","slug":"uctan-uca-ornek-proje-nodejs-api-production","status":"publish","type":"post","link":"https:\/\/konteynerium.com\/index.php\/2026\/09\/04\/uctan-uca-ornek-proje-nodejs-api-production\/","title":{"rendered":"U\u00e7tan Uca \u00d6rnek Proje: Bir Node.js API&#8217;sini S\u0131f\u0131rdan Production&#8217;a Ta\u015f\u0131mak"},"content":{"rendered":"<p>\u015eimdiye kadar Dockerfile yazmay\u0131, Compose ile \u00e7oklu servis y\u00f6netmeyi, volume&#8217;leri, networking&#8217;i, registry&#8217;i, CI\/CD&#8217;yi ve izlemeyi tek tek g\u00f6rd\u00fck. Bu yaz\u0131da hepsini bir araya getirip, basit bir Node.js API&#8217;sini s\u0131f\u0131rdan al\u0131p ger\u00e7ek\u00e7i bir production kurulumuna ta\u015f\u0131yoruz \u2014 bu portaldaki t\u00fcm konular\u0131n birle\u015fti\u011fi u\u00e7tan uca bir referans.<\/p>\n<h2>Proje: k\u00fc\u00e7\u00fck bir g\u00f6rev listesi API&#8217;si<\/h2>\n<p>Node.js\/Express ile yaz\u0131lm\u0131\u015f, PostgreSQL&#8217;e ba\u011flanan basit bir REST API&#8217;miz oldu\u011funu varsayal\u0131m. Klas\u00f6r yap\u0131m\u0131z \u015f\u00f6yle:<\/p>\n<pre><code>benim-api\/\n\u251c\u2500\u2500 src\/\n\u2502   \u2514\u2500\u2500 server.js\n\u251c\u2500\u2500 package.json\n\u251c\u2500\u2500 Dockerfile\n\u251c\u2500\u2500 .dockerignore\n\u2514\u2500\u2500 docker-compose.yml<\/code><\/pre>\n<h2>1. Ad\u0131m: Multi-stage, g\u00fcvenli bir Dockerfile<\/h2>\n<p>\u00d6nceki yaz\u0131larda g\u00f6rd\u00fc\u011f\u00fcm\u00fcz iyi pratikleri (k\u00fc\u00e7\u00fck taban imaj, root olmayan kullan\u0131c\u0131, sabit s\u00fcr\u00fcm etiketi, health check) tek bir Dockerfile&#8217;da birle\u015ftiriyoruz:<\/p>\n<pre><code>FROM node:20.15.1-alpine3.20 AS base\nWORKDIR \/app\nCOPY package*.json .\/\n\nFROM base AS deps\nRUN npm ci --production\n\nFROM base AS runtime\nCOPY --from=deps \/app\/node_modules .\/node_modules\nCOPY src\/ .\/src\n\nRUN addgroup -S uygulama && adduser -S uygulama -G uygulama\nUSER uygulama\n\nEXPOSE 3000\nHEALTHCHECK --interval=30s --timeout=3s --retries=3 \\\n  CMD wget -q -O- http:\/\/localhost:3000\/saglik || exit 1\n\nCMD [\"node\", \"src\/server.js\"]<\/code><\/pre>\n<pre><code># .dockerignore\nnode_modules\n.git\n.env\n*.log<\/code><\/pre>\n<h2>2. Ad\u0131m: Compose ile API + veritaban\u0131 + reverse proxy<\/h2>\n<p>Volume&#8217;ler kal\u0131c\u0131 veriyi, servis isimleri (network) container&#8217;lar aras\u0131 ileti\u015fimi, <code>depends_on<\/code> ba\u015flatma s\u0131ras\u0131n\u0131 y\u00f6netir:<\/p>\n<pre><code>services:\n  api:\n    build: .\n    environment:\n      DB_HOST: db\n      DB_PASSWORD: ${DB_PASSWORD}\n    depends_on:\n      - db\n    restart: unless-stopped\n\n  db:\n    image: postgres:16.3-alpine\n    environment:\n      POSTGRES_PASSWORD: ${DB_PASSWORD}\n    volumes:\n      - pg_data:\/var\/lib\/postgresql\/data\n    restart: unless-stopped\n\n  proxy:\n    image: nginx:1.27-alpine\n    ports:\n      - \"443:443\"\n      - \"80:80\"\n    volumes:\n      - .\/nginx.conf:\/etc\/nginx\/conf.d\/default.conf:ro\n    depends_on:\n      - api\n    restart: unless-stopped\n\nvolumes:\n  pg_data:<\/code><\/pre>\n<p>Veritaban\u0131 portu (<code>5432<\/code>) d\u0131\u015far\u0131ya hi\u00e7 a\u00e7\u0131lm\u0131yor \u2014 sadece <code>api<\/code> servisi, ayn\u0131 Compose a\u011f\u0131 \u00fczerinden <code>db<\/code> ismiyle eri\u015febiliyor. D\u0131\u015far\u0131dan gelen t\u00fcm trafik <code>proxy<\/code> (Nginx) \u00fczerinden ge\u00e7iyor; bu hem TLS sonland\u0131rmay\u0131 tek yerde toplar hem de API&#8217;yi do\u011frudan internete a\u00e7maz.<\/p>\n<div class=\"callout\">\n  <span class=\"glyph\">\u2713<\/span><\/p>\n<p>\u015eifreler ve gizli de\u011ferler (<code>DB_PASSWORD<\/code>) do\u011frudan <code>docker-compose.yml<\/code>&#8216;a yaz\u0131lmad\u0131 \u2014 bir <code>.env<\/code> dosyas\u0131ndan okunuyor (<code>${DB_PASSWORD}<\/code>). <code>.env<\/code> dosyas\u0131n\u0131 <code>.gitignore<\/code>&#8216;a eklemeyi unutmay\u0131n.<\/p>\n<\/div>\n<h2>3. Ad\u0131m: CI\/CD ile otomatik build, tarama ve push<\/h2>\n<p>\u00d6nceki &#8220;CI\/CD ile Docker&#8221; yaz\u0131m\u0131zdaki pipeline&#8217;\u0131 bu projeye uygularsak: kod <code>main<\/code>&#8216;e push edilince imaj build edilir \u2192 Trivy ile taran\u0131r \u2192 ge\u00e7erse \u00e7ok mimarili (amd64 + arm64) olarak registry&#8217;e g\u00f6nderilir \u2192 sunucuda otomatik \u00e7ekilip devreye al\u0131n\u0131r. T\u00fcm bu ad\u0131mlar tek bir <code>.github\/workflows\/deploy.yml<\/code> dosyas\u0131nda birle\u015fir; her ad\u0131m\u0131n detay\u0131n\u0131 ilgili yaz\u0131lar\u0131m\u0131zda bulabilirsiniz.<\/p>\n<h2>4. Ad\u0131m: Sunucuda devreye alma<\/h2>\n<pre><code># Sunucuda ilk kurulum\ngit clone https:\/\/github.com\/kullanici-adi\/benim-api.git\ncd benim-api\necho \"DB_PASSWORD=guclu-bir-parola\" > .env\ndocker compose up -d\n\n# CI\/CD her yeni s\u00fcr\u00fcmde bu ad\u0131m\u0131 otomatik \u00e7al\u0131\u015ft\u0131r\u0131r\ndocker compose pull\ndocker compose up -d --no-deps --build api<\/code><\/pre>\n<h2>5. Ad\u0131m: Sa\u011fl\u0131\u011f\u0131 ve kaynak kullan\u0131m\u0131n\u0131 izleme<\/h2>\n<pre><code># H\u0131zl\u0131, tek sunuculuk kontrol\ndocker compose ps        # health check durumlar\u0131 burada g\u00f6r\u00fcn\u00fcr\ndocker stats\ndocker compose logs -f api<\/code><\/pre>\n<p>Trafik b\u00fcy\u00fcd\u00fck\u00e7e, &#8220;Container \u0130zleme ve Loglama&#8221; yaz\u0131m\u0131zda anlatt\u0131\u011f\u0131m\u0131z cAdvisor + Prometheus + Grafana \u00fc\u00e7l\u00fcs\u00fcn\u00fc ayn\u0131 <code>docker-compose.yml<\/code>&#8216;a birer servis olarak eklemek, tek komutla (<code>docker compose up -d<\/code>) t\u00fcm izleme y\u0131\u011f\u0131n\u0131n\u0131 da aya\u011fa kald\u0131r\u0131r.<\/p>\n<h2>T\u00fcm par\u00e7alar\u0131n birbirine ba\u011fland\u0131\u011f\u0131 yer<\/h2>\n<table>\n<thead>\n<tr>\n<th>\u0130htiya\u00e7<\/th>\n<th>Kullan\u0131lan ara\u00e7\/kavram<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>K\u00fc\u00e7\u00fck, g\u00fcvenli imaj<\/td>\n<td>Multi-stage Dockerfile, alpine taban imaj, USER, HEALTHCHECK<\/td>\n<\/tr>\n<tr>\n<td>Kal\u0131c\u0131 veri<\/td>\n<td>Named volume (<code>pg_data<\/code>)<\/td>\n<\/tr>\n<tr>\n<td>Servisler aras\u0131 ileti\u015fim<\/td>\n<td>Compose&#8217;un otomatik olu\u015fturdu\u011fu a\u011f, servis ismiyle DNS<\/td>\n<\/tr>\n<tr>\n<td>D\u0131\u015f d\u00fcnyaya g\u00fcvenli eri\u015fim<\/td>\n<td>Nginx reverse proxy, sadece 80\/443 d\u0131\u015far\u0131 a\u00e7\u0131k<\/td>\n<\/tr>\n<tr>\n<td>\u0130maj da\u011f\u0131t\u0131m\u0131<\/td>\n<td>GitHub Container Registry \/ Docker Hub<\/td>\n<\/tr>\n<tr>\n<td>Otomasyon<\/td>\n<td>GitHub Actions: build \u2192 tara \u2192 push \u2192 deploy<\/td>\n<\/tr>\n<tr>\n<td>\u00c7oklu donan\u0131m deste\u011fi<\/td>\n<td>Buildx ile <code>linux\/amd64,linux\/arm64<\/code><\/td>\n<\/tr>\n<tr>\n<td>Sa\u011fl\u0131k ve g\u00f6zlemlenebilirlik<\/td>\n<td>HEALTHCHECK, docker stats, Prometheus + Grafana<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Bu portaldaki her yaz\u0131, burada bir araya gelen par\u00e7alardan birini derinlemesine ele al\u0131yor. Kendi projeniz i\u00e7in bu \u015fablonu ba\u015flang\u0131\u00e7 noktas\u0131 olarak kullanabilir, ihtiyac\u0131n\u0131za g\u00f6re (Kubernetes&#8217;e ge\u00e7i\u015f, \u00e7oklu b\u00f6lge da\u011f\u0131t\u0131m\u0131, otomatik \u00f6l\u00e7ekleme gibi) \u00fczerine katman katman ekleyebilirsiniz.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Multi-stage Dockerfile, volume, network, reverse proxy, CI\/CD ve izlemeyi tek bir ger\u00e7ek\u00e7i projede birle\u015ftiren, portaldaki t\u00fcm konular\u0131n bulu\u015ftu\u011fu u\u00e7tan uca referans yaz\u0131.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[6],"tags":[],"seviye":[9],"class_list":["post-221","post","type-post","status-publish","format-standard","hentry","category-production-uygulamalar","seviye-ileri"],"_links":{"self":[{"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/posts\/221","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/comments?post=221"}],"version-history":[{"count":1,"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/posts\/221\/revisions"}],"predecessor-version":[{"id":580,"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/posts\/221\/revisions\/580"}],"wp:attachment":[{"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/media?parent=221"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/categories?post=221"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/tags?post=221"},{"taxonomy":"seviye","embeddable":true,"href":"https:\/\/konteynerium.com\/index.php\/wp-json\/wp\/v2\/seviye?post=221"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}