Call by value와 Call by reference

 

Call by value

type a struct {
	value int
}

func run(aa,bb a) {
	aa.value = 111
	bb = aa
	fmt.Println(aa, bb)
}

func main()  {
	a1 := a{value: 1}
	a2 := a{value: 2}

	run(a1,a2)
	fmt.Println(a1, a2)
}

결과

{111} {111}
{1} {2}
  • run 함수 내부에서는 변경이 되지만 외부까지 변경이 전파가 되진 않는다.
    • 그 이유는 run 함수 내부의 파라미터가 실제 객체를 복사한 값을 사용하기 때문
    • 이 방식이 Call by Value

 

 

Call by reference

  • 하지만 Go 에서는 Call By Value 대신 Call By Referece도 사용할 수 있는데
  • 그 방법은 바로 Pointer를 사용하는 방법이다
type a struct {
	value int
}

func run(aa,bb *a) {
	aa.value = 111
	bb = aa
	fmt.Println(aa, bb)
}

func main()  {
	a1 := a{value: 1}
	a2 := a{value: 2}

	run(&a1,&a2)
	fmt.Println(a1, a2)
}

결과

&{111} &{111}
{111} {2}
  • run 함수 내부의 변경이 외부까지 전파가 되었다.
    • 하지만 bb 파라미터의 변경은 외부에 전파가 되지 않았다. 그 이유는 뭘까 ?
      • 내부 파라미터 자체를 변경했기 때문이다.
      • 실제 객체를 변경하는 것이 아닌 파라미터에 할당한 값이 bb의 주소에서 aa의 주소로 변경 된것일 뿐이다.

 

 

자바와 다른점

  • java는 Call by value 지만 객체를 넘겼을 경우 객체 내부의 변경이 외부에 전파가 된다.
  • 이는 파라미터로 객체를 전달시에 객체의 주소를 넘기기 때문이다.
    • 주소를 넘김으로써 주소가 가리키는 객체가 외부의 객체가 되고 변경의 여파가 외부까지 전파되는 것이다.

 

 

파라미터에 Pointer를 사용하는 경우

  • 보통 Pointer를 사용시 내부에서의 변경이 외부까지 전파가 되므로 최대한 사용을 자제하는 방향이다.
  • 하지만 객체가 생성비용이 비싼 경우 Call by value로 복사를 하므로 생성비용이 과하게 나오는 경우가 생긴다.
  • 이런 경우 Pointer를 사용하여 객체 생성비용을 절약할 수 있다.

https://www.notion.so/K8s-Apiversion-a7c2c8da3e184d659175d191820db1e0

 

K8s Apiversion

왜 api버전이 바뀌어 있을까 ?

www.notion.so

 

왜 api버전이 바뀌어 있을까 ?

머선129

  • hpa autoscaling v2beta2버전으로 배포한 어느날 수정을 위해 k edit hpa -n ns hpa-name 명령어로 조회한 순간 apiversion이 autoscaling/v1으로 변경된것을 확인했다.
  • 어찌된 일일까 ?

출처

https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api_changes.md#operational-overview

 

```java
To demonstrate the general process, here is a (hypothetical) example:

1. A user POSTs a Pod object to /api/v7beta1/...
2. The JSON is unmarshalled into a v7beta1.Pod structure
3. Default values are applied to the v7beta1.Pod
4. The v7beta1.Pod is converted to an api.Pod structure
5. The api.Pod is validated, and any errors are returned to the user
6. The api.Pod is converted to a v6.Pod (because v6 is the latest stable version)
7. The v6.Pod is marshalled into JSON and written to etcd
```
  • 공식문서에 보면 beta버전으로 등록할 경우 베타버전으로 등록되지않고 최신안정화버전으로 변경되어 등록된 후 etcd에 저장된다.
Now that we have the Pod object stored, a user can GET that object in any supported api version. For example:

1. A user GETs the Pod from /api/v5/...
2. The JSON is read from etcd and unmarshalled into a v6.Pod structure
3. Default values are applied to the v6.Pod
4. The v6.Pod is converted to an api.Pod structure
5. The api.Pod is converted to a v5.Pod structure
6. The v5.Pod is marshalled into JSON and sent to the user
  • 호출할 경우 지원되는 모든버전으로 호출할수 있습니다.

이런 이유로 v2beta2로 등록한 autoscaling이 버전없이 조회할경우 etcd에 저장된 v1버전으로 조회가 된것.

'개발 > Kubernetes' 카테고리의 다른 글

컨테이너 런타임  (0) 2021.03.07
Mutating Webhook 를 이용한 pod injection  (0) 2021.02.16
쿠버네티스의 클러스터 구성  (0) 2021.01.28
k8s 에서 spring boot jib image command 수정  (0) 2020.12.24
클러스터 구성  (0) 2020.09.13

문제

어느날 갑자기 api server가 죽어서 엄청 느려진 상태가 됬다.

파드를 내렸다가 올리면 정상적으로 작동하다 다시 느려지는 상태가 반복.

파드를 재시작할경우 정상적으로 작동하는것으로 보아 뭔가 쌓여서 문제가 되는것 같아

우선 네트워크부터 봤다.

처음시작할경우 netstat -antp로 보면 Est + Time_wait된 포트가 close_wait된 포트보다 많았으나

시간이 지날수록 close_wait된 포트가 많아지면서 서버가 응답이 느려지기 시작했다.

결론

Resttemplate으로 api서버에 요청을하는 서버에서 connection이 종료되지 않아서

CLOSE_WAIT이 쌓여 결국 서버가 멈추는 문제.

 

 

예측

  • Resttemplate이 작업을 처리하느라 close 요청을 못받는줄암.
  • 그래서 Resttemplate에서 응답을 받고 강제로 connection 을 종료 시켜주기로함.
    • Resttemplate는 직접 connection을 닫지않아도 자동으로 닫아준다고 했는데 안닫아지는건 뭔가 외부적인 이슈가 있을거라 판단
  • https://www.popit.kr/마이크로-서비스와-time_wait-문제/
    • 위 링크를 참조하여 EntityUtils로 닫아보려고 했지만 타입이 맞지 않아 에러로 닫아짐

해결

  • api서버에서 CLOSE 요청을 내려주질 않아서 Connection 을 종료할수 가 없는 상황.
  • 그래서 Resttemplate의 timeout 설정을 줘서 일정시간 없으면 요청을 종료하기로함.
  • 정상적으로 timeout 시간이 지나면 timeout으로 종료됨.

문제 2.

  • timeout줘서 요청을 종료하기로 했으나 api 서버에서 응답을 늦게내려줘서 모든 요청이 타임아웃 걸리는 상황.
  • api 서버를 확인해보니 포트 4만6천개정도가 사용중인 상태.

해결

  • 커넥션 새로 열때마다 새로운 포트를 생성하므로 커넥션풀을 만들어 사용하기로함.
  • 이미 커넥션풀이 있었으나 특정 요청은 커넥션풀이 적용되지 않았었음.
  • 그래서 적용되니 요청이 정상적으로 작동.
  1. 자식은 부모를 따라야된다 근데 자신을 반환할수도 있다
  2. 배열

공변 , 불변, 반공변

  • 공변 = ( ? extends T )
  • 반공변 = 공변의 반대 ( ? super T )
  • 불변 = 공변과 관계가 없음 ( 하위에서 상위로감 )
  • 메소드를 만드는 사람의 입장에서 다른사람이 이걸 어떻게 사용할지 공변 반공변을 정의해준다.

이펙티브 자바 31장 확인

 

PECS ?

제공하는 사람의 입장, 가져다 쓰는사람의 입장

'개발 > java' 카테고리의 다른 글

TDD 와 리팩토링  (0) 2020.09.07
JVM 구조 복습 - 1  (0) 2020.08.03
enum 사용시 주의할점  (0) 2020.05.13
멤버변수의 초기화 시기와 순서  (0) 2020.04.15

docker0

container가 통신하기 위한 가상 linux bridge

brdige는 기본적으로 l2통신 기반임

container가 하나 생성되면 이 bridge에 cotainer의 interface가 하나씩 binding되는 형태

그래서 container가 외부로 통신할때는 무조건 docker0 interface를 지나야 한다.

특징

  • IP는 자동으로 172.17.42.1로 설정됨. 16 bit netmask ( 255.255.0.0 ) 으로 설정
  • IP는 DHCP를 통해 할당받는 것이 아닌 docker 내부 로직에 의해 자동 할당받음
  • docker0는 일반적인 interface가 아닌 virtual ethernet bridge다.

'개발 > docker' 카테고리의 다른 글

Docker Image  (0) 2021.03.05
Linux Container  (0) 2021.03.03
docker-compose.yml 작성  (0) 2020.06.18
DOCKERFILE  (0) 2020.03.01
내가 자주쓰는 docker 명령어  (0) 2020.02.23
  • 컨테이너 생성

  • 이미지 안에 레이어로 이뤄져 있는 계층구조로 파일이 이뤄져 있음

  • 이 위에 READ WRITE 레이어를 하나 더 올림

  • 이 RW레이어를 통해 이미지의 파일을 읽고 쓰고 할수 있음.

  • 이 상태가 컨테이너

  • 이 RW 레이어를 통해 파일을 읽고 쓰도록 해주는 드라이버가

  • AUFS나 OVERLAY2

  • 도커 컨테이너의 성능은 그래서 이 스토리지 드라이버에 따라 차이가 남.

  • 컨테이너 내부에 프로그램을 구동시켜야되는데

  • 도커파일에 엔트리포인트에 정의된 실행파일 같은걸 실행시킴.

  • 이 과정에서 namespace 격리 cgroup의 자원할당이 일어남.

  • 일어나면서 외부와 격리됨.

  • 위의 과정에서 cgroup와 namespace는 저수준 컨테이너 런타임이 실행과정에서 처리해줌.

  • 저수준 컨테이너 런타임 = ( runc )

  • /var/lib/docker/overlay2/에 이미지가 저장됨

Docker와 containerd

  • 위의 runc로 cgroup namespace 일일히 다 oci 스펙에 맞게 config 작성하는것은 쉽지 않은 일.

  • 이걸 쉽게 해주는게 containerd

  • 도커는 continerd위에 올라가 있는 cli나 데몬 ( dockerd ) 임.

  • 도커는 컨테이너를 다루기 위한 모든 동작이 포함되어있음.

  • 그래서 자체 api나 자체 cli툴이 매우 강력함.

CRI-O

  • 이름부터 container runtime interface - open container initative 이다.

  • CRI 및 이미지가 OCI와 호환되는것에 중점을 둔 CR

  • 오로지 컨테이너의 실행을 목적으로 경량화 했기 떄문에 도커가 제공하는 컨테이너 생성 및 이미지 빌드와 같은 과정이 없었음.

  • 그래서 도커가 필요했음.

  • CRI-O 팀은 도커를 대체하는 툴들을 개발함.

  • Buildah, Podman, Skopeo

 

참고

https://www.samsungsds.com/kr/insights/docker.html

https://ssup2.github.io/theory_analysis/Docker_Component/

lugi 님

  • 유니온 파일 시스템 형식으로 이미지의 변경부분을 관리함

  • 유니온 파일 시스템 형식

    • aufs
    • btrfs
    • devicemapper
  • 도커는 베이스 이미지에서 바뀐 부분만 이미지로 생성

  • 컨테이너로 실행시 베이스 이미지와 바뀐 부분이미지를 합쳐서 실행

  • 각 이미지는 16진수로 ID를 구분한다.

Image Layer

이미지는 하나의 통짜 바이너리 덩어리가 아닌 여러개의 레이어로 이루어져 있다.

이 여러개 레이어는 계층구조로 이루어져 있다.

서로 다른 이미지가 여러개의 레이어를 공유할 수 있다.

만약 OS가 윈도우인 이미지를 받으면

이 윈도우 이미지와 동일한 이미지 위에 추가된 이미지들은 저 OS윈도우 이미지를 같이 사용한다.

그래서 이미지를 새로 받지 않는다.

( 모든 이미지 레이어는 한번만 받는다 )

이미지를 가져올때 도커는 각 레이어를 개별적으로 다운로드 한다.

여러개의 레이어가 이미 저장돼 있다면 도커는 저장되지 않은 레이어만 다운받는다.

이미지 삭제시 이미지가 사용중일 경우에는 이미지의 레이어들이 실제로 삭제 되지는 않는다.

참고

쿠버네티스 인 액션

시작하세요 도커 쿠버

'개발 > docker' 카테고리의 다른 글

docker 0  (0) 2021.03.14
Linux Container  (0) 2021.03.03
docker-compose.yml 작성  (0) 2020.06.18
DOCKERFILE  (0) 2020.03.01
내가 자주쓰는 docker 명령어  (0) 2020.02.23

www.notion.so/Linux-Container-cffb608779874506a6ac8df218105d36

컨테이너와 vm의 차이점

컨테이너

Linux Container는 linux의 가상화 기술을 사용하여 작동한다.

  • 차이점

컨테이너에서 실행되는 프로세스는 다른 모든 프로세스와 마찬가지로 호스트 운영체제 내에서 실행된다.

( 프로세스가 별도의 운영체제에서 실행되는 vm과는 다르다 )

컨테이너의 프로세스는 여전히 다른 프로세스와 격리돼 있음.

컨테이너는 더 가벼움 → 이유는 ? vm은 각각 독립적인 게스트OS를 사용하지만

컨테이너는 하나의 호스트 운영체제 내에서 실행되기 때문이다.

( vm은 구성 요소 프로세스뿐만 아니라 시스템 프로세스도 실행해야 하기 때문에 컴퓨팅 리소스가 더 필요하다 반면 컨테이너는 하나의 격리된 프로세스에 지나지 않음 )

출처: https://blog.netapp.com/blogs/containers-vs-vms/

VM은 호스트OS위의 가상화 프로세스인 하이퍼바이저를 통해 가상 머신을 실행한다.

이 가상머신내에서 실행되는 어플리케이션이 자기자신의 게스트 OS 커널에 대한 시스템 콜을 수행하면 커널은 하이퍼바이저로 호스트의 물리적 CPU에서 x86명령을 수행한다.

하이퍼바이저는 두가지 타입이 있다.

  1. 베어메탈의 하이퍼바이저는 직접 하드웨어를 제어하기 때문에 호스트OS가 없다.
    • Citrix Xen, VMWare ESX Server, MS Hyper-V등이 위 타입
  2. 호스트 운영체제에서 실행되는 하이퍼바이저
    • VMware Server, QEMU, 오라클사의 VirtualBox등이 있다.

반면 컨테이너는 호스트 OS에서 실행되는 동일한 커널에서 시스템 콜을 수행한다.

이 커널은 호스트 CPU에서 x86명령을 수행하는 유일한 커널이다.

CPU는 가상머신과 같은 방식으로 어떠한 종류의 가상화도 필요가 없다.

 

출처 https://livebook.manning.com/book/kubernetes-in-action/chapter-1/68

Vm은 각 vm이 자체 리눅스 커널을 실행해 완전한 격리를 제공

컨테이너는 모든 컨테이너가 동일한 커널을 호출한다. 하지만 보안의 위험이 있음.

하드웨어 리소스가 한정적일 경우 실행시킬 프로세스가 적다 ?

그럼 vm

많이 돌려야된다 ?

그럼 컨테이너

컨테이너는 부팅이 필요가 없다 같은 커널을 공유하기 떄문에 그냥 프로세스 실행하면 바로 실행이 시작된다.

컨테이너는 어떻게 동일한 운영체제에서 프로세스를 격리시킬수 있는것인가 ?

리눅스 네임스페이스로 각 프로세스가 시스템 ( 파일, 프로세스, 네트워크 인터페이스 , 호스트 이름 등) 에 대한 독립된 뷰만 볼수 있도록 한다.

두번째는 Cgroup으로 프로세스가 사용할 수 있는 리소스 ( CPU, 메모리, 네트워크 대역폭 등) 의 양을 설정 또는 제한 할수 있다.

NameSpace

각 리눅스 시스템은 기본적으로 초기 구동시 하나의 네임스페이스가 존재한다.

파일시스템 , 프로세스 ID , 사용자 ID , 네트워크 인ㅍ터페이스 등과 같은 모든 시스템 리소스는

하나의 네임스페이스에 속한다.

여기서 추가 네임스페이스를 생성해서 리소스를 구성할수 있다.

프로세스를 실행할때 해당 네임스페이스 중 하나에서 프로세스를 실행한다.

프로세스는 동일한 네임스페이스 내에 있는 리소스만 볼수 있다.

( 프로세스가 돌아가고있는 네임스페이스의 리소스만 볼수 있음 )

여러 종류의 네임스페이스가 있기 떄문에 프로세스는 하나의 NS에 속하는게 아니라 여러 NS에 속할수 있다.

NS의 종류

마운트, 프로세스 ID ( pid ) , 네트워크 ( net ) , 프로세스 간 통신 ( ipc )

호스트와 도메인 이름 ( uts )

  • 이 UTS는 Unix Time Sharing으로 호스트 이름과 Network Inetrnet Service 일명 nis 도메인 이름 관련 네임스페이스다.

사용자 ID ( user )

UTS는 해당 네임스페이스 내에서 실행 중인 프로세스가 사용할 호스트 이름과 도메인 이름을 결정한다.

두개의 서로 다른 UTS 네임스페이스를 한쌍의 프로세스에 각각 지정하면

서로다른 로컬 호스트이름을 보게 할수도 있다.

즉 두 프로세스를 마치 두개의 다른 시스템에서 실행중인것처럼 보이게 가능 ( 호스트 이름만 )

마찬가지로 프로세스가 속한 네트워크 네임스페이스는 실행 중인 애플리케이션의 프로세스에서 볼 수 있는 네트워크 인터페이스를 결정한다.

각 네트워크 인터페이스는 정확히 하나의 ns에 속하지만 한 ns에서 다른 ns로 이동할 수 있다.

각 컨테이너는 고유한 네트워크 ns를 사용하므로 각 컨테이너는 고유한 ns interface set를 볼수 있다.

ns를 이용해 컨테이너에서 app을 분리하는 방법을 알수 있다.

Cgroup

Cgroup의 역할은 namespace로 각 컨테이너를 격리 시킨 후 각 컨테이너가 사용할 수 있는 시스템 리소스의 양을 설정하거나 제한하는 것.

( 프로세스의 리소스사용을 제한하는 것이 cgroup )

Cgroup를 이용하여 제한을 걸면 다른 프로세스용으로 예약된 리소스를 사용할수 없다.

이는 각 프로세스가 별도의 시스템에서 실행될때와 비슷하다.

초반 도커는 LXC를 이용하여 개발

0.9버전부터는 libcontainer를 개발하여 사용

현재는 runc 사용

참고

쿠버네티스 인 액션

시작하세요 도커 쿠버

'개발 > docker' 카테고리의 다른 글

docker 0  (0) 2021.03.14
Docker Image  (0) 2021.03.05
docker-compose.yml 작성  (0) 2020.06.18
DOCKERFILE  (0) 2020.03.01
내가 자주쓰는 docker 명령어  (0) 2020.02.23

+ Recent posts