📢 해당 포스트는 Docker Compose를 사용하여 여러 컨테이너들을 하나의 서비스로 묶어 관리하고 호스트 PC에 설치된 httpd(Apache)를 통해 특정 도메인과 도커 컨테이너를 연결하는 과정을 설명합니다.

 

🛠️ 참고
Virtualbox - Oracle VM Virtualbox
Linux OS - Rocky Linux
HTTPD - Apache

0. Docker Compose 개념

docker-compose.yml 파일을 사용해 하나의 가상 서버에서 여러 개의 컨테이너를 하나의 서비스로 정의해 컨테이너를 묶음으로 관리할 수 있다.

 

만약 Docker Compose를 사용하지 않는다면 컨테이너를 하나 하나씩 run 해야하는 번거러움이 있다. 하지만 Docker Compose를 사용하면 한번의 명령어로 설정 파일 안에 여러 개의 컨테이너들을 관리할 수 있다.(각 컨테이너들의 의존성, 네트워크, 볼륨 등등)

 

정말 편하긴 하더라.. 이제 docker run 안해도 된다!

1. Docker Compose 설치

# 도커 깃허브 저장소에서 Docker Compose 다운로드
[root@localhost ~]# curl -L https://github.com/docker/compose/releases/download/1.11.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 8052k  100 8052k    0     0  4269k      0  0:00:01  0:00:01 --:--:-- 6290k
# 권한 부여
[root@localhost ~]# chmod +x /usr/local/bin/docker-compose
# 버전 확인
[root@localhost ~]# docker-compose -v
docker-compose version 1.11.0, build 6de1806

2. docker-compose.yml 생성

💡 하위 항목은 탭(Tab)이 아닌 2개의 공백으로 하위 항목을 구분한다.

 

version: "3.0"
services:
  app5221:
    build:
      context: /home/user5221
      dockerfile: Dockerfile
    container_name: app5221
    ports:
      - "5221:80"
    volumes:
      - 5221:/home/download
  app5222:
    build:
      context: /home/user5222
      dockerfile: Dockerfile
    container_name: app5222
    ports:
      - "5222:80"
    volumes:
      - 5222:/home/download
  app5224:
    build:
      context: /home/user5224
      dockerfile: Dockerfile
    container_name: app5224
    ports:
      - "5224:80"
    volumes:
      - 5224:/home/download

volumes:
  '5221':
    external: true
  '5222':
    external: true
  '5224':
    external: true
  • version - Docker Compose 버전을 지정한다. 3.0 버전은 Docker Compose 1.10 및 Docker Engine 1.13.0 이상에서 사용된다.
    • services - 생성될 컨테이너를 묶어놓은 단위로 services 아래에 생성할 컨테이너 서비스를 지정한다.
      • app5221, app5222, app5224 - 생성될 서비스의 이름으로 각각의 서비스는 하나의 컨테이너를 나타낸다.
      • build - build 아래에 하위 항목으로 빌드할 때 사용할 옵션을 지정한다.
        • context
          • Dockerfile이 있는 경로를 지정한다.
          • 이전에 Dockerfile로 빌드 할 때 su - user5221 계정으로 접속해 루트 경로에서 Dockerfile과 app.jar을 두고 진행했었다. 현재 경로에서 dockerfile과 build 파일이 어디 있는지 적어주면 된다.
        • dockerfile - Dockerfile 그대로 적어준다.
      • container_name - 컨테이너 명을 지정한다.
      • ports
        • 해당 컨테이너로 접속할 포트를 지정한다.
        • 5221 포트로 요청 시 컨테이너 내부의 80 포트로 전달한다.
        • FROM openjdk:17-jdk-alpine
          ARG JAR_FILE=*.jar ADD ${JAR_FILE} app.jar
          EXPOSE 80
          WORKDIR /home/user5221
          VOLUME /home/download/
          ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]
        • 조금 더 쉽게 이해하기 위해 위의 dockerfile을 보자. dockerfile에 EXPOSE를 80으로 지정했다. 이것은 도커 컨테이너 내부에 포트 번호를 80으로 지정한다는 뜻이다. 그리고 docker-compose.yml에서 지정한 ports 5221은 내가 도메인:5221 로 접근할 경우 5221 포트를 확인하고 도커 컨테이너 내부의 80 포트로 매핑해주는 것이다.
      • volumes - 호스트와 컨테이너 간의 디렉토리 혹은 파일을 공유할 수 있는 볼륨을 지정한다.(볼륨이 없다면 docker volume create --name ${볼륨명 } 으로 만들면 된다) 왼쪽엔 내가 만들어놨던 도커 볼륨을 오른쪽엔 컨테이너 내에 파일이 저장될 경로를 적어주면 된다.
  • volumes
    • 외부 볼륨을 설정한다.
    • 5221 ~ 5224 - 볼륨명(참고로 숫자만 있다면 따옴표(’)로 감싸줘야 한다. 아니면 docker-compose.yml 실행 시 따옴표로 감싸라며 에러가 발생한다)
    • external: true - docker-compose.yml에서 정의한 볼륨이 외부에 이미 존재하며 Docker Compose가 해당 볼륨을 새로 생성하지 않고 기존에 있던 볼륨을 사용할 수 있도록 지정한다.

3. httpd 수정

❓ 앞에 숫자가 있는 것은 vi 편집기에서 `:set number` 명령어로 넘버 라인을 표시했기 때문이다.

 

47 Listen 80

100 ServerName localhost:80

360 <VirtualHost *:80>
361   ServerName ljy.r-e.kr
362   ProxyPreserveHost On
363   ProxyPass / http://192.168.56.103:5221/
364   ProxyPassReverse / http://192.168.56.103:5221/
365 </VirtualHost>
366
367 <VirtualHost *:80>
368   ServerName replay.o-r.kr
369   ProxyPreserveHost On
370   ProxyPass / http://192.168.56.103:5222/
371   ProxyPassReverse / http://192.168.56.103:5222/
372 </VirtualHost>
373
374 <VirtualHost *:80>
375   ServerName ttoganjip.kro.kr
376   ProxyPreserveHost On
377   ProxyPass / http://192.168.56.103:5224/
378   ProxyPassReverse / http://192.168.56.103:5224/
379 </VirtualHost>
  • Listen 80 - 호스트 PC의 포트 번호는 기본적으로 80이다. 어디서 사용하는 거 아니면 변경할 필요는 없다.
  • ServerName localhost:80 - ServerName에 앞에 #로 주석 처리가 되어있을 경우 'systemctl restart httpd' 했을 때 에러가 발생하므로 주석을 풀어준다.
  • <VirtualHost> - 가상 호스트를 설정하는 시작 태그
    • * - 와일드 카드, 모든 IP에서 접근할 수 있다.
    • 80 - 웹 브라우저에서 접근할 포트
    • ServerName - 발급받은 도메인 주소를 적어주면 된다.
    • ProxyPass
      • 웹 서버가 받은 요청을 다른 서버로 전달한다.
      • 간단하게 설명하면 ljy.r-e.kr 도메인으로 접근할 경우 포트 80은 http의 기본 포트이기 때문에 생략이 가능하다. 그리고 ProxyPass 옆에 / 로 명시했기 때문에 ServerName 자체로 접근이 가능하다. 만약 / 옆에 /app1 과 같이 문자가 있을 경우 http://ljy.r-e.kr/app1 까지 접근해줘야 192.xxx.xxx.xx.xxx:5221 로 매핑된다.
      • / 우측의 192.xxx.xx.xxx 는 호스트 PC(도커 컨테이너가 있는 가상 서버)의 IP 주소이며 5221 은 위에 docker-compose.yml을 실행하며 생성된 도커 컨테이너의 포트 번호이다.
    • ProxyPassReverse - ProxyPassReverse는 ProxyPass로 전달된 요청의 응답을 원래 요청한 URL(http://ljy.r-e.kr)로 반환한다.

4. docker-compose.yml 실행

[root@localhost ~]# docker-compose up -d
Building app5221
Step 1/7 : FROM openjdk:17-jdk-alpine
17-jdk-alpine: Pulling from library/openjdk
Digest: sha256:4b6abae565492dbe9e7a894137c966a7485154238902f2f25e9dbd9784383d81
Status: Downloaded newer image for openjdk:17-jdk-alpine
 ---> 264c9bdce361
Step 2/7 : ARG JAR_FILE=*.jar
 ---> Running in d4e39d164dab
 ---> Removed intermediate container d4e39d164dab
 ---> 123f25dbf9ef
Step 3/7 : ADD ${JAR_FILE} app.jar
 ---> 3c33b3c3ab69
Step 4/7 : EXPOSE 80
 ---> Running in 2b4b2b2843d1
 ---> Removed intermediate container 2b4b2b2843d1
 ---> a5d5b704de06
Step 5/7 : WORKDIR /home/user5221
 ---> Running in 2e3190589b51
 ---> Removed intermediate container 2e3190589b51
 ---> de8bdb8b89a9
Step 6/7 : VOLUME /home/download/
 ---> Running in 8e8f719dee29
 ---> Removed intermediate container 8e8f719dee29
 ---> d1906f0955fd
Step 7/7 : ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]
 ---> Running in 76b283596830
 ---> Removed intermediate container 76b283596830
 ---> 45add8706680
Successfully built 45add8706680
Successfully tagged root_app5221:latest
Step 1/7 : FROM openjdk:17-jdk-alpine
 ---> 264c9bdce361
Step 2/7 : ARG JAR_FILE=*.jar
 ---> Using cache
 ---> 123f25dbf9ef
Step 3/7 : ADD ${JAR_FILE} app.jar
 ---> d82f9b2101cf
Step 4/7 : EXPOSE 80
 ---> Running in 5de73cff26e0
 ---> Removed intermediate container 5de73cff26e0
 ---> e882c553ce93
Step 5/7 : WORKDIR /home/user5222
 ---> Running in 1e8354ca8a60
 ---> Removed intermediate container 1e8354ca8a60
 ---> 0f4aa1023a3f
Step 6/7 : VOLUME /home/download/
 ---> Running in e960781f165a
 ---> Removed intermediate container e960781f165a
 ---> 865d83bf6c09
Step 7/7 : ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]
 ---> Running in 2b4961456f2e
 ---> Removed intermediate container 2b4961456f2e
 ---> 05a578f785fc
Successfully built 05a578f785fc
Successfully tagged root_app5222:latest
Step 1/7 : FROM openjdk:17-jdk-alpine
 ---> 264c9bdce361
Step 2/7 : ARG JAR_FILE=*.jar
 ---> Using cache
 ---> 123f25dbf9ef
Step 3/7 : ADD ${JAR_FILE} app.jar
 ---> 3320792a1923
Step 4/7 : EXPOSE 80
 ---> Running in cebb9be1f1f7
 ---> Removed intermediate container cebb9be1f1f7
 ---> d236bab3f5ae
Step 5/7 : WORKDIR /home/user5224
 ---> Running in b569d6082ae4
 ---> Removed intermediate container b569d6082ae4
 ---> 717ddfe785e0
Step 6/7 : VOLUME /home/download/
 ---> Running in afac756c4972
 ---> Removed intermediate container afac756c4972
 ---> b8509aceb6a7
Step 7/7 : ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]
 ---> Running in 85b6a4ae19c8
 ---> Removed intermediate container 85b6a4ae19c8
 ---> 27fa929132f9
Successfully built 27fa929132f9
Successfully tagged root_app5224:latest
Creating app5221
Creating app5224
Creating app5222
Creating apache

그리고 docker ps 명령어를 통해 컨테이너가 정상적으로 띄워졌는지 확인해보자.

CONTAINER ID   IMAGE          COMMAND                  CREATED       STATUS       PORTS                                       NAMES
e4475ce40736   root_app5221   "java -jar -Dspring.…"   3 hours ago   Up 3 hours   0.0.0.0:5221->80/tcp, :::5221->80/tcp       app5221
f344f94e5267   root_app5222   "java -jar -Dspring.…"   3 hours ago   Up 3 hours   0.0.0.0:5222->80/tcp, :::5222->80/tcp       app5222
bb9858deaf5f   root_app5224   "java -jar -Dspring.…"   3 hours ago   Up 3 hours   0.0.0.0:5224->80/tcp, :::5224->80/tcp       app5224
925df5c890d9   mariadb        "docker-entrypoint.s…"   8 days ago    Up 2 days    0.0.0.0:3307->3306/tcp, :::3307->3306/tcp   mariadb

5. 포트 포워딩 및 방화벽 포트 등록

1. 공유기 포트 포워딩

80 포트는 가상 서버의 httpd 기본 포트(변경하지 않았으므로 이 포트를 사용했다) 그리고 5221 ~ 5229까지 되어있는 것은 위에서 5221, 5222, 5224와 같이 도커 컨테이너 접근 시 사용하는 포트 번호를 미리 설정해놓은 것이다.

2. 가상 머신 포트 포워딩

내가 포스팅한 네트워크 관련 글에는 죄다 이 캡처가 들어가있는 것 같은데.. 여튼 이렇게 외부에서 들어오는 요청을 받을 수 있게 설정해준다. 외부에서 80 포트로 들어오면 내부의 80 포트(http 기본 포트)로 연결시킨다. 만약 httpd.conf에서 포트를 8080 뭐 이런 식으로 잡았다면 80, 8080으로 설정해야겠지..

3. 방화벽 포트 등록

80 포트를 받을 수 있게 firewall을 통해 포트를 등록한다.

[root@localhost ~]# firewall-cmd --permanent --add-port=80/tcp
[root@localhost ~]# firewall-cmd --reload
[root@localhost ~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: enp0s3 enp0s8
  sources:
  services: cockpit dhcpv6-client ssh
  ports: 30000/tcp 80/tcp
  protocols:
  forward: yes
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

이렇게 등록되어 있으면 된다.

6. 도메인 등록

나는 무료로 도메인을 발급해주는 내도메인.한국을 사용하고 있다.

궁금하면 아래 이전 포스팅 링크를 통해 발급받는 방법을 확인할 수 있다.

 

[ETC] 무료 도메인 발급

포트폴리오, 실습용으로 도메인을 구입하기엔 치킨 한마리 값이라도 뭔가 비싸보였다. 그래서 무료 도메인 발급을 검색하다가 내도메인.한국 이라는 곳을 알게 되었는데 사용법을 검색하지 않

mytilblog.tistory.com

 

그리고 IP 연결할 때 뒤에 포트 번호는 붙이지 않는다.

공인 IP로 해당 도메인에 접근했을 때 위에 httpd.conf에서 지정한 5221 포트로 매핑된다.

7. 확인

짠~

'Network > Docker' 카테고리의 다른 글

[Network | Docker] jar 배포  (0) 2024.07.07
[Network | Docker] Dockerfile  (0) 2024.07.05
[Network | Docker] 이미지  (0) 2024.07.04
[Network | Docker] 네트워크  (0) 2024.07.03
[Network | Docker] 데이터베이스 외부 원격 접속  (0) 2024.07.03

✅ 진행은 root 계정이 아닌 배포할 도커 컨테이너의 관리자 계정으로 진행했다.(컨테이너 1개 당 각각 다른 관리자 계정을 가진다)

 

(참고) 컨테이너 전용 계정 docker 그룹에 추가

더보기
$ usermod -aG ${그룹명 } ${계정명 }
$ cat /etc/group # 그룹 및 그룹에 속한 계정 확인

1. jar 추출

  1. Gradle - build - bootJar 더블 클릭!
  2. Project - build - libs - ${프로젝트명 }-0.0.1-SNAPSHOT.jar 오른쪽 클릭 - Open In - Explorer
  3. 해당 jar 파일을 원하는 곳으로 복사해둔다.(filezilla에서 쉽게 찾아 옮기기 위함 …)

2. FTP 프로그램(Filezilla)을 통해 jar 파일 이동(Host PC)

3. jdk 이미지 다운로드(Linux)

jdk 버전은 구글에 검색하면 잘나오니 확인하고 다운로드 받도록 한다. 혹은 docker search ${이미지명 } 을 통해 검색한다.

 

$ docker pull openjdk:17-alpine

4. Dockerfile 생성(Linux)

FROM openjdk:17-jdk-alpine
ARG JAR_FILE=*.jar
ADD ${JAR_FILE} app.jar
EXPOSE 80
WORKDIR /home/user5221
VOLUME /home/download/
ENTRYPOINT ["java","-jar","-Dspring.profiles.active=prod","/app.jar"]

5. docker build(Linux)

$ docker build -t blog .

6. docker run(Linux)

$ docker run -d \
> --name blog \
> -p 3000:80 \
> blog

$ docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                                   NAMES
80eb44c2d36d   blog      "java -jar -Dspring.…"   16 seconds ago   Up 15 seconds   0.0.0.0:3000->80/tcp, :::3000->80/tcp   blog

7. 확인(Host PC)

💥 template might not exist or might not be accessible by any of the configured Template Resolvers

검색해보니 기본적으로 jar 파일의 classpath의 기본 값은 public/, resources/, static/, templates/, META-INF/\*\*, \* 인데 IntellJ에서는 컨트롤러의 return 타입에 ‘/’가 1개 더 붙더라도 알아서 처리해주지만 jar 배포할 때는 처리해주지 않는다.

 

2024-07-05T03:23:56.802Z ERROR 1 --- [p-nio-80-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] :
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception
[Request processing failed: org.thymeleaf.exceptions.TemplateInputException: Error resolving template [/login],
template might not exist or might not be accessible by any of the configured Template Resolvers] with root cause

 

위와 같이 src/main/resources/templates 경로 뒤에 ‘/’가 붙기 때문에 컨트롤러에서 return 값으로 ‘/’를 붙여버리면 //${templates 내 파일명 } 이 되기 때문에 에러가 발생한다.

 

따라서 컨트롤러에서 return 타입을 String으로 갖고있는 메소드들의 반환 타입들을 확인 후 return "/ ..." return 뒤에 ‘/’가 붙을 경우 아래와 같이 지워준다.

 

    // 변경 전
    @GetMapping("/login")
    public String login() {
        return "/login";
    }

    // 변경 후
    @GetMapping("/login")
    public String login() {
        return "login";
    }

 

 

잘나오는 것을 확인할 수 있다!

 

외부에서도 볼 수 있게 하고싶다면 docker run 할 때 지정했던 포트 번호를 공유기랑 가상 서버에 포트 포워딩 시켜주면 된다. 하는 방법은 이전 포스팅에서 많이 다뤄서 패스한다.

 

PS. 다시 보니 도커 컨테이너 관리자 계정명은 user5221인데 포트는 3000..😅


참고 레퍼런스

https://velog.io/@hyunho058/Docker-docker를-이용한-jar파일-배포

https://myserena.tistory.com/155

  • 완성된 이미지를 생성하기 위해 컨테이너에 설치해야 하는 패키지, 소스코드, 명령어, 셸 스크립트 등을 하나의 파일에 기록하면 도커가 해당 파일을 읽어 컨테이너에서 작업을 수행한 뒤 이미지로 만들어내는 작업
  • 직접 컨테이너를 생성하고 이미지로 커밋해야 하는 번거러움을 덜 수 있다.
  • 깃과 같은 개발 도구를 통해 애플리케이션의 빌드 및 배포를 자동화 할 수 있다.

1. Dockerfile 작성

[root@localhost ~]# mkdir dockerfile && cd dockerfile
[root@localhost dockerfile]# echo hello world! >> test.html
[root@localhost dockerfile]# ls
test.html

[root@localhost dockerfile]# vi Dockerfile

# Dockerfile 내에서 아래와 같이 작성한다.
FROM rockylinux/rockylinux:latest # 베이스 이미지 지정
MAINTAINER jy94 # 이미지 생성 개발자 지정
LABEL "purpose"="practice" # 라벨 지정
RUN yum install httpd -y # 이미지 생성 시 apache 설치
ADD test.html /var/www/html # 현재 디렉터리(dockerfile)에 있는 test.html을 /var/www/html에 추가
WORKDIR /var/www/html # 작업 디렉터리 지정
RUN ["/bin/bash", "-c", "echo hello docker! >> test2.html"] # bash shell을 통해 test2.html 파일 생성
EXPOSE 80 # 컨테이너가 사용할 포트 지정
CMD apachectl -DFOREGROUND # 컨테이너 명령 지정

Dockerfile은 한 줄이 하나의 명령어이며 명령어 뒤에 옵션을 추가하는 방식으로 작성한다.

일반적으로 대문자로 표기한다.(FROM, RUN, ADD …)

 

  • FROM - 생성할 이미지의 베이스가 될 이미지 / Dockerfile 작성 시 무조건 입력해야 한다. 사용하려는 이미지가 없다면 자동으로 full한다.
  • MAINTAINER - 이미지를 생성한 개발자의 정보 … 도커 1.13.0 버전 이후로 사용하지 않는다.
  • LABEL - 이미지의 메타 데이터 / key=value로 구분한다. 여러 개의 메타 데이터를 저장할 수 있다. 추가된 메타 데이터는 docker inspect 명령어를 통해 확인 가능하다.
  • RUN - 이미지를 만들기 위해 컨테이너 내부에서 명령어를 실행한다. 이미지 빌드 시 입력을 받아야 하는(y / n) RUN이 있다면 오류로 간주 후 빌드가 종료된다.(’RUN yum install ~’ 실행 시 입력받아야 하는 부분이 있는데 -y가 없으면 에러로 간주돼 빌드 실패 에러가 뜬다)
  • ADD - 파일을 이미지에 추가한다.현재는 Dockerfile이 위치한 디렉터리에서 파일을 가져와 이미지 내 /var/www/html 디렉터리에 추가한다.
  • WORKDIR - 실행할 디렉터리를 지정한다.
  • EXPOSE - Dockerfile 빌드 시 생성된 이미지에서 노출할 포트를 지정한다. 바인딩을 보장하는 것이 아니며 단지 컨테이너의 80번 포트를 사용하는 것 임을 나타내는 것 뿐이다.
  • CMD - 컨테이너 시작 시 실행할 명령어를 설정하며 Dockerfile에서 단 한번만 사용 할 수 있다.

2. Dockerfile 빌드 및 컨테이너 생성

[root@localhost dockerfile]# docker build -t mybuild:0.0 ./
[+] Building 16.5s (10/10) FINISHED                                           docker:default
 => [internal] load build definition from Dockerfile                                    0.0s
 => => transferring dockerfile: 349B                                                    0.0s
 => WARN: MaintainerDeprecated: Maintainer instruction is deprecated in favor of using  0.0s
 => WARN: JSONArgsRecommended: JSON arguments recommended for CMD to prevent unintende  0.0s
 => [internal] load metadata for docker.io/rockylinux/rockylinux:latest                 0.0s
 => [internal] load .dockerignore                                                       0.0s
 => => transferring context: 2B                                                         0.0s
 => CACHED [1/5] FROM docker.io/rockylinux/rockylinux:latest                            0.0s
 => [2/5] RUN yum install httpd -y                                                     14.1s
 => [internal] load build context                                                       0.0s
 => => transferring context: 90B                                                        0.0s
 => [3/5] ADD test.html /var/www/html                                                   0.3s
 => [4/5] WORKDIR /var/www/html                                                         0.1s
 => [5/5] RUN ["/bin/bash", "-c", "echo hello docker! >> test2.html"]                   0.7s
 => exporting to image                                                                  0.9s
 => => exporting layers                                                                 0.9s
 => => writing image sha256:7d423b7c547fec09d468c7e7b3ecef98797556f70413dcd01d424cd869  0.0s
 => => naming to docker.io/library/mybuild:0.0                                          0.0s

[root@localhost dockerfile]# docker images
REPOSITORY              TAG       IMAGE ID       CREATED         SIZE
mybuild                 0.0       7d423b7c547f   7 minutes ago   250MB

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
02521efde9f3   mybuild:0.0             "/bin/sh -c 'apachec…"   24 minutes ago   Up 24 minutes   0.0.0.0:32782->80/tcp, :::32782->80/tcp                myserver
  • -t - 생성될 이미지의 이름을 지정한다. 위 명령 실행 시 mybuild:0.0 이름의 이미지가 생성된다.(-t 옵션을 사용하지 않을 경우 16진수 형태의 이름으로 저장되기 때문에 -t 옵션을 사용해서 이미지 이름을 지정해주자)

 

build 명령어 끝에는 Dockerfile이 저장된 경로를 입력하면 된다.

./ 는 Dockerfile이 저장된 현재 디렉터리를 뜻한다.

이제 위에서 docker build한 이미지로 컨테이너를 만들어보자.

 

$ docker run -d -P --name myserver2 mybuild:0.0
# docker run -[백그라운드 실행] -[호스트에 Dockerfile EXPOSE 포트 연결] -[컨테이너명 지정] -[이미지:태그]

3. 확인

docker build 하기 전에 만들었던 test.html이 정상적으로 출력된다.

 

docker build 시 RUN 명령어에 의해 만들어졌던 test2.html 또한 정상적으로 출력된다.

[추가] 난 외부(공인 IP)에서도 들어오게 하고싶다! 🫨

외부로도 들어올 수 있게 설정해주면 나중에 만들었던 프로젝트들을 배포할 수 있을테니.. 해보자!

이전에 가상 서버 자체에서 배포했을 때랑 비슷했다.

 

일단 Dockerfile에서 EXPOSE를 포트 포워딩 할 포트 번호로 수정했다.

FROM rockylinux/rockylinux:latest
MAINTAINER jy94
LABEL "purpose"="practice"
RUN yum update -y
RUN yum install httpd -y
ADD test.html /var/www/html
WORKDIR /var/www/html
EXPOSE 5221
CMD apachectl -DFOREGROUND

 

 

해당 Dockerfile을 build 한다.

[root@localhost dockerfile]# docker build -t build5221 ./

[root@localhost dockerfile]# docker images
REPOSITORY              TAG       IMAGE ID       CREATED         SIZE
build5221               latest    a3ea627ac00f   3 hours ago     564MB

 

 

이후 컨테이너를 생성하고 생성한 컨테이너를 확인한다.

[root@localhost dockerfile]# docker run -d \
> -p 5221:80 \
> --name server5221 \
> build5221

[root@localhost dockerfile]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS          PORTS                                             NAMES
44c1ee590545   build5221   "/bin/sh -c 'apachec…"   11 seconds ago   Up 11 seconds   5221/tcp, 0.0.0.0:5221->80/tcp, :::5221->80/tcp   server5221

kt 와이파이 기준으로 아래와 같이 포트 포워딩을 해준다.

 

5222로도 테스트해본거라 2개가 있다.

172.xxx.xx.x.xx는 공유기 사설 IP이다.

공인 IP 221.xxx … 로 5221 포트를 갖고 들어오면 공유기 사설 IP인 172.xxx … 가 5221번 포트를 포워딩시킨다.

 

oracle vm virtualbox 기준으로 아래와 같이 포트 포워딩을 아래와 같이 해준다.

 

여기까지 하고나면 아래와 같이 공인 IP로도 접속이 가능하다.

1. 실습용 컨테이너 생성

[root@localhost ~]# docker run -it \
> --name commit_test \
> rockylinux/rockylinux

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
c2fed75bfdf6   rockylinux/rockylinux   "/bin/bash"              50 minutes ago   Up 50 minutes                                                          commit_test

[root@c2fed75bfdf6 /]# ls
bin  etc   lib    lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr

[root@c2fed75bfdf6 /]# echo my_first_commit! >> first

[root@c2fed75bfdf6 /]# ls
bin  etc    home  lib64       media  opt   root  sbin  sys  usr
dev  first  lib   lost+found  mnt    proc  run   srv   tmp  var

[root@c2fed75bfdf6 /]# cat first
my_first_commit!

2. docker commit 명령어를 통한 이미지 생성

[root@localhost ~]# docker commit \
> -a "jy94" \
> -m "my first commit!" \
> commit_test \
> commit_test:first

[root@localhost ~]# docker images
REPOSITORY              TAG       IMAGE ID       CREATED              SIZE
commit_test             first     1437cb40ff5f   5 seconds ago        196MB
  • -a - author / 이미지 작성자를 나타내는 메타 데이터
  • -m - 커밋 메세지
  • commit_test - 컨테이너 이름
  • commit_test:first - 생성할 이미지(commit_test - 이미지 이름 / first - 태그 이름)

docker commit 후 도커에 저장된 이미지를 확인하면 방금 커밋했던 이미지가 목록에 출력된 것을 확인할 수 있다.

3. 직접 만든 이미지로 이미지 생성

[root@localhost ~]# docker run -it \
> --name commit_test2 \
> commit_test:first

[root@dd17230f0ed1 /]# echo test_second! >> second

[root@dd17230f0ed1 /]# ls
bin  etc    home  lib64       media  opt   root  sbin    srv  tmp  var
dev  first  lib   lost+found  mnt    proc  run   second  sys  usr

[root@dd17230f0ed1 /]# cat second
test_second!

 

[root@localhost ~]# docker commit \
> -a "jy94" -m "my second commit!" \
> commit_test2 \
> commit_test:second

[root@localhost ~]# docker images
REPOSITORY              TAG       IMAGE ID       CREATED         SIZE
commit_test             second    77d22cf302fb   5 seconds ago   196MB
commit_test             first     1437cb40ff5f   8 minutes ago   196MB

 

docker commit 명령어를 통해 이미지를 만들었다. pull로 받은 이미지나 내가 만든 이미지를 확인하기 위해선 inspect 명령어를 사용하면 된다.

 

[root@localhost ~]# docker inspect commit_test:first
[
    {
        "Id": "sha256:1437cb40ff5f3b9f3c5701f4d7682ca653387c32621493eefd278817220e1aa4",
        "RepoTags": [
            "commit_test:first"
        ],
        "RepoDigests": [],
        "Parent": "sha256:523ffac7fb2e245e5e7c407b9f7377be9c6c3bf03d380981168311f49030da17",
        "Comment": "my first commit!",
        "Created": "2024-07-01T08:38:13.621284304Z",
        "DockerVersion": "27.0.2",
        "Author": "jy94",
        "Config": {
            "Hostname": "c2fed75bfdf6",
            "Domainname": "",
            "User": "",
            "AttachStdin": true,
            "AttachStdout": true,
            "AttachStderr": true,
            "Tty": true,
            "OpenStdin": true,
            "StdinOnce": true,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "/bin/bash"
            ],
            "Image": "rockylinux/rockylinux",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "io.buildah.version": "1.23.1"
            }
        },
        "Architecture": "amd64",
        "Os": "linux",
        "Size": 195598609,
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/86c4813fcc4481dce106bf1304158998369e06d7f6b06f7e5e6c4db76ebe0473/diff",
                "MergedDir": "/var/lib/docker/overlay2/891461206d466413b0db77538e89a57e675bda73ffc242673c5b3885d43ab641/merged",
                "UpperDir": "/var/lib/docker/overlay2/891461206d466413b0db77538e89a57e675bda73ffc242673c5b3885d43ab641/diff",
                "WorkDir": "/var/lib/docker/overlay2/891461206d466413b0db77538e89a57e675bda73ffc242673c5b3885d43ab641/work"
            },
            "Name": "overlay2"
        },
        "RootFS": {
            "Type": "layers",
            "Layers": [
                "sha256:44e6e3eb06d8ec453315fb8767b27ef54f69ca5c5364b6251d6bb2b907cc14bc",
                "sha256:8ac2a4d04b1579d008bbc70b38ce3cad4b12f02f2884237935caa8b78d2144cb"
            ]
        },
        "Metadata": {
            "LastTagTime": "2024-07-01T17:38:13.685229347+09:00"
        }
    }
]

중요하게 봐야 할 부분은 Layers 부분이다. 나는 rocky linux 이미지를 땡겨와 commit_test:first로 이미지를 한번 만들고 만들어진 commit_test:first 이미지를 commit_test:second라는 이름으로 한번 더 만들었다.

 

rocky linux, commit_test:first, commit_test:second 총 3개의 파일 크기가 188MB 일 때 188MB 크기의 이미지가 3개 존재하는 게 아니라 컨테이너에서 변경된 사항만 새로운 레이어로 저장되고 그 레이어를 포함해 새로운 이미지를 생성한다. 때문에 전체 이미지의 실제 크기는 188MB + first 파일 크기 + second 파일 크기가 되는 것이다.

  • first 파일 - commit_test:first 생성 시 사용했던 컨테이너가 변경된 사항
  • second 파일 - commit_test:second 생성 시 사용했던 컨테이너가 변경된 사항 …

 

특정 이미지가 어떤 레이어로 생성됐는지 알고싶다면 docker history 명령어를 통해 알 수 있다.

 

[root@localhost ~]# docker history commit_test:second
IMAGE          CREATED          CREATED BY                                      SIZE      COMMENT
77d22cf302fb   15 minutes ago   /bin/bash                                       13B       my second commit!
1437cb40ff5f   23 minutes ago   /bin/bash                                       17B       my first commit!
523ffac7fb2e   24 months ago    /bin/sh -c #(nop) CMD ["/bin/bash"]             0B
<missing>      24 months ago    /bin/sh -c #(nop) ADD file:1fc0280b685c9dca1…   196MB

4. 이미지 삭제

이미지를 삭제할 때는 사용 중인 컨테이너는 삭제할 수 없으며 docker rm -f ${컨테이너 이름 } 처럼 강제로 삭제할 수 있지만 이는 이미지 레이어 파일을 실제로 삭제하지는 않으며 이미지 이름만 삭제시킨다. 따라서 이미지 삭제 시에는 사용 중인 컨테이너를 중지시키고 삭제해야 한다.

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                                  NAMES
dd17230f0ed1   commit_test:first       "/bin/bash"              23 minutes ago   Up 23 minutes                                                          commit_test2
c2fed75bfdf6   rockylinux/rockylinux   "/bin/bash"              35 minutes ago   Up 35 minutes                                                          commit_test
b97d76ab0c2d   mysql:5.7               "docker-entrypoint.s…"   7 hours ago      Up 7 hours      33060/tcp, 0.0.0.0:3308->3306/tcp, :::3308->3306/tcp   mysql
3d3ebab72926   rockylinux/rockylinux   "/usr/sbin/init"         24 hours ago     Up 16 hours     0.0.0.0:8081->80/tcp, :::8081->80/tcp                  rockylinux

[root@localhost ~]# docker stop commit_test2 && docker rm commit_test2
commit_test2
commit_test2

[root@localhost ~]# docker rmi commit_test:second
Untagged: commit_test:second

 

그리고 댕글링 이미지는 docker image prune 명령어를 통해 삭제가 가능하다.

# <none> - 댕글링(dangling) 이미지로 이미지 이름이 <none>으로 출력된다.
[root@localhost ~]# docker images -f dangling=true # 댕글링 이미지만 출력한다.
REPOSITORY              TAG       IMAGE ID       CREATED          SIZE
<none>                  <none>    67ee4013357e   34 minutes ago   196MB

[root@localhost ~]# docker image prune # 댕글링 이미지만 모두 삭제한다.

[root@localhost ~]# docker images
REPOSITORY              TAG       IMAGE ID       CREATED          SIZE

 

추출, 배포는 다음에...🥹

'Network > Docker' 카테고리의 다른 글

[Network | Docker] jar 배포  (0) 2024.07.07
[Network | Docker] Dockerfile  (0) 2024.07.05
[Network | Docker] 네트워크  (0) 2024.07.03
[Network | Docker] 데이터베이스 외부 원격 접속  (0) 2024.07.03
[Network | Docker] 볼륨  (0) 2024.07.02

컨테이너의 eth0 인터페이스는 호스트의 veth … 인터페이스(도커 컨테이너 시작 시 생성)와 연결되며 veth 인터페이스는 docker0 브리지와 바인딩되기 때문에 외부 통신이 가능하다.

 

이해하기 위해 직접 확인해보자.

 

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED        STATUS        PORTS                                                  NAMES
b97d76ab0c2d   mysql:5.7               "docker-entrypoint.s…"   3 hours ago    Up 3 hours    33060/tcp, 0.0.0.0:3308->3306/tcp, :::3308->3306/tcp   mysql
3d3ebab72926   rockylinux/rockylinux   "/usr/sbin/init"         19 hours ago   Up 12 hours   0.0.0.0:8081->80/tcp, :::8081->80/tcp                  rockylinux

현재 실행 중인 컨테이너를 출력한다. 전체 컨테이너를 출력하려면 -a 옵션을 붙여주면 된다. 나는 rockylinux로 들어갈 것이다.

 

[root@localhost ~]# docker exec -it rockylinux /bin/bash
  • exec - 현재 실행 중인 컨테이너에 새로운 명령을 부여한다.
  • -i - 터미널 입력을 받을 수 있다.
  • -t - 가상 터미널을 할당한다.
  • rockylinux - 컨테이너 생성 시 지정한 컨테이너 이름
  • /bin/bash - 컨테이너 내에서 실행할 명령어로 지금은 bash shell을 실행한다.

 

이후 해당 컨테이너에 bash shell로 접속 후 네트워크 인터페이스 정보를 확인한다.

[root@3d3ebab72926 /]# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.4  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:04  txqueuelen 0  (Ethernet)
        RX packets 1496  bytes 15634237 (14.9 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 953  bytes 71539 (69.8 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

 

컨테이너 내부에서 ifconfig를 통해 출력된 결과와 그림을 같이 비교하면 1. 도커 컨테이너의 네트워크 인터페이스에는 eth0과 lo 네트워크 인터페이스가 존재한다. 그리고 2. 컨테이너의 eth0 인터페이스는 호스트의 veth … 인터페이스(도커 컨테이너 시작 시 생성)와 연결되어 있으며 3. veth 인터페이스는 docker0 브리지와 바인딩되기 때문에 외부 통신이 가능한 것이다.

 

veth는 컨테이너 1개 당 하나씩 생성되는 네트워크 인터페이스인데 위에서 설명하기를 veth 인터페이스는 docker0 브리지와 바인딩된다고 설명했다.

 

[root@localhost ~]# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:8eff:fefe:2f30  prefixlen 64  scopeid 0x20<link>
        ether 02:42:8e:fe:2f:30  txqueuelen 0  (Ethernet)
        RX packets 5951  bytes 1743562 (1.6 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8329  bytes 96413755 (91.9 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 1  bytes 576 (576.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1  bytes 576 (576.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

veth3109825: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::78e6:85ff:fe27:4d11  prefixlen 64  scopeid 0x20<link>
        ether 7a:e6:85:27:4d:11  txqueuelen 0  (Ethernet)
        RX packets 953  bytes 71539 (69.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1497  bytes 15634307 (14.9 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

vethb9d211c: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet6 fe80::24a2:9ff:fedd:cbb8  prefixlen 64  scopeid 0x20<link>
        ether 26:a2:09:dd:cb:b8  txqueuelen 0  (Ethernet)
        RX packets 481  bytes 260087 (253.9 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 599  bytes 64745 (63.2 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ifconfig를 통해 네트워크 인터페이스를 확인하면 컨테이너 개수 만큼 veth 인터페이스가 생성되어 있는 것을 확인할 수 있다. docker0 브리지 또한 존재한다.

 

이제 브리지 네트워크를 구성 & 관리할 수 있는 brctl 명령어를 통해 veth 인터페이스와 docker0 브리지가 연결되어있는지 확인한다.

 

[root@localhost ~]# brctl show docker0
bridge name     bridge id               STP enabled     interfaces
docker0         8000.02428efe2f30       no              veth3109825
                                                        vethb9d211

게시글 작성 기준으로 현재는 호스트 PC에서 접속했지만 이후 외부에서 원격으로 접속해야 한다면 공유기에서 포트 포워딩을 해주면 될 것 같다. 🐳🌊

1. 테스트용 컨테이너 생성

> docker run -d \ # 백그라운드 실행
> --name mysql \ # 컨테이너 이름
> -p 3308:3306 \ # 포트 매핑(외부에서 3308 포트로 들어오면 내부 3306 포트로 매핑된다)
> -e MYSQL_ROOT_PASSWORD=${password } \ # root 비밀번호 지정
> -e MYSQL_DATABASE=test \ # 테스트용 데이터베이스 생성
> mysql:5.7 # 이미지

2. 외부 접속 허용 설정

> docker ps -a
> docker exec -it mysql /bin/bash # 위에서 만들었던 컨테이너의 쉘에 접속
> mysql -u root -p # 컨테이너 내에서 root 계정으로 mysql에 로그인
> grant all privileges on *.* to '${계정명 }'@'%';
> flush privileges;
  • grant all privileges on *.* - 모든 데이터베이스와 모든 테이블에 대해 모든 권한을 부여
  • to '${계정명 }'@'%' - 특정 계정이 모든 호스트에서 접속 가능하도록 설정

 

데이터도 한번 넣어보자.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.00 sec)

mysql> use test;
Database changed
mysql> create table test_tbl ( \
    -> id int unsigned auto_increment primary key, \
    -> name varchar(30) not null \
    -> );
Query OK, 0 rows affected (0.05 sec)

mysql> insert test_tbl(name) values('lee juyoung');
Query OK, 1 row affected (0.02 sec)

mysql> select * from test_tbl;
+----+-------------+
| id | name        |
+----+-------------+
|  1 | lee juyoung |
+----+-------------+
1 row in set (0.00 sec)

3. 접속 테스트

호스트 PC에서 mysql workbench를 통해 접속 테스트를 진행한다. hostname은 가상 서버의 ip이며 port는 컨테이너 생성 시 컨테이너 내부 mysql 포트와 매핑한 포트 번호이다. 그리고 username과 password는 docker에서 mysql 이미지로 컨테이너 만들 때 사용했던 username과 password를 사용하면 된다.

 

컨테이너 내에서 mysql 접속 후 만들었던 테이블과 데이터가 출력되는 것을 확인할 수 있다!

0. 볼륨이란?

  • 컨테이너의 데이터를 영속적으로 활용할 수 있는 방법으로 방식에는 호스트 볼륨 공유, 볼륨 컨테이너, 도커 볼륨 방식 총 3가지가 있다.
  • 컨테이너는 생성 & 삭제가 간편하므로 데이터 보존에 있어 위험에 노출되어 있다. 볼륨은 컨테이너를 삭제하더라도 해당 컨테이너에서 만들어진 파일들을 특정 호스트 디렉토리 혹은 볼륨에 저장시킬 수 있기 때문에 많이 사용된다.
  • 컨테이너가 아닌 외부에 데이터를 저장시키기 때문에 스테이트리스(stateless)하다.
  • 스테이트리스한 컨테이너 설계는 도커를 사용할 때 매우 바람직한 설계 방법 …

1. 호스트 볼륨 공유 방식

# mysql 컨테이너 생성
> docker run -d \ # 백그라운드 실행(detached)
> -e MYSQL_ROOT_PASSWORD=${password } \
> -e MYSQL_DATABASE=wordpress \ # DB 생성
**> -v /home/wordpress_db:/var/lib/mysql \ # 볼륨 설정(아래 설명 참고)**
> --name wordpressdb_volume \ # 컨테이너 이름 지정
> mysql:5.7 # 이미지 설정

# wordpress 컨테이너 생성
> docker run -d \
> -e WORDPRESS_DB_HOST=mysql \ # 데이터베이스 연결 시 link에 참조된 이름과 일치해야 함
> -e WORDPRESS_DB_USER=root \
> -e WORDPRESS_DB_PASSWORD=${password} \
> --name wordpress_volume \
**> --link wordpressdb_volume:mysql \ # 링크 설정(아래 설명 참고)**
> -p 80 \ # 80 포트와 연결
> wordpress

[root@localhost ~]# docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED              STATUS              PORTS                                     NAMES
2820c729f256   wordpress               "docker-entrypoint.s…"   6 seconds ago        Up 4 seconds        0.0.0.0:32781->80/tcp, :::32781->80/tcp   wordpress_volume
34dd147e9e76   mysql:5.7               "docker-entrypoint.s…"   About a minute ago   Up About a minute   3306/tcp, 33060/tcp                       wordpressdb_volume
f0aea5c53e3a   mysql:5.7               "docker-entrypoint.s…"   7 minutes ago        Up 7 minutes        3306/tcp, 33060/tcp                       mysql
3d3ebab72926   rockylinux/rockylinux   "/usr/sbin/init"         10 hours ago         Up 2 hours          0.0.0.0:8081->80/tcp, :::8081->80/tcp     rockylinux
  • -v /home/wordpress_db:/var/lib/mysql
    • -v 라는 명령어를 통해 호스트의 디렉토리와 컨테이너 내 디렉토리를 공유한다.
    • -v /home/mysql:/var/lib/mysql
    • -v ${호스트의 공유 디렉토리 }:${컨테이너의 공유 디렉토리 }
    • 💡 /var/lib/mysql 디렉터리는 MYSQL이 데이터베이스 데이터를 저장하는 기본 디렉터리이다.
  • --link wordpressdb_volume:mysql
  • 🌟 위에서 만든 mysql 컨테이너를 현재 생성 중인 컨테이너 내에서 mysql이라는 별칭으로 지정한다. wordpress라는 name을 가진 컨테이너는 --link 옵션의 사용으로 wordpressdb_volume의 ip를 알고있지 않아도 mysql이라는 별칭으로 지정해 사용 가능한 것이다. 해당 별칭은 `-e WORDPRESS_DB_HOST` 에서 사용되었다.

1-2. 컨테이너 삭제 후 데이터 보존 확인

[root@localhost ~]# cd /home
[root@localhost home]# ls
user  wordpress_db
[root@localhost home]# cd wordpress_db/
[root@localhost wordpress_db]# ls
auto.cnf         client-key.pem  ibdata1     performance_schema  server-key.pem
ca-key.pem       ib_buffer_pool  ibtmp1      private_key.pem     sys
ca.pem           ib_logfile0     mysql       public_key.pem      wordpress
client-cert.pem  ib_logfile1     mysql.sock  server-cert.pem
[root@localhost wordpress_db]# docker ps
CONTAINER ID   IMAGE                   COMMAND                  CREATED        STATUS       PORTS                                     NAMES
2820c729f256   wordpress               "docker-entrypoint.s…"   3 hours ago    Up 3 hours   0.0.0.0:32781->80/tcp, :::32781->80/tcp   wordpress_volume
34dd147e9e76   mysql:5.7               "docker-entrypoint.s…"   3 hours ago    Up 3 hours   3306/tcp, 33060/tcp                       wordpressdb_volume
f0aea5c53e3a   mysql:5.7               "docker-entrypoint.s…"   4 hours ago    Up 4 hours   3306/tcp, 33060/tcp                       mysql
3d3ebab72926   rockylinux/rockylinux   "/usr/sbin/init"         13 hours ago   Up 5 hours   0.0.0.0:8081->80/tcp, :::8081->80/tcp     rockylinux
[root@localhost wordpress_db]# docker stop 34dd
34dd
[root@localhost wordpress_db]# docker rm 34dd
34dd
[root@localhost wordpress_db]# ls
auto.cnf         client-key.pem  ibdata1             private_key.pem  sys
ca-key.pem       ib_buffer_pool  mysql               public_key.pem   wordpress
ca.pem           ib_logfile0     mysql.sock          server-cert.pem
client-cert.pem  ib_logfile1     performance_schema  server-key.pem

wordpressdb_volume 컨테이너를 삭제해도 mysql 컨테이너가 사용한 데이터가 그대로 남아있는 것을 확인할 수 있다.

2. 도커 볼륨 방식

  • 도커 자체에서 제공하는 볼륨 기능으로 볼륨 생성 시 플러그인을 통해 여러 종류의 스토리지 백엔드를 사용할 수 있다.
  • 볼륨을 다루는 명령어는 docker volume 으로 시작하며 docker volume create 명령어로 볼륨을 생성한다.
> docker volume create \
> --name myvolume
myvolume

> docker volume ls
DRIVER    VOLUME NAME
local     myvolume

볼륨 생성 시 플러그인을 통해 여러 종류의 스토리지 백엔드를 사용할 수 있다.

해당 예제에서는 기본적으로 제공되는 드라이버인 local을 사용하며 해당 볼륨은 로컬 호스트에 저장되고 도커 엔진에 의해 생성 & 삭제된다.

[root@localhost ~]# docker run -it \
> --name myvolume1 \
# myvolume(위에서 만든 도커 볼륨):/root(컨테이너 내 공유 디렉터리 지정)
> -v myvolume:/root/ \
> rockylinux/rockylinux # 이미지
# echo - 인자로 받은 문자열을 출력
# >> - 출력 리다이렉션 연산자로 파일 생성 / 수정 시 파일이 이미 존재한다면 해당 파일의 끝에 내용을 추가한다.
[root@320480a4fbe0 /]# echo hello world! >> /root/volume

[root@320480a4fbe0 /]# ls
bin  etc   lib    lost+found  mnt  proc  run   srv  tmp  var
dev  home  lib64  media       opt  root  sbin  sys  usr

[root@320480a4fbe0 /]# cd root

[root@320480a4fbe0 ~]# ls
anaconda-ks.cfg  anaconda-post.log  original-ks.cfg  volume

[root@320480a4fbe0 ~]# cat volume
hello world!

호스트 볼륨 방식과는 다르게 도커 볼륨 방식은 아래와 같이 입력한다.

 

호스트 볼륨 공유 방식: -v ${호스트의 공유 디렉토리 }:${컨테이너의 공유 디렉토리 }

도커 볼륨 방식: -v ${볼륨 이름 }:${컨테이너의 공유 디렉토리 }

 

도커 볼륨 방식은 볼륨을 컨테이너의 공유 디렉토리에 마운트한다. 따라서 myvolume1 컨테이너 내부의 /root 경로에 파일을 생성하면 생성된 파일은 myvolume 볼륨에 저장되는 것이다.

 

만약 같은 볼륨 컨테이너를 가진 컨테이너를 생성하면 파일이 어떻게 들어있을까?

[root@localhost ~]# docker run -it \
> --name myvolume2 \
> -v myvolume:/root/ \
> rockylinux/rockylinux

[root@2a9c1a285496 /]# ls root
anaconda-ks.cfg  anaconda-post.log  original-ks.cfg  volume

[root@2a9c1a285496 /]# cat root/volume
hello world!

생성된 컨테이너의 root 경로에 들어가면 myvolume1에서 생성했던 volume 텍스트 파일이 그대로 존재한다. 도커 볼륨은 여러 개의 컨테이너에 의해 공유할 수 있다는 것을 알 수 있다.

 

볼륨은 디렉터리 하나에 상응하는 단위로써 도커 엔진에서 관리한다. 도커 볼륨도 호스트 볼륨 공유와 마찬가지로 호스트에 저장함으로써 데이터를 보존하지만 파일이 실제로 어디에 저장되는지 사용자는 알 필요가 없다.

 

만약 알고 싶다면 아래와 같이 docker inspect 명령어를 사용해 알 수 있다.

[root@localhost ~]# docker inspect --type volume myvolume
[
    {
        "CreatedAt": "2024-07-01T08:05:31+09:00",
        "Driver": "local", # 볼륨이 사용하는 드라이버
        "Labels": null, # 볼륨 구분 라벨
        "Mountpoint": "/var/lib/docker/volumes/myvolume/_data", # 실제 호스트 저장 경로
        "Name": "myvolume", # 볼륨 이름
        "Options": null,
        "Scope": "local"
    }
]
✅ 도커 볼륨을 사용 중인 컨테이너를 삭제할 경우 볼륨은 삭제되지 않는다. 필요없는 볼륨들을 한꺼번에 지울 때는 `docker volume prune` 명령어를 통해 삭제 가능하다.

1. MYSQL 이미지 다운로드 및 컨테이너 생성

$ docker run -d \
$ --name wordpressdb \
$ -e MYSQL_ROOT_PASSWORD=${pwd } \
$ -e MYSQL_DATABASE=wordpress \
$ mysql:5.7

mysql 이미지를 가져와 데이터베이스 컨테이너를 생성한다.

  • --name - 컨테이너 이름
  • ${pwd } - 자기가 원하는 비밀번호를 입력한다.
  • MYSQL_DATABASE - 컨테이너 생성 시 만들어질 데이터베이스 이름
  • mysql:5.7 - mysql 버전

2. 1. Wordpress 이미지 다운로드 및 컨테이너 생성

$ docker run -d \
$ -e WORDPRESS_DB_HOST=mysql \
$ -e WORDPRESS_DB_USER=root \
$ -e WORDPRESS_DB_PASSWORD=${pwd } \
$ --name wordpress \
$ --link wordpress:mysql \
$ -p 80 \
$ wordpress

wordpress 이미지를 가져와 웹 서버 컨테이너를 생성한다.

  • -d - mysql, wordpress와 같이 백그라운드에서 실행되어야 하는 컨테이너 생성 시 붙이는 옵션으로 컨테이너 실행 시 터미널을 차지하지 않고 프로세스가 계속 실행된다.
  • -e - 컨테이너 내부의 환경변수를 설정하는 옵션으로 컨테이너화된 애플리케이션의 환경변수에서 값을 가져와 쓸 수 있다.(예를들어 ‘WORDPRESS_DB_PASSWORD=1234’는 환경변수의 값을 1234로 지정한다는 뜻 …)
  • WORDPRESS_DB_HOST - wordpress가 연결한 데이터베이스 호스트
  • WORDPRESS_DB_USER - 데이터베이스 사용자명
  • WORDPRESS_DB_PASSWORD - 데이터베이스 비밀번호
  • --link mysql:mysql - 위에서 만든 mysql 컨테이너를 mysql이라는 별칭으로 링크한다.(같은 호스트 내에 있는 컨테이너들끼리 통신할 수 있는 방법)
  • -p 80 - 호스트 포트 중 하나와 컨테이너의 80 포트와 연결된다.

‘docker ps -a’를 통해 확인하면 아래와 같이 출력된다.

CONTAINER ID   IMAGE                   COMMAND                  CREATED          STATUS          PORTS                                                                          NAMES
2753eeb5d79d   wordpress               "docker-entrypoint.s…"   12 minutes ago   Up 12 minutes   0.0.0.0:32768->80/tcp, :::32768->80/tcp                                        wordpress
2d5d3961e73b   mysql:5.7               "docker-entrypoint.s…"   17 minutes ago   Up 17 minutes   3306/tcp, 33060/tcp                                                            mysql

3. 웹 브라우저에서 확인하기

$ docker port wordpress
80/tcp -> 0.0.0.0:32768
80/tcp -> [::]:32768

위에서 생성한 wordpress에 바인딩된 포트만 확인하고 싶다면 docker port ${컨테이너명 } 을 통해 확인할 수 있다. 내 컨테이너는 호스트의 32768 포트와 연결됐기 때문에 url에 192.168.56.103:32768 을 입력하면 워드프레스 설치 화면이 정상적으로 나타난다.

 

4. 개념 설명

📌 -d

  • mysql, wordpress와 같이 백그라운드에서 실행되어야 하는 컨테이너 생성 시 붙이는 옵션으로 컨테이너 실행 시 터미널을 차지하지 않고 프로세스가 계속 실행된다.
  • mysql과 같이 백그라운드 실행으로 생성한 컨테이너는 기본적으로 컨테이너 내부에 들어갈 수 없다. 하지만 exec 명령어를 사용하면 컨테이너 내부의 셸을 사용할 수 있다.
    $ docker exec -it mysql bin/bash bash-4.2
    $ echo $MYSQL_ROOT_PASSWORD
    $ ${password }

📌 --link

$ --link wordpressdb:mysql
  • 내부 ip를 알 필요없이 컨테이너의 별명(alias)으로 접근하도록 설정할 수 있다.
  • wordpressdb 컨테이너를 mysql이라는 이름으로 설정함으로써 워드프레스 웹 서버 컨테이너는 wordpressdb의 IP를 몰라도 mysql이라는 호스트명으로 접근이 가능하다. ⭐
  • --link에 입력된 컨테이너가 실행 중이 아니거나 존재하지 않다면 --link를 적용한 컨테이너 또한 실행시킬 수 없다.
  • 현재 deprecated된 옵션이며 추후 삭제될 수 있다.
  • 도커 브리지(bridge) 네트워크를 사용하면 --link 옵션과 동일한 기능을 더욱 더 쉽게 사용할 수 있다.

1. 호스트 포트와 바인딩할 컨테이너 생성하기

Ubuntu

docker pull ubuntu:latest # ubuntu 최신 버전 다운로드
docker images # 다운로드한 이미지 확인

docker run -it --name ubuntu -p 80:80 ubuntu:latest

apt-get update
apt-get install apache2 -y # apache 설치
service apache2 start # apache 실행

 

Rocky Linux

docker pull rockylinux:9.3 # ubuntu 최신 버전 다운로드
docker images # 다운로드한 이미지 확인

docker run -it --privileged --name rockylinux -p 8081:80 rockylinux/rockylinux /usr/sbin/init

docker exec -it rockylinux /bin/bash # shell 모드 접속

yum install httpd
systemctl start httpd
systemctl status httpd
systemctl enable httpd
  • -i - interactive / 표준 입력을 활성화하여 사용자가 컨테이너와 상호 작용할 수 있게 도와준다.
  • -t - 가상 터미널을 할당하여 shell 모드 진입을 도와준다.
  • -d - 컨테이너를 백그라운드에서 실행시킨다.
  • --privileged - 컨테이너 내부 주요 자원에 접근할 수 있다.
  • --name - 컨테이너에 특정 이름을 부여한다.
  • -p
    • -p ${호스트 포트 }: ${컨테이너 포트}
    • 컨테이너의 포트를 호스트의 포트와 바인딩해 연결할 수 있는 속성
    • (예) -p 3307:3307 -p 192.168.56.103:80:80
    • 만약 여러 개의 포트를 외부에 개방하려면 -p 옵션을 여러 번 설정하면 된다.
  • exec - docker 컨테이너 내 특정 명령을 입력하거나 shell에 접근할 수 있다.
  • /usr/sbin/init - 컨테이너 띄울 때 함께 실행한다. 해당 옵션과 --privileged 옵션이 함께 사용되어야 systemctl 이 동작한다.

 

이후 모든 컨테이너들을 확인하면 아래와 같이 나타난다.

3. URL 접속

${호스트 PC(도커를 설치한 가상 서버)의 IPv4 }:${컨테이너 생성 시 바인딩한 port 번호 } 를 입력해주면 아래와 같이 출력된다.

 

Ubuntu - 192.168.56.103:80

 

Rocky Linux - 192.168.56.103:8081


참고 레퍼런스

https://coding-orca.tistory.com/7

1. docker 설치

yum install -y yum-utils # yum 패키지 매니저 관련 유틸리티 모음 설치

# 저장소 url을 추가하면 이후 도커 관련 패키지를 설치 / 업데이트할 수 있음
# 끌어쓴다? 는 느낌이 강하다..
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

yum install -y docker-ce # 도커 설치

systemctl start docker # 실행
systemctl status docker # 상태 확인

systemctl enable docker # 이후 재시작 시 도커 자동 실행 등록
systemctl enable containerd # 이후 재시작 시 컨테이너 자동 실행 등록

docker info # 설치된 도커 엔진 정보 확인

2. docker 실행 테스트

docker run hello-world

3. 일반 사용자로 docker 관리하기

groupadd docker # 원래부터 생성되어 있었다.
usermod -aG docker ${생성한 유저 아이디 } # docker 그룹에 해당 사용자를 추가한다.
newgrp docker # 그룹 변경 사항 활성화

su - ${생성한 유저 아이디 } # 계정 변경

4. centos 이미지 확인하기

su - ${생성한 유저 아이디 }
# centos 이미지 다운로드(from. docker hub) ...
# centos 이미지가 로컬 도커 엔진에 존재하지 않으므로 도커 허브에서 자동으로 이미지를 내려받는다!
docker pull centos

# centos 이미지로 컨테이너 생성 후 인터렉티브 모드로 bash 셸 실행
# 인터렉티브 모드 - 사용자 입력을 허용하고 터미널 출력이 바로 나타나는 모드
# docker run -it ~ - 컨테이너 생성 / 실행과 동시에 컨테이너 내부에 들어온다.
docker run -it centos bash 

# ls -l 등 여러 셸 명령어를 실행해본다.

# exit # 인터렉티브 모드 종료

docker container ls -a # 모든 도커 컨테이너 확인

+ Recent posts