00. 기본 설정
00-01. 디렉토리 구조
이번 실습에서 필요한 디렉토리 구조는 아래와 같습니다.
├── db
│ ├── Dockerfile
│ └── company.sql
├── docker-compose.yaml
└── app
├── Dockerfile
├── app.js
├── edit.html
├── insert.html
├── list.html
└── package.json
00-02. EC2 생성
실습을 위한 EC2를 생성해주겠습니다.
OS는 Ubuntu 18.04를 이용하고 인터넷 통신이 가능한 서브넷에서 실습을 진행하겠습니다.
00-03. 패키지 설치
인스턴스를 생성했다면 SSH 접속 후 필요한 패키지를 설치합니다.
[docker 설치]
$ sudo su -
# apt-get update
# apt-get install \
ca-certificates \
curl \
gnupg \
lsb-release
# curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# apt-get update
# apt-get install docker-ce=5:20.10.14~3-0~ubuntu-bionic
[docker-compose 설치]
# apt install -y docker-compose
[npm 설치]
# apt install -y nodejs
# apt install -y npm
[설치 확인]
# nodejs --version
# npm --version
01. node js app 생성 및 Dockerfile 작성
01-00. 사전 준비
node js 애플리케이션을 위한 디렉토리를 생성합니다. [01. 단계]는 모두 아래에서 생성된 디렉토리 안에서 진행됩니다.# mkdir ~/app
# cd ~/app
해당 디렉토리에서 packeage.json 파일을 만들기 위해 npm init 실행합니다. # npm init
실행 후 모두 Enter로 스킵하면 package.json파일이 만들어지는 것을 확인 할 수 있습니다.
파일을 자세히 확인해보고 싶다면 app 디렉토리에서 #
cat package.json
명령을 이용하면 됩니다.
필요한 모듈을 설치합니다. # npm install express body-parser ejs cjs mysql http
01-01. package.json 수정
필요한 모듈을 설치하게 되면 package.json 파일에 “dependencies”의 목록으로 패키지들이 추가 된 것을 확인 할 수 있습니다.
만약 추가 되지 않았다면 직접 파일을 수정하여 아래 내용으로 변경하면 됩니다.
또한 다음 단계에서 생성한 node.js 애플리케이션을 실행하기 위해 필요한 몇가지 작업을 위해 package.json 파일 열어 내용을 수정합니다.
# vi package.json
{
"name": "app",
"version": "1.0.0",
"main": "app.js",
"scripts": {
"start": "node app.js"
},
"author": "",
"license": "ISC",
"dependencies": {
"body-parser": "^1.20.0",
"ejs": "^3.1.6",
"express": "^4.17.3",
"fs": "0.0.1-security",
"http": "0.0.1-security",
"mysql": "^2.18.1"
},
"devDependencies": {},
"description": ""
}
01-02. app.js 생성
app 디렉토리에서 app.js를 생성 후 아래 내용을 넣어줍니다.
# vi app.js
var fs = require('fs');
var ejs = require('ejs');
var http = require('http');
var mysql = require('mysql');
var express = require('express');
var bodyParser = require('body-parser');
var mySqlClient = mysql.createConnection({
host: process.env.MYSQL_HOST,
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE
});
mySqlClient.connect(function (err) {
if (err) throw err;
console.log("Database Connected!");
});
var app = express();
http.createServer(app).listen(3000, function () {
console.log('MYSQL_HOST: %s', process.env.MYSQL_HOST)
console.log('Server running');
});
app.get('/', function (req, res) {
fs.readFile('list.html', 'utf8', function (error, data) {
if (error) {
console.log('readFile Error');
} else {
mySqlClient.query('select * from employee', function (error, results) {
if (error) {
console.log('error : ', error.message);
} else {
res.send(ejs.render(data, {
prodList: results
}));
}
});
}
})
});
app.get('/delete/:id', function (req, res) {
mySqlClient.query('delete from employee where id = ?', [req.params.id], function (error, result) {
if (error) {
console.log('delete Error');
} else {
console.log('delete id = %d', req.params.id);
res.redirect('/');
}
});
});
app.get('/insert', function (req, res) {
fs.readFile('insert.html', 'utf8', function (error, data) {
if (error) {
console.log('readFile Error');
} else {
res.send(data);
}
})
});
app.get('/edit/:id', function (req, res) {
fs.readFile('edit.html', 'utf8', function (error, data) {
mySqlClient.query('select * from employee where id = ?', [req.params.id], function (error, result) {
if (error) {
console.log('readFile Error');
} else {
res.send(ejs.render(data, {
product: result[0]
}));
}
});
});
});
app.use(bodyParser.urlencoded({
extended: true
}));
app.post('/insert', function (req, res) {
var body = req.body;
mySqlClient.query('insert into employee(name, email, dept) values(?, ?, ?)', [body.name, body.email, body.dept], function (error, result) {
if (error) {
console.log('insert error : ', error.message);
} else {
res.redirect('/');
}
});
});
app.post('/edit/:id', function (req, res) {
var body = req.body;
mySqlClient.query('update employee set name=?, email=?, dept=? where id=?', [body.name, body.email, body.dept, body.id], function (error, result) {
if (error) {
console.log('update error : ', error.message);
} else {
res.redirect('/');
}
});
});
01-03. edit.html 생성
app 디렉토리에서 node.js 애플리케이션의 편집을 위한 페이지를 생성하는 단계입니다.
# wget <아래의 edit 다운로드 URL>
명령으로 파일을 다운 받거나 , vi 편집기로 파일을 생성합니다.
# vi edit.html
<!DOCTYPE html>
<html>
<head>
<title>Edit Page</title>
</head>
<body>
<h1>Edit Page</h1>
<form method='post'>
<fieldset>
<legend>EDIT DATA</legend>
<table>
<tr>
<td><lable>Id</lable></td>
<td><input type="text" name="id" value="<%= employee.id %>" disable /></td>
</tr>
<tr>
<td><lable>Name</lable></td>
<td><input type="text" name="name" value="<%= employee.name %>" /></td>
</tr>
<tr>
<td><lable>Email</lable></td>
<td><input type="text" name="email" value="<%= employee.email %>" /></td>
</tr>
<tr>
<td><lable>Dept</lable></td>
<td><input type="text" name="dept" value="<%= employee.dept %>" /></td>
</tr>
</table>
<input type="submit" value="Update"/>
</fieldset>
</form>
</body>
</html>
01-04. insert.html 생성
app 디렉토리에서 node.js 애플리케이션의 입력을 위한 페이지를 생성하는 단계입니다.
# wget <아래의 insert 다운로드 URL>
명령으로 파일을 다운 받거나 , vi 편집기로 파일을 생성합니다.
# vi insert.html
<html>
<head>
<title>List Page</title>
</head>
<body>
<h1>Insert Page</h1>
<form method="post">
<fieldset>
<legend>INSERT DATA</legend>
<table>
<tbody>
<tr>
<td>
<lable>Name</lable>
</td>
<td><input name="name" type="text"></td>
</tr>
<tr>
<td>
<lable>Email</lable>
</td>
<td><input name="email" type="text"></td>
</tr>
<tr>
<td>
<lable>Dept</lable>
</td>
<td><input name="dept" type="text"></td>
</tr>
</tbody>
</table>
<input type="submit" value="Insert">
</fieldset>
</form>
</body>
</html>
01-05. list.html 생성
app 디렉토리에서 node.js 애플리케이션의 리스트를 보기 위한 페이지를 생성하는 단계입니다.
# wget <아래의 list 다운로드 URL>
명령으로 파일을 다운 받거나 , vi 편집기로 파일을 생성합니다.
# vi list.html
<!DOCTYPE html>
<html>
<head>
<title>List Page</title>
</head>
<body>
<h1>List Page</h1>
<a href='/insert'>Insert Data</a>
<hr/>
<table width="100%" border="1">
<tr>
<th>DELETE</th>
<th>EDIT</th>
<th>ID</th>
<th>Name</th>
<th>Email</th>
<th>Dept</th>
</tr>
<% prodList.forEach(function(item, index){ %>
<tr>
<td><a href='/delete/<%= item.id %>'>DELETE</a></td>
<td><a href='/edit/<%= item.id %>'>EDIT</a></td>
<td><%= item.id %></td>
<td><%= item.name %></td>
<td><%= item.email %></td>
<td><%= item.dept %></td>
</tr>
<% }); %>
</table>
</body>
01-06. Dockerfile 생성
위에서 만든 node.js, edit.html, insert.html, list.html을 이용하여 app 디렉토리에 Dockerfile를 만드는 단계입니다.
기본 이미지는 node:12를 사용하며 앞서 작업한 ‘app’ 디렉토리에 있는 파일들을 가져와 npm을 실행하게 되는 구조입니다.
이 애플리케이션은 3000번 포트로 노출할 것이므로 인스턴스의 보안 그룹의 3000번 포트를 열어주어야 합니다.
# vi Dockerfile
FROM node:12
WORKDIR /app
COPY package.json /app
COPY . /app
RUN npm install
EXPOSE 3000
CMD ["npm", "start"]
01-07. “.dockerignore” 파일 생성
Dockerfile에서 app 디렉토리를 복사할 때 필요하지 않은 파일을 제거하는 단계입니다.
.dockerignore 파일을 생성하여 아래 내용을 넣어줍니다.
# vi .dockerignore
node_modules
npm-debug.log
02. Mysql Dockerfile 작성
02-00. 사전 준비
MySQL을 위한 디렉토리를 생성합니다. [02. 단계]는 모두 아래에서 생성된 디렉토리 안에서 진행됩니다.
# mkdir ~/db
# cd ~/db
02-01. Dockerfile 생성
mysql을 만들기 위해 db 디렉토리에 Dockerfile을 생성하는 단계입니다.
이 mysql은 3306 포트로 노출할 것이므로 인스턴스의 보안 그룹에 3306포트를 열어줍니다.
또한 이미지가 생성 될 때 table이 생성될 수 있도록 만든 sql 파일이 실행될 수 있도록 합니다.
# vi Dockerfile
FROM mysql:5.7
EXPOSE 3306
COPY ./company.sql /docker-entrypoint-initdb.d/
CMD ["mysqld"]
02-02. company.sql
db 디렉토리에 mysql 동작 시 실행 되어야 할 sql 코드를 담은 sql 파일을 생성합니다.
# vi company.sql
USE company;
CREATE TABLE employee
(
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL,
dept VARCHAR(50) NOT NULL
);
03. docker-compose
03-01. docker-compose.yml
‘app’, ‘db’ 디렉토리 밖에서 docker-compose.yml 파일을 생성합니다.
각각의 디렉토리에서 생성한 Dockerfile을 이용하여 build를 진행하고 컨테이너를 실행하게 됩니다.
vi ~/docker-compose.yml
version: "3.4"
services:
db:
container_name: "mysql_db"
cap_add:
- SYS_NICE
build:
context: ./db
volumes:
- ./data:/var/lib/mysql
environment:
MYSQL_DATABASE: company
MYSQL_ROOT_PASSWORD: 1234
MYSQL_USER: user
MYSQL_PASSWORD: 1234
TZ: Asia/Seoul
ports:
- "3306:3306"
healthcheck:
test: [ "CMD", "mysql", "-uuser", "-p1234", "--execute", "show databases;" ]
interval: 30s
timeout: 30s
retries: 3
start_period: 30s
app:
container_name: "node_app"
build:
context: ./app
environment:
MYSQL_HOST: db
MYSQL_USER: user
MYSQL_PASSWORD: 1234
MYSQL_DATABASE: company
ports:
- "3000:3000"
links:
- db
depends_on:
- db
command: [ "npm", "start" ]
03-02. docker-compose up
위에서 생성한 docker-compose.yml 파일을 실행해줍니다.
이때 명령을 실행하는 경로는 docker-compose.yml 파일이 있는 디렉토리 안입니다.
# docker-compose up
만약 오류가 날 경우 docker-compose down 후 docker-compose up을 다시 실행해줍니다.
03-03. 실행 확인
브라우저에 접속하여 ‘<인스턴스IP>:3000’에 접속하여 결과를 확인합니다.
- INSERT로 한글을 넣고 싶을 경우, docker로 위의 구성을 하게 되면 한글을 지원하지 않기 때문에 OS와 MySQL의 문자셋 재설정을 추가로 해주어야 합니다.