✅ 포트 번호란?
포트는 065,535번까지 사용할 수 있으며, 01023번 포트는 주요 통신을 위한 규약에 따라 미리 정해진 포트입니다.
포트 번호 용도
22번 원격 접속을 위한 포트 번호 (ex. EC2 인스턴스 연결 시)
80번 HTTP 통신 시 사용
443번 HTTPS 통신 시 사용
Docker는 컨테이너 기술을 기반으로 프로그램을 각각 분리된 환경에서 실행 및 관리할 수 있는 툴입니다.
하나의 컴퓨터 환경 내에서 독립적인 컴퓨터 환경을 구성해서, 각 환경에 프로그램을 별도로 설치할 수 있도록 만든 개념입니다.
프로그램의 설치 과정, 설정, 버전 정보 등을 포함하고 있는 패키지입니다.

docker pull 이미지명 # 이미지 다운로드
docker image ls # 다운로드된 이미지 목록 확인
docker pull 이미지명:버전명 # 특정 버전 이미지 다운로드
docker image rm 이미지ID # 이미지 삭제 (일부 ID 입력도 가능)
docker image rm -f 이미지ID # 강제 삭제 (중단된 컨테이너 이미지까지)
docker image rm $(docker images -q) # 사용하지 않는 이미지 일괄 삭제
docker create 이미지명 # 이미지로부터 컨테이너 생성
docker run -d 이미지명 # 컨테이너 실행 (백그라운드)
docker run -d --name my-web-server nginx # 이름 지정 실행
docker rm -f 컨테이너명 # 실행 중인 컨테이너 중지 및 삭제
docker rm $(docker ps -qa) # 중지된 모든 컨테이너 삭제
docker ps # 실행 중인 컨테이너 확인
docker ps -a # 중지된 것 포함 전체 확인
docker start 컨테이너ID # 컨테이너 시작
docker stop 컨테이너ID # 컨테이너 중지
docker kill 컨테이너ID # 강제 중지
docker logs 컨테이너ID # 전체 로그
docker logs --tail 10 컨테이너ID # 마지막 10줄 로그
docker logs -f 컨테이너ID # 실시간 로그 확인
docker run -d -p 4000:80 nginx # 호스트:컨테이너 포트
docker run -d -p [호스트 포트]:[컨테이너 포트] 이미지명[:태그명]

포그라운드(foreground)
: 내가 실행시킨 프로그램의 내용이 화면에서 실행되고 출력되는 상태
(장점) : 실시간으로 로그 확인 가능/(단점) : 입력불가
백그라운드
:내가 실행시킨 프로그램이 컴퓨터 내부적으로 실행되는 상태를 의미
(장점) : 입력가능/(단점) : 실시간으로 프로그램 확인 불가
docker exec -it 컨테이너ID bash # 컨테이너 내부로 접속
ls # 환경 확인
cat 파일명 # 파일 내용 확인
docker pull redis
docker run -d -p 6379:6379 redis
redis-cli
set 1 jscode
get 1
exit
Docker Compose로 여러 개의 마이크로서비스를 구성하여 전체 애플리케이션을 구축하고, 각 서비스 간의 통신을 테스트합니다.
services:
my-web-server:
container_name: web-server
image: nginx
ports:
- 80:80

3. 아래 터미널을 열고 ls로 compose위치를 확인하고 docker compose up -d 명령어 실행합니다.
(여기서 -d는 명령어를 사용할 수 있게 해줌)
4. 시크릿 모드에서 localhost:80검색을 합니다.(종료는 ctrl+c,command+c)
5. 컨테이너 내리려면 docker compose down 명령어 실행합니다.
docker compose ps -a #compose 관련 내용들 출력
docker compose logs #compose에서 발생한 로그출력
docker compose up --build. #이미지를 다시 빌드해서 컨테이너 실행하고 싶을때
docker compose pull #이미지 버전을 최신으로 바꿀때
services:
my-cache-server:
image: redis
ports:
- 6379:6379

원래 명령어

services:
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: pwd1234
volums:
- ./mysql_data:/var/lib/mysql
ports:
- 3306:3306
명령어 입력 : docker compose up -d
Dependencies를 다음과 같이 설정한다.
src/main/java/com/example/demo위에 AppController.java 추가 후 내용은
package main.java.com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AppController {
@GetMapping("/")
public String home() {
return "Hello, World!";
}
}
Dockerfile 추가 후
FROM openjdk:17-jdk
COPY build/libs/*SNAPSHOT.jar /app.jar
EXPOSE 8080
ENTRYPOINT [ "java", "-jar", "./app.jar" ]
터미널에 빌드하면 다음과 같은 jar파일이 생긴다.
./gradlew clean build

compose.yml파일을 만든 후 docker compose up -d --build 명령어 실행한다.
services:
my-server:
build: .
ports:
- 8080:8080
build: . : compose.yml이 존재하는 디렉토리(.)에 있는 Dockerfile로 이미지를 생성해 컨테이너를 띄우겠다는 의미이다.


에러발생 : 정의된 라우트(URL 경로)가 없어서 Spring이 자동으로 보여주는 404 에러 페이지

AppController.java에 package 내용 바꾸고, settings.json에 내용추가 하고 명령어를 넣어봤습니다.
./gradlew clean build
docker compose up -d --build

성공은 했지만, 에러표시가 사라지지 않음..

오버레이 네트워크 등 고급 도커 네트워크 설정을 구성하여 여러 호스트 간의 컨테이너 네트워크를 설정합니다. Docker Swarm 모드를 사용할 수 있습니다.
swarm: 클러스터 구축 및 관리(주로 멀티 호스트)

Magager Node : 매니저 노드는 클러스터의 상태를 유지하고, 사용자의 명령을 받아 컨테이너의 배치와 관리를 조정하는 역할을 한다.
Worker Node : Docker Swarm에서 워커 노드는 실제로 컨테이너가 배포되고 실행되는 노드이다. 워커 노드는 매니저 노드로부터 배포 명령을 받아 컨테이너를 실행하고, 그 상태를 관리한다.
인스턴스는 두 개(매니저 노드, 워커노드)를 설정할 예정이다.
1️⃣ ipconfig로 ip주소 확인 후
docker swarm init --advertise-addr 192.168.219.126

2️⃣ 매니저 노드에서 워커노드를 위한 조인 토큰을 생성
docker swarm join-token worker

다음과 같은 결과가 나온다.
3️⃣ 스웜 모드는 여러 개의 도커 엔진에 같은 컨테이너를 분산해서 할당하기 때문에 각 도커 데몬의 네트워크가 하나로 묶인 환경이 필요하다.
docker network ls #네트워크 확인

docker_gwbridge와 ingress 네트워크가 생성확인
docker_gwbridge 네트워크는 스웜에서 오버레이 네트워크를 사용할 때 사용되고 ingress 네트워크는 로드 밸런싱과 라우팅 메시지에 사용
docker network create --driver overlay my-overlay
# 유레카 서버 서비스 생성
docker service create --name eureka-service --replicas 2 --network my-overlay 도커허브아이디/eureka-server:latest
# 게이트웨이 서비스 생성
docker service create --name gateway-service --replicas 2 --network my-overlay 도커허브아이디/gateway-server:latest
# 팀 서비스 생성
docker service create --name team-service --replicas 2 --network my-overlay 도커허브아이디/team-server:latest
# 유저 서비스 생성
docker service create --name user-service --replicas 2 --network my-overlay 도커허브아이디/user-server:latest

명령어 | docker network inspect my-overlay
도커의 멀티 스테이지 빌드를 사용하여 빌드 과정과 실제 실행 이미지를 분리함으로써 이미지 크기를 줄이고 보안을 강화합니다.
dockerfile은 크게 FROM, COPY, ENTRY, RUN, WORKDIR, EXPOSE가 있다.
FROM openjdk:17
FROM 이미지ID:태그(버전)
COPY app.txt /app.txt
COPY 복사할곳 복붙할곳
ENTRYPOINT ["/bin/bash", "-c", "echo hello"]
RUN echo "설치 중..."
WORKDIR /app
EXPOSE 8080
Dockerfile을 최적화하여 이미지 빌드 시간을 단축하고 이미지 크기를 최소화합니다. 다양한 최적화 기법을 실습합니다.

우분투 대신 Alpine Linux 이미지를 기본으로 사용한다.
불필요한 파일을 제거한다.
레이어 수를 최소화한다.
불필요한 패키지 설치하지 않는다.
FROM node:18-alpine
WORKDIR /app
COPY package*.json tsconfig.json ./
COPY src ./src
RUN npm ci && \
npm run build && \
npm prune --production
VOLUME /app/data
CMD ["node", "dist/index.js"]
도커 볼륨을 사용하여 컨테이너가 종료되거나 다시 시작되더라도 데이터가 유지되도록 관리합니다. docker volume create, docker run -v 명령어를 사용합니다.
프로그램에 업그레이드가 생길때마다 변경된 부분을 수정하지 않고, 새 컨테이너를 만들어 교체한다.
ㄴ> 교체과정에서 데이터를 보존 시키기 위해 볼륨 개념 활용
도커 컨테이너에서 데이터를 영속적으로 저장하기 위한 방법. 볼륨은 컨테이너 자체의 저장공간을 사용하지 않고, 호스트 자체의 저장공간을 공유하는 형태

docker run -v [호스트 경로]:[컨테이너 경로] 이미지명:태그

도커볼륨을 설정한다.
docker exec -it 컨테이너ID bash
mysql -u root -p
show databases;
create database testdb;
exit

도커 네트워크를 설정하고, 서로 다른 컨테이너 간의 통신을 설정하는 방법을 실습합니다. docker network create, docker network connect 명령어를 사용합니다.
docker network create --driver bridge my-custom-net
docker run -d --name my-mysql \
--network my-custom-net \
-e MYSQL_ROOT_PASSWORD=password123 \
mysql
docker run -d --name my-app \
--network my-custom-net \
minji1130/my-spring-app
my-app은 my-mysql이라는 컨테이너 이름(hostname) 으로 DB에 접속 가능
application.properties
spring.datasource.url=jdbc:mysql://my-mysql:3306/your_db
docker exec -it my-app ping my-mysql

Docker Compose를 사용하여 여러 개의 컨테이너로 구성된 애플리케이션을 정의하고 실행합니다. docker-compose.yml 파일을 작성하고 해당 파일을 통해 다중 컨테이너를 관리합니다.
intellij-src-main-java-com.example.demo-DemoApplication
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AppController {
@GetMapping("/")
public String home() {
return "Hello, World!";
}
}

resource에 yml파일 만든다.
Dockerfile을 만들고 내용을 작성한다.
FROM openjdk:17-jdk
COPY build/libs/*SNAPSHOT.jar /app.jar
ENTRYPOINT ["java", "-jar", "/app.jar"]
compose.yml파일 만든다.
services:
my-server:
build: .
ports:
- 8080:8080
# my-db의 컨테이너가 생성되고 healthy 하다고 판단 될 때, 해당 컨테이너를 생성한다.
depends_on: #my-server는 my-db에 의존하고 있다. my-db가 끝나야 실행된다.
my-db:
condition: service_healthy
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: pwd1234
MYSQL_DATABASE: mydb # MySQL 최초 실행 시 mydb라는 데이터베이스를 생성해준다.
volumes:
- ./mysql_data:/var/lib/mysql
ports:
- 3306:3306
healthcheck:
test: [ "CMD", "mysqladmin", "ping" ] # MySQL이 healthy 한 지 판단할 수 있는 명령어
interval: 5s # 5초 간격으로 체크
retries: 10 # 10번까지 재시도

다음과 같이 mysql과 string boot가 잘 뜬 것을 볼 수 있다

buildgradle에 가서
...
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
}
application.yml에 내용 추가
data:
redis:
host: my-cache-server
port: 6379
RedisConfig.java
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
AppController.java
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AppController {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@GetMapping("/")
public String home() {
redisTemplate.opsForValue().set("abc", "def");
return "Hello, World!";
}
}
compose.yml
services:
my-server:
build: .
ports:
- 8080:8080
depends_on:
my-db:
condition: service_healthy
my-cache-server:
condition: service_healthy
my-db:
image: mysql
environment:
MYSQL_ROOT_PASSWORD: pwd1234
MYSQL_DATABASE: mydb
volumes:
- ./mysql_data:/var/lib/mysql
ports:
- 3306:3306
healthcheck:
test: [ "CMD", "mysqladmin", "ping" ]
interval: 5s
retries: 10
my-cache-server:
image: redis
ports:
- 6379:6379
healthcheck:
test: [ "CMD", "redis-cli", "ping" ]
interval: 5s
retries: 10
$ ./gradlew clean build
$ docker compose down
$ docker compose up --build -d
나는 ./gradlew clean build시 오류가 발생하여 ./gradlew clean build -x test 사용.
테스트 task를 제외하고 빌드만 한다는 의미

완료

특정 애플리케이션을 컨테이너화하기 위해 Dockerfile을 작성하고, 이를 통해 도커 이미지를 빌드합니다. Dockerfile 작성 시 이해해야 할 주요 명령어와 꾸미기 기법을 학습합니다.