SW Engineering/DevOps, SRE

Build an image and run it as one container

SungWookKang 2019. 3. 26. 03:11
반응형

Build an image and run it as one container

 

·         Version : Docker

 

문서는 Docker 공식 문서를 번역한 내용이며 필자의 생각과 의견이 반영되어 있습니다. 자세한 내용은 원문을 참고 바랍니다.

 

Introduction

Docker 방식으로 앱을 제작할 차례이다. 응용 프로그램은 계층 구조 아래부터 시작한다. 이번 포스트에서 다루는 응용 프로그램 내용은 컨테이너이다. 레벨 위에는 컨테이너가 프로덕션 환경에서 작동하는 방식을 정의하는 서비스가 있다. 마지막 최상위 단계는 스택이며 다루는 모든 서비스의 상호 작용을 정의한다.

Stack

Service

Container (이번 포스트는 여기)

 

 

Your new development environment

과거에는 Python 응용프로그램을 작성하기 위해 가장 먼저 하는 번째 순서는 Python 런타임을 시스템에 설치하는 것이었다. 그리고 이러한 프로그램이 정상적으로 작동하려면 개발 컴퓨터 환경과 프로덕션 환경이 일치해야 하는 상황이 필요하다.

Docker 사용하면 이식 가능한 Python 런타임을 이미지로 가져올 있으며 설치가 필요 없다. 그런 다음 빌드는 코드와 함께 기본 Python 이미지를 포함 있으므로 종속성 런타임이 함께 이동한다. 휴대용 이미지는 Dockerfile이라고 하는 무언가에 의해 정의된다.

 

 

Define a container with Dockerfile

Dockerfile 컨테이너 내부의 환경을 정의한다. 네트워킹 인터페이스 디스크 드라이브와 같은 리소스에 대한 액세스는 시스템의 나머지 부분과 격리 되어있는 환경에서 가상화 되므로 포트를 외부에 매핑하고 사용자가 설정한 파일을 복사하여 환경을 구성한다. 작업을 수행한 후에는 Dockerfile 정의된 앱의 빌드가 어디서나 똑같이 작동한다고 기대할 있다.

 

Dockerfile

디렉터리를 만든다. 디렉터리 경로를 새로운 디렉터리로 변경( cd) 하고 Dockerfile이라는 파일을 작성한 다음 내용을 해당 파일에 복사하여 붙여 넣고 저장한다. 아래 코드는 Dockerfile 문장을 설명하는 주석이 함께 기록되어 있다.

# Use an official Python runtime as a parent image

FROM python:2.7-slim

 

# Set the working directory to /app

WORKDIR /app

 

# Copy the current directory contents into the container at /app

COPY . /app

 

# Install any needed packages specified in requirements.txt

RUN pip install --trusted-host pypi.python.org -r requirements.txt

 

# Make port 80 available to the world outside this container

EXPOSE 80

 

# Define environment variable

ENV NAME World

 

# Run app.py when the container launches

CMD ["python", "app.py"]

 

Dockerfile 아직 작성하지 않은 가지 파일, app.py requirements.txt 포함한다. 다음 단계에서 해당 파일을 만들어 본다.

 

The app itself

Dockerfile 같은 폴더에 requirements.txt app.py 파일을 두개 만든다.  위의 Dockerfile 이미지에 내장되면 app.py copy 명령으로 인해 app.py requirements.txt 존재하고 EXPOSE명령으로 HTTP 통해 app.py 출력에 액세스 있다.

 

[requirements.txt]

Flask

Redis

 

[app.py]

from flask import Flask

from redis import Redis, RedisError

import os

import socket

 

# Connect to Redis

redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)

 

app = Flask(__name__)

 

@app.route("/")

def hello():

    try:

        visits = redis.incr("counter")

    except RedisError:

        visits = "<i>cannot connect to Redis, counter disabled</i>"

 

    html = "<h3>Hello {name}!</h3>" \

           "<b>Hostname:</b> {hostname}<br/>" \

           "<b>Visits:</b> {visits}"

    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)

 

if __name__ == "__main__":

    app.run(host='0.0.0.0', port=80)

 

이제 pip install -r requirements.txt Python Flask Redis 라이브러리를 설치하고 app socket.gethostname() 대한 호출 결과와 함께 환경변수 NAME 인쇄하는지 확인한다. 마지막으로 Redis 실행되지 않기 때문에 (Redis 자체가 아닌 Python 라이브러리만 설치했기 때문에) 여기에서 사용하려는 시도가 실패하고 오류 메시지가 나타난다.

참고 : 컨테이너 내부에서 호스트 이름에 액세스하면 실행중인 실행 파일의 프로세스ID 같은 컨테이너ID 검색된다.

 

 

Build the app

우리는 앱을 만들 준비가 되었다. 디렉터리의 최상위 레벨에 있는지 확인한다. ls명령어를 실행하였을 여기에 아래 그림과 같이 표시되어야 한다.

ls

 

 

이제 빌드 명령을 실행한다. 이렇게 하면 Docker 이미지가 생성되며 이미지는 -t 태그를 사용하여 사용자에게 친숙한 이름을 부여할 있다.

docker build . -t friendlyhello

 

 

내장된 이미지는 어디에 있을까? 컴퓨터의 로컬Docker이미지 레지스트리에 있다.

Docker images

 

 

Run the app

응용프로그램을 실행하여 -p 사용하여 컴퓨터 포트 4000 컨테이너의 게시 포트 80 매핑한다.

docker run -p 4000:80 friendlyhello

 

 

 

Python http://0.0.0.0:80에서 앱을 제공한다는 메시지가 표시되어야 한다. 하지만 메시지는 컨테이너 내부에서 들어 오는데 컨테이너의 포트 80 4000으로 매핑했는지 모르므로 실제 테스트에서는 올바른 URL http://localhost:4000  사용 한다. 브라우저에서 해당URL 이동하여 페이지에 표시된 디스플레이 내용을 확인한다.

 


참고 : Windows7에서 Docker Toolbox 사용하는 경우 localhost 대신 Docker Machine IP 사용한다.

Ex ) http://192.168.99.100:4000/ IP 주소를 찾으려면 docker-mchine ip 명령을 사용한다.

 

 

쉘에서도 curl 명령을 사용하여 동일한 내용을 있다.

$ curl http://localhost:4000

 

<h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>

 

4000:80 포트 재매핑은 Dockerfile내의 EXPOSE docker run -p publish 값이 설정된 값의 차이를 보여준다. 나중 단계에서 호스트의 포트 4000 컨테이너 포트 80 매핑하고 http://localhost 사용. CTRL+C 눌러 종료한다.

 

Windows에서는 명시적으로 컨테이너를 중지한다. Windows 시스템에서 CTRL+C 컨테이너를 중지하지 않는다. 따라서 CTRL+C 입력하여 프롬프트를 다시 얻거나 다른 쉘을 다음 실행중인 컨테이너 목록을 표시하려면 docker container ls 입력하고 컨테이너를 중지하려면 docker container stop <Container NAME or ID> 입력한다. 그렇지 않으면 다음 단계에서 컨테이너를 다시 실행하려고 시도할 데몬에서 오류 응답을 받는다.

 

이제 백그라운드에서 분리 모드로 앱을 실행해 본다,

docker run -d -p 4000:80 friendlyhello

 


터미널에서는 앱에 대한 컨테이너 ID 나타낸다. 컨테이너가 백그라운드에서 실행 중이다. docker container ls 명령을 사용하여 축약된 컨테이너 ID 수도 있다. ( 명령을 실행 상호 교환 적으로 작동한다.)

docker container ls

 

 

Container ID http://localhost:4000 있는것과 일치한다. 다음과 같이 Container ID 사용하여 프로세스를 종료하려면 docker container stop 사용한다.

docker container stop 58427d7fd3dc

 

 

Share your image

방금 만든 이미지의 이식성을 보여주기 위해 빌드 이미지를 업로드하고 다른 곳으로 옴긴다. 결국 프로덕션에 컨테이너를 배포하려는 경우 레지스트리로 푸시하는 방법을 알아야 한다. 레지스트리는 저장소 모음이며 저장소는 이미 만들어진 코드를 제외하고는 Github 저장소와 같은 종류의 이미지 모음이다. 레지스트리의 계정은 많은 리포지토리를 생성할 있다. docker CLI 기본적으로 Docker 공개 레지스트리를 사용한다.

참고 : Docker 공용 레지스트리는 무료이며 미리 구성되어 있기 때문에 여기에서 사용한다. 그러나 선택할 있는 공용 레지스트리가 많으며 Docker Trusted Registry 사용하여 개인 레지스트리를 설정할 수도 있다.

 

 

Log in with your Docker ID

Docker 계정이 없다면 hub.docker.com 가입한다. 로컬 시스템의 Docker 공용 레지스트리에 로그인 한다.

docker login

 

 

Tag the image

로컬 이미지를 레지스트리의 저장소와 연관시키는 표기법은 username/repository:tag이다. 태그는 선택사항이지만 Docker 이미지에 버전을 제공하는데 사용하는 메커니즘이기 때문에 권장된다. 저장소에 제공하고 get-started:part2 같이 컨텍스트에 의미있는 이름을 태그한다. 그러면 이미지가 get-started 저장되고 part2 태그가 지정된다. 이제 이미지를 모두 태그를 추가한다. Docker tag image 원하는 대상에 업로드되도록 사용자 이름, 저장소 태그 이름으로 docker tag image 실행한다. 명령 구문은 다음과 같다.

Syntax : docker tag image username/repository:tag

Ex : docker tag friendlyhello gordon/get-started:part2

 

docker images 실행하면 새로 태그가 추가된 이미지를 있다.

docker images

 

 

Publish the image

태그가 지정된 이미지를 저장소에 업로드 한다. 완료되면 업로드 결과가 공개된다. Docker Hub 로그인하면 끌어오기 명령과 함께 이미지가 표시된다.

docker push username/repository:tag

 

 

 


Pull and run the image from the remote repository

이제부터는 다음 명령어로 모든 컴퓨터에서 docker run하고 앱을 실행할 있다.

docker run -p 4000:80 username/repository:tag

 

 

이미지가 로컬 컴퓨터에서 사용할 없는 경우 Docker 저장소에서 이미지를 가져온다.

 

Docker run 되는 위치에 관계 없이 Python requirements.txt 모든 종속성과 함께 이미지를 가져와서 코드를 실행한다. 모든 것이 깔끔한 작은 패키지로 함께 진행되므로 Docker 호스트 시스템을 실행하기 위해 호스트 시스템에 아무것도 설치할 필요가 없다.

 

Recap and cheat sheet [optional]

이번 포스트에서 다룬 내용은 아래와 같다.

동영상 : https://asciinema.org/a/blkah0l4ds33tbe06y4vkme6g

 

아래는 이번 포스트에 사용된 Docker 명령어 목록이다.

docker build -t friendlyhello .  # Create image using this directory's Dockerfile

docker run -p 4000:80 friendlyhello  # Run "friendlyname" mapping port 4000 to 80

docker run -d -p 4000:80 friendlyhello         # Same thing, but in detached mode

docker container ls                                # List all running containers

docker container ls -a             # List all containers, even those not running

docker container stop <hash>           # Gracefully stop the specified container

docker container kill <hash>         # Force shutdown of the specified container

docker container rm <hash>        # Remove specified container from this machine

docker container rm $(docker container ls -a -q)         # Remove all containers

docker image ls -a                             # List all images on this machine

docker image rm <image id>            # Remove specified image from this machine

docker image rm $(docker image ls -a -q)   # Remove all images from this machine

docker login             # Log in this CLI session using your Docker credentials

docker tag <image> username/repository:tag  # Tag <image> for upload to registry

docker push username/repository:tag            # Upload tagged image to registry

docker run username/repository:tag                   # Run image from a registry

 

 

[참고자료]

https://docs.docker.com/get-started/part2

 

 

2018-12-20 / Sungwook Kang / http://sqlmvp.kr

 

 

containerspythoncodecodingbuildpushrun

반응형

'SW Engineering > DevOps, SRE' 카테고리의 다른 글

Docker Stack  (0) 2019.03.26
Docker Swarms  (0) 2019.03.26
Docker Services  (0) 2019.03.26
Docker Orientation and Setup  (0) 2019.03.26
Docker란 무엇인가?  (0) 2019.03.26