DockerでGoのAPIサーバーを立てる

# はじめに
DockerでGoのAPIサーバーを立てるDockerFileを作成しました。
調べたこと、DockerFileについて調べたことをまとめます。

[入門Docker 学習メモ](https://qiita.com/bear05/items/302de545018948b5c9c7)
後の実践

# 基礎知識の補足
### ビルドコンテキスト（build context）
>docker buildコマンドを実行したときの、カレントなワーキングディレクトリのこと

[Dockerfile 記述のベストプラクティス](https://matsuand.github.io/docs.docker.jp.onthefly/develop/develop-images/dockerfile_best-practices/)

### Dockerデーモン
Docker Engineの一部。
Docker EngineはDocker CLI、Docker Engine API、 Dockerデーモンで構成されている。
イメージのビルドやコンテナの起動を行ってくれるもの。
> Docker Engineとは、コンテナ化及びコンテナを動作させるためのアプリケーションで、Dockerの核となる部分です。
[【触って理解！】Docker入門 - 初心者に向けて使い方や基本コマンドを解説](https://www.pasonatech.co.jp/workstyle/column/detail.html?p=2675)


そもそもデーモンって何、
→UNIX系OSにおけるメモリ上で命令を待機している常駐プログラムのこと
>Docker Engine はクライアント・サーバーモデルのアプリケーション

[さわって理解する Docker 入門　第6回]
(https://www.ogis-ri.co.jp/otc/hiroba/technical/docker/part6.html)

###   .dockerignore を使ったファイル除外の指定
ビルドに関係ないファイルを除外する

### アプリケーションの分割
1 つのコンテナーが取り扱う内容は 1 つにしぼる。
例えば、Go APIサーバーとmysqlサーバーを作成したいときは、
Goのコンテナ、mysqlコンテナをそれぞれ作成する。


# Dockerfileを書くときのポイント
### ・イメージのビルドに必要がないファイルは含まないこと
### ・レイヤー数は最小に
> Docker イメージは読み取り専用のレイヤーにより構成されます。 個々のレイヤーは Dockerfile の各命令を表現しています。 レイヤーは順に積み上げられ、それぞれは直前のレイヤーからの差分を表わします。
[Dockerfile 記述のベストプラクティス](https://matsuand.github.io/docs.docker.jp.onthefly/develop/develop-images/dockerfile_best-practices/)

COPYなどの命令一行でレイヤーが一枚作成される。レイヤーが一行ごとに積みあがってimageが作成される。
同じDockerfileからimageを作成するとき、更新がなければ前回のimageのキャッシュが利用できる。imageを作成する時間が少なくて済むため、更新が少ないファイルに対する命令を前半に、更新が多いファイルに対する処理は後半に書くこと。


# 初回 Dockerfileとdocker-composeの設定
``` 
FROM golang:1.18-alpine as builder

ENV ROOT=/cafekatu/goapi
RUN mkdir -p ${ROOT}
WORKDIR ${ROOT}

COPY goapi/go.mod ./
COPY goapi/go.sum ./

RUN go mod download

COPY goapi/main.go ./
COPY goapi/controller ./
COPY goapi/model ./

RUN go get

RUN CGO_ENABLED=0 GOOS=linux go build -o $ROOT/binary
EXPOSE 8080
CMD ["/cafeKatu/goapi/binary"]
```

1行目:Goの元ファイルをダウンロード
2~4行目:linuxに/cafeKatu/goapiフォルダを作成、ワーキングディレクトリを設定。
ワーキングディレクトリとは、Dockerイメージの中のどのディレクトリで動作するかを設定する。
今回だと、Dockerイメージの/root/cafeKatu/goapiということ。

5~11行目
カレントディレクトリ(ビルドコンテキスト)からDockerの/cafeKatu/goapiにgo.mod go.sumをコピー
カレントディレクトリ(ビルドコンテキスト)からDockerの/cafeKatu/goapiにgoのソースをコピー
go.modに必要なライブラリを追加、不要なライブラリを削除
疑問　外部のリポジトリを利用してないからgogetするひつようあるのか？？？

12行目　クロスコンパイルで実行ファイルを作成する。Goはクロスコンパイルが便利なところが特徴の一つ。
 >クロスコンパイルとは、ソースコードを元に、開発に使用している機種やOSとは異なる環境向けに実行可能なコードを生成すること。
[クロスコンパイル 【cross compile】](https://e-words.jp/w/%E3%82%AF%E3%83%AD%E3%82%B9%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB.html)

「 GOOS=linux」でDockerがLinuxの環境だからLinuxOS 向けのバイナリを生成してる？
[Go のクロスコンパイル環境構築](https://qiita.com/Jxck_/items/02185f51162e92759ebe)
「CGO_ENABLED=0」CGO_ENABLEDはコンパイル時にシステム native な C ライブラリの使用を指定するもの、0で無効になる。これよくわからない。
[golang でクロスコンパイルする話](https://www.getto.systems/entry/2020/06/11/013316#cgo_enabled)
14行目　バイナリファイルを実行
[Docker CMDとENTRYPOINTの使い方](https://www.grandream.jp/blog/docker-cmd-entrypoint/)

### ○Dockerfile参考
[Goで作成したAPIをDockerで動かす]
(https://zenn.dev/kenny/articles/b9dd668bf09efe)
[Golang - Dockerfileの最小構成](https://qiita.com/Yuuki557/items/3d088de91ab86bc71600)

# docker-compose
```
version: '3'
  go-api-server:
    image: go-api-server
    container_name: go-api-server
    build:
      context: ./goapi
      dockerfile: ../dockerfile/goapi/Dockerfile
    env_file: ./goapi/env/dev.env
    ports:
      - "127.0.0.1:8080:8080"
    depends_on:
     - cafekatu_db
```
docker-composeを実施
以下のエラーが発生

```
cafekatu % docker-compose build --no-cache
[+] Building 3.1s (14/15)                                                                                                                     
 => [internal] load build definition from Dockerfile                                                                                     0.0s
 => => transferring dockerfile: 32B                                                                                                      0.0s
 => [internal] load .dockerignore                                                                                                        0.0s
 => => transferring context: 2B                                                                                                          0.0s
 => [internal] load metadata for docker.io/library/golang:1.18-alpine                                                                    1.0s
 => CACHED [ 1/11] FROM docker.io/library/golang:1.18-alpine@sha256:7dc3de187ac994ac6d95a2e55fe87e962db391dddb4e28a0fdf0be4cd5962bbe     0.0s
 => [internal] load build context                                                                                                        0.0s
 => => transferring context: 640B                                                                                                        0.0s
 => [ 2/11] RUN mkdir -p /cafekatu/goapi                                                                                                 0.2s
 => [ 3/11] WORKDIR /cafekatu/goapi                                                                                                      0.0s
 => [ 4/11] COPY goapi/go.mod ./                                                                                                         0.0s
 => [ 5/11] COPY goapi/go.sum ./                                                                                                         0.0s
 => [ 6/11] RUN go mod download                                                                                                          0.6s
 => [ 7/11] COPY goapi/main.go ./                                                                                                        0.0s
 => [ 8/11] COPY goapi/controller ./                                                                                                     0.0s
 => [ 9/11] COPY goapi/model ./                                                                                                          0.0s
 => ERROR [10/11] RUN go get                                                                                                             1.1s
------                                                                                                                                        
 > [10/11] RUN go get:
#0 0.986 go: downloading github.com/akane-05/cafekatu v0.0.0-20220706064737-fe3f9a36b152
#0 1.044 github.com/akane-05/cafekatu/goapi imports
#0 1.044        github.com/akane-05/cafekatu/goapi/controller: cannot find module providing package github.com/akane-05/cafekatu/goapi/controller
#0 1.044 github.com/akane-05/cafekatu/goapi imports
#0 1.044        github.com/akane-05/cafekatu/goapi/controller/dto: cannot find module providing package github.com/akane-05/cafekatu/goapi/controller/dto
#0 1.045 github.com/akane-05/cafekatu/goapi imports
#0 1.045        github.com/akane-05/cafekatu/goapi/model/entity: cannot find module providing package github.com/akane-05/cafekatu/goapi/model/entity
#0 1.045 github.com/akane-05/cafekatu/goapi imports
#0 1.045        github.com/akane-05/cafekatu/goapi/model/repository: cannot find module providing package github.com/akane-05/cafekatu/goapi/model/repository
```


# # 改善 Dockerfileとdocker-composeの設定

## dockerfile
```
FROM golang:1.18-alpine3.15 as builder

ARG ROOT=/go/src/github.com/akane-05/cafekatu/goapi
WORKDIR ${ROOT}

COPY go.mod ./
COPY go.sum ./
COPY main.go ./
COPY controller/ ./controller/
COPY model/ ./model/
RUN go mod download
RUN CGO_ENABLED=0 GOOS=linux go install -v \
    -ldflags="-w -s" \
    ${ROOT}

FROM alpine:3.15
COPY --from=builder /go/bin/goapi /goapi
RUN chmod a+x /goapi

RUN env

EXPOSE 8080
CMD ["/goapi"]
```

### 改善点
1.ディレクトリごとコピーしたい場合は
``` COPY controller/ ./controller/``` と書く
[DockerfileのCOPYでファイルやディレクトリごとコンテナにコピーする](https://t8.dev/ja/blog/docker_dockerfile_copy/?fbclid=IwAR0X1MOTt5M-_Poudi5diNo-xXxgnPoiAl-SXx1xxuIScPSmj8LymR8U1Jw)

2.envをARGに変更
ARGはbuild以降に有効、ENVはコンテナ(docker up)以降に有効。
テスト環境と本番環境でファイルを切り替えたりするときに便利。
>ENVでは、コンテナ内で環境変数として変数が定義されます。 ARG は、 Dockerfileのbuild時に一時的に変数が定義され、コマンドの実行時に展開されます。ENVを使用する場合は、CMDやENTRYPOINTによって実行されたコマンドに適しており、ARGは、変数を展開した状態でコンテナ内にファイルを配置したい場合に有効です。
ARGは変数を展開した状態でコンテナに状態を設定するという特性から、コンテナが管理するファイルには有効ですが、 VOLUMEによって管理されているディレクトリに配置されているファイルに対してはENVを使ったほうが便利な場面が多いです。
[Dockerfile ARG入門](https://qiita.com/nacika_ins/items/cf8ceb20711bd077f770)

3.go install だと不要にgo.modを変更することがなくなる。
[Go1.16からは go get は使わず go install を使おう](https://qiita.com/eihigh/items/9fe52804610a8c4b7e41)

4.コンパイルするとき-ldflags '-w -s'をするとサイズが小さくなって良い
[Goプログラムのサイズを小さくする](https://shinpei.github.io/blog/2014/12/24/make-go-binary-size-small)
[Go のバイナリには -ldflags '-w -s' でコンパイルしてもたくさんパスが埋め込まれていた](https://qiita.com/kitsuyui/items/d03a9de90330d8c275c8)

5.go install -v 
>-v 現在コンパイル中のパッケージ名を出力します。
>
>go install
このコマンドは実際には内部で2ステップの操作に分かれます。第1ステップはリザルトファイルの生成（実行可能ファイルまたはaパッケージ）、第2ステップはコンパイルし終わった結果を$GOPATH/pkgまたは$GOPATH/binに移動する操作です。
>
>引数はgo buildのコンパイルオプションをサポートしています。みなさんは-vオプションだけ覚えていただければ結構です。これにより低レイヤーの実行状況をいつでも確認することができます。
[1.3 Goのコマンド](https://astaxie.gitbooks.io/build-web-application-with-golang/content/ja/01.3.html)

6.4と5のまとめ
```
RUN CGO_ENABLED=0 GOOS=linux go install -v \
    -ldflags="-w -s" \
    ${ROOT}
```
上記では${ROOT}を小さいサイズgo install(リザルトファイルの生成)して現在コンパイル中のパッケージ名を出力してる

7.```RUN chmod a+x```
すべてのユーザーに全ての権限と実行権限を与える
[chmod コマンド](https://qiita.com/ntkgcj/items/6450e25c5564ccaa1b95)

8.ROOT=/go/src/github.com/akane-05/cafekatu/goapi
ベースイメージの「golang:1.18-alpine3.15」の中で、環境変数の「$GOPATH」に「/go」が設定されている
Goの各コードの中でのimport文の中で、モジュールを参照する際に「github.com/akane-05/cafekatu/goapi/controller」とが使用されていたため、
「$GOPATH/src」の下にソースコードを配置しないとモジュールのパスの解決ができないのでは？と考えられるため変更。
おそらく今はモジュールモードのため他パスでも大丈夫

9./go/bin/goapi
>Executables are installed in the directory named by the GOBIN environment
variable, which defaults to $GOPATH/bin or $HOME/go/bin if the GOPATH
environment variable is not set. Executables in $GOROOT
are installed in $GOROOT/bin or $GOTOOLDIR instead of $GOBIN.
https://zenn.dev/asataka/scraps/71cbd9c6706474

10.Dockerfile内に「RUN env」という一文を追加
ベースとなるDockerイメージの環境変数の一覧を確認したい場合は、Dockerfile内に「RUN env」を追加
「docker-compose build --progress=plain --no-cache」というコマンドを実行😄


## docker-compose
```
version: '3'
  go-api-server:
    image: go-api-server
    container_name: go-api-server
    build:
      context: ./goapi
      dockerfile: ../dockerfile/goapi/Dockerfile
    env_file: ./goapi/env/dev.env
    ports:
      - "127.0.0.1:8080:8080"
    depends_on:
     - cafekatu_db
```
### 改善点
1.環境変数を定義するenvファイルを作成、追加

[【Go】.envを使って環境変数を読み込む(godotenv) +osパッケージでenvを触ってみる。](https://zenn.dev/a_ichi1/articles/c9f3870350c5e2?fbclid=IwAR0x1i-XEyAk_fGy71TyCN2LGg28qgpA-UkTK176k-sBOWMnN1GhjpLYnnM)
[.envで定義した環境変数をDocker,docker-compseで使用する例](https://mzqvis6akmakplpmcjx3.hatenablog.com/entry/2020/05/03/184918)


# その他
## パスワード生成
[パスワード生成](luft.co.jp/cgi/randam.php?fbclid=IwAR0RIJFENq9xu10puU08xd_azN6wGcT2lhVsROPpXO64O4JHSH6OvyJFpK8)

## Goプロジェクト作成の基本
[Go言語の開発環境セットアップとサンプルプロジェクト作成](https://dev.classmethod.jp/articles/go-setup-and-sample/)
## go getについて
[Go1.16からは go get は使わず go install を使おう](https://qiita.com/eihigh/items/9fe52804610a8c4b7e41)