도커

프로젝트 생성

deploy_eb_docker 프로젝트를 생성한다.

$ mkdir deploy_eb_docker
$ cd deploy_eb_docker

$ pyenv virtualenv 3.5.2
$ pyenv local deploy_eb_docker

$ pip install django
$ django-admin startproject eb
$ mv eb django_app

$ pip freeze > requirements.txt
$ cp ~/deploy_ec2/.gitignore .
$ git init

# pycharm setting : 인터프리터, 소스설정

.conf-secret 폴더와 settings_common.json, settings_local.json 파일을 생성하여 설정 키를 분리한다.

{
  "django": {
    "secret_key": "..."
  }
}

settings.py에 기본 설정을 한다.

# settings.py
import json
import os

DEBUG = True

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT_DIR = os.path.dirname(BASE_DIR)
CONF_DIR = os.path.join(ROOT_DIR, '.conf-secret')
CONF_FILE_COMMON = os.path.join(CONF_DIR, 'settings_common.json')

if DEBUG:
    CONF_FILE = os.path.join(CONF_DIR, 'settings_local.json')
else:
    CONF_FILE = os.path.join(CONF_DIR, 'settings_deploy.json')
config_common = json.loads(open(CONF_FILE_COMMON).read())
config = json.loads(open(CONF_FILE).read())

for key, key_dict in config_common.items():
    if not config.get(key):
        config[key] = {}
    for inner_key, inner_key_dict in key_dict.items():
        config[key][inner_key] = inner_key_dict

SECRET_KEY = config['django']['secret_key']
ALLOWED_HOSTS = []

eb 폴더 이름을 config 로 변경

# settings.py
"""
Django settings for 'WPS Deploy ElasticBeanstalk with Docker' project.
"""
# eb. > config.으로 변경
ROOT_URLCONF = 'config.urls'
WSGI_APPLICATION = 'config.wsgi.application'
# manage.py
if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")
# wsgi.py
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings")

TIP : Github에 푸시한 후, 커밋내용에서 설정 키들이 빠져있는지 확인한다.

도커 설정

간단한 실습

$ cd deploy_eb_docker
$ docker version
$ docker images

# 우분투 이미지가 없으면 설치
# docker pull ubuntu:16.04

# 우분투 이미지를 이용해 컨테이너가 열림
$ docker run --rm -it ubuntu:16.04 /bin/bash

# 서버세팅 진행
$ apt-get update

# 서버에서는 가상환경 사용할 필요가 없다.
$ apt-get install python3
$ apt-get install python3-pip
$ apt-get install nginx

# 장고 프로젝트 생성
$ cd /srv/
$ mkdir app
$ pip3 install django
$ pip3 install uwsgi
$ django-admin startproject eb
$ l
app/  eb/

$ cd eb/
$ python3 manage.py runserver
# 컨테이너 종료하면 작업내용이 사라진다.
$ exit

# 다시 접속하면 작업한 것들이 없다.
$ docker run --rm -it ubuntu:16.04 /bin/bash

Dockerfile 작성

FROM    ubuntu:16.04
MAINTAINER dev@xyz.com

RUN     apt-get -y update
RUN     apt-get -y install python3
RUN     apt-get -y install python3-pip
RUN     apt-get -y install nginx

WORKDIR /srv
RUN     mkdir app
WORKDIR /srv/app
$ Docker build -t eb .

# 설치 완료 후

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
eb                  latest              8e026adc20cd        8 seconds ago       510 MB

$ docker run --rm -it eb /bin/bash
$ root@29d752729486:/srv/app$ pip3 list
pip (8.1.1)
setuptools (20.7.0)
wheel (0.29.0)

Dockerfiles 내용이 바뀔때 마다, 패키지가 다시 설치되는 문제가 있다. 이를 해결하기 위해 _Dockerfile_base를 만들어 분리한다.

FROM    ubuntu:16.04
MAINTAINER dev@xyz.com

RUN     apt-get -y update
RUN     apt-get -y install python3
RUN     apt-get -y install python3-pip
RUN     apt-get -y install nginx
$ docker build -t eb-base . -f Dockerfile_base
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
eb                  latest              8e026adc20cd        12 minutes ago      510 MB
eb-base             latest              fef68ca2aa6b        12 minutes ago      510 MB

_Dockerfile_base 이미지를 사용하도록 Dockerfile도 수정한다.

FROM    eb-base
MAINTAINER dev@xyz.com

WORKDIR /srv
RUN     mkdir app
WORKDIR /srv/app
$ Docker build -t eb .

# 기존에 Dockerfiles로 빌드했던 것이, none으로 사라짐
$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
eb                  latest              284ce9f44402        12 seconds ago      510 MB
<none>              <none>              8e026adc20cd        14 minutes ago      510 MB

# none은 삭제해준다.
$ docker rmi 8e026adc20cd

Dockerfile에 pip 패키지 추가

FROM    eb-base
MAINTAINER dev@xyz.com

COPY    . /srv/app
WORKDIR /srv/app

RUN     pip3 install -r requirements.txt
RUN     pip3 install uwsgi
$ Docker build -t eb .
Removing intermediate container ae170aa08f73
$ docker run --rm -it eb /bin/bash
root@d54499949d68:/srv/app$ l
Dockerfile  _Dockerfile_base  django_app/  requirements.txt
root@d54499949d68:/srv/app$ pip3 list
Django (1.10.6)
pip (8.1.1)
setuptools (20.7.0)
uWSGI (2.0.14)
wheel (0.29.0)

Dockerfile에 Django 서버실행 코드 추가

FROM    eb-base
MAINTAINER dev@xyz.com

COPY    . /srv/app
WORKDIR /srv/app

RUN     pip3 install -r requirements.txt
RUN     pip3 install uwsgi
WORKDIR /srv/app/django_app
CMD     ["python3", "manage.py", "runserver", "0:8080"]
$ Docker build -t eb .
# local의 4567포트를 Docker의 8080 포트와 연결
$ docker run -p 4567:8080 eb

Tip : 이미지 삭제

$ docker stop $(docker ps -a -q)

# Delete all docker containers
$ docker rm $(docker ps -a -q)

# Delete all docker images
$ docker rmi $(docker images -q)

# Delete name docker images force
$ docker rmi -f $(docker images --filter "dangling=true" -q)

Tip : Dokcer 자동완성

$ brew search docker
$ brew install docker-completion

uWsgi 설정

.confuwsgi-app.ini 파일을 작성한다.

[uwsgi]
chdir = /srv/app/django_app
module = config.wsgi:application
;가상환경을 사용하지 않으므로 삭제
;home = /home/ubuntu/.pyenv/versions/deploy_ec2

;uid = www-data
;gid = www-data

socket = /tmp/app.sock
chmod-socket = 666
;chown-socket = www-data:www-data

enable-threads = ture
master = true
vacuum = true
pidfile = /tmp/app.pid

logger = file:/tmp/uwsgi.log
logger = internalservererror file:/tmp/uwsgi500.log

Dockfile에 uwsgi 설정을 연결

FROM    eb-base
MAINTAINER dev@xyz.com

COPY    . /srv/app
WORKDIR /srv/app

RUN     pip3 install -r requirements.txt
RUN     pip3 install uwsgi

COPY    .conf/uwsgi-app.ini /etc/uwsgi/sites/app.ini
$ Docker build -t eb .
$ docker run --rm -it -p 4040:8080 eb
root@17abe9e49232:/srv/app$ uwsgi --http :8080 --chdir /srv/app/django_app/ -w config.wsgi

Dockerfile 분리

uwsgi와 nginx를 계속 설치해 오래 걸리니 분리한다.

_Dockerfile_base 파일을 수정한다.

FROM    ubuntu:16.04
MAINTAINER dev@xyz.com

RUN     apt-get -y update
RUN     apt-get -y install python3
RUN     apt-get -y install python3-pip
RUN     apt-get -y install nginx

RUN     pip3 install django
RUN     pip3 install uwsgi
$ docker build -t eb-base . -f _Dockerfile_base

Dockerfile 파일도 수정한다.

FROM    eb-base
MAINTAINER dev@xyz.com

COPY    . /srv/app
WORKDIR /srv/app

COPY    .conf-secret/uwsgi-app.ini /etc/uwsgi/sites/app.ini
$ Docker build -t eb .
$ docker run --rm -it -p 4040:8080 eb
root@17abe9e49232:/srv/app$ uwsgi --http :8080 --chdir /srv/app/django_app/ -w config.wsgi
root@b087c675ad47:/srv/app$ uwsgi --http :8080 -i /etc/uwsgi/sites/app.ini

.gitignore 파일에 Dockerfile 추가한다.

$ vi .gitignore
_Dockerfile*

로그를 확인해본다.

# 새로운 터미널에서 접속
$ docker ps
$ docker exec -it b087c675ad47 /bin/bash
root@b087c675ad47:/srv/app$ cd /tmp/
root@b087c675ad47:/tmp$ cat uwsgi.log

# 새로고침 후 다시 확인
root@b087c675ad47:/tmp$ cat uwsgi.log

nginx 설정

.confnginx.conf 파일을 작성한다.

user root;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
	worker_connections 768;
	# multi_accept on;
}

http {

	##
	# Basic Settings
	##

	sendfile on;
	tcp_nopush on;
	tcp_nodelay on;
	keepalive_timeout 65;
	types_hash_max_size 2048;
	# server_tokens off;

	server_names_hash_bucket_size 512;
	# server_name_in_redirect off;

	include /etc/nginx/mime.types;
	default_type application/octet-stream;

	##
	# SSL Settings
	##

	ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3, ref: POODLE
	ssl_prefer_server_ciphers on;

	##
	# Logging Settings
	##

	access_log /var/log/nginx/access.log;
	error_log /var/log/nginx/error.log;

	##
	# Gzip Settings
	##

	gzip on;
	gzip_disable "msie6";

	# gzip_vary on;
	# gzip_proxied any;
	# gzip_comp_level 6;
	# gzip_buffers 16 8k;
	# gzip_http_version 1.1;
	# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

	##
	# Virtual Host Configs
	##

	include /etc/nginx/conf.d/*.conf;
	include /etc/nginx/sites-enabled/*;
}


#mail {
#	# See sample authentication script at:
#	# http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
#	# auth_http localhost/auth.php;
#	# pop3_capabilities "TOP" "USER";
#	# imap_capabilities "IMAP4rev1" "UIDPLUS";
#
#	server {
#		listen     localhost:110;
#		protocol   pop3;
#		proxy      on;
#	}
#
#	server {
#		listen     localhost:143;
#		protocol   imap;
#		proxy      on;
#	}
#}

.confnginx-app.conf 파일을 작성한다.

server {
	listen 4040;
	server_name localhost ec2-13-124-39-197.ap-northeast-2.compute.amazonaws.com ec2.devlim.net devlim.net;
	charset utf-8;
	client_max_body_size 128M;

	location / {
		uwsgi_pass	unix:///tmp/app.sock;
		include		uwsgi_params;
	}
}

Dockerfile

FROM    eb-base
MAINTAINER dev@xyz.com

COPY    . /srv/app
WORKDIR /srv/app

COPY    .conf/uwsgi-app.ini     /etc/uwsgi/sites/app.ini
COPY    .conf/nginx.conf        /etc/nginx/nginx.conf
COPY    .conf/nginx-app.conf    /etc/nginx/sites-available/app.conf
RUN     ln -s /etc/nginx/sites-available/app.conf   /etc/nginx/sites-enabled/app.conf
$ docker build -t eb .
$ docker run --rm -it -p 5050:4040 eb
root@b620284a56e1:/etc/nginx$ nginx
root@b620284a56e1:/etc/nginx$ uwsgi --ini /etc/uwsgi/sites/app.ini

supervisor

uwsgi와 nginx중 프로세스가 하나라도 죽으면 안되니, supervisor를 사용한다. 우리가 지정한 프로그램이 죽으면 다시 실행해준다.

# nginx.conf
# 데몬이 아니라 프롬프트에서 작동할 수 있도록 한다.
daemon off;

supervisor-app.conf 생성하여 작성한다.

[program:uwsgi]
command = uwsgi --ini /etc/uwsgi/sites/app.ini

[program:nginx]
command = nginx

_Dockerfile_base 수정

FROM    ubuntu:16.04
MAINTAINER dev@xyz.com

RUN     apt-get -y update
RUN     apt-get -y install python3
RUN     apt-get -y install python3-pip
RUN     apt-get -y install nginx
RUN     apt-get -y install supervisor

RUN     pip3 install django
RUN     pip3 install uwsgi
$ docker build -t eb-base . -f _Dockerfile_base

Dockerfile 수정

FROM    eb-base
MAINTAINER dev@xyz.com

COPY    . /srv/app
WORKDIR /srv/app

COPY    .conf/uwsgi-app.ini     /etc/uwsgi/sites/app.ini
COPY    .conf/nginx.conf        /etc/nginx/nginx.conf
COPY    .conf/nginx-app.conf    /etc/nginx/sites-available/app.conf
COPY    .conf/supervisor-app.conf   /etc/supervisor/conf.d/
RUN     ln -s /etc/nginx/sites-available/app.conf   /etc/nginx/sites-enabled/app.conf

EXPOSE  4040
CMD     supervisord -n
$ docker build -t eb .
$ docker run --rm -it -p 5050:4040 eb

Dockerfile 통합

하나의 Dockerfile로 실행되도록 합친다.

# _Dockerfile_base에서 ubuntu로 변경
FROM    ubuntu:16.04
MAINTAINER dev@xyz.com

COPY    . /srv/app
WORKDIR /srv/app

RUN     apt-get -y update
RUN     apt-get -y install python3
RUN     apt-get -y install python3-pip
RUN     apt-get -y install nginx
RUN     apt-get -y install supervisor

# requirements 파일을 이용해 장고 설치
RUN     pip3 install -r requirements.txt
RUN     pip3 install uwsgi

COPY    .conf/uwsgi-app.ini     /etc/uwsgi/sites/app.ini
COPY    .conf/nginx.conf        /etc/nginx/nginx.conf
COPY    .conf/nginx-app.conf    /etc/nginx/sites-available/app.conf
COPY    .conf/supervisor-app.conf   /etc/supervisor/conf.d/
RUN     ln -s /etc/nginx/sites-available/app.conf   /etc/nginx/sites-enabled/app.conf

EXPOSE  4040
CMD     supervisord -n
$ docker build -t eb .
$ docker run --rm -it -p 5050:4040 eb

EB

개념

  • 엘라스틱 빈이 도커를 자동으로 관리
  • 애플리케이션은 프로젝트 단위를 의미
  • CLI 이용
$ cd deploy_eb_docker
$ pip install awsebcli

$ eb init
(default is 3): 10

Enter Application Name
(default is "deploy_eb_docker"):
ERROR: Elastic Beanstalk could not find any platforms. Ensure you have the necessary permissions to access Elastic Beanstalk.

권한 에러가 나므로 IAM에 가서 권한을 준다.

User > 유저선택 > Add permissions > Attach existing > elasticbeanstalkFullAccess 체크해서 권한을 추가한다.

$ eb init
(default is 3): 10

Enter Application Name
(default is "deploy_eb_docker"): WPS deploy EB with Docker

Select a platform version.
1) Docker 1.12.6
2) Docker 1.11.2
3) Docker 1.9.1
(default is 1):
Do you want to set up SSH for your instances?
(Y/n): y

Select a keypair.
1) wps-deploy
2) wps-deploy-reivew
3) [ Create new KeyPair ]
(default is 2): 1

프로젝트를 확인하면 .elasticbeanstalk 폴더가 생긴다.

$ eb create
Enter Environment Name
(default is WPSdeployEBwithDocker-dev): wps-eb
Enter DNS CNAME prefix
(default is wps-eb):

Select a load balancer type
1) classic
2) application
(default is 1):

2.0+ Platforms require a service role. We will attempt to create one for you. You can specify your own role using the --service-role option.
Type "view" to see the policy, or just press ENTER to continue:

# ...

ERROR: Create environment operation is complete, but with errors. For more information, see troubleshooting documentation.

AWS elasticbeanstalk 대시보드에 접속하면 한 개가 생성된 것을 확인할 수 있다.

Dockerfile.aws.json 파일을 생성하고 작성한다.

Multicontainer Docker Configuration

{
  "AWSEBDockerrunVersion": 2,
  "volumes": [
    {
      "name": "wps-eb",
      "host": {
        "sourcePath": "var/app/current/django_app"
      }
    }
  ],
  "containerDefinitions": [
    {
      "name": "wps-eb",
      "essential": true,
      "memory": 512,
      "portMappings": [
        {
          "hostPort": 80,
          "containerPort": 4040
        }
      ]
    }
  ]
}
$ git commit -m "Your message"

# 커밋된 파일만 배포하므로 eb deploy 전에
# 변경사항을 먼저 commit
$ eb deploy  # = scp -i -r
# ...
$ eb open

AWS EB Dashboard에 서버가 생성된 것을 확인한 후 실행해보면, Internal Server Error 발생한다.

에러를 확인하기 위해 다음과 같이 한다.

$ eb logs

# 위의 로그가 너무 길어 보기 힘드니,
# ssh 접속해서 로그 확인
$ eb ssh
[ec2-user@ip-172-31-5-219 ~]$ cd /var/log
[ec2-user@ip-172-31-5-219 log]$ ls
[ec2-user@ip-172-31-5-219 log]$ vi eb-activity.log

# 위의 방법도 보기 힘드니,
# eb 도커 이미지를 실행해 확인
[ec2-user@ip-172-31-5-219 log]$ sudo docker exec -it `sudo docker ps --no-trunc -q | head -n 1` /bin/bash
root@261c6c40d5f0:/srv/app$ cd /tmp/
root@261c6c40d5f0:/tmp$ vi uwsgi.log

FileNotFoundError: [Errno 2] No such file or directory: '/srv/app/.conf-secret/settings_common.json'

.gitignore 파일에 관리되고 있어 .conf-secret 파일을 찾지 못해 에러가 발생한다. 해결하기 위해 .ebignore 파일을 만든다. .ebignore 파일에 있는 파일은 .gitignore에 있어도 무시하게된다.

!.conf-secret/

다시 실행해본다.

$ git commit -m ".ebignore 추가"
$ eb deploy
$ eb open

allowd 호스트를 찾을 수 없다는 에러가 발생하면, 수정하여 다시 배포한다. 정상 작동하는 것을 확인 할 수 있다.