1. 개요

지난 몇 년 동안 Docker는 Linux의 컨테이너화를 위한 사실상의 표준이 되었습니다. Docker는 사용하기 쉽고 가벼운 가상화를 제공하므로 점점 더 많은 서비스가 클라우드에서 실행됨에 따라 애플리케이션과 마이크로서비스를 구축하는 데 이상적입니다.

첫 번째 이미지를 만드는 것은 상대적으로 쉬울 수 있지만 효율적인 이미지를 구축하려면 미리 생각해야 합니다. 이 사용방법(예제)에서는 효율적인 Docker 이미지를 작성하는 방법과 각 권장 사항의 이유에 대한 예를 볼 것입니다.

공식 이미지를 사용하는 것부터 시작하겠습니다.

2. 공식 이미지 기반 이미지 기반

2.1. 공식 이미지란 무엇입니까?

공식 Docker 이미지Docker 가 후원하거나 최소한 승인된 팀에서 만들고 유지 관리하는 이미지 입니다. GitHub 프로젝트에서 공개적으로 Docker 이미지를 관리합니다. 또한 취약점이 발견되면 변경하고 이미지가 최신 상태이고 모범 사례를 따르도록 합니다.

Nginx 공식 이미지 를 사용한 예제를 통해 이를 보다 명확하게 살펴보자 . 웹 서버의 작성자는 이 이미지를 유지 관리합니다.

Nginx를 사용하여 정적 웹사이트를 호스팅한다고 가정해 보겠습니다. Dockerfile을 만들고 공식 이미지를 기반으로 할 수 있습니다.

FROM nginx:1.19.2
COPY my-static-website/ /usr/share/nginx/html

그런 다음 이미지를 구축할 수 있습니다.

$ docker build -t my-static-website .

마지막으로 다음을 실행합니다.

$ docker run -p 8080:80 -d my-static-website

Dockerfile은 단 두 줄입니다. 기본 공식 이미지는 기본 구성 파일 및 노출되어야 하는 포트와 같은 Nginx 서버의 모든 세부 정보를 처리했습니다.

보다 구체적으로 말하면 기본 이미지는 Nginx가 데몬이 되어 초기 프로세스를 종료하는 것을 방지합니다. 이 동작은 다른 환경에서는 예상되는 동작이지만 Docker에서는 이것이 애플리케이션의 끝으로 해석되므로 컨테이너가 종료됩니다. 해결책은 Nginx가 데몬이 되지 않도록 구성하는 것입니다. 공식 이미지 구성 은 다음과 같습니다.

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

공식 이미지를 기반으로 이미지를 만들면 디버그하기 어려운 예기치 않은 오류를 피할 수 있습니다 . 공식 이미지 유지 관리자는 Docker 및 우리가 사용하려는 소프트웨어의 전문가이므로 우리는 그들의 모든 지식을 활용하고 잠재적으로 시간을 절약할 수 있습니다.

2.2. 제작자가 유지 관리하는 이미지

앞에서 설명한 의미에서 공식적이지는 않지만 Docker Hub에는 애플리케이션 작성자가 유지 관리하는 다른 이미Map 있습니다.

예를 들어 설명하겠습니다. EMQX 는 MQTT 메시지 브로커입니다. 이 브로커를 애플리케이션의 마이크로서비스 중 하나로 사용하려고 한다고 가정해 보겠습니다. 이미지를 기반으로 구성 파일을 추가할 수 있습니다. 또는 더 나은 방법으로 환경 변수를 통해 EMQX를 구성하기 위한 프로비저닝을 사용할 수 있습니다 .

예를 들어 EMQX가 수신 대기하는 기본 포트를 변경하려면 EMQX_LISTENER__TCP__EXTERNAL 환경 변수를 추가할 수 있습니다 .

$ docker run -d -e EMQX_LISTENER__TCP__EXTERNAL=9999 -p 9999:9999 emqx/emqx:v4.1.3

특정 소프트웨어 뒤에 있는 커뮤니티로서 그들은 소프트웨어와 함께 Docker 이미지를 제공할 수 있는 가장 좋은 위치에 있습니다 .

어떤 경우에는 사용하려는 응용 프로그램에 대한 공식 이미지를 찾을 수 없습니다. 이러한 상황에서도 참조로 사용할 수 있는 이미지에 대해 Docker Hub를 검색하는 것이 좋습니다.

H2를 예로 들어보겠습니다. H2는 Java로 작성된 경량 관계형 데이터베이스입니다. H2에 대한 공식 이미지는 없지만 타사에서 생성하여 잘 문서화했습니다. 우리는 그들의 GitHub 프로젝트 를 사용하여 H2를 독립형 서버로 사용하는 방법을 배우고 프로젝트 를 최신 상태로 유지하기 위해 협력할 수도 있습니다.

Docker 이미지 프로젝트를 이미지 빌드의 시작점으로만 사용하더라도 처음부터 시작하는 것보다 더 많은 것을 배울 수 있습니다.

3. 가능하면 새 이미지를 구축하지 마십시오.

Docker에 익숙해지면 기본 이미지와 비교하여 거의 변경 사항이 없는 경우에도 항상 새 이미지를 생성하는 습관이 생길 수 있습니다. 이러한 경우 이미지를 빌드하는 대신 실행 중인 컨테이너에 구성을 직접 추가하는 것을 고려할 수 있습니다 .

변경 사항이 있을 때마다 사용자 지정 Docker 이미지를 다시 빌드해야 합니다. 또한 나중에 레지스트리에 업로드해야 합니다. 이미지에 민감한 정보가 포함된 경우 개인 저장소에 저장해야 할 수 있습니다. 경우에 따라 매번 사용자 지정 이미지를 빌드하는 대신 기본 이미지를 사용하고 동적으로 구성하여 더 많은 이점을 얻을 수 있습니다.

이를 설명하기 위한 예로 HAProxy를 사용하겠습니다. Nginx와 마찬가지로 HAProxy를 역방향 프록시로 사용할 수 있습니다. Docker 커뮤니티는 공식 이미지를 유지 관리합니다 .

요청을 애플리케이션의 적절한 마이크로서비스로 리디렉션하도록 HAProxy를 구성해야 한다고 가정합니다. 이러한 모든 논리는 my-config.cfg 와 같은 하나의 구성 파일에 작성될 수 있습니다 . Docker 이미지를 사용하려면 해당 구성을 특정 경로에 배치해야 합니다.

실행 중인 컨테이너에 탑재된 사용자 지정 구성으로 HAProxy를 실행하는 방법을 살펴보겠습니다.

$ docker run -d -v my-config.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro haproxy:2.2.2

이렇게 하면 태그만 변경하면 되므로 HAProxy를 업그레이드하는 것도 더 간단해집니다. 물론 구성이 새 버전에서도 여전히 작동하는지 확인해야 합니다.

많은 컨테이너로 구성된 솔루션을 구축하는 경우 Docker Swarm 또는 Kubernetes와 같은 오케스트레이터를 이미 사용하고 있을 수 있습니다. 구성을 저장한 다음 실행 중인 컨테이너에 연결하는 수단을 제공합니다. Swarm은 이를 Configs 라고 하고 Kubernetes는 이를 ConfigMaps 라고 합니다 .

오케스트레이션 도구는 이미 우리가 사용하는 이미지 외부에 일부 구성을 저장할 수 있다고 생각합니다 . 이미지 외부에 구성을 유지하는 것이 경우에 따라 최상의 절충안이 될 수 있습니다.

4. 날씬한 이미지 만들기

이미지 크기는 두 가지 이유로 중요합니다. 첫째, 밝은 이미지가 더 빨리 전송 됩니다. 개발 머신에서 이미지를 빌드할 때 게임 체인저처럼 보이지 않을 수 있습니다. 그러나 CI/CD 파이프라인에서 여러 이미지를 빌드하고 여러 서버에 배포할 때 각 배포에서 절약된 총 시간은 감지할 수 있습니다.

둘째, 더 슬림한 이미지 버전을 얻으려면 이미지에서 사용하지 않는 추가 패키지제거 해야 합니다 . 이는 공격 표면을 줄이는 데 도움이 되므로 이미지의 Security을 높일 수 있습니다.

Docker 이미지의 크기를 줄이는 두 가지 쉬운 방법을 살펴보겠습니다.

4.1. 가능한 경우 슬림 버전 사용

여기에는 Debian의 슬림 버전과 Alpine Linux 배포의 두 가지 주요 옵션이 있습니다.

슬림 버전은 표준 이미지에서 불필요한 파일을 제거하려는 데비안 커뮤니티의 노력입니다. 많은 Docker 이미지는 이미 Debian 기반으로 축소된 버전입니다 .

예를 들어, HAProxy 및 Nginx 이미지는 Debian 배포판 debian:buster-slim 의 슬림 버전을 기반으로 합니다 . 덕분에 이 이미지는 수백 MB에서 수십 MB로 늘어났습니다.

다른 경우에는 이미지가 표준 전체 크기 버전과 함께 슬림 버전을 제공합니다. 예를 들어, 최신 Python 이미지표준 이미지보다 거의 10배 작은 슬림 버전(현재 python:3.7.9-slim )을 제공합니다.

반면에 앞서 언급한 Python 이미지와 같이 많은 이미지가 알파인 버전을 제공합니다. Alpine 기반 이미지는 일반적으로 크기가 약 10MB입니다 .

Alpine Linux는 처음부터 리소스 효율성과 Security을 염두에 두고 설계되었습니다. 따라서 기본 Docker 이미지에 완벽하게 맞습니다.

명심해야 할 점은 Alpine Linux가 몇 년 전에 시스템 라이브러리를 보다 일반적인 glibc 에서 musl 로 변경하기로 선택 했다는 것 입니다. 대부분의 소프트웨어는 문제 없이 작동하지만 Alpine을 기본 이미지로 선택하면 응용 프로그램을 철저히 테스트하는 것이 좋습니다.

4.2. 다단계 빌드 사용

다단계 빌드 기능은 일반적으로 다음 중 하나에 이전 단계의 결과를 사용하여, 같은 Dockerfile 하나 이상의 단계에서 이미지를 설치할 수있게되어있다 . 이것이 어떻게 유용할 수 있는지 봅시다.

HAProxy를 사용하고 REST API인 Data Plane API 를 사용하여 동적으로 구성하려고 한다고 가정해 보겠습니다 . 이 API 바이너리는 기본 HAProxy 이미지에서 사용할 수 없으므로 빌드 시 다운로드해야 합니다.

한 단계에서 HAProxy API 바이너리를 다운로드하고 다음 단계에서 사용할 수 있도록 할 수 있습니다.

FROM haproxy:2.2.2-alpine AS downloadapi
RUN apk add --no-cache curl
RUN curl -L https://github.com/haproxytech/dataplaneapi/releases/download/v2.1.0/dataplaneapi_2.1.0_Linux_x86_64.tar.gz --output api.tar.gz
RUN tar -xf api.tar.gz
RUN cp build/dataplaneapi /usr/local/bin/

FROM haproxy:2.2.2-alpine
COPY --from=downloadapi /usr/local/bin/dataplaneapi /usr/local/bin/dataplaneapi
...

첫 번째 단계인 downloadapi 는 최신 API를 다운로드하고 tar 파일의 압축을 풉니다. 두 번째 단계에서는 HAProxy가 나중에 사용할 수 있도록 바이너리를 복사합니다. 첫 번째 단계가 완전히 삭제되고 최종 이미지에 표시되지 않으므로 curl 을 제거 하거나 다운로드한 tar 파일을 삭제할 필요 가 없습니다.

다단계 이미지의 이점이 훨씬 더 명확한 다른 상황이 있습니다. 예를 들어 소스 코드에서 빌드해야 하는 경우 최종 이미지에는 빌드 도구가 필요하지 않습니다. 첫 번째 단계에서는 모든 빌드 도구를 설치하고 바이너리를 빌드할 수 있으며 다음 단계에서는 해당 바이너리만 복사합니다.

항상 이 기능을 사용하는 것은 아니지만 존재한다는 사실을 아는 것이 좋습니다. 어떤 경우에는 이미지를 슬림하게 만드는 것이 최선의 선택일 수 있습니다.

5. 결론

컨테이너화는 계속 존재하며 Docker는 컨테이너화를 사용하기 시작하는 가장 쉬운 방법입니다. 일부 교훈은 경험을 통해서만 배울 수 있지만 단순성은 우리가 신속하게 생산하는 데 도움이 됩니다.

이 사용방법(예제)에서는 보다 강력하고 안전한 이미지를 빌드하기 위한 몇 가지 팁을 검토했습니다. 컨테이너는 최신 애플리케이션의 빌딩 블록이므로 더 안정적일수록 애플리케이션이 더 강력해집니다.

Generic footer banner