이미지
- 컨테이너 실행에 필요한 모든 파일과 설정 등을 포함하는 템플릿이다. 컨테이너를 실행하기 위한 환경설정, 작업 디렉터리, 환경변수, 실행할 명령어 등을 정의한 패키지 파일이라고 볼 수 있다.
- 여러 개의 레이어(layer)로 구성된다. 각 레이어는 이전 레이어의 변경 사항을 적용하여 구성된다. Docker 이미지의 변화가 있다면, 컨테이너를 실행시키기 위해 모든 내용을 전부 다시 빌드하는 것이 아니라, 변경이 필요한 부분만을 다시 빌드하게 된다. 레이어 기반의 구조는 이미지 크기를 최소화하고 이미지 간에 공통된 레이어를 공유하여 효율적인 디스크 공간 사용을 가능하게 한다.
- 이미지는 Dockerfile이라는 텍스트 파일에 정의된다. Dockerfile은 어떻게 이미지를 빌드할지에 대한 명령과 설정을 포함한다. Docker CLI를 사용하여 Dockerfile을 기반으로 이미지를 빌드할 수 있다.
- Docker Hub와 같은 이미지 레지스트리에 이미지를 공유하여 다른 사람과 이미지를 공유할 수도 있다.
컨테이너
- 컨테이너는 이미지를 "컨테이너"라는 격리된 환경에서 실행하고 있는 인스턴스이다. 이미지를 기반으로 생성된 격리된 프로세스의 집합으로, 호스트와 독립적으로 실행된다. 따라서 외부 시스템 환경의 영향을 받지 않으므로 컨테이너 내부의 환경 설정에만 집중하면 된다. 이는 이식성과 확장성을 높일 수 있다는 장점으로서 작용한다.
- 컨테이너는 비슷한 개념인 가상 머신과 달리 호스트 운영 체제의 커널을 공유하며 가벼우면서도 실행에 필요한 모든 라이브러리와 파일을 가지고 있다. 이러한 특성 때문에 컨테이너는 빠르게 시작되고, 더 많은 컨테이너를 호스트 시스템에서 실행할 수 있다.
위 내용을 요약하자면
이미지는 컨테이너 안의 환경설정, 환경변수 등을 구성하고, 컨테이너 안에서 실행시킬 프로그램들에 대한 명령어 등을 정의하여 패키지 형태로 빌드한 것으로 보면 된다.
컨테이너는 실제로 격리된 환경 속에서 위의 이미지를 실행시키는 환경을 의미한다.
"이미지를 컨테이너에 Up" 시켰다는 뜻은 빌드된 이미지를 실제로 컨테이너 내부에서 실행시키고 있다는 뜻을 의미한다.
Dockerfile
위에서 이미지는 Dockerfile이라는 텍스트 파일에 의해 정의된다고 하였다. 하나의 예시를 보면서 설명하겠다.
Ruby 웹 애플리케이션을 ubuntu에 배포하는 과정(쉘 스크립트)
1. ubuntu 설치 (패키지 업데이트)
- apt-get update
2. Ruby 설치
- apt-get install ruby
- gem install bundler
3. 소스 복사
- mkdir -p /usr/src/app
- scp Gemfile app.rb root@ubuntu:/usr/src/app # From host
4. Gem 패키지 설치
- bundle install
5. Sinatra 서버 실행
- bundle exec ruby app.rb
위에 작성한 내용은 Ruby 웹 어플레케이션을 직접 우분투 환경에서 작동시킬 때 해야 할 사항들이다. 즉, 당신이 직접 해당 서버 컴퓨터에 우분투도 설치하고, 루비도 설치하고, 소스코드도 복사하고, 필요한 패키지도 설치하고, 서버도 실행시켜주어야 한다는 뜻이며, 다른 서버 컴퓨터에도 Web App을 실행시킬 환경을 구축하려면 똑같이 이 짓을 해야 한다는 것이다.
아래는 위의 과정을 Dockerfile로 작성한 내용이다.
# 1. ubuntu 설치 (패키지 업데이트 + 만든사람 표시)
FROM ubuntu:16.04 #베이스 이미지 우분투로 지정
MAINTAINER sjh9708@gmail.com #만든사람 표시
RUN apt-get -y update #패키지 업데이트
# 2. ruby 설치
RUN apt-get -y install ruby # 루비 패키지 설치(-y옵션을 통해 (y/n) 물어보는 것 방지)
RUN gem install bundler # gem 명령어를 통해 bundler 설치
# 3. 소스 복사
COPY . /usr/src/app
# 4. Gem 패키지 설치 (실행 디렉토리 설정)
WORKDIR /usr/src/app # 디렉토리 설정
RUN bundle install # bundler에 들어있는 패키지 설치
# 5. Sinatra 서버 실행 (Listen 포트 정의)
EXPOSE 4567 # 컨테이너 포트 설정
CMD bundle exec ruby app.rb -o 0.0.0.0 # bundle 명령어를 통해 서버 실행
Dockerfile이라는 이름으로 텍스트를 다음과 같이 작성한다. 이는 이미지를 빌드할 때 쓰일 Dockerfile이며, 위 행위를 그대로 담은 것이다.
Dockerfile을 이용하여 Docker Image로 생성해두면, 어플리케이션을 실행시키는 환경설정 과정 등을 자동화할 수 있으며, 다른 컴퓨터에도 환경을 구축시키고 싶으면, 이미 생성해 둔 Docker Image를 이용하여 컨테이너에 올리기만 하면 된다는 뜻이다.
베이스 이미지
# 1. ubuntu 설치 (패키지 업데이트 + 만든사람 표시)
# FROM ubuntu:16.04 #베이스 이미지 우분투로 지정
# MAINTAINER sjh9708@gmail.com #만든사람 표시
# RUN apt-get -y update #패키지 업데이트
# 2. ruby 설치
# RUN apt-get -y install ruby # 루비 패키지 설치(-y옵션을 통해 (y/n) 물어보는 것 방지)
# RUN gem install bundler # gem 명령어를 통해 bundler 설치
# 1/2. Ruby Base Image 사용
FROM ruby:2.3 # 이미 ruby 환경이 구축된 베이스 이미지 사용
MAINTAINER sjh9708@gmail.com
# 3. 소스 복사
COPY . /usr/src/app
# 4. Gem 패키지 설치 (실행 디렉토리 설정)
WORKDIR /usr/src/app # 디렉토리 설정
RUN bundle install # bundler에 들어있는 패키지 설치
# 5. Sinatra 서버 실행 (Listen 포트 정의)
EXPOSE 4567 # 컨테이너 포트 설정
CMD bundle exec ruby app.rb -o 0.0.0.0 # bundle 명령어를 통해 서버 실행
Ubuntu 환경에서 ruby 설치 커멘드를 모두 작성하는 것 대신,
Docker에서는 Ruby, Node.js 등 웹 프레임워크 사용 환경을 위한 설치를 위한 Docker 작성을 모은 Base Image를 제공한다.
위에서는 Ubuntu를 설치하고, Ruby를 설치하는 과정을 베이스 이미지 사용을 통해서 간편하게 작성하였다.
Spring Boot 어플레케이션을 Docker에서 실행시키기
그렇다면 Spring Boot 어플레케이션을 실행시키기 위해서는 Dockerfile을 어떻게 작성해야 할까?
우리가 먼저 직접 환경을 구축한다고 생각한다면, 다음 사항들을 고려해야 할 것이다.
1. Java를 실행시킬 수 있도록, JDK가 설치되어 있어야 할 것이다.
2. Spring Boot 프로젝트를 빌드하여 나온 JAR 파일을 실행시켜야 한다.
참고로 Spring Boot 프로젝트를 빌드시키는 방법은
Gradle의 Tasks -> build -> bootJar를 통해 프로젝트를 빌드시키고, 빌드된 파일은 build/libs/**.jar 파일의 형태로 산출물이 나오게 된다.
Dockerfile 작성하기
FROM openjdk:11 #1
WORKDIR /app/wisefee/ #2
ARG JAR_PATH=../build/libs/ #3
COPY ${JAR_PATH}/*.jar /app/wisefee/wisefee.jar #4
ENV PROFILE dev #5
ENTRYPOINT ["java", "-jar", "wisefee.jar"] #6
다음은 Spring Boot의 Dockerfile을 작성한 내용이다.
1. 빌드된 Spring boot 웹 Application을 실행시키기 위해서 필요한 JDK를 기반 이미지로 사용하였다. 해당 이미지에는 Java 11과, 필요한 라이브러리들이 설치되어 있다.
2. 컨테이너 내부에서 명령어 등을 실행할 WORKDIR 경로를 지정해준다. 이후의 명령은 해당 디렉토리에서 실행될 것이다.
3. ARG는 Dockerfile에서의 변수를 지정하는 것이다.
4. 빌드된 JAR 파일을 선택하여 Docker 이미지 안으로 복사한다. Web 어플리케이션을 실행시키기 위해서는 소스파일이 있어야 하기 때문에, 언급했던 빌드 산출물은 .jar 파일을 복사해야 한다.
- ${JAR_PATH}/*.jar : 이미지 안으로 복사할 JAR 파일을 선택하기 위한 경로이다.
- /app/wisefee/wisefee.jar : 이미지 내에서 JAR 파일이 위치할 경로와 파일 이름을 지정한다.
5. Docker 컨테이너에서 사용할 환경 변수를 정의한다. 애플리케이션이 개발 환경에서 실행되도록 설정하는데 사용하려고 작성한 부분이다.
6. 컨테이너가 실행될 때, JAR 파일을 실행하는 커멘드를 정의한다. ENTRYPOINT는 컨테이너가 시작될 때, 자동으로 실행되는 프로세스를 지정한다.
Dockerfile 빌드하기
이제 작성한 Dockerfile을 기반으로 컨테이너에 올릴 Image를 생성할 차례이다.
아래의 명령어는 현재 디렉토리에 있는 Dockerfile을 사용하여 Docker 이미지를 생성하고, 이미지에 dev-wisefee-app이라는 이름(태그)를 붙이는 것이다.
docker build -t dev-wisefee-app .
컨테이너에 Image 올리기
이제 빌드된 이미지를 컨테이너에 올려서 실행해보도록 하자.
아래의 명령어는 컨테이너에 빌드한 이미지를 실행시키는 명령어이다.
docker run -d -p 8080:8080 --network=host --name dev-wisefee-app-comtainer dev-wisefee-app
dev-wisefee-app이라는 이미지를 dev-wisefee-app-container라는 이름의 컨테이너 안에서 실행시키겠다는 뜻이다.
-d 옵션은 백그라운드에서 실행시키겠다는 뜻이다.
-p 8080:8080은 [호스트 Port : 컨테이너 내부 Port]를 매칭시키겠다는 뜻이다.
호스트 Port는 컨테이너 외부의 포트를 의미하며, 컨테이너 내부에서 사용되는 포트를 외부에서 이용할 때의 Port를 지정하는 것이다.
--network=host는 application.yml에 설정된 데이터베이스의 url을 정상 작동되게 하기 위해서 지정한 것이다.
spring:
datasource:
driver-class-name: org.mariadb.jdbc.Driver
url: jdbc:mariadb://localhost:3307/wisefee-database?characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 1111
application.yml에서는 현재 localhost의 데이터베이스로 연결을 하고 있다.
그러나, Docker 컨테이너 안에서 localhost를 해석한 의미는 컨테이너 내부를 의미한다.
다른 말로는 호스트 컴퓨터(Docker 컨테이너 외부)의 localhost와 연결되는 것이 아니다.
따라서 호스트 컴퓨터에서 돌고 있는 데이터베이스와 연결시켜 주기 위해서 --network=host를 지정해주었다.
--network=host 옵션은 컨테이너가 호스트의 네트워크를 사용하도록 설정한다. 이렇게 하면 컨테이너는 호스트와 동일한 네트워크를 사용하므로 로컬 호스트에서 실행 중인 다른 서비스 및 데이터베이스와 같은 네트워크 인터페이스를 공유한다. 하지만 이로 인해 호스트와 동일한 IP 주소 및 포트를 사용하게 되므로, 컨테이너가 이미 사용 중인 포트 또는 호스트에서 실행 중인 다른 서비스와 충돌이 발생할 수 있다.
따라서 --network=host 옵션은 데이터베이스 연결에 문제를 일으킬 수 있으며, 특히 데이터베이스 서버가 호스트와 같은 IP 주소와 포트를 사용하는 경우 더욱 문제가 될 수 있다.
따라서 해당 방법으로 데이터베이스의 커넥션에 오류가 날 수 있으며, network=host는 권장되지 않는다.
다음 포스팅에서 설명할 briedge network를 이용하는 것이 권장되는 편이다. 일단 해당 방법으로 되지 않으면, 이미지를 빌드하고, 컨테이너에 올렸다는 것에만 의의를 두고 다음 포스팅의 내용으로 컨테이너를 실행시켜보자.
해당 명령이 완료될 시 이미지가 컨테이너 안에서 실행되게 되고, localhost:8080에서 Sprinb Boot 웹 어플리케이션이 작동하게 된다.
현재 실행중인 Docker 컨테이너 조회
docker ps
해당 명령어를 통해서 작동중인 컨테이너들을 확인할 수 있다.
DockerHub
맨 처음 부분에서 잠시 DockerHub에 대해서 언급했었는데, 아래 작성 내용은 ChatGPT가 DockerHub에 대해서 설명해 준 내용이다.
Docker Hub는 Docker 이미지를 저장, 공유 및 관리하기 위한 클라우드 기반 이미지 레지스트리 서비스입니다. Docker Hub는 Docker 컨테이너를 구축하고 배포하는 데 도움이 되는 중앙화된 플랫폼으로, Docker 커뮤니티와 개발자들에게 널리 사용되고 있습니다.
Docker Hub는 이미지를 저장하고, 버전 관리를 하며, 커뮤니티와 개발자들 간에 이미지를 공유할 수 있는 플랫폼을 제공합니다.
Docker Hub의 주요 기능과 역할은 다음과 같습니다:
1. Docker 이미지 저장소: Docker Hub는 Docker 이미지를 저장하기 위한 클라우드 기반 저장소 역할을 합니다. Docker 이미지를 업로드하면 Docker Hub에 저장되어 이미지를 효과적으로 관리할 수 있습니다.
2. 이미지 버전 관리: Docker Hub는 동일한 이미지의 여러 버전을 관리할 수 있습니다. 각 버전은 고유한 태그를 가지며, 이를 통해 특정 버전을 선택적으로 다운로드하거나 사용할 수 있습니다.
3. 공개 및 비공개 이미지: Docker Hub는 이미지를 공개적으로 공유할 수 있도록 설정할 수 있습니다. 또한 비공개 이미지를 개인 또는 조직 내에서 사용할 수 있도록 설정할 수도 있습니다.
4. 사용자 및 조직 관리: Docker Hub는 사용자 및 조직을 관리할 수 있는 기능을 제공합니다. Docker 컨테이너와 관련된 다양한 이미지와 리소스를 사용자 간에 조직적으로 관리할 수 있습니다.
5. 자동 빌드 (Automated Builds): Docker Hub는 소스 코드 저장소(GitHub, Bitbucket 등)와 연동하여 자동 빌드 기능을 제공합니다. 소스 코드가 변경될 때마다 자동으로 Docker 이미지를 빌드하여 최신 버전을 유지할 수 있습니다.
6. 공개적인 이미지 공유: Docker Hub는 공개 이미지들을 커뮤니티와 공유하기 위한 공간으로 사용됩니다. 다양한 개발자들이 자신의 이미지를 Docker Hub에 공유하고, 다른 사용자들은 이러한 이미지를 다운로드하여 사용할 수 있습니다.
즉 DockerHub를 이용하면 NPM, Maven, Git 처럼, 빌드된 이미지를 배포하여 나 뿐만 아니라 다른 사람들 또한 이미지를 사용할 수 있도록 할 수 있다. 다른 사람이 만든 이미지를 받아서 사용하는 것도 물론 가능해진다.
지금부터 DockerHub에 생성한 이미지를 배포하는 것을 한번 해보겠다.
DockerHub에 이미지 배포하기
해당 사이트는 DockerHub 사이트이다. 해당 플랫폼에 이미지를 올리고 다운받을 수 있으며, 개발자들 간 이미지를 공유할 수 있다.
만약 계정이 없다면 우선 생성해주도록 하자.
1. 로그인
docker login
위의 CLI 명령어를 통해 로그인을 해주자.
2. 기존 이미지에 태그 붙이기
docker tag dev-wisefee-app sjh9708/devserver-wisefee-app
dev-wisefee-app : 현재 로컬 PC에서 DockerHub에 배포할 이미지의 이름이다.
sjh9708/devserver-wisefee-app : [DockerHub의 사용자 / 배포할 이미지의 공식 이름]을 지정한다.
3. DockerHub에 이미지 배포하기
docker push sjh9708/devserver-wisefee-app
태그를 붙인 Docker Image를 Push 명령어를 통해 배포할 수 있다.
Next
이번 포스팅에서 Spring Boot 프로젝트를 Dockerfile을 작성하여 빌드하고, 컨테이너상에서 실행시키는 것. 그리고 Dockerhub에 배포해 보는 것까지를 해 보았다.
그런데 우리는 일반적으로 Spring Boot 이미지만을 사용하여 웹 서비스를 운영할 수 없다. 데이터베이스도 필요할 것이고, 모니터링 도구 등 각종 다른 App들을 함께 사용하게 될 것이다.
해당 포스팅에서는 기존 Host 컴퓨터에 깔아둔 데이터베이스와 연결해보는 내용으로 포스팅 해 보았는데,
다음 포스팅에서는 컨테이너 안에서 Spring Application 외에 데이터베이스와 같은 필요한 서비스들을 일괄적으로 컨테이너에 올려 실행시킬 수 있도록 하고, 컨테이너 간의 관계 연결을 정의하는 docker-compose에 대해서 다루어 볼 예정이다.
> 다음 포스팅
https://sjh9708.tistory.com/82
'Backend > Docker & Kubernetes' 카테고리의 다른 글
[Docker] Docker Image를 DockerHub로 배포 시 아키텍처 호환 문제 해결하기(Mac M1) (0) | 2023.09.12 |
---|---|
[Kubernetes] 쿠버네티스의 개념과 구성 요소 (0) | 2023.09.11 |
[Docker] Spring Boot App을 Dockerlizing하기 : 2. Docker-compose를 이용하여 멀티 컨테이너 구성하기(+MariaDB 연결) (0) | 2023.08.04 |
[Docker] 도커의 개념과 장점 (0) | 2023.01.25 |