Ssoon

1주 : Jenkins CI/CD + Docker : 1 본문

CICD 맛보기

1주 : Jenkins CI/CD + Docker : 1

구구달스 2024. 12. 2. 21:54
CloudNet@ 가시다님이 CI/CD 맛보기 스터디

✅ 실습환경구성

  • Oracle VirtualBox 에 RockyLinux을 설치합니다.
  • VSCode를 설치하고 RockyLinux 에 원격접속 합니다.

  • VSCode 의 Extension 을 클릭하여 Remote -SSH 을 설치합니다.

  • Remote -SSH 의 설정파일을 설정합니다.

  • .ssh\config

Host myserver
    HostName 192.168.56.105
    User ssoon
    #IdentityFile ~/.ssh/mykey.pem

  • RockyLinux 에 접속합니다.

  • 작업폴더를 선택합니다.

  • docker 를 설치합니다.
[ssoon@localhost CICD]$ sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo

[ssoon@localhost CICD]$ sudo dnf -y install docker-ce docker-ce-cli containerd.io docker-compose-plugin

[ssoon@localhost CICD]$ sudo systemctl --now enable docker

[ssoon@localhost CICD]$ docker --version
Docker version 27.3.1, build ce12230

[ssoon@localhost CICD]$ sudo usermod -a -G docker $(whoami)

[ssoon@localhost CICD]$ newgrp docker

✅ 컨테이너를 활용한 애플리케이션 개발

🧿 python 으로 특정 문자열 출력

  • echo "print ('Hello Docker')" > hello.py
    • "print ('Hello Docker')" 내용을 가진 hello.py라는 파일을 생성합니다.
  • FROM python:3
    • Python 3 버전이 설치된 Docker 이미지를 기반으로 사용합니다.
  • COPY . /app
    • 현재 디렉토리(.)의 모든 파일을 Docker 컨테이너 내부의 /app 디렉토리에 복사합니다.
  • WORKDIR /app
    • 컨테이너에서 작업 디렉토리를 /app으로 설정합니다.
    •  
  • CMD ["python3", "hello.py"]
    • 컨테이너 실행 시 기본적으로 python3 hello.py 명령을 실행하도록 설정합니다.
[ssoon@localhost CICD]$ mkdir 1.1 && cd 1.1
[ssoon@localhost 1.1]$ echo "print ('Hello Docker')" > hello.py
[ssoon@localhost 1.1]$ cat > Dockerfile <<EOF
FROM python:3
COPY . /app
WORKDIR /app 
CMD ["python3", "hello.py"]
EOF
  • docker pull python:3
    • Python 3 버전이 포함된 Docker 이미지를 DockerHub에서 다운로드합니다.
[ssoon@localhost CICD]$ docker pull python:3
3: Pulling from library/python
b2b31b28ee3c: Pull complete 
c3cc7b6f0473: Pull complete 
2112e5e7c3ff: Pull complete 
af247aac0764: Pull complete 
ef45f15f570b: Pull complete 
4d87d670c3ff: Pull complete 
3ed3f00b0d2c: Pull complete 
Digest: sha256:bc78d3c007f86dbb87d711b8b082d9d564b8025487e780d24ccb8581d83ef8b0
Status: Downloaded newer image for python:3
docker.io/library/python:3

[ssoon@localhost CICD]$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
python       3         c41ea8273365   6 weeks ago   1.02GB
  • docker build .: 현재 디렉토리(.)에 있는 Dockerfile을 기반으로 Docker 이미지를 생성합니다.
  • -t hello: 생성된 이미지를 hello라는 이름으로 태그 지정합니다.
[ssoon@localhost 1.1]$ docker build . -t hello
[+] Building 0.2s (8/8) FINISHED                                                                                docker:default
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 164B                                                                                      0.0s
 => [internal] load metadata for docker.io/library/python:3                                                               0.0s
 => [internal] load .dockerignore                                                                                         0.0s
 => => transferring context: 2B                                                                                           0.0s
 => [internal] load build context                                                                                         0.0s
 => => transferring context: 250B                                                                                         0.0s
 => CACHED [1/3] FROM docker.io/library/python:3                                                                          0.0s
 => [2/3] COPY . /app                                                                                                     0.0s
 => [3/3] WORKDIR /app                                                                                                    0.0s
 => exporting to image                                                                                                    0.0s
 => => exporting layers                                                                                                   0.0s
 => => writing image sha256:e374a4b54a5c14ad8e1c9324b238a03f39256e5fbff04ad0a360b42e54d4124a                              0.0s
 => => naming to docker.io/library/hello                                                                                  0.0s
  • docker image ls: 로컬에 저장된 Docker 이미지를 나열합니다.
  • -f reference=hello: 이름이 hello인 이미지만 필터링하여 표시합니다.
[ssoon@localhost 1.1]$ docker image ls -f reference=hello
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        latest    e374a4b54a5c   40 seconds ago   1.02GB
  • docker run: hello 이미지를 기반으로 컨테이너를 실행합니다.
  • --rm: 컨테이너 실행이 종료되면 해당 컨테이너를 자동으로 삭제합니다.
  • hello: 실행할 이미지 이름.
[ssoon@localhost 1.1]$ docker run --rm hello
Hello Docker
  • Python 스크립트의 출력을 Hello Docker에서 Hello SSOON으로 변경합니다.
[ssoon@localhost 1.1]$ echo "print ('Hello SSOON')" > hello.py
  • docker build .: 현재 디렉토리(.)의 Dockerfile을 기반으로 Docker 이미지를 빌드합니다.
  • -t hello:1: 빌드된 이미지를 hello라는 이름으로 태그하고, 버전을 1로 지정합니다.
[ssoon@localhost 1.1]$ docker build . -t hello:1
[+] Building 0.2s (8/8) FINISHED                                                                                docker:default
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 164B                                                                                      0.0s
 => [internal] load metadata for docker.io/library/python:3                                                               0.0s
 => [internal] load .dockerignore                                                                                         0.0s
 => => transferring context: 2B                                                                                           0.0s
 => [internal] load build context                                                                                         0.0s
 => => transferring context: 208B                                                                                         0.0s
 => CACHED [1/3] FROM docker.io/library/python:3                                                                          0.0s
 => [2/3] COPY . /app                                                                                                     0.0s
 => [3/3] WORKDIR /app                                                                                                    0.0s
 => exporting to image                                                                                                    0.0s
 => => exporting layers                                                                                                   0.0s
 => => writing image sha256:412d5f194ec4e407c5a960c4e616ee80480cbaa65c441dd14b135c6cd19148a8                              0.0s
 => => naming to docker.io/library/hello:1                                                                                0.0s
  • docker tag: 기존 이미지를 새로운 이름 또는 태그로 재지정합니다.
  • hello:1 hello:latest: hello:1 이미지를 hello:latest로 태그합니다.
[ssoon@localhost 1.1]$ docker image ls -f reference=hello
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        1         412d5f194ec4   20 seconds ago   1.02GB
hello        latest    e374a4b54a5c   2 minutes ago    1.02GB

[ssoon@localhost 1.1]$ docker tag hello:1 hello:latest

[ssoon@localhost 1.1]$ docker image ls -f reference=hello
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        1         412d5f194ec4   57 seconds ago   1.02GB
hello        latest    412d5f194ec4   57 seconds ago   1.02GB
  • Python 코드 변경(hello.py)이 Docker 빌드 과정을 통해 컨테이너에 반영되었으며, 성공적으로 업데이트되었습니다.
[ssoon@localhost 1.1]$ docker run --rm hello:1
Hello SSOON

[ssoon@localhost 1.1]$ docker run --rm hello
Hello SSOON

🧿 Docker에서 코드 컴파일하기

  • Hello 클래스: 프로그램의 기본 구조.
  • main 메서드: 프로그램 실행 시작 지점.
  • System.out.println: 표준 출력으로 "Hello Docker" 메시지를 출력합니다.
[ssoon@localhost CICD]$ mkdir 1.2 && cd 1.2
[ssoon@localhost 1.2]$ cat > Hello.java <<EOF
class Hello {
    public static void main(String[] args) {
        System.out.println("Hello Docker");
    }
}
EOF

 

  • FROM openjdk:
    • Docker 이미지를 빌드할 때 기반이 되는 OpenJDK 이미지를 사용합니다.
  • COPY . /app:
    • 현재 디렉토리(.)의 모든 파일과 디렉토리를 컨테이너 내부의 /app 디렉토리에 복사합니다.
  • WORKDIR /app:
    • 작업 디렉토리를 /app으로 설정합니다.
  • RUN javac Hello.java:
    • 컨테이너 내에서 javac 명령을 실행하여 Hello.java 파일을 컴파일합니다.
    • 결과로 Hello.class 파일이 생성됩니다.
  • CMD ["java", "Hello"]:
    • 컨테이너가 실행될 때 java Hello 명령을 실행합니다.
[ssoon@localhost 1.2]$ cat > Dockerfile <<EOF
FROM openjdk
COPY . /app
WORKDIR /app
RUN javac Hello.java  
CMD ["java", "Hello"]
EOF
  • openjdk 이미지를 필터링하여 목록을 출력합니다.
[ssoon@localhost 1.2]$ docker image ls -f reference=openjdk
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
openjdk      latest    71260f256d19   22 months ago   470MB
  • 최신 OpenJDK 이미지를 다운로드합니다.
[ssoon@localhost 1.2]$ docker pull openjdk
Using default tag: latest
latest: Pulling from library/openjdk
197c1adcd755: Pull complete 
57b698b7af4b: Pull complete 
95a27dbe0150: Pull complete 
Digest: sha256:9b448de897d211c9e0ec635a485650aed6e28d4eca1efbc34940560a480b3f1f
Status: Downloaded newer image for openjdk:latest
docker.io/library/openjdk:latest

 

  • Dockerfile을 사용해 새로운 Docker 이미지를 빌드하고, 이 이미지를 hello:2 라는 태그로 지정합니다.
  • Docker 이미지 hello:2  latest 태그를 추가합니다.
  • hello 라는 이름을 가진 이미지만 필터링하여 목록을 출력합니다.
[ssoon@localhost 1.2]$ docker build . -t hello:2
[+] Building 1.2s (9/9) FINISHED                                                                                docker:default
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 202B                                                                                      0.0s
 => [internal] load metadata for docker.io/library/openjdk:latest                                                         0.0s
 => [internal] load .dockerignore                                                                                         0.0s
 => => transferring context: 2B                                                                                           0.0s
 => [internal] load build context                                                                                         0.0s
 => => transferring context: 290B                                                                                         0.0s
 => CACHED [1/4] FROM docker.io/library/openjdk:latest                                                                    0.0s
 => [2/4] COPY . /app                                                                                                     0.0s
 => [3/4] WORKDIR /app                                                                                                    0.0s
 => [4/4] RUN javac Hello.java    # The complie command                                                                   0.9s
 => exporting to image                                                                                                    0.1s
 => => exporting layers                                                                                                   0.1s
 => => writing image sha256:752de94919a32290f134b83fb82793d11828029b9baa144870b5a590005c1b68                              0.0s
 => => naming to docker.io/library/hello:2                                                                                0.0s

[ssoon@localhost 1.2]$ docker tag hello:2 hello:latest

[ssoon@localhost 1.2]$ docker image ls -f reference=hello
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        2         752de94919a3   11 seconds ago   470MB
hello        latest    752de94919a3   11 seconds ago   470MB
hello        1         412d5f194ec4   7 minutes ago    1.02GB
  • 컨테이너 내에서 java Hello 가 실행되었으며, 그 결과  Hello Docker 라는 메시지가 출력되었습니다.
  • 컨테이너 내에 Dockerfile, Hello.class, Hello.java 파일이 포함되어 있음을 확인할 수 있습니다.
[ssoon@localhost 1.2]$ docker run --rm hello:2
Hello Docker

[ssoon@localhost 1.2]$ docker run --rm hello
Hello Docker

[ssoon@localhost 1.2]$ docker run --rm hello ls -l
total 12
-rw-r--r--. 1 root root 106 Dec  2 12:21 Dockerfile
-rw-r--r--. 1 root root 416 Dec  2 12:21 Hello.class
-rw-r--r--. 1 root root 111 Dec  2 12:17 Hello.java

🧿 multistage 빌드로 코드 컴파일하기

멀티스테이지 빌드란?

멀티스테이지 빌드는 Docker 이미지 빌드를 여러 단계로 나누어 진행하는 방식입니다. 각 단계에서 FROM 명령을 사용하여 서로 다른 이미지를 기반으로 작업을 할 수 있으며, 이전 단계에서 빌드된 파일을 후속 단계로 복사해서 사용할 수 있습니다. 이렇게 하면, 최종 이미지에서 불필요한 빌드 도구나 파일을 제외할 수 있어 이미지 크기가 작아지고, 더 효율적이고 깨끗한 이미지를 생성할 수 있습니다.

 

일반적인 빌드에는 소스 코드 파일을 컴파일하기 위한 도구들이 필요하지만, 최종적으로는 컴파일된 파일만 최종 이미지에 포함되어야 합니다. 멀티스테이지 빌드를 사용하면, 빌드 도구들을 최종 이미지에서 제외할 수 있습니다.

 

1단계: 빌드 단계

  • openjdk:11 이미지를 기반으로 빌드 환경을 설정합니다. 이 단계에서는 Java Development Kit (JDK)를 포함한 이미지가 사용됩니다.
  • AS buildstage: 이 단계의 이름을 buildstage로 지정합니다. 이 이름은 후속 단계에서 참조할 때 사용됩니다.
  • RUN javac Hello.java: Java 소스 파일인 Hello.java 를 컴파일하여 Hello.class 파일을 생성합니다. 이 클래스 파일은 후속 단계에서 실행될 것입니다.

2단계: 실행 단계

  • 이번 단계에서는 더 가벼운 openjdk:11-jre-slim 이미지를 사용합니다. 이 이미지는 Java Runtime Environment (JRE) 만 포함되어 있어, 애플리케이션을 실행하는 데 필요한 최소 환경만 제공합니다. 빌드 도구는 포함되지 않기 때문에, 최종 이미지 크기를 줄일 수 있습니다.
  • COPY --from=buildstage /app/Hello.class /app/:
    • --from=buildstage: 첫 번째 단계(buildstage)에서 빌드된 Hello.class 파일을 가져옵니다.
    • /app/Hello.class 파일을 현재 컨테이너의 /app 디렉토리로 복사합니다.
FROM openjdk:11 AS buildstage
COPY . /app
WORKDIR /app
RUN javac Hello.java

FROM openjdk:11-jre-slim
COPY --from=buildstage /app/Hello.class /app/
WORKDIR /app
CMD ["java", "Hello"]
  • docker build . -t hello:3 는 이미지를 빌드하고 hello:3 태그를 지정하는 명령입니다.
  • docker tag hello:3 hello:latest 는 기존에 빌드된 hello:3 이미지를 **hello:latest**라는 새로운 태그로 추가하는 명령입니다.
  • 결과적으로 hello:3 과 hello:latest 는 동일한 이미지를 가리키게 됩니다.
[ssoon@localhost 1.3]$ docker build . -t hello:3
[+] Building 28.6s (13/13) FINISHED                                                                             docker:default
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 281B                                                                                      0.0s
 => [internal] load metadata for docker.io/library/openjdk:11-jre-slim                                                    2.4s
 => [internal] load metadata for docker.io/library/openjdk:11                                                             2.3s
 => [internal] load .dockerignore                                                                                         0.0s
 => => transferring context: 2B                                                                                           0.0s
 => [internal] load build context                                                                                         0.0s
 => => transferring context: 512B                                                                                         0.0s
 => [stage-1 1/3] FROM docker.io/library/openjdk:11-jre-slim@sha256:93af7df2308c5141a751c4830e6b6c5717db102b3b31f012ea2  13.1s
 => => resolve docker.io/library/openjdk:11-jre-slim@sha256:93af7df2308c5141a751c4830e6b6c5717db102b3b31f012ea29d842dc4f  0.0s
 => => sha256:12cca292b13cb58fadde25af113ddc4ac3b0c5e39ab3f1290a6ba62ec8237afd 212B / 212B                                0.3s
 => => sha256:93af7df2308c5141a751c4830e6b6c5717db102b3b31f012ea29d842dc4f2b02 549B / 549B                                0.0s
 => => sha256:884c08d0f406a81ae1b5786932abaf399c335b997da7eea6a30cc51529220b66 1.16kB / 1.16kB                            0.0s
 => => sha256:764a04af3eff09cc6a29bcc19cf6315dbea455d7392c1a588a5deb331a929c29 7.55kB / 7.55kB                            0.0s
 => => sha256:1efc276f4ff952c055dea726cfc96ec6a4fdb8b62d9eed816bd2b788f2860ad7 31.37MB / 31.37MB                          1.6s
 => => sha256:a2f2f93da48276873890ac821b3c991d53a7e864791aaf82c39b7863c908b93b 1.58MB / 1.58MB                            0.6s
 => => sha256:d73cf48caaac2e45ad76a2a9eb3b311d0e4eb1d804e3d2b9cf075a1fa31e6f92 46.04MB / 46.04MB                          3.0s
 => => extracting sha256:1efc276f4ff952c055dea726cfc96ec6a4fdb8b62d9eed816bd2b788f2860ad7                                 9.2s
 => => extracting sha256:a2f2f93da48276873890ac821b3c991d53a7e864791aaf82c39b7863c908b93b                                 0.2s
 => => extracting sha256:12cca292b13cb58fadde25af113ddc4ac3b0c5e39ab3f1290a6ba62ec8237afd                                 0.0s
 => => extracting sha256:d73cf48caaac2e45ad76a2a9eb3b311d0e4eb1d804e3d2b9cf075a1fa31e6f92                                 1.5s
 => [buildstage 1/4] FROM docker.io/library/openjdk:11@sha256:99bac5bf83633e3c7399aed725c8415e7b569b54e03e4599e580fc9cd  24.4s
 => => resolve docker.io/library/openjdk:11@sha256:99bac5bf83633e3c7399aed725c8415e7b569b54e03e4599e580fc9cdb7c21ab       0.0s
 => => sha256:e81b7f317654b0f26d3993e014b04bcb29250339b11b9de41e130feecd4cd43c 1.79kB / 1.79kB                            0.0s
 => => sha256:99bac5bf83633e3c7399aed725c8415e7b569b54e03e4599e580fc9cdb7c21ab 1.04kB / 1.04kB                            0.0s
 => => sha256:47a932d998b743b9b0bcce55aa8ede77de94a6a183c8a67dec9d5e3b8ce0faa7 6.26kB / 6.26kB                            0.0s
 => => sha256:001c52e26ad57e3b25b439ee0052f6692e5c0f2d5d982a00a8819ace5e521452 55.00MB / 55.00MB                          4.0s
 => => sha256:d9d4b9b6e964657da49910b495173d6c4f0d9bc47b3b44273cf82fd32723d165 5.16MB / 5.16MB                            2.3s
 => => sha256:2068746827ec1b043b571e4788693eab7e9b2a95301176512791f8c317a2816a 10.88MB / 10.88MB                          3.1s
 => => sha256:9daef329d35093868ef75ac8b7c6eb407fa53abbcb3a264c218c2ec7bca716e6 54.58MB / 54.58MB                          5.6s
 => => sha256:d85151f15b6683b98f21c3827ac545188b1849efb14a1049710ebc4692de3dd5 5.42MB / 5.42MB                            3.8s
 => => sha256:66223a710990a0ae7162aeed80417d30303afa3f24aafa57aa30348725e2230b 213B / 213B                                4.1s
 => => sha256:db38d58ec8ab4111b072f6700f978a51985acd252aabce3be377f25162e68301 202.07MB / 202.07MB                       10.6s
 => => extracting sha256:001c52e26ad57e3b25b439ee0052f6692e5c0f2d5d982a00a8819ace5e521452                                 9.2s
 => => extracting sha256:d9d4b9b6e964657da49910b495173d6c4f0d9bc47b3b44273cf82fd32723d165                                 0.5s
 => => extracting sha256:2068746827ec1b043b571e4788693eab7e9b2a95301176512791f8c317a2816a                                 0.4s
 => => extracting sha256:9daef329d35093868ef75ac8b7c6eb407fa53abbcb3a264c218c2ec7bca716e6                                 4.6s
 => => extracting sha256:d85151f15b6683b98f21c3827ac545188b1849efb14a1049710ebc4692de3dd5                                 0.4s
 => => extracting sha256:66223a710990a0ae7162aeed80417d30303afa3f24aafa57aa30348725e2230b                                 0.0s
 => => extracting sha256:db38d58ec8ab4111b072f6700f978a51985acd252aabce3be377f25162e68301                                 4.6s
 => [buildstage 2/4] COPY . /app                                                                                          0.6s
 => [buildstage 3/4] WORKDIR /app                                                                                         0.0s
 => [buildstage 4/4] RUN javac Hello.java                                                                                 0.9s
 => [stage-1 2/3] COPY --from=buildstage /app/Hello.class /app/                                                           0.0s
 => [stage-1 3/3] WORKDIR /app                                                                                            0.0s
 => exporting to image                                                                                                    0.0s
 => => exporting layers                                                                                                   0.0s
 => => writing image sha256:50d8140fb24673e94e6b5edb220da6462882bac4206c296f758095e9aa1eb054                              0.0s
 => => naming to docker.io/library/hello:3                                                                                0.0s

[ssoon@localhost 1.3]$ docker tag hello:3 hello:latest

[ssoon@localhost 1.3]$ docker image ls -f reference=hello
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
hello        3         50d8140fb246   14 seconds ago   223MB
hello        latest    50d8140fb246   14 seconds ago   223MB
hello        2         752de94919a3   5 minutes ago    470MB
hello        1         412d5f194ec4   12 minutes ago   1.02GB
  • docker run --rm hello:3  docker run --rm hello 명령을 실행하면, 두 경우 모두 Hello Multistage container build 메시지가 출력됩니다. 두 이미지는 동일한 실행 결과를 가집니다.
  • docker run --rm hello ls -l 명령을 실행하면, Hello.class 파일이 /app 디렉토리에 존재하는 것을 확인할 수 있습니다. 이는 Java 소스 코드가 성공적으로 컴파일되었음을 의미합니다.
[ssoon@localhost 1.3]$ docker run --rm hello:3
Hello Multistage container build

[ssoon@localhost 1.3]$ docker run --rm hello
Hello Multistage container build

[ssoon@localhost 1.3]$ docker run --rm hello ls -l
total 4
-rw-r--r--. 1 root root 436 Dec  2 12:27 Hello.class

🧿 애플리케이션 서버 컨테이너화

  • 간단한 HTTP 서버를 설정하고, 클라이언트가 GET 요청을 보내면 현재 시간을 반환하는 방식으로 동작합니다. 
[ssoon@localhost 1.5]$ cat > server.py <<EOF
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from datetime import datetime

class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        now = datetime.now()
        response_string = now.strftime("The time is %-I:%M %p, UTC.\n")
        self.wfile.write(bytes(response_string, "utf-8")) 

def startServer():
    try:
        server = ThreadingHTTPServer(('', 80), RequestHandler)
        print("Listening on " + ":".join(map(str, server.server_address)))
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

if __name__== "__main__":
    startServer()
EOF
  • Python 3.12 버전을 기반으로 한 이미지를 사용하여, 애플리케이션 파일을 /app 디렉토리에 복사하고, 그 디렉토리에서 server.py 파일을 실행하는 설정입니다. PYTHONUNBUFFERED 환경 변수를 설정하여 Python 출력에 버퍼링을 사용하지 않도록 하고, 이를 통해 실시간 로그 출력을 가능하게 합니다.
[ssoon@localhost 1.5]$ cat > Dockerfile <<EOF
FROM python:3.12
ENV PYTHONUNBUFFERED=1
COPY . /app
WORKDIR /app 
CMD ["python3", "server.py"]
EOF
  • Docker Hub에서 python:3.12 이미지를 다운로드합니다.
[ssoon@localhost 1.5]$ docker pull python:3.12
3.12: Pulling from library/python
b2b31b28ee3c: Already exists 
c3cc7b6f0473: Already exists 
2112e5e7c3ff: Already exists 
af247aac0764: Already exists 
c7e62dc73b01: Pull complete 
35c3ce70330a: Pull complete 
f8af1611cc8d: Pull complete 
Digest: sha256:f71437b2bad6af0615875c8f7fbeeeae1b73e3c76b82056d283644aca5afe355
Status: Downloaded newer image for python:3.12
docker.io/library/python:3.12

[ssoon@localhost 1.5]$ docker image ls -f reference=python:3.12
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
python       3.12      0386d5b29b17   6 weeks ago   1.02GB
  • docker build . -t timeserver:1:
    • 현재 디렉토리(.)에 있는 Dockerfile을 사용하여 Docker 이미지를 빌드합니다.
    • 빌드된 이미지는 timeserver:1이라는 태그를 붙여 저장됩니다..
  • docker tag timeserver:1 timeserver:latest:
    • timeserver:1 이미지를 timeserver:latest라는 새로운 태그로 덮어씁니다.
    • 이를 통해 timeserver 이미지는 1 버전과 latest라는 두 가지 태그를 가질 수 있게 됩니다. 
[ssoon@localhost 1.5]$ docker build . -t timeserver:1 && docker tag timeserver:1 timeserver:latest
[+] Building 0.2s (8/8) FINISHED                                                                                docker:default
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 191B                                                                                      0.0s
 => [internal] load metadata for docker.io/library/python:3.12                                                            0.0s
 => [internal] load .dockerignore                                                                                         0.0s
 => => transferring context: 2B                                                                                           0.0s
 => [internal] load build context                                                                                         0.0s
 => => transferring context: 279B                                                                                         0.0s
 => CACHED [1/3] FROM docker.io/library/python:3.12                                                                       0.0s
 => [2/3] COPY . /app                                                                                                     0.0s
 => [3/3] WORKDIR /app                                                                                                    0.0s
 => exporting to image                                                                                                    0.0s
 => => exporting layers                                                                                                   0.0s
 => => writing image sha256:f16d483d07fd19157f6a06e8cd139ab35e8a7922a97cc0dabb1e94db3987321e                              0.0s
 => => naming to docker.io/library/timeserver:1  
 
 [ssoon@localhost 1.5]$ docker image ls -f reference=timeserver
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
timeserver   1         f16d483d07fd   21 seconds ago   1.02GB
timeserver   latest    f16d483d07fd   21 seconds ago   1.02GB
  • docker run -d -p 8080:80 --name=timeserver timeserver: 컨테이너를 백그라운드에서 실행하고, 8080 포트를 통해 접근할 수 있게 설정.
  • curl http://localhost:8080: 컨테이너의 웹 서버에 HTTP 요청을 보내 현재 시간을 받아옴.
  • docker logs timeserver: 컨테이너의 로그를 조회하여 서버가 정상적으로 동작하는지 확인.
  • docker exec -it timeserver ls -l: 컨테이너 내부에서 파일 목록을 확인.
  • docker rm -f timeserver: 컨테이너를 강제로 종료하고 삭제.
[ssoon@localhost 1.5]$ docker run -d -p 8080:80 --name=timeserver timeserver
564ddacfd6e5ad84c460a4197c3f1d3369bcc5e268ab432d6e10a6bebb294cc9

[ssoon@localhost 1.5]$ curl http://localhost:8080
The time is 12:40 PM, UTC.

[ssoon@localhost 1.5]$ docker logs timeserver
Listening on 0.0.0.0:80
172.17.0.1 - - [02/Dec/2024 12:40:38] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [02/Dec/2024 12:40:44] "GET / HTTP/1.1" 200 -
172.17.0.1 - - [02/Dec/2024 12:40:44] "GET /favicon.ico HTTP/1.1" 200 -

[ssoon@localhost 1.5]$ docker exec -it timeserver ls -l
total 8
-rw-r--r--. 1 root root  95 Dec  2 12:39 Dockerfile
-rw-r--r--. 1 root root 740 Dec  2 12:33 server.py

[ssoon@localhost 1.5]$ docker rm -f timeserver
timeserver
  • 코드를 변경하고 이미지 빌드합니다.
        response_string = now.strftime("The time is %-I:%M %p, SSOON.\n")
        
 
 [ssoon@localhost 1.5]$ docker build . -t timeserver:2 && docker tag timeserver:2 timeserver:latest
[+] Building 0.2s (8/8) FINISHED                                                                                docker:default
 => [internal] load build definition from Dockerfile                                                                      0.0s
 => => transferring dockerfile: 191B                                                                                      0.0s
 => [internal] load metadata for docker.io/library/python:3.12                                                            0.0s
 => [internal] load .dockerignore                                                                                         0.0s
 => => transferring context: 2B                                                                                           0.0s
 => [internal] load build context                                                                                         0.0s
 => => transferring context: 931B                                                                                         0.0s
 => CACHED [1/3] FROM docker.io/library/python:3.12                                                                       0.0s
 => [2/3] COPY . /app                                                                                                     0.0s
 => [3/3] WORKDIR /app                                                                                                    0.0s
 => exporting to image                                                                                                    0.0s
 => => exporting layers                                                                                                   0.0s
 => => writing image sha256:d5f9dbb0ef297a3fa86426a80844910b7012a9018d4c01fd0ec2d025500acefb                              0.0s
 => => naming to docker.io/library/timeserver:2                                                                           0.0s
 
 [ssoon@localhost 1.5]$ docker image ls -f reference=timeserver
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
timeserver   2         d5f9dbb0ef29   24 seconds ago   1.02GB
timeserver   latest    d5f9dbb0ef29   24 seconds ago   1.02GB
timeserver   1         f16d483d07fd   4 minutes ago    1.02GB

[ssoon@localhost 1.5]$ docker run -d -p 8080:80 --name=timeserver timeserver
3b0fe57766de1389a2011afea2d440b660ccdea7eca982490660ddcf1fd646e0

[ssoon@localhost 1.5]$ curl http://localhost:8080
The time is 12:44 PM, SSOON.

[ssoon@localhost 1.5]$ docker rm -f timeserver
timeserver

🧿 로컬 테스트에 Docker Compose 사용

 

[ssoon@localhost CICD]$ mkdir 1.6 && cd 1.6

[ssoon@localhost 1.6]$ cat > server.py <<EOF
from reloading import reloading
from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
from datetime import datetime

class RequestHandler(BaseHTTPRequestHandler):
    @reloading   # By adding the @reloading tag to our method, it will be reloaded from disk every time it runs so we can change our do_GET function while it’s running.
    def do_GET(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/plain')
        self.end_headers()
        now = datetime.now()
        response_string = now.strftime("The time is %H:%M:%S, Docker End.")
        self.wfile.write(bytes(response_string,"utf-8")) 

def startServer():
    try:
        server = ThreadingHTTPServer(('',80), RequestHandler)
        print("Listening on " + ":".join(map(str, server.server_address)))
        server.serve_forever()
    except KeyboardInterrupt:
        server.shutdown()

if __name__== "__main__":
    startServer()
EOF
  • FROM python:3
    • 이 명령어는 Python 3 이미지를 기반으로 컨테이너를 생성합니다.
  • RUN pip install reloading
    • 이 명령어는 Python 패키지 관리자인 pip를 사용하여 reloading이라는 Python 패키지를 설치합니다.
    • reloading은 보통 코드 변경 시 서버를 자동으로 재시작하도록 도와주는 패키지입니다. 
  • ENV PYTHONUNBUFFERED=1
    • PYTHONUNBUFFERED 환경 변수를 설정하여 Python 출력이 버퍼링되지 않도록 만듭니다.
    • 이 설정은 Python 애플리케이션의 로그나 출력이 실시간으로 바로 표시되도록 하기 위해 사용됩니다. Docker 컨테이너 내에서 로그를 실시간으로 확인하려는 경우 유용합니다.
  • COPY . /app
    • 현재 디렉토리(호스트 머신의 .)의 모든 파일을 컨테이너의 /app 디렉토리로 복사합니다.
  • WORKDIR /app
    • 작업 디렉토리를 /app으로 설정합니다.
  • CMD ["python3", "server.py"]
    • 컨테이너가 실행될 때 기본적으로 실행할 명령을 설정합니다.
    • python3 server.py 명령어는 /app 디렉토리 내에 있는 server.py 파일을 실행하는 명령입니다.
    • 이 명령어는 컨테이너가 시작될 때 자동으로 실행됩니다.
[ssoon@localhost 1.6]$ cat > Dockerfile <<EOF
FROM python:3
RUN pip install reloading
ENV PYTHONUNBUFFERED=1
COPY . /app
WORKDIR /app 
CMD ["python3", "server.py"]
EOF
  • services:
    • Docker Compose에서는 여러 개의 서비스(컨테이너)를 정의할 수 있습니다. 이 services 항목 아래에 각 서비스(컨테이너)를 나열합니다. 여기서는 frontend라는 이름의 서비스가 정의되어 있습니다.
  • frontend 서비스:
    • 이 서비스는 Docker Compose가 실행할 하나의 컨테이너를 정의합니다.
  • build: .:
    • build: .는 현재 디렉토리(.)에서 Dockerfile을 사용하여 이미지를 빌드하도록 설정합니다.
  • command: python3 server.py:
    • 이 명령은 컨테이너가 시작될 때 실행할 명령을 설정합니다.
    • python3 server.py 명령이 실행되어 server.py 파일을 Python 3로 실행합니다.
  • volumes:
    • volumes 항목은 로컬 파일 시스템과 Docker 컨테이너 간의 파일 공유를 설정합니다.
    • type: bind는 로컬 파일 시스템의 특정 디렉토리와 컨테이너 내의 디렉토리를 연결하는 방법입니다.
    • source: .는 현재 디렉토리(호스트 머신)를, target: /app은 해당 디렉토리를 컨테이너 내 /app 디렉토리로 마운트합니다.
    • 이 설정을 통해 로컬에서 코드가 변경될 때, 변경된 파일이 컨테이너 내 /app 디렉토리에도 즉시 반영됩니다. 이로 인해 코드 변경 후 컨테이너를 재시작하지 않고도 바로 수정된 코드를 사용할 수 있습니다.
  • environment:
    • environment 항목은 컨테이너 내에서 설정할 환경 변수를 정의합니다.
    • PYTHONDONTWRITEBYTECODE: 1은 Python이 .pyc 바이트코드 파일을 생성하지 않도록 설정합니다. 이는 코드 변경 시 Python이 소스 파일을 다시 로드하게 도와줍니다.또한, 불필요한 .pyc 파일이 컨테이너 내에 생성되지 않도록 합니다.
  • ports:
    • ports 항목은 호스트와 컨테이너 간의 포트 매핑을 설정합니다.
    • "8080:80"은 호스트의 8080 포트를 컨테이너의 80 포트로 매핑합니다.
    • 이를 통해 로컬 머신에서 localhost:8080으로 접속하면 컨테이너 내에서 실행 중인 애플리케이션에 접근할 수 있습니다.
[ssoon@localhost 1.6]$ cat > docker-compose.yaml <<EOF
services:
  frontend:
    build: .
    command: python3 server.py
    volumes:
      - type: bind
        source: .
        target: /app
    environment:
      PYTHONDONTWRITEBYTECODE: 1  # Sets a new environment variable so that Python can be made to reload our source
    ports:
      - "8080:80"
EOF
  • docker compose build로 이미지를 빌드
  • docker compose up -d로 컨테이너 실행
  • curl http://localhost:8080으로 서비스 확인
  • docker compose logs로 로그 확인
[ssoon@localhost 1.6]$ docker compose build; docker compose up -d
[+] Building 3.3s (10/10) FINISHED                                                                              docker:default
 => [frontend internal] load build definition from Dockerfile                                                             0.0s
 => => transferring dockerfile: 214B                                                                                      0.0s
 => [frontend internal] load metadata for docker.io/library/python:3                                                      0.0s
 => [frontend internal] load .dockerignore                                                                                0.0s
 => => transferring context: 2B                                                                                           0.0s
 => CACHED [frontend 1/4] FROM docker.io/library/python:3                                                                 0.0s
 => [frontend internal] load build context                                                                                0.0s
 => => transferring context: 1.67kB                                                                                       0.0s
 => [frontend 2/4] RUN pip install reloading                                                                              2.9s
 => [frontend 3/4] COPY . /app                                                                                            0.1s 
 => [frontend 4/4] WORKDIR /app                                                                                           0.0s 
 => [frontend] exporting to image                                                                                         0.2s 
 => => exporting layers                                                                                                   0.2s 
 => => writing image sha256:461aaa9fccc9b2c90a8ba5cac91eca954dca4e03af7946752c55f7a10ad253ca                              0.0s 
 => => naming to docker.io/library/16-frontend                                                                            0.0s 
 => [frontend] resolving provenance for metadata file                                                                     0.0s
[+] Running 2/2
 ✔ Network 16_default       Created                                                                                       0.4s 
 ✔ Container 16-frontend-1  Started                                                                                       0.4s
 
[ssoon@localhost 1.6]$ docker compose ps
NAME            IMAGE         COMMAND               SERVICE    CREATED          STATUS          PORTS
16-frontend-1   16-frontend   "python3 server.py"   frontend   51 seconds ago   Up 50 seconds   0.0.0.0:8080->80/tcp, [::]:8080->80/tcp

[ssoon@localhost 1.6]$ docker compose images
CONTAINER           REPOSITORY          TAG                 IMAGE ID            SIZE
16-frontend-1       16-frontend         latest              461aaa9fccc9        1.03GB

[ssoon@localhost 1.6]$ curl http://localhost:8080
The time is 12:49:39, Docker End.

[ssoon@localhost 1.6]$ docker compose logs
frontend-1  | Listening on 0.0.0.0:80
frontend-1  | 172.18.0.1 - - [02/Dec/2024 12:49:39] "GET / HTTP/1.1" 200 -
  • 호스트에서 server.py 코드 수정(볼륨 공유 상태) 후 curl 접속 시 자동 반영을 확인합니다.
        response_string = now.strftime("The time is %H:%M:%S, Docker END SSOON.")
        

[ssoon@localhost 1.6]$ curl http://localhost:8080
The time is 12:52:00, Docker END SSOON.

[ssoon@localhost 1.6]$ docker compose down
[+] Running 2/2
 ✔ Container 16-frontend-1  Removed                                                                                      10.2s 
 ✔ Network 16_default       Removed                                                                                       0.3s
 

'CICD 맛보기' 카테고리의 다른 글

2주 : GitHub Actions CI/CD : 2  (1) 2024.12.12
2주 : GitHub Actions CI/CD : 1  (0) 2024.12.11
1주 : Jenkins CI/CD + Docker : 4  (2) 2024.12.05
1주 : Jenkins CI/CD + Docker : 3  (0) 2024.12.04
1주 : Jenkins CI/CD + Docker : 2  (0) 2024.12.03
Comments