1215 웹 개발 파이프라인 구축 - 도커
웹개발 파이프라인

배포-인도 자동화의 중요성
전통적 인도 프로세스

한계점
- 느린 인도 기간
- 느린 피드백 주기
- 자동화 미비
- 핫픽스 위험성
- 개발문화 건전성 제한
해결책 : 지속적 인도/배포 방식
- 변경 내용이 단지 코드 한 줄이라고 할 때
- 시간 소요? 작업을 반복해서 안정적으로 수행?
- 해결안: 프로세스의 각 단계를 자동화
- 빠른 제품 인도/배포
- 짧은 피드백 주기
- 위험도가 낮은 릴리스 : 반복과 롤백
- 유연한 릴리스 정책 결정 가능
자동 배포 파이프라인

지속적 통합(CI ; Continuous Integration)
코드가 올바르게 빌드 및 통합되는지를 자동으로 확인
- 리포지토리에서 코드를 체크아웃
- 빌드(컴파일 및 링크 등)를 수행하고 단위테스트를 행함
- 테스트 커버리지 리포트 생성
- 코드 품질을 검증
- 정적 분석을 통한 규칙 검사
- 코딩 규약 등의 준수 여부 검사
- 개발팀에 1차적인 피드백 제공
자동 인수 테스트
인수 테스트(UAT; User Acceptance Test)
- 제품이 릴리스할 준비가 되었는지를 사용자 요구사항에 견주어 확인
- 전통적으로 QA 팀의 역할
- 통합 테스트, 인수 테스트, 비기능적 분석(성능, 확장성, 보안,...) 등을 포함
CD 파이프라인에 통합
- 품질 점검을 나중에 하는 것이 아니라 개발 중에 제품에 내재
- 개발자가 구현을 마치는 즉시 고객이 원하는 제품인지를 검증
- 소프트웨어의 인도 결정을 자동화한다는 뜻
구성 관리(Configuration Management)
구성관리
- 소프트웨어와 환경 변화를 추적하고 제어
- 전통적으로 운영 팀의 역할
- 필수 도구 준비와 설치
- 응용의 배포와 관련한 다양한 서비스 인스턴스와 배포 버전 관리
CD 파이프라인에 통합
- 프로덕션 환경의 응용을 자동으로 구성하고 배포
- 구성 관리 도구를 이용하여 구성 관리 파일을 버전 관리 시스템에 저장하고 변경 이력 추적
CD를 위한 기술적 전제 조건
자동 빌드, 테스트, 패키징, 배포
- 전체 프로세스 중 자동화되지 않는 부분이 있다면 지속적 인도가 불가능
신속한 파이프라인 실행
- 리포지토리에 커밋 발생할 때마다 실행되어야 하므로 소요시간이 길면 안 됨
신속한 장애 복구
- 신속한 롤백이 가능해야 하며, 그렇지 못할 경우 잦은 릴리스에 따른 위험도가 높아짐
무중단 배포
- 잦은 (하루에도 수 회) 배포가 이루어지므로 배포 중 서비스 다운타임이 발생하면 안됨
트렁크 기반 개발
- 로컬 브랜치에만 코드 체크인하면 코드 통합 검증이 이루어지지 앟고 릴리스 회수가 들어듬
웹개발 파이프라인 구성 도구
컨테이너 가상화 및 클러스터 운용
- Docker+Kubernetes
소프트웨어 개발 파이프라인 자동화 서버
- Jenkins
구성 관리 자동화
- Ansible
소프트웨어 버전 관리(SCM; Souce Code Management)
- GitHub
그 외
- 빌드 도구(자동화 지원), 단위 테스트 프레임워크, 정적 코드 분석기, 인수 테스트 프레임워크
컨테이너화(Containerization)
응용 프로그램, 설정(configuration) 파일, 라이브러리,
그리고 이들 사이의 의존성 관계를 한군데에 묶어 (컨테이너 안에 넣어) 관리
- 소프트웨어 개발 및 배포의 효율과 안정성을 향상시킴
- 하이퍼바이저에 의한 가상 기계의 대체 및 보완 방식으로 각광받고 있음
- 시스템 의존성이 최소화되어 소프트웨어 시스템의 이식이 용이해짐
- 예측 가능하고 유연하 소프트웨어 실행 환경을 제공하여 클라우드 컴퓨팅 인프라에서 활용도가 높음

지속적 통합 파이프라인(CI Pipeline)
리포지토리에 코드 커밋이 발생할 때마다 빌드, 단위 테스트, 정적 분석 등을 행함
자동 인수 테스트
Docker와 Jenkins를 결헙하여 인수 테스트 환경을 만들고 테스트 수행

쿠버네티스 클러스터링
Docker Host 대신에 Kubernetes Cluster가 연결된 형태
구성 관리
다중 환경을 생성, 미러링하여 테스트 환경과 프로덕션 환경을 미러링

컨테이너 가상화와 도커
가상화
- 컴퓨팅 자원(리소스)의 추상화를 일컫는 광범위한 용어
- 컴퓨터 안에 또 다른, 즉 가상의 컴퓨터가 존재하도록 하는 기술
가상화 컴퓨팅의 이점
- 시스템 측면
- 비즈니스 측면
- 우리의 관심
- 개발한 소프트웨어 배포, 테스트, 구성 관리 등에(실제 물리적 하드웨어에 상관 없이)
통일된 환경을 제공할 수 있음 - 소프트웨어 통합/인도 프로세스의 자동화에 적용하기 좋음
서버 가상화 기술의 진화
- 가상 기계(VM; Virtual Machine) 기반
- 하이퍼바이저 이용
- 타입1: 네이티브 or 베어메탈형
- 전가상화
- 반가상화
- 타입2: 호스트형
- 호스트: 다른 시스템을 가상화하여 실행하는 컴퓨터
- 게스트: 호스트 시스템 위에 가상화되어 제공되는 (가상) 시스템
- 컨테이너 기반
- 호스트 OS의 컨테이너 기술을 이용
컨테이너 가상화

컨테이너 사용의 이점
- 디스크 요량 절감
- 낮은 오버헤드
도커
널리 이용되고 있는 컨테이너 기반 가상화 플랫폼
도커를 이용한 소프트웨어 개발/배포

도커 허브
컨테이너 이미지들에 대해 원격 저장, 유지관리, 공유, 권한 관리 등을 효율적으로 행할 수 있는 온라인 서비스 제공
=> 소프트웨어 개발/배포 프로세스의 효율성이 크게 증대
도커 설치
docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
17eec7bbc9d7: Pull complete
Digest: sha256:d4aaab6242e0cace87e2ec17a2ed3d779d18fbfd03042ea58f2995626396a274
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
도커의 구성요소
이미지: 실행 가능한 컨테이너를 만들기 위한 거푸집
컨테이너: 도커가 실행하는 격리된 환경과 그 내부

도커 이미지
- 응용을 실행하는 데 필요한 모든 파일들과 그것을 실행하는 방법을 한데 묶어 놓은 것
- 상태를 저장하지 않는(stateless) 방식 - 네트워크로 전송, 레지스트리에 저장, 이름 및 버전 지정 가능
- 계층화되어 있다는 특징을 갖고 있으며, 어떤 이미지로부터 다른 이미지를 만드는 것이 가능
도커 컨테이너
- 이미지의 실행 인스턴스
- 하나의 이미지로부터 여러 컨테이너(인스턴스)를 만들어 동일한 응용을 여러 개 실행 가능(각각은 독립)
- 상태를 저장하는(stateful) 방식 - 컨테이너를 사용하면서 상태 변경할 수 있음
- 그러나 컨테이너가 소명하면 이 상태도 잊어버림
이미지의 계층 구조

도커 허브에서 이미지 찾기
cli 에서 검색 가능 : docker search apache
검색한 이미지 실행 : docker run httpd
ip 주소로 접속 가능
지금까지 익힌 것
도커 이미지를 이용해서 컨테이너를 만들어 실행할 수 있다.
도커 이미지는
- 계층 구조 가지고 있다.
- 컨테이너를 만드는 데 이용되는 거푸집으로 상태를 저장 안함
- 이미지 레지스트리를 통하여 네트워크를 통해 전송 가능
도커 컨테이너는
- 이미지를 이용하여 만들어진, 응용 소프트웨어를 실제로 실행하는 격리된 환경
- 도커 엔진에 의해서 관리되며 마치 컴퓨터 하나가 새로 생겨서 정해진 일을 수행하는 것과 같은 모습을 보여줌
이미지 조회
docker images
실행되고 있는 도커 검색
docker ps
도커 이미지 삭제
docker rmi httpd
도커 컨테이너 삭제 (컨테이너 id 혹은 names)
docker rm 86eabd75b362
컨테이너와 이미지 관련 명령어 요약
- docker run <이미지 이름>
- 이름이 주어진 이미지를 로컬에서 또는 레지스트리에서 가져다가 컨테이너 만들어 실행
- docker ps, docker ps -a
- 현재 실행 중인(또는 중단되어 있는 것까지 포함하여) 컨테이너들의 정보를 조회
- docker images
- 로컬 컴퓨터에 가지고 있는 이미지들의 정보를 조회
- docker stop <컨테이너 이름/ID>
- 현재 실행 중인 컨테이너의 실행을 중단
- 컨테이너가 없어지지는 않음
- docker rm <컨테이너 이름/ID>
- 컨테이너를 삭제
- docker rmi <이미지 이름/ID>
- 이미지를 삭제
도커 이미지 만들기
[이슈]
문제발생 : 도커에서 docker run httpd 로 받아온 이미지 컨테이너를 실행하려 했으나 접속 불가
원인파악
- 172.17.0.2는 컨테이너 내부 IP로 Docker 내부 네트워크에서만 유효
- 호스트(윈도우) 브라우저에서는 직접 접근 불가
- 포트포워딩을 안함
해결방안 - 포트 매핑해서 실행 : 윈도우 8080 포트 => 컨테이너 80 포트(Apache)
-
docker run -p 8080:80 httpd - 접속 URL : http://localhost:8080
쉘 얻기
- docker run -it httpd /bin/bash
도큐먼트 루트 검색
grep DocumentRoot /usr/local/apache2/conf/httpd.conf
도큐먼트 루트 내용
DocumentRoot "/usr/local/apache2/htdocs"
- htdocs에 index.html 있다.
현재 디렉토리 검색
pwd
htdocs 폴더로 이동
index.html 파일 내용 조회
- cat index.html
index.html 파일 내용 수정
- cat > index.html (리다이렉션)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html> <head> <title>It works! Apache httpd</title> </head> <body> <p>New page!</p> </body> </html> - ctrl + D로 닫아줌
- 실행 : httpd-foreground
[이슈]
문제발생 : 윈도우 8080에서 안열림
해결: 포트포워딩을 run -it 할때부터 해줘야 윈도우 8080에서 열 수 있음.
docker run -it -p 8080:80 httpd /bin/bash
이전에 만들었던 도커 컨테이너 실행
- docker start <컨테이너ID>
지금 실행 중 컨테이너의 bash 쉘 얻기
docker exec -it <컨테이너ID> /bin/bash
변경한 컨테이너를 이미지로 만들기
- docker commit <컨테이너ID> <이미지이름:태그>
새로 만든 이미지 확인
- docker images
새로 만든 이미지 실행
- docker run -p 8080:80 my_httpd:0.1 httpd-foreground
이미지 생성 자동화
Dockerfile
- 계층 구조를 이용하여 도커 이미지를 만드는 절차를 기술하는 파일(텍스트)
- FROM [--platform=] [AS ]
- RUN
- ENTRYPOINT ["executable", "param1", "param2"]
FROM httpd:latest
RUN echo "<html><body><h1>Docker build test</h1></body></html>" > /usr/local/apache2/htdocs/index.html
ENTRYPOINT /usr/local/bin/httpd-foreground
이미지 빌드 명령어
- docker build [OPTIONS] PATH | URL | -
- docker build -t my_httpd:0.2 .
빌드한 이미지 실행
- docker run -p 8080:80 my_httpd:0.2
이미지 레지스트리에 업로드
docker login
- 도커 허브에 로그인
docker tag my_httpd:0.2 <도커username>/my_httpd:0.2
- 원격에 올릴 이미지 태그하기
docker push <도커username>/my_httpd:0.2
- 원격 도커허브에 올리기
docker run -p 8080:80 <도커username>/my_httpd:0.2
- 원격 도커허브 이미지 실행
요약 정리
도커의 편리함 (CI/CD 관점에서 생각해보기)
- 응용을 실행하는 데 이용되는 실제(물리적) 컴퓨터 환경과 독립적으로 통일된 실행 환경 제공 가능
- 이미지로부터 여러 개의 동일한 컨테이너 인스턴스를 만들고 실행 가능
- 필요한 소프트웨어 도구 및 설정 파일 등을 사전에 지정해두고 알려진 상태로 컨테이너 생성 가능(자동화!)
남아있는 일들
- 환경 변수의 이용
- 컨테이너들 사이 및 호스트와 컨테이너 사이에 TCP/IP를 이용한 통신 가능하게 하기
- 호스트와 컨테이너 사이의 볼륨 공유
- 도커 클린 업
※ 실습과제
간단한 도커 응용 만들기 - 이미지 이름(과 태그)는 hello:0.1
- 베이스 이미지:ubuntu:22.04
- 패키지 설치:python3(apt-get 이용)
- 응용 스크립트 설치: hello.py
로컬 이미지 이용하여 컨테이너 만들고 실행
- docker run hello:0.1
도커 파일 내용
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3 COPY hello.py .
ENTRYPOINT ["python3", "hello.py"]
hello.py 내용
print("Hello World from Python!")
컨테이너 다루기
도커에서 환경 변수의 이용
- 환경 변수를 참조하도록 hello.py를 수정
import os print("Hello World from %s!" %os.environ["NAME"])- Dockerfile 내에 ENV 지시자를 이용하여 환경변수 내용 지정 (FROM 보다 바로 뒤에)
ENV NAME=Grepp- 이미지 빌드 (hello:0.2) 하고 실행해서 결과 확인
docker build -t hello:0.2 . docker run --rm hello:0.2- 환경변수 동적으로 받기
docker run -e NAME=Programmers --rm hello:0.2
실행이 끝난 컨테이너의 자동 삭제
- docker run 명령에 --rm 옵션을 붙이면 실행 완료와 함께 컨테이너 삭제
백그라운드에 실행
docker run -d --rm tomcat
docker logs <컨테이너id>
docker inspect <컨테이너id>
- ip주소 검색 가능
호스트의 포트와 컨테이너 포트를 연결

- docker run -d --rm -p 8888:8080 tomcat
컨테이너 이름 지정하기
도커의 컨테이너는 이름과 ID(고유해시값)에 의하여 식별
- 이름을 지정하지 않으면 docker가 컨테이너를 만들 때 이름을 자동 부여
- 그러나 관리 편의 및 자동화 위해 이름 지정하는 것 필요
- 명령어 : docker tun --name <컨테이너 이름><이미지>
도커 클린업
컨테이너 삭제
- docker rm <컨테이너>
이미지 삭제
- docker rmi <이미지>
컨테이너 전체 삭제
- docker container prune
이미지 전체 삭제
- docker image prune -a
컨테이너 안의 파일들에 접근하는 방법
실행하고 있는 컨테이너와 호스트 사이 파일 복사
- 명령어 docker cp를 이용
이미지 빌드할 때 호스트로부터 파일을 컨테이너에 추가
- Dockerfile 안에 ADD 지시자를 활용
바인드 마운트
- docker -v 옵션으로 호스트의 특정 디렉토리를 컨테이너와 공유
도커 볼륨
- docker -v 옵션으로 호스트와 공유하는 것은 비슷하지만 컨테이너가 마운트하는 것은 추상화된 볼륨
실습
nginx 이미지 가져와서 컨테이너 실행
docker run --rm -d -p 8080:80 --name my_nginx nginx:latest
쉘 얻기
docker exec -it my_nginx /bin/bash
내용 보기
cat /usr/share/nginx/html/index.html
대체파일 만들어 해당 컨테이너에 복사
docker cp index.html my_nginx:/usr/share/nginx/html
컨테이너에 있는 파일 바깥으로 복사
docker cp my_nginx:/usr/share/nginx/html/50x.html .
이미지 빌드에 파일 추가
Dockerfile 안에 ADD 지시자를 이용해 호스트가 제공하는 파일을 특정 위치에 둠
- ADD <호스트 내 source 파일의 경로> <컨테이너 내에 배치할 파일의 경로>
FROM nginx:latest
ADD ./index.html /usr/share/nginx/html/new.html
CMD ["nginx", "-g", "daemon off;"]
도커 빌드 : docker build -t your_nginx .
실행 : docker run --rm -d -p 8888:80 --name my_nginx your_nginx
바인드 마운트
명령어
docker run -v <호스트 경로>:<컨테이너 경로> <이미지>
실습
- 호스트에 실험용 디렉토리 하나 만들고, 이 안에 간단한 텍스트 파일 하나 생성
- 이미지 ubuntu:22.04 를 interactive/tty 모드로 하여 /bin/bash 실행, 위 디렉토리를 볼륨 공유
- 컨테이너 안에서 새로운 파일 하나 생성, 컨테이너는 종료
- 호스트에서 새로 생성된 파일 확인
docker run -it -v C:\devCourse\Course-pipeline\vol:/host_directory ubuntu:22.04 /bin/bash
컨테이너 폴더에서 내용 확인 가능
ls -l /host_directory
새 파일 생성
cat > /host_directory/B.txt
컨테이너 종료 후에도 호스트에서 새로 생성된 파일 확인 가능
도커 볼륨 이용
볼륨 생성 명령어
- docker volume create <볼륨의 이름>
- 서로 다른 컨테이너에서도 이용 가능
docker volume ls
- 가지고 있는 볼륨 검색
docker volume create my-volume
- 볼륨 생성
docker inspect my-volume
- 볼륨 정보 검색
- "Mountpoint": "/var/lib/docker/volumes/my-volume/_data",
docker run --rm -d -p 8888:80 -v my-volume:/usr/share/nginx/html --name my_nginx nginx:latest
- 볼륨 실행
docker exec -it my_nginx /bin/bash
cd /usr/share/nginx/html
- 쉘에서 확인
- new 파일 생성
실습
- 간단한 웹 응용 만들어 서버를 도커 이미지로 구성
- 코딩
- 서버 환경 구성
- 필요한 패키지는 컨테이너 안에 apt-get 등으로 설치해보면 테스트
- 이미지 만들기
- 위 패키지 구성, 코드 배치(ADD 하거나 tar 옮겨 풀너ㅏ, git pull 하거나)
설정 파일 구성 등을 Dockerfile에 작성하고 docker build 해서 만들어 docker run 테스트
- 위 패키지 구성, 코드 배치(ADD 하거나 tar 옮겨 풀너ㅏ, git pull 하거나)
- 레지스트리에 게시
- 태그 붙이고 docker push
- 이 과정에서 코드 변경 일어나면 어떤 일들이 자동으로 실행되어야 할지 생각