- Go는 네이티브 바이너리로 컴파일되는 현대적인 크로스 플랫폼 언어
- 원하는 어떤 플랫폼(윈도, 리눅스, amd64 아키텍처, ARM 아키텍처)이든 해당 플랫폼에서 동작하는 바이너리를 컴파일할 수 있다
- 자바나 Node.js 닷넷 코어 혹은 파이썬처럼 별도의 런타임이 필요하지 않기 때문에 도커 이미지의 크기도 매우 작아진다.
- 러스트(Rust)와 스위프트(Swift) 등 네이티브 바이너리로 컴파일되는 언어는 Go 외에도 몇가지가 더 있지만, 그 중에서도 Go가 가장 지원 플랫폼의 범위가 넓다.
- 또한 Go는 클라우드 네이티브 언어로서도 인기가 높다. 애초에 도커 자체가 Go로 구현됐을 정도
- 도커를 이용한 Go 애플리케이션의 멀티 스테이지 빌드는 자바의 빌드 방식과 유사한 점이 많지만 몇 가지 중요한 차이점이 있다.
Go 애플리케이션의 멀티 스테이지 빌드를 위한 Dockerfile 스크립트 예
- Go는 네이티브 바이너리로 컴파일된다.
- 그러므로 각 빌드 단계는 서로 다른 기반 이미지를 사용한다.
001) FROM diamol/golang AS builder
002)
003) COPY main.go .
004) RUN go build -o /server
005)
006) # app
007) FROM diamol/base
008) ENV IMAGE_API_URL="http://iotd/image" \
009) ACCESS_API_URL="http://accesslog/access-log"
010)
011) CMD ["/web/server"]
012)
013) WORKDIR /web
014) COPY index.html .
015) COPY --from=builder /server .
016) RUN chmod +x server
- 001: Go 언어의 도구가 설치된 이미지를 사용
- 003~004: Go 애플리케이션 빌드는 일반적으로 의존 모듈을 내려받는 단계 없이 곧장 빌드에 들어간다. 대개 main.go 단일 파일로 구성된다.
- 007: 최소한의 운영체제 레이어만 포함하는 이미지 사용
- 013~015: builder 단계에서 빌드한 웹 서버 바이너리와 이 웹 서버가 제공할 HTML 파일을 복사하는 과정
- 016: 바이너리 파일이 chmod 명령을 통해 명시적으로 실행 권한을 부여받는다.(윈도우에서는 효과가 없다.)
Dockerfile 스크립트 빌드
001) ➜ docker build -t image-gallery .
002) [+] Building 13.4s (16/16) FINISHED
003) => [internal] load .dockerignore 0.0s
004) => => transferring context: 2B 0.0s
005) => [internal] load build definition from Dockerfile 0.0s
006) => => transferring dockerfile: 339B 0.0s
007) => [internal] load metadata for docker.io/diamol/base:latest 3.0s
008) => [internal] load metadata for docker.io/diamol/golang:latest 3.4s
009) => [auth] diamol/golang:pull token for registry-1.docker.io 0.0s
010) => [auth] diamol/base:pull token for registry-1.docker.io 0.0s
011) => [builder 1/3] FROM docker.io/diamol/golang@sha256:ffc019466b60046d67b71628afafc9f80cc4d6a6bce824dc89d51300ecec0902 7.6s
012) => => resolve docker.io/diamol/golang@sha256:ffc019466b60046d67b71628afafc9f80cc4d6a6bce824dc89d51300ecec0902 0.0s
013) => => sha256:ffc019466b60046d67b71628afafc9f80cc4d6a6bce824dc89d51300ecec0902 1.41kB / 1.41kB 0.0s
014) => => sha256:928e2ab79c640602110b5ef0ca5969e6856fc874ee8537c6ede1c60dd18bf1ea 6.35kB / 6.35kB 0.0s
015) => => sha256:3feb40d9f5fecfa098b8f7ece6c287c6fd61b114043c8b4647359120a7d943a3 9.98MB / 9.98MB 1.2s
016) => => sha256:1910bbf0b9a0f73613c202e13d9940c1050acf3f4522a553c60637992f5dd7a2 1.79kB / 1.79kB 0.0s
017) => => sha256:d5517ee72007172d5b814636405254dea459120ce08f85777bb287d106a6a240 49.18MB / 49.18MB 1.7s
018) => => sha256:0283897ad4463628bd259e1cbb5eb788e7df554b42ae17fc6f5d02c4a56035c2 7.68MB / 7.68MB 1.0s
019) => => sha256:f86b5fabb62f79acd92186da3c02d23bd8c15d79603a700959b582bd9e62854c 52.16MB / 52.16MB 4.2s
020) => => sha256:37c836803dd1977ffe173b42414e8c2ae2e147cee2e1ee34a383f4251cf15a44 62.53MB / 62.53MB 4.7s
021) => => sha256:d9dc248055f60dc2aa62c9eb18d6b6f4ec0de19f410168704b3a59da5801d8fe 97.69MB / 97.69MB 5.6s
022) => => extracting sha256:d5517ee72007172d5b814636405254dea459120ce08f85777bb287d106a6a240 0.7s
023) => => extracting sha256:0283897ad4463628bd259e1cbb5eb788e7df554b42ae17fc6f5d02c4a56035c2 0.1s
024) => => extracting sha256:3feb40d9f5fecfa098b8f7ece6c287c6fd61b114043c8b4647359120a7d943a3 0.1s
025) => => extracting sha256:f86b5fabb62f79acd92186da3c02d23bd8c15d79603a700959b582bd9e62854c 0.9s
026) => => sha256:f7bc7fcd16054fc4f78b4420338b2694561bcb99063f896a8d4cf6e61a2596f5 156B / 156B 4.5s
027) => => extracting sha256:37c836803dd1977ffe173b42414e8c2ae2e147cee2e1ee34a383f4251cf15a44 0.8s
028) => => extracting sha256:d9dc248055f60dc2aa62c9eb18d6b6f4ec0de19f410168704b3a59da5801d8fe 1.3s
029) => => extracting sha256:f7bc7fcd16054fc4f78b4420338b2694561bcb99063f896a8d4cf6e61a2596f5 0.0s
030) => [internal] load build context 0.0s
031) => => transferring context: 1.67kB 0.0s
032) => CACHED [stage-1 1/5] FROM docker.io/diamol/base@sha256:787fe221a14f46b55e224ea0436aca77d345c3ded400aaf6cd40125e247f35c7 0.0s
033) => [stage-1 2/5] WORKDIR /web 0.0s
034) => [stage-1 3/5] COPY index.html . 0.0s
035) => [builder 2/3] COPY main.go . 0.2s
036) => [builder 3/3] RUN go build -o /server 1.6s
037) => [stage-1 4/5] COPY --from=builder /server . 0.0s
038) => [stage-1 5/5] RUN chmod +x server 0.2s
039) => exporting to image 0.0s
040) => => exporting layers 0.0s
041) => => writing image sha256:a6d96205f40742ae386f4878686cb8b13bc9cf7f858718b42d140e69f0dc2ceb 0.0s
042) => => naming to docker.io/library/image-gallery
- 034: 호스트 컴퓨터에 있는 HTML 파일을 최종 이미지로 복사해온다.
- 037: builder 단계에서 빌드한 웹 서버 바이너리를 복사해 온다.
컨테이너 실행
- 이번 장에서 실행했던 다른 애플리케이션이 제공하는 API를 사용한다.
- 3개의 컨테이너에 걸쳐 실행되는 분산 애플리케이션이 실행된다.
- Go로 구현된 웹 애플리케이션이 자바로 구현된 API를 호출해 이미지의 상세정보를 얻은 다음 Node.js로 구현된 API에 접근 로그를 남긴다.
001) ➜ docker run -dp 802:80 --network nat image-gallery
002) a74e2aced9294c42f3682c52b1a732b2e90a9ccf6779433c190f7ceabb5f1dc9
http://localhost:802
로 접근해 보면 NASA가 제공하는 오늘의 천문 사진을 볼 수 있다.
- 멀티 스테이지 빌드를 적용한 Dockerfile 스크립트를 통해 프로젝트의 이식성을 극적으로 향상시켰다.
- 이 시점에서 젠킨스를 이용해 애플리케이션을 빌드할 수도 있지만, 앱베이어(AppVeyor)의 매니지드 CI 서비스나 애저의 데브옵스를 적용하면 빌드 파이프라인을 추가로 정의하지 않아도 된다.
docker image build
명령 자체가 파이프라인 정의 역할을 한다.