2016년 8월 23일 화요일

docker+alias를 사용해서 golang 편하게 쓰기

이 글은 docker 설치를 했다는 가정에서 진행한다.

먼저 go docker 버전을 가져오자
docker pull 해도 되지만 필요한 걸 추가할 수도 있을 것 같아
Dockerfile 을 만들었다.
FROM golang:1.7-alpine
go 1.7을 비교적 가벼운 alpine linux버전에 올리자.
docker build .
해서 image를 만들자
docker images를 해서 확인할 수 있다.
alpine 이 아니라 그냥 1.7도 docker pull golang:1.7 해서 가져와봤는데 

$ docker images
REPOSITORY                  TAG                 IMAGE ID            CREATED            SIZE
golang                      1.7-alpine          52493611af1e        5 days ago          241.1 MB
golang                      1.7                 fe25c00086fb        5 days ago          669.6 MB

용량 차이가 제법 난다.
1.7은 안쓸거니까 docker rmi golang:1.7 해서 바이바이.

docker run -it --rm --name migolang golang:1.7-alpine go

작동을 확인해보자. 잘되면 OK. golang:1.7-alpine이 불편하면 docker build -t migolang . 과 같이 태그를 지정할 수도 있다. docker run -it --rm --name migolang migolang go 처럼 쓸 수 있는 것.

옵션에 대해 구구절절 설명을 좀 하면
docker run -it --rm --name migolang golang:1.7-alpine go
-it 옵션은 -i -t 를 붙여서 쓴건데 -i 입력을 유지하는 옵션이고 -t는 TTY 즉 docker 에서 일어난 결과를 화면 포워딩 하는 것으로 사실 이 경우엔 쓸 필요없지만 습관처럼 붙여쓰고 있다.
그러니까 줘서 해 될거 없는 옵션

docker run -it --rm --name migolang golang:1.7-alpine go
--rm 옵션은 docker run 을 할 때마다 컨테이너를 생성하는데 이게 매번 지우기가 귀찮을 때가 많다. 그래서 만일 컨테이너가 있다면 자동으로 지우는 옵션. 역시 습관처럼 붙여쓰고 있다.

docker run -it --rm --name migolang golang:1.7-alpine go
--name 옵션은 컨테이너 이름을 지정하지 않으면 지맘대로 임의로 생성한다.
역시 귀찮으니까 지정해서 --rm 옵션으로 지정했다. my > me > mi + golang 해서 migolang 이라고 지었다.
미고랭. 그것은 넘나 맛있는 것. 원래는 Mie goreng이다.

이제 go 소스코드를 작성하고 실행해보자.
package main
import "fmt"
func main() {
  fmt.Printf("Hello, world.\n")
}
hello.go 를 이렇게 만들고 
docker run -it --rm --name migolang golang:1.7-alpine go run hello.go
하면 안될거다 (읭?)
왜냐면 go run은 docker 컨테이너 안에서 실행하고 우리들의 hello.go는 그 바깥에 있기 때문이다.
그러면 현재 hello.go를 docker 컨테이너 않으로 넣어야하는데 -v 옵션으로 서로 연결해주자.
-v "내 경로":"docker 컨테이너 경로" 로 맞춰주자. 현재 경로로 할거고 임의로 docker 컨테이너 안에 /usr/src/myapp (이 경로는 자유롭게) 이란 곳이랑 붙여준다. 
하는 김에 -v 옵션으로 지정한 경로를 go 명령이 실행하는 위치가 되도록 -w 옵션으로 경로를 맞춰주자.
docker run -it -v `echo $PWD`:/usr/src/myapp -w /usr/src/myapp --rm --name migolang golang:1.7-alpine go run hello.go
Hello, world. 가 나오는가? 좋다. 근데 이거 매번 일일이 다 치긴 나는 좀 많이 귀찮다.

alias @="docker run -it -v '$PWD':/usr/src/myapp -w /usr/src/myapp --rm --name migolang golang:1.7-alpine"

go 명령이 시작되는 부분 딱 이전까지 alias로 만들어서 사용하면 편할 것 같다.
@ go run hello.go 해보자.
$ @ go run hello.go
Hello, world.
잘 된다. @ 만 앞에 붙여주면 되니까 편하다.

------------- 절 취 선 -------------

조금 더 욕심을 내서 간단한 웹서버를 하나 띄워보자.
node.js 랑 비슷하게 특정포트를 웹서버로 열어주고 응답을 보내는 예를 만들어서
package mainimport (        "net/http")
func main() {        http.HandleFunc("/", func(res http.ResponseWriter, req *http.Request) {                res.Write([]byte("Hello, world!"))        })        http.ListenAndServe(":8000", nil)}
web.go를 이런 식으로 해보자. 8000 포트를 열고 '/'로 요청이 오면 "Hello, world!"를 출력하는 간단한 예다.
먼저 Dockerfile 을 약간 수정하자.
vi Dockerfile 해서
FROM golang:1.7-alpine
이었던 것에 포트를 노출하기 위해 EXPOSE 를 사용하자
FROM golang:1.7-alpine
EXPOSE 8000
8000번 포트를 노출해주었다.
그리고 alias @도 조금 수정하자.
alias @='docker run -it -v '\''/Users/spectrum/Documents/go/jadeExam/src'\'':/usr/src/myapp -w /usr/src/myapp --rm --name migolang -p 8000:8000 golang:1.7-alpine'
-p 부분을 추가하여 포트 매핑을 해주자 -p <실제포트>:<Docker안에서 Expose한 포트> 순서가 되겠다.
@ docker run web.go
한 뒤, 다른 터미널 하나를 열고 curl http://localhost:8000 해보자.
$ curl localhost:8000
Hello, world!
이렇게 나온다면 승리자.
docker run 커맨드가 너무 길어서 포기하는 경우가 많은데 alias나 sh(혹은 .bat)등을 이용해서 조금만 자동화하면 생각보다 어렵지 않게 쓸 수 있다.

지금은 go get 을 쓰기 위해 Dockerfile 에 git을 포함하여
FROM golang:1.7-alpine
MAINTAINER spectrum <spectrick@gmail.com>
RUN apk --update add git &&\
    rm -rf /var/lib/apt/lists/* && \
    rm /var/cache/apk/*
EXPOSE 8000
이런 식으로 만들고

docker build -t migolang .

이렇게 migolang 이란 이름을 가진 이미지를 생성하여

alias를 마지막으로

alias @="docker run -it -v $PWD:/usr/src/myapp -w /usr/src/myapp --rm --name migolang -p 8000:8000 migolang"

이렇게 migolang 을 지정.
@ go get 을 잘 쓰고 있다. 아주 좋다.

2016년 8월 14일 일요일

ReactiveX(Rx), Promise, 그리고 Future

Rx를 하다보니 fiber/future도 새롭게 보인다.
가령 비동기 구간을 setTimeout -> ..... , 1000 으로 하는 구현을 해보자.
Rx.Observable.create (observable)->
  # callback block
  setTimeout ->
    observable.onNext "<결과값>"
  , 1000
.subscribe (res)->
  console.log res
요건 Promise로 풀면
new Promise (resolve, reject)->
  # callback block
  setTimeout ->
    resolve "<결과값>"
  , 1000
.then (res)->
  console.log res
거의 같은 모양을 하고 있다.
그 fiber/future로 풀면
doSomeAsync = ->
  future = new Future()
  setTimeout ->
    future.return "<결과값>"
  ,1000
  future.wait()
console.log doAsync()
핵심은 동기에서 return 에 들어갈 부분이 대체되고 그걸 외부에서 받는 부분이랑 한 쌍을 구성한다는 점인데 future는 c coroutine library 인 libcoro(https://github.com/ramonza/libcoro)를 사용하고 있다. 
사실 coroutine만 해도 c에서 구현만 http://www.chiark.greenend.org.uk/~sgtatham/coroutines.html 이렇게 다양한데.
ECMA6 (coffeescript 1.10.0) 에서 반쪽짜리 코루틴이라고 말하는 generator도 같이 비교해도 봐야하지 않을까 싶어서 보니
req=(url)->
  setTimeout ->
    it.next(url.length)
  , 1000
main=-> console.log yield req "hohoho bababa"
it=main()
it.next()
req가 impure 한 function이 되는게 맘에 걸린다. 어째서 생성시점 이전에 외부의 it를 받아서 .next를 하는 걸까.
이전 글(http://spectrumdig.blogspot.kr/2016/07/generator.html)처럼 Promise를 쓸 수 있지만 그럴거면 그냥 다 Promise로 하는게 낫지 않나 하는 생각도 들고.

 이곳의 소스는 모두 여기(http://decaffeinate-project.org/repl/) 에서 돌려볼 수 있다.


2016년 8월 11일 목요일

[삽질주의] js에선 object를 어떻게 확장하는가? .map 만들기.

http://spectrum.egloos.com/5580651
옛날 글 소환인데 map을 object 에서도 한번 해볼려고 이렇게 시도해 보았다.

> Object.defineProperty(Object.prototype, "map", {value: function(fn) {
  for (idx in this) this[idx]=fn(this[idx]); return this;
} });
> q={a:1, b:2}
> q.map(v=>v+1)
Object {a: 2, b: 3}

ECMA5부터 Object.defineProperty 를 쓸 수 있고 prototype 삽질을 막을 수 있다.

> q={a:1, b: {c: 2, d: 4}}
> Object.defineProperty(Object.prototype, "map", {value: function(fn) {
  let map=(f,arr)=>{
    for (idx in arr)
        arr[idx]=typeof arr[idx]==="object" && map(f,arr[idx]) || f(arr[idx]);
    return arr;
  }
  return map(fn, this);
}});
> q.map(v=>v+1)
Object {a: 2, b: Object}
a:2
b:Object
  c:3
  d:5

뭐 의도한대로 잘 나오긴 한다.
value 인 경우만 fn을 실행하고 object면 재귀를 사용한다.
뭐 array만 들어와도 이건 망하겠네;
google 에서 왜 [[a,2], [b,3]] 따위 자료 구조를 썼는지 어느정도 이해가 된다.