🥞 BE
home

Docker 개념 및 실습

Date
2023/08/01
Category
DevOps
Tag
Docker
Detail
BOAZ 멘멘 A조 2주차
목차

설치

Docker Desktop vs. Docker Engine
Docker Desktop은 Docker Engine + 여러가지 툴들
Docker 사용을 위한 시스템 사양 체크
2023년 5월 기준
최소 4GB의 메모리
맥 OS 버전 11 혹은 그 이상
윈도우
최소 4GB의 메모리
64비트 윈도우 10이나 윈도우 11
그 이외에 다른 조건이 존재
예) Hyper-V와 Container 기능이 활성화되어 있어야함
예) WSL 버전 (이는 윈도우 위에서 돌아가는 리눅스 커널)

Docker 필요한 이유와 도커 개념

내 컴퓨터 환경을 그대로 패키징해서 다른 이에게 줄 수 있다면?
Docker Image: 이렇게 독립적으로 완전하게 만들어진 패키지
Docker Container: 이 Docker Image를 독립된 환경에서 실행한 것
도커의 목표는 소프트웨어를 일관되게 빌드하고 실행, 배포하는 것에 있습니다.

Virtual Machine 소개

AWS의 EC2가 대표적인 Virtual Machine (VM)
하드웨어를 추상화하여 한 컴퓨터 위에 가상 컴퓨터를 올리는 것
즉 컴퓨터 하드웨어 단의 추상화
Virtual Machines
보통 하나의 컴퓨터 위에 다수의 VM을 실행하는 것이 일반적
이 안에서 소프트웨어가 동작
VM을 생성하고 관리하기 위한 소프트웨어: VMWare, VirtualBox, Hyper-v, …
Virtual Machine의 장단점
장점
소프트웨어를 실행하기 위한 독립적이고 분리된 공간을 제공
다수의 소프트웨어를 각 VM단에서 독립적으로 실행가능
단점
각 VM은 자신만의 OS를 필요로 함 (가상 하드웨어 위에서 돌기 때문)
유료 OS라면 라이센스 비용 필요
그러다보니 시작하는데 오래 걸림
자원을 많이 사용함 (VM들끼리 자원을 나눠써야함)

Docker Container 소개

소프트웨어를 실행하기 위한 독립적이고 분리된 공간
도커(컨테이너 가상화)는 OS에서 제공하는 자원격리 기술을 이용하여 컨테이너라는 단위로 서비스를 분리할 수 있게 만들어주고, 개발환경에 대한 걱정없이 배포가 가능
자체 파일 시스템을 갖고 있음 (Volume이라고 부름)

Container의 장단점

장점
소프트웨어를 실행하기 위한 독립적이고 분리된 공간을 제공
다수의 소프트웨어를 각 컨테이너단에서 독립적으로 실행가능
자원 소비가 적음 (lightweight)
몇 십개에서 몇 백개의 container를 실행 가능
호스트 OS를 사용 (별도 비용 없음)
따라서 빠르게 실행됨
단점
Host OS를 사용하기에 Cross-platform compatibility를 항상 지원하지 않음
GUI 소프트웨어 개발에 적합치 않음
많은 수의 Docker Container를
‘관리하는 것은 쉽지 않음
컨테이너 오케스트레이션이 필요 (k8s, Docker swarm 등등)
Virtual Machines와 (Docker) Containers 비교
가장 큰 차이점은 아래와 같다. Guest OS가 VM에서는 풀(full OS)로 설치해야 사용 가능하고, docker에서는 격리 개념으로 분리된다는 것. docker에서는 Host OS(Linux)와 다른 부분만 컨테이너에 있고, 커널은 Host OS와 공유하는 방식이다.
도커는 여러 컨테이너들간의 호스트 자원을 분리해서 사용하게 해준다. 이것은 리눅스 고유기술인 name spacecgroup를 사용하여 격리하는 것이다.
시스템 구조적으로 컨테이너는 한 OS를 공유하는 구조이고 VM은 각각의 OS를 띄워야하는 구조이기 때문에 컨테이너가 빠르다.
VM은 사용자가 윈도우를 사용하고 있더라도 새로운 GuestOS를 설치할 때 리눅스 OS를 설치해서 사용할 수 있지만, 컨테이너는 리눅스 OS에서 윈도우용 컨테이너를 사용할 수 없다.

도커 프로그램 개발 프로세스

하이레벨 Docker 사용 프로세스

먼저 대상 소프트웨어를 선택
다수의 컴포넌트로 구성되는 소프트웨어라면 각각이 Docker Image로 만들어져야할 수도 있음
이를 Docker Image로 빌드하자: Dockerization이라고 부름
Dockerfile이란 텍스트 파일로 세부 정보를 기술
해당 소프트웨어를 이미지로 바꾸기 위한 Docker에게 주는 명령들을 포함
Docker Image: 하나의 Docker Container안에서 실행됨!
Dockerfile을 기준으로 만들어지며 소프트웨어를 실행하기위해 필요한 모든 것을 포함
docker build -t tag

Docker Image

Docker Image의 구성 요소
기본 OS (리눅스라면 우분투, 데비안 등등)와 같은 소프트웨어의 실행환경
소프트웨어 자체 (코드)
소프트웨어가 필요로 하는 라이브러리
파일 시스템 스냅샷: 이는 스택화된 형태로 구현됨 (뒤에서 더 설명)
환경 설정 변수: 빌드할 때 변수와 실행 때 변수 두 가지가 존재
ENV, ARG
메타 데이터: 이미지 자체에 대한 정보 (버전, 작성자, 설명 등등)
위 정보와 설치 관련 실행 순서등이 Dockerfile에 기술됨
Docker Image는 다수의 파일로 구성됨 (“docker image ls”)
Docker Image의 실행
Container를 통해 Docker Image안의 소프트웨어를 실행
Container는 자체 파일 시스템을 가진 특수한 프로세스로 이미지의 파일 시스템이 로딩됨
Image를 Container 안에서 실행
docker run … : 이미지를 컨테이너로 띄우는 것
docker exec
이미 실행 중인 도커에 명령을 내림
Docker Image의 등록: Docker Hub
Docker Registry는 Docker Image들의 보관소
On-prem registry와 Cloud registry가 존재
docker hub이 가장 유명
여기에 등록을 하면 회사내 혹은 퍼블릭하게 이미지를 공유 가능
Docker Hub이란 무엇인가?
Docker가 제공해주는 서비스로 Docker Image를 공유하고 찾기 위한 서비스
Teams & Organizations
Public과 Private Repo 제공
Official Images
Github과 연동을 통한 Automated Build 제공
최종 환경

간단한 Hello World 프로그램 실습을 통한 Dockerfile 작성

전체적인 프로세스

cd node
OS 선택
Node 설치
코드 복사
프로그램 실행 (node app.js)

Dockerfile의 생성

Docker에게 소프트웨어 설치 명령을 기술
먼저 베이스 이미지를 기술 (FROM)
다음으로 코드 복사
마지막으로 코드 실행
FROM : OS 종류를 적어줌. 여기서는 Alpine이라는 경량 리눅스를 데모 목적으로 선택
COPY : 코드 복사
WORKDIR : working directory
CMD : 실행하는 명령 앞에 지정
ARG:
도커 이미지 빌드를 위해 Dockerfile 내에서 사용하는 변수
빌드 시점에 사용하며, docker build 명령의 --build-arg 옵션에 해당한다.
Docker Image를 만들 때 사용되는 변수 지정. 최종 이미지에는 안 들어감
ENV
환경변수를 설정하는 명령어
설정된 환경 변수는 이미지 빌드 시 사용되며, 해당 이미지를 실행한 컨테이너 내에서 사용할 수 있다.
docker run 명령의 -e 옵션에 해당한다.
$JAVA_HOME
컨테이너가 실행될 때 사용되는 환경변수. 최종 이미지에 저장됨
USER
컨테이너를 실행할 때 사용할 유저 ID
EXPOSE
서비스 사용 포트번호
RUN
빌드시 실행되어야하는 명령들이 지정됨 (docker build)
RUN apt-get update && apt-get install -y curl

Dockerfile 키워드: CMD vs. ENTRYPOINT

둘 다 컨테이너 시작시 실행할 명령어를 지정해준다. 컨테이너를 시작하려면 둘 중에 하나는 지정해주어야 한다.
CMD
컨테이너를 생성할 때만 실행됩니다. (docker run)
컨테이너 생성 시, 추가적인 명령어에 따라 설정한 명령어를 수정할 수 있습니다.
ENTRYPOINT
컨테이너를 시작할 때마다 실행됩니다. (docker start)
컨테이너 시작 시, 추가적인 명령어 존재 여부와 상관 없이 무조건 실행됩니다.
Container가 시작할 때 실행되어야 하는 명령어를 지정하는데 사용 (docker run)
굉장히 흡사한 기능을 제공하지만 동시 사용시, ENTRYPOINT에 우선 순위가 있음
둘 다 한 DOCKERFILE에서 여러 번 실행되면 각각 마지막 명령 줄만 사용됨
아래의 경우 docker run 실행시 동일한 결과가 나옴
ENTRYPOINT 명령어는 오버라이딩이 어렵고 CMD 명령어는 오버라이딩이 쉽다.
ENTRYPOINT는 --entrypoint 옵션을 통해서만 덮어쓰기가 가능
CMD나 ENTRYPOINT 중 하나만 지정되면 그게 container가 실행될 때 실행
CMD cli 오버라이딩
둘이 한 DOCKERFILE에서 같이 지정 가능함
둘이 같이 사용되면 ENTRYPOINT가 기본 명령이 되고 CMD가 인자를 제공
결국 CMD or ENTRYPOINT?
️ Best Practice는 CMD 사용
최대한 CMD만 사용
항상 실행해야 하는 명령을 사용하여 실행 가능한 도커 이미지를 빌드할 때는 ENTRYPOINT를 사용한다.
CMD 명령어는 도커 컨테이너가 실행될 때 command line에 명시적으로 인자값을 지정하지 않는 경우에 기본 명령어 역할을 하는 인자를 설정하는 데에 사용한다.
ENTRYPOINT를 사용하면 실행시 타이핑을 덜 할 수 있음
파라미터를 지정해주면 되기 때문이지만 감춰지기 때문에 오히려 혼란을 줄 수 있음
요약:
1.
ENTRYPOINT가 있으면 CMD 값이 파라미터로 실행됨
2.
아니면 CMD가 실행됨

남이 만든 Dockerfile : Airflow 리뷰

풀 도커파일
ARG는 빌드 타임에도 사용이 되지만, 이미지에도 최종 저장이 된다. 컨테이너가 실행될 때도 이용 가능한 변수가 된다
CMD에서도 이용 가능함
USER : 컨테이너를 돌리는 유저 이름
Entry point를 통해 명령 실행 후, CMD로 파라미터 입력

Docker Image 생성 및 컨테이너 빌드

docker build --platform linux/amd64 -t hello-world-docker .
docker build -t hello-world-docker .
docker build --platform linux/amd64 -t hello-world-docker .
Bash
복사
=> [internal] load build definition from Dockerfile … => => writing image sha256:cb6c638168780afd3d74fc1cddd813917a6a397dad453c8e1a8063635c1521fe 0.0s => => naming to docker.io/library/hello-world-docker
Plain Text
복사
docker build를 실행하면 Dockerfile에서 RUN 명령이 실행됨
1.
만일 Apple M1 chip 기반 맥에서 빌드하는 경우 그 이미지는 ARM 기반 아키텍처로 만들어지기 때문에 일반 리눅스에서 안 돌아감. 그래서 --platform 옵션을 사용해서 linux/amd64로 지정
2.
t는 태그를 지정하는 것으로 뒤에서 더 설명
Docker Image 확인
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE hello-world-docker latest cb6c63816878 22 minutes ago 179MB
SQL
복사
Docker Container로 실행
$ docker run hello-world-docker >> docker run을 Dockerfile에서 CMD 명령이 실행됨 Hello Docker!
SQL
복사
만일 이 이미지를 다른 컴퓨터에서 실행하고자 한다면?
Docker Registry (예를 들면 Docker hub)으로 먼저 등록

Docker HUB 등록

Docker Registry에 등록
1.
Docker Hub 회원등록
먼저 Docker Hub에 회원 등록: https://hub.docker.com/
ID와 Password를 기억해둘 것
2.
hello-world-docker repo 만들기
Create repository 선택
이름을 “hello-world-docker”로 지정. Public 속성 지정:
hajun/hello-world-docker
3.
Docker Registry에 등록
터미널로 이동하여 다음 명령을 실행
먼저 현재 이미지의 repo 이름을 hajun/hello-world-docker로 변경
$ docker image ls $ docker tag hello-world-docker:latest hajun/hello-world-docker:latest $ docker image ls $ docker login --username=hajun >> 별도의 프롬프트로 물어봅니다. $ docker push hajun/hello-world-docker
SQL
복사
4.
Docker Hub에서 결과 확인

Docker 명령 요약

docker version
docker build -t
docker push
docker login
docker tag
docker pull
docker run
-p option (port mapping)
-v option (volume mapping)

docker run vs. docker exec

docker run과 docker exec의 차이점은 무엇일까?
docker run은 새로 Container를 실행하는 것
docker exec는 실행된 Container에 작업을 하는 것
그래서 이 명령은 Container ID가 필요함
인터렉티브하게 명령을 내릴 때 옵션
-it 옵션
docker exec -it container_id /bin/bash
두 명령 모두 --user root 혹은 -u root를 통해 루트 유저로 연결가능
m1의 경우, 그냥 run을 통해 빌드하게 되면, 애플 칩 위에서만 돌아가는 컨테이너가 되어버림
docker build —platform linux/amd64 -t hello-world-docker .
docker run hello-world-docker

Docker Image 이름?

docker image ls와 docker images와 동일한 결과를 보여줌
앞서 docker tag 명령의 경우 별칭을 만들어주는 것임 (이름을 바꾸는 것이 아님)
docker tag hello-world-docker hajun/hello-world-docker
Docker image의 실제 ID는 IMAGE ID임
Image 이름 자체는 REPOSITORY 이름과 TAG로 구성됨
한번에 쓰는 경우 :을 사이에 두고 같이 씀 ⇒ redis:13, hajun/hello-world-docker:latest
Docker tag란?
Docker Image의 버전이나 변형을 나타내는 문자열
디폴트 값은 latest
Docker Image의 부가정보를 나타냄
Docker Image 이름에서 :뒤에 해당
예: ubuntu:18.04
예: bitnami/airflow
예: node:alpine
docker inspect
이미지 조사하기
다음과 같이 httpd:alpine 이미지와 직접 Dockerfile로 작성한 이미지 조사해보기 ⇒ docker inspect 이미지 이름
Dockerfile 내 Labels 명령어로 입력한 부분이 Label 정보로 이미지 안에도 들어가 있음

Hangman 웹서비스를 Github을 통해 Docker Image로 Docker Hub에 업로드

레포 및 코드 설명

Hangman 프로그램
hangman 프로그램을 flask를 사용하여 웹으로 노출
포트번호는 어디든 바인딩 가능하며 실행할 때 지정
flask 관련 모듈 설치가 필요함: requirements.txt
실행 방법
python3 -m flask run --host=0.0.0.0 --port=4000
이 경우 app.py를 기본으로 사용함
레포 구성
requirements txt
Flask==2.3.2 Flask-HTTPAuth==4.5.0 Flask-Login==0.6.2 Flask-SQLAlchemy==3.0.3
SQL
복사
app.py
from flask import Flask,session app = Flask(__name__) … app.secret_key = "Python Study" if __name__ == "__main__": app.run()
Python
복사
프로그램 실행 데모
코드 설명을 간단히 한 후에 터미널에서 실행하는 데모 수행 git clone https://github.com/HaJunYoo/boaz-docker cd hangman_web pip3 install -r requirements.txt python3 -m flask run --host=0.0.0.0 --port=4000
Python
복사

Hangman 서비스를 Docker Image로 구성 후 허브에 푸시

Docker 컨테이너 내부 프로세스와 호스트 프로세스간의 통신
Dockerfile 작성
FROM python:3.8-slim-buster LABEL Maintainer="hajuny129@gmail.com" # 메타데이터로 docker inspect 명령으로 찾아볼 수 있음 COPY . /app WORKDIR /app RUN pip3 install -r requirements.txt EXPOSE 4000 # 이 포트 번호를 사용하니 포트 맵핑을 할때 참고하라는 정보 # 나중에 포트의 인바운드 규칙으로써 활용 가능 CMD ["python3", "-m", "flask", "run", "--host=0.0.0.0", "--port=4000"]
Python
복사
Docker 컨테이너로 포트 4000에 실행된 Flask app이 있다
그냥 docker run 하게 되면 에러가 발생
이 app을 호스트 운영체제에서 접근하려면?
Docker 컨테이너를 실행할 때 포트 맵핑을 통해 호스트 운영체제 단에서 접근되는 포트를 컨테이너쪽으로 포워드해주어야함 ⇒ -p 옵션
Docker 컨테이너 내부 프로세스가 오픈한 포트번호를 외부로 노출해주는 것이 포트 맵핑
이미지 빌드
docker build --platform=linux/amd64 -t hangman .
Python
복사
docker run 수행시 -p 옵션 사용
docker run -p 4000:4000 이미지이름
docker run -p 4000:4000 hangman
Python
복사
전체 명령어 요약
$ docker build --platform=linux/amd64 -t hangman . $ docker image ls $ docker inspect hajuny129/hangman $ docker run -p 4000:4000 hajuny129/hangman $ docker run -p 4000:4000 -d hajuny129/hangman # background run $ docker login $ docker push hajuny129/hangman # docker image hub에 푸시
Python
복사
컨테이너 ~ 이미지 삭제 방법
docker stop 컨테이너_id docker rm 컨테이너_id docker rmi 이미지_id
Python
복사
Docker image hub에 push 되었는지 확인
추가 명령어
○ docker inspect 명령 ○ docker run "-d" 옵션 ○ docker stop 명령
Python
복사

MySQL Dockerfile 실습 - (시간적 여유 될 때)

리눅스 커널과 배포판 컨테이너 사용

리눅스 커널: 리눅스의 핵심부분. Linus B. Torvalds가 1992년에 처음 공개
배포판 : 동일한 커널을 보유, 하지만 커널 이외의 것들이 달라짐(쉘, 응용 프로그램)
어떤 리눅스 배포판이 있는가?
우분투: 가장 많이 사용되며 데비안에 기반해서 만들어진 리눅스 배포판
데비안
알파인: 임베드 시스템에서 사용할 용도로 만들어진 경량화 리눅스 배포판
페도라
센트OS
배포판에 따라 다른 패키지 매니저가 존재
npm
yarn
pip
apt
NuGet
보통은 docker image를 pull하고 run을 해야함! 바로 run을 하면?
docker run ubuntu
docker ps
docker ps -a
docker run -it ubuntu
nano
설치가 안되어서 없을 것
apt list
apt install nano
apt update
apt install nano
nano
apt remove nano

MySQL 8.0을 Docker로 실행해보기

1.
먼저 Docker Engine이 실행된 것 확인하고 terminal 프로그램 실행
2.
MySQL docker image를 다운로드
a.
docker pull mysql/mysql-server:8.0
3.
다운로드받은 이미지로 Docker container 실행
a.
docker run --name=mysql_container mysql/mysql-server:8.0
4.
MySQL root 계정의 패스워드 찾기
a.
docker logs mysql_container 2>&1 | grep GENERATED
5.
마지막으로 MySQL shell 실행하기
a.
docker exec -it mysql_container mysql -uroot -p
실행 예제
$ docker pull mysql/mysql-server:8.0 8.0: Pulling from mysql/mysql-server ... 38b3da6a86f7: Pull complete Digest: sha256:5241f7de0483a70f5856da995fea98904cfce8f1c51734b7f3836c1663eead17 Status: Downloaded newer image for mysql/mysql-server:8.0 docker.io/mysql/mysql-server:8.0 $ docker run --name=mysql_container mysql/mysql-server:8.0 6137dc6ea2fe283dd007b2e55a1cc2c37b0c89dc45eaa3b58085bae7daa799ed $ docker logs mysql_container 2>&1 | grep GENERATED [Entrypoint] GENERATED ROOT PASSWORD: kmK,K#;bEte?65?kq41/;o4.Nd5YyV41
SQL
복사
컨테이너 접속
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6137dc6ea2fe mysql/mysql-server:8.0 "/entrypoint.sh my…" 8 secs ago Up 7 secs (health: starting) 3306/tcp,33060-33061/tcp mysql_container $ docker logs mysql_container 2>&1 | grep GENERATED [Entrypoint] GENERATED ROOT PASSWORD: [PASSWORD] $ docker exec -it mysql_container mysql -uroot -p Enter password: mysql> show databases; ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement. mysql> ALTER USER root@localhost IDENTIFIED BY 'MeadowoodDr9$'; mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sys | +--------------------+ 4 rows in set (0.01 sec) mysql> use mysql; Database changed mysql> show tables; +------------------------------------------------------+ | Tables_in_mysql | +------------------------------------------------------+ | columns_priv | | component | | db |
SQL
복사
기억할 명령어
docker run --name
기억하기 쉬운 이름을 docker ps로 찾은 Container ID 대신 사용 가능
docker logs
Container쪽에서 생성된 stdout, stderr단의 로그를 읽어옴
--follow 옵션을 사용하면 로그가 계속적으로 스트리밍이 됨
docker logs container_id —follow