LoginSignup
4
3

More than 1 year has passed since last update.

【環境構築】VSCode:Remote-Container機能を利用した[Docker Compose+Go+MySQL]の開発用コンテナ構築

Last updated at Posted at 2022-09-25

はじめに

今回はVSCodeのRemote-Container機能】と【Docker】を使用した、
開発用コンテナ」が便利ということで環境構築を行いました。
最小構成から自分がやりたいことを増やしていく中で、
つまづいた点・検索してもなかなか効果的なサイトが見つからなかった点についてまとめてみました。

この記事で分かること

  • VSCodeのRemote-Container機能を使用した開発用コンテナ作成手順
  • GoおよびMySQLのdocker-compose & Dockerfile devcontainer.jsonの設定で注意する点
  • 上記2点を含む、webアプリ開発用Go+DBコンテナのソースコード

環境とコンテナ起動までの流れ

動作環境

Docker Docker Desktop for Windows 4.12.0
Dockerのbackend WSL2 Ubuntu 20.04 LTS
IDE VSCode(latest)
ホストOS Windows11

コンテナ起動までの流れ

事前に必要なもの

  • WSL2
  • Docker Desktop
  • VSCode

3点のインストールと最低限の設定は必要です。
やり方に関しては本記事では取り扱いません。
WSL導入まで知りたい方はコチラを → 【#50 エンジニア転職学習】WSL2でUbuntu20.04のGo開発環境をつくる 参考に構築ください。
Docker Desktopに関しては公式マニュアル → Windows に Docker Desktop をインストール
どおりに実行すれば問題なくインストールまでいけるはずです。

開発用コンテナ起動までの全体流れ

上記【事前に必要なもの】のセットアップまで終わっている状態からスタートとします。
手順6に記述している通り、.devcontainerが直下にあるディレクトリからReopen in Containerを実行してください。
違う階層で実行するとVSCodeが.devcontainerを認識してくれないらしく、
設定した各種ファイルが反映されなかったり、指定したコンテナが起動しなかったりします。

  1. ローカルでVSCodeを開き、Extensionsから【Remote Development】をinstallするimage.png

  2. WindowsTerminalでWSL2のUbuntuシェルを開き、任意のプロジェクトディレクトリ作成

  3. cd [プロジェクト]でプロジェクトに移動してcode .を実行、WSLからVSCodeを開く

  4. .devcontainerフォルダ作成

  5. docker-compose.yml,Dockerfile,devcontainer.jsonファイルの作成(後ほど詳しく解説)

  6. Docker Desktopを起動しておく

  7. .devcontainerが直下にあるフォルダ(私の例では/build)でcode .をしてVSCodeを開きなおす

  8. F1を押す

  9. 中からRemote-Containers:Reopen in Containerを実行image.png

  10. すぐに新しいVSCodeウィンドウが立ち上がり、開発用コンテナ起動(初回は結構時間かかります)

完成したコードと各ポイント

各コードとポイントを解説します。
今回のRemoteContainerを使用した開発用コンテナに関しては、人によって記述が違うことが多々あったため、
VSCodeの公式ドキュメントDockerの公式ドキュメントをメインに参照していきました。

プロジェクト構成

全体像になります。

.
├── build
│   ├── .devcontainer
│   │   ├── devcontainer.json
│   │   └── docker-compose.yml
│   ├── golang
│   │   └── Dockerfile
│   └── mysql
│       ├── .env
│       ├── Dockerfile
│       └── conf
│           └── my.cnf
├── cmd
│   ├── app
│   │   ├── controllers
│   │   │   ├── route.go
│   │   │   └── webserver.go
│   │   ├── models
│   │   │   ├── albumdb.go
│   │   │   └── base.go
│   │   └── views
│   │       ├── index.html
│   │       ├── keyalbumlist.html
│   │       └── update.html
│   └── main.go
├── go.mod
└── go.sum

Goに関するプロジェクト構成で公式っぽいものはありますが、
正確ではないとGo開発チームから異議が唱えられているそうです。(Go のパッケージ構成のデファクトはあるのか?)
JAVAプロジェクトに見えないように、「src」ではなく「cmd」を使用しましょうとのことなのでその点だけ守ってます。
それ以外の構成はコチラ→ (The Go Blog Package names) の内容を開発進めながら身に付けていくしかないのかと思います。

.devcontainer

Remote-Containerで開発用コンテナをビルドするためのディレクトリです。

devcontainer.json

開発用コンテナとRemote時のVSCodeの設定をメインにするファイルです。
Microsoftの開発チームがGitで公開している → microsoft/vscode-dev-containers をベースにしています。
Docker-Composeを使用してコンテナを立ち上げるため、dockerComposeFileでの指定は必須です。

build/.devcontainer/devcontainer.json
{
	// リモートコンテナ名 適当でok
	"name": "go-db-devcontainer",

	// docker-composeファイルのパス指定 今回は同階層にあるためファイル名のみ記述
	"dockerComposeFile": [
		"docker-compose.yml"
	],
	
	// docker-compose.ymlの中で開発用コンテナとして作業するサービスを指定
	"service": "app",

	// リモートコンテナのVSCodeで実際に作業するディレクトリ
	"workspaceFolder": "/workspace",

	// VSCodeを閉じたときの動作指定
	"shutdownAction": "stopCompose",

	// 非ルートユーザーの作成
	"remoteUser": "vscode",

	// VSCodeの設定を記述 .vscodeフォルダの代わりになる(らしい)
	"settings": {
		"terminal.integrated.defaultProfile.linux": "bash",
		"go.toolsManagement.checkForUpdates": "local",
		"go.useLanguageServer": true,
		"go.gopath": "/go",
		"go.goroot": "/usr/local/go"
	},

	// VSCodeのExtensionsで追加したパッケージを指定
	"extensions": [
		"golang.Go"
	]
}

docker-compose.yml

通常のDocker-Composeファイルです。今回はGo言語とMySQL用のコンテナビルドを指示してます。

build/.devcontainer/docker-compose.yml
version: '3.8'
services:
   app:
    user: vscode

    # Go用のDockerfileがあるpath、dockerfile名を指示
    build:
      context: ../golang
      dockerfile: Dockerfile
    env_file:
      - ../mysql/.env
    # dbコンテナが起動した後に起動するように指示
    depends_on:
      - db

    # プロジェクトフォルダのpathを指定して、workspace(開発用コンテナ内の作業場所)にマウント
    volumes:
      - ../..:/workspace:cached

    # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
    # 何用かは分からないですがGoで開発する際のデバッガーとして必要らしいです
    cap_add:
      - SYS_PTRACE
    security_opt:
      - seccomp:unconfined

    # appコンテナへのポートフォワード指定
    ports:
      - 8080:8080
    # コンテナ切断をしないように指示
    command:
      /bin/sh -c "while sleep 1000; do :; done"
      /bin/bash

  db:
    container_name: db
    
    # MySQL用のDockerfile指定
    build: 
      context: ../mysql
      dockerfile: Dockerfile
    command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    platform: linux/x86_64

    # envファイルのpath
    env_file:
      - ../mysql/.env
    # データ永続化
    volumes:
      - mysql_data:/var/lib/mysql
      - ../mysql/conf/my.cnf:/etc/mysql/conf.d/my.cnf
    ports:
      - 13306:3306
    # コンテナ間の接続を可能にするためのネットワーク設定
    networks:
      - default

volumes:
  mysql_data:
    driver: local
networks:
  default:

Dockerfile(Go用)

Goアプリ用のDockerfileです。ポイントは2点あります。

  • 最後のsudo chmod -R a+rwX /go/pkg/を入れておく
  • RUN&&で繋いで処理を軽くする(個別にRUNを記述しない)

特にパーミッション変更は、開発時にサードパーティパッケージ等を使用する際に
go modへの追加変更をするために必要ですので忘れずいれておきましょう。

FROM golang:1.19

ENV GO111MODULE on
WORKDIR /workspace

# コンテナ内の非ルートユーザの設定
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# 非ルートユーザー作成
# sudoコマンドの追加
# Go用の開発ツール
# go mod用のパーミッション変更 など
RUN groupadd --gid $USER_GID $USERNAME \
    && useradd --uid $USER_UID --gid $USER_GID -m $USERNAME \
    && apt update \
    && apt install -y sudo \
    && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \
    && chmod 0440 /etc/sudoers.d/$USERNAME \
    && go install golang.org/x/tools/gopls@latest \
    && go install github.com/go-delve/delve/cmd/dlv@latest \
    && go install github.com/ramya-rao-a/go-outline@latest \
    && go install github.com/fatih/gomodifytags@latest \
    && go install github.com/josharian/impl@latest \
    && sudo chmod -R a+rwX /go/pkg/

mysql

既に似たようなコンテナがあると正常に起動されない時があるようです。
そのときはdocker system prunedocker system prune --volumeを実行して、
Dockerをきれいにしましょう。私の場合はそれで問題なく起動しました。

my.cnf

文字コード指定やプラグイン方法の指定をしています。

build/mysql/conf/my.cnf
[mysqld]
character-set-server = utf8mb4
collation-server = utf8mb4_bin

default-time-zone = SYSTEM
log_timestamps = SYSTEM

# mysql8.0以降であれば必須↓
default-authentication-plugin = mysql_native_password

[mysql]
default-character-set = utf8mb4

[client]
default-character-set = utf8mb4

.env

環境変数としてDatabaseやパスワードの記述を行います。

build/mysql/.env
# 任意の値にしてください
MYSQL_DATABASE=test1
MYSQL_USER=dev_user
MYSQL_PASSWORD=password
MYSQL_ROOT_PASSWORD=root_password

Dockerfile(MySQL用)

FROM mysql:8.0

EXPOSE 3306

ENV LANG ja_JP.UTF-8

cmd

アプリのGoファイル等を置いています。
ベースは以前作成したGin等をしようしたCRUDアプリ(【#46 エンジニア転職学習】GinTutorialのWebApp改造 HTMLに連動させる)になります。
内容自体は変わっていないので、コンテナ化に際して変更した点のみ紹介します。

route.go

最後のrouter.Run(:8080)は以前までなら(localhost:8080)でしたが、
コンテナ化に伴いlocalhost部分は消します。
docker-comopose.ymlでポートフォワーディングしているので、ブラウザでlocalhost:8080を実行すればコンテナからレスポンスが返ってきます。

cmd/app/controllers/route.go
package controllers

import (
	"net/http"
	"time"

	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/gorm"
)

func NewRoutes(dbConn *gorm.DB) {
	//dbConnをインターフェースとして持つalbumhandlerを定義
	albumHandler := AlbumHandler{
		Db: dbConn,
	}
	//gin.Engineインターフェースを作成
	router := gin.Default()

	router.Use(cors.New(cors.Config{
		AllowOrigins:     []string{"*"},
		AllowMethods:     []string{"PUT", "PATCH", "DELETE", "POST", "GET"},
		AllowHeaders:     []string{"Origin"},
		ExposeHeaders:    []string{"Content-Length"},
		AllowCredentials: true,
		AllowOriginFunc: func(origin string) bool {
			return origin == "*"
		},
		MaxAge: 12 * time.Hour,
	}))

	//pattern一致するhtmlファイルをロードして、rendererに渡す
	router.LoadHTMLGlob("app/views/*.html")

	router.GET("/albums", func(c *gin.Context) {
		albums := albumHandler.GetAlbums()
		c.HTML(http.StatusOK, "index.html", gin.H{"albums": albums})
	})
	router.GET("/albums/:id", albumHandler.GetAlbumByID)
	router.POST("/albums/new", albumHandler.InsertAlbums)
	router.POST("/albums/update/:id", albumHandler.UpdateAlbumByID)
	router.POST("/albums/delete/:id", albumHandler.DeleteAlbumByID)
	router.POST("/albums/search", albumHandler.GetAlbumsByKeyword)

	router.Run(":8080")
}

base.go

dsn変数部分を2点変更。

  • MySQLの.envファイルで設定したUSER,PASSWORD,DATABASEを使用
  • @のあとにtcp(db:3306)の接続方法追加
cmd/app/models/base.go
package models

import (
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
)

//Initialized DB and retrun gormDBInterface
func DbInit() *gorm.DB {
	var dsn = "dev_user:password@tcp(db:3306)/test1?charset=utf8mb4&parseTime=True&loc=Local"
	db, err := gorm.Open("mysql", dsn)
	if err != nil {
		panic("failed connecting DB")
	}
	db.LogMode(true)
	db.AutoMigrate(&Album{})
	return db
}

main.go

他の個人packageを使用しているファイルも同様ですが、
importをローカルファイル指定からGitHubリポジトリ指定に変更

cmd/main.go
package main

import (
	"github.com/XXXXXXXXXXX.git/cmd/app/controllers"
	"github.com/XXXXXXXXXXX.git/cmd/app/models"
)

func main() {
	dbConn := models.DbInit()
	controllers.NewRoutes(dbConn)
}

実際に起動してみる

手順7まで行き、F1を押しRemote-Containers:Reopen in Containerを実行。
VSCodeが開きなおしされ、下図のようにコンテナverに切り替わっていれば成功です。(初回は時間かかります)

↓プロジェクトのサイドバー
image.png
↓VSCode左下の表示
image.png

試しにブラウザからリクエスト送信すると、、
↓のように無事レスポンスが返ってきました!!
image.png

おわりに

今回はRemote-Containerの環境構築を行いました。
Dockerをはじめ技術面的な学習はもちろん、logを見てエラー原因の仮説立てすることや公式ドキュメントを読むことの大切さを学習できたと思います。
個人的な都合で1か月振りの投稿となりましたが、またこれから再開していきたいと思います。
アドバイスや質問等ございましたらお願い致します。

参考

4
3
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
4
3