Docker

Docker 기초 배우기

드리프트2 2024. 3. 30. 15:15

Docker를 사용하는 이점

  • 환경 구축 시간 단축
  • 인프라를 코드화하여 누구나 동일한 환경을 구축할 수 있음
  • CI, CD 등의 자동화 소프트웨어와 잘 어울림

컨테이너란?

화물선 등에서 사용되는 격리되고 패키징된 상자와 같습니다. 인프라의 컨테이너도 마찬가지로, 서버 상에서 격리된 앱 공간을 만들 수 있는 기술입니다.

 

Docker 외에도

  • Linux Containers
  • Hyper-V 컨테이너
  • Windows 컨테이너 등이 있지만, Linux에서의 사용 편의성 때문에 Docker가 인기를 얻고 있는 듯합니다.

가상화와 컨테이너(Docker)의 차이

Docker(컨테이너)

  • 메모리 상에 존재
  • 게스트 OS가 없음
  • 메모리 상에 존재하므로 데이터는 일시적
  • Dockerfile로 코드화 가능

가상화

  • 디스크 상에 존재
  • 호스트 OS와 게스트 OS로 나뉨
  • 항상 저장됨
  • 코드화 불가능

Dockerfile, Docker 이미지, 컨테이너의 관계

컨테이너는 격리된 앱의 상자입니다.

 

Docker 이미지는 설계도(레이어로 구성된 정적 템플릿 파일)입니다.


Dockerfile은 그것을 언어화, 파일화한 것입니다.

 

Docker 이미지가 클래스라면 컨테이너가 인스턴스라고 해석하는 것이 적절할 것 같습니다.

 

Docker Hub에는 예를 들어 PHP의 공식 Docker 이미지 등이 준비되어 있어 그것을 가져오고(pull) 사용할 수 있습니다.

Docker 이미지의 태그에 대해

태그는 반드시 지정해야 합니다.

 

태그명 = 버전명

ubuntu:18.04

 

지정하지 않으면 latest(최신판)가 들어오게 됩니다.


배포된 시기에 따라 버전 차이가 발생하여 문제가 생길 수 있습니다.

Docker Hub에 대해

계정을 만들지 않으면 pull에 제한이 있는 듯 하니 만들어 두는 것이 좋습니다.


만드는 것만으로는 부족하고 Docker Desktop에서 로그인해야 합니다.


명령줄에서도 로그인할 수 있습니다.

docker login

자주 사용하는 명령어

먼저 연습용 디렉토리를 만들고 그 안에서 실행해 봅시다.


기본적으로 공식 레퍼런스를 보는 것이 좋습니다.

docker run

실행하고 싶은 이미지를 지정하여 실행

docker run hello-world

 

이미지가 로컬에 없다면 자동으로 docker hub에서 pull해 옵니다.

 

docker run 명령은 먼저 지정된 이미지 위에 쓸 수 있는 컨테이너 레이어를 create(생성)합니다.

 

그런 다음 지정된 명령어를 사용하여 start(시작)합니다.

docker ps

컨테이너 프로세스 목록을 표시합니다.

docker ps

 

방금 만든 hello-world가 표시되지 않습니다.


이는 hello-world가 다음 메시지를 출력하고 나면 프로세스를 종료하는 이미지이기 때문입니다.

Hello from Docker! 
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon. 
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
   (arm64v8)
3. The Docker daemon created a new container from that image which runs the
   executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
   to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

 

다음 옵션을 주면 중지된 프로세스도 표시됩니다.

docker ps -a  

 

프로세스에 다른 이름을 붙일 수도 있습니다.


예를 들어 hello-world를 konitiwa라는 이름으로 실행시킵니다.

docker run --name konitiwa hello-world

 

--rm을 붙이면 중지와 동시에 컨테이너를 삭제해 줍니다.

docker run --name rmtest --rm hello-world

 

ID와 name만 출력하고 싶다면

docker ps --format "{{.ID}}: {{.Names}}" -a

docker start, stop, restart

start, stop, restart를 시험해 보기 위해 centos 이미지에서 컨테이너를 실행합니다.

# -i, -t 옵션으로 실행하며 bash를 지정하여 컨테이너에 들어감
docker run -it --name mycentos centos:8 /bin/bash
# 연결되었다가 일단 나옴 
exit
# mycentos가 중지되었는지 확인
docker ps -a

 

-it 옵션에 대해서

 

name, container id 어떤 것으로 지정해도 동작함을 알 수 있습니다.

docker start mycentos  
docker stop mycentos
docker restart 3bbb6244c880

docker exec

컨테이너에 들어가 버전 표시

docker exec -it mycentos /bin/bash
[root@3bbb6244c880 /]# cat /etc/redhat-release  
CentOS Linux release 8.4.2105

 

컨테이너에 들어가지 않고 centos의 버전 정보를 가져옴

docker exec mycentos cat /etc/redhat-release
↓
CentOS Linux release 8.4.2105

docker rm

컨테이너를 삭제합니다.

 

-f로 강제 삭제가 가능하지만 중지한 후에 하는 것이 좋습니다.

docker rm 컨테이너지정

 

데이터를 영구화하지 않으면 생성한 파일까지 모두 사라지므로 주의해야 합니다.

docker images

로컬에 있는 모든 이미지를 표시합니다.

docker images

 

예상외로 용량이 크므로 정기적으로 확인하고 삭제하는 것이 좋습니다.


이미지 역시 컨테이너와 마찬가지로 ID를 가지고 있습니다.

docker rmi

Docker 이미지를 삭제합니다.


실행 중이거나 의존 관계가 있는 베이스 이미지는 삭제할 수 없습니다.

docker rmi 이미지지정

 

컨테이너가 실행 중인 이미지를 삭제하려 하면 오류가 발생합니다.

% docker rmi e6a0117ec169
Error response from daemon: conflict: unable to delete e6a0117ec169 (cannot be forced) - image is being used by running container 3bbb6244c880

docker image prune

사용하지 않는 이미지를 일괄 삭제할 수 있습니다.

% docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Deleted Images:
deleted: sha256:bad28a0252ef2a7490c95c11
deleted: sha256:845fc4a1c9954fd36895c11  
deleted: sha256:e7579523d95c1195c11
deleted: sha256:e060cc6d9995c1195c11
deleted: sha256:63cca88695c1195c1195c11

docker build

Dockerfile에서 이미지를 생성합니다.

docker build Dockerfile의경로

docker cp

컨테이너와 호스트 머신 간에 파일 송수신을 할 수 있는 복사 명령어입니다.

# 반대로도 가능합니다.
docker cp 컨테이너의파일경로:호스트의파일경로  

docker logs

# 실행한 명령의 로그를 볼 수 있습니다.
docker logs 컨테이너명

# 실시간 모니터링  
docker logs -f 컨테이너명

docker inspect

컨테이너의 세부 정보를 확인할 수 있습니다.


주로 문제 발생 시 사용하는 것 같습니다.

docker inspect 컨테이너명

docker pull

이미지를 다운로드하는 명령어입니다.


프라이빗 이미지를 다운로드할 때 등에도 사용합니다.

docker pull 이미지명:태그 [레지스트리URL]

docker commit

컨테이너를 이미지화하는 명령어입니다.


push하려면 다음 형식이어야 합니다.

docker commit 컨테이너지정 DockerHubID/이미지명:태그

docker push

레지스트리에 이미지나 리포지토리를 전송(push)합니다.

docker push 계정명/이미지명  

docker history

이미지의 이력을 확인합니다.


타인이 만든 이미지의 내용을 알고 싶을 때 사용합니다.


Dockerfile이 없을 때 사용합니다.

docker history 이미지지정

컨테이너의 스토리지

컨테이너의 영구화

컨테이너를 삭제하면 저장된 데이터도 사라집니다.


그렇다면 DB 등은 어떻게 해야 할까요?

호스트의 볼륨(스토리지)을 옵션으로 컨테이너에 마운트하면 해결할 수 있습니다.

-v 플래그는 현재 작업 디렉토리를 컨테이너 내부에 마운트합니다.

 

공유되므로 양방향으로 저장됩니다.

docker run -v 호스트의경로:컨테이너의경로 이미지명
↓
docker run -v /GitHub/dockertest/source:/var/www/html mycentos  

 

웹 서버를 이중화한 실제 운영 환경에서는 DB를 컨테이너화하지 않고 한 곳에 집중하여 운영하기도 합니다.

 

볼륨은 매우 어려운 개념이므로 다음 기사를 참고하시기 바랍니다.

Dockerfile

RUN과 CMD의 차이

둘 다 명령어를 실행하지만 실행 타이밍이 다릅니다.

RUN apt-get install -y nginx
CMD ["nginx", "-g", "daemon off;"]
  • RUN: Dockerfile → 이미지
  • CMD: 이미지 → 컨테이너

COPY와 ADD의 차이

둘 다 파일을 이미지에 추가하는 명령어입니다.

  • ADD는 네트워크를 통해서도 추가 가능
  • COPY는 로컬에서만 추가 가능

ADD와 COPY의 기능은 비슷하지만, 일반적으로 COPY를 우선적으로 사용합니다. COPY가 ADD보다 기능이 명확하기 때문입니다.

ENV

환경변수를 설정합니다.

  • DB 사용자명 등
  • 앱 버전 지정 등

이 섹션을 정리하는 차원에서 MariaDB를 만들어 보겠습니다.


mariadb라는 디렉토리를 만듭니다.


아래 파일들을 생성합니다.

  • Dockerfile
  • my.cnf
  • create-table.sql

 

Dockerfile

# OS에서 하는 것보다 최소한의 공식 패키지를 사용하면 여러가지를 해결해 줍니다.
FROM mariadb:10.4
RUN apt-get update -y
COPY my.conf /etc/mysql/conf.d
# 초기 데이터 배치
COPY create-table.sql /docker-entrypoint-initdb.d
ENV MYSQL_USER=root
# 처음부터 이 DB명으로 생성됩니다.
ENV MYSQL_DATABASE=docker
ENV MYSQL_ROOT_PASSWORD=root

 

 

my.conf

# client 섹션: mysql 클라이언트 도구 설정
[client]
port=3306
socket=/tmp/mysql.sock

# mysqld 섹션: mysql 서버 설정 
[mysqld]
port=3306
socket=/tmp/mysql.sock
key_buffer_size=16M
max_allowed_packet=8M

# mysqldump 섹션: 백업 명령어 설정
[mysqldump]
quick

# mysqld_safe 섹션: 시작 파일 설정
[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

 

 

create-table.sql

CREATE TABLE persons (
    id int,
    lastname varchar(255),
    firstname varchar(255),    
    address varchar(255),
    city varchar(255)
);

 

빌드해 나가겠습니다.


-t로 태그명을 붙이고, 그 다음에 Dockerfile의 경로를 지정합니다.

docker build -t mymariadb mariadb  

 

mymariadb 이미지가 있는지 확인합니다.

docker images

 

maria라는 이름으로 생성한 mymariadb 이미지를 실행합니다.

docker run -d --name maria mymariadb

 

컨테이너 프로세스를 확인하여 의도대로 컨테이너가 만들어졌는지 확인합니다.

docker ps

-----------
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS      NAMES
6135ab07a702   mymariadb   "docker-entrypoint.s…"   8 seconds ago   Up 6 seconds   3306/tcp   maria  

 

컨테이너에 들어가 내용을 확인해 봅니다.

docker exec -it maria /bin/bash

 

Dockerfile에 기재된 대로 DB와 테이블이 생성되었음을 확인할 수 있습니다.

root@6135ab07a702:/# mysql -u root -p
Enter password: 
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 8
Server version: 10.4.25-MariaDB-1:10.4.25+maria~focal mariadb.org binary distribution

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> show databases;
+--------------------+
| Database           |
+--------------------+
| docker             |
| information_schema |
| mysql              | 
| performance_schema |
+--------------------+
4 rows in set (0.006 sec)

MariaDB [(none)]> use docker;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [docker]> show tables;
+------------------+
| Tables_in_docker |
+------------------+
| persons          |
+------------------+
1 row in set (0.001 sec)

docker-compose

Compose는 여러 컨테이너를 정의하고 실행하는 Docker 애플리케이션을 위한 도구입니다.

 

과거에는 docker run --link를 사용하여 컨테이너 하나씩 실행하고 연결하는 방법도 있었지만, 비권장되고 있는 것 같습니다.

 

docker-compose는 컨테이너 간 링크를 만들고 일괄적으로 관리할 수 있는 편리한 도구입니다.


(네트워크 기능은 어려워 보여 다음 기회에 공부하겠습니다)

docker-compose.yml 파일이란?

애플리케이션을 구성하는 서비스를 파일 내에 정의합니다.


들여쓰기에 의미가 있는 작성 방식으로, 공백으로 부모-자식 관계가 됩니다.


전체 설계도가 yaml이고 Dockerfile이 개별 설계도 같은 이미지입니다.

 

 

docker-compose.yml

version: '3'
services:
  app:
    # build는 Dockerfile의 경로를 yml 파일이 있는 곳에서의 상대 경로로 지정
    build: ./php
    # docker run --name과 같이 컨테이너 이름을 자유롭게 지을 수 있음
    container_name:"php8"
    # docker run -p와 같이 포트 포워딩
    ports:
     "8080:80" 
  db:
    # image는 DockerHub에서 지정
    image:mariadb:10.4
    # docker run -v와 같이 데이터 영구화
    volume:
      ./data:/var/lib/mysql

docker-compose로 컨테이너 간 통신이 가능한 이유는 무엇일까요?

기본적으로 네트워크에 참여하고 있기 때문인 것 같습니다.

wordpress를 docker-compose로 만들어 보겠습니다.

공식 내용이 좋으므로 직접 작성할 필요 없이 참조하겠습니다.

 

my_wordpress라는 디렉토리를 만듭니다.


공식 내용을 인용하여 수정했고, wordpress 파일도 영구화하였습니다.


wordpress가 latest가 되어있던 부분도 지정했습니다.


공식에서도 실제 운영 시에는 미묘한 부분도 있다고 하니 주의해야 합니다.

 

 

my_wordpress/docker-compose.yml

version: '3'

services:
   db:
     image: mysql:5.7
     # M1 Mac의 경우 지정 
     platform: linux/amd64
     volumes:
       # 명명된 볼륨
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:6
     volumes:
       # 바인드 마운트
       - ./wp-data:/var/www/html
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
volumes:
    # 명명된 볼륨 정의
    db_data:

 

여러 자료를 찾아보니 container_name보다는 아래 링크 방식이 좋다고 하여 실행해 보았습니다.


실제 운영 경험이 부족하여 장단점을 명확히 이해하지 못하지만 연습삼아 해보겠습니다.

 

컨테이너 이름을 결정하는 요소를 .env에 분리할 수 있고, 이름이 기계적으로 결정되므로 이 방식이 바람직할 것 같습니다.

 

 

my_wordpress/.env

COMPOSE_PROJECT_NAME=test_project  

 

작업 디렉토리로 이동하여 docker-compose 실행 명령어를 내립니다.

cd my_wordpress
docker-compose up -d

 

wordpress가 제대로 동작하는 것을 확인했습니다.

# yml의 mysql 부분에 platform: linux/amd64를 기재하지 않으면 다음 오류가 발생합니다.
no matching manifest for linux/arm64/v8 in the manifest list entries

docker-compose 명령어

# 컨테이너 정지 및 삭제
docker-compose down

# 모든 컨테이너 재시작
docker-compose restart  

# 컨테이너 목록 표시
docker-compose ps

# docker-compose.yml로 관리되는 서비스 중 하나를 지정하여 명령어 실행
docker-compose run 서비스명 실행할명령어