4.4.애플리케이션 빌드 실전 예제: Go 소스 코드

  • 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
  1. 001: Go 언어의 도구가 설치된 이미지를 사용
  2. 003~004: Go 애플리케이션 빌드는 일반적으로 의존 모듈을 내려받는 단계 없이 곧장 빌드에 들어간다. 대개 main.go 단일 파일로 구성된다.
  3. 007: 최소한의 운영체제 레이어만 포함하는 이미지 사용
  4. 013~015: builder 단계에서 빌드한 웹 서버 바이너리와 이 웹 서버가 제공할 HTML 파일을 복사하는 과정
  5. 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
  1. 034: 호스트 컴퓨터에 있는 HTML 파일을 최종 이미지로 복사해온다.
  2. 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 명령 자체가 파이프라인 정의 역할을 한다.

links

social