컨텐츠 내 위젯


commjs / es6 module JAVASCRIPT

모듈을 불러들이는 방법이  js에선 크게 두 가지가 있다 하나는 commonJS, 하나는 es6이다.


import 방법



<commonJs>

const test = require("test");


<es6>

import test from "test";

import {A, B} from "test";

import  * as A from "test";

--> 참고로 이처럼 *를 쓰면 commJS 로 되어 있는 것도 가져올 수 있다.




export 방법



<commonJs>

module.export = 객체;

export.함수명 = 함수내용;


<es6>

export *;

export 객체;

export default 객체;

export { 함수 };



이터러블, 이더레이터, 이뉴머블, 이뮤테이블 HTML5 / HTML5 API

1. 이터러블 iterable
반복 가능한 객체 (순서를 가진것 for 문 사용가능한...), 이터레이터를 준수한 객체

2. 이더레이터 iterator
순서를 지니게 하는 프로토콜. next 메서드를 가지고 있으며 이 안에서 { value: number, done: boolean }을 리턴함

3. 이뉴머블 enumerable
for in 에서 사용하는? 키를 열거 가능하게 하는 속성

4. 이뮤테이블 immutable
해당 속성의 재정의가 가능한가 지정하는 속성

for - in ==> key
for- of ==> value

2. 간단한 버튼 Component React Native

기본적으로는 예전에 React 배웠을 때와 비슷하다.
entry는 index.js 이며 이전에 reactDOM.render(컴포넌트, 엔트리용 element); 썼던 것처럼 AppRegistry가 들어간다.

1
2
3
4
5
6
7
8
9
/**
* @format
*/

import {AppRegistry} from 'react-native';
import App from './App';
import {name as appName} from './app.json';

AppRegistry.registerComponent(appName, () => App);


1. app.js
기본적으로 사용되는 View들을 react-native 에서 가져와서 확장하는 형태이다. 함수형 컴포넌트를 쓰고 있다.

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import React from 'react';
import type {Node} from 'react';
import {
SafeAreaView,
ScrollView,
StatusBar,
StyleSheet,
Text,
useColorScheme,
View,
} from 'react-native';

import {
Colors,
DebugInstructions,
Header,
LearnMoreLinks,
ReloadInstructions,
} from 'react-native/Libraries/NewAppScreen';

import MyButton from './component/MyButton';



const App: () => Node = () => {
const isDarkMode = useColorScheme() === 'dark';

const backgroundStyle = {
backgroundColor: isDarkMode ? Colors.darker : Colors.lighter,
};

return (
<SafeAreaView style={backgroundStyle}>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<ScrollView
contentInsetAdjustmentBehavior="automatic"
style={backgroundStyle}>
<View
style={{
backgroundColor: isDarkMode ? Colors.black : Colors.white,
}}>
<MyButton text={'인사합시다'} onPress={()=>console.log('Hi')} />
</View>
</ScrollView>
</SafeAreaView>
);
};

export default App;


2. Mybutton.js
플랫폼 문제 때문에 Button이 아닌 TuchableOpacity를 쓴다는 점과 몇몇 style이 web 과는 다르고 'px' 단위를 쓰지 못한다는 점이 있다. 그리고 typescript 를 안써서... PropTypes 를 통해 props의 타입을 정하고, DefaultProps 를 통해 기본 props를 지정한다.


 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import React from 'react';
import { TouchableOpacity, Text } from 'react-native';
import PropTypes from 'prop-types';

// TouchableOpacity - ios와 android 에서 둘다 디자인을 동일하게 가져가기 위해 button 대신 쓰임

const MyButton = (props) => {
return <TouchableOpacity>
<Text
onPress={()=>{
props.onPress();
}}
style={
{
backgroundColor: 'red',
padding: 5,
fontSize: 10,
width: 150,
borderRadius: 15,
alignSelf: 'center',
textAlign: 'center'
}
}
>
{props.text}
</Text>
</TouchableOpacity>
}

// 기본 props
MyButton.defaultProps = {
text: 'button',
onPress: () => {console.log('안녕')}
}

// 타입 props
MyButton.propTypes = {
text: PropTypes.string.isRequired,
onPress: PropTypes.func.isRequired
}

export default MyButton;






1. 환경설정 React Native

그동안 react 16버전 이상의 최신(?)형 문법을 사용하지 않아 스스로 뒤쳐진 것은 물론, 이 기회에 APP도 만들어 보고 싶어 Native에 도전하고자 한다. 

1. JDK 자바 개발 도구 설치
https://www.oracle.com/java/technologies/javase-jdk16-downloads.html
JAVA_HOME 환경변수 등록
-> 드라이브 내 java 위치
PATH 추가
-> %JAVA_HOME%bin

2. 안드로이드 스튜디오 설치
https://developer.android.com/studio
ANDROID_HOME 환경변수 등록
-> %LOCALAPPDATA%AndroidSdk
PATH 추가
-> %LOCALAPPDATA%AndroidSdkplatform-tools

3. cmd 에서 "adb --version" "java --version" 으로 설치 확인

4. 안드로이드 스튜디오 내 ADV Manager 에서 가상기기 추가 및 필요한 시스템 이미지 설치

5. react-native 설치
-> npm i react-native -g
-> npm i -g react-native-cli --force    // 이걸로 안하면 나중에 apk 빌드가 안떨어진다

6. 프로젝트 생성
-> react-native init 프로젝트명
참고로 이 때 Laren once, write anywhere 라고 나오는데... 개멋있다...

7. 실행해보기
-> npm run android

8. 앱 번들링하기
-> react-native bundle --platform android --dev false --entry-file index.js --bundle-output android/app/src/main/assets/index.android.bundle --assets-dest android/app/src/main/res/

9. android studio에서 프로젝트 빌드하기
-> android studio에서 해당 프로젝트 선택 -> build 메뉴 -> build Apk(s)
    \MyFirstApp\android\app\build\outputs\apk\debug 에 생김


Redux 및 Provider Consumer 패턴 메모 Angular / React

리듀서란 어플리케이션의 상태(저장소...라고해야 하나.. 원래는 스토어니까)를 반환하는 함수
액션은 스테이트를 변화시키는 함수
디스패치는 액션을 호출하는 함수

<react-redux>
커넥트는 컴포넌트와 디스패치를 연결하는 함수. 나머지는 금방 찾을 수 있지만 자주 헷갈리므로 메모..

function connect(mapStateToProps?, mapDispatchToProps?, mergeProps?, options?)

const mapStateToProps = (state, ownProps) => ({
todo: state.todos[ownProps.id],
})

const mapStateToProps = (state) => ({ todos: state.todos })



<Provider Consumer 패턴>

-> Context :: 전파할 state 지정
-> Provider :: Context를 자신의 pops를 이용해 배포하는 컴포넌트
-> Consumer :: Provider를 구독하는 컴포넌트

// 기본값이 light인  ThemeContextconst ThemeContext = React.createContext('light');// 로그인한 유저 정보를 담는 UserContextconst UserContext = React.createContext({  name: 'Guest',});class App extends React.Component {  render() {    const {signedInUser, theme} = this.props;    // context 초기값을 제공하는 App 컴포넌트    return (      <ThemeContext.Provider value={theme}>        <UserContext.Provider value={signedInUser}>          <Layout />        </UserContext.Provider>      </ThemeContext.Provider>    );  }}function Layout() {  return (    <div>      <Sidebar />      <Content />    </div>  );}// 여러 context의 값을 받는 컴포넌트function Content() {  return (    <ThemeContext.Consumer>      {theme => (        <UserContext.Consumer>          {user => (            <ProfilePage user={user} theme={theme} />          )}        </UserContext.Consumer>      )}    </ThemeContext.Consumer>  );}


SQL문 정리 프로그래밍 상식

===== 정의어 DDL  ==========================================

1.CREATE 문 (TABLE, VIEW.....)

CREATE TABLE 테이블명 {
   이름: CHAR(8),
   나이: INT,
   지역코드: CHAR(8),
   생년월일:DATE,
   성별: CHAR(8),
   PRIMARY KEY(이름), UNIQUE(생년월일),
   FOREIGN KEY(지역코드) REFERENCES 대한민국지역(지역코드)
   ON DELETE SET NULL,
   ON UPDATE CASCADE,
   CHECK (나이 >= 19)
}

- 테이블명에 해당하는 테이블을 생성한다. (CREATE)
- 테이블은 이름, 나이, 지역코드, 생년월일의 속성을 가진 튜플들로 구성된다. 
- 기본키는 이름이고, 생년월일은 중복되지 않는다.(PRIMARY KEY, UNIQUE)
- 외래키는 지역코드이며 대한민국지역 테이블의 지역코드를 참조한다. (FOREGIN KEY, REPRENCES)
- 내용 삭제시 참조한 테이블의 내용을 NULL로 만든다. (ON DELETE SET NULL)
- 내용 수정시 참조한 테이블 또한 같이 수정된다. (ON UPDATE CASCADE)
- 내용 추가 및 수정시엔 반드시 나이가 19살 이상이어야 한다. (CHECK)



2. ALTER 문 (TABLE 만 가능)

ALTER TABLE 학생 ADD 학년 VARCHAR(3);
학생 테이블에 학년이란 문자변수형을 가진 속성을 추가한다.

ALTER TABLE 학생 MODIFY 학년 '1학년' NOT NULL
학생 테이블에 학년속성의 기본값을 1학년으로 하고 NULL이 될 수 없도록 한다.

ALTER TABLE 학생 ADD FOREIGN KEY (지역코드) REFERENCES 대한민국지역(지역코드) ON DELETE CASCAED
학생 테이블에 지역코드라는 대한민국지역의 지역코드를 참조하는 왜래키를 추가하는데 삭제 할때 둘 다 삭제되도록 한다.



3. DROP 문 (TABLE, VIEW...)

DROP TABLE 학생 [CASCADE | RESRICT | SET NULL | SET DEFAULT]

*** 참조 제약조건 정리
CASCAED :: 둘 다 영향이 가도록 한다
RESRICT:: 금지시킨다
SET NULL :: 원본 쪽을 NULL 로 만든다
SET DEFAULT :: 원본 쪽을 기본값으로 만든다.
*** 참조 제약조건 정리



===== 조작어 DCL  ==========================================

4. SELECT 문

SELECT 학생 WHERE 점수>= 90 FROM 성적표 GROUP BY 나이;
- 성적표에서 점수가 90점 이상인 학생을 나이별로 데이터를 묶는다.
  ** 이걸 테이블로 표현하기엔 어중간한데 묶인 데이터 자체는 메모리에 존재하지만 이걸 한번에 눈으로 표시하진 않는다. 그냥 데이터 덩어리 라고 생각하면 된다.

SELECT 학생, AVG(점수) AS 평균 WHERE POINT >= 90 FROM 성적표 GROUP BY 나이 ORDER BY DESC;
- 각 나이별로 점수가 90점 이상인 학생들 중 평균을 모아 '평균'속성을 가진 테이블로 내림차순으로 나타낸다. 

SELECT 학생, AVG(점수) AS 평균 WHERE POINT >= 90 FROM 성적표 GROUP BY 나이 HAVING 성별='여자' ORDER BY DESC;
- 각 나이별로 점수가 90점 이상인 학생들 중 평균을 모은 다음 그 모은것들 중 여자만 골라 '평균'속성을 가진 테이블로 내림차순으로 나타낸다. 
  ** WHEN 그룹화 하기 전, HAVING 그룹화 한 다음 그 그룹별로.

** 확장 조건

'%' 와일드카드(모든문자)
'_' 와일드카드(한자리문자)
DISTINCT 중복제거
EXISTS 결과가 존재하면 참
<> 같지 않다
UNION 합집합 중복제거
UNION ALL 합집합 중복허용



5. INSERT 문

INSERT INTO 성적표(이름, 학과) VALUES('홍길동','사회복지과')



6. DELETE 문

DELETE FROM 성적표 WHERE 이름='홍길동';



7. UPDATE 문

UPDATE 성적표 SET 학과='유아교육과' WHERE 이름='성춘향';



===== 제어어 DML  ==========================================

8. GRANT/ REVOKE 문

GRANT 권한종류 ON 테이블명 TO 사용자명 [WITH GRANT OPTION]
사용자에게 테이블의 어떠한 권한종류를 부여한다 [써 있을 경우 권한을 남한테 줄 수 있다]


도커 이미지의 이해를 위한 튜토리얼(4 / 6) 프로그래밍 상식

본 글은 https://qiita.com/zembutsu/items/24558f9d0d254e33088f 의 번역본 이며, 글 내용에 대한 권리는 원작자에게 있습니다.

4. hello-world 도커 컨테이너의 실행

다음으로 hello-world 이미지를 사용해, 도커 컨테이너 (이하, 컨테이너로 생략) 을 실행합니다. 실행하기 전에, 컨테이너는 무언가를 단순히 복습합니다. 한마디로 정리하자면, 특별한 상태로 리눅스 프로세스를 기동합니다. 도커는 리눅스 커널이 가진 이름공간 (네임스페이스)의 분리기술혹은 cgroup 에 의한 소스제한, 그 밖에 도커 엔진의 실장(역자 - 장치 등을 구성 개체, 추가 개체)에 따라 도커 이미지안에 있는 파일시스템 안에서 프로그램을 특별한 상태로 하여 기동합니다.


즉, hello-world를 도커 컨테이너로 움직인다는것은,

- hello-wrold 라는 이름의 도커 이미지를 준비한다.
- hello-wold의 이미지 안에 있는 무엇인가의 프로그램을 실행한다.
- 이미지내 정의(CMD 명령)으로 /hello 를 자동적으로 실행한다.
- 그러나 /hello 를 컨테이너로써 (네임스페이스 등의 제약을 받고) 실행.

이상의 동작을 행합니다.

또, 컨에이너는 컨테이너용 읽고쓰기가 가능한 도커 이미지·레이어가 자동적으로 작성됩니다. 이 이미지·레이어는 통상 이미지용과 같이, 친자 상속관계를 가집니다. 그렇기 때문에 같은 호스트 상에서 복수의 컨테이너를 실행하더라도 원래 존재하는 도커 이미지 이상의 용랴을 필요로 하지 않는 이점이 있습니다.


그럼 hello-world 컨테이너를 실행합니다. docker run hello world를 실행 해봅시다.

$ docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.
...省略...
For more examples and ideas, visit:
https://docs.docker.com/get-started/


이렇듯이 직접 hello의 바이너리를 실행한것과 같은 처리(문자열의 표시)를 했는지 알 수 있습니다.

중요한것은, 이 hello 바이너리는 호스트상의 존재하고 있다는 점 입니다. 좀 전에 본 도커 이미지의 실체로써 hello가 놓여져 있는 패스에 있습니다. 단지, 컨테이너로써 실행하고 있으니깐, hello는 PID(프로세스 식별자) 네임스페이스를 분리하기 위해 hello만 존재하는 프로세스 공간입니다. (역자 주 - hello 바이너리는 호스트 상에 존재하는데 별도의 네임스페이스를 갖도록 분리된 프로세스 공간에 배치하여 사용한다는 의미입니다. ) 더욱이, mount 네임스페이스의 분리에 의해 hello 가 존재하는 디렉토리가 컨테이너를 실행하는 프로세스 공간으로써 '/'로 마운트 됩니다.


즉, '/hello'밖에 없는 파일시스템 상에서 리눅스 유저공간으로 hello의 단일 프로세스만 존재하는 것처럼 특별한 상태를 지니게 합니다. 이것이 도커의 컨테이너 입니다. 그리고, 컨테이너는 이 네임스페이스에서 PID(프로세스 식별자)를 1로해서 hello를 실행합니다. hello가 화면상 문자열로 출력된 다음 exit 상태가 되어, 컨테이너 그자체가 실행 완료(extied)가 됩니다.



이대로,
- docker 이미지를 준비한다
- docker 컨테이너용 네임스페이스공간 (PID, mount, 등등)을 분리 (isolate) 한 환경을 작성
- docker는 docker 이미지의 안에 있는 파일 (바이너리 등의 프로그램)을, 그 네임스페이스 안에서 실행
- 실행한 프로그램을 처리완료하면, 도커 컨테이너도 완료(정지) 한다.

이 컨테이너 기동으로부터 완료까지의 흐름을, 도커의 라이프사이클이라 부르겠습니다.
컨테이너 상태를 조사하려면 docker ps에 -a (전부를 의미) 플래그를 붙여봅시다.

$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e8c0bab26796 hello-world "/hello" 4 minutes ago Exited (0) 4 minutes ago frosty_bhabha


- CONTAINER ID... 컨테이너에 대하여 랜덤 할당되는 64문자의 컨테이너 ID 입니다. 이 컨테이너 ID 또는 컨테이너명으로 컨테이너를 조작합니다.
- IMAGE... 컨테이너 실행의 원본이되는 이미지 명입니다.
- COMMAND... 컨테이너 안에 현재 실행되고 있는 커맨드 입니다.
- CREATE... 컨테이너가 언제 작성되었는가 입니다.
- STATUS... 컨테이너의 상태입니다.
- PORT... 컨테이너가 포트를 공개하고 있는 경우에는, 여기에 표시 됩니다.
- NAMES... 실행시에 지정하지 않으면 자동적으로 랜덤한 문자열 (형용사 + 계산식과학계의 공헌자) 링크 : https://github.com/moby/moby/blob/master/pkg/namesgenerator/names-generator.go

이 hello-wrold 컨테이너는 이 컨테이너 전용의 네임스페이스에서 /hello 프로그램을 실행하고, 완료했습니다. docker ps 로 표시된것은 컨테이너라는것 보다도, 컨테이너용 이미지레이어의 일람표시(역자 - 메타정보 같은 것들을 한 눈에 보여주는것)를 하고 있다, 라고 생각하는게 편하게 이해됩니다.

hello-world 컨테이너는 너무 심플하므로, 이거 이상으로 컨테이너 안의 조작은 일으킬 수 없습니다. 리눅스 디스트리뷰전(배포판)에 포함되어 있는 커맨드나 쉘이, 이 이미지에는 담겨있지 않기 때문입니다.
마지막으로, 이 컨테이너를 삭제합니다. docker rm <컨테이너 ID 혹은 컨테이너명> 을 실행합니다.

$ docker rm e8
e8
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

덧붙여, 이렇듯이 컨테이너 ID는 미리 일치시켜 지정할 수 있습니다. 긴 컨테이너 ID는 복사 해두지 않더래도, 통상 2항 혹은 3항으로 지정하더래도 조작이 가능하니까 편리합니다. 그리고, docker ps -a 을 실행하면, 컨테이너(용의 이미지레이어)가 남아 있지 않다는걸 알 수 있습니다.

여기서 docker run 에 새로운 옵션 --rm 을 붙여서 실행해보도록 하죠.
$ docker run --rm hello-world
$ docker ps -a

이 옵션을 붙이면, 컨테이너가 완료되면, 자동적으로 컨테이너 (용 이미지레이어)을 삭제합니다. 기억해 두면, 일시적으로만 컨테이너를 실행할때에 편리한 옵션입니다.



도커 이미지의 이해를 위한 튜토리얼(3 / 6) 프로그래밍 상식

본 글은 https://qiita.com/zembutsu/items/24558f9d0d254e33088f 의 번역본 이며, 글 내용에 대한 권리는 원작자에게 있습니다.

3. hello-world 이미지를 상세하게 조사한다.
다운로드한 hello-world:latest 이미지를 확인합니다. 로컬에 다운로드완료한 이미지를 확인하려면 먼저 docker images를 실행합니다.

1
2
3
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 5 months ago 13.3kB

이러한 이미지의 정보가 표시 됩니다.

- repository... 리포지토리의 이름입니다. 여기서는 hello-world 입니다. (리포지토리이름이 사실상의 이미지명이라 생각해도 상관은 없습니다만, 정확히는 창고같은 보관장소로써의 리포지토리가 있어, 그 안에 [이미지명:태그] 이라는 인식표(라벨)의 정체성을 가진 씰이 붙여져 있는 단보루상자(역자 - 이사짐 등 큰거에 쓰이는 종이 골판지 상자) 있는것처럼 상상해 주세요.)
- tag... 이미지에 붙어 있는 태그 입니다. 여기서는 latest 입니다. 
- imge id... 이미지가 갖고있는 고유이미지 아이디(64자릿수) 입니다. 여기서는 짧은 아이디(12자리수) 정보가 나오고 있습니다.
- created... 그 이미지가 언제 작성되었는지 입니다. 5months ago 라고 되어 있는걸 봐서 5개월 전입니다.
- size... 이 이미지의 실체로써 디스크위에 소비하고 있는 용량 입니다. 13.3kb를 사용하고 있습니다.

다음으로 docker inspect hello-wrold:latest을 실행하고, 이 이미지의 상세정보를 보겠습니다.
 1
2
3
4
5
6
7
8
9
10
11
$ docker inspect hello-world:latest
[
{
"Id": "sha256:bf756fb1ae65adf866bd8c456593cd24beb6a0a061dedf42b26a993176745f6b",
"RepoTags": [
"hello-world:latest"
],
"RepoDigests": [
"hello-world@sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9"
],
"Parent": "",


여기서는 이하의 것들을 알 수 있습니다.
- id... 이 이미지의 id(64문자) 입니다.
- RepoTags... 이 이미지에 나눠 펼쳐진 태그가 hello-world:latest라고 알 수 있습니다.
- RepoDigests... 이 이미지내용에 대하여 해쉬식(해쉬코드) 입니다. 태그는 바뀌지 않습니다만, 이 식은 내용이 변경되지 않는 한 바뀌지 않습니다.
- Parent 부모 이미지의 정보 입니다. 빈 큰따옴표("") 의 기술은 의존관계를 가진 부모 이미지가 없습니다. 그 말은 이 hello-worle:latest 이미지는, 이 하나의 이미지 · 레이어에 의해 구성되어 있습니다.

화면을 좀 더 스크롤하면 "cmd"섹션이 보입니다. 이건 컨테이너 실행시 인수(매개변수)가 없으면 컨테이너 안에서 어떤 커맨드를 실행할지 지정합니다.
이 CMD["/hello"] 라고 적는 기술로써, 이 컨테이너를 실행하면 컨테이너 안의 path "/hello"를 실행하는걸 알 수 있습니다.

(덧붙여, "cmd"를 포함한 "ContainerConfig" 섹션은 컨테이너의 내용을 이미지로 commit할 때의 정보입니다. "cmd"섹션에 "/bin/sh"의 기술이있습니다만. 어디까지나 이미지의 커밋 (작성시)의 내부적인 기록일 뿐이며, CMD 명령으로 "/bin/sh"을 실행하는의도는 아닙니다.

참고1 :: https://github.com/moby/moby/blob/9842c7a741ee480c71bfd8e71e1f22893d010411/image/image.go#L32
참고2 :: https://stackoverrun.com/ja/q/9984570

화면을 거의 끝까지 스크롤하면, 다음과 같은 섹션이 보입니다.

1
2
3
4
5
6
            "Cmd": [
"/bin/sh",
"-c",
"#(nop) ",
"CMD [\"/hello\"]"
],


UpperDir 이라 적혀져 있는 path가, 이 도커를 실행하고 있는 호스트 상에, hello-wrold 이미지의 실체를 보존하고 있는 디렉토리 입니다. 이 화면상에는 94f... 로 시작하는 문자열 입니다만, 환경에 따라 랜덤한 문자열로 바꾸어 나타납니다.

그러면, 컨테이너 안을 ls -l <디렉토리명>의 콘테이너로 조사해 봅시다. 디렉토리명은 환경에 따라 서로 다르기 때문에 주의 부탁 드립니다. 하나 덧붙이자면 커맨드의 실행에는 root 권한이 필요합니다. 환경에 따라서는 sudo ls -l... 을 실행해 주세요.

1
2
3
4
5
# ls -al /var/lib/docker/overlay2/94f82ed22188e11a7f2a75b015929aea7c3eaa5e170c9ca19c966bf978147f19/diff
합계 24
drwxr-xr-x 2 root root 4096 6月 13 12:39 .
drwx------ 3 root root 4096 6月 13 12:39 ..
-rwxrwxr-x 1 root root 13336 1月 3 10:21 hello

hello라는 이름의 파일이 보입니다. 이로부터 hello-world:latest 이미지의 파일시스템은, hello 라는 바이너리일 뿐이라는걸 알 수 있습니다.

(덧붙여서 이번 예제에선 하나의 파일만 있습니다만, 예를 들어 ubuntu 나 centos 등의 Linux 배포판(디스트리뷰션)의 이미지를 다운로드 하면 각 이미지용의 디렉토리 안에는 "./bin", "./sbin", "./var" 등의 각 배포판(디스트리뷰션)용의 "/" 이하 파일시스템이 전개 되어 있습니다. 그리고, 그걸로부터 도커 컨테이너를 실행하면, 호스트 상의 컨테이너 안에 서로 다른 리눅스 배포판(디스트리뷰션)이 동작 하고 있는 듯하게 "보이기"는 합니다만, 실제는 호스트 상의 리눅스 커널 상에서 도커는 지정한 도커 이미지의 배포판(디스트리뷰션), 예를 들면 centos 라고 하자면 centos의 파일시스템을 마운트하고 디폴트로 그 안에 포함 되어 있는 "bin/bash"을 PID 1개로 하는 이름공간(역자 - 네임스페이스)안에
실행하고 있습니다. 컨테이너 실행시, 하나의 리눅스 상에서 복수의 리눅스가 움직이고 있는 건 아닙니다)

<역자 설명>
OS별로 따로 마치 여러 컨테이너를 돌리고 있는 것처럼 보이지만. 여러 OS를 한번에 돌리는 것이 아닌, 하나의 OS에서 미리 세팅한 환경변수 등을 이용해 가상 으로 돌리고 있는 것 입니다.

이번에 실행한 hello-world는 레이어가 하나바께 없기 때문에 호스트상에서 하나의 디렉토리 안에서 hello-world 도커 이미지의 내용물을 전부 포함합니다. 그러기 위해 복수의 이미지레이어로 구성한 도커 이미지가 있다면 호스트 상에 복수의 디렉토리가 존재합니다.

더욱이, 이미지는 메타정보를 가지고 포함합니다. hello-world 로는 CMD 명령으로 /hello를 실행하는 명령이 있었습니다. 이 메타정보에도 이미지·레이어가 필요합니다. (하나 덧붙여, 메타정보는 개념으로써 이미지·레이어 이며 호스트상에서 실체로서 파일이나 디렉토리는 없습니다.)

이렇게 해서 도커 엔진은 도커의 이미지·레이어를 추상적인 도커 이미라는 단위로 취급하는 듯한 모습을 가지고 있습니다. 도커는 콘텐츠 실행시 모스트 상에선 여기저기 분산되어 있는 파일이나 디렉토리를 하나의 파일시스템으로 통합하여 조작가능 하게 해두고 있습니다.

상기 내용으로부터 도커 이미지는 호스트상에 하나의 실체로써 파일이 존재하지 않는다든걸 알 수 있습니다.


자, 이 hello를 컨테이너가 아니라 직접 실행 해보겠습니다. full path로 "/var/lib/docker/overlay2/<디렉토리명>/diff/hello" 를 실행합니다. ※이 path는 나중 단계에서 쓰이기때문에 에디터 등에 대기시켜 두겠습니다.

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# /var/lib/docker/overlay2/94f82ed22188e11a7f2a75b015929aea7c3eaa5e170c9ca19c966bf978147f19/diff/hello

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

실행하면 "Hello from Docker!"에계속하여 문자열이 표시됩니다. 이건 소스코드 hello.c 에 적혀 있는 문자열 입니다.

상기 내용으로부터, hello-world:latest 이미지를 조사하면, 이 hello 라고 하는 바이너리를 실행하는 걸 알 수 있습니다. 그리고, 이 바이너리는 amd64/x86 지향 컴파일이 되어 있으므로 리눅스 상에서 그대로 실행할 수 있다는걸 확인 했습니다.

(덧붙여, CPU amd64/86 용 컴파일한 바이너리는 실행 할수 있습니다만. 다른 CPU 아키텍쳐 지향 바이너리는 amd64/86 상에서는 실행 할 수 없습니다. 예를 들어 Raspberry Pi는 ARM 이라고 하는 아키텍쳐라서 이 hello 바이너리는 Raspberry Pi 에서 그대로는 작동하지 않습니다. 바이너리로써 실행할수 없는 이상, 가짜로 도커 이미지에 넣었다 하더래도 이 다음 섹션에서 설명하는 도커 컨테이너로써 실행할 수 없습니다. 도커는 하드웨어의 애뮬레이션을 하지 않으며, 하드웨어를 가상화하는 기술 또한 아닙니다.)

도커 이미지의 이해를 위한 튜토리얼(2 / 6) 프로그래밍 상식

본 글은 https://qiita.com/zembutsu/items/24558f9d0d254e33088f 의 번역본 이며, 글 내용에 대한 권리는 원작자에게 있습니다.

2) hello-world 이미지 다운로드 (pull)

도커 이미지를 사용하는데 있어서 도커 허브 등에서 docker pull커맨드로 다운로드 하던가, docker build 커맨드를 사용해 스스로 작성(빌드) 합니다.

도커 허브란 공식 도커 이미지를 포함해, 다양한 도커 이미지가 공개 혹은 공유 되거나 공동작성(콜라보레이션) 하기위한 장소입니다. (누구나 이용 가능하므로, 도커 이미지의 공개 레지스트리 라고 부릅니다) 깃허브가 소스코드를 공유하거나 공동작성 하는게 가능하는것과 비슷한 기능 및 역할을 하고 있습니다.

이 도커 허브에서 hello-world 라는 이름의 도커 이미지를 다운로드 합니다. 이 이미지는 C언어로쓰여진 hello 라는 이름을 설명용 문자를 표시하기 위한 바이너리가 들어가 있습니다.

다운로드는 docker pull hello-world 를 실행하도록 합시다.


1
2
3
4
5
6
7
$ docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest

표시되어있는 메세지내용을 위에서부터 순서대로 읽어 내려가겠습니다.
- Using default tag: latest
도커 이미지는"태그"이라고하는 개념이 있습니다. 주로 버전을 표기하기 위해 이용되는 경우가 많습니다. 하나의 이미지에 대해, 복수의 "태그"을 나눠서 할당 하는것이 가능 합니다.
docker pull <이미지명>:<태그명>이 공식적인 서식 입니다만, 댑을 생략하면 자동적으로 lastest가 적용됩니다.
즉 docker pull hello-wrold는 docker-pull hello-wrold:latest 와 똑같습니다.
- latest: Pullin g from library/hello-world
hellow-wolrd의 전에 library/ 라는 이름공간(디렉토리명) 이 자동적으로 부여되어 있습니다. 이 library는 docker 공식 이미지 전용 이름공간(역자 - 네임스페이스) 입니다.
docker pull 등 실행시, 이름공간(역자 - 네임스페이스) 의 지정이 없으면, 디폴트로 공식 이미지의 이미지를 취득합니다.
- 0e03bdcc26d7: Pull complete
도커 허브 상에 있는 이미지 · 레이어 0e03bdcc26d7 의 다운로드 상황이 완료되었습니다.
hello-world 는 하나의 레이어입니다만, 이미지에따라 복수의 레이어로 구성 되어 있습니다.
- Digest: sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9
이 hello-world 이미지 (정확히는, hello-world:latest의 태그를 가진 이미지 · 레이어)의 해쉬식(역자 - 해쉬코드?) 입니다.
내용물이 같다면 태그가 변하더래도 이 해쉬식(역자 - 해쉬코드?) 은바뀌지 않습니다.
다운로드는 태그 지정뿐만 아니라 docker pull sha256:해쉬식의 지정도 가능합니다.
- Status: Downloaded newer image for hello-world:latest
hello-world:latest의 최신 이미지 다운로드 완료했다는 상태를 표시하고 있습니다.
- docker.io/library/hello-world:latest
최종적으로 다운로드가 완료된 이미지의 정보입니다.
docker.io는 레지스트리 docker hub, 이름공간(이미지를 격납하는 레지스트리)은 library (공식 레지스트리), 그 중 hello-world 이미지의, 태그명 latest (최신)을 다운로드 (pull) 했습니다.



이렇듯이, docker pull 커맨드를 실행하는 것만으로, 이런저런 처리가 이루어지며 그 경과는 화면상에 표시되어있는지 아닌지에 따라 알 수 있습니다.



도커 이미지의 이해를 위한 튜토리얼(1 / 6) 프로그래밍 상식

본 글은 https://qiita.com/zembutsu/items/24558f9d0d254e33088f 의 번역본 이며, 글 내용에 대한 권리는 원작자에게 있습니다.

1) 요약 - 스스로 "나에요" hello-world 이미지를 빌드.

이 튜토리얼은 hello-world 이미지를 스스로 빌드하는 예를 들겠습니다. 코딩 하면서 도커 이미지의 구성 및 성질의 이해를 더해갑니다. 또 효과적인 도커 이미지의 작성 및 Dockerfile의 활용을 목표로 하기 위한 기초인거 외에 (주로 개발자에게) 멀티스테이지·빌드를 배우겠습니다.

포인트는, 도커 이미지(image) 라고 하면, 도커 콘텐츠의 실행에 중요한 개념 으로써 패키지 즉, 파일이나 메타정보의 집합체 라는 것 입니다. 가상머신 이미지 처럼 실행하는 주체로써 한 파일로 구성된 것은 아닙니다.

그리고 Docker 이미지를 구성하는것은 추상적인 이미지 레이어의 모음입니다. 레이어라고 하면 [step - 층]의 의미이고, 도커는 복수의 레이어 시스템을 하나로 흡수합니다. 일반적으로 도커 이미지는 복수의 이미지 · 레이어로 구성되어 있습니다. 또한, 이미지 · 레이어는 읽기전용으로써 레이어간에 친자의존관계를 지니고 있습니다. (역자 주 - 읽기 전용이며 위에서 아래로만 흐르는 일방적인 포함관계를 의미)


통상, 도커의 이미지를 자동구축 가능하게끔 설정하는것이 도커파일이라고 부르는 설정 파일 이며, 이 파일안에서 이미지를 구성하는 명령을 적습니다. 이 명령 하나하나가 중요한 개념상 이미지 · 레이어에 상응합니다.

더욱이, 커맨드의 실행환경은 도커 CE 19.03, 호스트 OS는 리눅스(amd64 환경), 동시에 스토리지 드라이버는 디폴트 (overlay2) 을 설정했습니다.

※ 이 표현으로 이해가 안가시는 부분이 있으시다면, 지적 부탁드립니다.

(※ 역자 주 - 원문은 친자관계로 1:1 상속관계를 의미합니다 편의성을 위해 앞으로는 상속관계로 번역하겠습니다.)


1) 튜토리얼

1. 도커 이미지와 이미지 · 레이어

도커 이미지라는건, 상속관계를 가져, 복수의 이미지 레이어에 의해서 구성되어 있습니다. 이미지 · 레이어는 읽기전용 입니다. 도커는 복수의 이미지 · 레이어는 포함하여 파일이나 디렉토리 정보를 하나로 통합하는 기술을 쓰고 있습니다.





이 이미지 · 레이어의 안에는 도커 컨테이너의 실행에 필요한 리눅스 파일시스템과 메타정보를 포함합니다. 리눅스 파일시스템이라 하면, "/" 디렉토리 이하의 "/ect,/bin,/sbin,/usr" 등의 디렉토리 층계에 미치는 파일입니다. 
도커에서는 컨테이너로써 활동 시키고 싶은 어플리케이션이 필요합니다. 최소한의 어플을 도커 이미지의 안에 넣을 수 있습니다. (정확히는 이미지 · 레이어의 안에 어플리케이션 시스템에 집어넣을 수 있습니다.)

또한, 하나하나의 이미지 · 레이어에는 상속관계를 가지고 있습니다. 보다 상위에 있는 이미지 · 레이어 부터는, 부모로 되어 있는 이미지 · 레이어의 위의 파일시스템을 참조가 가능합니다. 즉, 도커 이미지를 다운로드 하면, 그 이미지가 복수의 이미지 · 레이어로 구성 되어 있다 하더래도, 그것을 의식하지 않고도 이용 가능합니다.


더욱이, 그 어플리케이션을 움직이게 하기 위해서 필요한 디폴트커맨드 라든지 매개변수(역자 - 원문은 인수)의 지정, 그 외에 공개할 포트번호의 정보, 볼륨영역의 정보가 있습니다. 이것들을 메타정보로 하여, 동일하게 도커 이미지 · 레이어 의 안에 넣을 수 있습니다.







이렇듯이 도커 이미지는[이미지] 라고 하는 명찰이 붙어 있습니다. 그러나 가상머신용 디스크이미지이며, OS의 템플릿을 가리키는 이미지는 전부 용법 개념이 서로 다르기 때문에 주의가 필요합니다.

통상 뭔가 도커 이미지를 가리킬 때에는 그 이미지의 최상위에 적재되어 있는 이미지 · 레이어 를 가리킵니다. (디폴트로는 latest 태그이라고 하는 태그를 가진 이미지·레이어) 그 이미지·레이어에게 상속관계를 가지는 레이어 가 있으면, 이미지의 취득시 등에, 자동적으로 한데 묶어 다운로드 하거나, 업로드를 하는게 가능합니다.

이후로는, 커맨드 실행을 해가면서 이미지와 이미지 · 레이어에 대해 확인하겠습니다.

1 2 3 4 5 6 7 8 9