⚠️ 記事内に広告を含みます。

Dockerのチュートリアルで操作を学ぼう

goのアプリをDockerで開発するチュートリアルで学ぶ

オフィシャル提供のチュートリアルでDockerの動かし方を学びます

Dockerfileの書式は以下を確認してください

dockerfileでコンテナイメージをビルドする

goのWebサーバを作成していきます。

参考のチュートリアルは下記です。

What will you learn in this module?

webサーバ作成の手順は下記を参考

Build your Go image

リポジトリ

https://github.com/olliefr/docker-gs-ping

1. dockerfileからイメージを作成

適当なディレクトリを作成・移動してリポジトリをクローンする

$ mkdir develop/golang && cd develop/golang && pwd
$ git clone <https://github.com/olliefr/docker-gs-ping>
$ cd docker-gs-ping && ls
Dockerfile             LICENSE                go.mod                 main.go
Dockerfile.multistage  README.md              go.sum                 main_test.g

Dockerfileからイメージをビルドする

dockerfileがあるディレクトリでビルドを実行します。

$ docker build --tag docker-gs-ping ./

###---docker-gs-pingというイメージが作成されています。
$ docker image ls | grep gs-ping
docker-gs-ping                      latest        1c2a2efabe2f   3 hours ago     534MB

(応用)マルチステージビルドを実行する

Dockerfileの作業は意味的に2つに分けることができます。

  1. main.goをビルドしてバイナリを生成する
  2. バイナリ実行ファイルをコンテナイメージに設置する
FROM golang:1.16-alpine

# Set destination for COPY
WORKDIR /app

# Download Go modules
COPY go.mod .
COPY go.sum .
RUN go mod download

# Copy the source code. Note the slash at the end, as explained in
# https://docs.docker.com/engine/reference/builder/#copy
COPY *.go ./

# Build
RUN go build -o /docker-gs-ping

# This is for documentation purposes only.
# To actually open the port, runtime parameters
# must be supplied to the docker command.
EXPOSE 8080

# (Optional) environment variable that our dockerised
# application can make use of. The value of environment
# variables can also be set via parameters supplied
# to the docker command on the command line.
#ENV HTTP_PORT=8081

# Run
CMD [ "/docker-gs-ping" ]

dockerfileのRUN go build -o /docker-gs-pingまでがバイナリ実行ファイルの生成作業です。1.ビルドに必要なファイル(go.modm go.sum, main.go)をベースイメージ内の/appにコピー
2. ビルドしてコンテナのルートディレクトリにdocker-gs-pingを吐き出し

次のバイナリ設置について、ビルドの結果バイナリが生成するので同時に行っています。
このコンテナの作成・実行時には作成したバイナリ(docker-gs-ping)を実行するようにCMDで書かれています。

何が言いたいか?というと、ビルドとデプロイを分けようということです。

  • goのバイナリを生成する(ビルド)
  • バイナリを実行コンテナに設置する(デプロイ)

goのビルドには高機能なベースイメージのコンテナ、実行用コンテナは最小のコンテナに設置すればイメージのサイズの節約が可能です。

プログラムのビルドにはある程度の機能が必要

goのビルドにはある程度機能が充実した=容量の大きい ベースイメージが必要です。

バイナリの実行には最小限のベースイメージでOK

高機能なOSの機能・ライブラリ等、あるいはsshdなどのサーバ機能、各種コマンドはバイナリの実行には不要です。したがって、非常に軽量なコンテナイメージが利用できます。

マルチイメージビルド用のdockerfile

マルチステージビルドとは一つのコンテナベースイメージで完結せずに、複数のイメージを利用してdcokerコンテナイメージをビルドすることです。

上記の通り、goビルドと実行用コンテナのベースイメージを分けます。

multi-stage用のdockerfile

下記のdockerfileにはFROMが2つあるのがわかります。
最初のFROMはgoビルド用のイメージgolang:1.16-buster AS buildを利用し、2つ目のFROMでは軽量で有名なdistrolessのbase-debian10イメージを利用しています。

###--- Build ---###
FROM golang:1.16-buster AS build

WORKDIR /app

COPY go.mod .
COPY go.sum .
RUN go mod download

COPY *.go ./

RUN go build -o /docker-gs-ping

###--- Deploy ---###
FROM gcr.io/distroless/base-debian10
WORKDIR /

COPY --from=build /docker-gs-ping /docker-gs-ping

EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/docker-gs-ping"]

ビルドする

$ docker build -t docker-gs-ping:multistage -f Dockerfile.multistage ./

マルチステージビルドの詳細

上の例では、FROM golang:1.16-buster AS buildとあるように、最初のコンテナイメージの名前をbuildと設定し、ビルドしてdocker-ps-pingを生成しています。

2つ目のbase-debian10では最初のコンテナイメージbuildのdocker-gs-pingをbase-debian10にコピーしています。

マルチステージビルドの結果、イメージサイズを約1/20以下にまで縮小することができました。

$ docker images
REPOSITORY                          TAG           IMAGE ID       CREATED         SIZE
docker-gs-ping                      multistage    e908e1e06f43   3 minutes ago   23.6MB
docker-gs-ping                      latest        1c2a2efabe2f   3 hours ago     534MB

マルチステージビルドについては下記のURLを参考にしてください

https://docs.docker.com/build/building/multi-stage/

マルチステージビルドがない時代?はビルド用とデプロイ用のdockerfileを2つ作成し、ホストとバインドマウントしてビルド用コンテナで作成したバイナリをホストと共有し、デプロイ用コンテナでそのバイナリをコピーしてデプロイするというような方法で実施する必要があります。

マルチステージビルドは一つのdockerfileで良いので効率的です。

作成したイメージからコンテナを作成して実行する

$ docker run -d -p 8080:8080 --name rest-server docker-gs-ping


## curlでWebサーバからの応答を確認する
$ curl -iL http://localhost:8080
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Date: Sat, 14 Jan 2023 14:33:26 GMT
Content-Length: 17

Hello, Docker! <3

DBと連携する

postgreSQLと互換性のあるCockroachDBと連携します。

DBは重要な情報を保持するので、データを永続化させるためのvolumeが必要です。
WebとDBの2つのコンテナがセットになるので、docker-composeを利用します。

docker-composeの作成

DB連携するためmain.goに変更が入ります。

別のディレクトリで以下のレポジトリをクローンしてください

$ git clone https://github.com/olliefr/docker-gs-ping-roach.git
version: '3.8'

services:
  docker-gs-ping-roach:
    depends_on:
      - roach
    build:
      context: .
    container_name: rest-server
    hostname: rest-server
    networks:
      - mynet
    ports:
      - 80:8080
    environment:
      - PGUSER=${PGUSER:-totoro}
      - PGPASSWORD=${PGPASSWORD:?database password not set}
      - PGHOST=${PGHOST:-db}
      - PGPORT=${PGPORT:-26257}
      - PGDATABASE=${PGDATABASE:-mydb}
    deploy:
      restart_policy:
        condition: on-failure
  roach:
    image: cockroachdb/cockroach:latest-v20.1
    container_name: roach
    hostname: db
    networks:
      - mynet
    ports:
      - 26257:26257
      - 8080:8080
    volumes:
      - roach:/cockroach/cockroach-data
    command: start-single-node --insecure

volumes:
  roach:

networks:
  mynet:
    driver: bridge

DBパスワード用の.envファイルを作成

.envファイル

PGPASSWORD=whatever

docker-composeで起動

$ docker-compose config
→設定の確認とエラーチェック

$ docker-compose up -d --build
 → イメージをビルドして起動する。後にdocker-composeファイルを変更した場合には、--buildを入れないとイメージの再構築が行われずイメージに反映されないので注意

DB設定

DBコンテナに入ってデータベースとユーザを作成します。

$ docker exec -it roach ./cockroach sql --insecure

oot@:26257/defaultdb> show databases;
  database_name
-----------------
  defaultdb
  postgres
  system
(3 rows)

Time: 34.377ms

root@:26257/defaultdb> CREATE DATABASE mydb;
CREATE DATABASE

Time: 41.215ms

root@:26257/defaultdb> show databases;
  database_name
-----------------
  defaultdb
  mydb
  postgres
  system
(4 rows)

Time: 27.289ms

root@:26257/defaultdb> CREATE USER totoro;
CREATE ROLE

Time: 44.194ms

root@:26257/defaultdb> GRANT ALL ON DATABASE mydb TO totoro;
GRANT

Time: 41.578ms

アプリケーションの動作確認

ローカルホスト:80にアクセスして動作を確認します。

$ curl http://localhost
Hello, Docker! (0)

##POSTでデータを送るとユニークなバリューが増えると()の中の数字が増加する

$ curl --request POST \
>   --url http://localhost/send \
>   --header 'content-type: application/json' \
>   --data '{"value": "Hello, Docker!"}'
{"value":"Hello, Docker!"}

$ curl  http://localhost/
Hello, Docker! (1)

$ curl --request POST   --url http://localhost/send   --header 'content-type: application/json'   --data '{"value": "Hello, Docker"}'
{"value":"Hello, Docker"}

$ curl  http://localhost/
Hello, Docker! (2)


$ curl --request POST   --url http://localhost/send   --header 'content-type: application/json'   --data '{"value": "WWWWWWWWOOOOOOOW"}'
{"value":"WWWWWWWWOOOOOOOW"}

$ curl  http://localhost/
Hello, Docker! (3)

$ curl --request POST   --url http://localhost/send   --header 'content-type: application/json'   --data '{"value": "WWWWWWWWOOOOOOOW"}'
{"value":"WWWWWWWWOOOOOOOW"}

$ curl  http://localhost/
Hello, Docker! (3)

docker volumeの作成

roachという名前のボリュームを作成します。

volumes:
  roach:
$ docker volume create roach
roach

$ docker volume list
DRIVER    VOLUME NAME
local     roach

作成したroachボリュームをDBコンテナの/cockroach/cockroach-dataディレクトリにマウントします。

    volumes:
      - roach:/cockroach/cockroach-data

ネットワークの作成

WebサーバとDBサーバが互いに通信できるようにネットワークを作成します。
mynetというブリッジネットワークを新規作成します。

networks:
  mynet:
    driver: bridge
$ docker network create -d bridge mynet
b34d47e8bcf6b26117281b1696972b2c4e58a1e196f8df22424cafbee770659f

$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
4dcc1f533e83   bridge    bridge    local
763de16ea37b   host      host      local
b34d47e8bcf6   mynet     bridge    local
82071b8789b8   none      null      local

web(docker-gs-ping-roach)とDB(roach)共にmynetネットワークを利用する。ついでにポートも設定。

services:
  docker-gs-ping-roach:
    networks:
      - mynet
    ports:
      - 80:8080
  roach:
    networks:
      - mynet
    ports:
      - 26257:26257
      - 8080:8080

残りの部分

depends_onで設定したroachが起動してからWebサーバ側を起動する

    depends_on:
      - roach

contextの設定

ビルドのcontextの設定はdocker-compose.ymlと同じカレントディレクトリを指定してる。ビルドに用いるdockerfileの場所が同じディレクトリにあるので.で指定。

    build:
      context: .
      ## 別ディレクトリにあるならdockerfile: ./setting/Dockerfile

コンテナ終了した時の再起動オプション。conditionで再起動する条件を設定。on-failureで終了コードが0以外のエラー時に再起動する。

    deploy:
      restart_policy:
        condition: on-failure

cockroach DBのTLS・認証をスキップするコマンドをDBコンテナで実行する

    command: start-single-node --insecure
services:
  docker-gs-ping-roach:
    depends_on:
      - roach
    build:
      context: .
    container_name: rest-server
    hostname: rest-server
    networks:
      - mynet
    ports:
      - 80:8080
    environment:
      - PGUSER=${PGUSER:-totoro}
      - PGPASSWORD=${PGPASSWORD:?database password not set}
      - PGHOST=${PGHOST:-db}
      - PGPORT=${PGPORT:-26257}
      - PGDATABASE=${PGDATABASE:-mydb}
    deploy:
      restart_policy:
        condition: on-failure
  roach:
    image: cockroachdb/cockroach:latest-v20.1
    container_name: roach
    hostname: db
    networks:
      - mynet
    ports:
      - 26257:26257
      - 8080:8080
    volumes:
      - roach:/cockroach/cockroach-data
    command: start-single-node --insecure

CI/CDのテスト

docker hubのアカウント登録(docker IDの取得)

Docker Hub Container Image Library | App Containerization

サインアップ時のユーザ名がdocker IDになります。

Github

  1. リポジトリを作成する
    1. https://github.com/dvdksn/clockbox/generate
  2. リポジトリのページからsetting, secres and variables→ Actionsを選択
  3. New Repository secretにName:DOCKERHUB_USERNAME, SECRETにdocker IDを入力して作成
  4. アクセストークンを発行する
    1. https://hub.docker.com/settings/security
    2. clockboxciというdiscriptionを入れる
  5. githubでDOCKERHUB_TOKENという名前のシークレットを作成、値にアクセストークンを入力する

ワークフロー設定

  1. GithubのレポジトリページでActionsに移動
  2. set up a a workdlow yourselfを選択
  3. 下記の内容をmain.ymlに入力

ワークフローの手順を定義

メインブランチにプッシュされた際にDocker Setup Buildx の機能を使ってコンテナイメージを構築して、docker hubにイメージをプッシュする

name: ci

on:
  push:
    branches:
      - "main"

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      -
        name: Checkout
        uses: actions/checkout@v3
      -
        name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      -
        name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      -
        name: Build and push
        uses: docker/build-push-action@v3
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: ${{ secrets.DOCKERHUB_USERNAME }}/clockbox:latest

ワークフローの開始

  1. main.ymlをStart Commitして、mainブランチにプッシュする
  2. Actionタブに移動してコミットが正常に終了するか確認
  3. Docker hubに移動

Docker

  1. docekr hubにgithubのリポジトリで新しいイメージがプッシュされていれば完了。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です