1. beeeyan

    Posted

    beeeyan
Changes in title
+DockerコンテナにLinuxbrewをDockerfileを使ってインストールする
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,355 @@
+この記事は[株式会社クロノス](https://www.kronos.jp/)の[「~2020年春~勝手にやりますアドベントカレンダー」](https://qiita.com/beeeyan/items/342d51fa301bcf81e9c0)の7日目の記事です!
+
+## はじめに
+自分のパソコンでは環境が出来上がっている状態で
+みんなで環境構築をやろう! 教えよう! となったとき。
+みなさんならどうするでしょうか。
+
+細かい差異も見逃さないために、自分のパソコンに入っているパッケージをアンインストールして、環境構築の実演をしてみせるでしょうか。
+
+(自分のパソコンの環境をいじるのは面倒くさいので)細かいことは気にせず、手順書を作って見守る(適宜困ったことがあったら助ける)でしょうか。
+
+今回は、「自分の環境を汚したくないけど、確実に環境構築をやってもらうために実演をしたい」ってときに`Dockerコンテナ`使えないかなーと色々調べてみた結果は紹介させてもらいます。
+※ あまり実践的ではないです。
+
+## 知れること・知れないこと
+
+※ 結果的にやっていることはubuntu18.04のイメージに`Linuxbrew`をDockerfileでインストールする方法になります。
+
+
+**知れること**
+
+- ubuntuのイメージにLinuxbrewをDockerfileを使ってインストールする方法
+- 素人目線のDockerfileの書き方のポイント
+
+**知れないこと**
+
+- windowsで環境構築を実演する際のDockerイメージの作り方
+
+## 実現方法の考え方
+おおよそmacでは`Homebrew`使って、パッケージインストールして、環境構築等するだろうという~~偏見~~仮定の元、
+「LinuxbrewをLinuxのDockerイメージに入れたら、見かけ上はmacでHomebrew使うのと変わらなくない? ほらコマンドもbrewで変わらないし……」
+という安直な考えで、`Linuxbrew`の利用を考えます。
+### Linuxbrew
+
+`Linuxbrew`とは`Homebrew`のLinux版(名前そのまま)です。
+2019年2月から本家 Homebrew 2.0.0 にてLinuxbrewが正式にサポートされるとのことなので([homebrew-2.0.0](https://brew.sh/2019/02/02/homebrew-2.0.0/))、使わない手はないです。
+
+参考
+[LinuxbrewでUbuntu18.04のパッケージ管理](https://qiita.com/tikogr/items/e19c2a2cec41a8d4d85f)
+[Linuxbrewのススメ](https://qiita.com/thermes/items/926b478ff6e3758ecfea)
+
+## 目標
+作成したイメージのDockerコンテナに入って
+
+```console
+$ brew doctor
+```
+とコマンドを叩いて、一発で`Your system is ready to brew.`と出てくることを目指します。
+
+## ホームディレクトリにLinuxbrewをインストールするDockerfile
+
+こちらは道半ばバージョンです。
+Linuxbrewは`/home/linuxbrew/.linuxbrew`下にインストールすることが推奨されています。
+上記デフォルトのディレクトリにインストールすることで、ビルド済みバイナリがあるパッケージはバイナリをインストールできるようになります。
+そのためインストール場所がデフォルトでないと`brew doctor`でWarningが出ます。
+
+ただ、Dockerfileで`/home/linuxbrew/.linuxbrew`配下にLinuxbrewをインストールするには、私が試した限り、少々特殊なことをする必要がありました。
+こちらはこちらで「素直にDockerfileを書いたバージョン」として、記載しておきたいと思います。
+
+こちらの方法だと`/home/[ユーザ名]/.linuxbrew/`にインストールされることになります。Warningが出るだけでLinuxbrewは普通に利用可能です。
+
+```Dockerfile
+FROM ubuntu:18.04
+LABEL maintainer beeeeyan
+#環境変数を設定
+ENV DEBIAN_FRONTEND=noninteractive
+ENV USER beeeeyan
+ENV HOME /home/${USER}
+ENV SHELL /bin/bash
+ENV PW password
+
+# 種々インストール
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ vim \
+ sudo \
+ locales \
+ build-essential \
+ ca-certificates \
+ curl \
+ file \
+ git && \
+ # 一般ユーザーアカウント追加
+ useradd -m ${USER} && \
+ # 一般ユーザーにsudo権限を付与
+ gpasswd -a ${USER} sudo && \
+ # 一般ユーザーのパスワードを設定
+ echo "${USER}:${PW}" | chpasswd && \
+ # ログインシェルを指定
+ sed -i.bak -r s#${HOME}:\(.+\)#${HOME}:${SHELL}# /etc/passwd && \
+ # localの設定
+ locale-gen en_US.UTF-8
+
+# コマンドを実行するUSERを変更
+USER ${USER}
+# 作業ディレクトリを指定
+WORKDIR ${HOME}
+
+# Linuxbrewをインストール
+RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/Linuxbrew/install/master/install.sh)" && \
+# pathの設定
+ echo 'export PATH=${HOME}/.linuxbrew/bin:$PATH' >> .bash_profile
+```
+
+### 説明
+素人目線のDockerfileの書き方にも触れています。
+今回の「Linuxbrewのインストール」に深く関わる箇所には気持ち最初に☆付けました。
+
+```Dockerfile
+LABEL maintainer beeeeyan
+```
+MAINTAINER(作成者情報)はDocker 1.13から非推奨(deprecated)となっているそうです。LABELで書きましょう。
+参考 : [1.13以降はMAINTAINERの代わりにLABELを使うようにしよう](https://qiita.com/okisanjp/items/a43068f956d273703ea8)
+
+
+```Dockerfile
+ENV DEBIAN_FRONTEND=noninteractive
+```
+`brew doctor`で出てくるエラーを潰す仮定で`locales`をインストールすることにしたのですが(後述)、`locales`をインストールしようとすると`apt-get install`自体が止まったので設定しました。詳しくは以下参照ください。
+参考 : [Docker Ubuntu18.04でtzdataをinstallするときにtimezoneの選択をしないでinstallする](https://qiita.com/yagince/items/deba267f789604643bab)
+
+
+```Dockerfile
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+```
+ここいらへんは呪文ですね。`--no-install-recommends`の意味を知らなかったのですが「指定したもの以外余計なものは入れない」ということみたいです。
+参考 : [#Linux #Ubuntu #docker #Dockerfile のこれは何? apt-get install --no-install-recommends](https://qiita.com/YumaInaura/items/2cc1b64f875a36777f2d)
+
+
+```Dockerfile
+ vim \
+ sudo \
+ locales \
+ build-essential \
+ ca-certificates \
+ curl \
+ file \
+ git && \
+ ~省略~
+   # localの設定
+ locale-gen en_US.UTF-8
+```
+
+- `vim`と`sudo`
+あると便利かな、くらいで入れています。`sudo`に関しては「完成版」では必須です。
+
+- `locales`…(省略)…`locale-gen en_US.UTF-8`
+`brew doctor`とコマンド叩いて、以下のようなエラーが出たのでインストール&設定しました。
+>warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8): No such file or directory …(省略)
+
+- ☆ `build-essential`と`ca-certificates`と`curl`と`file`と `git `
+調べてもらったらわかりますが、`build-essential`と`curl`と`file`と `git `については、Linuxbrewをインストールするときに公式で先にインストールするよう指定されているパッケージです。
+ポイントは **ca-certificates**を指定するところにあります。(これがないと`curl`などを使っても通信ができません)このパッケージは**普段は自動でインストールされるもの**です。勘のいい方はお分かりかと思いますが`apt-get install`に`--no-install-recommends`を指定した影響で明示的に書いておく必要が出てきたパッケージです。
+参考 : [公式](https://docs.brew.sh/Homebrew-on-Linux)
+
+```Dockerfile
+ # 一般ユーザーアカウント追加
+ useradd -m ${USER} && \
+ # 一般ユーザーにsudo権限を付与
+ gpasswd -a ${USER} sudo && \
+ # 一般ユーザーのパスワードを設定
+ echo "${USER}:${PW}" | chpasswd && \
+ # ログインシェルを指定
+ sed -i.bak -r s#${HOME}:\(.+\)#${HOME}:${SHELL}# /etc/passwd && \
+```
+☆ 一般ユーザを追加している部分
+ユーザを追加する理由は**rootユーザでLinuxbrewをインストールしようとするエラーになるから**です。参考先の情報から`sed`コマンドの書き方は多少調整しました。
+参考 : [Dockerで開発環境を仮想化する](https://qiita.com/Riliumph/items/3b09e0804d7a04dff85b)
+
+```Dockerfile
+# コマンドを実行するUSERを変更
+USER ${USER}
+# 作業ディレクトリを指定
+WORKDIR ${HOME}
+```
+コマンドを実行するユーザとディレクトリを作成したユーザに変更しています。
+
+
+後は[公式](https://docs.brew.sh/Homebrew-on-Linux)のインストールコマンド叩いて`.bash_profile`にLinuxbrewのパス(今回だと`/home/[ユーザ名]/.linuxbrew/bin`)を指定するだけです。
+
+### なぜ「素直な書き方」では`/home/linuxbrew/.linuxbrew`にインストールできないのか?
+
+素直な書き方だとビルド中に以下のような応答が求められます。
+
+```console
+==> Select the Homebrew installation directory
+- Enter your password to install to /home/linuxbrew/.linuxbrew (recommended)
+- Press Control-D to install to /home/[ユーザ名]/.linuxbrew
+- Press Control-C to cancel installation
+```
+ここで`Enter your password`できなくてつみます。
+ただ不思議と`Control-D`の挙動はしてくれて、`/home/[ユーザ名]/.linuxbrew`にインストールされるという寸法です。
+
+## [完成版]`/home/linuxbrew/.linuxbrew`にLinuxbrewをインストールするDockerfile
+
+ほんの少しトリッキーなことをしたのですが、以下でうまくいきました。
+
+```Dockerfile
+FROM ubuntu:18.04
+LABEL maintainer beeeeyan
+#環境変数を設定
+ENV DEBIAN_FRONTEND=noninteractive
+ENV USER beeeeyan
+ENV HOME /home/${USER}
+ENV SHELL /bin/bash
+ENV PW password
+
+# 種々インストール
+RUN apt-get update && \
+ apt-get install -y --no-install-recommends \
+ vim \
+ sudo \
+ locales \
+ build-essential \
+ ca-certificates \
+ curl \
+ file \
+ git && \
+ # 一般ユーザーアカウント追加
+ useradd -m ${USER} && \
+ # 一般ユーザーにsudo権限を付与
+ gpasswd -a ${USER} sudo && \
+ # 一般ユーザーのパスワードを設定
+ echo "${USER}:${PW}" | chpasswd && \
+ # ログインシェルを指定
+ sed -i.bak -r s#${HOME}:\(.+\)#${HOME}:${SHELL}# /etc/passwd && \
+ # localの設定
+ locale-gen en_US.UTF-8 && \
+ # linuxbrewの「Alternative Installation」を実行
+ git clone https://github.com/Homebrew/brew /home/linuxbrew/.linuxbrew/Homebrew && \
+ mkdir /home/linuxbrew/.linuxbrew/bin && \
+ ln -s /home/linuxbrew/.linuxbrew/Homebrew/bin/brew /home/linuxbrew/.linuxbrew/bin && \
+ eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
+
+# コマンドを実行するUSERを変更
+USER ${USER}
+# 作業ディレクトリを指定
+WORKDIR ${HOME}
+
+# Linuxbrew関連のフォルダ作成
+RUN echo ${PW} | sudo -S mkdir -p /home/linuxbrew/.linuxbrew/etc \
+ /home/linuxbrew/.linuxbrew/include \
+ /home/linuxbrew/.linuxbrew/lib \
+ /home/linuxbrew/.linuxbrew/opt \
+ /home/linuxbrew/.linuxbrew/sbin \
+ /home/linuxbrew/.linuxbrew/share \
+ /home/linuxbrew/.linuxbrew/var/homebrew/linked \
+ /home/linuxbrew/.linuxbrew/var/homebrew/locks \
+ /home/linuxbrew/.linuxbrew/Cellar && \
+ # 権限変更
+ echo ${PW} | sudo -S chown -R ${USER} /home/linuxbrew/.linuxbrew/etc \
+ /home/linuxbrew/.linuxbrew/include \
+ /home/linuxbrew/.linuxbrew/lib \
+ /home/linuxbrew/.linuxbrew/opt \
+ /home/linuxbrew/.linuxbrew/sbin \
+ /home/linuxbrew/.linuxbrew/share \
+ /home/linuxbrew/.linuxbrew/var/homebrew/linked \
+ /home/linuxbrew/.linuxbrew/Cellar \
+ /home/linuxbrew/.linuxbrew/Homebrew \
+ /home/linuxbrew/.linuxbrew/bin \
+ /home/linuxbrew/.linuxbrew/var/homebrew/locks && \
+ # パスの設定
+ echo 'export PATH="/home/linuxbrew/.linuxbrew/bin:$PATH"' >> .bash_profile && \
+ # パスの反映
+ . ~/.bash_profile && \
+ # brew doctorの実行
+ brew doctor
+```
+ポイントは[公式](https://docs.brew.sh/Homebrew-on-Linux)の「Alternative Installation」の部分です。
+
+```
+ # linuxbrewの「Alternative Installation」を実行
+ git clone https://github.com/Homebrew/brew /home/linuxbrew/.linuxbrew/Homebrew && \
+ mkdir /home/linuxbrew/.linuxbrew/bin && \
+ ln -s /home/linuxbrew/.linuxbrew/Homebrew/bin/brew /home/linuxbrew/.linuxbrew/bin && \
+ eval $(/home/linuxbrew/.linuxbrew/bin/brew shellenv)
+```
+
+
+「Alternative Installation」で説明されている内容は、任意の場所に`Linuxbrew`をインストールする際によく利用されるコマンドです。これを**デフォルトの場所にインストールする**方式で利用します。
+つまり「Alternative Installation」に書かれているインストール場所`~/.linuxbrew/Homebrew`を`/home/linuxbrew/.linuxbrew/Homebrew`に変更します。(変更した状態のものが上記コマンドです)
+
+```
+# Linuxbrew関連のフォルダ作成
+RUN echo ${PW} | sudo -S mkdir -p /home/linuxbrew/.linuxbrew/etc \
+ /home/linuxbrew/.linuxbrew/include \
+ /home/linuxbrew/.linuxbrew/lib \
+ /home/linuxbrew/.linuxbrew/opt \
+ /home/linuxbrew/.linuxbrew/sbin \
+ /home/linuxbrew/.linuxbrew/share \
+ /home/linuxbrew/.linuxbrew/var/homebrew/linked \
+ /home/linuxbrew/.linuxbrew/var/homebrew/locks \
+ /home/linuxbrew/.linuxbrew/Cellar && \
+ # 権限変更
+ echo ${PW} | sudo -S chown -R ${USER} /home/linuxbrew/.linuxbrew/etc \
+ /home/linuxbrew/.linuxbrew/include \
+ /home/linuxbrew/.linuxbrew/lib \
+ /home/linuxbrew/.linuxbrew/opt \
+ /home/linuxbrew/.linuxbrew/sbin \
+ /home/linuxbrew/.linuxbrew/share \
+ /home/linuxbrew/.linuxbrew/var/homebrew/linked \
+ /home/linuxbrew/.linuxbrew/Cellar \
+ /home/linuxbrew/.linuxbrew/Homebrew \
+ /home/linuxbrew/.linuxbrew/bin \
+ /home/linuxbrew/.linuxbrew/var/homebrew/locks && \
+```
+後半は`brew doctor`と打って怒られる部分を先回りして処理しています。
+`sudo`で求められるパスワードは`echo`と`-S`オプションで乗り越え、
+前半は必要なフォルダの作成・後半はフォルダの権限の変更(rootユーザでは`Linuxbrew`が利用できないので)、、といった具合です。
+権限の変更はエラーでは`${whoami}`でしたが、Dockerfileの統一感を考えて`${USER}`でここでは書きました。
+
+```
+ # brew doctorの実行
+ brew doctor
+```
+パスを追加して、最後に一回`brew doctor`を実行しておきます。
+一回目の`brew doctor`では、一部`brew`のツールらしきものがインストールされます。(一発で`Your system is ready to brew`と出てこない!)
+
+## コンテナを立ち上げるまで〜〜
+ビルドします。(試行錯誤する段階では、私は[Automated Build Docker](http://docs.docker.jp/docker-hub/builds.html)でやってました)
+Dockerfileがカレントディレクトリに存在する状態で以下コマンドを実行
+
+```console
+$ docker build -t [イメージ名の指定] .
+```
+コンテナの起動
+
+```
+$ docker run --name [コンテナ名の指定] -it -d [イメージ名] /bin/bash
+```
+起動中のコンテナに入る
+※ちょっとした話ですが`--login`をつけておかないと`.bash_profile`に指定した設定が反映されないのでご注意ください。(パスが反映されず`brew doctor`が実行できない…)
+
+起動中のコンテナに入る
+
+```console
+$ docker exec -it [コンテナ名] /bin/bash --login
+```
+
+`brew doctor`!
+<img width="714" alt="スクリーンショット 2020-03-06 0.22.19.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/277737/c042d8bd-aea0-8b71-a28b-c41d7dd3d665.png">
+
+今回の目標達成です!
+
+## 参考
+
+- [DockerでCentOS7のコンテナを用意してLinuxbrewをインストールする](https://mabui.org/docker-centos7-linuxbrew-install/)
+expectコマンドに言及されていて、私も格闘しましたが難しかったです。
+
+- [Dockerfileを書くときに気をつけていること10選](https://qiita.com/c18t/items/f3a911ef01f124071c95)
+Dockerfileの書き方の参考。
+
+- [LinuxbrewでUbuntu18.04のパッケージ管理](https://qiita.com/tikogr/items/e19c2a2cec41a8d4d85f)
+Linuxbrewについて