Ssoon
1주 : Jenkins CI/CD + Docker : 1 본문
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