はじめに
記載情報は CI/CD に関連します。
実務上関わった環境・機能を身近な情報としてお届けしたく、投稿をはじめました。
各学習上、お金のかからない方法を選択していきます。
以下の手順を実施することで、運用者が nginx の Docker Image を利用して、静的コンテンツを公開する工程を模倣します。
対象者と関連環境・機能
この記事は下記のような人を対象にしています。
- CI/CD 初学者
- プログラミング初学者
- 駆け出しエンジニア
記載している環境・機能は以下です。
- nginx
- nginx.conf
- default.conf.template
- Docker
- minikube(kubernetes)
まえおき
お仕事で nginx のコンテナ使って静的コンテンツ公開する部位の設計を命じられました。
で、恥ずかしながら nginx の読み方から調べました。(エンジンエックス、ですかね。)
もう設計始まるので時間もなくどこから手を付けてよいやら悩みましたが、直近調べたことをまずは残そうとしています。
元々、Web サーバーとして Apache は聞いたことがありましたが、これは色々な用途で活用できる有名どころとして耳に入っていたのかもしれません。
今回取り扱うものが「静的コンテンツ」であることより、「nginx」の選定となった模様でした。
(ザックリ印象「静的コンテンツの表示」や「リバースプロキシ」、「ロードバランサー」といった動作を得意とした Web サーバでしょうか。)
あと、OpenShift とか kubernetes で他所のボリューム見に行くところも初。
いや、手元の Docker でマウントとかもそういえばしたことなくて、自習に至った形です。
目論見
以下の流れで進めようと思いました。
- nginx のコンテナを動かす
- nginx のコンテナを Dockerfile で作って動かす
- nginx の利用上で必要になるであろう、port 番号やルートパスの指定方法を抑えておく
- 作った nginx イメージに Docker 上でマウントしてみる
- Docker でマウントする方法と、kubernetes でマウントする方法でどんな具合の差になるのかを抑えておく
恐らく、、たぶん、ここまで把握できれば、あとは何等か環境が変わっても部分的な書き換えとかでなんとか行けるのではないか。。
前提作業
- ubuntu に Docker をインストールする Install Docker Engine on Ubuntu
- minikube、kuectl のインストール
nginx のコンテナを動かす
真っ先に参照したのは Docker Hub 上の nginx 本家イメージ。とにかく最短動かしたく。
nginx - Official Image - Docker Hub
$ docker pull nginx:latest ※latestをプル
latest: Pulling from library/nginx
Digest: sha256:943c25b4b66b332184d5ba6bb18234273551593016c0e0ae906bab111548239f
Status: Image is up to date for nginx:latest
docker.io/library/nginx:latest
$ docker run -d -p 8080:80 nginx:latest ※起動
513779d0aaa6b8e3486a4809fa4aa1bd769a7bf78993255007ccf096c5f7238f
$ curl http://localhost:8080/ ※接続OK
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
nginx のコンテナを Dockerfile で作って動かす
ここも本家の情報で行けました。
FROM nginx
COPY nginx.conf /etc/nginx/nginx.conf
で、nginx.conf とは設定ファイルなのではないか。そうだとありがたく。
先ほど立ち上げた nginx で見てみると以下。
あれ、port 番号やルートパスの指定するのってこれではないの?
$ docker exec -it 0a3df5313506 bash
root@0a3df5313506:/# cat /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log notice;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf; ※ここが怪しい
}
nginx で必要になるであろう、port 番号やルートパスの指定方法を抑えておく
本家サイトに記載はありました。。分かる人なら分かるかもしれませんが、私にはちょっと。。
So if you place templates/default.conf.template file, which contains variable references like this:
listen ${NGINX_PORT};
outputs to /etc/nginx/conf.d/default.conf like this:
listen 80;
default.conf.template があるかというとそのようなものはありません。
root@0a3df5313506:/etc/nginx# pwd
/etc/nginx
root@0a3df5313506:/etc/nginx# ls -la
total 48
drwxr-xr-x 1 root root 4096 Oct 25 10:23 .
drwxr-xr-x 1 root root 4096 Nov 13 03:05 ..
drwxr-xr-x 1 root root 4096 Nov 13 03:05 conf.d
-rw-r--r-- 1 root root 1007 Oct 19 07:56 fastcgi_params
-rw-r--r-- 1 root root 5349 Oct 19 07:56 mime.types
lrwxrwxrwx 1 root root 22 Oct 19 09:32 modules -> /usr/lib/nginx/modules
-rw-r--r-- 1 root root 648 Oct 19 09:32 nginx.conf
-rw-r--r-- 1 root root 636 Oct 19 07:56 scgi_params
-rw-r--r-- 1 root root 664 Oct 19 07:56 uwsgi_params
ただ、conf.d/default.conf があり、nginx.conf 定義の「include /etc/nginx/conf.d/*.conf; ※ここが怪しい」で読んでいるような。
「default.conf.template は default.conf を元に注入したい環境変数を変数化して己で配置」が妥当なような。
実際やってみると意図通りにはなりました。
FROM nginx
COPY static-html-directory /usr/share/nginx/html ※静的コンテンツを配置
COPY nginx.conf /etc/nginx/nginx.conf ※ここは変更加えていませんが、後で外からも注入できるように一応コピー
COPY default.conf.template /etc/nginx/templates/default.conf.template ※default.conf を元に作ったテンプレートを配置
server {
listen ${NGINX_PORT}; ※ここにコンテナ起動時に注入する環境変数が入る想定
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /app;
省略
$ docker build -t oad3jp999/my-nginx:1.0.0 .
Sending build context to Docker daemon 14.85kB
Step 1/4 : FROM nginx
---> 76c69feac34e
Step 2/4 : COPY static-html-directory /usr/share/nginx/html
---> Using cache
---> 00de7b966f5c
Step 3/4 : COPY nginx.conf /etc/nginx/nginx.conf
---> Using cache
---> c0a6076cded2
Step 4/4 : COPY default.conf.template /etc/nginx/templates/default.conf.template
---> 35f5ff0e0e9e
Successfully built 35f5ff0e0e9e
Successfully tagged oad3jp999/my-nginx:1.0.0
$ docker run -d \
> -it \
> -p 8080:81 \ ※コンテナの81番ポートに、外より8080でつながる
> --name my-nginx \
> --env NGINX_PORT=81 \ ※コンテナ起動時に注入するポート番号
> oad3jp999/my-nginx:1.0.0
b4d4e29956b8fa146e6bbfc9e2d02dfd455d506e8379aa0bcdb7f36813d871a6
$
$ curl http://localhost:8080/ ※接続OK
hello this is static-text from docker coppy
作った nginx イメージに Docker 上でマウントしてみる
テンプレートをちょっと変えてみて。
server {
listen ${NGINX_PORT};
listen [::]:80;
server_name localhost;
#access_log /var/log/nginx/host.access.log main;
location / {
root /app; ※コンテナ内の何も入っていない/appにマウントしていきます
# root /usr/share/nginx/html;
index send.txt; ※/appに置いたsend.txtがindexとして読みだされる想定
# index index.html index.htm send.txt;
}
docker build してテンプレートを再配置
$ docker build -t oad3jp999/my-nginx:1.0.0 .
Sending build context to Docker daemon 13.82kB
Step 1/4 : FROM nginx
---> 76c69feac34e
Step 2/4 : COPY static-html-directory /usr/share/nginx/html
---> Using cache
---> 00de7b966f5c
Step 3/4 : COPY nginx.conf /etc/nginx/nginx.conf
---> Using cache
---> c0a6076cded2
Step 4/4 : COPY default.conf.template /etc/nginx/templates/default.conf.template
---> 39f088b173e2
Successfully built 39f088b173e2
Successfully tagged oad3jp999/my-nginx:1.0.0
$ docker run -d \
> -it \
> -p 8080:81 \
> --name my-nginx \
> --mount type=bind,source="$(pwd)"/target,target=/app,readonly \ ※ここで、ホストマシン側の「"$(pwd)"/target」を、コンテナ内の「/app」へマウント
> --env NGINX_PORT=81 \
> oad3jp999/my-nginx:1.0.0
cc97265d50624a1d5455665b83c1af58f288ca067e07c454269b7ca659c2a0c1
$ curl http://localhost:8080/ ※接続OK
hello this is mount target with nginx!
add text
$ docker exec -it my-nginx bash
root@cc97265d5062:/# cat /app/send.txt ※ディレクトリがマウントされて、send.txtなるファイルがルートアクセスで表示。ホスト側で「add text」を追記したらコンテナ側でも認識OK
hello this is mount target with nginx!
add text
root@cc97265d5062:/#
Docker でマウントする方法と、kubernetes でマウントする方法でどんな具合の差になるのかを抑えておく
結論、コンテナ起動時にマウント指定する点で類似。
(Docker の思想として環境依存なしに動くことが重要視されている点からは、なるほど、コンテナ起動時の指定がよさそうですね。納得。)
あとは接続可能なストレージは複数存在するも、「volumes」の書きっぷりを OpenShift 側の PVC なるものと比較しながら修正すれば、なんとなく行けそうな。
ということで minikube で 準備。
$ minikube start --driver=docker ※minikube起動
* Ubuntu 20.04 上の minikube v1.22.0
* プロフィールを元に、 docker ドライバを使用します
* コントロールプレーンのノード minikube を minikube 上で起動しています
* イメージを Pull しています...
* 既存の docker container を "minikube" のために再起動しています...
* Docker 20.10.7 で Kubernetes v1.21.2 を準備しています...
* Kubernetes コンポーネントを検証しています...
- イメージ kubernetesui/metrics-scraper:v1.0.4 を使用しています
- イメージ kubernetesui/dashboard:v2.1.0 を使用しています
- イメージ gcr.io/k8s-minikube/storage-provisioner:v5 を使用しています
* 有効なアドオン: storage-provisioner, default-storageclass, dashboard
! /usr/local/bin/kubectl is version 1.23.5, which may have incompatibilites with Kubernetes 1.21.2.
- Want kubectl v1.21.2? Try 'minikube kubectl -- get pods -A'
* 完了しました! kubectl が「"minikube"」クラスタと「"default"」ネームスペースを使用するよう構成されました
$ cd /tmp
$ mkdir hostpath_pv ※マウント用ディレクトり作成
$ ls -la
合計 96
drwxrwxrwt 21 root root 4096 11月 13 12:54 .
drwxr-xr-x 21 root root 4096 3月 14 2021 ..
drwxrwxr-x 2 senju senju 4096 11月 13 12:54 hostpath_pv
$ cd hostpath_pv
$ vi send.txt
$ cat send.txt
This is mount from hostpath for minikube! ※マウントされた際には、nginxがこちらを返す想定
$ cd ..
$ minikube mount /tmp/hostpath_pv:/host ※前段として、minikubeのVMが認識するようにマウントしておく必要がある模様
* Mounting host path /tmp/hostpath_pv into VM as /host ...
- マウントタイプ:
- ユーザー ID: docker
- グループ ID: docker
- バージョン: 9p2000.L
- メッセージのサイズ: 262144
- Permissions: 755 (-rwxr-xr-x)
- Options: map[]
- アドレスをバインドします: 192.168.58.1:40645
* Userspace file server: ufs starting
* Successfully mounted /tmp/hostpath_pv to /host ※ホストマシンの「/tmp/hostpath_pv」が「/host」としてマウントされた
* NOTE: This process must stay alive for the mount to be accessible ...
Pod を浮かべる用の yml を準備
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: oad3jp999/my-nginx:1.0.0
name: nginx
env:
- name: NGINX_PORT ※テンプレートで変数化したポート番号を環境変数として注入
value: "81"
volumeMounts:
- name: storage
mountPath: /app ※nginxの「/app」にマウントする。⇒ルートディレクトリに指定してあるので、例の「send.txt」なるものが読まれる。
volumes:
- name: storage
hostPath: ※本来、kubernetesではホストパスのストレージマウントは極めて限定的な目的での利用とされているが、いまはymlの書きっぷりが把握したいので、うってつけ
path: /host ※ホストマシンの「/tmp/hostpath_pv」がマウントされた「/host」を指定
type: DirectoryOrCreate ※直アクセス、無ければ作る
Pod を浮かべて接続 OK
$ kubectl apply -f my-nginx.yml
pod/nginx created
$ kubectl get all -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
pod/nginx 1/1 Running 0 7s 172.17.0.5 minikube <none> <none>
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 28h <none>
$ kubectl exec -it nginx bash ※面倒なので、中から突きます
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx:/# curl http://localhost:81/ ※接続OK
This is mount from hostpath for minikube! ※想定通り!
root@nginx:/#
関連記事
- CI/CD とは?
- Azure Devops (Artifacts) を無償、無料で使う
- Spring Tool Suite 4 インストール、Spring Initializr 、gradlew の利用
- Spring Boot Web アプリケーションを作る
- Jar の Library を作り、Azure Artifacts を利用して成果物を配布する
- ライブラリ開発者より指定されたバージョンを利用してアプリを作る
- アプリを Docker 、Docker Compose でコンテナを動かしてみる
- Azure Devops (PileLines) を無償、無料で使う
- Azure Devops (PileLines) でテスト、ビルド、イメージプッシュをする
- minikube(kubernetes) でコンテナを動かしてみる
- nginx のコンテナを Docker や minikube(kubernetes) で動かし、静的コンテンツ公開工程を模倣