AWS 노트

Docker 컨테이너를 AWS ECR 에 작성한 이미지로 build 하기 (SageMaker training job)

Jonchann 2020. 7. 15. 20:43

디렉토리 구조

새로운 컨테이터를 빌드할 때에는 base 가 필요하다.
(굳이 base 를 나눌 필요는 없지만 나누는 편이 깔끔한 것 같다)

/your_repository
    ├── lib
    │    ├── build_and_push.sh
    │    ├── deploy.sh
    │    └── create_training_job.py
    └── containers
         ├── {image}
         │    └── sub_directory
         │    │    ├── train
         │    │    ├── wsgi.py
         │    │    ├── nginx.conf
         │    │    └── some_processer.py
         │    └── Dockerfile
         └── {image}-base
               └── Dockerfile

build_and_push.sh

수정 전 원래 코드: awslabs/amazon-sagemaker-examples - build_and_push.sh

docker push 부분은 AWS ECR 에서 {image} 레포지토리를 작성하고 클릭하면 View push commands가 나오는데 아래는 그걸 바탕으로 shell script 를 작성한 것이다.

#!/usr/bin/env bash

# This script shows how to build the Docker image and push it to ECR to be ready for use
# by SageMaker.
profile=$1
image=$2

if test "$image" == "" -o "$profile" == ""; then
    echo "Usage: $0 <profile_name> <image-name>"
    exit 1
fi

# 실행 권한 부여
chmod +x user-vectorizer/vectorizer/serve
chmod +x user-vectorizer-training-job/training/train

# Get the account number associated with the current IAM credentials
account=$(aws --profile $profile sts get-caller-identity --query Account --output text)
if [ $? -ne 0 ]; then
    exit 255
fi

# Get the region defined in the current configuration (default to us-west-2 if none defined)
region=$(aws --profile $profile configure get region)
region=${region:-us-west-2}

fullname="${account}.dkr.ecr.${region}.amazonaws.com/${image}:latest"

# If the repository doesn't exist in ECR, create it.
aws --profile $profile ecr describe-repositories --repository-names "${image}" >/profile/null 2>&1

if [ $? -ne 0 ]; then
    aws --profile $profile ecr create-repository --repository-name "${image}" >/profile/null
fi

# Get the login command from ECR and execute it directly
# $(aws --profile $profile ecr get-login --no-include-email --region ${region})
aws --profile ${profile} ecr get-login-password --region "${region}" | docker login --username AWS --password-stdin "${account}".dkr.ecr."${region}".amazonaws.com

# Build the docker image locally with the image name and then push it to ECR
# with the full name.

# 디렉토리 이름 == image 이름
docker build -f ${image}/Dockerfile -t ${image} . --no-cache  # update 할 때에는 `--no-cache`를 코멘트 아웃
docker tag ${image} ${fullname}
docker push ${fullname}

{image}-base

base 는 컨테이터에 필요한 환경을 ubuntu 등의 이미지에 구축한 것이기 때문에 {image}-base 폴더 하위에 있는 Dockerfile 에는 아래와 같은 내용을 적는다.

참고: awslabs/amazon-sagemaker-examples - Dockfile

FROM ubuntu:18.04  # python3 을 사용할 수 있는 환경

LABEL maintainer '{name_of_maintainer} <{mail_address@any_domain.com}>'

RUN apt-get clean && apt-get update \
  && apt-get install -y --no-install-recommends \
  python3 \
  python3-pip \
  python3-setuptools \
  nginx \
  swig \
  ca-certificates \
  build-essential gcc \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/*

# Set locale
ENV LANG {your_language}.UTF-8
ENV LANGUAGE {your_language}.UTF-8
ENV LC_ALL {your_language}.UTF-8

# Here we get all python packages.
RUN pip3 install --upgrade pip
RUN pip3 install {python_libraries_divided_with_whitespace} && rm -rf /root/.cache
# Set some environment variables. PYTHONUNBUFFERED keeps Python from buffering our standard
# output stream, which means that logs can be delivered to the user quickly. PYTHONDONTWRITEBYTECODE
# keeps Python from writing the .pyc files which are unnecessary in this case. We also update
# PATH so that the script_of_train and serve programs are found when the container is invoked.

ENV PATH="/opt/program:${PATH}"
ENV PYTHONUNBUFFERED=TRUE
ENV PYTHONDONTWRITEBYTECODE=TRUE
EXPOSE 8080

container 를 Docker image 로 build

AWS ECR 상에 이미지가 작성되어 있지 않다면 아래 커맨드를 실행한다.

$ aws ecr create-repository --repository-name {base_image} --profile_name your_profile

lib 의 상위폴더에만 가면 되지만 위와 같은 구조에서는 그 상위 폴더가 your_repository이니 your_repository로 이동한다.

$ cd your_repository
$ lib/build_and_push.sh profile_name image_name

아래 커맨드를 실행해 {account}.dkr.ecr.{region}.amazonaws.com/{image}-base가 제대로 build 되어 있는지 확인한다.
이게 없으면 base 가 제대로 build 되지 않은 것이다.

create_training_job.py

InstanceType 은 아래에 포함되어 있는 것만 가능하다.

[ml.p2.xlarge, ml.m5.4xlarge, ml.m4.16xlarge, ml.c5n.xlarge, ml.p3.16xlarge, ml.m5.large, ml.p2.16xlarge, ml.c4.2xlarge, ml.c5.2xlarge, ml.c4.4xlarge, ml.c5.4xlarge, ml.c5n.18xlarge, ml.g4dn.xlarge, ml.g4dn.12xlarge, ml.c4.8xlarge, ml.g4dn.2xlarge, ml.c5.9xlarge, ml.g4dn.4xlarge, ml.c5.xlarge, ml.g4dn.16xlarge, ml.c4.xlarge, ml.g4dn.8xlarge, ml.c5n.2xlarge, ml.c5n.4xlarge, ml.c5.18xlarge, ml.p3dn.24xlarge, ml.p3.2xlarge, ml.m5.xlarge, ml.m4.10xlarge, ml.c5n.9xlarge, ml.m5.12xlarge, ml.m4.xlarge, ml.m5.24xlarge, ml.m4.2xlarge, ml.p2.8xlarge, ml.m5.2xlarge, ml.p3.8xlarge, ml.m4.4xlarge]

aws cli 로 해 보려고 했으나 Docker 컨테이너를 참조하는 법을 찾지 못했으므로 boto3 로 실행한다.

import argparse

import boto3


def fetch_param(profile: str, hierarchy_path: str) -> str:
    return boto3.Session(profile_name=profile).client('ssm').get_parameter(
        Name=hierarchy_path,
        WithDecryption=False
    )['Parameter']['Value']


def main():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--profile', type=str, required=True,
        help='Type profile name associated with the current IAM credentials'
    )
    args = parser.parse_args()

    account = fetch_param(args.profile, '/{project}/account')
    region = fetch_param(args.profile, '/{project}/region')
    out_bucket = fetch_param(args.profile, '/{project}/{sub_project}/{common_key}/out_bucket')

    session = boto3.Session(profile_name=args.profile)
    try:
        session.client('sagemaker').create_training_job(
            TrainingJobName='training_job_name',
            AlgorithmSpecification={
                'TrainingImage': f'{account}.dkr.ecr.{region}.amazonaws.com/{image}:latest',
                'TrainingInputMode': 'Pipe'
            },
            RoleArn=f'arn:aws:iam::{account}:role/AmazonSageMakerFullAccess',
            OutputDataConfig={
                'S3OutputPath': f's3://{out_bucket}/your/output/path/'
            },
            ResourceConfig={
                'InstanceType': 'instance_type',
                'InstanceCount': 123,
                'VolumeSizeInGB': 123  # https://aws.amazon.com/ko/releasenotes/host-instance-storage-volumes-table/
            },
            StoppingCondition={
                'MaxRuntimeInSeconds': 3600
            },
            DebugHookConfig={
                'S3OutputPath': f's3://{out_bucket}/your/debug-hook/path/'
            }
        )
    except Exception as e:
        print(e)


if __name__ == '__main__':
    main()

{image}

편의를 위해 디렉토리 이름도 이미지와 같은 이름으로 만든다 (build_and_push.py 참조) .

container 를 Docker image 로 build

위에서 한 것과 동일하게 AWS ECR 에 이미지가 작성되어 있지 않다면 아래 커맨드를 실행해 작성한다.

$ aws ecr create-repository --repository-name {image} --profile_name your_profile 

{image}-base 와 똑같이 build_and_push.sh를 실행해 build 와 push 를 진행한다.

$ cd your_repository
$ lib/build_and_push.sh profile_name image_name

AWS SageMaker 에 training job 작성하기

$ cd your_repository
$ python3 lib/create_training_job.py --profile profile_name