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]
[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명령어를 실행하였을
때 여기에
아래 그림과
같이 표시되어야
한다.

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

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

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를 볼
수도 있다. (둘 다 명령을
실행 할
때 상호
교환 적으로
작동한다.)

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 공용 레지스트리에 로그인
한다.

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를 실행하면 새로 태그가
추가된 이미지를
볼 수
있다.

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
containers, python, code, coding, build, push, run