소개
Docker는 가볍고 가상화되었으며 이식성이 뛰어난 소프트웨어 정의 표준화 환경인 컨테이너 플랫폼입니다. 이를 통해 소프트웨어는 물리적 호스트 머신에서 실행되는 다른 소프트웨어와 격리되어 실행될 수 있습니다. Docker는 소프트웨어 개발의 지속적 개발 및 통합(CI/CD) 측면을 정의하는 핵심 구성 요소입니다. 이는 가상 머신에 대한 가벼운 대안을 제공하며 개발자가 분산 애플리케이션 아키텍처를 누릴 수 있도록 합니다. Docker 생태계에 대한 철저한 개요는 이 기사를 확인해 보세요..
Docker로 애플리케이션을 빌드하는 프로세스는 개발자가 애플리케이션용 이미지를 생성하는 것부터 시작됩니다. 그런 다음 이미지가 컨테이너 내부에 배포됩니다. 이미지는 애플리케이션 코드, 라이브러리, 구성 파일, 환경 변수 및 런타임 환경과 같은 애플리케이션의 정의 구성 요소를 보유합니다. 이미지는 컨테이너 내부의 환경을 표준화하여 컨테이너화에 이식성 특성을 부여합니다. Node.js는 웹 브라우저 외부에서 JavaScript 코드를 실행할 수 있는 오픈 소스, 크로스 플랫폼 백엔드 JavaScript 런타임 환경입니다. Chrome의 V8 JavaScript 엔진을 기반으로 구축되었습니다.. Express.js는 Node.js 위에서 실행되는 미니멀한 백엔드 JavaScript 프레임워크입니다.
이 튜토리얼에서는 Express 프레임워크에서 실행되는 웹사이트용 이미지를 생성해 보겠습니다. 프론트엔드를 더 보기 좋게 만들기 위해 프론트엔드 라이브러리인 Bootstrap을 사용할 것입니다. 이미지를 생성한 후에는 컨테이너를 빌드하고 이를 Docker Hub에 푸시할 것입니다. Docker Hub를 사용하면 개발자가 컨테이너화된 애플리케이션을 호스팅하여 모든 Docker 환경에 쉽게 배포할 수 있습니다. 컨테이너가 Docker Hub에 호스팅되면 이를 가져와서 실제로 웹사이트를 서비스할 다른 이미지를 빌드할 것입니다.
전제 조건
이 튜토리얼은 실습 위주로 진행됩니다. 따라 할 수 있는 환경을 만들어야 합니다.
- 초기 운영 환경으로 Ubuntu 20.04가 설치되어 있어야 하며, sudo 권한이 있는 non-root 사용자를 생성해야 합니다. non-root 사용자로 로그인하고 다음 단계를 진행하세요.
- Docker를 설치해야 합니다. 다음 튜토리얼의 1, 2, 3, 4단계를 따르세요: Ubuntu에서 Docker를 설치하고 작동하는 방법. 이는 모든 Ubuntu 배포판에서 작동할 것입니다.
- 아직 Docker Hub 계정이 없다면 계정을 생성하세요. 다음 링크에서 Docker Hub 빠른 시작 가이드.
- 를 확인할 수 있습니다. Node.js와 NPM을 설치하세요. NPM은 JavaScript 패키지 관리자입니다. 다음 Node 및 npm 설치에 관한 지침을 따르실 수 있습니다..
1단계: 애플리케이션 종속성 구성
이미지를 생성하기 전에 애플리케이션 소스 코드를 생성해야 합니다. 애플리케이션 소스 코드에는 컨테이너에 복사될 코드, 정적 콘텐츠 및 종속성이 포함됩니다. non-root 사용자의 홈 디렉터리에 프로젝트용 디렉터리를 생성하는 것부터 시작합니다. 여기서는 이 디렉터리를 node_express라고 부르겠지만, 원하는 디렉터리 이름을 자유롭게 사용하셔도 됩니다:
|
1 |
mkdir node_express |
다음으로, 이 디렉터리로 이동합니다:
|
1 |
cd node_express |
이곳이 애플리케이션 루트 디렉터리가 됩니다. node.js 애플리케이션은 루트 폴더에 package.json 파일이 있을 것으로 예상합니다. Npm은 이 파일을 사용하여 애플리케이션에 필요한 종속성을 결정합니다. 다음 명령을 입력하여 이 파일을 생성하세요:
|
1 |
nano package.json |
그 후, 파일에 다음 코드 스니펫을 추가합니다. 이름, 작성자, 설명 및 진입점 파일은 원하는 대로 업데이트할 수 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
{ "name": "node-express-docker-image", "version": "1.0.0", "description": "Nodejs Express Docker Image Example", "author": "hackins", "main": "index.js", "license": "ISC", "keywords": [ "nodejs", "express", "bootstrap" ], "dependencies": { "express": "^4.17.1" } } |
보시다시피 이 파일은 프로젝트 이름, 버전, 작성자 및 애플리케이션 코드가 공유될 라이선스를 지정합니다. 다음에서 중복을 방지하기 위해 프로젝트에 짧고 설명적인 이름을 사용하는 것이 좋습니다: npm registry. 우리는 이 프로젝트에 ISC license를 지정했으며, 이는 애플리케이션 코드를 자유롭게 복사, 수정 또는 배포할 수 있도록 허용합니다.
가장 중요한 것은 파일에서 다음 지시문을 유념해야 한다는 점입니다:
- “
main”: 이 지시문은 애플리케이션의 진입점을 지정하며, 여기서는 index.js로 설정했습니다. 곧 이 파일을 생성할 것입니다. - “
dependencies”: 이 지시문은 다음 명령을 실행할 때 npm 레지스트리에서 가져올 애플리케이션 의존성을 지정합니다:npm명령. 우리의 경우 Express 버전 4.17.1 이상을 원합니다.
이제 Ctrl + O를 눌러 파일을 저장할 수 있습니다. 그런 다음 Ctrl + X를 눌러 파일을 닫습니다. 다음으로, 아래 명령을 실행하여 의존성을 설치합니다:
|
1 |
npm install |
이 명령은 다음에 지정된 애플리케이션 의존성을 설치합니다: package.json 파일, 위치는 node_modules 디렉터리 내부입니다. 이 디렉터리는 명령을 처음 실행할 때 자동으로 생성되었습니다. 애플리케이션 의존성이 설치되었으므로 이제 애플리케이션 코드를 추가하기 시작할 수 있습니다.
2단계: 애플리케이션 코드 파일 추가하기
우리는 다음의 제공을 받아 기본 레시피 웹사이트를 제작할 것입니다: allrecipes. 애플리케이션의 메인 진입점은 index.js 파일입니다. 우리는 프로젝트의 다양한 페이지와 정적 에셋을 담을 views 디렉터리를 추가할 것입니다. 웹사이트에는 소개 정보와 일부 레시피 링크가 포함된 랜딩 페이지가 있게 됩니다.
랜딩 페이지 코드는 다음 파일에 위치하게 됩니다: home.html 파일. 먼저, 다음 명령을 입력하여 index.js 파일을 생성합니다:
|
1 |
nano index.js |
Express 애플리케이션을 가져오고 생성하는 다음 코드를 추가합니다. 또한 Router 객체, 기본 디렉터리 및 이 앱이 서비스될 포트를 지정합니다:
|
1 2 3 4 5 6 |
const express = require('express'); const app = express(); const router = express.Router(); const path = __dirname + '/views/'; const port = 8090; |
require 은 모듈을 로드하는 JavaScript 함수입니다. 이 경우 express 모듈을 로드하고 있습니다. 그런 다음 가져온 모듈을 사용하여 express 및 router 객체를 생성합니다. router 객체는 튜토리얼을 진행하면서 이 객체에 추가될 HTTP 메서드 호출에 응답하여 앱의 라우팅 기능을 수행합니다.
우리는 또한 path와 port를 설정했습니다. path 상수는 코드의 기본 디렉터리를 정의합니다. 우리의 경우 프로젝트 루트 디렉터리 내부의 views 하위 디렉터리입니다. port는 express 앱이 수신 대기해야 하는 포트를 지정하며, 이 예제에서는 다음과 같이 설정했습니다: 8090.
상수가 준비되면, 다음 객체를 사용하여 애플리케이션의 일부 라우트를 지정할 수 있습니다: router 객체. 라우트를 지정하려면 다음 코드를 index.js 파일에 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
router.use(function (req,res,next) { console.log('/' + req.method); next(); }); router.get('/', function(req,res){ res.sendFile(path + 'home.html'); }); router.get('/lasagna', function(req,res){ res.sendFile(path + 'lasagna.html'); }); router.get('/guacamole', function(req,res){ res.sendFile(path + 'guacamole.html'); }); router.get('/banana-bread', function(req,res){ res.sendFile(path + 'banana_bread.html'); }); |
다음을 사용하여 라우트에 미들웨어를 추가할 수 있습니다: router.use 함수. 이 경우, 라우터의 요청을 애플리케이션 라우트로 전달하기 전에 기록하는 함수를 추가합니다. 애플리케이션의 베이스에 대한 GET 요청은 다음을 반환합니다: home.html 페이지. 그런 다음 특정 레시피 페이지에 대한 GET 요청을 사용하여 검색할 세 가지 레시피에 대한 페이지를 추가했습니다.
마지막으로 다음 코드를 추가하여 router 미들웨어와 애플리케이션 정적 자산을 마운트합니다. 또한 express 애플리케이션이 다음 포트에서 수신 대기하도록 지시합니다. 8090:
|
1 2 3 4 5 6 |
app.use(express.static(path)); app.use('/', router); app.listen(port, function () { console.log('Nodejs Express Example App listening on port ' + port) }) |
완성된 index.js 파일은 다음과 같아야 합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
const express = require('express'); const app = express(); const router = express.Router(); const path = __dirname + '/views/'; const port = 8090; router.use(function (req,res,next) { console.log('/' + req.method); next(); }); router.get('/', function(req,res){ res.sendFile(path + 'home.html'); }); router.get('/lasagna', function(req,res){ res.sendFile(path + 'lasagna.html'); }); router.get('/guacamole', function(req,res){ res.sendFile(path + 'guacamole.html'); }); router.get('/banana-bread', function(req,res){ res.sendFile(path + 'banana_bread.html'); }); app.use(express.static(path)); app.use('/', router); app.listen(port, function () { console.log('Nodejs Express Example App listening on port ' + port) }) |
이제 파일을 저장하고 닫아도 됩니다. 다음 단계는 정적 웹 페이지를 views 디렉터리에 추가하는 것입니다. 다음 명령을 입력하여 디렉터리를 생성하는 것부터 시작합니다:
|
1 |
mkdir views |
다음 명령을 입력하여 home.html 랜딩 페이지 파일을 엽니다:
|
1 |
nano views/home.html |
파일에 다음 코드를 추가합니다. 이 코드는 Bootstrap을 가져오고 웹사이트 방문자에게 웹사이트가 무엇인지에 대한 정보를 제공합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Hugo 0.80.0"> <title>Awesome Recipes</title> <!-- Bootstrap core CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">네비게이션 토글</span> </button> <a class="navbar-brand" href="#">Awesome Recipes</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">홈</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">라자냐</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">과카몰리</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">바나나 브레드</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">멋진 레시피</h1> <p class="lead text-muted"> 이 멋진 레시피들로부터 일상 요리의 영감을 찾고 공유해 보세요. 레시피, 요리사, 동영상, 그리고 좋아하는 음식과 팔로우하는 친구들을 바탕으로 한 노하우를 발견해 보세요. <br /> <em>(심각한 것은 아니며, 저희의 데모 node-express-docker 이미지 앱을 위한 것입니다)</em> </p> </div> </div> </section> </main> </body> </html> |
Bootstrap을 가져오는 것 외에도, 이 페이지는 페이지 간 이동 및 랜딩 페이지로의 복귀를 돕기 위해 기본적인 네비게이션 메뉴를 추가합니다. 또한 사용자 정의 CSS 파일을 가져오기 위해 다음 줄을 추가했습니다:
|
1 |
<link href="css/custom.css" rel="stylesheet"> |
나중에 이 파일을 사용하여 애플리케이션에 사용자 정의 스타일을 추가할 것입니다. 이제 레시피를 위한 세 개의 페이지를 만들어 보겠습니다. 먼저 라자냐 페이지를 만드는 것부터 시작합니다. 다음 명령어를 사용하여 nano 편집기로 파일을 엽니다:
|
1 |
nano views/lasagna.html |
열린 파일에 다음 코드를 추가합니다. 이 파일은 Bootstrap과 custom.css 파일을 가져오고, 네비게이션 메뉴를 지정하며, 몇 가지 라자냐 레시피 정보를 제공합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap 기여자들"> <meta name="generator" content="Hugo 0.80.0"> <title>라자냐 레시피</title> <!-- Bootstrap 코어 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">네비게이션 토글</span> </button> <a class="navbar-brand" href="#">멋진 레시피</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">홈</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">라자냐</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">과카몰리</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">바나나 브레드</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">최고의 라자냐 레시피</h1> <p class="lead text-muted"> 이것은 당신이 만들 수 있는 최고의 라자냐입니다. <br /> <em>(진지한 것은 아니며, 저희 데모 node-express-docker 이미지 앱을 위한 것입니다)</em> </p> <h3>재료</h3> <ul class="list-group"> <li class="list-group-item">스위트 이탈리안 소시지 1파운드</li> <li class="list-group-item">지방이 적은 다진 소고기 ¾파운드</li> <li class="list-group-item">다진 양파 ½컵</li> <li class="list-group-item">으깬 마늘 2쪽</li> <li class="list-group-item">으깬 토마토 1캔(28온스)</li> <li class="list-group-item">토마토 페이스트 2캔(6온스)</li> <li class="list-group-item">통조림 토마토 소스 2캔(6.5온스)</li> <li class="list-group-item">물 ½컵</li> <li class="list-group-item">백설탕 2큰술</li> <li class="list-group-item">말린 바질 가루 1 ½작은술</li> <li class="list-group-item">펜넬 씨앗 ½작은술</li> <li class="list-group-item">이탈리안 시즈닝 1작은술</li> <li class="list-group-item">소금 1 ½작은술(나누어 사용하거나 기호에 맞게 조절)</li> <li class="list-group-item">후춧가루 ¼작은술</li> <li class="list-group-item">다진 신선한 파슬리 4큰술</li> <li class="list-group-item">라자냐 면 12장</li> <li class="list-group-item">리코타 치즈 16온스</li> <li class="list-group-item">달걀 1개</li> <li class="list-group-item">슬라이스한 모차렐라 치즈 ¾파운드</li> <li class="list-group-item">파르메산 치즈 가루 ¾컵</li> </ul> </div> </div> </section> </main> </body> </html> |
동일한 과정을 따라 다음을 위한 파일을 생성해 보겠습니다: 과카몰리 레시피 페이지. 다음 명령어를 실행하여 nano로 파일을 엽니다:
|
1 |
nano views/guacamole.html |
그런 다음 파일에 이 코드를 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Hugo 0.80.0"> <title>과카몰리 레시피</title> <!-- Bootstrap 핵심 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">네비게이션 토글</span> </button> <a class="navbar-brand" href="#">멋진 레시피</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">Home</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">Lasagna</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">Guacamole</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">Banana Bread</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">최고의 과카몰리 레시피</h1> <p class="lead text-muted"> 취향에 따라 이 아보카도 샐러드를 부드럽게 또는 덩어리지게 만들 수 있습니다. <br /> <em>(별다른 것은 아니고, 데모용 node-express-docker 이미지 앱을 위한 것입니다)</em> </p> <h3>재료</h3> <ul class="list-group"> <li class="list-group-item">아보카도 3개 - 껍질과 씨를 제거하고 으깬 것</li> <li class="list-group-item">라임 1개, 즙을 낸 것</li> <li class="list-group-item">소금 1티스푼</li> <li class="list-group-item">잘게 썬 양파 ½컵</li> <li class="list-group-item">다진 신선한 고수 3테이블스푼</li> <li class="list-group-item">로마(플럼) 토마토 2개, 잘게 썬 것</li> <li class="list-group-item">다진 마늘 1티스푼</li> <li class="list-group-item">고춧가루(카옌페퍼) 한 꼬집 (선택 사항)</li> </ul> </div> </div> </section> </main> </body> </html> |
마지막으로, 다음 명령어를 입력하여 banana_bread.html 파일을 생성해 보겠습니다:
|
1 |
nano views/banana_bread.html |
그런 다음, 파일에 다음 HTML 코드를 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
<!DOCTYPE html> <html lang="ko"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="description" content=""> <meta name="author" content="Mark Otto, Jacob Thornton, and Bootstrap contributors"> <meta name="generator" content="Hugo 0.80.0"> <title>바나나 브레드 레시피</title> <!-- 부트스트랩 핵심 CSS --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta2/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> <link href="css/custom.css" rel="stylesheet"> </head> <body> <nav class="navbar navbar-dark bg-dark navbar-static-top navbar-expand-md"> <div class="container"> <button type="button" class="navbar-toggler collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false"> <span class="sr-only">네비게이션 토글</span> </button> <a class="navbar-brand" href="#">멋진 레시피</a> <div class="collapse navbar-collapse justify-content-center" id="bs-example-navbar-collapse-1"> <ul class="nav navbar-nav justify-content-center"> <li class="active nav-item"> <a href="/" class="nav-link">홈</a> </li> <li class="nav-item"> <a href="/lasagna" class="nav-link">라자냐</a> </li> <li class="nav-item"> <a href="/guacamole" class="nav-link">과카몰리</a> </li> <li class="nav-item"> <a href="/banana-bread" class="nav-link">바나나 브레드</a> </li> </ul> </div> </div> </nav> <main> <section class="py-5 text-center container bg-light"> <div class="row py-lg-5"> <div class="col-lg-6 col-md-8 mx-auto"> <h1 class="fw-light">최고의 바나나 바나나 브레드 레시피</h1> <p class="lead text-muted"> 바나나 맛을 왜 타협하나요? 이 바나나 브레드는 바나나 풍미가 가득해 촉촉하고 맛있습니다! 친구들과 가족들이 제 레시피를 정말 좋아하며 단연 최고라고 말합니다! 토스트해서 먹으면 정말 환상적이에요!! 맛있게 드세요! <br /> <em>(심각한 것은 아니며, 저희 데모 node-express-docker 이미지 앱을 위한 것입니다)</em> </p> <h3>재료</h3> <ul class="list-group"> <li class="list-group-item">다목적 밀가루 2컵</li> <li class="list-group-item">베이킹 소다 1티스푼</li> <li class="list-group-item">소금 ¼티스푼</li> <li class="list-group-item">버터 ½컵</li> <li class="list-group-item">황설탕 ¾컵</li> <li class="list-group-item">풀어둔 달걀 2개</li> <li class="list-group-item">으깬 잘 익은 바나나 2⅓컵</li> </ul> </div> </div> </section> </main> </body> </html> |
이제 모든 페이지를 생성했습니다. 기억하시겠지만, css/custom.css 파일을 추가해야 합니다. 다음 명령어를 입력하여 디렉터리를 생성하세요:
|
1 |
mkdir views/css |
그런 다음 아래 명령어로 nano 편집기에서 파일을 생성하고 엽니다:
|
1 |
nano views/css/custom.css |
원하는 대로 웹사이트 스타일을 지정하기 위해 CSS 코드를 더 추가할 수 있습니다. 간결함을 위해 파일에 다음 코드 스니펫을 추가해 보겠습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
.bd-placeholder-img { font-size: 1.125rem; text-anchor: middle; -webkit-user-select: none; -moz-user-select: none; user-select: none; } @media (min-width: 768px) { .bd-placeholder-img-lg { font-size: 3.5rem; } } |
완료되면 파일을 저장하고 닫습니다.
이제 애플리케이션 소스 코드와 프로젝트 종속성이 설치되었으므로 애플리케이션을 시작할 수 있습니다.
앱이 특정 포트에서 수신 대기하도록 설정했으므로, 8090, 다음 명령어를 실행하여 방화벽이 이 포트를 통해 트래픽을 허용하도록 지시합니다. 다른 포트를 지정한 경우 명령어의 포트 번호를 변경하세요:
|
1 |
sudo ufw allow 8090 |
이제 애플리케이션을 시작할 수 있습니다. 하지만 먼저 다음 명령어를 실행하여 프로젝트 루트 디렉터리에 있는지 확인하세요:
|
1 |
cd ~/node_express |
node index.js로 애플리케이션을 시작합니다. 다른 진입점을 지정한 경우 해당 진입점으로 변경하세요:
|
1 |
node index.js |
브라우저에서 http://your_public_server_ip:8090로 이동하면 정의된 대로 레시피(Recipes) 랜딩 페이지가 표시됩니다:
네비게이션에서 다양한 레시피로 연결되는 링크를 볼 수 있습니다. 몇 개를 클릭해 보겠습니다. 아래는 라자냐 레시피 페이지입니다:
그리고 여기 과카몰리 레시피 페이지가 있습니다:
여기까지 애플리케이션을 생성하고 예상대로 작동하는지 테스트했습니다. Ctrl + C를 눌러 서버를 종료하고 Dockerfile 생성을 진행할 수 있습니다. Dockerfile은 필요할 때 애플리케이션 인스턴스를 다시 생성할 수 있도록 하여 확장성에 도움을 줍니다.
3단계: Dockerfile 생성
Docker는 이미지를 빌드할 때 Dockerfile에 지정된 지침을 읽습니다. 이는 애플리케이션의 런타임 환경을 지정합니다. 따라서 개발자가 종속성 불일치나 런타임 버전 변경 문제를 피하는 데 도움이 됩니다. Dockerfile을 생성하려면 다음 명령을 입력하세요:
|
1 |
nano Dockerfile |
Docker 이미지는 서로 위에 빌드되는 여러 레이어의 이미지를 사용하여 생성됩니다. 먼저 앱의 시작점을 형성할 베이스 이미지를 추가하는 것으로 시작합니다.
애플리케이션이 node.js 환경에서 실행될 것이므로, node:10-alpine 이미지를 node.js용으로 추가하는 것부터 시작합니다. 현재 이 튜토리얼을 작성하는 시점에서 이는 권장되는 Node.js의 LTS 버전입니다. 이 특정 이미지를 선택한 이유는 Alpine Linux 프로젝트에서 파생되었기 때문입니다. 따라서 이미지 크기를 최소한으로 유지하는 데 도움이 됩니다. 다음 Docker Hub Node 이미지 페이지에서 필요에 따라 선택할 수 있는 여러 이미지 변형을 찾을 수 있습니다.
다음 코드를 추가하여 FROM 지시어를 사용해 애플리케이션의 베이스 이미지를 설정하세요:
|
1 |
FROM node:10-alpine |
이 이미지에는 Node.js와 npm이 포함되어 있습니다. 모든 Dockerfile은 FROM 지시어로 시작해야 합니다. Docker node 이미지에는 기본적으로 애플리케이션 컨테이너를 루트 권한으로 실행하는 데 사용할 수 있는 루트가 아닌 node 사용자가 포함되어 있습니다. Docker 보안에서는 컨테이너를 루트 권한으로 실행하지 않고, 리소스를 실행하는 데 필요한 권한으로만 제한할 것을 권장합니다.
이 경우, 우리는 node 사용자의 홈 디렉터리를 애플리케이션의 작업 디렉터리로 사용하고, 컨테이너 내부의 사용자도 이 사용자로 설정할 것입니다. 자세한 내용은 이 Docker Node 이미지 모범 사례 가이드를 참조하세요.
우리는 node_modules 하위 디렉터리를 /home/node 디렉터리 내부에 app 디렉터리와 함께 생성하여 애플리케이션 코드의 권한을 간소화할 것입니다. 이러한 디렉터리를 생성하면 컨테이너 내부에서 로컬로 npm install 명령을 실행할 때 올바른 권한을 갖게 됩니다. 디렉터리를 생성한 후에는 해당 디렉터리의 소유권을 node 사용자에게 부여해야 합니다. Dockerfile 내부에 다음 줄을 추가하여 이 작업을 수행합니다:
|
1 |
mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app |
그런 다음 다음 줄을 추가하여 작업 디렉터리를 설정합니다:
|
1 |
WORKDIR /home/node/app |
Docker가 기본적으로 작업 디렉터리를 생성하지 않도록 항상 WORKDIR을 설정하는 것이 좋습니다.
다음 줄을 추가하여 package.json 및 package-lock.json 파일을 복사하세요:
|
1 |
COPY package*.json ./ |
It’s recommended to add the COPY 지침은 npm install을 실행하거나 애플리케이션 소스 코드를 복사하기 전에 추가하는 것이 좋습니다. 이를 통해 Docker의 캐싱 메커니즘을 활용할 수 있습니다. 빌드 프로세스 중에 Docker는 모든 지침에 대해 캐시된 레이어가 있는지 확인합니다. 즉, package.json 파일을 변경하지 않았다면 Docker는 기존 이미지 레이어를 사용하고 node modules를 다시 설치하지 않으므로 빌드 프로세스가 더 빨라집니다.
Before running npm install을 실행하기 전에, 다음 줄을 추가하여 사용자를 node로 전환함으로써 모든 애플리케이션 파일과 node_modules 디렉터리가 루트가 아닌 node 사용자의 소유가 되도록 합니다:
|
1 |
USER node |
이제 컨테이너에서 npm install 명령을 실행할 준비가 되었습니다. Dockerfile에 다음 줄을 추가하세요:
|
1 |
RUN npm install |
Once node_modules가 설치되면, Docker에 애플리케이션 코드를 올바른 권한 및 소유권(즉, 루트가 아닌 node 사용자)을 가진 컨테이너의 애플리케이션 디렉터리로 복사하도록 지시하는 다음 줄을 추가합니다:
|
1 |
COPY --chown=node:node . . |
마지막 단계는 8090 포트를 컨테이너에 노출하는 것입니다. 이는 진입점 index.js 파일에 정의한 대로입니다:
|
1 2 |
EXPOSE 8090 CMD [ "node", "index.js" ] |
EXPOSE는 런타임에 컨테이너에서 열릴 포트를 설정합니다. CMD는 애플리케이션을 시작하는 명령을 실행하며, 이 경우에는 node index.js입니다.
Dockerfile에는 마지막 CMD 명령만 적용되므로 CMD 명령은 하나만 있어야 합니다. 다음을 확인해 보세요. Dockerfile 참조 문서에서 Dockerfile로 할 수 있는 작업 목록을 확인하실 수 있습니다.
완성된 Dockerfile은 다음과 같아야 합니다:
|
1 2 3 4 5 6 7 8 9 |
FROM node:10-alpine RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app WORKDIR /home/node/app COPY package*.json ./ USER node RUN npm install COPY --chown=node:node . . EXPOSE 8090 CMD [ "node", "index.js" ] |
이제 파일을 저장하고 닫으셔도 됩니다.
다음으로 할 일은 .dockerignore 파일을 추가하는 것입니다. .gitignore 파일과 마찬가지로, .dockerignore 파일은 프로젝트 디렉토리 내에서 컨테이너로 복사되지 않아야 할 파일과 디렉토리를 지정합니다.
nano 편집기로 파일을 엽니다:
|
1 |
nano .dockerignore |
파일 내부에 다음 줄을 추가합니다:
|
1 2 3 4 |
node_modules npm-debug.log Dockerfile .dockerignore |
만약 git 저장소로 작업하는 경우, .git 디렉토리와 .gitignore 파일도 추가해야 합니다. 파일을 저장하고 닫습니다.
모든 과정이 잘 진행되었다면, 이제 docker build 명령을 사용하여 애플리케이션 이미지를 빌드할 차례입니다. –t 플래그를 docker build 명령에 추가하여 docker가 기본적으로 설정하는 임의의 문자열 대신 기억하기 쉬운 이름으로 이미지에 태그를 지정할 수 있습니다. 또한 이미지를 Docker Hub에 푸시할 예정이므로 태그에 Docker Hub 사용자 이름을 포함하는 것이 좋습니다.
태그 이름으로 nodejs-express-image를 사용하겠습니다. 원하는 태그 이름을 자유롭게 선택하셔도 됩니다. 이미지를 빌드하는 명령은 다음과 같습니다:
|
1 |
sudo docker build -t your_dockerhub_username/nodejs-express-image . |
your_dockerhub_username을 실제 Docker Hub 사용자 이름으로 변경해야 합니다. 끝에 있는 . (점)은 빌드 컨텍스트가 현재 디렉토리임을 지정합니다.
빌드 프로세스는 1~2분 정도 소요됩니다. 완료되면 다음 명령을 입력하여 이미지를 확인합니다:
|
1 |
sudo docker images |
다음과 같은 출력이 표시되어야 합니다:
여기서 your_dockerhub_username은 실제 사용자 이름으로 대체되었습니다.
이미지가 빌드된 것을 확인한 후, 이제 docker run을 사용하여 해당 이미지로 컨테이너를 생성할 수 있습니다. 다음 플래그들이 포함됩니다:
-p: 컨테이너의 포트를 공개하고 이를 호스트 시스템의 포트에 매핑합니다. 데모 목적으로 호스트 시스템의 80번 포트를 사용하겠습니다. 하지만 해당 포트에서 다른 프로세스가 실행 중인 경우 필요에 따라 자유롭게 수정하십시오. 자세한 내용은 Docker 문서의 포트 바인딩을 참조하십시오..-d: 백그라운드(detached) 모드용입니다. 컨테이너가 백그라운드에서 계속 실행되도록 합니다.--name: Docker가 임의의 문자열을 할당하도록 하는 대신 기억하기 쉬운 이름을 설정하는 데 사용할 수 있습니다.
컨테이너를 빌드하는 명령은 다음과 같습니다. Docker Hub 사용자 이름을 적절하게 변경하십시오:
|
1 |
sudo docker run --name nodejs-express-image -p 80:8090 -d your_dockerhub_username/nodejs-express-image |
컨테이너가 빌드되고 실행을 시작할 때까지 기다립니다. 다음 명령을 사용하여 실행 중인 모든 컨테이너를 검사할 수 있습니다:
|
1 |
sudo docker ps |
다음과 유사한 출력이 표시되어야 합니다:
출력에서 볼 수 있듯이 컨테이너가 현재 실행 중입니다. 브라우저에서 포트 번호 없이 서버의 공인 IP 주소로 접속하면 브라우저에서 확인할 수 있습니다. 홈페이지가 로드됩니다:
Docker를 사용하여 Node Express 정적 웹사이트를 성공적으로 배포했습니다. 향후 사용 및 확장 목적으로 이 이미지를 Docker Hub에 푸시하는 방법을 살펴보겠습니다.
4단계: Docker 이미지 저장소 작업
이미지를 Docker Hub와 같은 이미지 레지스트리에 푸시하여 나중에 사용하기 위해 저장하거나, 다른 개발자와 공유하거나, 컨테이너를 확장할 수 있습니다. 생성한 이미지를 Docker Hub에 푸시하고 이를 사용하여 컨테이너를 다시 만들 수 있습니다.
다음 명령어를 사용하여 Docker Hub 계정에 로그인합니다. 이를 실제 Docker Hub 사용자 이름으로 바꾸십시오:
|
1 |
sudo docker login -u your_dockerhub_username |
메시지가 표시되면 비밀번호를 입력합니다. 로그인하면 ~/.docker/config.json 파일이 사용자의 홈 디렉터리에 생성되며, 이 파일에는 Docker Hub 자격 증명이 포함되어 있습니다.
설정이 완료되면 다음 명령어를 입력하여 이미지를 Docker Hub에 푸시합니다. 이때 이전 단계에서 이미지를 빌드할 때 설정한 태그를 지정합니다:
|
1 |
sudo docker push your_dockerhub_username/nodejs-express-image |
이 명령어는 Docker 이미지를 사용자의 Docker Hub 계정으로 푸시합니다. 계정을 방문하면 최근에 푸시된 이미지를 확인할 수 있습니다:
현재 애플리케이션 컨테이너를 삭제하고 저장소에 있는 이미지를 사용하여 다시 빌드함으로써 이미지 저장소의 유용성을 테스트할 수 있습니다.
다음 명령어를 입력하여 현재 컨테이너 목록을 확인합니다:
|
1 |
sudo docker ps |
다음과 유사한 출력이 표시됩니다:
출력에 표시된 CONTAINER ID를 확인하고 복사한 후, 다음 명령어를 사용하여 컨테이너를 중지합니다. 이때 ID를 실제 사용자의 ID로 바꾸십시오:
|
1 |
sudo docker stop 1bb2d65279bb |
시스템에서 사용할 수 있는 모든 Docker 이미지 목록을 확인하려면 다음 명령어를 입력합니다:
|
1 |
sudo docker images –a |
출력에는 사용자의 이미지 이름, node.js 이미지 및 빌드 프로세스에서 생성된 다른 이미지들이 표시됩니다.
사용하지 않거나 대롱거리는(dangling) 이미지를 포함하여 이미지를 제거하려면 다음 명령어를 입력합니다:
|
1 |
sudo docker system prune |
확인하려면 y를 입력합니다. 이렇게 하면 중지된 컨테이너와 이미지가 제거됩니다. 목록을 확인하면 출력에 빈 목록이 표시됩니다:
이제 애플리케이션을 실행 중인 컨테이너와 이미지 자체를 모두 제거했습니다. 다음 튜토리얼을 통해 Docker 컨테이너, 이미지 및 볼륨을 제거하는 방법에 대해 자세히 알아보십시오..
이제 다음 명령어로 Docker Hub에서 이미지를 먼저 가져와(pull) 전체 프로세스를 다시 수행할 수 있습니다. Docker Hub 사용자 이름을 적절하게 바꾸십시오:
|
1 |
sudo docker pull your_dockerhub_username/nodejs-express-image |
다음 명령어로 Docker 이미지 목록을 다시 확인합니다:
|
1 |
sudo docker images |
출력에 해당 이미지가 표시되어야 합니다:
이제 3단계의 명령어를 사용하여 컨테이너를 다시 빌드할 수 있습니다. 물론 Docker Hub 사용자 이름을 적절하게 바꾸십시오:
|
1 |
sudo docker run --name nodejs-express-image -p 80:8090 -d your_dockerhub_username/nodejs-express-image |
컨테이너가 다시 빌드되었는지 확인하기 위해 컨테이너 목록을 확인합니다:
|
1 |
sudo docker ps |
다음과 유사한 출력이 표시됩니다:
브라우저에서 서버의 공인 IP 주소로 이동하면 앱이 실행 중인 것을 확인할 수 있습니다.
결론
여기까지 튜토리얼을 잘 따라오셨다면, 이제 Express와 Bootstrap으로 제작하고 Docker로 배포한 정적 웹사이트를 갖게 되었습니다. 정적 웹사이트 파일을 사용하여 Docker 이미지를 빌드하고, 이 이미지를 사용하여 컨테이너를 생성했습니다. 그런 다음 이미지를 Docker 이미지 레지스트리인 Docker Hub에 푸시하여 향후 사용하거나 확장할 수 있도록 했습니다. 이미지 레지스트리의 사용을 테스트하기 위해 이미지와 컨테이너를 삭제하고, 레지스트리에서 이미지를 가져와 컨테이너를 다시 빌드했습니다.
이 튜토리얼에서는 Node.js 앱을 배포하는 방법을 설명했습니다. 다른 웹 개발 스택을 사용하는 방법을 배우고 싶다면, 다음 튜토리얼을 참조하십시오: Nginx에서 Docker Compose를 사용하여 Laravel 앱 배포하기.
Docker 활용에 대한 더 많은 리소스를 보려면 다음 튜토리얼을 확인하십시오:
- Ubuntu 20.04에서 Docker Compose 설치 및 구성 방법
- Docker 컨테이너와 호스트 간에 데이터를 공유하는 방법
- CentOS 7에 Docker 설치 및 설정하기
즐거운 컴퓨팅 되세요!











댓글
아직 댓글이 없습니다. 첫 번째로 작성해 보세요.