기록은 기억의 연장선

더 많은 것을 기억하기 위해 기록합니다


  • Home

  • Tags

  • Categories

  • Archives

  • Search

[docker] image 다루기

Posted on 2019-06-08 | Edited on 2020-11-02 | In docker | Comments:

도커는 크게 도커 이미지와 도커 컨테이너로 나뉜다
그 중 도커 이미지는 컨테이너를 생성하는 템플릿 역할을 한다

아래는 도커 이미지를 관리하면서 자주 사용하는 명령어들과 그에 대한 간단한 설명이다
추가적인 명령어나 옵션이 궁금하다면 https://docs.docker.com/engine/reference/commandline/image/ 를 참조한다

docker image build

1
$ docker image build [options] Dockerfile_경로

Dockerfile에 기술된 내용을 따라 도커 이미지를 생성하는 명령이다

Dockerfile은 필수로 필요하다

마지막 인자로 Dockerfile의 경로를 주는것에 유의해야한다

-t 옵션

1
$ docker image build -t javatest:latest .

도커 이미지에 이름과 태그를 붙이는 옵션으로 실제 사용에선 거의 필수적으로 쓰인다
지정한 경로에서 Dockerfile 이라는 이름의 파일을 찾고 그 내용을 토대로 이미지를 만든다

태그명은 생략 가능한데, 생략하면 기본적으로 latest가 붙는다

-f 옵션

1
$ docker image build -f Dockerfile_test -t javatest:latest .

Dockerfile이 아닌 다른 이름을 쓰고 싶을 경우 사용한다

–pull 옵션

1
$ docker image build --pull=true -t javatest:latest .

기본적으로 도커 이미지 빌드 시 FROM 인스트럭션에 지정한 이미지를 레지스트리에서 받은 후, 이를 호스트 운영체제 저장해놓고 재사용한다
이를 매번 새로 받아오게 하고 싶을 경우 사용하면 좋다

이 옵션은 아래와 같은 상황을 대비하기 위해 사용하면 좋다

1
2
3
FROM somelibrary:latest

...
  1. 도커 이미지를 빌드한다
    • FROM 인스트럭션의 somelibrary:latest 이미지를 받고 로컬에 저장해둔다
  2. somelibrary 이미지에 버그가 있어서 수정하고, 다시 latest 태그로 레지스트리에 올렸다
  3. 위의 Dockerfile 로 이미지를 새로 빌드한다
  4. 레지스트리의 변경사항이 생성된 이미지에 반영되지 않는다
    • somelibrary:latest가 이미 로컬에 있기 때문에 새로 받아오지 않고 재사용한다

이러한 이유로 실무에서는 latest 대신 보통 태그명을 직접 입력한다

docker search

1
$ docker search [options] 검색_키워드

도커 레지스트리에서 이미지를 검색할 때 사용한다
레지스트리를 따로 설정하지 않았다면 기본적으로 도커 허브가 사용된다

도커 허브(Docker Hub)?
도커 사 자체에서 관리하는 도커 이미지 레지스트리로, 깃허브처럼 자신의 계정이나 조직 이름으로 리포티저티를 만들고 이미지를 올릴 수 있다
도커 허브에는 이미 매우 많은 리포지터리가 등록되어 있어서, 직접 도커 이미지를 만들 필요없이 만들어놓은 이미지를 간단하게 사용할 수 있다

참고로 여기서 몇가지 용어의 혼재가 있을 수 있다

  • 도커 이미지가 저장되고 관리되는 공간을 도커 레지스트리라고 부른다

    도커 허브, ECR, 사내 도커 레지스트리 등등이 될 수 있다

  • 이러한 도커 레지스트리내에서 이미지를 저장해두는 공간을 리포지터리라고 부른다
  • 그러므로 레지스트리에서 리포지터리를 찾는것은 이미지를 찾는것과 동일한 행위이다

    리포지터리명:태그로 접근한다

  • 네임스페이스는 레지스트리내에서 리포지터리의 이름이 중복되는 것을 막기 위해 리포지터리 이름 앞에 작성하는 것을 말한다
1
2
$ docker search mysql
$ docker search --limit 5 mysql # 5건만 검색

검색결과는 STARS(깃헙과 동일) 순으로 출력되고, 태그명까지는 검색할 수 없다

mysql 처럼 namespace가 없는 애들도 있는데 이는 mysql의 공식 리포지터리라서 그렇다

docker image name format

로컬 내에서는 상관없지만, 도커 허브와 같은 registry 를 끼게 되면 이미지 이름(태그)에 일종의 포멧이 생기게 된다

1
[레지스트리_호스트/]리포지터리명[:태그]
  • 레지스트리 호스트를 지정해서 리포지터리를 땡겨올 수 있다

    생략하면 기본적으로 docker.io 가 추가된다(도커 허브)

  • 도커 허브를 사용할 경우 리포지터리명 앞에 namespace로 자신의 계정을 줘야한다

    그래야 도커 허브 시스템 내에서 리포지터리를 구분할 수 있다

docker image pull

1
$ docker image pull [options] 리포지터리명[:태그명]

도커 레지스트리에서 도커 이미지를 내려받을 때 사용한다

1
$ docker image pull jenkins

태그명을 생략하면 default 태그가 사용된다(latest)

docker image ls

1
$ docker image ls [options] [이미지명[:태그명]]

현재 호스트 운영체제에 저장된 도커 이미지의 목록을 보여준다

참고로 여기서 조회되는 IMAGE ID는 CONTAINER ID와는 별개의 것이므로 주의해야 한다

도커 IMAGE ID

도커는 이미지를 빌드할 때 마다 새로운 이미지 ID가 부여된다

  1. docker 파일을 빌드하여 이미지를 만든다

    1
    $ docker image build -t javatest:latest .
  2. docker image ls로 빌드된 이미지가 잘 저장되었는지 확인한다

    1
    2
    3
    4
    $ docker image ls -a

    REPOSITORY TAG IMAGE ID CREATED SIZE
    javatest latest 2327e281c9b2 1 hour ago 4.41MB
  3. Dockerfile을 조금 수정하고 다시 빌드하고, docker image ls 로 확인해본다

    1
    2
    3
    4
    5
    6
    $ docker image build -t javatest:latest .
    $ docker image ls -a

    REPOSITORY TAG IMAGE ID CREATED SIZE
    javatest latest 57ca5e531d73 1 hour ago 4.40MB
    <none> <none> 2327e281c9b2 2 hour ago 4.41MB

    IMAGE ID가 다른 이미지가 새롭게 빌드되었음을 볼 수 있다
    (Dockerfile을 수정할 때 뿐만 아니라 COPY 되는 대상이 바뀌거나 하여도 새로운 이미지로 빌드된다)
    똑같은 이미지명:태그명 으로 빌드했기 때문에 기존의 이미지에서 REPOSITORY와 TAG가 <none>으로 표시되는 것을 볼 수 있다

결국 IMAGE ID는 도커 이미지의 버전이라고 봐도 무방하다

docker image tag

위에서 봤다시피 태그는 이미지 ID를 식별하기 위해 붙이는 일종의 별칭이다

1
$ docker image tag 기반이미지명[:태그] 새이미지명[:태그]

이미지를 가리키는 태그를 추가 생성할 떄 사용한다
새이미지명[:태그]가 기반이미지명[:태그]가 가리키고 있던 이미지 ID를 가리키는 상태로 추가된다

1
$ docker image tag joont92/javatest:latest joont92/javatest:1.0.0

보통은 이처럼 latest의 특정 시점에 버전 넘버를 태그로 붙이기위해 사용한다
이렇게 하면 아래와 같이 생성된다

1
2
joont92/javatest           1.0.0               4a1fc394fbef
joont92/javatest latest 4a1fc394fbef

latest 가 가지고 있던 이미지를 가리키는 1.0.0 이 추가로 생성되었다

docker image push

1
$ docker image push [options] 리포지터리명[:태그]

레지스트리로 도커 허브를 사용할 경우 리포지터리명 앞에 자신의 도커 ID로 네임스페이스를 붙여줘야한다

1
2
3
4
# 네임스페이스를 추가해주고
$ docker image tag javatest:latest joont92/javatest:latest
# push
$ docker image push joont92/javatest:latest

도커허브-push
리포지터리에 올라갔음을 볼 수 있다

참고 :

  • 야마다 아키노리, 『도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문』, 심효섭 옮김, 위키북스(2019)
Read more »

[docker] Dockerfile

Posted on 2019-06-07 | Edited on 2020-11-02 | In docker | Comments:

Dockerfile이란?

도커 이미지를 만들 때 꼭 필요한 설정파일이다
이 파일내에 작성된 인스트럭션들을 참조하여 이미지가 만들어진다

기본으로 Dockerfile 이라는 이름을 사용하고, 이름을 변경하고 싶다면 이미지 빌드시에 추가 옵션을 줘야한다(-f)

인스트럭션

Dockerfile 내에 있는 명령어들을 말한다
아래는 각 인스트럭션에 대해 간단히 나열한 것이며, 자세한 내용이 궁금하면 https://docs.docker.com/engine/reference/builder/ 를 참조한다

자주 사용하는 인스트럭션

예시로 사용할 자바 어플리케이션과 Dockerfile 이다

Test.java

1
2
3
4
5
public class Test {
public static void main(String[] args) {
System.out.println("Hello World!");
}
}

Dockerfile

1
2
3
4
5
6
FROM openjdk:8-jdk

COPY Test.java .
RUN javac Test.java

CMD ["java", "Test"]

FROM

해당 도커 이미지의 바탕이 될 베이스 이미지를 지정하는 인스트럭션이다
openjdk를 베이스 이미지로 땡겨왔기 때문에 javac, java 명령어가 실행 가능함을 볼 수 있다

openjdk의 Dockerfile을 따라가보면 FROM에 ubuntu를 사용하고 있다
이런식으로 이미지가 겹겹이 포장되어 빌드되는 형식이다

openjdk 는 이미지명이며, 8-jdk는 태그명이다
레지스트리를 따로 지정하지 않았기 때문에 도커 허브에서 땡겨온다

COPY

호스트 머신의 파일이나 디렉터리를 도커 컨테이너 안으로 복사하는 인스트럭션이다
이미지가 빌드될 때 1번만 실행되는 명령어이다

컨테이너안의 현재 디렉토리로 호스트 머신의 Test.java 이 복사된다

ADD

기본적인 기능은 COPY와 동일하고, 아래의 기능이 추가로 더 있다

  • source가 remote URL 이면 다운받은 뒤 destination 에 복사한다
  • source가 잘 알려진 압축파일 형식이면(tar, zip 등) 압축을 풀어준다
  • source가 remote URL + 압축파일 형식이면 압축을 풀지는 않는다

아무래도 위와 같이 특수한 상황이 아니라면 COPY를 쓰는게 낫다
명시적이기 때문이다

RUN

도커 이미지를 실행할 컨테이너 안에서 실행할 명령을 정의한다
이 또한 이미지가 빌드될 때 1번만 실행되는 명령어이다

복사된 Test.java를 javac로 컴파일하고 있다

CMD

컨테이너 안에서 실행할 프로세스(명령)를 지정한다
이는 이미지가 컨테이너화 될 때(실행될 때)마다 실행되는 명령어이다

컴파일된 Test.class 파일을 실행하고 있다

작성법이 조금 특이한데, 총 3가지 작성법을 제공한다

  • CMD command param1 param2 […]

    • 가장 익숙한 형태이다
    • FROM 으로 설정한 이미지에 포함된 쉘 파일을 사용하여 명령을 실행한다
    • 쉘 스크립트 구문을 사용할 수 있다
  • CMD [“executable”, “param1”, “param2” [, …]]

    • 쉘 없이 바로 실행하면서 매개변수를 던져주는 형태이다

    • 도커에서 권장하는 형태이다

    • 쉘 스크립트 구문을 사용할 수 없다

      1
      2
      3
      CMD ["echo", "Hello, $name"]

      $ Hello $name
    • 만약 쉘 스크립트를 사용하고 싶다면 쉘을 실행시키면서 인자로 전달해줘야 한다

      1
      CMD ["/bin/bash", "-c", "echo Hello, $name"]
  • CMD [“param1”, “param2” [, …]]

    • ENTRYPOINT에 지정된 명령에 사용할 인자를 전달한다

CMD는 Dockerfile 내에 하나만 작성할 수 있다

만약 CMD를 여러개 작성한다면 가장 앞부분껀 전부 무시되고 가장 마지막에 있는 명령만이 실행된다

1
2
CMD ["javac", "Test.java"]
CMD ["java", "Test"]

했다가 안되서 찾아봤음…

CMD 명령 오버라이드도 가능하다

위와 같이 선언된 상황에서

1
2
$ docker container run javatest:latest echo joont92
$ joont92 # 출력

CMD 명령이 무시됨을 볼 수 있다

그 외 인스트럭션

ENTRYPOINT

CMD와 마찬가지로 컨테이너 안에서 실행될 프로세스(명령)를 지정하는 인스트럭션이다
CMD와 다른점은 조금 기준점(?) 이 되는 프로세스를 지정하는 것이랄까…
ENTRYPOINT를 입력하면 CMD에 전달된 인자들은 전부 ENTRYPOINT의 인자로 전달된다

1
2
3
4
FROM openjdk:jdk-8

ENTRYPOINT ["java"]
CMD ["version"]

또한 아래와 같이 사용해서 컨테이너의 용도를 어느정도 제한 할수도 있다

1
2
3
4
FROM golang:1.10

ENTRYPOINT ["go"]
CMD [""]

자동으로 go 가 입력된 상태라고 보면 된다
go 이후의 명령어만 인자로 넘기면 된다

1
2
$ docker container run gotest:latest version
$ go version go1.10.3 linux/amd64 # 출력

LABEL

이미지를 만든 사람의 이름 등을 적을 수 있다

1
LABEL maintainer="joont92@github.com"

ENV

도커 안에서 사용할 환경 변수를 지정한다

1
2
3
ENV CLASSPATH=/workspace/javatest

CMD ["java", "Main"]

ARG

이미지 빌드할 떄 환경변수를 정의하여 사용할 수 있다

1
2
3
4
ARG classpath=.
ENV CLASSPATH=${classpath}

CMD ["java", "Main"]

외부에서 환경변수를 전달 받을수도 있다
--build-arg를 통해 인자를 전달한다

1
$ docker image build --build-arg classpath=/workapce/javatest -t javatest:latest .

이미지 빌드시에만 사용할 수 있다
컨테이너 생성시에 클래스패스를 바꾸는 것은 불가능하다

참고 :

  • 야마다 아키노리, 『도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문』, 심효섭 옮김, 위키북스(2019)
Read more »

[docker] 도커(docker)란?

Posted on 2019-06-07 | Edited on 2020-11-02 | In docker | Comments:

기존의 가상화 기술

기존에 우리가 알고 있는 가상화는 하이퍼바이저(VMM)라는 기술을 이용하여 구현된 것이다
하이퍼바이저는 호스트 컴퓨터에서 다수의 운영체제를 동시에 실행하기 위한 논리적 플랫폼을 말한다

하이퍼바이저를 이용한 가상화 방식에는 크게 2가지가 있다

Virtual Machine Type1(native)

호스트 OS 없이 하이퍼바이저가 하드웨어 바로 위에서 직접 동작하는 방식이다
중간에 OS가 없으므로 하드웨어에서 가상화 기술을 지원해줘야 하는 단점이 있지만, 요즘 CPU들은 대부분 가상화를 기본으로 지원하기 떄문에 그다지 단점이 되진 않는다

type1에서도 구현방식이 또 2가지로 나뉜다

전가상화

하드웨어 전체를 가상화한 뒤 가상머신을 올리는 방법을 말한다
모든 가상머신의 요청은 항상 하이퍼바이저를 통해서 가게 된다

반가상화

하드웨어 전체를 다 가상화하진 않는 방식을 말한다
특정 요청은 하드웨어에 직접 요청가능하고, 특정 요청은 하이퍼바이저를 통해서만 가능하다(CPU, 메모리 할당 등)
이러한 특성 때문에 OS를 조금 수정해줘야하는 단점이 있지만, 속도면에서는 당연히 더 빠르다는 장점이 있다

Virtual Machine Type2(hosted)

호스트 운영체제 위에서 하이퍼바이저가 동작하는 방식이다
우리가 잘 아는 VMWare, VirtualBox 등이 여기 속한다
당연히 속도는 더 느리다

컨테이너 가상화 기술?

우리가 원하는건 특정 환경에 종속되지 않은 상태로 어플리케이션을 띄우는 것이다
하지만 말 그대로, 단순히 어플리케이션만을 띄우고 싶을 뿐인데 OS까지 띄우는것은 엄청난 낭비이다
하지만 이러한 요구를 만족시키기 위해선 어플리케이션 격리를 해결해야 한다

필요한 것 : 격리된 CPU, 메모리, 디스크, 네트워크를 가진 공간을 만들고 이 공간에서 프로세스를 실행해서 유저에게 서비스

Cgroup, namespace

위의 필요한 부분을 달성하기 위해 사용한 대표적인 2가지 기술이다(리눅스 기술이다)

Croup는 CPU, 시스템 메모리, 네트워크 대역폭과 같은 자원을 시스템에서 실행 중인 프로세스에 할당할 수 있는 기술을 말한다
처음에는 process containers 라는 이름으로 구글에서 개발되었으나, 이후 Cgroup 으로 이름이 변경되었다
개발 이후 리눅스 메인 커널에 하나의 기능으로 들어갔다

namespace는 리소스들을 서로 다른 네임스페이스 그룹으로 나눠서, 서로 다른 네임스페이스에서는 볼 수 없다록 하는 기술이다
네임스페이스를 구성하는 방식에는 pid, uts, network 등등 총 6가지의 방법이 있다(?)

결과적으로 이 Cgroup와 namespace를 사용하여 컨테이너라는 기술이 탄생하게 된다

  • Cgroup을 이용하여 프로세스에 리소스를 할당
  • namespace를 이용하여 리소스간 볼 수 없게하여 격리시킴

이 Cgroups, namespaces를 표준으로 정의해둔 것을 OCI 스펙이라고 하고, 도커에서 사용했던 LXC나 현재 사용하는 runC는 모두 이 스펙을 구현한 구현체이다


도커란?

컨테이너 기반의 오픈소스 가상화 플랫폼을 말한다
앞서 언급했던 컨테이너 가상화 기술을 사용하는 것은 물론이고, 그것을 관리하기 위한 명령어들, 이미지 버전관리, 도커 레지스트리 등등의 많은 기능을 제공한다

이곳에 잘 설명되어 있다 https://subicura.com/2017/01/19/docker-guide-for-beginners-1.html

제공하는 기능 외에 도커의 주요 장점을 간단히 살펴보면 아래와 같다

코드로 관리하는 인프라와 불변 인프라

알다시피 애플리케이션 배포는 항상 인프라의 가변성을 포함하고 있어서 문제가 많다
(어플리케이션이 동일하더라도 배포하는 서버의 환경에 따라 여러가지 문제를 가져올 수 있다)
이러한 문제를 해결하는 가장 좋은 방법은 애플리케이션이 의존하는 환경의 차이를 가능한 한 배제하는 것 이다

도커는 특정 시점의 서버 상태를 복제하는 기능(docker image)과 이를 코드로 관리할 수 있는 기능(Dockerfile)을 제공한다
그리고 애플리케이션과 환경을 같이 묶어서 빌드할 수 있게 함으로써 기존에 존재하던 환경의 차이를 최소한으로 줄여버렸다

이로인해 높은 이식성 또한 갖추게 되었다
빌드된 이미지는 도커가 설치된 머신이라면 어디서든 실행할 수 있기 때문이다

구성관리의 용이함

일정규모를 넘는 시스템은 보통 여러개의 어플리케이션과 미들웨어를 조합하는 형태로 구성된다
도커를 사용한다고해도 이 부분은 여전히 어려운 문제이다

하지만 도커는 이 문제를 해결했다
설정파일을 사용하여 컨테이너 간 의존 관계와 시작 순서롤 제어할 수 있는 docker compose를 개발했고,
대규모 트래픽에 맞춰 어플리케이션을 오케스트레이션 해주는 docker swarm 을 개발했다
그리고 최근에는 끝판왕으로 등장한 구글의 쿠버네티스가 있다

이렇듯 도커는, 도커를 편리하게 사용할 수 있게 해주는 주변 도구가 잘 갖춰져 있다는 장점도 가지고 있다

이러한 장점으로 도커는 굉장히 빠른 속도로 전파되어갔고, 사실상 표준(de fecto)가 되어 새로운 개발스타일을 많이 만들어내게 되었다

도커 설치 및 실행

앞서 언급한 Cgroups, namespaces는 리눅스 기술이기 때문에 mac이나 Windows에서 바로 사용할 수 없다
그러므로 LinuxKit 이라는 경량 시스템을 실행하고, 그 위에서 컨테이너를 실행하는 구조로 동작한다
물론 직접 설치할 필요는 없고 mac, windows 용 도커 설치하고 실행 시 자동으로 같이 실행된다

설치 및 실행법은 좋은 글이 있어서 아래의 글로 대체한다
https://subicura.com/2017/01/19/docker-guide-for-beginners-2.html

참고 :

  • 야마다 아키노리, 『도커/쿠버네티스를 활용한 컨테이너 개발 실전 입문』, 심효섭 옮김, 위키북스(2019)
  • https://www.joinc.co.kr/w/man/12/docker/InfrastructureForDocker/about
  • https://tech.ssut.me/what-even-is-a-container/
Read more »

[aws] VPC, subnet

Posted on 2019-06-06 | Edited on 2020-11-02 | In aws | Comments:

VPC

우리가 공유기 깔고 네트워크 구성하듯이, VPC는 AWS 내에서 논리적 가상 네트워크를 구성할 수 있는 기능을 말한다
EC2 인스턴스, RDS 인스턴스 같은 리소스들은 전부 VPC 내에 등록하게 되며, VPC에 ACL 적용, Security Group 적용 등 여러가지 보안 정책을 추가할 수 있다

참고로 VPC 메뉴를 가면 이미 VPC가 하나 생성되어 있는것을 볼 수 있는데, 이는 AWS 계정 생성시 자동으로 생성되는 default VPC이다
172.31.0.0/16 대역을 사용하며 각 리전마다 1개씩 생성되어 있다(VPC는 리전을 기준으로 생성할 수 있다)
default VPC는 리전마다 1개만 설정할 수 있다

VPC 메뉴로 가서 Create VPC 버튼을 눌러 간단하게 등록할 수 있다
VPC 생성

나만의 작은 IDC(혹은 공유기 환경) 생성한다고 생각하면 편하다
VPC 이름과 IP 대역(CIDR 형식)을 입력해줘서 간단히 생성할 수 있다

CIDR 형식
서브넷 마스크를 2진수로 바꿨을 때 1의 개수를 나타낸다 e.g. 255.255.255.0 == 24
192.168.0.0 IP 대역을 사용하고, 255.255.255.0 의 서브넷마스크를 사용한다면 192.168.0.0/24와 같이 표기한다

IP 대역에는 가상 네트워크를 구성할 것이기 IP 대역을 입력한다
RFC 1918에 정의된 사설 IP 대역을 입력해주면 된다(10.0.0.0 ~ 10.255.255.255, 172.16.0.0 ~ 172.31.255.255, 192.168.0.0 ~ 192.168.255.255)

AWS에서는 이처럼 RFC 1918에 정의된 사설 IP 대역을 사용하는 것을 권장한다고 하는데, 공인 IP 대역을 입력할 수 있기나 한가?
공인 IP 대역을 사용하면 같은 공인 IP를 만났을 때 통신이 불가능하지 않는가?

만약 192.168.0.0/16의 형태로 입력했다고 하면 해당 VPC는 192.168.0.1 ~ 192.168.255.254 까지의 IP 대역을 가지게 되는 것이다

만약 이 IP 영역내에 AWS에서 사용하는 예약 IP가 있다면 이는 제외된다

서브넷

아쉽게도 VPC에서 네트워크 대역을 설정했다고 해서 바로 그 대역을 사용할 수 있는것이 아니다
생성된 VPC 대역 내에서 실제로 사용할 대역을 추가로 설정하고, 어떤 AZ에 위치시킬지 설정하는 서브넷이라는 것을 하위에 추가로 만들어줘야 한다

서브넷은 가용영역을 기준으로 생성한다

아래는 AWS 계정 생성 시 자동으로 생성되는 default VPC 안에 들어있는 default 서브넷 목록이다
default 서브넷 목록

default VPC의 IP 대역대는 172.31.0.0/16 이었고(172.31.0.0 ~ 172.31.255.255),
이를 다시 3개로 쪼개서(CIDR 20) 각각 다른 AZ에 할당한 것을 볼 수 있다

VPC 생성시에 172.31.0.0/20 으로 생성하면 자동으로 3개로 나눠질텐데, 굳이 이렇게 하는 이유는 AZ를 설정하기 위함일까?
그렇다면 위에서 CIDR 값을 받을 필요가 없지않나?

이제 AWS 리소스를 생성할 때 이렇게 생성된 VPC와 서브넷 대역을 지정해주면 영역 내에서의 IP가 private IP로 리소스에 박히게 된다

첫번째 서브넷을 선택하면 172.31.0.1 ~ 172.31.15.255 사이의 IP가 private IP로 박히게 된다

리소스 자체에 public IP를 박을 수 있는데, 이는 어떻게 처리되는 것인지?
VPC에서 NAT를 수행해서 통신 가능하게끔 해주는 것인지?
게이트웨이, 라우팅테이블을 읽어보면 이 부분이 해소될 것 같다
추가로 ACL, SG 설정하는 부분도 봐야할 듯

https://bcho.tistory.com/779

  • private, public subnet을 나누는 기준이 뭔가?
    • 외부 통신 기준인가?
  • 통신은 어떻게 하는가?
  • gateway에 ip가 많은 이유는 동적할당을 위해서?
  • VPC 의 CIDR 값은 서브넷 값을 더 크게 설정하지 못하기 위한 일종의 fence 같은 것

AWS 계정 전용 가상 네트워크, AZ가 달라도 논리적으로 묶어서 VPC 형성 가능
다른 가상 네트워크들과 논리적으로 분리
서브넷은 VPC IP 주소 범위. AWS 리소스 생성시에는 VPC가 필요함
AWS 리소스 보호를 위해 ACL 등등을 서브넷에 설정할 수 있다
기본 VPC에는 인터넷 게이트웨이가 포함된다

퍼블릭 IP를 가진 인스턴스가 있는 서브넷의 경우 퍼블릭 서브넷이라고 부른다
라우터는 각각 서브넷 앞에 있어야하지… 않는가?
그러므로 VPN 연결되는 서브넷을 VPN 전용 서브넷이라고 하는것은 이해가 간다

퍼블릭 IP주소는 직접 지정해줘야하고, 프라이빗 IP 주소는 항상 있음
기본적인 공유기 환경에서 NAT 를 타고 인터넷에 접속하는것과는 다른 구조
VPC 내의 라우터는 VPC 내 서브넷간의 통신만을 담당한다
여기 인터넷 게이트웨이를 붙임으로써 외부 통신이 가능해진다

인터넷 게이트웨이는 외부와 private IP를 이어주는 역할인 것 같다
외부와 통신하려면 무조건 인터넷 게이트웨이를 연결해야한다
그러므로 VPC 내의 모든 인스턴스가 외부 통신을 가능하게 하려면 NAT 디바이스를 사용하고, 이를 인터넷 게이트웨이와 연결시켜 줘야한다

인터넷 게이트웨이는 private IP, public IP의 mapping 정도를 해주는 애라서 가장 바깥쪽(VPC)에 있다
서브넷은 각각 라우터를 가지는데, 여기에 NAT를 붙이고 다시 이 NAT가 인터넷 게이트웨이와 붙어서 서브넷 내의 모든 리소스들의 외부 통신이 가능해진다

VPC 생성하면 Route Table, ACL, SG가 기본으로 생긴다
기본세팅으로 설정되어 있음
라우트테이블의 경우 local 이 들어가있는데, 이는 서브넷간의 리소스들은 모두 통신할 수 있음을 말함

  • Route Table
    VPC 내의 서브넷간의 통신?
    모든 서브넷은 생성시 private 서브넷으로 생성되고, 기본적으로 다른 서브넷간의 연결을 제한함
    인터넷 게이트웨이를 만드려면 무조건 라우트테이블을 생성하고 등록해줘야한다
    여기서 추가설정으로 서브넷간 통신이나 인터넷 게이트웨이 등을 연결할 수 있다
Read more »

[aws] region, az

Posted on 2019-06-03 | Edited on 2020-11-02 | In aws | Comments:

region

  • AWS의 모든 서비스(리소스)가 위치하고 있는 물리적인 장소
    • 정확히 물리적인 장소는 아니고 조금은 추상적 개념
  • 네트워크 기술이 아무리 발전했더라도, 물리적으로 먼 거리는 시간이 많이 걸릴 수 밖에 없음
    • 경유하는 라우터의 개수가 많기 떄문
    • 어떻게든 물리적인 거리를 좁히는게 최적의 방법임
    • 리전을 바꾸고 테스트해보면 속도차이를 확실히 느낄 수 있음 https://dezang.github.io/aws-ping-latency-check/
  • 전 세계 주요 지역에 위치하고 있음
    • https://aws.amazon.com/ko/about-aws/global-infrastructure/
  • 서비스가 크다면 자연재해 등을 대비하여 여러 리전을 사용하는것을 권장함
    • seoul 리전이 맛탱이 갔을때 쿠팡, 배민 등의 서비스가 모두 중지되었었다
    • 영업 손실이 보통 수준이 아니었을 것이다
  • 리전간 리소스는 공유되지 않음
    • 리전간 리소스 복사는 가능함
  • 계정당 액세스 할 수 있는 리전이 정해져있다?
    • 한국은 여러곳에 열려있다?
    • e.g. 중국은 몇몇 지역 접근 못함
    • 각자가 사용할 수 있는 리전과 가용영역이 있다

가용영역(AZ, Avaliability Zone)

  • 실질적으로 IDC 센터가 위치하는 장소
    • 리전은 여러개의 AZ를 가짐
    • Seoul 리전이라고 해서 서울에 IDC가 있는것이 아님
      • 수도이기 때문에 그렇게 표기한 것임
    • 가용영역의 위치가 실제 IDC가 있는 위치임
    • Seoul 리전의 경우 3개의 AZ가 있는데, 이는 한국에 AWS IDC가 3군데 있음을 뜻함
      • 가용영역의 위치는 비공개임
      • 물리적 피해를 막기 위함
  • 가용영역간 fail over를 elastic IP로 해결할 수 있다?
  • 리소스가 리전의 가용 영역에 걸쳐 배포될 수 있도록 AWS는 각 AWS 계정의 이름에 가용 영역을 독립적으로 매핑합니다. 예를 들어 AWS 계정의 us-east-1a 가용 영역은 다른 AWS 계정에 대한 us-east-1a 가용 영역과 위치가 동일하지 않을 수 있습니다
    • 1a가 특정 위치를 명시하는게 아닐 수 있음을 말하는 듯?
    • AZ ID를 설정하면 특정 위치를 아예 지정할 수 있다
  • 이번 AZ 추가가 테라폼 설정에 왜 문제가 되었을까?

  • AZ간 통신은 가능한가?
  • 리전간 통신은 가능한가?
  • AZ간 VPC는 묶지만, 리전간은 안된다?
  • 리전간 failover는 가능한가?
    • 리전간 스위칭이 가능한가?(seoul -> tokyo)
    • 최종 도메인에 연결되는 AWS 주소를 바꿔주면 될 듯 하다
      • 데이터베이스 동기화는 어떻게하는가?
  • AZ가 망가지는 경우?
  • 리전이 망가지는 경우?
  • 컨테이너 4개를 띄운다고 하면 2개는 2a에 2개는 2c에?
  • 가용영역에 영향받을 수 있는 리소스들은 가용영역을 선택하지 않는다
    • 아마도 모든 가용영역끼리 복사되어있지 않을까?
    • e.g. Elastic Beanstalk 등
  • beanstalk 은 가용영역이 없고 ec2 를 가용영역을 선택해서 띄운다
  • loadbalancer에서 az 범위 선택하고 그 범위 내에서 로드밸런싱
Read more »

매개변수 제거

Posted on 2019-06-01 | Edited on 2020-11-02 | In refactoring | Comments:

메서드가 어떤 매개변수를 더 이상 사용하지 않을 땐 그 매개변수를 삭제하자

동기

  • 프로그래머는 매개변수 추가는 부담없이 하면서도 매개변수 삭제는 꺼린다
    • 껍데기뿐인 매개변수를 놔둔다고 문제가 생기진 않을테고, 혹시 나중에 다시 필요할수도 있다는 자기합리화 때문이다
  • 매개변수는 필요한 정보를 나타낸다
    • 호출 부분에서 무슨 값을 전달할지 신중해야 한다
    • 매개변수를 제거하지 않으면 그 메서드를 사용하는 모든곳이 불필요한 추가 작업을 수행하게 된다
    • 이는 매우 혹독한 대가이고, 매개변수 제거가 어렵지 않다라는 사실을 생각하면 더욱 혹독한 대가이다

방법

  1. 메서드 시그니쳐가 상위클래스나 하위클래스에 선언되어 있는지 검사한다
    • 재정의 된 곳에서 해당 매개변수를 사용하지 않는지 확인해봐야 한다
    • 메서드 추가나 메서드 변경보다 좀 더 신중해야한다
  2. 매개변수를 제거한 새 메서드를 선언하고, 원본 메서드의 내용을 복사한다
  3. 원본 메서드에서 새 메서드를 호출하도록 수정한다
    • 참조하는 부분이 별로 없다면 생략 가능하다
  4. 원본 메서드 호출 부분을 전부 찾아서 새 메서드 호출로 바꾼다
  5. 원본 메서드를 삭제한다
    • 원본 메서드의 모든 호출 부분에 접근할 수 없다면 @Deprecated로 표시한다

참고 : 마틴 파울러, 『리팩토링』, 김지원 옮김, 한빛미디어(2012)

Read more »

[ddd] 리포지터리와 모델구현(JPA)

Posted on 2019-05-29 | Edited on 2020-11-02 | In ddd | Comments:

데이터 보관소로 RDBMS를 사용할 때 객체 기반의 도메인 모델과 관계형 데이터 모델간의 매핑을 처리하는 기술로 ORM 만한 것이 없다

리파지토리 구현

모듈 위치

리포지터리 인터페이스는 애그리거트와 같이 도메인 영역에 속하고, 리포지터리를 구현한 클래스는 인프라스트럭쳐 영역에 속한다

리포지터리 구현 클래스를 domain.impl 같은 패키지에 위치시키는 것은 좋은 설계 방법이 아니다

기본 기능 구현

리포지터리의 기본 기능은 다음 2가지 이다

  • 아이디로 애그리거트 조회
  • 애그리거트 저장

이를 제공하는 인터페이스의 형식은 다음과 같다

1
2
3
4
interface OrderRepository {
public Order findById(OrderNo no);
public void save(Order order);
}
  • 인터페이스는 애그리거트 루트를 기준으로 작성한다
  • 애그리거트를 조회하는 기능의 이름을 지을 때 널리 사용되는 규칙은 findBy프로퍼티(프로퍼티 값) 의 형식을 사용하는 것이다
    • e.g. findById, findByName, findByOrdererId
    • ID가 아닌 값으로 조회할때는 JPQL을 이용한다
  • 조회에 해당하는 애그리거트가 존재하면 애그리거트 도메인(e.g. Order)를 반환하고, 존재하지 않는다면 null이나 Optional을 반환한다
  • 1건이상 존재하면 List 컬렉션을 반환한다
  • JPA는 트랜잭션 범위에서 변경한 데이터를 자동으로 DB에 반영하므로 따로 수정 메서드를 추가할 필요없다
  • 삭제 기능을 구현한다면 삭제할 애그리거트 객체를 파라미터로 전달받게끔 한다
    • soft delete로 구현하는 것이 좋다

모델(매핑) 구현

기본 매핑

엔티티와 벨류가 한 테이블에 매핑

  • 애그리거트 루트는 엔티티이므로 @Entity로 매핑한다

    1
    2
    3
    4
    @Entity
    class Order {
    // ...
    }
  • 벨류는 @Embeddable로 매핑한다

    1
    2
    3
    4
    @Embeddable
    class Orderer {
    // ...
    }
  • 밸류를 사용하는 곳에서는 @Embedded로 매핑한다

    1
    2
    3
    4
    5
    @Entity
    class Order {
    @Embedded
    private Orderer orderer;
    }
  • 벨류가 다른 벨류를 포함할 수 있다

    1
    2
    3
    4
    5
    @Embeddable
    class Orderer {
    @Embedded
    private MemberId memberId;
    }
  • 데이터베이스 컬럼명이 다를 경우 @AttributeOverride(s) 어노테이션을 사용한다

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Embeddable
    class ShippigInfo {
    @Embedded
    @AttributeOverrides({
    @AttributeOverride(name = "zipcode", column = @Column(name="shipping_zipcode")),
    @AttributeOverride(name = "address1", column = @Column(name="shipping_address1")),
    @AttributeOverride(name = "address2", column = @Column(name="shipping_address2")),
    })
    private Address address;
    }

기본 생성자

생성자는 기본적으로 객체를 생성할 때 필요한 것을 받는 용도로 사용되는 것이다

벨류 오브젝트의 경우 생성 시점에 모든 값을 전달받고, setter를 제공하지 않으므로, 기본 생성자가 필요없다

하지만 JPA의 @Entity나 @Embeddable을 사용하려면 기본 생성자를 제공해야한다

기존 객체를 상속한 프록시 객체를 사용하여 지연로딩 기능을 구현하기 때문이다

이러한 이유 때문에 기본 생성자를 추가해줘야하는데, 알다시피 기본 생성자를 추가하면 객체가 온전하지 못한 상태로 제공될 수 있게된다
그러므로 기본 생성자를 protected로 선언해서 상속한 프록시 객체에서만 사용할 수 있게 해야한다

필드 접근 방식 사용

JPA는 기본적으로 필드와 메서드(프로퍼티)의 두가지 방식으로 매핑을 처리할 수 있다

  • 필드

    1
    2
    3
    4
    5
    6
    7
    @Entity
    @Access(AccessType.FIELD)
    class Order {
    @Column(name = "state")
    @Enumberated(EnumType.STRING)
    private OrderState state;
    }
  • 메서드(프로퍼티)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @Entity
    @Access(AccessType.PROPERTY)
    class Order {
    @Column(name = "state")
    @Enumerated(EnumType.STRING)
    public OrderState getState() {
    return state;
    }

    public void setState(OrderState state) {
    this.state = state;
    }
    }

하이버네이트는 @Access가 없으면 @Id나 @EmbeddedId를 보고 접근 방식을 결정한다

공개 getter/setter를 추가하는 메서드(프로퍼티) 방식을 사용하게 되면 도메인의 의도가 사라지고, 객체가 아닌 데이터 기반으로 엔티티를 구현할 가능성이 높아지게 된다
그러므로 가능하다면 필드 방식을 사용해서 객체가 제공할 기능 중심으로 구현하게끔 해야한다

AttributeConverter를 이용한 벨류 매핑

개발하다보면 가끔 벨류 프로퍼티 하나를 한 개 컬럼에 매핑해야 할 때가 있다

1
2
3
4
5
6
7
8
/**
* 이 벨류 오브젝트를 DB 컬럼 "WIDTH VARCHAR(20)" 에 매핑
* e.g. 1000mm
**/
class Length {
private int value;
private String unit;
}

이럴땐 JPA 2.1 이후로 추가된 AttributeConverter를 이용하면 된다

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface AttributeConverter<X, Y> { // X = 벨류 타입, Y = DB 타입
public Y convertToDatabaseColumn(X attribute); // 벨류 타입 -> DB 컬럼 값
public Y convertToEntityAttribute(Y dbData); // DB 컬럼 값 -> 벨류 타입
}

class MoneyConverter implements AttributeConverter<Money, Integer> {
@Override
public Integer converToDatabaseColumn(Money money) {
return money.getValue();
}

@Override
public Money convertToEntityAttribute(Integer value) {
return new Money(value);
}
}

작성한 컨버터를 적용시키려면 아래 2가지 방법을 사용하면 된다
(컨버터를 사용했기 때문에 @Embedded를 사용하지 않고 @Column으로 직접 매핑한다)

  • 특정 시점에만 적용

    1
    2
    3
    4
    5
    class Order {
    @Column(name = "totalAmounts")
    @Converter(converter = MoneyConverter.class)
    private Money totalAmounts;
    }

    리포지터리로 Order 를 handling 할 때 적용된다

  • 모든 벨류 오브젝트에 적용

    1
    2
    3
    4
    @Converter(autoApply = true)
    class MoneyConverter implements AttributeConverter<Money, Integer> {
    // ...
    }

    모든 Money 타입 프로퍼티에 자동 적용된다

Read more »

메서드명 변경

Posted on 2019-05-26 | Edited on 2020-11-02 | In refactoring | Comments:

메서드명을 봐도 기능을 알 수 없을땐 메서드를 직관적인 이름으로 바꾸자

동기

  • 복잡한 메서드를 잘게 쪼개는 것은 중요하지만, 이를 잘못 적용하면 오히려 그 작은 메서드들의 역할을 파악하기 힘들어질 수도 있다
    • 이러한 문제를 방지하려면 메서드명을 잘 지어야 한다
  • 메서드명만 봐도 그 메서드의 의도를 한눈에 알 수 있어야 한다
  • 메서드명이 적절치 않다면 반드시 변경해야 한다
    • 코드는 컴퓨터보다 인간이 알아보기 쉽게 작성해야 한다
  • 이름을 잘 짓게 되는 것이야말로 진정으로 노련한 프로그래머가 되는 열쇠다
  • 매개변수를 재정렬해서 코드를 알아보기 쉬워진다면 매개변수 재정렬을 실시해야한다

방법

  1. 메서드 시그니쳐가 상위클래스나 하위클래스에 선언되어 있는지 검사한다
    • 구현되어 있다면 이 과정을 모든 구현부마다 실행한다
  2. 새 이름으로 메서드를 선언하고, 원본 코드를 복사한 뒤 적절히 수정한다
  3. 새 메서드를 호출하게 원본 메서드의 내용을 수정한다
  4. 원본 메서드 호출 부분을 전부 찾아서 새 메서드 호출로 바꾼다
  5. 원본 메서드를 삭제한다
    • 원본 메서드의 모든 호출 부분에 접근할 수 없다면 @Deprecated로 표시한다

요즘 IDE에서는 2~5번이 필요없이 매우 간단히 수행할 수 있다
인텔리제이 짱짱

참고 : 마틴 파울러, 『리팩토링』, 김지원 옮김, 한빛미디어(2012)

Read more »

매개변수 추가

Posted on 2019-05-26 | Edited on 2020-11-02 | In refactoring | Comments:

메서드가 자신을 호출한 부분의 정보를 더 많이 알아야 할 땐
그 정보를 전달할 수 있는 매개변수를 추가한다

동기

  • 사실 이 리팩토링은 자주 실행되면 안된다
    • 매개변수를 추가하는 대신 다른 방법을 사용할 수 있을 때도 많고, 그럴 땐 가능하면 그 대안을 사용하는 것이 낫다
    • 매개변수를 추가하면 매개변수 세트가 더 길어지고, 기억하거나 알아보기 힘들어진다
  • 대안이 없다면 실행해도 된다
  • 기존 매개변수 세트를 살펴보면서 새 매개변수를 추가하면 어떻게 될지 생각해봐야 한다

방법

  1. 메서드 시그니쳐가 상위클래스나 하위클래스에 선언되어 있는지 검사한다

    • 구현되어 있다면 이 과정을 모든 구현부마다 실행한다
  2. 추가한 매개변수를 전달받는 새 메서드를 선언하고, 원본 메서드의 내용을 복사한다

  3. 새 메서드를 호출하게 원본 메서드의 내용을 수정한다

    1
    2
    3
    4
    5
    6
    7
    8
    public int originMethod(int a, int b) {
    return newMethod(a, b, 0);
    }

    public int newMethod(int a, int b, int c) {
    // do somtehing
    return someVar;
    }
    • 새 매개변수가 객체일 경우는 보통 null을 전달한다
    • primitive 타입일 경우는 명백히 이상한 값을 전달해서 참조하는 부분을 찾아낸다(?)
      • int일 경우 보통 0을 전달한다
    • 참조하는 부분이 별로 없다면 생략 가능하다
  4. 원본 메서드 호출 부분을 전부 찾아서 새 메서드 호출로 바꾼다

  5. 원본 메서드를 삭제한다

    • 원본 메서드의 모든 호출 부분에 접근할 수 없다면 @Deprecated로 표시한다

참고 : 마틴 파울러, 『리팩토링』, 김지원 옮김, 한빛미디어(2012)

Read more »

매개변수 세트를 객체로 전환

Posted on 2019-05-25 | Edited on 2020-11-02 | In refactoring | Comments:

참고 : 마틴 파울러, 『리팩토링』, 김지원 옮김, 한빛미디어(2012)

Read more »
123…19

JunYoung Park

182 posts
18 categories
344 tags
RSS
© 2020 JunYoung Park
Powered by Hexo v3.6.0
|
Theme – NexT.Muse v7.1.0