실행환경

- Windows 10 WSL(Ubuntu 20.04.3 LTS)

- Docker Compose version v2.2.3

- Docker version 20.10.12

 

DockerFile 이란?
DockerFile 은 코드의 형태로 인프라를 구성하는 방법을 텍스트 형식으로 저장해 놓은 파일이며 docker build 를 사용하여 자신만의 이미지를 만들 수 있다.
Docker-Compose 란?
다중 컨테이너 애플리케이션을 정의하고 공유할 수 있도록 개발된 도구입니다. Compose에서 서비스를 정의하는 YAML 파일을 만들고, 단일 명령을 사용하여 모두 실행하거나 모두 종료할 수 있습니다. 즉 여러 컨테이너를 YAML 파일로 관리할 수 있습니다.
 
 
2. DockerFile을 이용하여 만들어 보자!
2.1 서비스 설계 구성
  DataBase Server WEB/WAS Service
Docker Hub Base Image mariadb:10.4.18 Ubuntu 20.04
OS Ubuntu 20.04.2 LTS Ubuntu 20.04.2 LTS
Service Mariadb 10.4.18
 
Nginx 1.18.0
Tomcat 9.0.45
Developer  
OpenJDK 1.8.x
Maven 3.8.1
NodeJS 14.16.1
Dev Source   USE Github
Service Port 3306 80, 8080
 

2.2 아키텍처 구성도

대규모 3-Tier 구성 : 로드 발란스를 이용하여 3-Tier 구성을 확장하는 방식.
2.3 Database(Mariadb:10.4.18) Docker Image 만들기
 
- deverse.sql 파일
 
 
- db_user.sql 내용
create database demodb;
USE demodb;
GRANT ALL PRIVILEGES ON *.* TO 'devers'@'localhost' IDENTIFIED BY '1234' WITH GRANT OPTION;
GRANT ALL PRIVILEGES ON *.* TO 'devers'@'%' IDENTIFIED BY '1234' WITH GRANT OPTION;
FLUSH PRIVILEGES;

 

- dockerfile 내용

### Base Image 지정    
FROM mariadb:10.4.18  

### TimeZone 환경 변수 지정
ENV TZ Asia/Seoul  

### Mariadb root 비밀번호 설정
ENV MYSQL_ROOT_PASSWORD=root

### Mariadb Database 생성  
ENV MYSQL_DATABASE=deverse      

### TimeZone 설
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

### Mariadb User 및 DB 스키마 설정 스크립
ADD deverse.sql /docker-entrypoint-initdb.d/deverse.sql
ADD db_user.sql /docker-entrypoint-initdb.d/db_user.sql

### 홈 디렉토리
WORKDIR /home/dev

### 서비스 포
EXPOSE 3306
 
 
소스 파일은 Github 에서 참고 하시기 바랍니다.
 
2.4 Nginx & Tomcat(ubuntu:20.04) Docker Image 만들기
- dockerfile 내용
### Base Image 지정
FROM ubuntu:20.04

### TimeZone 환경 변수 지정
ENV TZ Asia/Seoul

### TimeZone 설정    
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

### /home/dev 폴더 생성
RUN mkdir /home/dev

### update 및 패키지 설치     
RUN apt update && apt -y install vim git tar gzip build-essential curl alien openjdk-8-jdk nginx

### nodejs 설
RUN curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_14_setup.sh && bash nodesource_14_setup.sh && apt -y install nodejs

### build, tomcat, maven, nginx(default.conf) 파일 복사   
COPY build.tar.gz /home/dev/build.tar.gz
COPY tomcat-9.0.45.tar.gz /home/dev/tomcat-9.0.45.tar.gz
COPY apache-maven-3.8.1.tar.gz /home/dev/apache-maven-3.8.1.tar.gz
COPY default.conf /etc/nginx/conf.d/default.conf

### ubuntu01 계정 생성
RUN addgroup --gid 1100 ubuntu01 && adduser --disabled-password --home /home/dev --no-create-home --system -q --uid 1000 --ingroup ubuntu01 ubuntu01

### Github Source 파일 다운로드     
RUN git clone https://github.com/bc-hwang/TEST.git /home/dev/deverse

### /home/dev 폴더 이동   
WORKDIR /home/dev

### 암축 파일 해제    
RUN tar -zxvf apache-maven-3.8.1.tar.gz
RUN tar -zxvf tomcat-9.0.45.tar.gz
RUN tar -zxvf build.tar.gz

### maven link 설정
RUN ln -s /home/dev/apache-maven-3.8.1/bin/mvn /usr/bin/mvn

### Build 실행
RUN cd /home/dev/build && bash ./back_build.sh
RUN cd /home/dev/build && bash ./front_build.sh

### Nginx & Tomcat Service 실행    
CMD nginx -g 'daemon on;' && /home/dev/tomcat-9.0.45/bin/catalina.sh run

### 서비스 포
EXPOSE 80 8080
 
 소스 파일은 Github 에서 참고 하시기 바랍니다.
 
docker image를 생성하는 명령어입니다.
 
$ docker build -t tomcat_mariadb:mariadb -f nginx-image.dockerfile .
 
 
 
2.5 DockerFile Buil 하기 후 Docker Hub 에 업로드 하기

Database(mariadb:10.4.18) image 생성 및 업로드

Step1. docker loign 

Step2. 작성 한 DockerFile 에서 빌드 하기
  • docker build -t tomcat_mariadb:mariadb .
  • docker images ## 로컬에 저장된 Docker Images 확인

Step3. 로컬에 만든 이미지 실행 및 확인
  • docker run –d tomcat_mariadb:mariadb 

Step4. Docker Hub 이미지 업로드
  • docker tag tomcat_mariadb:mariadb <docker_id>/tomcat_mariadb:mariadb
  • docker push <docker_id>/tomcat_mariadb:mariadb  

Step5. Docker Hub 에 등록된 이미지 확인
  • Docker Hub 에 개인 개정으로 로그인 후 Docker Image 및 Tag 확인
 
 
 
Nginx&Tomcat(ubuntu:20.04) 이미지 생성 및 업로드
Step1. docker loign 

Step2. 작성 한 DockerFile 에서 빌드 하기
  • docker build -t tomcat_mariadb:nginx_tomcat .
  • docker images ## 로컬에 저장된 Docker Images 확인

Step3. 로컬에 만든 이미지 실행 및 확인
  • docker run –d –p 80:80 tomcat_mariadb: nginx_tomcat 

Step4. Docker Hub 이미지 업로드
  • docker tag tomcat_mariadb:nginx_tomcat bchwang/tomcat_mariadb:nginx_tomcat 
  • docker push bchwang/tomcat_mariadb: nginx_tomcat 

Step5. Docker Hub 에 등록된 이미지 확인
  • Docker Hub 에 개인 개정으로 로그인 후 Docker Image 및 Tag 확인
 
 
 
 
 
 
 
 
소스 파일은 Github 에서 참고 하시기 바랍니다.
2.6.2 Docker Hub Image 를 이용하여 docker-compose.yml 만들기
docker-compose.yml
1
### 빌드할 버전
2
version: '2'
3
4
### 서비스 설정
5
services:
6
mariadb:
7
image: bchwang/tomcat_mariadb:mariadb
8
restart: always
9
10
nginx-tomcat:
11
image: bchwang/tomcat_mariadb:nginx_tomcat
12
restart: always
13
ports:
14
- 80:80
15
links:
16
- mariadb
 
Copied!
3. NHN Cloud 를 이용한 Kubernetes 서비스를 구축 방법
3.1 커뮤니티 블로그 서비스 Yaml 설정 하기
NHN Cloud 를 이용하여 커뮤니티 블로그 서비스를 Kubernetes 로 구축 하는 방법을 소개해 드립니다.
아래의 내용을 따라 하시거나 GitHub 의 소스 파일을 참고 시기 바랍니다.
3.2 NameSpace Yaml
1
apiVersion: v1
2
kind: Namespace
3
metadata:
4
name: project
 
Copied!
3.3 Mariadb Yaml
1
apiVersion: apps/v1
2
kind: Deployment
3
metadata:
4
name: tomcat-mariadb
5
labels:
6
app: mariadb
7
namespace: project
8
spec:
9
replicas: 1
10
selector:
11
matchLabels:
12
app: mariadb
13
template:
14
metadata:
15
labels:
16
app: mariadb
17
spec:
18
hostname: mariadb
19
containers:
20
- name: mariadb
21
image: bchwang/tomcat_mariadb:mariadb
22
ports:
23
- containerPort: 3306
24
name: mariadb
 
Copied!
3.4. Mariadb Service Yaml
1
apiVersion: v1
2
kind: Service
3
metadata:
4
name: tomcat-mariadb
5
labels:
6
app: mariadb
7
namespace: project
8
spec:
9
clusterIP: None
10
ports:
11
- port: 3306
12
selector:
13
app: mariadb
 
Copied!
3.5 Nginx & Tomcat Service Yaml
1
apiVersion: v1
2
kind: Service
3
metadata:
4
name: nginx-tomcat
5
labels:
6
app: nginx-tomcat
7
namespace: project
8
## 오브젝트 선언형 관리
9
annotations
10
## Floating ip 유지 설정
11
loadbalancer.openstack.org/keep-floatingip: "true"
12
spec:
13
ports:
14
- port: 80
15
protocol: TCP
16
targetPort: 30000
17
selector:
18
app: nginx-tomcat
19
## NodePort 가 아니라 LoadBalancer 로 설정 해 주어야 함.
20
type: LoadBalancer
21
## Floating ip 를 지정 하여 Loadbalancer ip 로 설정.
22
loadBalancerIP: 133.186.216.107
 
Copied!
3.6 Nginx & Tomcat Yaml
아래의 내용 중 hostAliases 의 ip 정보가 mariadb 의 IP 정보를 입력 하여야 함.
Mariadb 를 실행 한 후에 kuberctl get pod -n project 로 나온 IP 값을 입력 하면 됨.
1
apiVersion: apps/v1
2
kind: Deployment
3
metadata:
4
name: nginx-tomcat
5
labels:
6
app: nginx-tomcat
7
namespace: project
8
spec:
9
replicas: 10
10
selector:
11
matchLabels:
12
app: nginx-tomcat
13
template:
14
metadata:
15
labels:
16
app: nginx-tomcat
17
name: nginx-tomcat
18
spec:
19
hostname: nginx-tomcat
20
hostAliases:
21
- ip: "10.100.4.9" ### mariadb hosts 지정
22
hostnames:
23
- "mariadb"
24
containers:
25
- name: nginx-tomcat
26
image: bchwang/tomcat_mariadb:nginx_tomcat
27
ports:
28
- containerPort: 80
29
name: nginx-tomcat
 
Copied!
 
소스 파일은 Github 에서 참고 하시기 바랍니다.
3.7 커뮤니티 블로그 서비스 Yaml 실행 하기
NHN Cloud Manager Server 에 접속 하여 실행 함
1
### mariadb 및 서비스 실행
2
$ kubectl apply -f 1.kubernetes_mariadb+service.yaml
3
4
### Mariadb Pod 실행 및 IP 정보 확인
5
$ kubectl get pod -n project -o wide
6
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
7
mariadb-service-59868b7dd6-xb8tq 1/1 Running 0 1m 10.100.4.9 kubernetes <none> <none>
8
9
### ip 입력 후 nginx & tomcat 실행
10
kubectl apply -f 2.kubernetes_nginx+tomcat.yaml
11
12
### Pod 실행 및 IP 정보 확인
13
$ kubectl get pod -n project -o wide
14
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
15
mariadb-service-59868b7dd6-xb8tq 1/1 Running 0 1m 10.100.4.9 kubernetes <none> <none>
16
nginx_tomcat-service-59868b7dd6-xb8tq 1/1 Running 0 1m 10.100.4.10 kubernetes <none> <none>
17
18
### Service 정보 확인
19
$ kubectl get service -n project -o wide
20
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
21
nginx_tomcat nginx_tomcat LoadBalancer 133.186.216.107 <none> 80/TCP 8m4s app=nginx_tomcat
22
mariadb mariadb ClusterIP <none> <none> 3306/TCP 18m4s app=mariadb
 
Copied!
3.8 공인 IP로 접속 한 화면
4. GitHub 정보 및 커뮤니티 블로그 소개
4.1 개발 환경
  •  
    Ubuntu/20.x (OS)
  •  
    mariadb/10.4.18 (DB Service)
  •  
    tomcat/9.0.45 (WAS Service)
  •  
    nginx/1.18.0 (WEB Service)
  •  
    OpenJDK/1.8.x
  •  
    Maven/3.8.1
  •  
    NodeJS/14.6.1
4.2 NHN Cloud Kubernetes & Docker 사용해 보기
A. DockerFile 을 이용하여 Docker Images 만들기(Tomcat, Mariadb)
  •  
    Dockerfile 을 이용하여 Mariadb 이미지 만들기
1
- /install/mariadb/db_user.sql (DB 계정 스크립트)
2
- /install/mariadb/devers.sql (DB 스키마 스크립트)
3
- /install/mariadb/dockerfile (Docker Image 스크립트)
 
Copied!
  •  
    Dockerfile Build 방법(mariadb Image)
1
- /install/mariadb 의 폴더에 dockerfile 을 확인 후 아래 명령어 실행
2
-> docker build -t tomcat_mariadb:mariadb .
3
- docker images (tomcat_mariadb:mariadb 이미지 확인)
 
Copied!
  •  
    Dockerfile 을 이용하여 nginx_tomcat 이미지 만들기
1
- /install/nginx_tomcat/apache-maven-3.8.1.tar.gz (maven src 파일)
2
- /install/nginx_tomcat/tomcat-9.0.45.tar.gz (tomcat src 파일)
3
- /install/nginx_tomcat/build.tar.gz (src build 파일)
4
- /install/nginx_tomcat/dockerfile (Docker Image 스크립트)
5
- /install/nginx_tomcat/default.conf (nginx 설정 파일)
6
- /install/nginx_tomcat/nginx.conf (nginx 설정 파일)
 
Copied!
  •  
    Dockerfile Build 하는 방법(nginx_tomcat Image)
1
- /install/nginx_tomcat 의 폴더에 dockerfile 을 혹인 후 아래 명령어 실행
2
-> docker build -t tomcat_mariadb:nginx_tomcat .
3
- docker images (tomcat_mariadb:nginx_tomcat 이미지 확인)
 
Copied!
  •  
    Docker Hub push 하는 방법
1
- docker login 을 함.
2
- docker tag tomcat_mariadb:nginx_tomcat bchwang/tomcat_mariadb:nginx_tomcat (tag 설정)
3
- docker tag tomcat_mariadb:mariadb bchwang/tomcat_mariadb:mariadb (tag 설정)
4
- docker push bchwang/tomcat_mariadb:nginx_tomcat (docker hub 에 업로드 진행)
5
- docker push bchwang/tomcat_mariadb:mariadb (docker hub 에 업로드 진행)
 
Copied!
B. Build 된 Tomcat, Mariadb Images 실행 하기
  •  
    docker images 확인 하기
1
- docker images
 
Copied!
  •  
    Local Image 로 실행 하기
1
- docker run -d --name mariadb tomcat_mariadb:mariadb
2
- docker run -d --name tomcat -p 80:80 --link mariadb tomcat_mariadb:nginx_tomcat
 
Copied!
  •  
    docker Hub Image 로 실행 하기
1
- docker run -d --name mariadb bchwang/tomcat_mariadb:mariadb
2
- docker run -d --name tomcat -p 80:80 --link mariadb bchwang/tomcat_mariadb:nginx_tomcat
 
Copied!
C. Docker-Compose 사용하기
  •  
    Local Images 로 실행 하기
1
- /install/docker-compose_local.yml (docker-compose 설정 값 Local Images 사용)
 
Copied!
  •  
    Docker Hub 에 Push 한 Images 로 실행 하기
1
- /install/docker-compose.yml (docker-compose 설정 값 Docker Hub Images 사용)
 
Copied!
D. NHN Cloud Kubernetes 로 사용하기
  •  
    NHN Cloud 회원 가입 및 Console 로그인 : http://toast.com
  •  
    NHN Cloud Console 에 Kubernetes Manager Instance 생성
1
- NHN Cloud Console 접속 -> Compute -> Instance -> 인스턴스 생성
2
- 이미지(Ubuntu Server 20.04 LTS) -> 인스턴스 이름(kube-manager) -> 인스턴스 타입(Standard : t2.c1m1) -> 키 페어 선택(생성 or 기존에 사용하던것)
3
-> 블록 스토리지 타입(HDD or SDD) -> 블록 스토리지 크기(50~100GB) -> 인스턴스 생성
 
Copied!
  •  
    NHN Cloud Console 에서 Kubernetes 생성
1
- NHN Cloud Console 접속 -> Container -> Kubernetes -> 클러스터 생성
2
- 클러스터 이름(kube-master) -> 인스턴스 타입(Standard : m2.c8m16) -> 노드 수(2~3개) -> 키 페어 선택(생성 or 기존에 사용하던것)
3
-> 블록 스토리지 타입(HDD or SDD) -> 블록 스토리지 크기(50~100GB) -> 오토 스케일러(사용 or 사용 안 함) -> 클러스터 생성
 
Copied!
  •  
    NHN Cloud Console 에서 SSH 접속 설정
1
- NHN Cloud Console 접속 -> Network -> Security Groups -> Default 선택 -> 보안정책 생성 -> 포트: 22, 원격 공인IP 입력(ex 포트: 22, CIDR: 111.111.111.111/32) 후 확인
2
- 자세한 사항은 https://docs.toast.com/ko/Compute/Instance/ko/overview/#linux (SSH 접속 방법) 참고
 
Copied!
  •  
    NHN Cloud 에서 생성된 Instance(Kube-manager) 접속 방법 및 Kubernetes 연결 방법
1
- https://doc.skill.or.kr/nhn-cloud#5-2-kube-manager-api 에서 NHN Cloud Kubernetes API 설정 방법
 
Copied!
  •  
    NHN Cloud 에서 Kubernetes 를 이용하여 서비스 사용하기
1
- /install/1.kubernetes_mariadb+service.yaml (mariadb Pod/Service 등 설정 값)
2
-> kubectl apply -f 1.kubernetes_mariadb+service.yaml (mariadb pod 와 Service 실행) -> kubectl get pod -n project -o wide (Mariadb IP 확인)
3
- /install/2.kubernetes_nginx+tomcat.yaml (tomcat Pod 설정 값)
4
-> vi 2.kubernetes_nginx+tomcat.yaml (- ip: "10.100.4.9" 의 값을 Mariadb IP 로 수정) -> kubectl apply -f 2.kubernetes_nginx+tomcat.yaml (Nginx 와 Tomcat Pod 실행)
5
- Build 설정 파일
6
- nginx+tomcat Server 접속
7
- /home/dev/build/application.yml 에 DB 접속 정보 설정
8
- /home/dev/deverse/frontend/.env.build 에 url 접속 정보 설정
 
Copied!
TIPs.1 DockerFile 기본 형식 자세히 알아 보기
TIPs 1.1 DockerFile 기본 형식
Command
Description
FROM
베이스 이미지 지정
RUN
명령어 실행
CMD
데몬 실행
LABEL
라벨 설정
EXPOSE
포트 설정
ENV
환경 변수 설정
ADD
파일 추가
COPY
파일 복사
USER
사용자 설정
WORKDIR
작업 디렉토리 지정
VOLUME
볼륨 마운트
ENTRYPOINT
데몬 실행
ONBUILD
빌드 후 실행 명
TIPs 1.2 Docker Build 기본 형식
Name, Shorthand
Default
Description
--add-host
Add a custom host-to-IP mapping (host:ip)
--build-arg
Set build-time variables
--cache-from
Images to consider as cache sources
TIPs 1.3 Docker-Compose 기본 형식
rm
Remove stopped containers
run
Run a one-off command
scale
Set number of containers for a service
start
Start services
stop
Stop services
top
Display the running processes
unpause
Unpause services
up
Create and start containers
version
Show version information and quit
Name, Shorthand
Description
-f, --file FILE
Specify an alternate compose file (default: docker-compose.yml)
-p, --project-name NAME
Specify an alternate project name (default: directory name)
--profile NAME
Specify a profile to enable
-c, --context NAME
Specify a context name
--verbose
Show more output
--log-level LEVEL
Set log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
--ansi (never|always|auto)
Control when to print ANSI control characters
--no-ansi
Do not print ANSI control characters (DEPRECATED)
-v, --version
Print version and exit
-H, --host HOST
Daemon socket to connect to
--tls
Use TLS; implied by --tlsverify
--tlscacert CA_PATH
Trust certs signed only by this CA
--tlscert CLIENT_CERT_PATH
Path to TLS certificate file
--tlskey TLS_KEY_PATH
Path to TLS key file
--tlsverify
Use TLS and verify the remote
--skip-hostname-check
Don't check the daemon's hostname against the name specified in the client certificate
--project-directory PATH
Specify an alternate working directory (default: the path of the Compose file)
--compatibility
If set, Compose will attempt to convert keys in v3 files to their non-Swarm equivalent (DEPRECATED)
--env-file PATH
Specify an alternate environment file

'Docker' 카테고리의 다른 글

Run the Docker daemon as a non-root user (Rootless mode)  (0) 2022.04.18
docker compose 설치  (0) 2022.03.18
podman Error Message ERRO[0000]  (0) 2022.01.13
Docker Compose란  (1) 2022.01.07
Window10 Podman 설치  (0) 2021.12.26

How to Solve Error Message ERRO[0000] error joining network namespace for container when listing a container using podman

 

오류

컨테이너 관리를 위해 WSL2에서 podman을 사용하던 도중 WSL 재기동 후 아래와 같은 오류 메시지가 발생했다.

 

$ podman ps
ERRO[0000] error joining network namespace for container 7933f20a3a8bd59a4f4eb11fb02: 
error retrieving network namespace at /tmp/podman-run-1000/netns/cni-ef8cccee-adcf-dbe7-26c1-99cdad524c81: 
unknown FS magic on "/tmp/podman-run-1000/netns/cni-ef8cccee-adcf-dbe7-26c1-99cdad524c81": ef53
CONTAINER ID  IMAGE       COMMAND     CREATED     STATUS      PORTS       NAMES

 

해결책

오류 메시지를 해결하기 위한 해결 방법은 다음과 같습니다.  container_id 에는 위에서 표시된 container_id 를 써 줍니다.

예제에서는 7933f20a3a8bd59a4f4eb11fb02 입니다. 비정상 컨테이너를 강제로 제거해 줍니다.

podman rm --force <container_id>

위 명령의 실행결과입니다.

podman rm --force 7933f20a3a8bd59a4f4eb11fb02 
ERRO[0000] error joining network namespace for container 7933f20a3a8bd59a4f4eb11fb02 : error retrieving network namespace at /tmp/podman-run-1000/netns/cni-ef8cccee-adcf-dbe7-26c1-99cdad524c81: unknown FS magic on "/tmp/podman-run-1000/netns/cni-ef8cccee-adcf-dbe7-26c1-99cdad524c81": ef53
Error: error freeing lock for container 7933f20a3a8bd59a4f4eb11fb02 : no such file or directory

 

위의 명령을 실행한 후 다시 명령을 실행하면 오류 없이 화면이 출력됩니다.

$ podman ps
CONTAINER ID  IMAGE                                    COMMAND               CREATED         STATUS             PORTS                   NAMES
d322844fa64b  docker.io/provectuslabs/kafka-ui:latest  /bin/sh -c java $...  22 minutes ago  Up 22 minutes ago  0.0.0.0:8090->8080/tcp  ui_kafka

 

'Docker' 카테고리의 다른 글

docker compose 설치  (0) 2022.03.18
Docker Image 만들기  (0) 2022.02.22
Docker Compose란  (1) 2022.01.07
Window10 Podman 설치  (0) 2021.12.26
Podman(PodMANger tool)  (0) 2021.12.26

What is Docker Compose?

Docker Compose란? 무엇일까요.

Docker Compose는 다중 컨테이너 애플리케이션을 정의 공유할 수 있도록 개발된 도구로
단일 명령을 사용하여 모두 실행 또는 종료할 수 있도록 개발된 도구

위 말을 이해하기 쉽게 풀어서 정리해 보겠습니다.

다중 컨테이너 애플리케이션을 정의

우리는 서비스를 서버 위에 실행시킬 때 가끔 여러 개의 서비스들이 필요할 경우가 있습니다.
예를 들어 새로 만든 application가 사용하는 database는 mysql을 사용한다고 가정한다면
mysql를 설치/실행 한 뒤 우리가 만든 application을 서버 위에 실행을 시켜야 합니다.

그런데 docker-compose는 여러 개의 서비스를 한 번에 정의를 가능하게 합니다.
위에 예시로 들었던 서비스를 실행하려면 아래와 같이 정의만 해주면 됩니다.

services:
  nhn_app:
    image: nhn_app_v2

  nhn_mysq:
    image: mysql

이렇게 하나의 문서에 여러 개의 컨테이너를 정의할 수 있습니다.

단일 명령을 사용하여 모두 실행 또는 종료

docker-compose up이라는 명령어 하나로 문서에 정의한 서비스들이 한꺼번에 컨테이너로 실행되고
반대로 docker-compose down이라는 명령어로 정의한 모든 서비스를 내릴 수도 있습니다.
물론 먼저 실행되어야 하는 순서까지 지정이 가능합니다.
이렇게 단일 명령을 사용하여 편하게 서비스를 실행 또는 종료시킬 수 있는 장점이 있습니다.

Docker Compose의 특징

단일 호스트의 여러 격리된 환경

Compose는 프로젝트 이름을 사용하여 환경을 서로 격리하고 여러 다른 콘텍스트에서 이 프로젝트 이름을 사용하여 접근을 합니다.

예를 들어 application이라는 서비스를 실행시킬 때 my_application, your_application 형식으로 여러 개를 서로 격리하여 서비스가 가능합니다.

프로젝트 이름은 기본으로 실행한 폴더명이 기준이 되며 별도로 지정이 가능합니다.

$ docker-compose -p my up 
$ docker-compose -p your up

위와 같이 -p 옵션으로 프로젝트명을 주어 실행이 가능한데 하나의 애플리케이션을 두 개의 격리된 환경으로 제공해줍니다.

컨테이너 생성 시 볼륨 데이터 보존

컨테이너 생성 시 볼륨 데이터 보존하여 데이터가 휘발되지 않도록 처리해줍니다.
컨테이너 내부에서 생성하여 사용하는 파일 볼륨을 로컬 경로와 공유하여 실수로 컨테이너가 종료되더라도 재실 행시 같은 볼륨을 유지해주어 컨테이너를 내렸다가 다시 실행시키더라도 이전에 사용했던 환경 그대로 사용이 가능한 장점이 있습니다.

변경된 컨테이너만 재생성

컨테이너를 만드는 데 사용되는 구성을 캐시 하여 변경되지 않은 서비스를 다시 시작하면 Compose는 기존 컨테이너를 다시 사용합니다.

변수 및 환경 간 구성 이동

Compose 파일의 변수를 지원하여 다양한 환경 또는 다른 사용자에 맞게 컴포지션 커스텀이 가능합니다.
해당 내용은 아래 Docker Compose Environment 에서 자세히 다루도록 하겠습니다.

서비스 정의

다음은 Dockerfile을 compose로 변환하여 간략하게 다중 컨테이너 애플리케이션을 compose로 정의해보겠습니다.

App Service 정의

$ docker run -dp 3000:3000 \
  -w /app -v ${PWD}:/app \
  --network todo-app \
  -e MYSQL_HOST=mysql \
  -e MYSQL_USER=root \
  -e MYSQL_PASSWORD=secret \
  -e MYSQL_DB=todos \
  node:12-alpine \
  sh -c "yarn install && yarn run dev"

상단의 Docker실행 명령어를 아래처럼 Compose로 작성이 가능합니다.

version: "3.8"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

compose 장점 중의 하나로는 상대 경로 입력이 가능하며 yaml로 구성하여 설정 파일의 가독성이 좋아집니다.

MySQL 서비스 정의

$ docker run -d \
  --network todo-app --network-alias mysql \
  -v todo-mysql-data:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=secret \
  -e MYSQL_DATABASE=todos \
  mysql:5.7
version: "3.8"

services:
  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment: 
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:

최종본

version: "3.8"

services:
  app:
    image: node:12-alpine
    command: sh -c "yarn install && yarn run dev"
    ports:
      - 3000:3000
    working_dir: /app
    volumes:
      - ./:/app
    environment:
      MYSQL_HOST: mysql
      MYSQL_USER: root
      MYSQL_PASSWORD: secret
      MYSQL_DB: todos

  mysql:
    image: mysql:5.7
    volumes:
      - todo-mysql-data:/var/lib/mysql
    environment: 
      MYSQL_ROOT_PASSWORD: secret
      MYSQL_DATABASE: todos

volumes:
  todo-mysql-data:

이렇게 파일 하나에 application, mysql 두 개의 설정을 한꺼번에 정의하여 실행환경을 빠르게 구성할 수 있습니다.
하나의 설정 파일로 관리가 되어 서비스를 올리고 내리는걸 빠르고 쉽게 할 수 있습니다.
여러 개의 서버에 같은 환경을 제공할 때 유용하게 사용이 가능합니다.

Dockerfile을 compose로 변환해주는 사이트가 있습니다.
100% 완벽하게 변환해주진 않지만 굉장히 유용하게 사용할 수 있는 사이트라 추천해드립니다.
https://www.composerize.com/

주요 명령어

이제 가장 중요한 docker의 명령어들을 소개해드리도록 하겠습니다.
docker-compose에서 사용하는 명령어들은 기존 docker에 있는 명령어들과 비슷합니다.
자주 사용되고 유용한 명령어에 대해서 간략하게 정리를 해보았습니다.
테스트로 사용될 docker-compose.yml 파일은 위에서 예제로 정의한 YAML을 그대로 사용하겠습니다.

예제들은 docker-compose 로 실행하였지만 현재 버전의 docker는 compose가 내장되어있어 docker compose명령어로도 실행이 가능합니다.

up

docker-compose.yml 파일의 내용에 따라 이미지를 빌드하고 서비스를 실행합니다.

$ docker-compose -p nhn up -d
Creating network "nhn_default" with the default driver
Creating nhn_mysql_1 ... done
Creating nhn_app_1   ... done

up 명령어로 compose를 실행 시의 단계별 진행사항은 다음과 같습니다.

  1. 서비스를 띄울 네트워크 설정
  2. 필요한 볼륨 생성(혹은 이미 존재하는 볼륨과 연결)
  3. 필요한 이미지 풀(pull)
  4. 필요한 이미지 빌드(build)
  5. 서비스 의존성에 따라 서비스 실행

options

  • -d: 서비스 백그라운드로 실행. (docker run에서의 -d와 같음)
  • --force-recreate: 컨테이너를 지우고 새로 생성.
  • --build: 서비스 시작 전 이미지를 새로 생성
  • -f: 기본으로 제공하는 docker-compose.yml이 아닌 별도의 파일명을 실행할 때 사용
  • docker-compose -f docker-compose.yml -f docker-compose-test.yml up 형태로 두 개의 파일 실행도 가능
  • YAML을 두 개 이상 설정할 경우 앞에 있는 설정보다 뒤에 있는 파일이 우선

down

실행 중인 서비스를 삭제합니다.
컨테이너와 네트워크를 삭제하며, 옵션에 따라 볼륨도 같이 삭제할 수 있습니다.

$ docker-compose down
Stopping nhn_mysql_1 ... done
Removing nhn_app_1   ... done
Removing nhn_mysql_1 ... done
Removing network nhn_default

options

  • -v, --volume: 볼륨까지 같이 삭제
    • DB 데이터 초기화하는데 용이함
    • 모든 설정을 초기화하고 새로 시작하는 데 사용

stop, start

서비스를 멈추거나, 멈춰 있는 서비스를 시작합니다.

$ docker-compose stop
Stopping nhn_mysql_1 ... done

$ docker-compose start
Starting app   ... done
Starting mysql ... done

ps

현재 환경에서 실행 중인 각 서비스의 상태를 표시합니다.

   Name                  Command               State           Ports
---------------------------------------------------------------------------
nhn_app_1     docker-entrypoint.sh sh -c ...   Exit 1
nhn_mysql_1   docker-entrypoint.sh mysqld      Up       3306/tcp, 33060/tcp

exec

실행 중인 컨테이너에서 명령어를 실행합니다.

명령어는 다음과 같은 패턴으로 실행됩니다.
docker-compose exec {정의한 service name} {실행될 명령어}

$ docker-compose exec app ./upload_logs.sh

$ docker-compose exec mysql mysql -uroot -psecret todos

run

docker run과 마찬 가지로 특정 명령어를 일회성으로 실행합니다.
run은 exec와 비슷한 역할을 실행하지만 다른 점은 컨테이너 재실행의 여부입니다.
run은 실행 시에 새로운 컨테이너를 띄우는 반면 exec는 실행되어있는 컨테이너에 접속합니다.
exec는 프로세서를 실행시켜 놓을 때 사용되고 run은 batch성 작업에 특화되어있습니다.

options

  • --rm : 해당 명령어가 종료된 뒤 컨테이너도 종료

logs

output으로 나온 log들을 확인할 때 사용합니다.
docker의 logs와 마찬가지로 --follow(혹은 -f)를 하여 실시간으로 나오는 로그 확인이 가능합니다.

$ docker-compose logs -f
Attaching to docker_app_1, docker_mysql_1
app_1    | yarn install v1.22.5
app_1    | info No lockfile found.
app_1    | [1/4] Resolving packages...
app_1    | [2/4] Fetching packages...
app_1    | [3/4] Linking dependencies...
app_1    | [4/4] Building fresh packages...
app_1    | success Saved lockfile.
app_1    | Done in 0.14s.
app_1    | yarn run v1.22.5
app_1    | error Couldn't find a package.json file in "/app"
app_1    | info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
mysql_1  | 2021-01-05 04:43:03+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 5.7.32-1debian10 started.

options

  • -f, --follow: 실시간 로그 출력

config

docker-compose에 최종적으로 적용된 설정을 보여줍니다.
docker-compose -f docker-compose.yml -f docker-compose-test.yml up 형태처럼 여러 개의 YAML파일을 실행시켰을 경우
최종적으로 적용된 설정을 확인하기 좋은 옵션입니다.

$ docker-compose config
services:
  app:
    command: sh -c "yarn install && yarn run dev"
    environment:
      MYSQL_DB: todos
      MYSQL_HOST: mysql
      MYSQL_PASSWORD: secret
      MYSQL_USER: root
    image: node:12-alpine
    ports:
    - published: 3000
      target: 3000
    volumes:
    - /Users/ssabae/project/docker-compose-test/nhn:/app:rw
    working_dir: /app
  mysql:
    environment:
      MYSQL_DATABASE: todos
      MYSQL_ROOT_PASSWORD: secret
    image: mysql:5.7
    volumes:
    - todo-mysql-data:/var/lib/mysql:rw
version: '3.8'
volumes:
  todo-mysql-data: {}

Docker Compose Environment

docker compose의 장점으로는 하나를 정의해놓으면 여러 군데서 동일한 동작을 보장하는 것입니다.
하지만 각 실행 환경에 따라 변경되어야 하는 옵션들이 있는데 이것들 때문에 일일이 파일을 수정하는 건 비효율적입니다.
그래서 각 실행하는 환경마다 환경변수 파일을 정의하여 동일한 compose 파일로 각각의 환경과 동적인 옵션으로 실행이 가능합니다.

. env file

기본적으로 docker-compose up을 한 상태에서 . env 파일을 찾아 파일 내부에 있는 값을 환경 변수로 사용합니다.

$ cat .env     # 환경변수 파일
TAG=v1.5

$ cat docker-compose.yml   # compose 파일
version: '3'
services:
  web:
    image: "webapp:${TAG}"$

$ docker-compose config    # 실제 적용된 옵션

version: '3'
services:
  web:
    image: 'webapp:v1.5'


$ export TAG=v2.0
$ docker-compose config

version: '3'
services:
  web:
    image: 'webapp:v2.0'

여러 파일에서 동일한 환경 변수를 설정할 때 사용할 값을 선택하기 위해 Compose에서 사용하는 우선순위는 다음과 같습니다.

  1. Compose file
  2. Shell environment variables
  3. Environment file
  4. Dockerfile
  5. Variable is not defined

여러 개의 복잡합인 상황에 따라서 의도하지 않은 설정이 들어가지 않게 우선순위를 잘 파악해보는 것이 중요합니다.

상황에 따른. env 적용 예제

dev, prod, local 환경에 따라서
.env.dev, .env.prod, .env.local 형식으로 할당을 줄 수 있는데
--env-file 옵션으로 환경변수 파일 지정이 가능합니다.

$ docker-compose --env-file ./config/.env.dev up

CLI Environment

아래의 명령어들은 각각 그 아래의 compose 파일이 일치하게 맵핑됩니다.

$ docker run -e VARIABLE1

web:
  environment:
    - VARIABLE1
$ docker-compose run -e DEBUG=1 web python console.py

web:
  environment:
    - DEBUG=1

Docker Cli Environment Variable

Compose CLI에서 실행할 때의 참고 변수 키 값 정의가 가능합니다.
https://docs.docker.com/compose/reference/envvars/

Compose Network 설정 옵션

version: "3.8"
services:
  app:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres
    ports:
      - "8001:5432"

위 Compose를 실행했을 때의 네트워크가 생성되는 과정은 아래와 같습니다. ( myapp이라는 project name으로 가정 )

  1. myapp_default 네트워크 생성
  2. web 컨테이너가 생성. (web이라는 이름으로 myapp_default에 접속)
  3. db 컨테이너가 생성. (db이라는 이름으로 myapp_default에 접속)

네트워크가 구성된 이후 postgres://db:5432 주소로 접속 가능합니다.
내부 컨테이너끼리는 postgres://db:5432로 접속하고 외부에선 postgres://{DOCKER_IP}:8001로 접속해야 합니다.
컨테이너 업데이트를 위해 다시 시작할 경우에는 컨테이너 이름은 같지만 다른 IP로 네트워크가 생성되어
IP 베이스는 IP를 다시 찾아서 연결하여야 하고(재실행 시 IP 변경) 다른 컨테이너에선 기존에 접속했던 컨테이너 이름으로 재 연결을 시도하여야 합니다.

links

links는 다른 서비스의 컨테이너와 연결시켜주는 역할을 하고 있습니다.
이름만 지정하거나 {name}:{alias}형식으로 지정할 수 있습니다.

version: "3"
services:
  web:
    build: .
    links:
      - "redis"
      - "db:database"
  db:
    image: postgres

links는 레거시 기능으로 공식 페이지에서 권장하지 않는 방법입니다.
depend_on처럼 디펜던시 관계가 맺어지며 네트워크랑 같이 쓰일 때는 하나 이상의 네트워크가 관리되기 때문에 네트워크만 사용하길 권장합니다.

Custom Network

Custom Network를 통해 좀 더 복잡한 네트워크 토폴로지를 만들 수 있습니다.
아래 간단한 예시를 통해서 설명을 드리겠습니다.

version: "3"
services:
  proxy:
    build: ./proxy
    networks:
      - frontend
  app:
    build: ./app
    networks:
      - frontend
      - backend
  db:
    image: postgres
    networks:
      - backend
networks:  # top-level network
  frontend:
    driver: custom-driver-1 # Use a custom driver
  backend:
    driver: custom-driver-2 # Use a custom driver which takes special options
    driver_opts:
      foo: "1"
      bar: "2"

top-level의 network 옵션에서 커스텀하게 지정이 가능합니다.

위와 같이 설정을 하면 app에서만 proxy, db에 접근 가능하고 db와 proxy는 서로 접근이 불가능하게 설정이 됩니다.

IPv4, IPv6

IPv6를 사용하기 위해서는 top-level network에 enable_ipv6 옵션이 반드시 들어가야 합니다.

version: "2.4"

services:
  app:
    image: nginx:alpine
    networks:
      app_net:
        ipv4_address: 172.16.238.10
        ipv6_address: 2001:3984:3989::10

networks:
  app_net:
    enable_ipv6: true
    ipam:
      driver: default
      config:
        - subnet: "172.16.238.0/24"
        - subnet: "2001:3984:3989::/64"

Default Network

default를 이용하여 자체 네트워크를 지정하면서 앱 전체의 기본적인 네트워크 구성이 가능합니다.

version: "3"

services:
  web:
    build: .
    ports:
      - "8000:8000"
  db:
    image: postgres

networks:
  default:
    driver: custom-driver-1    # Use a custom driver

Use Pre Existing Network

기존의 외부의 네트워크를 사용하려면 다음과 같이 external을 사용하여 명시해야 합니다.

networks:
  default:
    external:
      name: my-pre-existing-network

부록 : Compose Version별 특성

다음은 버전별 특성에 대해서 정리된 공식 홈페이지 내용을 요약해서 정리해본 것을 공유드립니다.

https://docs.docker.com/compose/compose-file/compose-versioning/

versioning

docker-compose.yml상단에 작성하는 compose file format의 버전을 명시해주어야 합니다.

version: "3.8"
...

versioning은 버전별로 다음과 같은 특징이 있습니다.

  • Version 1에서는 버저닝을 생략
  • Version 2부터 마이너 버전(2.x)까지 설정해야 함 (생략 시 2.0으로 적용된다.)
  • Version 3은 도커 스웜과 같이 사용되도록 디자인됨

version dependency

다음은 각 docker 버전 별 사용 가능한 docker-compose.yml 버전을 표로 작성해 보았습니다.
Compose version은 3 버전부터는 직접 명시되어있는 게 없어 빈칸으로 두었습니다.
필요하신 분은 릴리즈 노트에서 찾아보시면 쉽게 찾아보실 수 있습니다.

Compose file formatDocker Engine releaseCompose Version

3.8 19.03.0+ -
3.7 18.06.0+ -
3.6 18.02.0+ -
3.5 17.12.0+ -
3.4 17.09.0+ -
3.3 17.06.0+ -
3.2 17.04.0+ -
3.1 1.13.1+ -
3.0 1.13.0+ -
2.4 17.12.0+ 1.21.0+
2.3 17.06.0+ 1.16.0+
2.2 1.13.0+ 1.13.0+
2.1 1.12.0+ 1.9.0+
2.0 1.10.0+ 1.6.0+
1.0 1.9.0.+  

 

설치

 

'Docker' 카테고리의 다른 글

Docker Image 만들기  (0) 2022.02.22
podman Error Message ERRO[0000]  (0) 2022.01.13
Window10 Podman 설치  (0) 2021.12.26
Podman(PodMANger tool)  (0) 2021.12.26
Docker vs Podman  (0) 2021.12.26

Podman은 Windows10 WSL2  Ubuntu 20.04에 설치하거나 Windows10 WSL2 에 Ubuntu 20.10 이상에서 설치할 수 있습니다.

Windows 10 WSL2 에 Ubuntu 20.10 이상인 경우에는 설치 방법이 간단합니다.

단, 현재까지는 Window10에 Unbuntu 20.10을 직접 설치할 수 없어, Ubuntu 20.04를 20.10 이상으로 Upgrade해야 합니다.

 

Windows 10 WSL 2  Ubuntu 20.04에 Podman 설치

Windows 10 WSL 2 에 Ubuntu 20.04가 설치되어 있지 않다면

Microsoft Store 또는 Powershell을 사용하여 Ubuntu 20.04 LTS Linux 앱을 설치합니다.
CLI와 GUI의 두 가지 방법이 있습니다.

 

1. Window10에 Ubuntu 20.04 설치

1) Command-line 설치

다음 명령어로 사용 가능한 다른 Linux 배포판을 확인합니다.

wsl --list --online

명령줄을 사용하려면 Windows 시작 메뉴에서 Powershell(관리자) 또는 Windows 터미널(관리자)을 선택합니다.

wsl --install -d Ubuntu-20.04

2) GUI 로 설치

Microsoft Store 에서 Ubuntu 20.04 LTS 를 검색한 후 [설치] 버튼을 클릭하여 설치합니다.

 

2. Ubuntu 20.04가 설치된 WSL2 실행

명령창, Powershell, Window Terminal 중 평소 본인이 사용하는 명령창을 관리자모드로 실행합니다. 설치된 Ubuntu 에 wsl 명령어를 입력하여 접속합니다. 최초 접속하는 경우에는 설정을 완료하는 데 시간이 걸리며 그 후에 사용자 이름과 비밀번호를 설정하라는 메시지가 표시됩니다.

한번 만들면 해당 계정이 배포의 기본 사용자가 되며 시작 시 자동으로 로그인됩니다. 

이 계정은 sudo(Super User Do) 명령을 실행할 수 있는 Linux 관리자입니다. 

최초 설정이 완료되면 먼저 업데이트 명령을 실행합니다.

$ sudo apt update

 

3. Podman 설치

저장소를 사용하여 Ubuntu 20.04 LTS에서 Podman을 가져옵니다

 

repository 추가

echo "deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_20.04/ /" |
sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list

GPG key 추가

curl -L "https://download.opensuse.org/repositories/devel:/kubic:\
/libcontainers:/stable/xUbuntu_20.04/Release.key" | sudo apt-key add -

시스템 업데이트 및 업그레이드를 실행하여 Repo 캐시를 새로 고치고 사용 가능한 패키지의 최신 버전을 설치합니다.

sudo apt-get update
sudo apt-get -y upgrade

apt 로 podman 을 설치합니다.

sudo apt install podman

podman -v 로 설치된 버전을 확인합니다.

$ podman -v
podman version 3.4.2

 

사내시스템에서 인트라넷을 사용하는 경우 아래와 같은 문제가 발생할 수 있다. 아래와 같은 에러가 발생하는 이유는 각 회사 컴퓨터에는 신뢰할 수 있는 루트 인증 기관을 가지고 있게 되는데, 인증 기관으로 도메인을 포함하지 않은 경우에 이런 에러가 발생할 수 있다.

curl failed to verify the legitimacy of the server and ...
...
gpg: no valid OpenPGP data found.

 

위와 같은 오류로 인해 설치가 안될 경우, 아래 명령어를 수행하면 됩니다.

permission deny가 날 경우 sudo 명령어를 붙여서 수행합니다.

. /etc/os-release
sudo sh -c "echo 'deb http://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/x${NAME}_${VERSION_ID}/ /' > /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list"
wget -nv https://download.opensuse.org/repositories/devel:kubic:libcontainers:stable/x${NAME}_${VERSION_ID}/Release.key -O Release.key
sudo apt-key add - < Release.key
sudo apt-get update -qq
sudo apt-get -qq -y install podman
sudo mkdir -p /etc/containers
echo -e "[registries.search]\nregistries = ['docker.io', 'quay.io']" | sudo tee /etc/containers/registries.conf

 

 

 

Windows 10 WSL 2  Ubuntu 20.10에 Podman 설치

저장소를 수동으로 추가하고 싶지 않다면 Ubuntu 20.04를 Ubuntu 20.20 LTS로 업그레이드합니다. 그러면 공식 리포지토리를 통해 패키지를 설치할 수 있습니다.

1단계: 실행 중인 WSL2를 터미널을 사용하여 Ubuntu 20.04에서 Ubuntu 21.10으로 업그레이드합니다

             본 문서에서는 업그레이드 방법은 설명하지 않습니다.

             20.04에서 21.04로 업그레이드하는 방법은 링크된 문서를 참고하세요
2단계: Ubuntu 20.10 LTS 이상을 사용 중이면 다음을 실행하면 됩니다

sudo apt install podman

 

Podman으로 컨테이너 생성

Podman에서 사용하는 명령줄은 기본적으로 Docker와 동일하고 추가된 기능이 있습니다.

 

podman pull 명령어로 Ubuntu 이미지를 내려받습니다.

$ podman pull ubuntu
Resolved "ubuntu" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/ubuntu:latest...
Getting image source signatures
Copying blob 7b1a6ab2e44d done
Copying config ba6acccedd done
Writing manifest to image destination
Storing signatures
ba6acccedd2923aee4c2acc6a23780b14ed4b8a5fa4e14e252a23b846df9b6c1

podman pull 명령어로 Fedora 이미지를 내려받습니다.

$ podman pull fedora

Resolved "fedora" as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull registry.fedoraproject.org/fedora:latest...
Getting image source signatures
Copying blob 4545346f2a49 done
Copying config 3059bef432 done
Writing manifest to image destination
Storing signatures
3059bef432ebb91a6a51d8f5cf20b033041dbddb3cab79628c1eb3412cbde0ae

podman images 명령어로 내려받은 이미지를 조회합니다.

$ podman images

REPOSITORY                         TAG         IMAGE ID      CREATED       SIZE
registry.fedoraproject.org/fedora  latest      3059bef432eb  4 weeks ago   159 MB
docker.io/library/ubuntu           latest      ba6acccedd29  2 months ago  75.2 MB

 

Note: 이미지를 가져오는 동안 경고가 표시되는 경우 접근권한의 문제이므로 아래 명령어를 수행합니다.

: WARN[0000] "/"는 공유 마운트가 아닌 경우 루트가 없는 컨테이너에 문제가 발생하거나 마운트가 누락될 수 있습니다.

sudo chmod 4755 /usr/bin/newgidmap
sudo chmod 4755 /usr/bin/newuidmap

 

Podman run

다운로드한 이미지로 컨테이너를 생성 합니다. 

$ podman run -dit --name fe_cont fedora

-d, --detach : 백그라운드에서 컨테이너를 실행하고 새 컨테이너 ID를 출력합니다. 기본값은 false입니다.

- i, --interactive : true로 설정하면 연결되지 않은 경우에도 stdin을 열어 둡니다. 기본값은 false입니다.

- t, --tty : TTY를 할당합니다. 기본값은 false입니다

--name <container_name> : 컨테이너 이름을 지정합니다.

위 명령어는 다운로드한 fedora 이미지로 fe_cont 라는 컨테이너를 생성하고 stdin과 tty 로 리눅스 콘솔을 할당하고 백그라운드로 실행하라는 명령어입니다.

$ podman ps
CONTAINER ID  IMAGE                                     COMMAND     CREATED         STATUS             PORTS       NAMES
7933f20a3a8b  registry.fedoraproject.org/fedora:latest  /bin/bash   14 minutes ago  Up 14 minutes ago              fe_cont

 

podman attach 로 실행중인 컨테이너에 접속합니다. fe_cont 컨테이너에 접속 한 후 /etc/os-release 로 조회해 봅니다.

$ podman attach fe_cont

[root@7933f20a3a8b /]# cat /etc/os-release
NAME="Fedora Linux"
VERSION="35 (Container Image)"
ID=fedora
VERSION_ID=35
.. ...
VARIANT="Container Image"
VARIANT_ID=container

 

Docker를 Podman의 별칭으로 사용

 

Docker 명령어가 친숙한 경우 docker로 별칭을 지정하여 사용할 수 있습니다.

$ alias docker=podman
$ docker --version

podman version 3.4.2

그래픽 사용자 인터페이스를 사용하여 Podman을 관리하려면  Cockpit 웹 콘솔을 설치하는 방법으로 참고하세요.

 

 

 

 

 

Getting Started with Podman

Podman is a utility provided as part of the libpod library. It can be used to create and maintain containers. The following tutorial will teach you how to set up Podman and perform some basic commands.

Podman Documentation

The documentation for Podman is located here.

Installing Podman

For installing or building Podman, please see the installation instructions.

Familiarizing yourself with Podman

The code samples are intended to be run as a non-root user, and use sudo where root escalation is required.

Getting help

To get some help and find out how Podman is working, you can use the help:

$ podman --help
$ podman <subcommand> --help

For more details, you can review the manpages:

$ man podman
$ man podman-<subcommand>

Please also reference the Podman Troubleshooting Guide to find known issues and tips on how to solve common configuration mistakes.

Searching, pulling & listing images

Podman can search for images on remote registries with some simple keywords.

$ podman search <search_term>

You can also enhance your search with filters:

$ podman search httpd --filter=is-official

Downloading (Pulling) an image is easy, too.

$ podman pull docker.io/library/httpd

After pulling some images, you can list all images, present on your machine.

$ podman images

Note: Podman searches in different registries. Therefore it is recommend to use the full image name (docker.io/library/httpd instead of httpd) to ensure, that you are using the correct image.

Running a container

This sample container will run a very basic httpd server that serves only its index page.

$ podman run -dt -p 8080:80/tcp docker.io/library/httpd

Note: Because the container is being run in detached mode, represented by the -d in the podman run command, Podman will print the container ID after it has executed the command. The -t also adds a pseudo-tty to run arbitrary commands in an interactive shell.

Note: We use port forwarding to be able to access the HTTP server. For successful running at least slirp4netns v0.3.0 is needed.

Listing running containers

The podman ps command is used to list created and running containers.

$ podman ps

Note: If you add -a to the podman ps command, Podman will show all containers (created, exited, running, etc.).

Testing the httpd container

As you are able to see, the container does not have an IP Address assigned. The container is reachable via it’s published port on your local machine.

$ curl http://localhost:8080

From another machine, you need to use the IP Address of the host, running the container.

$ curl http://<IP_Address>:8080

Note: Instead of using curl, you can also point a browser to http://localhost:8080.

Inspecting a running container

You can “inspect” a running container for metadata and details about itself. podman inspect will provide lots of useful information like environment variables, network settings or allocated resources.

Since, the container is running in rootless mode, no IP Address is assigned to the container.

$ podman inspect -l | grep IPAddress
            "IPAddress": "",

Note: The -l is a convenience argument for latest container. You can also use the container’s ID or name instead of -l or the long argument --latest.

Viewing the container’s logs

You can view the container’s logs with Podman as well:

$ podman logs -l

127.0.0.1 - - [04/May/2020:08:33:48 +0000] "GET / HTTP/1.1" 200 45
127.0.0.1 - - [04/May/2020:08:33:50 +0000] "GET / HTTP/1.1" 200 45
127.0.0.1 - - [04/May/2020:08:33:51 +0000] "GET / HTTP/1.1" 200 45
127.0.0.1 - - [04/May/2020:08:33:51 +0000] "GET / HTTP/1.1" 200 45
127.0.0.1 - - [04/May/2020:08:33:52 +0000] "GET / HTTP/1.1" 200 45
127.0.0.1 - - [04/May/2020:08:33:52 +0000] "GET / HTTP/1.1" 200 45

Viewing the container’s pids

You can observe the httpd pid in the container with podman top.

$ podman top -l

USER     PID   PPID   %CPU    ELAPSED            TTY     TIME   COMMAND
root     1     0      0.000   22m13.33281018s    pts/0   0s     httpd -DFOREGROUND
daemon   3     1      0.000   22m13.333132179s   pts/0   0s     httpd -DFOREGROUND
daemon   4     1      0.000   22m13.333276305s   pts/0   0s     httpd -DFOREGROUND
daemon   5     1      0.000   22m13.333818476s   pts/0   0s     httpd -DFOREGROUND

Stopping the container

You may stop the container:

$ podman stop -l

You can check the status of one or more containers using the podman ps command. In this case, you should use the -a argument to list all containers.

$ podman ps -a

Removing the container

Finally, you can remove the container:

$ podman rm -l

You can verify the deletion of the container by running podman ps -a.

Network

For a more detailed guide about Networking and DNS in containers, please see the network guide.

Checkpoint, Migration and Restoring containers

Checkpointing a container stops the container while writing the state of all processes in the container to disk. With this, a container can later be migrated and restored, running at exactly the same point in time as the checkpoint. For more details, see the checkpoint instructions.

Integration Tests

For more information on how to setup and run the integration tests in your environment, checkout the Integration Tests README.md.

More information

For more information on Podman and its subcommands, checkout the asciiart demos on the README.md page.

'Docker' 카테고리의 다른 글

Docker Compose란  (1) 2022.01.07
Window10 Podman 설치  (0) 2021.12.26
Docker vs Podman  (0) 2021.12.26
흔들리는 도커(Docker)의 위상 - OCI와 CRI 중심으로 재편되는 컨테이너 생태계  (0) 2021.12.26
헷갈리는 Docker - 수정  (0) 2021.12.26

Docker 엔진을 실행하지 않았을 경우, docker 명령어를 수행하면 docker 명령어를 찾을 수 없다는 메시지가 출력된다.

$ docker --help

The command 'docker' could not be found in this WSL 2 distro.
We recommend to activate the WSL integration in Docker Desktop settings.

Window에 설치된 Docker Desktop 을 실행하면 Docker Enging이 실행되고 도커 데몬과 통신 할 수 있게 되어 docker 명령어가 정상적으로 수행된다.

즉, 도커 엔진이 실행되지 않을 경우 도커 엔진에서 관리하는 이미지나 컨테이너는 사용할 수 없게된다.

반면, podman은 엔진을 가지지 않으므로 별도의 엔진(데몬) 기동없이도 이미지나 컨테이너를 사용할 수 있다.

 

가상머신과 컨테이너

요즘 기술 트렌드를 보면 가상화 기술에서 컨테이너 기술로 사람들의 관심이 많이 넘어가고 있는 추세입니다. 왜 이렇게 컨테이너 기술에 사람들은 열광을 하는걸까요? 기술은 계속 진화하여 메인프레임 시대에서 클라우드 컴퓨팅 시대로 넘어 왔습니다. 수개월이 걸리던 시스템 구성을 단 몇 분안이면 끝낼 수 있는 환경이 되었습니다. 어플리케이션 또한, 동일 서버에서 모든 프로세스가 실행되던 방식에서 여러 분산된 환경에서 프로세스가 독립적으로 실행이 되도록 변경되었습니다. 이는 무엇을 의미할까요? 이제 누가 더 빨리 비즈니스 어플리케이션을 시장에 출시하느냐가 중요한 핵심 포인트가 되었다는 뜻일 것입니다.  

 

따라서 이제는 가상화를 모르면 시스템을 운영하기조차 힘든 상황이 되어 버렸습니다. 이런 상황에서 기존에 사용하던 하이퍼바이저 위의 가상머신은 CPU, Memory, Network, Disk와 같은 물리자원을 가상화하여 사용함으로써 무거운데 반면, 운영체제 위의 컨테이너는 라이브러리 형식으로 가볍게 올라가 설치하고자 하는 어플리케이션만 실행하면 되기 때문에 매우 가볍습니다.

레드햇의 새로운 컨테이너 기술 Podman

아마도 컨테이너 하면 떠오르는 단어는 Docker일 것입니다. 그리고, Docker엔진을 통해 올라 가는 컨테이너를 관리하기 위한 가장 대표적인 관리 소프트웨어가 바로 Kubernetes일 것입니다. 그런데, 2017년에 Docker가 엔터프라이즈 버전을 상용화하면서 레드햇은 또다른 컨테이너 오픈소스 기술인 Podman을 사용하여 레드햇의 엔터프라이즈 제품들을 출시하였습니다. 

가장 먼저 2019년 3월에 릴리즈한 Red Hat Enterprise Linux 8에 Podman이 추가되었습니다. 그리고, 뒤이어 릴리즈 된 Red Hat OpenShift Container Platform 4와 Red Hat OpenStack Platform 16 모두 Docker에서 Podman으로 변경되었습니다. 

컨테이너 기술 Docker와 Podman

Docker와 Podman은 매우 유사한것 같지만, Docker와 Podman은 엄연히 크게 다른 부분이 존재합니다. 그럼 Docker와 Podman은 어떤 부분이 같고, 어떤 부분이 다른 걸까요? 지금부터 Docker와 Podman의 기술을 알아보겠습니다.

 

Docker는 애플리케이션을 빌드하고 컨테이너화하기 위한 Docker Engine, 컨테이너 이미지를 배포하고 제공하기 위한 Docker Registry, 여러개의 컨테이너 애플리케이션을 정의하고 실행하기 위한 Docker Compose, 사용자의 로컬 컴퓨터나 클라우드 인스턴스에 도커 호스트를 구성해주는 Docker Machine, 컨테이너 클러스터링 및 스케줄링을 위한 Docker Swarm으로 구성됩니다. Podman은 Docker의 핵심 기능인 Docker Engine 기능과 유사하다고 볼 수 있습니다. 

Docker Engine에는 컨테이너 이미지를 관리하고, 컨테이너 이미지를 이용해 컨테이너를 실행하기 위한 Docker daemon이 있습니다. 그리고, 이미지와 컨테이너를 사용자가 관리하고 사용할 수 있도록 커맨드 기반으로 된 Docker Client가 있습니다. Podman 역시 컨테이너를 실행하기 위한 컨테이너 이미지, 그리고, 이미지를 통해 실행된 컨테이너와 이를 사용하기 위한 커맨드 기반의 유틸리티가 있습니다. 

Docker는 컨테이너 레지스트리로부터 이미지를 받아와 Docker 내부의 이미지 저장소에 저장을 합니다. 그리고, 이미지 저장소에 저장된 이미지를 이용하여 컨테이너를 실행할 수 있으며, 실행 중인 컨테이너를 이미지로 빌드할 수도 있습니다. Docker는 이런 다양한 작업들을 Docker daemon을 통해 수행합니다. 그렇기 때문에 데몬에 문제가 발생했을 경우 모든 컨테이너와 이미지에 영향이 가며, 커맨드 명령어로 컨테이너를 제어할 때도 영향을 미칩니다. 

반면에 Podman은 daemon 없이 커맨드로 컨테이너 레지스트리로부터 이미지를 받아와 Podman 호스트의 로컬 이미지 저장소에 이미지를 저장하고, 해당 이미지를 이용하여 컨테이너를 실행합니다. 이때 podman 라이브러리를 통해 바로 컨테이너를 실행하기 때문에 컨테이너 간에 서로 영향을 주지 않으며, 컨테이너와 이미지 사이, 커맨드 명령어로 컨테이너를 제어하거나 이미지를 관리할 때도 서로 영향을 주지 않습니다.

내가 경험한 컨테이너 이야기

이는 Docker 기반이였던 Red Hat OpenStack Platfom 13을 사용했을때와 현재 Red Hat OpenStack Platform 16에서 확실하게 경험할 수 있었습니다. 사실 Red Hat OpenStack은 11버전 (Ocata)부터 오픈스택의 모든 서비스들을 컨테이너화하기 위한 준비를 하고 있었습니다. 그리고, 13버전 (Queens)에서 Docker기반으로 오버클라우드의 오픈스택 서비스들이 모두 컨테이너 화가 되었습니다. 이 당시 모 프로젝트에서 OpenStack 13버전을 구축하게 되었는데, 그 당시  구축했던 버전의 Https Health-Check에서 메모리 누수 현상이 발생을 했었습니다. 메모리를 사용하면 메모리를 다시 반환해야 하는데, 반환하지 않아 사용 가능한 메모리가 줄어드는 현상이였습니다. 이런 경우 Docker 엔진이 영향을 받고, Docker 엔진이 정상적으로 수행할 수 없는 상태가 되면, 그 위에 올라가 있는 모든 컨테이너가 정상 동작을 하지 않습니다. 이렇게 되면 시스템 재부팅시 컨테이너를 모두 종료하고 시스템이 다운되는데, Docker 엔진이 정상적으로 종료되지 않기 때문에 컨테이너들을 제대로 종료할 수 없었습니다. 따라서, 강제 종료를 해야만 하는 상황이 발생을 합니다. 물론 이 문제는 그 다음 릴리즈에서 모두 해결되어 현재 설치하는 OpenStack 13 버전에서는 찾아볼 수 없는 현상이 되었습니다.

그런데, 이번에 릴리즈한 OpenStack 16(Train) 버전에서는 Docker 기반이 아닌 Podman으로 변경이 되었고, 설상가상으로 위와 같은 상황이 동일하게 발생한다고 하더라도, 특정 데몬 위에서 컨테이너가 실행되는 것이 아니므로 모든 컨테이너에 영향을 주지는 않을 것입니다. 문제가 발생한 특정 컨테이너만 트러블 슈팅을 하고 조치를 취하면 되기 때문입니다.


출처: https://naleejang.tistory.com/227 [Nalee와 함께 떠나는 IT이야기]

 

 

 

1. 컨테이너 표준 정립

IT 업계 종사자라면 컨테이너(Container)에 대해 한 번쯤은 들어본 적이 있을 것입니다. 애플리케이션과 바이너리, 라이브러리 등을 패키지로 묶어 배포하는 컨테이너는 서로 다른 컴퓨팅 환경에서 애플리케이션을 안정적으로 실행할 수 있으며 개발 환경에 구애 받지 않고 빠른 개발과 배포가 가능하다는 장점이 있습니다. 대표적인 IT 기업 중 하나인 구글은 지메일에서 유튜브, 검색에 이르기까지 모든 제품을 컨테이너에서 실행하고 있기도 합니다. 이처럼 컨테이너 기술은 IT 개발과 운영에 있어서 빼놓을 수 없는 필수 요소로 자리잡았습니다.

컨테이너에 대한 관심이 급격히 증가하면서 대부분의 주요 IT 벤더와 클라우드 공급자들은 컨테이너 기반의 솔루션을 발표했고 관련 스타트업 또한 급증해 컨테이너의 생태계를 넓혀왔습니다. 하지만 포맷과 런타임에 대한 특정한 규격이 없다 보니 컨테이너의 미래는 불안했던 것이 사실입니다. 일례로 2013년 출시된 도커(Docker)가 사실상의 컨테이너 표준 역할을 했지만 코어OS(CoreOS)는 도커와는 다른 규격으로 표준화를 추진하려 했습니다. 이러한 문제를 해결하기 위해 2015년 6월 도커, 코어OS, AWS, 구글, 마이크로소프트, IBM 등 주요 플랫폼 벤더들은 애플리케이션의 이식성(Portability) 관점에서 컨테이너 포맷과 런타임에 대한 개방형 업계 표준을 만들기 위해 OCI(Open Container Initiative)를 구성하였습니다. 이후 컨테이너 시장은 OCI의 런타임 명세와 이미지 명세를 준수하는 방향으로 성장하였고 그 과정에서 2016년 12월 쿠버네티스(Kubernetes)의 컨테이너 런타임을 만들기 위한 CRI(Container Runtime Interface)가 등장했습니다.

2. 컨테이너 런타임

CRI의 등장 배경을 이해하려면 먼저 컨테이너 런타임에 대해 살펴봐야 합니다. 컨테이너를 실행하기 위해서는 다음과 같은 세 단계를 거칩니다.

[그림 1] 컨테이너 실행 단계

OCI가 만들어질 당시 비공식적 표준 역할을 하던 도커는 컨테이너 런타임의 표준화를 위해 필요한 모든 단계가 아닌 세 번째 단계인 컨테이너의 실행 부분만 표준화하였습니다. 이로 인해 컨테이너의 런타임은 실제 컨테이너를 실행하는 저수준 컨테이너 런타임인 OCI 런타임과 컨테이너 이미지의 전송 및 관리, 이미지 압축 풀기 등을 실행하는 고수준 컨테이너 런타임으로 나뉘게 되었습니다.

저수준 컨테이너 런타임(Low-Level Container Runtimes)
컨테이너는 Linux namespace와 cgroup을 사용하여 구현합니다. namespace는 각 컨테이너에 대해 파일 시스템이나 네트워킹과 같은 시스템 리소스를 가상화하고 cgroup은 각 컨테이너가 사용할 수 있는 CPU 및 메모리와 같은 리소스 양을 제한하는 역할을 합니다. 저수준 컨테이너 런타임은 이러한 namespace와 cgroup을 설정한 다음 해당 namespace 및 cgroup 내에서 명령을 실행합니다.

[그림 2] 도커와 runC

OCI를 준수하는 저수준 컨테이너 런타임으로 가장 잘 알려진 것은 runC입니다. runC는 원래 도커에서 컨테이너를 실행하기 위해 개발되었으나, OCI 런타임 표준을 위해 독립적인 라이브러리로 사용되었습니다. 저수준 컨테이너 런타임은 컨테이너를 실제 실행하는 역할을 하지만 이미지로부터 컨테이너를 실행하려면 이미지와 관련된 API 같은 기능이 필요합니다. 이러한 기능은 고수준 컨테이너 런타임에서 제공됩니다.

고수준 컨테이너 런타임(High-Level Container Runtimes)
일반적으로 고수준 컨테이너 런타임은 원격 애플리케이션이 컨테이너를 논리적으로 실행하고 모니터링 하는데 사용할 수 있는 데몬 및 API를 제공합니다. 또한 컨테이너를 실행하기 위해 저수준 런타임 위에 배치됩니다.

이처럼 컨테이너를 실행하려면 저수준 및 고수준 컨테이너 런타임이 필요하기 때문에 OCI 런타임과 함께 도커가 그 역할을 했습니다. 도커는 docker-containerd라는 가장 잘 알려진 고수준 컨테이너 런타임을 제공합니다. containerd도 runC와 마찬가지로 도커에서 컨테이너를 실행하기 위해 개발되었으나 나중에 독립적인 라이브러리로 추출되었습니다.

[그림 3] 고수준·저수준 컨테이너 런타임 관계와 도커 아키텍처

CRI(Container Runtime Interface)
CRI는 쿠버네티스에서 만든 컨테이너 런타임 인터페이스로 개발자들의 컨테이너 런타임 구축에 대한 진입 장벽을 낮추어 줍니다. 초기 쿠버네티스는 컨테이너를 실행하기 위해 도커를 사용하였는데 이는 쿠버네티스 클러스터 워커 노드의 에이전트인 Kubelet 소스코드 내부에 통합되어 있었습니다. 이처럼 통합된 프로세스는 Kubelet에 대한 깊은 이해를 필요로 하였고 쿠버네티스 커뮤니티에 상당한 유지보수 오버헤드를 발생시켰습니다. 이러한 문제를 해결하기 위해 쿠버네티스는 CRI를 만들어 명확하게 정의된 추상화 계층을 제공함으로써 개발자가 컨테이너 런타임 구축에 집중할 수 있게 하였습니다.

[그림 4] Kubelet 동작 흐름과 CRI

3. 컨테이너의 새로운 생태계

CRI가 만들어진 후 주요 플랫폼 벤더들은 본격적으로 컨테이너 런타임 구축을 위해 노력하였습니다. 그 중 레드햇, 인텔, SUSE, Hyper, IBM 등의 관리자와 컨트리뷰터들이 커뮤니티 중심의 오픈소스 프로젝트인 CRI-O를 개발하였습니다.

CRI-O(Container Runtime Interface - Open Container Initiative)
CRI-O는 CRI와 OCI에서 유래된 프로젝트로 컨테이너 런타임 및 이미지가 OCI와 호환되는 것에 중점을 두고 있습니다. CRI 표준 컴포넌트를 최소한의 런타임으로 구현하며 쿠버네티스에서 모든 OCI 호환 런타임 및 컨테이너 이미지를 지원합니다.

[그림 5] 쿠버네티스와 도커 및 CRI-O

CRI-O는 컨테이너의 실행을 목적으로 경량화했기 때문에 도커가 제공하는 컨테이너 생성 및 이미지 빌드와 같은 기능은 제공하지 않습니다. 즉, CRI-O 덕분에 쿠버네티스는 컨테이너를 실행할 때 도커가 필요없었으나, 컨테이너의 생성 및 이미지 빌드와 같은 과정에서는 여전히 도커를 필요로 했습니다. 이러한 이유로 CRI-O 개발팀은 도커를 대체할 수 있는 새로운 생태계를 만들기 위해 노력하였습니다.

도커의 문제점
도커가 컨테이너의 생성 및 이미지 빌드를 모두 처리하는데 새로운 툴이 왜 필요할까요? 물론 기존 방식대로 도커를 사용할 수 있습니다. 그럼에도 CRI-O 개발팀이 도커의 역할을 대신할 수 있는 생태계를 위한 툴(Buildah 빌다, Podman 포드맨, Skopeo 스코피오)을 개발한 이유는 다음과 같은 문제점이 제기되었기 때문입니다.

도커는 클라이언트/서버 애플리케이션으로 클라이언트인 Docker CLI와 서버인 Docker daemon으로 구성됩니다. 그 중 서버는 컨테이너 이미지 빌드, 관리, 공유, 실행 및 컨테이너 인스턴스 관리와 같이 너무 많은 기능을 담당하는 데몬으로 모든 컨테이너를 자식 프로세스로 소유합니다. 이로 인해 무거울 뿐 아니라 장애가 발생하면 모든 자식 프로세스에 영향을 끼쳐 단일 실패점(Single point of failure)이 될 위험이 있습니다. 또한 클라이언트-서버 모델을 사용할 경우 리눅스의 audit.log를 통해 관리자가 시스템의 보안 이벤트를 감시하고 기록된 정보를 볼 수 있는 audit 보안 기능을 사용할 수 없게 됩니다.

그림 6은 fork·exec 모델 및 클라이언트-서버 모델에서의 UID, auid 설정 동작 방식이 있으며, 클라이언트 - 서버의 경우 컨테이너 이미지 빌드, 관리, 공유 같이 많은 기능이 이 있어 무겁고 장애 발생 시 모든 자식 프로세스에 영향을 미치게 된다는 것을 보여주는 그림[그림 6] fork·exec 모델 및 클라이언트-서버 모델에서의 UID, auid 설정 동작 방식

추가로 모든 도커 명령은 루트 권한을 가진 사용자에 의해서만 실행할 수 있어 보안 문제가 발생할 수 있습니다. 이는 아래에 소개하는 Buildah, Podman, Skopeo를 사용하면 해결할 수 있습니다.

CRI-O와 함께 사용 가능한 툴: Buildah, Podman, Skopeo
Buildah, Podman, Skopeo는 별도의 데몬 없이 전통적인 fork·exec 모델을 사용하며 사용자 네임 스페이스를 이용해 컨테이너를 실행함으로써 단일실패점, audit 보안 기능 사용 및 루트 권한 문제를 해결하였습니다. 도커의 서버가 너무 많은 기능을 가지고 있는 단점은 각 툴 별로 다음과 같이 기능을 나누어 제공하는 방식으로 보완하였습니다.

[그림 7] 도커의 동작 흐름 및 Podman, Buidah, Skopeo의 역할

Buildah는 CRI-O에서 이미지를 빌드할 때 도커의 종속성을 제거하기 위해 개발되었고 Dockerfile 없이 다른 스크립트 언어를 사용해 컨테이너 이미지를 빌드하는 것을 목표로 합니다.

Podman은 pull 및 tag 지정과 같은 OCI 컨테이너 이미지를 유지관리하고 수정하는데 도움이 되는 모든 명령 및 기능을 제공합니다. 또한 컨테이너 작성, 실행 및 유지보수도 할 수 있습니다. 즉, Docker CLI에서 수행할 수 있는 명령은 Podman CLI에서도 동일하게 수행 할 수 있습니다.

Buildah와 Podman은 일부 겹치는 기능이 있는데 Buildah는 OCI 이미지를 생성하는 효율적인 도구로, Podman은 그러한 이미지와 이미지를 통해 생성한 컨테이너를 유지하고 관리하는 도구로 이해하면 됩니다. 기술적으로 buildah run은 Dockerfile RUN을 에뮬레이트하며 podman run은 docker run을 에뮬레이트 합니다.

Skopeo는 이미지 저장소에서 다양한 작업을 수행하는 명령줄 도구입니다. 기존 도커가 다른 레지스트리에 이미지를 복사하기 위해 pull, tag, push를 사용했다면 Skopeo는 간단하게 copy 명령으로 해당 기능을 제공합니다. 추가로 저장소에 있는 이미지 이름에 대해 로우 레벨 정보를 제공해줍니다.

4. 컨테이너의 미래

지금까지 도커, OCI, CRI, Buildah, Podman 등 컨테이너 기술의 전반적인 흐름과 생태계에 대해 알아보았습니다. 컨테이너는 온프레미스 환경에서 클라우드 네이티브 환경으로 옮기는 것을 쉽게 해주기 때문에 클라우드 컴퓨팅 분야에서 가장 주목 받는 기술 중 하나로 성장하고 있습니다. 그리고 그 중심에는 단연 도커가 자리잡고 있습니다. 하지만 컨테이너의 표준화가 이뤄진 후 컨테이너 시장은 OCI와 CRI를 중심으로 성장하고 있습니다. 그 과정에서 사실상 컨테이너 표준으로 사용되던 도커의 역할을 대신하는 다양한 기술이 나오고 있으며 그 중 Buildah, Podman, Skopeo는 도커의 기능을 역할별로 나눠 구현하고 있습니다. 이들은 또한 도커의 보안 관련 단점을 보완하며 기존에는 없던 편리한 기능도 추가로 제공합니다.

컨테이너 생태계는 계속해서 성장하고 있으며 도커 외에도 사용할 수 있는 다양한 대체기술을 선보이고 있습니다. 향후 컨테이너는 OCI의 설립 목적인 ‘통일된 표준을 통해 어디서든 작동하는 이식성 제공’을 위해 OCI와 CRI 표준을 중심으로 생태계를 넓혀갈 것입니다.

 

출처 : https://www.samsungsds.com/kr/insights/docker.html

'Docker' 카테고리의 다른 글

Podman(PodMANger tool)  (0) 2021.12.26
Docker vs Podman  (0) 2021.12.26
헷갈리는 Docker - 수정  (0) 2021.12.26
Docker Commands for Managing Container Lifecycle  (0) 2021.12.25
Docker run, start, create 차이  (0) 2021.12.25

이번 글은 Docker의 개념 보다는 Docker를 처음 접하게 되었을 때 혼란스러웠던 내용을 정리한 글이다. Docker 컨테이너를 사용하는 용도는 여러가지가 있겠지만 이 글에서는 개발자가 만든 애플리케이션을 Tomcat과 같은 애플리케이션 서버에 탑재해서 배포하는 경우 겪게 되었던 내용이다.

 

Docker는 Virtual machine이 아니다!

처음 docker를 실행할 때 다음과 같은 명령을 실행하면 우분투 서버가 실행된다고 생각했다. 즉,  Virtual machine과 같이 컨테이너 내에 우분투 서버가 실행되는 줄 알았다.

$ docker run --name ubuntu_test ubuntu

위 명령을 실행하면 그냥 아무것도 실행하지 않은 것 처럼 아무런 변화가 없다. 다만 다음과 같이 실행되지 않는 docker container를 보는 옵션(-a)을 주고 docker container의 목록을 보면 종료(Exit)되었다고 나타난다.

$ docker ps -a
CONTAINER ID   IMAGE                             COMMAND                  CREATED          STATUS
PORTS     NAMES
243c9dff7955   ubuntu                            "bash"                   19 minutes ago   Exited (0) 19 minutes ago              ubuntu_test

필자의 경우 여기서 부터 혼란스러웠다. 우분투 image를 실행했는데 왜 아무것도 실행되지 않고 바로 Exit 되었을까? 결론은 Docker의 컨테이너는 Virtual machine과 같이 하나의 온전한 서버를 제공하는 것이 아니라 명령을 실행하는 환경만 제공하고 그 명령을 실행할 뿐이다[1].

위의 예제에서 보면 "docker ps -a" 명령으로 나타난 컨테이너 목록에서 다음과 같은 내용을 볼 수 있다.

/bin/bash     19 seconds ago

이 결과로 유추해보면 우분투 컨테이너를 실행하면 우분투 서버가 실행되는 것이 아니라 "/bin/bash" 가 실행되는 것 뿐이다. 이것이 Virtual machine의 컨테이너와 Docker 컨테이너의 가장 큰 차이점이다. 일반적으로 Linux 서버(Ubuntu or CentOS 등)나  Windows와 같은 운영 체제를 실행한다는 의미에는 많은 것을 내포하고 있다. 대략 다음과 같은 기능들이 실행될 것이다.

  • 프로그램 실행 기능
    • Memory, CPU 등의 하드웨어 자원을 이용하여 프로그램을 실행할 수 있는 환경 제공
  • 네트워크 서비스 제공
    • NIC 등을 하드웨어 자원을 인식해서 네트워크 처리가 가능한 환경을 제공
  • 키보드, 모니터, 마우스 등과 같은 주변 장치의 입출력에 대한 처리
    • 사용자로부터의 입력과 결과를 출력해주는 기능 제공
  • 외부에서 접속할 수 있는 환경
    • sshd 등과 같은 데몬을 실행하여 서버 외부에서 네트워크를 이용하여 원격에서 접속할 수 있는 기능 제공

그리고 이 모든 것은 사용자가 임의로 전원을 끄기 전에는 지속적으로 동작하는 특징을 가지고 있다. Virtual machine 들은 이런 속성을 가지고 있다. 다만 여러 하드웨어 자원을 Host OS로 부터 할당 받은 것만 사용하도록 되어 있는 것이다.

 

그러면 컨테이너를 실행(run)하면 bash가 실행되어 prompt가 container의 bash prompt가 나타나야 하는게 정상 아닌가? 왜 Exit 되어 버리는가?

Docker 컨테이너는 단지 명령만 실행하고 그 결과만 보여주는 기능을 수행한다.[1]

즉, 앞의 예제에서는 우분투의 docker image에서 설정된 default 실행 명령[2]인 "/bin/bash" 를 실행하고 그 결과를 출력하고 종료된 것이다. "/bin/bash" 명령은 표준 출력(STDOUT) 또는 표준 에러(STDERR)로 아무것도 출력을 하지 않기 때문에 사용자가 보기에는 실행이 안된 것과 같은 느낌으로 다가 온다. 다음을 실행해보면 무엇을 말하는 것인지 알 수 있다.

$ docker run --name ubuntu_test ubuntu "env"
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=c4dc55ac2ddb
HOME=/root

docker run 명령에서 마지막에 주는 인자 값은 이 컨테이너가 실행할 명령을 전달하는 인자이다. 우분투 컨테이너의 경우 아무런 값을 입력하지 않으면 "/bin/bash"가 실행되고, 인자를 전달하면 그 값이 실행된다. 예제에서는 "env" 명령을 실행하도록 하였는데 "env" 명령은 시스템의 환경 정보를 출력하는 명령이다. 디렉토리 목록을 출력하는 "ls" 명령도 실행할 수 있다.

$ docker run --name ubuntu_test ubuntu "ls"
bin
boot
dev
etc
home
lib
...

docker 컨테이너를 실행한 다음 동일한 명령을 실행하면 다음과 같은 에러가 나타나는데 이것은 docker run 명령이 "create" 와 "start" 명령을 한번에 실행시키는 명령이기 때문에 create 시 이미 동일한 이름의 컨테이너가 존재하기 때문에 발생하는 문제이다. 이 경우 docker rm <id or name>으로 삭제한 후 다시 실행하면 된다.

docker: Error response from daemon: Conflict. The container name "/ubuntu_test" is already in use by container "065b413106d50d69b01077daccc5f7c1c406d98f993c71e6834d6e22318b93e4". You have to remove (or rename) that container to be able to reuse that name. See 'docker run --help'.

아니면 docker run 명령에 "--rm" 옵션을 주어 docker  컨테이너가 종료됨과 동시에 자동으로 삭제되게 할 수 있다. 실제 환경에서는 이 옵션을 사용하기도 하지만 이 글에 있는 예제를 확인하기 위해서라면 --rm 옵션을 주지 않고 실행하는 것을 권장한다.

우분투의 bash shell에서 명령을 실행하려면

위 예제에서 Docker run  명령행이 아닌 우분투 image의 bash shell에서 "ls", "cat" 등과 같은 명령을 실행하려면 어떻게 해야 할까? Docker 컨테이너를 실행할 때 다음 두 옵션을 추가하면 가능한데 대략 다음과 같은 의미이다.

  • i : Interactive 모드로 표준입력과 표준출력을 키보드와 화면을 통해 가능하도록 하는 옵션이다.
  • t:  텍스트 기반의 터미널(TTY)을 애뮬레이션해주는 옵션이다.

실행은 다음과 같이 한다.

$ docker run -it --name ubuntu_test ubuntu
# 다음과 같이 ubuntu의 bash shell로 prompt가 나타나고 해당 container 내에서 명령을 실행할 수 있다.
root@7a08aa924dcd:/# df -k
Filesystem     1K-blocks    Used Available Use% Mounted on
none            61896484 9147280  49581972  16% /
tmpfs              65536       0     65536   0% /dev
tmpfs            1023376       0   1023376   0% /sys/fs/cgroup
/dev/vda2       61896484 9147280  49581972  16% /etc/hosts
shm                65536       0     65536   0% /dev/shm
tmpfs            1023376       0   1023376   0% /sys/firmware

이 부분에서 Docker를 처음 접하는 개발자는 또 한번 혼란스러움을 겪을 수 있다. 위 예제와 같이 shell 이 나오고 내가 필요한 명령이 사용 가능하게 되면 마치 우분투 서버가 실행되었다는 착각을 하게 된다. 그리고 해당 shell에서 tomcat이나 rails와 같은 애플리케이션 서버를 설치하고, 실행해본다. 잘 돌아간다. 문제는 이렇게 한 다음 shell에서 "exit" 를 입력하여 shell에서 나오는 순간 컨테이너는 다시 중지된다.

Docker 컨테이너를 백그라운드로 실행하면?

여기까지 진행해보면 이런 생각을 하게 된다.

Docker의 컨테이너도 Host OS의 입장에서 보면 하나의 프로세스이기 때문에 프로세스가 종료(위에서는 shell에서 exit를 입력) 되면 컨테이너가 종료되겠지. 그러면 종료되지 않게 Docker 컨테이너 프로세스를 백그라운드로 실행하면 되지 않을까?

Docker 명령 옵션에 보면 다음과 같은 옵션이 있다.

$ docker help run
-d, --detach       Run container in background and print container ID
$ docker run -d --name ubuntu_test ubuntu
981c0ec37c33631a8625549027ca644bb064d5e00eecb4d890b011d4396dbdb9
$ docker ps -a | grep ubuntu
981c0ec37c33        ubuntu         "/bin/bash"     7 seconds ago       Exited (0) 6 seconds ago        ubuntu_test

"-d" 옵션이 Docker의 컨테이너를 백그라운드 프로세스로 실행하는 옵션이다.  위 예제에서 처럼 실행하면 컨테이너 ID가 출력되는데 이 ID나 --name 옵션으로 입력한 이름을 이용하여 컨테이너에 접근할 수 있다. "-d" 옵션을 주고 실행해도 docker의 컨테이너 상태를 확인해보면 "Exited" 상태인 것을 알 수 있다.

왜 이렇게 되는 것일까? docker 의 컨테이너를 실행한다는 것은 Host OS에서 프로세스를 실행하는 것과 동일한 개념이기 때문에 docker  컨테이너에서 실행되는 명령이 계속 실행되고 있는 상황이 아니면 그 명령이 종료됨과 동시에 컨테이너도 종료되기 때문이다. 이제 앞에서 실행한 interactive 모드인 "-it" 옵션을 주어 실행해보자.

$ docker run -d -it --name ubuntu_test ubuntu
8e7e71b7b692c7f293593d3f504bacec70e5541bae2021ebc0dbb28d9b8add21
$ docker ps -a | grep ubuntu
8e7e71b7b692        ubuntu         "/bin/bash"     2 seconds ago       Up 2 seconds      ubuntu_test

이렇게 해서 일단 1차 원하는 목적이었던 shell을 백그라운드로 실행하는데는 성공하는 듯 보였다.

일반적으로 Virtual machine 으로 우분투를 실행한 경우, 이 서버에 접속하려면 ssh 와 같은 리모트쉘을 이용하지만 docker 컨테이너의 경우 일반적으로는 sshd를 실행하지 않는다. 대신 Host OS에서 docker attach 명령을 이용하여 컨테이너에 접속할 수 있다. 다음 명령을 이용하여  docker의 컨테이너에 접속할 수 있다.

$ docker attach ubuntu_test
root@2b206e1f3c07:/#
root@2b206e1f3c07:/# ls -al
total 72
drwxr-xr-x  34 root root 4096 Dec 17 13:02 .
drwxr-xr-x  34 root root 4096 Dec 17 13:02 ..
-rwxr-xr-x   1 root root    0 Dec 17 13:02 .dockerenv

하지만 여기서도 여전히 문제가 발생한다. 이 shell에서 "exit" 명령을 이용하여 shell을 나오게 되면 컨테이너도 같이 종료하게 된다.

root@2b206e1f3c07:/# exit
$ docker ps -a | grep ubuntu
8e7e71b7b692        ubuntu         "/bin/bash"     5 seconds ago       Exited (0) 5 seconds ago       ubuntu_test

이것은 run -it 옵션과 attach 명령의 내용을 조금만 보면 예측할 수 있다. "-it" 옵션은 컨테이너의 입출력을 interactive 하게 하는 옵션과 TTY 터미널을 애뮬레이션 해주는 옵션이다. 이것을 백그라운드로 실행시킨 것이다. 그리고 attach 명령은 Virtual machine의 리모트쉘 접속과 같은 개념이 아니라 컨테이너의 현재 Host OS shell(local)의 stdout, stderr을 docker 컨테이너에 붙이는 명령인 것 뿐이다.

attach      Attach local standard input, output, and error streams to a running container

즉 -it 옵션을 이용하여 interactive 모드로 실행하고 이것을 다시 -d 옵션을 주어 백드라운드로 실행하게 되면 interactive 쉘이 백그라운드로 동작하고 있는 것이다. 여기에 attach 명령으로 접속하여 exit 명령을 실행하면 interactive 쉘(/bin/bash)이 종료되고 이 쉘이 종료되면 결국 docker 컨테이너도 종료하게 되는 것이다.

Docker에 애플리케이션 서버 실행하기

여기까지 확인한 상태에서 필자가 내뱉은 한마디는 "이런 황당한 시츄에이션이! 이런 방식이면 어떻게 애플리케이션 서버를 실행하냐고?" 였다. 하지만 정답은 위에서 이미 다 나와 있었다. Docker의 컨테이너에서 실행되는 명령(위 예제에서는 /bin/bash)을 영원히 실행되게 하면 된다. 예를 들어 다음과 같이 컨테이너를 실행한다.

$ docker run -d --name ubuntu_test ubuntu /bin/bash -c "while true; do echo "still live"; sleep 100; done"
eb3b9e69b18d826dcc8788fc01930b4c411dabee4cbdfb646af79cb2cfbeacba
$ docker ps -a | grep ubuntu
eb3b9e69b18d        ubuntu             "/bin/bash -c 'whi..."   7 seconds ago       Up 8 seconds       ubuntu_test

위 실행 옵션은 /bin/bash를 실행하면서 -c 옵션에 있는 명령을 실행하게 하는 것이다. -c 에 있는 옵션은 무한 루프를 돌면서 100초마다 한번씩 "still live"를 출력하는 기능을 수행하는 shell script 이다. 이렇게 무한 루프를 도는 명령을 -d 옵션을 이용하여 백드라운드로 실행하기 때문에 컨테이너 실행 후 바로 Host OS의 Prompt로 돌아오지만 컨테이너는 여전히 살아 있는 것을 확인할 수 있다.

이 컨테이너에 이제 attach 해보자. attach 명령을 실행해보면 아무 반응도 없고 100초마다 "still live"만 출력하는 것을 볼 수 있다. 그렇다고 shell prompt가 나타나지도 않는다. 이것은 attach 명령어가 stdout, stderr을 가져오는 것이기 때문에 당연한 것이다. Shell에 접속하기 위해서는 attach가 아닌 exec 명령을 이용해서 컨테이너의 쉘 환경에 접속할 수 있다.

$ docker help exec
Usage:    docker exec [OPTIONS] CONTAINER COMMAND [ARG...]
Run a command in a running container
Options:
-i, --interactive          Keep STDIN open even if not attached
--privileged           Give extended privileges to the command
-t, --tty                  Allocate a pseudo-TTY
$ docker exec -it eb3b9e69b18d /bin/bash
root@eb3b9e69b18d:/# exit
$ docker ps -a | grep ubuntu
eb3b9e69b18d        ubuntu             "/bin/bash -c 'whi..."   14 minutes ago       Up 14 minutes       ubuntu_test

이렇게 접속한 다음 exit 명령을 이용하여 shell을 빠져 나와도 컨테이너는 정상적으로 동작하는 것을 알 수 있다.

지금까지 내용을 종합해보면 Docker의 컨테이너 내에 애플리케이션 서버를 실행하려면 애플리케이션 서버가 무한루프로 동작하게 해야 한다. 하지만 이미 애플리케이션 서버들은 무한루프로 동작하는 프로그램들이다. 따라서 다음 내용만 주의하면 된다.

Docker 컨테이너에서 실행되는 애플리케이션 서버(DB 서버 포함)은 back ground 모드가 아닌 fore ground 모드로 실행해야 한다.

Tomcat의 경우 예를 들면 일반적으로 다음과 같이 실행하여 백드라운드 모드로 동작하게 한다. 이유는 tomcat 서버를 실행시킨 shell이 종료되더라도 tomcat 서버는 정상적으로 계속 동작하게 하기 위해서이다.

$ cd $CATALINA_HOME/bin;
$ ./catalina.sh start;

하지만 docker 환경에서 이렇게 하면 컨테이너가 바로 종료되어 Tomcat 서버가 죽는 것과 동일한 상황을 맞게 된다. 다음과 같이 fore ground로 실행해야 한다.

$ cd $CATALINA_HOME/bin;
$ ./catalina.sh run;

실제 docker 환경에서 tomcat을 실행하는 경우 위 예제와 같이 명령을 직접 입력하지 않고 Dockerfile에 실행 명령을 지정하는데 이때 위와 같이 fore ground 명령을 사용해야 하는 것이다. dockerhub에 있는 공식  tomcat 컨테이너의 image를 만드는 Dockerfile에도 다음과 같이 사용하고 있다.

FROM openjdk:8-jre-alpine
ENV CATALINA_HOME /usr/local/tomcat
ENV PATH $CATALINA_HOME/bin:$PATH
...
EXPOSE 8080
CMD ["catalina.sh", "run"]

대부분의 컨테이너는 최소한의 구성만 있다.

여기까지 진행하면 Docker가 대충 어떻게 돌아가는 지를 이해했으며 원하는 목적을 이루었다. 하지만 이 상황에서 다시 몇가지 사소한 이슈가 발생하였다.  가장 큰 이슈는 컨테이너 내부에 들어가면 애플리케이션 서버를 관리할 수 있는 것이 아무것도 없다는 것이다. Docker의 컨테이너는 대부분 Minimalism을 중요시하기 때문에 딱 필요한 것만 설치한다. 이렇게 작게 만드는 것은 마이크로 서비스 환경에서 하나의 서버에 수십개 이상의 컨테이너가 실행되는 환경을 상상해보면 쉽게 이해할 수 있다.

하지만 컨테이너를 작게 만들다 보니 기본적인 도구조차 없는 경우가 대부분이다. 필자의 경우 기본으로 제공하는 이미지를 사용하지 않고 다음 정도는 설치되어 있는 환경을 구성하여 사용하고 있다.

  • JRE 가 아닌 JDK로 구성 이렇게 구성하는 가장 큰 이유는 JRE로 구성하게 되면 자바 프로세스에 대한 모니터닝을 할 수 있는 도구가 없다. 애플리케이션 서버에 성능상 문제가 발생했을 때 jstat, jmap, jstack 등과 같은 명령을 이용하여 jvm의 상태 정보를 조회할 수 있어야 하는데 이런 도구가 없어 불편한 경우가 많다.
  • vi 파일을 내용을 확인하기 위해서는 cat, grep 등 기본 명령만으로는 부족해서
  • net-tools ifconfig도 없고, netstat 등 네트워크 관련 뭔가 볼 수 있는게 없는데 기본적으로 이런 명령은 되어야 운영 환경에서 문제가 생겼을 때 원인 파악이라도 할 수 있어야 하기 때문이다.

환경설정을 바꾸고 다시 실행하면...

앞에서 설명했듯이 Docker 컨테이너에서 실행되는 애플리케이션 서버는 fore ground로 실행되는데 이렇게 할 경우 귀찮은 문제가 발생한다. 간단한 성능 확인이나 기능 확인 등을 위해 Tomcat 서버의 옵션을 하나 수정한 다음 재시작하게 될 때 문제가 된다. fore ground로 실행되어 있기 때문에 Tomcat 서버가 재 시작되면 해당 컨테이너가 종료된다. 즉, 옵션 하나 바꾸는 것도 컨테이너에서 직접 바꾸면 안되고 Dockerfile을 이용해서 바꾼 후 이미지를 빌드하고, 컨테이너를 시작해야만 적용할 수 있다. 이런 사이클은 개발 단계나 운영 환경에 문제가 발생하여 지속적으로 튜닝을 하면서 상태를 지켜보는 과정이라면 좋은 사이클이라 할 수 없다.

필자가 선택한 방법은 서비스 개발 후 초기 얼마 동안은 다음과 같은 형태로 컨테이너를 구성하였다.

  • 애플리케이션 서버는 백그라운드로 실행
  • 애플리케이션 서버 실행 후 바로 shell script로 무한 반복 스크립트 실행

즉, 다음과 같은 bootstrap.sh 명령을 실행하도록 하였다.

# Run background
bin/catalina.sh start
# Run script forever
while true; 
  do echo "still live"; 
  sleep 600; 
done

이렇게 하면 설정 변경 후 재시작해도 Docker 컨테이너는 여전히 계속 살아 있고 변경된 옵션에 대해 확인할 수 있다. 이 방법은 서비스 초기 어떤 옵션이 서비스에 맞는지 확인하는 용도로만 사용하고, 정상적인 운영환경에서는 사용하지 않는 것을 권장한다.

프로그램에서 출력하는 로그는 어떻게 해야 하나?

일반적인 환경에서 tomcat등과 같은 애플리케이션 서버의 로그는 파일에 저장하도록 하였다. 로그를 파일에 저장하고 로그 파일의 크기 또는 일자에 따라 파일을 롤링하는 방식을 많이 사용하였는데 이것은 애플리케이션 서버를 백그라운드로 실행하기 때문에 백그라운드로 실행된 상태에서 로그를 확인하기 위해서는 로그를 표준출력으로 보내기 보다는 파일로 저장하는 것이 훨씬 관리하기 편했기 때문이다. 물론 백그라운드로 실행했다고 해서 파일로 저장 못하는 것은 아니지만 사이즈, 크기 등에 따른 롤링 등을 설정하기 위해서는 별도 작업을 해줘야 하는 번거로움이 있다.

그러면 Docker  컨테이너에 애플리케이션 서버를 실행하는 경우 로그는 어떻게 해야 할까? 다음 세가지 방안 정도로 생각할 수 있다.

  1. 이전과 동일하게 파일로 저장한다.이것은 컨테이너의 특정 디렉토리에 저장하게 한다는 의미인데 이렇게 할 경우 해당 컨테이너가 삭제되면 로그도 같이 삭제되기 때문에 권장하지 않는다.
  2. 모든 로그를 표준 출력(STDOUT) 또는 표준 에러(STDERR) 로 출력한다.Docker 는 컨테이너에서 STDOUT나 STDERR로 출력하는 모든 메시지를 Host OS의 특정 디렉토리에 저장하고 이를 쉽게 조회할 수 있는 명령도 제공한다(docker logs 명령). 따라서 모든 로그를 표준 출력으로 보내면 쉽게 로그에 접근할 수 있게 된다.
  3. 이 방식도 문제가 존재하는데 이 로그 파일을 하나의 파일로 관리하게 되면 파일이 너무 커지게 되어 스토리지를 모두 차지하게 되는 문제가 있다. 최근 버전의 Docker에서는 로그 파일을 롤링할 수 있는 기능을 제공하는데 이 방식을 사용할 경우 반드시 이 옵션을 사용하는 것을 권장한다.
  4. 1번과 같이 파일에 로그를 저장하지만 로그 디렉토리를 Host OS의 볼륨을 이용한다.이렇게 하면 1번의 로그가 삭제되는 문제와 2번의 롤링 문제를 해결할 수 있지만 컨테이너 생성 시 볼륨을 붙여줘야 한다.

필자의 환경에서는 2번을 사용하고 있다. 스테이징 환경에서는 사이즈나 롤링 설정을 하지 않아 가끔 스테이징 서버의 디스크가 Full 나는 상황이 발생하는데 대부분 로그 문제 때문이다.

맺음말

지금까지 필자가 Docker 를 이용하여 애플리케이션 서버를 구성할 때 부딪혔던 Docker 컨테이너에 대해 잘못 알고 있었던 내용과 몇가지 이슈를 설명하였다. 필자가 속해 있는 개발팀에 Docker 를 처음 접해보는 개발자들이 어떻게 Docker를 이해하는지 유심히 살펴보니 대략 필자와 비슷한 오해와 사용을 하고 있는 것을 관찰할 수 있었다. 그리고 대부분의 Docker 문서에서는 이런 내용들에 대한 설명이 많지 않다. 이 글이 이런 오해를 하는 개발자들에게 조금이나마 도움이 되었으면 하는 바램이다.

각주

[1]물론 Docker 내부적으로는 다른 무엇인가 있겠지만 그런 내용을 설명하기 위한 글이 아니라 이렇더라 정도를 알려주는 것이 목적이기 때문에 이렇게만 설명한다. 이것도 정확한 내용이라기 보다는 필자가 나름대로 추측해서 정리한 것이다. 필자 역시 Docker에 대해 깊게 고민을 하지 않았다.

[2] 이것은 Docker image를 만들때 임의로 지정할 수 있다. 우분투 기본 image가 /bin/bash를 실행하게 만들어졌다.

 

원문 : https://www.popit.kr/개발자가-처음-docker-접할때-오는-멘붕-몇가지/

 

+ Recent posts