20200604のdockerに関する記事は11件です。

mutagenのsyncとdocker-composeを連携させて使う

はじめに

最近Docker for MacのMutagen-based cachingを使うという記事が話題になっていたが、mutagen単体でDockerコンテナと連携するやり方が英語の記事を含めてもなかなか見つけられなかったのでまとめておく。

なお、本記事の方法ではMutagen-based cachengは利用しないため、Dockerはstable版のままでよい(上記記事の設定は試していないため動作の違い等は不明)。

またここで扱っているのはmutagenのsyncforwardについてはこちらの記事に載っている。

環境

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.14.6
BuildVersion:   18G5033
$ docker -v
Docker version 19.03.8, build afacb8b
$ docker-compose -v
docker-compose version 1.25.5, build 8a1c60f6

準備

1. インストール

公式に書いてある通り。投稿時のバージョンは0.11.4。

$ brew install mutagen-io/mutagen/mutagen
$ mutagen version
0.11.4

2. デーモン起動

$ mutagen daemon start

3. コンテナ起動

テスト用に用意したdocker-compose.ymlは以下。

docker-compose.yml
version: '3.7'
services:
  web:
    image: php:7.4-apache
    container_name: web
    ports:
      - 80:80
  db:
    image: mysql:8.0
    container_name: db
    environment:
      MYSQL_ROOT_PASSWORD: root
$ docker-compose up -d

手動で使う

1. セッションの作成

$ mutagen sync create --name=mutagen-test --default-file-mode-beta=644 . docker://web/var/www/html

こちらのコマンドでmutagen-testという名前でカレントディレクトリ(alpha)の内容をwebコンテナの/var/www/html(beta)と同期するセッションを作成できる。なぜsourcedestではなくalphabetaなのかと疑問だったが、mutagenは双方向に同期できるためのようだ
--default-file-mode-beta=644はbeta(コンテナ)側のファイルパーミッションを644にする設定。mutagenのデフォルトではパーミッションがディレクトリ700、ファイル600になる(参考)ので、ここで指定して(もしくは--default-owner-beta=www-dataとして)おかないと、今回のようにapacheから参照したい場合にパーミッションエラーになる。

セッションを作成した時点で同期が始まる。これまで使っていてファイルが消えたりしたことはないが、万が一に備えてバージョン管理されている状態で行った方がよい。

2. 同期の状況の確認

  • 実行時点
$ mutagen sync list
--------------------------------------------------------------------------------
Name: mutage-test
Identifier: sync_pPfc22rlD8RcEDldZ9CbMTJzTnRszZHfokhkZtopw7a
Alpha:
        URL: /tmp/mutagen
        Connection state: Connected
Beta:
        URL: docker://web/var/www/html
                DOCKER_HOST=
                DOCKER_TLS_VERIFY=
                DOCKER_CERT_PATH=
        Connection state: Connected
Status: Watching for changes
  • リアルタイム
$ mutagen sync monitor
Name: mutagen-test
Identifier: sync_pPfc22rlD8RcEDldZ9CbMTJzTnRszZHfokhkZtopw7a
Labels: None
Alpha: /tmp/mutagen
Beta: docker://web/var/www/html
        DOCKER_HOST=
        DOCKER_TLS_VERIFY=
        DOCKER_CERT_PATH=
Status: Watching for changes                                                    

セッション作成直後の最初の同期中にmonitorを見ているとStatus: Staging files on beta: 41% (9191/22567)などといった状態になり、これがStatus: Watching for changesになったら同期完了。
ファイルの量やマシンスペックにもよると思われるが、上記の22567ファイルの規模(今回のものとは別のプロジェクト)で20秒程度だった。monitorは同期が完了しても勝手には終わらないので^Cで抜ける。

3. 動作確認

  • 新規作成
$ ls
docker-compose.yml
$ echo '<?php echo "It works!";' > index.php
$ docker-compose exec web ls -la
total 20
drwxrwxrwx 1 www-data www-data 4096 Jun  4 05:29 .
drwxr-xr-x 1 root     root     4096 May 15 12:41 ..
-rw-r--r-- 1 root     root      206 Jun  4 05:28 docker-compose.yml
-rw-r--r-- 1 root     root       24 Jun  4 05:29 index.php
$ curl localhost
It works!
  • 編集
$ echo '<?php echo "It works fine!";' > index.php
$ curl localhost
It works fine!

ちゃんと同期されているようだ。

今回はサイズも小さいので一瞬だが、実プロジェクトでは数秒のラグはある(コンテナでファイル自動生成などをしてすぐにホストでgit statusで見るとまだ反映されていなかったりする)。

4. セッションの終了

$ mutagen sync terminate mutagen-test
$ mutagen sync list
Error: no matching sessions exist

5. 手動の問題点

最初はこれでしばらく使っていたが、例えばセッション実行中にコンテナを落とすと、Status: [Errored] Connecting to betaとなり同期エラーになったり、そこからdocker-compose up -dで再度コンテナを起動しても、Status: Halted due to one-sided root emptyingとなって同期はできずセッションを作り直さなければならないという問題があった。

先述のオーナーやパーミッションは設定ファイルに書くこともできるが、コンテナとの連携も合わせて一括管理したいなあと思っていたところ、次のmutagen projectを使う方法を見つけた。

mutagen projectを使う

mutagenにはprojectという機能があり、セッションの作成や終了といったライフサイクルと任意のコマンドを組み合わせて設定ファイルで管理し実行できる。

公式のexamplesがわかりやすい。

今回は簡略化した以下のmutagen.ymlを使用する。

mutagen.yml
beforeCreate:
  - docker-compose up -d

afterTerminate:
  - docker-compose down

sync:
  defaults:
    ignore:
      vcs: true
  mutage-test:
    alpha: "."
    beta: "docker://web/var/www/html"
    mode: "two-way-resolved"
    configurationAlpha:
      permissions:
        defaultFileMode: 644
        defaultDirectoryMode: 755
    configurationBeta:
      permissions:
        defaultOwner: www-data
        defaultGroup: www-data
        defaultFileMode: 644
        defaultDirectoryMode: 755

beforeCreateでセッション作成前にコンテナを起動、afterTerminateでセッション終了後にコンテナも終了する。

また、syncの項目にセッションの設定を書いておける。
ここではalphaのパーミッションも設定しているが、これはフレームワークなどのファイル自動生成やcomposerやnpmでのライブラリのインストールをコンテナ上で行った場合に700600でホストに同期されるのを防ぐためである。

公式ではafterCreateで同期完了までのwaitを入れている。同期が速いといっても多少のラグはあるので、実プロジェクトで採用する際は真似しておくと、同期完了前にアクセスしてファイルが見つからないなどのエラーを避けられる。

docker-compose.ymlと同階層に置いてバージョン管理しておく。

1. セッションの開始

$ mutagen project start
Started Mutagen daemon in background (terminate with "mutagen daemon stop")
> docker-compose up -d
Creating network "mutagen_default" with the default driver
Creating web ... done
Creating db  ... done
Created session sync_3bwd2dlhwaFrNNeUNRe4WYegNxOdPjEj3swQ8Xx1Hz6

セッション開始前にコンテナが起動しているのが確認できる。デーモンが起動していなかった場合はそっちも起動してくれて便利。

セッション実行中はmutagen.yml.lockというファイルができるので.gitignoreに追加しておくとよい。

2. セッションの終了

$ mutagen project terminate
> docker-compose down
Stopping web ... done
Stopping db  ... done
Removing web ... done
Removing db  ... done
Removing network mutagen_default

3. コマンドの実行

また、composerやnpmなどのscriptsと同様に任意のコマンドを定義できるようになっている。以下を設定ファイルに追加する。

mutagen.yml
commands:
  repl: docker-compose exec web php -a
  db: docker-compose exec db mysql -uroot -p
$ mutagen project start # セッションを終了していない場合は不要
$ mutagen project run repl
Interactive shell

php > echo 'hello';
hello
php > ^D
$ mutagen project run db
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.20 MySQL Community Server - GPL

Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql>

おわりに

mutagen projectを利用することで、mutagenとdocker-composeを連携できて便利だった。
現状そこまで不満もないのでDockerのMutagen-based cachingがstableになるまではこの方法を使おうと思う。

参考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kubernetes 構築手順 Proxy編 ※大体コピペでOK

Kubernetes 環境構築 Proxy編

Kubernetes(以下、K8s)をイントラネット内で構築する手順です。
K8s環境構築の流れを理解しつつ、作業はなるべくコピペで可能なように記載しています。

1. モチベーション

  • K8sを会社内の開発イントラネット内環境で構築したい。 (プロキシサーバ経由で開発イントラネット内環境からインターネットにアクセスできる場合)
  • K8sを使ってみたいが、構築のハードルが高い。

2. 手順

本手順で以下のK8sクラスタ構成を構築します。

  • 準備するもの
    • OS : Centos7.4(minimal)
    • PC 3台 ※仮想でも可
    • CPU 2Core 以上
    • Memroy 2GB 以上
  • 出来上がるもの
    • K8s クラスタ構成 ※Masterはシングル
    • Master
    • Worker
    • Worker

2.1. Docker環境の構築(全ノード対象)

K8sは複数ノードにまたがりコンテナの駆動を管理するツールで、実際のコンテナの実行はDockerを経由しています。
また、K8sの機能もコンテナとして動作しているため、クラスタの全ノードでDockerが動作することが必要です。

すでにDockerが使えている場合は先に進んでもらって結構です。
ただし、※公式推奨※後述の作業に必要 の作業は確認しておいてください。

2.1.1. CentOS7のインストール(CLI)と基本設定

  1. CentOSをインストールします。
  2. 開発イントラネット内通信が可能となる設定をし、ローカルネットワークに接続可能な状態にしておきます。 IP,サブネットマスクの固定/MACアドレス登録など
  3. swapを無効化します。 ※公式推奨
  4. SELINUX無効化してOS再起動します。
  5. yumのプロキシ設定 vi /etc/yum.conf で以下のように編集→保存
#swapを無効化
swapoff -a
sed -i -e "/swap/d" /etc/fstab
cat /etc/fstab
#SELINUX無効化してOS再起動
sed -i -e "s/^SELINUX=enforcing$/SELINUX=disabled/g" /etc/selinux/config
reboot
#yumのプロキシ設定
cat <<EOF >> /etc/yum.conf
proxy=http://{プロキシIP}:{プロキシポート}
EOF

2.1.2. Dockerのインストールと設定

  1. Dockerインストールします。 最後にバージョンが表示されれば成功です。
  2. Dockerのネットワーク設定1 ※後述の作業に必要 とhello-worldコンテナの実行をします。
  3. うまく行かない場合は、プロキシ設定を確認します。
#Dockerインストール
yum install -y yum-utils device-mapper-persistent-data lvm2 #Docker ceに必要なパッケージ
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo #Docker ceのリポジトリ追加
yum install -y --setopt=obsoletes=0 docker-ce-18.06.1.ce #Docker ceインストール
docker version #Dockerバージョン確認

↓Dockerバージョン確認結果

Client:
 Version:           18.06.1-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        e68fc7a
 Built:             Tue Aug 21 17:23:03 2018
 OS/Arch:           linux/amd64
 Experimental:      false
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
[root@K8sWorker1 ~]# docker version #Dockerバージョン確認
Client:
 Version:           18.06.1-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        e68fc7a
 Built:             Tue Aug 21 17:23:03 2018
 OS/Arch:           linux/amd64
 Experimental:      false
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
#Dockerのネットワーク設定
systemctl disable firewalld #Firewall無効化
systemctl stop firewalld #Firewall停止
mkdir -p /etc/systemd/system/docker.service.d # dockerのプロキシ設定
cat <<EOF > /etc/systemd/system/docker.service.d/http-proxy.conf #Proxy設定
[Service]
Environment="HTTP_PROXY=http://{プロキシIP}:{プロキシポート}" "HTTPS_PROXY=http://{プロキシIP}:{プロキシポート}" "NO_PROXY=localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,{master nodeのIPアドレス},{worker node1のIPアドレス},{worker node2のIPアドレス}"
EOF
systemctl daemon-reload #プロキシ反映
systemctl enable docker && systemctl start docker #Docker再起動
docker run --rm hello-world #hello-worldコンテナ起動

↓ hello-worldコンテナ起動結果


Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
# プロキシ設定を確認
docker info | grep Proxy

2.2. kubeadmインストール~kubelet有効化 (全ノード対象)

2.2.1. kubeadmインストール

  1. kubeadmのためのプロキシを設定してOS再起動します。1
  2. k8sのためのyumリポジトリを追加します。
  3. kubeadmと関連パッケージのインストールします。 2
#kubeadmのためのプロキシを設定してOS再起動
cat <<EOF >> ~/.bash_profile
PROXY_PORT={プロキシポート}
PROXY_HOST={プロキシIP}
http_proxy=http://\$PROXY_HOST:\$PROXY_PORT
HTTP_PROXY=\$http_proxy
https_proxy=\$http_proxy
HTTPS_PROXY=\$http_proxy
no_proxy="localhost,127.0.0.1,10.96.0.0/12,10.244.0.0/16,{master nodeのIPアドレス},{worker node1のIPアドレス},{worker node2のIPアドレス}"
EOF
reboot
#k8sのためのyumリポジトリを追加
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
sslverify=0
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
#kubeadmと関連パッケージのインストール
yum install -y kubelet-1.16.0-0.x86_64 kubeadm-1.16.0-0.x86_64 kubectl-1.16.0-0.x86_64 --disableexcludes=kubernetes

2.2.2. kubelet有効化

  1. リソース管理ドライバをcgroupfsに統一します。 ※Docker daemonの設定(デフォルトではsystemd)をkubelet側(cgroupfs)に合わせる。
  2. kubelet有効化と確認をします。※起動してなくてOK
  3. ブリッジの設定をします。
#リソース管理ドライバをcgroupfsに統一
cat << EOF > /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=cgroupfs"]
}
EOF
systemctl restart docker
docker info | grep -i cgroup
# kubelet有効化と確認
systemctl enable kubelet && systemctl start kubelet
systemctl status kubelet

↓ kubelet確認

● kubelet.service - kubelet: The Kubernetes Node Agent
   Loaded: loaded (/usr/lib/systemd/system/kubelet.service; enabled; vendor preset: disabled)
  Drop-In: /usr/lib/systemd/system/kubelet.service.d
           mq10-kubeadm.conf
   Active: activating (auto-restart) (Result: exit-code) since Tue 2020-06-02 17:00:13 JST; 932ms ago
     Docs: https://kubernetes.io/docs/
  Process: 16490 ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS (code=exited, status=255)
 Main PID: 16490 (code=exited, status=255)

Jun 02 17:00:13 K8sWorker2.localdomain systemd[1]: Unit kubelet.service enter...
Jun 02 17:00:13 K8sWorker2.localdomain systemd[1]: kubelet.service failed.
Hint: Some lines were ellipsized, use -l to show in full.
#ブリッジの設定
cat <<EOF >  /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

2.3. K8sクラスタ構築

2.3.1. Masterの構築 (Masterノード対象)

この作業はMasterノードでのみ行います。

  1. /tmpに以下のファイルを配置しておきます。 > https://github.com/coreos/flannel/blob/master/Documentation/kube-flannel.yml > kube-flannel-ds-amd64がPullできない場合があります。 > kube-flannel.ymlを編集し、今回はイメージのロード先を easzlabに変更しました。 ※2020/06/02 時点
  2. kubeadmを実行します。 tokenが出力されるので控えておきます。
  3. 設定の編集をします。 最後にcreatedが10個表示されればOK
  4. Podを確認します。 Podが8個あり、すべてRunningになっていればOK
# イメージのロード先変更 ※他箇所あり
initContainers:
  - name: install-cni
    image: easzlab/flannel:v0.12.0-amd64
    command:
#kubeadmを実行
kubeadm init --kubernetes-version 1.16.0  --apiserver-advertise-address={master nodeのIPアドレス} --pod-network-cidr=10.244.0.0/16 --token-ttl 0 --image-repository gotok8s

↓kubeadmを実行結果

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join {master nodeのip}:6443 --token oe4by1.8wxrpf52w2y6xulw \
    --discovery-token-ca-cert-hash sha256:a184db6aaaa3529ea9cb4f46038945ceae4878e16f21213aac1a605583071d19
# 設定の編集
mkdir -p $HOME/.kube #これをやらないと動作しない場合あり。
sudo cp -f /etc/kubernetes/admin.conf $HOME/.kube/config #これをやらないと動作しない場合あり。
sudo chown $(id -u):$(id -g) $HOME/.kube/config #これをやらないと動作しない場合あり。

kubectl apply -f /tmp/kube-flannel.yml #kube-flannel設定反映
#Podを確認
kubectl get pods --all-namespaces

↓Pod確認結果

NAMESPACE     NAME                                             READY   STATUS    RESTARTS   AGE
kube-system   coredns-8655596978-ff4rv                         1/1     Running   0          69s
kube-system   coredns-8655596978-tprdp                         1/1     Running   0          69s
kube-system   etcd-k8smaster1.localdomain                      1/1     Running   0          27s
kube-system   kube-apiserver-k8smaster1.localdomain            1/1     Running   0          19s
kube-system   kube-controller-manager-k8smaster1.localdomain   1/1     Running   0          12s
kube-system   kube-flannel-ds-amd64-7vjkq                      1/1     Running   0          37s
kube-system   kube-proxy-4562b                                 1/1     Running   0          69s
kube-system   kube-scheduler-k8smaster1.localdomain            1/1     Running   0          9s

2.3.2. Workerの構築 (Workerノード対象)

この作業はWorkderノードでのみ行います。

  1. Masterから設定のコピーをします。
  2. クラスターに参加します。
#Masterから設定のコピー
mkdir -p /etc/cni/net.d/
scp {master nodeのIPアドレス}:/etc/cni/net.d/10-flannel.conflist /etc/cni/net.d/10-flannel.conflist
#クラスターに参加
kubeadm join {master nodeのIPアドレス}:6443 --token {上述の作業で控えた値} \
    --discovery-token-ca-cert-hash sha256:{上述の作業で控えた値}

2.3.3. ノードの確認

  • それぞれのノードがクラスタに参加していることを確認します。
#ノードの確認
kubectl get nodes

↓ ノードの確認結果

NAME                     STATUS   ROLES    AGE     VERSION
k8smaster1.localdomain   Ready    master   17h     v1.16.0
k8sworker1.localdomain   Ready    <none>   2m26s   v1.16.0
k8sworker2.localdomain   Ready    <none>   82s     v1.16.0

3. 動作確認

3.1. ダッシュボードの表示

  1. 外部からアクセスできるようにrecommended.yamlを編集し、Masterノードの/tmpに配置しておきます。 https://github.com/kubernetes/dashboard
  2. ダッシュボードのPodを作成します。
  3. admin-userを作成し、トークンを作ります。
  4. 以下URLにアクセスし、トークンを入力するとダッシュボードが開きます。 https://{master nodeのIP}:30843
---
# 外部からアクセスできるようにrecommended.yaml編集
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
  type: NodePort #追加
  ports:
    - port: 443
      targetPort: 8443
      nodePort: 30843 #追加
  selector:
    k8s-app: kubernetes-dashboard
---
#ダッシュボードのPodを作成
kubectl apply -f /tmp/recommended.yaml
kubectl get pod --namespace=kubernetes-dashboard
# admin-userを作る
cat <<EOF > service-account.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kube-system
EOF
kubectl apply -f service-account.yaml #ユーザをデプロイ
kubectl -n kube-system get secret | grep admin #権限を表示
#トークン取得
kubectl -n kube-system describe secret admin-user-token-n72ds

3.2. コンテナ起動

コンテナを起動してみます。

3.2.1. Podのデプロイ
まずは、K8sではマニフェストファイル(YAML)を作成し、それをkubectl applyすることでコンテナをPodという単位で起動(デプロイ)します。
ここで気がつくのが、構築の時点でkubectl applyを使っていたということです。
つまり、K8sクラスターはポッドとサービスにより提供されているということです。

この段階では、クラスタノードからはコンテナにアクセスできるが外部からはアクセスできません。

3.2.2. サービスのデプロイ
外部からアクセスを可能にするために、サービスを追加しノードのポートとPodのポートを接続します。

3.2.1. Podのデプロイ

  1. Podをデプロイします。
  2. PodのIPを確認します。
  3. 確認したIPに対してアクセスしてみます。Welcome to nginx!のHTMLが表示されます。
  4. ブラウザでhttp://{PodのIP}:80にアクセスしてみます。不能となります。
# Podをデプロイ
cd /tmp
cat <<EOF > deployments.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
EOF

kubectl apply -f deployments.yml
#PodのIPを確認
kubectl get pod -o wide

3.2.2. サービスのデプロイ

  1. サービスをデプロイします。
  2. http://10.10.33.72:30036/ にアクセスします。Welcome to nginx!のページが表示されます。
# サービスをデプロイ
cat <<EOF > service.yml
kind: Service
apiVersion: v1
metadata:
  name: my-service
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: 80
    nodePort: 30036
    protocol: TCP
  selector: # 1Pod複数コンテナの場合は、ココでパケットを振り分ける。
    app: nginx
EOF
kubectl apply -f service.yml

4. まとめ

上述の手順で以下が達成できました。

  • イントラネット環境内でK8sクラスターの構築
  • 任意コンテナデプロイ

4.1. おさらい

  • K8sはコンテナエンジンの上位レイヤのサービスであり、コンテナエンジンが駆動していることは前提条件(今回はDocker)
  • K8sを駆動させるのに必要なものは以下
    • kubeadm...K8sクラスターのインストールソフト 公式
    • kubelet...クラスター内の各ノードで実行されるエージェント 公式
    • flannel...K8sクラスタの仮想ネットワークを提供する。
  • 任意のコンテナはPodとサービスを使ってデプロイする。

4.2. 次のステップ

  • ヘルスチェック・冗長性設定
  • Master 兼 Worker設定
  • イントラネット内コンテナレジストリ・レポジトリとの連携

  1. NO_PROXYにはすべてのk8s master/worker nodeのIPアドレスとk8sの各Serviceに割り振られる仮想IP(10.96.0.0/12)とpodネットワークプラグインが展開するOverlayネットワークのIPアドレス範囲(10.244.0.0/16)をCIDRで記載しておく。 

  2. 1.16.0に固定 k8sとバーション合わせないといけないので注意 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【dcoker, gitlab-ci】 ERROR: unsatisfiable constraints の対応

問題

gitlab-ci上でこれまで正常に動いていた処理にて、ERROR: unsatisfiable constraints が発生
alpineイメージ絡みの問題らしい。

エラー内容

$ apk add --no-cache curl jq python3 py3-pip
 fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/main/x86_64/APKINDEX.tar.gz
 fetch http://dl-cdn.alpinelinux.org/alpine/v3.12/community/x86_64/APKINDEX.tar.gz
 ERROR: unsatisfiable constraints:
   python (missing):
     required by: world[python]

対応方法

  • pythonをバージョン2から3ににする
$ apk add --no-cache curl jq python py-pip

参考

github
https://github.com/docker-library/docker/commit/eb305f7
alpineリリースノート
https://wiki.alpinelinux.org/wiki/Release_Notes_for_Alpine_3.12.0#python2_no_longer_provides_python_and_python-devel

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Docker初心者がDockerfileを使ってalpineのDockerイメージをビルドする

Dockerfile

qiita Dockerfile
FROM alpine:3.12.0

LABEL version="1.0"
LABEL description="Dockerfile test at alpine"

前提

docker for mac を起動しておくこと

ディレクトリ

DockerFileTest
>Dockerfile

コマンド

mac:terminal

qiita terminal
cd DockerfileTest
docker build .

※build . でDockerfileTestディレクトリ内からDockerfileを探し、その中で定義されているイメージをビルドします。
コンテナ生成は行われません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel nginxでroot以外のページがNot Foundになってしまうとき、これをconfに書く。

dockerでngixを追加してやってみたときの話。
rootのいつものLaravelの画面は出るが、追加したURLでNot Foundになってしまう。

404 Not Found
nginx/1.19.0

解決策

try_files $uri $uri/ /index.php?$query_string;を書く

nginx/default.conf
server {
  listen 80;
    index index.php index.html;
    root /var/www/public;

  location / {
    root /var/www/public;
    index  index.html index.php;
    try_files $uri $uri/ /index.php?$query_string; # <==
  }

nginxのconfにこれがないと/以外のときNot foundになる。

dockerでnginxでやろうと思ってやるとはまる。
"Docker Laravel"で検索したページでもここを強調して書かれているところはないと思うので書きました。

公式に書いてました。

参考
Installation - Laravel - The PHP Framework For Web Artisans

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

dockerfileで親ディレクトリを参照したい!

はじめに

会社の研修で詰まったところをメモ程度に書いていく

やりたかったこと

以下のようなディレクトリ構造の時、dokerfileから親ディレクトリのindex.htmlをCOPYしたかった

ディレクトリ構造
content/
 │
 ├ .infrastructure/
 │   ∟ dockerfile
 │
 ├ files/
     ∟ index.html
dockerfile
FROM: nginx:latest
COPY: ../files/ /usr/share/nginx/html
WORKDIR /usr/share/nginx/html

これで.infrastructureディレクトリで docker buildを実行。

実行結果

.infrastructure $ docker build . -t docker-html
-- 実行結果 --
Sending build context to Docker daemon  3.072kB
Step 1/2 : FROM nginx:latest
 ---> 9beeba249f3e
Step 2/2 : COPY ../files/  /usr/share/nginx/html
COPY failed: Forbidden path outside the build context: ../files/ ()

このようにCOPY failed: Forbidden path outside the build context: ../files/ ()と表示され親ディレクトリを参照できない

解決方法

-f オプションを使って、contentディレクトリからdocker buildコマンドを実行する
以下の通り

# docker build -f [dockerfileの指定] -t [イメージ名] [イメージを作成する時に参照するディレクトリ]
$ docker build -f .infrastructure/dockerfile -t image_name .

注意点

最後の.忘れないように! 
今回の場合はカレントディレクトリからimageを作成するため.

まとめ

dockerfileから別のディレクトリのファイルを参照することはできない。
なので、親ディレクトリから-fオプションを用いてコマンドを実行すれば良い。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Redmine on Docker

はじめに

本記事は動画( https://youtu.be/-DrdYw8fHwc )で説明に使用している
スライドを記事化したものです。動画と合わせて御覧ください。
-DrdYw8fHwc

※記事化する際に補足説明を加筆しており、内容は動画のスライドと異なります。
※本記事の内容は2020年3月時点の内容となります。OSやミドルウェア、Dockerのバージョンが異なることで記載通りの内容で完了しない場合があります。


ミドルウェア構成

  1. OS : CentOS 7.8
  2. Webサービス : Apache 2.4.6
  3. アプリ言語 : Ruby 2.4
  4. DBサービス : MariaDB 10.4.3
  5. Redmine : Redmine 4.1

Dockerfileで下準備

# centos7のイメージを利用する
FROM centos:7
LABEL maintainer=Takemi

SHELL ["/bin/bash", "-o", "pipefail", "-c"]"

# 累積アップデートの実行
RUN yum -y upgrade

#takemiユーザが存在していない場合ユーザ追加する
RUN echo 'make user takemi'
RUN adduser -m takemi;echo "takemi:takemi123" | chpasswd;

#sshのインストール
RUN yum install -y openssh-server
RUN systemctl enable sshd

#Apacheのインストール
RUN yum install -y httpd
RUN systemctl enable httpd

#MariaDBのインストール
RUN curl -sS https://downloads.mariadb.com/MariaDB/mariadb_repo_setup | bash
RUN yum install -y MariaDB-server MariaDB-devel MariaDB-shared
RUN systemctl enable mariadb

#ruby インストール準備
RUN yum -y groupinstall "Development Tools"
RUN yum -y install openssl-devel readline-devel zlib-devel curl-devel libyaml-devel libffi-devel
RUN yum -y install httpd-devel
RUN yum -y install ImageMagick ImageMagick-devel ipa-pgothic-fonts
WORKDIR /root
RUN curl -O https://cache.ruby-lang.org/pub/ruby/2.4/ruby-2.4.1.tar.gz
RUN tar xf ruby-2.4.1.tar.gz
WORKDIR /root/ruby-2.4.1
RUN ./configure --disable-install-doc
RUN make
RUN make install
RUN gem install bundler --no-rdoc --no-ri
        > docker build -t redmine .

Dockerfileでやってること!

  1. OSのイメージ構築
  2. OSのアップデート
  3. ルートユーザパスワード発行
  4. 一般ユーザ作成、パスワード発行
  5. sshのインストール
  6. Apacheのインストール
  7. MariaDBのインストール
  8. Rubyのインストール

DBの設定

DBのrootユーザの追加

root > mysql
mysql > GRANT ALL PRIVILEGES ON *.* TO 'takemi'@'localhost' IDENTIFIED BY 'takemi123' WITH GRANT OPTION;
mysql > FLUSH PRIVILEGES;

※DBのユーザ名とOSのユーザ名を合わせておくと、OSのログインユーザがmysqlコマンドを実行する時にユーザ名指定を省略することができるようになります。


Redmineのインストール

root > cd /var/www
root > svn co https://svn.redmine.org/redmine/branches/4.1-stable redmine-4.1
user > cd redmin-4.1
user > cd config
user > cp configuration.yml.example configuration.yml
user > cp database.yml.example database.yml
user > vi database.yml
    username: takemi
    password: "takemi123"

root > cd /var/www
root > chown -R takemi:takemi redmine-4.1
root > vi /etc/group
    takemi:x:1000:apache

root > cd /var/www/redmine-4.1
root > gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'
root > bundle install --without development test --path vendor/bundle
root > bundle exec rake generate_secret_token
root > gem install passenger --no-rdoc --no-ri
root > passenger-install-apache2-module --auto
root > passenger-install-apache2-module --snippet
root > vi /etc/httpd/conf.d/redmine.conf
root > systemctl restart httpd

user > mysql -p
    create database redmine default character set utf8;
user > RAILS_ENV=production bundle exec rake db:migrate
user > RAILS_ENV=production REDMINE_LANG=ja bundle exec rake redmine:load_default_data


Redmineのセットアップ

ブラウザでアクセス!
詳しくは動画で!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerfile for Rails6のベストプラクティスを解説

本記事の趣旨

令和時代のRails運用
こちらのスライドに掲載されている以下のDockerfileが、キャッシュやマルチステージビルドを利用したベストプラクティスとして参考になりました。

Dockerfile
# syntax = docker/dockerfile:experimental

# Node.jsダウンロード用ビルドステージ
FROM ruby:2.6.5 AS nodejs

WORKDIR /tmp

# Node.jsのダウンロード
RUN curl -LO https://nodejs.org/dist/v12.18.0/node-v12.18.0-linux-x64.tar.xz
RUN tar xvf node-v12.18.0-linux-x64.tar.xz
RUN mv node-v12.18.0-linux-x64 node

FROM ruby:2.6.5

# nodejsをインストールしたイメージからnode.jsをコピーする
COPY --from=nodejs /tmp/node /opt/node
ENV PATH /opt/node/bin:$PATH

# アプリケーション起動用のユーザーを追加
RUN useradd -m -u 1000 rails
RUN mkdir /app && chown rails /app
USER rails

# yarnのインストール
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
ENV PATH /home/rails/.yarn/bin:/home/rails/.config/yarn/global/node_modules/.bin:$PATH

# ruby-2.7.0でnewした場合を考慮
RUN gem install bundler

WORKDIR /app

# Dockerのビルドステップキャッシュを利用するため
# 先にGemfileを転送し、bundle installする
COPY --chown=rails Gemfile Gemfile.lock package.json yarn.lock /app/

RUN bundle config set app_config .bundle
RUN bundle config set path .cache/bundle
# mount cacheを利用する
RUN --mount=type=cache,uid=1000,target=/app/.cache/bundle \
    bundle install && \
    mkdir -p vendor && \
    cp -ar .cache/bundle vendor/bundle
RUN bundle config set path vendor/bundle

RUN --mount=type=cache,uid=1000,target=/app/.cache/node_modules \
    bin/yarn install --modules-folder .cache/node_modules && \
    cp -ar .cache/node_modules node_modules

COPY --chown=rails . /app

RUN --mount=type=cache,uid=1000,target=/app/tmp/cache bin/rails assets:precompile

# 実行時にコマンド指定が無い場合に実行されるコマンド
CMD ["bin/rails", "s", "-b", "0.0.0.0"]

ここで使用されているBuildkitなどの要素について、勉強した内容をまとめたいと思います。

これらの方法により、開発効率の向上を実感しましたので、RailsとDockerを学習中の方のご参考になればと思います。

(投稿者はDockerを勉強中で、実務は未経験ですので、気になる点がありましたらコメントでご指摘をお願いします。)

注記:上記のDockerfileは、元スライドの物から、Node.jsのバージョンだけ、20/6/6時点での最新verに変更しています。

参考記事

Dockerfileを改善するためのBest Practice 2019年版

Docker Buildにおけるリードタイム短縮のための3つの改善ポイント

開発環境

  • Mac OS X 10.15.4
  • Docker 19.03.8
  • Docker Desktop for Mac 2.3.0.3
  • Ruby 2.6.5 Rails 6.0.2

Dockerfileの解説

上記のDockerfileの要点を見ていきます。
1行目の# syntax =という部分は後述するBuildkitに関する記述です。

その次の

# Node.jsダウンロード用ビルドステージ
FROM ruby:2.6.5 AS nodejs

WORKDIR /tmp

# Node.jsのダウンロード
RUN curl -LO https://nodejs.org/dist/v12.18.0/node-v12.18.0-linux-x64.tar.xz
RUN tar xvf node-v12.18.0-linux-x64.tar.xz
RUN mv node-v12.18.0-linux-x64 node

ここでは、Railsに必要なNode.jsをインストールしています。

  • tmpに移動
  • node-v12.18.0-linux-x64.tar.xzをダウンロード、展開
  • node-v12.18.0-linux-x64をnodeにリネーム

の結果、/tmp/node(本体), /tmp/node-v12.18.0-linux-x64.tar.xz(不要)
が生成されます。

最終的なDockerイメージを軽量にするために、必要なnode本体だけを残す必要があります。Dockerはレイヤー構造で履歴が残っているため、ただ単にRUN rm node-v12.18.0-linux-x64.tar.xzとしても意味がないようです。

そこで、マルチステージビルドを利用しています。

マルチステージビルド

マルチステージビルドは、1つのDockerfileに複数のステージを分けて記述し、最後のステージの内容だけが最終イメージに含まれます。
例のDockerfileでは、FROM行が2箇所、つまり2つのステージがあります。

FROM ruby:2.6.5 AS nodejs
...
FROM ruby:2.6.5

1つ目のAS nodejsの記載で、ステージにnodejsと名付けています。これによって、2つ目のステージで、

# nodejsをインストールしたイメージからnode.jsをコピーする
COPY --from=nodejs /tmp/node /opt/node
ENV PATH /opt/node/bin:$PATH

上でインストールしたtmp/nodeだけをコピーする事ができます。

今回の場合は、これによって節約できるのは15MBほどですが、Goのようなコンパイル言語で、ビルド結果のファイルだけを次のステージに渡すと、かなりの軽量化ができるようです。

ちなみに、2つ目のステージのyarnインストールは、ファイルそのものではなく、install.shをダウンロードして実行しているだけで、不要なファイルが残らないため、このままで問題ないのだと思います。

# yarnのインストール
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
ENV PATH /home/rails/.yarn/bin:/home/rails/.config/yarn/global/node_modules/.bin:$PATH

ユーザーの追加

# アプリケーション起動用のユーザーを追加
RUN useradd -m -u 1000 rails
RUN mkdir /app && chown rails /app
USER rails

ここでは、コンテナ内にユーザーを追加しています。デフォルトのrootユーザーのままでは、ホストとファイルを共有する際に権限の問題が発生するようです。

ただ、Docker for Macの場合、その問題は起こらないので、この設定は省略しても良いかもしれません。
(非rootユーザーにすると、vimを使いたい時にapt-getができないなど、色々と困る場面もありましたので..)

bundle install

WORKDIR /app

# Dockerのビルドステップキャッシュを利用するため
# 先にGemfileを転送し、bundle installする
COPY --chown=rails Gemfile Gemfile.lock package.json yarn.lock /app/

作業ディレクトリに必要なパッケージ管理ファイルを置いています。その後、

RUN bundle config set app_config .bundle
RUN bundle config set path .cache/bundle

まずbundle configコマンドを使用して、installするpathを設定しています。

bundle config

例として、bundle config set path vendor/bundleと設定しておくと、bundle installの際に、bundle install --path vendor/bundleとパスを指定した事と同じになります。

installの際に、--pathを指定する方法は非推奨となったようなので、今後はbundle configを使いましょう。

Bundlerでビルドオプションを指定する

Cache Mount

bundle installで、ビルド効率のためにCache Mountを利用しています。

RUN bundle config set path .cache/bundle
# mount cacheを利用する
RUN --mount=type=cache,uid=1000,target=/app/.cache/bundle \
    bundle install && \
    mkdir -p vendor && \
    cp -ar .cache/bundle vendor/bundle
RUN bundle config set path vendor/bundle

まずbundleのinstall先を.cache/bundleに設定します。

続く--mount=type=cachetarget=/app/.cache/bundleという記載が、Cache Mountを利用している部分です。
この記載を含むRUN命令の中では、targetに指定したpath(ここでは/app/.cache/bundle)の中身が、ホスト内に保存され、次回以降のbuildでcacheとして利用されるようになります。

したがって、直後のbundle install.cache/bundleにインストールされたgemは、ホスト内部に保存されています。
このままだと、コンテナ内にgemがない状態になってしまうため、続けてvendorディレクトリを作り、そこに.cacheディレクトリから中身をコピーしています。

最後にbundle config set path vendor/bundleでpathを指定することで、bundlerがvendor/bundleを読みに行ってくれるようになります。

やや回りくどい気もしますが、これによってbuild時間が劇的に改善しました。こちらによると、

RUN --mount=type=cache 命令をうまく活用すると,従来のdocker buildより33倍以上速いビルドも可能です.

私の環境ではbuildのたびにbundle installで300秒以上かかっていました。cacheがあれば、build時のbundle installは変更差分のみですぐ終わるので、気軽にbuildできます。

Buildkit

上述のCache Mountを使うためには、Buildkitでbuildをする必要があります。

Buildkitとは、dockerのイメージビルドを便利にしてくれるビルドツールキットです。こちらにあるように、ビルドのそれぞれの過程ごとにかかった時間を表示してくれたりします。
他にもビルドの並列実行など、たくさんの機能があるようです。

BuildKit によるイメージ構築

Buildkitの導入

主に2つの方法があります。

  • 環境変数DOCKER_BUILDKIT=1を設定する。
  • 試験機能モードを有効にすることでdocker buildxコマンドを使う(Docker 19.03以上)。

1つ目は、DOCKER_BUILDKIT=1 docker build .のように環境変数を指定する簡単な方法です。

2つ目は、buildxというプラグインを利用する方法で、buildkitの全ての機能が有効になるとのことです。config.json (デフォルトでは~/.docker/config.json) に次のように指定します。

~/.docker/config.json
{
    "experimental": "enabled"
}

これにより、環境変数なしでdocker buildx build .のようにビルドを実行する事ができます。
BuildKitによりDockerとDocker Composeで外部キャッシュを使った効率的なビルドをする方法

Buildkit Cache Mountの利用

--mountは新しい構文のため、Dockerfileの1行目に次の記述をする必要があります。

# syntax = docker/dockerfile:experimental

Cacheの削除

docker builder prune

以上がbuildkitの使い方です。
簡単な設定をするだけで良いので、cacheを使わない場合も、取り入れてみるといいかもしれません。

yarn install

RUN --mount=type=cache,uid=1000,target=/app/.cache/node_modules \
    bin/yarn install --modules-folder .cache/node_modules && \
    cp -ar .cache/node_modules node_modules

yarn installも同じくCache Mountを使います。

asset precompile

COPY --chown=rails . /app

RUN --mount=type=cache,uid=1000,target=/app/tmp/cache bin/rails assets:precompile

最後にホストのファイルを全てコピーして、Cache Mountを利用してアセットをプリコンパイルします。

開発環境と本番環境でさらにステージを分けて、本番環境でのみプリコンパイルを行うなどの設定も考えられます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Dockerイメージを快適にビルド for Rails6

本記事の趣旨

令和時代のRails運用
こちらのスライドに掲載されている以下のDockerfileが、キャッシュやマルチステージビルドを利用したベストプラクティスとして参考になりました。

Dockerfile
# syntax = docker/dockerfile:experimental

# Node.jsダウンロード用ビルドステージ
FROM ruby:2.6.5 AS nodejs

WORKDIR /tmp

# Node.jsのダウンロード
RUN curl -LO https://nodejs.org/dist/v12.14.1/node-v12.14.1-linux-x64.tar.xz
RUN tar xvf node-v12.14.1-linux-x64.tar.xz
RUN mv node-v12.14.1-linux-x64 node

FROM ruby:2.6.5

# nodejsをインストールしたイメージからnode.jsをコピーする
COPY --from=nodejs /tmp/node /opt/node
ENV PATH /opt/node/bin:$PATH

# アプリケーション起動用のユーザーを追加
RUN useradd -m -u 1000 rails
RUN mkdir /app && chown rails /app
USER rails

# yarnのインストール
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
ENV PATH /home/rails/.yarn/bin:/home/rails/.config/yarn/global/node_modules/.bin:$PATH

# ruby-2.7.0でnewした場合を考慮
RUN gem install bundler

WORKDIR /app

# Dockerのビルドステップキャッシュを利用するため
# 先にGemfileを転送し、bundle installする
COPY --chown=rails Gemfile Gemfile.lock package.json yarn.lock /app/

RUN bundle config set app_config .bundle
RUN bundle config set path .cache/bundle
# mount cacheを利用する
RUN --mount=type=cache,uid=1000,target=/app/.cache/bundle \
    bundle install && \
    mkdir -p vendor && \
    cp -ar .cache/bundle vendor/bundle
RUN bundle config set path vendor/bundle

RUN --mount=type=cache,uid=1000,target=/app/.cache/node_modules \
    bin/yarn install --modules-folder .cache/node_modules && \
    cp -ar .cache/node_modules node_modules

COPY --chown=rails . /app

RUN --mount=type=cache,uid=1000,target=/app/tmp/cache bin/rails assets:precompile

# 実行時にコマンド指定が無い場合に実行されるコマンド
CMD ["bin/rails", "s", "-b", "0.0.0.0"]

ここで使用されているBuildkitなどの要素について、勉強した内容をまとめたいと思います。

これらの方法により、開発効率の向上を実感しましたので、RailsとDockerを学習中の方のご参考になればと思います。

(投稿者はDockerを勉強中で、実務は未経験ですので、気になる点がありましたらコメントでご指摘をお願いします。)

参考記事

Dockerfileを改善するためのBest Practice 2019年版

Docker Buildにおけるリードタイム短縮のための3つの改善ポイント

開発環境

  • Mac OS X 10.15.4
  • Docker 19.03.8
  • Docker Desktop for Mac 2.3.0.3
  • Ruby 2.6.5 Rails 6.0.2

Dockerfileの解説

上記のDockerfileの要点を見ていきます。
1行目の# syntax =という部分は後述するBuildkitに関する記述です。

その次の

# Node.jsダウンロード用ビルドステージ
FROM ruby:2.6.5 AS nodejs

WORKDIR /tmp

# Node.jsのダウンロード
RUN curl -LO https://nodejs.org/dist/v12.14.1/node-v12.14.1-linux-x64.tar.xz
RUN tar xvf node-v12.14.1-linux-x64.tar.xz
RUN mv node-v12.14.1-linux-x64 node

ここでは、Railsに必要なNode.jsをインストールしています。

  • tmpに移動
  • node-v12.14.1-linux-x64.tar.xzをダウンロード、展開
  • node-v12.14.1-linux-x64をnodeにリネーム

の結果、/tmp/node(本体), /tmp/node-v12.14.1-linux-x64.tar.xz(不要)
が生成されます。

最終的なDockerイメージを軽量にするために、必要なnode本体だけを残す必要があります。Dockerはレイヤー構造で履歴が残っているため、ただ単にRUN rm node-v12.14.1-linux-x64.tar.xzとしても意味がないようです。

そこで、マルチステージビルドを利用しています。

マルチステージビルド

マルチステージビルドは、1つのDockerfileに複数のステージを分けて記述します。
例のDockerfileでは、FROM行が2箇所、つまり2つのステージがあります。

FROM ruby:2.6.5 AS nodejs
...
FROM ruby:2.6.5

1つ目のAS nodejsの記載で、ステージにnodejsと名付けています。これによって、2つ目のステージで、

# nodejsをインストールしたイメージからnode.jsをコピーする
COPY --from=nodejs /tmp/node /opt/node
ENV PATH /opt/node/bin:$PATH

上でインストールしたtmp/nodeだけをコピーする事ができます。

今回の場合は、これによって節約できるのは15MBほどですが、Goのようなコンパイル言語で、ビルド結果のファイルだけを次のステージに渡すと、かなりの軽量化ができるようです。

ちなみに、2つ目のステージのyarnインストールは、ファイルそのものではなく、install.shをダウンロードして実行しているだけで、不要なファイルが残らないため、このままで問題ないのだと思います。

# yarnのインストール
RUN curl -o- -L https://yarnpkg.com/install.sh | bash
ENV PATH /home/rails/.yarn/bin:/home/rails/.config/yarn/global/node_modules/.bin:$PATH

ユーザーの追加

# アプリケーション起動用のユーザーを追加
RUN useradd -m -u 1000 rails
RUN mkdir /app && chown rails /app
USER rails

ここでは、コンテナ内にユーザーを追加しています。デフォルトのrootユーザーのままでは、ホストとファイルを共有する際に権限の問題が発生するようです。

ただ、Docker for Macの場合、その問題は起こらないので、この設定は省略しても良いかもしれません。
(非rootユーザーにすると、vimを使いたい時にapt-getができないなど、色々と困る場面もありましたので..)

bundle install

WORKDIR /app

# Dockerのビルドステップキャッシュを利用するため
# 先にGemfileを転送し、bundle installする
COPY --chown=rails Gemfile Gemfile.lock package.json yarn.lock /app/

作業ディレクトリに必要なパッケージ管理ファイルを置いています。その後、

RUN bundle config set app_config .bundle
RUN bundle config set path .cache/bundle

まずbundle configコマンドを使用して、installするpathを設定しています。

bundle config

例として、bundle config set path vendor/bundleと設定しておくと、bundle installの際に、bundle install --path vendor/bundleとパスを指定した事と同じになります。

installの際に、--pathを指定する方法は非推奨となったようなので、今後はbundle configを使いましょう。

Bundlerでビルドオプションを指定する

Cache Mount

RUN bundle config set path .cache/bundle
# mount cacheを利用する
RUN --mount=type=cache,uid=1000,target=/app/.cache/bundle \
    bundle install && \
    mkdir -p vendor && \
    cp -ar .cache/bundle vendor/bundle
RUN bundle config set path vendor/bundle

まずbundleのinstall先を.cache/bundleに設定します。

続く--mount=type=cachetarget=/app/.cache/bundleという記載が、Cache Mountを利用している部分です。
この記載を含むRUN命令の中では、targetに指定したpath(ここでは/app/.cache/bundle)の中身が、ホスト内に保存され、次回以降のbuildでcacheとして利用されるようになります。

したがって、直後のbundle install.cache/bundleにインストールされたgemは、ホスト内部に保存されています。
このままだと、コンテナ内にgemがない状態になってしまうため、続けてvendorディレクトリを作り、そこに.cacheディレクトリから中身をコピーしています。

最後にbundle config set path vendor/bundleでpathを指定することで、bundlerがvendor/bundleを読みに行ってくれるようになります。

やや回りくどい気もしますが、これによってbuild時間が劇的に改善しました。こちらによると、

RUN --mount=type=cache 命令をうまく活用すると,従来のdocker buildより33倍以上速いビルドも可能です.

私の環境ではbuildのたびにbundle installで300秒以上かかっていました。cacheがあれば、build時のbundle installは変更差分のみですぐ終わるので、気軽にbuildできます。

Buildkit

上述のCache Mountを使うためには、Buildkitでbuildをする必要があります。

Buildkitとは、dockerのイメージビルドを便利にしてくれるビルドツールキットです。こちらにあるように、ビルドのそれぞれの過程ごとにかかった時間を表示してくれたりします。
他にもビルドの並列実行など、たくさんの機能があるようです。

BuildKit によるイメージ構築

Buildkitの導入

主に2つの方法があります。

  • 環境変数DOCKER_BUILDKIT=1を設定する。
  • 試験機能モードを有効にすることでdocker buildxコマンドを使う(Docker 19.03以上)。

1つ目は、DOCKER_BUILDKIT=1 docker build .のように環境変数を指定する簡単な方法です。

2つ目は、buildxというプラグインを利用する方法で、buildkitの全ての機能が有効になるとのことです。config.json (デフォルトでは~/.docker/config.json) に次のように指定します。

~/.docker/config.json
{
    "experimental": "enabled"
}

これにより、環境変数なしでdocker buildx build .のようにビルドを実行する事ができます。
BuildKitによりDockerとDocker Composeで外部キャッシュを使った効率的なビルドをする方法

Buildkit Cache Mountの利用

--mountは新しい構文のため、Dockerfileの1行目に次の記述をする必要があります。

# syntax = docker/dockerfile:experimental

Cacheの削除

docker builder prune

以上がbuildkitの使い方です。
簡単な設定をするだけで良いので、cacheを使わない場合も、取り入れてみるといいかもしれません。

yarn install

RUN --mount=type=cache,uid=1000,target=/app/.cache/node_modules \
    bin/yarn install --modules-folder .cache/node_modules && \
    cp -ar .cache/node_modules node_modules

yarn installも同じくCache Mountを使います。

asset precompile

COPY --chown=rails . /app

RUN --mount=type=cache,uid=1000,target=/app/tmp/cache bin/rails assets:precompile

最後にホストのファイルを全てコピーして、Cache Mountを利用してアセットをプリコンパイルします。

開発環境と本番環境でさらにステージを分けて、本番環境でのみプリコンパイルを行うなどの設定も考えられます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカル環境でnginxをhttps通信をする方法

はじめに

 就活中の選考課題でGeolocation apiを使って、位置情報を利用するサイトを作る際に
Google chromeのブラウザでは、https通信しないと位置情報を取得できないので、
https通信をできるようにしました。
 この記事は、ゆうきゃんさんの記事である最強のLaravel開発環境をDockerを使って構築する【新編集版】をベースにhttps通信を構築しています。1から環境を用意したい方は、この記事はじめに参照してから記事をご覧ください。

開発環境

開発環境
Windows10 Home
docker tool box 19.03.1
docker-compose 1.24.1
Mysql 8.0.20
Nginx 1.18.0

https通信する手順

Chocolateyのインストール

https通信に必要なssl証明書を簡単に発行することができるmkcertツールインストールするために
Chocolateyというパッケージ管理ツールをインストールします

①スタートメニューを右クリックしてコマンドプロンプトを管理者権限で開く
スクリーンショット (37).png
②コマンドか公式サイトからChocolateyインストール

・コマンド
C:\>Chocolatey Install
Chocolatey v0.10.15

公式サイト
スクリーンショット (39).png

C:\>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))`

mkcertのインストールしてSSL証明書発行

C:\>choco install mkcert
C:\>mkcert localhost 196.168.99.101(ご自身のIPアドレスを入力)
mkcert localhost 196.168.99.101
Using the local CA at "C:\Users\ユーザ名\AppData\Local\mkcert" 

Created a new certificate valid for the following names 
 - "localhost"
 - "196.168.99.101(ご自身のIPアドレス)"

The certificate is at "./localhost+1.pem" and the key at "./localhost+1-key.pem" 

発行したら今いるディレクトリに発行させるので
localhost+1.pem localhost+1-key.pemの+1を消して
docker-laravel\infrastructure\docker\nginxのディレクトリに移動します

⚠もし、mkcert localhost 196.168.99.101(ご自身のIPアドレスを入力)と打って
システムリソース不足のため...と言われたら、
セキュリティーソフトのリアルタイムスキャンの停止や再起動をして見てください

docker-compose.ymlの記述

web:
    build: ./docker/nginx
    ports:
      - 196.168.99.101:80:80
      - 196.168.99.101:443:443(ここを追加で記述する)
    volumes:
      - php-fpm-socket:/var/run/php-fpm
      - ../backend:/work/backend

Dockerfileの記述

FROM node:14.2-alpine as node
FROM nginx:1.18-alpine
SHELL ["/bin/ash", "-oeux", "pipefail", "-c"]

ENV TZ=UTC

RUN apk update && \
  apk add --update --no-cache --virtual=.build-dependencies g++

  COPY --from=node /usr/local/bin /usr/local/bin
  COPY --from=node /opt /opt
  COPY ./default.conf /etc/nginx/conf.d/default.conf
  ADD ./localhost.pem /etc/certs/localhost.pem(ここを追加で記述する)
  ADD ./localhost-key.pem /etc/certs/localhost-key.pem(ここを追加で記述する)

  WORKDIR /work/backend

default.confの記述

access_log /dev/stdout main;
error_log /dev/stderr warn;

  server {
    listen 80;
    root /work/backend/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
  }

  server {(ここから追加で記述する)
    listen 443 ssl;
    root /work/backend/public;
    ssl_certificate /etc/certs/localhost.pem;
    ssl_certificate_key /etc/certs/localhost-key.pem;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
  }

docker-compose の起動

//コンテナをビルド
make build = docker-compose build --no-cache --force-rm
//コンテナを作成する
make up = docker-compose up -d
//全コンテナを起動する
docker start $(dokcer ps -a -q)
⚠makefileがない場合はdockerコマンドを入力してください

結果

下記のようにhttps通信ができました!
最近のブラウザはhttps通信を要求してくるので環境を整えて開発することをおすすめします
お読み頂きましてありがとうございました。
スクリーンショット (41).png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカル環境でnginxをhttps通信にする方法

はじめに

 就活中の選考課題でGeolocation apiを使って、位置情報を利用するサイトを作る際に
Google chromeのブラウザでは、https通信しないと位置情報を取得できないので、
https通信をできるようにしました。
 この記事は、ゆうきゃんさんの記事である最強のLaravel開発環境をDockerを使って構築する【新編集版】をベースにhttps通信を構築しています。1から環境を用意したい方は、上記の記事をはじめに参照してから本記事をご覧ください。

開発環境

開発環境
Windows10 Home
docker tool box 19.03.1
docker-compose 1.24.1
Mysql 8.0.20
Nginx 1.18.0

https通信する手順

Chocolateyのインストール

https通信に必要なSSL証明書を簡単に発行することができるmkcertというツールをインストールするために
Chocolateyというパッケージ管理ツールをインストールします

①スタートメニューを右クリックしてコマンドプロンプトを管理者権限で開く
スクリーンショット (37).png
②コマンドか公式サイトからChocolateyインストール

・コマンド
C:\>Chocolatey Install
Chocolatey v0.10.15

公式サイト
スクリーンショット (39).png

C:\>Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))`

mkcertをインストールしてSSL証明書を発行

C:\>choco install mkcert
C:\>mkcert localhost 196.168.99.101(ご自身のIPアドレスを入力)
mkcert localhost 196.168.99.101
Using the local CA at "C:\Users\ユーザ名\AppData\Local\mkcert" 

Created a new certificate valid for the following names 
 - "localhost"
 - "196.168.99.101(ご自身のIPアドレス)"

The certificate is at "./localhost+1.pem" and the key at "./localhost+1-key.pem" 

発行したら今いるディレクトリに発行させるので
localhost+1.pem localhost+1-key.pemの+1を消して
docker-laravel\infrastructure\docker\nginxのディレクトリにファイルを移動します

⚠もし、mkcert localhost 196.168.99.101(ご自身のIPアドレスを入力)と打って
システムリソース不足のため...と言われたら、
セキュリティーソフトのリアルタイムスキャンの停止や再起動をして見てください

docker-compose.ymlの記述

web:
    build: ./docker/nginx
    ports:
      - 196.168.99.101:80:80
      - 196.168.99.101:443:443(ここを追加で記述する)
    volumes:
      - php-fpm-socket:/var/run/php-fpm
      - ../backend:/work/backend

Dockerfileの記述

FROM node:14.2-alpine as node
FROM nginx:1.18-alpine
SHELL ["/bin/ash", "-oeux", "pipefail", "-c"]

ENV TZ=UTC

RUN apk update && \
  apk add --update --no-cache --virtual=.build-dependencies g++

  COPY --from=node /usr/local/bin /usr/local/bin
  COPY --from=node /opt /opt
  COPY ./default.conf /etc/nginx/conf.d/default.conf
  ADD ./localhost.pem /etc/certs/localhost.pem(ここを追加で記述する)
  ADD ./localhost-key.pem /etc/certs/localhost-key.pem(ここを追加で記述する)

  WORKDIR /work/backend

default.confの記述

access_log /dev/stdout main;
error_log /dev/stderr warn;

  server {
    listen 80;
    root /work/backend/public;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
  }

  server {(ここから追加で記述する)
    listen 443 ssl;
    root /work/backend/public;
    ssl_certificate /etc/certs/localhost.pem;
    ssl_certificate_key /etc/certs/localhost-key.pem;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";

    index index.html index.htm index.php;

    charset utf-8;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }
  }

docker-compose の起動

//コンテナをビルド
make build = docker-compose build --no-cache --force-rm
//コンテナを作成する
make up = docker-compose up -d
//全コンテナを起動する
docker start $(dokcer ps -a -q)
⚠makefileがない場合はdockerコマンドを入力してください

結果

https://(ご自身のアドレス)を入力して接続すると下記のようにhttps通信ができました!
最近のブラウザはhttps通信を要求してくるので環境を整えて開発することをおすすめします
お読み頂きましてありがとうございました。
スクリーンショット (41).png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む