1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

<Golang+Nginx+MySQL>Macローカル上にDocker Composeでアプリを立ち上げてみた

Last updated at Posted at 2021-03-15

#Dockerアプリケーションのアーキテクチャー
スクリーンショット 2021-03-15 10.32.57.png

##ざっくりとやること・できることの説明

  • docker-composeでNginxとGo言語のAPI, DBとしてMySQLのコンテナを3つ立ち上げる
  • Nginxのindex.htmlからGo言語で書いたREST API(go-ginのフレームワーク使用)を経由して、DBに問い合わせ
  • DBにCRUD(gormのフレームワーク使用)ができることを確認
  • ※DockerとDocker Composeはインストールされているものとする。

##バージョン確認
確認してみたら、以下のバージョンでした。

Docker version 20.10.2
docker-compose version 1.27.4
golang:1.14.7-alpine3.12
mysql:5.7
nginx:1.17.3-alpine

※厳密にはバージョンの依存関係とかも考えないといけないが、一旦ローカルで動かすので、スルーしますm(__)m

#フォルダ構成

docker-local
├── .env
├── api
│   └── src
│       ├── main.go
│       ├── model  ── model.go
│       └── config ── config.go
├── db
│   ├── conf ── my.cnf
│   └── db-data
├── docker
│   └── nginx
│       ├── Dockerfile
│       └── nginx.conf
├── docker-compose.yml
└── web
    ├── env
    │   └── nginx ── site.conf
    └ src ── index.html

###ディレクトリとファイルの用途解説

名前 種類 説明
docker-local フォルダ dockerローカルアプリケーションの親フォルダ
.env ファイル docker-composeで使う環境変数のファイル
api フォルダ APIのフォルダ
src フォルダ ソースフォルダ
main.go ファイル goを起動するときのファイル
model/model.go ファイル DBとやりとりする時のファイル
struct, dao系のメソッドを記載
config/config.go ファイル DBの接続情報等を記載
db フォルダ DBに関するフォルダ
conf/my.cnf ファイル 任意のDB設定を記載。マウント用
db-data フォルダ DBのデータをsyncするマウント用
docker フォルダ Dockerfileたちを格納するフォルダ
nginx フォルダ NginxのimageをビルドするDockerfileを格納
Dockerfile ファイル nginxのimage
nginx.conf ファイル nginxの設定ファイル
docker-compose.yml ファイル docker containerを一括でコントロールするyamlファイル
web フォルダ フロントのファイルを格納
env フォルダ 環境設定ファイルを格納
nginx/site.conf ファイル nginxのserver設定ファイル
IPアドレス、ポート、ルーティングなど設定
src/index.html ファイル 画面表示用ファイル

#Docker Composeの中身

version: "3.8"

services:
  nginx:
    image: infra-challenge:nginx-20210312
    # command: 
    build: 
      context: ./docker/nginx
      dockerfile: Dockerfile
    container_name: nginx
    ports:
      - 80:80
    volumes:
      - ./web/env/nginx:/etc/nginx/conf.d
      - ./web/src:/var/www/html
    depends_on:
      - api
    logging:
      driver: "none"
    restart: always
    networks:
      - services

  api:
    image: golang:1.14.7-alpine3.12
    command: go run main.go --host 0.0.0.0 --port 8080
    container_name: api
    ports:
      - 8080:8080
    volumes:
      - ./api:/go/src/github.com/infra-challenge/api
      - /Users/username/go:/go
    working_dir: /go/src/github.com/infra-challenge/api/src
    environment:
      - APP_STAGE=local
      - LOG_LEVEL=debug
      - MYSQL_CONNECTION
    depends_on:
      - db
    restart: always
    networks:
      - services

  db:
    image: mysql:5.7
    # build: ./docker/mysql
    container_name: db
    ports:
      - 3306:3306
    volumes:
      - ./db/conf/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./db/db-data:/var/lib/mysql
    environment:
      - MYSQL_ROOT_PASSWORD
      - MYSQL_DATABASE
      - MYSQL_USER
      - MYSQL_PASSWORD
    healthcheck:
      test: ["CMD-SHELL", "mysqlshow -u ${MYSQL_USER} -p${MYSQL_PASSWORD}"]
      interval: "5s"
      retries: 10
    logging:
      driver: "none"
    restart: always
    networks:
      - services
    
networks:
  services:
    external:
      name: local_infra_challenge_networks

###主な属性の解説

  • docker composeの文法バージョンは3.8だよ
  • services配下に、コンテナの定義を書いていく。今回はnginx, api, dbの3つ
  • nginxDockerfileを使うから、image名は任意で、buildの属性を指定。
    • contextはDockerfileのある相対パス、dockerfileはDockerfile名
    • ※ファイル名がDockerfileとなっていたら、build: ./docker/nginxとしてもOK
  • container_nameを指定すると、docker stopとかするときに、idじゃなくて名前を指定すれば制御できるので便利
  • ports<ホスト(Macローカル)>:<ゲスト(Dockerコンテナ)>でポートを接続
  • volumes<ホスト(Macローカル)>:<ゲスト(Dockerコンテナ)>でマウントフォルダを指定
    • マウントとは、フォルダ同士を共有してsync(同期)すること。
  • depends_onは他のコンテナが起動成功でき次第〜みたいなやつ
  • apiとdbは、自分でDockerfileは用意せずに、Docker-Hub公式のimageを使っている
  • environmentに環境変数を指定する。
    • docker-compose.ymlと同階層に.envファイルを置き指定
  • DBではhealthcheckを行い、起動しているか随時確認
  • ネットワークを組むことで、コンテナ間通信が可能となり、APIからDBに問い合わせができるようになった

###ネットワークについて
作ったネットワークの情報を確認してみると、下記のようなObjectが返ってきますね〜
"Subnet": "172.19.0.0/16",の中で、Containers(nginx, api, db)がIPアドレスを振られて存在しているのがわかります。
ちなみに、networksだけでなくlinksという属性もあり、linksを使うと、一方的にコンテナ間通信ができるようになる設定もあるので、TPOによって使い分ける◎
公式docker:links

$ docker network inspect local_infra_challenge_networks
[
    {
        "Name": "local_infra_challenge_networks",
        "Id": "f6a978e6f3aa803be061418c1c7081d132c9538c207ea08b1d732fc55ba2b917",
        "Created": "2021-03-12T03:57:20.0697997Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "7a81d8c6f965a07ce446aa0983180bbb4775187ed6873a9e26dbc9540f623392": {
                "Name": "nginx",
                "EndpointID": "09336c1acc58f9b75ea3ced604a7d82b168a5f0cd132ae4f2fffd267a416b022",
                "MacAddress": "02:42:ac:13:00:04",
                "IPv4Address": "172.19.0.4/16", #<-これ!!
                "IPv6Address": ""
            },
            "842244244bf00593e25317dd1cafb35ae03cfbc5246a87c666c6434a6ad7540d": {
                "Name": "db",
                "EndpointID": "3810c5b1b5f87b3ba4331320fcb2106696f3dcd157a771f9de63aeffc3a14720",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16", #<-これ!!
                "IPv6Address": ""
            },
            "c0906b3fc86d92f4b89474b7647e85a36e489469f83905c94785f2f278807532": {
                "Name": "api",
                "EndpointID": "e291176e7bd08040c2486d0409dab009a724fb7e3f0da3a5a9585cb23344a999",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16", #<-これ!!
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

####コンテナ間通信をするときは、コンテナ名でエンドポイントを指定することができるから便利

# db:3306 の部分 <コンテナ名:ポート>
MYSQL_CONNECTION=local:local@tcp(db:3306)/infra-challenge 

###マウントについて
####nginx

    volumes:
      - ./web/env/nginx:/etc/nginx/conf.d
      - ./web/src:/var/www/html
  • nginxにはdefault.confというものがあり、それを自分の設定に書き換えたいので、./web/env/nginx
    で書き換えてる
  • /var/www/html以下がdockerコンテナ内のnginxフォルダ構成になる(する)ので、index.htmlがあるディレクトリを同期

####api

    volumes:
      - ./api:/go/src/github.com/infra-challenge/api
      - /Users/username/go:/go
  • とりあえずGo Pathが通っているところをdockerコンテナ側にマウントしてる。
  • APIのファイル等も同期

####db

    volumes:
      - ./db/conf/my.cnf:/etc/mysql/conf.d/my.cnf
      - ./db/db-data:/var/lib/mysql
  • MySQLのデータの格納先が/var/lib/mysqlになるので、ホストの./db/db-dataに同期して、コンテナを除去してもデータが消えないようにする
  • 自分のDB設定、例えば、character-setとかcollationとかをマウントして上書き
# my.cnf
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci

[mysql]
default-character-set=utf8mb4

#いざ、Docker Composeで起動してみる!!
##コマンド

$ cd /Users/username/src/github.com/infra-challege/docker-local
$ docker network create --driver bridge local_infra_challenge_networks
[~省略~]
$ docker-compose build --no-cache
[~省略~]
$ docker-compose up -d --remove-orphans
Creating db ... done
Creating api ... done
Creating nginx ... done

$ docker ps -a
CONTAINER ID   IMAGE                                 COMMAND                  CREATED         STATUS                            PORTS                               NAMES
8fxxxb102ed4   infra-challenge:nginx-20210312        "nginx -g 'daemon of…"   4 seconds ago   Up 3 seconds                      0.0.0.0:80->80/tcp                  nginx
8fxxx25640e6   golang:1.14.7-alpine3.12              "go run main.go --ho…"   6 seconds ago   Up 4 seconds                      0.0.0.0:8080->8080/tcp              api
e5xxxe97320e   mysql:5.7                             "docker-entrypoint.s…"   6 seconds ago   Up 5 seconds (health: starting)   0.0.0.0:3306->3306/tcp, 33060/tcp   db

  • docker-composeファイルがある階層に移動
  • networks属性を指定したので、そのネットワークを作成する。
  • nginxはDockerfileをつかってimageを作成するので、ビルドする必要がある。
    • --no-cacheオプション:構築時にイメージのキャッシュを使わない。
  • docker-compose upで起動
    • -dオプション:バックグラウンド実行
    • --remove-orphansオプション:Composeファイルで定義されていないサービス用のコンテナを削除

#Webページにアクセスできるか確認
(超しょぼいですが...w)
アクセスできた!
スクリーンショット 2021-03-15 11.54.07.png

user1を作成してみよう。
スクリーンショット 2021-03-15 11.57.28.png
作成できたことを確認◎
スクリーンショット 2021-03-15 11.59.15.png

##コンテナ終了コマンド

$ docker-compose down
Stopping nginx ... done
Stopping api   ... done
Stopping db    ... done
Removing nginx ... done
Removing api   ... done
Removing db    ... done

止まりました。

#ハマったポイント

  • go run main.goした後に、起動はしていたがポート設定がミスっていて、APIにアクセスできなかった。
    • 「なんかうまくいかないな〜〜汗」ってなったので、VSCodeから一旦APIサーバーを止めて再起動して、通信がうまくいったけど、それはコンテナ間通信ではなかった、、、
      • コンテナ上ではなくローカルでAPIサーバーが立ち上がってしまい、通信できていると勘違いしてしまった。
  • index.htmlの画面表示するポートとAPIを司るポートそれぞれが別だったので、その調整にハマった。
  • nginxには、サーバー設定が必要だった。
    • nginx.confdefault.confというファイルが存在しこれのIPアドレス、ポート等を設定しないといけないので、前もってnginxの知識をいれておかないと簡単には動かなかった
# site.conf (default.confの上書き)
server {
    listen      80;
    listen      8080; # APIで8080ポートも使ってる関係で、これも必要だった
    server_name localhost;
    root        /var/www/html;

    location / {
        index  index.html index.htm;
        root   /var/www/html;
    }

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /var/www/html/error;
    }
}

#まとめ
docker-compose.ymlでコンテナを起動して、Dockerアプリケーションを作る感覚がつかめたので、楽しかったですわい!

コンテナのコントロールってすごい簡単で、サクサク動くから、せっかちな自分でも全然苦じゃない。
マスターしていきたいなああああ

以上、ありがとうございました!

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?