2024.03.07(목)

🛢️웹 개발 파이프라인

CI/CD(CI CD, 지속적 통합/지속적 배포): 개념, 툴, 구축, 차이

🛠️지속적 통합 (CI; Continuous Integration)

  • 코드가 올바르게 빌드 및 통합되는지를 자동으로 확인
    • 리포지토리에서 코드를 체크아웃 (보통 Git 사용)
    • 빌드(컴파일 및 링크 등)를 수행하고 단위테스트(UT, Unit Test)를 행함
      • 테스트 커버리지(test coverage) 리포트 생성
    • 코드 품질 검증
      • 정적 분석(static analysis)을 통한 규칙 검사
      • 코딩 규약 등의 준수 여부 검사
  • 개발팀에 1차적인 피드백 제공

🏁자동 인수 테스트

인수 테스트 (UAT; User Acceptance Test)

  • 제품이 릴리스할 준비가 되었는지를 “사용자(고객) 요구사항에 견주어” 확인
  • 전통적으로 QA(Quality Assurance) 팀의 역할
  • 통합 테스트, 인수 테스트, 비기능적 분석(성능, 확장성, 보안, …) 등을 포함

CD 파이프라인에 통합

  • 품질 점검을 나중에 하는 것이 아니라 개발 중에 제품에 내재시키자는 것
  • 개발자가 구현을 마치는 즉시 고객이 원하는 제품인지를 검증
  • 소프트웨어의 인도 결정을 자동화한다는 뜻

⚙️구성 관리

구성 관리 (Configuration Management)

  • 소프트웨어와 환경 변화를 추적하고 제어
  • 전통적으로 운영(Operation)팀의 역할
  • 필수 도구 준비와 설치
  • 응용의 배포와 관련한 다양한 인스턴스와 배포 버전 관리

CD 파이프라인에 통합

  • 프로덕션 환경의 응용을 자동으로 구성하고 배포
  • 구성 관리 도구를 이용하여 구성 관리 파일을 버전 관리 시스템에 저장하고 변경 이력 추적

📝CD를 위한 기술적 전제조건

  • 자동 빌드, 테스트, 패키징, 배포: 전체 프로세스 중 자동화되지 않는 부분이 있다면 지속적 인도가 불가능
  • 신속한 파이프라인 실행: 리포지토리에 커밋이 발생할 때마다 실행되어야 하므로 소요시간이 길면 안됨
  • 신속한 장애 복구: 신속한 롤백이 가능해야 하며, 그렇지 못할 경우 잦은 릴리스에 따른 위험도가 높아짐
  • 무중단 배포: 잦은(하루에도 수 회) 배포가 이루어지므로 배포 중 서비스 다운타임(downtime)이 발생하면 안됨
  • 트렁크 기반 개발: 로컬 브랜치에만 코드 체크인하면 코드 통합 검증이 이루어지지 않고 릴리스 회수가 줄어듬

🔧웹 개발 파이프라인 자동화 도구들

  • 컨테이너 가상화 및 클러스터 운용: 🐋Docker + 🕸️Kubernetes
  • 소프트웨어 개발 파이프라인 자동화 서버: 👔Jenkins
  • 구성 관리 자동화: 🎩Ansible
  • 소프트웨어 버전 관리(SCM; Source Code Management): 🐱GitHub
  • 그 외: 빌드 도구 (자동화 지원), 단위 테스트 프레임워크, 정적 코드 분석기, 인수 테스트 프레임워크, …

🕶️가상화(Virtualization)

컴퓨팅 자원(resource)의 추상화를 일컫는 광범위한 용어로 컴퓨터 안에 또 다른 가상의 컴퓨터가 존재하도록 하는 기술

  • 가상화 컴퓨팅의 이점
    • 시스템 측면
      • 시스템 이용률(utilization)의 향상
      • 설정(configuration)의 구성과 복원이 용이
    • 비즈니스 측면
      • 자본 및 운영 비용 절감
      • 다운타임 최소화 (또는 제거)
      • 비즈니스 연속성 및 재해 복구 향상
      • 데이터 센터 관리 간소화
    • 우리의 관심
      • 개발한 소프트웨어의 배포, 테스트, 구성 관리 등에 (실제 물리적 하드웨어에 상관 없이) 통일된 환경을 제공할 수 있음
      • 소프트웨어 통합/인도 프로세서의 자동화에 적용하기 좋음

☁️서버 가상화 기술의 진화

  • 가상 기계(VM; Virtual Machine) 기반
    • 하이퍼바이저(hyperviosor) 이용

      • Type 1 하이퍼바이저: 네이티브(native) 또는 베어메탈(bare-metal) 형
        • 전가상화(full virtualization)
        • 반가상화(para virtualization)
      • Type 2 하이퍼바이저: 호스트형 (hosted)

      호스트: 다른 시스템을 가상화하여 실행하는 컴퓨터
      게스트: 호스트 시스템 위에 가상화되어 제공되는 (가상) 시스템

  • 컨테이너(Container) 기반
    • 호스트 OS의 컨테이너 기술을 이용

📦컨테이너 가상화(Containerization)

  • 컨테이너 엔진이 가상의 런타임 환경을 제공
  • 각각의 응용 소프트웨어는 격리된 환경에서 실행
    • 컨테이너: 응용 프로그램, 설정 파일, 라이브러리, 그리고 이들 사이의 의존성 관계를 하나의 묶음으로 관리하는 것
    • 컨테이너 엔진의 도움으로 시스템 의존성이 최소화되어 소프트웨어 시스템의 이식이 용이해짐
  • 컨테이너가 모사하는 대상은 하드웨어가 아니라 운영체제(OS) → 하이퍼바이저를 이용하는 가상화에 비해 가볍고 빠름(OS 부팅 필요X)!
    (http://www.opennaru.com/cloud/virtualization-vs-container/)
    • 낮은 오버헤드
    • 디스크 용량 절감

🐋Docker

널리 이용되고 있는 컨테이너 기반 가상화 플랫폼

  • Linux, Windows, MacOS 상에서 실행되는 컨테이너 엔진을 제공 (우리가 사용하고 있는 Host OS 위에 어디든 올릴 수 있음)
  • AWS, GCP, Azure 등의 클라우드 컴퓨팅 인프라와 결합하면 더욱 유연한 운영 가능
  • 도커 허브(Docker Hub): 컨테이너 이미지들에 대해 원격 저장, 유지관리, 공유, 권한 관리 등을 효율적으로 행할 수 있는 온라인 서비스

🖥️Docker Desktop & Docker Engine

  • Docker Desktop
    • GUI 제공: 보다 간단한 컨테이너 및 서비스 관리가 가능
    • Docker Engine은 Linux에서만 구동되기 때문에 Window나 Mac에서 사용하기 위해서는 Linux VM을 생성하고 그 위에서 Docker Engine을 실행
  • Docker Engine
    • Linux OS를 사용한다면 Docker Engine을 바로 올릴 수 있음
    • Docker는 Linux의 프로세스 컨트롤 그룹을 이용하여 호스트 커널의 서비스들을 이용하여 격리되어 실행

🖼️Docker Image & 📦Docker Container

  • 도커 이미지(Docker Images)

    실행 가능한 컨테이너를 만들기 위한 거푸집

    • 응용을 실행하는 데 필요한 모든 파일들과 그것을 실행하는 방법을 한데 묶어 놓은 것
    • 상태를 저장하지 않는 (stateless) 방식 - 네트워크로 전송, 레지스트리에 저장, 이름 및 버전 지정 가능
    • 계층화되어 있다는 특징을 갖고 있으며, 어떤 이미지로부터 다른 이미지를 만드는 것이 가능
    • 이미지 레지스트리(Image Registry, Docker Hub와 같이 Docker 이미지를 저장하고 관리하는 서비스)를 통해 네트워크를 통해 전송 가능
  • 도커 컨테이너(Docker Containers)

    도커가 실행하는 격리된 환경과 그 내부

    • 이미지의 실행 인스턴스(instance)
    • 하나의 이미지로부터 여러 컨테이너(인스턴스)를 만들어 동일한 응용을 여러 개 실행할 수 있음 (각각은 독립)
    • 상태를 저장하는 (stateful) 방식 - 컨테이너를 사용하면서 상태 변경 가능, 컨테이너 소멸 시 이 상태도 잊어버림
    • 도커 엔진에 의해 관리되며 마치 컴퓨터 하나가 새로 생겨서 정해진 일을 수행하는 것과 같은 모습을 보여줌

🥪이미지의 계층(Layer) 구조

  • 보통 Ubuntu에 Ngnix 서버까지 올려져 있는 이미지는 이미 만들어져 있는 것을 가져올 수 있고(pull) 그 위에 내가 Web App을 만들어 올릴 수 있음
  • 이미지를 기반으로 각각의 독립적인 컨테이너들을 구동할 수 있음

🔍사용 가능한 이미지 탐색

  1. 도커 허브(Docker Hub)에서 쉽게 탐색 가능
  2. CLI 환경에서 docker search <name> 명령어로 탐색 가능

⌨️간단한 실습

  • docker run <name>으로 실행 (local에 존재하지 않는 경우 pull & run)
    • 172.17.0.2에서 실행 중이라는데 접속이 안됨

      WHY?

      • Windows와 Mac에서 실습할 때 Docker Desktop을 사용하게 되는데 이 경우 VM 위에서 Docker Engine을 실행하게 됨
      • 기본적으로 Docker Container는 자체적인 네트워크 환경을 가지고 있고, 이는 Host의 네트워크와 격리되어 있음
      • 따라서 Host에서 Container의 서비스에 접근하려면 Host와 Container 간의 통신을 중계해주는 포트 포워딩(Port Forwarding)을 사용하여 Host의 특정 포트로 들어온 요청을 Container 내부의 포트로 전달하여 서비스에 접근해야함!
      • Linux에 Docker Engine을 직접 설치하는 실제 배포 환경에서는 VM과 같은 별도의 중간 계층 없이 Docker가 Host의 네트워크 인터페이스에 직접 연결되기 때문에 Container는 Host와 동일한 네트워크에 존재하게 되어 바로 접속 가능
    • docker run 명령어에 -p, --publish option을 사용해서 포트 포워딩을 해주어야 함!
      • -p <host-port>:<container-port> 형식
      • 공식 문서 참고

        ⚠️ 주의할 점

        • 공식 문서에서도 강조되어 있듯 Container의 port를 publishing할 때 Host의 IP address를 명시하지 않고 port만 사용하면  (i.e., -p 127.0.0.1:80:80 대신 -p 80:80) Docker는 default로 post를 모든 interface에 publish (address 0.0.0.0) ⇒ Docker Host 뿐만 아니라 외부에 공개됨 (누구나 접근 가능)
        • localhost IP address (127.0.0.1)를 포함하면 Docker Host만 해당 Container의 port에 접근 가능
  • publish option을 사용해서 해결! (💡apache server의 default port number는 80)


  • 중지된 컨테이너를 시작: docker start <container-name/id>
  • 현재 실행 중인 컨테이너의 실행을 중단: docker stop <container-name/id>
  • 컨테이너 목록 보기
    • 현재 실행 중인 컨테이너 목록: docker ps
    • 모든 컨테이너 목록: docker ps -a
  • 컨테이너 삭제하기: docker rm <container-name/id>
  • 이미지 목록 보기: docker images
  • 이미지 삭제하기: docker rmi <image-name/id> (해당 이미지로 만든 컨테이너가 없어야 삭제 가능)

🖼️도커 이미지 만들기

🎯목표

  1. 도커 컨테이너의 “상태”를 조정해서 우리가 원하는 것 만들기 → 변경한 상태를 반영하는 이미지 만들기
    • httpd 이미지를 가져다가 실행하되 “It works!” 말고 다른 기본 페이지가 나오도록 설정해보자
    • 이 상태로 실행되는 이미지를 작성해보자
  2. 이미지 만드는 과정 자동화하기

⌨️실습1: 이미지 수정 및 커밋

  • docker run -p 8080:80 **-it** httpd /bin/bash

  • container 내부 터미널이 열리고 다음과 같이 serve되고 있는 html 문서를 찾아 수정

    • grep DocumentRoot /usr/local/apache2/conf/httpd.conf
    • cd htdocs
    • cat index.html (파일 내용 출력)
    • cat > index.html (파일 내용 덮어쓰기) 또는 cat >> index.html (기존 파일 내용 추가) → Ctrl + D 키를 눌러서 저장
      <html><body><h1>Hello World!</h1></body></html>
    • httpd-foreground (웹서버 실행)


  • exit (컨테이너 터미널 나오기 & 실행 중단)

  • docker start <container-name/id>로 다시 실행 & docker exec -it <container-name/id> /bin/bash로 컨테이너 터미널 접속 가능

  • docker commit <container-name/id> [repository-name[:tag]]으로 컨테이너를 이미지로 만들기!

    • 기존 httpd 컨테이너는 8080:80을 사용했으니 이번에는 8081:80으로 포트 포워딩
      • apache server의 default port number는 80이므로 컨테이너 포트는 80 그대로, 호스트 포트만 바꿔줌
      • 컨테이너들은 기본적으로 동일한 호스트에서 실행되기 때문에 기존 httpd 컨테이너가 실행 중이라면 포트 충돌이 발생할 수 있음 → 기존 httpd 컨테이너는 중단하고 실행해야함!

⌨️실습2: 이미지 생성 자동화

  • Dockerfile

    • 계층 구조를 이용하여 도커 이미지를 만드는 절차를 기술하는 파일 (텍스트)

    • FROM [--platform=<platform>]<image>[AS <name>]

    • RUN <command>

    • 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 | - → local에 이미지가 저장됨

    docker build -t my_httpd:0.2 .



  • 이미지 레지스트리에 올리기

    • docker login
    • docker tag <source-image> <target-image>
      • 태그를 지정하지 않으면 default로 latest 태그가 할당됨
      • 💡보통 <docker_hub-id>/<image-name> 형식의 이름 사용
    • docker push <image-name>



  • 만들어둔 컨테이너 및 이미지 모두 지우고 이미지 레지스트리에 올려둔 이미지를 다운받아 컨테이너 실행해보기

📝실습 과제

  • 문제

    • 간단한 도커 응용 만들기 - 이미지 이름(과 태그)는 hello:0.1
      • 베이스 이미지: ubuntu:22.04

      • 패키지 설치: python3 (apt-get 이용)

      • 응용 스크립트 설치: hello.py

        FROM ubuntu:22.04
        RUN apt-get update && apt-get install -y python3
        COPY hello.py .
        ENTRYPOINT ["python3", "hello.py"]
    • 로컬 이미지 이용하여 컨테이너 만들고 실행
      > docker run hello:0.1
      Hello World from Python!
  • 실습

    • COPY hello.py .라는 명령으로 현재 로컬 디렉토리에 있는 hello.py 파일을 Docker 이미지로 복사 & ENTRYPOINT 명령으로 실행

    • 따라서 Dockerfile과 같은 위치에 hello.py 파일을 작성해줌!


    • 환경 변수 사용해보기: ENV 명령어로 환경 변수를 지정


    • -e, --env option으로 환경 변수 설정 가능

    • --rm option으로 실행이 끝나면 컨테이너를 자동으로 삭제(clean up)

    • -d, --detach option으로 컨테이너를 background에서 실행

      • docker run -d --rm tomcat
      • docker logs <container-name/id>로 로그 확인 가능
      • docker inspect <container-name/id>로 도커 객체 정보 확인 가능
    • 지금은 컨테이너의 이름을 지정해주지 않아서 Docker가 이름을 무작위로 정하게 되는데 --name option으로 컨테이너 이름을 지정할 수 있음

      • docker run --rm -d -P --name my_tomcat tomcat
        • 대문자 -P option 사용 시 컨테이너의 포트를 호스트의 임의의 포트에 매핑 → docker ps에서 포트 번호 확인 가능
        • 컨테이너 이름이 my_tomcat으로 지정됨

🗃컨테이너 안의 파일들과 도커 볼륨

🤓컨테이너 안의 파일들에 접근하는 방법

  • 실행하고 있는 컨테이너와 호스트 사이의 파일 복사(호스트 ↔ 컨테이너)
  • 이미지를 빌드할 때 호스트로부터 파일을 컨테이너에 추가(호스트 → 컨테이너)
    • Dockerfile 안에 ADD 지시자 활용
  • 바인드 마운트(Bind Mount): docker run에서 -v, --volume option으로 호스트의 특정 디렉토리를 컨테이너와 공유
    • 호스트의 특정 디렉토리를 컨테이너와 직접 연결 → 컨테이너에서 호스트 파일 시스템에 직접 액세스하게 됨
    • ☠️컨테이너가 호스트의 non-Docker process에 의도치 않은 영향을 미칠 수 있음
    • 호스트의 파일 시스템 성능에 영향을 받을 수 있음 (특히 많은 파일을 읽거나 쓰는 작업이 있는 경우에는 성능 저하가 발생할 수 있음)
    • 마운트된 Host의 디렉토리가 Container 디렉토리를 덮어씀
    • 개발 중인 애플리케이션의 코드를 실시간으로 수정하고 테스트할 때, 컨테이너 내부에서 외부 파일 시스템의 데이터에 액세스해야 할 때 적합
  • 도커 볼륨(Docker Volume): docker run에서 -v, --volume option으로 호스트와 공유하는 것은 비슷하지만 마운트하는 것은 추상화된 볼륨
    • 도커 볼륨은 도커 엔진에 의해 별도의 공간에 저장되어 관리되며, 도커 호스트의 파일 시스템과는 독립적으로 작동
    • 주로 Host의 /var/lib/docker/volumes/<volume-name>/_data에 저장됨
    • 처음에 빈 볼륨을 생성하게 되고 Container에 연결하게 하게 되면 내용이 동기화
    • 일관된 환경을 제공하기 때문에 프로덕션 환경, 여러 개의 컨테이너에서 동일한 데이터에 접근해야 하는 경우에 적합

➕ 바인드 마운트와 도커 볼륨 모두 컨테이너가 종료되더라도 data를 보존하기 위해 사용
도커 볼륨을 사용하는 것을 더 추천하는 듯
Docker volumes vs. bind mounts - LogRocket Blog

📁컨테이너 안의 파일들 다루기

  • 실습 준비

    • nginx:latest 이미지로 컨테이너를 실행하되, 컨테이너의 80포트를 호스트의 8080 포트에 연결
      docker run --rm -d -p 8080:80 --name my_nginx nginx:latest
    • 호스트에서 브라우저를 실행하여 localhost:8080 주소에서 웹 접속 성공하는지 확인

  • 대체할 파일 하나 준비

    • 기본으로 표시되는 내용과 식별되는, 간단한 html 파일을 하나 준비해서 이것을 컨테이너에 보낼 준비
      • docker exec -it my_nginx /bin/bash로 컨테이너 접속

      • /usr/share/nginx/html 폴더에 50x.htmlindex.html이 존재하고 index.html이 serve되고 있음

      • 준비할 html 파일 이름을 index.html로 해서 덮어쓰기

        <!DOCTYPE html>
        <html>
        
        <head>
            <title>Welcome to nginx!</title>
            <style>
                html {
                    color-scheme: light dark;
                }
        
                body {
                    width: 35em;
                    margin: 0 auto;
                    font-family: Tahoma, Verdana, Arial, sans-serif;
                }
            </style>
        </head>
        
        <body>
            <h1>Hello</h1>
            <p>A file from Host!</p>
        </body>
        
        </html>
  • docker cp index.html my_nginx:/usr/share/nginx/html로 호스트에 있는 파일을 컨테이너로 복사 (호스트 → 컨테이너)


  • docker cp my_nginx:/usr/share/nginx/html/50x.html .로 컨테이너의 파일을 호스트로 복사 (컨테이너 → 호스트)

➕이미지 빌드에 파일 추가

  • Dockerfile 내에 ADD 지시자를 이용해 호스트가 제공하는 파일을 특정 위치에 둠
    FROM nginx:latest
    
    ADD ./index.html /usr/share/nginx/html/new.html
    CMD ["nginx", "-g", "daemon off;"]
  • docker build -t your_nginx .로 Dockerfile 기반으로 이미지를 빌드해 로컬에 저장
  • docker run --rm -d -p 8888:80 --name your_nginx your_nginx
  • localhost:8888/new.html에서 추가된 파일 확인 가능

🔗바인드 마운트

  • docker run -v <host-path>:<container-path> <image>
  • 실습
    • 호스트에 실험용 디렉토리를 하나 만들고, 이 안에 간단한 텍스트 파일 하나를 작성
    • 이미지 ubuntu:22.04를 interactive/tty 모드로 하여 /bin/bash 실행하되, 위 디렉토리를 볼륨 공유
      docker run -it -v .:/host_directory ubuntu:22.04 /bin/bash
    • 컨테이너 안에서 새로운 파일 하나를 생성, 컨테이너는 종료
    • 호스트에서 새로 생성된 파일 확인

🔗도커 볼륨

  • 도커 볼륨 생성: docer volume create <volume-name>

  • docker run --rm -d -p 8888:80 -v my-volume:/usr/share/nginx/html --name my_nginx nginx:latest
    • Linux에서는 바로 volume 경로에서 파일들을 확인할 수 있지만 Windows나 Mac에서는 VM을 사용하기 때문에 CLI로 확인 불가능
      → Docker Desktop 앱의 Volume 탭에서 확인 가능
profile
이것저것 관심 많은 개발자👩‍💻

0개의 댓글