
오늘 오전 30분은 없었습ㄴㅣ다....
잠깐의 자습시간을 조금 가지구..? 버추얼을 했구용
바로 들어가기~~
오늘의 순서
| 교시 | 무엇 |
|---|---|
| 2교시 | 경계 기반 보안의 한계 · Zero Trust · 5대 원칙 |
| 3교시 | 점수로 판단하는 함수 (PDP) |
| 4교시 | PDP 와 PEP · ZT 성숙도 점수 · A사 적용 방안 문서 |
| 5교시 | 왜 도커인가 · 이미지와 컨테이너 · 같이 설치하기 |
| 6교시 | nginx 서버 켜고 접속 기록 읽기 |
| 7교시 | 내 페이지 연결 · 두 서버 · 올리기 |
2교시 경계 기반 보안의 한계 · Zero Trust · 5대 원칙
오늘 들어온 일
A사 보안 담당이 보낸 메시지:
「월요일 아침 정산 포털 기록을 보니 새벽에 정산 내역이 대량으로 조회됐습니다. 방화벽은 아무것도 막지 않았습니다. 무슨 일이고 왜 통과됐나요?」
이 교시에서는 접근 기록에서 의심스러운 줄을 찾고, 5대 원칙의 어느 칸이 비어 있었는지 확인했다.
또 기록만으로 말할 수 있는 것과 없는 것을 구분했다.
왜 필요한가
지난주에 본 방화벽 · VPN 은 접속이 시작되는 곳에서 확인하는 방식이었다.
한 번 통과한 뒤에 그 계정이 무엇을 하는지는 따로 확인하지 않았다.
로그인이 성공한 요청 안에도 정상과 이상이 섞여 있기 때문에 어떤 항목을 확인해야 하는지 알아야 규칙으로 만들 수 있었다.
또 기록에 적힌 값과 그 값으로 추정한 내용을 구분해야 과장 없이 보고할 수 있었다.
2.1 경계 기반 보안과 Zero Trust
경계 기반 보안
회사 네트워크 안은 신뢰하고 밖은 의심하는 방식이다.
방화벽 · VPN 이라는 경계를 통과하면 내부 자원에 넓게 접근할 수 있다.
Zero Trust
누구도 기본적으로 신뢰하지 않고, 접근할 때마다 확인한다는 보안 원칙이다.
미국 NIST의 SP 800-207 문서에서 이 구조를 정리했다.
자원
보호할 대상을 뜻한다.
같은 포털 안에서도 정산 내역 조회와 계정 권한 변경은 서로 다른 자원이다.
횡이동
침입한 뒤 내부에서 다른 서버로 옮겨 가면서 공격 범위를 넓히는 것을 말한다.
경계 기반 보안에서는 내부 이동을 막기 어렵다.
| 기준 | 경계 기반 보안 | Zero Trust |
|---|---|---|
| 신뢰 전제 | 경계 안으로 들어오면 신뢰한다 | 안팎 구분 없이 기본적으로 신뢰하지 않는다 |
| 검증 시점 | 로그인할 때 한 번 | 접근할 때마다, 세션 중에도 반복 |
| 접근 범위 | 인증되면 내부에 넓게 접근 | 업무에 필요한 자원에만 최소한으로 접근 |
Zero Trust 가 모든 이상 행동을 막는 것은 아니다.
정상 계정 · 정상 기기 · 평소 시간에 자기 업무 시스템에서 자료 수만 건을 내려받는 경우처럼 접근 자체는 정상인데 행동이 이상할 수도 있다.
이런 경우는 접근을 허용할지 정하는 것만으로는 찾기 어렵고, 사용 기록을 계속 분석하는 이상탐지가 따로 필요하다.
실제 서비스로는 Cloudflare Access가 사내 앱마다 접근 정책을 걸고 사용자 신원과 기기 상태를 확인한다.
Microsoft Entra 조건부 액세스는 회사 기기가 보안 기준을 지킨 기기일 때만 접속하도록 정책을 만들 수 있다.
2.2 5대 원칙 — 요청 하나를 다섯 칸으로 읽기
Zero Trust 는 제품 하나가 아니라 확인할 항목의 묶음이다.
요청 하나를 다섯 칸으로 나누면 어느 부분이 부족했는지 확인할 수 있다.
ID
이 사용자가 진짜 본인인지 확인하는 항목이다.
MFA는 비밀번호 외에 휴대폰 승인 같은 인증 수단을 하나 더 확인하는 방식이다.
Device
접속하는 기기가 회사 보안 기준을 지키는지 확인한다.
등록된 기기인지, 백신 · 패치 상태가 괜찮은지 등을 확인한다.
Network
요청이 필요한 자원에만 접근하는지 확인한다.
마이크로 세그멘테이션은 네트워크를 작은 구역으로 나누고 구역 사이의 통신을 제한하는 방식이다.
Application
사용자가 해당 앱의 기능을 사용할 권한이 있는지 확인한다.
Policy
시간 · 위치 · 위험도에 따라 정책을 다르게 적용한다.
예를 들어 새벽 로그인이나 해외 IP 접속이라면 추가 인증을 요구할 수 있다.
| 칸 | 묻는 것 | 확인하는 예 | 대표 제품 |
|---|---|---|---|
| ID | 이 사용자가 진짜 본인인가 | 비밀번호 + MFA | Microsoft Entra ID |
| Device | 기기가 회사 기준을 지키나 | 등록 기기 · 백신 · 패치 · 디스크 암호화 | Microsoft Intune |
| Network | 필요한 자원에만 닿나 | 구역을 나누고 구역 사이 통신 제한 | 방화벽 구역 규칙 |
| Application | 이 앱의 권한이 있나 | 앱마다 따로 접근 정책 | Cloudflare Access |
| Policy | 상황에 따라 정책이 바뀌나 | 해외 IP · 새벽 로그인이면 추가 인증 | Microsoft Entra 조건부 액세스 |
문서마다 항목 이름이나 개수가 조금씩 다를 수 있지만 이번 과정에서는 위 다섯 가지로 구분했다.
요청 하나가 한 항목을 통과해도 다른 항목에서 막힐 수 있다.
어느 항목에서 문제가 생겼는지 확인하면 어디를 고쳐야 하는지도 알 수 있다.
A사 재무팀 junho의 정상적인 정산 조회를 다섯 항목으로 보면 다음과 같았다.
| 칸 | 이 요청에서 확인한 것 | 결과 |
|---|---|---|
| ID | 계정 junho, 로그인 방식 비밀번호 + MFA | 정상 |
| Device | 기기 NB-0522 는 junho 의 등록 기기이고 보안 상태 정상 | 정상 |
| Network | 사내 대역 10.20.3.14 에서 정산 포털로 접속 | 정상 |
| Application | junho 는 정산 내역(SETTLE) 담당 | 정상 |
| Policy | 낮 14시, 평소 조회량 12건 | 정상 |
2.3 기록으로 단정할 수 있는 것과 없는 것
확인된 사실은 기록에 실제로 적힌 값이다.
시각 · 계정 · 기기 · 출발 IP · 건수 · 결과 등이 여기에 해당했다.
추정은 기록을 보고 짐작한 내용이다.
예를 들어 「본인이 아닐 것이다」는 기록에 직접 적힌 값이 아니므로 추정이다.
출발 IP가 사내 대역이라는 것은 사내망에서 접속했다는 사실일 뿐, 그 기기가 회사 등록 기기라는 뜻은 아니다.
기기 등록 여부는 별도의 기록을 확인해야 한다.
「로그인 성공」이라는 기록에도 계정 확인, 비밀번호 확인, MFA 여부가 섞여 있기 때문에 무엇을 확인하고 성공한 것인지 따로 봐야 했다.
기록만으로 알 수 없는 것은 모른다고 쓰고, 어떤 자료를 추가로 확인해야 하는지 같이 적는 것이 중요했다.
2교시 한눈에
| 하려는 일 | 보는 곳 |
|---|---|
| 의심 줄 찾기 | 시각 · 기기 · 자원 · 건수 · 로그인 방식을 등록 정보와 같은 계정의 다른 줄에 대조 |
| 이상해 보이지만 정상인 줄 거르기 | 등록 정보의 설명 |
| 한 줄씩은 정상인데 모으면 이상한 줄 | 맡은 자원과 대조하고 같은 계정의 반복 횟수와 건수를 합산 |
| 요청이 어느 칸에서 걸리는지 | ID → Device → Network → Application → Policy 순서 |
| 정상 로그인인데 걸린다면 | Policy 칸 |
| 단정할 수 있는지 | 기록에 적힌 값만 사실, 나머지는 추정 |
3교시 점수로 판단하는 함수 (PDP)
오늘 들어온 일
A사 보안 담당이 보낸 메시지:
「2교시에 본 새벽 사건 같은 요청을 앞으로는 자동으로 거르고 싶습니다. 요청이 들어올 때마다 허용 · 추가 확인 · 거부를 정하는 규칙을 코드로 만들어 주세요.」
이번에는 점수로 판단하는 파이썬 함수를 읽고, 요청을 한꺼번에 판정했다.
왜 필요한가
로그인이 성공한 요청을 사람이 하나씩 읽기는 어렵다.
규칙을 코드로 만들면 모든 요청을 같은 기준으로 바로 판정할 수 있다.
이 규칙을 결정하는 부분이 PDP이다.
3.0 파일 만들기
이번부터 파일은 터미널에서 vim으로 만들었다.
먼저 위치를 확인했다.
pwd
결과가 network_zt 폴더가 아니라면 이동했다.
cd network_zt
vim으로 파일을 만드는 순서는 다음과 같았다.
| 순서 | 입력 | 하는 일 |
|---|---|---|
| ① | vim 파일이름 |
파일을 연다 |
| ② | i |
입력 모드로 들어간다 |
| ③ | 붙여넣기 | 코드를 붙여 넣는다 |
| ④ | Esc → :wq → Enter |
저장하고 나간다 |
키가 제대로 동작하지 않으면 한/영 상태를 확인하고 Esc를 먼저 누른 뒤 명령을 입력했다.
붙여넣기 과정에서 공백이 계속 밀리면 :q!로 저장하지 않고 나와 touch로 파일을 만든 뒤 VS Code에서 수정할 수도 있었다.
3.1 PDP 와 점수 판단
PDP
Policy Decision Point
요청을 받아 허용 · 추가 확인 · 거부를 결정하는 곳이다.
이번에는 파이썬 함수 하나가 이 역할을 했다.
필수 조건
하나라도 어긋나면 점수와 관계없이 바로 거부하는 조건이다.
예:
- 재직 중인 계정인지
- 등록된 기기인지
위험 점수
이상한 신호마다 점수를 더해 합계로 판단하는 방식이다.
0점에서 시작했다.
추가 확인
비밀번호만으로 부족할 때 인증을 한 번 더 요구하는 단계이다.
허용과 거부 사이의 단계라고 볼 수 있다.
최소권한
업무에 필요한 만큼만 권한을 주는 원칙이다.
더 중요한 자원일수록 기준도 더 엄격하게 정할 수 있다.
| 구분 | 정산 내역 조회 | 계정 권한 변경 |
|---|---|---|
| 필수 조건 | 재직 중 · 등록 기기 | 재직 중 · 등록 기기 · 역할이 account_admin |
| 기기 보안 상태 이상 | +40 | 3-1에서 정함 |
| MFA 안 함 | +20 | 3-1에서 정함 |
| 해외에서 접속 | +30 | 3-1에서 정함 |
| 새벽 접속 | +20 | 보지 않음 |
| 20건 이상 조회 | +30 | 보지 않음 |
| 결정 | 50점 이상 거부 · 20점 이상 추가확인 · 그 아래 허용 | 50점 이상 거부 · 10점 이상 추가확인 · 그 아래 허용 |
ACCOUNTS.get(req["user"])를 사용하면 계정 정보를 찾고 없을 경우 None을 받을 수 있었다.
reasons.append()로 판단 이유를 리스트에 추가했다.
return "거부", reasons처럼 값 두 개를 한 번에 리턴할 수도 있었다.
함수는 return을 만나면 바로 끝나기 때문에 필수 조건에서 거부되면 아래의 점수 계산은 실행되지 않았다.
3.2 판단 함수
ACCOUNTS = {
"seoyeon": {"employed": True, "device": "NB-0417", "role": "sales"},
"junho": {"employed": True, "device": "NB-0522", "role": "finance"},
"gaeun": {"employed": True, "device": "NB-0188", "role": "account_admin"},
"dami": {"employed": False, "device": "NB-0333", "role": "sales"},
}
정산 내역 조회 요청을 판단하는 함수는 다음 구조였다.
def decide_settlement(req):
"""정산 내역 조회 요청을 판단해 (결정, 이유 목록)을 리턴한다"""
account = ACCOUNTS.get(req["user"])
if account is None or not account["employed"]:
return "거부", ["재직 중인 계정이 아님"]
if req["device"] != account["device"]:
return "거부", ["이 계정에 등록된 기기가 아님"]
score = 0
reasons = []
if req["health"] != "ok":
score = score + 40
reasons.append("기기 보안 상태 이상 (+40)")
if req["country"] != "KR":
score = score + 30
reasons.append("해외에서 접속 (+30)")
if req["hour"] < 6:
score = score + 20
reasons.append("새벽 접속 (+20)")
if req["mfa"] == False:
score = score + 20
reasons.append("MFA 안 함 (+20)")
if req["count"] >= 20:
score = score + 30
reasons.append("대량 조회 (+30)")
if score >= 50:
return "거부", reasons
if score >= 20:
return "추가확인", reasons
return "허용", reasons
흐름은 다음과 같았다.
요청
|
v
[필수 조건 검사]
|
├─ 어긋남 → 거부
|
v
[위험 점수 계산]
|
├─ 50점 이상 → 거부
├─ 20점 이상 → 추가확인
└─ 그 외 → 허용
3.3 요청 한 건 따라가기
요청:
seoyeon
등록 기기 NB-0417
기기 상태 ok
국가 KR
시각 2시
MFA 안 함
40건 조회
| 단계 | 검사 | 요청의 값 | 결과 | 누적 점수 |
|---|---|---|---|---|
| 1 | 재직 중인 계정인가 | employed = True | 통과 | 0 |
| 2 | 등록 기기인가 | NB-0417 | 통과 | 0 |
| 3 | 기기 보안 상태 | ok | 0점 | 0 |
| 4 | 접속 국가 | KR | 0점 | 0 |
| 5 | 새벽인가 | 2시 | +20 | 20 |
| 6 | MFA 했는가 | False | +20 | 40 |
| 7 | 20건 이상 조회인가 | 40건 | +30 | 70 |
| 8 | 결정 | 70점 ≥ 50 | 거부 | 70 |
결과는 다음과 같았다.
(
"거부",
[
"새벽 접속 (+20)",
"MFA 안 함 (+20)",
"대량 조회 (+30)"
]
)
한 가지 신호만 보고 바로 거부하는 것이 아니라 여러 위험 신호가 겹칠수록 점수가 올라갔다.
모두 정상이면 0점이기 때문에 허용됐다.
3교시 한눈에
| 하려는 일 | 코드 |
|---|---|
| 요청의 계정 찾기 | ACCOUNTS.get(req["user"]) |
| 필수 조건으로 바로 거부 | return "거부", [이유] |
| 점수 쌓기 | score = score + 40 |
| 이유 추가 | reasons.append() |
| 합계로 결정 | 50점 이상 거부 · 20점 이상 추가확인 |
| 요청 목록 판정 | for req in REQUESTS: |
| 결정별 건수 세기 | counts[decision] = counts[decision] + 1 |
| 이유별 건수 세기 | .get(이유, 0) + 1 |
4교시 PDP 와 PEP · ZT 성숙도 점수 · A사 적용 방안 문서
오늘 들어온 일
A사 대표가 보낸 메시지:
「우리 회사의 지금 보안 수준이 어느 정도인지 점수로 알려 주시고, Zero Trust 도입 방안을 문서 하나로 정리해 주세요. 임원 보고에 씁니다.」
이번에는 PDP와 PEP의 역할을 구분하고 직원 정보를 이용해 Zero Trust 성숙도 점수를 계산했다.
왜 필요한가
3교시에서 만든 함수는 결정만 했다.
그 결정을 실제 요청에 적용해 통과시키거나 막는 역할도 필요했고, 그 역할이 PEP였다.
회사 보안 수준도 단순히 좋다 · 나쁘다로 말하는 것보다 점수로 나타내면 무엇부터 개선해야 하는지 확인하기 쉬웠다.
4.1 PDP 와 PEP
PEP
Policy Enforcement Point
PDP가 내린 결정을 실제로 적용하는 곳이다.
요청을 허용하거나 차단하고, 필요한 경우 추가 인증을 요구한다.
예:
- 프록시
- 방화벽
- API 게이트웨이
PDP와 PEP를 나누면 정책을 한 곳에서 수정해도 여러 PEP가 같은 규칙을 사용할 수 있었다.
결정 기록도 한 곳에 모을 수 있었다.
기본 거부
허용 목록에 없는 것은 모두 거부하는 방식이다.
최소권한은 기본적으로 아무 권한도 없는 상태에서 필요한 권한만 추가하는 방식으로 볼 수 있다.
사용자 --요청--> PEP --요청 정보--> PDP
^ |
| |
+--- 결정 -------+
허용 → 앱으로 전달
추가확인 → MFA 요구
거부 → 차단하고 로그 기록
이번에 만든 decide_settlement 함수가 PDP 역할을 했다.
4.2 ZT 성숙도 점수
성숙도는 회사가 Zero Trust를 어느 정도 도입했는지를 나타냈다.
이번에는 다섯 항목에 각각 20점을 주고 총 100점으로 계산했다.
| 항목 | 만점 | 계산 | 5대 원칙 |
|---|---|---|---|
| MFA 적용률 | 20 | MFA 사용 직원 ÷ 전체 직원 × 20 | ID |
| 기기 보안 점검 비율 | 20 | 점검 통과 기기 사용자 ÷ 전체 직원 × 20 | Device |
| 최소권한 준수율 | 20 | 초과 권한 없는 직원 ÷ 전체 직원 × 20 | Application |
| 마이크로 세그멘테이션 | 20 | 적용했으면 20, 아니면 0 | Network |
| 위험 기반 인증 | 20 | 적용했으면 20, 아니면 0 | Policy |
예를 들어 직원 12명 중 6명이 MFA를 사용한다면 다음처럼 계산할 수 있었다.
round(6 / 12 * 20)
결과는 10점이다.
4.3 파일 만들기
network_zt 폴더 안에서 vim으로 파일을 만들었다.
vim 파일이름
순서는 다음과 같았다.
vim 파일이름
→ i
→ 내용 입력
→ Esc
→ :wq
→ Enter
touch로 파일을 만들고 VS Code에서 작성하는 방법도 사용할 수 있었다.
저장한 내용은 cat으로 확인했다.
cat 파일이름
4교시 한눈에
| 하려는 일 | 방법 |
|---|---|
| 결정하는 곳 / 막는 곳 | PDP / PEP |
| 성숙도 점수 | 항목당 20점 × 5 |
| 허용 목록에 없는 것 | 기본 거부 |
| 새 파일 만들기 | vim 또는 touch |
| 저장 내용 확인 | cat 파일이름 |
5교시 왜 도커인가 · 이미지와 컨테이너 · 같이 설치하기
왜 필요한가
프로그램을 PC에 직접 설치하면 PC마다 버전과 설정이 달라질 수 있다.
삭제한 뒤에도 기존 파일이나 설정이 남을 수 있다.
이런 차이 때문에 「내 PC에서는 되는데 다른 PC에서는 안 된다」는 문제가 생긴다.
도커는 프로그램과 필요한 파일을 하나로 묶어서 어느 PC에서든 같은 환경으로 실행할 수 있게 해 준다.
[도커 없이]
PC-A: nginx 1.18
PC-B: nginx 1.24
PC-C: nginx 1.18 + 예전 파일
→ PC마다 환경이 다름
[도커 사용]
PC-A
PC-B → nginx:1.28 이미지 사용
PC-C
→ 같은 환경
5.1 도커 없이 설치하는 것과 도커로 실행하는 것
직접 설치하면 버전이나 설정이 서로 달라질 수 있다.
도커에서는 같은 이미지를 받아 실행하기 때문에 여러 PC에서 같은 환경을 만들 수 있었다.
컨테이너를 지울 때도 컨테이너 하나만 지우면 됐다.
5.2 이미지와 컨테이너 · Docker Hub · 엔진
이미지
실행할 프로그램과 필요한 파일을 하나로 묶어 둔 것이다.
이미지는 읽기 전용이다.
nginx:1.28
처럼 이름:태그 형태로 사용했다.
태그는 버전을 나타냈다.
컨테이너
이미지를 실제로 실행한 것이다.
같은 이미지로 여러 개의 컨테이너를 만들 수 있었다.
레지스트리
이미지를 모아 두고 내려받을 수 있게 해 주는 서버이다.
Docker Hub가 대표적인 공개 레지스트리이다.
이미지를 받을 때는 registry-1.docker.io 같은 이름을 DNS로 IP 주소로 바꾸고 인터넷을 통해 이미지를 내려받았다.
Docker Engine
이미지를 받고 컨테이너를 만들어 실행하는 도커의 핵심 프로그램이다.
데몬이라고도 부른다.
Windows에서는 Docker Desktop이 Docker Engine을 설치하고 실행했다.
Docker Desktop은 WSL 2 위에서 엔진을 실행했다.
Docker Client
터미널에서 입력하는 docker 명령이다.
사용자의 명령을 Docker Engine에 전달했다.
Docker Hub
|
| pull
v
nginx:1.28 이미지
|
├── 컨테이너 1
└── 컨테이너 2
이미지는 한 번 받아 두면 여러 컨테이너를 만들 수 있었다.
컨테이너를 삭제해도 이미지는 남아 있기 때문에 다음에 다시 사용할 수 있었다.
5.3 nginx 기반
이번 실습은 nginx:1.28 이미지를 사용했다.
nginx는 웹 서버 프로그램이다.
브라우저에서 요청이 들어오면 파일을 응답하고 접속 기록을 남긴다.
설정에 따라 다른 서버로 요청을 넘겨 주는 프록시 역할도 할 수 있다.
직접 서버 코드를 만들지 않아도 서버 실행 · 로그 확인 · 종료 등을 연습할 수 있었다.
5.4 Docker 설치
Windows에서는 Docker Desktop을 설치했다.
WSL 2가 없다면 다음 명령으로 설치할 수 있었다.
wsl --install
이미 설치되어 있다면 업데이트할 수도 있었다.
wsl --update
Docker Desktop을 실행한 뒤 Engine running 상태가 될 때까지 기다렸다.
설치 확인은 다음 명령으로 했다.
docker --version
첫 컨테이너는 다음 명령으로 실행했다.
docker run hello-world
이 명령을 실행하면 다음 순서로 동작했다.
docker 명령
|
v
Docker Engine
|
| 이미지가 없으면
v
Docker Hub
|
| 이미지 다운로드
v
Docker Engine
|
| 컨테이너 생성
v
hello-world 컨테이너
5교시 한눈에
| 하려는 일 | 명령 |
|---|---|
| 설치 확인 | docker --version |
| 첫 컨테이너 | docker run hello-world |
| 내려받은 이미지 | docker images |
| 컨테이너 기록 | docker ps -a |
| 이미지 이름 | 이름:태그 |
6교시 nginx 서버 켜고 접속 기록 읽기
왜 필요한가
서버를 켰는데 접속이 안 되는 경우에는 서버가 실제로 실행 중인지, 포트가 연결됐는지, 로그가 남았는지 순서대로 확인해야 한다.
이번에는 docker ps, docker logs, docker exec 등을 사용했다.
컨테이너는 기본적으로 외부에서 바로 접근할 수 없기 때문에 내 PC의 포트와 컨테이너의 포트를 연결해야 했다.
6.1 포트를 연결해야 접속이 된다
브라우저
http://127.0.0.1:8080
|
v
내 PC 8080
|
| -p 8080:80
v
컨테이너 nginx 80
nginx는 컨테이너 안의 80번 포트를 사용했다.
내 PC의 8080번으로 들어온 요청을 컨테이너의 80번으로 넘기도록 연결했다.
-p 내PC포트:컨테이너포트
이런 방식은 포트 포워딩이라고도 한다.
포트를 연결하지 않으면 컨테이너가 실행 중이어도 브라우저에서 접속할 수 없다.
주소 읽기
127.0.0.1
내 PC 자신을 가리킨다.
0.0.0.0
이 PC가 가진 모든 주소를 의미한다.
80/tcp
80번 포트를 TCP 방식으로 사용한다는 뜻이다.
6.2 접속 기록 한 줄 읽기
nginx는 요청을 처리할 때마다 로그를 한 줄씩 남겼다.
예:
172.17.0.1 - - [19/Oct/2026:14:31:05 +0000] "GET / HTTP/1.1" 200 615
| 부분 | 뜻 |
|---|---|
172.17.0.1 |
요청을 보낸 쪽의 주소 |
[19/Oct/2026:14:31:05 +0000] |
요청을 받은 시각 |
"GET / HTTP/1.1" |
요청 방식 · 주소 · 프로토콜 |
200 |
응답 번호 |
615 |
응답 크기 |
200은 요청이 성공했다는 뜻이다.
404는 요청한 파일이나 주소가 없다는 뜻이다.
서버를 실행한 직후 docker logs를 확인하면 접속 기록이 없을 수도 있다.
아직 브라우저에서 요청을 보내지 않았기 때문이다.
docker run
|
v
docker logs
→ 시작 메시지만 있음
|
v
브라우저 접속
|
v
docker logs
→ GET 요청 기록 생성
브라우저가 favicon.ico를 요청했는데 파일이 없으면 404가 나올 수 있었다.
이 경우는 정상적인 상황이라 넘어갔다.
새로고침 후 304가 나오기도 했다.
304는 바뀐 내용이 없으니 저장해 둔 화면을 그대로 사용하라는 의미이다.
6.3 Docker 명령
nginx 서버를 실행했다.
docker run -d --name web -p 8080:80 nginx:1.28
실행 중인 컨테이너를 확인했다.
docker ps
로그를 확인했다.
docker logs web
컨테이너 내부에 들어갔다.
docker exec -it web sh
나올 때는 다음 명령을 사용했다.
exit
컨테이너를 멈췄다.
docker stop web
멈춘 컨테이너를 삭제했다.
docker rm web
| 명령 | 하는 일 |
|---|---|
docker run -d --name web -p 8080:80 nginx:1.28 |
서버 실행 |
docker ps |
실행 중인 컨테이너 확인 |
docker logs web |
로그 확인 |
docker exec -it web sh |
컨테이너 안으로 들어가기 |
docker stop web |
컨테이너 정지 |
docker rm web |
컨테이너 삭제 |
-d는 컨테이너를 백그라운드로 실행했다.
--name은 컨테이너 이름을 지정했다.
-p는 포트를 연결했다.
-it는 컨테이너 안의 터미널을 직접 사용할 수 있게 했다.
6교시 한눈에
| 하려는 일 | 명령 |
|---|---|
| 서버 켜기 | docker run -d --name 이름 -p 내PC포트:컨테이너포트 이미지:태그 |
| 켜진 것 보기 | docker ps |
| 끈 것까지 보기 | docker ps -a |
| 로그 보기 | docker logs 이름 |
| 컨테이너 안에 들어가기 | docker exec -it 이름 sh |
| 컨테이너 끄기 | docker stop 이름 |
| 컨테이너 삭제 | docker rm 이름 |
| 실행 중인 상태로 삭제 | docker rm -f 이름 |
7교시 내 페이지 연결 · 두 서버 · 올리기
왜 필요한가
컨테이너 안에서 직접 수정한 파일은 컨테이너를 삭제하면 같이 사라진다.
웹 페이지의 index.html이나 설정 파일처럼 계속 보관해야 하는 파일은 내 PC에 두고 컨테이너가 읽도록 연결하는 것이 좋다.
서버를 여러 개 실행할 때는 서로 다른 포트를 사용하면 한 PC에서도 여러 서버를 띄울 수 있다.
읽기 전용으로 연결하면 컨테이너가 내 파일을 수정하지 못하게 할 수 있다.
내 폴더를 컨테이너에 연결
Git Bash에서는 다음과 같이 실행했다.
MSYS_NO_PATHCONV=1 docker run -d --name 이름 -p 포트:80 -v "$(pwd)/폴더:/usr/share/nginx/html:ro" nginx:1.28
-v를 사용해 내 PC의 폴더와 컨테이너의 폴더를 연결했다.
내 PC 폴더
|
| -v
v
/usr/share/nginx/html
|
v
nginx
:ro는 read only를 뜻했다.
컨테이너가 연결된 파일을 읽을 수는 있지만 수정하지는 못했다.
최소권한과 비슷한 방식으로 볼 수 있었다.
서버 두 대 실행
한 PC에서 서버를 두 개 실행하려면 내 PC 쪽 포트를 다르게 연결했다.
내 PC 8080 → 컨테이너 1의 80
내 PC 8081 → 컨테이너 2의 80
컨테이너 안의 nginx는 둘 다 80번을 사용할 수 있었다.
바깥에서 접근할 때 사용하는 내 PC의 포트만 다르게 지정했다.
브라우저에서는 다음처럼 각각 접근할 수 있었다.
http://127.0.0.1:8080
http://127.0.0.1:8081
여러 컨테이너를 한 번에 정리할 수도 있었다.
docker rm -f 이름1 이름2
7교시 한눈에
| 하려는 일 | 명령 |
|---|---|
| 내 폴더를 서버에 연결 | -v "$(pwd)/폴더:/usr/share/nginx/html:ro" |
| 컨테이너 안에서 바꾼 파일 | 컨테이너를 지우면 같이 사라짐 |
| 서버 두 대 실행 | 내 PC 포트를 8080 · 8081처럼 다르게 연결 |
| 읽기 전용 연결 | :ro |
| 한꺼번에 정리 | docker rm -f 이름1 이름2 |
오늘은 도커를 깔고 해보는 과정을 가졌는데, 오랜만에 해본 것 같아요
사실 딱 한번..? 두번 밖에 안해봤는데 자세하게 알려주셔서 그런가 좋았습니다.
오늘 사실 너무 피곤해요,,, 어제 어디 놀러갔다왔는데 진짜 피곤... 하지만 ?? 수업은 열심이 들었다...
그럼 아됴
'공부 > SKT ALEPH' 카테고리의 다른 글
| 네트워크·ZT(Zero Trust) 운영 기초 - 5 (0) | 2026.10.16 |
|---|---|
| 네트워크·ZT(Zero Trust) 운영 기초 - 4 (0) | 2026.10.15 |
| 네트워크·ZT(Zero Trust) 운영 기초 - 3 (0) | 2026.10.14 |
| 네트워크·ZT(Zero Trust) 운영 기초 - 2 (0) | 2026.10.13 |
| 네트워크·ZT(Zero Trust) 운영 기초 - 1 (0) | 2026.10.12 |


