📌 컨테이너(Container)란?
컨테이너는 애플리케이션과 그 실행에 필요한 모든 라이브러리, 종속성, 설정 파일 등을 하나의 패키지로 묶어 독립적으로 실행할 수 있도록 하는 가상화 기술임
✅ 특징
- 경량성: 운영체제(OS)를 가상화하는 기존 VM(Virtual Machine) 보다 훨씬 가벼움
- 이식성(Portability): 개발 환경과 운영 환경이 동일하여 "내 PC에서는 되는데 서버에서는 안 돼요" 같은 문제가 적어짐
- 격리성(Isolation): 각 컨테이너는 독립적으로 실행되어 충돌을 방지함
- 빠른 배포: 컨테이너는 가볍고 실행 속도가 빠르므로 DevOps 및 CI/CD 환경에서 유용

📌도커(Docker)란?
도커(Docker)는 컨테이너를 쉽게 생성하고 관리할 수 있도록 도와주는 컨테이너 관리 플랫폼
✅ 도커의 역할
- 애플리케이션을 컨테이너로 패키징
- 컨테이너 실행 및 관리
- 이미지(컨테이너 템플릿) 생성 및 배포
✅ 도커 주요 개념
- 이미지(Image): 컨테이너를 실행하기 위한 템플릿(설정 및 애플리케이션 포함)
- 컨테이너(Container): 이미지를 기반으로 실행되는 독립적인 환경
- 도커 허브(Docker Hub): 이미지 저장소 (GitHub 같은 역할)
- 도커 파일(Dockerfile): 컨테이너 이미지를 생성하기 위한 설정 파일
- 도커 컴포즈(Docker Compose): 여러 개의 컨테이너를 한꺼번에 관리하는 도구
✅ 도커 사용 시 장점
- 실행 환경 일관성 유지 (개발-PC, 스테이징, 운영 서버 간 환경 차이 해결)
- 배포 속도 향상 (빠르게 컨테이너 실행 가능)
- 확장성 (AWS ECS, Kubernetes와 연동 가능)
📌하이퍼바이저 가상화 (Hypervisor Virtualization)
하이퍼바이저는 가상 머신을 관리하고, 각 가상 머신은 독립된 운영 체제와 애플리케이션을 실행함.
하이퍼바이저 종류
- 타입 1 하이퍼바이저 (Bare-metal Hypervisor):
- 직접 하드웨어 위에서 실행되며, 운영 체제가 필요 없음
- 예: VMware ESXi, Microsoft Hyper-V, Xen
- 타입 2 하이퍼바이저 (Hosted Hypervisor):
- 호스트 운영 체제 위에서 실행됨
- 예: VMware Workstation, Oracle VirtualBox
하이퍼바이저의 특징
- 물리적 하드웨어를 가상화하여 각 VM에 독립적인 운영 체제와 하드웨어 리소스를 할당함
- 리소스 격리가 매우 강력하며, 가상 머신은 서로 독립적이고 다른 가상 머신과 상호작용하지 않음
- 하이퍼바이저는 하드웨어 리소스를 관리하고 각 VM에 가상화된 자원을 할당함
📌 하이퍼바이저 가상화& 컨테이너 가상화(Docker)

하이퍼바이저 가상화 | 컨테이너 가상화 | |
격리수준 | 하드웨어 수준 | 프로세스 수준 |
자원 | 오버헤드가 높음 | 오버헤드가 낮음 |
보안 | 컨테이너 가상화에 비해 높음 | 하이퍼바이저에 비해 낮음 |
프로비저닝 속도 | 상대적으로 느림 guest VM부터 구성 |
상대적으로 빠름, 호스트 H/W와 커널까지 공유하고 있음. 따로 guest VM과 Guest Kernel을 구성할 필요가 없음. |
호환성 | 원하는 OS 설치 가능 | 호스트에 종속 됨 리눅스 기반이 컨테이너만 가능 |
- 컨테이너의 최상위 디렉토리를 변경하여, 컨테이너 내부에서 접근 가능한 디렉터리(공간)를 제한함.
📌chroot

📌Cgroup(Control Group)
- Cgroup(Control Group): 자원을 컨트롤.
-> 컨테이너에 호스트의 자원을 '제한'
-> 하이퍼바이저 VM의 경우엔 자원을 '할당' (2 core, 2GB)하는 개념이라면 컨테이너의 경우 '제한'을 하는 개념(램을 2GB까지 쓸 수 있음 )
-> 호스트의 모든 자원을 하나의 컨테이너가 끌어다 쓰는 것을 제한
컨테이너 가상화의 종류
- Docker(컨테이너를 관리하는데 최적화, 앞으로 K8s를 할 때도 계속 사용): 도커에서는 이미지화하는 것이 중요하고, 이걸 어떻게 활용할지 생각을 잘해야 됨.
- Containerd( K8s를 구성하는 런타임으로 쓸 예정)
- CRIO
- LXC(Linux Container)
실습 환경 구성
도커 설치
- ubuntu_tem을 clone 해서 host vm 만들기
- hostname - host, IP - 211.183.3.100 /24

root@ubun-tem:/home/user1# vi /etc/netplan/00-installer-config.yaml
# ubuntu 20.04의 네트워크 설정 파일. ip를 수정할 수 있음
root@ubun-tem:~# netplan apply
#systemctl restart network이랑 비슷

curl -fsSL https://get.docker.com -o get-docker.sh
- curl: **curl**은 데이터 전송 도구로, HTTP, FTP와 같은 프로토콜을 통해 웹 서버에서 데이터를 가져올 수 있음. 이 명령어는 Docker 설치 스크립트를 다운로드하는 데 사용됨.
- -f: fail silently 옵션으로, HTTP 요청이 실패하면 오류 메시지를 출력하지 않고 종료됨.
- -s: silent 옵션으로, 출력 메시지를 최소화함.
- -S: show error 옵션으로, 오류가 발생하면 오류 메시지를 출력함.
- -L: location 옵션으로, 리다이렉트된 URL을 자동으로 따라가서 데이터를 다운로드함.
- https://get.docker.com: Docker를 설치하기 위한 설치 스크립트 URL임
- -o get-docker.sh: 다운로드한 데이터를 **get-docker.sh**라는 파일로 저장
root@host:~# chmod +x get-docker.sh
- chmod: 파일 권한을 변경하는 명령어
- +x: 파일에 실행 권한을 부여
- get-docker.sh: 권한을 부여할 파일명
root@host:~# ./get-docker.sh

- 잘 설치된 걸 볼 수 있음!

- 네임도 변경시켜주기

- ip add 했을 때 보이는 docker0를 잘 확인해 주는 게 중요!
- 어떤 네트워크 안에 있어야 됨, docker0라는 NIC를 통해서, 어떤 네트워크가 구성되어 있음
- 지금 보이는 캡처 본은 정상적인 상태인데, 여기서 ip가 안 보이면 비정상적인 상태임
- 나중에 컨테이너는 실행이 되는데, 컨테이너끼리나 외부로 통신이 안되면 ip add를 쳐서 docker0이라는 인터페이스의 아이피가 잘 올라왔는지 확인을 하자
- 아이피가 없으면 systemctl restart docker를 실행

- nat 대역과 비슷한 docker 대역?
- 리눅스 측면에서 바라본 컨테이너 네트워크(172.16.0.0/16)

docker run nginx
- 원래는 docker container run nginx라고 해야 되지만 생략가능
- 왜냐면 run 될게 container 말고 없음
- 도커 허브에서 nginx를 설치함?
root@host:~# docker run nginx
-> nginx라는 이미지를 동작시켜줘라는 명령어
Unable to find image 'nginx:latest' locally
-> 가장 최근 nginx이미지를 설치해 줌
latest: Pulling from library/nginx
7cf63256a31a: Pull complete
bf9acace214a: Pull complete
-> 하나만 다운 받는 것이 아니라 여러 개 다운을 받음
-> pull 했다는 것은 다운로드했다는 의미임 <-> push
-> 여러 개의 layer가 층을 쌓아 nginx를 실행시킴
container image
"nginx: latest" => tag를 생략하면 :latest가 디폴트 값
docker run에 어떤 의미가 내포되어 있냐라고 생각했을 때, create랑 start라는 의미가 담겨있음.

- 뭘 입력할 수 없는 상태는 foreground 상태임
- 컨테이너가 잘 동작하려면 반드시 foreground 상태인 프로세스가 존재해여함
ex) 자바컨테이너: 자바가 잘 동작하기 위한 명령을 실행해둬야 함
java -jar app.jar
=> 아주 기본적이면서 제일 중요한 개념임.

- sweet_wozniak라는 컨테이너 이름이 생성됨

ctrl + c를 하면 나가짐

- docker가 끝난 상태에서는 정상동작중인 컨테이너를 찾을 수 없음
- 컨테이너 내부에서 포어그라운드로 동작하던 프로세스가 종료되자 컨테이너가 중지 됐음. 이 차이를 이해하고 해결하는 게 컨테이너 가상화에서 필수이자 가장 기본이 되는 능력임.

docker ps -a
-> 모든 도커 상태를 볼 수 있음
-> 중지된 컨테이너는 -a 옵션으로 볼 수 있음
컨테이너 지우기

docker rm -f sweet_wozniak
-f 강제로 sweet_wozniak이라는 컨테이너를 지우겠다는 의미임.
root@host:~# docker run --name test centos:7
- name: test
- centos는 7로 할 것
- centos:7 이미지로 test라는 이름을 갖는 컨테이너를 띄우겠다는 의미임

- 하지만

- 이렇게 실행되고 있지 않은 게 보임

- COMMAND: 컨테이너가 실행 시 수행되는 명령어
- /bin/bash이 명령으로는 특정한 프로세스가 실행가 없네
- 다시 말해, centos:7이라는 이미지는 처음에 설계 당시, '이 컨테이너를 실행하면 bin/bash라는 명령을 수행해라'라고 만들어졌음. 따라서 우리가 docker run 명령으로 컨테이너를 실행 시키면서 /bin/bash라는 명령어 수행됐을 것 임. 하지만 컨테이너가 '정상 동작'하려면 반드시 컨테이너 내부에서 포어그라운드로 프로세스가 실행되어야하기때문에 그 조건을 충족시키지 못해서 결국 컨테이너는 stop된 상태로 존재함.
root@host:~# docker run --name test centos:7 sleep 60

- 강제로 실행된 모습을 볼 수 있음
- 컨테이너 실행시 = command -> 수행할 명령어
-> 보통은 컨테이너를 foreground로 유지하는 것이 목적임

- 60초가 지나니까 자연스럽게 off 된 걸 볼 수 있음


- ststus가 up 상태이면 잘 실행된 상태인 걸 알 수 있음
docker start : 실행
docker restart : 재실행.
docker stop : 중지.
질문) 컨테이너의 경우엔 꼭 운영체제가 아닌, nginx나 python, java 같은 앱도 컨테이너로 만들 수 있다 = 이미 호스트의 커널을 공유 중이기 때문.
- 원래 있던 건 실행이 안됨


- kvm의 이미지와 컨테이너 이미지가 어떤 차이가 있는지 이해하면 좋음
- 컨테이너 이미지로 컨테이너를 띄우더라도 원래 이미지는 그대로 존재함.
- vm은 많은 리소스로 무겁고, 컨테이너는 적은 리소스로 가벼움
detach (-d, 매우 중요)
- -d는 detach 된 상태라는 걸 의미함
- detach는 백그라운드로 컨테이너를 실행

- 컨테이너의 세부 정보를 알 수 있음

root@host:~# docker inspect ntest | grep -i ipa
- 컨테이너의 ip를 조회할 수 있음

- 내부에 실제로 컨테이너가 생긴 걸 확인할 수 있음
- ntest안에 nginx가 있을 거고 그 앱은 foreground 한 상태일 것 임
- 80번/ 172.17.0.2를 통해서 빠져나감?

- curl을 쳤을 때 잘 나오는 걸 확인할 수 있음
- 리눅스에서 컨테이너로 통신이 잘 되는 걸 확인할 수 있음.

- logs는 컨테이너의 로그, 주로 컨테이너가 잘 동작하지 않을 때 볼 수 있음
- docker logs: 일반적으로 컨테이너를 실행시켰을 때 내부의 프로세스가 적절하게 동작하지 않아 컨테이너가 중지되는 경우가 있음. 왜 중지됐는지 여부를 어느 정도 확인이 가능함

- 지금 우리는 3개의 대역대가 있음
- 차이가 있다면 DRIVER의 차이임
- NAT대역이랑 똑같은 bridge 대역
root@host:~# docker image ls
- 컨테이너 이미지 목록

- REPOSITOTY는 책장
- TAG는 책
- 우리가 pull 해온 두 개의 이미지 (nginx:latest, centos:7은 도커허브에서 가져온 오피셜 이미지)

root@host:~# docker exec ntest ls
- 컨테이너 내부에서 명령을 수행
ntest: 명령을 수행할 컨테이너 이름
ls: 명령
root@host:~# docker rm -f ntest
- 지우고
- it: 컨테이너 접속
root@host:~# docker run -it --name cent centos:7
- i: interactive, 상호작용 -t: terminal
해당 컨테이너와 터미널로 상호작용하겠다 = 해당 컨테이너 접속

- cent라는 컨테이너를 실행과 동시에 위에 입력한 해당 컨테이너 안으로 들어감

- 나로 인해서 잘 동작하고 있음
- 내가 연 터미널이 bash
- 몰래 빠져나가는 거 ctrl +pq: 조심스럽게 빠져나갈 수 있음?


- 내가 열었던 터미널이 유지되어 컨테이너가 잘 동작됨.

- cent 컨테이너 삭제 후

- ctrl + d

- 포어그라운드로 동작하는 프로세스가 없기 때문에, 컨테이너는 중지 상태가 됨.
실습 1) myhttpd라는 이름을 갖는 컨테이너를 띄워보세요. 이 컨테이너를 백그라운드로 실행시키고, 컨테이너 이미지는 httpd:latest 이걸로 하세요. 그리고 컨테이너의 ip를 조회해서 curl을 쳐보세요.

root@host:~# docker run -d --name myhttpd httpd:latest
-d가 백그라운드를 의미함

- 어떤 특정 컨테이너가 있는데, docker ps를 쳤는데 잘 나타나면서 막상 연동이 오래 걸려서 안될 수도 있음
root@host:~# docker inspect myhttpd | grep -i ipa

root@host:~# curl 172.17.0.2



- bash라는 command에 의해서는 외부에서 활성화되지 않음
- test라는 컨테이너는 실행되면서 bash라는 명령이 수행이 됨
- 근데 이 명령만으로는 내부에서 httpd 프로세스가 정상적으로 동작하지 않음

- 컨테이너를 foreground로 httpd:latest라는 유지시키는 명령어
- 컨테이너 내부에서 httpd-foreground라는 명령을 수행하면 외부에서 curl이 잘 됨.
ctrl +pq 하고 나오기

- 내부로 진입해서 명령을 치겠다는 의미 = 콘솔 접속하는 느낌
- httpd라는 컨테이너이미지의 웹루트디렉터리
실습 2) httpd:latest 이미지로 이름이 index_http라는 컨테이너를 만들고 이 컨테이너의 주소로 curl 했을 때 hello라는 기본페이지가 보이도록 해보세요.
방법 1) 컨테이너 접속
root@host:~# docker run -d --name index_http httpd:latest
root@host:~# docker inspect index_http | grep -i ipa
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAMConfig": null,
"IPAddress": "172.17.0.2",
root@host:~# docker exec -it index_http bash
root@517173ca3a88:/usr/local/apache2# cd htdocs/
root@517173ca3a88:/usr/local/apache2/htdocs# echo hello > index.html


- 컨테이너 내부에서 curl을 쳤을 때 안 나가는 것이 아니라 curl이 설치가 안되어 있어서 그런 거임. 그래서 curl을 설치하고 curl치면 외부로 나감 localhost든 ip든 잘 html파일을 불어오는 걸 알 수 있음
- 명령어가 최소한으로 들어있기 때문에 사소한 예를 들어 curl이란 명령어나 ip add이라는 명령어가 없음. 그러니까 사소하게 필요한 것들을 다 설치해 줘야 됨
방법 2) 호스트에서 echo 명령 수행
root@host:~# docker exec index_http sh -c 'echo hello2 > /usr/local/apache2/htdocs/index.html'
root@host:~# curl 172.17.0.2
hello2
-> 특문이 없었으면 명령을 그대로 수행해도 되는데, 특문(리다이렉션)이 있었기 때문에 sh -c를 통해서 내가 수행할 명령어를 묶어줌.
실습 3) nginx:latest 이미지로 이름이 index_nginx라는 컨테이너를 만들고 이 컨테이너의 주소로 curl 했을 때 hello_nginx라는 기본페이지가 보이도록 해보세요.
방법 1)
root@host:~# docker run -d --name index_nginx nginx:latest
root@host:~# docker exec -it index_nginx bash

# 직접 들어가서 echo
방법 2) 호스트에서 exec 실행
root@host:~# docker exec index_nginx sh -c 'echo hello_nginx2 > /usr/share/nginx/html/index.html'


- 모든 컨테이너 삭제 = docker rm -f $(docker ps -qa)
root@host:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
5a983f0210b2 bridge bridge local : 외부와 통신 가능
d1b03fbcbfa4 host host local: 호스트 내에서만 통신
11c9b4e084bc none null local: 네트워크 없음
volume (-v, 매우 중요)
docker volume: 컨테이너와 호스트를 마운트 개념
- 컨테이너의 경우어떤 식으로든 컨테이너가 삭제되면, 내부의 데이터나 로그 같은 것들도 같이 삭제가 됨. 데이터베이스 컨테이너를 띄웠는데, 갑자기 컨테이너가 날아가면, 데이터도 다 날아감. 따라서 내가 영구적으로 저장시키고 싶은 데이터가 았다면 -v(볼륨)을 통해 host에 영구 저장을 시킬 수 있음

* 여러 번 사용 가능.
root@host:~# mkdir /host-txt
root@host:~# docker run -d --name vol_nginx -v /host-txt:/txt nginx:latest
root@host:~# echo vol-test > /host-txt/test.txt
root@host:~# docker exec vol_nginx cat /txt/test.txt
vol-test
root@host:~# docker exec -it vol_nginx bash

- 내부에서 삭제가 되면

- 외부에서도 삭제가 됨.
실습 4) -it 옵션이나 exec를 쓰지 않고 nginx:latest 이미지로 컨테이너를 생성했을 때 vol_test라는 문구가 뜨도록 해보세요.
root@host:~# cd /
root@host:/# cd host-txt
root@host:/host-txt# ls
root@host:/host-txt# echo vol_test > index.html
root@host:/host-txt# ls
index.html
root@host:/host-txt# docker run -d --name my_nginx -v/host-txt:/usr/share/nginx/html nginx:latest
root@host:/host-txt# docker inspect my_nginx | grep -i ipa
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAMConfig": null,
"IPAddress": "172.17.0.2",
root@host:/host-txt# curl 172.17.0.2
vol_test

root@host:/host-txt# docker run -it --name test nginx:latest bash
root@edc9e008ab85:/# cd /usr/share/nginx/html/
root@edc9e008ab85:/usr/share/nginx/html# ls
50x.html index.html
# 새로 nginx 컨테이너를 띄워서 내부로 진입하여, /usr/share/nginx/html로 가보니, 이미 index.html이 존재하더라.
# => 기존에 파일이 존재하면, 호스트의 파일이 우선순위가 높음
root@host:~# docker exec -it my_nginx bash
root@0cabd61818fc:/# ls /usr/share/nginx/html
index.html
# 50x.html이 존재하지 않는 상황. 컨테이너 내부의 /usr/share/nginx/html이 바라보는 대상이 호스트의 /host-txt라는 경로로 변경됨
Publish (-p, 매우중요)
- 컨테이너를 외부에 노출, 배포(publish) 시키는 옵션

root@host:~# cd /
root@host:/# cd host-txt
root@host:/host-txt# docker run -d --name pub -p 8080:80 nginx:latest
-p : publish
8080: 호스트의 포트
80: 컨테이너의 포트

- 컨테이너 내부에서 동작하는 프로세스가 서비스를 제공하는 포트임
- 일반적으로 웹서버는 80. python 장고는 8000, node.js 기반은 3000번 등등... 중요한 건 컨테이너 내부에서 어떤 포트로 서비스가 제공됨
- 실습, 테스트 할 때는 되도록이면 호스트의 윌노운 포트는 쓰지 않는 게 좋다. 만약에 최종적으로 웹서버를 배포하는 거면 80번 포트 같은 웰노운포트를 쓰는 게 당연히 좋음
실습) 호스트의 /host_vol 이라는 경로에는 간단한 댕댕이 템플릿이 존재한다. 이 무료템플릿을 호스트의 7979번 포트로 퍼블리쉬해보세요.
풀이) httpd:latest로 해보겠습니다.
root@host:/# apt update -y && apt install -y unzip wget
root@host:~# wget https://www.free-css.com/assets/files/free-css-templates/download/page8/dogcare.zip
# 템플릿 다운
root@host:~# unzip dogcare.zip
root@host:~# cd dogcare/
root@host:~/dogcare# mkdir /host_vol
root@host:~/dogcare# cp -r ./* /host_vol
root@host:~/dogcare# cd /host_vol/
root@host:/host_vol# docker run -d --name web -v /host_vol:/usr/local/apache2/htdocs -p 7979:80 httpd:latest

root@host:/host_vol# docker run -d --name web -v /host_vol:/usr/local/apache2/htdocs -p 7979:80 httpd:latest
2084946d521f04156cafe2c8b8743a98beb818b5803c7e6f314599946d2508d1
root@host:/host_vol# ls
about.html blog.html css fonts images index.html js shop.html
root@host:/host_vol# echo cptest > cptest.txt
root@host:/host_vol#

docker cp (중요도 하)
1. 호스트의 파일을 컨테이너에 복사


root@host:/host_vol# docker cp web:/cptest.txt ./
Successfully copied 2.05kB to /host_vol/./
root@host:/host_vol# ls
about.html blog.html cptest.txt css fonts images index.html js shop.html
root@host:/host_vol# cat cptest.txt
cptest
- 최상위 디렉터리에 cptest.txt가 존재하는 걸 확인가능

- 컨테이너의 파일을 호스트에 복사
환경 변수 지정: -e (중요도: 중)
root@host:/host_vol# docker run -d --name envcon -e ENVTEST=test nginx:latest
2e4b8f2033da2b70c44f76c10d738566d0ebd9c1fcab08e1723f506ddeac54e9
root@host:~# docker exec -it envcon bash
root@2e4b8f2033da:/# echo $ENVTEST
test
-> 데이터베이스 컨테이너 생성 시 많이 사용함
-> 데이터베이스 컨테이너의 경우엔 적어도 root패스워드를 설정해야 컨테이너가 잘 동작함
작업디렉터리지정: -w(나중에 중요함)
root@host:~# docker run -d --name workdir nginx:latest
root@host:/host_vol# docker exec -it workdir bash
root@84275b56ac90:/# pwd
/
- 컨테이너에 진입했을 때의 경로 = 현재작업디렉터리(pwd)
root@host:/host_vol# docker run -d --name workdir1 -w /usr nginx:latest
- 작업디렉터리를 /usr로 지정
root@host:~# systemctl restart docker

- 도커 엔진 재시작 시 동작중이던 컨테이너들이 멈추게 됨
root@host:~# docker rm -f $(docker ps -qa)
root@host:~# docker run -d --name retest --restart=always nginx:latest

- 원래는 도커가 정지 상태가 맞지만 restart always를 사용했기 때문에 restart를 해줘도 도커가 안 꺼지는 걸 확인할 수 있음.
- -- restart=always: 컨테이너가 중지가 돼도 항상 재시작
과제)
# 1. 호스트에 파일이 존재하는 경우
# 2. 마운트 포인트가 이미 존재하고 파일도 있는 경우.
-> 두 개가 어떤 차이가 있고, 어떻게 되는지 한 번 생각해 보세요.
-> 호스트 파일이 존재하고 파일도 이미 있음. 가려진 상태이지 없어진 건 아님
'AWS Cloud School 8기 > 도커(docker)' 카테고리의 다른 글
디스크 용량 확장/ mysql 컨테이너 생성/--link 옵션/ 사설저장소(private registry) (1) | 2025.04.02 |
---|---|
Docker Swarm (2) | 2025.04.01 |
docker compose/ 자동 재시작 스크립트/ 2번 기본 vim 편집기 (0) | 2025.03.30 |
다양한 앱 배포/ 웹 어플리케이션/ 프레임워크/ sudo lsof -i :<포트 번호>/ sudo kill -9 <PID>/ (0) | 2025.03.30 |
도커이미지(Docker Image)/ FROM/ WORKDIR/ COPY/ RUN/ CMD/ EXPOSE/ ENV/ Dockerfile (4) | 2025.03.19 |