Help us understand the problem. What is going on with this article?

(初心者用)Docker+goを使って簡易アプリケーションを作成する

この記事で学べること

  • Dockerのイメージ、コンテナでよく使うコマンドの使い方がわかる。
  • 自身で作成したコンテナを起動できる。
  • Goに触れられる

この記事の最後ではDockerfileを元に作成したイメージからコンテナを作成、起動出来るようになります!
最後まで読んで頂けたら嬉しいです。

用意するファイル

main.go
package main

import (
  "fmt"
  "log"
  "net/http"
)

func main(){
  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request){
    log.Println("received request")
    fmt.Fprintf(w, "Hello Dcoker!!")
  })

  log.Println("start server")
  server := &http.Server{Addr: ":8080"}
  if err := server.ListenAndServe(); err !=nil {

     log.Println(err)
    }
  }

Dockerfile
FROM golang:1.9

RUN mkdir /echo
COPY main.go /echo
CMD ["go", "run", "/echo/main.go"]

※goに関しての解説はしません。
 この記はDocker初心者を対象にしているためDockerfile内のコードについて解説します。

FROM

FROMは作成するDockerイメージのベースとなるイメージを指定します。
Dockerfileでイメージをビルドする際、FROMでは指定されたイメージをダウンロードしてから実行されます。
FROMで取得するイメージはDocker hubとゆうレジストリに公開されているものです。

RUN

RUNはDockerイメージビルド時に、Dokcerコンテナ内で実行するコマンドを定義します。
RUNの引数にはDockerコンテナ内で実行するコマンドをそのまま指定します。
今回はmain.goを配置する為の/echoディレクトリをmkdirで作成しています。

COPY

COPYはDockerを動作させているホストマシン上のファイルやディレクトリをDockerコンテナ内にコピーする為のインストラクションです。ここではホストマシン上で作成したmain.goをRUNで作成した/echoディレクトリコピーしています。

CMD

CMDはDockerをコンテナとして実行する際に、コンテナ内で実行するプロセスを指定します。
イメージをビルドする為のRUNに対して、CMDはコンテナ起動時に一度実行されます。
RUNでアプリケーションの更新や配置、CMDでアプリケーションそのものを動作させると考えてください。
CMD ["go", "run", "/echo/main.go"]はシェルで書くと以下と同じ動作をします。

shell
$ go run /echo/main.go

イメージをビルドする

以下のコマンド形式でイメージをビルドします。

shell
$ docker image build -t イメージ名[:タグ名] Dockerfile配置ディレクトリのパス

今回の例ではexaple/echoとゆうイメージ名にします。

以下のように表示されたらビルド成功です

shell
$docker image build -t example/echo:latest .
Sending build context to Docker daemon  10.24kB
Step 1/4 : FROM golang:1.9
 ---> ef89ef5c42a9
Step 2/4 : RUN mkdir /echo
 ---> Using cache
 ---> 4eec5940f0fc
Step 3/4 : COPY main.go /echo
 ---> 0005c6f610cf
Step 4/4 : CMD ["go", "run", "/echo/main.go"]
 ---> Running in 1f1204c5d28e
Removing intermediate container 1f1204c5d28e
 ---> d59acfee20ca
Successfully built d59acfee20ca
Successfully tagged example/echo:latest

※-tオプションはイメージ名、タグ名を指定する為のコマンドです。

作成されたイメージは以下のコマンドで確認できます。

shell
$docker image ls
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
example/echo         latest              d59acfee20ca        4 minutes ago       750MB

コンテナを起動する

docker container run コマンドを利用してコンテナを起動します。

shell
$ docker container run -d example/echo:latest

今回は-dオプションをつけてコンテナをバックグラウンドで起動しています。

以下のようにハッシュ値のような文字列が出力されたらコンテナ起動成功です。

shell
$ docker container run -d example/echo:latest
6ed22edebc39a3e74d7a5c318129aa2fce5551fb1003617acc436092f80aa917

作成されたコンテナは以下のコマンドで確認できます。

shell
$ docker container ls
CONTAINER ID        IMAGE                 COMMAND                  CREATED             STATUS              PORTS               NAMES
6ed22edebc39        example/echo:latest   "go run /echo/main.go"   8 minutes ago       Up 8 minutes                            hopeful_thompson

コンテナ起動時に出力されたハッシュ値のような文字列の最初の12文字がコンテナIDとして利用されます。

さてこの起動したコンテナ上のアプリケーションが正しく実行されているかをどのように確認すれば良いのでしょうか?
これを説明する為に、今回はポートフォワーディング機能について簡単に説明いたします。

ポートフォワーディング機能

今回GO言語で書いたコードでは、このアプリケーションは8080ポートで公開されています。
ローカル環境で8080ポートに向けてcurlコマンドを使ってGETリクエストを送信してみましょう

shell
$curl http://localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused

Connection refusedが表示されました。この挙動を見る限り、アプリケーションのポートはローカル環境の8080ポートでは公開されていないようです。ここにDockerならではの特徴があります。
Dockerコンテナは仮想環境ですが、外から1つの独立したマシンのように扱える特徴があります。
echoアプリケーションは8080ポートを公開していますが、このポートはコンテナポートと呼ばれるコンテナ内に限定されたポートです。もしcurlをコンテナの中で実行するのであれば正しくレスポンスを得ることができますが、コンテナの外(今回はローカル環境)からコンテナポートを直接利用できない為Connection refusedとなるのです。
このようなHTTPリクエストを受けるアプリケーションの場合、コンテナの外から来たリクエストをコンテナ内で実行しているアプリケーションにまで到達させる必要があります。
そこで登場するのがDockerのポートフォワーディングです。ポートフォワーディングではホストマシンのポートをコンテナポートに紐づけ、コンテナ外から来た通信をコンテナポートに転送することができます。
この機能によって、コンテナポートをコンテナ外からでも利用できるようになります。
ポートフォワーディング機能を使う前に、先程実行したコンテナを以下のコマンドで停止しておきます。

shell
 $ docker container stop $(docker container ls --filter "ancestor=example/echo" -q)

ポートフォワーディングはdocker container runに-pオプションで指定できます。
-pオプションは{ホスト側ポート}:{コンテナポート}の書式で記述します。
ホスト側も8080にするとイメージしづらくなってしまうので、ホスト側の9000ポートをコンテナ側の8080ポートにポートフォワーディングします。

shell
$ docker container run -d -p 9000:8080 example/echo:latest
38702a897282618660d1d96e2301c6f5be3e4615e8babd8a2b171f42b05ed884

コンテナが実行されたらホスト側ポート、すなわちlocalhostの9000ポートにcurlでGETリクエストを送信してみましょう。

shell
 $curl http://localhost:9000
Hello Dcoker!!

上記のように「Hello Dcoker!!」と出力されたら、しっかりポートフォワーディングによってホスト側の9000ポートと関連づけられているコンテナ内の8080ポートにGETリクエストを送り、レスポンスを得ることができたとゆうことになります。

http://localhost:9000 をブラウザーの検索窓で検索すると同様に「Hello Dcoker!!」と表示されると思います。

参考図書 Docker/kubernetes実践コンテナ開発入門
https://www.amazon.co.jp/Docker-Kubernetes-%E5%AE%9F%E8%B7%B5%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80-%E5%B1%B1%E7%94%B0-%E6%98%8E%E6%86%B2/dp/4297100339/ref=sr_1_1?adgrpid=50113670541&gclid=CjwKCAjw_MnmBRAoEiwAPRRWW_N3mI6rJVGGOeZE3rKJ57-FlB7ixiHwCJz8MJmrYGJ9cm1RT_gS0BoCn70QAvD_BwE&hvadid=338539067959&hvdev=c&hvlocphy=1009247&hvnetw=g&hvpos=1t1&hvqmt=b&hvrand=10835942176646325467&hvtargid=kwd-496136850930&hydadcr=27263_11561109&jp-ad-ap=0&keywords=%E5%AE%9F%E8%B7%B5%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80&qid=1557374861&s=gateway&sr=8-1

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away