
CI/CD를 구축하기 위해 필요한 개념 중 하나인 도커에 대해 알아보자.
CI/CD에서 도커는 왜 필요한 개념인가?
지속적인 Integration 지속적인 Deploy 중 Deploy를 위해 필요한 요소는 다음과 같은 것들이 있다.
1. 서비스를 위한 서버 구축
2. 서버 애플리케이션 구동 환경 설정
3. SW 의존성 구성 + 버전 관리
4. 네트워크 구성 등 . . .
이러한 것들을 서버를 하나 세팅할 때마다 반복하는 것은 여간 번거로운 일이 아니며, 다양한 서비스가 서버 한대에 동시에 실행되면서 야기되는 문제점도 물론 생길 수 있다.
그렇다면 서버에 가상 머신을 필요한 서비스 마다 할당해서 내부 네트워크로 묶어보면 어떨까? WAS, Database, Web Server, Reverse Proxy 등 처럼 말이다. 물리 서버는 하나지만 논리적으로는 서비스마다 운영환경이 쪼개져, 서로에 영향도 덜 주고 기본적으로 각 VM을 스냅샷하여 이미지로 관리할 수 있게 되면, 다른 서버로의 이식도 간편해진다.
다만 여기서 문제점은 VM은 실제로 VM엔진 위에서 HOST의 리소스를 나눠받은 각각의 운영체제가 실행된다는 것인데, 그렇다면 각 VM의 이미지는 운영체제를 탑재하므로 사이즈가 매우 커질것이고 각각의 머신도 매우 무거운 상태로 실행되게 될것이다.
이러한 문제점을 해결하기 위해 기존 VM의 이미지에서 무거운 운영체제를 들어내고 나머지 환경들만 이미지로 묶어 동일한 운영체제, 커널을 공유하며 가볍게 각 이미지만의 독립적인 환경으로 구동할 수 있도록 한 것이 바로 도커이다.
이제 도커를 알아보기 위해 용어를 정의해야 한다.
도커 파일 : 도커 이미지를 만드는데 사용되는 구성 및 설정 파일
도커 이미지 : 도커 파일을 실행하여, 특정 구성과 여러 설정 정보들을 하나로 묶은 파일
도커 컨테이너 : 도커 이미지를 구동하는 하나의 독립적인 구동 환경으로 도커 엔진에 의해 구동되고 관리된다.
+) 우분투 기준 도커 설치
https://haengsin.tistory.com/128
자주 사용되는 Docker Command
docker container ls -a
; 도커 컨테이너를 리스트로 조회, -a옵션 추가 시 실행되지 않는 컨테이너까지 조회
docker exec -it [container name][command]
; 해당 도커 컨테이너 내에 Command를 실행하는 명령
; docker exec -it [container name] /bin/bash 를 실행시 해당 컨테이너의 쉘을 실행하여 조작할 수 있다. 단, bash쉘이 존재하는 이미지 한정
docker logs [container name]
; 현재 실행중인 컨테이너의 쉘에 찍힌 로그를 확인할 수 있다.
docker run [name]
; 도커 이미지 실행
docker stop [name]
; 도커 이미지 실행 중단
docker rm [name]
; 도커 이미지 삭제
docker stats
; 각 컨테이너 리소스 사용량 확인
docker container inspect [container name]
; 컨테이너의 세부 정보를 확인할 수 있다. 해당 컨테이너가 할당받은 도커 사설 네트워크 대역 및 ip를 확인 가능
; 주로 172.17.0.1 부터 끝자리가 1씩 증가하는 형태로 할당됨
docker netword create [network name]
; 도커 네트워크를 생성할 수 있음
; 생성된 도커 네트워크를 이미지 실행(run)시 --network [network name] 옵션을 사용하면, 해당 컨테이너간 통신 시 컨테이너 명으로 통신이 가능하다. http://localhost:3000 -> http://dockerexam:3000
docker system prune -a -f
; 도커 내 쌓인 모든 이미지 및 캐시파일을 삭제하는 명령
+추가
도커를 이용해 다양한 환경 설정이 이루어진 이미지를 도커 파일로 만들거나 이미지 자체를 공유할 수 있지만, 마찬가지로 이미 만들어지고 검증된 이미지를 내려받아 사용할 수도 있다. 다음은 공식 제공된 이미지를 내려받아 실행하는 예시다.
mariadb 설치 Command
docker pull mariadb
docker run -p 3306:3306 --name mariadb_1 -e MARIADB_ROOT_PASSWORD=1234 -d --restart unless-stopped -e TZ=Asia/Seoul -v /docker_project/mariadb_1/conf.d:/etc/mysql/conf.d -v /docker_project/mariadb_1/mysql:/var/lib/mysql -v /docker_project/mariadb_1/run/mysqld:/run/mysqld mariadb:latest
+) -v 볼륨 옵션으로 물리 볼륨과 컨테이너의 가상 볼륨을 바인딩하여 컨테이너를 내렸다 올리더라도 데이터가 유지됨
persistent volume
[추가 예시]
springBoot Dockerfile
FROM openjdk:17-jdk-alpine -- 기본 제공되는 Docker 이미지 Base
ARG JAR_FILE=build/libs/react-demo-0.0.1-SNAPSHOT.jar -- Argument 인자 설정, jar 빌드파일 생성 위치
COPY $JAR_FILE app.jar -- 해당 위치에서 컨테이너 루트 경로로 app.jar라는 이름으로 복사
EXPOSE 8080 -- 컨테이너 포트
ENTRYPOINT ["java", "-jar", "-Dspring.profiles.active=prod", "./app.jar"] -- jar실행 명령 공백 기준으로 배열 형태로 작성
React Dockerfile
FROM node:lts-alpine as build
WORKDIR /app -- working directory 이동
COPY package.json package-lock.json ./ -- 두 파일을 /app 밑으로 복사
RUN npm ci -- 의존성 설치
COPY . .
ENV NODE_OPTIONS="--max-old-space-size=8192" -- 힙 메모리 이슈로 빌드 실패시 추가
RUN npm run build -- 프로젝트 빌드
FROM nginx:stable-alpine
COPY --from=build /app/build /usr/share/nginx/html -- 빌드파일 nginx index 경로로 복사
COPY nginx.conf /etc/nginx/nginx.conf -- 설정파일 또한 설정파일 경로로 복사(덮어쓰기)
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
nginx.conf
user root;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
keepalive_timeout 65;
types_hash_max_size 4096;
client_max_body_size 20M;
include /etc/nginx/mime.types;
default_type application/octet-stream;
server {
listen 80;
listen [::]:80;
server_name _;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri /index.html;
}
location /api/ {
rewrite /api/(.*) /$1 break;
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, DELETE, PATCH, OPTIONS';
add_header 'Access-Control-Max-Age' 86400;
proxy_pass http://172.17.0.1:8080;
proxy_http_version 1.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
}
}
}
+) nextjs 사용시
링크텍스트