使用docker自定義一個nginx+mysql+jdk的鏡像


我想做一個小項目,一個基於websocket的B端聊天室,需要的環境有:

mysql,jdk,nginx

我的想法是這樣:springboot寫的后端服務,前端web的頁面就不放進去了,在nginx下運行,因為之前學springboot的時候感覺懵懵的,分開寫好一點。其實不怕麻煩的話我是打算把mysql,jdk,nginx完全分開,做成3個容器,但是考慮了諸多因素之后還是決定做一個“一勞永逸“的鏡像或者dockerfile

准備文件:

1.jdk-linux-x64.tar.gz(jdk1.8,解壓后大概350MB,如果不打算裝jdk就可以跳過了)

2.my_path.sh(設置環境變量的腳本,暫時沒有用到)

#!/bin/bash
export JAVA_HOME=/usr/local/java/jdk1.8.0_131
export JRE_HOME=$JAVA_HOME/jre
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin

3.authorized_keys和sshd_config(公鑰文件和sshd配置文件,這里提示一下,公鑰文件除了所屬用戶外不能有寫權限,否則不能用,這是我踩的坑,我samba共享了一個路徑,路徑指向一個ntfs格式的硬盤,但是這個硬盤的權限有問題,所有文件都是777)

sshd_config其實也只改了兩行,允許root登錄,並且允許用密鑰登錄,按照我的習慣還應該禁止密碼登錄,不過隨便啦,docker的root密碼就沒初始化過。

PermitRootLogin yes
PubkeyAuthentication yes

4.mysql.cnf(這個是我當時在centos下用mysql時的配置文件,文件編碼全都改成utf8)

[client]
default-character-set=utf8
[mysql]
default-character-set=utf8
[mysqld]
character-set-server=utf8

datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock

symbolic-links=0

log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

5.mysqld.cnf(這是直接從安裝過一次的系統里面提取出來的,唯一改的是bind_address,原本的是127.0.0.1,遠程是連接不到的)

[mysqld_safe]
socket          = /var/run/mysqld/mysqld.sock
nice            = 0

[mysqld]
#
# * Basic Settings
#
user            = mysql
pid-file        = /var/run/mysqld/mysqld.pid
socket          = /var/run/mysqld/mysqld.sock
port            = 3306
basedir         = /usr
datadir         = /var/lib/mysql
tmpdir          = /tmp
lc-messages-dir = /usr/share/mysql
skip-external-locking
#
# Instead of skip-networking the default is now to listen only on
# localhost which is more compatible and is not less secure.
bind-address            = 0.0.0.0

key_buffer_size         = 16M
max_allowed_packet      = 16M
thread_stack            = 192K
thread_cache_size       = 8

myisam-recover-options  = BACKUP

query_cache_limit       = 1M
query_cache_size        = 16M

log_error = /var/log/mysql/error.log

expire_logs_days        = 10
max_binlog_size   = 100M

6.init.sh

#!/bin/bash

service nginx start #web服務正常啟動
service ssh start #ssh服務正常啟動

usermod -d /var/lib/mysql/ mysql
ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock
chown -R mysql:mysql /var/lib/mysql  #這三行解決No directory, logging in with HOME=/的問題
service mysql start #mysqlserver正常啟動

mysql -u root < /init.sql #執行sql文件的腳本,因為root用戶默認沒有密碼,所以可以這么搞

service mysql restart #mysqlserver重啟

rm -f init.sql #卸磨殺驢

7.init.sql(修改root密碼,修改數據庫的默認用戶為root,允許root通過任意地址登錄)

USE mysql;

GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION;
UPDATE user SET authentication_string=password('123456') WHERE user='root' AND host ='localhost';
UPDATE user SET plugin="mysql_native_password"; 
flush PRIVILEGES;

8.dockerfile(書寫要配置的鏡像內容)

FROM ubuntu:18.04
MAINTAINER thankvinci "thankvinci@163.com"
COPY authorized_keys /root/.ssh/authorized_keys #將公鑰文件復制到對應目錄
COPY sshd_config /etc/ssh/sshd_config #復制sshd配置文件
COPY jdk1.8.0_131 /usr/local/java/jdk1.8.0_131 #復制jdk
COPY my_path.sh /etc/profile.d/my_path.sh #復制自定義環境變量
COPY init.sql /init.sql #將初始化mysql的sql腳本放到根目錄
COPY init.sh /init.sh #將進行初始化的腳本放到根目錄
COPY start.sh /start.sh #最后一次修改時添加
RUN apt-get update \ 
	&& apt-get install -y openssh-server \
	&& apt-get install -y mysql-server-5.7 \  
		#通過apt安裝的mysql下面的默認用戶不是root,而是debian-sys-maint,密碼保存在/etc/mysql/debian.cnf里面,不過經過我的測試不用理會這個默認用戶,按照下面的操作就行
	&& apt-get install -y nginx \	
	&& apt-get install -y lsof \
	&& apt-get install -y vim \
	&& apt-get install -y iproute2 
	
COPY mysql.cnf /etc/mysql/conf.d/mysql.cnf #覆蓋原來的配置文件,改編碼為utf8
COPY mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf #修改bind_address為0.0.0.0,這樣才可以遠程訪問
	
ENV LANG=C.UTF-8 #語言環境

EXPOSE 22 80 3306 8080 #聲明要映射的端口,沒啥用,我比較常用的還是socat
CMD /bin/bash -c "/start.sh && /bin/bash" #最后一次修改時添加的

然后進行鏡像構建

docker build -t ubuntu:runenv .

構建完成:

Successfully built b19c9cdf2132
Successfully tagged ubuntu:runenv
thankvinci-desktop# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
ubuntu       runenv    b19c9cdf2132   2 minutes ago   969MB

3MB/s的小水管真的難受,最終得到的鏡像是969MB,近1個G,挺大的,而且后端服務還沒搭上去,前端頁面也沒放進去,甚至數據庫還沒有建表。

如果我要用socat,那么運行前,我得先建立一個網絡,這樣我才能定這個容器的IP地址,當然現在只是測試,而且有端口映射就不用知道容器的IP,所以網絡這一步就省了。

docker network create --driver bridge --subnet=172.18.12.0/16 --gateway=172.18.1.1 myprojectnet

接下來運行一下試試

docker run --name runenv -it ubuntu:runenv /bin/bash

init初始化腳本運行結果是:mysql編碼,root密碼,默認用戶 都修改成功了,但是source /etc/profile的內容卻不生效,仔細研究了一下,因為這個source是在腳本內的,所以$PATH也是更新在腳本的shell,與父進程無關,如果非要它實現的話,目前只找到一個方法,那就是source init腳本。

接着我又想到如果我關閉容器后要重新開啟這些服務呢?

我有兩個思路,一個是在init腳本初始化結束后寫一個啟動服務腳本,然后清空init腳本,在init腳本中執行啟動服務腳本,

另一個和這個一樣,但不同的是要修改鏡像內容,當也就是容器啟動CMD,這樣不加執行參數就是默認執行初始化,


寫到這里后我發現一件事情,就是我根本就沒必要把jdk加載到全局PATH里面,我可以之后再寫一個啟動服務的腳本,所以別太復雜化。

這里插一下docker啟動容器的一些特點吧,首先是CMD也就是容器啟動時運行的程序,這里分為3種情況,第1種是,當docker run 的參數為沒有d時,容器啟動后會直接進容器,當從容器出來時,容器就停止了,第2種是,當docker run的參數有d時,容器是后台運行的,使用exec進入容器后再出來容器還在運行,第3種是,當CMD為一個腳本時,腳本執行完容器就關閉了,不論是啟動參數有沒有d,而通常情況下CMD都是/bin/bash,也就是相當於一個shell進程一直在運行,如果參數沒有d的話容器停止后再啟動,其實是放在后台運行,相當於加了d,再通過exec進入容器就和上面第二點一樣了。


根據在菜鳥上看到的dockerfile文件中CMD只能有一條是生效的,像下面這種

CMD /init.sh && /bin/bash

執行完init.sh,容器就退出了,/bin/bash變成了在物理機執行,但是,也不是說不能執行兩條指令,使用-c 參數,把后面的字符串解析成一條命令去執行,就像這樣

CMD /bin/bash -c "/init.sh && /bin/bash"

最終

最后再做億次改動

1.多出來一個start.sh,就不要用init.sh去生成了

#!/bin/bash

if [ -f init.sh ]
then
/init.sh
echo "初始化鏡像服務完成"
rm -f init.sh #卸磨殺驢
else
service nginx start
service ssh start
service mysql start
fi

2.CMD跑的指令也改成這樣,當然copy的內容也多了一條

COPY start.sh /start.sh
CMD /bin/bash -c "/start.sh && /bin/bash"

再次構建后啟動

容器啟動,映射我要的全部端口

docker run --name runenv -p 80:80 -p 3306:3306 -p 8080:8080 -p 10022:22 -itd ubuntu:runenv
###強烈建議用--name參數去命名重要的容器,因為如果要啟停容器自己就知道容器名,不用去docker ps找,比較方便,然后docker容器名是唯一的,不能命名沖突
##-p指定端口映射,左邊是物理機的端口,右邊是容器的端口
#-itd,其實-d就可以,d表示容器啟動后擱后台運行

用docker ps查看可以看到

thankvinci-desktop# docker ps
CONTAINER ID   IMAGE           COMMAND                  CREATED          STATUS          PORTS                                                                                                                                                              NAMES
1605c64bf987   ubuntu:runenv   "/bin/sh -c '/bin/ba…"   15 seconds ago   Up 13 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp, 0.0.0.0:10022->22/tcp, :::10022->22/tcp   runenv

實際上用navicat,瀏覽器,putty都能訪問現有的三個服務

關於容器的啟停

docker start 1605c64bf987 #啟動容器,容器ID
docker start runenv #啟動容器,容器名
#停止是stop,重啟是restart,主要是如果忘了機器開着容器我就關機了,那么重新開機后這個容器是處於關閉的狀態的,要start,這個時候知道容器名就不用去找了,很方便。

關於容器的進出

docker exec -it runenv /bin/bash #runenv為容器名
#從容器中退出只需要輸入exit就可以,如果容器啟動時有用-d參數,那么可以直接用這個指令進入容器
#如果容器啟動時沒有用-d參數,exit后就停止了,需要start后再exec進入。
#通過exec進入后的容器,exit后不會停止,不論他原來run的時候有沒有-d參數。

我本來是以為說容器關閉后,再開啟可能服務不會啟動才寫了else里面的內容,測試了一下,容器關閉前停止現有的三個服務,然后再次啟動后,這三個服務都在運行中,也就是說else里面三個服務的啟動很多余,里面可以酌情改成別的,比如說后續寫的java后端打成jar包后就可以在else里面去執行,畢竟我也不想去把java的服務注冊在系統里面。第一次啟動容器時的步驟確實很重要,似乎得有第一次啟動后面才會自動啟動。

寫到這里我想起把常用於服務端的socket通信端口給忘了😂,不過還沒做到那一步,到時候再改改

最后注明一下:nginx的網站目錄默認在/var/www/html,如果靜態頁面不用經常修改的話直接在dockerfile里面nginx安裝后copy進去就行,如果和java后端一樣需要經常修改的話,還是把三個服務分開裝容器里好。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM