카테고리 없음

SW 개발보안 7일차 - Docker

ajh 2026. 5. 18. 10:18

Centos7 도커 설치

 

1. 필요한 패키지 설치
[root@victim ~]# yum -y install yum-utils device-mapper-persistent-data lvm2

2. Docker CE 저장소 추가
[root@victim ~]# yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

3. Docker CE 설치
CentOS 7에서는 Docker CE 를 설치한다.
[root@victim ~]# vi /etc/yum.repos.d/CentOS-Vault.repo

# C7.9.2009
[C7.9.2009-base]
name=CentOS-7.9.2009 - Base
baseurl=http://vault.centos.org/7.9.2009/os/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[C7.9.2009-updates]
name=CentOS-7.9.2009 - Updates
baseurl=http://vault.centos.org/7.9.2009/updates/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[C7.9.2009-extras]
name=CentOS-7.9.2009 - Extras
baseurl=http://vault.centos.org/7.9.2009/extras/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[C7.9.2009-centosplus]
name=CentOS-7.9.2009 - CentOSPlus
baseurl=http://vault.centos.org/7.9.2009/centosplus/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1

[C7.9.2009-fasttrack]
name=CentOS-7.9.2009 - Fasttrack
baseurl=http://vault.centos.org/7.9.2009/fasttrack/$basearch/
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-CentOS-7
enabled=1  

 

패키지 설치
[root@victim ~]# yum -y install docker-ce docker-ce-cli containerd.io
[root@victim ~]# systemctl enable --now docker.service
[root@victim ~]# systemctl status docker.service

패키지 설명
  . docker-ce 
    - 도커 컨테이너를 빌드하고 실행하는 데 사용되는 기본 기술을 제공 (dockerd). 
  . docker-ce-cli           
    - 명령 줄 인터페이스 (CLI) 클라이언트 도커 도구 (docker) 제공
  . containerd.io  
    - 컨테이너 런타임 (runc)을 제공

 

외부에서 패킷을 받으면 자신이 받는게 아니고 컨네이너로 넘겨줘야 하므로 nat 테이블에 도커룰이 설정되어 있다.
[root@victim ~]# iptables -t nat -nL | grep -i Docker

 

도커 컨테이너 생성하기
docker 사이트에서 hello-world 이미지를 가져오고 컨테이너를 자동으로 생성한다.
[root@victim ~]# docker run hello-world

도커 이미지를 확인한다.
[root@victim ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED       SIZE
hello-world   latest    e2ac70e7319a   5 weeks ago   10.1kB

도커 컨테이너를 확인한다.
필드 설명
  . CONTAINER ID   
    - 컨테이너의 고유 ID(짧은 해시로 출력, 전체 해시의 앞 12자리만 표시됨)
  . IMAGE     
    - 컨테이너가 생성될 때 사용한 도커 이미지 이름
  . COMMAND    
    - 컨테이너가 시작될 때 실행된 명령어
  . CREATED        
    - 컨테이너가 만들어진 시간 (e.g.: 2 minutes ago, 5 days ago)
  . STATUS        
    - 컨테이너의 현재 상태
  . PORTS     
    - 컨테이너가 호스트와 연결한 포트 정보
  . NAMES
    - 도커가 자동 생성한 컨테이너 이름
    - 사용자가 컨테이너 이름을 설정하려면 --name 옵션을 사용한다.
[root@victim ~]# docker ps -a

방금 생성한 컨테이너를 삭제한다.
  . docker rm 명령어를 사용한다.
  . CONTAINER ID, NAMES 로 삭제할 수 있고 값은 모두 다르다. 
[root@victim ~]# docker rm 0f0

도커 컨테이너를 삭제한다.
  . docker rmi <image name>:<tag name>
[root@victim ~]# docker rmi hello-world:latest
[root@victim ~]# docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE


도커로 오라클 설치하기 

1. Oracle 11g 설치

[root@victim ~]# docker run -d --restart=always -p 1522:1521 \
--name oracle11gxe oracleinanutshell/oracle-xe-11g

 

2. Oracle 11g XE 접속

[root@victim ~]# docker exec -it oracle11gxe /bin/bash
root@899b1147e996:/# sqlplus system/oracle  // sqlplus sys/oracle as sysdba

 

3. 애플리케이션 접속
SQLDeveloper 접속 설정

Oracle sqldeveloper / DBever 를 사용해서 DBMS에 접속한다.
이때 터널링 설정을 하지 않았기 때문에 Host를 192.168.100.10로 접속한다.

왼쪽 위에 + 버튼을 클릭해서 접속 설정을 저장하고 sys 사용자로 접속한다.
접속 설정
  . 윈도우 관리자용 접속 설정
    - DockerLinuxOracle11g_sys
      NAME: DockerLinuxOracle11g_sys
      사용자 이름: sys
      롤: SYSDBA
      비밀번호: oracle
      비밀번호 저장: 체크
      호스트 이름: 192.168.100.10
      포트: 1522
      SID: xe

scott 사용자를 등록한다. 
왼쪽 위에 + 버튼을 클릭해서 접속 설정을 저장한다.

접속 설정
  . 윈도우 관리자용 접속 설정
    - DockerLinuxOracle11g_scott
      NAME: DockerLinuxOracle11g_scott
      사용자 이름: scott
      롤: 기본값
      비밀번호: tiger
      비밀번호 저장: 체크
      호스트 이름: 192.168.100.10
      포트: 1522
      SID: xe     


SSH 터널링을 이용한 Docker 의 Oracle DBMS 접속하기

1. 터널링의 기본 개념

Host OS에서 Oracle 서버 접속하기
  . SQLDeveloper 이용해서 접속한다.
    - Host OS에서 SQLDeveloper로 접속할 수 있도록 터널링을 설정한다.
    - 터널링 설정 전의 접속방법
    - [Host OS] 5000 -------------- 1521 [CentOS 7] ------ 1522 [Docker Oracle]
         .1     192.168.100.0/24            .10
         
    - 터널링 설정 후의 접속방법
    - [Host OS] 1522 -------------- [CentOS 7] ------ 1522 [Docker Oracle]
    - 1522/tcp 포트를 오픈하는 이유는 이미 1521/tcp 가 사용중이기 때문이다.
    

docker inspect: 컨테이너/이미지/볼륨/네트워크의 상세 정보 확인하는 명령어
[root@victim ~]# docker inspect oracle11gxe | grep IPAddress

            "SecondaryIPAddresses": null,
            "IPAddress": "172.17.0.2",
           =>       "IPAddress": "172.17.0.2",

도커에서 IP주소는 고정 IP주소가 아니다 

 

2. ssh 터널링 접속 설정
linuxadmin 사용자에서 터널링으로 접속할 사용자를 생성한다.
  . useradd: 리눅스에서 사용자를 생성하는 명령어
  . passwd: 비밀번호를 변경하는 명령어
[root@victim ~]# useradd -g users oracleuser
[root@victim ~]# passwd oracleuser

 

cmd에서

C:\Users\ITSC>del .ssh\known_hosts

C:\Users\ITSC>ssh -L 1522:172.17.0.2:1521 oracleuser@192.168.100.10 -f -N

 

sql developer에 호스트 이름을 localhost로 바꾸면 접속이 가능하다.

 

3. 실제 접속 설정
보안 강화를 위해서 아래 내용들을 설정해야 한다.
  . 사용자의 셸 수정
    - /bin/bash -> /bin/false
  . 백그라운 프로세스로 실행
    - -f -N 옵션 사용


Tomcat 설치하기

[root@victim ~]# systemctl stop tomcat.service

 

[root@victim ~]# docker run -d --restart=always \
-p 8080:8080 -p 8009:8009 \
-e LC_ALL=C.UTF-8 \
-v /home/tomcat:/usr/local/tomcat/webapps/ROOT/ \
--name tomcat9.0 \
tomcat:9.0


볼륨 마운트 
  . 컨테이너 생성을 할 때 Host OS에 /home/tomcat 이 Container의 /usr/local/tomcat/webapps/ROOT/ 와 연결되어 있다.

docker run 명령어의 옵션 
  . docker run 
    - local 에 이미지가 없으면 pull + run 명령어이다.
  . -d : 데몬 모드
  . -it : 입출력: 이미지를 다운받고 컨테이너 생성 후 컨테이너 안으로 들어간다 
  . -p 8080:8080 -p 8009:8009 
    - Host OS 포트와 컨테이너 포트를 동기화할 때 사용한다.
  . -e LC_ALL=C.UTF-8 : utf-8 한글입력 가능
  . -v /home/tomcat:/usr/local/tomcat/webapps/ROOT/  
    - Host OS 디렉터리와 컨테이너 디렉터리와의 파일을 동기화한다.
    - 웹 소스 파일을 컨터이너에서 빼서 Host OS의 디렉터리로 빼내는 작업을 한다.
    - 나중에 컨테이너를 삭제해도 Host OS의 소스 파일을 삭제되지 않는다.
  . --name tomcat9.0 : 컨테이너 이름 
  . tomcat:9.0 : 이미지 이름  

 

로그인해서 dockerps를 사용하기 위해서 자신의 홈디렉터리에 .bashrc 파일에 넣어준다.
[root@docker ~]# vi .bashrc
  :
alias dockerps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Names}}"'

 

2. jdbcdrivertest 
[root@victim ~]# cd /home/tomcat/
[root@victim tomcat]# vi jdbcdrivertest.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<%@ page import="java.sql.*" %>
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>::: Oracle JDBC 드라이버 테스트 :::</title>
</head>
<body>

<%
try {
    Class.forName("oracle.jdbc.OracleDriver");
    out.println("드라이버 로드 성공<br>");
} catch (ClassNotFoundException e) {
    out.println("드라이버 로드 실패<br>");
    e.printStackTrace(out);
}
%>

</body>
</html>


JDBC 드라이버가 없으면 드라이버 로드 실패 메세지가 출력된다.
http://192.168.100.10:8080/jdbcdrivertest.jsp

 

[root@victim tomcat]# docker exec -it tomcat9.0 bash

JDBC 드라이버가 없으면 드라이버 로드 실패 메세지가 출력된다.
http://192.168.100.10:8080/jdbcdrivertest.jsp

 

Docker인 경우
  . Docker 안에 JDBC 드라이버를 아래처럼 복사한다.
    - [Docker oracle11gxe] ---> [Docker tomcat9.0]
    - oracle11gxe: /u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar
    - tomcat9.0: /usr/local/tomcat/lib
    - Docker oracle11gxe에 오라클이 설치된 디렉터리에 JDBC 드라이버가 존재한다.
    - JDBC 드라이버를 Docker tomcat9.0의 [톰캣 디렉터리]/lib/ojdbc6.jar 로 복사한다.
    - Docker tomcat9.0을 재시한다.
[root@victim tomcat]# docker cp oracle11gxe:/u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar . <= 오라클 컨테이너에서 호스트로 복사
[root@victim tomcat]# docker cp ojdbc6.jar tomcat9.0:/usr/local/tomcat/lib <= 호스트에서 톰캣 컨테이너로 복사한걸 보냄
[root@victim tomcat]# docker restart tomcat9.0
  

  . Docker에서 파일 복사 방식
    - 호스트 -> 컨테이너: 복사 가능
    - 컨테이너 -> 호스트: 복사 가능
    - 컨테이너 -> 컨테이너: 복사 불가능
    - 컨테이너끼리 복사를 하면 에러가 발생한다.
      copying between containers is not supported  <-- 컨테이너 → 컨테이너 직접 복사는 지원되지 않는다.


JDBC 드라이버가 있으면 드라이버 로드 성공 메세지가 출력된다.
http://192.168.100.10:8080/jdbcdrivertest.jsp
드라이버 로드 성공


3. getemp.jsp 작성

[root@victim tomcat]# vi getemp.jsp
<%--
파일명: getemp.jsp
프로그램 설명: EMP 테이블의 내용을 출력한다.
--%>

<%@ page import="java.sql.DriverManager"%>
<%@ page import="java.sql.ResultSet"%>
<%@ page import="java.sql.Statement"%>
<%@ page import="java.sql.Connection"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%!
  // 변수 선언
  //String dbhost = "jdbc:oracle:thin:@oracle11gxe:1521:XE";
  String dbhost = "jdbc:oracle:thin:@192.168.100.10:1521:XE";
  String dbuser = "scott";
  String dbpass = "tiger";
  String query = "SELECT * FROM EMP";
  String title = "::: EMP 테이블 출력하기 :::";

  Connection conn = null;
  Statement stmt = null;
  ResultSet rs = null;
%>

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title> <%=title%> </title>
  <style>
    table {
      margin-left:auto;
      margin-right:auto;
      width: 800px;
      border: 1px solid #444444;
      border-collapse: collapse;
    }    
    tr, td { 
      border: 1px solid #444444;
      padding: 7px;
    }    
   </style>
</head>
<body>

<table>

<tr align="center">
  <td align="center" colspan="8"> 테이블명: EMP (사원 테이블) </td>
</tr>

<tr align="center" bgcolor="#ffffff">
  <td align="center"> 사원 번호 </td>
  <td align="center"> 사원 이름 </td>
  <td align="center"> 사원 직책 </td>
  <td align="center"> 직속 상관 </td>
  <td align="center"> 입사일 </td>
  <td align="center"> 급여 </td>
  <td align="center"> 추가 수당 </td>
  <td align="center"> 부서 번호 </td>
</tr>

<%
      try {
          Class.forName("oracle.jdbc.driver.OracleDriver");
      } catch (ClassNotFoundException e ) {
            e.printStackTrace();
        }
        try {
            conn = DriverManager.getConnection(dbhost, dbuser, dbpass);
            stmt = conn.createStatement();
            rs = stmt.executeQuery(query);

            // EMP 테이블에서 자료가 없을 때 까지 출력한다.
            while (rs.next())
            {
                // EMP 테이블에서 자료를 가져오는 부분
                String EMPNO    = rs.getString("EMPNO");  // 컬럼명으로 가져오기
                String ENAME    = rs.getString("ENAME");
                String JOB      = rs.getString("JOB");
                String MGR      = rs.getString(4);  // 컬럼의 순서로 가져오기
                String HIREDATE = rs.getString(5);
                String SAL      = rs.getString(6);
                String COMM     = rs.getString(7);
                String DEPTNO   = rs.getString(8);

                // 가져온 데이터를 브라우저에 출력하는 부분
                out.print("<tr align=\"center\" bgcolor=\"#ffffff\">" +
                          "<td>" + EMPNO    + "</td>" +
                          "<td>" + ENAME    + "</td>" +
                          "<td>" + JOB      + "</td>" +
                          "<td>" + MGR      + "</td>" +
                          "<td>" + HIREDATE + "</td>" +
                          "<td>" + SAL      + "</td>" +
                          "<td>" + COMM     + "</td>" +
                          "<td>" + DEPTNO   + "</td>" +
                          "</tr>");
            }
        } catch ( Exception e ) {
            e.printStackTrace();
        } finally {
            try {
                rs.close();
                stmt.close();
                conn.close();
            } catch ( Exception e ) {
                e.printStackTrace();
            }
        }
%>
</table>
</body>
</html>

4. 접속
emp 테이블의 내용이 출력된다.
http://192.168.100.10:8080/getemp.jsp

 


Docker network

IP주소를 172.17.0.0으로 잡으면 도커에 실행 순서에 따라 IP주소가 바뀌기 때문에 접속이 안될수도 있다

그래서 네트워크를 만들어 고정 IP처럼 사용한다


서버의 네트워크를 확인한다.
[root@victim ~]# ip -br -4 a

 

사용자 정의 네트워크 생성(고정 IP용)
명령어 설명
  . docker network create
    - Docker에 새 네트워크를 생성한다.
    - 생성된 네트워크는 이후 docker run --network mynetwork ... 형태로 컨테이너를 붙일 때 사용한다.
  . --driver bridge
    - 네트워크 드라이버를 bridge로 지정한다.
    - 한 대의 서버(단일 호스트)에서 컨테이너끼리 통신시키는 가장 일반적인 방식이다.
    - 이 네트워크를 생성하면 리눅스에 브리지 인터페이스(br-<네트워크ID>)가 새로 생성된다.
    - br-<네트워크ID> 에서 네트워크ID는 랜덤으로 생성되므로 각자 다르다.
  . --subnet 172.30.10.0/24
    - 네트워크에서 사용할 IP 대역(서브넷) 을 지정한다.
    - /24는 대역 크기(네트워크 주소: 172.30.10.0, 브로드캐스트주소: 172.30.10.255)
    - 컨테이너에 할당 가능한 범위(Host OS: 172.30.10.1, 172.30.10.2 ~ 172.30.10.254)
    - Docker는 이 범위 내에서 컨테이너에 IP를 자동 할당하거나(동적), 고정으로 사용할 수 있다.
    - --ip 옵션을 이용해서 컨테이너에 고정으로 IP주소를 할당할 수 있다.
  . --gateway 172.30.10.1
    - 해당 서브넷에서 게이트웨이 IP(기본 라우터 역할)를 지정한다.
    - Docker가 만든 브리지 인터페이스가 이 주소를 할당 받는다.
    - 컨테이너 관점에서는 172.30.10.1이 “기본 게이트웨이”가 되어서 NAT 통신을 한다.
  . mynetwork 
    - 생성될 네트워크의 이름으로 자유롭게 설정할 수 있다. 

 

[root@victim ~]# docker network create --driver bridge \
--subnet 172.30.10.0/24 \
--gateway 172.30.10.1 \
mynetwork

[root@victim ~]# docker network ls
NETWORK ID     NAME        DRIVER    SCOPE
...
8457b1611de4   mynetwork   bridge    local

 

도커 네트워크가 생성되면 인터페이스가 1개 생성된다.
[root@victim ~]# ip -br -4 a

자세한 정보를 보려면 아래 inspect 를 사용한다.
[root@victim ~]# docker network inspect mynetwork

 

[root@victim ~]# docker run -d --name mariadb \
--restart=always \
--network mynetwork --ip 172.30.10.12 \
-e MARIADB_ROOT_PASSWORD=1 \
-e MARIADB_DATABASE=mydb \
-v mariadb_data:/var/lib/mysql \
mariadb:10.11

 

명령어 옵션 설명
Docker가 재실행되어도 컨테이너가 재시작하는 방법
  . 신규 컨테이너 생성 시 적용하는 방법
    -  형식: docker run ... --restart=always ...
    - e.g.) docker run -d --restart=always --name apache_web_server -p 80:80 httpd

  . 이미 실행중인 컨테이너에 적용하는 방법
    - 형식: docker update --restart=always <컨테이너명>
    - e.g.) docker update --restart=always apache_web_server  

명령어 및 옵션 설명
  . docker run
    - 새 컨테이너를 생성하고 실행한다. 
    - 이미지가 없는 경우: 도커 레지스트리에서 다운로드 받는다.
    - 이미지가 있는 경우: 다운 받은 이미지에서 컨테이너 인스턴스로 생성한다
   . -d
    - Detached 모드: 터미널을 점유하지 않고 백그라운드에서 실행하는 데몬모드이다.
    - 실행 후 컨테이너 ID만 출력되고 프롬프트로 돌아온다.
   . --name mariadb 
    - 컨테이너 이름을 mariadb로 설정한다.
    - 컨테이너에 이름을 설정하면 컨테이너의 이름으로 제어할 수 있다.
      e.g.) docker logs mariadb, docker exect -it mariadb ... 
    - 이 옵션이 없을 경우 랜덤으로 자동 생성된다.
    - 사용자 정의 네트워크에서는 이 이름이 내부 DNS 이름으로도 동작하기 때문에 
      다른 컨테이너에서 mariadb:3306 으로 접근이 가능하게 된다.
   . --restart=always 
    - 서버가 부팅되거나 Docker를 재실행해도 컨테이너가 시작된다.
   . --network mynetwork 
    - 컨테이너를 Docker 네트워크 mynetwork에 연결한다.
    - 같은 네트워크에 있는 컨테이너끼리는 사설 IP 주소로 직접 통신이 가능하다.

  . --ip 172.30.10.12 
    - mynetwork에서 이 컨테이너에 할당될 고정 IPv4 주소를 설정한다.
    - 전제 조건
      mynetwork가 172.30.10.0/24 같은 서브넷으로 만들어져 있어야 한다.
      해당 IP주소가 다른 컨테이너/장비와 중복되면 충돌된다.
      
  . -e MARIADB_ROOT_PASSWORD=1 
    - 컨테이너 내부 초기화 스크립트에 전달되는 환경 변수이다.
    - MariaDB 공식 이미지의 엔트리포인트가 "데이터 디렉터리가 비어있을 때(첫 실행)" 이 값을 사용해서 root 계정 비밀번호를 설정한다.
    - 여기서는 연습용이므로 1로 설정하는 것이지만 실제로 사용할 때는 강한 비밀번호를 설정하기를 권장한다.
  . -e MARIADB_DATABASE=mydb 
    - 첫 초기화 시 mydb 데이터베이스를 자동으로 생성한다.
    - 이것도 "첫 초기화(빈 데이터 디렉터리)" 시점에만 적용되는 것이 일반적이다.
   . -v mariadb_data:/var/lib/mysql 
    - 볼륨 마운트로 컨터이너에 있는 DB를 Host OS로 연결한다. (영구 저장)
    -v mariadb_data:/var/lib/mysql 

    - 호스트 디렉터리를 사용할 때 
      절대 경로: /var/test/mysql 설정하면 /var/test/에 mysql 디렉터리가 생성된다. 
      [root@docker ~]# ls -ld /var/test/mysql/
      drwxr-xr-x. 6 systemd-coredump systemd-coredump 4096 Dec 17 10:51 /var/test/mysql/

      상대 경로: mariadb_data 만들면 /var/lib/docker/volumes/ 디렉터리에 mariadb_data 디렉터리가 생성된다.
      [root@docker ~]# ls -ld /var/lib/docker/volumes/mariadb_data/
      drwx-----x. 3 root root 19 Dec 17 09:49 /var/lib/docker/volumes/mariadb_data/

  . mariadb:10.11    
    - 사용할 이미지와 태그이다.

 

[root@victim ~]# systemctl stop mariadb
[root@victim ~]# docker exec -it mariadb bash

 

패스워드를 설정해뒀기 때문에 mysql 실행시 -p로 패스워드를 입력한다
root@bad3157cc7fc:/# mysql -p1

 

root@633766df7780:/# cd
cat << EOF > .my.cnf
[client]
host = localhost
user = root
password = 1
EOF


wordpress 설치

 


[root@victim ~]# systemctl stop httpd.service

[root@victim ~]# docker run -d --name wordpress \
--restart=always \
--network mynetwork --ip 172.30.10.13 \
-p 80:80 \
-e WORDPRESS_DB_HOST='172.30.10.12:3306' \
-e WORDPRESS_DB_USER='root' \
-e WORDPRESS_DB_PASSWORD=1 \
-e WORDPRESS_DB_NAME='mydb' \
-v wp_data:/var/www/html \
 wordpress:latest


도커 네트워크를 이용해 고정 IP주소로 설정하기

--조건 --
1. Docker network 생성
  . tomcatoracle
    - 172.17.20.0/24 

 

[root@ docker ~]# docker network create --driver bridge \
--subnet 172.20.20.0/24 \
--gateway 172.20.20.1 \
tomcatoracle


2.  Oracle 11gXE
  . 컨테이너 이름
    - oracle11gxe
    - IP Address: 172.20.20.2/24

 

[root@docker ~]# docker run -d --restart=always -p 1521:1521 \
--network tomcatoracle --ip 172.20.20.2 \  <= 고정 네트워크 할당
--name oracle11gxe oracleinanutshell/oracle-xe-11g

 

[root@docker ~]# dnf -y install wget
[root@docker ~]# wget linuxmaster.net/scott.sql.txt
[root@docker ~]# docker cp scott.sql.txt oracle11gxe:/root

[root@docker ~]# docker exec -it oracle11gxe bash
root@f23d35c94108:/# sqlplus sys/oracle as sysdba
SQL> @/root/scott.sql.txt
SQL> exit
root@f23d35c94108:/# exit


3. Tomcat
  . 컨테이너 이름
    - tomcat9
    - IP Address: 172.20.20.3/24

 

[root@ docker ~]# docker run -d --restart=always \
--network tomcatoracle --ip 172.20.20.3 \
-p 8080:8080 -p 8009:8009 \
-e LC_ALL=C.UTF-8 \
-v /home/tomcat:/usr/local/tomcat/webapps/ROOT/ \
--name tomcat9.0 \
tomcat:9.0


4. JDBC 드라이버 연동

[root@docker ~]# docker cp oracle11gxe:/u01/app/oracle/product/11.2.0/xe/jdbc/lib/ojdbc6.jar .
[root@docker ~]# docker cp ojdbc6.jar tomcat9.0:/usr/local/tomcat/lib
[root@docker ~]# docker restart tomcat9.0


5. getemp.jsp 

[root@ docker tomcat]# vi getemp.jsp
<%--
파일명: getemp.jsp
프로그램 설명: EMP 테이블의 내용을 출력한다.
--%>

<%@ page import="java.sql.DriverManager"%>
<%@ page import="java.sql.ResultSet"%>
<%@ page import="java.sql.Statement"%>
<%@ page import="java.sql.Connection"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<%!
  // 변수 선언
  String dbhost = "jdbc:oracle:thin:@oracle11gxe:1521:XE";
  String dbuser = "scott";
  String dbpass = "tiger";
  String query = "SELECT * FROM EMP";
  String title = "::: EMP 테이블 출력하기 :::";

  Connection conn = null;
  Statement stmt = null;
  ResultSet rs = null;
%>

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title> <%=title%> </title>
  <style>
    table {
      margin-left:auto;
      margin-right:auto;
      width: 800px;
      border: 1px solid #444444;
      border-collapse: collapse;
    }    
    tr, td { 
      border: 1px solid #444444;
      padding: 7px;
    }    
   </style>
</head>
<body>

<table>

<tr align="center">
  <td align="center" colspan="8"> 테이블명: EMP (사원 테이블) </td>
</tr>

<tr align="center" bgcolor="#ffffff">
  <td align="center"> 사원 번호 </td>
  <td align="center"> 사원 이름 </td>
  <td align="center"> 사원 직책 </td>
  <td align="center"> 직속 상관 </td>
  <td align="center"> 입사일 </td>
  <td align="center"> 급여 </td>
  <td align="center"> 추가 수당 </td>
  <td align="center"> 부서 번호 </td>
</tr>

<%
      try {
          Class.forName("oracle.jdbc.driver.OracleDriver");
      } catch (ClassNotFoundException e ) {
            e.printStackTrace();
        }
        try {
            conn = DriverManager.getConnection(dbhost, dbuser, dbpass);
            stmt = conn.createStatement();
            rs = stmt.executeQuery(query);

            // EMP 테이블에서 자료가 없을 때 까지 출력한다.
            while (rs.next())
            {
                // EMP 테이블에서 자료를 가져오는 부분
                String EMPNO    = rs.getString("EMPNO");  // 컬럼명으로 가져오기
                String ENAME    = rs.getString("ENAME");
                String JOB      = rs.getString("JOB");
                String MGR      = rs.getString(4);  // 컬럼의 순서로 가져오기
                String HIREDATE = rs.getString(5);
                String SAL      = rs.getString(6);
                String COMM     = rs.getString(7);
                String DEPTNO   = rs.getString(8);

                // 가져온 데이터를 브라우저에 출력하는 부분
                out.print("<tr align=\"center\" bgcolor=\"#ffffff\">" +
                          "<td>" + EMPNO    + "</td>" +
                          "<td>" + ENAME    + "</td>" +
                          "<td>" + JOB      + "</td>" +
                          "<td>" + MGR      + "</td>" +
                          "<td>" + HIREDATE + "</td>" +
                          "<td>" + SAL      + "</td>" +
                          "<td>" + COMM     + "</td>" +
                          "<td>" + DEPTNO   + "</td>" +
                          "</tr>");
            }
        } catch ( Exception e ) {
            e.printStackTrace();
        } finally {
            try {
                rs.close();
                stmt.close();
                conn.close();
            } catch ( Exception e ) {
                e.printStackTrace();
            }
        }
%>
</table>
</body>
</html>

>scp jumsu.war admin@192.168.100.23:

[root@ docker ~]# cp /home/admin/jumsu.war .

 

혹은 tomcat 메니저로 war 파일을 올린다

 

root@1806de65757a:/usr/local/tomcat# apt update
root@1806de65757a:/usr/local/tomcat# apt -y install vim

root@1806de65757a:/usr/local/tomcat# vi conf/tomcat-users.xml
...
  <user username="role1" password="<must-be-changed>" roles="role1"/>
-->
  <role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <role rolename="manager-status"/>
  <role rolename="admin-gui"/>
  <user username="admin" password="tomcat1234" roles="manager-gui,manager-script,manager-status,admin-gui"/>
</tomcat-users>
 
root@1806de65757a:/usr/local/tomcat# cp -r /usr/local/tomcat/webapps.dist/manager /usr/local/tomcat/webapps/

manager는 기본적으로 localhost에서만 접속 가능하도록 제한되어 있기 때문에 192.168.100.1에서 접속할 수 있도록 

설정한다.


root@1806de65757a:/usr/local/tomcat# vi /usr/local/tomcat/webapps/manager/META-INF/context.xml
<Context antiResourceLocking="false" privileged="true" >
  <CookieProcessor className="org.apache.tomcat.util.http.Rfc6265CookieProcessor"
                   sameSiteCookies="strict" />
  <Valve className="org.apache.catalina.valves.RemoteCIDRValve"
         allow="127.0.0.0/8,::1/128,192.168.100.1/24" />
  <Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>

root@1806de65757a:/usr/local/tomcat# exit

Rocky Linux 9 Docker Host에서 실행한다.
[root@docker ~]# docker restart tomcat9.0
http://192.168.100.24:8080/manager/html

 

애플리케이션 접속
SQLDeveloper 접속 설정
Oracle sqldeveloper / DBever 를 사용해서 DBMS에 접속한다

 

SQLDeveloper 로 접속한 후 아래 JUMSU DB를 생성한다.

DROP TABLE JUMSUMEMBER PURGE;
CREATE TABLE JUMSUMEMBER
(
    NO NUMBER(4), -- 번호
    USERNAME VARCHAR2(30) NOT NULL,  -- 회원명
    USERID VARCHAR2(30) NOT NULL,  -- 아이디
    USERPW VARCHAR2(20) NOT NULL,  -- 비밀번호
    REGDATE DATE,  -- 가입날짜
        CONSTRAINT JUMSUMEMBER_NO_PK PRIMARY KEY(NO),
        CONSTRAINT JUMSUMEMBER_USERID_UK UNIQUE(USERID)
);

-- JUMSUMEMBER_SEQ 시퀀스 생성
DROP SEQUENCE JUMSUMEMBER_SEQ;
CREATE SEQUENCE JUMSUMEMBER_SEQ;

-- JUMSUMEMBER 테이블 코멘트 설정
COMMENT ON TABLE JUMSUMEMBER IS '회원테이블';
COMMENT ON COLUMN JUMSUMEMBER.NO IS '번호';
COMMENT ON COLUMN JUMSUMEMBER.USERNAME IS '회원명';
COMMENT ON COLUMN JUMSUMEMBER.USERID IS '아이디';
COMMENT ON COLUMN JUMSUMEMBER.USERPW IS '비밀번호';
COMMENT ON COLUMN JUMSUMEMBER.REGDATE IS '가입날짜';

-- JUMSU 테이블 생성
DROP TABLE JUMSU PURGE;
CREATE TABLE JUMSU
(
    NO NUMBER(4) NOT NULL, -- 번호
    USERID VARCHAR2(30) NOT NULL,  -- 이름
    KOR NUMBER(3) NOT NULL,  -- 국어점수
    ENG NUMBER(3) NOT NULL,  -- 영어점수
    MATH NUMBER(3) NOT NULL,  -- 수학점수
    TOTAL NUMBER(3) NOT NULL,  -- 총점
    AVERAGE NUMBER(5,2) NOT NULL,  -- 평균
    REGDATE DATE,  -- 저장날짜
    CONSTRAINT JUMSU_NO_PK PRIMARY KEY(no),
    CONSTRAINT JUMSU_USERID_FK FOREIGN KEY(userid) REFERENCES JUMSUMEMBER(userid)
);

-- JUMSUMEMBER 시퀀스 생성
DROP SEQUENCE JUMSU_SEQ;
CREATE SEQUENCE JUMSU_SEQ;

-- JUMSU 테이블 코멘트 설정
COMMENT ON TABLE JUMSU IS '점수테이블';
COMMENT ON COLUMN JUMSU.NO IS '번호';
COMMENT ON COLUMN JUMSU.USERID IS '아이디';
COMMENT ON COLUMN JUMSU.KOR IS '국어점수';

COMMENT ON COLUMN JUMSU.ENG IS '영어점수';
COMMENT ON COLUMN JUMSU.MATH IS '수학점수';
COMMENT ON COLUMN JUMSU.TOTAL IS '총점';
COMMENT ON COLUMN JUMSU.AVERAGE IS '평균';
COMMENT ON COLUMN JUMSU.REGDATE IS '가입날짜';

-- 코멘트 조회
--SELECT * FROM ALL_COL_COMMENTS WHERE TABLE_NAME = 'JUMSUMEMBER';
--SELECT * FROM ALL_COL_COMMENTS WHERE TABLE_NAME = 'JUMSU';

-- 시퀀스 조회
--SELECT * FROM USER_SEQUENCES;

 

이클립스를 실행해서 DB 접속 부분을 docker 로 수정해야 한다. 
<%--
dbConnect.jsp
DB 접속 정보
--%>

<%
//String dbhost = "jdbc:oracle:thin:@localhost:1521:XE";  
String dbhost = "jdbc:oracle:thin:@oracle11gxe:1521:XE";
String dbuser = "scott";
String dbpass = "tiger";
%>


Nginx Reverse Proxy로 Tomcat 8080 연결하기

요청 흐름
  . 사용자 브라우저 -> 192.168.100.24:80 -> nginx-proxy 컨테이너 -> tomcat9.0:8080 -> Tomcat 웹 애플리케이션 실행

 

프록시와 리버스프록시 차이
  . 리버스 프록시는 외부 요청의 "입구(게이트웨이)" 역할을 하면서 내부 서비스로 안전하게 라우팅해주는 프론트 서버이다.
  . 정방향(Forward) 프록시: 내부 사용자가 외부로 나갈 때 프록시 서버가 받아서 대신 나간다.
    e.g.) 회사에서 직원 PC → 프록시 서버 → 인터넷, iptables의 SNAT를 생각하자
  . 리버스(Reverse) 프록시: 외부 사용자가 내부 서버로 들어올 때 앞에서 프록시 서버가 대신 받는다.
    e.g.) 인터넷 사용자 → Nginx → 내부의 WordPress/DB/, iptables DNAT를 생각하자.

 

[root@docker ~]# mkdir -p /root/nginx-proxy/conf.d
[root@docker ~]# vi /root/nginx-proxy/conf.d/tomcat.conf
server {
    listen 80;
    server_name 192.168.100.24;

    location / {
        proxy_pass http://tomcat9.0:8080;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

[root@docker ~]# docker run -d \
--name nginx-proxy \
--network tomcatoracle \
--ip 172.20.20.4 \
-p 80:80 \
-v /root/nginx-proxy/conf.d:/etc/nginx/conf.d:ro \
nginx

 

nginx-proxy 컨테이너 안에서 Nginx 설정 검사를 실행한다.
[root@docker ~]# docker exec nginx-proxy nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok    <-- Nginx 설정 파일 문법이 정상
nginx: configuration file /etc/nginx/nginx.conf test is successful  <-- 설정 파일 전체 검사가 성공