반응형

이번에는 docker 이미지 최적화에 대해서 설명하겠습니다.


이미지 최적화를 위해서는 다음의 3가지를 잘 활용하면 됩니다.


1. 레이어를 줄이기 위해서 다중 RUN 명령어는 하나의 RUN 명령어로 구성
2. 파일 복사와 라이브러리 Install 은 순서가 중요
3. 컴파일과 같은 작업은 Multistep build 를 이용

 


alpine linux 로 nginx 를 실행시기 위한 방법으로 다음과 같은 docker 이미지를 만들 수 있습니다.


먼저, nginx.conf 파일을 로컬 컴퓨터에 생성합니다.

$ vi nginx.conf

user www;
worker_processes auto;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    sendfile on;
    access_log /var/log/nginx/access.log;
    keepalive_timeout 3000;
    server {
       listen 80;
       root /www;
       index index.html index.htm;
       server_name localhost;
       client_max_body_size 32m;
       error_page 500 502 503 504 /50x.html;
       location = /50x.html {
             root /var/lib/nginx/html;
       }
    }
}



다음은 간단한 index.html 입니다.

$ vi index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>HTML5</title>
</head>
<body>
    Server is online
</body>
</html>

 


이 두 파일을 활용한 Dockerfile 은 다음과 같습니다.

$ vi Dockerfile


FROM alpine:3.8
RUN apk update
RUN apk add --no-cache nginx
RUN adduser -D -g 'www' www
RUN mkdir /www
RUN chown -R www:www /var/lib/nginx
RUN chown -R www:www /www

COPY nginx.conf /etc/nginx/nginx.conf
COPY index.html /www/index.html

ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]

 


docker 이미지를 빌드하고 실행시키면 index.html 결과를 얻을 수 있습니다.


$ sudo docker build -t seungkyua/nginx-alpine .

$ docker run -d -p 30080:80 --name nginx-alpine seungkyua/nginx-alpine

$ curl http://localhost:30080

 

하나의 RUN 명령어로 구성

여기서 첫번째 이미지 최적화 포인트가 보입니다.


앞의 Dockerfile 에서 하나의 RUN 은 하나의 이미지 레이어가 되므로 이것을 하나로 다음과 같이 줄일 수 있습니다.

 

RUN apk update && \

    apk add --no-cache nginx && \

    adduser -D -g 'www' www && \

    mkdir /www && \

    chown -R www:www /var/lib/nginx && \

    chown -R www:www /www

 




이번에는 nodejs docker 이미지를 만들어 보겠습니다.

 

$ package.json
{
    "name": "docker_web_app",
    "version": "1.0.0",
    "description": "Node.js on Docker",
    "private": true,
    "author": "Seungkyu Ahn <seungkyua@gmail.com>",
    "main": "server.js",
    "scripts": {
       "start": "node server.js"
    },
    "dependencies": {
       "express": "^4.16.1"
    }
}



$ vi server.js

'use strict';

const express = require('express');

const PORT = 8080;

const HOST = '0.0.0.0';

const app = express();

app.get('/', (req, res) => {

  res.send('Hello world\n');

});

app.listen(PORT, HOST);

console.log(`Running on http://${HOST}:${PORT}`);

 


$ vi Dockerfile

FROM node:8

RUN mkdir -p /app

COPY package*.json /app/

WORKDIR /app

COPY . /app

RUN npm install --only=production

EXPOSE 8080

CMD [ "npm", "start" ]



파일 COPY 와 관련 라이브러리 설치 순서가 중요

위의 Dockerfile 의 경우 현재 디렉토리 소스를 COPY 한 후에 npm install 을 수행합니다.


docker 이미지는 변경된 레이어만 build 되지만 연관된 하위 레이어까지 build 됩니다.


여기서는 현재 디렉토리 소스가 변경되면 npm install 을 매번 다시 수행합니다.


그러므로 일단 package 설치를 먼저하고 COPY 를 나중에 하면 package 설치 내용이 변경되지 않는다면 npm install 은 캐시를 바로 사용하여 설치하지 않습니다.

 


RUN npm install --only=production

COPY . /app

 




마지막으로, 컴파일 하는 소스의 docker 이미지를 살펴보겠습니다.

 

$ vi hello.c

#include <stdio.h>

int main () {

  printf ("Hello, world!\n");

  return 0;

}



$ vi Dockerfile

FROM alpine:3.8

RUN apk update && \

    apk add --update alpine-sdk

RUN mkdir -p /app

COPY . /tmp

WORKDIR /tmp

RUN gcc hello.c -o hello

ENTRYPOINT ["/tmp/hello"]


 

Multistep build 활용

위의 경우에 c 컴파일을 하기 위해 c 컴파일로가 들어있는 sdk 패키지를 설치하고 바이너리 파일로 컴파일을 하므로 이미지 사이즈가 커집니다.


여기서 build 단계를 활용하면 sdk 패키지는 제외하고 최종 바이너리 파일만 docker 이미지에 넣을 수 있습니다.

 

$ vi Dockerfile

FROM alpine:3.8 AS build

RUN apk update && \

    apk add --update alpine-sdk

RUN mkdir -p /app

COPY . /tmp

WORKDIR /tmp

RUN gcc hello.c -o hello


FROM alpine:3.8

COPY --from=build /tmp/hello /app/hello

ENTRYPOINT ["/app/hello"]

 




아래 이미지 사이즈는 build 스텝을 활용하지 않은 파일 사이즈와 활용한 사이즈의 차이입니다.



seungkyua/c-hello-world      176MB

 


seungkyua/c-hello-world      4.42MB


반응형
Posted by seungkyua@gmail.com
,